diff --git a/Sources/LanguageServerProtocol/ClientCapabilities.swift b/Sources/LanguageServerProtocol/ClientCapabilities.swift index 5ad462d3c..07b663514 100644 --- a/Sources/LanguageServerProtocol/ClientCapabilities.swift +++ b/Sources/LanguageServerProtocol/ClientCapabilities.swift @@ -225,11 +225,23 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { /// Capabilities specific to `SignatureInformation`. public struct SignatureInformation: Hashable, Codable { + public struct ParameterInformation: Hashable, Codable { + /// The client supports processing label offsets instead of a simple label string. + var labelOffsetSupport: Bool? = nil + + public init(labelOffsetSupport: Bool? = nil) { + self.labelOffsetSupport = labelOffsetSupport + } + } + /// Documentation formats supported by the client from most to least preferred. - public var signatureInformation: [MarkupKind]? = nil + public var documentationFormat: [MarkupKind]? = nil + + public var parameterInformation: ParameterInformation? = nil - public init(signatureInformation: [MarkupKind]? = nil) { - self.signatureInformation = signatureInformation + public init(signatureInformation: [MarkupKind]? = nil, parameterInformation: ParameterInformation? = nil) { + self.documentationFormat = signatureInformation + self.parameterInformation = parameterInformation } } @@ -276,6 +288,19 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { } } + public struct DynamicRegistrationLinkSupportCapability: Hashable, Codable { + /// Whether the client supports dynamic registaration of this request. + public var dynamicRegistration: Bool? = nil + + /// The client supports additional metadata in the form of declaration links. + public var linkSupport: Bool? = nil + + public init(dynamicRegistration: Bool? = nil, linkSupport: Bool?) { + self.dynamicRegistration = dynamicRegistration + self.linkSupport = linkSupport + } + } + /// Capabilities specific to the `textDocument/codeAction` request. public struct CodeAction: Hashable, Codable { @@ -294,24 +319,38 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { } } - /// Whether the client supports dynamic registaration of this request. - public var dynamicRegistration: Bool? = nil - public var codeActionKind: CodeActionKind - public init(dynamicRegistration: Bool? = nil, codeActionKind: CodeActionKind) { - self.dynamicRegistration = dynamicRegistration + public init(codeActionKind: CodeActionKind) { self.codeActionKind = codeActionKind } } + /// Whether the client supports dynamic registaration of this request. + public var dynamicRegistration: Bool? + public var codeActionLiteralSupport: CodeActionLiteralSupport? = nil - public init(codeActionLiteralSupport: CodeActionLiteralSupport? = nil) { + public init(dynamicRegistration: Bool? = nil, codeActionLiteralSupport: CodeActionLiteralSupport? = nil) { self.codeActionLiteralSupport = codeActionLiteralSupport } } + /// Capabilities specific to `textDocument/rename`. + public struct Rename: Hashable, Codable { + + /// Whether the client supports dynamic registaration of this request. + public var dynamicRegistration: Bool? + + /// The client supports testing for validity of rename operations before execution. + public var prepareSupport: Bool? + + public init(dynamicRegistration: Bool? = nil, prepareSupport: Bool? = nil) { + self.dynamicRegistration = dynamicRegistration + self.prepareSupport = prepareSupport + } + } + /// Capabilities specific to `textDocument/publishDiagnostics`. public struct PublishDiagnostics: Hashable, Codable { /// Whether the client accepts diagnostics with related information. @@ -364,11 +403,13 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { public var onTypeFormatting: DynamicRegistrationCapability? = nil - public var definition: DynamicRegistrationCapability? = nil + public var declaration: DynamicRegistrationLinkSupportCapability? = nil + + public var definition: DynamicRegistrationLinkSupportCapability? = nil - public var typeDefinition: DynamicRegistrationCapability? = nil + public var typeDefinition: DynamicRegistrationLinkSupportCapability? = nil - public var implementation: DynamicRegistrationCapability? = nil + public var implementation: DynamicRegistrationLinkSupportCapability? = nil public var codeAction: CodeAction? = nil @@ -384,13 +425,27 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { public var foldingRange: FoldingRange? = nil - public init(synchronization: Synchronization? = nil, completion: Completion? = nil, hover: Hover? = nil, - signatureHelp: SignatureHelp? = nil, references: DynamicRegistrationCapability? = nil, documentHighlight: DynamicRegistrationCapability? = nil, - documentSymbol: DocumentSymbol? = nil, formatting: DynamicRegistrationCapability? = nil, rangeFormatting: DynamicRegistrationCapability? = nil, - onTypeFormatting: DynamicRegistrationCapability? = nil, definition: DynamicRegistrationCapability? = nil, typeDefinition: DynamicRegistrationCapability? = nil, - implementation: DynamicRegistrationCapability? = nil, codeAction: CodeAction? = nil, codeLens: DynamicRegistrationCapability? = nil, - documentLink: DynamicRegistrationCapability? = nil, colorProvider: DynamicRegistrationCapability? = nil, rename: DynamicRegistrationCapability? = nil, - publishDiagnostics: PublishDiagnostics? = nil, foldingRange: FoldingRange? = nil) { + public init(synchronization: Synchronization? = nil, + completion: Completion? = nil, + hover: Hover? = nil, + signatureHelp: SignatureHelp? = nil, + references: DynamicRegistrationCapability? = nil, + documentHighlight: DynamicRegistrationCapability? = nil, + documentSymbol: DocumentSymbol? = nil, + formatting: DynamicRegistrationCapability? = nil, + rangeFormatting: DynamicRegistrationCapability? = nil, + onTypeFormatting: DynamicRegistrationCapability? = nil, + declaration: DynamicRegistrationLinkSupportCapability? = nil, + definition: DynamicRegistrationLinkSupportCapability? = nil, + typeDefinition: DynamicRegistrationLinkSupportCapability? = nil, + implementation: DynamicRegistrationLinkSupportCapability? = nil, + codeAction: CodeAction? = nil, + codeLens: DynamicRegistrationCapability? = nil, + documentLink: DynamicRegistrationCapability? = nil, + colorProvider: DynamicRegistrationCapability? = nil, + rename: DynamicRegistrationCapability? = nil, + publishDiagnostics: PublishDiagnostics? = nil, + foldingRange: FoldingRange? = nil) { self.synchronization = synchronization self.completion = completion self.hover = hover @@ -401,6 +456,7 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { self.formatting = formatting self.rangeFormatting = rangeFormatting self.onTypeFormatting = onTypeFormatting + self.declaration = declaration self.definition = definition self.typeDefinition = typeDefinition self.implementation = implementation diff --git a/Sources/LanguageServerProtocol/Completion.swift b/Sources/LanguageServerProtocol/Completion.swift index d7e72d0dd..f74c8e9ec 100644 --- a/Sources/LanguageServerProtocol/Completion.swift +++ b/Sources/LanguageServerProtocol/Completion.swift @@ -32,7 +32,7 @@ public struct CompletionRequest: TextDocumentRequest, Hashable { public var position: Position - // public var context: CompletionContext? + public var context: CompletionContext? public init(textDocument: TextDocumentIdentifier, position: Position) { self.textDocument = textDocument @@ -40,6 +40,37 @@ public struct CompletionRequest: TextDocumentRequest, Hashable { } } +/// How a completion was triggered +public struct CompletionTriggerKind: RawRepresentable, Codable, Hashable { + /// Completion was triggered by typing an identifier (24x7 code complete), manual invocation (e.g Ctrl+Space) or via API. + public static let invoked = CompletionTriggerKind(rawValue: 1) + + /// Completion was triggered by a trigger character specified by the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + public static let triggerCharacter = CompletionTriggerKind(rawValue: 2) + + /// Completion was re-triggered as the current completion list is incomplete. + public static let triggerFromIncompleteCompletions = CompletionTriggerKind(rawValue: 3) + + public let rawValue: Int + public init(rawValue: Int) { + self.rawValue = rawValue + } +} + +/// Contains additional information about the context in which a completion request is triggered. +public struct CompletionContext: Codable, Hashable { + /// How the completion was triggered. + public var triggerKind: CompletionTriggerKind + + /// The trigger character (a single character) that has trigger code complete. Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` + public var triggerCharacter: String? + + public init(triggerKind: CompletionTriggerKind, triggerCharacter: String? = nil) { + self.triggerKind = triggerKind + self.triggerCharacter = triggerCharacter + } +} + /// List of completion items. If this list has been filtered already, the `isIncomplete` flag /// indicates that the client should re-query code-completions if the filter text changes. public struct CompletionList: ResponseType, Hashable { @@ -56,4 +87,24 @@ public struct CompletionList: ResponseType, Hashable { self.isIncomplete = isIncomplete self.items = items } + + public init(from decoder: Decoder) throws { + // Try decoding as CompletionList + do { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.isIncomplete = try container.decode(Bool.self, forKey: .isIncomplete) + self.items = try container.decode([CompletionItem].self, forKey: .items) + return + } catch {} + + // Try decoding as [CompletionItem] + do { + self.items = try [CompletionItem](from: decoder) + self.isIncomplete = false + return + } catch {} + + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected CompletionList or [CompletionItem]") + throw DecodingError.dataCorrupted(context) + } } diff --git a/Sources/LanguageServerProtocol/CompletionItem.swift b/Sources/LanguageServerProtocol/CompletionItem.swift index 02c587aaf..62914948c 100644 --- a/Sources/LanguageServerProtocol/CompletionItem.swift +++ b/Sources/LanguageServerProtocol/CompletionItem.swift @@ -16,9 +16,15 @@ public struct CompletionItem: Codable, Hashable { /// The display name of the completion. public var label: String + /// The kind of completion item - e.g. method, property. + public var kind: CompletionItemKind + /// An extended human-readable name (longer than `label`, but simpler than `documentation`). public var detail: String? + /// A human-readable string that represents a doc-comment. + public var documentation: CompletionItemDocumentation? + /// The name to use for sorting the result. If `nil`, use `label. public var sortText: String? @@ -40,23 +46,19 @@ public struct CompletionItem: Codable, Hashable { /// The format of the `textEdit.nextText` or `insertText` value. public var insertTextFormat: InsertTextFormat? - /// The kind of completion item - e.g. method, property. - public var kind: CompletionItemKind - /// Whether the completion is for a deprecated symbol. public var deprecated: Bool? - // TODO: remaining members - public init( label: String, + kind: CompletionItemKind, detail: String? = nil, + documentation: CompletionItemDocumentation? = nil, sortText: String? = nil, filterText: String? = nil, textEdit: TextEdit? = nil, insertText: String? = nil, insertTextFormat: InsertTextFormat? = nil, - kind: CompletionItemKind, deprecated: Bool? = nil) { self.label = label @@ -80,3 +82,30 @@ public enum InsertTextFormat: Int, Codable, Hashable { /// The text to insert is a "snippet", which may contain placeholders. case snippet = 2 } + +public enum CompletionItemDocumentation: Hashable { + case string(String) + case markupContent(MarkupContent) +} + +extension CompletionItemDocumentation: Codable { + public init(from decoder: Decoder) throws { + if let string = try? String(from: decoder) { + self = .string(string) + } else if let markupContent = try? MarkupContent(from: decoder) { + self = .markupContent(markupContent) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkupContent") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .string(let string): + try string.encode(to: encoder) + case .markupContent(let markupContent): + try markupContent.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/DefinitionRequest.swift b/Sources/LanguageServerProtocol/DefinitionRequest.swift index f0e652ccd..0b7520a73 100644 --- a/Sources/LanguageServerProtocol/DefinitionRequest.swift +++ b/Sources/LanguageServerProtocol/DefinitionRequest.swift @@ -24,7 +24,7 @@ /// - Returns: The location of the definition(s). public struct DefinitionRequest: TextDocumentRequest, Hashable { public static let method: String = "textDocument/definition" - public typealias Response = [Location] + public typealias Response = LocationsOrLocationLinksResponse? /// The document in which to lookup the symbol location. public var textDocument: TextDocumentIdentifier diff --git a/Sources/LanguageServerProtocol/DocumentSymbol.swift b/Sources/LanguageServerProtocol/DocumentSymbol.swift index 5dbbfa07d..4c950616a 100644 --- a/Sources/LanguageServerProtocol/DocumentSymbol.swift +++ b/Sources/LanguageServerProtocol/DocumentSymbol.swift @@ -24,7 +24,7 @@ /// - Returns: An array of document symbols, if any. public struct DocumentSymbolRequest: TextDocumentRequest, Hashable { public static let method: String = "textDocument/documentSymbol" - public typealias Response = [DocumentSymbol]? + public typealias Response = DocumentSymbolResponse? /// The document in which to lookup the symbol location. public var textDocument: TextDocumentIdentifier @@ -34,6 +34,31 @@ public struct DocumentSymbolRequest: TextDocumentRequest, Hashable { } } +public enum DocumentSymbolResponse: ResponseType, Hashable { + case documentSymbols([DocumentSymbol]) + case symbolInformation([SymbolInformation]) + + public init(from decoder: Decoder) throws { + if let documentSymbols = try? [DocumentSymbol](from: decoder) { + self = .documentSymbols(documentSymbols) + } else if let symbolInformation = try? [SymbolInformation](from: decoder) { + self = .symbolInformation(symbolInformation) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected [DocumentSymbol] or [SymbolInformation]") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .documentSymbols(let documentSymbols): + try documentSymbols.encode(to: encoder) + case .symbolInformation(let symbolInformation): + try symbolInformation.encode(to: encoder) + } + } +} + /// Represents programming constructs like variables, classes, interfaces etc. that appear /// in a document. Document symbols can be hierarchical and they have two ranges: one that encloses /// its definition and one that points to its most interesting range, e.g. the range of an identifier. @@ -70,12 +95,12 @@ public struct DocumentSymbol: Hashable, Codable, ResponseType { public init( name: String, - detail: String?, + detail: String? = nil, kind: SymbolKind, - deprecated: Bool?, + deprecated: Bool? = nil, range: Range, selectionRange: Range, - children: [DocumentSymbol]?) + children: [DocumentSymbol]? = nil) { self.name = name self.detail = detail diff --git a/Sources/LanguageServerProtocol/Hover.swift b/Sources/LanguageServerProtocol/Hover.swift index cb82dfeb6..4c94d7a45 100644 --- a/Sources/LanguageServerProtocol/Hover.swift +++ b/Sources/LanguageServerProtocol/Hover.swift @@ -78,7 +78,7 @@ extension MarkedString: Codable { } else if let codeBlock = try? MarkdownCodeBlock(from: decoder) { self = .codeBlock(language: codeBlock.language, value: codeBlock.value) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "MarkedString is neither pure string nor code block") + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkdownCodeBlock") throw DecodingError.dataCorrupted(context) } } @@ -104,7 +104,7 @@ extension HoverResponseContents: Codable { } else if let value = try? MarkupContent(from: decoder) { self = .markupContent(value) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "HoverResponseContents is neither MarkedString, nor [MarkedString], nor MarkupContent") + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected MarkedString, [MarkedString], or MarkupContent") throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/ImplementationRequest.swift b/Sources/LanguageServerProtocol/ImplementationRequest.swift index 63f763d2b..aecfa4e7e 100644 --- a/Sources/LanguageServerProtocol/ImplementationRequest.swift +++ b/Sources/LanguageServerProtocol/ImplementationRequest.swift @@ -25,7 +25,7 @@ /// protocol conforming types, subclasses, or overrides. public struct ImplementationRequest: TextDocumentRequest, Hashable { public static let method: String = "textDocument/implementation" - public typealias Response = [Location] + public typealias Response = LocationsOrLocationLinksResponse? /// The document in which the given symbol is located. public var textDocument: TextDocumentIdentifier diff --git a/Sources/LanguageServerProtocol/LocationLink.swift b/Sources/LanguageServerProtocol/LocationLink.swift new file mode 100644 index 000000000..003d68554 --- /dev/null +++ b/Sources/LanguageServerProtocol/LocationLink.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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 struct LocationLink: Codable, Hashable { + /// Span of the origin of this link. + /// + /// Used as the underlined span for mouse interaction. Defaults to the word range at the mouse position. + @CustomCodable + public var originSelectionRange: Range? + + /// The target resource identifier of this link. + public var targetUri: DocumentURI + + /// The full target range of this link. If the target for example is a symbol then target range is the + /// range enclosing this symbol not including leading/trailing whitespace but everything else + /// like comments. This information is typically used to highlight the range in the editor. + @CustomCodable + public var targetRange: Range + + /// The range that should be selected and revealed when this link is being followed, e.g the name of a function. + /// Must be contained by the the `targetRange`. See also `DocumentSymbol#range` + @CustomCodable + public var targetSelectionRange: Range + + public init(originSelectionRange: Range? = nil, + targetUri: DocumentURI, + targetRange: Range, + targetSelectionRange: Range) { + self._originSelectionRange = CustomCodable(wrappedValue: originSelectionRange) + self.targetUri = targetUri + self._targetRange = CustomCodable(wrappedValue: targetRange) + self._targetSelectionRange = CustomCodable(wrappedValue: targetSelectionRange) + } +} diff --git a/Sources/LanguageServerProtocol/LocationsOrLocationLinksResponse.swift b/Sources/LanguageServerProtocol/LocationsOrLocationLinksResponse.swift new file mode 100644 index 000000000..3edf18815 --- /dev/null +++ b/Sources/LanguageServerProtocol/LocationsOrLocationLinksResponse.swift @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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 LocationsOrLocationLinksResponse: ResponseType, Hashable { + case locations([Location]) + case locationLinks([LocationLink]) + + public init(from decoder: Decoder) throws { + if let locations = try? [Location](from: decoder) { + self = .locations(locations) + } else if let locationLinks = try? [LocationLink](from: decoder) { + self = .locationLinks(locationLinks) + } else if let location = try? Location(from: decoder) { + // Fallback: Decode single location as array with one element + self = .locations([location]) + } else { + let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected [Location], [LocationLink], or Location") + throw DecodingError.dataCorrupted(context) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .locations(let locations): + try locations.encode(to: encoder) + case .locationLinks(let locationLinks): + try locationLinks.encode(to: encoder) + } + } +} + diff --git a/Sources/LanguageServerProtocol/ReferencesRequest.swift b/Sources/LanguageServerProtocol/ReferencesRequest.swift index 33168d487..6664baab8 100644 --- a/Sources/LanguageServerProtocol/ReferencesRequest.swift +++ b/Sources/LanguageServerProtocol/ReferencesRequest.swift @@ -33,12 +33,20 @@ public struct ReferencesRequest: RequestType, Hashable { /// The document location at which to lookup symbol information. public var position: Position - /// Whether to include the declaration in the list of symbols, or just the references. - public var includeDeclaration: Bool? + public var context: ReferencesContext - public init(textDocument: TextDocumentIdentifier, position: Position, includeDeclaration: Bool? = nil) { + public init(textDocument: TextDocumentIdentifier, position: Position, context: ReferencesContext) { self.textDocument = textDocument self.position = position + self.context = context + } +} + +public struct ReferencesContext: Codable, Hashable { + /// Whether to include the declaration in the list of symbols, or just the references. + public var includeDeclaration: Bool + + public init(includeDeclaration: Bool) { self.includeDeclaration = includeDeclaration } } diff --git a/Sources/LanguageServerProtocol/WorkspaceSettings.swift b/Sources/LanguageServerProtocol/WorkspaceSettings.swift index 77d2d3f2c..ceaa419f1 100644 --- a/Sources/LanguageServerProtocol/WorkspaceSettings.swift +++ b/Sources/LanguageServerProtocol/WorkspaceSettings.swift @@ -16,7 +16,7 @@ public enum WorkspaceSettingsChange: Codable, Hashable { case clangd(ClangWorkspaceSettings) - case unknown + case unknown(LSPAny) public init(from decoder: Decoder) throws { // FIXME: doing trial deserialization only works if we have at least one non-optional unique @@ -25,7 +25,8 @@ public enum WorkspaceSettingsChange: Codable, Hashable { if let settings = try? ClangWorkspaceSettings(from: decoder) { self = .clangd(settings) } else { - self = .unknown + let settings = try LSPAny(from: decoder) + self = .unknown(settings) } } @@ -33,8 +34,8 @@ public enum WorkspaceSettingsChange: Codable, Hashable { switch self { case .clangd(let settings): try settings.encode(to: encoder) - case .unknown: - break // Nothing to do. + case .unknown(let settings): + try settings.encode(to: encoder) } } } diff --git a/Sources/LanguageServerProtocol/WorkspaceSymbols.swift b/Sources/LanguageServerProtocol/WorkspaceSymbols.swift index e71488420..a342b2145 100644 --- a/Sources/LanguageServerProtocol/WorkspaceSymbols.swift +++ b/Sources/LanguageServerProtocol/WorkspaceSymbols.swift @@ -45,7 +45,11 @@ public struct SymbolInformation: Hashable, ResponseType { public var containerName: String? - public init(name: String, kind: SymbolKind, deprecated: Bool?, location: Location, containerName: String?) { + public init(name: String, + kind: SymbolKind, + deprecated: Bool? = nil, + location: Location, + containerName: String? = nil) { self.name = name self.kind = kind self.deprecated = deprecated diff --git a/Sources/SourceKit/SourceKitServer.swift b/Sources/SourceKit/SourceKitServer.swift index 2dd3650ce..95a0fcc42 100644 --- a/Sources/SourceKit/SourceKitServer.swift +++ b/Sources/SourceKit/SourceKitServer.swift @@ -80,8 +80,8 @@ public final class SourceKitServer: LanguageServer { registerToolchainTextDocumentRequest(SourceKitServer.completion, CompletionList(isIncomplete: false, items: [])) registerToolchainTextDocumentRequest(SourceKitServer.hover, nil) - registerToolchainTextDocumentRequest(SourceKitServer.definition, []) - registerToolchainTextDocumentRequest(SourceKitServer.implementation, []) + registerToolchainTextDocumentRequest(SourceKitServer.definition, .locations([])) + registerToolchainTextDocumentRequest(SourceKitServer.implementation, .locations([])) registerToolchainTextDocumentRequest(SourceKitServer.symbolInfo, []) registerToolchainTextDocumentRequest(SourceKitServer.documentSymbolHighlight, nil) registerToolchainTextDocumentRequest(SourceKitServer.foldingRange, nil) @@ -574,7 +574,7 @@ extension SourceKitServer { if let error = result.failure { req.reply(.failure(error)) } else { - req.reply([]) + req.reply(.locations([])) } return } @@ -582,7 +582,7 @@ extension SourceKitServer { let fallbackLocation = [symbol.bestLocalDeclaration].compactMap { $0 } guard let usr = symbol.usr, let index = index else { - return req.reply(fallbackLocation) + return req.reply(.locations(fallbackLocation)) } log("performing indexed jump-to-def with usr \(usr)") @@ -608,7 +608,7 @@ extension SourceKitServer { ) } - req.reply(locations.isEmpty ? fallbackLocation : locations) + req.reply(.locations(locations.isEmpty ? fallbackLocation : locations)) } let request = Request(symbolInfo, id: req.id, clientID: ObjectIdentifier(self), cancellation: req.cancellationToken, reply: callback) @@ -627,13 +627,13 @@ extension SourceKitServer { if let error = result.failure { req.reply(.failure(error)) } else { - req.reply([]) + req.reply(.locations([])) } return } guard let usr = symbol.usr, let index = index else { - return req.reply([]) + return req.reply(.locations([])) } var occurs = index.occurrences(ofUSR: usr, roles: .baseOf) @@ -655,7 +655,7 @@ extension SourceKitServer { ) } - req.reply(locations) + req.reply(.locations(locations)) } let request = Request(symbolInfo, id: req.id, clientID: ObjectIdentifier(self), cancellation: req.cancellationToken, reply: callback) @@ -688,7 +688,7 @@ extension SourceKitServer { log("performing indexed jump-to-def with usr \(usr)") var roles: SymbolRole = [.reference] - if req.params.includeDeclaration != false { + if req.params.context.includeDeclaration { roles.formUnion([.declaration, .definition]) } diff --git a/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift b/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift index b625bd2e1..1a89e6a65 100644 --- a/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift +++ b/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift @@ -387,13 +387,13 @@ extension SwiftLanguageServer { let kind: sourcekitd_uid_t? = value[self.keys.kind] result.items.append(CompletionItem( label: name, + kind: kind?.asCompletionItemKind(self.values) ?? .value, detail: typeName, sortText: nil, filterText: filterName, textEdit: nil, insertText: text, insertTextFormat: isInsertTextSnippet ? .snippet : .plain, - kind: kind?.asCompletionItemKind(self.values) ?? .value, deprecated: nil )) @@ -539,7 +539,7 @@ extension SwiftLanguageServer { return } guard let results: SKResponseArray = dict[self.keys.substructure] else { - return req.reply([]) + return req.reply(.documentSymbols([])) } func documentSymbol(value: SKResponseDictionary) -> DocumentSymbol? { @@ -592,7 +592,7 @@ extension SwiftLanguageServer { return result } - req.reply(documentSymbols(array: results)) + req.reply(.documentSymbols(documentSymbols(array: results))) } // FIXME: cancellation _ = handle diff --git a/Tests/LanguageServerProtocolTests/CodingTests.swift b/Tests/LanguageServerProtocolTests/CodingTests.swift index e0df07a8c..1f8a7ee71 100644 --- a/Tests/LanguageServerProtocolTests/CodingTests.swift +++ b/Tests/LanguageServerProtocolTests/CodingTests.swift @@ -265,6 +265,88 @@ final class CodingTests: XCTestCase { } } """) + + checkCoding(CompletionList(isIncomplete: true, items: [CompletionItem(label: "abc", kind: .function)]), json: """ + { + "isIncomplete" : true, + "items" : [ + { + "kind" : 3, + "label" : "abc" + } + ] + } + """) + + checkDecoding(json: """ + [ + { + "kind" : 3, + "label" : "abc" + } + ] + """, expected: CompletionList(isIncomplete: false, items: [CompletionItem(label: "abc", kind: .function)])) + + checkCoding(CompletionItemDocumentation.markupContent(MarkupContent(kind: .markdown, value: "some **Markdown***")), json: """ + { + "kind" : "markdown", + "value" : "some **Markdown***" + } + """) + + checkCoding(CompletionItemDocumentation.string("Some documentation"), json: """ + "Some documentation" + """) + + checkCoding(LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)]), json: """ + [ + { + "range" : \(rangejson.indented(4, skipFirstLine: true)), + "uri" : "\(urljson)" + } + ] + """) + + checkCoding(LocationsOrLocationLinksResponse.locationLinks([LocationLink(targetUri: uri, targetRange: range, targetSelectionRange: range)]), json: """ + [ + { + "targetRange" : \(rangejson.indented(4, skipFirstLine: true)), + "targetSelectionRange" : \(rangejson.indented(4, skipFirstLine: true)), + "targetUri" : "\(urljson)" + } + ] + """) + + checkDecoding(json: """ + { + "range" : \(rangejson.indented(2, skipFirstLine: true)), + "uri" : "\(urljson)" + } + """, expected: LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)])) + + checkCoding(DocumentSymbolResponse.documentSymbols([DocumentSymbol(name: "mySymbol", kind: .function, range: range, selectionRange: range)]), json: """ + [ + { + "kind" : 12, + "name" : "mySymbol", + "range" : \(rangejson.indented(4, skipFirstLine: true)), + "selectionRange" : \(rangejson.indented(4, skipFirstLine: true)) + } + ] + """) + + checkCoding(DocumentSymbolResponse.symbolInformation([SymbolInformation(name: "mySymbol", kind: .function, location: Location(uri: uri, range: range))]), json: """ + [ + { + "kind" : 12, + "location" : { + "range" : \(rangejson.indented(6, skipFirstLine: true)), + "uri" : "\(urljson)" + }, + "name" : "mySymbol" + } + ] + """) } func testPositionRange() { diff --git a/Tests/SourceKitTests/DocumentSymbolTests.swift b/Tests/SourceKitTests/DocumentSymbolTests.swift index b8ce9de37..35b0d2e4d 100644 --- a/Tests/SourceKitTests/DocumentSymbolTests.swift +++ b/Tests/SourceKitTests/DocumentSymbolTests.swift @@ -51,7 +51,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { workspace = connection.server!.workspace! } - func performDocumentSymbolRequest(text: String) -> [DocumentSymbol] { + func performDocumentSymbolRequest(text: String) -> DocumentSymbolResponse { let url = URL(fileURLWithPath: "/a.swift") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( @@ -77,7 +77,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { let text = "" let symbols = performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, []) + XCTAssertEqual(symbols, .documentSymbols([])) } func testStruct() { @@ -89,7 +89,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { """ let symbols = performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, [ + XCTAssertEqual(symbols, .documentSymbols([ DocumentSymbol( name: "Foo", detail: nil, @@ -99,7 +99,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { selectionRange: range(from: (0, 7), to: (0, 10)), children: [] ), - ]) + ])) } func testUnicode() { @@ -118,7 +118,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { // but cake is two utf-16 code units let cakeRange = range(from: (1, 0), to: (1, 13)) let cakeSelectionRange = range(from: (1, 7), to: (1, 9)) - XCTAssertEqual(symbols, [ + XCTAssertEqual(symbols, .documentSymbols([ DocumentSymbol( name: "Żółć", detail: nil, @@ -137,7 +137,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { selectionRange: cakeSelectionRange, children: [] ), - ]) + ])) } func testEnum() { @@ -159,7 +159,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { """ let symbols = performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, [ + XCTAssertEqual(symbols, .documentSymbols([ DocumentSymbol( name: "Foo", detail: nil, @@ -288,7 +288,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { ) ] ) - ]) + ])) } func testAll() { @@ -333,7 +333,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { """ let symbols = performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, [ + XCTAssertEqual(symbols, .documentSymbols([ DocumentSymbol( name: "Int", detail: nil, @@ -646,6 +646,6 @@ func initialize(capabilities: DocumentSymbolCapabilities) { ) ] ) - ]) + ])) } } diff --git a/Tests/SourceKitTests/ImplementationTests.swift b/Tests/SourceKitTests/ImplementationTests.swift index 71cf76123..6d04d0709 100644 --- a/Tests/SourceKitTests/ImplementationTests.swift +++ b/Tests/SourceKitTests/ImplementationTests.swift @@ -25,7 +25,11 @@ final class ImplementationTests: XCTestCase { func impls(at testLoc: TestLocation) throws -> Set { let textDocument = testLoc.docIdentifier let request = ImplementationRequest(textDocument: textDocument, position: Position(testLoc)) - let implementations = try ws.sk.sendSync(request) + let response = try ws.sk.sendSync(request) + guard case .locations(let implementations) = response else { + XCTFail("Response was not locations") + return [] + } return Set(implementations) } func testLoc(_ name: String) -> TestLocation { diff --git a/Tests/SourceKitTests/SourceKitTests.swift b/Tests/SourceKitTests/SourceKitTests.swift index 047c6d7fe..f5827d18d 100644 --- a/Tests/SourceKitTests/SourceKitTests.swift +++ b/Tests/SourceKitTests/SourceKitTests.swift @@ -66,9 +66,13 @@ final class SKTests: XCTestCase { // MARK: Jump to definition - let jump = try ws.sk.sendSync(DefinitionRequest( + let response = try ws.sk.sendSync(DefinitionRequest( textDocument: locRef.docIdentifier, position: locRef.position)) + guard case .locations(let jump) = response else { + XCTFail("Response is not locations") + return + } XCTAssertEqual(jump.count, 1) XCTAssertEqual(jump.first?.uri, DocumentURI(locDef.url)) @@ -78,7 +82,8 @@ final class SKTests: XCTestCase { let refs = try ws.sk.sendSync(ReferencesRequest( textDocument: locDef.docIdentifier, - position: locDef.position)) + position: locDef.position, + context: ReferencesContext(includeDeclaration: true))) XCTAssertEqual(Set(refs), [ Location(locDef), @@ -110,9 +115,13 @@ final class SKTests: XCTestCase { let locDef = ws.testLoc("aaa:def") let locRef = ws.testLoc("aaa:call:c") try ws.openDocument(locRef.url, language: .swift) - let jump = try ws.sk.sendSync(DefinitionRequest( + let response = try ws.sk.sendSync(DefinitionRequest( textDocument: locRef.docIdentifier, position: locRef.position)) + guard case .locations(let jump) = response else { + XCTFail("Response is not locations") + return nil + } XCTAssertEqual(jump.count, 1) XCTAssertEqual(jump.first?.uri, DocumentURI(locDef.url)) XCTAssertEqual(jump.first?.range.lowerBound, locDef.position) @@ -153,23 +162,23 @@ final class SKTests: XCTestCase { XCTAssertEqual(results, CompletionList(isIncomplete: false, items: [ CompletionItem( label: "method(a: Int)", + kind: .method, detail: "Void", sortText: nil, filterText: "method(a:)", textEdit: nil, insertText: "method(a: )", insertTextFormat: .plain, - kind: .method, deprecated: nil), CompletionItem( label: "self", + kind: .keyword, detail: "A", sortText: nil, filterText: "self", textEdit: nil, insertText: "self", insertTextFormat: .plain, - kind: .keyword, deprecated: nil), ])) } diff --git a/Tests/SourceKitTests/SwiftPMIntegration.swift b/Tests/SourceKitTests/SwiftPMIntegration.swift index f9c6daca9..5ce138944 100644 --- a/Tests/SourceKitTests/SwiftPMIntegration.swift +++ b/Tests/SourceKitTests/SwiftPMIntegration.swift @@ -22,7 +22,7 @@ final class SwiftPMIntegrationTests: XCTestCase { let call = ws.testLoc("Lib.foo:call") let def = ws.testLoc("Lib.foo:def") try ws.openDocument(call.url, language: .swift) - let refs = try ws.sk.sendSync(ReferencesRequest(textDocument: call.docIdentifier, position: call.position)) + let refs = try ws.sk.sendSync(ReferencesRequest(textDocument: call.docIdentifier, position: call.position, context: ReferencesContext(includeDeclaration: true))) XCTAssertEqual(Set(refs), [ Location(call), @@ -34,23 +34,23 @@ final class SwiftPMIntegrationTests: XCTestCase { XCTAssertEqual(completions, CompletionList(isIncomplete: false, items: [ CompletionItem( label: "foo()", + kind: .method, detail: "Void", sortText: nil, filterText: "foo()", textEdit: nil, insertText: "foo()", insertTextFormat: .plain, - kind: .method, deprecated: nil), CompletionItem( label: "self", + kind: .keyword, detail: "Lib", sortText: nil, filterText: "self", textEdit: nil, insertText: "self", insertTextFormat: .plain, - kind: .keyword, deprecated: nil), ])) }