You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

281 lines
14 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Setting up Emacs for JavaScript (part #1)</title>
<meta name="description" content="There’s a lot that can be done to make Emacs a great tool for JavaScript development. So much that I had to split it up into several posts.">
<link rel="shortcut icon" type="image/png" href="/favicon.png"/>
<link href="https://fonts.googleapis.com/css?family=Fira+Mono|Gentium+Book+Basic|Lato" rel="stylesheet">
<link rel="stylesheet" href="/assets/main.css">
<link rel="stylesheet" href="/css/cafe.css">
<link rel="canonical" href="https://emacs.cafe/emacs/javascript/setup/2017/04/23/emacs-setup-javascript.html">
<link rel="alternate" type="application/rss+xml" title="Emacs café" href="/feed.xml">
</head>
<body>
<header class="site-header" role="banner">
<div class="wrapper">
<a class="site-title" href="/"><img src="/img/emacscafe.png"/>Emacs café</a>
<nav class="site-nav">
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger">
<span class="menu-icon">
<svg viewBox="0 0 18 15" width="18px" height="15px">
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
</svg>
</span>
</label>
<div class="trigger">
<a class="page-link" href="/about/">About Emacs café</a>
</div>
</nav>
</div>
</header>
<main class="page-content" aria-label="Content">
<div class="wrapper">
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
<header class="post-header">
<h1 class="post-title" itemprop="name headline">Setting up Emacs for JavaScript (part #1)</h1>
<p class="post-meta">
<time datetime="2017-04-23T14:50:00+02:00" itemprop="datePublished">
Apr 23, 2017
</time>
<span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Nicolas Petton</span></span>
</p>
</header>
<div class="post-content" itemprop="articleBody">
<p>There’s a lot that can be done to make Emacs a great tool for JavaScript
development. So much that I had to split it up into several posts.</p>
<p>In this first article we’ll see how to
setup <a href="https://github.com/mooz/js2-mode">js2-mode</a> and two other packages that
rely upon it: <a href="https://github.com/magnars/js2-refactor.el">js2-refactor</a>
and <a href="https://github.com/nicolaspetton/xref-js2">xref-js2</a>.</p>
<p>Emacs comes with a major mode for JavaScript named <code class="highlighter-rouge">js-mode</code>. While it is a
good major mode, we’ll be using <code class="highlighter-rouge">js2-mode</code> instead, an external package that
extends <code class="highlighter-rouge">js-mode</code> and provides a very interesting feature: instead of using
regular expressions, it parses buffers and builds an AST for things like syntax
highlighting. While diverging a bit from the traditional “Emacs way of doing
things”, this is really interesting, and used as the foundation for other
features like refactorings.</p>
<h2 id="setting-up-js2-mode">Setting up js2-mode</h2>
<p>If you haven’t done it already, you should first setup <code class="highlighter-rouge">package.el</code> to
use <a href="https://melpa.org">MELPA</a>, then install and setup <code class="highlighter-rouge">js2-mode</code> like the
following:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>M-x package-install RET js2-mode RET
</code></pre>
</div>
<figure class="highlight"><pre><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">require</span> <span class="ss">'js2-mode</span><span class="p">)</span>
<span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">'auto-mode-alist</span> <span class="o">'</span><span class="p">(</span><span class="s">"\\.js\\'"</span> <span class="o">.</span> <span class="nv">js2-mode</span><span class="p">))</span>
<span class="c1">;; Better imenu</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'js2-mode-hook</span> <span class="nf">#'</span><span class="nv">js2-imenu-extras-mode</span><span class="p">)</span></code></pre></figure>
<h2 id="js2-refactor-and-xref-js2">js2-refactor and xref-js2</h2>
<p>Now that we’re using <code class="highlighter-rouge">js2-mode</code> for JavaScript buffers, let’s take advantage its
capabilities of and install two other
packages: <a href="https://github.com/magnars/js2-refactor.el">js2-refactor</a>
and <a href="https://github.com/nicolaspetton/xref-js2">xref-js2</a>.</p>
<p><code class="highlighter-rouge">js2-refactor</code> adds powerful refactorings based on the AST generated by
<code class="highlighter-rouge">js2-mode</code>, and <code class="highlighter-rouge">xref-js2</code> makes it easy to jump to function references or
definitions.</p>
<p><code class="highlighter-rouge">xref-js2</code> uses <code class="highlighter-rouge">ag</code> to perform searches, so you’ll need to install it as well.</p>
<div class="highlighter-rouge"><pre class="highlight"><code>M-x package-install RET js2-refactor RET
M-x package-install RET xref-js2 RET
</code></pre>
</div>
<figure class="highlight"><pre><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nb">require</span> <span class="ss">'js2-refactor</span><span class="p">)</span>
<span class="p">(</span><span class="nb">require</span> <span class="ss">'xref-js2</span><span class="p">)</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'js2-mode-hook</span> <span class="nf">#'</span><span class="nv">js2-refactor-mode</span><span class="p">)</span>
<span class="p">(</span><span class="nv">js2r-add-keybindings-with-prefix</span> <span class="s">"C-c C-r"</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">js2-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-k"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">js2r-kill</span><span class="p">)</span>
<span class="c1">;; js-mode (which js2 is based on) binds "M-." which conflicts with xref, so</span>
<span class="c1">;; unbind it.</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">js-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"M-."</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'js2-mode-hook</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">()</span>
<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'xref-backend-functions</span> <span class="nf">#'</span><span class="nv">xref-js2-xref-backend</span> <span class="no">nil</span> <span class="no">t</span><span class="p">)))</span></code></pre></figure>
<p>Now that everything’s setup, let’s see how to use <code class="highlighter-rouge">js2-refactor</code> and <code class="highlighter-rouge">xref-js2</code>.</p>
<h2 id="using-js2-refactor">Using js2-refactor</h2>
<p><code class="highlighter-rouge">js2-refactor</code> is a JavaScript refactoring library for emacs.</p>
<p>It provides a collection of refactoring functions leveraging the AST provided by
<code class="highlighter-rouge">js2-mode</code>.</p>
<p>Refactorings go from inlining/extracting variables to converting ternary
operators to if statements.
The <a href="https://github.com/magnars/js2-refactor.el">README</a> provides the full list of
keybindings.</p>
<p>One minor tweak that I really couldn’t live without is binding <code class="highlighter-rouge">js2r-kill</code> to
<code class="highlighter-rouge">C-k</code> in JS buffers:</p>
<figure class="highlight"><pre><code class="language-elisp" data-lang="elisp"><span class="p">(</span><span class="nv">define-key</span> <span class="nv">js2-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-k"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">js2r-kill</span><span class="p">)</span></code></pre></figure>
<p>This command is very similar to killing in <code class="highlighter-rouge">paredit</code>: It kills up to the end of
the line, but always keeping the AST valid.</p>
<p>Here’s a usage example of <code class="highlighter-rouge">js2-refactor</code>: renaming a function parameter and
inlining a variable.</p>
<p><img src="/img/js2-refactorings.gif" alt="Refactorings" /></p>
<h2 id="using-xref-js2">Using xref-js2</h2>
<p><code class="highlighter-rouge">xref-js2</code> adds support for quickly jumping to function definitions or
references to JavaScript projects in Emacs (&gt;= 25.1).</p>
<p>Instead of using a tag system, it relies on <code class="highlighter-rouge">ag</code> to query the codebase of a
project.</p>
<ul>
<li><code class="highlighter-rouge">M-.</code> Jump to definition</li>
<li><code class="highlighter-rouge">M-?</code> Jump to references</li>
<li><code class="highlighter-rouge">M-,</code> Pop back to where <code class="highlighter-rouge">M-.</code> was last invoked.</li>
</ul>
<p>Here’s a usage example of <code class="highlighter-rouge">xref-js2</code>:</p>
<p><img src="/img/xref-jump-to-references.gif" alt="Jumping to references" /></p>
<h2 id="until-next-time">Until next time</h2>
<p>You should now have a decent setup for <code class="highlighter-rouge">js2-mode</code> and associated tools.</p>
<p>We still have a lot to explore like linting, getting good auto-completion, using
snippets, setting up a REPL and debugger, etc. but I promised I would keep posts
short, so stay tuned for part #2!</p>
</div>
<div id="disqus_thread"></div>
<script>
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'emacs-cafe';
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
</article>
</div>
</main>
<footer class="site-footer">
<div class="wrapper">
<h2 class="footer-heading">Emacs café</h2>
<div class="footer-col-wrapper">
<div class="footer-col footer-col-1">
<ul class="contact-list">
<li>
Emacs café
</li>
<li><a href="mailto:nicolas@petton.fr">nicolas@petton.fr</a></li>
</ul>
</div>
<div class="footer-col footer-col-2">
<ul class="social-media-list">
<li>
<a href="https://github.com/NicolasPetton"><span class="icon icon--github"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/></svg>
</span><span class="username">NicolasPetton</span></a>
</li>
<li>
<a href="https://twitter.com/NicolasPetton"><span class="icon icon--twitter"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/></svg>
</span><span class="username">NicolasPetton</span></a>
</li>
</ul>
</div>
<div class="footer-col footer-col-3">
<p>A blog about Emacs, mostly focused on JavaScript development, by Nicolas Petton.
</p>
</div>
</div>
</div>
</footer>
</body>
</html>