Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve unknown file type handling in language server #1455

Merged
merged 3 commits into from Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/langium/src/lsp/document-update-handler.ts
Expand Up @@ -12,6 +12,7 @@ import type { TextDocument } from '../workspace/documents.js';
import type { WorkspaceLock } from '../workspace/workspace-lock.js';
import type { LangiumSharedServices } from './lsp-services.js';
import type { WorkspaceManager } from '../workspace/workspace-manager.js';
import type { ServiceRegistry } from '../service-registry.js';

/**
* Shared service for handling text document changes and watching relevant files.
Expand All @@ -35,11 +36,13 @@ export class DefaultDocumentUpdateHandler implements DocumentUpdateHandler {
protected readonly workspaceManager: WorkspaceManager;
protected readonly documentBuilder: DocumentBuilder;
protected readonly workspaceLock: WorkspaceLock;
protected readonly serviceRegistry: ServiceRegistry;

constructor(services: LangiumSharedServices) {
this.workspaceManager = services.workspace.WorkspaceManager;
this.documentBuilder = services.workspace.DocumentBuilder;
this.workspaceLock = services.workspace.WorkspaceLock;
this.serviceRegistry = services.ServiceRegistry;

let canRegisterFileWatcher = false;
services.lsp.LanguageServer.onInitialize(params => {
Expand Down Expand Up @@ -73,6 +76,9 @@ export class DefaultDocumentUpdateHandler implements DocumentUpdateHandler {
}

protected fireDocumentUpdate(changed: URI[], deleted: URI[]): void {
// Filter out URIs that do not have a service in the registry
// Running the document builder update will fail for those URIs
changed = changed.filter(uri => this.hasService(uri));
// Only fire the document update when the workspace manager is ready
// Otherwise, we might miss the initial indexing of the workspace
this.workspaceManager.ready.then(() => {
Expand All @@ -83,6 +89,23 @@ export class DefaultDocumentUpdateHandler implements DocumentUpdateHandler {
});
}

/**
* Check whether the service registry contains a service instance for the given URI.
*
* Some language clients (vscode) decide to send update notifications for files that have been renamed to a different file extension.
* In this case, the service registry may not contain a service for the new URI. We have to ignore any changes to those files.
*
* In case we only have a single language in our registry, we can safely use that language for all URIs.
*/
protected hasService(uri: URI): boolean {
try {
this.serviceRegistry.getServices(uri);
return true;
} catch {
return false;
}
msujew marked this conversation as resolved.
Show resolved Hide resolved
}

didChangeContent(change: TextDocumentChangeEvent<TextDocument>): void {
this.fireDocumentUpdate([URI.parse(change.document.uri)], []);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/langium/src/lsp/index.ts
Expand Up @@ -15,7 +15,9 @@ export * from './default-lsp-module.js';
export * from './document-highlight-provider.js';
export * from './document-link-provider.js';
export * from './document-symbol-provider.js';
export * from './document-update-handler.js';
export * from './execute-command-handler.js';
export * from './file-operation-handler.js';
export * from './folding-range-provider.js';
export * from './formatter.js';
export * from './fuzzy-matcher.js';
Expand Down
24 changes: 17 additions & 7 deletions packages/langium/src/lsp/language-server.ts
Expand Up @@ -643,8 +643,8 @@ export function createHierarchyRequestHandler<P extends TypeHierarchySupertypesP
const language = serviceRegistry.getServices(uri);
if (!language) {
const message = `Could not find service instance for uri: '${uri.toString()}'`;
console.error(message);
throw new Error(message);
console.debug(message);
return responseError<E>(new Error(message));
}
try {
return await serviceCall(language, params, cancelToken);
Expand All @@ -667,11 +667,16 @@ export function createServerRequestHandler<P extends { textDocument: TextDocumen
if (cancellationError) {
return cancellationError;
}
const language = serviceRegistry.getServices(uri);
let language: LangiumCoreAndPartialLSPServices | undefined;
try {
language = serviceRegistry.getServices(uri);
} catch {
// NOOP
}
if (!language) {
const errorText = `Could not find service instance for uri: '${uri}'`;
console.error(errorText);
throw new Error(errorText);
console.debug(errorText);
return responseError<E>(new Error(errorText));
}
const document = await documents.getOrCreateDocument(uri);
try {
Expand All @@ -695,9 +700,14 @@ export function createRequestHandler<P extends { textDocument: TextDocumentIdent
if (cancellationError) {
return cancellationError;
}
const language = serviceRegistry.getServices(uri);
let language: LangiumCoreAndPartialLSPServices | undefined;
try {
language = serviceRegistry.getServices(uri);
} catch {
// NOOP
}
if (!language) {
console.error(`Could not find service instance for uri: '${uri.toString()}'`);
console.debug(`Could not find service instance for uri: '${uri.toString()}'`);
return null;
}
const document = documents.getDocument(uri);
Expand Down