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