Skip to content

Commit b7063a8

Browse files
authored
Merge pull request #925 from ahoppen/ahoppen/document-color-syntax
Implement the document color request using the SwiftSyntax tree
2 parents 7772f07 + 1b3fd5f commit b7063a8

File tree

1 file changed

+47
-85
lines changed

1 file changed

+47
-85
lines changed

Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift

Lines changed: 47 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -607,106 +607,68 @@ extension SwiftLanguageServer {
607607
}
608608

609609
public func documentColor(_ req: DocumentColorRequest) async throws -> [ColorInformation] {
610-
let keys = self.keys
611-
612610
guard let snapshot = self.documentManager.latestSnapshot(req.textDocument.uri) else {
613611
logger.error("failed to find snapshot for url \(req.textDocument.uri.forLogging)")
614612
return []
615613
}
616614

617-
let helperDocumentName = "DocumentColor:" + snapshot.uri.pseudoPath
618-
let skreq = SKDRequestDictionary(sourcekitd: self.sourcekitd)
619-
skreq[keys.request] = self.requests.editor_open
620-
skreq[keys.name] = helperDocumentName
621-
skreq[keys.sourcetext] = snapshot.text
622-
skreq[keys.syntactic_only] = 1
623-
624-
let dict = try await self.sourcekitd.send(skreq)
625-
defer {
626-
let closeHelperReq = SKDRequestDictionary(sourcekitd: self.sourcekitd)
627-
closeHelperReq[keys.request] = self.requests.editor_close
628-
closeHelperReq[keys.name] = helperDocumentName
629-
// FIXME: (async) We might receive two concurrent document color requests for the
630-
// same document, in which race to open/close a document with the same name in
631-
// sourcekitd. The solution is to either
632-
// - Not open the helper document and instead rely on the document that is already
633-
// open or
634-
// - Prefix the helper document with a UUID to make sure the two concurrent
635-
// requests operate on different documents as far as sourcekitd is concerned.
636-
Task {
637-
_ = try? await self.sourcekitd.send(closeHelperReq)
638-
}
639-
}
615+
let syntaxTree = await syntaxTreeManager.syntaxTree(for: snapshot)
640616

641-
guard let results: SKDResponseArray = dict[self.keys.substructure] else {
642-
return []
643-
}
617+
class ColorLiteralFinder: SyntaxVisitor {
618+
let snapshot: DocumentSnapshot
619+
var result: [ColorInformation] = []
644620

645-
func colorInformation(dict: SKDResponseDictionary) -> ColorInformation? {
646-
guard let kind: sourcekitd_uid_t = dict[self.keys.kind],
647-
kind == self.values.expr_object_literal,
648-
let name: String = dict[self.keys.name],
649-
name == "colorLiteral",
650-
let offset: Int = dict[self.keys.offset],
651-
let start: Position = snapshot.positionOf(utf8Offset: offset),
652-
let length: Int = dict[self.keys.length],
653-
let end: Position = snapshot.positionOf(utf8Offset: offset + length),
654-
let substructure: SKDResponseArray = dict[self.keys.substructure]
655-
else {
656-
return nil
621+
init(snapshot: DocumentSnapshot) {
622+
self.snapshot = snapshot
623+
super.init(viewMode: .sourceAccurate)
657624
}
658-
var red, green, blue, alpha: Double?
659-
substructure.forEach { (i: Int, value: SKDResponseDictionary) in
660-
guard let name: String = value[self.keys.name],
661-
let bodyoffset: Int = value[self.keys.bodyoffset],
662-
let bodylength: Int = value[self.keys.bodylength]
663-
else {
664-
return true
625+
626+
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
627+
guard node.macroName.text == "colorLiteral" else {
628+
return .visitChildren
665629
}
666-
let view = snapshot.text.utf8
667-
let bodyStart = view.index(view.startIndex, offsetBy: bodyoffset)
668-
let bodyEnd = view.index(view.startIndex, offsetBy: bodyoffset + bodylength)
669-
let value = String(view[bodyStart..<bodyEnd]).flatMap(Double.init)
670-
switch name {
671-
case "red":
672-
red = value
673-
case "green":
674-
green = value
675-
case "blue":
676-
blue = value
677-
case "alpha":
678-
alpha = value
679-
default:
680-
break
630+
func extractArgument(_ argumentName: String, from arguments: LabeledExprListSyntax) -> Double? {
631+
for argument in arguments {
632+
if argument.label?.text == argumentName {
633+
if let integer = argument.expression.as(IntegerLiteralExprSyntax.self) {
634+
return Double(integer.literal.text)
635+
} else if let integer = argument.expression.as(FloatLiteralExprSyntax.self) {
636+
return Double(integer.literal.text)
637+
}
638+
}
639+
}
640+
return nil
641+
}
642+
guard let red = extractArgument("red", from: node.arguments),
643+
let green = extractArgument("green", from: node.arguments),
644+
let blue = extractArgument("blue", from: node.arguments),
645+
let alpha = extractArgument("alpha", from: node.arguments)
646+
else {
647+
return .skipChildren
681648
}
682-
return true
683-
}
684-
if let red = red,
685-
let green = green,
686-
let blue = blue,
687-
let alpha = alpha
688-
{
689-
let color = Color(red: red, green: green, blue: blue, alpha: alpha)
690-
return ColorInformation(range: start..<end, color: color)
691-
} else {
692-
return nil
693-
}
694-
}
695649

696-
func colorInformation(array: SKDResponseArray) -> [ColorInformation] {
697-
var result: [ColorInformation] = []
698-
array.forEach { (i: Int, value: SKDResponseDictionary) in
699-
if let documentSymbol = colorInformation(dict: value) {
700-
result.append(documentSymbol)
701-
} else if let substructure: SKDResponseArray = value[self.keys.substructure] {
702-
result += colorInformation(array: substructure)
650+
guard let startPosition = snapshot.position(of: node.position),
651+
let endPosition = snapshot.position(of: node.endPosition)
652+
else {
653+
return .skipChildren
703654
}
704-
return true
655+
656+
result.append(
657+
ColorInformation(
658+
range: startPosition..<endPosition,
659+
color: Color(red: red, green: green, blue: blue, alpha: alpha)
660+
)
661+
)
662+
663+
return .skipChildren
705664
}
706-
return result
707665
}
708666

709-
return colorInformation(array: results)
667+
try Task.checkCancellation()
668+
669+
let colorLiteralFinder = ColorLiteralFinder(snapshot: snapshot)
670+
colorLiteralFinder.walk(syntaxTree)
671+
return colorLiteralFinder.result
710672
}
711673

712674
public func colorPresentation(_ req: ColorPresentationRequest) async throws -> [ColorPresentation] {

0 commit comments

Comments
 (0)