mirror of
https://github.com/git/git
synced 2024-09-29 21:27:13 +00:00
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>
This commit is contained in:
parent
43072b4ca1
commit
ec0e3075d2
20
t/t4018/csharp-exclude-assignments
Normal file
20
t/t4018/csharp-exclude-assignments
Normal file
|
@ -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";
|
||||
}
|
34
t/t4018/csharp-exclude-control-statements
Normal file
34
t/t4018/csharp-exclude-control-statements
Normal file
|
@ -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";
|
||||
}
|
29
t/t4018/csharp-exclude-exceptions
Normal file
29
t/t4018/csharp-exclude-exceptions
Normal file
|
@ -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");
|
||||
}
|
12
t/t4018/csharp-exclude-generic-method-calls
Normal file
12
t/t4018/csharp-exclude-generic-method-calls
Normal file
|
@ -0,0 +1,12 @@
|
|||
class Example
|
||||
{
|
||||
string Method(int RIGHT)
|
||||
{
|
||||
GenericMethodCall<int, int>(
|
||||
);
|
||||
|
||||
return "ChangeMe";
|
||||
}
|
||||
|
||||
string GenericMethodCall<T, T2>() => "test";
|
||||
}
|
22
t/t4018/csharp-exclude-init-dispose
Normal file
22
t/t4018/csharp-exclude-init-dispose
Normal file
|
@ -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() {}
|
||||
}
|
26
t/t4018/csharp-exclude-iterations
Normal file
26
t/t4018/csharp-exclude-iterations
Normal file
|
@ -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";
|
||||
}
|
20
t/t4018/csharp-exclude-method-calls
Normal file
20
t/t4018/csharp-exclude-method-calls
Normal file
|
@ -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;
|
||||
}
|
18
t/t4018/csharp-exclude-other
Normal file
18
t/t4018/csharp-exclude-other
Normal file
|
@ -0,0 +1,18 @@
|
|||
class Example
|
||||
{
|
||||
string Method(int RIGHT)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
}
|
||||
unsafe
|
||||
{
|
||||
byte[] bytes = [1, 2, 3];
|
||||
fixed (byte* pointerToFirst = bytes)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return "ChangeMe";
|
||||
}
|
||||
}
|
10
t/t4018/csharp-method
Normal file
10
t/t4018/csharp-method
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Example
|
||||
{
|
||||
string Method(int RIGHT)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
return "ChangeMe";
|
||||
}
|
||||
}
|
10
t/t4018/csharp-method-array
Normal file
10
t/t4018/csharp-method-array
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Example
|
||||
{
|
||||
string[] Method(int RIGHT)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
return ["ChangeMe"];
|
||||
}
|
||||
}
|
12
t/t4018/csharp-method-explicit
Normal file
12
t/t4018/csharp-method-explicit
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
class Example : IDisposable
|
||||
{
|
||||
void IDisposable.Dispose() // RIGHT
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
// ChangeMe
|
||||
}
|
||||
}
|
11
t/t4018/csharp-method-generics
Normal file
11
t/t4018/csharp-method-generics
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Example<T1, T2>
|
||||
{
|
||||
Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
// ChangeMe
|
||||
return null;
|
||||
}
|
||||
}
|
11
t/t4018/csharp-method-generics-alternate-spaces
Normal file
11
t/t4018/csharp-method-generics-alternate-spaces
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Example<T1, T2>
|
||||
{
|
||||
Example<int,string> Method<TA ,TB>(TA RIGHT, TB b)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
// ChangeMe
|
||||
return null;
|
||||
}
|
||||
}
|
13
t/t4018/csharp-method-modifiers
Normal file
13
t/t4018/csharp-method-modifiers
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
class Example
|
||||
{
|
||||
static internal async Task Method(int RIGHT)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
// ChangeMe
|
||||
await Task.Delay(1);
|
||||
}
|
||||
}
|
10
t/t4018/csharp-method-multiline
Normal file
10
t/t4018/csharp-method-multiline
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Example
|
||||
{
|
||||
string Method_RIGHT(
|
||||
int a,
|
||||
int b,
|
||||
int c)
|
||||
{
|
||||
return "ChangeMe";
|
||||
}
|
||||
}
|
10
t/t4018/csharp-method-params
Normal file
10
t/t4018/csharp-method-params
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Example
|
||||
{
|
||||
string Method(int RIGHT, int b, int c = 42)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
return "ChangeMe";
|
||||
}
|
||||
}
|
11
t/t4018/csharp-method-special-chars
Normal file
11
t/t4018/csharp-method-special-chars
Normal file
|
@ -0,0 +1,11 @@
|
|||
class @Some_Type
|
||||
{
|
||||
@Some_Type @Method_With_Underscore(int RIGHT)
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
// ChangeMe
|
||||
return new @Some_Type();
|
||||
}
|
||||
}
|
10
t/t4018/csharp-method-with-spacing
Normal file
10
t/t4018/csharp-method-with-spacing
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Example
|
||||
{
|
||||
string Method ( int RIGHT )
|
||||
{
|
||||
// Filler
|
||||
// Filler
|
||||
|
||||
return "ChangeMe";
|
||||
}
|
||||
}
|
11
t/t4018/csharp-property
Normal file
11
t/t4018/csharp-property
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Example
|
||||
{
|
||||
public bool RIGHT
|
||||
{
|
||||
get { return true; }
|
||||
set
|
||||
{
|
||||
// ChangeMe
|
||||
}
|
||||
}
|
||||
}
|
10
t/t4018/csharp-property-braces-same-line
Normal file
10
t/t4018/csharp-property-braces-same-line
Normal file
|
@ -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 a new issue