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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/rust-mode c9c7871 179/486: Merge pull request #79 from Mic


From: ELPA Syncer
Subject: [nongnu] elpa/rust-mode c9c7871 179/486: Merge pull request #79 from MicahChalmer/angle-bracket-madness
Date: Sat, 7 Aug 2021 09:25:14 -0400 (EDT)

branch: elpa/rust-mode
commit c9c7871f2fc1dde03d8f1d99cfff223fee902377
Merge: f1b6007 07943f0
Author: Niko Matsakis <niko@alum.mit.edu>
Commit: Niko Matsakis <niko@alum.mit.edu>

    Merge pull request #79 from MicahChalmer/angle-bracket-madness
    
    Match angle brackets syntactically
---
 rust-mode-tests.el | 752 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 rust-mode.el       | 721 +++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 1298 insertions(+), 175 deletions(-)

diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 58fcaaf..4a6cf20 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -362,6 +362,29 @@ use foo::bar::baz;
 fn foo() { }
 "))
 
+(ert-deftest font-lock-multi-raw-strings-in-a-row ()
+  (rust-test-font-lock
+   "
+r\"foo\\\", \"bar\", r\"bar\";
+r\"foo\\.\", \"bar\", r\"bar\";
+r\"foo\\..\", \"bar\", r\"foo\\..\\bar\";
+r\"\\\", \"foo\", r\"\\foo\";
+not_a_string();
+
+"
+
+   (apply 'append (mapcar (lambda (s) (list s 'font-lock-string-face))
+                          '("r\"foo\\\"" "\"bar\"" "r\"bar\""
+                            "r\"foo\\.\"" "\"bar\"" "r\"bar\""
+                            "r\"foo\\..\"" "\"bar\"" "r\"foo\\..\\bar\""
+                            "r\"\\\"" "\"foo\"" "r\"\\foo\"")))
+   ))
+
+(ert-deftest font-lock-raw-string-after-normal-string-ending-in-r ()
+  (rust-test-font-lock
+   "\"bar\" r\"foo\""
+   '("\"bar\"" font-lock-string-face "r\"foo\"" font-lock-string-face)))
+
 (ert-deftest indent-params-no-align ()
   (test-indent
    "
@@ -687,6 +710,7 @@ INIT-POS, FINAL-POS are position symbols found in 
`rust-test-positions-alist'."
   (with-temp-buffer
     (rust-mode)
     (insert source-code)
+    (font-lock-fontify-buffer)
     (goto-char (rust-get-buffer-pos init-pos))
     (apply manip-func args)
     (should (equal (point) (rust-get-buffer-pos final-pos)))))
@@ -700,6 +724,7 @@ All positions are position symbols found in 
`rust-test-positions-alist'."
   (with-temp-buffer
     (rust-mode)
     (insert source-code)
+    (font-lock-fontify-buffer)
     (goto-char (rust-get-buffer-pos init-pos))
     (apply manip-func args)
     (should (equal (list (region-beginning) (region-end))
@@ -1280,7 +1305,7 @@ fn indented_already() {
     \n    // The previous line already has its spaces
 }
 ")
-
+    (font-lock-fontify-buffer)
     (goto-line 11)
     (move-to-column 0)
     (indent-for-tab-command)
@@ -1459,3 +1484,728 @@ la la\");
   (test-indent
    ;; Needs to leave 1 space before "world"
    "\"hello \\\n world\""))
+
+(ert-deftest indent-multi-line-type-param-list ()
+  (test-indent
+   "
+pub fn foo<T,
+           V>() {
+    hello();
+}"))
+
+(defun rust-test-matching-parens (content pairs &optional nonparen-positions)
+  "Assert that in rust-mode, given a buffer with the given `content',
+  emacs's paren matching will find all of the pairs of positions
+  as matching braces.  The list of nonparen-positions asserts
+  specific positions that should NOT be considered to be
+  parens/braces of any kind.
+
+  This does not assert that the `pairs' list is
+  comprehensive--there can be additional pairs that don't appear
+  in the list and the test still passes (as long as none of their
+  positions appear in `nonparen-positions'.)"
+  (with-temp-buffer
+    (rust-mode)
+    (insert content)
+    (font-lock-fontify-buffer)
+    (dolist (pair pairs)
+      (let* ((open-pos (nth 0 pair))
+             (close-pos (nth 1 pair)))
+        (should (equal 4 (syntax-class (syntax-after open-pos))))
+        (should (equal 5 (syntax-class (syntax-after close-pos))))
+        (should (equal (scan-sexps open-pos 1) (+ 1 close-pos)))
+        (should (equal (scan-sexps (+ 1 close-pos) -1) open-pos))))
+    (dolist (nonpar-pos nonparen-positions)
+      (let ((nonpar-syntax-class (syntax-class (syntax-after nonpar-pos))))
+        (should (not (equal 4 nonpar-syntax-class)))
+        (should (not (equal 5 nonpar-syntax-class)))))))
+
+(ert-deftest rust-test-unmatched-single-quote-in-comment-paren-matching ()
+  ;; This was a bug from the char quote handling that affected the paren
+  ;; matching.  An unmatched quote char in a comment caused the problems.
+  (rust-test-matching-parens
+   "// If this appeared first in the file...
+\"\\
+{\";
+
+// And the { was not the on the first column:
+ {
+    // This then messed up the paren matching: '\\'
+}
+
+"
+   '((97 150) ;; The { and } at the bottom
+     )))
+
+(ert-deftest rust-test-two-character-quotes-in-a-row ()
+  (with-temp-buffer
+    (rust-mode)
+    (font-lock-fontify-buffer)
+    (insert "'\\n','a', fn")
+    (font-lock-after-change-function 1 12 0)
+
+    (should (equal 'font-lock-string-face (get-text-property 3 'face)))
+    (should (equal nil (get-text-property 5 'face)))
+    (should (equal 'font-lock-string-face (get-text-property 7 'face)))
+    (should (equal nil (get-text-property 9 'face)))
+    (should (equal 'font-lock-keyword-face (get-text-property 12 'face)))
+    )  
+  )
+
+(ert-deftest single-quote-null-char ()
+  (rust-test-font-lock
+   "'\\0' 'a' fn"
+   '("'\\0'" font-lock-string-face
+     "'a'" font-lock-string-face
+     "fn" font-lock-keyword-face)))
+
+(ert-deftest r-in-string-after-single-quoted-double-quote ()
+  (rust-test-font-lock
+   "'\"';\n\"r\";\n\"oops\";"
+   '("'\"'" font-lock-string-face
+     "\"r\"" font-lock-string-face
+     "\"oops\"" font-lock-string-face
+     )))
+
+(ert-deftest char-literal-after-quote-in-raw-string ()
+  (rust-test-font-lock
+   "r#\"\"\"#;\n'q'"
+   '("r#\"\"\"#" font-lock-string-face
+     "'q'" font-lock-string-face)))
+
+(ert-deftest rust-test-basic-paren-matching ()
+  (rust-test-matching-parens
+   "
+fn foo() {
+    let a = [1, 2, 3];
+}"
+   '((8 9) ;; Parens of foo()
+     (11 36) ;; Curly braces
+     (25 33) ;; Square brackets
+   )))
+
+(ert-deftest rust-test-paren-matching-generic-fn ()
+  (rust-test-matching-parens
+   "
+fn foo<A>() {
+}"
+   '((8 10) ;; Angle brackets <A>
+     (11 12) ;; Parens
+     (14 16) ;; Curly braces
+     )))
+
+(ert-deftest rust-test-paren-matching-generic-fn-with-return-value ()
+  (rust-test-matching-parens
+   "
+fn foo<A>() -> bool {
+    false
+}"
+   '((8 10) ;; Angle brackets <A>
+     (11 12) ;; Parens
+     (22 34) ;; Curly braces
+     )
+   
+   '(15 ;; The ">" in "->" is not an angle bracket
+     )))
+
+(ert-deftest rust-test-paren-matching-match-stmt ()
+  (rust-test-matching-parens
+   "
+fn foo() {
+    something_str(match <Type as Trait>::method() {
+        Some(_) => \"Got some\",
+        None => \"Nada\"
+    });
+}"
+   '((8 9) ;; parens of fn foo
+     (11 127) ;; curly braces of foo
+     (30 124) ;; parens of something_str
+     (37 51) ;; angle brackets of <Type as Trait>
+     (60 61) ;; parens of method()
+     (63 123) ;; curly braces of match
+     (77 79) ;; parens of Some(_)
+     )
+   
+   '(82 ;; > in first =>
+     112 ;; > in second =>
+     )))
+
+(ert-deftest rust-test-paren-matching-bitshift-operators ()
+  (rust-test-matching-parens
+  "
+fn foo(z:i32) {
+    let a:Option<Result<i32,i32>> = Some(Ok(4 >> 1));
+    let b = a.map(|x| x.map(|y| y << 3));
+    let trick_question = z<<<Type as Trait>::method();  // First two <s are 
not brackets, third is
+}"
+    '((34 50) ;; angle brackets of Option
+      (41 49) ;; angle brackets of Result
+      (142 156) ;; angle brackets of <Type as Trait>
+      )
+    '(64 ;; The >> inside Some(Ok()) are not angle brackets
+      65 ;; The >> inside Some(Ok()) are not angle brackets
+      106 ;; The << inside map() are not angle brackets
+      107 ;; The << inside map() are not angle brackets
+      140 ;; The << before <Type as Trait> are not angle brackets
+      141 ;; The << before <Type as Trait> are not angle brackets
+      183 ;; The < inside the comment
+      )))
+
+(ert-deftest rust-test-paren-matching-angle-bracket-after-colon-ident ()
+  (rust-test-matching-parens
+   "
+struct Bla<T> {
+    a:Option<(i32,Option<bool>)>,
+    b:Option<T>,
+    c:bool
+}
+
+fn f(x:i32,y:Option<i32>) {
+    let z:Option<i32> = None;
+    let b:Bla<i8> = Bla{
+        a:None,
+        b:None,
+        c:x<y.unwrap();
+    }
+}"
+   '((12 14) ;; Angle brackets of Bla<T>
+     (30 49) ;; Outer angle brackets of a:Option<...>
+     (42 47) ;; Inner angle brackets of Option<bool>
+     (64 66) ;; Angle brackets of Option<T>
+     (102 106) ;; Angle brackets of y:Option<i32>
+     (127 131) ;; Angle brackets of z:Option<i32>
+     (154 157) ;; Angle brackets of b:Bla<i8>
+     )
+   '(209 ;; less than operator in c:x<y.unwrap...
+     )))
+
+(ert-deftest rust-test-paren-matching-struct-literals ()
+  (rust-test-matching-parens
+   "
+fn foo(x:i32) -> Bar {
+    Bar {
+        b:x<3
+    }
+}"
+  '()
+  '(17 ;; the -> is not a brace
+    46 ;; x<3 the < is a less than sign
+    ))
+  )
+
+(ert-deftest rust-test-paren-matching-nested-struct-literals ()
+  (rust-test-matching-parens
+   "
+fn f(x:i32,y:i32) -> Foo<Bar> {
+    Foo{
+        bar:Bar{
+            a:3,
+            b:x<y
+        }
+    }
+}
+"
+   '((26 30)) ;; Angle brackets of Foo<Bar>
+   )
+  '(92 ;; less than operator x<y
+    ))
+
+(ert-deftest rust-test-paren-matching-fn-types-in-type-params ()
+  (rust-test-matching-parens
+   "
+fn foo<T:Fn() -> X<Y>>() -> Z {
+}
+"
+   '((8 23) ;; The angle brackets of foo<T...>
+     (20 22 ;; The angle brackets of X<Y>
+         ))
+   '(17 ;; The first ->
+     28 ;; The second ->
+     )
+   ))
+
+(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params ()
+  (rust-test-matching-parens
+   "
+fn foo(x:i32) {
+    f(x < 3);
+}
+"
+   '()
+   '(26 ;; The < inside f is a less than operator
+     )
+   ))
+
+(ert-deftest rust-test-paren-matching-lt-ops-in-fn-params ()
+  (rust-test-matching-parens
+   "
+fn foo(x:i32) -> bool {
+    return x < 3;
+}
+"
+   '()
+   '(17 ;; The ->
+     39 ;; The < after return is a less than operator
+     )
+   ))
+
+(ert-deftest rust-test-type-paren-matching-angle-brackets-in-type-items ()
+  (rust-test-matching-parens
+   "
+type Foo = Blah<Z,Y>;
+type Bar<X> = (Foo, Bletch<X>);
+type ThisThing<Z,A,D,F> = HereYouGo<Z,Y<Fn(A) -> B<C<D>>,E<F>>>;"
+   '((17 21) ;; Angle brackets of Blah<Z,Y>
+     (32 34) ;; Angle brackets of Bar<X>
+     (50 52) ;; Angle brackets of Bletch<X>
+     (70 78) ;; Angle brackets of ThisThing<Z,A,D,F>
+     (91 118) ;; Angle brackets of HereYouGo<...>
+     (95 117) ;; Angle brackets of Y<Fn...>
+     (106 111) ;; Angle brackets of B<C<D>>
+     (108 110) ;; Angle brackets of C<D>
+     (114 116) ;; Angle brackets of E<F>
+     )))
+
+(ert-deftest rust-test-paren-matching-tuple-like-struct ()
+  (rust-test-matching-parens
+   "
+struct A(Option<B>);
+struct C<Q>(Result<Q,i32>);"
+   '((17 19) ;; The angle brackets <B>
+     (10 20) ;; The parens of A();
+     (31 33) ;; The angle brackets of C<Q>
+     (41 47) ;; The angle brackets of Result<Q,i32>
+     )
+   '()))
+
+(ert-deftest rust-test-paren-matching-in-enum ()
+  (rust-test-matching-parens
+   "
+enum Boo<A> {
+    TupleLike(Option<A>),
+    StructLike{foo: Result<A,i32>}
+}"
+   '((10 12) ;; Angle brackets of Boo<A>
+     (36 38) ;; Angle brackets of Option<A>
+     (68 74) ;; Angle brackets of Result<A,i32>
+     )))
+
+(ert-deftest rust-test-paren-matching-assoc-type-bounds ()
+  (rust-test-matching-parens
+   "impl <A:B<AssocType = C<A> >> Thing<A> {}"
+   '((6 29) ;; Outer angle brackets of impl
+     (10 28) ;; Outer angle brackets of B<AssocType = C<A>>
+     (24 26) ;; Inner angle brackets of C<A>
+     (36 38) ;; Angle brackets of Thing<A>
+     )
+   ))
+
+(ert-deftest rust-test-paren-matching-plus-signs-in-expressions-and-bounds ()
+  ;; Note that as I write this, the function "bluh" below does not compile, but
+  ;; it warns that the equality constraint in a where clause is "not yet
+  ;; supported."  It seems that the compiler will support this eventually, so
+  ;; the emacs mode needs to support it.
+  (rust-test-matching-parens
+   "fn foo<A:Trait1+Trait2<i32>,B>(a:A,b:B) -> bool where 
B:Trait3<Foo>+Trait4<Bar> {
+    2 + a < 3 && 3 + b > 11
+}
+
+fn bluh<A>() where A:Fn()+MyTrait<i32>, MyTrait<A>::AssocType = Option<bool> {
+}
+
+fn fluh<C>() where C:Fn(i32) -> (i32, i32) + SomeTrait<i32>, C::AssocType = 
OtherThing<bool> {
+}"
+   '((7 30) ;; Angle brackets of foo<...>
+     (23 27) ;; Angle brackets of Trait2<i32>
+     (63 67) ;; Angle brackets of Trait3<Foo>
+     (75 79) ;; Angle brackets of Trait4<Bar>
+
+     (121 123) ;; Angle brackets of bluh<A>
+     (147 151) ;; Angle brackets of MyTrait<i32>
+
+     (161 163) ;; Angle brackets of MyTrait<A>
+     (184 189) ;; Angle brackets of Option<bool>
+
+     (203 205) ;; Angle brackets of <C>
+     (250 254) ;; Angle brackets of SomeTrait<i32>
+     (282 287) ;; Angle brackets of Option<bool>
+     )
+   '(93 ;; Less-than sign of a < 3
+     106 ;; Greater than sign of b > 11
+     )))
+
+(ert-deftest rust-test-paren-matching-generic-type-in-tuple-return-type ()
+  (rust-test-matching-parens
+   "pub fn take(mut self) -> (EmptyBucket<K, V, M>, K, V) {}"
+   '((38 46))
+   ))
+
+(ert-deftest rust-test-paren-matching-references-and-logical-and ()
+  (rust-test-matching-parens
+   "
+fn ampersand_check(a: &Option<i32>, b:bool) -> &Option<u32> {
+    a.map(|x| {
+        b && x < 32
+    })
+}"
+   '((31 35) ;; Option<i32>
+     (56 60) ;; Option<u32>
+     )
+   '(95 ;; x < 32
+     )
+   )
+  )
+
+(ert-deftest rust-test-paren-matching-lt-sign-in-if-statement ()
+  (rust-test-matching-parens
+   "
+fn if_check(a:i32,b:i32,c:i32) {
+    if a + b < c {
+        
+    }
+    if a < b {
+        
+    }
+    if (c < a) {
+        
+    }
+}
+
+fn while_check(x:i32,y:i32) -> bool {
+    while x < y {
+    }
+    for x in y < x {
+    }
+    match y < z {
+        true => (), _ => ()
+    }
+    return z < y;
+}"
+   '()
+   '(48 ;; b < c
+     78 ;; a < b
+     109 ;; (c < a)
+
+     184 ;; x < y
+     211 ;; y < x
+     235 ;; y < z
+     288 ;; z < y
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-expr-with-field ()
+  (rust-test-matching-parens
+   "fn foo() { x.y < 3 }"
+   '()
+   '(16 ;; x.y < 3
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-expr-with-quote ()
+  (rust-test-matching-parens
+   "
+fn quote_check() {
+    'x' < y;
+     \"y\" < x;
+    r##\"z\"## < q;
+    a <= 3 && b < '2'
+}"
+   '()
+   '(29 ;; 'x' < y
+     42 ;; "y" < x
+     60 ;; r##"z"## < q
+     71 ;; a <= '3'
+     81 ;; b < '2'
+     )))
+
+(ert-deftest rust-test-paren-matching-keywords-capitalized-are-ok-type-names ()
+  (rust-test-matching-parens
+   "
+fn foo() -> Box<i32> {
+    let z:If<bool> = If(a < 3);
+}"
+   '((17 21) ;; Box<i32>
+     (37 42) ;; If<bool>
+     )
+   '(51 ;; If(a < 3)
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-expression-inside-macro ()
+  (rust-test-matching-parens
+   "fn bla() { assert!(x < y); }"
+   '()
+   '(22 ;; x < y
+     )))
+
+(ert-deftest rust-test-paren-matching-array-types-with-generics ()
+  (rust-test-matching-parens
+   "fn boo () -> [Option<i32>] {}"
+   '((21 25))))
+
+(ert-deftest rust-test-paren-matching-angle-bracket-inner-reference ()
+  (rust-test-matching-parens
+   "fn x() -> Option<&Node<T>> {}"
+   '((17 26) ;; Option
+     (23 25) ;; Node
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-operator-after-semicolon ()
+  (rust-test-matching-parens
+   "fn f(x:i32) -> bool { (); x < 3 }"
+   '()
+   '(29
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-operator-after-comma ()
+  (rust-test-matching-parens
+   "fn foo() {
+    (e, a < b)
+}"
+   '((16 25) ;; The parens ()
+     )
+   '(22 ;; The < operator
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-operator-after-let ()
+  (rust-test-matching-parens
+   "fn main() {
+    let x = a < b;
+}"
+   '((11 32) ;; The { }
+     )
+   '(27 ;; The < operator
+     )))
+
+(ert-deftest rust-test-paren-matching-two-lt-ops-in-a-row ()
+  (rust-test-matching-parens
+   "fn next(&mut self) -> Option<<I as Iterator>::Item>"
+   '((29 51) ;; Outer Option<>
+     (30 44) ;; Inner <I as Iterator>
+     )
+   '(21
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-after-caret ()
+  (rust-test-matching-parens
+   "fn foo() { x^2 < 3 }"
+   '((10 20) ;; The { }
+     )
+   '(16 ;; The < operator
+     )))
+
+(ert-deftest rust-test-paren-matching-lt-operator-after-special-type ()
+  (rust-test-matching-parens
+   "fn foo() { low as uint <= c }"
+   '((10 29))
+   '(24)))
+
+(ert-deftest rust-test-paren-matching-lt-operator-after-closing-curly-brace ()
+  (rust-test-matching-parens
+   "fn main() { if true {} a < 3 }"
+   '((11 30)
+     )
+   '(26)))
+
+(ert-deftest rust-test-paren-matching-const ()
+  (rust-test-matching-parens
+   "
+const BLA = 1 << 3;
+const BLUB = 2 < 4;"
+   '()
+   '(16
+     17 ;; Both chars of the << in 1 << 3
+     37 ;; The < in 2 < 4
+     )))
+
+(ert-deftest rust-test-paren-matching-c-like-enum ()
+  (rust-test-matching-parens
+   "
+enum CLikeEnum {
+    Two = 1 << 1,
+    Four = 1 << 2 
+}"
+   '((17 56 ;; The { } of the enum
+         ))
+   '(31
+     32 ;; The first <<
+     50
+     51 ;; The second <<
+     )))
+
+(ert-deftest rust-test-paren-matching-no-angle-brackets-in-macros ()
+  (rust-test-matching-parens
+   "
+fn foo<A>(a:A) {
+    macro_a!( foo::<ignore the bracets> );
+    macro_b![ foo as Option<B> ];
+}
+
+macro_c!{
+    struct Boo<D> {}
+}"
+   '((8 10))
+   ;; Inside macros, it should not find any angle brackets, even if it normally
+   ;; would
+   '(38 ;; macro_a <
+     57 ;; macro_a >
+     89 ;; macro_b <
+     91 ;; macro_b >
+     123 ;; macro_c <
+     125 ;; macro_d >
+     )))
+
+(ert-deftest rust-test-paren-matching-type-with-module-name ()
+  (rust-test-matching-parens
+   "
+const X: libc::c_int = 1 << 2;
+fn main() {
+    let z: libc::c_uint = 1 << 4;
+}
+"
+   '((43 79)) ;; The curly braces
+   '(27
+     28 ;; The first <<
+     73
+     74 ;; The second <<
+     )))
+
+(ert-deftest rust-test-paren-matching-qualififed-struct-literal ()
+  (rust-test-matching-parens
+   "
+fn foo() -> Fn(asd) -> F<V> {
+    let z = foo::Struct{ b: 1 << 4, c: 2 < 4  }
+}"
+   '((30 80) ;; Outer curly brackets
+     )
+   '(62
+     63 ;; The shift operator
+     73 ;; The less than operator
+     )))
+
+(ert-deftest rust-test-paren-matching-let-mut ()
+  (rust-test-matching-parens
+   "
+fn f() {
+    let mut b = 1 < 3;
+    let mut i = 1 << 3;
+}
+"
+   '()
+   '(28 ;; 1 < 3
+     51
+     52 ;; 1 << 3
+     )))
+
+(ert-deftest rust-test-paren-matching-as-ref-type ()
+  (rust-test-matching-parens
+   "fn f() {
+    let a = b as &Foo<Bar>;
+}"
+   '((31 35) ;; Angle brackets Foo<Bar>
+     )))
+
+(ert-deftest rust-test-paren-matching-type-ascription ()
+  (rust-test-matching-parens
+   "
+fn rfc803() {
+    let z = a < b:FunnkyThing<int>;
+    let s = Foo {
+        a: b < 3,
+        b: d:CrazyStuff<int> < 3,
+        c: 2 < x:CrazyStuff<uint>
+    }
+}"
+   '((45 49) ;; FunkyThing<int>
+     (111 115) ;; CrazyStuff<int>
+     (149 154) ;; CrazyStuff<uint>
+     )
+   '(30 ;; a < b
+     83 ;; b < 3
+     117 ;; d... < 3
+     135 ;; 2 < x
+     )))
+
+(ert-deftest 
rust-test-paren-matching-angle-brackets-in-enum-with-where-claause ()
+  (rust-test-matching-parens
+   "
+enum MyEnum<T> where T:std::fmt::Debug {
+    Thing(Option<T>)
+}"
+   '((13 15) ;; MyEnum<T>
+     (59 61) ;; Option<T>
+     )))
+
+(ert-deftest rust-test-paren-matching-where-clauses-with-closure-types ()
+  (rust-test-matching-parens
+   "
+enum Boo<'a,T> where T:Fn() -> Option<&'a str> + 'a {
+   Thingy(Option<&'a T>)
+}
+
+fn foo<'a>() -> Result<B,C> where C::X: D<A>, B:FnMut() -> Option<Q>+'a {
+    Foo(a < b)
+}
+
+type Foo<T> where T: Copy = Box<T>;
+"
+   '((10 15) ;; Boo<'a,T>
+     (39 47) ;; Option<&'a str>
+     (72 78) ;; Option<&'a T>
+
+     (106 110) ;; Result<B,C>
+     (125 127) ;; D<A>
+     (149 151) ;; Option<Q>
+     (184 186) ;; Foo<T>
+     (207 209) ;; Box<T>
+     )
+
+   '(168 ;; Foo(a < b)
+     )
+   ))
+
+(ert-deftest rust-test-angle-bracket-matching-turned-off ()
+  (let ((rust-match-angle-brackets nil))
+    (rust-test-matching-parens
+     "fn foo<a>() {}"
+     '((10 11))
+     '(7 9))))
+
+;; If electric-pair-mode is available, load it and run the tests that use it.  
If not,
+;; no error--the tests will be skipped.
+(require 'elec-pair nil t)
+
+;; The emacs 23 version of ERT does not have test skipping functionality.  So
+;; don't even define these tests if elec-pair is not available.
+(when (featurep 'elec-pair)
+  (defun test-electric-pair-insert (original point-pos char closer)
+    (let ((old-electric-pair-mode electric-pair-mode))
+      (electric-pair-mode 1)
+      (unwind-protect
+          (with-temp-buffer
+            (rust-mode)
+            (insert original)
+            (font-lock-fontify-buffer)
+
+            (goto-char point-pos)
+            (deactivate-mark)
+            (let ((last-command-event char)) (self-insert-command 1))
+            (should (equal (char-after)
+                           (or closer (aref original point-pos)))))
+        (electric-pair-mode (or old-electric-pair-mode 1)))))
+
+  (ert-deftest rust-test-electric-pair-generic-fn ()
+    (test-electric-pair-insert "fn foo() { }" 7 ?< ?>))
+
+  (ert-deftest rust-test-electric-pair-impl-param ()
+    (test-electric-pair-insert "impl Foo<T> for Bar<T>" 5 ?< ?>))
+
+  (ert-deftest rust-test-electric-pair-impl-for-type-param ()
+    (test-electric-pair-insert "impl<T> Foo<T> for Bar" 22 ?< ?>))
+
+  (ert-deftest rust-test-electric-pair-lt-expression ()
+    (test-electric-pair-insert "fn foo(bar:i32) -> bool { bar  }" 30 ?< nil))
+
+  (ert-deftest rust-test-electric-pair-lt-expression-in-struct-literal ()
+    (test-electric-pair-insert "fn foo(x:i32) -> Bar { Bar { a:(bleh() + 
whatever::<X>()), b:x   }  }" 63 ?< nil))
+
+  (ert-deftest rust-test-electric-pair-lt-expression-capitalized-keyword ()
+    (test-electric-pair-insert "fn foo() -> Box" 16 ?< ?>))
+  )
diff --git a/rust-mode.el b/rust-mode.el
index 7fcc09e..d890299 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -20,6 +20,31 @@
       "Set variable VAR to value VAL in current buffer."
       (list 'set (list 'make-local-variable (list 'quote var)) val))))
 
+(defun rust-looking-back-str (str)
+  "Like `looking-back' but for fixed strings rather than regexps (so that it's 
not so slow)"
+  (let ((len (length str)))
+    (and (> (point) len)
+         (equal str (buffer-substring-no-properties (- (point) len) 
(point))))))
+
+(defun rust-looking-back-symbols (SYMS)
+  "Return non-nil if the point is just after a complete symbol that is a 
member of the list of strings SYMS"
+  (save-excursion
+    (let* ((pt-orig (point))
+           (beg-of-symbol (progn (forward-thing 'symbol -1) (point)))
+           (end-of-symbol (progn (forward-thing 'symbol 1) (point))))
+      (and
+       (= end-of-symbol pt-orig)
+       (member (buffer-substring-no-properties beg-of-symbol pt-orig) SYMS)))))
+
+(defun rust-looking-back-ident ()
+  "Non-nil if we are looking backwards at a valid rust identifier"
+  (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point))))
+    (looking-back rust-re-ident beg-of-symbol)))
+
+(defun rust-looking-back-macro ()
+  "Non-nil if looking back at an ident followed by a !"
+  (save-excursion (backward-char) (and (= ?! (char-after)) 
(rust-looking-back-ident))))
+
 ;; Syntax definitions and helpers
 (defvar rust-mode-syntax-table
   (let ((table (make-syntax-table)))
@@ -32,6 +57,11 @@
     (modify-syntax-entry ?\" "\"" table)
     (modify-syntax-entry ?\\ "\\" table)
 
+    ;; Angle brackets.  We suppress this with syntactic fontification when
+    ;; needed
+    (modify-syntax-entry ?< "(>" table)
+    (modify-syntax-entry ?> ")<" table)
+
     ;; Comments
     (modify-syntax-entry ?/  ". 124b" table)
     (modify-syntax-entry ?*  ". 23"   table)
@@ -71,17 +101,33 @@
   :type 'string
   :group 'rust-mode)
 
+(defcustom rust-match-angle-brackets t
+  "Enable angle bracket matching.  Attempt to match `<' and `>' where
+  appropriate."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
 (defun rust-paren-level () (nth 0 (syntax-ppss)))
 (defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss)))
 (defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss))))
 (defun rust-rewind-irrelevant ()
   (let ((starting (point)))
     (skip-chars-backward "[:space:]\n")
-    (if (looking-back "\\*/" nil) (backward-char))
+    (if (rust-looking-back-str "*/") (backward-char))
     (if (rust-in-str-or-cmnt)
         (rust-rewind-past-str-cmnt))
     (if (/= starting (point))
         (rust-rewind-irrelevant))))
+(defun rust-in-macro ()
+  (save-excursion
+    (when (> (rust-paren-level) 0)
+      (backward-up-list)
+      (rust-rewind-irrelevant)
+      (or (rust-looking-back-macro)
+          (and (rust-looking-back-ident) (save-excursion (backward-sexp) 
(rust-rewind-irrelevant) (rust-looking-back-str "macro_rules!")))
+          (rust-in-macro))
+      )))
 
 (defun rust-align-to-expr-after-brace ()
   (save-excursion
@@ -141,13 +187,13 @@
           ;;
           ((skip-dot-identifier
             (lambda ()
-              (when (looking-back (concat "\\." rust-re-ident) nil)
+              (when (and (rust-looking-back-ident) (save-excursion 
(forward-thing 'symbol -1) (= ?. (char-before))))
                 (forward-thing 'symbol -1)
                 (backward-char)
                 (- (current-column) rust-indent-offset)))))
         (cond
          ;; foo.bar(...)
-         ((looking-back ")" nil)
+         ((rust-looking-back-str ")")
           (backward-list 1)
           (funcall skip-dot-identifier))
 
@@ -271,7 +317,7 @@
                           ;; ..or if the previous line ends with any of these:
                           ;;     { ? : ( , ; [ }
                           ;; then we are at the beginning of an expression, so 
stay on the baseline...
-                          (looking-back "[(,:;?[{}]\\|[^|]|" nil)
+                          (looking-back "[(,:;?[{}]\\|[^|]|" (- (point) 2))
                           ;; or if the previous line is the end of an 
attribute, stay at the baseline...
                           (progn 
(rust-rewind-to-beginning-of-current-level-expr) (looking-at "#")))))
                       baseline
@@ -321,6 +367,7 @@
     "str" "char"))
 
 (defconst rust-re-CamelCase "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*")
+(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]")
 (defun rust-re-word (inner) (concat "\\<" inner "\\>"))
 (defun rust-re-grab (inner) (concat "\\(" inner "\\)"))
 (defun rust-re-grabword (inner) (rust-re-grab (rust-re-word inner)))
@@ -374,111 +421,512 @@
              ("fn" . font-lock-function-name-face)
              ("static" . font-lock-constant-face)))))
 
-(defun rust-extend-region-raw-string ()
+(defun rust-font-lock-extend-region ()
   "Extend the region given by `font-lock-beg' and `font-lock-end'
-  to include the beginning of a string if it includes part of it.
-  Adjusts to include the r[#] of a raw string as well."
-  
-  (let* ((orig-beg font-lock-beg)
-         (orig-end font-lock-end)
-         (beg-ppss (syntax-ppss font-lock-beg))
-         (beg-in-str (nth 3 beg-ppss))
-         (end-ppss (syntax-ppss font-lock-end))
-         (end-in-str (nth 3 end-ppss)))
-    
-    (when (and beg-in-str (> font-lock-beg (nth 8 beg-ppss)))
-      (setq font-lock-beg str-beg)
-      (while (equal ?# (char-before font-lock-beg))
-        (setq font-lock-beg (1- font-lock-beg)))
-      (when (equal ?r (char-before font-lock-beg))
-        (setq font-lock-beg (1- font-lock-beg))))
-    
-    (when end-in-str
-      (save-excursion
-        (goto-char (nth 8 end-ppss))
-        (ignore-errors (forward-sexp))
-        (setq font-lock-end (max font-lock-end (point)))))
-    
-    ;; If we have the beginning of a raw string in the region, make sure we 
have the end of
-    ;; it.
-    (when (or beg-in-str end-in-str)
-      (save-excursion
-        (goto-char font-lock-beg)
-        (while (and (< (point) font-lock-end) (ignore-errors 
(rust-look-for-raw-string (buffer-end 1)))))
-        (setq font-lock-end (max font-lock-end (point)))))
+  to include the beginning of a string or comment if it includes
+  part of it.  Adjusts to include the r[#] of a raw string as
+  well."
+
+  (let ((orig-beg font-lock-beg)
+        (orig-end font-lock-end))
+    (cond
+     ;; If we are not syntactically fontified yet, we cannot correctly cover
+     ;; anything less than the full buffer. The syntactic fontification
+     ;; modifies the syntax, so until it's done we can't use the syntax to
+     ;; determine what to fontify.
+     ((< (or font-lock-syntactically-fontified 0) font-lock-end)
+      (setq font-lock-beg 1)
+      (setq font-lock-end (buffer-end 1)))
+     
+     ((let* ((beg-ppss (syntax-ppss font-lock-beg))
+             (beg-in-cmnt (and (nth 4 beg-ppss) (nth 8 beg-ppss)))
+             (beg-in-str (nth 3 beg-ppss))
+             (end-ppss (syntax-ppss font-lock-end))
+             (end-in-str (nth 3 end-ppss)))
+        
+        (when (and beg-in-str (> font-lock-beg (nth 8 beg-ppss)))
+          (setq font-lock-beg (nth 8 beg-ppss))
+          (while (equal ?# (char-before font-lock-beg))
+            (setq font-lock-beg (1- font-lock-beg)))
+          (when (equal ?r (char-before font-lock-beg))
+            (setq font-lock-beg (1- font-lock-beg))))
+
+        (when (and beg-in-cmnt (> font-lock-beg beg-in-cmnt))
+          (setq font-lock-beg beg-in-cmnt))
+        
+        (when end-in-str
+          (save-excursion
+            (goto-char (nth 8 end-ppss))
+            (ignore-errors (forward-sexp))
+            (setq font-lock-end (max font-lock-end (point)))))
+        
+        ;; If we have the beginning of a raw string in the region, make sure 
we have the end of
+        ;; it.
+        (when (or beg-in-str end-in-str)
+          (save-excursion
+            (goto-char font-lock-beg)
+            (while (and (< (point) font-lock-end) (ignore-errors 
(rust-look-for-raw-string (buffer-end 1)))))
+            (setq font-lock-end (max font-lock-end (point)))))
+        )))
 
     (or (/= font-lock-beg orig-beg)
         (/= font-lock-end orig-end))
     ))
 
-(defun rust-look-for-raw-string (bound)
-  ;; Find a raw string, but only if it's not in the middle of another string or
-  ;; a comment
-
-  (let* ((raw-str-regexp
-          (rx
-           (seq
-            ;; The "r" starts the raw string.  Capture it as group 1 to mark 
it as such syntactically:
-            (group "r")
-
-            ;; Then either:
-            (or
-             ;; a sequence at least one "#" (followed by quote).  Capture all
-             ;; but the last "#" as group 2 for this case.
-             (seq (group (* "#")) "#\"")
-
-             ;; ...or a quote without any "#".  Capture it as group 3. This is
-             ;; used later to match the opposite quote only if this capture
-             ;; occurred
-             (group "\""))
-
-            ;; The contents of the string:
-            (*? anything)
-
-            ;; If there are any backslashes at the end of the string, capture
-            ;; them as group 4 so we can suppress the normal escape syntax
-            ;; parsing:
-            (group (* "\\"))
-
-            ;; Then the end of the string--the backreferences ensure that we
-            ;; only match the kind of ending that corresponds to the beginning
-            ;; we had:
-            (or
-             ;; There were "#"s - capture the last one as group 5 to mark it as
-             ;; the end of the string:
-             (seq "\"" (backref 2) (group "#"))
-
-             ;; No "#"s - capture the ending quote (using a backref to group 3,
-             ;; so that we can't match a quote if we had "#"s) as group 6
-             (group (backref 3))))))
-         ;; If it matches, it ends up with the starting character of the string
-         ;; as group 1, any ending backslashes as group 4, and the ending
-         ;; character as either group 5 or group 6.
-
+(defun rust-conditional-re-search-forward (regexp bound condition)
+  ;; Search forward for regexp (with bound).  If found, call condition and 
return the found
+  ;; match only if it returns true.
+  (let* (found
+         found-ret-list
          (ret-list (save-excursion
-                    (let* ((match-end (re-search-forward raw-str-regexp bound 
t))
-                           (ret-list (and match-end (list match-end 
(match-beginning 0) (match-data) (point)))))
-                      (when (and ret-list
-                                 (save-excursion
-                                   (goto-char (nth 1 ret-list))
-                                   (not (rust-in-str-or-cmnt))))
-                        ret-list)))))
+                     (while (and (not found) (re-search-forward regexp bound 
t))
+                       (setq
+                        found-ret-list (list (point) (match-data))
+                        found (save-match-data (save-excursion (ignore-errors 
(funcall condition)))))
+                       ;; If the condition filters out a match, need to search
+                       ;; again just after its beginning.  This will allow
+                       ;; cases such as:
+                       ;;    "bar" r"foo"
+                       ;; where the filtered out search (r" r") should not
+                       ;; prevent finding another one that begins in the middle
+                       ;; of it (r"foo")
+                       (when (not found)
+                         (goto-char (1+ (match-beginning 0))))
+                       )
+                     (when found found-ret-list))))
     (when ret-list
-      (goto-char (nth 3 ret-list))
-      (set-match-data (nth 2 ret-list))
+      (goto-char (nth 0 ret-list))
+      (set-match-data (nth 1 ret-list))
       (nth 0 ret-list))))
 
+(defun rust-look-for-non-standard-string (bound)
+  ;; Find a raw string or character literal, but only if it's not in the middle
+  ;; of another string or a comment.
+
+  (let* ((non-standard-str-regexp
+          (rx
+           (or
+            ;; Raw string: if it matches, it ends up with the starting 
character
+            ;; of the string as group 1, any ending backslashes as group 4, and
+            ;; the ending character as either group 5 or group 6.
+            (seq
+             ;; The "r" starts the raw string.  Capture it as group 1 to mark 
it as such syntactically:
+             (group "r")
+
+             ;; Then either:
+             (or
+              ;; a sequence at least one "#" (followed by quote).  Capture all
+              ;; but the last "#" as group 2 for this case.
+              (seq (group (* "#")) "#\"")
+
+              ;; ...or a quote without any "#".  Capture it as group 3. This is
+              ;; used later to match the opposite quote only if this capture
+              ;; occurred
+              (group "\""))
+
+             ;; The contents of the string:
+             (*? anything)
+
+             ;; If there are any backslashes at the end of the string, capture
+             ;; them as group 4 so we can suppress the normal escape syntax
+             ;; parsing:
+             (group (* "\\"))
+
+             ;; Then the end of the string--the backreferences ensure that we
+             ;; only match the kind of ending that corresponds to the beginning
+             ;; we had:
+             (or
+              ;; There were "#"s - capture the last one as group 5 to mark it 
as
+              ;; the end of the string:
+              (seq "\"" (backref 2) (group "#"))
+
+              ;; No "#"s - capture the ending quote (using a backref to group 
3,
+              ;; so that we can't match a quote if we had "#"s) as group 6
+              (group (backref 3))))
+
+            ;; Character literal: match the beginning ' of a character literal
+            ;; as group 7, and the ending one as group 8
+            (seq
+             (group "'")
+             (or
+              (seq
+               "\\"
+               (or
+                (: "U" (= 8 xdigit))
+                (: "u" (= 4 xdigit))
+                (: "x" (= 2 xdigit))
+                (any "'nrt0\"\\")))
+              (not (any "'\\"))
+              )
+             (group "'"))
+            )
+           )))
+    (rust-conditional-re-search-forward
+     non-standard-str-regexp bound
+     (lambda ()
+       (let ((pstate (syntax-ppss (match-beginning 0))))
+         (not
+          (or
+           (nth 4 pstate) ;; Skip if in a comment
+           (and (nth 3 pstate) (wholenump (nth 8 pstate)) (< (nth 8 pstate) 
(match-beginning 0))) ;; Skip if in a string that isn't starting here
+           )))))))
+
+(defun rust-syntax-class-before-point ()
+  (when (> (point) 1)
+    (syntax-class (syntax-after (1- (point))))))
+
+(defun rust-rewind-qualified-ident ()
+  (while (rust-looking-back-ident)
+    (backward-sexp)
+    (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str 
"::"))
+      (rust-rewind-irrelevant)
+      (backward-char 2)
+      (rust-rewind-irrelevant))))
+
+(defun rust-rewind-type-param-list ()
+  (cond
+   ((and (rust-looking-back-str ">") (equal 5 
(rust-syntax-class-before-point)))
+    (backward-sexp)
+    (rust-rewind-irrelevant))
+
+   ;; We need to be able to back up past the Fn(args) -> RT form as well.  If
+   ;; we're looking back at this, we want to end up just after "Fn".
+   ((member (char-before) '(?\] ?\) ))
+    (let* ((is-paren (rust-looking-back-str ")"))
+           (dest (save-excursion
+                  (backward-sexp)
+                  (rust-rewind-irrelevant)
+                  (or
+                   (when (rust-looking-back-str "->")
+                     (backward-char 2)
+                     (rust-rewind-irrelevant)
+                     (when (rust-looking-back-str ")")
+                       (backward-sexp)
+                       (point)))
+                   (and is-paren (point))))))
+      (when dest
+        (goto-char dest))))))
+
+(defun rust-rewind-to-decl-name ()
+  "If we are before an ident that is part of a declaration that
+  can have a where clause, rewind back to just before the name of
+  the subject of that where clause and return the new point.
+  Otherwise return nil"
+  
+  (let* ((ident-pos (point))
+         (newpos (save-excursion
+                   (rust-rewind-irrelevant)
+                   (rust-rewind-type-param-list)
+                   (cond
+                       ((rust-looking-back-symbols '("fn" "trait" "enum" 
"struct" "impl" "type")) ident-pos)
+
+                       ((equal 5 (rust-syntax-class-before-point))
+                        (backward-sexp)
+                        (rust-rewind-to-decl-name))
+
+                       ((looking-back "[:,'+=]" (1- (point)))
+                        (backward-char)
+                        (rust-rewind-to-decl-name))
+
+                       ((rust-looking-back-str "->")
+                        (backward-char 2)
+                        (rust-rewind-to-decl-name))
+
+                       ((rust-looking-back-ident)
+                        (rust-rewind-qualified-ident)
+                        (rust-rewind-to-decl-name))))))
+    (when newpos (goto-char newpos))
+    newpos))
+
+(defun rust-is-in-expression-context (token)
+  "Return t if what comes right after the point is part of an
+  expression (as opposed to starting a type) by looking at what
+  comes before.  Takes a symbol that roughly indicates what is
+  after the point.
+
+  This function is used as part of `rust-is-lt-char-operator' as
+  part of angle bracket matching, and is not intended to be used
+  outside of this context."
+
+  (save-excursion
+    (let ((postchar (char-after)))
+      (rust-rewind-irrelevant)
+
+      ;; A type alias or ascription could have a type param list.  Skip 
backwards past it.
+      (when (member token '(ambiguous-operator open-brace))
+        (rust-rewind-type-param-list))
+      
+      (cond
+
+       ;; Certain keywords always introduce expressions
+       ((rust-looking-back-symbols '("if" "while" "match" "return" "box" 
"in")) t)
+
+       ;; "as" introduces a type
+       ((rust-looking-back-symbols '("as")) nil)
+
+       ;; An open angle bracket never introduces expression context WITHIN the 
angle brackets
+       ((and (equal token 'open-brace) (equal postchar ?<)) nil)
+
+       ;; An ident! followed by an open brace is a macro invocation.  Consider
+       ;; it to be an expression.
+       ((and (equal token 'open-brace) (rust-looking-back-macro)) t)
+       
+       ;; An identifier is right after an ending paren, bracket, angle bracket
+       ;; or curly brace.  It's a type if the last sexp was a type.
+       ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point)))
+        (backward-sexp)
+        (rust-is-in-expression-context 'open-brace))
+
+       ;; If a "for" appears without a ; or { before it, it's part of an
+       ;; "impl X for y", so the y is a type.  Otherwise it's
+       ;; introducing a loop, so the y is an expression
+       ((and (equal token 'ident) (rust-looking-back-symbols '("for")))
+        (backward-sexp)
+        (rust-rewind-irrelevant)
+        (looking-back "[{;]" (1- (point))))
+       
+       ((rust-looking-back-ident)
+        (rust-rewind-qualified-ident)
+        (rust-rewind-irrelevant)
+        (cond
+         ((equal token 'open-brace)
+          ;; We now know we have:
+          ;;   ident <maybe type params> [{([]
+          ;; where [{([] denotes either a {, ( or [.  This character is bound 
as postchar.
+          (cond
+           ;; If postchar is a paren or square bracket, then if the brace is a 
type if the identifier is one
+           ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 
'ident))
+
+           ;; If postchar is a curly brace, the brace can only be a type if
+           ;; ident2 is the name of an enum, struct or trait being declared.
+           ;; Note that if there is a -> before the ident then the ident would
+           ;; be a type but the { is not.
+           ((equal ?{ postchar)
+            (not (and (rust-rewind-to-decl-name)
+                      (progn
+                        (rust-rewind-irrelevant)
+                        (rust-looking-back-symbols '("enum" "struct" "trait" 
"type"))))))
+           ))
+         
+         ((equal token 'ambiguous-operator)
+          (cond
+           ;; An ampersand after an ident has to be an operator rather than a 
& at the beginning of a ref type
+           ((equal postchar ?&) t)
+
+           ;; A : followed by a type then an = introduces an expression 
(unless it is part of a where clause of a "type" declaration)
+           ((and (equal postchar ?=)
+                 (looking-back "[^:]:" (- (point) 2))
+                 (not (save-excursion (and (rust-rewind-to-decl-name) (progn 
(rust-rewind-irrelevant) (rust-looking-back-symbols '("type"))))))))
+
+           ;; "let ident =" introduces an expression--and so does "const" and 
"mut"
+           ((and (equal postchar ?=) (rust-looking-back-symbols '("let" 
"const" "mut"))) t)
+
+           ;; As a specific special case, see if this is the = in this 
situation:
+           ;;     enum EnumName<type params> { Ident =
+           ;; In this case, this is a c-like enum and despite Ident
+           ;; representing a type, what comes after the = is an expression
+           ((and
+             (> (rust-paren-level) 0)
+             (save-excursion
+               (backward-up-list)
+               (rust-rewind-irrelevant)
+               (rust-rewind-type-param-list)
+               (and
+                (rust-looking-back-ident)
+                (progn
+                  (rust-rewind-qualified-ident)
+                  (rust-rewind-irrelevant)
+                  (rust-looking-back-str "enum")))))
+            t)
+           
+           ;; Otherwise the ambiguous operator is a type if the identifier is 
a type
+           ((rust-is-in-expression-context 'ident) t)))
+
+         ((equal token 'colon)
+          (cond
+           ;; If we see a ident: not inside any braces/parens, we're at top 
level.
+           ;; There are no allowed expressions after colons there, just types.
+           ((<= (rust-paren-level) 0) nil)
+
+           ;; We see ident: inside a list
+           ((looking-back "[{,]" (1- (point)))
+            (backward-up-list)
+
+            ;; If a : appears whose surrounding paren/brackets/braces are
+            ;; anything other than curly braces, it can't be a field
+            ;; initializer and must be denoting a type.
+            (when (looking-at "{")
+              (rust-rewind-irrelevant)
+              (rust-rewind-type-param-list)
+              (when (rust-looking-back-ident)
+                ;; We have a context that looks like this:
+                ;;    ident2 <maybe type params> { [maybe paren-balanced code 
ending in comma] ident1:
+                ;; the point is sitting just after ident2, and we trying to
+                ;; figure out if the colon introduces an expression or a type.
+                ;; The answer is that ident1 is a field name, and what comes
+                ;; after the colon is an expression, if ident2 is an
+                ;; expression.
+                (rust-rewind-qualified-ident)
+                (rust-is-in-expression-context 'ident))))
+
+
+           ;; Otherwise, if the ident: appeared with anything other than , or {
+           ;; before it, it can't be part of a struct initializer and therefore
+           ;; must be denoting a type.
+           nil
+           ))
+         ))
+
+       ;; An operator-like character after a string is indeed an operator
+       ((and (equal token 'ambiguous-operator)
+             (member (rust-syntax-class-before-point) '(5 7 15))) t)
+
+       ;; A colon that has something other than an identifier before it is a
+       ;; type ascription
+       ((equal token 'colon) nil)
+
+       ;; A :: introduces a type (or module, but not an expression in any case)
+       ((rust-looking-back-str "::") nil)
+       
+       ((rust-looking-back-str ":")
+        (backward-char)
+        (rust-is-in-expression-context 'colon))
+
+       ;; A -> introduces a type
+       ((rust-looking-back-str "->") nil)
+
+       ;; If we are up against the beginning of a list, or after a comma inside
+       ;; of one, back up out of it and check what the list itself is
+       ((or
+         (equal 4 (rust-syntax-class-before-point))
+         (rust-looking-back-str ","))
+        (backward-up-list)
+        (rust-is-in-expression-context 'open-brace))
+
+       ;; A => introduces an expression
+       ((rust-looking-back-str "=>") t)
+
+       ;; A == introduces an expression
+       ((rust-looking-back-str "==") t)
+
+       ;; These operators can introduce expressions or types
+       ((looking-back "[-+=!?&*]" (1- (point)))
+        (backward-char)
+        (rust-is-in-expression-context 'ambiguous-operator))
+
+       ;; These operators always introduce expressions.  (Note that if this
+       ;; regexp finds a < it must not be an angle bracket, or it'd
+       ;; have been caught in the syntax-class check above instead of this.)
+       ((looking-back rust-re-pre-expression-operators (1- (point))) t)
+       ))))
+
+(defun rust-is-lt-char-operator ()
+  "Return t if the < sign just after point is an operator rather
+  than an opening angle bracket, otherwise nil."
+  
+  (let ((case-fold-search nil))
+    (save-excursion
+      (rust-rewind-irrelevant)
+      ;; We are now just after the character syntactically before the <.
+      (cond
+
+       ;; If we are looking back at a < that is not an angle bracket (but not
+       ;; two of them) then this is the second < in a bit shift operator
+       ((and (rust-looking-back-str "<")
+             (not (equal 4 (rust-syntax-class-before-point)))
+             (not (rust-looking-back-str "<<"))))
+       
+       ;; On the other hand, if we are after a closing paren/brace/bracket it
+       ;; can only be an operator, not an angle bracket.  Likewise, if we are
+       ;; after a string it's an operator.  (The string case could actually be
+       ;; valid in rust for character literals.)
+       ((member (rust-syntax-class-before-point) '(5 7 15)) t)
+
+       ;; If we are looking back at an operator, we know that we are at
+       ;; the beginning of an expression, and thus it has to be an angle
+       ;; bracket (starting a "<Type as Trait>::" construct.)
+       ((looking-back rust-re-pre-expression-operators (1- (point))) nil)
+
+       ;; If we are looking back at a keyword, it's an angle bracket
+       ;; unless that keyword is "self", "true" or "false"
+       ((rust-looking-back-symbols rust-mode-keywords)
+        (rust-looking-back-symbols '("self" "true" "false")))
+
+       ;; If we're looking back at an identifier, this depends on whether
+       ;; the identifier is part of an expression or a type
+       ((rust-looking-back-ident)
+        (backward-sexp)
+        (or
+         ;; The special types can't take type param lists, so a < after one is
+         ;; always an operator
+         (looking-at (regexp-opt rust-special-types 'symbols))
+         
+         (rust-is-in-expression-context 'ident)))
+
+       ;; Otherwise, assume it's an angle bracket
+       ))))
+
+(defun rust-electric-pair-inhibit-predicate-wrap (char)
+  "Wraps the default `electric-pair-inhibit-predicate' to prevent
+  inserting a \"matching\" > after a < that would be treated as a
+  less than sign rather than as an opening angle bracket."
+  (or
+   (when (= ?< char)
+     (save-excursion
+       (backward-char)
+       (rust-is-lt-char-operator)))
+   (funcall (default-value 'electric-pair-inhibit-predicate) char)))
+
+(defun rust-look-for-non-angle-bracket-lt-gt (bound)
+  "Find an angle bracket (\"<\" or \">\") that should be part of
+  a matched pair Relies on the fact that when it finds a < or >,
+  we have already decided which previous ones are angle brackets
+  and which ones are not.  So this only really works as a
+  font-lock-syntactic-keywords matcher--it won't work at
+  arbitrary positions without the earlier parts of the buffer
+  having already been covered."
+
+  (rust-conditional-re-search-forward
+   "[<>]" bound
+   (lambda ()
+     (goto-char (match-beginning 0))
+     (cond
+      ;; If matching is turned off suppress all of them
+      ((not rust-match-angle-brackets) t)
+
+      ;; We don't take < or > in strings or comments to be angle brackets
+      ((rust-in-str-or-cmnt) t)
+
+      ;; Inside a macro we don't really know the syntax.  Any < or > may be an
+      ;; angle bracket or it may not.  But we know that the other braces have
+      ;; to balance regardless of the < and >, so if we don't treat any < or >
+      ;; as angle brackets it won't mess up any paren balancing.
+      ((rust-in-macro) t)
+      
+      ((looking-at "<")
+       (rust-is-lt-char-operator))
+
+      ((looking-at ">")
+       (cond
+        ;; Don't treat the > in -> or => as an angle bracket
+        ((member (char-before (point)) '(?- ?=)) t)
+
+        ;; If we are at top level and not in any list, it can't be a closing
+        ;; angle bracket
+        ((>= 0 (rust-paren-level)) t)
+
+        ;; Otherwise, treat the > as a closing angle bracket if it would
+        ;; match an opening one
+        ((save-excursion
+           (backward-up-list)
+           (not (looking-at "<"))))))))))
+
 (defvar rust-mode-font-lock-syntactic-keywords
   (append
-   ;; Handle single quoted character literals:
-   (mapcar (lambda (re) (list re '(1 "\"") '(2 "\"")))
-           '("\\('\\)[^']\\('\\)"
-             "\\('\\)\\\\['nrt\"\\]\\('\\)"
-             "\\('\\)\\\\x[[:xdigit:]]\\{2\\}\\('\\)"
-             "\\('\\)\\\\u[[:xdigit:]]\\{4\\}\\('\\)"
-             "\\('\\)\\\\U[[:xdigit:]]\\{8\\}\\('\\)"))
-   ;; Handle raw strings:
-   `((rust-look-for-raw-string (1 "|") (4 "_" nil t) (5 "|" nil t) (6 "|" nil 
t)))))
+   ;; Handle raw strings and character literals:
+   `((rust-look-for-non-standard-string (1 "|" nil t) (4 "_" nil t) (5 "|" nil 
t) (6 "|" nil t) (7 "\"" nil t) (8 "\"" nil t)))
+   ;; Find where < and > characters represent operators rather than angle 
brackets:
+   '((rust-look-for-non-angle-bracket-lt-gt (0 "." t)))))
 
 (defun rust-mode-syntactic-face-function (state)
   "Syntactic face function to distinguish doc comments from other comments."
@@ -660,81 +1108,6 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
     ;; There is no opening brace, so consider the whole buffer to be one 
"defun"
     (goto-char (point-max))))
 
-;; Angle-bracket matching. This is kind of a hack designed to deal
-;; with the fact that we can't add angle-brackets to the list of
-;; matching characters unconditionally. Basically we just have some
-;; special-case code such that whenever `>` is typed, we look
-;; backwards to find a matching `<` and highlight it, whether or not
-;; this is *actually* appropriate. This could be annoying so it is
-;; configurable (but on by default because it's awesome).
-
-(defcustom rust-blink-matching-angle-brackets t
-  "Blink matching `<` (if any) when `>` is typed"
-  :type 'boolean
-  :group 'rust-mode)
-
-(defvar rust-point-before-matching-angle-bracket 0)
-
-(defvar rust-matching-angle-bracker-timer nil)
-
-(defun rust-find-matching-angle-bracket ()
-  (save-excursion
-    (let ((angle-brackets 1)
-          (start-point (point))
-          (invalid nil))
-      (while (and
-              ;; didn't find a match
-              (> angle-brackets 0)
-              ;; we have no guarantee of a match, so give up eventually
-             (or (not blink-matching-paren-distance)
-                 (< (- start-point (point)) blink-matching-paren-distance))
-              ;; didn't hit the top of the buffer
-              (> (point) (point-min))
-              ;; didn't hit something else weird like a `;`
-              (not invalid))
-        (backward-char 1)
-        (cond
-         ((looking-at ">")
-           (setq angle-brackets (+ angle-brackets 1)))
-          ((looking-at "<")
-           (setq angle-brackets (- angle-brackets 1)))
-          ((looking-at "[;{]")
-           (setq invalid t))))
-      (cond
-       ((= angle-brackets 0) (point))
-       (t nil)))))
-
-(defun rust-restore-point-after-angle-bracket ()
-  (goto-char rust-point-before-matching-angle-bracket)
-  (when rust-matching-angle-bracker-timer
-    (cancel-timer rust-matching-angle-bracker-timer))
-  (setq rust-matching-angle-bracker-timer nil)
-  (remove-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket))
-
-(defun rust-match-angle-bracket-hook ()
-  "If the most recently inserted character is a `>`, briefly moves point to 
matching `<` (if any)."
-  (interactive)
-  (when (and rust-blink-matching-angle-brackets
-             (looking-back ">" nil))
-    (let ((matching-angle-bracket-point (save-excursion
-                                          (backward-char 1)
-                                          (rust-find-matching-angle-bracket))))
-      (when matching-angle-bracket-point
-        (progn
-          (setq rust-point-before-matching-angle-bracket (point))
-          (goto-char matching-angle-bracket-point)
-          (add-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket)
-          (setq rust-matching-angle-bracker-timer
-                (run-at-time blink-matching-delay nil 
'rust-restore-point-after-angle-bracket)))))))
-
-(defun rust-match-angle-bracket ()
-  "The point should be placed on a `>`. Finds the matching `<` and moves point 
there."
-  (interactive)
-  (let ((matching-angle-bracket-point (rust-find-matching-angle-bracket)))
-    (if matching-angle-bracket-point
-        (goto-char matching-angle-bracket-point)
-      (message "no matching angle bracket found"))))
-
 ;; For compatibility with Emacs < 24, derive conditionally
 (defalias 'rust-parent-mode
   (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
@@ -749,7 +1122,7 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
   (setq-local indent-line-function 'rust-mode-indent-line)
 
   ;; Fonts
-  (add-to-list 'font-lock-extend-region-functions 
'rust-extend-region-raw-string)
+  (add-to-list 'font-lock-extend-region-functions 
'rust-font-lock-extend-region)
   (setq-local font-lock-defaults '(rust-mode-font-lock-keywords
                                    nil nil nil nil
                                    (font-lock-syntactic-keywords . 
rust-mode-font-lock-syntactic-keywords)
@@ -777,7 +1150,7 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
   (setq-local beginning-of-defun-function 'rust-beginning-of-defun)
   (setq-local end-of-defun-function 'rust-end-of-defun)
   (setq-local parse-sexp-lookup-properties t)
-  (add-hook 'post-self-insert-hook 'rust-match-angle-bracket-hook))
+  (setq-local electric-pair-inhibit-predicate 
'rust-electric-pair-inhibit-predicate-wrap))
 
 ;;;###autoload
 (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))



reply via email to

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