forked from swiftlang/swift-syntax
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSyntaxClassifier.swift.gyb
145 lines (134 loc) · 4.81 KB
/
SyntaxClassifier.swift.gyb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
%{
from gyb_syntax_support import *
# -*- mode: Swift -*-
# Ignore the following admonition it applies to the resulting .swift file only
}%
//// Automatically Generated From SyntaxClassifier.swift.gyb.
//// Do Not Edit Directly!
//===------------ SyntaxClassifier.swift.gyb - Syntax Collection ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
public enum SyntaxClassification {
% for classification in SYNTAX_CLASSIFICATIONS:
% for line in dedented_lines(classification.description):
% if line:
/// ${line}
% end
% end
case ${classification.swift_name}
% end
}
fileprivate class _SyntaxClassifier: SyntaxVisitor {
/// Don't classify nodes with these IDs or any of their children
var skipNodeIds: Set<SyntaxNodeId> = []
/// The top of the `contextStack` determines the classification for all tokens
/// encountered that do not have a native classification. If `force` is `true`
/// the top of the stack also determines the classification of tokens with a
/// native classification
private var contextStack: [(classification: SyntaxClassification, force: Bool)] =
[(classification: .none, force: false)]
/// The classifications that have determined so far are collected in this
/// dictionary
var classifications: [TokenSyntax: SyntaxClassification] = [:]
private func visit(
_ node: Syntax,
classification: SyntaxClassification,
force: Bool = false
) {
contextStack.append((classification: classification, force: force))
visit(node)
contextStack.removeLast()
}
private func getContextFreeClassificationForTokenKind(_ tokenKind: TokenKind)
-> SyntaxClassification? {
switch (tokenKind) {
% for token in SYNTAX_TOKENS:
case .${token.swift_kind()}:
% if token.classification:
return SyntaxClassification.${token.classification.swift_name}
% else:
return nil
% end
% end
case .eof:
return SyntaxClassification.none
}
}
override func visit(_ token: TokenSyntax) {
// FIXME: We need to come up with some way in which the SyntaxClassifier can
// classify trivia (i.e. comments). In particular we need to be able to
// look into the comments to find things like URLs or keywords like MARK.
var classification = contextStack.last!.classification
if !contextStack.last!.force {
if let contextFreeClassification =
getContextFreeClassificationForTokenKind(token.tokenKind) {
classification = contextFreeClassification
}
if case .unknown = token.tokenKind, token.text.starts(with: "\"") {
classification = .stringLiteral
} else if case .identifier = token.tokenKind,
token.text.starts(with: "<#") && token.text.hasSuffix("#>") {
classification = .editorPlaceholder
}
}
assert(classifications[token] == nil,
"\(token) has already been classified")
classifications[token] = classification
}
% for node in SYNTAX_NODES:
% if is_visitable(node):
override func visit(_ node: ${node.name}) {
if skipNodeIds.contains(node.raw.id) {
return
}
% if node.is_unknown() or node.is_syntax_collection():
super.visit(node)
% else:
% for child in node.children:
% if child.is_optional:
if let ${child.swift_name} = node.${child.swift_name} {
% if child.classification:
visit(${child.swift_name},
classification: .${child.classification.swift_name},
force: ${"true" if child.force_classification else "false"})
% else:
visit(${child.swift_name})
% end
}
% else:
% if child.classification:
visit(node.${child.swift_name},
classification: .${child.classification.swift_name},
force: ${"true" if child.force_classification else "false"})
% else:
visit(node.${child.swift_name})
% end
% end
% end
% end
}
% end
% end
}
public enum SyntaxClassifier {
/// Classify all tokens in the given syntax tree for syntax highlighting.
/// If a `IncrementalTreeTransferSession` is passed, only nodes that have
/// changed since the last transfer will be classified.
public static func classifyTokensInTree(
_ syntaxTree: SourceFileSyntax,
skipNodes: Set<SyntaxNodeId> = []
) -> [TokenSyntax: SyntaxClassification] {
let classifier = _SyntaxClassifier()
classifier.skipNodeIds = skipNodes
classifier.visit(syntaxTree)
return classifier.classifications
}
}