[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] master be86ece 04/28: js-syntax-propertize: Disambiguate J
From: |
Jackson Ray Hamilton |
Subject: |
[Emacs-diffs] master be86ece 04/28: js-syntax-propertize: Disambiguate JS from JSX, fixing some indents |
Date: |
Tue, 9 Apr 2019 02:00:12 -0400 (EDT) |
branch: master
commit be86ece42cbb6204480c794d018b02fbda74689b
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>
js-syntax-propertize: Disambiguate JS from JSX, fixing some indents
Fix some JSX indentation bugs:
- Bug#24896 / https://github.com/mooz/js2-mode/issues/389
- Bug#30225
- https://github.com/mooz/js2-mode/issues/459
* lisp/progmodes/js.el (js--dotted-captured-name-re)
(js--unary-keyword-re, js--unary-keyword-p)
(js--disambiguate-beginning-of-jsx-tag)
(js--disambiguate-end-of-jsx-tag)
(js--disambiguate-js-from-jsx): New variables and functions.
(js-syntax-propertize): Additionally clarify when syntax is JS so that
‘(with-syntax-table sgml-mode-syntax-table …)’ does not mistake some
JS punctuation syntax for SGML parenthesis syntax, namely ‘<’ and ‘>’.
* test/manual/indent/js-jsx-unclosed-2.js: Add additional test for
unary operator parsing.
---
lisp/progmodes/js.el | 100 +++++++++++++++++++++++++++++++-
test/manual/indent/js-jsx-unclosed-2.js | 14 +++++
2 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index 5b99253..d0556f3 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -82,6 +82,10 @@
(concat js--name-re "\\(?:\\." js--name-re "\\)*")
"Regexp matching a dot-separated sequence of JavaScript names.")
+(defconst js--dotted-captured-name-re
+ (concat "\\(" js--name-re "\\)\\(?:\\." js--name-re "\\)*")
+ "Like `js--dotted-name-re', but capture the first name.")
+
(defconst js--cpp-name-re js--name-re
"Regexp matching a C preprocessor name.")
@@ -1731,6 +1735,99 @@ This performs fontification according to
`js--class-styles'."
'syntax-table (string-to-syntax "\"/"))
(goto-char end)))))
+(defconst js--unary-keyword-re
+ (js--regexp-opt-symbol '("await" "delete" "typeof" "void" "yield"))
+ "Regexp matching unary operator keywords.")
+
+(defun js--unary-keyword-p (string)
+ "Check if STRING is a unary operator keyword in JavaScript."
+ (string-match-p js--unary-keyword-re string))
+
+(defun js--disambiguate-beginning-of-jsx-tag ()
+ "Parse enough to determine if a JSX tag starts here.
+Disambiguate JSX from equality operators by testing for syntax
+only valid as JSX."
+ ;; “</…” - a JSXClosingElement.
+ ;; “<>” - a JSXOpeningFragment.
+ (if (memq (char-after) '(?\/ ?\>)) t
+ (save-excursion
+ (skip-chars-forward " \t\n")
+ (and
+ (looking-at js--dotted-captured-name-re)
+ ;; Don’t match code like “if (i < await foo)”
+ (not (js--unary-keyword-p (match-string 1)))
+ (progn
+ (goto-char (match-end 0))
+ (skip-chars-forward " \t\n")
+ (or
+ ;; “>”, “/>” - tag enders.
+ ;; “{” - a JSXExpressionContainer.
+ (memq (char-after) '(?\> ?\/ ?\{))
+ ;; Check if a JSXAttribute follows.
+ (looking-at js--name-start-re)))))))
+
+(defun js--disambiguate-end-of-jsx-tag ()
+ "Parse enough to determine if a JSX tag ends here.
+Disambiguate JSX from equality operators by testing for syntax
+only valid as JSX, or extremely unlikely except as JSX."
+ (save-excursion
+ (backward-char)
+ ;; “…/>” - a self-closing JSXOpeningElement.
+ ;; “</>” - a JSXClosingFragment.
+ (if (= (char-before) ?/) t
+ (let (last-tag-or-attr-name last-non-unary-p)
+ (catch 'match
+ (while t
+ (skip-chars-backward " \t\n")
+ ;; Check if the end of a JSXAttribute value or
+ ;; JSXExpressionContainer almost certainly precedes.
+ ;; The only valid JS this misses is
+ ;; - {} > foo
+ ;; - "bar" > foo
+ ;; which is no great loss, IMHO…
+ (if (memq (char-before) '(?\} ?\" ?\' ?\`)) (throw 'match t)
+ (if (and last-tag-or-attr-name last-non-unary-p
+ ;; “<”, “</” - tag starters.
+ (memq (char-before) '(?\< ?\/)))
+ ;; Leftmost name parsed was the name of a
+ ;; JSXOpeningElement.
+ (throw 'match t))
+ ;; Technically the dotted name could span multiple
+ ;; lines, but dear God WHY?! Also, match greedily to
+ ;; ensure the entire name is valid.
+ (if (looking-back js--dotted-captured-name-re (point-at-bol) t)
+ (if (and (setq last-non-unary-p (not (js--unary-keyword-p
(match-string 1))))
+ last-tag-or-attr-name)
+ ;; Valid (non-unary) name followed rightwards by
+ ;; another name (any will do, including
+ ;; keywords) is invalid JS, but valid JSX.
+ (throw 'match t)
+ ;; Remember match and skip backwards over it when
+ ;; it is the first matched name or the N+1th
+ ;; matched unary name (unary names on the left are
+ ;; still ambiguously JS or JSX, so keep parsing to
+ ;; disambiguate).
+ (setq last-tag-or-attr-name (match-string 1))
+ (goto-char (match-beginning 0)))
+ ;; Nothing else to look for; give up parsing.
+ (throw 'match nil)))))))))
+
+(defun js--disambiguate-js-from-jsx (start end)
+ "Figure out which ‘<’ and ‘>’ chars (from START to END) aren’t JSX.
+
+Later, this info prevents ‘sgml-’ functions from treating some
+‘<’ and ‘>’ chars as parts of tokens of SGML tags — a good thing,
+since they are serving their usual function as some JS equality
+operator or arrow function, instead."
+ (goto-char start)
+ (while (re-search-forward "[<>]" end t)
+ (unless (if (eq (char-before) ?<) (js--disambiguate-beginning-of-jsx-tag)
+ (js--disambiguate-end-of-jsx-tag))
+ ;; Inform sgml- functions that this >, >=, >>>, <, <=, <<<, or
+ ;; => token is punctuation (and not an open or close parenthesis
+ ;; as per usual in sgml-mode).
+ (put-text-property (1- (point)) (point) 'syntax-table '(1)))))
+
(defun js-syntax-propertize (start end)
;; JavaScript allows immediate regular expression objects, written /.../.
(goto-char start)
@@ -1758,7 +1855,8 @@ This performs fontification according to
`js--class-styles'."
'syntax-table (string-to-syntax "\"/"))
(js-syntax-propertize-regexp end)))))
("\\`\\(#\\)!" (1 "< b")))
- (point) end))
+ (point) end)
+ (if js-jsx-syntax (js--disambiguate-js-from-jsx start end)))
(defconst js--prettify-symbols-alist
'(("=>" . ?⇒)
diff --git a/test/manual/indent/js-jsx-unclosed-2.js
b/test/manual/indent/js-jsx-unclosed-2.js
index 2d42cf7..8b6f333 100644
--- a/test/manual/indent/js-jsx-unclosed-2.js
+++ b/test/manual/indent/js-jsx-unclosed-2.js
@@ -15,3 +15,17 @@ if (foo > bar) void 0
// Don’t even misinterpret unary operators as JSX.
if (foo < await bar) void 0
while (await foo > bar) void 0
+
+// Allow unary keyword names as null-valued JSX attributes.
+// (As if this will EVER happen…)
+<Foo yield>
+ <Bar void>
+ <Baz
+ zorp
+ typeof>
+ <Please do_n0t delete this_stupidTest >
+ How would we ever live without unary support
+ </Please>
+ </Baz>
+ </Bar>
+</Foo>
- [Emacs-diffs] master 2bedd23 09/28: Update expectations for JSX indentation in JSXAttribute space, (continued)
- [Emacs-diffs] master 2bedd23 09/28: Update expectations for JSX indentation in JSXAttribute space, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 16669d7 16/28: Fix counting of nested self-closing JSXOpeningElements, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 52a3113 06/28: Add basic JSX font-locking, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 339be7c 11/28: Finish replacing SGML-based JSX detection with js-mode’s parsing, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 8dae742 08/28: Propertize and font-lock JSXText and JSXExpressionContainers, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 3eadf1e 24/28: Identify JSX strings (for js2-mode), Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 4b305bb 02/28: Refactor JSX indentation code to improve enclosing JSX discovery, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 58c77f1 01/28: Add failing tests for JSX indentation bugs, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 4d2b5bb 07/28: Font-lock JSX while editing it by extending regions, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 462baab 19/28: Add tests for miscellaneous JSX parsing feats, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master be86ece 04/28: js-syntax-propertize: Disambiguate JS from JSX, fixing some indents,
Jackson Ray Hamilton <=
- [Emacs-diffs] master d9d1bb2 14/28: Rename tests to use the “.jsx” file extension, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 84b1cfb 15/28: Indent broken arrow function bodies as an N+1th arg, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 6f53576 05/28: Use js-jsx- prefix for functions and variables, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master bf37078 12/28: Automatically detect JSX in JavaScript files, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master cf416d9 28/28: Explain reasonings for JSX syntax support design decisions, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 9545519 26/28: Add open/close parenthesis syntax to “<” and “>” in JSX, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 7c3ffda 27/28: Move curly functions closer to where they’re used, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 1a1ef28 10/28: Indent JSX as parsed in a JS context, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 55c80d4 17/28: Indent expressions in JSXAttributes relative to the attribute’s name, Jackson Ray Hamilton, 2019/04/09
- [Emacs-diffs] master 18bbfc4 25/28: Permit non-ASCII identifiers in JS, Jackson Ray Hamilton, 2019/04/09