Skip to content

Commit fccf19f

Browse files
authored
Merge pull request #336 from benlangmuir/code-completion-server-side-filtering-5.3-20201012
[5.3-20201012] [completion] Filter completions on the server side
2 parents 22116ca + 64b2350 commit fccf19f

22 files changed

+1122
-300
lines changed

Sources/LSPTestSupport/TestJSONRPCConnection.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -18,7 +18,7 @@ import XCTest
1818
// Workaround ambiguity with Foundation.
1919
public typealias Notification = LanguageServerProtocol.Notification
2020

21-
public struct TestJSONRPCConnection {
21+
public final class TestJSONRPCConnection {
2222
public let clientToServer: Pipe = Pipe()
2323
public let serverToClient: Pipe = Pipe()
2424
public let client: TestClient
@@ -27,11 +27,6 @@ public struct TestJSONRPCConnection {
2727
public let serverConnection: JSONRPCConnection
2828

2929
public init() {
30-
// FIXME: DispatchIO doesn't like when the Pipes close behind its back even after the tests
31-
// finish. Until we fix the lifetime, leak.
32-
_ = Unmanaged.passRetained(clientToServer)
33-
_ = Unmanaged.passRetained(serverToClient)
34-
3530
clientConnection = JSONRPCConnection(
3631
protocol: testMessageRegistry,
3732
inFD: serverToClient.fileHandleForReading.fileDescriptor,
@@ -47,8 +42,16 @@ public struct TestJSONRPCConnection {
4742
client = TestClient(server: clientConnection)
4843
server = TestServer(client: serverConnection)
4944

50-
clientConnection.start(receiveHandler: client)
51-
serverConnection.start(receiveHandler: server)
45+
clientConnection.start(receiveHandler: client) {
46+
// FIXME: keep the pipes alive until we close the connection. This
47+
// should be fixed systemically.
48+
withExtendedLifetime(self) {}
49+
}
50+
serverConnection.start(receiveHandler: server) {
51+
// FIXME: keep the pipes alive until we close the connection. This
52+
// should be fixed systemically.
53+
withExtendedLifetime(self) {}
54+
}
5255
}
5356

5457
public func close() {

Sources/LanguageServerProtocol/Connection.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -104,14 +104,18 @@ public final class LocalConnection {
104104

105105
extension LocalConnection: Connection {
106106
public func send<Notification>(_ notification: Notification) where Notification: NotificationType {
107-
precondition(state == .started)
108-
handler!.handle(notification, from: ObjectIdentifier(self))
107+
handler?.handle(notification, from: ObjectIdentifier(self))
109108
}
110109

111110
public func send<Request>(_ request: Request, queue: DispatchQueue, reply: @escaping (LSPResult<Request.Response>) -> Void) -> RequestID where Request: RequestType {
112-
precondition(state == .started)
113111
let id = nextRequestID()
114-
handler!.handle(request, id: id, from: ObjectIdentifier(self)) { result in
112+
guard let handler = handler else {
113+
queue.async { reply(.failure(.cancelled)) }
114+
return id
115+
}
116+
117+
precondition(state == .started)
118+
handler.handle(request, id: id, from: ObjectIdentifier(self)) { result in
115119
queue.async {
116120
reply(result)
117121
}

Sources/LanguageServerProtocol/Requests/CompletionRequest.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -22,6 +22,8 @@
2222
/// - Parameters:
2323
/// - textDocument: The document to perform completion in.
2424
/// - position: The location to perform completion at.
25+
/// - context: Optional code-completion context.
26+
/// - sourcekitlspOptions: **(LSP Extension)** code-completion options for sourcekit-lsp.
2527
///
2628
/// - Returns: A list of completion items to complete the code at the given position.
2729
public struct CompletionRequest: TextDocumentRequest, Hashable {
@@ -34,9 +36,18 @@ public struct CompletionRequest: TextDocumentRequest, Hashable {
3436

3537
public var context: CompletionContext?
3638

37-
public init(textDocument: TextDocumentIdentifier, position: Position) {
39+
public var sourcekitlspOptions: SKCompletionOptions?
40+
41+
public init(
42+
textDocument: TextDocumentIdentifier,
43+
position: Position,
44+
context: CompletionContext? = nil,
45+
sourcekitlspOptions: SKCompletionOptions? = nil)
46+
{
3847
self.textDocument = textDocument
3948
self.position = position
49+
self.context = context
50+
self.sourcekitlspOptions = sourcekitlspOptions
4051
}
4152
}
4253

Sources/LanguageServerProtocol/SupportTypes/Position.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -56,3 +56,13 @@ extension Position: LSPAnyCodable {
5656
])
5757
}
5858
}
59+
60+
extension Position: CustomStringConvertible, CustomDebugStringConvertible {
61+
public var description: String {
62+
"\(line + 1):\(utf16index+1)"
63+
}
64+
65+
public var debugDescription: String {
66+
"Position(line: \(line), utf16index: \(utf16index))"
67+
}
68+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 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+
/// Code-completion configuration.
14+
///
15+
/// **(LSP Extension)**: This is used as part of an extension to the
16+
/// code-completion request.
17+
public struct SKCompletionOptions: Codable, Hashable {
18+
19+
/// Whether to use server-side filtering or to return all results and let the
20+
/// client handle all filtering.
21+
public var serverSideFiltering: Bool
22+
23+
/// The maximum number of completion results to return, or `nil` for unlimited.
24+
public var maxResults: Int?
25+
26+
public init(serverSideFiltering: Bool = true, maxResults: Int? = 200) {
27+
self.serverSideFiltering = serverSideFiltering
28+
self.maxResults = maxResults
29+
}
30+
}

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,11 @@ private func makeJSONRPCBuildServer(client: MessageHandler, serverPath: Absolute
248248
outFD: clientToServer.fileHandleForWriting.fileDescriptor
249249
)
250250

251-
connection.start(receiveHandler: client)
251+
connection.start(receiveHandler: client) {
252+
// FIXME: keep the pipes alive until we close the connection. This
253+
// should be fixed systemically.
254+
withExtendedLifetime((clientToServer, serverToClient)) {}
255+
}
252256
let process = Foundation.Process()
253257

254258
if #available(OSX 10.13, *) {

Sources/SKCore/BuildSystemManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public final class BuildSystemManager {
4343
weak var mainFilesProvider: MainFilesProvider?
4444

4545
/// Build system delegate that will receive notifications about setting changes, etc.
46-
var _delegate: BuildSystemDelegate?
46+
weak var _delegate: BuildSystemDelegate?
4747

4848
/// Create a BuildSystemManager that wraps the given build system. The new manager will modify the
4949
/// delegate of the underlying build system.

Sources/SKTestSupport/TestServer.swift

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -19,7 +19,7 @@ import SourceKit
1919
import class Foundation.Pipe
2020
import LSPTestSupport
2121

22-
public struct TestSourceKitServer {
22+
public final class TestSourceKitServer {
2323
public enum ConnectionKind {
2424
case local, jsonrpc
2525
}
@@ -40,6 +40,8 @@ public struct TestSourceKitServer {
4040
public let client: TestClient
4141
let connImpl: ConnectionImpl
4242

43+
public var hasShutdown: Bool = false
44+
4345
/// The server, if it is in the same process.
4446
public let server: SourceKitServer?
4547

@@ -63,11 +65,6 @@ public struct TestSourceKitServer {
6365
let clientToServer: Pipe = Pipe()
6466
let serverToClient: Pipe = Pipe()
6567

66-
// FIXME: DispatchIO doesn't like when the Pipes close behind its back even after the tests
67-
// finish. Until we fix the lifetime, leak.
68-
_ = Unmanaged.passRetained(clientToServer)
69-
_ = Unmanaged.passRetained(serverToClient)
70-
7168
let clientConnection = JSONRPCConnection(
7269
protocol: MessageRegistry.lspProtocol,
7370
inFD: serverToClient.fileHandleForReading.fileDescriptor,
@@ -84,8 +81,16 @@ public struct TestSourceKitServer {
8481
serverConnection.close()
8582
})
8683

87-
clientConnection.start(receiveHandler: client)
88-
serverConnection.start(receiveHandler: server!)
84+
clientConnection.start(receiveHandler: client) {
85+
// FIXME: keep the pipes alive until we close the connection. This
86+
// should be fixed systemically.
87+
withExtendedLifetime((clientToServer, serverToClient)) {}
88+
}
89+
serverConnection.start(receiveHandler: server!) {
90+
// FIXME: keep the pipes alive until we close the connection. This
91+
// should be fixed systemically.
92+
withExtendedLifetime((clientToServer, serverToClient)) {}
93+
}
8994

9095
connImpl = .jsonrpc(
9196
clientToServer: clientToServer,
@@ -95,15 +100,15 @@ public struct TestSourceKitServer {
95100
}
96101
}
97102

103+
deinit {
104+
close()
105+
}
106+
98107
func close() {
99-
switch connImpl {
100-
case .local(clientConnection: let cc, serverConnection: let sc):
101-
cc.close()
102-
sc.close()
103-
104-
case .jsonrpc(clientToServer: _, serverToClient: _, clientConnection: let cc, serverConnection: let sc):
105-
cc.close()
106-
sc.close()
108+
if !hasShutdown {
109+
hasShutdown = true
110+
_ = try! self.client.sendSync(ShutdownRequest())
111+
self.client.send(ExitNotification())
107112
}
108113
}
109114
}

Sources/SourceKit/SourceKitServer+Options.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import LanguageServerProtocol
1314
import SKCore
1415

1516
extension SourceKitServer {
@@ -27,10 +28,19 @@ extension SourceKitServer {
2728
/// Additional options for the index.
2829
public var indexOptions: IndexOptions
2930

30-
public init(buildSetup: BuildSetup = .default, clangdOptions: [String] = [], indexOptions: IndexOptions = .init()) {
31+
/// Options for code-completion.
32+
public var completionOptions: SKCompletionOptions
33+
34+
public init(
35+
buildSetup: BuildSetup = .default,
36+
clangdOptions: [String] = [],
37+
indexOptions: IndexOptions = .init(),
38+
completionOptions: SKCompletionOptions = .init())
39+
{
3140
self.buildSetup = buildSetup
3241
self.clangdOptions = clangdOptions
3342
self.indexOptions = indexOptions
43+
self.completionOptions = completionOptions
3444
}
3545
}
3646
}

Sources/SourceKit/SourceKitServer.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public final class SourceKitServer: LanguageServer {
3636
var language: Language
3737
}
3838

39-
let options: Options
39+
var options: Options
4040

4141
let toolchainRegistry: ToolchainRegistry
4242

@@ -298,6 +298,21 @@ extension SourceKitServer {
298298
if case .bool(let listenToUnitEvents) = options["listenToUnitEvents"] {
299299
indexOptions.listenToUnitEvents = listenToUnitEvents
300300
}
301+
if case .dictionary(let completionOptions) = options["completion"] {
302+
if case .bool(let serverSideFiltering) = completionOptions["serverSideFiltering"] {
303+
self.options.completionOptions.serverSideFiltering = serverSideFiltering
304+
}
305+
switch completionOptions["maxResults"] {
306+
case .none:
307+
break
308+
case .some(.null):
309+
self.options.completionOptions.maxResults = nil
310+
case .some(.int(let maxResults)):
311+
self.options.completionOptions.maxResults = maxResults
312+
case .some(let invalid):
313+
log("expected null or int for 'maxResults'; got \(invalid)", level: .warning)
314+
}
315+
}
301316
}
302317

303318
// Any messages sent before initialize returns are expected to fail, so this will run before
@@ -346,7 +361,7 @@ extension SourceKitServer {
346361
save: TextDocumentSyncOptions.SaveOptions(includeText: false)
347362
),
348363
hoverProvider: true,
349-
completionProvider: CompletionOptions(
364+
completionProvider: LanguageServerProtocol.CompletionOptions(
350365
resolveProvider: false,
351366
triggerCharacters: ["."]
352367
),
@@ -406,6 +421,10 @@ extension SourceKitServer {
406421

407422
func shutdown(_ request: Request<ShutdownRequest>) {
408423
_prepareForExit()
424+
for service in languageService.values {
425+
service.shutdown()
426+
}
427+
languageService = [:]
409428
request.reply(VoidResponse())
410429
}
411430

@@ -781,7 +800,12 @@ public func languageService(
781800

782801
case .swift:
783802
guard let sourcekitd = toolchain.sourcekitd else { return nil }
784-
return try makeLocalSwiftServer(client: client, sourcekitd: sourcekitd, buildSettings: (client as? SourceKitServer)?.workspace?.buildSettings, clientCapabilities: (client as? SourceKitServer)?.workspace?.clientCapabilities)
803+
return try makeLocalSwiftServer(
804+
client: client,
805+
sourcekitd: sourcekitd,
806+
buildSettings: (client as? SourceKitServer)?.workspace?.buildSettings,
807+
clientCapabilities: (client as? SourceKitServer)?.workspace?.clientCapabilities,
808+
options: options)
785809

786810
default:
787811
return nil

Sources/SourceKit/ToolchainLanguageServer.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -20,6 +20,7 @@ public protocol ToolchainLanguageServer: AnyObject {
2020

2121
func initializeSync(_ initialize: InitializeRequest) throws -> InitializeResult
2222
func clientInitialized(_ initialized: InitializedNotification)
23+
func shutdown()
2324

2425
// MARK: - Text synchronization
2526

0 commit comments

Comments
 (0)