Skip to content

Commit c0d2ad0

Browse files
authored
Merge pull request swiftlang#126 from Trzyipolkostkicukru/implementation
Implement "textDocument/implementation" request
2 parents 578d8ac + 719bed2 commit c0d2ad0

File tree

11 files changed

+245
-1
lines changed

11 files changed

+245
-1
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// The go to implementation request is sent from the client to the server
14+
/// to resolve the implementation location of a symbol at a given
15+
/// text document position.
16+
///
17+
/// Servers that provide Goto Implementation support should set
18+
/// the `implementationProvider` server capability.
19+
///
20+
/// - Parameters:
21+
/// - textDocument: The document in which the given symbol is located.
22+
/// - position: The document location of a given symbol.
23+
///
24+
/// - Returns: The location of the implementations of protocol requirements,
25+
/// protocol conforming types, subclasses, or overrides.
26+
public struct ImplementationRequest: TextDocumentRequest, Hashable {
27+
public static let method: String = "textDocument/implementation"
28+
public typealias Response = [Location]
29+
30+
/// The document in which the given symbol is located.
31+
public var textDocument: TextDocumentIdentifier
32+
33+
/// The document location of a given symbol.
34+
public var position: Position
35+
36+
public init(textDocument: TextDocumentIdentifier, position: Position) {
37+
self.textDocument = textDocument
38+
self.position = position
39+
}
40+
}

