swift.js 20 KB

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