1// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers 2// 3// SPDX-License-Identifier: MPL-2.0 4 5import Foundation 6import JSONRPC 7import LanguageServer 8import LanguageServerProtocol 9import Logging 10import PterodactylBuild 11import TSCBasic 12import llbuild2fx 13 14final class EventHandler { 15 private let connection: JSONRPCClientConnection 16 private let buildEngine: FXEngine 17 private let casContext: TSCUtility.Context 18 private let casClient: LLBCASFSClient 19 private let sourceTreeManager: SourceTreeManager 20 21 init(connection: JSONRPCClientConnection) async throws { 22 self.connection = connection 23 24 let group = LLBMakeDefaultDispatchGroup() 25 let db = LLBInMemoryCASDatabase(group: group) 26 let functionCache = FXInMemoryFunctionCache(group: group) 27 let executor = FXLocalExecutor() 28 self.buildEngine = FXEngine(group: group, db: db, functionCache: functionCache, executor: executor) 29 self.casClient = LLBCASFSClient(db) 30 self.casContext = Context() 31 32 self.sourceTreeManager = try await SourceTreeManager(buildEngine: buildEngine, casClient: casClient, casContext: casContext) 33 } 34 35 func publishDiagnostics(uri: String, version: Int?) async throws { 36 37 } 38} 39 40 41extension EventHandler: LanguageServer.EventHandler { 42 var textDocumentSinkOptions: TextDocumentSyncOptions { 43 TextDocumentSyncOptions( 44 openClose: true, 45 change: .full, 46 save: .optionA(false) 47 ) 48 } 49 50 var completionOptions: CompletionOptions { 51 CompletionOptions( 52 workDoneProgress: false, 53 triggerCharacters: [], // TODO 54 allCommitCharacters: nil, 55 resolveProvider: false, 56 completionItem: nil 57 ) 58 } 59 60 var semanticTokensLegend: SemanticTokensLegend { 61 SemanticTokensLegend(tokenTypes: SemanticTokenTypes.allStrings, tokenModifiers: SemanticTokenModifiers.allStrings) 62 } 63 64 var semanticTokensOptions: SemanticTokensOptions { 65 SemanticTokensOptions(legend: semanticTokensLegend, full: .optionB(SemanticTokensClientCapabilities.Requests.Full(delta: false))) 66 } 67 68 func initialize(id: JSONId, params: InitializeParams) async -> Response<InitializationResponse> { 69 Logger.shared.debug("Received initialize request") 70 var serverCapabilities = ServerCapabilities() 71 serverCapabilities.textDocumentSync = .optionA(textDocumentSinkOptions) 72 serverCapabilities.completionProvider = completionOptions 73 serverCapabilities.hoverProvider = .optionA(false) 74 serverCapabilities.semanticTokensProvider = .optionA(semanticTokensOptions) 75 serverCapabilities.foldingRangeProvider = .optionA(true) 76 let response = InitializationResponse( 77 capabilities: serverCapabilities, 78 serverInfo: nil 79 ) 80 return .success(response) 81 } 82 83 func textDocumentDidChange(_ params: DidChangeTextDocumentParams) async { 84 guard let text = params.contentChanges.first?.text else { return } 85 do { 86 try await sourceTreeManager.setBufferText(uri: params.textDocument.uri, text: text) 87 // FIXME: this needs to be a transaction. 88 // try await publishDiagnostics(uri: params.textDocument.uri, version: params.textDocument.version) 89 } catch {} 90 } 91 92 func textDocumentDidOpen(_ params: DidOpenTextDocumentParams) async { 93 // TODO: restore 94 // await bufferManager.setBufferText(key: params.textDocument.uri, text: params.textDocument.text) 95 do { 96 try await publishDiagnostics(uri: params.textDocument.uri, version: params.textDocument.version) 97 } catch {} 98 } 99 100 func semanticTokensFull(id: JSONId, params: SemanticTokensParams) async -> Response<SemanticTokensResponse> { 101 // TODO: restore 102 // guard let document = await bufferManager.documentForBuffer(key: params.textDocument.uri) else { return .success(nil) } 103 104 let tokenSink: [SemanticToken] = [] 105 // TODO: restore 106 // document.tree.collectSemanticTokens(&tokenSink) 107 108 let response = SemanticTokensResponse(SemanticTokens(resultId: nil, tokens: tokenSink)) 109 return .success(response) 110 } 111 112 func foldingRange(id: JSONId, params: FoldingRangeParams) async -> Response<FoldingRangeResponse> { 113 // TODO: restore 114 // guard let document = await bufferManager.documentForBuffer(key: params.textDocument.uri) else { return .success(nil) } 115 let rangeSink: [FoldingRange] = [] 116 // TODO: restore 117 // document.tree.collectFoldingRanges(&rangeSink) 118 let response = FoldingRangeResponse(rangeSink) 119 Logger.shared.info("Ranges: \(rangeSink)") 120 return .success(response) 121 } 122 123 124 func typeHierarchySupertypes( 125 id: JSONRPC.JSONId, 126 params: TypeHierarchySupertypesParams 127 ) async -> Response<TypeHierarchySupertypesResponse> { 128 .success(nil) 129 } 130 131 func typeHierarchySubtypes( 132 id: JSONId, 133 params: TypeHierarchySubtypesParams 134 ) async -> Response<TypeHierarchySubtypesResponse> { 135 .success(nil) 136 } 137 138 func internalError(_ error: any Error) async { 139 Logger.shared.error("Received error: \(error)") 140 } 141 142 func initialized(_ params: InitializedParams) async { 143 144 } 145 146 func exit() async { 147 Logger.shared.info("Received exit notification") 148 Foundation.exit(0) 149 } 150 151 func diagnostics(id: JSONId, params: DocumentDiagnosticParams) async -> Response<DocumentDiagnosticReport> { 152 return .success(.init(kind: .unchanged)) 153 } 154 155 156 157 func textDocumentDidClose(_ params: DidCloseTextDocumentParams) async { 158 159 } 160 161 func textDocumentWillSave(_ params: WillSaveTextDocumentParams) async { 162 163 } 164 165 func textDocumentDidSave(_ params: DidSaveTextDocumentParams) async { 166 167 } 168 169 func protocolCancelRequest(_ params: CancelParams) async { 170 171 } 172 173 func protocolSetTrace(_ params: SetTraceParams) async { 174 175 } 176 177 func workspaceDidChangeWatchedFiles(_ params: DidChangeWatchedFilesParams) async { 178 179 } 180 181 func windowWorkDoneProgressCancel(_ params: WorkDoneProgressCancelParams) async { 182 183 } 184 185 func workspaceDidChangeWorkspaceFolders(_ params: DidChangeWorkspaceFoldersParams) async { 186 187 } 188 189 func workspaceDidChangeConfiguration(_ params: DidChangeConfigurationParams) async { 190 191 } 192 193 func workspaceDidCreateFiles(_ params: CreateFilesParams) async { 194 195 } 196 197 func workspaceDidRenameFiles(_ params: RenameFilesParams) async { 198 199 } 200 201 func workspaceDidDeleteFiles(_ params: DeleteFilesParams) async { 202 203 } 204 205 func shutdown(id: JSONId) async {} 206 func workspaceInlayHintRefresh(id: JSONId) async {} 207 func workspaceExecuteCommand(id: JSONId, params: ExecuteCommandParams) async -> Response<LSPAny?> { .success(nil) } 208 func workspaceWillCreateFiles(id: JSONId, params: CreateFilesParams) async -> Response<WorkspaceEdit?> { .success(nil) } 209 func workspaceWillRenameFiles(id: JSONId, params: RenameFilesParams) async -> Response<WorkspaceEdit?> { .success(nil) } 210 func workspaceWillDeleteFiles(id: JSONId, params: DeleteFilesParams) async -> Response<WorkspaceEdit?> { .success(nil) } 211 func workspaceSymbol(id: JSONId, params: WorkspaceSymbolParams) async -> Response<WorkspaceSymbolResponse> { .success(nil) } 212 // func workspaceSymbolResolve(id: JSONId, params: WorkspaceSymbol) async -> Response<WorkspaceSymbol> { .success(nil) } 213 func textDocumentWillSaveWaitUntil(id: JSONId, params: WillSaveTextDocumentParams) async -> Response<[TextEdit]?> { .success(nil) } 214 func completion(id: JSONId, params: CompletionParams) async -> Response<CompletionResponse> { 215 .success(CompletionResponse(.optionB(.init(isIncomplete: false, items: [])))) 216 } 217 // func completionItemResolve(id: JSONId, params: CompletionItem) async -> Response<CompletionItem> { .success(nil) } 218 func hover(id: JSONId, params: TextDocumentPositionParams) async -> Response<HoverResponse> { .success(nil) } 219 func signatureHelp(id: JSONId, params: TextDocumentPositionParams) async -> Response<SignatureHelpResponse> { .success(nil) } 220 func declaration(id: JSONId, params: TextDocumentPositionParams) async -> Response<DeclarationResponse> { .success(nil) } 221 func definition(id: JSONId, params: TextDocumentPositionParams) async -> Response<DefinitionResponse> { .success(nil) } 222 func typeDefinition(id: JSONId, params: TextDocumentPositionParams) async -> Response<TypeDefinitionResponse> { .success(nil) } 223 func implementation(id: JSONId, params: TextDocumentPositionParams) async -> Response<ImplementationResponse> { .success(nil) } 224 func documentHighlight(id: JSONId, params: DocumentHighlightParams) async -> Response<DocumentHighlightResponse> { .success(nil) } 225 func documentSymbol(id: JSONId, params: DocumentSymbolParams) async -> Response<DocumentSymbolResponse> { .success(nil) } 226 func codeAction(id: JSONId, params: CodeActionParams) async -> Response<CodeActionResponse> { .success(nil) } 227 // func codeActionResolve(id: JSONId, params: CodeAction) async -> Response<CodeAction> { .success(nil) } 228 func codeLens(id: JSONId, params: CodeLensParams) async -> Response<CodeLensResponse> { .success(nil) } 229 // func codeLensResolve(id: JSONId, params: CodeLens) async -> Response<CodeLens> { .success(nil) } 230 func selectionRange(id: JSONId, params: SelectionRangeParams) async -> Response<SelectionRangeResponse> { .success(nil) } 231 func linkedEditingRange(id: JSONId, params: LinkedEditingRangeParams) async -> Response<LinkedEditingRangeResponse> { .success(nil) } 232 func prepareCallHierarchy(id: JSONId, params: CallHierarchyPrepareParams) async -> Response<CallHierarchyPrepareResponse> { .success(nil) } 233 func prepareRename(id: JSONId, params: PrepareRenameParams) async -> Response<PrepareRenameResponse> { .success(nil) } 234 func prepareTypeHeirarchy(id: JSONId, params: TypeHierarchyPrepareParams) async -> Response<PrepareTypeHeirarchyResponse> { .success(nil) } 235 func rename(id: JSONId, params: RenameParams) async -> Response<RenameResponse> { .success(nil) } 236 func inlayHint(id: JSONId, params: InlayHintParams) async -> Response<InlayHintResponse> { .success(nil) } 237 func inlayHintResolve(id: JSONId, params: InlayHint) async -> Response<InlayHintResponse> { .success(nil) } 238 func documentLink(id: JSONId, params: DocumentLinkParams) async -> Response<DocumentLinkResponse> { .success(nil) } 239 // func documentLinkResolve(id: JSONId, params: DocumentLink) async -> Response<DocumentLink> { .success(nil) } 240 // func documentColor(id: JSONId, params: DocumentColorParams) async -> Response<DocumentColorResponse> { .success(nil) } 241 // func colorPresentation(id: JSONId, params: ColorPresentationParams) async -> Response<ColorPresentationResponse> { .success(nil) } 242 func formatting(id: JSONId, params: DocumentFormattingParams) async -> Response<FormattingResult> { .success(nil) } 243 func rangeFormatting(id: JSONId, params: DocumentRangeFormattingParams) async -> Response<FormattingResult> { .success(nil) } 244 func onTypeFormatting(id: JSONId, params: DocumentOnTypeFormattingParams) async -> Response<FormattingResult> { .success(nil) } 245 func references(id: JSONId, params: ReferenceParams) async -> Response<ReferenceResponse> { .success(nil) } 246 func moniker(id: JSONId, params: MonikerParams) async -> Response<MonikerResponse> { .success(nil) } 247 func semanticTokensFullDelta(id: JSONId, params: SemanticTokensDeltaParams) async -> Response<SemanticTokensDeltaResponse> { .success(nil) } 248 func semanticTokensRange(id: JSONId, params: SemanticTokensRangeParams) async -> Response<SemanticTokensResponse> { .success(nil) } 249 func callHierarchyIncomingCalls(id: JSONId, params: CallHierarchyIncomingCallsParams) async -> Response<CallHierarchyIncomingCallsResponse> { .success(nil) } 250 func callHierarchyOutgoingCalls(id: JSONId, params: CallHierarchyOutgoingCallsParams) async -> Response<CallHierarchyOutgoingCallsResponse> { .success(nil) } 251 func custom(id: JSONId, method: String, params: LSPAny) async -> Response<LSPAny> { .success(nil) } 252}