Skip to content

Commit

Permalink
Refactorings to complement large PR in LanguageServerProtocol (#9)
Browse files Browse the repository at this point in the history
* Update for refactored LanguageServerClient

* Message framing moved to generic wrapper in LanguageServerProtocol

* Make RestartingServerError public

* Refactor ordered event stream handling

* hylo-lsp local JSONRPC directory build

* Fix non-mac build

* Use released JSONRPC 0.9.0 version

* Update to match refactor

* Refactor

* Update example code in readme

* Pin to specific version to get tests to run again

* Package.swift formatting

* Bump swift version to 5.9

* Refactor to not gate FileHandle stuff on ProcessEnv

* Depend on LSP 0.11.0

---------

Co-authored-by: Matt <[email protected]>
  • Loading branch information
koliyo and mattmassicotte authored Nov 28, 2023
1 parent b6e5b48 commit eda2fdc
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 159 deletions.
131 changes: 64 additions & 67 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,70 +1,67 @@
{
"object": {
"pins": [
{
"package": "FSEventsWrapper",
"repositoryURL": "https://github.com/Frizlab/FSEventsWrapper",
"state": {
"branch": null,
"revision": "70bbea4b108221fcabfce8dbced8502831c0ae04",
"version": "2.1.0"
}
},
{
"package": "GlobPattern",
"repositoryURL": "https://github.com/ChimeHQ/GlobPattern",
"state": {
"branch": null,
"revision": "4ebb9e89e07cc475efa74f87dc6d21f4a9e060f8",
"version": "0.1.1"
}
},
{
"package": "JSONRPC",
"repositoryURL": "https://github.com/ChimeHQ/JSONRPC",
"state": {
"branch": null,
"revision": "5f48cfdc1c4ce70cd50d996a95ee60d26aa72fee",
"version": "0.8.0"
}
},
{
"package": "LanguageServerProtocol",
"repositoryURL": "https://github.com/ChimeHQ/LanguageServerProtocol",
"state": {
"branch": null,
"revision": "a244efe43ac7a42577b4afd9ccc43b5223c7f18d",
"version": "0.10.0"
}
},
{
"package": "ProcessEnv",
"repositoryURL": "https://github.com/ChimeHQ/ProcessEnv",
"state": {
"branch": null,
"revision": "83f1ebc9dd6fb1db0bd89a3fcae00488a0f3fdd9",
"version": "1.0.0"
}
},
{
"package": "Queue",
"repositoryURL": "https://github.com/mattmassicotte/Queue",
"state": {
"branch": null,
"revision": "8d6f936097888f97011610ced40313655dc5948d",
"version": "0.1.4"
}
},
{
"package": "Semaphore",
"repositoryURL": "https://github.com/groue/Semaphore",
"state": {
"branch": null,
"revision": "f1c4a0acabeb591068dea6cffdd39660b86dec28",
"version": "0.0.8"
}
"pins" : [
{
"identity" : "fseventswrapper",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Frizlab/FSEventsWrapper",
"state" : {
"revision" : "70bbea4b108221fcabfce8dbced8502831c0ae04",
"version" : "2.1.0"
}
]
},
"version": 1
},
{
"identity" : "globpattern",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/GlobPattern",
"state" : {
"revision" : "4ebb9e89e07cc475efa74f87dc6d21f4a9e060f8",
"version" : "0.1.1"
}
},
{
"identity" : "jsonrpc",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/JSONRPC",
"state" : {
"revision" : "c6ec759d41a76ac88fe7327c41a77d9033943374",
"version" : "0.9.0"
}
},
{
"identity" : "languageserverprotocol",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/LanguageServerProtocol",
"state" : {
"revision" : "a355005e6c00775bb567e1d5927a4d57423474c9"
}
},
{
"identity" : "processenv",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/ProcessEnv",
"state" : {
"revision" : "83f1ebc9dd6fb1db0bd89a3fcae00488a0f3fdd9",
"version" : "1.0.0"
}
},
{
"identity" : "queue",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattmassicotte/Queue",
"state" : {
"revision" : "8d6f936097888f97011610ced40313655dc5948d",
"version" : "0.1.4"
}
},
{
"identity" : "semaphore",
"kind" : "remoteSourceControl",
"location" : "https://github.com/groue/Semaphore",
"state" : {
"revision" : "f1c4a0acabeb591068dea6cffdd39660b86dec28",
"version" : "0.0.8"
}
}
],
"version" : 2
}
9 changes: 5 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.8
// swift-tools-version: 5.9

import PackageDescription

Expand All @@ -15,10 +15,10 @@ let package = Package(
targets: ["LanguageClient"]),
],
dependencies: [
.package(url: "https://github.com/ChimeHQ/LanguageServerProtocol", from: "0.10.0"),
.package(url: "https://github.com/ChimeHQ/LanguageServerProtocol", from: "0.11.0"),
.package(url: "https://github.com/Frizlab/FSEventsWrapper", from: "2.1.0"),
.package(url: "https://github.com/ChimeHQ/GlobPattern", from: "0.1.1"),
.package(url: "https://github.com/ChimeHQ/JSONRPC", from: "0.8.0"),
.package(url: "https://github.com/ChimeHQ/JSONRPC", from: "0.9.0"),
.package(url: "https://github.com/ChimeHQ/ProcessEnv", from: "1.0.0"),
.package(url: "https://github.com/groue/Semaphore", from: "0.0.8"),
.package(url: "https://github.com/mattmassicotte/Queue", from: "0.1.4"),
Expand All @@ -30,7 +30,8 @@ let package = Package(
.product(name: "FSEventsWrapper", package: "FSEventsWrapper", condition: .when(platforms: [.macOS])),
.product(name: "GlobPattern", package: "GlobPattern", condition: .when(platforms: [.macOS])),
"JSONRPC",
"LanguageServerProtocol",
.product(name: "LanguageServerProtocol", package: "LanguageServerProtocol"),
.product(name: "LSPClient", package: "LanguageServerProtocol"),
.product(name: "ProcessEnv", package: "ProcessEnv", condition: .when(platforms: [.macOS])),
"Queue",
"Semaphore",
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ let server = JSONRPCServer(dataChannel: channel)
```swift
import LanguageClient
import LanguageServerProtocol
import LSPClient
import Foundation

let executionParams = Process.ExecutionParameters(path: "/usr/bin/sourcekit-lsp", environment: ProcessInfo.processInfo.userEnvironment)

let channel = DataChannel.localProcessChannel(parameters: executionParams, terminationHandler: { print("terminated") })
let localServer = JSONRPCServer(dataChannel: channel)
let localServer = JSONRPCServerConnection(dataChannel: channel)

let provider: InitializingServer.InitializeParamsProvider = {
// you may need to fill in more of the textDocument field for completions
Expand All @@ -45,14 +47,14 @@ let provider: InitializingServer.InitializeParamsProvider = {
window: nil,
general: nil,
experimental: nil)

// pay careful attention to rootPath/rootURI/workspaceFolders, as different servers will
// have different expectations/requirements here

return InitializeParams(processId: Int(ProcessInfo.processInfo.processIdentifier),
locale: nil,
rootPath: nil,
rootURI: projectURL.absoluteString,
rootUri: projectURL.absoluteString,
initializationOptions: nil,
capabilities: capabilities,
trace: nil,
Expand All @@ -72,7 +74,7 @@ Task {
text: docContent)
let docParams = DidOpenTextDocumentParams(textDocument: doc)

try await server.didOpenTextDocument(params: docParams)
try await server.textDocumentDidOpen(params: docParams)

// make sure to pick a reasonable position within your test document
let pos = Position(line: 5, character: 25)
Expand Down
2 changes: 0 additions & 2 deletions Sources/LanguageClient/AsyncStreamTap.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Foundation

#if compiler(>=5.9)
/// Maintains a consistent external `AsyncStream` as interal source streams are changed.
public actor AsyncStreamTap<Element: Sendable> {
public typealias Stream = AsyncStream<Element>
Expand Down Expand Up @@ -30,4 +29,3 @@ public actor AsyncStreamTap<Element: Sendable> {
}
}
}
#endif
27 changes: 1 addition & 26 deletions Sources/LanguageClient/DataChannel+LocalProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,6 @@ import JSONRPC
#if canImport(ProcessEnv)
import ProcessEnv

#if compiler(>=5.9)

extension FileHandle {
public var dataStream: AsyncStream<Data> {
let (stream, continuation) = AsyncStream<Data>.makeStream()

readabilityHandler = { handle in
let data = handle.availableData

if data.isEmpty {
handle.readabilityHandler = nil
continuation.finish()
return
}

continuation.yield(data)
}

return stream
}
}

extension DataChannel {
@available(macOS 12.0, *)
public static func localProcessChannel(
Expand Down Expand Up @@ -54,10 +32,8 @@ extension DataChannel {

Task {
let dataStream = stdoutPipe.fileHandleForReading.dataStream
let byteStream = AsyncByteSequence(base: dataStream)
let framedData = AsyncMessageFramingSequence(base: byteStream)

for try await data in framedData {
for try await data in dataStream {
continuation.yield(data)
}

Expand Down Expand Up @@ -85,6 +61,5 @@ extension DataChannel {
return DataChannel(writeHandler: handler, dataSequence: stream)
}
}
#endif

#endif
33 changes: 13 additions & 20 deletions Sources/LanguageClient/DataChannel+UserScript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@ import Foundation
import LanguageServerProtocol
import JSONRPC


#if canImport(ProcessEnv)
import ProcessEnv

#if compiler(>=5.9)

#if os(macOS)
/// The user script directory for this app.
///
@available(macOS 12.0, *)
private let userScriptDirectory = try? FileManager.default.url(for: .applicationScriptsDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
private let userScriptDirectory = try? FileManager.default.url(
for: .applicationScriptsDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false
)

extension DataChannel {

Expand All @@ -26,11 +23,11 @@ extension DataChannel {
/// - terminationHandler: Termination handler to invoke when the user script terminates.
///
@available(macOS 12.0, *)
public static func userScriptChannel(scriptPath: String,
arguments: [String] = [],
terminationHandler: @escaping @Sendable () -> Void)
throws -> DataChannel
{
public static func userScriptChannel(
scriptPath: String,
arguments: [String] = [],
terminationHandler: @escaping @Sendable () -> Void
) throws -> DataChannel {
guard let scriptURL = userScriptDirectory?.appendingPathComponent(scriptPath) else {
throw CocoaError(.fileNoSuchFile)
}
Expand All @@ -45,10 +42,8 @@ extension DataChannel {
// Forward stdout to the data channel
Task {
let dataStream = stdoutPipe.fileHandleForReading.dataStream
let byteStream = AsyncByteSequence(base: dataStream)
let framedData = AsyncMessageFramingSequence(base: byteStream)

for try await data in framedData {
for try await data in dataStream {
continuation.yield(data)
}

Expand Down Expand Up @@ -91,5 +86,3 @@ extension DataChannel {
}

#endif

#endif
4 changes: 3 additions & 1 deletion Sources/LanguageClient/FileEventAsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ extension FileChangeType {
}
}

#if compiler(>=5.9)
public struct FileEventAsyncSequence: AsyncSequence {
public typealias Element = FileEvent

public struct FileEventAsyncIterator: AsyncIteratorProtocol {
private let stream: AsyncCompactMapSequence<FSEventAsyncStream, Element>
private var internalIterator: AsyncCompactMapSequence<FSEventAsyncStream, Element>.Iterator
Expand Down Expand Up @@ -111,6 +111,8 @@ public struct FileEventAsyncSequence: AsyncSequence {
public func makeAsyncIterator() -> FileEventAsyncIterator {
FileEventAsyncIterator(root: root.path, kind: kind, pattern: pattern, filterInProcessChanges: filterInProcessChanges)
}

}
#endif

#endif
21 changes: 21 additions & 0 deletions Sources/LanguageClient/FileHandle+DataStream.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

extension FileHandle {
public var dataStream: AsyncStream<Data> {
let (stream, continuation) = AsyncStream<Data>.makeStream()

readabilityHandler = { handle in
let data = handle.availableData

if data.isEmpty {
handle.readabilityHandler = nil
continuation.finish()
return
}

continuation.yield(data)
}

return stream
}
}
Loading

0 comments on commit eda2fdc

Please sign in to comment.