[d24f17c] | 1 | /**
|
---|
| 2 | * @param {string} value
|
---|
| 3 | * @returns {RegExp}
|
---|
| 4 | * */
|
---|
| 5 |
|
---|
| 6 | /**
|
---|
| 7 | * @param {RegExp | string } re
|
---|
| 8 | * @returns {string}
|
---|
| 9 | */
|
---|
| 10 | function source(re) {
|
---|
| 11 | if (!re) return null;
|
---|
| 12 | if (typeof re === "string") return re;
|
---|
| 13 |
|
---|
| 14 | return re.source;
|
---|
| 15 | }
|
---|
| 16 |
|
---|
| 17 | /**
|
---|
| 18 | * @param {RegExp | string } re
|
---|
| 19 | * @returns {string}
|
---|
| 20 | */
|
---|
| 21 | function lookahead(re) {
|
---|
| 22 | return concat('(?=', re, ')');
|
---|
| 23 | }
|
---|
| 24 |
|
---|
| 25 | /**
|
---|
| 26 | * @param {...(RegExp | string) } args
|
---|
| 27 | * @returns {string}
|
---|
| 28 | */
|
---|
| 29 | function concat(...args) {
|
---|
| 30 | const joined = args.map((x) => source(x)).join("");
|
---|
| 31 | return joined;
|
---|
| 32 | }
|
---|
| 33 |
|
---|
| 34 | /**
|
---|
| 35 | * Any of the passed expresssions may match
|
---|
| 36 | *
|
---|
| 37 | * Creates a huge this | this | that | that match
|
---|
| 38 | * @param {(RegExp | string)[] } args
|
---|
| 39 | * @returns {string}
|
---|
| 40 | */
|
---|
| 41 | function either(...args) {
|
---|
| 42 | const joined = '(' + args.map((x) => source(x)).join("|") + ")";
|
---|
| 43 | return joined;
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | const keywordWrapper = keyword => concat(
|
---|
| 47 | /\b/,
|
---|
| 48 | keyword,
|
---|
| 49 | /\w$/.test(keyword) ? /\b/ : /\B/
|
---|
| 50 | );
|
---|
| 51 |
|
---|
| 52 | // Keywords that require a leading dot.
|
---|
| 53 | const dotKeywords = [
|
---|
| 54 | 'Protocol', // contextual
|
---|
| 55 | 'Type' // contextual
|
---|
| 56 | ].map(keywordWrapper);
|
---|
| 57 |
|
---|
| 58 | // Keywords that may have a leading dot.
|
---|
| 59 | const optionalDotKeywords = [
|
---|
| 60 | 'init',
|
---|
| 61 | 'self'
|
---|
| 62 | ].map(keywordWrapper);
|
---|
| 63 |
|
---|
| 64 | // should register as keyword, not type
|
---|
| 65 | const keywordTypes = [
|
---|
| 66 | 'Any',
|
---|
| 67 | 'Self'
|
---|
| 68 | ];
|
---|
| 69 |
|
---|
| 70 | // Regular keywords and literals.
|
---|
| 71 | const keywords = [
|
---|
| 72 | // strings below will be fed into the regular `keywords` engine while regex
|
---|
| 73 | // will result in additional modes being created to scan for those keywords to
|
---|
| 74 | // avoid conflicts with other rules
|
---|
| 75 | 'associatedtype',
|
---|
| 76 | 'async',
|
---|
| 77 | 'await',
|
---|
| 78 | /as\?/, // operator
|
---|
| 79 | /as!/, // operator
|
---|
| 80 | 'as', // operator
|
---|
| 81 | 'break',
|
---|
| 82 | 'case',
|
---|
| 83 | 'catch',
|
---|
| 84 | 'class',
|
---|
| 85 | 'continue',
|
---|
| 86 | 'convenience', // contextual
|
---|
| 87 | 'default',
|
---|
| 88 | 'defer',
|
---|
| 89 | 'deinit',
|
---|
| 90 | 'didSet', // contextual
|
---|
| 91 | 'do',
|
---|
| 92 | 'dynamic', // contextual
|
---|
| 93 | 'else',
|
---|
| 94 | 'enum',
|
---|
| 95 | 'extension',
|
---|
| 96 | 'fallthrough',
|
---|
| 97 | /fileprivate\(set\)/,
|
---|
| 98 | 'fileprivate',
|
---|
| 99 | 'final', // contextual
|
---|
| 100 | 'for',
|
---|
| 101 | 'func',
|
---|
| 102 | 'get', // contextual
|
---|
| 103 | 'guard',
|
---|
| 104 | 'if',
|
---|
| 105 | 'import',
|
---|
| 106 | 'indirect', // contextual
|
---|
| 107 | 'infix', // contextual
|
---|
| 108 | /init\?/,
|
---|
| 109 | /init!/,
|
---|
| 110 | 'inout',
|
---|
| 111 | /internal\(set\)/,
|
---|
| 112 | 'internal',
|
---|
| 113 | 'in',
|
---|
| 114 | 'is', // operator
|
---|
| 115 | 'lazy', // contextual
|
---|
| 116 | 'let',
|
---|
| 117 | 'mutating', // contextual
|
---|
| 118 | 'nonmutating', // contextual
|
---|
| 119 | /open\(set\)/, // contextual
|
---|
| 120 | 'open', // contextual
|
---|
| 121 | 'operator',
|
---|
| 122 | 'optional', // contextual
|
---|
| 123 | 'override', // contextual
|
---|
| 124 | 'postfix', // contextual
|
---|
| 125 | 'precedencegroup',
|
---|
| 126 | 'prefix', // contextual
|
---|
| 127 | /private\(set\)/,
|
---|
| 128 | 'private',
|
---|
| 129 | 'protocol',
|
---|
| 130 | /public\(set\)/,
|
---|
| 131 | 'public',
|
---|
| 132 | 'repeat',
|
---|
| 133 | 'required', // contextual
|
---|
| 134 | 'rethrows',
|
---|
| 135 | 'return',
|
---|
| 136 | 'set', // contextual
|
---|
| 137 | 'some', // contextual
|
---|
| 138 | 'static',
|
---|
| 139 | 'struct',
|
---|
| 140 | 'subscript',
|
---|
| 141 | 'super',
|
---|
| 142 | 'switch',
|
---|
| 143 | 'throws',
|
---|
| 144 | 'throw',
|
---|
| 145 | /try\?/, // operator
|
---|
| 146 | /try!/, // operator
|
---|
| 147 | 'try', // operator
|
---|
| 148 | 'typealias',
|
---|
| 149 | /unowned\(safe\)/, // contextual
|
---|
| 150 | /unowned\(unsafe\)/, // contextual
|
---|
| 151 | 'unowned', // contextual
|
---|
| 152 | 'var',
|
---|
| 153 | 'weak', // contextual
|
---|
| 154 | 'where',
|
---|
| 155 | 'while',
|
---|
| 156 | 'willSet' // contextual
|
---|
| 157 | ];
|
---|
| 158 |
|
---|
| 159 | // NOTE: Contextual keywords are reserved only in specific contexts.
|
---|
| 160 | // Ideally, these should be matched using modes to avoid false positives.
|
---|
| 161 |
|
---|
| 162 | // Literals.
|
---|
| 163 | const literals = [
|
---|
| 164 | 'false',
|
---|
| 165 | 'nil',
|
---|
| 166 | 'true'
|
---|
| 167 | ];
|
---|
| 168 |
|
---|
| 169 | // Keywords used in precedence groups.
|
---|
| 170 | const precedencegroupKeywords = [
|
---|
| 171 | 'assignment',
|
---|
| 172 | 'associativity',
|
---|
| 173 | 'higherThan',
|
---|
| 174 | 'left',
|
---|
| 175 | 'lowerThan',
|
---|
| 176 | 'none',
|
---|
| 177 | 'right'
|
---|
| 178 | ];
|
---|
| 179 |
|
---|
| 180 | // Keywords that start with a number sign (#).
|
---|
| 181 | // #available is handled separately.
|
---|
| 182 | const numberSignKeywords = [
|
---|
| 183 | '#colorLiteral',
|
---|
| 184 | '#column',
|
---|
| 185 | '#dsohandle',
|
---|
| 186 | '#else',
|
---|
| 187 | '#elseif',
|
---|
| 188 | '#endif',
|
---|
| 189 | '#error',
|
---|
| 190 | '#file',
|
---|
| 191 | '#fileID',
|
---|
| 192 | '#fileLiteral',
|
---|
| 193 | '#filePath',
|
---|
| 194 | '#function',
|
---|
| 195 | '#if',
|
---|
| 196 | '#imageLiteral',
|
---|
| 197 | '#keyPath',
|
---|
| 198 | '#line',
|
---|
| 199 | '#selector',
|
---|
| 200 | '#sourceLocation',
|
---|
| 201 | '#warn_unqualified_access',
|
---|
| 202 | '#warning'
|
---|
| 203 | ];
|
---|
| 204 |
|
---|
| 205 | // Global functions in the Standard Library.
|
---|
| 206 | const builtIns = [
|
---|
| 207 | 'abs',
|
---|
| 208 | 'all',
|
---|
| 209 | 'any',
|
---|
| 210 | 'assert',
|
---|
| 211 | 'assertionFailure',
|
---|
| 212 | 'debugPrint',
|
---|
| 213 | 'dump',
|
---|
| 214 | 'fatalError',
|
---|
| 215 | 'getVaList',
|
---|
| 216 | 'isKnownUniquelyReferenced',
|
---|
| 217 | 'max',
|
---|
| 218 | 'min',
|
---|
| 219 | 'numericCast',
|
---|
| 220 | 'pointwiseMax',
|
---|
| 221 | 'pointwiseMin',
|
---|
| 222 | 'precondition',
|
---|
| 223 | 'preconditionFailure',
|
---|
| 224 | 'print',
|
---|
| 225 | 'readLine',
|
---|
| 226 | 'repeatElement',
|
---|
| 227 | 'sequence',
|
---|
| 228 | 'stride',
|
---|
| 229 | 'swap',
|
---|
| 230 | 'swift_unboxFromSwiftValueWithType',
|
---|
| 231 | 'transcode',
|
---|
| 232 | 'type',
|
---|
| 233 | 'unsafeBitCast',
|
---|
| 234 | 'unsafeDowncast',
|
---|
| 235 | 'withExtendedLifetime',
|
---|
| 236 | 'withUnsafeMutablePointer',
|
---|
| 237 | 'withUnsafePointer',
|
---|
| 238 | 'withVaList',
|
---|
| 239 | 'withoutActuallyEscaping',
|
---|
| 240 | 'zip'
|
---|
| 241 | ];
|
---|
| 242 |
|
---|
| 243 | // Valid first characters for operators.
|
---|
| 244 | const operatorHead = either(
|
---|
| 245 | /[/=\-+!*%<>&|^~?]/,
|
---|
| 246 | /[\u00A1-\u00A7]/,
|
---|
| 247 | /[\u00A9\u00AB]/,
|
---|
| 248 | /[\u00AC\u00AE]/,
|
---|
| 249 | /[\u00B0\u00B1]/,
|
---|
| 250 | /[\u00B6\u00BB\u00BF\u00D7\u00F7]/,
|
---|
| 251 | /[\u2016-\u2017]/,
|
---|
| 252 | /[\u2020-\u2027]/,
|
---|
| 253 | /[\u2030-\u203E]/,
|
---|
| 254 | /[\u2041-\u2053]/,
|
---|
| 255 | /[\u2055-\u205E]/,
|
---|
| 256 | /[\u2190-\u23FF]/,
|
---|
| 257 | /[\u2500-\u2775]/,
|
---|
| 258 | /[\u2794-\u2BFF]/,
|
---|
| 259 | /[\u2E00-\u2E7F]/,
|
---|
| 260 | /[\u3001-\u3003]/,
|
---|
| 261 | /[\u3008-\u3020]/,
|
---|
| 262 | /[\u3030]/
|
---|
| 263 | );
|
---|
| 264 |
|
---|
| 265 | // Valid characters for operators.
|
---|
| 266 | const operatorCharacter = either(
|
---|
| 267 | operatorHead,
|
---|
| 268 | /[\u0300-\u036F]/,
|
---|
| 269 | /[\u1DC0-\u1DFF]/,
|
---|
| 270 | /[\u20D0-\u20FF]/,
|
---|
| 271 | /[\uFE00-\uFE0F]/,
|
---|
| 272 | /[\uFE20-\uFE2F]/
|
---|
| 273 | // TODO: The following characters are also allowed, but the regex isn't supported yet.
|
---|
| 274 | // /[\u{E0100}-\u{E01EF}]/u
|
---|
| 275 | );
|
---|
| 276 |
|
---|
| 277 | // Valid operator.
|
---|
| 278 | const operator = concat(operatorHead, operatorCharacter, '*');
|
---|
| 279 |
|
---|
| 280 | // Valid first characters for identifiers.
|
---|
| 281 | const identifierHead = either(
|
---|
| 282 | /[a-zA-Z_]/,
|
---|
| 283 | /[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,
|
---|
| 284 | /[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,
|
---|
| 285 | /[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,
|
---|
| 286 | /[\u1E00-\u1FFF]/,
|
---|
| 287 | /[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,
|
---|
| 288 | /[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,
|
---|
| 289 | /[\u2C00-\u2DFF\u2E80-\u2FFF]/,
|
---|
| 290 | /[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,
|
---|
| 291 | /[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,
|
---|
| 292 | /[\uFE47-\uFEFE\uFF00-\uFFFD]/ // Should be /[\uFE47-\uFFFD]/, but we have to exclude FEFF.
|
---|
| 293 | // The following characters are also allowed, but the regexes aren't supported yet.
|
---|
| 294 | // /[\u{10000}-\u{1FFFD}\u{20000-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}]/u,
|
---|
| 295 | // /[\u{50000}-\u{5FFFD}\u{60000-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}]/u,
|
---|
| 296 | // /[\u{90000}-\u{9FFFD}\u{A0000-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}]/u,
|
---|
| 297 | // /[\u{D0000}-\u{DFFFD}\u{E0000-\u{EFFFD}]/u
|
---|
| 298 | );
|
---|
| 299 |
|
---|
| 300 | // Valid characters for identifiers.
|
---|
| 301 | const identifierCharacter = either(
|
---|
| 302 | identifierHead,
|
---|
| 303 | /\d/,
|
---|
| 304 | /[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/
|
---|
| 305 | );
|
---|
| 306 |
|
---|
| 307 | // Valid identifier.
|
---|
| 308 | const identifier = concat(identifierHead, identifierCharacter, '*');
|
---|
| 309 |
|
---|
| 310 | // Valid type identifier.
|
---|
| 311 | const typeIdentifier = concat(/[A-Z]/, identifierCharacter, '*');
|
---|
| 312 |
|
---|
| 313 | // Built-in attributes, which are highlighted as keywords.
|
---|
| 314 | // @available is handled separately.
|
---|
| 315 | const keywordAttributes = [
|
---|
| 316 | 'autoclosure',
|
---|
| 317 | concat(/convention\(/, either('swift', 'block', 'c'), /\)/),
|
---|
| 318 | 'discardableResult',
|
---|
| 319 | 'dynamicCallable',
|
---|
| 320 | 'dynamicMemberLookup',
|
---|
| 321 | 'escaping',
|
---|
| 322 | 'frozen',
|
---|
| 323 | 'GKInspectable',
|
---|
| 324 | 'IBAction',
|
---|
| 325 | 'IBDesignable',
|
---|
| 326 | 'IBInspectable',
|
---|
| 327 | 'IBOutlet',
|
---|
| 328 | 'IBSegueAction',
|
---|
| 329 | 'inlinable',
|
---|
| 330 | 'main',
|
---|
| 331 | 'nonobjc',
|
---|
| 332 | 'NSApplicationMain',
|
---|
| 333 | 'NSCopying',
|
---|
| 334 | 'NSManaged',
|
---|
| 335 | concat(/objc\(/, identifier, /\)/),
|
---|
| 336 | 'objc',
|
---|
| 337 | 'objcMembers',
|
---|
| 338 | 'propertyWrapper',
|
---|
| 339 | 'requires_stored_property_inits',
|
---|
| 340 | 'testable',
|
---|
| 341 | 'UIApplicationMain',
|
---|
| 342 | 'unknown',
|
---|
| 343 | 'usableFromInline'
|
---|
| 344 | ];
|
---|
| 345 |
|
---|
| 346 | // Contextual keywords used in @available and #available.
|
---|
| 347 | const availabilityKeywords = [
|
---|
| 348 | 'iOS',
|
---|
| 349 | 'iOSApplicationExtension',
|
---|
| 350 | 'macOS',
|
---|
| 351 | 'macOSApplicationExtension',
|
---|
| 352 | 'macCatalyst',
|
---|
| 353 | 'macCatalystApplicationExtension',
|
---|
| 354 | 'watchOS',
|
---|
| 355 | 'watchOSApplicationExtension',
|
---|
| 356 | 'tvOS',
|
---|
| 357 | 'tvOSApplicationExtension',
|
---|
| 358 | 'swift'
|
---|
| 359 | ];
|
---|
| 360 |
|
---|
| 361 | /*
|
---|
| 362 | Language: Swift
|
---|
| 363 | Description: Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns.
|
---|
| 364 | Author: Steven Van Impe <steven.vanimpe@icloud.com>
|
---|
| 365 | Contributors: Chris Eidhof <chris@eidhof.nl>, Nate Cook <natecook@gmail.com>, Alexander Lichter <manniL@gmx.net>, Richard Gibson <gibson042@github>
|
---|
| 366 | Website: https://swift.org
|
---|
| 367 | Category: common, system
|
---|
| 368 | */
|
---|
| 369 |
|
---|
| 370 | /** @type LanguageFn */
|
---|
| 371 | function swift(hljs) {
|
---|
| 372 | const WHITESPACE = {
|
---|
| 373 | match: /\s+/,
|
---|
| 374 | relevance: 0
|
---|
| 375 | };
|
---|
| 376 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID411
|
---|
| 377 | const BLOCK_COMMENT = hljs.COMMENT(
|
---|
| 378 | '/\\*',
|
---|
| 379 | '\\*/',
|
---|
| 380 | {
|
---|
| 381 | contains: [ 'self' ]
|
---|
| 382 | }
|
---|
| 383 | );
|
---|
| 384 | const COMMENTS = [
|
---|
| 385 | hljs.C_LINE_COMMENT_MODE,
|
---|
| 386 | BLOCK_COMMENT
|
---|
| 387 | ];
|
---|
| 388 |
|
---|
| 389 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID413
|
---|
| 390 | // https://docs.swift.org/swift-book/ReferenceManual/zzSummaryOfTheGrammar.html
|
---|
| 391 | const DOT_KEYWORD = {
|
---|
| 392 | className: 'keyword',
|
---|
| 393 | begin: concat(/\./, lookahead(either(...dotKeywords, ...optionalDotKeywords))),
|
---|
| 394 | end: either(...dotKeywords, ...optionalDotKeywords),
|
---|
| 395 | excludeBegin: true
|
---|
| 396 | };
|
---|
| 397 | const KEYWORD_GUARD = {
|
---|
| 398 | // Consume .keyword to prevent highlighting properties and methods as keywords.
|
---|
| 399 | match: concat(/\./, either(...keywords)),
|
---|
| 400 | relevance: 0
|
---|
| 401 | };
|
---|
| 402 | const PLAIN_KEYWORDS = keywords
|
---|
| 403 | .filter(kw => typeof kw === 'string')
|
---|
| 404 | .concat([ "_|0" ]); // seems common, so 0 relevance
|
---|
| 405 | const REGEX_KEYWORDS = keywords
|
---|
| 406 | .filter(kw => typeof kw !== 'string') // find regex
|
---|
| 407 | .concat(keywordTypes)
|
---|
| 408 | .map(keywordWrapper);
|
---|
| 409 | const KEYWORD = {
|
---|
| 410 | variants: [
|
---|
| 411 | {
|
---|
| 412 | className: 'keyword',
|
---|
| 413 | match: either(...REGEX_KEYWORDS, ...optionalDotKeywords)
|
---|
| 414 | }
|
---|
| 415 | ]
|
---|
| 416 | };
|
---|
| 417 | // find all the regular keywords
|
---|
| 418 | const KEYWORDS = {
|
---|
| 419 | $pattern: either(
|
---|
| 420 | /\b\w+/, // regular keywords
|
---|
| 421 | /#\w+/ // number keywords
|
---|
| 422 | ),
|
---|
| 423 | keyword: PLAIN_KEYWORDS
|
---|
| 424 | .concat(numberSignKeywords),
|
---|
| 425 | literal: literals
|
---|
| 426 | };
|
---|
| 427 | const KEYWORD_MODES = [
|
---|
| 428 | DOT_KEYWORD,
|
---|
| 429 | KEYWORD_GUARD,
|
---|
| 430 | KEYWORD
|
---|
| 431 | ];
|
---|
| 432 |
|
---|
| 433 | // https://github.com/apple/swift/tree/main/stdlib/public/core
|
---|
| 434 | const BUILT_IN_GUARD = {
|
---|
| 435 | // Consume .built_in to prevent highlighting properties and methods.
|
---|
| 436 | match: concat(/\./, either(...builtIns)),
|
---|
| 437 | relevance: 0
|
---|
| 438 | };
|
---|
| 439 | const BUILT_IN = {
|
---|
| 440 | className: 'built_in',
|
---|
| 441 | match: concat(/\b/, either(...builtIns), /(?=\()/)
|
---|
| 442 | };
|
---|
| 443 | const BUILT_INS = [
|
---|
| 444 | BUILT_IN_GUARD,
|
---|
| 445 | BUILT_IN
|
---|
| 446 | ];
|
---|
| 447 |
|
---|
| 448 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418
|
---|
| 449 | const OPERATOR_GUARD = {
|
---|
| 450 | // Prevent -> from being highlighting as an operator.
|
---|
| 451 | match: /->/,
|
---|
| 452 | relevance: 0
|
---|
| 453 | };
|
---|
| 454 | const OPERATOR = {
|
---|
| 455 | className: 'operator',
|
---|
| 456 | relevance: 0,
|
---|
| 457 | variants: [
|
---|
| 458 | {
|
---|
| 459 | match: operator
|
---|
| 460 | },
|
---|
| 461 | {
|
---|
| 462 | // dot-operator: only operators that start with a dot are allowed to use dots as
|
---|
| 463 | // characters (..., ...<, .*, etc). So there rule here is: a dot followed by one or more
|
---|
| 464 | // characters that may also include dots.
|
---|
| 465 | match: `\\.(\\.|${operatorCharacter})+`
|
---|
| 466 | }
|
---|
| 467 | ]
|
---|
| 468 | };
|
---|
| 469 | const OPERATORS = [
|
---|
| 470 | OPERATOR_GUARD,
|
---|
| 471 | OPERATOR
|
---|
| 472 | ];
|
---|
| 473 |
|
---|
| 474 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#grammar_numeric-literal
|
---|
| 475 | // TODO: Update for leading `-` after lookbehind is supported everywhere
|
---|
| 476 | const decimalDigits = '([0-9]_*)+';
|
---|
| 477 | const hexDigits = '([0-9a-fA-F]_*)+';
|
---|
| 478 | const NUMBER = {
|
---|
| 479 | className: 'number',
|
---|
| 480 | relevance: 0,
|
---|
| 481 | variants: [
|
---|
| 482 | // decimal floating-point-literal (subsumes decimal-literal)
|
---|
| 483 | {
|
---|
| 484 | match: `\\b(${decimalDigits})(\\.(${decimalDigits}))?` + `([eE][+-]?(${decimalDigits}))?\\b`
|
---|
| 485 | },
|
---|
| 486 | // hexadecimal floating-point-literal (subsumes hexadecimal-literal)
|
---|
| 487 | {
|
---|
| 488 | match: `\\b0x(${hexDigits})(\\.(${hexDigits}))?` + `([pP][+-]?(${decimalDigits}))?\\b`
|
---|
| 489 | },
|
---|
| 490 | // octal-literal
|
---|
| 491 | {
|
---|
| 492 | match: /\b0o([0-7]_*)+\b/
|
---|
| 493 | },
|
---|
| 494 | // binary-literal
|
---|
| 495 | {
|
---|
| 496 | match: /\b0b([01]_*)+\b/
|
---|
| 497 | }
|
---|
| 498 | ]
|
---|
| 499 | };
|
---|
| 500 |
|
---|
| 501 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#grammar_string-literal
|
---|
| 502 | const ESCAPED_CHARACTER = (rawDelimiter = "") => ({
|
---|
| 503 | className: 'subst',
|
---|
| 504 | variants: [
|
---|
| 505 | {
|
---|
| 506 | match: concat(/\\/, rawDelimiter, /[0\\tnr"']/)
|
---|
| 507 | },
|
---|
| 508 | {
|
---|
| 509 | match: concat(/\\/, rawDelimiter, /u\{[0-9a-fA-F]{1,8}\}/)
|
---|
| 510 | }
|
---|
| 511 | ]
|
---|
| 512 | });
|
---|
| 513 | const ESCAPED_NEWLINE = (rawDelimiter = "") => ({
|
---|
| 514 | className: 'subst',
|
---|
| 515 | match: concat(/\\/, rawDelimiter, /[\t ]*(?:[\r\n]|\r\n)/)
|
---|
| 516 | });
|
---|
| 517 | const INTERPOLATION = (rawDelimiter = "") => ({
|
---|
| 518 | className: 'subst',
|
---|
| 519 | label: "interpol",
|
---|
| 520 | begin: concat(/\\/, rawDelimiter, /\(/),
|
---|
| 521 | end: /\)/
|
---|
| 522 | });
|
---|
| 523 | const MULTILINE_STRING = (rawDelimiter = "") => ({
|
---|
| 524 | begin: concat(rawDelimiter, /"""/),
|
---|
| 525 | end: concat(/"""/, rawDelimiter),
|
---|
| 526 | contains: [
|
---|
| 527 | ESCAPED_CHARACTER(rawDelimiter),
|
---|
| 528 | ESCAPED_NEWLINE(rawDelimiter),
|
---|
| 529 | INTERPOLATION(rawDelimiter)
|
---|
| 530 | ]
|
---|
| 531 | });
|
---|
| 532 | const SINGLE_LINE_STRING = (rawDelimiter = "") => ({
|
---|
| 533 | begin: concat(rawDelimiter, /"/),
|
---|
| 534 | end: concat(/"/, rawDelimiter),
|
---|
| 535 | contains: [
|
---|
| 536 | ESCAPED_CHARACTER(rawDelimiter),
|
---|
| 537 | INTERPOLATION(rawDelimiter)
|
---|
| 538 | ]
|
---|
| 539 | });
|
---|
| 540 | const STRING = {
|
---|
| 541 | className: 'string',
|
---|
| 542 | variants: [
|
---|
| 543 | MULTILINE_STRING(),
|
---|
| 544 | MULTILINE_STRING("#"),
|
---|
| 545 | MULTILINE_STRING("##"),
|
---|
| 546 | MULTILINE_STRING("###"),
|
---|
| 547 | SINGLE_LINE_STRING(),
|
---|
| 548 | SINGLE_LINE_STRING("#"),
|
---|
| 549 | SINGLE_LINE_STRING("##"),
|
---|
| 550 | SINGLE_LINE_STRING("###")
|
---|
| 551 | ]
|
---|
| 552 | };
|
---|
| 553 |
|
---|
| 554 | // https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID412
|
---|
| 555 | const QUOTED_IDENTIFIER = {
|
---|
| 556 | match: concat(/`/, identifier, /`/)
|
---|
| 557 | };
|
---|
| 558 | const IMPLICIT_PARAMETER = {
|
---|
| 559 | className: 'variable',
|
---|
| 560 | match: /\$\d+/
|
---|
| 561 | };
|
---|
| 562 | const PROPERTY_WRAPPER_PROJECTION = {
|
---|
| 563 | className: 'variable',
|
---|
| 564 | match: `\\$${identifierCharacter}+`
|
---|
| 565 | };
|
---|
| 566 | const IDENTIFIERS = [
|
---|
| 567 | QUOTED_IDENTIFIER,
|
---|
| 568 | IMPLICIT_PARAMETER,
|
---|
| 569 | PROPERTY_WRAPPER_PROJECTION
|
---|
| 570 | ];
|
---|
| 571 |
|
---|
| 572 | // https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
|
---|
| 573 | const AVAILABLE_ATTRIBUTE = {
|
---|
| 574 | match: /(@|#)available/,
|
---|
| 575 | className: "keyword",
|
---|
| 576 | starts: {
|
---|
| 577 | contains: [
|
---|
| 578 | {
|
---|
| 579 | begin: /\(/,
|
---|
| 580 | end: /\)/,
|
---|
| 581 | keywords: availabilityKeywords,
|
---|
| 582 | contains: [
|
---|
| 583 | ...OPERATORS,
|
---|
| 584 | NUMBER,
|
---|
| 585 | STRING
|
---|
| 586 | ]
|
---|
| 587 | }
|
---|
| 588 | ]
|
---|
| 589 | }
|
---|
| 590 | };
|
---|
| 591 | const KEYWORD_ATTRIBUTE = {
|
---|
| 592 | className: 'keyword',
|
---|
| 593 | match: concat(/@/, either(...keywordAttributes))
|
---|
| 594 | };
|
---|
| 595 | const USER_DEFINED_ATTRIBUTE = {
|
---|
| 596 | className: 'meta',
|
---|
| 597 | match: concat(/@/, identifier)
|
---|
| 598 | };
|
---|
| 599 | const ATTRIBUTES = [
|
---|
| 600 | AVAILABLE_ATTRIBUTE,
|
---|
| 601 | KEYWORD_ATTRIBUTE,
|
---|
| 602 | USER_DEFINED_ATTRIBUTE
|
---|
| 603 | ];
|
---|
| 604 |
|
---|
| 605 | // https://docs.swift.org/swift-book/ReferenceManual/Types.html
|
---|
| 606 | const TYPE = {
|
---|
| 607 | match: lookahead(/\b[A-Z]/),
|
---|
| 608 | relevance: 0,
|
---|
| 609 | contains: [
|
---|
| 610 | { // Common Apple frameworks, for relevance boost
|
---|
| 611 | className: 'type',
|
---|
| 612 | match: concat(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/, identifierCharacter, '+')
|
---|
| 613 | },
|
---|
| 614 | { // Type identifier
|
---|
| 615 | className: 'type',
|
---|
| 616 | match: typeIdentifier,
|
---|
| 617 | relevance: 0
|
---|
| 618 | },
|
---|
| 619 | { // Optional type
|
---|
| 620 | match: /[?!]+/,
|
---|
| 621 | relevance: 0
|
---|
| 622 | },
|
---|
| 623 | { // Variadic parameter
|
---|
| 624 | match: /\.\.\./,
|
---|
| 625 | relevance: 0
|
---|
| 626 | },
|
---|
| 627 | { // Protocol composition
|
---|
| 628 | match: concat(/\s+&\s+/, lookahead(typeIdentifier)),
|
---|
| 629 | relevance: 0
|
---|
| 630 | }
|
---|
| 631 | ]
|
---|
| 632 | };
|
---|
| 633 | const GENERIC_ARGUMENTS = {
|
---|
| 634 | begin: /</,
|
---|
| 635 | end: />/,
|
---|
| 636 | keywords: KEYWORDS,
|
---|
| 637 | contains: [
|
---|
| 638 | ...COMMENTS,
|
---|
| 639 | ...KEYWORD_MODES,
|
---|
| 640 | ...ATTRIBUTES,
|
---|
| 641 | OPERATOR_GUARD,
|
---|
| 642 | TYPE
|
---|
| 643 | ]
|
---|
| 644 | };
|
---|
| 645 | TYPE.contains.push(GENERIC_ARGUMENTS);
|
---|
| 646 |
|
---|
| 647 | // https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID552
|
---|
| 648 | // Prevents element names from being highlighted as keywords.
|
---|
| 649 | const TUPLE_ELEMENT_NAME = {
|
---|
| 650 | match: concat(identifier, /\s*:/),
|
---|
| 651 | keywords: "_|0",
|
---|
| 652 | relevance: 0
|
---|
| 653 | };
|
---|
| 654 | // Matches tuples as well as the parameter list of a function type.
|
---|
| 655 | const TUPLE = {
|
---|
| 656 | begin: /\(/,
|
---|
| 657 | end: /\)/,
|
---|
| 658 | relevance: 0,
|
---|
| 659 | keywords: KEYWORDS,
|
---|
| 660 | contains: [
|
---|
| 661 | 'self',
|
---|
| 662 | TUPLE_ELEMENT_NAME,
|
---|
| 663 | ...COMMENTS,
|
---|
| 664 | ...KEYWORD_MODES,
|
---|
| 665 | ...BUILT_INS,
|
---|
| 666 | ...OPERATORS,
|
---|
| 667 | NUMBER,
|
---|
| 668 | STRING,
|
---|
| 669 | ...IDENTIFIERS,
|
---|
| 670 | ...ATTRIBUTES,
|
---|
| 671 | TYPE
|
---|
| 672 | ]
|
---|
| 673 | };
|
---|
| 674 |
|
---|
| 675 | // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID362
|
---|
| 676 | // Matches both the keyword func and the function title.
|
---|
| 677 | // Grouping these lets us differentiate between the operator function <
|
---|
| 678 | // and the start of the generic parameter clause (also <).
|
---|
| 679 | const FUNC_PLUS_TITLE = {
|
---|
| 680 | beginKeywords: 'func',
|
---|
| 681 | contains: [
|
---|
| 682 | {
|
---|
| 683 | className: 'title',
|
---|
| 684 | match: either(QUOTED_IDENTIFIER.match, identifier, operator),
|
---|
| 685 | // Required to make sure the opening < of the generic parameter clause
|
---|
| 686 | // isn't parsed as a second title.
|
---|
| 687 | endsParent: true,
|
---|
| 688 | relevance: 0
|
---|
| 689 | },
|
---|
| 690 | WHITESPACE
|
---|
| 691 | ]
|
---|
| 692 | };
|
---|
| 693 | const GENERIC_PARAMETERS = {
|
---|
| 694 | begin: /</,
|
---|
| 695 | end: />/,
|
---|
| 696 | contains: [
|
---|
| 697 | ...COMMENTS,
|
---|
| 698 | TYPE
|
---|
| 699 | ]
|
---|
| 700 | };
|
---|
| 701 | const FUNCTION_PARAMETER_NAME = {
|
---|
| 702 | begin: either(
|
---|
| 703 | lookahead(concat(identifier, /\s*:/)),
|
---|
| 704 | lookahead(concat(identifier, /\s+/, identifier, /\s*:/))
|
---|
| 705 | ),
|
---|
| 706 | end: /:/,
|
---|
| 707 | relevance: 0,
|
---|
| 708 | contains: [
|
---|
| 709 | {
|
---|
| 710 | className: 'keyword',
|
---|
| 711 | match: /\b_\b/
|
---|
| 712 | },
|
---|
| 713 | {
|
---|
| 714 | className: 'params',
|
---|
| 715 | match: identifier
|
---|
| 716 | }
|
---|
| 717 | ]
|
---|
| 718 | };
|
---|
| 719 | const FUNCTION_PARAMETERS = {
|
---|
| 720 | begin: /\(/,
|
---|
| 721 | end: /\)/,
|
---|
| 722 | keywords: KEYWORDS,
|
---|
| 723 | contains: [
|
---|
| 724 | FUNCTION_PARAMETER_NAME,
|
---|
| 725 | ...COMMENTS,
|
---|
| 726 | ...KEYWORD_MODES,
|
---|
| 727 | ...OPERATORS,
|
---|
| 728 | NUMBER,
|
---|
| 729 | STRING,
|
---|
| 730 | ...ATTRIBUTES,
|
---|
| 731 | TYPE,
|
---|
| 732 | TUPLE
|
---|
| 733 | ],
|
---|
| 734 | endsParent: true,
|
---|
| 735 | illegal: /["']/
|
---|
| 736 | };
|
---|
| 737 | const FUNCTION = {
|
---|
| 738 | className: 'function',
|
---|
| 739 | match: lookahead(/\bfunc\b/),
|
---|
| 740 | contains: [
|
---|
| 741 | FUNC_PLUS_TITLE,
|
---|
| 742 | GENERIC_PARAMETERS,
|
---|
| 743 | FUNCTION_PARAMETERS,
|
---|
| 744 | WHITESPACE
|
---|
| 745 | ],
|
---|
| 746 | illegal: [
|
---|
| 747 | /\[/,
|
---|
| 748 | /%/
|
---|
| 749 | ]
|
---|
| 750 | };
|
---|
| 751 |
|
---|
| 752 | // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID375
|
---|
| 753 | // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID379
|
---|
| 754 | const INIT_SUBSCRIPT = {
|
---|
| 755 | className: 'function',
|
---|
| 756 | match: /\b(subscript|init[?!]?)\s*(?=[<(])/,
|
---|
| 757 | keywords: {
|
---|
| 758 | keyword: "subscript init init? init!",
|
---|
| 759 | $pattern: /\w+[?!]?/
|
---|
| 760 | },
|
---|
| 761 | contains: [
|
---|
| 762 | GENERIC_PARAMETERS,
|
---|
| 763 | FUNCTION_PARAMETERS,
|
---|
| 764 | WHITESPACE
|
---|
| 765 | ],
|
---|
| 766 | illegal: /\[|%/
|
---|
| 767 | };
|
---|
| 768 | // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380
|
---|
| 769 | const OPERATOR_DECLARATION = {
|
---|
| 770 | beginKeywords: 'operator',
|
---|
| 771 | end: hljs.MATCH_NOTHING_RE,
|
---|
| 772 | contains: [
|
---|
| 773 | {
|
---|
| 774 | className: 'title',
|
---|
| 775 | match: operator,
|
---|
| 776 | endsParent: true,
|
---|
| 777 | relevance: 0
|
---|
| 778 | }
|
---|
| 779 | ]
|
---|
| 780 | };
|
---|
| 781 |
|
---|
| 782 | // https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID550
|
---|
| 783 | const PRECEDENCEGROUP = {
|
---|
| 784 | beginKeywords: 'precedencegroup',
|
---|
| 785 | end: hljs.MATCH_NOTHING_RE,
|
---|
| 786 | contains: [
|
---|
| 787 | {
|
---|
| 788 | className: 'title',
|
---|
| 789 | match: typeIdentifier,
|
---|
| 790 | relevance: 0
|
---|
| 791 | },
|
---|
| 792 | {
|
---|
| 793 | begin: /{/,
|
---|
| 794 | end: /}/,
|
---|
| 795 | relevance: 0,
|
---|
| 796 | endsParent: true,
|
---|
| 797 | keywords: [
|
---|
| 798 | ...precedencegroupKeywords,
|
---|
| 799 | ...literals
|
---|
| 800 | ],
|
---|
| 801 | contains: [ TYPE ]
|
---|
| 802 | }
|
---|
| 803 | ]
|
---|
| 804 | };
|
---|
| 805 |
|
---|
| 806 | // Add supported submodes to string interpolation.
|
---|
| 807 | for (const variant of STRING.variants) {
|
---|
| 808 | const interpolation = variant.contains.find(mode => mode.label === "interpol");
|
---|
| 809 | // TODO: Interpolation can contain any expression, so there's room for improvement here.
|
---|
| 810 | interpolation.keywords = KEYWORDS;
|
---|
| 811 | const submodes = [
|
---|
| 812 | ...KEYWORD_MODES,
|
---|
| 813 | ...BUILT_INS,
|
---|
| 814 | ...OPERATORS,
|
---|
| 815 | NUMBER,
|
---|
| 816 | STRING,
|
---|
| 817 | ...IDENTIFIERS
|
---|
| 818 | ];
|
---|
| 819 | interpolation.contains = [
|
---|
| 820 | ...submodes,
|
---|
| 821 | {
|
---|
| 822 | begin: /\(/,
|
---|
| 823 | end: /\)/,
|
---|
| 824 | contains: [
|
---|
| 825 | 'self',
|
---|
| 826 | ...submodes
|
---|
| 827 | ]
|
---|
| 828 | }
|
---|
| 829 | ];
|
---|
| 830 | }
|
---|
| 831 |
|
---|
| 832 | return {
|
---|
| 833 | name: 'Swift',
|
---|
| 834 | keywords: KEYWORDS,
|
---|
| 835 | contains: [
|
---|
| 836 | ...COMMENTS,
|
---|
| 837 | FUNCTION,
|
---|
| 838 | INIT_SUBSCRIPT,
|
---|
| 839 | {
|
---|
| 840 | className: 'class',
|
---|
| 841 | beginKeywords: 'struct protocol class extension enum',
|
---|
| 842 | end: '\\{',
|
---|
| 843 | excludeEnd: true,
|
---|
| 844 | keywords: KEYWORDS,
|
---|
| 845 | contains: [
|
---|
| 846 | hljs.inherit(hljs.TITLE_MODE, {
|
---|
| 847 | begin: /[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/
|
---|
| 848 | }),
|
---|
| 849 | ...KEYWORD_MODES
|
---|
| 850 | ]
|
---|
| 851 | },
|
---|
| 852 | OPERATOR_DECLARATION,
|
---|
| 853 | PRECEDENCEGROUP,
|
---|
| 854 | {
|
---|
| 855 | beginKeywords: 'import',
|
---|
| 856 | end: /$/,
|
---|
| 857 | contains: [ ...COMMENTS ],
|
---|
| 858 | relevance: 0
|
---|
| 859 | },
|
---|
| 860 | ...KEYWORD_MODES,
|
---|
| 861 | ...BUILT_INS,
|
---|
| 862 | ...OPERATORS,
|
---|
| 863 | NUMBER,
|
---|
| 864 | STRING,
|
---|
| 865 | ...IDENTIFIERS,
|
---|
| 866 | ...ATTRIBUTES,
|
---|
| 867 | TYPE,
|
---|
| 868 | TUPLE
|
---|
| 869 | ]
|
---|
| 870 | };
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | module.exports = swift;
|
---|