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]?" | ||||
| 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"), | ||||
| PATTERNS("csharp", | ||||
| 	 /* Keywords */ | ||||
| 	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" | ||||
| 	 /* Methods and constructors */ | ||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n" | ||||
| 	 /* Properties */ | ||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n" | ||||
| 	 /* | ||||
| 	  * Jump over reserved keywords which are illegal method names, but which | ||||
| 	  * can be followed by parentheses without special characters in between, | ||||
| 	  * making them look like methods. | ||||
| 	  */ | ||||
| 	 "!(^|[ \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 */ | ||||
| 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n" | ||||
| 	 /* Namespace */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Steven Jeuris
						Steven Jeuris