swift.js 20 KB

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