aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJackson Ray Hamilton2019-03-23 20:14:29 -0700
committerJackson Ray Hamilton2019-04-08 22:48:22 -0700
commitbf37078df2cbea3a44a641ddbe40f11339c135a2 (patch)
treecff66452d2d9ca2fbe4d676462fe76c66a311819
parent339be7c00790fb407cc8449fa8f59baa792cbe69 (diff)
downloademacs-bf37078df2cbea3a44a641ddbe40f11339c135a2.tar.gz
emacs-bf37078df2cbea3a44a641ddbe40f11339c135a2.zip
Automatically detect JSX in JavaScript files
* lisp/files.el (auto-mode-alist): Simply enable javascript-mode (js-mode) when opening “.jsx” files, since the “.jsx” file extension will be used as an indicator of JSX syntax by js-mode, and more code is likely to work in js-mode than js-jsx-mode, and we probably want to guide users to use js-mode (with js-jsx-syntax) instead. Code that used to work exclusively in js-jsx-mode (if anyone ever wrote any) ought to be updated to work in js-mode too when js-jsx-syntax is set to t. * lisp/progmodes/js.el (js-jsx-detect-syntax, js-jsx-regexps) (js-jsx--detect-and-enable, js-jsx--detect-after-change): New variables and functions for detecting and enabling JSX. (js-jsx-syntax): Update docstring with respect to the widened scope of the effects and use of this variable. (js-syntactic-mode-name, js--update-mode-name) (js--idly-update-mode-name, js-jsx-enable): New variable and functions for indicating when JSX is enabled. (js-mode): Detect and enable JSX. Print all enabled syntaxes after the mode name whenever Emacs goes idle; this ensures lately-enabled syntaxes are evident. (js-jsx-mode): Update mode name for consistency with the state in which JSX is enabled in js-mode. Update docstring to suggest alternative means of using JSX without this mode. Going forward, it may be best to gently guide users away from js-jsx-mode, since a “one mode per syntax extension” model would not scale well if more syntax extensions were to be simultaneously supported (e.g. Facebook’s “Flow”).
-rw-r--r--lisp/files.el3
-rw-r--r--lisp/progmodes/js.el119
2 files changed, 115 insertions, 7 deletions
diff --git a/lisp/files.el b/lisp/files.el
index 1dae57593a0..b81550e297c 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2705,9 +2705,8 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|CBR\\|7Z\\)\\'" . archive-mo
2705 ("\\.dbk\\'" . xml-mode) 2705 ("\\.dbk\\'" . xml-mode)
2706 ("\\.dtd\\'" . sgml-mode) 2706 ("\\.dtd\\'" . sgml-mode)
2707 ("\\.ds\\(ss\\)?l\\'" . dsssl-mode) 2707 ("\\.ds\\(ss\\)?l\\'" . dsssl-mode)
2708 ("\\.jsm?\\'" . javascript-mode) 2708 ("\\.js[mx]?\\'" . javascript-mode)
2709 ("\\.json\\'" . javascript-mode) 2709 ("\\.json\\'" . javascript-mode)
2710 ("\\.jsx\\'" . js-jsx-mode)
2711 ("\\.[ds]?vh?\\'" . verilog-mode) 2710 ("\\.[ds]?vh?\\'" . verilog-mode)
2712 ("\\.by\\'" . bovine-grammar-mode) 2711 ("\\.by\\'" . bovine-grammar-mode)
2713 ("\\.wy\\'" . wisent-grammar-mode) 2712 ("\\.wy\\'" . wisent-grammar-mode)
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index df2c41332e7..0bba8159c18 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -574,10 +574,30 @@ then the \".\"s will be lined up:
574 :safe 'booleanp 574 :safe 'booleanp
575 :group 'js) 575 :group 'js)
576 576
577(defcustom js-jsx-detect-syntax t
578 "When non-nil, automatically detect whether JavaScript uses JSX.
579`js-jsx-syntax' (which see) may be made buffer-local and set to
580t. The detection strategy can be customized by adding elements
581to `js-jsx-regexps', which see."
582 :version "27.1"
583 :type 'boolean
584 :safe 'booleanp
585 :group 'js)
586
577(defcustom js-jsx-syntax nil 587(defcustom js-jsx-syntax nil
578 "When non-nil, parse JavaScript with consideration for JSX syntax. 588 "When non-nil, parse JavaScript with consideration for JSX syntax.
579This fixes indentation of JSX code in some cases. It is set to 589
580be buffer-local when in `js-jsx-mode'." 590This enables proper font-locking and indentation of code using
591Facebook’s “JSX” syntax extension for JavaScript, for use with
592Facebook’s “React” library. Font-locking is like sgml-mode.
593Indentation is also like sgml-mode, although some indentation
594behavior may differ slightly to align more closely with the
595conventions of the React developer community.
596
597When `js-mode' is already enabled, you should call
598`js-jsx-enable' to set this variable.
599
600It is set to be buffer-local (and t) when in `js-jsx-mode'."
581 :version "27.1" 601 :version "27.1"
582 :type 'boolean 602 :type 'boolean
583 :safe 'booleanp 603 :safe 'booleanp
@@ -4223,6 +4243,79 @@ If one hasn't been set, or if it's stale, prompt for a new one."
4223 (when temp-name 4243 (when temp-name
4224 (delete-file temp-name)))))) 4244 (delete-file temp-name))))))
4225 4245
4246;;; Syntax extensions
4247
4248(defvar js-syntactic-mode-name t
4249 "If non-nil, print enabled syntaxes in the mode name.")
4250
4251(defun js--update-mode-name ()
4252 "Print enabled syntaxes if `js-syntactic-mode-name' is t."
4253 (when js-syntactic-mode-name
4254 (setq mode-name (concat "JavaScript"
4255 (if js-jsx-syntax "+JSX" "")))))
4256
4257(defun js--idly-update-mode-name ()
4258 "Update `mode-name' whenever Emacs goes idle.
4259In case `js-jsx-syntax' is updated, especially by features of
4260Emacs like .dir-locals.el or file variables, this ensures the
4261modeline eventually reflects which syntaxes are enabled."
4262 (let (timer)
4263 (setq timer
4264 (run-with-idle-timer
4265 0 t
4266 (lambda (buffer)
4267 (if (buffer-live-p buffer)
4268 (with-current-buffer buffer
4269 (js--update-mode-name))
4270 (cancel-timer timer)))
4271 (current-buffer)))))
4272
4273(defun js-jsx-enable ()
4274 "Enable JSX in the current buffer."
4275 (interactive)
4276 (setq-local js-jsx-syntax t)
4277 (js--update-mode-name))
4278
4279(defvar js-jsx-regexps
4280 (list "\\_<\\(?:var\\|let\\|const\\|import\\)\\_>.*?React")
4281 "Regexps for detecting JSX in JavaScript buffers.
4282When `js-jsx-detect-syntax' is non-nil and any of these regexps
4283match text near the beginning of a JavaScript buffer,
4284`js-jsx-syntax' (which see) will be made buffer-local and set to
4285t.")
4286
4287(defun js-jsx--detect-and-enable (&optional arbitrarily)
4288 "Detect if JSX is likely to be used, and enable it if so.
4289Might make `js-jsx-syntax' buffer-local and set it to t. Matches
4290from the beginning of the buffer, unless optional arg ARBITRARILY
4291is non-nil. Return t after enabling, nil otherwise."
4292 (when (or (and (buffer-file-name)
4293 (string-match-p "\\.jsx\\'" (buffer-file-name)))
4294 (and js-jsx-detect-syntax
4295 (save-excursion
4296 (unless arbitrarily
4297 (goto-char (point-min)))
4298 (catch 'match
4299 (mapc
4300 (lambda (regexp)
4301 (if (re-search-forward regexp 4000 t) (throw 'match t)))
4302 js-jsx-regexps)
4303 nil))))
4304 (js-jsx-enable)
4305 t))
4306
4307(defun js-jsx--detect-after-change (beg end _len)
4308 "Detect if JSX is likely to be used after a change.
4309This function is intended for use in `after-change-functions'."
4310 (when (<= end 4000)
4311 (save-excursion
4312 (goto-char beg)
4313 (beginning-of-line)
4314 (save-restriction
4315 (narrow-to-region (point) end)
4316 (when (js-jsx--detect-and-enable 'arbitrarily)
4317 (remove-hook 'after-change-functions #'js-jsx--detect-after-change t))))))
4318
4226;;; Main Function 4319;;; Main Function
4227 4320
4228;;;###autoload 4321;;;###autoload
@@ -4259,6 +4352,12 @@ If one hasn't been set, or if it's stale, prompt for a new one."
4259 ;; Frameworks 4352 ;; Frameworks
4260 (js--update-quick-match-re) 4353 (js--update-quick-match-re)
4261 4354
4355 ;; Syntax extensions
4356 (unless (js-jsx--detect-and-enable)
4357 (add-hook 'after-change-functions #'js-jsx--detect-after-change nil t))
4358 (js--update-mode-name) ; If `js-jsx-syntax' was set from outside.
4359 (js--idly-update-mode-name)
4360
4262 ;; Imenu 4361 ;; Imenu
4263 (setq imenu-case-fold-search nil) 4362 (setq imenu-case-fold-search nil)
4264 (setq imenu-create-index-function #'js--imenu-create-index) 4363 (setq imenu-create-index-function #'js--imenu-create-index)
@@ -4304,10 +4403,20 @@ If one hasn't been set, or if it's stale, prompt for a new one."
4304 ) 4403 )
4305 4404
4306;;;###autoload 4405;;;###autoload
4307(define-derived-mode js-jsx-mode js-mode "JSX" 4406(define-derived-mode js-jsx-mode js-mode "JavaScript+JSX"
4308 "Major mode for editing JSX." 4407 "Major mode for editing JavaScript+JSX.
4408
4409Simply makes `js-jsx-syntax' buffer-local and sets it to t.
4410
4411`js-mode' may detect and enable support for JSX automatically if
4412it appears to be used in a JavaScript file. You could also
4413customize `js-jsx-regexps' to improve that detection; or, you
4414could set `js-jsx-syntax' to t in your init file, or in a
4415.dir-locals.el file, or using file variables; or, you could call
4416`js-jsx-enable' in `js-mode-hook'. You may be better served by
4417one of the aforementioned options instead of using this mode."
4309 :group 'js 4418 :group 'js
4310 (setq-local js-jsx-syntax t)) 4419 (js-jsx-enable))
4311 4420
4312;;;###autoload (defalias 'javascript-mode 'js-mode) 4421;;;###autoload (defalias 'javascript-mode 'js-mode)
4313 4422