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 * as languageModeIds from '../utils/languageModeIds';
import API from '../utils/api';
import { memoize } from '../utils/memoize';
import { getTempFile } from '../utils/temp';
enum BufferKind {
TypeScript = 1,
@ -69,6 +71,10 @@ class SyncedBuffer {
this.client.execute('open', args, false);
}
public get resource(): Uri {
return this.document.uri;
}
public get lineCount(): number {
return this.document.lineCount;
}
@ -126,6 +132,10 @@ class SyncedBufferMap {
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) {
const file = this.toKey(resource);
if (file) {
@ -149,7 +159,8 @@ class SyncedBufferMap {
}
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.syncedBuffers = new SyncedBufferMap(path => this.client.normalizedPath(path));
this.syncedBuffers = new SyncedBufferMap(path => this.normalizePath(path));
this.updateConfiguration();
workspace.onDidChangeConfiguration(() => this.updateConfiguration(), null);
@ -202,6 +213,14 @@ export default class BufferSyncSupport {
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 {
for (const buffer of this.syncedBuffers.allBuffers) {
buffer.open();
@ -376,4 +395,40 @@ export default class BufferSyncSupport {
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;
}
}
const resource = Uri.file(filepath);
return resource;
return this.bufferSyncSupport.toResource(filepath);
}
public getWorkspaceRootForResource(resource: Uri): string | undefined {
@ -976,7 +974,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
if (this.apiVersion.gte(API.v222)) {
this.cancellationPipeName = electron.getTempFile(`tscancellation-${electron.makeRandomHexString(20)}`);
this.cancellationPipeName = electron.getTempSock('tscancellation');
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.
*--------------------------------------------------------------------------------------------*/
import Logger from './logger';
import { getTempFile, makeRandomHexString } from './temp';
import path = require('path');
import os = require('os');
import net = require('net');
import cp = require('child_process');
import Logger from './logger';
export interface IForkOptions {
cwd?: string;
execArgv?: string[];
}
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 getTempSock(prefix: string): string {
const fullName = `vscode-${prefix}-${makeRandomHexString(20)}`;
return getTempFile(fullName + '.sock');
}
function generatePipeName(): string {
@ -38,12 +34,6 @@ function getPipeName(name: string): string {
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(
env: any,
stdInPipeName: string,
@ -131,7 +121,7 @@ export function fork(
};
// Create the process
logger.info('Forking TSServer', `PATH: ${newEnv['PATH']}`);
logger.info('Forking TSServer', `PATH: ${newEnv['PATH']} `);
const bootstrapperPath = require.resolve('./electronForkStart');
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);
}