blob: 42487fad89e9a9f9fb716cdab83ba5fff735bd0f [file] [log] [blame]
%{
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
}
}