// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 import Foundation import JSONRPC import LanguageServer import LanguageServerProtocol import Logging import PterodactylBuild import TSCBasic import llbuild2fx final class EventHandler { private let connection: JSONRPCClientConnection private let buildEngine: FXEngine private let casContext: TSCUtility.Context private let casClient: LLBCASFSClient private let sourceTreeManager: SourceTreeManager init(connection: JSONRPCClientConnection) async throws { self.connection = connection let group = LLBMakeDefaultDispatchGroup() let db = LLBInMemoryCASDatabase(group: group) let functionCache = FXInMemoryFunctionCache(group: group) let executor = FXLocalExecutor() self.buildEngine = FXEngine(group: group, db: db, functionCache: functionCache, executor: executor) self.casClient = LLBCASFSClient(db) self.casContext = Context() self.sourceTreeManager = try await SourceTreeManager(buildEngine: buildEngine, casClient: casClient, casContext: casContext) } func publishDiagnostics(uri: String, version: Int?) async throws { } } extension EventHandler: LanguageServer.EventHandler { var textDocumentSinkOptions: TextDocumentSyncOptions { TextDocumentSyncOptions( openClose: true, change: .full, save: .optionA(false) ) } var completionOptions: CompletionOptions { CompletionOptions( workDoneProgress: false, triggerCharacters: [], // TODO allCommitCharacters: nil, resolveProvider: false, completionItem: nil ) } var semanticTokensLegend: SemanticTokensLegend { SemanticTokensLegend(tokenTypes: SemanticTokenTypes.allStrings, tokenModifiers: SemanticTokenModifiers.allStrings) } var semanticTokensOptions: SemanticTokensOptions { SemanticTokensOptions(legend: semanticTokensLegend, full: .optionB(SemanticTokensClientCapabilities.Requests.Full(delta: false))) } func initialize(id: JSONId, params: InitializeParams) async -> Response { Logger.shared.debug("Received initialize request") var serverCapabilities = ServerCapabilities() serverCapabilities.textDocumentSync = .optionA(textDocumentSinkOptions) serverCapabilities.completionProvider = completionOptions serverCapabilities.hoverProvider = .optionA(false) serverCapabilities.semanticTokensProvider = .optionA(semanticTokensOptions) serverCapabilities.foldingRangeProvider = .optionA(true) let response = InitializationResponse( capabilities: serverCapabilities, serverInfo: nil ) return .success(response) } func textDocumentDidChange(_ params: DidChangeTextDocumentParams) async { guard let text = params.contentChanges.first?.text else { return } do { try await sourceTreeManager.setBufferText(uri: params.textDocument.uri, text: text) // FIXME: this needs to be a transaction. // try await publishDiagnostics(uri: params.textDocument.uri, version: params.textDocument.version) } catch {} } func textDocumentDidOpen(_ params: DidOpenTextDocumentParams) async { // TODO: restore // await bufferManager.setBufferText(key: params.textDocument.uri, text: params.textDocument.text) do { try await publishDiagnostics(uri: params.textDocument.uri, version: params.textDocument.version) } catch {} } func semanticTokensFull(id: JSONId, params: SemanticTokensParams) async -> Response { // TODO: restore // guard let document = await bufferManager.documentForBuffer(key: params.textDocument.uri) else { return .success(nil) } let tokenSink: [SemanticToken] = [] // TODO: restore // document.tree.collectSemanticTokens(&tokenSink) let response = SemanticTokensResponse(SemanticTokens(resultId: nil, tokens: tokenSink)) return .success(response) } func foldingRange(id: JSONId, params: FoldingRangeParams) async -> Response { // TODO: restore // guard let document = await bufferManager.documentForBuffer(key: params.textDocument.uri) else { return .success(nil) } let rangeSink: [FoldingRange] = [] // TODO: restore // document.tree.collectFoldingRanges(&rangeSink) let response = FoldingRangeResponse(rangeSink) Logger.shared.info("Ranges: \(rangeSink)") return .success(response) } func typeHierarchySupertypes( id: JSONRPC.JSONId, params: TypeHierarchySupertypesParams ) async -> Response { .success(nil) } func typeHierarchySubtypes( id: JSONId, params: TypeHierarchySubtypesParams ) async -> Response { .success(nil) } func internalError(_ error: any Error) async { Logger.shared.error("Received error: \(error)") } func initialized(_ params: InitializedParams) async { } func exit() async { Logger.shared.info("Received exit notification") Foundation.exit(0) } func diagnostics(id: JSONId, params: DocumentDiagnosticParams) async -> Response { return .success(.init(kind: .unchanged)) } func textDocumentDidClose(_ params: DidCloseTextDocumentParams) async { } func textDocumentWillSave(_ params: WillSaveTextDocumentParams) async { } func textDocumentDidSave(_ params: DidSaveTextDocumentParams) async { } func protocolCancelRequest(_ params: CancelParams) async { } func protocolSetTrace(_ params: SetTraceParams) async { } func workspaceDidChangeWatchedFiles(_ params: DidChangeWatchedFilesParams) async { } func windowWorkDoneProgressCancel(_ params: WorkDoneProgressCancelParams) async { } func workspaceDidChangeWorkspaceFolders(_ params: DidChangeWorkspaceFoldersParams) async { } func workspaceDidChangeConfiguration(_ params: DidChangeConfigurationParams) async { } func workspaceDidCreateFiles(_ params: CreateFilesParams) async { } func workspaceDidRenameFiles(_ params: RenameFilesParams) async { } func workspaceDidDeleteFiles(_ params: DeleteFilesParams) async { } func shutdown(id: JSONId) async {} func workspaceInlayHintRefresh(id: JSONId) async {} func workspaceExecuteCommand(id: JSONId, params: ExecuteCommandParams) async -> Response { .success(nil) } func workspaceWillCreateFiles(id: JSONId, params: CreateFilesParams) async -> Response { .success(nil) } func workspaceWillRenameFiles(id: JSONId, params: RenameFilesParams) async -> Response { .success(nil) } func workspaceWillDeleteFiles(id: JSONId, params: DeleteFilesParams) async -> Response { .success(nil) } func workspaceSymbol(id: JSONId, params: WorkspaceSymbolParams) async -> Response { .success(nil) } // func workspaceSymbolResolve(id: JSONId, params: WorkspaceSymbol) async -> Response { .success(nil) } func textDocumentWillSaveWaitUntil(id: JSONId, params: WillSaveTextDocumentParams) async -> Response<[TextEdit]?> { .success(nil) } func completion(id: JSONId, params: CompletionParams) async -> Response { .success(CompletionResponse(.optionB(.init(isIncomplete: false, items: [])))) } // func completionItemResolve(id: JSONId, params: CompletionItem) async -> Response { .success(nil) } func hover(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func signatureHelp(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func declaration(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func definition(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func typeDefinition(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func implementation(id: JSONId, params: TextDocumentPositionParams) async -> Response { .success(nil) } func documentHighlight(id: JSONId, params: DocumentHighlightParams) async -> Response { .success(nil) } func documentSymbol(id: JSONId, params: DocumentSymbolParams) async -> Response { .success(nil) } func codeAction(id: JSONId, params: CodeActionParams) async -> Response { .success(nil) } // func codeActionResolve(id: JSONId, params: CodeAction) async -> Response { .success(nil) } func codeLens(id: JSONId, params: CodeLensParams) async -> Response { .success(nil) } // func codeLensResolve(id: JSONId, params: CodeLens) async -> Response { .success(nil) } func selectionRange(id: JSONId, params: SelectionRangeParams) async -> Response { .success(nil) } func linkedEditingRange(id: JSONId, params: LinkedEditingRangeParams) async -> Response { .success(nil) } func prepareCallHierarchy(id: JSONId, params: CallHierarchyPrepareParams) async -> Response { .success(nil) } func prepareRename(id: JSONId, params: PrepareRenameParams) async -> Response { .success(nil) } func prepareTypeHeirarchy(id: JSONId, params: TypeHierarchyPrepareParams) async -> Response { .success(nil) } func rename(id: JSONId, params: RenameParams) async -> Response { .success(nil) } func inlayHint(id: JSONId, params: InlayHintParams) async -> Response { .success(nil) } func inlayHintResolve(id: JSONId, params: InlayHint) async -> Response { .success(nil) } func documentLink(id: JSONId, params: DocumentLinkParams) async -> Response { .success(nil) } // func documentLinkResolve(id: JSONId, params: DocumentLink) async -> Response { .success(nil) } // func documentColor(id: JSONId, params: DocumentColorParams) async -> Response { .success(nil) } // func colorPresentation(id: JSONId, params: ColorPresentationParams) async -> Response { .success(nil) } func formatting(id: JSONId, params: DocumentFormattingParams) async -> Response { .success(nil) } func rangeFormatting(id: JSONId, params: DocumentRangeFormattingParams) async -> Response { .success(nil) } func onTypeFormatting(id: JSONId, params: DocumentOnTypeFormattingParams) async -> Response { .success(nil) } func references(id: JSONId, params: ReferenceParams) async -> Response { .success(nil) } func moniker(id: JSONId, params: MonikerParams) async -> Response { .success(nil) } func semanticTokensFullDelta(id: JSONId, params: SemanticTokensDeltaParams) async -> Response { .success(nil) } func semanticTokensRange(id: JSONId, params: SemanticTokensRangeParams) async -> Response { .success(nil) } func callHierarchyIncomingCalls(id: JSONId, params: CallHierarchyIncomingCallsParams) async -> Response { .success(nil) } func callHierarchyOutgoingCalls(id: JSONId, params: CallHierarchyOutgoingCallsParams) async -> Response { .success(nil) } func custom(id: JSONId, method: String, params: LSPAny) async -> Response { .success(nil) } }