Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ Language improvements:

- enh(cpp): Support C++ pack expansion in function arguments [Martin Dørum][]
- enh(makefile): Add `make` as an alias (#2883) [tripleee][]
- fix(http) avoid recursive sublanguage and tighten rules (#2893) [Josh Goebel][]
- enh(swift) Improved grammar for strings (#2819) [Steven Van Impe][]
- enh(swift) Grammar improvements (#2908) [Steven Van Impe][]
- New grammar for keywords and built-ins
- Added support for operator highlighting
- New grammar for attributes
- Added support for quoted identifiers, implicit parameters, and property wrapper projections
- Support for more complex expressions in string interpolation
- fix(http) avoid recursive sublanguage and tighten rules (#2893) [Josh Goebel][]
- fix(asciidoc): Handle section titles level 5 (#2868) [Vaibhav Chanana][]

Grammar improvements:
Expand Down
307 changes: 307 additions & 0 deletions src/languages/lib/kws_swift.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
import {
concat,
either
} from '../../lib/regex.js';

export const keywordWrapper = keyword => concat(
/\b/,
keyword,
/\w$/.test(keyword) ? /\b/ : /\B/
);

// Keywords that require a leading dot.
export const dotKeywords = [
'Protocol', // contextual
'Type' // contextual
].map(keywordWrapper);

// Keywords that may have a leading dot.
export const optionalDotKeywords = [
'init',
'self'
].map(keywordWrapper);

// should register as keyword, not type
export const keywordTypes = [
'Any',
'Self'
];

// Regular keywords and literals.
export 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',
/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.

// TODO: Create a PRECEDENCE_GROUP mode to match the remaining contextual keywords:
// assignment associativity higherThan left lowerThan none right
// These aren't included in the list because they result in mostly false positives.

// Literals.
export const literals = [
'false',
'nil',
'true'
];

// Keywords that start with a number sign (#).
// #available is handled separately.
export 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.
export 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.
export 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.
export 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.
export const operator = concat(operatorHead, operatorCharacter, '*');

// Valid first characters for identifiers.
export 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-\uFFFD]/
// 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.
export const identifierCharacter = either(
identifierHead,
/\d/,
/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/
);

// Valid identifier.
export const identifier = concat(identifierHead, identifierCharacter, '*');

// Built-in attributes, which are highlighted as keywords.
// @available is handled separately.
export 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.
export const availabilityKeywords = [
'iOS',
'iOSApplicationExtension',
'macOS',
'macOSApplicationExtension',
'macCatalyst',
'macCatalystApplicationExtension',
'watchOS',
'watchOSApplicationExtension',
'tvOS',
'tvOSApplicationExtension',
'swift'
];
Loading