forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCodeiumLanguageServer.swift
More file actions
157 lines (139 loc) · 4.86 KB
/
CodeiumLanguageServer.swift
File metadata and controls
157 lines (139 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import Foundation
import JSONRPC
import LanguageClient
import LanguageServerProtocol
protocol CodeiumLSP {
func sendRequest<E: CodeiumRequestType>(_ endpoint: E) async throws -> E.Response
}
class CodeiumLanguageServer: CodeiumLSP {
let languageServerExecutableURL: URL
let managerDirectoryURL: URL
let supportURL: URL
let process: Process
let transport: StdioDataTransport
var terminationHandler: (() -> Void)?
var launchHandler: (() -> Void)?
var port: String?
init(
languageServerExecutableURL: URL,
managerDirectoryURL: URL,
supportURL: URL,
terminationHandler: (() -> Void)? = nil,
launchHandler: (() -> Void)? = nil
) {
self.languageServerExecutableURL = languageServerExecutableURL
self.managerDirectoryURL = managerDirectoryURL
self.supportURL = supportURL
self.terminationHandler = terminationHandler
self.launchHandler = launchHandler
process = Process()
transport = StdioDataTransport()
process.standardInput = transport.stdinPipe
process.standardOutput = transport.stdoutPipe
process.standardError = transport.stderrPipe
process.executableURL = languageServerExecutableURL
process.arguments = [
"--api_server_host",
"server.codeium.com",
"--api_server_port",
"443",
"--manager_dir",
managerDirectoryURL.path,
]
process.currentDirectoryURL = supportURL
process.terminationHandler = { [weak self] task in
self?.processTerminated(task)
}
}
func start() {
guard !process.isRunning else { return }
port = nil
do {
try process.run()
Task {
func findPort() -> String? {
// find a file in managerDirectoryURL whose name looks like a port, return the
// name if found
let fileManager = FileManager.default
let enumerator = fileManager.enumerator(
at: managerDirectoryURL,
includingPropertiesForKeys: nil
)
while let fileURL = enumerator?.nextObject() as? URL {
if fileURL.lastPathComponent.range(
of: #"^\d+$"#,
options: .regularExpression
) != nil {
return fileURL.lastPathComponent
}
}
return nil
}
try await Task.sleep(nanoseconds: 2_000_000)
port = findPort()
var waited = 0
while true {
try await Task.sleep(nanoseconds: 1_000_000_000)
waited += 1
port = findPort()
if port != nil { break }
if waited >= 60 {
process.terminate()
}
}
}
} catch {
print("start: ", error)
processTerminated(process)
}
}
deinit {
process.terminationHandler = nil
process.terminate()
transport.close()
}
private func processTerminated(_: Process) {
transport.close()
terminationHandler?()
}
func handleFileEvent(_ event: FileEvent) {
switch event.type {
case .created:
let fileURL = URL(fileURLWithPath: event.uri)
var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: fileURL.path, isDirectory: &isDirectory),
!isDirectory.boolValue
{
let portName = fileURL.lastPathComponent
print("set port", portName)
if port == nil {
port = portName
}
}
default:
break
}
}
func sendRequest<E>(_ request: E) async throws -> E.Response where E: CodeiumRequestType {
guard let port else { throw CancellationError() }
let request = request.makeURLRequest(server: "http://127.0.0.1:\(port)")
let (data, response) = try await URLSession.shared.data(for: request)
if (response as? HTTPURLResponse)?.statusCode == 200 {
do {
let response = try JSONDecoder().decode(E.Response.self, from: data)
return response
} catch {
dump(error)
throw error
}
} else {
do {
let error = try JSONDecoder().decode(CodeiumResponseError.self, from: data)
throw error
} catch {
print(String(data: data, encoding: .utf8))
throw error
}
}
}
}