userdiff: add support for Swift

Add a built-in userdiff driver for the Swift programming language so that
diff hunk headers and word diffs work out of the box for ".swift" files.

The funcname pattern is built for Swift's own declaration grammar: an
optional run of attributes ("@objc", "@available(iOS 13, *)", ...),
followed by an optional run of lowercase modifiers ("public", "static",
"final", ...), followed by a declaration keyword (func, class, struct,
enum, protocol, extension, actor, init, deinit, subscript). The keyword
is followed by a boundary that allows whitespace, "(" (init/subscript),
"?" or "!" (failable init), or "<" (generics), while still acting as a
word boundary so e.g. "initialize(" does not match.

The word regex recognizes Swift identifiers, hexadecimal, octal, binary,
integer and floating-point literals, and the language's operators.

Signed-off-by: Shlok Kulshreshtha <diy2903@gmail.com>
Acked-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Shlok Kulshreshtha
2026-07-21 12:27:36 +05:30
committed by Junio C Hamano
parent 41365c2a9b
commit 113d62b141
15 changed files with 86 additions and 0 deletions

View File

@@ -914,6 +914,8 @@ patterns are available:
- `scheme` suitable for source code in most Lisp dialects,
including Scheme, Emacs Lisp, Common Lisp, and Clojure.
- `swift` suitable for source code in the Swift language.
- `tex` suitable for source code for LaTeX documents.

5
t/t4018/swift-actor Normal file
View File

@@ -0,0 +1,5 @@
actor RIGHT {
let a = 1
// a comment
let b = ChangeMe
}

View File

@@ -0,0 +1,7 @@
struct View {
@available(iOS 13, *) public func RIGHT() {
let a = 1
// a comment
print(ChangeMe)
}
}

5
t/t4018/swift-class Normal file
View File

@@ -0,0 +1,5 @@
class RIGHT {
let a = 1
// a comment
let b = ChangeMe
}

5
t/t4018/swift-enum Normal file
View File

@@ -0,0 +1,5 @@
enum RIGHT {
case first
// a comment
case ChangeMe
}

5
t/t4018/swift-extension Normal file
View File

@@ -0,0 +1,5 @@
extension RIGHT {
static let a = 1
// a comment
static let b = ChangeMe
}

View File

@@ -0,0 +1,7 @@
class Bar {
init?(RIGHT: Int) {
let x = 0
// a comment
print(ChangeMe)
}
}

5
t/t4018/swift-func Normal file
View File

@@ -0,0 +1,5 @@
func RIGHT(x: Int) -> Int {
let y = x
// a comment
return ChangeMe
}

View File

@@ -0,0 +1,7 @@
struct Container {
subscript<RIGHT>(index: Int) -> Int {
let a = 0
// a comment
return ChangeMe
}
}

7
t/t4018/swift-init Normal file
View File

@@ -0,0 +1,7 @@
class Foo {
init(RIGHT: Int) {
let x = 0
// a comment
print(ChangeMe)
}
}

View File

@@ -0,0 +1,7 @@
class Service {
@objc func RIGHT() {
let path = "/api"
// a comment
log(ChangeMe)
}
}

4
t/t4018/swift-modifiers Normal file
View File

@@ -0,0 +1,4 @@
public static func RIGHT() -> Int {
// a comment
return ChangeMe
}

5
t/t4018/swift-protocol Normal file
View File

@@ -0,0 +1,5 @@
protocol RIGHT {
var first: Int { get }
// a comment
var second: ChangeMe { get }
}

5
t/t4018/swift-struct Normal file
View File

@@ -0,0 +1,5 @@
struct RIGHT {
let a = 1
// a comment
let b = ChangeMe
}

View File

@@ -362,6 +362,16 @@ PATTERNS("scheme",
"\\|([^|\\\\]|\\\\.)*\\|"
/* All other words should be delimited by spaces or parentheses. */
"|([^][)(}{ \t])+"),
PATTERNS("swift",
"^[ \t]*((@[A-Za-z_][A-Za-z0-9_]*(\\([^()]*\\))?[ \t]+)*([a-z]+[ \t]+)*(func|init|deinit|subscript|class|struct|enum|protocol|extension|actor)[ \t(?!<].*)$",
/* -- */
"[a-zA-Z_][a-zA-Z0-9_]*"
/* hexadecimal, octal, and binary literals */
"|0[xX][0-9a-fA-F_]+|0[oO][0-7_]+|0[bB][01_]+"
/* integers and floating-point numbers */
"|[0-9][0-9_]*([.][0-9_]+)?([eE][-+]?[0-9]+)?"
/* unary and binary operators */
"|[-+*/%<>=!&|^~?]=|&&|\\|\\||<<=?|>>=?|\\?\\?|\\.\\.[.<]|->"),
PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
"\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
{ .name = "default", .binary = -1 },