Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh(swift) Improved highlighting for types and generic arguments (#2920) #2920

Merged
merged 3 commits into from
Dec 17, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ Language improvements:
- New grammar for attributes
- Added support for quoted identifiers, implicit parameters, and property wrapper projections
- Support for more complex expressions in string interpolation
- enh(swift) Improved highlighting for types and generic arguments (#2920) [Steven Van Impe][]
- fix(http) avoid recursive sublanguage and tighten rules (#2893) [Josh Goebel][]
- fix(asciidoc): Handle section titles level 5 (#2868) [Vaibhav Chanana][]

3 changes: 3 additions & 0 deletions src/languages/lib/kws_swift.js
Original file line number Diff line number Diff line change
@@ -258,6 +258,9 @@ export const identifierCharacter = either(
// Valid identifier.
export const identifier = concat(identifierHead, identifierCharacter, '*');

// Valid type identifier.
export const typeIdentifier = concat(/[A-Z]/, identifierCharacter, '*');

// Built-in attributes, which are highlighted as keywords.
// @available is handled separately.
export const keywordAttributes = [
77 changes: 58 additions & 19 deletions src/languages/swift.js
Original file line number Diff line number Diff line change
@@ -16,6 +16,15 @@ import {

/** @type LanguageFn */
export default function(hljs) {
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID411
const BLOCK_COMMENT = hljs.COMMENT(
'/\\*',
'\\*/',
{
contains: [ 'self' ]
}
);

// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID413
// https://docs.swift.org/swift-book/ReferenceManual/zzSummaryOfTheGrammar.html
const DOT_KEYWORD = {
@@ -77,6 +86,11 @@ export default function(hljs) {
];

// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418
const OPERATOR_GUARD = {
// Prevent -> from being highlighting as an operator.
begin: /->/,
relevance: 0
};
const OPERATOR = {
className: 'operator',
relevance: 0,
@@ -92,6 +106,10 @@ export default function(hljs) {
}
]
};
const OPERATORS = [
OPERATOR_GUARD,
OPERATOR
];

// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#grammar_numeric-literal
// TODO: Update for leading `-` after lookbehind is supported everywhere
@@ -205,7 +223,7 @@ export default function(hljs) {
.join(' ')
},
contains: [
OPERATOR,
...OPERATORS,
NUMBER,
STRING
]
@@ -224,24 +242,46 @@ export default function(hljs) {
USER_DEFINED_ATTRIBUTE
];

// https://docs.swift.org/swift-book/ReferenceManual/Types.html
const TYPE = {
className: 'type',
begin: '\\b[A-Z][\\w\u00C0-\u02B8\']*',
relevance: 0
begin: lookahead(/\b[A-Z]/),
relevance: 0,
contains: [
{ // Common Apple frameworks, for relevance boost
className: 'type',
begin: concat(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/, Swift.identifierCharacter, '+')
},
{ // Type identifier
className: 'type',
begin: Swift.typeIdentifier,
relevance: 0
},
{ // Optional type
begin: /[?!]+/,
relevance: 0
},
{ // Variadic parameter
begin: /\.\.\./,
relevance: 0
},
{ // Protocol composition
begin: concat(/\s+&\s+/, lookahead(Swift.typeIdentifier)),
relevance: 0
}
]
};
// slightly more special to swift
const OPTIONAL_USING_TYPE = {
className: 'type',
begin: '\\b[A-Z][\\w\u00C0-\u02B8\']*[!?]',
relevance: 0
const GENERIC_ARGUMENTS = {
begin: /</,
end: />/,
keywords: KEYWORDS,
contains: [
...KEYWORD_MODES,
...ATTRIBUTES,
OPERATOR_GUARD,
TYPE
]
};
const BLOCK_COMMENT = hljs.COMMENT(
'/\\*',
'\\*/',
{
contains: [ 'self' ]
}
);
TYPE.contains.push(GENERIC_ARGUMENTS);

// Add supported submodes to string interpolation.
for (const variant of STRING.variants) {
@@ -251,7 +291,7 @@ export default function(hljs) {
const submodes = [
...KEYWORD_MODES,
...BUILT_INS,
OPERATOR,
...OPERATORS,
NUMBER,
STRING,
...IDENTIFIERS
@@ -333,12 +373,11 @@ export default function(hljs) {
},
...KEYWORD_MODES,
...BUILT_INS,
OPERATOR,
...OPERATORS,
NUMBER,
STRING,
...IDENTIFIERS,
...ATTRIBUTES,
OPTIONAL_USING_TYPE,
TYPE
]
};
43 changes: 43 additions & 0 deletions test/markup/swift/types.expect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<span class="hljs-comment">// Simple identifier</span>
<span class="hljs-type">Int</span>

<span class="hljs-comment">// Types from Apple frameworks</span>
<span class="hljs-type">CALayer</span>
<span class="hljs-type">CGRect</span>
<span class="hljs-type">MKMapView</span>
<span class="hljs-type">NSView</span>
<span class="hljs-type">UIView</span>
<span class="hljs-type">XCTest</span>

<span class="hljs-comment">// ?, !, ..., and &amp; should not be highlighted as operators</span>
<span class="hljs-type">Int</span>?
<span class="hljs-type">Int</span>!
<span class="hljs-type">Int</span>?!
<span class="hljs-type">String</span>...
<span class="hljs-type">SomeClass</span> &amp; <span class="hljs-type">SomeProtocol</span>

<span class="hljs-comment">// Arrays, dictionaries, and nested types</span>
[<span class="hljs-type">String</span>]
<span class="hljs-type">Array</span>&lt;<span class="hljs-type">String</span>&gt;
[[<span class="hljs-type">Int</span>?]]
<span class="hljs-type">Array</span>&lt;<span class="hljs-type">Array</span>&lt;<span class="hljs-type">Int</span>?&gt;&gt;
[<span class="hljs-type">String</span>: <span class="hljs-type">Int</span>]
<span class="hljs-type">Dictionary</span>&lt;<span class="hljs-type">String</span>, <span class="hljs-type">Int</span>&gt;
<span class="hljs-type">Swift</span>.<span class="hljs-type">Array</span>&lt;<span class="hljs-type">Int</span>&gt;.<span class="hljs-type">Element</span>

<span class="hljs-comment">// Tuples</span>
()
(<span class="hljs-type">Double</span>, <span class="hljs-type">Double</span>)
(x: <span class="hljs-type">Double</span>, y: <span class="hljs-type">Double</span>)

<span class="hljs-comment">// Functions</span>
(<span class="hljs-keyword">@escaping</span> (<span class="hljs-type">String</span>) -&gt; <span class="hljs-type">Void</span>, <span class="hljs-keyword">@autoclosure</span> () -&gt; <span class="hljs-type">String</span>) -&gt; <span class="hljs-type">String</span>
(<span class="hljs-type">Int</span>, <span class="hljs-type">String</span>...) -&gt; <span class="hljs-keyword">some</span> <span class="hljs-type">Collection</span>
() <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-keyword">Self</span>

<span class="hljs-comment">// Generic arguments</span>
<span class="hljs-type">Array</span>&lt;<span class="hljs-type">String</span>.<span class="hljs-keyword">Type</span>&gt;
<span class="hljs-type">Array</span>&lt;<span class="hljs-type">Sequence</span>.<span class="hljs-keyword">Protocol</span>&gt;
<span class="hljs-type">Dictionary</span>&lt;<span class="hljs-type">String</span>, <span class="hljs-keyword">Any</span>&gt;
<span class="hljs-type">Dictionary</span>&lt;<span class="hljs-type">String</span>, <span class="hljs-type">Array</span>&lt;<span class="hljs-type">Int</span>&gt;&gt;
<span class="hljs-type">Array</span>&lt;(<span class="hljs-keyword">@autoclosure</span> () -&gt; <span class="hljs-type">String</span>) <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span>?&gt;
43 changes: 43 additions & 0 deletions test/markup/swift/types.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Simple identifier
Int

// Types from Apple frameworks
CALayer
CGRect
MKMapView
NSView
UIView
XCTest

// ?, !, ..., and & should not be highlighted as operators
Int?
Int!
Int?!
String...
SomeClass & SomeProtocol

// Arrays, dictionaries, and nested types
[String]
Array<String>
[[Int?]]
Array<Array<Int?>>
[String: Int]
Dictionary<String, Int>
Swift.Array<Int>.Element

// Tuples
()
(Double, Double)
(x: Double, y: Double)

// Functions
(@escaping (String) -> Void, @autoclosure () -> String) -> String
(Int, String...) -> some Collection
() throws -> Self

// Generic arguments
Array<String.Type>
Array<Sequence.Protocol>
Dictionary<String, Any>
Dictionary<String, Array<Int>>
Array<(@autoclosure () -> String) throws -> String?>