emacs-elpa-diffs
[Top][All Lists]
Advanced

[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).



reply via email to

[Prev in Thread] Current Thread [Next in Thread]