fix invalid uri

Closes #237
This commit is contained in:
Qiming Zhao 2020-12-09 14:04:50 +08:00
parent c8c5d7eba5
commit cca85a2724
4 changed files with 82 additions and 46 deletions

View file

@ -135,7 +135,7 @@ class PendingDiagnostics extends ResourceMap<number> {
public getOrderedFileSet(): ResourceMap<void> { public getOrderedFileSet(): ResourceMap<void> {
const orderedResources = Array.from(this.entries) const orderedResources = Array.from(this.entries)
.sort((a, b) => a.value - b.value) .sort((a, b) => a.value - b.value)
.map(entry => entry.resource) .map(entry => entry.uri)
const map = new ResourceMap<void>(this._normalizePath) const map = new ResourceMap<void>(this._normalizePath)
for (const resource of orderedResources) { for (const resource of orderedResources) {
@ -270,24 +270,25 @@ class GetErrRequest {
public static executeGetErrRequest( public static executeGetErrRequest(
client: ITypeScriptServiceClient, client: ITypeScriptServiceClient,
files: string[], uris: Uri[],
onDone: () => void onDone: () => void
): GetErrRequest { ): GetErrRequest {
const token = new CancellationTokenSource() const token = new CancellationTokenSource()
return new GetErrRequest(client, files, token, onDone) return new GetErrRequest(client, uris, token, onDone)
} }
private _done = false private _done = false
private constructor( private constructor(
client: ITypeScriptServiceClient, client: ITypeScriptServiceClient,
public readonly files: string[], public readonly uris: Uri[],
private readonly _token: CancellationTokenSource, private readonly _token: CancellationTokenSource,
onDone: () => void onDone: () => void
) { ) {
let files = uris.map(uri => client.normalizePath(uri))
const args: Proto.GeterrRequestArgs = { const args: Proto.GeterrRequestArgs = {
delay: 0, delay: 0,
files: this.files files
} }
const done = () => { const done = () => {
if (this._done) { if (this._done) {
@ -522,8 +523,8 @@ export default class BufferSyncSupport {
const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet() const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet()
if (this.pendingGetErr) { if (this.pendingGetErr) {
this.pendingGetErr.cancel() this.pendingGetErr.cancel()
for (const file of this.pendingGetErr.files) { for (const uri of this.pendingGetErr.uris) {
let resource = Uri.file(file).toString() let resource = uri.toString()
if (this.syncedBuffers.get(resource)) { if (this.syncedBuffers.get(resource)) {
orderedFileSet.set(resource, undefined) orderedFileSet.set(resource, undefined)
} }
@ -535,8 +536,8 @@ export default class BufferSyncSupport {
orderedFileSet.set(buffer.resource, undefined) orderedFileSet.set(buffer.resource, undefined)
} }
if (orderedFileSet.size) { if (orderedFileSet.size) {
let files = Array.from(orderedFileSet.keys).map(uri => this.client.normalizePath(Uri.parse(uri))) let uris = Array.from(orderedFileSet.uris).map(uri => Uri.parse(uri))
const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, files, () => { const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, uris, () => {
if (this.pendingGetErr === getErr) { if (this.pendingGetErr === getErr) {
this.pendingGetErr = undefined this.pendingGetErr = undefined
} }

View file

@ -2,70 +2,79 @@
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information. * 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 * Attempts to handle correct mapping on both case sensitive and case in-sensitive
* file systems. * file systems.
*/ */
export class ResourceMap<T> { export class ResourceMap<T> {
private readonly _map = new Map<string, T>() private readonly _map = new Map<string, { uri: string, value: T }>()
constructor( constructor(
protected readonly _normalizePath?: (resource: string) => string | null protected readonly _normalizePath: (uri: string) => string | null = defaultPathNormalizer
) { } ) { }
public get size() { public get size() {
return this._map.size return this._map.size
} }
public get entries(): { resource: string, value: T }[] { public get entries(): Iterable<{ uri: string, value: T }> {
return Array.from(this._map.keys()).map(key => { return this._map.values()
return { resource: key, value: this._map[key] }
})
} }
public has(resource: string): boolean { public has(uri: string): boolean {
const file = this.toKey(resource) const file = this.toKey(uri)
return !!file && this._map.has(file) return !!file && this._map.has(file)
} }
public get(resource: string): T | undefined { public get(uri: string): T | undefined {
const file = this.toKey(resource) const file = this.toKey(uri)
return file ? this._map.get(file) : undefined if (!file) return undefined
let entry = this._map.get(file)
return entry ? entry.value : undefined
} }
public set(resource: string, value: T): void { public set(uri: string, value: T): void {
const file = this.toKey(resource) const file = this.toKey(uri)
if (file) { if (file) {
this._map.set(file, value) this._map.set(file, { uri, value })
} }
} }
public delete(resource: string): void { public delete(uri: string): void {
const file = this.toKey(resource) const file = this.toKey(uri)
if (file) { if (file) {
this._map.delete(file) this._map.delete(file)
} }
} }
public get values(): Iterable<T> { public get values(): Iterable<T> {
return this._map.values() return Array.from(this._map.values()).map(x => x.value)
} }
public get keys(): Iterable<string> { public get uris(): Iterable<string> {
return this._map.keys() return Array.from(this._map.values()).map(x => x.uri)
} }
public clear(): void { public clear(): void {
this._map.clear() this._map.clear()
} }
private toKey(resource: string): string | null { private toKey(uri: string): string | null {
const key = this._normalizePath const key = this._normalizePath
? this._normalizePath(resource) ? this._normalizePath(uri)
: resource : uri
if (!key) { if (!key) {
return key return key
} }

View file

@ -8,6 +8,7 @@ import fs from 'fs'
import os from 'os' import os from 'os'
import path from 'path' import path from 'path'
import { CancellationToken, CancellationTokenSource, Disposable, Emitter, Event } from 'vscode-languageserver-protocol' import { CancellationToken, CancellationTokenSource, Disposable, Emitter, Event } from 'vscode-languageserver-protocol'
import * as fileSchemes from '../utils/fileSchemess'
import { PluginManager } from '../utils/plugins' import { PluginManager } from '../utils/plugins'
import { CallbackMap } from './callbackMap' import { CallbackMap } from './callbackMap'
import BufferSyncSupport from './features/bufferSyncSupport' import BufferSyncSupport from './features/bufferSyncSupport'
@ -506,22 +507,22 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
return Uri.file(filepath).toString() return Uri.file(filepath).toString()
} }
public normalizePath(resource: Uri): string | null { public normalizePath(resource: Uri): string | undefined {
if (this._apiVersion.gte(API.v213)) { if (fileSchemes.disabledSchemes.has(resource.scheme)) {
if (resource.scheme == 'untitled') { return undefined
const dirName = path.dirname(resource.path) }
const fileName = this.inMemoryResourcePrefix + path.basename(resource.path) switch (resource.scheme) {
return resource case fileSchemes.file: {
.with({ path: path.posix.join(dirName, fileName) }) let result = resource.fsPath
.toString(true) 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 { private get inMemoryResourcePrefix(): string {

25
src/utils/fileSchemess.ts Normal file
View file

@ -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
])