yggdrasil/syntaxes/Swift.sublime-syntax
xfy 188e607ec2 fix(highlight): 补全 Swift 语法高亮
Swift.sublime-syntax 之前是残缺定义,expression 上下文仅包含
空白/字符串/数字,导致关键字、类型、函数等全部无法高亮。

补全声明关键字(import/func/class/let 等)、控制流(if/return/
async/await)、标准库类型(Int/String/Array)、函数声明与调用、
属性(@objc)等上下文,并调整 include 顺序使函数声明名优先于
函数调用识别。

新增两个回归测试覆盖关键字/函数/类型/字符串高亮。
2026-06-16 14:17:17 +08:00

278 lines
10 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
# This file is a porting of the Swift Language Reference
# https://docs.swift.org/swift-book/ReferenceManual/zzSummaryOfTheGrammar.html
name: Swift
scope: source.swift
file_extensions:
- swift
variables:
identifier_head_unicode: \x00A8\x00AA\x00AD\x00AF\x00B2\x00B5\x00B7\x00BA\x00BC\x00BE\x00C0\x00D6\x00D8\x00F6\x00F8\x00FF\x0100\x02FF\x0370\x167F\x1681\x180D\x180F\x1DBF\x1E00\x1FFF\x200B\x200D\x202A\x202E\x203F\x2040\x2054\x2060\x206F\x2070\x20CF\x2100\x218F\x2460\x24FF\x2776\x2793\x2C00\x2DFF\x2E80\x2FFF\x3004\x3007\x3021\x302F\x3031\x303F\x3040\xD7FF\xF900\xFD3D\xFD40\xFDCF\xFDF0\xFE1F\xFE30\xFE44\xFE47\xFFFD\x10000\x1FFFD\x20000\x2FFFD\x30000\x3FFFD\x40000\x4FFFD\x50000\x5FFFD\x60000\x6FFFD\x70000\x7FFFD\x80000\x8FFFD\x90000\x9FFFD\xA0000\xAFFFD\xB0000\xBFFFD\xC0000\xCFFFD\xD0000\xDFFFD\xE0000\xEFFFD
identifier_head: '[a-zA-Z_{{identifier_head_unicode}}]'
identifier_character: '[0-9\x0300\x036F\x1DC0\x1DFF\x20D0\x20FF\xFE20\xFE2F{{identifier_head}}]'
line_break: '(?:\x0D\x0A?|\x0A)'
inline_space: '[\x09\x20]'
contexts:
main:
- include: expression
whitespace:
- include: line-break
- include: inline-spaces
- include: comment
- include: multiline-comment
- match: '[\x00\x0B\x0C]+'
line-break:
- match: '{{line_break}}'
inline-spaces:
- match: '{{inline_space}}+'
comment:
- match: //
scope: punctuation.definition.comment.begin.swift
push:
- meta_scope: comment.line.swift
# TODO: add MARK section navigation
- match: '{{line_break}}'
pop: 1
multiline-comment:
- match: '/\*'
scope: punctuation.definition.comment.begin.swift
push:
- meta_scope: comment.block.swift
- include: multiline-comment
- match: '\*/'
scope: punctuation.definition.comment.end.swift
pop: 1
implicit-parameter-name:
- match: '\$[0-9]+\b'
property-wrapper-projection:
- match: '\${{identifier_character}}+'
identifier:
- match: '\b{{identifier_head}}{{identifier_character}}*'
scope: variable
- match: '`{{identifier_head}}{{identifier_character}}*`'
scope: variable
- include: implicit-parameter-name
- include: property-wrapper-projection
integer-literal:
- match: \b(0b)([01][01_]*)
scope: meta.number.integer.binary.swift
captures:
1: constant.numeric.base.swift
2: constant.numeric.value.swift
- match: \b(0o)([0-7][0-7_]*)
scope: meta.number.integer.octal.swift
captures:
1: constant.numeric.base.swift
2: constant.numeric.value.swift
- match: \b(0x)([0-9a-fA-F][0-9a-fA-F_]*)
scope: meta.number.integer.hexadecimal.swift
captures:
1: constant.numeric.base.swift
2: constant.numeric.value.swift
- match: \b[0-9][0-9_]*
scope: meta.number.integer.decimal.swift constant.numeric.value.swift
floating-point-literal:
- match: \b[0-9][0-9_]*(\.)[0-9][0-9_]*(?:[eE][\-+]?[0-9][0-9_]*)?
scope: meta.number.float.decimal.swift constant.numeric.value.swift
captures:
1: punctuation.separator.decimal.swift
- match: \b(0x)([0-9a-fA-F][0-9a-fA-F_]*(\.)[0-9a-fA-F][0-9a-fA-F_]*(?:[pP][\-+]?[0-9][0-9_]*)?)
scope: meta.number.float.hexadecimal.swift
captures:
1: constant.numeric.base.swift
2: constant.numeric.value.swift
3: punctuation.separator.decimal.swift
string-literal:
- match: '#+"""'
scope: punctuation.definition.string.begin.swift
push:
- meta_scope: meta.string.swift string.quoted.double.block.swift
- match: '\\#+[0\\tnr"'']'
scope: constant.character.escape.swift
- match: '\\#+u\{[0-9a-fA-F]{1,8}\}'
scope: constant.character.escape.unicode.swift
- match: '\\#+\('
scope: punctuation.section.interpolation.begin.swift
push:
- clear_scopes: 1
- meta_scope: meta.interpolation.swift
- match: '\)'
scope: punctuation.section.interpolation.end.swift
pop: 1
- match: '\\{{inline_space}}*{{line_break}}'
scope: punctuation.separator.continuation.line.swift
- match: '"""#+'
scope: punctuation.definition.string.end.swift
pop: 1
- match: '"""'
scope: punctuation.definition.string.begin.swift
push:
- meta_scope: meta.string.swift string.quoted.double.block.swift
- match: '\\[0\\tnr"'']'
scope: constant.character.escape.swift
- match: '\\u\{[0-9a-fA-F]{1,8}\}'
scope: constant.character.escape.unicode.swift
- match: '\\\('
scope: punctuation.section.interpolation.begin.swift
push:
- clear_scopes: 1
- meta_scope: meta.interpolation.swift
- include: expression
- match: '\)'
scope: punctuation.section.interpolation.end.swift
pop: 1
- match: '\\{{inline_space}}*{{line_break}}'
scope: punctuation.separator.continuation.line.swift
- match: '"""'
scope: punctuation.definition.string.end.swift
pop: 1
- match: '#+"'
scope: punctuation.definition.string.begin.swift
push:
- meta_scope: meta.string.swift string.quoted.double.swift
- match: '\\#+[0\\tnr"'']'
scope: constant.character.escape.swift
- match: '\\#+u\{[0-9a-fA-F]{1,8}\}'
scope: constant.character.escape.unicode.swift
- match: '\\#+\('
scope: punctuation.section.interpolation.begin.swift
push:
- clear_scopes: 1
- meta_scope: meta.interpolation.swift
- include: expression
- match: '\)'
scope: punctuation.section.interpolation.end.swift
pop: 1
- match: '{{line_break}}'
scope: invalid.illegal.unclosed-string.swift
pop: 1
- match: '"#+'
scope: punctuation.definition.string.end.swift
pop: 1
- match: '"'
scope: punctuation.definition.string.begin.swift
push:
- meta_scope: meta.string.swift string.quoted.double.swift
- match: '\\[0\\tnr"'']'
scope: constant.character.escape.swift
- match: '\\u\{[0-9a-fA-F]{1,8}\}'
scope: constant.character.escape.unicode.swift
- match: '\\\('
scope: punctuation.section.interpolation.begin.swift
push:
- clear_scopes: 1
- meta_scope: meta.interpolation.swift
- include: expression
- match: '\)'
scope: punctuation.section.interpolation.end.swift
pop: 1
- match: '\\'
scope: invalid.illegal.unexpected-text.swift
- match: '{{line_break}}'
scope: invalid.illegal.unclosed-string.swift
pop: 1
- match: '"'
scope: punctuation.definition.string.end.swift
pop: 1
expression:
- include: whitespace
- include: comments
- include: attribute
- include: string-literal
- include: floating-point-literal
- include: integer-literal
- include: function-declaration
- include: declaration-keywords
- include: control-keywords
- include: modifiers
- include: types
- include: constants
- include: function-calls
- include: types-and-identifiers
comments:
- include: comment
- include: multiline-comment
# 属性:@MainActor、@objc、@available(iOS 14, *)
attribute:
- match: '@\b{{identifier_head}}{{identifier_character}}*'
scope: meta.attribute.swift entity.other.attribute-name.swift
# 声明类关键字
declaration-keywords:
- match: '\b(?:import|func|class|struct|enum|protocol|extension|let|var|typealias|init|deinit|subscript|operator|precedencegroup|actor|macro)\b'
scope: keyword.declaration.swift
- match: '\b(?:associatedtype|fileprivate|internal|private|public|open|static|final|lazy|weak|unowned|dynamic|inout|mutating|nonmutating|convenience|required|optional|override|indirect|borrowing|consuming|distributed|nonisolated|isolated)\b'
scope: storage.modifier.swift
# 控制流关键字
control-keywords:
- match: '\b(?:if|else|guard|switch|case|default|for|in|while|repeat|do|where|fallthrough|break|continue|return|throw|throws|rethrows|try|catch|defer|as|is|async|await|yield|some|any|each)\b'
scope: keyword.control.swift
- match: '\b(?:true|false|nil|self|Self|super)\b'
scope: constant.language.swift
# try?/try!/as?/as!
- match: '\b(?:try|as)([?!])'
captures:
1: keyword.control.swift
# 单独列出的修饰符(已在 declaration-keywords 内,此处保留占位以便扩展)
modifiers: []
# 内置/标准库常用类型
types:
- match: '\b(?:Int|UInt|Int8|Int16|Int32|Int64|UInt8|UInt16|UInt32|UInt64|Float|Double|Bool|String|Character|Void|Optional|Array|Dictionary|Set|Range|ClosedRange|Any|Never|Result)\b'
scope: support.type.swift
# 常量与全局函数
constants:
- match: '\b(?:print|println|debugPrint|fatalError|assert|assertionFailure|precondition|preconditionFailure|min|max|abs|count|append|contains|map|filter|reduce|compactMap|flatMap|sorted|first|last|isEmpty)\b'
scope: support.function.swift
# 函数声明func name(...) 与 init/subscript
function-declaration:
- match: '\b(func)\s+({{identifier_head}}{{identifier_character}}*|`[^`]+`|operator)'
captures:
1: keyword.declaration.swift
2: entity.name.function.swift
- match: '\b(init)\b'
scope: keyword.declaration.swift entity.name.function.swift
- match: '\b(deinit)\b'
scope: keyword.declaration.swift
- match: '\b(subscript)\b'
scope: keyword.declaration.swift
# 函数调用name( 或 name <Generic>(
function-calls:
- match: '\b{{identifier_head}}{{identifier_character}}*(?=\s*(?:<[^(){}\n]*>)?\s*\()'
scope: variable.function.swift
- match: '\b{{identifier_head}}{{identifier_character}}*(?=\s*\{)'
scope: variable.function.swift
# 普通标识符(大写识别为类型风格,小写为变量)
types-and-identifiers:
- match: '\b[A-Z]{{identifier_character}}*\b'
scope: entity.name.type.swift
- match: '\b{{identifier_head}}{{identifier_character}}*\b'
scope: variable.swift