Skip to content

Implement textDocument/declaration #647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocol/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_library(LanguageServerProtocol STATIC
Requests/CodeActionRequest.swift
Requests/ColorPresentationRequest.swift
Requests/CompletionRequest.swift
Requests/DeclarationRequest.swift
Requests/DefinitionRequest.swift
Requests/DocumentColorRequest.swift
Requests/DocumentHighlightRequest.swift
Expand Down
1 change: 1 addition & 0 deletions Sources/LanguageServerProtocol/Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public let builtinRequests: [_RequestType.Type] = [
TypeHierarchyPrepareRequest.self,
TypeHierarchySupertypesRequest.self,
TypeHierarchySubtypesRequest.self,
DeclarationRequest.self,
DefinitionRequest.self,
ImplementationRequest.self,
ReferencesRequest.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//

/// Request to find the declarations(s) of the symbol at the given location.
///
/// Looks up the symbol at the given position and returns a list of all
/// declarations involving that symbol in the workspace.
///
/// This request differs from `DefinitionRequest` because a symbol usually has
/// only one definition but potentially has many declarations involving that
/// symbol. E.g. the definition of a type versus the forward declarations of that type.
///
/// - Parameters:
/// - textDocument: The document in which to lookup the symbol location.
/// - position: The document location at which to lookup symbol information.
///
/// - Returns: The location of the declaration(s).
public struct DeclarationRequest: TextDocumentRequest, Hashable {
public static let method: String = "textDocument/declaration"
public typealias Response = LocationsOrLocationLinksResponse?

/// The document in which to lookup the symbol location.
public var textDocument: TextDocumentIdentifier

/// The document location at which to lookup symbol information.
public var position: Position

public init(textDocument: TextDocumentIdentifier, position: Position) {
self.textDocument = textDocument
self.position = position
}
}
2 changes: 1 addition & 1 deletion Sources/SKTestSupport/INPUTS/BasicCXX/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ struct /*Object*/Object {
int field;
};

struct Object * newObject();
/*Object:decl:newObject*/struct Object * newObject();
2 changes: 1 addition & 1 deletion Sources/SKTestSupport/INPUTS/BasicCXX/main.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include /*Object:include:main*/"Object.h"

int main(int argc, const char *argv[]) {
struct /*Object:ref:main*/Object *obj = newObject();
struct /*Object:ref:main*/Object *obj = /*Object:ref:newObject*/newObject();
return obj->field;
}
7 changes: 7 additions & 0 deletions Sources/SourceKitLSP/Clang/ClangLanguageServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,13 @@ extension ClangLanguageServerShim {
return true
}

/// Returns true if the `ToolchainLanguageServer` will take ownership of the request.
public func declaration(_ req: Request<DeclarationRequest>) -> Bool {
// We handle it to provide jump-to-header support for #import/#include.
forwardRequestToClangdOnQueue(req)
return true
}

func completion(_ req: Request<CompletionRequest>) {
forwardRequestToClangdOnQueue(req)
}
Expand Down
12 changes: 12 additions & 0 deletions Sources/SourceKitLSP/SourceKitServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ public final class SourceKitServer: LanguageServer {
registerToolchainTextDocumentRequest(SourceKitServer.completion,
CompletionList(isIncomplete: false, items: []))
registerToolchainTextDocumentRequest(SourceKitServer.hover, nil)
registerToolchainTextDocumentRequest(SourceKitServer.declaration, .locations([]))
registerToolchainTextDocumentRequest(SourceKitServer.definition, .locations([]))
registerToolchainTextDocumentRequest(SourceKitServer.references, [])
registerToolchainTextDocumentRequest(SourceKitServer.implementation, .locations([]))
Expand Down Expand Up @@ -658,6 +659,7 @@ extension SourceKitServer {
)),
colorProvider: .bool(true),
foldingRangeProvider: .bool(!registry.clientHasDynamicFoldingRangeRegistration),
declarationProvider: .bool(true),
executeCommandProvider: executeCommandOptions,
workspace: WorkspaceServerCapabilities(workspaceFolders: .init(
supported: true,
Expand Down Expand Up @@ -1247,6 +1249,16 @@ extension SourceKitServer {
return .success(resolved.isEmpty ? fallback : resolved)
}

func declaration(
_ req: Request<DeclarationRequest>,
workspace: Workspace,
languageService: ToolchainLanguageServer
) {
guard languageService.declaration(req) else {
return req.reply(.locations([]))
}
}

func definition(
_ req: Request<DefinitionRequest>,
workspace: Workspace,
Expand Down
5 changes: 5 additions & 0 deletions Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,11 @@ extension SwiftLanguageServer {
return false
}

public func declaration(_ request: Request<DeclarationRequest>) -> Bool {
// We don't handle it.
return false
}

public func completion(_ req: Request<CompletionRequest>) {
queue.async {
self._completion(req)
Expand Down
1 change: 1 addition & 0 deletions Sources/SourceKitLSP/ToolchainLanguageServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public protocol ToolchainLanguageServer: AnyObject {

/// Returns true if the `ToolchainLanguageServer` will take ownership of the request.
func definition(_ request: Request<DefinitionRequest>) -> Bool
func declaration(_ request: Request<DeclarationRequest>) -> Bool

func documentSymbolHighlight(_ req: Request<DocumentHighlightRequest>)
func foldingRange(_ req: Request<FoldingRangeRequest>)
Expand Down
40 changes: 32 additions & 8 deletions Tests/SourceKitLSPTests/SourceKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,7 @@ final class SKTests: XCTestCase {
textDocument: mainLoc.docIdentifier, position: includePosition)
let resp = try withExtendedLifetime(ws) { try ws.sk.sendSync(goToInclude) }

guard let locationsOrLinks = resp else {
XCTFail("No response for go-to-#include")
return
}
let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-#include")
switch locationsOrLinks {
case .locations(let locations):
XCTAssert(!locations.isEmpty, "Found no locations for go-to-#include")
Expand Down Expand Up @@ -346,10 +343,7 @@ final class SKTests: XCTestCase {
textDocument: refLoc.docIdentifier, position: refPos)
let resp = try withExtendedLifetime(ws) { try ws.sk.sendSync(goToDefinition) }

guard let locationsOrLinks = resp else {
XCTFail("No response for go-to-definition")
return
}
let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-definition")
switch locationsOrLinks {
case .locations(let locations):
XCTAssert(!locations.isEmpty, "Found no locations for go-to-definition")
Expand All @@ -363,4 +357,34 @@ final class SKTests: XCTestCase {
}
}
}

func testClangdGoToDeclaration() throws {
guard let ws = try staticSourceKitTibsWorkspace(name: "BasicCXX") else { return }
guard ToolchainRegistry.shared.default?.clangd != nil else { return }

let mainLoc = ws.testLoc("Object:ref:newObject")
let expectedDoc = ws.testLoc("Object:decl:newObject").docIdentifier.uri
let includePosition =
Position(line: mainLoc.position.line, utf16index: mainLoc.utf16Column + 2)

try ws.openDocument(mainLoc.url, language: .c)

let goToInclude = DeclarationRequest(
textDocument: mainLoc.docIdentifier, position: includePosition)
let resp = try! ws.sk.sendSync(goToInclude)

let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-declaration")
switch locationsOrLinks {
case .locations(let locations):
XCTAssert(!locations.isEmpty, "Found no locations for go-to-declaration")
if let loc = locations.first {
XCTAssertEqual(loc.uri, expectedDoc)
}
case .locationLinks(let locationLinks):
XCTAssert(!locationLinks.isEmpty, "Found no location links for go-to-declaration")
if let link = locationLinks.first {
XCTAssertEqual(link.targetUri, expectedDoc)
}
}
}
}