diff --git a/CHANGES.md b/CHANGES.md
index ceb04d4ac3..3dfc86202f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,7 +4,19 @@ New Languages:
- Added 3rd party Laravel Blade grammar to SUPPORTED_LANGUAGES (#2944) [Michael Newton][]
+Language grammar improvements:
+
+- enh(swift) Improved highlighting for operator and precedencegroup declarations. (#2938) [Steven Van Impe][]
+
+Parser:
+
+- add `modes.MATCH_NOTHING_RE` that will never match
+ - This can be used with `end` to hold a mode open (it must then be ended with
+ `endsParent` in one of it's children modes) [Josh Goebel][]
+
[Michael Newton]: https://github.com/miken32
+[Steven Van Impe]: https://github.com/svanimpe/
+[Josh Goebel]: https://github.com/joshgoebel
## Version 10.5.0
diff --git a/src/languages/lib/kws_swift.js b/src/languages/lib/kws_swift.js
index a52ca459bc..b349c52603 100644
--- a/src/languages/lib/kws_swift.js
+++ b/src/languages/lib/kws_swift.js
@@ -117,10 +117,6 @@ export const keywords = [
// NOTE: Contextual keywords are reserved only in specific contexts.
// Ideally, these should be matched using modes to avoid false positives.
-// TODO: Create a PRECEDENCE_GROUP mode to match the remaining contextual keywords:
-// assignment associativity higherThan left lowerThan none right
-// These aren't included in the list because they result in mostly false positives.
-
// Literals.
export const literals = [
'false',
@@ -128,6 +124,17 @@ export const literals = [
'true'
];
+// Keywords used in precedence groups.
+export const precedencegroupKeywords = [
+ 'assignment',
+ 'associativity',
+ 'higherThan',
+ 'left',
+ 'lowerThan',
+ 'none',
+ 'right'
+];
+
// Keywords that start with a number sign (#).
// #available is handled separately.
export const numberSignKeywords = [
diff --git a/src/languages/swift.js b/src/languages/swift.js
index 5f8e1a7389..f499d1a9a7 100644
--- a/src/languages/swift.js
+++ b/src/languages/swift.js
@@ -413,6 +413,43 @@ export default function(hljs) {
],
illegal: /\[|%/
};
+ // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380
+ const OPERATOR_DECLARATION = {
+ beginKeywords: 'operator',
+ end: hljs.MATCH_NOTHING_RE,
+ contains: [
+ {
+ className: 'title',
+ match: Swift.operator,
+ endsParent: true,
+ relevance: 0
+ }
+ ]
+ };
+
+ // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID550
+ const PRECEDENCEGROUP = {
+ beginKeywords: 'precedencegroup',
+ end: hljs.MATCH_NOTHING_RE,
+ contains: [
+ {
+ className: 'title',
+ match: Swift.typeIdentifier,
+ relevance: 0
+ },
+ {
+ begin: /{/,
+ end: /}/,
+ relevance: 0,
+ endsParent: true,
+ keywords: [
+ ...Swift.precedencegroupKeywords,
+ ...Swift.literals
+ ].join(' '),
+ contains: [ TYPE ]
+ }
+ ]
+ };
// Add supported submodes to string interpolation.
for (const variant of STRING.variants) {
@@ -460,6 +497,8 @@ export default function(hljs) {
...KEYWORD_MODES
]
},
+ OPERATOR_DECLARATION,
+ PRECEDENCEGROUP,
{
beginKeywords: 'import',
end: /$/,
diff --git a/src/lib/modes.js b/src/lib/modes.js
index eb4c7f137d..e4d2ab45b9 100644
--- a/src/lib/modes.js
+++ b/src/lib/modes.js
@@ -2,6 +2,7 @@ import { inherit } from './utils.js';
import * as regex from './regex.js';
// Common regexps
+export const MATCH_NOTHING_RE = /\b\B/;
export const IDENT_RE = '[a-zA-Z]\\w*';
export const UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*';
export const NUMBER_RE = '\\b\\d+(\\.\\d+)?';
diff --git a/test/markup/swift/operator-declarations.expect.txt b/test/markup/swift/operator-declarations.expect.txt
new file mode 100644
index 0000000000..3330ebfcbb
--- /dev/null
+++ b/test/markup/swift/operator-declarations.expect.txt
@@ -0,0 +1,3 @@
+prefix operator +++
+postfix operator +++
+infix operator +-: AdditionPrecedence
diff --git a/test/markup/swift/operator-declarations.txt b/test/markup/swift/operator-declarations.txt
new file mode 100644
index 0000000000..695156346a
--- /dev/null
+++ b/test/markup/swift/operator-declarations.txt
@@ -0,0 +1,3 @@
+prefix operator +++
+postfix operator +++
+infix operator +-: AdditionPrecedence
diff --git a/test/markup/swift/precedencegroup.expect.txt b/test/markup/swift/precedencegroup.expect.txt
new file mode 100644
index 0000000000..4cb8ebe3f1
--- /dev/null
+++ b/test/markup/swift/precedencegroup.expect.txt
@@ -0,0 +1,8 @@
+precedencegroup MyGroup {
+ higherThan: OtherGroup, AnotherGroup
+ lowerThan: OtherGroup, AnotherGroup
+ assignment: true
+ associativity: left
+ associativity: right
+ associativity: none
+}
diff --git a/test/markup/swift/precedencegroup.txt b/test/markup/swift/precedencegroup.txt
new file mode 100644
index 0000000000..fdadf9889b
--- /dev/null
+++ b/test/markup/swift/precedencegroup.txt
@@ -0,0 +1,8 @@
+precedencegroup MyGroup {
+ higherThan: OtherGroup, AnotherGroup
+ lowerThan: OtherGroup, AnotherGroup
+ assignment: true
+ associativity: left
+ associativity: right
+ associativity: none
+}
diff --git a/types/index.d.ts b/types/index.d.ts
index e572abae87..223aa3e0c1 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -58,6 +58,7 @@ interface ModesAPI {
// built in regex
IDENT_RE: string
UNDERSCORE_IDENT_RE: string
+ MATCH_NOTHING_RE: string
NUMBER_RE: string
C_NUMBER_RE: string
BINARY_NUMBER_RE: string