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