userdiff: better method/property matching for C#
- Support multi-line methods by not requiring closing parenthesis. - Support multiple generics (comma was missing before). - Add missing `foreach`, `lock` and `fixed` keywords to skip over. - Remove `instanceof` keyword, which isn't C#. - Also detect non-method keywords not positioned at the start of a line. - Added tests; none existed before. The overall strategy is to focus more on what isn't expected for method/property definitions, instead of what is, but is fully optional. Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com> Acked-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									43072b4ca1
								
							
						
					
					
						commit
						ec0e3075d2
					
				|  | @ -0,0 +1,20 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         var constantAssignment = "test"; | ||||||
|  |         var methodAssignment = MethodCall(); | ||||||
|  |         var multiLineMethodAssignment = MethodCall( | ||||||
|  |         ); | ||||||
|  |         var multiLine = "first" | ||||||
|  |             + MethodCall() | ||||||
|  |             + | ||||||
|  |             ( MethodCall() | ||||||
|  |             ) | ||||||
|  |             + MethodCall(); | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     string MethodCall(int a = 0, int b = 0) => "test"; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         if (false) | ||||||
|  |         { | ||||||
|  |             return "out"; | ||||||
|  |         } | ||||||
|  |         else { } | ||||||
|  |         if (true) MethodCall( | ||||||
|  |         ); | ||||||
|  |         else MethodCall( | ||||||
|  |         ); | ||||||
|  |         switch ("test") | ||||||
|  |         { | ||||||
|  |             case "one": | ||||||
|  |             return MethodCall( | ||||||
|  |             ); | ||||||
|  |             case "two": | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         (int, int) tuple = (1, 4); | ||||||
|  |         switch (tuple) | ||||||
|  |         { | ||||||
|  |             case (1, 4): | ||||||
|  |               MethodCall(); | ||||||
|  | 		      break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     string MethodCall(int a = 0, int b = 0) => "test"; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,29 @@ | ||||||
|  | using System; | ||||||
|  |  | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             throw new Exception("fail"); | ||||||
|  |         } | ||||||
|  |         catch (Exception) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         try { } catch (Exception) {} | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             throw GetException( | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         catch (Exception) { } | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Exception GetException() => new Exception("fail"); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         GenericMethodCall<int, int>( | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     string GenericMethodCall<T, T2>() => "test"; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | using System; | ||||||
|  |  | ||||||
|  | class Example : IDisposable | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         new Example(); | ||||||
|  |         new Example( | ||||||
|  |             ); | ||||||
|  |         new Example { }; | ||||||
|  |         using (this) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         var def = | ||||||
|  |             this is default( | ||||||
|  |                 Example); | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void Dispose() {} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | using System.Linq; | ||||||
|  |  | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         do { } while (true); | ||||||
|  |         do MethodCall( | ||||||
|  |         ); while (true); | ||||||
|  |         while (true); | ||||||
|  |         while (true) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         for (int i = 0; i < 10; ++i) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         foreach (int i in Enumerable.Range(0, 10)) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]; | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     string MethodCall(int a = 0, int b = 0) => "test"; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         MethodCall(); | ||||||
|  |         MethodCall(1, 2); | ||||||
|  |         MethodCall( | ||||||
|  |             1, 2); | ||||||
|  |         MethodCall( | ||||||
|  |             1, 2, | ||||||
|  |             3); | ||||||
|  |         MethodCall( | ||||||
|  |             1, MethodCall(), | ||||||
|  |             2); | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int MethodCall(int a = 0, int b = 0, int c = 0) => 42; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         lock (this) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         unsafe | ||||||
|  |         { | ||||||
|  |             byte[] bytes = [1, 2, 3]; | ||||||
|  |             fixed (byte* pointerToFirst = bytes) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string[] Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |  | ||||||
|  |         return ["ChangeMe"]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | using System; | ||||||
|  |  | ||||||
|  | class Example : IDisposable | ||||||
|  | { | ||||||
|  |     void IDisposable.Dispose() // RIGHT | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         // ChangeMe | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | class Example<T1, T2> | ||||||
|  | { | ||||||
|  |     Example<int, string> Method<TA, TB>(TA RIGHT, TB b) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         // ChangeMe | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | class Example<T1, T2> | ||||||
|  | { | ||||||
|  |     Example<int,string> Method<TA ,TB>(TA RIGHT, TB b) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         // ChangeMe | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     static internal async Task Method(int RIGHT) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         // ChangeMe | ||||||
|  |         await Task.Delay(1); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method_RIGHT( | ||||||
|  |         int a, | ||||||
|  |         int b, | ||||||
|  |         int c) | ||||||
|  |     { | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     string Method(int RIGHT, int b, int c = 42) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | class @Some_Type | ||||||
|  | { | ||||||
|  |     @Some_Type @Method_With_Underscore(int RIGHT) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |          | ||||||
|  |         // ChangeMe | ||||||
|  |         return new @Some_Type(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     	string   Method 	( int 	RIGHT ) | ||||||
|  |     { | ||||||
|  |         // Filler | ||||||
|  |         // Filler | ||||||
|  |  | ||||||
|  |         return "ChangeMe"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     public bool RIGHT | ||||||
|  |     { | ||||||
|  |         get { return true; } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             // ChangeMe | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | class Example | ||||||
|  | { | ||||||
|  |     public bool RIGHT { | ||||||
|  |         get { return true; } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             // ChangeMe | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								userdiff.c
								
								
								
								
							
							
						
						
									
										48
									
								
								userdiff.c
								
								
								
								
							|  | @ -90,12 +90,48 @@ PATTERNS("cpp", | ||||||
| 	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?" | 	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?" | ||||||
| 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"), | 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"), | ||||||
| PATTERNS("csharp", | PATTERNS("csharp", | ||||||
| 	 /* Keywords */ | 	 /* | ||||||
| 	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" | 	  * Jump over reserved keywords which are illegal method names, but which | ||||||
| 	 /* Methods and constructors */ | 	  * can be followed by parentheses without special characters in between, | ||||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n" | 	  * making them look like methods. | ||||||
| 	 /* Properties */ | 	  */ | ||||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n" | 	 "!(^|[ \t]+)" /* Start of line or whitespace. */ | ||||||
|  | 		"(do|while|for|foreach|if|else|new|default|return|switch|case|throw" | ||||||
|  | 		"|catch|using|lock|fixed)" | ||||||
|  | 		"([ \t(]+|$)\n" /* Whitespace, "(", or end of line. */ | ||||||
|  | 	 /* | ||||||
|  | 	  * Methods/constructors: | ||||||
|  | 	  * The strategy is to identify a minimum of two groups (any combination | ||||||
|  | 	  * of keywords/type/name) before the opening parenthesis, and without | ||||||
|  | 	  * final unexpected characters, normally only used in ordinary statements. | ||||||
|  | 	  */ | ||||||
|  | 	 "^[ \t]*" /* Remove leading whitespace. */ | ||||||
|  | 		"(" /* Start chunk header capture. */ | ||||||
|  | 		"(" /* First group. */ | ||||||
|  | 			"[][[:alnum:]@_.]" /* Name. */ | ||||||
|  | 			"(<[][[:alnum:]@_, \t<>]+>)?" /* Optional generic parameters. */ | ||||||
|  | 		")+" | ||||||
|  | 		"([ \t]+" /* Subsequent groups, prepended with space. */ | ||||||
|  | 			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+" | ||||||
|  | 		")+" | ||||||
|  | 		"[ \t]*" /* Optional space before parameters start. */ | ||||||
|  | 		"\\(" /* Start of method parameters. */ | ||||||
|  | 		"[^;]*" /* Allow complex parameters, but exclude statements (;). */ | ||||||
|  | 		")$\n" /* Close chunk header capture. */ | ||||||
|  | 	 /* | ||||||
|  | 	  * Properties: | ||||||
|  | 	  * As with methods, expect a minimum of two groups. But, more trivial than | ||||||
|  | 	  * methods, the vast majority of properties long enough to be worth | ||||||
|  | 	  * showing a chunk header for don't include "=:;,()" on the line they are | ||||||
|  | 	  * defined, since they don't have a parameter list. | ||||||
|  | 	  */ | ||||||
|  | 	 "^[ \t]*(" | ||||||
|  | 		"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+" | ||||||
|  | 		"([ \t]+" | ||||||
|  | 			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+" | ||||||
|  | 		")+" /* Up to here, same as methods regex. */ | ||||||
|  | 		"[^;=:,()]*" /* Compared to methods, no parameter list allowed. */ | ||||||
|  | 		")$\n" | ||||||
| 	 /* Type definitions */ | 	 /* Type definitions */ | ||||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n" | 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n" | ||||||
| 	 /* Namespace */ | 	 /* Namespace */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Steven Jeuris
						Steven Jeuris