Skip to content

Commit 5d8c8b3

Browse files
committed
feat: Normalize document URIs for LSP clients, converting content:// to file:// for consistent handling.
1 parent a6806c3 commit 5d8c8b3

File tree

1 file changed

+61
-11
lines changed

1 file changed

+61
-11
lines changed

src/cm/lsp/clientManager.ts

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,28 @@ export class LspClientManager {
144144
}
145145

146146
async getExtensionsForFile(metadata: FileMetadata): Promise<Extension[]> {
147-
const { uri, languageId, languageName, view, file, rootUri } = metadata;
147+
const {
148+
uri: originalUri,
149+
languageId,
150+
languageName,
151+
view,
152+
file,
153+
rootUri,
154+
} = metadata;
148155

149156
const effectiveLang = safeString(languageId ?? languageName).toLowerCase();
150157
if (!effectiveLang) return [];
151158

152159
const servers = serverRegistry.getServersForLanguage(effectiveLang);
153160
if (!servers.length) return [];
154161

162+
// Normalize the document URI for LSP (convert content:// to file://)
163+
const normalizedUri = normalizeDocumentUri(originalUri);
164+
if (!normalizedUri) {
165+
console.warn(`Cannot normalize document URI for LSP: ${originalUri}`);
166+
return [];
167+
}
168+
155169
const lspExtensions: Extension[] = [];
156170
const diagnosticsUiExtension = this.options.diagnosticsUiExtension;
157171

@@ -162,28 +176,31 @@ export class LspClientManager {
162176
const resolved = server.resolveLanguageId({
163177
languageId: effectiveLang,
164178
languageName,
165-
uri,
179+
uri: normalizedUri,
166180
file,
167181
});
168182
if (resolved) targetLanguageId = safeString(resolved);
169183
} catch (error) {
170184
console.warn(
171-
`LSP server ${server.id} failed to resolve language id for ${uri}`,
185+
`LSP server ${server.id} failed to resolve language id for ${normalizedUri}`,
172186
error,
173187
);
174188
}
175189
}
176190

177191
try {
178192
const clientState = await this.#ensureClient(server, {
179-
uri,
193+
uri: normalizedUri,
180194
file,
181195
view,
182196
languageId: targetLanguageId,
183197
rootUri,
184198
});
185-
const plugin = clientState.client.plugin(uri, targetLanguageId);
186-
clientState.attach(uri, view as EditorView);
199+
const plugin = clientState.client.plugin(
200+
normalizedUri,
201+
targetLanguageId,
202+
);
203+
clientState.attach(normalizedUri, view as EditorView);
187204
lspExtensions.push(plugin);
188205
} catch (error) {
189206
const lspError = error as LSPError;
@@ -211,16 +228,25 @@ export class LspClientManager {
211228
metadata: FileMetadata,
212229
options: FormattingOptions = {},
213230
): Promise<boolean> {
214-
const { uri, languageId, languageName, view, file } = metadata;
231+
const { uri: originalUri, languageId, languageName, view, file } = metadata;
215232
const effectiveLang = safeString(languageId ?? languageName).toLowerCase();
216233
if (!effectiveLang || !view) return false;
234+
235+
const normalizedUri = normalizeDocumentUri(originalUri);
236+
if (!normalizedUri) {
237+
console.warn(
238+
`Cannot normalize document URI for formatting: ${originalUri}`,
239+
);
240+
return false;
241+
}
242+
217243
const servers = serverRegistry.getServersForLanguage(effectiveLang);
218244
if (!servers.length) return false;
219245

220246
for (const server of servers) {
221247
try {
222248
const context: RootUriContext = {
223-
uri,
249+
uri: normalizedUri,
224250
languageId: effectiveLang,
225251
view,
226252
file,
@@ -229,15 +255,15 @@ export class LspClientManager {
229255
const state = await this.#ensureClient(server, context);
230256
const capabilities = state.client.serverCapabilities;
231257
if (!capabilities?.documentFormattingProvider) continue;
232-
state.attach(uri, view);
258+
state.attach(normalizedUri, view);
233259
const plugin = LSPPlugin.get(view);
234260
if (!plugin) continue;
235261
plugin.client.sync();
236262
const edits = await state.client.request<
237263
{ textDocument: { uri: string }; options: FormattingOptions },
238264
TextEdit[] | null
239265
>("textDocument/formatting", {
240-
textDocument: { uri },
266+
textDocument: { uri: normalizedUri },
241267
options: buildFormattingOptions(view, options),
242268
});
243269
if (!edits || !edits.length) {
@@ -900,6 +926,30 @@ function normalizeRootUriForServer(
900926
return { normalizedRootUri: rootUri, originalRootUri: rootUri };
901927
}
902928

929+
function normalizeDocumentUri(uri: string | null | undefined): string | null {
930+
if (!uri || typeof uri !== "string") return null;
931+
932+
const schemeMatch = /^([a-zA-Z][\w+\-.]*):/.exec(uri);
933+
const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;
934+
935+
// Already a file:// URI or untitled use as-is
936+
if (scheme === "file" || scheme === "untitled") {
937+
return uri;
938+
}
939+
940+
// Convert content:// URIs to file:// URIs
941+
if (scheme === "content") {
942+
const fileUri = contentUriToFileUri(uri);
943+
if (fileUri) {
944+
return fileUri;
945+
}
946+
return null;
947+
}
948+
949+
// Unknown scheme
950+
return uri;
951+
}
952+
903953
function contentUriToFileUri(uri: string): string | null {
904954
try {
905955
const parsed = Uri.parse(uri) as ParsedUri | null;
@@ -912,7 +962,7 @@ function contentUriToFileUri(uri: string): string | null {
912962
}
913963

914964
const providerMatch =
915-
/^content:\/\/com\.((?![:<>"/\\|?*]).*)\\.documents\//.exec(
965+
/^content:\/\/com\.((?![:<>"/\\|?*]).*?)\.documents\//.exec(
916966
rootUri ?? "",
917967
);
918968
const providerId = providerMatch ? providerMatch[1] : null;

0 commit comments

Comments
 (0)