You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
873 lines
20 KiB
873 lines
20 KiB
/** |
|
* @param {string} value |
|
* @returns {RegExp} |
|
* */ |
|
|
|
/** |
|
* @param {RegExp | string } re |
|
* @returns {string} |
|
*/ |
|
function source(re) { |
|
if (!re) return null; |
|
if (typeof re === "string") return re; |
|
|
|
return re.source; |
|
} |
|
|
|
/** |
|
* @param {RegExp | string } re |
|
* @returns {string} |
|
*/ |
|
function lookahead(re) { |
|
return concat('(?=', re, ')'); |
|
} |
|
|
|
/** |
|
* @param {...(RegExp | string) } args |
|
* @returns {string} |
|
*/ |
|
function concat(...args) { |
|
const joined = args.map((x) => source(x)).join(""); |
|
return joined; |
|
} |
|
|
|
/** |
|
* Any of the passed expresssions may match |
|
* |
|
* Creates a huge this | this | that | that match |
|
* @param {(RegExp | string)[] } args |
|
* @returns {string} |
|
*/ |
|
function either(...args) { |
|
const joined = '(' + args.map((x) => source(x)).join("|") + ")"; |
|
return joined; |
|
} |
|
|
|
const keywordWrapper = keyword => concat( |
|
/\b/, |
|
keyword, |
|
/\w$/.test(keyword) ? /\b/ : /\B/ |
|
); |
|
|
|
// Keywords that require a leading dot. |
|
const dotKeywords = [ |
|
'Protocol', // contextual |
|
'Type' // contextual |
|
].map(keywordWrapper); |
|
|
|
// Keywords that may have a leading dot. |
|
const optionalDotKeywords = [ |
|
'init', |
|
'self' |
|
].map(keywordWrapper); |
|
|
|
// should register as keyword, not type |
|
const keywordTypes = [ |
|
'Any', |
|
'Self' |
|
]; |
|
|
|
// Regular keywords and literals. |
|
const keywords = [ |
|
// strings below will be fed into the regular `keywords` engine while regex |
|
// will result in additional modes being created to scan for those keywords to |
|
// avoid conflicts with other rules |
|
'associatedtype', |
|
'async', |
|
'await', |
|
/as\?/, // operator |
|
/as!/, // operator |
|
'as', // operator |
|
'break', |
|
'case', |
|
'catch', |
|
'class', |
|
'continue', |
|
'convenience', // contextual |
|
'default', |
|
'defer', |
|
'deinit', |
|
'didSet', // contextual |
|
'do', |
|
'dynamic', // contextual |
|
'else', |
|
'enum', |
|
'extension', |
|
'fallthrough', |
|
/fileprivate\(set\)/, |
|
'fileprivate', |
|
'final', // contextual |
|
'for', |
|
'func', |
|
'get', // contextual |
|
'guard', |
|
'if', |
|
'import', |
|
'indirect', // contextual |
|
'infix', // contextual |
|
/init\?/, |
|
/init!/, |
|
'inout', |
|
/internal\(set\)/, |
|
'internal', |
|
'in', |
|
'is', // operator |
|
'lazy', // contextual |
|
'let', |
|
'mutating', // contextual |
|
'nonmutating', // contextual |
|
/open\(set\)/, // contextual |
|
'open', // contextual |
|
'operator', |
|
'optional', // contextual |
|
'override', // contextual |
|
'postfix', // contextual |
|
'precedencegroup', |
|
'prefix', // contextual |
|
/private\(set\)/, |
|
'private', |
|
'protocol', |
|
/public\(set\)/, |
|
'public', |
|
'repeat', |
|
'required', // contextual |
|
'rethrows', |
|
'return', |
|
'set', // contextual |
|
'some', // contextual |
|
'static', |
|
'struct', |
|
'subscript', |
|
'super', |
|
'switch', |
|
'throws', |
|
'throw', |
|
/try\?/, // operator |
|
/try!/, // operator |
|
'try', // operator |
|
'typealias', |
|
/unowned\(safe\)/, // contextual |
|
/unowned\(unsafe\)/, // contextual |
|
'unowned', // contextual |
|
'var', |
|
'weak', // contextual |
|
'where', |
|
'while', |
|
'willSet' // contextual |
|
]; |
|
|
|
// NOTE: Contextual keywords are reserved only in specific contexts. |
|
// Ideally, these should be matched using modes to avoid false positives. |
|
|
|
// Literals. |
|
const literals = [ |
|
'false', |
|
'nil', |
|
'true' |
|
]; |
|
|
|
// Keywords used in precedence groups. |
|
const precedencegroupKeywords = [ |
|
'assignment', |
|
'associativity', |
|
'higherThan', |
|
'left', |
|
'lowerThan', |
|
'none', |
|
'right' |
|
]; |
|
|
|
// Keywords that start with a number sign (#). |
|
// #available is handled separately. |
|
const numberSignKeywords = [ |
|
'#colorLiteral', |
|
'#column', |
|
'#dsohandle', |
|
'#else', |
|
'#elseif', |
|
'#endif', |
|
'#error', |
|
'#file', |
|
'#fileID', |
|
'#fileLiteral', |
|
'#filePath', |
|
'#function', |
|
'#if', |
|
'#imageLiteral', |
|
'#keyPath', |
|
'#line', |
|
'#selector', |
|
'#sourceLocation', |
|
'#warn_unqualified_access', |
|
'#warning' |
|
]; |
|
|
|
// Global functions in the Standard Library. |
|
const builtIns = [ |
|
'abs', |
|
'all', |
|
'any', |
|
'assert', |
|
'assertionFailure', |
|
'debugPrint', |
|
'dump', |
|
'fatalError', |
|
'getVaList', |
|
'isKnownUniquelyReferenced', |
|
'max', |
|
'min', |
|
'numericCast', |
|
'pointwiseMax', |
|
'pointwiseMin', |
|
'precondition', |
|
'preconditionFailure', |
|
'print', |
|
'readLine', |
|
'repeatElement', |
|
'sequence', |
|
'stride', |
|
'swap', |
|
'swift_unboxFromSwiftValueWithType', |
|
'transcode', |
|
'type', |
|
'unsafeBitCast', |
|
'unsafeDowncast', |
|
'withExtendedLifetime', |
|
'withUnsafeMutablePointer', |
|
'withUnsafePointer', |
|
'withVaList', |
|
'withoutActuallyEscaping', |
|
'zip' |
|
]; |
|
|
|
// Valid first characters for operators. |
|
const operatorHead = either( |
|
/[/=\-+!*%<>&|^~?]/, |
|
/[\u00A1-\u00A7]/, |
|
/[\u00A9\u00AB]/, |
|
/[\u00AC\u00AE]/, |
|
/[\u00B0\u00B1]/, |
|
/[\u00B6\u00BB\u00BF\u00D7\u00F7]/, |
|
/[\u2016-\u2017]/, |
|
/[\u2020-\u2027]/, |
|
/[\u2030-\u203E]/, |
|
/[\u2041-\u2053]/, |
|
/[\u2055-\u205E]/, |
|
/[\u2190-\u23FF]/, |
|
/[\u2500-\u2775]/, |
|
/[\u2794-\u2BFF]/, |
|
/[\u2E00-\u2E7F]/, |
|
/[\u3001-\u3003]/, |
|
/[\u3008-\u3020]/, |
|
/[\u3030]/ |
|
); |
|
|
|
// Valid characters for operators. |
|
const operatorCharacter = either( |
|
operatorHead, |
|
/[\u0300-\u036F]/, |
|
/[\u1DC0-\u1DFF]/, |
|
/[\u20D0-\u20FF]/, |
|
/[\uFE00-\uFE0F]/, |
|
/[\uFE20-\uFE2F]/ |
|
// TODO: The following characters are also allowed, but the regex isn't supported yet. |
|
// /[\u{E0100}-\u{E01EF}]/u |
|
); |
|
|
|
// Valid operator. |
|
const operator = concat(operatorHead, operatorCharacter, '*'); |
|
|
|
// Valid first characters for identifiers. |
|
const identifierHead = either( |
|
/[a-zA-Z_]/, |
|
/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/, |
|
/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/, |
|
/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/, |
|
/[\u1E00-\u1FFF]/, |
|
/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/, |
|
/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/, |
|
/[\u2C00-\u2DFF\u2E80-\u2FFF]/, |
|
/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/, |
|
/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/, |
|
/[\uFE47-\uFEFE\uFF00-\uFFFD]/ // Should be /[\uFE47-\uFFFD]/, but we have to exclude FEFF. |
|
// The following characters are also allowed, but the regexes aren't supported yet. |
|
// /[\u{10000}-\u{1FFFD}\u{20000-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}]/u, |
|
// /[\u{50000}-\u{5FFFD}\u{60000-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}]/u, |
|
// /[\u{90000}-\u{9FFFD}\u{A0000-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}]/u, |
|
// /[\u{D0000}-\u{DFFFD}\u{E0000-\u{EFFFD}]/u |
|
); |
|
|
|
// Valid characters for identifiers. |
|
const identifierCharacter = either( |
|
identifierHead, |
|
/\d/, |
|
/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/ |
|
); |
|
|
|
// Valid identifier. |
|
const identifier = concat(identifierHead, identifierCharacter, '*'); |
|
|
|
// Valid type identifier. |
|
const typeIdentifier = concat(/[A-Z]/, identifierCharacter, '*'); |
|
|
|
// Built-in attributes, which are highlighted as keywords. |
|
// @available is handled separately. |
|
const keywordAttributes = [ |
|
'autoclosure', |
|
concat(/convention\(/, either('swift', 'block', 'c'), /\)/), |
|
'discardableResult', |
|
'dynamicCallable', |
|
'dynamicMemberLookup', |
|
'escaping', |
|
'frozen', |
|
'GKInspectable', |
|
'IBAction', |
|
'IBDesignable', |
|
'IBInspectable', |
|
'IBOutlet', |
|
'IBSegueAction', |
|
'inlinable', |
|
'main', |
|
'nonobjc', |
|
'NSApplicationMain', |
|
'NSCopying', |
|
'NSManaged', |
|
concat(/objc\(/, identifier, /\)/), |
|
'objc', |
|
'objcMembers', |
|
'propertyWrapper', |
|
'requires_stored_property_inits', |
|
'testable', |
|
'UIApplicationMain', |
|
'unknown', |
|
'usableFromInline' |
|
]; |
|
|
|
// Contextual keywords used in @available and #available. |
|
const availabilityKeywords = [ |
|
'iOS', |
|
'iOSApplicationExtension', |
|
'macOS', |
|
'macOSApplicationExtension', |
|
'macCatalyst', |
|
'macCatalystApplicationExtension', |
|
'watchOS', |
|
'watchOSApplicationExtension', |
|
'tvOS', |
|
'tvOSApplicationExtension', |
|
'swift' |
|
]; |
|
|
|
/* |
|
Language: Swift |
|
Description: Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns. |
|
Author: Steven Van Impe <steven.vanimpe@icloud.com> |
|
Contributors: Chris Eidhof <chris@eidhof.nl>, Nate Cook <natecook@gmail.com>, Alexander Lichter <manniL@gmx.net>, Richard Gibson <gibson042@github> |
|
Website: https://swift.org |
|
Category: common, system |
|
*/ |
|
|
|
/** @type LanguageFn */ |
|
function swift(hljs) { |
|
const WHITESPACE = { |
|
match: /\s+/, |
|
relevance: 0 |
|
}; |
|
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID411 |
|
const BLOCK_COMMENT = hljs.COMMENT( |
|
'/\\*', |
|
'\\*/', |
|
{ |
|
contains: [ 'self' ] |
|
} |
|
); |
|
const COMMENTS = [ |
|
hljs.C_LINE_COMMENT_MODE, |
|
BLOCK_COMMENT |
|
]; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID413 |
|
// https://docs.swift.org/swift-book/ReferenceManual/zzSummaryOfTheGrammar.html |
|
const DOT_KEYWORD = { |
|
className: 'keyword', |
|
begin: concat(/\./, lookahead(either(...dotKeywords, ...optionalDotKeywords))), |
|
end: either(...dotKeywords, ...optionalDotKeywords), |
|
excludeBegin: true |
|
}; |
|
const KEYWORD_GUARD = { |
|
// Consume .keyword to prevent highlighting properties and methods as keywords. |
|
match: concat(/\./, either(...keywords)), |
|
relevance: 0 |
|
}; |
|
const PLAIN_KEYWORDS = keywords |
|
.filter(kw => typeof kw === 'string') |
|
.concat([ "_|0" ]); // seems common, so 0 relevance |
|
const REGEX_KEYWORDS = keywords |
|
.filter(kw => typeof kw !== 'string') // find regex |
|
.concat(keywordTypes) |
|
.map(keywordWrapper); |
|
const KEYWORD = { |
|
variants: [ |
|
{ |
|
className: 'keyword', |
|
match: either(...REGEX_KEYWORDS, ...optionalDotKeywords) |
|
} |
|
] |
|
}; |
|
// find all the regular keywords |
|
const KEYWORDS = { |
|
$pattern: either( |
|
/\b\w+/, // regular keywords |
|
/#\w+/ // number keywords |
|
), |
|
keyword: PLAIN_KEYWORDS |
|
.concat(numberSignKeywords), |
|
literal: literals |
|
}; |
|
const KEYWORD_MODES = [ |
|
DOT_KEYWORD, |
|
KEYWORD_GUARD, |
|
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(...builtIns)), |
|
relevance: 0 |
|
}; |
|
const BUILT_IN = { |
|
className: 'built_in', |
|
match: concat(/\b/, either(...builtIns), /(?=\()/) |
|
}; |
|
const BUILT_INS = [ |
|
BUILT_IN_GUARD, |
|
BUILT_IN |
|
]; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418 |
|
const OPERATOR_GUARD = { |
|
// Prevent -> from being highlighting as an operator. |
|
match: /->/, |
|
relevance: 0 |
|
}; |
|
const OPERATOR = { |
|
className: 'operator', |
|
relevance: 0, |
|
variants: [ |
|
{ |
|
match: operator |
|
}, |
|
{ |
|
// dot-operator: only operators that start with a dot are allowed to use dots as |
|
// characters (..., ...<, .*, etc). So there rule here is: a dot followed by one or more |
|
// characters that may also include dots. |
|
match: `\\.(\\.|${operatorCharacter})+` |
|
} |
|
] |
|
}; |
|
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 |
|
const decimalDigits = '([0-9]_*)+'; |
|
const hexDigits = '([0-9a-fA-F]_*)+'; |
|
const NUMBER = { |
|
className: 'number', |
|
relevance: 0, |
|
variants: [ |
|
// decimal floating-point-literal (subsumes decimal-literal) |
|
{ |
|
match: `\\b(${decimalDigits})(\\.(${decimalDigits}))?` + `([eE][+-]?(${decimalDigits}))?\\b` |
|
}, |
|
// hexadecimal floating-point-literal (subsumes hexadecimal-literal) |
|
{ |
|
match: `\\b0x(${hexDigits})(\\.(${hexDigits}))?` + `([pP][+-]?(${decimalDigits}))?\\b` |
|
}, |
|
// octal-literal |
|
{ |
|
match: /\b0o([0-7]_*)+\b/ |
|
}, |
|
// binary-literal |
|
{ |
|
match: /\b0b([01]_*)+\b/ |
|
} |
|
] |
|
}; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#grammar_string-literal |
|
const ESCAPED_CHARACTER = (rawDelimiter = "") => ({ |
|
className: 'subst', |
|
variants: [ |
|
{ |
|
match: concat(/\\/, rawDelimiter, /[0\\tnr"']/) |
|
}, |
|
{ |
|
match: concat(/\\/, rawDelimiter, /u\{[0-9a-fA-F]{1,8}\}/) |
|
} |
|
] |
|
}); |
|
const ESCAPED_NEWLINE = (rawDelimiter = "") => ({ |
|
className: 'subst', |
|
match: concat(/\\/, rawDelimiter, /[\t ]*(?:[\r\n]|\r\n)/) |
|
}); |
|
const INTERPOLATION = (rawDelimiter = "") => ({ |
|
className: 'subst', |
|
label: "interpol", |
|
begin: concat(/\\/, rawDelimiter, /\(/), |
|
end: /\)/ |
|
}); |
|
const MULTILINE_STRING = (rawDelimiter = "") => ({ |
|
begin: concat(rawDelimiter, /"""/), |
|
end: concat(/"""/, rawDelimiter), |
|
contains: [ |
|
ESCAPED_CHARACTER(rawDelimiter), |
|
ESCAPED_NEWLINE(rawDelimiter), |
|
INTERPOLATION(rawDelimiter) |
|
] |
|
}); |
|
const SINGLE_LINE_STRING = (rawDelimiter = "") => ({ |
|
begin: concat(rawDelimiter, /"/), |
|
end: concat(/"/, rawDelimiter), |
|
contains: [ |
|
ESCAPED_CHARACTER(rawDelimiter), |
|
INTERPOLATION(rawDelimiter) |
|
] |
|
}); |
|
const STRING = { |
|
className: 'string', |
|
variants: [ |
|
MULTILINE_STRING(), |
|
MULTILINE_STRING("#"), |
|
MULTILINE_STRING("##"), |
|
MULTILINE_STRING("###"), |
|
SINGLE_LINE_STRING(), |
|
SINGLE_LINE_STRING("#"), |
|
SINGLE_LINE_STRING("##"), |
|
SINGLE_LINE_STRING("###") |
|
] |
|
}; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID412 |
|
const QUOTED_IDENTIFIER = { |
|
match: concat(/`/, identifier, /`/) |
|
}; |
|
const IMPLICIT_PARAMETER = { |
|
className: 'variable', |
|
match: /\$\d+/ |
|
}; |
|
const PROPERTY_WRAPPER_PROJECTION = { |
|
className: 'variable', |
|
match: `\\$${identifierCharacter}+` |
|
}; |
|
const IDENTIFIERS = [ |
|
QUOTED_IDENTIFIER, |
|
IMPLICIT_PARAMETER, |
|
PROPERTY_WRAPPER_PROJECTION |
|
]; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/Attributes.html |
|
const AVAILABLE_ATTRIBUTE = { |
|
match: /(@|#)available/, |
|
className: "keyword", |
|
starts: { |
|
contains: [ |
|
{ |
|
begin: /\(/, |
|
end: /\)/, |
|
keywords: availabilityKeywords, |
|
contains: [ |
|
...OPERATORS, |
|
NUMBER, |
|
STRING |
|
] |
|
} |
|
] |
|
} |
|
}; |
|
const KEYWORD_ATTRIBUTE = { |
|
className: 'keyword', |
|
match: concat(/@/, either(...keywordAttributes)) |
|
}; |
|
const USER_DEFINED_ATTRIBUTE = { |
|
className: 'meta', |
|
match: concat(/@/, identifier) |
|
}; |
|
const ATTRIBUTES = [ |
|
AVAILABLE_ATTRIBUTE, |
|
KEYWORD_ATTRIBUTE, |
|
USER_DEFINED_ATTRIBUTE |
|
]; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/Types.html |
|
const TYPE = { |
|
match: lookahead(/\b[A-Z]/), |
|
relevance: 0, |
|
contains: [ |
|
{ // Common Apple frameworks, for relevance boost |
|
className: 'type', |
|
match: concat(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/, identifierCharacter, '+') |
|
}, |
|
{ // Type identifier |
|
className: 'type', |
|
match: typeIdentifier, |
|
relevance: 0 |
|
}, |
|
{ // Optional type |
|
match: /[?!]+/, |
|
relevance: 0 |
|
}, |
|
{ // Variadic parameter |
|
match: /\.\.\./, |
|
relevance: 0 |
|
}, |
|
{ // Protocol composition |
|
match: concat(/\s+&\s+/, lookahead(typeIdentifier)), |
|
relevance: 0 |
|
} |
|
] |
|
}; |
|
const GENERIC_ARGUMENTS = { |
|
begin: /</, |
|
end: />/, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
...COMMENTS, |
|
...KEYWORD_MODES, |
|
...ATTRIBUTES, |
|
OPERATOR_GUARD, |
|
TYPE |
|
] |
|
}; |
|
TYPE.contains.push(GENERIC_ARGUMENTS); |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID552 |
|
// Prevents element names from being highlighted as keywords. |
|
const TUPLE_ELEMENT_NAME = { |
|
match: concat(identifier, /\s*:/), |
|
keywords: "_|0", |
|
relevance: 0 |
|
}; |
|
// Matches tuples as well as the parameter list of a function type. |
|
const TUPLE = { |
|
begin: /\(/, |
|
end: /\)/, |
|
relevance: 0, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
'self', |
|
TUPLE_ELEMENT_NAME, |
|
...COMMENTS, |
|
...KEYWORD_MODES, |
|
...BUILT_INS, |
|
...OPERATORS, |
|
NUMBER, |
|
STRING, |
|
...IDENTIFIERS, |
|
...ATTRIBUTES, |
|
TYPE |
|
] |
|
}; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID362 |
|
// Matches both the keyword func and the function title. |
|
// Grouping these lets us differentiate between the operator function < |
|
// and the start of the generic parameter clause (also <). |
|
const FUNC_PLUS_TITLE = { |
|
beginKeywords: 'func', |
|
contains: [ |
|
{ |
|
className: 'title', |
|
match: either(QUOTED_IDENTIFIER.match, identifier, operator), |
|
// Required to make sure the opening < of the generic parameter clause |
|
// isn't parsed as a second title. |
|
endsParent: true, |
|
relevance: 0 |
|
}, |
|
WHITESPACE |
|
] |
|
}; |
|
const GENERIC_PARAMETERS = { |
|
begin: /</, |
|
end: />/, |
|
contains: [ |
|
...COMMENTS, |
|
TYPE |
|
] |
|
}; |
|
const FUNCTION_PARAMETER_NAME = { |
|
begin: either( |
|
lookahead(concat(identifier, /\s*:/)), |
|
lookahead(concat(identifier, /\s+/, identifier, /\s*:/)) |
|
), |
|
end: /:/, |
|
relevance: 0, |
|
contains: [ |
|
{ |
|
className: 'keyword', |
|
match: /\b_\b/ |
|
}, |
|
{ |
|
className: 'params', |
|
match: identifier |
|
} |
|
] |
|
}; |
|
const FUNCTION_PARAMETERS = { |
|
begin: /\(/, |
|
end: /\)/, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
FUNCTION_PARAMETER_NAME, |
|
...COMMENTS, |
|
...KEYWORD_MODES, |
|
...OPERATORS, |
|
NUMBER, |
|
STRING, |
|
...ATTRIBUTES, |
|
TYPE, |
|
TUPLE |
|
], |
|
endsParent: true, |
|
illegal: /["']/ |
|
}; |
|
const FUNCTION = { |
|
className: 'function', |
|
match: lookahead(/\bfunc\b/), |
|
contains: [ |
|
FUNC_PLUS_TITLE, |
|
GENERIC_PARAMETERS, |
|
FUNCTION_PARAMETERS, |
|
WHITESPACE |
|
], |
|
illegal: [ |
|
/\[/, |
|
/%/ |
|
] |
|
}; |
|
|
|
// https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID375 |
|
// https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID379 |
|
const INIT_SUBSCRIPT = { |
|
className: 'function', |
|
match: /\b(subscript|init[?!]?)\s*(?=[<(])/, |
|
keywords: { |
|
keyword: "subscript init init? init!", |
|
$pattern: /\w+[?!]?/ |
|
}, |
|
contains: [ |
|
GENERIC_PARAMETERS, |
|
FUNCTION_PARAMETERS, |
|
WHITESPACE |
|
], |
|
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: 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: typeIdentifier, |
|
relevance: 0 |
|
}, |
|
{ |
|
begin: /{/, |
|
end: /}/, |
|
relevance: 0, |
|
endsParent: true, |
|
keywords: [ |
|
...precedencegroupKeywords, |
|
...literals |
|
], |
|
contains: [ TYPE ] |
|
} |
|
] |
|
}; |
|
|
|
// Add supported submodes to string interpolation. |
|
for (const variant of STRING.variants) { |
|
const interpolation = variant.contains.find(mode => mode.label === "interpol"); |
|
// TODO: Interpolation can contain any expression, so there's room for improvement here. |
|
interpolation.keywords = KEYWORDS; |
|
const submodes = [ |
|
...KEYWORD_MODES, |
|
...BUILT_INS, |
|
...OPERATORS, |
|
NUMBER, |
|
STRING, |
|
...IDENTIFIERS |
|
]; |
|
interpolation.contains = [ |
|
...submodes, |
|
{ |
|
begin: /\(/, |
|
end: /\)/, |
|
contains: [ |
|
'self', |
|
...submodes |
|
] |
|
} |
|
]; |
|
} |
|
|
|
return { |
|
name: 'Swift', |
|
keywords: KEYWORDS, |
|
contains: [ |
|
...COMMENTS, |
|
FUNCTION, |
|
INIT_SUBSCRIPT, |
|
{ |
|
className: 'class', |
|
beginKeywords: 'struct protocol class extension enum', |
|
end: '\\{', |
|
excludeEnd: true, |
|
keywords: KEYWORDS, |
|
contains: [ |
|
hljs.inherit(hljs.TITLE_MODE, { |
|
begin: /[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/ |
|
}), |
|
...KEYWORD_MODES |
|
] |
|
}, |
|
OPERATOR_DECLARATION, |
|
PRECEDENCEGROUP, |
|
{ |
|
beginKeywords: 'import', |
|
end: /$/, |
|
contains: [ ...COMMENTS ], |
|
relevance: 0 |
|
}, |
|
...KEYWORD_MODES, |
|
...BUILT_INS, |
|
...OPERATORS, |
|
NUMBER, |
|
STRING, |
|
...IDENTIFIERS, |
|
...ATTRIBUTES, |
|
TYPE, |
|
TUPLE |
|
] |
|
}; |
|
} |
|
|
|
module.exports = swift;
|
|
|