Sources/LanguageServerProtocol/Messages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public let builtinRequests: [_RequestType.Type] = [
2525
HoverRequest.self,
2626
WorkspaceSymbolsRequest.self,
2727
DefinitionRequest.self,
28+
ImplementationRequest.self,
2829
ReferencesRequest.self,
2930
DocumentHighlightRequest.self,
3031
DocumentFormatting.self,

Sources/LanguageServerProtocol/ServerCapabilities.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public struct ServerCapabilities: Codable, Hashable {
2424
/// Whether the server provides "textDocument/definition".
2525
public var definitionProvider: Bool?
2626

27+
/// Whether the server provides "textDocument/implementation".
28+
public var implementationProvider: Bool?
29+
2730
/// Whether the server provides "textDocument/references".
2831
public var referencesProvider: Bool?
2932

@@ -53,14 +56,15 @@ public struct ServerCapabilities: Codable, Hashable {
5356

5457
/// The server provides workspace symbol support.
5558
public var workspaceSymbolProvider: Bool?
56-
59+
5760
// TODO: fill-in the rest.
5861

5962
public init(
6063
textDocumentSync: TextDocumentSyncOptions? = nil,
6164
completionProvider: CompletionOptions? = nil,
6265
hoverProvider: Bool? = nil,
6366
definitionProvider: Bool? = nil,
67+
implementationProvider: Bool? = nil,
6468
referencesProvider: Bool? = nil,
6569
documentHighlightProvider: Bool? = nil,
6670
documentFormattingProvider: Bool? = nil,
@@ -77,6 +81,7 @@ public struct ServerCapabilities: Codable, Hashable {
7781
self.completionProvider = completionProvider
7882
self.hoverProvider = hoverProvider
7983
self.definitionProvider = definitionProvider
84+
self.implementationProvider = implementationProvider
8085
self.referencesProvider = referencesProvider
8186
self.documentHighlightProvider = documentHighlightProvider
8287
self.documentFormattingProvider = documentFormattingProvider
@@ -94,6 +99,7 @@ public struct ServerCapabilities: Codable, Hashable {
9499
self.completionProvider = try container.decodeIfPresent(CompletionOptions.self, forKey: .completionProvider)
95100
self.hoverProvider = try container.decodeIfPresent(Bool.self, forKey: .hoverProvider)
96101
self.definitionProvider = try container.decodeIfPresent(Bool.self, forKey: .definitionProvider)
102+
self.implementationProvider = try container.decodeIfPresent(Bool.self, forKey: .implementationProvider)
97103
self.foldingRangeProvider = try container.decodeIfPresent(Bool.self, forKey: .foldingRangeProvider)
98104
self.documentSymbolProvider = try container.decodeIfPresent(Bool.self, forKey: .documentSymbolProvider)
99105
self.colorProvider = try container.decodeIfPresent(Bool.self, forKey: .colorProvider)

Sources/SourceKit/SourceKitServer.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public final class SourceKitServer: LanguageServer {
7373
registerWorkspaceRequest(SourceKitServer.hover)
7474
registerWorkspaceRequest(SourceKitServer.workspaceSymbols)
7575
registerWorkspaceRequest(SourceKitServer.definition)
76+
registerWorkspaceRequest(SourceKitServer.implementation)
7677
registerWorkspaceRequest(SourceKitServer.references)
7778
registerWorkspaceRequest(SourceKitServer.documentSymbolHighlight)
7879
registerWorkspaceRequest(SourceKitServer.foldingRange)
@@ -261,6 +262,7 @@ extension SourceKitServer {
261262
),
262263
hoverProvider: true,
263264
definitionProvider: true,
265+
implementationProvider: true,
264266
referencesProvider: true,
265267
documentHighlightProvider: true,
266268
foldingRangeProvider: true,
@@ -468,6 +470,55 @@ extension SourceKitServer {
468470
}
469471
}
470472

473+
// FIXME: a lot of duplication with definition request
474+
func implementation(_ req: Request<ImplementationRequest>, workspace: Workspace) {
475+
// FIXME: sending yourself a request isn't very convenient
476+
477+
guard let service = workspace.documentService[req.params.textDocument.url] else {
478+
req.reply([])
479+
return
480+
}
481+
482+
let id = service.send(SymbolInfoRequest(textDocument: req.params.textDocument, position: req.params.position), queue: queue) { result in
483+
guard let symbols: [SymbolDetails] = result.success ?? nil, let symbol = symbols.first else {
484+
if let error = result.failure {
485+
req.reply(.failure(error))
486+
} else {
487+
req.reply([])
488+
}
489+
return
490+
}
491+
492+
guard let usr = symbol.usr, let index = workspace.index else {
493+
return req.reply([])
494+
}
495+
496+
var occurs = index.occurrences(ofUSR: usr, roles: .baseOf)
497+
if occurs.isEmpty {
498+
occurs = index.occurrences(relatedToUSR: usr, roles: .overrideOf)
499+
}
500+
501+
let locations = occurs.compactMap { occur -> Location? in
502+
if occur.location.path.isEmpty {
503+
return nil
504+
}
505+
return Location(
506+
url: URL(fileURLWithPath: occur.location.path),
507+
range: Range(Position(
508+
line: occur.location.line - 1, // 1-based -> 0-based
509+
// FIXME: we need to convert the utf8/utf16 column, which may require reading the file!
510+
utf16index: occur.location.utf8Column - 1
511+
))
512+
)
513+
}
514+
515+
req.reply(locations)
516+
}
517+
req.cancellationToken.addCancellationHandler { [weak service] in
518+
service?.send(CancelRequest(id: id))
519+
}
520+
}
521+
471522
// FIXME: a lot of duplication with definition request
472523
func references(_ req: Request<ReferencesRequest>, workspace: Workspace) {
473524
// FIXME: sending yourself a request isn't very convenient

Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ extension SwiftLanguageServer {
194194
triggerCharacters: ["."]),
195195
hoverProvider: true,
196196
definitionProvider: nil,
197+
implementationProvider: true,
197198
referencesProvider: nil,
198199
documentHighlightProvider: true,
199200
foldingRangeProvider: true,

Tests/INPUTS/Implementation/a.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*a.swift*/
2+
protocol /*Protocol*/Protocol {
3+
static var /*ProtocolStaticVar*/staticVar: Int { get }
4+
static func /*ProtocolStaticFunction*/staticFunction()
5+
var /*ProtocolVariable*/variable: Int { get }
6+
func /*ProtocolFunction*/function()
7+
}
8+
class /*Class*/Class {
9+
class var /*ClassClassVar*/classVar: Int { 123 }
10+
class func /*ClassClassFunction*/classFunction() {}
11+
var /*ClassVariable*/variable: Int { 123 }
12+
func /*ClassFunction*/function() {}
13+
}
14+
15+
16+
class /*Sepulcidae*/Sepulcidae {}
17+
class /*Parapamphiliinae*/Parapamphiliinae: /*ParapamphiliinaeConformance*/Sepulcidae {}
18+
class Micramphilius: /*MicramphiliusConformance*/Parapamphiliinae {}
19+
class Pamparaphilius: /*PamparaphiliusConformance*/Parapamphiliinae {}
20+
class /*Xyelulinae*/Xyelulinae: /*XyelulinaeConformance*/Sepulcidae {}
21+
class Xyelula: /*XyelulaConformance*/Xyelulinae {}
22+
class /*Trematothoracinae*/Trematothoracinae: /*TrematothoracinaeConformance*/Sepulcidae {}
23+
24+
25+
protocol /*Prozaiczne*/Prozaiczne {}
26+
protocol /*Sepulkowate*/Sepulkowate {
27+
func /*rozpocznijSepulenie*/rozpocznijSepulenie()
28+
}
29+
30+
class Pćma {}
31+
class /*Murkwia*/Murkwia: /*MurkwiaConformance1*/Sepulkowate, /*MurkwiaConformance2*/Prozaiczne {
32+
func /*MurkwiaFunc*/rozpocznijSepulenie() {}
33+
}
34+
class /*Sepulka*/Sepulka: /*SepulkaConformance1*/Prozaiczne, /*SepulkaConformance2*/Sepulkowate {
35+
var size: Double { 10 }
36+
var /*SepulkaVar*/patroka: String { "puszysta" }
37+
func /*SepulkaFunc*/rozpocznijSepulenie() {}
38+
}
39+
class SepulkaDwuuszna: /*SepulkaDwuusznaConformance*/Sepulka {
40+
override var /*SepulkaDwuusznaVar*/patroka: String { "glazurowana" }
41+
}
42+
class SepulkaPrzechylna: /*SepulkaPrzechylnaConformance*/Sepulka {
43+
override var /*SepulkaPrzechylnaVar*/patroka: String { "piaskowana" }
44+
}
45+
class PćmaŁagodna: Pćma {
46+
func /*PćmaŁagodnaFunc*/rozpocznijSepulenie() { }
47+
}
48+
extension PćmaŁagodna: /*PćmaŁagodnaConformance*/Sepulkowate {}
49+
50+
class PćmaZwyczajna: Pćma {}
51+
extension PćmaZwyczajna: /*PćmaZwyczajnaConformance*/Sepulkowate {
52+
func /*PćmaZwyczajnaFunc*/rozpocznijSepulenie() { }
53+
}

Tests/INPUTS/Implementation/b.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*b.swift*/
2+
struct Struct: /*StructConformance*/Protocol {
3+
static var /*StructStaticVar*/staticVar: Int { 123 }
4+
static func /*StructStaticFunction*/staticFunction() {}
5+
var /*StructVariable*/variable: Int { 123 }
6+
func /*StructFunction*/function() {}
7+
}
8+
class Subclass: /*SubclassConformance*/Class {
9+
override class var /*SubclassClassVar*/classVar: Int { 123 }
10+
override class func /*SubclassClassFunction*/classFunction() {}
11+
override var /*SubclassVariable*/variable: Int { 123 }
12+
override func /*SubclassFunction*/function() {}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"sources": ["a.swift", "b.swift"]}

Tests/LanguageServerProtocolJSONRPCTests/CodingTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ final class CodingTests: XCTestCase {
8282
triggerCharacters: ["."]),
8383
hoverProvider: nil,
8484
definitionProvider: nil,
85+
implementationProvider: nil,
8586
referencesProvider: nil,
8687
documentHighlightProvider: nil,
8788
foldingRangeProvider: nil,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@testable import SourceKit
14+
import LanguageServerProtocol
15+
import XCTest
16+
import SKTestSupport
17+
import ISDBTestSupport
18+
19+
final class ImplementationTests: XCTestCase {
20+
func testImplementation() throws {
21+
let ws = try staticSourceKitTibsWorkspace(name: "Implementation")!
22+
try ws.buildAndIndex()
23+
24+
try ws.openDocument(ws.testLoc("a.swift").url, language: .swift)
25+
try ws.openDocument(ws.testLoc("b.swift").url, language: .swift)
26+
27+
func impls(at testLoc: TestLocation) throws -> Set<Location> {
28+
let textDocument = testLoc.docIdentifier
29+
let request = ImplementationRequest(textDocument: textDocument, position: Position(testLoc))
30+
let implementations = try ws.sk.sendSync(request)
31+
return Set(implementations)
32+
}
33+
func testLoc(_ name: String) -> TestLocation {
34+
ws.testLoc(name)
35+
}
36+
func loc(_ name: String) -> Location {
37+
Location(ws.testLoc(name))
38+
}
39+
40+
try XCTAssertEqual(impls(at: testLoc("Protocol")), [loc("StructConformance")])
41+
try XCTAssertEqual(impls(at: testLoc("ProtocolStaticVar")), [loc("StructStaticVar")])
42+
try XCTAssertEqual(impls(at: testLoc("ProtocolStaticFunction")), [loc("StructStaticFunction")])
43+
try XCTAssertEqual(impls(at: testLoc("ProtocolVariable")), [loc("StructVariable")])
44+
try XCTAssertEqual(impls(at: testLoc("ProtocolFunction")), [loc("StructFunction")])
45+
try XCTAssertEqual(impls(at: testLoc("Class")), [loc("SubclassConformance")])
46+
try XCTAssertEqual(impls(at: testLoc("ClassClassVar")), [loc("SubclassClassVar")])
47+
try XCTAssertEqual(impls(at: testLoc("ClassClassFunction")), [loc("SubclassClassFunction")])
48+
try XCTAssertEqual(impls(at: testLoc("ClassVariable")), [loc("SubclassVariable")])
49+
try XCTAssertEqual(impls(at: testLoc("ClassFunction")), [loc("SubclassFunction")])
50+
51+
try XCTAssertEqual(impls(at: testLoc("Sepulcidae")), [loc("ParapamphiliinaeConformance"), loc("XyelulinaeConformance"), loc("TrematothoracinaeConformance")])
52+
try XCTAssertEqual(impls(at: testLoc("Parapamphiliinae")), [loc("MicramphiliusConformance"), loc("PamparaphiliusConformance")])
53+
try XCTAssertEqual(impls(at: testLoc("Xyelulinae")), [loc("XyelulaConformance")])
54+
try XCTAssertEqual(impls(at: testLoc("Trematothoracinae")), [])
55+
56+
try XCTAssertEqual(impls(at: testLoc("Prozaiczne")), [loc("MurkwiaConformance2"), loc("SepulkaConformance1")])
57+
// FIXME: For some reason we get a location in the middle of a symbol for PćmaŁagodnaConformance
58+
// try XCTAssertEqual(impls(at: testLoc("Sepulkowate")), [loc("MurkwiaConformance1"), loc("SepulkaConformance2"), loc("PćmaŁagodnaConformance"), loc("PćmaZwyczajnaConformance")])
59+
// FIXME: sourcekit returns wrong locations for the function (subclasses that don't override it, and extensions that don't implement it)
60+
// try XCTAssertEqual(impls(at: testLoc("rozpocznijSepulenie")), [loc("MurkwiaFunc"), loc("SepulkaFunc"), loc("PćmaŁagodnaFunc"), loc("PćmaZwyczajnaFunc")])
61+
try XCTAssertEqual(impls(at: testLoc("Murkwia")), [])
62+
try XCTAssertEqual(impls(at: testLoc("MurkwiaFunc")), [])
63+
try XCTAssertEqual(impls(at: testLoc("Sepulka")), [loc("SepulkaDwuusznaConformance"), loc("SepulkaPrzechylnaConformance")])
64+
try XCTAssertEqual(impls(at: testLoc("SepulkaVar")), [loc("SepulkaDwuusznaVar"), loc("SepulkaPrzechylnaVar")])
65+
try XCTAssertEqual(impls(at: testLoc("SepulkaFunc")), [])
66+
}
67+
}

Tests/SourceKitTests/XCTestManifests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ extension FoldingRangeTests {
4949
]
5050
}
5151

52+
extension ImplementationTests {
53+
// DO NOT MODIFY: This is autogenerated, use:
54+
// `swift test --generate-linuxmain`
55+
// to regenerate.
56+
static let __allTests__ImplementationTests = [
57+
("testImplementation", testImplementation),
58+
]
59+
}
60+
5261
extension LocalClangTests {
5362
// DO NOT MODIFY: This is autogenerated, use:
5463
// `swift test --generate-linuxmain`
@@ -114,6 +123,7 @@ public func __allTests() -> [XCTestCaseEntry] {
114123
testCase(DocumentColorTests.__allTests__DocumentColorTests),
115124
testCase(DocumentSymbolTest.__allTests__DocumentSymbolTest),
116125
testCase(FoldingRangeTests.__allTests__FoldingRangeTests),
126+
testCase(ImplementationTests.__allTests__ImplementationTests),
117127
testCase(LocalClangTests.__allTests__LocalClangTests),
118128
testCase(LocalSwiftTests.__allTests__LocalSwiftTests),
119129
testCase(SKTests.__allTests__SKTests),

0 commit comments

Comments
 (0)