Skip to content

Commit

Permalink
fix(swift) correctly highlight generics and conformances in type defi…
Browse files Browse the repository at this point in the history
…nitions (#3937)

* Add tests for generic class
* Only type declaration is `title.class`
* Add newline tests for class definition
* Add additional tests for type definition
* Add superclass and inheritance tests for classes
* Inheritances use `title.class.inherited`
* Add more test cases
* Test nested classes
  • Loading branch information
bradleymackey authored Dec 24, 2023
1 parent a66c24f commit 518a820
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Core Grammars:
- enh(delphi) add support for character strings with non-decimal numerics [Jonah Jeleniewski][]
- fix(javascript) incorrect function name highlighting [CY Fung][]
- fix(1c) fix escaped symbols "+-;():=,[]" literals [Vitaly Barilko][]
- fix(swift) correctly highlight generics and conformances in type definitions [Bradley Mackey][]

New Grammars:

Expand Down
45 changes: 32 additions & 13 deletions src/languages/swift.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,37 @@ export default function(hljs) {
end: /}/
};

const TYPE_DECLARATION = {
begin: [
/(struct|protocol|class|extension|enum|actor)/,
/\s+/,
Swift.identifier,
/\s*/,
],
beginScope: {
1: "keyword",
3: "title.class"
},
keywords: KEYWORDS,
contains: [
GENERIC_PARAMETERS,
...KEYWORD_MODES,
{
begin: /:/,
end: /\{/,
keywords: KEYWORDS,
contains: [
{
scope: "title.class.inherited",
match: Swift.typeIdentifier,
},
...KEYWORD_MODES,
],
relevance: 0,
},
]
};

// Add supported submodes to string interpolation.
for (const variant of STRING.variants) {
const interpolation = variant.contains.find(mode => mode.label === "interpol");
Expand Down Expand Up @@ -490,19 +521,7 @@ export default function(hljs) {
...COMMENTS,
FUNCTION_OR_MACRO,
INIT_SUBSCRIPT,
{
beginKeywords: 'struct protocol class extension enum actor',
end: '\\{',
excludeEnd: true,
keywords: KEYWORDS,
contains: [
hljs.inherit(hljs.TITLE_MODE, {
className: "title.class",
begin: /[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/
}),
...KEYWORD_MODES
]
},
TYPE_DECLARATION,
OPERATOR_DECLARATION,
PRECEDENCEGROUP,
{
Expand Down
2 changes: 1 addition & 1 deletion test/markup/swift/functions.expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

<span class="hljs-keyword">subscript</span>&lt;<span class="hljs-type">X</span>: <span class="hljs-type">A</span>&gt;(<span class="hljs-keyword">_</span> <span class="hljs-params">p</span>: <span class="hljs-meta">@attribute</span> <span class="hljs-keyword">inout</span> (x: <span class="hljs-type">Int</span>, var: <span class="hljs-type">Int</span>) <span class="hljs-operator">=</span> (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)) { }

<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">Comparable</span>: <span class="hljs-title class_">Equatable</span> {
<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">Comparable</span>: <span class="hljs-title class_ inherited__">Equatable</span> {

<span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">&lt;</span> (<span class="hljs-params">lhs</span>: <span class="hljs-keyword">Self</span>, <span class="hljs-params">rhs</span>: <span class="hljs-keyword">Self</span>) -&gt; <span class="hljs-type">Bool</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">&lt;=</span> (<span class="hljs-params">lhs</span>: <span class="hljs-keyword">Self</span>, <span class="hljs-params">rhs</span>: <span class="hljs-keyword">Self</span>) -&gt; <span class="hljs-type">Bool</span>
Expand Down
6 changes: 3 additions & 3 deletions test/markup/swift/ownership.expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ doStuffUniquely(with: <span class="hljs-keyword">consume</span> x)
<span class="hljs-keyword">_</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">copy</span> x
doStuff(with: <span class="hljs-keyword">copy</span> x)

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MoveOnly</span>: ~<span class="hljs-title class_">Copyable</span> {}
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MoveOnly</span>: ~<span class="hljs-title class_ inherited__">Copyable</span> {}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">B</span>: <span class="hljs-title class_">P</span> {
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">B</span>: <span class="hljs-title class_ inherited__">P</span> {
<span class="hljs-keyword">func</span> <span class="hljs-title function_">foo</span>(<span class="hljs-params">x</span>: <span class="hljs-keyword">borrowing</span> <span class="hljs-type">Foo</span>, <span class="hljs-params">y</span>: <span class="hljs-keyword">consuming</span> <span class="hljs-type">Foo</span>)
}
<span class="hljs-keyword">func</span> <span class="hljs-title function_">foo</span>(<span class="hljs-keyword">_</span>: <span class="hljs-keyword">borrowing</span> <span class="hljs-type">Foo</span>)
Expand All @@ -20,4 +20,4 @@ doStuff(with: <span class="hljs-keyword">copy</span> x)
<span class="hljs-keyword">consuming</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">foo</span>()
<span class="hljs-keyword">borrowing</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">foo</span>()
<span class="hljs-keyword">mutating</span> <span class="hljs-keyword">func</span> <span class="hljs-title function_">foo</span>()
}
}
2 changes: 1 addition & 1 deletion test/markup/swift/swiftui.expect.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<span class="hljs-keyword">@main</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MyApp</span>: <span class="hljs-title class_">App</span> {
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">MyApp</span>: <span class="hljs-title class_ inherited__">App</span> {
<span class="hljs-keyword">var</span> body: <span class="hljs-keyword">some</span> <span class="hljs-type">Scene</span> {
<span class="hljs-type">WindowGroup</span> {
<span class="hljs-keyword">#if</span> os(iOS)
Expand Down
55 changes: 55 additions & 0 deletions test/markup/swift/type-definition.expect.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span> {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>: <span class="hljs-title class_ inherited__">Superclass</span> {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>: <span class="hljs-title class_ inherited__">Superclass</span>, <span class="hljs-title class_ inherited__">Conform1</span>, <span class="hljs-title class_ inherited__">Conform2</span> {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>&lt;<span class="hljs-type">T</span>&gt; {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>&lt;<span class="hljs-keyword">repeat</span> <span class="hljs-keyword">each</span> <span class="hljs-type">T</span>&gt; {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>&lt;<span class="hljs-type">T</span>&gt; <span class="hljs-keyword">where</span> <span class="hljs-type">T</span>: <span class="hljs-type">Equatable</span> {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TestClass</span>&lt;<span class="hljs-type">T</span>&gt;: <span class="hljs-title class_ inherited__">Superclass</span> <span class="hljs-keyword">where</span> <span class="hljs-title class_ inherited__">T</span>: <span class="hljs-title class_ inherited__">Equatable</span> {}

<span class="hljs-keyword">class</span>
<span class="hljs-title class_">MoreThanOneLine</span>
{}

<span class="hljs-keyword">class</span>
<span class="hljs-title class_">MoreThanOneLineInherit</span>
:
<span class="hljs-title class_ inherited__">Superclass</span>
{}

<span class="hljs-keyword">class</span>
<span class="hljs-title class_">MoreThanOneLineInherit</span>
:
<span class="hljs-title class_ inherited__">Superclass1</span>,
<span class="hljs-title class_ inherited__">Superclass2</span>
{}

<span class="hljs-keyword">class</span>
<span class="hljs-title class_">MoreThanOneLineInherit</span>:
<span class="hljs-title class_ inherited__">Superclass</span>
{}

<span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span>
<span class="hljs-title class_">MoreThanOneLineGeneric</span>&lt;<span class="hljs-type">T</span>&gt;
<span class="hljs-keyword">where</span>
<span class="hljs-type">T</span>: <span class="hljs-type">Param</span>
{}

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Outer</span> {
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Inner</span>&lt;<span class="hljs-type">T</span>&gt; {}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">InnerInherit</span>: <span class="hljs-title class_ inherited__">Superclass</span> {}
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">TestStruct</span> {}
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">TestStruct</span>: <span class="hljs-title class_ inherited__">Conform</span> {}
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">TestStruct</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}

<span class="hljs-keyword">enum</span> <span class="hljs-title class_">TestEnum</span> {}
<span class="hljs-keyword">enum</span> <span class="hljs-title class_">TestEnum</span>: <span class="hljs-title class_ inherited__">Conform</span> {}
<span class="hljs-keyword">enum</span> <span class="hljs-title class_">TestEnum</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}

<span class="hljs-keyword">actor</span> <span class="hljs-title class_">TestActor</span> {}
<span class="hljs-keyword">actor</span> <span class="hljs-title class_">TestActor</span>: <span class="hljs-title class_ inherited__">Conform</span> {}
<span class="hljs-keyword">actor</span> <span class="hljs-title class_">TestActor</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}

<span class="hljs-keyword">extension</span> <span class="hljs-title class_">TestExtension</span> {}
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">TestExtension</span>: <span class="hljs-title class_ inherited__">Conform</span> {}
<span class="hljs-keyword">extension</span> <span class="hljs-title class_">TestExtension</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}

<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">TestProtocol</span> {}
<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">TestProtocol</span>: <span class="hljs-title class_ inherited__">Conform</span> {}
<span class="hljs-keyword">protocol</span> <span class="hljs-title class_">TestProtocol</span>&lt;<span class="hljs-type">T</span>, <span class="hljs-type">U</span>&gt; {}
55 changes: 55 additions & 0 deletions test/markup/swift/type-definition.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
class TestClass {}
class TestClass: Superclass {}
class TestClass: Superclass, Conform1, Conform2 {}
class TestClass<T> {}
class TestClass<T, U> {}
class TestClass<repeat each T> {}
class TestClass<T> where T: Equatable {}
class TestClass<T>: Superclass where T: Equatable {}

class
MoreThanOneLine
{}

class
MoreThanOneLineInherit
:
Superclass
{}

class
MoreThanOneLineInherit
:
Superclass1,
Superclass2
{}

class
MoreThanOneLineInherit:
Superclass
{}

final class
MoreThanOneLineGeneric<T>
where
T: Param
{}

class Outer {
class Inner<T> {}
class InnerInherit: Superclass {}
}

struct TestStruct {}
struct TestStruct: Conform {}
struct TestStruct<T, U> {}

enum TestEnum {}
enum TestEnum: Conform {}
enum TestEnum<T, U> {}

actor TestActor {}
actor TestActor: Conform {}
actor TestActor<T, U> {}

extension TestExtension {}
extension TestExtension: Conform {}
extension TestExtension<T, U> {}

protocol TestProtocol {}
protocol TestProtocol: Conform {}
protocol TestProtocol<T, U> {}

0 comments on commit 518a820

Please sign in to comment.