diff options
| author | Jackson Ray Hamilton | 2019-02-17 21:16:13 -0800 |
|---|---|---|
| committer | Jackson Ray Hamilton | 2019-04-08 22:48:21 -0700 |
| commit | 4d2b5bbfebc040ca477f1156b44989b4e19bbc3e (patch) | |
| tree | baf41c198d666df2175fa13b74060860d1d95d7c | |
| parent | 52a3113b9beae6672c4bc981ee0c7bcc84ee58b5 (diff) | |
| download | emacs-4d2b5bbfebc040ca477f1156b44989b4e19bbc3e.tar.gz emacs-4d2b5bbfebc040ca477f1156b44989b4e19bbc3e.zip | |
Font-lock JSX while editing it by extending regions
* lisp/progmodes/js.el (js-jsx--font-lock-keywords):
Call tag beginning and end matchers.
(js-jsx--match-tag-beg, js-jsx--match-tag-end): New functions.
(js-jsx--syntax-propertize-tag): Record buffer positions of JSXElement
beginning and end for font-locking.
(js--syntax-propertize-extend-region)
(js-jsx--syntax-propertize-extend-region): New functions for extending
the syntax-propertize region backwards to the start of a JSXElement so
its JSXAttribute children on its n+1th lines can be parsed as such
while editing those lines.
(js-mode): Add js--syntax-propertize-extend-region to
syntax-propertize-extend-region-functions.
| -rw-r--r-- | lisp/progmodes/js.el | 81 |
1 files changed, 74 insertions, 7 deletions
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 1319fa19394..7fb4bcc808a 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el | |||
| @@ -1496,8 +1496,10 @@ point of view of font-lock. It applies highlighting directly with | |||
| 1496 | 1496 | ||
| 1497 | (defconst js-jsx--font-lock-keywords | 1497 | (defconst js-jsx--font-lock-keywords |
| 1498 | `((js-jsx--match-tag-name 0 font-lock-function-name-face t) | 1498 | `((js-jsx--match-tag-name 0 font-lock-function-name-face t) |
| 1499 | (js-jsx--match-attribute-name 0 font-lock-variable-name-face t)) | 1499 | (js-jsx--match-attribute-name 0 font-lock-variable-name-face t) |
| 1500 | "JSX font lock faces.") | 1500 | (js-jsx--match-tag-beg) |
| 1501 | (js-jsx--match-tag-end)) | ||
| 1502 | "JSX font lock faces and multiline text properties.") | ||
| 1501 | 1503 | ||
| 1502 | (defun js-jsx--match-tag-name (limit) | 1504 | (defun js-jsx--match-tag-name (limit) |
| 1503 | "Match JSXBoundaryElement names, until LIMIT." | 1505 | "Match JSXBoundaryElement names, until LIMIT." |
| @@ -1521,6 +1523,28 @@ point of view of font-lock. It applies highlighting directly with | |||
| 1521 | (progn (set-match-data value) t)) | 1523 | (progn (set-match-data value) t)) |
| 1522 | (js-jsx--match-attribute-name limit)))))) | 1524 | (js-jsx--match-attribute-name limit)))))) |
| 1523 | 1525 | ||
| 1526 | (defun js-jsx--match-tag-beg (limit) | ||
| 1527 | "Match JSXBoundaryElements from start, until LIMIT." | ||
| 1528 | (when js-jsx-syntax | ||
| 1529 | (let ((pos (next-single-char-property-change (point) 'js-jsx-tag-beg nil limit)) | ||
| 1530 | value) | ||
| 1531 | (when (and pos (> pos (point))) | ||
| 1532 | (goto-char pos) | ||
| 1533 | (or (and (setq value (get-text-property pos 'js-jsx-tag-beg)) | ||
| 1534 | (progn (put-text-property pos (cdr value) 'font-lock-multiline t) t)) | ||
| 1535 | (js-jsx--match-tag-beg limit)))))) | ||
| 1536 | |||
| 1537 | (defun js-jsx--match-tag-end (limit) | ||
| 1538 | "Match JSXBoundaryElements from end, until LIMIT." | ||
| 1539 | (when js-jsx-syntax | ||
| 1540 | (let ((pos (next-single-char-property-change (point) 'js-jsx-tag-end nil limit)) | ||
| 1541 | value) | ||
| 1542 | (when (and pos (> pos (point))) | ||
| 1543 | (goto-char pos) | ||
| 1544 | (or (and (setq value (get-text-property pos 'js-jsx-tag-end)) | ||
| 1545 | (progn (put-text-property value pos 'font-lock-multiline t) t)) | ||
| 1546 | (js-jsx--match-tag-end limit)))))) | ||
| 1547 | |||
| 1524 | (defconst js--font-lock-keywords-3 | 1548 | (defconst js--font-lock-keywords-3 |
| 1525 | `( | 1549 | `( |
| 1526 | ;; This goes before keywords-2 so it gets used preferentially | 1550 | ;; This goes before keywords-2 so it gets used preferentially |
| @@ -1769,11 +1793,53 @@ This performs fontification according to `js--class-styles'." | |||
| 1769 | "Check if STRING is a unary operator keyword in JavaScript." | 1793 | "Check if STRING is a unary operator keyword in JavaScript." |
| 1770 | (string-match-p js--unary-keyword-re string)) | 1794 | (string-match-p js--unary-keyword-re string)) |
| 1771 | 1795 | ||
| 1796 | (defun js--syntax-propertize-extend-region (start end) | ||
| 1797 | "Extend the START-END region for propertization, if necessary. | ||
| 1798 | For use by `syntax-propertize-extend-region-functions'." | ||
| 1799 | (if js-jsx-syntax (js-jsx--syntax-propertize-extend-region start end))) | ||
| 1800 | |||
| 1801 | (defun js-jsx--syntax-propertize-extend-region (start end) | ||
| 1802 | "Extend the START-END region for propertization, if necessary. | ||
| 1803 | If any “>” in the region appears to be the end of a tag starting | ||
| 1804 | before the start of the region, extend region backwards to the | ||
| 1805 | start of that tag so parsing may proceed from that point. | ||
| 1806 | For use by `syntax-propertize-extend-region-functions'." | ||
| 1807 | (let (new-start | ||
| 1808 | forward-sexp-function ; Use the Lisp version. | ||
| 1809 | parse-sexp-lookup-properties) ; Fix backward-sexp error here. | ||
| 1810 | (catch 'stop | ||
| 1811 | (goto-char start) | ||
| 1812 | (while (re-search-forward ">" end t) | ||
| 1813 | (catch 'continue | ||
| 1814 | ;; Check if this is really a right shift bitwise operator | ||
| 1815 | ;; (“>>” or “>>>”). | ||
| 1816 | (unless (or (eq (char-before (1- (point))) ?>) | ||
| 1817 | (eq (char-after) ?>)) | ||
| 1818 | (save-excursion | ||
| 1819 | (backward-char) | ||
| 1820 | (while (progn (if (= (point) (point-min)) (throw 'continue nil)) | ||
| 1821 | (/= (char-before) ?<)) | ||
| 1822 | (skip-chars-backward " \t\n") | ||
| 1823 | (if (= (point) (point-min)) (throw 'continue nil)) | ||
| 1824 | (cond | ||
| 1825 | ((memq (char-before) '(?\" ?\' ?\` ?\})) | ||
| 1826 | (condition-case nil | ||
| 1827 | (backward-sexp) | ||
| 1828 | (scan-error (throw 'continue nil)))) | ||
| 1829 | ((memq (char-before) '(?\/ ?\=)) (backward-char)) | ||
| 1830 | ((looking-back js--dotted-name-re (line-beginning-position) t) | ||
| 1831 | (goto-char (match-beginning 0))) | ||
| 1832 | (t (throw 'continue nil)))) | ||
| 1833 | (when (< (point) start) | ||
| 1834 | (setq new-start (1- (point))) | ||
| 1835 | (throw 'stop nil))))))) | ||
| 1836 | (if new-start (cons new-start end)))) | ||
| 1837 | |||
| 1772 | (defun js-jsx--syntax-propertize-tag (end) | 1838 | (defun js-jsx--syntax-propertize-tag (end) |
| 1773 | "Determine if a JSXBoundaryElement is before END and propertize it. | 1839 | "Determine if a JSXBoundaryElement is before END and propertize it. |
| 1774 | Disambiguate JSX from inequality operators and arrow functions by | 1840 | Disambiguate JSX from inequality operators and arrow functions by |
| 1775 | testing for syntax only valid as JSX." | 1841 | testing for syntax only valid as JSX." |
| 1776 | (let ((tag-beg (1- (point))) tag-end (type 'open) | 1842 | (let ((tag-beg (1- (point))) (type 'open) |
| 1777 | name-beg name-match-data unambiguous | 1843 | name-beg name-match-data unambiguous |
| 1778 | forward-sexp-function) ; Use Lisp version. | 1844 | forward-sexp-function) ; Use Lisp version. |
| 1779 | (catch 'stop | 1845 | (catch 'stop |
| @@ -1783,8 +1849,7 @@ testing for syntax only valid as JSX." | |||
| 1783 | (cond | 1849 | (cond |
| 1784 | ((= (char-after) ?>) | 1850 | ((= (char-after) ?>) |
| 1785 | (forward-char) | 1851 | (forward-char) |
| 1786 | (setq unambiguous t | 1852 | (setq unambiguous t) |
| 1787 | tag-end (point)) | ||
| 1788 | (throw 'stop nil)) | 1853 | (throw 'stop nil)) |
| 1789 | ;; Handle a JSXSpreadChild (“<Foo {...bar}”) or a | 1854 | ;; Handle a JSXSpreadChild (“<Foo {...bar}”) or a |
| 1790 | ;; JSXExpressionContainer as a JSXAttribute value | 1855 | ;; JSXExpressionContainer as a JSXAttribute value |
| @@ -1852,8 +1917,8 @@ testing for syntax only valid as JSX." | |||
| 1852 | ;; Save JSXBoundaryElement’s name’s match data for font-locking. | 1917 | ;; Save JSXBoundaryElement’s name’s match data for font-locking. |
| 1853 | (if name-beg (put-text-property name-beg (1+ name-beg) 'js-jsx-tag-name name-match-data)) | 1918 | (if name-beg (put-text-property name-beg (1+ name-beg) 'js-jsx-tag-name name-match-data)) |
| 1854 | ;; Mark beginning and end of tag for features like indentation. | 1919 | ;; Mark beginning and end of tag for features like indentation. |
| 1855 | (put-text-property tag-beg (1+ tag-beg) 'js-jsx-tag-beg type) | 1920 | (put-text-property tag-beg (1+ tag-beg) 'js-jsx-tag-beg (cons type (point))) |
| 1856 | (if tag-end (put-text-property (1- tag-end) tag-end 'js-jsx-tag-end tag-beg))))) | 1921 | (put-text-property (point) (1+ (point)) 'js-jsx-tag-end tag-beg)))) |
| 1857 | 1922 | ||
| 1858 | (defconst js-jsx--text-properties | 1923 | (defconst js-jsx--text-properties |
| 1859 | '(js-jsx-tag-beg nil js-jsx-tag-end nil js-jsx-tag-name nil js-jsx-attribute-name nil) | 1924 | '(js-jsx-tag-beg nil js-jsx-tag-end nil js-jsx-tag-name nil js-jsx-attribute-name nil) |
| @@ -3945,6 +4010,8 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3945 | '(font-lock-syntactic-face-function | 4010 | '(font-lock-syntactic-face-function |
| 3946 | . js-font-lock-syntactic-face-function))) | 4011 | . js-font-lock-syntactic-face-function))) |
| 3947 | (setq-local syntax-propertize-function #'js-syntax-propertize) | 4012 | (setq-local syntax-propertize-function #'js-syntax-propertize) |
| 4013 | (add-hook 'syntax-propertize-extend-region-functions | ||
| 4014 | #'js--syntax-propertize-extend-region 'append 'local) | ||
| 3948 | (setq-local prettify-symbols-alist js--prettify-symbols-alist) | 4015 | (setq-local prettify-symbols-alist js--prettify-symbols-alist) |
| 3949 | 4016 | ||
| 3950 | (setq-local parse-sexp-ignore-comments t) | 4017 | (setq-local parse-sexp-ignore-comments t) |