Skip to content

Commit d21c983

Browse files
committed
feat(fontlock): fontify arrow fn arguments
1 parent 93e1040 commit d21c983

File tree

1 file changed

+113
-5
lines changed

1 file changed

+113
-5
lines changed

typescript-mode.el

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@ lines."
10151015
(progn
10161016
(forward-comment most-positive-fixnum)
10171017
(memq (char-after) '(?\, ?\; ?\] ?\) ?\}))))
1018-
do (forward-sexp)))
1018+
do (forward-sexp)))
10191019
while (and (eq (char-after) ?\n)
10201020
(save-excursion
10211021
(forward-char)
@@ -1686,6 +1686,82 @@ point of view of font-lock. It applies highlighting directly with
16861686
;; Matcher always "fails"
16871687
nil)
16881688

1689+
(defun typescript--function-argument-matcher (limit)
1690+
"Font-lock matcher for variables in argument lists.
1691+
1692+
Because the syntax of the argument list is shared between
1693+
functions, arrow functions and methods, this same matcher is used
1694+
for all of them. The context for the search is set up as
1695+
anchored matcher.
1696+
1697+
This is a cc-mode-style matcher that *always* fails, from the
1698+
point of view of font-lock. It applies highlighting directly
1699+
with `font-lock-apply-highlight'."
1700+
(condition-case nil
1701+
(save-restriction
1702+
(widen)
1703+
(narrow-to-region (point-min) limit)
1704+
(while (re-search-forward
1705+
(rx (group
1706+
(regexp "[a-zA-Z_$]\\(?:\\s_\\|\\sw\\)*")
1707+
;; name can be optionally followed by ? to mark
1708+
;; the argument optional
1709+
(? "?"))
1710+
(* whitespace)
1711+
(group (or "," ":" ")"
1712+
;; last variable in the list with a
1713+
;; paren on next line and no hanging
1714+
;; comma. extra logic is added to deal
1715+
;; with possible comments after the
1716+
;; variable.
1717+
eol
1718+
(and (* whitespace) (or "//" "/*") (* any) eol))))
1719+
nil t)
1720+
(font-lock-apply-highlight '(1 font-lock-variable-name-face t))
1721+
1722+
;; If ender is a ":" it means that the currently matched
1723+
;; variable also has a type signature.
1724+
(let ((ender (match-string 2)))
1725+
;; We need to skip the type specification. The regexp
1726+
;; basically either searches for the next thing which we
1727+
;; believe is a parameter or the end of the argument list.
1728+
(when (equal ender ":")
1729+
(let ((perform-match t))
1730+
(while (and perform-match
1731+
(re-search-forward
1732+
(rx (or
1733+
;; variable without type at the end
1734+
;; of line
1735+
(and "," eol)
1736+
;; next thing is a functional
1737+
;; argument, such as f:(x) => void
1738+
(and "(")
1739+
;; closing of a function type argument.
1740+
;; here, the type of `f'.
1741+
;; (f: (x: number) => foo): void => { }
1742+
(and ")" (? (* whitespace) "=>" (* whitespace)))
1743+
(and ","
1744+
(* whitespace)
1745+
(regexp "[a-zA-Z_$]\\(?:\\s_\\|\\sw\\)*")
1746+
;; optional ? to mark the
1747+
;; argument optional
1748+
(? "?")
1749+
(group (or ":" ")")))))
1750+
nil t))
1751+
;; In case the skipped type was the end of a
1752+
;; function type argument, the next token is the
1753+
;; return type of the inner function, so we need to
1754+
;; match but not fontify the next "name" (which
1755+
;; really is the type).
1756+
(if (string-match-p "=>" (match-string 0))
1757+
(setq perform-match t)
1758+
(goto-char (match-beginning 0))
1759+
(setq perform-match nil))))))))
1760+
;; conditions to handle
1761+
(scan-error nil)
1762+
(end-of-buffer nil))
1763+
nil)
1764+
16891765
(defun typescript--in-documentation-comment-p ()
16901766
"Reports whether point is inside a documentation comment."
16911767
(let ((parse (syntax-ppss)))
@@ -1855,16 +1931,48 @@ and searches for the next token to be highlighted."
18551931
(concat "\\_<instanceof\\_>\\s-+\\(" typescript--dotted-name-re "\\)")
18561932
(list 1 'font-lock-type-face))
18571933

1858-
;; formal parameters
1934+
;; formal parameters in "function" function call
1935+
;; function helloWorld(a: number, b: Promise<number>): void { }
18591936
,(list
18601937
(concat
18611938
"\\_<function\\_>\\(\\s-+" typescript--name-re "\\)?\\s-*\\(<.*>\\)?\\s-*(\\s-*"
18621939
"\\(?:$\\|" typescript--name-start-re "\\)")
1863-
`(,(concat "\\(" typescript--name-re "\\)\\(?:\\s-*?\\([,:)]\\|$\\)\\)")
1864-
(prog1 (save-excursion (re-search-forward ")" nil t))
1940+
`(typescript--function-argument-matcher
1941+
(prog1 (save-excursion (ignore-errors (up-list)) (point))
18651942
(backward-char))
18661943
nil
1867-
(1 font-lock-variable-name-face))))
1944+
nil))
1945+
1946+
;; formal parameters in arrow function
1947+
;; const helloWorld = (a: number, b: Promise<number>): void => { }
1948+
,(list
1949+
(rx (group "=>") (* whitespace) (? eol) (* whitespace) "{")
1950+
'(1 font-lock-keyword-face)
1951+
`(typescript--function-argument-matcher
1952+
(prog1 (progn
1953+
(backward-char)
1954+
(typescript--backward-to-parameter-list)
1955+
(point))
1956+
(backward-sexp))
1957+
(re-search-forward "{" nil t)
1958+
nil))
1959+
1960+
;; formal parameters in method definitions
1961+
;; class Foo { helloWorld(a: number, b: Promise<number>): void { } }
1962+
,(list
1963+
typescript--function-call-re
1964+
`(typescript--function-argument-matcher
1965+
(let ((point-orig (point))
1966+
(is-method-def
1967+
(ignore-errors
1968+
(up-list)
1969+
(looking-at-p
1970+
(rx (* (or whitespace ?\n)) (or ":" "{"))))))
1971+
(if is-method-def
1972+
(prog1 (point) (goto-char point-orig))
1973+
(point)))
1974+
nil
1975+
nil)))
18681976
"Level three font lock for `typescript-mode'.")
18691977

18701978
(defun typescript--flyspell-mode-predicate ()

0 commit comments

Comments
 (0)