From f9ef7056d67232c5541626a6cd867722fcd6fb83 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sat, 6 Jan 2024 23:39:34 +0000 Subject: [PATCH 01/12] Highlight function calls --- src/languages/lib/kws_swift.js | 42 ++++++++++++++------------ src/languages/swift.js | 27 +++++++++++++++-- test/markup/swift/macro.expect.txt | 6 ++-- test/markup/swift/numbers.expect.txt | 4 +-- test/markup/swift/ownership.expect.txt | 10 +++--- test/markup/swift/regex.expect.txt | 20 ++++++------ test/markup/swift/swiftui.expect.txt | 2 +- 7 files changed, 68 insertions(+), 43 deletions(-) diff --git a/src/languages/lib/kws_swift.js b/src/languages/lib/kws_swift.js index e2932bbb90..f9c166b225 100644 --- a/src/languages/lib/kws_swift.js +++ b/src/languages/lib/kws_swift.js @@ -151,28 +151,30 @@ export const precedencegroupKeywords = [ // Keywords that start with a number sign (#). // #(un)available is handled separately. -export const numberSignKeywords = [ - '#colorLiteral', - '#column', - '#dsohandle', - '#else', - '#elseif', - '#endif', - '#error', - '#file', - '#fileID', - '#fileLiteral', - '#filePath', - '#function', - '#if', - '#imageLiteral', - '#keyPath', - '#line', - '#selector', - '#sourceLocation', - '#warning' +export const numberSignKeywordsRaw = [ + 'colorLiteral', + 'column', + 'dsohandle', + 'else', + 'elseif', + 'endif', + 'error', + 'file', + 'fileID', + 'fileLiteral', + 'filePath', + 'function', + 'if', + 'imageLiteral', + 'keyPath', + 'line', + 'selector', + 'sourceLocation', + 'warning' ]; +export const numberSignKeywords = numberSignKeywordsRaw.map(x => `#${x}`); + // Global functions in the Standard Library. export const builtIns = [ 'abs', diff --git a/src/languages/swift.js b/src/languages/swift.js index 0d8481a416..1ef2c9e8cf 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -390,13 +390,16 @@ export default function(hljs) { endsParent: true, illegal: /["']/ }; + + const FUNCTION_IDENT = either(QUOTED_IDENTIFIER.match, Swift.identifier, Swift.operator); + // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID362 // https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations/#Macro-Declaration const FUNCTION_OR_MACRO = { match: [ /(func|macro)/, /\s+/, - either(QUOTED_IDENTIFIER.match, Swift.identifier, Swift.operator) + FUNCTION_IDENT, ], className: { 1: "keyword", @@ -491,6 +494,25 @@ export default function(hljs) { ] }; + function noneOf(list) { + return concat("(?!", list.join("|"), ")"); + } + + const FUNCTION_CALL = { + match: concat( + either(/\b/, /#/), + noneOf([ + ...Swift.keywords, + ...Swift.numberSignKeywordsRaw, + ...Swift.builtIns, + ].map(x => `${x}\\s*\\(`)), + FUNCTION_IDENT, + lookahead(/\s*\(/), + ), + scope: "title.function", + relevance: 0, + }; + // Add supported submodes to string interpolation. for (const variant of STRING.variants) { const interpolation = variant.contains.find(mode => mode.label === "interpol"); @@ -542,7 +564,8 @@ export default function(hljs) { ...IDENTIFIERS, ...ATTRIBUTES, TYPE, - TUPLE + TUPLE, + FUNCTION_CALL ] }; } diff --git a/test/markup/swift/macro.expect.txt b/test/markup/swift/macro.expect.txt index a4e780f1b1..82267107aa 100644 --- a/test/markup/swift/macro.expect.txt +++ b/test/markup/swift/macro.expect.txt @@ -1,7 +1,7 @@ -macro warning(_ message: String) = #externalMacro(module: "MyMacros", type: "WarningMacro") +macro warning(_ message: String) = #externalMacro(module: "MyMacros", type: "WarningMacro") @freestanding(declaration) -macro error(_ message: String) = #externalMacro(module: "MyMacros", type: "ErrorMacro") +macro error(_ message: String) = #externalMacro(module: "MyMacros", type: "ErrorMacro") @attached(member) macro OptionSetMembers() @@ -9,4 +9,4 @@ @attached(peer, names: overloaded) macro OptionSetMembers() -#myMacro() +#myMacro() diff --git a/test/markup/swift/numbers.expect.txt b/test/markup/swift/numbers.expect.txt index d022e50f9c..d32834db43 100644 --- a/test/markup/swift/numbers.expect.txt +++ b/test/markup/swift/numbers.expect.txt @@ -26,11 +26,11 @@ +-1 2-3 -10.magnitude -fn(-5) +fn(-5) 0x2.p2 // expressions not containing numeric literals -fn(x0.d); +fn(x0.d); // pseudo-expressions containing numeric literals .0 diff --git a/test/markup/swift/ownership.expect.txt b/test/markup/swift/ownership.expect.txt index d04b63c1fe..b8e345c027 100644 --- a/test/markup/swift/ownership.expect.txt +++ b/test/markup/swift/ownership.expect.txt @@ -1,9 +1,9 @@ consume x _ = consume y -doStuffUniquely(with: consume x) +doStuffUniquely(with: consume x) copy x _ = copy x -doStuff(with: copy x) +doStuff(with: copy x) struct MoveOnly: ~Copyable {} @@ -13,9 +13,9 @@ doStuff(with: copy x) func foo(_: borrowing Foo) func foo(_: consuming Foo) func foo(_: inout Foo) -let f: (borrowing Foo) -> Void = { a in a.foo() } -let f: (consuming Foo) -> Void = { a in a.foo() } -let f: (inout Foo) -> Void = { a in a.foo() } +let f: (borrowing Foo) -> Void = { a in a.foo() } +let f: (consuming Foo) -> Void = { a in a.foo() } +let f: (inout Foo) -> Void = { a in a.foo() } struct Foo { consuming func foo() borrowing func foo() diff --git a/test/markup/swift/regex.expect.txt b/test/markup/swift/regex.expect.txt index 1c14db3699..0765ca8cff 100644 --- a/test/markup/swift/regex.expect.txt +++ b/test/markup/swift/regex.expect.txt @@ -9,12 +9,12 @@ let n = /hello/ + /world/ - /nice/ let q = /hello/ / 2 (/hello/) -method(value: /hello/) -method(/hello/, world) -method(/hello/, /world/) -foo(/a, b/) // Will become regex literal '/a, b/' -qux(/, !/) // Will become regex literal '/, !/' -qux(/,/) // Will become regex literal '/,/' +method(value: /hello/) +method(/hello/, world) +method(/hello/, /world/) +foo(/a, b/) // Will become regex literal '/a, b/' +qux(/, !/) // Will become regex literal '/, !/' +qux(/,/) // Will become regex literal '/,/' let g = hasSubscript[/]/2 // Will become regex literal '/]/' let h = /0; let f = 1/ // Will become the regex literal '/0; let y = 1/' let i = /^x/ // Will become the regex literal '/^x/' @@ -37,9 +37,9 @@ qux(/,/) // Will let n = #/hello/# + /world/ - #/nice/# let q = #/hello/# / 2 (#/hello/#) -method(value: #/hello/#) -method(#/hello/#, world) -method(#/hello/#, #/world/#) +method(value: #/hello/#) +method(#/hello/#, world) +method(#/hello/#, #/world/#) #/regex with #not a comment/# // multiline extended literals @@ -100,4 +100,4 @@ x+#/y/# // unterminated /something another line -/ \ No newline at end of file +/ diff --git a/test/markup/swift/swiftui.expect.txt b/test/markup/swift/swiftui.expect.txt index ce514f88fc..a053b5f703 100644 --- a/test/markup/swift/swiftui.expect.txt +++ b/test/markup/swift/swiftui.expect.txt @@ -2,7 +2,7 @@ struct MyApp: App { var body: some Scene { WindowGroup { - #if os(iOS) + #if os(iOS) Text("Hello, world from iOS!") #else Text("Hello, world!") From dc4afd207dd0c9486be3ab209078b4319166ed89 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sat, 6 Jan 2024 23:44:24 +0000 Subject: [PATCH 02/12] Add more test cases to functions --- test/markup/swift/functions.expect.txt | 22 ++++++++++++++++++++++ test/markup/swift/functions.txt | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index 2398aaef43..a059b89579 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -30,3 +30,25 @@ static func > (lhs: Self, rhs: Self) -> Bool static func >= (lhs: Self, rhs: Self) -> Bool } + +// builtins +abs(1) +swap(&a, &b) +zip(a, b) +obj.abs(1) +obj.swap(&a, &b) +obj.zip(a, b) + +// methods +method() +method(1) +method(param: 1) +obj.method() +obj.method(1) +obj.method(param: 1) +obj.prop.method() +obj.prop.method(1) +obj.prop.method(param: 1) + +`if`() +obj.`if`() diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index 1f8807d3c2..c669efdc61 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -30,3 +30,25 @@ protocol Comparable: Equatable { static func > (lhs: Self, rhs: Self) -> Bool static func >= (lhs: Self, rhs: Self) -> Bool } + +// builtins +abs(1) +swap(&a, &b) +zip(a, b) +obj.abs(1) +obj.swap(&a, &b) +obj.zip(a, b) + +// methods +method() +method(1) +method(param: 1) +obj.method() +obj.method(1) +obj.method(param: 1) +obj.prop.method() +obj.prop.method(1) +obj.prop.method(param: 1) + +`if`() +obj.`if`() From 566f28fb73a807ffa0b56ed64d2eebe4308a7fff Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 10:20:15 +0000 Subject: [PATCH 03/12] Highlight builtin methods as functions --- src/languages/swift.js | 30 +++++++++++++++++++------- test/markup/swift/functions.expect.txt | 6 +++--- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/languages/swift.js b/src/languages/swift.js index 1ef2c9e8cf..d9417504c2 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -74,18 +74,32 @@ export default function(hljs) { KEYWORD ]; - // https://github.com/apple/swift/tree/main/stdlib/public/core - const BUILT_IN_GUARD = { - // Consume .built_in to prevent highlighting properties and methods. - match: concat(/\./, either(...Swift.builtIns)), - relevance: 0 + const BUILT_IN_PROP_GUARD = { + // Consumes .built_in to prevent highlighting properties as built-ins. + match: concat(/\./, either(...Swift.builtIns), noneOf(["\\("])), + relevance: 0, + }; + + const BUILT_IN_METHOD_GUARD = { + // .built_in(...) is actually a custom method being called. + match: [ + /\./, + either(...Swift.builtIns), + /\(/, + ], + scope: { + 2: 'title.function', + }, }; + const BUILT_IN = { - className: 'built_in', - match: concat(/\b/, either(...Swift.builtIns), /(?=\()/) + scope: 'built_in', + match: concat(/\b/, either(...Swift.builtIns), lookahead(/\(/)) }; + const BUILT_INS = [ - BUILT_IN_GUARD, + BUILT_IN_PROP_GUARD, + BUILT_IN_METHOD_GUARD, BUILT_IN ]; diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index a059b89579..d90b3d547a 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -35,9 +35,9 @@ abs(1) swap(&a, &b) zip(a, b) -obj.abs(1) -obj.swap(&a, &b) -obj.zip(a, b) +obj.abs(1) +obj.swap(&a, &b) +obj.zip(a, b) // methods method() From 2be2fce9b346356bcfcfc49dbfafd5d721998aca Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 10:43:49 +0000 Subject: [PATCH 04/12] Ensure newline before function parens does not highlight something as a function --- src/languages/swift.js | 20 ++++++++------------ src/lib/regex.js | 8 ++++++++ test/markup/swift/functions.expect.txt | 14 ++++++++++++++ test/markup/swift/functions.txt | 14 ++++++++++++++ 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/languages/swift.js b/src/languages/swift.js index d9417504c2..438dd4ce12 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -11,11 +11,14 @@ import * as Swift from './lib/kws_swift.js'; import { concat, either, - lookahead + lookahead, + negativeLookahead } from '../lib/regex.js'; /** @type LanguageFn */ export default function(hljs) { + const TRAILING_PAREN_REGEX = /[^\S\r\n]*\(/; + const WHITESPACE = { match: /\s+/, relevance: 0 @@ -74,18 +77,12 @@ export default function(hljs) { KEYWORD ]; - const BUILT_IN_PROP_GUARD = { - // Consumes .built_in to prevent highlighting properties as built-ins. - match: concat(/\./, either(...Swift.builtIns), noneOf(["\\("])), - relevance: 0, - }; - const BUILT_IN_METHOD_GUARD = { // .built_in(...) is actually a custom method being called. match: [ /\./, either(...Swift.builtIns), - /\(/, + TRAILING_PAREN_REGEX, ], scope: { 2: 'title.function', @@ -94,11 +91,10 @@ export default function(hljs) { const BUILT_IN = { scope: 'built_in', - match: concat(/\b/, either(...Swift.builtIns), lookahead(/\(/)) + match: concat(/\b/, either(...Swift.builtIns), lookahead(TRAILING_PAREN_REGEX)), }; const BUILT_INS = [ - BUILT_IN_PROP_GUARD, BUILT_IN_METHOD_GUARD, BUILT_IN ]; @@ -519,9 +515,9 @@ export default function(hljs) { ...Swift.keywords, ...Swift.numberSignKeywordsRaw, ...Swift.builtIns, - ].map(x => `${x}\\s*\\(`)), + ].map(x => concat(x, TRAILING_PAREN_REGEX))), FUNCTION_IDENT, - lookahead(/\s*\(/), + lookahead(TRAILING_PAREN_REGEX), ), scope: "title.function", relevance: 0, diff --git a/src/lib/regex.js b/src/lib/regex.js index da6cc457c8..bea55ac767 100644 --- a/src/lib/regex.js +++ b/src/lib/regex.js @@ -25,6 +25,14 @@ export function lookahead(re) { return concat('(?=', re, ')'); } +/** + * @param {RegExp | string } re + * @returns {string} + */ +export function negativeLookahead(re) { + return concat('(?!', re, ')'); +} + /** * @param {RegExp | string } re * @returns {string} diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index d90b3d547a..08537b67d1 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -31,6 +31,12 @@ static func >= (lhs: Self, rhs: Self) -> Bool } +// paren spacing +obj.fn(1) +obj.fn (1) +obj.prop +(1) // newline break, this is no longer a function + // builtins abs(1) swap(&a, &b) @@ -38,6 +44,9 @@ obj.abs(1) obj.swap(&a, &b) obj.zip(a, b) +obj.abs (1) +obj.abs +(1) // methods method() @@ -49,6 +58,11 @@ obj.method(param: method() obj.prop.method(1) obj.prop.method(param: 1) +obj.prop.method( + param: 1 +) +obj.prop + .method() `if`() obj.`if`() diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index c669efdc61..9b45824789 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -31,6 +31,12 @@ protocol Comparable: Equatable { static func >= (lhs: Self, rhs: Self) -> Bool } +// paren spacing +obj.fn(1) +obj.fn (1) +obj.prop +(1) // newline break, this is no longer a function + // builtins abs(1) swap(&a, &b) @@ -38,6 +44,9 @@ zip(a, b) obj.abs(1) obj.swap(&a, &b) obj.zip(a, b) +obj.abs (1) +obj.abs +(1) // methods method() @@ -49,6 +58,11 @@ obj.method(param: 1) obj.prop.method() obj.prop.method(1) obj.prop.method(param: 1) +obj.prop.method( + param: 1 +) +obj.prop + .method() `if`() obj.`if`() From 465fe422c5d6518896f353bb27850b18fee10850 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 11:34:20 +0000 Subject: [PATCH 05/12] Highlight keyword methods and quoted methods as functions, these are valid --- src/languages/swift.js | 76 +++++++++++++------------- test/markup/swift/functions.expect.txt | 8 ++- test/markup/swift/functions.txt | 4 ++ test/markup/swift/keywords.expect.txt | 4 +- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/languages/swift.js b/src/languages/swift.js index 438dd4ce12..07fd38a44f 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -17,6 +17,9 @@ import { /** @type LanguageFn */ export default function(hljs) { + /** + * Regex for detecting a function call following an identifier. + */ const TRAILING_PAREN_REGEX = /[^\S\r\n]*\(/; const WHITESPACE = { @@ -43,11 +46,6 @@ export default function(hljs) { ], className: { 2: "keyword" } }; - const KEYWORD_GUARD = { - // Consume .keyword to prevent highlighting properties and methods as keywords. - match: concat(/\./, either(...Swift.keywords)), - relevance: 0 - }; const PLAIN_KEYWORDS = Swift.keywords .filter(kw => typeof kw === 'string') .concat([ "_|0" ]); // seems common, so 0 relevance @@ -73,32 +71,14 @@ export default function(hljs) { }; const KEYWORD_MODES = [ DOT_KEYWORD, - KEYWORD_GUARD, KEYWORD ]; - const BUILT_IN_METHOD_GUARD = { - // .built_in(...) is actually a custom method being called. - match: [ - /\./, - either(...Swift.builtIns), - TRAILING_PAREN_REGEX, - ], - scope: { - 2: 'title.function', - }, - }; - const BUILT_IN = { scope: 'built_in', match: concat(/\b/, either(...Swift.builtIns), lookahead(TRAILING_PAREN_REGEX)), }; - const BUILT_INS = [ - BUILT_IN_METHOD_GUARD, - BUILT_IN - ]; - // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418 const OPERATOR_GUARD = { // Prevent -> from being highlighting as an operator. @@ -345,7 +325,7 @@ export default function(hljs) { ...COMMENTS, REGEXP, ...KEYWORD_MODES, - ...BUILT_INS, + BUILT_IN, ...OPERATORS, NUMBER, STRING, @@ -508,19 +488,37 @@ export default function(hljs) { return concat("(?!", list.join("|"), ")"); } + const METHODS_ONLY = [...Swift.keywords, ...Swift.numberSignKeywordsRaw, ...Swift.builtIns]; const FUNCTION_CALL = { - match: concat( - either(/\b/, /#/), - noneOf([ - ...Swift.keywords, - ...Swift.numberSignKeywordsRaw, - ...Swift.builtIns, - ].map(x => concat(x, TRAILING_PAREN_REGEX))), - FUNCTION_IDENT, - lookahead(TRAILING_PAREN_REGEX), - ), - scope: "title.function", relevance: 0, + variants: [ + { + scope: "title.function", + match: concat( + either(/#/, /\b/), + noneOf(METHODS_ONLY.map(x => concat(x, TRAILING_PAREN_REGEX))), + FUNCTION_IDENT, + lookahead(TRAILING_PAREN_REGEX), + ), + }, + { + match: [ + /\./, + either(...METHODS_ONLY), + TRAILING_PAREN_REGEX, + ], + scope: { + 2: "title.function", + } + }, + { + scope: "title.function", + match: concat( + QUOTED_IDENTIFIER.match, + lookahead(TRAILING_PAREN_REGEX), + ) + } + ] }; // Add supported submodes to string interpolation. @@ -530,7 +528,7 @@ export default function(hljs) { interpolation.keywords = KEYWORDS; const submodes = [ ...KEYWORD_MODES, - ...BUILT_INS, + BUILT_IN, ...OPERATORS, NUMBER, STRING, @@ -567,15 +565,15 @@ export default function(hljs) { }, REGEXP, ...KEYWORD_MODES, - ...BUILT_INS, + BUILT_IN, ...OPERATORS, NUMBER, STRING, - ...IDENTIFIERS, ...ATTRIBUTES, TYPE, TUPLE, - FUNCTION_CALL + FUNCTION_CALL, + ...IDENTIFIERS, ] }; } diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index 08537b67d1..49b8b1c901 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -64,5 +64,9 @@ obj.prop.method( obj.prop .method() -`if`() -obj.`if`() +// keywords +obj.if(condition: true) +`if`() +obj.`if`() +`notKeyword`() +obj.`notKeyword`() diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index 9b45824789..c14132cb85 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -64,5 +64,9 @@ obj.prop.method( obj.prop .method() +// keywords +obj.if(condition: true) `if`() obj.`if`() +`notKeyword`() +obj.`notKeyword`() diff --git a/test/markup/swift/keywords.expect.txt b/test/markup/swift/keywords.expect.txt index d9f90858e0..92361a1dcd 100644 --- a/test/markup/swift/keywords.expect.txt +++ b/test/markup/swift/keywords.expect.txt @@ -23,6 +23,6 @@ x is String #error("Error") #endif -x.as(y) -x.for(y) +x.as(y) +x.for(y) #notAKeyword From a419a2be320ad854c77a02fec4289423426ae83d Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 13:35:17 +0000 Subject: [PATCH 06/12] Don't highlight keyword .variable --- src/languages/swift.js | 6 ++++++ test/markup/swift/functions.expect.txt | 1 + test/markup/swift/functions.txt | 1 + 3 files changed, 8 insertions(+) diff --git a/src/languages/swift.js b/src/languages/swift.js index 07fd38a44f..7589f88188 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -46,6 +46,11 @@ export default function(hljs) { ], className: { 2: "keyword" } }; + const KEYWORD_PROP_GUARD = { + // Consume .keyword to prevent highlighting properties as keywords. .methods are highlighted seperately + match: concat(/\./, either(...Swift.keywords), negativeLookahead(TRAILING_PAREN_REGEX)), + relevance: 0 + }; const PLAIN_KEYWORDS = Swift.keywords .filter(kw => typeof kw === 'string') .concat([ "_|0" ]); // seems common, so 0 relevance @@ -71,6 +76,7 @@ export default function(hljs) { }; const KEYWORD_MODES = [ DOT_KEYWORD, + KEYWORD_PROP_GUARD, KEYWORD ]; diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index 49b8b1c901..8ed9dd4bcf 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -66,6 +66,7 @@ obj.prop // keywords obj.if(condition: true) +obj.if // variable `if`() obj.`if`() `notKeyword`() diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index c14132cb85..5ae59d7a2a 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -66,6 +66,7 @@ obj.prop // keywords obj.if(condition: true) +obj.if // variable `if`() obj.`if`() `notKeyword`() From 84a292b07b3178a153f283db460b280cbb1667d9 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 13:43:06 +0000 Subject: [PATCH 07/12] Additional test cases --- test/markup/swift/functions.expect.txt | 8 ++++++++ test/markup/swift/functions.txt | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index 8ed9dd4bcf..9c9138f299 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -17,6 +17,9 @@ p5: @attribute String? = "text" ) { } +func `escaped`() {} +func `if`() {} + init<X: A>(_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } init?(_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } init! (_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } @@ -53,9 +56,11 @@ obj.abs method(1) method(param: 1) obj.method() +obj .method() obj.method(1) obj.method(param: 1) obj.prop.method() +obj.prop .method() obj.prop.method(1) obj.prop.method(param: 1) obj.prop.method( @@ -67,7 +72,10 @@ obj.prop // keywords obj.if(condition: true) obj.if // variable +obj .if // variable `if`() obj.`if`() +obj.`if` () `notKeyword`() obj.`notKeyword`() +obj.`notKeyword` () diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index 5ae59d7a2a..20a3d37e6a 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -17,6 +17,9 @@ func f3( p5: @attribute String? = "text" ) { } +func `escaped`() {} +func `if`() {} + init(_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } init?(_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } init! (_ p: @attribute inout (x: Int, var: Int) = (0, 0)) { } @@ -53,9 +56,11 @@ method() method(1) method(param: 1) obj.method() +obj .method() obj.method(1) obj.method(param: 1) obj.prop.method() +obj.prop .method() obj.prop.method(1) obj.prop.method(param: 1) obj.prop.method( @@ -67,7 +72,10 @@ obj.prop // keywords obj.if(condition: true) obj.if // variable +obj .if // variable `if`() obj.`if`() +obj.`if` () `notKeyword`() obj.`notKeyword`() +obj.`notKeyword` () From 7e4f14fd40dc186ded8f59853098a6dd8cd8ad7c Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 13:51:47 +0000 Subject: [PATCH 08/12] More #keyword tests --- test/markup/swift/keywords.expect.txt | 15 ++++++++++++++- test/markup/swift/keywords.txt | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/test/markup/swift/keywords.expect.txt b/test/markup/swift/keywords.expect.txt index 92361a1dcd..ea8266ac46 100644 --- a/test/markup/swift/keywords.expect.txt +++ b/test/markup/swift/keywords.expect.txt @@ -19,10 +19,23 @@ x is String isolated nonisolated public private fileprivate package internal open -#if +#if DEBUG +#error("Error") +#elseif os(macOS) +#error("Error") +#elseif arch(arm64) +#error("Error") +#elseif compiler(>=5.0) +#error("Error") +#elseif canImport(UIKit) +#error("Error") +#elseif targetEnvironment(simulator) #error("Error") #endif +#imageLiteral(resourceName: expression) +#fileLiteral(resourceName: expression) + x.as(y) x.for(y) #notAKeyword diff --git a/test/markup/swift/keywords.txt b/test/markup/swift/keywords.txt index 09b7da607f..528f22d8df 100644 --- a/test/markup/swift/keywords.txt +++ b/test/markup/swift/keywords.txt @@ -19,10 +19,23 @@ async await isolated nonisolated public private fileprivate package internal open -#if +#if DEBUG +#error("Error") +#elseif os(macOS) +#error("Error") +#elseif arch(arm64) +#error("Error") +#elseif compiler(>=5.0) +#error("Error") +#elseif canImport(UIKit) +#error("Error") +#elseif targetEnvironment(simulator) #error("Error") #endif +#imageLiteral(resourceName: expression) +#fileLiteral(resourceName: expression) + x.as(y) x.for(y) #notAKeyword From 9952fabf1ccfdef66d63d024731283c59de4620a Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 14:07:58 +0000 Subject: [PATCH 09/12] Update CHANGES --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index c9694462fa..f77bd85cde 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -27,6 +27,7 @@ Core Grammars: - fix(swift) ensure keyword attributes highlight correctly [Bradley Mackey][] - fix(types) fix interface LanguageDetail > keywords [Patrick Chiu] - enh(java) add `goto` to be recognized as a keyword in Java [Alvin Joy][] +- enh(swift) highlight function and macro call usage [Bradley Mackey][] New Grammars: From a38e915df4cc1845426eb9d3658ac803022c37c5 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 14:54:12 +0000 Subject: [PATCH 10/12] Number sign keywords can appear as function names when without # --- src/languages/swift.js | 11 ++++++++--- test/markup/swift/functions.expect.txt | 15 +++++++++++++++ test/markup/swift/functions.txt | 15 +++++++++++++++ test/markup/swift/keywords.expect.txt | 16 ++++++++-------- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/languages/swift.js b/src/languages/swift.js index 7589f88188..4ea63c0a1e 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -494,20 +494,24 @@ export default function(hljs) { return concat("(?!", list.join("|"), ")"); } - const METHODS_ONLY = [...Swift.keywords, ...Swift.numberSignKeywordsRaw, ...Swift.builtIns]; + const METHODS_ONLY = [...Swift.keywords, ...Swift.builtIns]; const FUNCTION_CALL = { relevance: 0, variants: [ { + // Functions and macro calls scope: "title.function", + keywords: KEYWORDS, match: concat( - either(/#/, /\b/), - noneOf(METHODS_ONLY.map(x => concat(x, TRAILING_PAREN_REGEX))), + either(/\b/, /#/), + noneOf([...METHODS_ONLY].map(x => concat(x, TRAILING_PAREN_REGEX))), FUNCTION_IDENT, lookahead(TRAILING_PAREN_REGEX), ), }, { + // Keywords/built-ins that only can appear as a method call + // e.g. foo.if() match: [ /\./, either(...METHODS_ONLY), @@ -518,6 +522,7 @@ export default function(hljs) { } }, { + // Quoted methods calls, e.g. `foo`() scope: "title.function", match: concat( QUOTED_IDENTIFIER.match, diff --git a/test/markup/swift/functions.expect.txt b/test/markup/swift/functions.expect.txt index 9c9138f299..70c6387990 100644 --- a/test/markup/swift/functions.expect.txt +++ b/test/markup/swift/functions.expect.txt @@ -79,3 +79,18 @@ obj.`if` () `notKeyword`() obj.`notKeyword`() obj.`notKeyword` () + +// number sign keywords are fine +column() +keyPath() +sourceLocation() +obj.column() +obj.keyPath() +obj.sourceLocation() + +// attribute keywords are fine +frozen() +discardableResult() +obj.frozen() +obj.discardableResult() + diff --git a/test/markup/swift/functions.txt b/test/markup/swift/functions.txt index 20a3d37e6a..1849372aed 100644 --- a/test/markup/swift/functions.txt +++ b/test/markup/swift/functions.txt @@ -79,3 +79,18 @@ obj.`if` () `notKeyword`() obj.`notKeyword`() obj.`notKeyword` () + +// number sign keywords are fine +column() +keyPath() +sourceLocation() +obj.column() +obj.keyPath() +obj.sourceLocation() + +// attribute keywords are fine +frozen() +discardableResult() +obj.frozen() +obj.discardableResult() + diff --git a/test/markup/swift/keywords.expect.txt b/test/markup/swift/keywords.expect.txt index ea8266ac46..b8a204b0e5 100644 --- a/test/markup/swift/keywords.expect.txt +++ b/test/markup/swift/keywords.expect.txt @@ -20,21 +20,21 @@ x is String public private fileprivate package internal open #if DEBUG -#error("Error") +#error("Error") #elseif os(macOS) -#error("Error") +#error("Error") #elseif arch(arm64) -#error("Error") +#error("Error") #elseif compiler(>=5.0) -#error("Error") +#error("Error") #elseif canImport(UIKit) -#error("Error") +#error("Error") #elseif targetEnvironment(simulator) -#error("Error") +#error("Error") #endif -#imageLiteral(resourceName: expression) -#fileLiteral(resourceName: expression) +#imageLiteral(resourceName: expression) +#fileLiteral(resourceName: expression) x.as(y) x.for(y) From 7fffa5ea7a8cbab9821d3623405acca7c4b2cfc9 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 14:55:07 +0000 Subject: [PATCH 11/12] Remove unneeded raw number sign keywords --- src/languages/lib/kws_swift.js | 42 ++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/languages/lib/kws_swift.js b/src/languages/lib/kws_swift.js index f9c166b225..e2932bbb90 100644 --- a/src/languages/lib/kws_swift.js +++ b/src/languages/lib/kws_swift.js @@ -151,30 +151,28 @@ export const precedencegroupKeywords = [ // Keywords that start with a number sign (#). // #(un)available is handled separately. -export const numberSignKeywordsRaw = [ - 'colorLiteral', - 'column', - 'dsohandle', - 'else', - 'elseif', - 'endif', - 'error', - 'file', - 'fileID', - 'fileLiteral', - 'filePath', - 'function', - 'if', - 'imageLiteral', - 'keyPath', - 'line', - 'selector', - 'sourceLocation', - 'warning' +export const numberSignKeywords = [ + '#colorLiteral', + '#column', + '#dsohandle', + '#else', + '#elseif', + '#endif', + '#error', + '#file', + '#fileID', + '#fileLiteral', + '#filePath', + '#function', + '#if', + '#imageLiteral', + '#keyPath', + '#line', + '#selector', + '#sourceLocation', + '#warning' ]; -export const numberSignKeywords = numberSignKeywordsRaw.map(x => `#${x}`); - // Global functions in the Standard Library. export const builtIns = [ 'abs', From a0db36f0a7f35351869869cd8cd37ac3f8cc3f12 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Sun, 7 Jan 2024 14:57:56 +0000 Subject: [PATCH 12/12] Tidy noneOf --- src/languages/swift.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/swift.js b/src/languages/swift.js index 4ea63c0a1e..12aaf49aaa 100644 --- a/src/languages/swift.js +++ b/src/languages/swift.js @@ -491,7 +491,7 @@ export default function(hljs) { }; function noneOf(list) { - return concat("(?!", list.join("|"), ")"); + return negativeLookahead(either(...list)); } const METHODS_ONLY = [...Swift.keywords, ...Swift.builtIns]; @@ -504,7 +504,7 @@ export default function(hljs) { keywords: KEYWORDS, match: concat( either(/\b/, /#/), - noneOf([...METHODS_ONLY].map(x => concat(x, TRAILING_PAREN_REGEX))), + noneOf(METHODS_ONLY.map(x => concat(x, TRAILING_PAREN_REGEX))), FUNCTION_IDENT, lookahead(TRAILING_PAREN_REGEX), ),