swift.js 22 KB

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