From cca85a272453d366f07507bd3274bdbd598a2862 Mon Sep 17 00:00:00 2001 From: Qiming Zhao Date: Wed, 9 Dec 2020 14:04:50 +0800 Subject: [PATCH] fix invalid uri Closes #237 --- src/server/features/bufferSyncSupport.ts | 19 ++++---- src/server/features/resourceMap.ts | 55 ++++++++++++++---------- src/server/typescriptServiceClient.ts | 29 +++++++------ src/utils/fileSchemess.ts | 25 +++++++++++ 4 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 src/utils/fileSchemess.ts diff --git a/src/server/features/bufferSyncSupport.ts b/src/server/features/bufferSyncSupport.ts index 75528c1..fdfa8ae 100644 --- a/src/server/features/bufferSyncSupport.ts +++ b/src/server/features/bufferSyncSupport.ts @@ -135,7 +135,7 @@ class PendingDiagnostics extends ResourceMap { public getOrderedFileSet(): ResourceMap { const orderedResources = Array.from(this.entries) .sort((a, b) => a.value - b.value) - .map(entry => entry.resource) + .map(entry => entry.uri) const map = new ResourceMap(this._normalizePath) for (const resource of orderedResources) { @@ -270,24 +270,25 @@ class GetErrRequest { public static executeGetErrRequest( client: ITypeScriptServiceClient, - files: string[], + uris: Uri[], onDone: () => void ): GetErrRequest { const token = new CancellationTokenSource() - return new GetErrRequest(client, files, token, onDone) + return new GetErrRequest(client, uris, token, onDone) } private _done = false private constructor( client: ITypeScriptServiceClient, - public readonly files: string[], + public readonly uris: Uri[], private readonly _token: CancellationTokenSource, onDone: () => void ) { + let files = uris.map(uri => client.normalizePath(uri)) const args: Proto.GeterrRequestArgs = { delay: 0, - files: this.files + files } const done = () => { if (this._done) { @@ -522,8 +523,8 @@ export default class BufferSyncSupport { const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet() if (this.pendingGetErr) { this.pendingGetErr.cancel() - for (const file of this.pendingGetErr.files) { - let resource = Uri.file(file).toString() + for (const uri of this.pendingGetErr.uris) { + let resource = uri.toString() if (this.syncedBuffers.get(resource)) { orderedFileSet.set(resource, undefined) } @@ -535,8 +536,8 @@ export default class BufferSyncSupport { orderedFileSet.set(buffer.resource, undefined) } if (orderedFileSet.size) { - let files = Array.from(orderedFileSet.keys).map(uri => this.client.normalizePath(Uri.parse(uri))) - const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, files, () => { + let uris = Array.from(orderedFileSet.uris).map(uri => Uri.parse(uri)) + const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, uris, () => { if (this.pendingGetErr === getErr) { this.pendingGetErr = undefined } diff --git a/src/server/features/resourceMap.ts b/src/server/features/resourceMap.ts index 565201f..a7f5c0e 100644 --- a/src/server/features/resourceMap.ts +++ b/src/server/features/resourceMap.ts @@ -2,70 +2,79 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Uri } from 'coc.nvim' + +function defaultPathNormalizer(resource: string): string { + let u = Uri.parse(resource) + if (u.scheme === 'file') { + return u.fsPath + } + return resource.toString() +} /** - * Maps of file resources + * Maps of file uris * * Attempts to handle correct mapping on both case sensitive and case in-sensitive * file systems. */ export class ResourceMap { - private readonly _map = new Map() + private readonly _map = new Map() constructor( - protected readonly _normalizePath?: (resource: string) => string | null + protected readonly _normalizePath: (uri: string) => string | null = defaultPathNormalizer ) { } public get size() { return this._map.size } - public get entries(): { resource: string, value: T }[] { - return Array.from(this._map.keys()).map(key => { - return { resource: key, value: this._map[key] } - }) + public get entries(): Iterable<{ uri: string, value: T }> { + return this._map.values() } - public has(resource: string): boolean { - const file = this.toKey(resource) + public has(uri: string): boolean { + const file = this.toKey(uri) return !!file && this._map.has(file) } - public get(resource: string): T | undefined { - const file = this.toKey(resource) - return file ? this._map.get(file) : undefined + public get(uri: string): T | undefined { + const file = this.toKey(uri) + if (!file) return undefined + let entry = this._map.get(file) + return entry ? entry.value : undefined } - public set(resource: string, value: T): void { - const file = this.toKey(resource) + public set(uri: string, value: T): void { + const file = this.toKey(uri) if (file) { - this._map.set(file, value) + this._map.set(file, { uri, value }) } } - public delete(resource: string): void { - const file = this.toKey(resource) + public delete(uri: string): void { + const file = this.toKey(uri) if (file) { this._map.delete(file) } } public get values(): Iterable { - return this._map.values() + return Array.from(this._map.values()).map(x => x.value) } - public get keys(): Iterable { - return this._map.keys() + public get uris(): Iterable { + return Array.from(this._map.values()).map(x => x.uri) } public clear(): void { this._map.clear() } - private toKey(resource: string): string | null { + private toKey(uri: string): string | null { const key = this._normalizePath - ? this._normalizePath(resource) - : resource + ? this._normalizePath(uri) + : uri if (!key) { return key } diff --git a/src/server/typescriptServiceClient.ts b/src/server/typescriptServiceClient.ts index 5031549..0b901bc 100644 --- a/src/server/typescriptServiceClient.ts +++ b/src/server/typescriptServiceClient.ts @@ -8,6 +8,7 @@ import fs from 'fs' import os from 'os' import path from 'path' import { CancellationToken, CancellationTokenSource, Disposable, Emitter, Event } from 'vscode-languageserver-protocol' +import * as fileSchemes from '../utils/fileSchemess' import { PluginManager } from '../utils/plugins' import { CallbackMap } from './callbackMap' import BufferSyncSupport from './features/bufferSyncSupport' @@ -506,22 +507,22 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return Uri.file(filepath).toString() } - public normalizePath(resource: Uri): string | null { - if (this._apiVersion.gte(API.v213)) { - if (resource.scheme == 'untitled') { - const dirName = path.dirname(resource.path) - const fileName = this.inMemoryResourcePrefix + path.basename(resource.path) - return resource - .with({ path: path.posix.join(dirName, fileName) }) - .toString(true) + public normalizePath(resource: Uri): string | undefined { + if (fileSchemes.disabledSchemes.has(resource.scheme)) { + return undefined + } + switch (resource.scheme) { + case fileSchemes.file: { + let result = resource.fsPath + if (!result) return undefined + result = path.normalize(result) + // Both \ and / must be escaped in regular expressions + return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/') + } + default: { + return this.inMemoryResourcePrefix + resource.toString(true) } } - - const result = resource.fsPath - if (!result) return null - - // Both \ and / must be escaped in regular expressions - return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/') } private get inMemoryResourcePrefix(): string { diff --git a/src/utils/fileSchemess.ts b/src/utils/fileSchemess.ts new file mode 100644 index 0000000..ef1510d --- /dev/null +++ b/src/utils/fileSchemess.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const file = 'file' +export const untitled = 'untitled' +export const git = 'git' +/** Live share scheme */ +export const vsls = 'vsls' +export const walkThroughSnippet = 'walkThroughSnippet' + +export const semanticSupportedSchemes = [ + file, + untitled, + walkThroughSnippet, +] + +/** + * File scheme for which JS/TS language feature should be disabled + */ +export const disabledSchemes = new Set([ + git, + vsls +])