diff options
| author | Stephen Leake | 2019-04-11 14:00:02 -0700 |
|---|---|---|
| committer | Stephen Leake | 2019-04-11 14:00:02 -0700 |
| commit | 7ba7def5caf7ec9d9bebffff489f0a658229fbda (patch) | |
| tree | e0cfcb59937ca0528fb81769d7d48a904a91f5dc /lisp/progmodes | |
| parent | 7768581172e11be52b1fcd8224f4594e126bbdb7 (diff) | |
| parent | de238b39e335c6814283faa171b35145f124edf2 (diff) | |
| download | emacs-7ba7def5caf7ec9d9bebffff489f0a658229fbda.tar.gz emacs-7ba7def5caf7ec9d9bebffff489f0a658229fbda.zip | |
Merge commit 'de238b39e335c6814283faa171b35145f124edf2'
Diffstat (limited to 'lisp/progmodes')
| -rw-r--r-- | lisp/progmodes/bug-reference.el | 6 | ||||
| -rw-r--r-- | lisp/progmodes/compile.el | 5 | ||||
| -rw-r--r-- | lisp/progmodes/grep.el | 12 | ||||
| -rw-r--r-- | lisp/progmodes/js.el | 1200 | ||||
| -rw-r--r-- | lisp/progmodes/python.el | 40 | ||||
| -rw-r--r-- | lisp/progmodes/sh-script.el | 3 | ||||
| -rw-r--r-- | lisp/progmodes/verilog-mode.el | 6 |
7 files changed, 983 insertions, 289 deletions
diff --git a/lisp/progmodes/bug-reference.el b/lisp/progmodes/bug-reference.el index 759db1f5686..813ecbe3847 100644 --- a/lisp/progmodes/bug-reference.el +++ b/lisp/progmodes/bug-reference.el | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | ;; bug-reference.el --- buttonize bug references | 1 | ;; bug-reference.el --- buttonize bug references -*- lexical-binding: t; -*- |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 2008-2019 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2008-2019 Free Software Foundation, Inc. |
| 4 | 4 | ||
| @@ -91,7 +91,7 @@ The second subexpression should match the bug reference (usually a number)." | |||
| 91 | (bug-reference-set-overlay-properties) | 91 | (bug-reference-set-overlay-properties) |
| 92 | 92 | ||
| 93 | (defun bug-reference-unfontify (start end) | 93 | (defun bug-reference-unfontify (start end) |
| 94 | "Remove bug reference overlays from region." | 94 | "Remove bug reference overlays from the region between START and END." |
| 95 | (dolist (o (overlays-in start end)) | 95 | (dolist (o (overlays-in start end)) |
| 96 | (when (eq (overlay-get o 'category) 'bug-reference) | 96 | (when (eq (overlay-get o 'category) 'bug-reference) |
| 97 | (delete-overlay o)))) | 97 | (delete-overlay o)))) |
| @@ -99,7 +99,7 @@ The second subexpression should match the bug reference (usually a number)." | |||
| 99 | (defvar bug-reference-prog-mode) | 99 | (defvar bug-reference-prog-mode) |
| 100 | 100 | ||
| 101 | (defun bug-reference-fontify (start end) | 101 | (defun bug-reference-fontify (start end) |
| 102 | "Apply bug reference overlays to region." | 102 | "Apply bug reference overlays to the region between START and END." |
| 103 | (save-excursion | 103 | (save-excursion |
| 104 | (let ((beg-line (progn (goto-char start) (line-beginning-position))) | 104 | (let ((beg-line (progn (goto-char start) (line-beginning-position))) |
| 105 | (end-line (progn (goto-char end) (line-end-position)))) | 105 | (end-line (progn (goto-char end) (line-end-position)))) |
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 5bfb0bf9018..1a0d9bdbb70 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el | |||
| @@ -562,7 +562,7 @@ LINE, END-LINE, COL, and END-COL can also be functions of no argument | |||
| 562 | that return the corresponding line or column number. They can assume REGEXP | 562 | that return the corresponding line or column number. They can assume REGEXP |
| 563 | has just been matched, and should correspondingly preserve this match data. | 563 | has just been matched, and should correspondingly preserve this match data. |
| 564 | 564 | ||
| 565 | f/usr/shaTYPE is 2 or nil for a real error or 1 for warning or 0 for info. | 565 | TYPE is 2 or nil for a real error or 1 for warning or 0 for info. |
| 566 | TYPE can also be of the form (WARNING . INFO). In that case this | 566 | TYPE can also be of the form (WARNING . INFO). In that case this |
| 567 | will be equivalent to 1 if the WARNING'th subexpression matched | 567 | will be equivalent to 1 if the WARNING'th subexpression matched |
| 568 | or else equivalent to 0 if the INFO'th subexpression matched. | 568 | or else equivalent to 0 if the INFO'th subexpression matched. |
| @@ -2056,8 +2056,7 @@ by replacing the first word, e.g., `compilation-scroll-output' from | |||
| 2056 | (if (boundp 'byte-compile-bound-variables) | 2056 | (if (boundp 'byte-compile-bound-variables) |
| 2057 | (memq (cdr v) byte-compile-bound-variables))) | 2057 | (memq (cdr v) byte-compile-bound-variables))) |
| 2058 | `(set (make-local-variable ',(car v)) ,(cdr v)))) | 2058 | `(set (make-local-variable ',(car v)) ,(cdr v)))) |
| 2059 | '(compilation-buffer-name-function | 2059 | '(compilation-directory-matcher |
| 2060 | compilation-directory-matcher | ||
| 2061 | compilation-error | 2060 | compilation-error |
| 2062 | compilation-error-regexp-alist | 2061 | compilation-error-regexp-alist |
| 2063 | compilation-error-regexp-alist-alist | 2062 | compilation-error-regexp-alist-alist |
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index c0f47159c95..8c7a58fd8bd 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el | |||
| @@ -959,8 +959,16 @@ substitution string. Note dynamic scoping of variables.") | |||
| 959 | The pattern can include shell wildcards. As whitespace triggers | 959 | The pattern can include shell wildcards. As whitespace triggers |
| 960 | completion when entering a pattern, including it requires | 960 | completion when entering a pattern, including it requires |
| 961 | quoting, e.g. `\\[quoted-insert]<space>'." | 961 | quoting, e.g. `\\[quoted-insert]<space>'." |
| 962 | (let* ((bn (or (buffer-file-name) | 962 | (let* ((grep-read-files-function (get major-mode 'grep-read-files)) |
| 963 | (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name)))) | 963 | (file-name-at-point |
| 964 | (run-hook-with-args-until-success 'file-name-at-point-functions)) | ||
| 965 | (bn (if grep-read-files-function | ||
| 966 | (funcall grep-read-files-function) | ||
| 967 | (or (if (and (stringp file-name-at-point) | ||
| 968 | (not (file-directory-p file-name-at-point))) | ||
| 969 | file-name-at-point) | ||
| 970 | (buffer-file-name) | ||
| 971 | (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name))))) | ||
| 964 | (fn (and bn | 972 | (fn (and bn |
| 965 | (stringp bn) | 973 | (stringp bn) |
| 966 | (file-name-nondirectory bn))) | 974 | (file-name-nondirectory bn))) |
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 4d91da73340..a0adaa84eeb 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el | |||
| @@ -50,7 +50,6 @@ | |||
| 50 | (require 'imenu) | 50 | (require 'imenu) |
| 51 | (require 'moz nil t) | 51 | (require 'moz nil t) |
| 52 | (require 'json) | 52 | (require 'json) |
| 53 | (require 'sgml-mode) | ||
| 54 | (require 'prog-mode) | 53 | (require 'prog-mode) |
| 55 | 54 | ||
| 56 | (eval-when-compile | 55 | (eval-when-compile |
| @@ -66,7 +65,7 @@ | |||
| 66 | 65 | ||
| 67 | ;;; Constants | 66 | ;;; Constants |
| 68 | 67 | ||
| 69 | (defconst js--name-start-re "[a-zA-Z_$]" | 68 | (defconst js--name-start-re (concat "[[:alpha:]_$]") |
| 70 | "Regexp matching the start of a JavaScript identifier, without grouping.") | 69 | "Regexp matching the start of a JavaScript identifier, without grouping.") |
| 71 | 70 | ||
| 72 | (defconst js--stmt-delim-chars "^;{}?:") | 71 | (defconst js--stmt-delim-chars "^;{}?:") |
| @@ -572,6 +571,119 @@ then the \".\"s will be lined up: | |||
| 572 | :safe 'booleanp | 571 | :safe 'booleanp |
| 573 | :group 'js) | 572 | :group 'js) |
| 574 | 573 | ||
| 574 | (defcustom js-jsx-detect-syntax t | ||
| 575 | "When non-nil, automatically detect whether JavaScript uses JSX. | ||
| 576 | `js-jsx-syntax' (which see) may be made buffer-local and set to | ||
| 577 | t. The detection strategy can be customized by adding elements | ||
| 578 | to `js-jsx-regexps', which see." | ||
| 579 | :version "27.1" | ||
| 580 | :type 'boolean | ||
| 581 | :safe 'booleanp | ||
| 582 | :group 'js) | ||
| 583 | |||
| 584 | (defcustom js-jsx-syntax nil | ||
| 585 | "When non-nil, parse JavaScript with consideration for JSX syntax. | ||
| 586 | |||
| 587 | This enables proper font-locking and indentation of code using | ||
| 588 | Facebook’s “JSX” syntax extension for JavaScript, for use with | ||
| 589 | Facebook’s “React” library. Font-locking is like sgml-mode. | ||
| 590 | Indentation is also like sgml-mode, although some indentation | ||
| 591 | behavior may differ slightly to align more closely with the | ||
| 592 | conventions of the React developer community. | ||
| 593 | |||
| 594 | When `js-mode' is already enabled, you should call | ||
| 595 | `js-jsx-enable' to set this variable. | ||
| 596 | |||
| 597 | It is set to be buffer-local (and t) when in `js-jsx-mode'." | ||
| 598 | :version "27.1" | ||
| 599 | :type 'boolean | ||
| 600 | :safe 'booleanp | ||
| 601 | :group 'js) | ||
| 602 | |||
| 603 | (defcustom js-jsx-align->-with-< t | ||
| 604 | "When non-nil, “>” will be indented to the opening “<” in JSX. | ||
| 605 | |||
| 606 | When this is enabled, JSX indentation looks like this: | ||
| 607 | |||
| 608 | <element | ||
| 609 | attr=\"\" | ||
| 610 | > | ||
| 611 | </element> | ||
| 612 | <input | ||
| 613 | /> | ||
| 614 | |||
| 615 | When this is disabled, JSX indentation looks like this: | ||
| 616 | |||
| 617 | <element | ||
| 618 | attr=\"\" | ||
| 619 | > | ||
| 620 | </element> | ||
| 621 | <input | ||
| 622 | />" | ||
| 623 | :version "27.1" | ||
| 624 | :type 'boolean | ||
| 625 | :safe 'booleanp | ||
| 626 | :group 'js) | ||
| 627 | |||
| 628 | (defcustom js-jsx-indent-level nil | ||
| 629 | "When non-nil, indent JSX by this value, instead of like JS. | ||
| 630 | |||
| 631 | Let `js-indent-level' be 4. When this variable is also set to | ||
| 632 | nil, JSX indentation looks like this (consistent): | ||
| 633 | |||
| 634 | return ( | ||
| 635 | <element> | ||
| 636 | <element> | ||
| 637 | Hello World! | ||
| 638 | </element> | ||
| 639 | </element> | ||
| 640 | ) | ||
| 641 | |||
| 642 | Alternatively, when this variable is also set to 2, JSX | ||
| 643 | indentation looks like this (different): | ||
| 644 | |||
| 645 | return ( | ||
| 646 | <element> | ||
| 647 | <element> | ||
| 648 | Hello World! | ||
| 649 | </element> | ||
| 650 | </element> | ||
| 651 | )" | ||
| 652 | :version "27.1" | ||
| 653 | :type 'integer | ||
| 654 | :safe (lambda (x) (or (null x) (integerp x))) | ||
| 655 | :group 'js) | ||
| 656 | ;; This is how indentation behaved out-of-the-box until Emacs 27. JSX | ||
| 657 | ;; indentation was controlled with `sgml-basic-offset', which defaults | ||
| 658 | ;; to 2, whereas `js-indent-level' defaults to 4. Users who had the | ||
| 659 | ;; same values configured for both their HTML and JS indentation would | ||
| 660 | ;; luckily get consistent JSX indentation; most others were probably | ||
| 661 | ;; unhappy. I’d be surprised if anyone actually wants different | ||
| 662 | ;; indentation levels, but just in case, here’s a way back to that. | ||
| 663 | |||
| 664 | (defcustom js-jsx-attribute-offset 0 | ||
| 665 | "Specifies a delta for JSXAttribute indentation. | ||
| 666 | |||
| 667 | Let `js-indent-level' be 2. When this variable is also set to 0, | ||
| 668 | JSXAttribute indentation looks like this: | ||
| 669 | |||
| 670 | <element | ||
| 671 | attribute=\"value\"> | ||
| 672 | </element> | ||
| 673 | |||
| 674 | Alternatively, when this variable is also set to 2, JSXAttribute | ||
| 675 | indentation looks like this: | ||
| 676 | |||
| 677 | <element | ||
| 678 | attribute=\"value\"> | ||
| 679 | </element> | ||
| 680 | |||
| 681 | This variable is like `sgml-attribute-offset'." | ||
| 682 | :version "27.1" | ||
| 683 | :type 'integer | ||
| 684 | :safe 'integerp | ||
| 685 | :group 'js) | ||
| 686 | |||
| 575 | ;;; KeyMap | 687 | ;;; KeyMap |
| 576 | 688 | ||
| 577 | (defvar js-mode-map | 689 | (defvar js-mode-map |
| @@ -1485,6 +1597,102 @@ point of view of font-lock. It applies highlighting directly with | |||
| 1485 | ;; Matcher always "fails" | 1597 | ;; Matcher always "fails" |
| 1486 | nil) | 1598 | nil) |
| 1487 | 1599 | ||
| 1600 | ;; It wouldn’t be sufficient to font-lock JSX with mere regexps, since | ||
| 1601 | ;; a JSXElement may be nested inside a JS expression within the | ||
| 1602 | ;; boundaries of a parent JSXOpeningElement, and such a hierarchy | ||
| 1603 | ;; ought to be fontified like JSX, JS, and JSX respectively: | ||
| 1604 | ;; | ||
| 1605 | ;; <div attr={void(<div></div>) && void(0)}></div> | ||
| 1606 | ;; | ||
| 1607 | ;; <div attr={ ← JSX | ||
| 1608 | ;; void( ← JS | ||
| 1609 | ;; <div></div> ← JSX | ||
| 1610 | ;; ) && void(0) ← JS | ||
| 1611 | ;; }></div> ← JSX | ||
| 1612 | ;; | ||
| 1613 | ;; `js-syntax-propertize' unambiguously identifies JSX syntax, | ||
| 1614 | ;; including when it’s nested. | ||
| 1615 | ;; | ||
| 1616 | ;; Using a matcher function for each relevant part, retrieve match | ||
| 1617 | ;; data recorded as syntax properties for fontification. | ||
| 1618 | |||
| 1619 | (defconst js-jsx--font-lock-keywords | ||
| 1620 | `((js-jsx--match-tag-name 0 font-lock-function-name-face t) | ||
| 1621 | (js-jsx--match-attribute-name 0 font-lock-variable-name-face t) | ||
| 1622 | (js-jsx--match-text 0 'default t) ; “Undo” keyword fontification. | ||
| 1623 | (js-jsx--match-tag-beg) | ||
| 1624 | (js-jsx--match-tag-end) | ||
| 1625 | (js-jsx--match-expr)) | ||
| 1626 | "JSX font lock faces and multiline text properties.") | ||
| 1627 | |||
| 1628 | (defun js-jsx--match-tag-name (limit) | ||
| 1629 | "Match JSXBoundaryElement names, until LIMIT." | ||
| 1630 | (when js-jsx-syntax | ||
| 1631 | (let ((pos (next-single-char-property-change (point) 'js-jsx-tag-name nil limit)) | ||
| 1632 | value) | ||
| 1633 | (when (and pos (> pos (point))) | ||
| 1634 | (goto-char pos) | ||
| 1635 | (or (and (setq value (get-text-property pos 'js-jsx-tag-name)) | ||
| 1636 | (progn (set-match-data value) t)) | ||
| 1637 | (js-jsx--match-tag-name limit)))))) | ||
| 1638 | |||
| 1639 | (defun js-jsx--match-attribute-name (limit) | ||
| 1640 | "Match JSXAttribute names, until LIMIT." | ||
| 1641 | (when js-jsx-syntax | ||
| 1642 | (let ((pos (next-single-char-property-change (point) 'js-jsx-attribute-name nil limit)) | ||
| 1643 | value) | ||
| 1644 | (when (and pos (> pos (point))) | ||
| 1645 | (goto-char pos) | ||
| 1646 | (or (and (setq value (get-text-property pos 'js-jsx-attribute-name)) | ||
| 1647 | (progn (set-match-data value) t)) | ||
| 1648 | (js-jsx--match-attribute-name limit)))))) | ||
| 1649 | |||
| 1650 | (defun js-jsx--match-text (limit) | ||
| 1651 | "Match JSXText, until LIMIT." | ||
| 1652 | (when js-jsx-syntax | ||
| 1653 | (let ((pos (next-single-char-property-change (point) 'js-jsx-text nil limit)) | ||
| 1654 | value) | ||
| 1655 | (when (and pos (> pos (point))) | ||
| 1656 | (goto-char pos) | ||
| 1657 | (or (and (setq value (get-text-property pos 'js-jsx-text)) | ||
| 1658 | (progn (set-match-data value) | ||
| 1659 | (put-text-property (car value) (cadr value) 'font-lock-multiline t) | ||
| 1660 | t)) | ||
| 1661 | (js-jsx--match-text limit)))))) | ||
| 1662 | |||
| 1663 | (defun js-jsx--match-tag-beg (limit) | ||
| 1664 | "Match JSXBoundaryElements from start, until LIMIT." | ||
| 1665 | (when js-jsx-syntax | ||
| 1666 | (let ((pos (next-single-char-property-change (point) 'js-jsx-tag-beg nil limit)) | ||
| 1667 | value) | ||
| 1668 | (when (and pos (> pos (point))) | ||
| 1669 | (goto-char pos) | ||
| 1670 | (or (and (setq value (get-text-property pos 'js-jsx-tag-beg)) | ||
| 1671 | (progn (put-text-property pos (cdr value) 'font-lock-multiline t) t)) | ||
| 1672 | (js-jsx--match-tag-beg limit)))))) | ||
| 1673 | |||
| 1674 | (defun js-jsx--match-tag-end (limit) | ||
| 1675 | "Match JSXBoundaryElements from end, until LIMIT." | ||
| 1676 | (when js-jsx-syntax | ||
| 1677 | (let ((pos (next-single-char-property-change (point) 'js-jsx-tag-end nil limit)) | ||
| 1678 | value) | ||
| 1679 | (when (and pos (> pos (point))) | ||
| 1680 | (goto-char pos) | ||
| 1681 | (or (and (setq value (get-text-property pos 'js-jsx-tag-end)) | ||
| 1682 | (progn (put-text-property value pos 'font-lock-multiline t) t)) | ||
| 1683 | (js-jsx--match-tag-end limit)))))) | ||
| 1684 | |||
| 1685 | (defun js-jsx--match-expr (limit) | ||
| 1686 | "Match JSXExpressionContainers, until LIMIT." | ||
| 1687 | (when js-jsx-syntax | ||
| 1688 | (let ((pos (next-single-char-property-change (point) 'js-jsx-expr nil limit)) | ||
| 1689 | value) | ||
| 1690 | (when (and pos (> pos (point))) | ||
| 1691 | (goto-char pos) | ||
| 1692 | (or (and (setq value (get-text-property pos 'js-jsx-expr)) | ||
| 1693 | (progn (put-text-property pos value 'font-lock-multiline t) t)) | ||
| 1694 | (js-jsx--match-expr limit)))))) | ||
| 1695 | |||
| 1488 | (defconst js--font-lock-keywords-3 | 1696 | (defconst js--font-lock-keywords-3 |
| 1489 | `( | 1697 | `( |
| 1490 | ;; This goes before keywords-2 so it gets used preferentially | 1698 | ;; This goes before keywords-2 so it gets used preferentially |
| @@ -1596,7 +1804,10 @@ point of view of font-lock. It applies highlighting directly with | |||
| 1596 | (forward-symbol -1) | 1804 | (forward-symbol -1) |
| 1597 | (end-of-line)) | 1805 | (end-of-line)) |
| 1598 | '(end-of-line) | 1806 | '(end-of-line) |
| 1599 | '(0 font-lock-variable-name-face)))) | 1807 | '(0 font-lock-variable-name-face))) |
| 1808 | |||
| 1809 | ;; jsx (when enabled) | ||
| 1810 | ,@js-jsx--font-lock-keywords) | ||
| 1600 | "Level three font lock for `js-mode'.") | 1811 | "Level three font lock for `js-mode'.") |
| 1601 | 1812 | ||
| 1602 | (defun js--inside-pitem-p (pitem) | 1813 | (defun js--inside-pitem-p (pitem) |
| @@ -1722,9 +1933,414 @@ This performs fontification according to `js--class-styles'." | |||
| 1722 | 'syntax-table (string-to-syntax "\"/")) | 1933 | 'syntax-table (string-to-syntax "\"/")) |
| 1723 | (goto-char end))))) | 1934 | (goto-char end))))) |
| 1724 | 1935 | ||
| 1936 | (defconst js--unary-keyword-re | ||
| 1937 | (js--regexp-opt-symbol '("await" "delete" "typeof" "void" "yield")) | ||
| 1938 | "Regexp matching unary operator keywords.") | ||
| 1939 | |||
| 1940 | (defun js--unary-keyword-p (string) | ||
| 1941 | "Check if STRING is a unary operator keyword in JavaScript." | ||
| 1942 | (string-match-p js--unary-keyword-re string)) | ||
| 1943 | |||
| 1944 | ;; Adding `syntax-multiline' text properties to JSX isn’t sufficient | ||
| 1945 | ;; to identify multiline JSX when first typing it. For instance, if | ||
| 1946 | ;; the user is typing a JSXOpeningElement for the first time… | ||
| 1947 | ;; | ||
| 1948 | ;; <div | ||
| 1949 | ;; ^ (point) | ||
| 1950 | ;; | ||
| 1951 | ;; …and the user inserts a line break after the tag name (before the | ||
| 1952 | ;; JSXOpeningElement starting on that line has been unambiguously | ||
| 1953 | ;; identified as such), then the `syntax-propertize' region won’t be | ||
| 1954 | ;; extended backwards to the start of the JSXOpeningElement: | ||
| 1955 | ;; | ||
| 1956 | ;; <div ← This line wasn’t JSX when last edited. | ||
| 1957 | ;; attr=""> ← Despite completing the JSX, the next | ||
| 1958 | ;; ^ `syntax-propertize' region wouldn’t magically | ||
| 1959 | ;; extend back a few lines. | ||
| 1960 | ;; | ||
| 1961 | ;; Therefore, to try and recover from this scenario, parse backward | ||
| 1962 | ;; from “>” to try and find the start of JSXBoundaryElements, and | ||
| 1963 | ;; extend the `syntax-propertize' region there. | ||
| 1964 | |||
| 1965 | (defun js--syntax-propertize-extend-region (start end) | ||
| 1966 | "Extend the START-END region for propertization, if necessary. | ||
| 1967 | For use by `syntax-propertize-extend-region-functions'." | ||
| 1968 | (if js-jsx-syntax (js-jsx--syntax-propertize-extend-region start end))) | ||
| 1969 | |||
| 1970 | (defun js-jsx--syntax-propertize-extend-region (start end) | ||
| 1971 | "Extend the START-END region for propertization, if necessary. | ||
| 1972 | If any “>” in the region appears to be the end of a tag starting | ||
| 1973 | before the start of the region, extend region backwards to the | ||
| 1974 | start of that tag so parsing may proceed from that point. | ||
| 1975 | For use by `syntax-propertize-extend-region-functions'." | ||
| 1976 | (let (new-start | ||
| 1977 | forward-sexp-function ; Use the Lisp version. | ||
| 1978 | parse-sexp-lookup-properties) ; Fix backward-sexp error here. | ||
| 1979 | (catch 'stop | ||
| 1980 | (goto-char start) | ||
| 1981 | (while (re-search-forward ">" end t) | ||
| 1982 | (catch 'continue | ||
| 1983 | ;; Check if this is really a right shift bitwise operator | ||
| 1984 | ;; (“>>” or “>>>”). | ||
| 1985 | (unless (or (eq (char-before (1- (point))) ?>) | ||
| 1986 | (eq (char-after) ?>)) | ||
| 1987 | (save-excursion | ||
| 1988 | (backward-char) | ||
| 1989 | (while (progn (if (= (point) (point-min)) (throw 'continue nil)) | ||
| 1990 | (/= (char-before) ?<)) | ||
| 1991 | (skip-chars-backward " \t\n") | ||
| 1992 | (if (= (point) (point-min)) (throw 'continue nil)) | ||
| 1993 | (cond | ||
| 1994 | ((memq (char-before) '(?\" ?\' ?\` ?\})) | ||
| 1995 | (condition-case nil | ||
| 1996 | (backward-sexp) | ||
| 1997 | (scan-error (throw 'continue nil)))) | ||
| 1998 | ((memq (char-before) '(?\/ ?\=)) (backward-char)) | ||
| 1999 | ((looking-back js--dotted-name-re (line-beginning-position) t) | ||
| 2000 | (goto-char (match-beginning 0))) | ||
| 2001 | (t (throw 'continue nil)))) | ||
| 2002 | (when (< (point) start) | ||
| 2003 | (setq new-start (1- (point))) | ||
| 2004 | (throw 'stop nil))))))) | ||
| 2005 | (if new-start (cons new-start end)))) | ||
| 2006 | |||
| 2007 | ;; When applying syntax properties, since `js-syntax-propertize' uses | ||
| 2008 | ;; `syntax-propertize-rules' to parse JSXBoundaryElements iteratively | ||
| 2009 | ;; and statelessly, whenever we exit such an element, we need to | ||
| 2010 | ;; determine the JSX depth. If >0, then we know we to apply syntax | ||
| 2011 | ;; properties to JSXText up until the next JSXBoundaryElement occurs. | ||
| 2012 | ;; But if the JSX depth is 0, then—importantly—we know to NOT parse | ||
| 2013 | ;; the following code as JSXText, rather propertize it as regular JS | ||
| 2014 | ;; as long as warranted. | ||
| 2015 | ;; | ||
| 2016 | ;; Also, when indenting code, we need to know if the code we’re trying | ||
| 2017 | ;; to indent is on the 2nd or later line of multiline JSX, in which | ||
| 2018 | ;; case the code is indented according to XML-like JSX conventions. | ||
| 2019 | ;; | ||
| 2020 | ;; For the aforementioned reasons, we find ourselves needing to | ||
| 2021 | ;; determine whether point is enclosed in JSX or not; and, if so, | ||
| 2022 | ;; where the JSX is. The following functions provide that knowledge. | ||
| 2023 | |||
| 2024 | (defconst js-jsx--tag-start-re | ||
| 2025 | (concat "\\(" js--dotted-name-re "\\)\\(?:" | ||
| 2026 | ;; Whitespace is only necessary if an attribute implies JSX. | ||
| 2027 | "\\(?:\\s-\\|\n\\)*[{/>]" | ||
| 2028 | "\\|" | ||
| 2029 | "\\(?:\\s-\\|\n\\)+" js--name-start-re | ||
| 2030 | "\\)") | ||
| 2031 | "Regexp unambiguously matching a JSXOpeningElement.") | ||
| 2032 | |||
| 2033 | (defun js-jsx--matched-tag-type () | ||
| 2034 | "Determine if the last “<” was a JSXBoundaryElement and its type. | ||
| 2035 | Return `close' for a JSXClosingElement/JSXClosingFragment match, | ||
| 2036 | return `self-closing' for some self-closing JSXOpeningElements, | ||
| 2037 | else return `other'." | ||
| 2038 | (cond | ||
| 2039 | ((= (char-after) ?/) (forward-char) 'close) ; JSXClosingElement/JSXClosingFragment | ||
| 2040 | ((= (char-after) ?>) (forward-char) 'other) ; JSXOpeningFragment | ||
| 2041 | ((and (looking-at js-jsx--tag-start-re) ; JSXOpeningElement | ||
| 2042 | (not (js--unary-keyword-p (match-string 1)))) | ||
| 2043 | (goto-char (match-end 0)) | ||
| 2044 | (if (= (char-before) ?/) 'self-closing 'other)))) | ||
| 2045 | |||
| 2046 | (defconst js-jsx--self-closing-re "/\\s-*>" | ||
| 2047 | "Regexp matching the end of a self-closing JSXOpeningElement.") | ||
| 2048 | |||
| 2049 | (defun js-jsx--matching-close-tag-pos () | ||
| 2050 | "Return position of the closer of the opener before point. | ||
| 2051 | Assuming a JSXOpeningElement or a JSXOpeningFragment is | ||
| 2052 | immediately before point, find a matching JSXClosingElement or | ||
| 2053 | JSXClosingFragment, skipping over any nested JSXElements to find | ||
| 2054 | the match. Return nil if a match can’t be found." | ||
| 2055 | (let ((tag-stack 1) tag-pos type last-pos pos) | ||
| 2056 | (catch 'stop | ||
| 2057 | (while (and (re-search-forward "<\\s-*" nil t) (not (eobp))) | ||
| 2058 | (when (setq tag-pos (match-beginning 0) | ||
| 2059 | type (js-jsx--matched-tag-type)) | ||
| 2060 | (when last-pos | ||
| 2061 | (setq pos (point)) | ||
| 2062 | (goto-char last-pos) | ||
| 2063 | (while (re-search-forward js-jsx--self-closing-re pos 'move) | ||
| 2064 | (setq tag-stack (1- tag-stack)))) | ||
| 2065 | (if (eq type 'close) | ||
| 2066 | (progn | ||
| 2067 | (setq tag-stack (1- tag-stack)) | ||
| 2068 | (when (= tag-stack 0) | ||
| 2069 | (throw 'stop tag-pos))) | ||
| 2070 | ;; JSXOpeningElements that we know are self-closing aren’t | ||
| 2071 | ;; added to the stack at all (because point is already | ||
| 2072 | ;; past that syntax). | ||
| 2073 | (unless (eq type 'self-closing) | ||
| 2074 | (setq tag-stack (1+ tag-stack)))) | ||
| 2075 | (setq last-pos (point))))))) | ||
| 2076 | |||
| 2077 | (defun js-jsx--enclosing-tag-pos () | ||
| 2078 | "Return beginning and end of a JSXElement about point. | ||
| 2079 | Look backward for a JSXElement that both starts before point and | ||
| 2080 | also ends at/after point. That may be either a self-closing | ||
| 2081 | JSXElement or a JSXOpeningElement/JSXClosingElement pair." | ||
| 2082 | (let ((start (point)) tag-beg tag-beg-pos tag-end-pos close-tag-pos) | ||
| 2083 | (while | ||
| 2084 | (and | ||
| 2085 | (setq tag-beg (js--backward-text-property 'js-jsx-tag-beg)) | ||
| 2086 | (progn | ||
| 2087 | (setq tag-beg-pos (point) | ||
| 2088 | tag-end-pos (cdr tag-beg)) | ||
| 2089 | (not | ||
| 2090 | (or | ||
| 2091 | (and (eq (car tag-beg) 'self-closing) | ||
| 2092 | (< start tag-end-pos)) | ||
| 2093 | (and (eq (car tag-beg) 'open) | ||
| 2094 | (or (< start tag-end-pos) | ||
| 2095 | (progn | ||
| 2096 | (unless | ||
| 2097 | ;; Try to read a cached close position, | ||
| 2098 | ;; but it might not be available yet. | ||
| 2099 | (setq close-tag-pos | ||
| 2100 | (get-text-property (point) 'js-jsx-close-tag-pos)) | ||
| 2101 | (save-excursion | ||
| 2102 | (goto-char tag-end-pos) | ||
| 2103 | (setq close-tag-pos (js-jsx--matching-close-tag-pos))) | ||
| 2104 | (when close-tag-pos | ||
| 2105 | ;; Cache the close position to make future | ||
| 2106 | ;; searches faster. | ||
| 2107 | (put-text-property | ||
| 2108 | (point) (1+ (point)) | ||
| 2109 | 'js-jsx-close-tag-pos close-tag-pos))) | ||
| 2110 | ;; The JSXOpeningElement may be unclosed, else | ||
| 2111 | ;; the closure must occur at/after the start | ||
| 2112 | ;; point (otherwise, a miscellaneous previous | ||
| 2113 | ;; JSXOpeningElement has been found, so keep | ||
| 2114 | ;; looking backwards for an enclosing one). | ||
| 2115 | (or (not close-tag-pos) (<= start close-tag-pos))))))))) | ||
| 2116 | ;; Don’t return the last tag pos, as it wasn’t enclosing. | ||
| 2117 | (setq tag-beg nil close-tag-pos nil)) | ||
| 2118 | (and tag-beg (list tag-beg-pos tag-end-pos close-tag-pos)))) | ||
| 2119 | |||
| 2120 | (defun js-jsx--at-enclosing-tag-child-p () | ||
| 2121 | "Return t if point is at an enclosing tag’s child." | ||
| 2122 | (let ((pos (save-excursion (js-jsx--enclosing-tag-pos)))) | ||
| 2123 | (and pos (>= (point) (nth 1 pos))))) | ||
| 2124 | |||
| 2125 | ;; We implement `syntax-propertize-function' logic fully parsing JSX | ||
| 2126 | ;; in order to provide very accurate JSX indentation, even in the most | ||
| 2127 | ;; complex cases (e.g. to indent JSX within a JS expression within a | ||
| 2128 | ;; JSXAttribute…), as over the years users have requested this. Since | ||
| 2129 | ;; we find so much information during this parse, we later use some of | ||
| 2130 | ;; the useful bits for font-locking, too. | ||
| 2131 | ;; | ||
| 2132 | ;; Some extra effort is devoted to ensuring that no code which could | ||
| 2133 | ;; possibly be valid JS is ever misinterpreted as partial JSX, since | ||
| 2134 | ;; that would be regressive. | ||
| 2135 | ;; | ||
| 2136 | ;; We first parse trying to find the minimum number of components | ||
| 2137 | ;; necessary to unambiguously identify a JSXBoundaryElement, even if | ||
| 2138 | ;; it is a partial one. If a complete one is parsed, we move on to | ||
| 2139 | ;; parse any JSXText. When that’s terminated, we unwind back to the | ||
| 2140 | ;; `syntax-propertize-rules' loop so the next JSXBoundaryElement can | ||
| 2141 | ;; be parsed, if any, be it an opening or closing one. | ||
| 2142 | |||
| 2143 | (defun js-jsx--put-syntax-table (start end value) | ||
| 2144 | "Set syntax-table text property from START to END as VALUE. | ||
| 2145 | Redundantly set the value to two properties, syntax-table and | ||
| 2146 | js-jsx-syntax-table. Derivative modes that remove syntax-table | ||
| 2147 | text properties may recover the value from the second property." ; i.e. js2-mode | ||
| 2148 | (add-text-properties start end (list 'syntax-table value | ||
| 2149 | 'js-jsx-syntax-table value))) | ||
| 2150 | |||
| 2151 | (defun js-jsx--text-range (beg end) | ||
| 2152 | "Identify JSXText within a “>/{/}/<” pair." | ||
| 2153 | (when (> (- end beg) 0) | ||
| 2154 | (save-excursion | ||
| 2155 | (goto-char beg) | ||
| 2156 | (while (and (skip-chars-forward " \t\n" end) (< (point) end)) | ||
| 2157 | ;; Comments and string quotes don’t serve their usual | ||
| 2158 | ;; syntactic roles in JSXText; make them plain punctuation to | ||
| 2159 | ;; negate those roles. | ||
| 2160 | (when (or (= (char-after) ?/) ; comment | ||
| 2161 | (= (syntax-class (syntax-after (point))) 7)) ; string quote | ||
| 2162 | (js-jsx--put-syntax-table (point) (1+ (point)) '(1))) | ||
| 2163 | (forward-char))) | ||
| 2164 | ;; Mark JSXText so it can be font-locked as non-keywords. | ||
| 2165 | (put-text-property beg (1+ beg) 'js-jsx-text (list beg end (current-buffer))) | ||
| 2166 | ;; Ensure future propertization beginning from within the | ||
| 2167 | ;; JSXText determines JSXText context from earlier lines. | ||
| 2168 | (put-text-property beg end 'syntax-multiline t))) | ||
| 2169 | |||
| 2170 | ;; In order to respect the end boundary `syntax-propertize-function' | ||
| 2171 | ;; sets, care is taken in the following functions to abort parsing | ||
| 2172 | ;; whenever that boundary is reached. | ||
| 2173 | |||
| 2174 | (defun js-jsx--syntax-propertize-tag-text (end) | ||
| 2175 | "Determine if JSXText is before END and propertize it. | ||
| 2176 | Text within an open/close tag pair may be JSXText. Temporarily | ||
| 2177 | interrupt JSXText by JSXExpressionContainers, and terminate | ||
| 2178 | JSXText when another JSXBoundaryElement is encountered. Despite | ||
| 2179 | terminations, all JSXText will be identified once all the | ||
| 2180 | JSXBoundaryElements within an outermost JSXElement’s tree have | ||
| 2181 | been propertized." | ||
| 2182 | (let ((text-beg (point)) | ||
| 2183 | forward-sexp-function) ; Use Lisp version. | ||
| 2184 | (catch 'stop | ||
| 2185 | (while (re-search-forward "[{<]" end t) | ||
| 2186 | (js-jsx--text-range text-beg (1- (point))) | ||
| 2187 | (cond | ||
| 2188 | ((= (char-before) ?{) | ||
| 2189 | (let (expr-beg expr-end) | ||
| 2190 | (condition-case nil | ||
| 2191 | (save-excursion | ||
| 2192 | (backward-char) | ||
| 2193 | (setq expr-beg (point)) | ||
| 2194 | (forward-sexp) | ||
| 2195 | (setq expr-end (point))) | ||
| 2196 | (scan-error nil)) | ||
| 2197 | ;; Recursively propertize the JSXExpressionContainer’s | ||
| 2198 | ;; (possibly-incomplete) expression. | ||
| 2199 | (js-syntax-propertize (1+ expr-beg) (if expr-end (min (1- expr-end) end) end)) | ||
| 2200 | ;; Ensure future propertization beginning from within the | ||
| 2201 | ;; (possibly-incomplete) expression can determine JSXText | ||
| 2202 | ;; context from earlier lines. | ||
| 2203 | (put-text-property expr-beg (1+ expr-beg) 'js-jsx-expr (or expr-end end)) ; font-lock | ||
| 2204 | (put-text-property expr-beg (if expr-end (min expr-end end) end) 'syntax-multiline t) ; syntax-propertize | ||
| 2205 | ;; Exit the JSXExpressionContainer if that’s possible, | ||
| 2206 | ;; else move to the end of the propertized area. | ||
| 2207 | (goto-char (if expr-end (min expr-end end) end)))) | ||
| 2208 | ((= (char-before) ?<) | ||
| 2209 | (backward-char) ; Ensure the next tag can be propertized. | ||
| 2210 | (throw 'stop nil))) | ||
| 2211 | (setq text-beg (point)))))) | ||
| 2212 | |||
| 2213 | (defconst js-jsx--attribute-name-re (concat js--name-start-re | ||
| 2214 | "\\(?:\\s_\\|\\sw\\|-\\)*") | ||
| 2215 | "Like `js--name-re', but matches “-” as well.") | ||
| 2216 | |||
| 2217 | (defun js-jsx--syntax-propertize-tag (end) | ||
| 2218 | "Determine if a JSXBoundaryElement is before END and propertize it. | ||
| 2219 | Disambiguate JSX from inequality operators and arrow functions by | ||
| 2220 | testing for syntax only valid as JSX." | ||
| 2221 | (let ((tag-beg (1- (point))) tag-end (type 'open) | ||
| 2222 | name-beg name-match-data expr-attribute-beg unambiguous | ||
| 2223 | forward-sexp-function) ; Use Lisp version. | ||
| 2224 | (catch 'stop | ||
| 2225 | (while (and (< (point) end) | ||
| 2226 | (progn (skip-chars-forward " \t\n" end) | ||
| 2227 | (< (point) end))) | ||
| 2228 | (cond | ||
| 2229 | ((= (char-after) ?>) | ||
| 2230 | ;; Make the closing “>” a close parenthesis. | ||
| 2231 | (js-jsx--put-syntax-table (point) (1+ (point)) '(5)) | ||
| 2232 | (forward-char) | ||
| 2233 | (setq unambiguous t) | ||
| 2234 | (throw 'stop nil)) | ||
| 2235 | ;; Handle a JSXSpreadChild (“<Foo {...bar}”) or a | ||
| 2236 | ;; JSXExpressionContainer as a JSXAttribute value | ||
| 2237 | ;; (“<Foo bar={…}”). Check this early in case continuing a | ||
| 2238 | ;; JSXAttribute parse. | ||
| 2239 | ((or (and name-beg (= (char-after) ?{)) | ||
| 2240 | (setq expr-attribute-beg nil)) | ||
| 2241 | (setq unambiguous t) ; JSXExpressionContainer post tag name ⇒ JSX | ||
| 2242 | (when expr-attribute-beg | ||
| 2243 | ;; Remember that this JSXExpressionContainer is part of a | ||
| 2244 | ;; JSXAttribute, as that can affect its expression’s | ||
| 2245 | ;; indentation. | ||
| 2246 | (put-text-property | ||
| 2247 | (point) (1+ (point)) 'js-jsx-expr-attribute expr-attribute-beg) | ||
| 2248 | (setq expr-attribute-beg nil)) | ||
| 2249 | (let (expr-end) | ||
| 2250 | (condition-case nil | ||
| 2251 | (save-excursion | ||
| 2252 | (forward-sexp) | ||
| 2253 | (setq expr-end (point))) | ||
| 2254 | (scan-error nil)) | ||
| 2255 | (forward-char) | ||
| 2256 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2257 | (skip-chars-forward " \t\n" end) | ||
| 2258 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2259 | (if (= (char-after) ?}) (forward-char) ; Shortcut to bail. | ||
| 2260 | ;; Recursively propertize the JSXExpressionContainer’s | ||
| 2261 | ;; expression. | ||
| 2262 | (js-syntax-propertize (point) (if expr-end (min (1- expr-end) end) end)) | ||
| 2263 | ;; Exit the JSXExpressionContainer if that’s possible, | ||
| 2264 | ;; else move to the end of the propertized area. | ||
| 2265 | (goto-char (if expr-end (min expr-end end) end))))) | ||
| 2266 | ((= (char-after) ?/) | ||
| 2267 | ;; Assume a tag is an open tag until a slash is found, then | ||
| 2268 | ;; figure out what type it actually is. | ||
| 2269 | (if (eq type 'open) (setq type (if name-beg 'self-closing 'close))) | ||
| 2270 | (forward-char)) | ||
| 2271 | ((and (not name-beg) (looking-at js--dotted-name-re)) | ||
| 2272 | ;; Don’t match code like “if (i < await foo)” | ||
| 2273 | (if (js--unary-keyword-p (match-string 0)) (throw 'stop nil)) | ||
| 2274 | ;; Save boundaries for later fontification after | ||
| 2275 | ;; unambiguously determining the code is JSX. | ||
| 2276 | (setq name-beg (match-beginning 0) | ||
| 2277 | name-match-data (match-data)) | ||
| 2278 | (goto-char (match-end 0))) | ||
| 2279 | ((and name-beg (looking-at js-jsx--attribute-name-re)) | ||
| 2280 | (setq unambiguous t) ; Non-unary name followed by 2nd name ⇒ JSX | ||
| 2281 | ;; Save JSXAttribute’s name’s match data for font-locking later. | ||
| 2282 | (put-text-property (match-beginning 0) (1+ (match-beginning 0)) | ||
| 2283 | 'js-jsx-attribute-name (match-data)) | ||
| 2284 | (goto-char (match-end 0)) | ||
| 2285 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2286 | (skip-chars-forward " \t\n" end) | ||
| 2287 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2288 | ;; “=” is optional for null-valued JSXAttributes. | ||
| 2289 | (when (= (char-after) ?=) | ||
| 2290 | (forward-char) | ||
| 2291 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2292 | (skip-chars-forward " \t\n" end) | ||
| 2293 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2294 | ;; Skip over strings (if possible). Any | ||
| 2295 | ;; JSXExpressionContainer here will be parsed in the | ||
| 2296 | ;; next iteration of the loop. | ||
| 2297 | (if (memq (char-after) '(?\" ?\' ?\`)) | ||
| 2298 | (progn | ||
| 2299 | ;; Record the string’s position so derived modes | ||
| 2300 | ;; applying syntactic fontification atypically | ||
| 2301 | ;; (e.g. js2-mode) can recognize it as part of JSX. | ||
| 2302 | (put-text-property (point) (1+ (point)) 'js-jsx-string t) | ||
| 2303 | (condition-case nil | ||
| 2304 | (forward-sexp) | ||
| 2305 | (scan-error (throw 'stop nil)))) | ||
| 2306 | ;; Save JSXAttribute’s beginning in case we find a | ||
| 2307 | ;; JSXExpressionContainer as the JSXAttribute’s value which | ||
| 2308 | ;; we should associate with the JSXAttribute. | ||
| 2309 | (setq expr-attribute-beg (match-beginning 0))))) | ||
| 2310 | ;; There is nothing more to check; this either isn’t JSX, or | ||
| 2311 | ;; the tag is incomplete. | ||
| 2312 | (t (throw 'stop nil))))) | ||
| 2313 | (when unambiguous | ||
| 2314 | ;; Save JSXBoundaryElement’s name’s match data for font-locking. | ||
| 2315 | (if name-beg (put-text-property name-beg (1+ name-beg) 'js-jsx-tag-name name-match-data)) | ||
| 2316 | ;; Make the opening “<” an open parenthesis. | ||
| 2317 | (js-jsx--put-syntax-table tag-beg (1+ tag-beg) '(4)) | ||
| 2318 | ;; Prevent “out of range” errors when typing at the end of a buffer. | ||
| 2319 | (setq tag-end (if (eobp) (1- (point)) (point))) | ||
| 2320 | ;; Mark beginning and end of tag for font-locking. | ||
| 2321 | (put-text-property tag-beg (1+ tag-beg) 'js-jsx-tag-beg (cons type tag-end)) | ||
| 2322 | (put-text-property tag-end (1+ tag-end) 'js-jsx-tag-end tag-beg) | ||
| 2323 | ;; Use text properties to extend the syntax-propertize region | ||
| 2324 | ;; backward to the beginning of the JSXBoundaryElement in the | ||
| 2325 | ;; future. Typically the closing angle bracket could suggest | ||
| 2326 | ;; extending backward, but that would also involve more rigorous | ||
| 2327 | ;; parsing, and the closing angle bracket may not even exist yet | ||
| 2328 | ;; if the JSXBoundaryElement is still being typed. | ||
| 2329 | (put-text-property tag-beg (1+ tag-end) 'syntax-multiline t)) | ||
| 2330 | (if (js-jsx--at-enclosing-tag-child-p) (js-jsx--syntax-propertize-tag-text end)))) | ||
| 2331 | |||
| 2332 | (defconst js-jsx--text-properties | ||
| 2333 | (list | ||
| 2334 | 'js-jsx-tag-beg nil 'js-jsx-tag-end nil 'js-jsx-close-tag-pos nil | ||
| 2335 | 'js-jsx-tag-name nil 'js-jsx-attribute-name nil 'js-jsx-string nil | ||
| 2336 | 'js-jsx-text nil 'js-jsx-expr nil 'js-jsx-expr-attribute nil | ||
| 2337 | 'js-jsx-syntax-table nil) | ||
| 2338 | "Plist of text properties added by `js-syntax-propertize'.") | ||
| 2339 | |||
| 1725 | (defun js-syntax-propertize (start end) | 2340 | (defun js-syntax-propertize (start end) |
| 1726 | ;; JavaScript allows immediate regular expression objects, written /.../. | 2341 | ;; JavaScript allows immediate regular expression objects, written /.../. |
| 1727 | (goto-char start) | 2342 | (goto-char start) |
| 2343 | (if js-jsx-syntax (remove-text-properties start end js-jsx--text-properties)) | ||
| 1728 | (js-syntax-propertize-regexp end) | 2344 | (js-syntax-propertize-regexp end) |
| 1729 | (funcall | 2345 | (funcall |
| 1730 | (syntax-propertize-rules | 2346 | (syntax-propertize-rules |
| @@ -1748,7 +2364,8 @@ This performs fontification according to `js--class-styles'." | |||
| 1748 | (put-text-property (match-beginning 1) (match-end 1) | 2364 | (put-text-property (match-beginning 1) (match-end 1) |
| 1749 | 'syntax-table (string-to-syntax "\"/")) | 2365 | 'syntax-table (string-to-syntax "\"/")) |
| 1750 | (js-syntax-propertize-regexp end))))) | 2366 | (js-syntax-propertize-regexp end))))) |
| 1751 | ("\\`\\(#\\)!" (1 "< b"))) | 2367 | ("\\`\\(#\\)!" (1 "< b")) |
| 2368 | ("<" (0 (ignore (if js-jsx-syntax (js-jsx--syntax-propertize-tag end)))))) | ||
| 1752 | (point) end)) | 2369 | (point) end)) |
| 1753 | 2370 | ||
| 1754 | (defconst js--prettify-symbols-alist | 2371 | (defconst js--prettify-symbols-alist |
| @@ -1774,6 +2391,11 @@ This performs fontification according to `js--class-styles'." | |||
| 1774 | (js--regexp-opt-symbol '("in" "instanceof"))) | 2391 | (js--regexp-opt-symbol '("in" "instanceof"))) |
| 1775 | "Regexp matching operators that affect indentation of continued expressions.") | 2392 | "Regexp matching operators that affect indentation of continued expressions.") |
| 1776 | 2393 | ||
| 2394 | (defun js-jsx--looking-at-start-tag-p () | ||
| 2395 | "Non-nil if a JSXOpeningElement immediately follows point." | ||
| 2396 | (let ((tag-beg (get-text-property (point) 'js-jsx-tag-beg))) | ||
| 2397 | (and tag-beg (memq (car tag-beg) '(open self-closing))))) | ||
| 2398 | |||
| 1777 | (defun js--looking-at-operator-p () | 2399 | (defun js--looking-at-operator-p () |
| 1778 | "Return non-nil if point is on a JavaScript operator, other than a comma." | 2400 | "Return non-nil if point is on a JavaScript operator, other than a comma." |
| 1779 | (save-match-data | 2401 | (save-match-data |
| @@ -1796,7 +2418,9 @@ This performs fontification according to `js--class-styles'." | |||
| 1796 | (js--backward-syntactic-ws) | 2418 | (js--backward-syntactic-ws) |
| 1797 | ;; We might misindent some expressions that would | 2419 | ;; We might misindent some expressions that would |
| 1798 | ;; return NaN anyway. Shouldn't be a problem. | 2420 | ;; return NaN anyway. Shouldn't be a problem. |
| 1799 | (memq (char-before) '(?, ?} ?{)))))))) | 2421 | (memq (char-before) '(?, ?} ?{))))) |
| 2422 | ;; “<” isn’t necessarily an operator in JSX. | ||
| 2423 | (not (and js-jsx-syntax (js-jsx--looking-at-start-tag-p)))))) | ||
| 1800 | 2424 | ||
| 1801 | (defun js--find-newline-backward () | 2425 | (defun js--find-newline-backward () |
| 1802 | "Move backward to the nearest newline that is not in a block comment." | 2426 | "Move backward to the nearest newline that is not in a block comment." |
| @@ -1816,6 +2440,10 @@ This performs fontification according to `js--class-styles'." | |||
| 1816 | (setq result nil))) | 2440 | (setq result nil))) |
| 1817 | result)) | 2441 | result)) |
| 1818 | 2442 | ||
| 2443 | (defun js-jsx--looking-back-at-end-tag-p () | ||
| 2444 | "Non-nil if a JSXClosingElement immediately precedes point." | ||
| 2445 | (get-text-property (point) 'js-jsx-tag-end)) | ||
| 2446 | |||
| 1819 | (defun js--continued-expression-p () | 2447 | (defun js--continued-expression-p () |
| 1820 | "Return non-nil if the current line continues an expression." | 2448 | "Return non-nil if the current line continues an expression." |
| 1821 | (save-excursion | 2449 | (save-excursion |
| @@ -1833,12 +2461,19 @@ This performs fontification according to `js--class-styles'." | |||
| 1833 | (and (js--find-newline-backward) | 2461 | (and (js--find-newline-backward) |
| 1834 | (progn | 2462 | (progn |
| 1835 | (skip-chars-backward " \t") | 2463 | (skip-chars-backward " \t") |
| 1836 | (or (bobp) (backward-char)) | 2464 | (and |
| 1837 | (and (> (point) (point-min)) | 2465 | ;; The “>” at the end of any JSXBoundaryElement isn’t |
| 1838 | (save-excursion (backward-char) (not (looking-at "[/*]/\\|=>"))) | 2466 | ;; part of a continued expression. |
| 1839 | (js--looking-at-operator-p) | 2467 | (not (and js-jsx-syntax (js-jsx--looking-back-at-end-tag-p))) |
| 1840 | (and (progn (backward-char) | 2468 | (progn |
| 1841 | (not (looking-at "\\+\\+\\|--\\|/[/*]")))))))))) | 2469 | (or (bobp) (backward-char)) |
| 2470 | (and (> (point) (point-min)) | ||
| 2471 | (save-excursion | ||
| 2472 | (backward-char) | ||
| 2473 | (not (looking-at "[/*]/\\|=>"))) | ||
| 2474 | (js--looking-at-operator-p) | ||
| 2475 | (and (progn (backward-char) | ||
| 2476 | (not (looking-at "\\+\\+\\|--\\|/[/*]")))))))))))) | ||
| 1842 | 2477 | ||
| 1843 | (defun js--skip-term-backward () | 2478 | (defun js--skip-term-backward () |
| 1844 | "Skip a term before point; return t if a term was skipped." | 2479 | "Skip a term before point; return t if a term was skipped." |
| @@ -2064,23 +2699,182 @@ indentation is aligned to that column." | |||
| 2064 | (when comma-p | 2699 | (when comma-p |
| 2065 | (goto-char (1+ declaration-keyword-end)))))))) | 2700 | (goto-char (1+ declaration-keyword-end)))))))) |
| 2066 | 2701 | ||
| 2067 | (defconst js--line-terminating-arrow-re "\\s-*=>\\s-*\\(/[/*]\\|$\\)" | 2702 | (defconst js--line-terminating-arrow-re "=>\\s-*\\(/[/*]\\|$\\)" |
| 2068 | "Regexp matching the last \"=>\" (arrow) token on a line. | 2703 | "Regexp matching the last \"=>\" (arrow) token on a line. |
| 2069 | Whitespace and comments around the arrow are ignored.") | 2704 | Whitespace and comments around the arrow are ignored.") |
| 2070 | 2705 | ||
| 2071 | (defun js--looking-at-broken-arrow-function-p () | 2706 | (defun js--broken-arrow-terminates-line-p () |
| 2072 | "Helper function for `js--proper-indentation'. | 2707 | "Helper function for `js--proper-indentation'. |
| 2073 | Return t if point is at the start of a (possibly async) arrow | 2708 | Return t if the last non-comment, non-whitespace token of the |
| 2074 | function and the last non-comment, non-whitespace token of the | 2709 | current line is the \"=>\" token (of an arrow function)." |
| 2075 | current line is the \"=>\" token." | 2710 | (let ((from (point))) |
| 2076 | (when (looking-at "\\s-*async\\s-*") | 2711 | (end-of-line) |
| 2077 | (goto-char (match-end 0))) | 2712 | (re-search-backward js--line-terminating-arrow-re from t))) |
| 2078 | (cond | 2713 | |
| 2079 | ((eq (char-after) ?\() | 2714 | ;; When indenting, we want to know if the line is… |
| 2080 | (forward-list) | 2715 | ;; |
| 2081 | (looking-at-p js--line-terminating-arrow-re)) | 2716 | ;; - within a multiline JSXElement, or |
| 2082 | (t (looking-at-p | 2717 | ;; - within a string in a JSXBoundaryElement, or |
| 2083 | (concat js--name-re js--line-terminating-arrow-re))))) | 2718 | ;; - within JSXText, or |
| 2719 | ;; - within a JSXAttribute’s multiline JSXExpressionContainer. | ||
| 2720 | ;; | ||
| 2721 | ;; In these cases, special XML-like indentation rules for JSX apply. | ||
| 2722 | ;; If JS is nested within JSX, then indentation calculations may be | ||
| 2723 | ;; combined, such that JS indentation is “relative” to the JSX’s. | ||
| 2724 | ;; | ||
| 2725 | ;; Therefore, functions below provide such contextual information, and | ||
| 2726 | ;; `js--proper-indentation' may call itself once recursively in order | ||
| 2727 | ;; to finish calculating that “relative” JS+JSX indentation. | ||
| 2728 | |||
| 2729 | (defun js-jsx--context () | ||
| 2730 | "Determine JSX context and move to enclosing JSX." | ||
| 2731 | (let ((pos (point)) | ||
| 2732 | (parse-status (syntax-ppss)) | ||
| 2733 | (enclosing-tag-pos (js-jsx--enclosing-tag-pos))) | ||
| 2734 | (when enclosing-tag-pos | ||
| 2735 | (if (< pos (nth 1 enclosing-tag-pos)) | ||
| 2736 | (if (nth 3 parse-status) | ||
| 2737 | (list 'string (nth 8 parse-status)) | ||
| 2738 | (list 'tag (nth 0 enclosing-tag-pos) (nth 1 enclosing-tag-pos))) | ||
| 2739 | (list 'text (nth 0 enclosing-tag-pos) (nth 2 enclosing-tag-pos)))))) | ||
| 2740 | |||
| 2741 | (defun js-jsx--contextual-indentation (line context) | ||
| 2742 | "Calculate indentation column for LINE from CONTEXT. | ||
| 2743 | The column calculation is based off of `sgml-calculate-indent'." | ||
| 2744 | (pcase (nth 0 context) | ||
| 2745 | |||
| 2746 | ('string | ||
| 2747 | ;; Go back to previous non-empty line. | ||
| 2748 | (while (and (> (point) (nth 1 context)) | ||
| 2749 | (zerop (forward-line -1)) | ||
| 2750 | (looking-at "[ \t]*$"))) | ||
| 2751 | (if (> (point) (nth 1 context)) | ||
| 2752 | ;; Previous line is inside the string. | ||
| 2753 | (current-indentation) | ||
| 2754 | (goto-char (nth 1 context)) | ||
| 2755 | (1+ (current-column)))) | ||
| 2756 | |||
| 2757 | ('tag | ||
| 2758 | ;; Special JSX indentation rule: a “dangling” closing angle | ||
| 2759 | ;; bracket on its own line is indented at the same level as the | ||
| 2760 | ;; opening angle bracket of the JSXElement. Otherwise, indent | ||
| 2761 | ;; JSXAttribute space like SGML. | ||
| 2762 | (if (and | ||
| 2763 | js-jsx-align->-with-< | ||
| 2764 | (progn | ||
| 2765 | (goto-char (nth 2 context)) | ||
| 2766 | (and (= line (line-number-at-pos)) | ||
| 2767 | (looking-back "^\\s-*/?>" (line-beginning-position))))) | ||
| 2768 | (progn | ||
| 2769 | (goto-char (nth 1 context)) | ||
| 2770 | (current-column)) | ||
| 2771 | ;; Indent JSXAttribute space like SGML. | ||
| 2772 | (goto-char (nth 1 context)) | ||
| 2773 | ;; Skip tag name: | ||
| 2774 | (skip-chars-forward " \t") | ||
| 2775 | (skip-chars-forward "^ \t\n") | ||
| 2776 | (skip-chars-forward " \t") | ||
| 2777 | (if (not (eolp)) | ||
| 2778 | (current-column) | ||
| 2779 | ;; This is the first attribute: indent. | ||
| 2780 | (goto-char (+ (nth 1 context) js-jsx-attribute-offset)) | ||
| 2781 | (+ (current-column) (or js-jsx-indent-level js-indent-level))))) | ||
| 2782 | |||
| 2783 | ('text | ||
| 2784 | ;; Indent to reflect nesting. | ||
| 2785 | (goto-char (nth 1 context)) | ||
| 2786 | (+ (current-column) | ||
| 2787 | ;; The last line isn’t nested, but the rest are. | ||
| 2788 | (if (or (not (nth 2 context)) ; Unclosed. | ||
| 2789 | (< line (line-number-at-pos (nth 2 context)))) | ||
| 2790 | (or js-jsx-indent-level js-indent-level) | ||
| 2791 | 0))) | ||
| 2792 | |||
| 2793 | )) | ||
| 2794 | |||
| 2795 | (defun js-jsx--enclosing-curly-pos () | ||
| 2796 | "Return position of enclosing “{” in a “{/}” pair about point." | ||
| 2797 | (let ((parens (reverse (nth 9 (syntax-ppss)))) paren-pos curly-pos) | ||
| 2798 | (while | ||
| 2799 | (and | ||
| 2800 | (setq paren-pos (car parens)) | ||
| 2801 | (not (when (= (char-after paren-pos) ?{) | ||
| 2802 | (setq curly-pos paren-pos))) | ||
| 2803 | (setq parens (cdr parens)))) | ||
| 2804 | curly-pos)) | ||
| 2805 | |||
| 2806 | (defun js-jsx--goto-outermost-enclosing-curly (limit) | ||
| 2807 | "Set point to enclosing “{” at or closest after LIMIT." | ||
| 2808 | (let (pos) | ||
| 2809 | (while | ||
| 2810 | (and | ||
| 2811 | (setq pos (js-jsx--enclosing-curly-pos)) | ||
| 2812 | (if (>= pos limit) (goto-char pos)) | ||
| 2813 | (> pos limit))))) | ||
| 2814 | |||
| 2815 | (defun js-jsx--expr-attribute-pos (start limit) | ||
| 2816 | "Look back from START to LIMIT for a JSXAttribute." | ||
| 2817 | (save-excursion | ||
| 2818 | (goto-char start) ; Skip the first curly. | ||
| 2819 | ;; Skip any remaining enclosing curlies until the JSXElement’s | ||
| 2820 | ;; beginning position; the last curly ought to be one of a | ||
| 2821 | ;; JSXExpressionContainer, which may refer to its JSXAttribute’s | ||
| 2822 | ;; beginning position (if it has one). | ||
| 2823 | (js-jsx--goto-outermost-enclosing-curly limit) | ||
| 2824 | (get-text-property (point) 'js-jsx-expr-attribute))) | ||
| 2825 | |||
| 2826 | (defvar js-jsx--indent-col nil | ||
| 2827 | "Baseline column for JS indentation within JSX.") | ||
| 2828 | |||
| 2829 | (defvar js-jsx--indent-attribute-line nil | ||
| 2830 | "Line relative to which indentation uses JSX as a baseline.") | ||
| 2831 | |||
| 2832 | (defun js-jsx--expr-indentation (parse-status pos col) | ||
| 2833 | "Indent using PARSE-STATUS; relative to POS, use base COL. | ||
| 2834 | To indent a JSXExpressionContainer’s expression, calculate the JS | ||
| 2835 | indentation, using JSX indentation as the base column when | ||
| 2836 | indenting relative to the beginning line of the | ||
| 2837 | JSXExpressionContainer’s JSXAttribute (if any)." | ||
| 2838 | (let* ((js-jsx--indent-col col) | ||
| 2839 | (js-jsx--indent-attribute-line | ||
| 2840 | (if pos (line-number-at-pos pos)))) | ||
| 2841 | (js--proper-indentation parse-status))) | ||
| 2842 | |||
| 2843 | (defun js-jsx--indentation (parse-status) | ||
| 2844 | "Helper function for `js--proper-indentation'. | ||
| 2845 | Return the proper indentation of the current line if it is part | ||
| 2846 | of a JSXElement expression spanning multiple lines; otherwise, | ||
| 2847 | return nil." | ||
| 2848 | (let ((current-line (line-number-at-pos)) | ||
| 2849 | (curly-pos (js-jsx--enclosing-curly-pos)) | ||
| 2850 | nth-context context expr-p beg-line col | ||
| 2851 | forward-sexp-function) ; Use the Lisp version. | ||
| 2852 | ;; Find the immediate context for indentation information, but | ||
| 2853 | ;; keep going to determine that point is at the N+1th line of | ||
| 2854 | ;; multiline JSX. | ||
| 2855 | (save-excursion | ||
| 2856 | (while | ||
| 2857 | (and | ||
| 2858 | (setq nth-context (js-jsx--context)) | ||
| 2859 | (progn | ||
| 2860 | (unless context | ||
| 2861 | (setq context nth-context) | ||
| 2862 | (setq expr-p (and curly-pos (< (point) curly-pos)))) | ||
| 2863 | (setq beg-line (line-number-at-pos)) | ||
| 2864 | (and | ||
| 2865 | (= beg-line current-line) | ||
| 2866 | (or (not curly-pos) (> (point) curly-pos))))))) | ||
| 2867 | ;; When on the second or later line of JSX, indent as JSX, | ||
| 2868 | ;; possibly switching back to JS indentation within | ||
| 2869 | ;; JSXExpressionContainers, possibly using the JSX as a base | ||
| 2870 | ;; column while switching back to JS indentation. | ||
| 2871 | (when (and context (> current-line beg-line)) | ||
| 2872 | (save-excursion | ||
| 2873 | (setq col (js-jsx--contextual-indentation current-line context))) | ||
| 2874 | (if expr-p | ||
| 2875 | (js-jsx--expr-indentation | ||
| 2876 | parse-status (js-jsx--expr-attribute-pos curly-pos (nth 1 context)) col) | ||
| 2877 | col)))) | ||
| 2084 | 2878 | ||
| 2085 | (defun js--proper-indentation (parse-status) | 2879 | (defun js--proper-indentation (parse-status) |
| 2086 | "Return the proper indentation for the current line." | 2880 | "Return the proper indentation for the current line." |
| @@ -2089,6 +2883,8 @@ current line is the \"=>\" token." | |||
| 2089 | (cond ((nth 4 parse-status) ; inside comment | 2883 | (cond ((nth 4 parse-status) ; inside comment |
| 2090 | (js--get-c-offset 'c (nth 8 parse-status))) | 2884 | (js--get-c-offset 'c (nth 8 parse-status))) |
| 2091 | ((nth 3 parse-status) 0) ; inside string | 2885 | ((nth 3 parse-status) 0) ; inside string |
| 2886 | ((when (and js-jsx-syntax (not js-jsx--indent-col)) | ||
| 2887 | (save-excursion (js-jsx--indentation parse-status)))) | ||
| 2092 | ((eq (char-after) ?#) 0) | 2888 | ((eq (char-after) ?#) 0) |
| 2093 | ((save-excursion (js--beginning-of-macro)) 4) | 2889 | ((save-excursion (js--beginning-of-macro)) 4) |
| 2094 | ;; Indent array comprehension continuation lines specially. | 2890 | ;; Indent array comprehension continuation lines specially. |
| @@ -2113,7 +2909,7 @@ current line is the \"=>\" token." | |||
| 2113 | (goto-char (nth 1 parse-status)) ; go to the opening char | 2909 | (goto-char (nth 1 parse-status)) ; go to the opening char |
| 2114 | (if (or (not js-indent-align-list-continuation) | 2910 | (if (or (not js-indent-align-list-continuation) |
| 2115 | (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)") | 2911 | (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)") |
| 2116 | (save-excursion (forward-char) (js--looking-at-broken-arrow-function-p))) | 2912 | (save-excursion (forward-char) (js--broken-arrow-terminates-line-p))) |
| 2117 | (progn ; nothing following the opening paren/bracket | 2913 | (progn ; nothing following the opening paren/bracket |
| 2118 | (skip-syntax-backward " ") | 2914 | (skip-syntax-backward " ") |
| 2119 | (when (eq (char-before) ?\)) (backward-list)) | 2915 | (when (eq (char-before) ?\)) (backward-list)) |
| @@ -2125,17 +2921,24 @@ current line is the \"=>\" token." | |||
| 2125 | (and switch-keyword-p | 2921 | (and switch-keyword-p |
| 2126 | in-switch-p))) | 2922 | in-switch-p))) |
| 2127 | (indent | 2923 | (indent |
| 2128 | (cond (same-indent-p | 2924 | (+ |
| 2129 | (current-column)) | 2925 | (cond |
| 2130 | (continued-expr-p | 2926 | ((and js-jsx--indent-attribute-line |
| 2131 | (+ (current-column) (* 2 js-indent-level) | 2927 | (eq js-jsx--indent-attribute-line |
| 2132 | js-expr-indent-offset)) | 2928 | (line-number-at-pos))) |
| 2133 | (t | 2929 | js-jsx--indent-col) |
| 2134 | (+ (current-column) js-indent-level | 2930 | (t |
| 2135 | (pcase (char-after (nth 1 parse-status)) | 2931 | (current-column))) |
| 2136 | (?\( js-paren-indent-offset) | 2932 | (cond (same-indent-p 0) |
| 2137 | (?\[ js-square-indent-offset) | 2933 | (continued-expr-p |
| 2138 | (?\{ js-curly-indent-offset))))))) | 2934 | (+ (* 2 js-indent-level) |
| 2935 | js-expr-indent-offset)) | ||
| 2936 | (t | ||
| 2937 | (+ js-indent-level | ||
| 2938 | (pcase (char-after (nth 1 parse-status)) | ||
| 2939 | (?\( js-paren-indent-offset) | ||
| 2940 | (?\[ js-square-indent-offset) | ||
| 2941 | (?\{ js-curly-indent-offset)))))))) | ||
| 2139 | (if in-switch-p | 2942 | (if in-switch-p |
| 2140 | (+ indent js-switch-indent-offset) | 2943 | (+ indent js-switch-indent-offset) |
| 2141 | indent))) | 2944 | indent))) |
| @@ -2151,193 +2954,6 @@ current line is the \"=>\" token." | |||
| 2151 | (+ js-indent-level js-expr-indent-offset)) | 2954 | (+ js-indent-level js-expr-indent-offset)) |
| 2152 | (t (prog-first-column))))) | 2955 | (t (prog-first-column))))) |
| 2153 | 2956 | ||
| 2154 | ;;; JSX Indentation | ||
| 2155 | |||
| 2156 | (defsubst js--jsx-find-before-tag () | ||
| 2157 | "Find where JSX starts. | ||
| 2158 | |||
| 2159 | Assume JSX appears in the following instances: | ||
| 2160 | - Inside parentheses, when returned or as the first argument | ||
| 2161 | to a function, and after a newline | ||
| 2162 | - When assigned to variables or object properties, but only | ||
| 2163 | on a single line | ||
| 2164 | - As the N+1th argument to a function | ||
| 2165 | |||
| 2166 | This is an optimized version of (re-search-backward \"[(,]\n\" | ||
| 2167 | nil t), except set point to the end of the match. This logic | ||
| 2168 | executes up to the number of lines in the file, so it should be | ||
| 2169 | really fast to reduce that impact." | ||
| 2170 | (let (pos) | ||
| 2171 | (while (and (> (point) (point-min)) | ||
| 2172 | (not (progn | ||
| 2173 | (end-of-line 0) | ||
| 2174 | (when (or (eq (char-before) 40) ; ( | ||
| 2175 | (eq (char-before) 44)) ; , | ||
| 2176 | (setq pos (1- (point)))))))) | ||
| 2177 | pos)) | ||
| 2178 | |||
| 2179 | (defconst js--jsx-end-tag-re | ||
| 2180 | (concat "</" sgml-name-re ">\\|/>") | ||
| 2181 | "Find the end of a JSX element.") | ||
| 2182 | |||
| 2183 | (defconst js--jsx-after-tag-re "[),]" | ||
| 2184 | "Find where JSX ends. | ||
| 2185 | This complements the assumption of where JSX appears from | ||
| 2186 | `js--jsx-before-tag-re', which see.") | ||
| 2187 | |||
| 2188 | (defun js--jsx-indented-element-p () | ||
| 2189 | "Determine if/how the current line should be indented as JSX. | ||
| 2190 | |||
| 2191 | Return `first' for the first JSXElement on its own line. | ||
| 2192 | Return `nth' for subsequent lines of the first JSXElement. | ||
| 2193 | Return `expression' for an embedded JS expression. | ||
| 2194 | Return `after' for anything after the last JSXElement. | ||
| 2195 | Return nil for non-JSX lines. | ||
| 2196 | |||
| 2197 | Currently, JSX indentation supports the following styles: | ||
| 2198 | |||
| 2199 | - Single-line elements (indented like normal JS): | ||
| 2200 | |||
| 2201 | var element = <div></div>; | ||
| 2202 | |||
| 2203 | - Multi-line elements (enclosed in parentheses): | ||
| 2204 | |||
| 2205 | function () { | ||
| 2206 | return ( | ||
| 2207 | <div> | ||
| 2208 | <div></div> | ||
| 2209 | </div> | ||
| 2210 | ); | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | - Function arguments: | ||
| 2214 | |||
| 2215 | React.render( | ||
| 2216 | <div></div>, | ||
| 2217 | document.querySelector('.root') | ||
| 2218 | );" | ||
| 2219 | (let ((current-pos (point)) | ||
| 2220 | (current-line (line-number-at-pos)) | ||
| 2221 | last-pos | ||
| 2222 | before-tag-pos before-tag-line | ||
| 2223 | tag-start-pos tag-start-line | ||
| 2224 | tag-end-pos tag-end-line | ||
| 2225 | after-tag-line | ||
| 2226 | parens paren type) | ||
| 2227 | (save-excursion | ||
| 2228 | (and | ||
| 2229 | ;; Determine if we're inside a jsx element | ||
| 2230 | (progn | ||
| 2231 | (end-of-line) | ||
| 2232 | (while (and (not tag-start-pos) | ||
| 2233 | (setq last-pos (js--jsx-find-before-tag))) | ||
| 2234 | (while (forward-comment 1)) | ||
| 2235 | (when (= (char-after) 60) ; < | ||
| 2236 | (setq before-tag-pos last-pos | ||
| 2237 | tag-start-pos (point))) | ||
| 2238 | (goto-char last-pos)) | ||
| 2239 | tag-start-pos) | ||
| 2240 | (progn | ||
| 2241 | (setq before-tag-line (line-number-at-pos before-tag-pos) | ||
| 2242 | tag-start-line (line-number-at-pos tag-start-pos)) | ||
| 2243 | (and | ||
| 2244 | ;; A "before" line which also starts an element begins with js, so | ||
| 2245 | ;; indent it like js | ||
| 2246 | (> current-line before-tag-line) | ||
| 2247 | ;; Only indent the jsx lines like jsx | ||
| 2248 | (>= current-line tag-start-line))) | ||
| 2249 | (cond | ||
| 2250 | ;; Analyze bounds if there are any | ||
| 2251 | ((progn | ||
| 2252 | (while (and (not tag-end-pos) | ||
| 2253 | (setq last-pos (re-search-forward js--jsx-end-tag-re nil t))) | ||
| 2254 | (while (forward-comment 1)) | ||
| 2255 | (when (looking-at js--jsx-after-tag-re) | ||
| 2256 | (setq tag-end-pos last-pos))) | ||
| 2257 | tag-end-pos) | ||
| 2258 | (setq tag-end-line (line-number-at-pos tag-end-pos) | ||
| 2259 | after-tag-line (line-number-at-pos after-tag-line)) | ||
| 2260 | (or (and | ||
| 2261 | ;; Ensure we're actually within the bounds of the jsx | ||
| 2262 | (<= current-line tag-end-line) | ||
| 2263 | ;; An "after" line which does not end an element begins with | ||
| 2264 | ;; js, so indent it like js | ||
| 2265 | (<= current-line after-tag-line)) | ||
| 2266 | (and | ||
| 2267 | ;; Handle another case where there could be e.g. comments after | ||
| 2268 | ;; the element | ||
| 2269 | (> current-line tag-end-line) | ||
| 2270 | (< current-line after-tag-line) | ||
| 2271 | (setq type 'after)))) | ||
| 2272 | ;; They may not be any bounds (yet) | ||
| 2273 | (t)) | ||
| 2274 | ;; Check if we're inside an embedded multi-line js expression | ||
| 2275 | (cond | ||
| 2276 | ((not type) | ||
| 2277 | (goto-char current-pos) | ||
| 2278 | (end-of-line) | ||
| 2279 | (setq parens (nth 9 (syntax-ppss))) | ||
| 2280 | (while (and parens (not type)) | ||
| 2281 | (setq paren (car parens)) | ||
| 2282 | (cond | ||
| 2283 | ((and (>= paren tag-start-pos) | ||
| 2284 | ;; Curly bracket indicates the start of an embedded expression | ||
| 2285 | (= (char-after paren) 123) ; { | ||
| 2286 | ;; The first line of the expression is indented like sgml | ||
| 2287 | (> current-line (line-number-at-pos paren)) | ||
| 2288 | ;; Check if within a closing curly bracket (if any) | ||
| 2289 | ;; (exclusive, as the closing bracket is indented like sgml) | ||
| 2290 | (cond | ||
| 2291 | ((progn | ||
| 2292 | (goto-char paren) | ||
| 2293 | (ignore-errors (let (forward-sexp-function) | ||
| 2294 | (forward-sexp)))) | ||
| 2295 | (< current-line (line-number-at-pos))) | ||
| 2296 | (t))) | ||
| 2297 | ;; Indicate this guy will be indented specially | ||
| 2298 | (setq type 'expression)) | ||
| 2299 | (t (setq parens (cdr parens))))) | ||
| 2300 | t) | ||
| 2301 | (t)) | ||
| 2302 | (cond | ||
| 2303 | (type) | ||
| 2304 | ;; Indent the first jsx thing like js so we can indent future jsx things | ||
| 2305 | ;; like sgml relative to the first thing | ||
| 2306 | ((= current-line tag-start-line) 'first) | ||
| 2307 | ('nth)))))) | ||
| 2308 | |||
| 2309 | (defmacro js--as-sgml (&rest body) | ||
| 2310 | "Execute BODY as if in sgml-mode." | ||
| 2311 | `(with-syntax-table sgml-mode-syntax-table | ||
| 2312 | (let (forward-sexp-function | ||
| 2313 | parse-sexp-lookup-properties) | ||
| 2314 | ,@body))) | ||
| 2315 | |||
| 2316 | (defun js--expression-in-sgml-indent-line () | ||
| 2317 | "Indent the current line as JavaScript or SGML (whichever is farther)." | ||
| 2318 | (let* (indent-col | ||
| 2319 | (savep (point)) | ||
| 2320 | ;; Don't whine about errors/warnings when we're indenting. | ||
| 2321 | ;; This has to be set before calling parse-partial-sexp below. | ||
| 2322 | (inhibit-point-motion-hooks t) | ||
| 2323 | (parse-status (save-excursion | ||
| 2324 | (syntax-ppss (point-at-bol))))) | ||
| 2325 | ;; Don't touch multiline strings. | ||
| 2326 | (unless (nth 3 parse-status) | ||
| 2327 | (setq indent-col (save-excursion | ||
| 2328 | (back-to-indentation) | ||
| 2329 | (if (>= (point) savep) (setq savep nil)) | ||
| 2330 | (js--as-sgml (sgml-calculate-indent)))) | ||
| 2331 | (if (null indent-col) | ||
| 2332 | 'noindent | ||
| 2333 | ;; Use whichever indentation column is greater, such that the sgml | ||
| 2334 | ;; column is effectively a minimum | ||
| 2335 | (setq indent-col (max (js--proper-indentation parse-status) | ||
| 2336 | (+ indent-col js-indent-level))) | ||
| 2337 | (if savep | ||
| 2338 | (save-excursion (indent-line-to indent-col)) | ||
| 2339 | (indent-line-to indent-col)))))) | ||
| 2340 | |||
| 2341 | (defun js-indent-line () | 2957 | (defun js-indent-line () |
| 2342 | "Indent the current line as JavaScript." | 2958 | "Indent the current line as JavaScript." |
| 2343 | (interactive) | 2959 | (interactive) |
| @@ -2349,23 +2965,9 @@ Currently, JSX indentation supports the following styles: | |||
| 2349 | (when (> offset 0) (forward-char offset))))) | 2965 | (when (> offset 0) (forward-char offset))))) |
| 2350 | 2966 | ||
| 2351 | (defun js-jsx-indent-line () | 2967 | (defun js-jsx-indent-line () |
| 2352 | "Indent the current line as JSX (with SGML offsets). | 2968 | "Indent the current line as JavaScript+JSX." |
| 2353 | i.e., customize JSX element indentation with `sgml-basic-offset', | ||
| 2354 | `sgml-attribute-offset' et al." | ||
| 2355 | (interactive) | 2969 | (interactive) |
| 2356 | (let ((indentation-type (js--jsx-indented-element-p))) | 2970 | (let ((js-jsx-syntax t)) (js-indent-line))) |
| 2357 | (cond | ||
| 2358 | ((eq indentation-type 'expression) | ||
| 2359 | (js--expression-in-sgml-indent-line)) | ||
| 2360 | ((or (eq indentation-type 'first) | ||
| 2361 | (eq indentation-type 'after)) | ||
| 2362 | ;; Don't treat this first thing as a continued expression (often a "<" or | ||
| 2363 | ;; ">" causes this misinterpretation) | ||
| 2364 | (cl-letf (((symbol-function #'js--continued-expression-p) 'ignore)) | ||
| 2365 | (js-indent-line))) | ||
| 2366 | ((eq indentation-type 'nth) | ||
| 2367 | (js--as-sgml (sgml-indent-line))) | ||
| 2368 | (t (js-indent-line))))) | ||
| 2369 | 2971 | ||
| 2370 | ;;; Filling | 2972 | ;;; Filling |
| 2371 | 2973 | ||
| @@ -3856,6 +4458,77 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3856 | (when temp-name | 4458 | (when temp-name |
| 3857 | (delete-file temp-name)))))) | 4459 | (delete-file temp-name)))))) |
| 3858 | 4460 | ||
| 4461 | ;;; Syntax extensions | ||
| 4462 | |||
| 4463 | (defvar js-syntactic-mode-name t | ||
| 4464 | "If non-nil, print enabled syntaxes in the mode name.") | ||
| 4465 | |||
| 4466 | (defun js--syntactic-mode-name-part () | ||
| 4467 | "Return a string like “[JSX]” when `js-jsx-syntax' is enabled." | ||
| 4468 | (if js-syntactic-mode-name | ||
| 4469 | (let (syntaxes) | ||
| 4470 | (if js-jsx-syntax (push "JSX" syntaxes)) | ||
| 4471 | (if syntaxes | ||
| 4472 | (concat "[" (mapconcat #'identity syntaxes ",") "]") | ||
| 4473 | "")) | ||
| 4474 | "")) | ||
| 4475 | |||
| 4476 | (defun js-use-syntactic-mode-name () | ||
| 4477 | "Print enabled syntaxes if `js-syntactic-mode-name' is t. | ||
| 4478 | Modes deriving from `js-mode' should call this to ensure that | ||
| 4479 | their `mode-name' updates to show enabled syntax extensions." | ||
| 4480 | (when (stringp mode-name) | ||
| 4481 | (setq mode-name `(,mode-name (:eval (js--syntactic-mode-name-part)))))) | ||
| 4482 | |||
| 4483 | (defun js-jsx-enable () | ||
| 4484 | "Enable JSX in the current buffer." | ||
| 4485 | (interactive) | ||
| 4486 | (setq-local js-jsx-syntax t)) | ||
| 4487 | |||
| 4488 | ;; To make discovering and using syntax extensions features easier for | ||
| 4489 | ;; users (who might not read the docs), try to safely and | ||
| 4490 | ;; automatically enable syntax extensions based on heuristics. | ||
| 4491 | |||
| 4492 | (defvar js-jsx-regexps | ||
| 4493 | (list "\\_<\\(?:var\\|let\\|const\\|import\\)\\_>.*?React") | ||
| 4494 | "Regexps for detecting JSX in JavaScript buffers. | ||
| 4495 | When `js-jsx-detect-syntax' is non-nil and any of these regexps | ||
| 4496 | match text near the beginning of a JavaScript buffer, | ||
| 4497 | `js-jsx-syntax' (which see) will be made buffer-local and set to | ||
| 4498 | t.") | ||
| 4499 | |||
| 4500 | (defun js-jsx--detect-and-enable (&optional arbitrarily) | ||
| 4501 | "Detect if JSX is likely to be used, and enable it if so. | ||
| 4502 | Might make `js-jsx-syntax' buffer-local and set it to t. Matches | ||
| 4503 | from the beginning of the buffer, unless optional arg ARBITRARILY | ||
| 4504 | is non-nil. Return t after enabling, nil otherwise." | ||
| 4505 | (when (or (and (buffer-file-name) | ||
| 4506 | (string-match-p "\\.jsx\\'" (buffer-file-name))) | ||
| 4507 | (and js-jsx-detect-syntax | ||
| 4508 | (save-excursion | ||
| 4509 | (unless arbitrarily | ||
| 4510 | (goto-char (point-min))) | ||
| 4511 | (catch 'match | ||
| 4512 | (mapc | ||
| 4513 | (lambda (regexp) | ||
| 4514 | (if (re-search-forward regexp 4000 t) (throw 'match t))) | ||
| 4515 | js-jsx-regexps) | ||
| 4516 | nil)))) | ||
| 4517 | (js-jsx-enable) | ||
| 4518 | t)) | ||
| 4519 | |||
| 4520 | (defun js-jsx--detect-after-change (beg end _len) | ||
| 4521 | "Detect if JSX is likely to be used after a change. | ||
| 4522 | This function is intended for use in `after-change-functions'." | ||
| 4523 | (when (<= end 4000) | ||
| 4524 | (save-excursion | ||
| 4525 | (goto-char beg) | ||
| 4526 | (beginning-of-line) | ||
| 4527 | (save-restriction | ||
| 4528 | (narrow-to-region (point) end) | ||
| 4529 | (when (js-jsx--detect-and-enable 'arbitrarily) | ||
| 4530 | (remove-hook 'after-change-functions #'js-jsx--detect-after-change t)))))) | ||
| 4531 | |||
| 3859 | ;;; Main Function | 4532 | ;;; Main Function |
| 3860 | 4533 | ||
| 3861 | ;;;###autoload | 4534 | ;;;###autoload |
| @@ -3871,6 +4544,10 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3871 | '(font-lock-syntactic-face-function | 4544 | '(font-lock-syntactic-face-function |
| 3872 | . js-font-lock-syntactic-face-function))) | 4545 | . js-font-lock-syntactic-face-function))) |
| 3873 | (setq-local syntax-propertize-function #'js-syntax-propertize) | 4546 | (setq-local syntax-propertize-function #'js-syntax-propertize) |
| 4547 | (add-hook 'syntax-propertize-extend-region-functions | ||
| 4548 | #'syntax-propertize-multiline 'append 'local) | ||
| 4549 | (add-hook 'syntax-propertize-extend-region-functions | ||
| 4550 | #'js--syntax-propertize-extend-region 'append 'local) | ||
| 3874 | (setq-local prettify-symbols-alist js--prettify-symbols-alist) | 4551 | (setq-local prettify-symbols-alist js--prettify-symbols-alist) |
| 3875 | 4552 | ||
| 3876 | (setq-local parse-sexp-ignore-comments t) | 4553 | (setq-local parse-sexp-ignore-comments t) |
| @@ -3878,6 +4555,7 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3878 | 4555 | ||
| 3879 | ;; Comments | 4556 | ;; Comments |
| 3880 | (setq-local comment-start "// ") | 4557 | (setq-local comment-start "// ") |
| 4558 | (setq-local comment-start-skip "\\(//+\\|/\\*+\\)\\s *") | ||
| 3881 | (setq-local comment-end "") | 4559 | (setq-local comment-end "") |
| 3882 | (setq-local fill-paragraph-function #'js-fill-paragraph) | 4560 | (setq-local fill-paragraph-function #'js-fill-paragraph) |
| 3883 | (setq-local normal-auto-fill-function #'js-do-auto-fill) | 4561 | (setq-local normal-auto-fill-function #'js-do-auto-fill) |
| @@ -3888,6 +4566,11 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3888 | ;; Frameworks | 4566 | ;; Frameworks |
| 3889 | (js--update-quick-match-re) | 4567 | (js--update-quick-match-re) |
| 3890 | 4568 | ||
| 4569 | ;; Syntax extensions | ||
| 4570 | (unless (js-jsx--detect-and-enable) | ||
| 4571 | (add-hook 'after-change-functions #'js-jsx--detect-after-change nil t)) | ||
| 4572 | (js-use-syntactic-mode-name) | ||
| 4573 | |||
| 3891 | ;; Imenu | 4574 | ;; Imenu |
| 3892 | (setq imenu-case-fold-search nil) | 4575 | (setq imenu-case-fold-search nil) |
| 3893 | (setq imenu-create-index-function #'js--imenu-create-index) | 4576 | (setq imenu-create-index-function #'js--imenu-create-index) |
| @@ -3898,8 +4581,7 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3898 | c-paragraph-separate "$" | 4581 | c-paragraph-separate "$" |
| 3899 | c-block-comment-prefix "* " | 4582 | c-block-comment-prefix "* " |
| 3900 | c-line-comment-starter "//" | 4583 | c-line-comment-starter "//" |
| 3901 | c-comment-start-regexp "/[*/]\\|\\s!" | 4584 | c-comment-start-regexp "/[*/]\\|\\s!") |
| 3902 | comment-start-skip "\\(//+\\|/\\*+\\)\\s *") | ||
| 3903 | (setq-local comment-line-break-function #'c-indent-new-comment-line) | 4585 | (setq-local comment-line-break-function #'c-indent-new-comment-line) |
| 3904 | (setq-local c-block-comment-start-regexp "/\\*") | 4586 | (setq-local c-block-comment-start-regexp "/\\*") |
| 3905 | (setq-local comment-multi-line t) | 4587 | (setq-local comment-multi-line t) |
| @@ -3932,19 +4614,33 @@ If one hasn't been set, or if it's stale, prompt for a new one." | |||
| 3932 | ;;(syntax-propertize (point-max)) | 4614 | ;;(syntax-propertize (point-max)) |
| 3933 | ) | 4615 | ) |
| 3934 | 4616 | ||
| 3935 | ;;;###autoload | 4617 | ;; Since we made JSX support available and automatically-enabled in |
| 3936 | (define-derived-mode js-jsx-mode js-mode "JSX" | 4618 | ;; the base `js-mode' (for ease of use), now `js-jsx-mode' simply |
| 3937 | "Major mode for editing JSX. | 4619 | ;; serves as one other interface to unconditionally enable JSX in |
| 3938 | 4620 | ;; buffers, mostly for backwards-compatibility. | |
| 3939 | To customize the indentation for this mode, set the SGML offset | 4621 | ;; |
| 3940 | variables (`sgml-basic-offset', `sgml-attribute-offset' et al.) | 4622 | ;; Since it is probably more common for packages to integrate with |
| 3941 | locally, like so: | 4623 | ;; `js-mode' than with `js-jsx-mode', it is therefore probably |
| 4624 | ;; slightly better for users to use one of the many other methods for | ||
| 4625 | ;; enabling JSX syntax. But using `js-jsx-mode' can’t be that bad | ||
| 4626 | ;; either, so we won’t bother users with an obsoletion warning. | ||
| 3942 | 4627 | ||
| 3943 | (defun set-jsx-indentation () | 4628 | ;;;###autoload |
| 3944 | (setq-local sgml-basic-offset js-indent-level)) | 4629 | (define-derived-mode js-jsx-mode js-mode "JavaScript" |
| 3945 | (add-hook \\='js-jsx-mode-hook #\\='set-jsx-indentation)" | 4630 | "Major mode for editing JavaScript+JSX. |
| 4631 | |||
| 4632 | Simply makes `js-jsx-syntax' buffer-local and sets it to t. | ||
| 4633 | |||
| 4634 | `js-mode' may detect and enable support for JSX automatically if | ||
| 4635 | it appears to be used in a JavaScript file. You could also | ||
| 4636 | customize `js-jsx-regexps' to improve that detection; or, you | ||
| 4637 | could set `js-jsx-syntax' to t in your init file, or in a | ||
| 4638 | .dir-locals.el file, or using file variables; or, you could call | ||
| 4639 | `js-jsx-enable' in `js-mode-hook'. You may be better served by | ||
| 4640 | one of the aforementioned options instead of using this mode." | ||
| 3946 | :group 'js | 4641 | :group 'js |
| 3947 | (setq-local indent-line-function #'js-jsx-indent-line)) | 4642 | (js-jsx-enable) |
| 4643 | (js-use-syntactic-mode-name)) | ||
| 3948 | 4644 | ||
| 3949 | ;;;###autoload (defalias 'javascript-mode 'js-mode) | 4645 | ;;;###autoload (defalias 'javascript-mode 'js-mode) |
| 3950 | 4646 | ||
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 5d0d03d5029..b05f9a33e90 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -675,7 +675,7 @@ Which one will be chosen depends on the value of | |||
| 675 | 675 | ||
| 676 | (defconst python-syntax-propertize-function | 676 | (defconst python-syntax-propertize-function |
| 677 | (syntax-propertize-rules | 677 | (syntax-propertize-rules |
| 678 | ((python-rx string-delimiter) | 678 | ((rx (or "\"\"\"" "'''")) |
| 679 | (0 (ignore (python-syntax-stringify)))))) | 679 | (0 (ignore (python-syntax-stringify)))))) |
| 680 | 680 | ||
| 681 | (define-obsolete-variable-alias 'python--prettify-symbols-alist | 681 | (define-obsolete-variable-alias 'python--prettify-symbols-alist |
| @@ -701,35 +701,27 @@ is used to limit the scan." | |||
| 701 | 701 | ||
| 702 | (defun python-syntax-stringify () | 702 | (defun python-syntax-stringify () |
| 703 | "Put `syntax-table' property correctly on single/triple quotes." | 703 | "Put `syntax-table' property correctly on single/triple quotes." |
| 704 | (let* ((num-quotes (length (match-string-no-properties 1))) | 704 | (let* ((ppss (save-excursion (backward-char 3) (syntax-ppss))) |
| 705 | (ppss (prog2 | 705 | (string-start (and (eq t (nth 3 ppss)) (nth 8 ppss))) |
| 706 | (backward-char num-quotes) | 706 | (quote-starting-pos (- (point) 3)) |
| 707 | (syntax-ppss) | 707 | (quote-ending-pos (point))) |
| 708 | (forward-char num-quotes))) | 708 | (cond ((or (nth 4 ppss) ;Inside a comment |
| 709 | (string-start (and (not (nth 4 ppss)) (nth 8 ppss))) | 709 | (and string-start |
| 710 | (quote-starting-pos (- (point) num-quotes)) | 710 | ;; Inside of a string quoted with different triple quotes. |
| 711 | (quote-ending-pos (point)) | 711 | (not (eql (char-after string-start) |
| 712 | (num-closing-quotes | 712 | (char-after quote-starting-pos))))) |
| 713 | (and string-start | 713 | ;; Do nothing. |
| 714 | (python-syntax-count-quotes | ||
| 715 | (char-before) string-start quote-starting-pos)))) | ||
| 716 | (cond ((and string-start (= num-closing-quotes 0)) | ||
| 717 | ;; This set of quotes doesn't match the string starting | ||
| 718 | ;; kind. Do nothing. | ||
| 719 | nil) | 714 | nil) |
| 720 | ((not string-start) | 715 | ((nth 5 ppss) |
| 716 | ;; The first quote is escaped, so it's not part of a triple quote! | ||
| 717 | (goto-char (1+ quote-starting-pos))) | ||
| 718 | ((null string-start) | ||
| 721 | ;; This set of quotes delimit the start of a string. | 719 | ;; This set of quotes delimit the start of a string. |
| 722 | (put-text-property quote-starting-pos (1+ quote-starting-pos) | 720 | (put-text-property quote-starting-pos (1+ quote-starting-pos) |
| 723 | 'syntax-table (string-to-syntax "|"))) | 721 | 'syntax-table (string-to-syntax "|"))) |
| 724 | ((= num-quotes num-closing-quotes) | 722 | (t |
| 725 | ;; This set of quotes delimit the end of a string. | 723 | ;; This set of quotes delimit the end of a string. |
| 726 | (put-text-property (1- quote-ending-pos) quote-ending-pos | 724 | (put-text-property (1- quote-ending-pos) quote-ending-pos |
| 727 | 'syntax-table (string-to-syntax "|"))) | ||
| 728 | ((> num-quotes num-closing-quotes) | ||
| 729 | ;; This may only happen whenever a triple quote is closing | ||
| 730 | ;; a single quoted string. Add string delimiter syntax to | ||
| 731 | ;; all three quotes. | ||
| 732 | (put-text-property quote-starting-pos quote-ending-pos | ||
| 733 | 'syntax-table (string-to-syntax "|")))))) | 725 | 'syntax-table (string-to-syntax "|")))))) |
| 734 | 726 | ||
| 735 | (defvar python-mode-syntax-table | 727 | (defvar python-mode-syntax-table |
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index dd3a6fa411e..853a3500ee1 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el | |||
| @@ -2905,8 +2905,7 @@ STRING This is ignored for the purposes of calculating | |||
| 2905 | (setq align-point (point)))) | 2905 | (setq align-point (point)))) |
| 2906 | (or (bobp) | 2906 | (or (bobp) |
| 2907 | (forward-char -1)) | 2907 | (forward-char -1)) |
| 2908 | ;; FIXME: This charset looks too much like a regexp. --Stef | 2908 | (skip-chars-forward "*0-9?[]a-z") |
| 2909 | (skip-chars-forward "[a-z0-9]*?") | ||
| 2910 | ) | 2909 | ) |
| 2911 | ((string-match "[])}]" x) | 2910 | ((string-match "[])}]" x) |
| 2912 | (setq x (sh-safe-forward-sexp -1)) | 2911 | (setq x (sh-safe-forward-sexp -1)) |
diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el index 7b9c3921fba..9226291ffbb 100644 --- a/lisp/progmodes/verilog-mode.el +++ b/lisp/progmodes/verilog-mode.el | |||
| @@ -14263,13 +14263,13 @@ and the case items." | |||
| 14263 | (defun verilog-sk-define-signal () | 14263 | (defun verilog-sk-define-signal () |
| 14264 | "Insert a definition of signal under point at top of module." | 14264 | "Insert a definition of signal under point at top of module." |
| 14265 | (interactive "*") | 14265 | (interactive "*") |
| 14266 | (let* ((sig-re "[a-zA-Z0-9_]*") | 14266 | (let* ((sig-chars "a-zA-Z0-9_") |
| 14267 | (v1 (buffer-substring | 14267 | (v1 (buffer-substring |
| 14268 | (save-excursion | 14268 | (save-excursion |
| 14269 | (skip-chars-backward sig-re) | 14269 | (skip-chars-backward sig-chars) |
| 14270 | (point)) | 14270 | (point)) |
| 14271 | (save-excursion | 14271 | (save-excursion |
| 14272 | (skip-chars-forward sig-re) | 14272 | (skip-chars-forward sig-chars) |
| 14273 | (point))))) | 14273 | (point))))) |
| 14274 | (if (not (member v1 verilog-keywords)) | 14274 | (if (not (member v1 verilog-keywords)) |
| 14275 | (save-excursion | 14275 | (save-excursion |