{"id":576,"date":"2010-09-17T03:53:47","date_gmt":"2010-09-17T11:53:47","guid":{"rendered":"http:\/\/www.visophyte.org\/blog\/?p=576"},"modified":"2011-01-01T18:36:43","modified_gmt":"2011-01-02T02:36:43","slug":"non-infuriating-indentation-with-emacs-and-js2-mode-with-require-def-asynchronous-module-definition-commonjs-boilerplate","status":"publish","type":"post","link":"https:\/\/www.visophyte.org\/blog\/2010\/09\/17\/non-infuriating-indentation-with-emacs-and-js2-mode-with-require-def-asynchronous-module-definition-commonjs-boilerplate\/","title":{"rendered":"non-infuriating indentation with emacs and js2-mode with require.def asynchronous module definition CommonJS boilerplate"},"content":{"rendered":"<p>Classic CommonJS modules assume a synchronous execution environment (for the purposes of &#8220;require&#8221;) with a specialized loader mechanism that evaluates the module in its proper context and takes care of namespacing it.\u00a0 If you want to use CommonJS modules in the browser you can either:<\/p>\n<ul>\n<li>Leave the source code as it is and use an XHR-based loader that uses eval to perform the namespacing trick.\u00a0 In order to deal with the synchronous require assumption you can use some combination of deferring the evaluation of the module until you think you have all the dependencies and synchronous XHR.\u00a0 Commonly, regular expressions are used to figure out the dependencies, but one could also use some form of static analysis.\u00a0 Examples of browser-based CommonJS loaders supporting this are <a href=\"http:\/\/github.com\/Gozala\/teleport\">teleport<\/a> and <a href=\"http:\/\/github.com\/jbrantly\/yabble\">yabble<\/a>.<\/li>\n<li>Wrap your source code in boilerplate that takes care of the namespacing.\u00a0 This can be done via a build system or done permanently in the source.\u00a0 Pretty much every browser-based CommonJS loader supports this, with <a href=\"http:\/\/requirejs.org\/\">RequireJS<\/a> being the only one I&#8217;m going to name-check because there are too many of these suckers as is.<\/li>\n<\/ul>\n<p>The synchronous idiom for module &#8220;foo&#8221; might look like this:<\/p>\n<pre lang=\"javascript\">var bar = require(\"bar\");\r\nvar baz = require(\"baz\");\r\n\r\nexports.doStuff = function() {\r\n  return \"awwww yeah.\";\r\n};<\/pre>\n<p>The <a href=\"http:\/\/wiki.commonjs.org\/wiki\/Modules\/AsynchronousDefinition\">asynchronous module definition<\/a> for &#8220;foo&#8221; might look like this, noting that there are actually a couple of possible variations on this:<\/p>\n<pre lang=\"javascript\">require.def(\"foo\", [\"exports\", \"bar\", \"baz\"], function(exports, bar, baz) {\r\n\r\nexports.doStuff = function() {\r\n  return \"awwww yeah.\";\r\n};\r\n\r\n);<\/pre>\n<p>The thing that may jump out at you is that the asynchronous wrapping means that the body of our module actually lives inside a function definition within the argument list of a function call.\u00a0 Let&#8217;s assume you enjoy the finer things in life and are using emacs and js2-mode for your javascript editing.\u00a0 js2-mode will helpfully suggest indenting 14 characters because that puts us 2 characters in from the enclosing function call&#8217;s opening paren.<\/p>\n<p>That indentation could drive a man crazy and was really my only reason for avoiding the asynchronous idiom.\u00a0 Thankfully, emacs being what it is, I was able to make it do what I roughly what I want:<\/p>\n<pre lang=\"lisp\">;; Check if the suggested indentation is due to require.def().  If it is, force\r\n;;  the indentation down to zero.  We detect this case by checking whether the\r\n;;  parse depth is 2 and the last top-level point was preceded by require.def.\r\n(defun require-def-deindent (list index)\r\n  (when (and (eq (nth 0 parse-status) 2)\r\n             (save-excursion\r\n               (let ((tl-point (syntax-ppss-toplevel-pos parse-status)))\r\n                 (goto-char tl-point)\r\n                 (backward-word 2)\r\n                 (equal \"require.def\" (buffer-substring (point) tl-point))))\r\n             ;; only intercede if they are suggesting what the sexprs suggest\r\n             (let ((suggested-column (js-proper-indentation parse-status)))\r\n               (eq (nth index list) suggested-column))\r\n             )\r\n    (indent-line-to 0)\r\n    't\r\n    ))\r\n;; Uncomment the following to enable the hook if you want tab to always slam you\r\n;;  to column 0 rather than doing the cycle thing.  (With the newline hook in\r\n;;  place, I haven't seen the need yet.)\r\n;(add-hook 'js2-indent-hook 'require-def-deindent)\r\n\r\n;; Unfortunately, js2-enter-key turns off the bounce indent logic so we need to\r\n;;  intentionally do something to get our helper invoked.  In this case, we use\r\n;;  advice but we could also mess with the keybinding.\r\n;; This assumes js2-enter-indents-newline is enabled \/ desired.\r\n(defadvice js2-enter-key (around js2-enter-key-around)\r\n  \"Trigger require-def-deindent on enter for the newline.\"\r\n  ad-do-it\r\n  (let ((parse-status (save-excursion\r\n                        (parse-partial-sexp (point-min) (point-at-bol))))\r\n        positions)\r\n    (push (current-column) positions)\r\n    (require-def-deindent positions 0)))\r\n(ad-activate 'js2-enter-key)<\/pre>\n<p>If you paste the above into your .emacs and have sufficient emacs karma, hopefully the above will work for you too.<\/p>\n<p><strong>UPDATE (2011\/1\/1):<\/strong><\/p>\n<p>The AMD idiom has settled on using &#8220;define&#8221; instead of &#8220;require.def&#8221;, so here is the above code modified to this end:<\/p>\n<pre lang=\"lisp\">;; --- CommonJS AMD define() compensation\r\n\r\n;; Check if the suggested indentation is due to define().  If it is, force\r\n;;  the indentation down to zero.  We detect this case by checking whether the\r\n;;  parse depth is 2 and the last top-level point was preceded by define.\r\n(defun require-def-deindent (list index)\r\n  (when (and (eq (nth 0 parse-status) 2)\r\n             (save-excursion\r\n               (let ((tl-point (syntax-ppss-toplevel-pos parse-status)))\r\n                 (goto-char tl-point)\r\n                 (backward-word 1)\r\n                 (equal \"define\" (buffer-substring (point) tl-point))))\r\n             ;; only intercede if they are suggesting what the sexprs suggest\r\n             (let ((suggested-column (js-proper-indentation parse-status)))\r\n               (eq (nth index list) suggested-column))\r\n             )\r\n    (indent-line-to 0)\r\n    't\r\n    ))\r\n;; Uncomment the following to enable the hook if you want tab to always slam you\r\n;;  to column 0 rather than doing the cycle thing.  (With the newline hook in\r\n;;  place, I haven't seen the need yet.)\r\n;(add-hook 'js2-indent-hook 'require-def-deindent)\r\n\r\n;; Unfortunately, js2-enter-key turns off the bounce indent logic so we need to\r\n;;  intentionally do something to get our helper invoked.  In this case, we use\r\n;;  advice but we could also mess with the keybinding.\r\n;; This assumes js2-enter-indents-newline is enabled \/ desired.\r\n(defadvice js2-enter-key (around js2-enter-key-around)\r\n  \"Trigger require-def-deindent on enter for the newline.\"\r\n  ad-do-it\r\n  (let ((parse-status (save-excursion\r\n                        (parse-partial-sexp (point-min) (point-at-bol))))\r\n        positions)\r\n    (push (current-column) positions)\r\n    (require-def-deindent positions 0)))\r\n(ad-activate 'js2-enter-key)\r\n\r\n;; (end define compensation)<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Classic CommonJS modules assume a synchronous execution environment (for the purposes of &#8220;require&#8221;) with a specialized loader mechanism that evaluates the module in its proper context and takes care of namespacing it.\u00a0 If you want to use CommonJS modules in &hellip; <a href=\"https:\/\/www.visophyte.org\/blog\/2010\/09\/17\/non-infuriating-indentation-with-emacs-and-js2-mode-with-require-def-asynchronous-module-definition-commonjs-boilerplate\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[92,79,91],"class_list":["post-576","post","type-post","status-publish","format-standard","hentry","category-software","tag-commonjs","tag-emacs","tag-js2-mode"],"_links":{"self":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/576","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/comments?post=576"}],"version-history":[{"count":9,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/576\/revisions"}],"predecessor-version":[{"id":584,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/576\/revisions\/584"}],"wp:attachment":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/media?parent=576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/categories?post=576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/tags?post=576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}