From 86675cbbf75081899d8efe060540f6274f5ae493 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Mon, 30 Mar 2026 07:25:42 +0200 Subject: [PATCH] refactor: split server flix.ts and handlers.ts into domain-focused modules Split engine/flix.ts (268 lines) into config.ts (types), process.ts (lifecycle), and workspace.ts (file/pkg/jar ops). Split handlers/handlers.ts (388 lines) into lifecycle.ts, navigation.ts, editing.ts, symbols.ts, semantic.ts, and resources.ts by concern. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/src/engine/config.ts | 38 ++++ server/src/engine/index.ts | 4 +- server/src/engine/{flix.ts => process.ts} | 119 +--------- server/src/engine/queue.ts | 2 +- server/src/engine/workspace.ts | 115 ++++++++++ server/src/handlers/editing.ts | 69 ++++++ server/src/handlers/index.ts | 7 +- .../handlers/{handlers.ts => lifecycle.ts} | 215 +----------------- server/src/handlers/navigation.ts | 57 +++++ server/src/handlers/resources.ts | 78 +++++++ server/src/handlers/semantic.ts | 37 +++ server/src/handlers/symbols.ts | 57 +++++ 12 files changed, 475 insertions(+), 323 deletions(-) create mode 100644 server/src/engine/config.ts rename server/src/engine/{flix.ts => process.ts} (65%) create mode 100644 server/src/engine/workspace.ts create mode 100644 server/src/handlers/editing.ts rename server/src/handlers/{handlers.ts => lifecycle.ts} (50%) create mode 100644 server/src/handlers/navigation.ts create mode 100644 server/src/handlers/resources.ts create mode 100644 server/src/handlers/semantic.ts create mode 100644 server/src/handlers/symbols.ts diff --git a/server/src/engine/config.ts b/server/src/engine/config.ts new file mode 100644 index 00000000..6f7845b7 --- /dev/null +++ b/server/src/engine/config.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface UserConfiguration { + extraJvmArgs: string + extraFlixArgs: string +} + +/** + * Payload sent from the client via the `internalReady` notification. + * + * In single-file mode (no folder open) `workspaceFolders` is omitted and + * `workspaceFiles` contains only the currently open `.flix` file(s). + */ +export interface StartEngineInput { + flixFilename: string + workspaceFolders?: string[] + extensionPath: string + extensionVersion: string + globalStoragePath: string + workspaceFiles: string[] + workspacePkgs: string[] + workspaceJars: string[] + userConfiguration: UserConfiguration +} diff --git a/server/src/engine/index.ts b/server/src/engine/index.ts index e88fab95..6c4f1845 100644 --- a/server/src/engine/index.ts +++ b/server/src/engine/index.ts @@ -14,4 +14,6 @@ * limitations under the License. */ -export * from './flix' +export * from './config' +export * from './process' +export * from './workspace' diff --git a/server/src/engine/flix.ts b/server/src/engine/process.ts similarity index 65% rename from server/src/engine/flix.ts rename to server/src/engine/process.ts index 9a42d87e..88c80b54 100644 --- a/server/src/engine/flix.ts +++ b/server/src/engine/process.ts @@ -20,38 +20,16 @@ import javaVersion from '../util/javaVersion' import { ChildProcess, spawn } from 'child_process' import { getPortPromise } from 'portfinder' +import { UserConfiguration, StartEngineInput } from './config' +import { initWorkspaceFiles } from './workspace' import * as jobs from './jobs' import * as queue from './queue' import * as socket from './socket' import { USER_MESSAGE } from '../util/userMessages' -export interface UserConfiguration { - extraJvmArgs: string - extraFlixArgs: string -} - -/** - * Payload sent from the client via the `internalReady` notification. - * - * In single-file mode (no folder open) `workspaceFolders` is omitted and - * `workspaceFiles` contains only the currently open `.flix` file(s). - */ -export interface StartEngineInput { - flixFilename: string - workspaceFolders?: string[] - extensionPath: string - extensionVersion: string - globalStoragePath: string - workspaceFiles: string[] - workspacePkgs: string[] - workspaceJars: string[] - userConfiguration: UserConfiguration -} - let flixInstance: ChildProcess | undefined = undefined let startEngineInput: StartEngineInput let flixRunning: boolean = false -let currentWorkspaceFiles: Set = new Set() export function isRunning() { return flixRunning @@ -81,7 +59,7 @@ export async function start(input: StartEngineInput) { const { flixFilename, extensionPath, workspaceFiles, workspacePkgs, workspaceJars } = input - currentWorkspaceFiles = new Set(workspaceFiles) + initWorkspaceFiles(workspaceFiles) // Check for valid Java version const { majorVersion, versionString } = await javaVersion(extensionPath) @@ -175,94 +153,3 @@ export async function stop() { flixInstance.kill() } } - -/** - * Add the given `uri` to the workspace. - */ -export function addUri(uri: string) { - currentWorkspaceFiles.add(uri) - - const job: jobs.Job = { - request: jobs.Request.apiAddUri, - uri, - } - queue.enqueue(job) -} - -/** - * Handle a change in the file with the given `uri`. - * - * If this URI has not already been added to the workspace via {@linkcode addUri}, - * it will be ignored, making it safe to call this function on any file. - */ -export function updateUri(uri: string, src: string) { - if (!currentWorkspaceFiles.has(uri)) { - return - } - - // Including the source code in the job is necessary because the file might not yet have been saved - const job: jobs.Job = { - request: jobs.Request.apiAddUri, - uri, - src, - } - - queue.enqueue(job) -} - -/** - * Remove the given `uri` from the workspace. - */ -export function remUri(uri: string) { - currentWorkspaceFiles.delete(uri) - - const job: jobs.Job = { - request: jobs.Request.apiRemUri, - uri, - } - queue.enqueue(job) -} - -export function addPkg(uri: string) { - const job: jobs.Job = { - request: jobs.Request.apiAddPkg, - uri, - } - queue.enqueue(job) -} - -export function remPkg(uri: string) { - const job: jobs.Job = { - request: jobs.Request.apiRemPkg, - uri, - } - queue.enqueue(job) -} - -export function addJar(uri: string) { - const job: jobs.Job = { - request: jobs.Request.apiAddJar, - uri, - } - queue.enqueue(job) -} - -export function remJar(uri: string) { - const job: jobs.Job = { - request: jobs.Request.apiRemJar, - uri, - } - queue.enqueue(job) -} - -export function enqueueJobWithFlattenedParams(request: jobs.Request, params?: any) { - const job: jobs.Job = { - request, - ...(params || {}), - } - return queue.enqueue(job) -} - -export function unfinishedJobs() { - return queue.unfinishedJobs() -} diff --git a/server/src/engine/queue.ts b/server/src/engine/queue.ts index f69ce6c8..b94bccf1 100644 --- a/server/src/engine/queue.ts +++ b/server/src/engine/queue.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as engine from './flix' +import * as engine from './process' import * as jobs from './jobs' import * as socket from './socket' import { fileURLToPath } from 'url' diff --git a/server/src/engine/workspace.ts b/server/src/engine/workspace.ts new file mode 100644 index 00000000..aa900188 --- /dev/null +++ b/server/src/engine/workspace.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as jobs from './jobs' +import * as queue from './queue' + +let currentWorkspaceFiles: Set = new Set() + +export function initWorkspaceFiles(files: string[]) { + currentWorkspaceFiles = new Set(files) +} + +/** + * Add the given `uri` to the workspace. + */ +export function addUri(uri: string) { + currentWorkspaceFiles.add(uri) + + const job: jobs.Job = { + request: jobs.Request.apiAddUri, + uri, + } + queue.enqueue(job) +} + +/** + * Handle a change in the file with the given `uri`. + * + * If this URI has not already been added to the workspace via {@linkcode addUri}, + * it will be ignored, making it safe to call this function on any file. + */ +export function updateUri(uri: string, src: string) { + if (!currentWorkspaceFiles.has(uri)) { + return + } + + // Including the source code in the job is necessary because the file might not yet have been saved + const job: jobs.Job = { + request: jobs.Request.apiAddUri, + uri, + src, + } + + queue.enqueue(job) +} + +/** + * Remove the given `uri` from the workspace. + */ +export function remUri(uri: string) { + currentWorkspaceFiles.delete(uri) + + const job: jobs.Job = { + request: jobs.Request.apiRemUri, + uri, + } + queue.enqueue(job) +} + +export function addPkg(uri: string) { + const job: jobs.Job = { + request: jobs.Request.apiAddPkg, + uri, + } + queue.enqueue(job) +} + +export function remPkg(uri: string) { + const job: jobs.Job = { + request: jobs.Request.apiRemPkg, + uri, + } + queue.enqueue(job) +} + +export function addJar(uri: string) { + const job: jobs.Job = { + request: jobs.Request.apiAddJar, + uri, + } + queue.enqueue(job) +} + +export function remJar(uri: string) { + const job: jobs.Job = { + request: jobs.Request.apiRemJar, + uri, + } + queue.enqueue(job) +} + +export function enqueueJobWithFlattenedParams(request: jobs.Request, params?: any) { + const job: jobs.Job = { + request, + ...(params || {}), + } + return queue.enqueue(job) +} + +export function unfinishedJobs() { + return queue.unfinishedJobs() +} diff --git a/server/src/handlers/editing.ts b/server/src/handlers/editing.ts new file mode 100644 index 00000000..7cb8ba9f --- /dev/null +++ b/server/src/handlers/editing.ts @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DocumentFormattingParams, TextEdit } from 'vscode-languageserver' + +import * as jobs from '../engine/jobs' +import * as engine from '../engine' +import * as socket from '../engine/socket' + +import { makePositionalHandler, makeEnqueuePromise, makeDefaultResponseHandler } from './util' + +/** + * @function + */ +export const handleComplete = makePositionalHandler(jobs.Request.lspComplete) + +/** + * @function + */ +export const handleHover = makePositionalHandler(jobs.Request.lspHover) + +export const handleSignature = makePositionalHandler(jobs.Request.lspSignature) + +/** + * @function + */ +export const handleRename = makeEnqueuePromise(makeRenameJob, makeDefaultResponseHandler) as ( + params: any, +) => Promise + +function makeRenameJob(params: any) { + return { + request: jobs.Request.lspRename, + uri: params.textDocument.uri, + position: params.position, + newName: params.newName, + } +} + +/** + * Handle document formatting requests. + * + * @param params - The document formatting parameters. + * @returns A promise that resolves to an array of text edits. + */ +export const handleDocumentFormatting = (params: DocumentFormattingParams): Promise => { + const uri = params.textDocument?.uri + const options = params.options + + return new Promise(function (resolve) { + const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspFormatting, { uri, options }) + socket.eventEmitter.once(job.id, ({ result }: socket.FlixResponse) => { + resolve((result ?? []) as unknown as TextEdit[]) + }) + }) +} diff --git a/server/src/handlers/index.ts b/server/src/handlers/index.ts index 69ce6cb5..04a08c9a 100644 --- a/server/src/handlers/index.ts +++ b/server/src/handlers/index.ts @@ -14,4 +14,9 @@ * limitations under the License. */ -export * from './handlers' +export * from './lifecycle' +export * from './navigation' +export * from './editing' +export * from './symbols' +export * from './semantic' +export * from './resources' diff --git a/server/src/handlers/handlers.ts b/server/src/handlers/lifecycle.ts similarity index 50% rename from server/src/handlers/handlers.ts rename to server/src/handlers/lifecycle.ts index 4388fce6..6794616d 100644 --- a/server/src/handlers/handlers.ts +++ b/server/src/handlers/lifecycle.ts @@ -14,30 +14,17 @@ * limitations under the License. */ -import { - InitializeParams, - InitializeResult, - InlayHintParams, - TextDocumentChangeEvent, - TextDocumentSyncKind, - DocumentFormattingParams, - TextEdit, -} from 'vscode-languageserver' -import { TextDocument } from 'vscode-languageserver-textdocument' +import { InitializeParams, InitializeResult, TextDocumentSyncKind } from 'vscode-languageserver' import * as jobs from '../engine/jobs' import * as engine from '../engine' import * as socket from '../engine/socket' import { clearDiagnostics, sendDiagnostics, sendNotification } from '../server' -import { makePositionalHandler, makeEnqueuePromise, makeDefaultResponseHandler } from './util' +import { makeEnqueuePromise } from './util' import { USER_MESSAGE } from '../util/userMessages' import { StatusCode } from '../util/statusCodes' -interface UriInput { - uri: string -} - export function handleInitialize(_params: InitializeParams) { const result: InitializeResult = { capabilities: { @@ -114,8 +101,15 @@ export function handleInitialize(_params: InitializeParams) { return result } -export function handleReplaceConfiguration(userConfiguration: engine.UserConfiguration) { - engine.updateUserConfiguration(userConfiguration) +/** + * Runs when both client and server are ready. + */ +export function handleReady(engineInput: engine.StartEngineInput) { + engine.start(engineInput) +} + +export function handleExit() { + engine.stop() } /** @@ -127,39 +121,6 @@ export function handleDisconnect() { socket.sendMessage({ id: 'disconnect', request: jobs.Request.apiDisconnect }, expectResponse) } -/** - * Runs when both client and server are ready. - */ -export function handleReady(engineInput: engine.StartEngineInput) { - engine.start(engineInput) -} - -export function handleAddUri({ uri }: UriInput) { - engine.addUri(uri) -} - -export function handleRemUri({ uri }: UriInput) { - engine.remUri(uri) -} - -export function handleAddPkg({ uri }: UriInput) { - engine.addPkg(uri) -} - -export function handleRemPkg({ uri }: UriInput) { - engine.remPkg(uri) -} - -export function handleAddJar({ uri }: UriInput) { - engine.addJar(uri) -} - -export function handleRemJar({ uri }: UriInput) { - engine.remJar(uri) -} - -export const handleShowAst = makeEnqueuePromise(makeShowAstJob, makeShowAstResponseHandler) - /** * Request a response to be sent when all jobs are finished. */ @@ -177,160 +138,6 @@ export function handleFinishedAllJobs() { } } -function makeShowAstJob(params: any) { - return { - request: jobs.Request.lspShowAst, - uri: params.uri, - phase: params.phase, - } -} - -function makeShowAstResponseHandler(promiseResolver: () => void) { - return function responseHandler({ status, result }: any) { - sendNotification(jobs.Request.lspShowAst, { status, result }) - promiseResolver() - } -} - -export function handleExit() { - engine.stop() -} - -export function handleChangeContent(params: TextDocumentChangeEvent) { - const document = params.document - engine.updateUri(document.uri, document.getText()) -} - -/** - * @function - */ -export const handleGotoDefinition = makePositionalHandler(jobs.Request.lspGoto, makeGotoDefinitionResponseHandler) - -function makeGotoDefinitionResponseHandler(promiseResolver: (result?: socket.FlixResult) => void) { - return function responseHandler({ status, result }: socket.FlixResponse) { - const targetUri = result?.targetUri - if (status === StatusCode.Success) { - if (targetUri?.startsWith('file://')) { - return promiseResolver(result) - } else { - sendNotification(jobs.Request.internalMessage, USER_MESSAGE.FILE_NOT_AVAILABLE(targetUri!)) - } - } - promiseResolver() - } -} - -/** - * @function - */ -export const handleImplementation = makePositionalHandler(jobs.Request.lspImplementation) - -/** - * @function - */ -export const handleHighlight = makePositionalHandler(jobs.Request.lspHighlight) - -/** - * @function - */ -export const handleComplete = makePositionalHandler(jobs.Request.lspComplete) - -/** - * @function - */ -export const handleHover = makePositionalHandler(jobs.Request.lspHover) - -export const handleSignature = makePositionalHandler(jobs.Request.lspSignature) - -/** - * @function - */ -export const handleReferences = makePositionalHandler(jobs.Request.lspUses) - -/** - * @function - */ -export const handleCodelens = makePositionalHandler(jobs.Request.lspCodelens) - -/** - * @function - */ -export const handleRename = makeEnqueuePromise(makeRenameJob, makeDefaultResponseHandler) as ( - params: any, -) => Promise - -function makeRenameJob(params: any) { - return { - request: jobs.Request.lspRename, - uri: params.textDocument.uri, - position: params.position, - newName: params.newName, - } -} - -/** - * @function - */ -export const handleDocumentSymbols = makePositionalHandler(jobs.Request.lspDocumentSymbols) - -export function handleCodeAction(params: any): Promise { - const uri = params.textDocument ? params.textDocument.uri : undefined - const range = params.range - const context = params.context - - return new Promise(function (resolve) { - const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspCodeAction, { uri, range, context }) - socket.eventEmitter.once(job.id, ({ status, result }) => resolve(result)) - }) -} - -/** - * Handle document formatting requests. - * - * @param params - The document formatting parameters. - * @returns A promise that resolves to an array of text edits. - */ -export const handleDocumentFormatting = (params: DocumentFormattingParams): Promise => { - const uri = params.textDocument?.uri - const options = params.options - - return new Promise(function (resolve) { - const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspFormatting, { uri, options }) - socket.eventEmitter.once(job.id, ({ result }: socket.FlixResponse) => { - resolve((result ?? []) as unknown as TextEdit[]) - }) - }) -} - -/** - * @function - */ -export const handleWorkspaceSymbols = makeEnqueuePromise(makeWorkspaceSymbolsJob, makeDefaultResponseHandler) as ( - params: any, -) => Promise - -function makeWorkspaceSymbolsJob(params: any) { - return { - request: jobs.Request.lspWorkspaceSymbols, - position: params.position, - query: params.query || '', - } -} - -/** - * @function - */ -export const handleSemanticTokens = makePositionalHandler(jobs.Request.lspSemanticTokens) - -export const handleInlayHints = (params: InlayHintParams): Thenable => - new Promise(resolve => { - const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspInlayHints, { - uri: params.textDocument.uri, - range: params.range, - }) - socket.eventEmitter.once(job.id, makeDefaultResponseHandler(resolve)) - }) - /** * @function */ diff --git a/server/src/handlers/navigation.ts b/server/src/handlers/navigation.ts new file mode 100644 index 00000000..105e855a --- /dev/null +++ b/server/src/handlers/navigation.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as jobs from '../engine/jobs' +import * as socket from '../engine/socket' + +import { sendNotification } from '../server' +import { makePositionalHandler } from './util' +import { USER_MESSAGE } from '../util/userMessages' +import { StatusCode } from '../util/statusCodes' + +/** + * @function + */ +export const handleGotoDefinition = makePositionalHandler(jobs.Request.lspGoto, makeGotoDefinitionResponseHandler) + +function makeGotoDefinitionResponseHandler(promiseResolver: (result?: socket.FlixResult) => void) { + return function responseHandler({ status, result }: socket.FlixResponse) { + const targetUri = result?.targetUri + if (status === StatusCode.Success) { + if (targetUri?.startsWith('file://')) { + return promiseResolver(result) + } else { + sendNotification(jobs.Request.internalMessage, USER_MESSAGE.FILE_NOT_AVAILABLE(targetUri!)) + } + } + promiseResolver() + } +} + +/** + * @function + */ +export const handleImplementation = makePositionalHandler(jobs.Request.lspImplementation) + +/** + * @function + */ +export const handleHighlight = makePositionalHandler(jobs.Request.lspHighlight) + +/** + * @function + */ +export const handleReferences = makePositionalHandler(jobs.Request.lspUses) diff --git a/server/src/handlers/resources.ts b/server/src/handlers/resources.ts new file mode 100644 index 00000000..78cd567b --- /dev/null +++ b/server/src/handlers/resources.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TextDocumentChangeEvent } from 'vscode-languageserver' +import { TextDocument } from 'vscode-languageserver-textdocument' + +import * as jobs from '../engine/jobs' +import * as engine from '../engine' + +import { sendNotification } from '../server' +import { makeEnqueuePromise } from './util' + +interface UriInput { + uri: string +} + +export function handleAddUri({ uri }: UriInput) { + engine.addUri(uri) +} + +export function handleRemUri({ uri }: UriInput) { + engine.remUri(uri) +} + +export function handleAddPkg({ uri }: UriInput) { + engine.addPkg(uri) +} + +export function handleRemPkg({ uri }: UriInput) { + engine.remPkg(uri) +} + +export function handleAddJar({ uri }: UriInput) { + engine.addJar(uri) +} + +export function handleRemJar({ uri }: UriInput) { + engine.remJar(uri) +} + +export function handleReplaceConfiguration(userConfiguration: engine.UserConfiguration) { + engine.updateUserConfiguration(userConfiguration) +} + +export function handleChangeContent(params: TextDocumentChangeEvent) { + const document = params.document + engine.updateUri(document.uri, document.getText()) +} + +export const handleShowAst = makeEnqueuePromise(makeShowAstJob, makeShowAstResponseHandler) + +function makeShowAstJob(params: any) { + return { + request: jobs.Request.lspShowAst, + uri: params.uri, + phase: params.phase, + } +} + +function makeShowAstResponseHandler(promiseResolver: () => void) { + return function responseHandler({ status, result }: any) { + sendNotification(jobs.Request.lspShowAst, { status, result }) + promiseResolver() + } +} diff --git a/server/src/handlers/semantic.ts b/server/src/handlers/semantic.ts new file mode 100644 index 00000000..1ab96a30 --- /dev/null +++ b/server/src/handlers/semantic.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InlayHintParams } from 'vscode-languageserver' + +import * as jobs from '../engine/jobs' +import * as engine from '../engine' +import * as socket from '../engine/socket' + +import { makePositionalHandler, makeDefaultResponseHandler } from './util' + +/** + * @function + */ +export const handleSemanticTokens = makePositionalHandler(jobs.Request.lspSemanticTokens) + +export const handleInlayHints = (params: InlayHintParams): Thenable => + new Promise(resolve => { + const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspInlayHints, { + uri: params.textDocument.uri, + range: params.range, + }) + socket.eventEmitter.once(job.id, makeDefaultResponseHandler(resolve)) + }) diff --git a/server/src/handlers/symbols.ts b/server/src/handlers/symbols.ts new file mode 100644 index 00000000..9b32f732 --- /dev/null +++ b/server/src/handlers/symbols.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Thomas Plougsgaard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as jobs from '../engine/jobs' +import * as engine from '../engine' +import * as socket from '../engine/socket' + +import { makePositionalHandler, makeEnqueuePromise, makeDefaultResponseHandler } from './util' + +/** + * @function + */ +export const handleDocumentSymbols = makePositionalHandler(jobs.Request.lspDocumentSymbols) + +/** + * @function + */ +export const handleCodelens = makePositionalHandler(jobs.Request.lspCodelens) + +export function handleCodeAction(params: any): Promise { + const uri = params.textDocument ? params.textDocument.uri : undefined + const range = params.range + const context = params.context + + return new Promise(function (resolve) { + const job = engine.enqueueJobWithFlattenedParams(jobs.Request.lspCodeAction, { uri, range, context }) + socket.eventEmitter.once(job.id, ({ status, result }) => resolve(result)) + }) +} + +/** + * @function + */ +export const handleWorkspaceSymbols = makeEnqueuePromise(makeWorkspaceSymbolsJob, makeDefaultResponseHandler) as ( + params: any, +) => Promise + +function makeWorkspaceSymbolsJob(params: any) { + return { + request: jobs.Request.lspWorkspaceSymbols, + position: params.position, + query: params.query || '', + } +}