Use case-insensitive synced buffer map on case-insensitive file systems

Fixes #51183
Fixes #50505
This commit is contained in:
Matt Bierner 2018-06-05 15:10:44 -07:00
parent 488030350d
commit c40a2fd096
4 changed files with 87 additions and 23 deletions

View file

@ -11,6 +11,8 @@ import { Delayer } from '../utils/async';
import { disposeAll } from '../utils/dispose'; import { disposeAll } from '../utils/dispose';
import * as languageModeIds from '../utils/languageModeIds'; import * as languageModeIds from '../utils/languageModeIds';
import API from '../utils/api'; import API from '../utils/api';
import { memoize } from '../utils/memoize';
import { getTempFile } from '../utils/temp';
enum BufferKind { enum BufferKind {
TypeScript = 1, TypeScript = 1,
@ -69,6 +71,10 @@ class SyncedBuffer {
this.client.execute('open', args, false); this.client.execute('open', args, false);
} }
public get resource(): Uri {
return this.document.uri;
}
public get lineCount(): number { public get lineCount(): number {
return this.document.lineCount; return this.document.lineCount;
} }
@ -126,6 +132,10 @@ class SyncedBufferMap {
return file ? this._map.get(file) : undefined; return file ? this._map.get(file) : undefined;
} }
public getForPath(filePath: string): SyncedBuffer | undefined {
return this.get(Uri.file(filePath));
}
public set(resource: Uri, buffer: SyncedBuffer) { public set(resource: Uri, buffer: SyncedBuffer) {
const file = this.toKey(resource); const file = this.toKey(resource);
if (file) { if (file) {
@ -149,7 +159,8 @@ class SyncedBufferMap {
} }
private toKey(resource: Uri): string | null { private toKey(resource: Uri): string | null {
return this._normalizePath(resource); const key = this._normalizePath(resource);
return key ? key.toLowerCase() : key;
} }
} }
@ -178,7 +189,7 @@ export default class BufferSyncSupport {
this.diagnosticDelayer = new Delayer<any>(300); this.diagnosticDelayer = new Delayer<any>(300);
this.syncedBuffers = new SyncedBufferMap(path => this.client.normalizedPath(path)); this.syncedBuffers = new SyncedBufferMap(path => this.normalizePath(path));
this.updateConfiguration(); this.updateConfiguration();
workspace.onDidChangeConfiguration(() => this.updateConfiguration(), null); workspace.onDidChangeConfiguration(() => this.updateConfiguration(), null);
@ -202,6 +213,14 @@ export default class BufferSyncSupport {
return this.syncedBuffers.has(resource); return this.syncedBuffers.has(resource);
} }
public toResource(filePath: string): Uri {
const buffer = this.syncedBuffers.getForPath(filePath);
if (buffer) {
return buffer.resource;
}
return Uri.file(filePath);
}
public reOpenDocuments(): void { public reOpenDocuments(): void {
for (const buffer of this.syncedBuffers.allBuffers) { for (const buffer of this.syncedBuffers.allBuffers) {
buffer.open(); buffer.open();
@ -376,4 +395,40 @@ export default class BufferSyncSupport {
return this._validateTypeScript; return this._validateTypeScript;
} }
} }
}
private normalizePath(path: Uri): string | null {
const key = this.client.normalizedPath(path);
if (!key) {
return key;
}
return this.isCaseInsensitivePath(key) ? key.toLowerCase() : key;
}
private isCaseInsensitivePath(path: string) {
if (isWindowsPath(path)) {
return true;
}
return path[0] === '/' && this.onIsCaseInsenitiveFileSystem;
}
@memoize
private get onIsCaseInsenitiveFileSystem() {
if (process.platform === 'win32') {
return true;
}
if (process.platform !== 'darwin') {
return false;
}
const temp = getTempFile('typescript-case-check');
fs.writeFileSync(temp, '');
return fs.existsSync(temp.toUpperCase());
}
}
function isWindowsPath(path: string): boolean {
return /^[a-zA-Z]:\\/.test(path);
}

View file

@ -663,9 +663,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
return resource; return resource;
} }
} }
const resource = Uri.file(filepath); return this.bufferSyncSupport.toResource(filepath);
return resource;
} }
public getWorkspaceRootForResource(resource: Uri): string | undefined { public getWorkspaceRootForResource(resource: Uri): string | undefined {
@ -976,7 +974,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
} }
if (this.apiVersion.gte(API.v222)) { if (this.apiVersion.gte(API.v222)) {
this.cancellationPipeName = electron.getTempFile(`tscancellation-${electron.makeRandomHexString(20)}`); this.cancellationPipeName = electron.getTempSock('tscancellation');
args.push('--cancellationPipeName', this.cancellationPipeName + '*'); args.push('--cancellationPipeName', this.cancellationPipeName + '*');
} }

View file

@ -3,25 +3,21 @@
* 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 Logger from './logger';
import { getTempFile, makeRandomHexString } from './temp';
import path = require('path'); import path = require('path');
import os = require('os'); import os = require('os');
import net = require('net'); import net = require('net');
import cp = require('child_process'); import cp = require('child_process');
import Logger from './logger';
export interface IForkOptions { export interface IForkOptions {
cwd?: string; cwd?: string;
execArgv?: string[]; execArgv?: string[];
} }
export function makeRandomHexString(length: number): string { export function getTempSock(prefix: string): string {
let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; const fullName = `vscode-${prefix}-${makeRandomHexString(20)}`;
let result = ''; return getTempFile(fullName + '.sock');
for (let i = 0; i < length; i++) {
const idx = Math.floor(chars.length * Math.random());
result += chars[idx];
}
return result;
} }
function generatePipeName(): string { function generatePipeName(): string {
@ -38,12 +34,6 @@ function getPipeName(name: string): string {
return path.join(os.tmpdir(), fullName + '.sock'); return path.join(os.tmpdir(), fullName + '.sock');
} }
export function getTempFile(name: string): string {
const fullName = 'vscode-' + name;
return path.join(os.tmpdir(), fullName + '.sock');
}
function generatePatchedEnv( function generatePatchedEnv(
env: any, env: any,
stdInPipeName: string, stdInPipeName: string,
@ -131,7 +121,7 @@ export function fork(
}; };
// Create the process // Create the process
logger.info('Forking TSServer', `PATH: ${newEnv['PATH']}`); logger.info('Forking TSServer', `PATH: ${newEnv['PATH']} `);
const bootstrapperPath = require.resolve('./electronForkStart'); const bootstrapperPath = require.resolve('./electronForkStart');
childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), {

View file

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import path = require('path');
import os = require('os');
export function makeRandomHexString(length: number): string {
let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
let result = '';
for (let i = 0; i < length; i++) {
const idx = Math.floor(chars.length * Math.random());
result += chars[idx];
}
return result;
}
export function getTempFile(name: string): string {
return path.join(os.tmpdir(), name);
}