diff --git a/src/document.ts b/src/document.ts index f941f258..c1b96046 100644 --- a/src/document.ts +++ b/src/document.ts @@ -5,10 +5,12 @@ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */ +import { extname } from 'node:path'; import { URI } from 'vscode-uri'; import * as lsp from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; import * as languageModeIds from './configuration/languageIds.js'; +import { LspClient } from './lsp-client.js'; import { CommandTypes, type ts } from './ts-protocol.js'; import { ClientCapability, type ITypeScriptServiceClient } from './typescriptService.js'; import API from './utils/api.js'; @@ -26,6 +28,24 @@ function mode2ScriptKind(mode: string): ts.server.protocol.ScriptKindName | unde return undefined; } +/** + * @deprecated Remove in next major. + */ +function getModeFromFileUri(uri: string): string | undefined { + const extension = extname(uri).toUpperCase(); + switch (extension) { + case '.TS': + return languageModeIds.typescript; + case '.TSX': + return languageModeIds.typescriptreact; + case '.JS': + return languageModeIds.javascript; + case '.JSX': + return languageModeIds.javascriptreact; + } + return undefined; +} + class PendingDiagnostics extends ResourceMap { public getOrderedFileSet(): ResourceMap { const orderedResources = Array.from(this.entries()) @@ -203,6 +223,7 @@ export class LspDocument { export class LspDocuments { private readonly client: ITypeScriptServiceClient; + private readonly lspClient: LspClient; private _validateJavaScript = true; private _validateTypeScript = true; @@ -216,9 +237,11 @@ export class LspDocuments { constructor( client: ITypeScriptServiceClient, + lspClient: LspClient, onCaseInsensitiveFileSystem: boolean, ) { this.client = client; + this.lspClient = lspClient; this.modeIds = new Set(languageModeIds.jsTsLanguageModes); const pathNormalizer = (path: URI) => this.client.toTsFilePath(path.toString()); @@ -251,7 +274,16 @@ export class LspDocuments { public openTextDocument(textDocument: lsp.TextDocumentItem): boolean { if (!this.modeIds.has(textDocument.languageId)) { - return false; + const detectedLanguageId = getModeFromFileUri(textDocument.uri); + if (detectedLanguageId) { + this.lspClient.logMessage({ + type: lsp.MessageType.Warning, + message: `Invalid langaugeId "${textDocument.languageId}" provided for uri "${textDocument.uri}". Correcting to "${detectedLanguageId}"`, + }); + textDocument.languageId = detectedLanguageId; + } else { + return false; + } } const resource = textDocument.uri; const filepath = this.client.toTsFilePath(resource); diff --git a/src/lsp-server.spec.ts b/src/lsp-server.spec.ts index d72948c1..f4627fd8 100644 --- a/src/lsp-server.spec.ts +++ b/src/lsp-server.spec.ts @@ -2423,3 +2423,24 @@ describe('linked editing', () => { ]); }); }); + +describe('handles invalid languageId', () => { + it('simple test', async () => { + const textDocument = { + uri: uri('foo.tsx'), + languageId: 'tsx', + version: 1, + text: 'let bar =
', + }; + await openDocumentAndWaitForDiagnostics(server, textDocument); + const position = positionAfter(textDocument, '