From: Shlok Kulshreshtha <diy2903@gmail.com>
To: git@vger.kernel.org
Cc: "Shlok Kulshreshtha" <diy2903@gmail.com>,
"D. Ben Knoble" <ben.knoble@gmail.com>,
"Junio C Hamano" <gitster@pobox.com>,
"René Scharfe" <l.s.r@web.de>, "Johannes Sixt" <j6t@kdbg.org>,
"Eric Sunshine" <sunshine@sunshineco.com>,
"Scott L. Burson" <Scott@sympoiesis.com>
Subject: [PATCH] userdiff: add support for Swift
Date: Fri, 17 Jul 2026 19:32:29 +0530 [thread overview]
Message-ID: <20260717140232.6722-1-diy2903@gmail.com> (raw)
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>
---
This addresses the "add a userdiff driver for a language" microproject.
Swift is not covered by a built-in driver yet, and I did not find an
in-flight patch adding one on the list; please let me know if one exists.
Motivation: without a Swift driver, ".swift" files use the generic
funcname heuristic, so "git diff" hunk headers name the enclosing type
instead of the changed function.
The pattern is built directly from Swift's declaration grammar rather
than adapted from another language's driver, so it covers a few things
that a straight port would miss:
- attributes, with or without arguments, whether on their own line
("@objc" above a "func") or inline with the declaration
("@objc func foo()", "@available(iOS 13, *) public func bar()");
- modifiers ("public", "static", "override", "mutating", ...) in any
combination, before or after attributes;
- failable initializers, "init?" and "init!";
- generics, "init<T>" and "subscript<T>";
- the keyword boundary still acts as a word boundary, so e.g.
"initialize(" is not mistaken for "init".
I verified all of the above against a built binary, including generic
functions with "where" clauses, multi-line signatures, and operator
functions such as "static func ==". I did not find a real case this
pattern misses; the ones I intentionally left out are one-line
declarations ("typealias", "associatedtype") and rarer forms
("operator", "precedencegroup", "macro"), which would not make useful
section headers anyway.
The word regex covers Swift identifiers; hexadecimal, octal, binary,
integer and floating-point literals; and operators including "<<=" /
">>=", "??" and the range operators. All of t4018 passes, including the
sorted builtin_drivers check and the new swift-* fixtures.
Documentation/gitattributes.adoc | 2 ++
t/t4018/swift-actor | 5 +++++
t/t4018/swift-attribute-with-args | 7 +++++++
t/t4018/swift-class | 5 +++++
t/t4018/swift-enum | 5 +++++
t/t4018/swift-extension | 5 +++++
t/t4018/swift-failable-init | 7 +++++++
t/t4018/swift-func | 5 +++++
t/t4018/swift-generic-subscript | 7 +++++++
t/t4018/swift-init | 7 +++++++
t/t4018/swift-inline-attribute | 7 +++++++
t/t4018/swift-modifiers | 4 ++++
t/t4018/swift-protocol | 5 +++++
t/t4018/swift-struct | 5 +++++
userdiff.c | 10 ++++++++++
15 files changed, 86 insertions(+)
create mode 100644 t/t4018/swift-actor
create mode 100644 t/t4018/swift-attribute-with-args
create mode 100644 t/t4018/swift-class
create mode 100644 t/t4018/swift-enum
create mode 100644 t/t4018/swift-extension
create mode 100644 t/t4018/swift-failable-init
create mode 100644 t/t4018/swift-func
create mode 100644 t/t4018/swift-generic-subscript
create mode 100644 t/t4018/swift-init
create mode 100644 t/t4018/swift-inline-attribute
create mode 100644 t/t4018/swift-modifiers
create mode 100644 t/t4018/swift-protocol
create mode 100644 t/t4018/swift-struct
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index bd76167a45..9fea75f96f 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -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.
diff --git a/t/t4018/swift-actor b/t/t4018/swift-actor
new file mode 100644
index 0000000000..e4852f40a7
--- /dev/null
+++ b/t/t4018/swift-actor
@@ -0,0 +1,5 @@
+actor RIGHT {
+ let a = 1
+ // a comment
+ let b = ChangeMe
+}
diff --git a/t/t4018/swift-attribute-with-args b/t/t4018/swift-attribute-with-args
new file mode 100644
index 0000000000..22b1ee32f1
--- /dev/null
+++ b/t/t4018/swift-attribute-with-args
@@ -0,0 +1,7 @@
+struct View {
+ @available(iOS 13, *) public func RIGHT() {
+ let a = 1
+ // a comment
+ print(ChangeMe)
+ }
+}
diff --git a/t/t4018/swift-class b/t/t4018/swift-class
new file mode 100644
index 0000000000..c3a9336027
--- /dev/null
+++ b/t/t4018/swift-class
@@ -0,0 +1,5 @@
+class RIGHT {
+ let a = 1
+ // a comment
+ let b = ChangeMe
+}
diff --git a/t/t4018/swift-enum b/t/t4018/swift-enum
new file mode 100644
index 0000000000..0a84302993
--- /dev/null
+++ b/t/t4018/swift-enum
@@ -0,0 +1,5 @@
+enum RIGHT {
+ case first
+ // a comment
+ case ChangeMe
+}
diff --git a/t/t4018/swift-extension b/t/t4018/swift-extension
new file mode 100644
index 0000000000..cbc18ab6ef
--- /dev/null
+++ b/t/t4018/swift-extension
@@ -0,0 +1,5 @@
+extension RIGHT {
+ static let a = 1
+ // a comment
+ static let b = ChangeMe
+}
diff --git a/t/t4018/swift-failable-init b/t/t4018/swift-failable-init
new file mode 100644
index 0000000000..5e4091d97c
--- /dev/null
+++ b/t/t4018/swift-failable-init
@@ -0,0 +1,7 @@
+class Bar {
+ init?(RIGHT: Int) {
+ let value = RIGHT
+ // a comment
+ print(ChangeMe)
+ }
+}
diff --git a/t/t4018/swift-func b/t/t4018/swift-func
new file mode 100644
index 0000000000..1fecae0911
--- /dev/null
+++ b/t/t4018/swift-func
@@ -0,0 +1,5 @@
+func RIGHT(x: Int) -> Int {
+ let y = x
+ // a comment
+ return ChangeMe
+}
diff --git a/t/t4018/swift-generic-subscript b/t/t4018/swift-generic-subscript
new file mode 100644
index 0000000000..565f93cd6c
--- /dev/null
+++ b/t/t4018/swift-generic-subscript
@@ -0,0 +1,7 @@
+struct Container {
+ subscript<RIGHT>(index: RIGHT) -> Int {
+ let a = 0
+ // a comment
+ return ChangeMe
+ }
+}
diff --git a/t/t4018/swift-init b/t/t4018/swift-init
new file mode 100644
index 0000000000..f683e74794
--- /dev/null
+++ b/t/t4018/swift-init
@@ -0,0 +1,7 @@
+class Foo {
+ init(RIGHT: Int) {
+ let value = RIGHT
+ // a comment
+ print(ChangeMe)
+ }
+}
diff --git a/t/t4018/swift-inline-attribute b/t/t4018/swift-inline-attribute
new file mode 100644
index 0000000000..2374c4b603
--- /dev/null
+++ b/t/t4018/swift-inline-attribute
@@ -0,0 +1,7 @@
+class Service {
+ @objc func RIGHT() {
+ let path = "/api"
+ // a comment
+ log(ChangeMe)
+ }
+}
diff --git a/t/t4018/swift-modifiers b/t/t4018/swift-modifiers
new file mode 100644
index 0000000000..9d80685a78
--- /dev/null
+++ b/t/t4018/swift-modifiers
@@ -0,0 +1,4 @@
+public static func RIGHT() -> Int {
+ // a comment
+ return ChangeMe
+}
diff --git a/t/t4018/swift-protocol b/t/t4018/swift-protocol
new file mode 100644
index 0000000000..07c39ec2a3
--- /dev/null
+++ b/t/t4018/swift-protocol
@@ -0,0 +1,5 @@
+protocol RIGHT {
+ var first: Int { get }
+ // a comment
+ var second: ChangeMe { get }
+}
diff --git a/t/t4018/swift-struct b/t/t4018/swift-struct
new file mode 100644
index 0000000000..e399ed7759
--- /dev/null
+++ b/t/t4018/swift-struct
@@ -0,0 +1,5 @@
+struct RIGHT {
+ let a = 1
+ // a comment
+ let b = ChangeMe
+}
diff --git a/userdiff.c b/userdiff.c
index b5412e6bc3..df37dd78a6 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -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 },
--
2.52.0
next reply other threads:[~2026-07-17 14:02 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 14:02 Shlok Kulshreshtha [this message]
2026-07-17 16:27 ` [PATCH] userdiff: add support for Swift Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260717140232.6722-1-diy2903@gmail.com \
--to=diy2903@gmail.com \
--cc=Scott@sympoiesis.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=j6t@kdbg.org \
--cc=l.s.r@web.de \
--cc=sunshine@sunshineco.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox