[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/typescript-mode 39b7ba9e54 117/222: Distinguish type argum
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/typescript-mode 39b7ba9e54 117/222: Distinguish type arguments from lesser/greater than. (Fixes #81) |
Date: |
Sun, 6 Feb 2022 16:59:24 -0500 (EST) |
branch: elpa/typescript-mode
commit 39b7ba9e542db04022b22fc429396c15feb19c70
Author: Valentin Robert <vrobert@cs.ucsd.edu>
Commit: Louis-Dominique Dubeau <ldd@lddubeau.com>
Distinguish type arguments from lesser/greater than. (Fixes #81)
When semi-colons are not used, the indentation code would have trouble
figuring that the symbols `<` and `>` are type argument delimiters rather than
less-than, greater-than, in cases like this one:
type foo<bar> = ....;
This commit fixes that problem.
---
test-files/indentation-reference-document.ts | 129 +++++++++++++++++++++++++++
typescript-mode.el | 82 +++++++++++++++++
2 files changed, 211 insertions(+)
diff --git a/test-files/indentation-reference-document.ts
b/test-files/indentation-reference-document.ts
index b053b3c83b..9d490ac6ae 100644
--- a/test-files/indentation-reference-document.ts
+++ b/test-files/indentation-reference-document.ts
@@ -496,5 +496,134 @@ function blipblop(): void {
}
}
+// The following section deals with distinguishing the purpose of the symbol >
+// when it appears at the end of a line.
+// cf. https://github.com/ananthakumaran/typescript.el/issues/81
+{
+
+ var a, b, c, d, e, f, l, o, t, x, z
+ type z = {} // Zero argument
+ type o<A> = {} // One argument
+ type t<A,B> = {} // Two arguments
+
+ // greater-than operator
+ x = b >
+ c
+ // looks like a<b,c> but is greater-than operator
+ x = t < z , z >
+ f()
+ // looks almost the same but this time, it's a type
+ type x = t < z , z >
+ f()
+ // looks almost the same but this time, it's a type
+ x = a as t < z , z >
+ f()
+ // tricky, this is greater-than, because "number" is a keyword
+ a = b as number < z , z >
+ f()
+
+ // Next one is ambiguous! It could be read as:
+ // (b as t) < z, z > f()
+ // or
+ // b as (t < z , z >) \n f()
+ // It turns out that when t is not a keyword, TypeScript always chooses the
+ // latter, and complains if you attempted the former
+ a = b as t < z , z >
+ f()
+
+ l = [
+ // operator at end of line
+ a >
+ b,
+ // operator alone on line
+ a
+ >
+ b,
+ // end of 1st line is type argument, 2nd is operator
+ a as b < c , d >
+ >
+ d
+ ]
+
+ // properly-closed parameterized type, followed by operator
+ g = a as o < z > >
+ b
+
+ // Good case
+ class Q<X> {
+ q: string = "a"
+ }
+
+ type a<X> =
+ Q<X>
+ const blah = 1
+
+ // Problem cases
+ interface Something {
+ a: string;
+ b: string;
+ c: -5;
+ }
+
+ class Fluff<X extends Something> {
+ }
+
+ // Example of = and - in a type parameter.
+ type c<X extends Something = { a: string; b: string; c: -5; more: string }>
+ = Fluff<X>
+ const moo = 1
+
+ // Example of + in a type parameter.
+ type d<X extends Something = { +readonly [P in keyof Something]:
Something[P] }>
+ = Fluff<X>
+ const moo2 = 1
+
+ class Foo {
+ a : O<Z>
+ public readonly a : O<Z>
+ public b : O<Z>
+ private c : O<Z>
+ private d : O<Z>
+ }
+
+ type Foo {
+ readonly a : O<Z>
+ b : O<Z>
+ readonly b : O<Z>
+ c : { }
+ d : O<Z>
+ }
+
+ interface Foo {
+ a : O<Z>
+ b : { }
+ }
+
+ a = a ? a < a : a >
+ a
+
+ a = a ? a : a < a || a >
+ a
+
+ a = a ? a < a : a >
+ a
+ // ^ This test is the same as two above, but a bad guess could answer
differently.
+
+ type Foo { }
+ a = a ? a < a : a >
+ a
+
+ class Foo { }
+ a = a ? a < a : a >
+ a
+
+ type A = B<import('../file').T>
+ foo
+
+ type A = import('../file').B<import('../file').C>
+ foo
+
+}
+
container.each(x => x)
something() // No reason for this to be indented! (cf. issue #83)
diff --git a/typescript-mode.el b/typescript-mode.el
index 818addd9c3..1718419df5 100644
--- a/typescript-mode.el
+++ b/typescript-mode.el
@@ -2030,6 +2030,52 @@ This performs fontification according to
`typescript--class-styles'."
"\\(?:NaN\\|-?\\(?:0[Bb][01]+\\|0[Oo][0-7]+\\|0[Xx][0-9a-fA-F]+\\|Infinity\\|\\(?:[[:digit:]]*\\.[[:digit:]]+\\|[[:digit:]]+\\)\\(?:[Ee][+-]?[[:digit:]]+\\)?\\)\\)"
"Regexp that matches number literals.")
+(defconst typescript--reserved-start-keywords-re
+ (typescript--regexp-opt-symbol '("const" "export" "function" "let" "var"))
+ "These keywords cannot be variable or type names and start a new sentence.
+Note that the \"import\" keyword can be a type import since TS2.9, so it might
+not start a sentence!")
+
+(defconst typescript--type-vs-ternary-re
+ (concat "[?]\\|" (typescript--regexp-opt-symbol '("as" "class" "interface"
"private" "public" "readonly")))
+ "Keywords/Symbols that help tell apart colon for types vs ternary
operators.")
+
+(defun typescript--search-backward-matching-angle-bracket-inner (depth)
+ "Auxiliary function for `typescript--search-backward-matching-angle-bracket'.
+DEPTH indicates how nested we think we are: it increases when we cross closing
+brackets, and decreases when we cross opening brackets."
+ ;; We look backwards for a "<" that would correspond to the ">" we started
+ ;; from. However, there is no guarantee that it exists, since our ">" could
+ ;; be a greater-than operation. Some symbols will make it clear that we are
+ ;; *not* in a type annotation, so we can return nil. Otherwise, we keep
+ ;; looking for the matching one.
+ (or (<= depth 0)
+ (and
+ ;; If we cross over a reserved start keyword, we abandon hope of finding
+ ;; a matching angle bracket. This prevents extreme recursion depths.
+ (typescript--re-search-backward (concat "[<>]\\|"
typescript--reserved-start-keywords-re) nil t)
+ (case (char-after)
+ (?< (typescript--search-backward-matching-angle-bracket-inner (-
depth 1)))
+ (?> (typescript--search-backward-matching-angle-bracket-inner (+
depth 1)))))))
+
+(defun typescript--search-backward-matching-angle-bracket ()
+ "Search for matching \"<\" preceding a starting \">\".
+DEPTH indicates how nested we think we are. Assumes the starting position is
+right before the closing \">\". Returns nil when a match was not found,
+otherwise returns t and the current position is right before the matching
+\"<\"."
+ (typescript--search-backward-matching-angle-bracket-inner 1))
+
+(defun typescript--re-search-backward-ignoring-angle-brackets ()
+ "Search backwards, jumping over text within angle brackets.
+Searches specifically for any of \"=\", \"}\", and \"type\"."
+ (and
+ (typescript--re-search-backward "[>=}]\\|\\_<type\\_>" nil t)
+ (or (not (looking-at ">"))
+ (and
+ (typescript--search-backward-matching-angle-bracket)
+ (typescript--re-search-backward-ignoring-angle-brackets)))))
+
(defun typescript--looking-at-operator-p ()
"Return non-nil if point is on a typescript operator, other than a comma."
(save-match-data
@@ -2055,6 +2101,42 @@ This performs fontification according to
`typescript--class-styles'."
(save-excursion
(typescript--backward-syntactic-ws)
(memq (char-before) '(?, ?{ ?} ?\;)))))
+ ;; Do not identify the symbol > if it is likely part of a type
argument
+ ;; T<A>, but identify it if it is likely a greater-than symbol. This
is
+ ;; a hard problem in the absence of semicolons, see:
+ ;; https://github.com/ananthakumaran/typescript.el/issues/81
+ (not (and
+ (looking-at ">")
+ (save-excursion
+ (and
+ (typescript--search-backward-matching-angle-bracket)
+ ;; If we made it here, we found a candidate matching opening
+ ;; angle bracket. We still need to guess whether it actually
+ ;; is one, and not a spurious less-than operator!
+
+ ;; Look backwards for the first of:
+ ;; - one of the symbols: = :
+ ;; - or a TypeScript keyword
+ ;; Depending on what comes first, we can make an educated
+ ;; guess on the nature of our ">" of interest.
+ (typescript--re-search-backward (concat "[=:]\\|"
typescript--keyword-re) nil t)
+ (or
+ ;; If the previous keyword is "as", definitely a type.
+ (looking-at "\\_<as\\_>")
+ ;; Same goes for type imports.
+ (looking-at "\\_<import\\_>")
+ ;; A colon could be either a type symbol, or a ternary
+ ;; operator, try to guess which.
+ (and (looking-at ":")
+ (typescript--re-search-backward
typescript--type-vs-ternary-re nil t)
+ (not (looking-at "?")))
+ ;; This final check lets us distinguish between a
+ ;; 2-argument type "t < a , b > ..." and a use of the ","
+ ;; operator between two comparisons "t < a , b > ...".
+ ;; Looking back a little more lets us guess.
+ (and (looking-at "=")
+
(typescript--re-search-backward-ignoring-angle-brackets)
+ (looking-at "\\_<type\\_>")))))))
(not (and
(looking-at "*")
;; Generator method (possibly using computed property).
- [nongnu] elpa/typescript-mode 393ac6db0f 081/222: add tests for regexp font locking, (continued)
- [nongnu] elpa/typescript-mode 393ac6db0f 081/222: add tests for regexp font locking, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 361564c4a9 057/222: typescript-current-column: Dont cause side-effects., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode d155a4b623 063/222: Revert indent of complex parameters., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode e0b4a6cb31 055/222: Merge pull request #36 from lddubeau/feature/improved-tslint-regex, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode bce2e73cf2 107/222: Remove unused dependencies, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 7172a23d86 072/222: Add a custom predicate for flyspell-prog-mode., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode d8501770f6 103/222: Fix indentation of multiple curly braces in sequence., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode a09e9c78f9 108/222: Highlight class names., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 7c6fd0f4a8 115/222: Document how to handle ANSI codes in error messages., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 9796425fdb 113/222: Add support for new type "unknown"., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 39b7ba9e54 117/222: Distinguish type arguments from lesser/greater than. (Fixes #81),
ELPA Syncer <=
- [nongnu] elpa/typescript-mode 30f8b8feb4 122/222: Merge pull request #87 from Ailrun/add-jsdoc-tags, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 29282a32b7 124/222: add never type, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 8910e12525 129/222: Add autoload cookie with safe-local-var for typescript-indent-level, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode fbaad515c9 132/222: Merge pull request #89 from Ailrun/update-jsdoc-tests, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 32146510b8 147/222: Pin our dist to Trusty., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 706f938aef 155/222: force generic regex to include symbol before it (#119), ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode e38492f1cd 179/222: Extend Type-name to support namespace-prefixes., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode 7040961735 166/222: Highlight type-names in type-casts too., ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode de1a696c3c 174/222: Ignore errors from backward-list, ELPA Syncer, 2022/02/06
- [nongnu] elpa/typescript-mode a7cd7a92a5 167/222: fix(font-lock): prefer fontifying constants over keywords, ELPA Syncer, 2022/02/06