mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-01 14:25:16 -05:00
Merge branch 'master' into watchImprovements
This commit is contained in:
@@ -277,7 +277,7 @@ namespace ts.server {
|
||||
|
||||
const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path);
|
||||
if (referencedFilePaths.length > 0) {
|
||||
return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
|
||||
return map<Path, ModuleBuilderFileInfo>(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(file: string, start: number, end: number, _options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
getFormattingEditsForRange(file: string, start: number, end: number, _options: FormatCodeOptions): ts.TextChange[] {
|
||||
const args: protocol.FormatRequestArgs = this.createFileLocationRequestArgsWithEndLineAndOffset(file, start, end);
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace ts.server {
|
||||
* Any compiler options that might contain paths will be taken out.
|
||||
* Enum compiler options will be converted to strings.
|
||||
*/
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
// "extends", "files", "include", or "exclude" will be undefined if an external config is used.
|
||||
// Otherwise, we will use "true" if the property is present and "false" if it is missing.
|
||||
readonly extends: boolean | undefined;
|
||||
@@ -201,7 +201,7 @@ namespace ts.server {
|
||||
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
|
||||
*/
|
||||
export function combineProjectOutput<T>(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
|
||||
const result = projects.reduce<T[]>((previous, current) => concatenate(previous, action(current)), []).sort(comparer);
|
||||
const result = flatMap(projects, action).sort(comparer);
|
||||
return projects.length > 1 ? deduplicate(result, areEqual) : result;
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace ts.server {
|
||||
getFileName: x => x,
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: (fileName, extraFileExtensions) => {
|
||||
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
const mixedContentExtensions = map(filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension));
|
||||
}
|
||||
};
|
||||
@@ -663,7 +663,7 @@ namespace ts.server {
|
||||
// If a change was made inside "folder/file", node will trigger the callback twice:
|
||||
// one with the fileName being "folder/file", and the other one with "folder".
|
||||
// We don't respond to the second one.
|
||||
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
if (fileName && !isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1120,7 +1120,7 @@ namespace ts.server {
|
||||
configFileName: configFileName(),
|
||||
projectType: project instanceof server.ExternalProject ? "external" : "configured",
|
||||
languageServiceEnabled: project.languageServiceEnabled,
|
||||
version: ts.version,
|
||||
version,
|
||||
};
|
||||
this.eventHandler({ eventName: ProjectInfoTelemetryEvent, data });
|
||||
|
||||
@@ -1130,7 +1130,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
const configFilePath = project instanceof server.ConfiguredProject && project.getConfigFilePath();
|
||||
const base = ts.getBaseFileName(configFilePath);
|
||||
const base = getBaseFileName(configFilePath);
|
||||
return base === "tsconfig.json" || base === "jsconfig.json" ? base : "other";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
/// <reference path="scriptInfo.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
|
||||
|
||||
export class CachedServerHost implements ServerHost {
|
||||
args: string[];
|
||||
newLine: string;
|
||||
@@ -32,7 +30,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getFileSystemEntries(rootDir: string) {
|
||||
const path = ts.toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
|
||||
const path = toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
|
||||
const cachedResult = this.cachedReadDirectoryResult.get(path);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
@@ -49,7 +47,7 @@ namespace ts.server {
|
||||
|
||||
private canWorkWithCacheForDir(rootDir: string) {
|
||||
// Some of the hosts might not be able to handle read directory or getDirectories
|
||||
const path = ts.toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
|
||||
const path = toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
|
||||
if (this.cachedReadDirectoryResult.get(path)) {
|
||||
return true;
|
||||
}
|
||||
@@ -66,11 +64,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
writeFile(fileName: string, data: string, writeByteOrderMark?: boolean) {
|
||||
const path = ts.toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
|
||||
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
|
||||
const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path));
|
||||
const baseFileName = getBaseFileName(toNormalizedPath(fileName));
|
||||
if (result && !some(result.files, file => this.fileNameEqual(file, baseFileName))) {
|
||||
result.files.push(baseFileName);
|
||||
if (result) {
|
||||
result.files = this.updateFileSystemEntry(result.files, baseFileName, /*isValid*/ true);
|
||||
}
|
||||
return this.host.writeFile(fileName, data, writeByteOrderMark);
|
||||
}
|
||||
@@ -102,7 +100,7 @@ namespace ts.server {
|
||||
|
||||
getDirectories(rootDir: string) {
|
||||
if (this.canWorkWithCacheForDir(rootDir)) {
|
||||
return this.getFileSystemEntries(rootDir).directories;
|
||||
return this.getFileSystemEntries(rootDir).directories.slice();
|
||||
}
|
||||
return this.host.getDirectories(rootDir);
|
||||
}
|
||||
@@ -115,14 +113,14 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
const path = ts.toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
|
||||
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
|
||||
const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path));
|
||||
const baseName = getBaseFileName(toNormalizedPath(fileName));
|
||||
return (result && some(result.files, file => this.fileNameEqual(file, baseName))) || this.host.fileExists(fileName);
|
||||
return (result && this.hasEntry(result.files, baseName)) || this.host.fileExists(fileName);
|
||||
}
|
||||
|
||||
directoryExists(dirPath: string) {
|
||||
const path = ts.toPath(dirPath, this.currentDirectory, this.getCanonicalFileName);
|
||||
const path = toPath(dirPath, this.currentDirectory, this.getCanonicalFileName);
|
||||
return this.cachedReadDirectoryResult.has(path) || this.host.directoryExists(dirPath);
|
||||
}
|
||||
|
||||
@@ -134,15 +132,20 @@ namespace ts.server {
|
||||
return this.getCanonicalFileName(name1) === this.getCanonicalFileName(name2);
|
||||
}
|
||||
|
||||
private updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) {
|
||||
if (some(entries, entry => this.fileNameEqual(entry, baseName))) {
|
||||
private hasEntry(entries: ReadonlyArray<string>, name: string) {
|
||||
return some(entries, file => this.fileNameEqual(file, name));
|
||||
}
|
||||
|
||||
private updateFileSystemEntry(entries: ReadonlyArray<string>, baseName: string, isValid: boolean) {
|
||||
if (this.hasEntry(entries, baseName)) {
|
||||
if (!isValid) {
|
||||
filterMutate(entries, entry => !this.fileNameEqual(entry, baseName));
|
||||
return filter(entries, entry => !this.fileNameEqual(entry, baseName));
|
||||
}
|
||||
}
|
||||
else if (isValid) {
|
||||
entries.push(baseName);
|
||||
return entries.concat(baseName);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
addOrDeleteFileOrFolder(fileOrFolder: NormalizedPath) {
|
||||
@@ -159,8 +162,8 @@ namespace ts.server {
|
||||
if (parentResult) {
|
||||
const baseName = getBaseFileName(fileOrFolder);
|
||||
if (parentResult) {
|
||||
this.updateFileSystemEntry(parentResult.files, baseName, this.host.fileExists(path));
|
||||
this.updateFileSystemEntry(parentResult.directories, baseName, this.host.directoryExists(path));
|
||||
parentResult.files = this.updateFileSystemEntry(parentResult.files, baseName, this.host.fileExists(path));
|
||||
parentResult.directories = this.updateFileSystemEntry(parentResult.directories, baseName, this.host.directoryExists(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,8 +188,9 @@ namespace ts.server {
|
||||
|
||||
}
|
||||
|
||||
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost {
|
||||
private compilationSettings: ts.CompilerOptions;
|
||||
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
|
||||
export class LSHost implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private compilationSettings: CompilerOptions;
|
||||
private readonly resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
private readonly resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
/* @internal */
|
||||
@@ -203,7 +207,7 @@ namespace ts.server {
|
||||
constructor(host: ServerHost, private project: Project, private readonly cancellationToken: HostCancellationToken) {
|
||||
this.host = host;
|
||||
this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds);
|
||||
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
|
||||
|
||||
if (host.trace) {
|
||||
this.trace = s => host.trace(s);
|
||||
@@ -285,7 +289,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
ts.Debug.assert(resolution !== undefined);
|
||||
Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedModules.push(getResult(resolution));
|
||||
}
|
||||
@@ -363,7 +367,7 @@ namespace ts.server {
|
||||
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings));
|
||||
}
|
||||
|
||||
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
|
||||
getScriptSnapshot(filename: string): IScriptSnapshot {
|
||||
const scriptInfo = this.project.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.getSnapshot();
|
||||
@@ -411,7 +415,7 @@ namespace ts.server {
|
||||
return this.host.directoryExists(path);
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return this.host.readDirectory(path, extensions, exclude, include, depth);
|
||||
}
|
||||
|
||||
@@ -450,7 +454,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
setCompilationSettings(opt: ts.CompilerOptions) {
|
||||
setCompilationSettings(opt: CompilerOptions) {
|
||||
if (changesAffectModuleResolution(this.compilationSettings, opt)) {
|
||||
this.resolvedModuleNames.clear();
|
||||
this.resolvedTypeReferenceDirectives.clear();
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace ts.server {
|
||||
export abstract class Project {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
|
||||
private program: ts.Program;
|
||||
private program: Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: Map<FileWatcher> = createMap<FileWatcher>();
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace ts.server {
|
||||
private readonly projectName: string,
|
||||
readonly projectKind: ProjectKind,
|
||||
readonly projectService: ProjectService,
|
||||
private documentRegistry: ts.DocumentRegistry,
|
||||
private documentRegistry: DocumentRegistry,
|
||||
hasExplicitListOfFiles: boolean,
|
||||
languageServiceEnabled: boolean,
|
||||
private compilerOptions: CompilerOptions,
|
||||
@@ -199,7 +199,7 @@ namespace ts.server {
|
||||
host: ServerHost) {
|
||||
|
||||
if (!this.compilerOptions) {
|
||||
this.compilerOptions = ts.getDefaultCompilerOptions();
|
||||
this.compilerOptions = getDefaultCompilerOptions();
|
||||
this.compilerOptions.allowNonTsExtensions = true;
|
||||
this.compilerOptions.allowJs = true;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ namespace ts.server {
|
||||
this.lsHost = new LSHost(host, this, this.projectService.cancellationToken);
|
||||
this.lsHost.setCompilationSettings(this.compilerOptions);
|
||||
|
||||
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
|
||||
this.languageService = createLanguageService(this.lsHost, this.documentRegistry);
|
||||
|
||||
if (!languageServiceEnabled) {
|
||||
this.disableLanguageService();
|
||||
@@ -729,7 +729,7 @@ namespace ts.server {
|
||||
}
|
||||
let strBuilder = "";
|
||||
for (const file of this.program.getSourceFiles()) {
|
||||
strBuilder += `${file.fileName}\n`;
|
||||
strBuilder += `\t${file.fileName}\n`;
|
||||
}
|
||||
return strBuilder;
|
||||
}
|
||||
@@ -914,7 +914,7 @@ namespace ts.server {
|
||||
// Used to keep track of what directories are watched for this project
|
||||
directoriesWatchedForTsconfig: string[] = [];
|
||||
|
||||
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) {
|
||||
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
|
||||
super(InferredProject.newName(),
|
||||
ProjectKind.Inferred,
|
||||
projectService,
|
||||
@@ -995,7 +995,7 @@ namespace ts.server {
|
||||
|
||||
constructor(configFileName: NormalizedPath,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: ts.DocumentRegistry,
|
||||
documentRegistry: DocumentRegistry,
|
||||
hasExplicitListOfFiles: boolean,
|
||||
compilerOptions: CompilerOptions,
|
||||
languageServiceEnabled: boolean,
|
||||
@@ -1232,7 +1232,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getEffectiveTypeRoots() {
|
||||
return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || [];
|
||||
return getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,7 +1244,7 @@ namespace ts.server {
|
||||
private typeAcquisition: TypeAcquisition;
|
||||
constructor(public externalProjectName: string,
|
||||
projectService: ProjectService,
|
||||
documentRegistry: ts.DocumentRegistry,
|
||||
documentRegistry: DocumentRegistry,
|
||||
compilerOptions: CompilerOptions,
|
||||
languageServiceEnabled: boolean,
|
||||
public compileOnSaveEnabled: boolean,
|
||||
|
||||
@@ -640,7 +640,7 @@ namespace ts.server.protocol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Location in source code expressed as (one-based) line and character offset.
|
||||
* Location in source code expressed as (one-based) line and (one-based) column offset.
|
||||
*/
|
||||
export interface Location {
|
||||
line: number;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace ts.server {
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
|
||||
public getLineInfo(line: number) {
|
||||
public getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
|
||||
}
|
||||
/**
|
||||
@@ -72,19 +72,12 @@ namespace ts.server {
|
||||
const lineMap = this.getLineMap();
|
||||
const start = lineMap[line]; // -1 since line is 1-based
|
||||
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
|
||||
return ts.createTextSpanFromBounds(start, end);
|
||||
return createTextSpanFromBounds(start, end);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineInfo = index.lineNumberToInfo(line + 1);
|
||||
let len: number;
|
||||
if (lineInfo.leaf) {
|
||||
len = lineInfo.leaf.text.length;
|
||||
}
|
||||
else {
|
||||
const nextLineInfo = index.lineNumberToInfo(line + 2);
|
||||
len = nextLineInfo.offset - lineInfo.offset;
|
||||
}
|
||||
return ts.createTextSpan(lineInfo.offset, len);
|
||||
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
|
||||
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
|
||||
return createTextSpan(absolutePosition, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,27 +86,19 @@ namespace ts.server {
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
if (!this.svc) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1);
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text);
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
|
||||
const lineInfo = index.lineNumberToInfo(line);
|
||||
// TODO: assert this offset is actually on the line
|
||||
return (lineInfo.offset + offset - 1);
|
||||
return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
if (!this.svc) {
|
||||
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
|
||||
return { line: line + 1, offset: character + 1 };
|
||||
}
|
||||
const index = this.svc.getSnapshot().index;
|
||||
const lineOffset = index.charOffsetToLineNumberAndPos(position);
|
||||
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
|
||||
return this.svc.getSnapshot().index.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
private getFileText(tempFileName?: string) {
|
||||
@@ -162,7 +147,7 @@ namespace ts.server {
|
||||
* All projects that include this file
|
||||
*/
|
||||
readonly containingProjects: Project[] = [];
|
||||
private formatCodeSettings: ts.FormatCodeSettings;
|
||||
private formatCodeSettings: FormatCodeSettings;
|
||||
readonly path: Path;
|
||||
|
||||
private fileWatcher: FileWatcher;
|
||||
@@ -343,7 +328,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getLineInfo(line: number) {
|
||||
getLineInfo(line: number): AbsolutePositionAndLineText {
|
||||
return this.textStorage.getLineInfo(line);
|
||||
}
|
||||
|
||||
@@ -373,11 +358,7 @@ namespace ts.server {
|
||||
return this.textStorage.lineOffsetToPosition(line, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
return this.textStorage.positionToLineOffset(position);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,13 @@ namespace ts.server {
|
||||
export interface LineCollection {
|
||||
charCount(): number;
|
||||
lineCount(): number;
|
||||
isLeaf(): boolean;
|
||||
isLeaf(): this is LineLeaf;
|
||||
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker): void;
|
||||
}
|
||||
|
||||
export interface ILineInfo {
|
||||
line: number;
|
||||
offset: number;
|
||||
text?: string;
|
||||
leaf?: LineLeaf;
|
||||
export interface AbsolutePositionAndLineText {
|
||||
absolutePosition: number;
|
||||
lineText: string | undefined;
|
||||
}
|
||||
|
||||
export enum CharRangeSection {
|
||||
@@ -97,22 +95,20 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
// path at least length two (root and leaf)
|
||||
let insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
|
||||
const leafNode = <LineLeaf>this.startPath[this.startPath.length - 1];
|
||||
const len = lines.length;
|
||||
|
||||
if (len > 0) {
|
||||
if (lines.length > 0) {
|
||||
leafNode.text = lines[0];
|
||||
|
||||
if (len > 1) {
|
||||
let insertedNodes = <LineCollection[]>new Array(len - 1);
|
||||
if (lines.length > 1) {
|
||||
let insertedNodes = <LineCollection[]>new Array(lines.length - 1);
|
||||
let startNode = <LineCollection>leafNode;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
insertedNodes[i - 1] = new LineLeaf(lines[i]);
|
||||
}
|
||||
let pathIndex = this.startPath.length - 2;
|
||||
while (pathIndex >= 0) {
|
||||
insertionNode = <LineNode>this.startPath[pathIndex];
|
||||
const insertionNode = <LineNode>this.startPath[pathIndex];
|
||||
insertedNodes = insertionNode.insertAt(startNode, insertedNodes);
|
||||
pathIndex--;
|
||||
startNode = insertionNode;
|
||||
@@ -134,6 +130,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
else {
|
||||
const insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
|
||||
// no content for leaf node, so delete it
|
||||
insertionNode.remove(leafNode);
|
||||
for (let j = this.startPath.length - 2; j >= 0; j--) {
|
||||
@@ -251,7 +248,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getTextChangeRange() {
|
||||
return ts.createTextChangeRange(ts.createTextSpan(this.pos, this.deleteLen),
|
||||
return createTextChangeRange(createTextSpan(this.pos, this.deleteLen),
|
||||
this.insertedText ? this.insertedText.length : 0);
|
||||
}
|
||||
}
|
||||
@@ -281,9 +278,9 @@ namespace ts.server {
|
||||
// REVIEW: can optimize by coalescing simple edits
|
||||
edit(pos: number, deleteLen: number, insertedText?: string) {
|
||||
this.changes.push(new TextChange(pos, deleteLen, insertedText));
|
||||
if ((this.changes.length > ScriptVersionCache.changeNumberThreshold) ||
|
||||
(deleteLen > ScriptVersionCache.changeLengthThreshold) ||
|
||||
(insertedText && (insertedText.length > ScriptVersionCache.changeLengthThreshold))) {
|
||||
if (this.changes.length > ScriptVersionCache.changeNumberThreshold ||
|
||||
deleteLen > ScriptVersionCache.changeLengthThreshold ||
|
||||
insertedText && insertedText.length > ScriptVersionCache.changeLengthThreshold) {
|
||||
this.getSnapshot();
|
||||
}
|
||||
}
|
||||
@@ -340,21 +337,21 @@ namespace ts.server {
|
||||
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
|
||||
if (oldVersion < newVersion) {
|
||||
if (oldVersion >= this.minVersion) {
|
||||
const textChangeRanges: ts.TextChangeRange[] = [];
|
||||
const textChangeRanges: TextChangeRange[] = [];
|
||||
for (let i = oldVersion + 1; i <= newVersion; i++) {
|
||||
const snap = this.versions[this.versionToIndex(i)];
|
||||
for (const textChange of snap.changesSincePreviousVersion) {
|
||||
textChangeRanges.push(textChange.getTextChangeRange());
|
||||
}
|
||||
}
|
||||
return ts.collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
|
||||
return collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ts.unchangedTextChangeRange;
|
||||
return unchangedTextChangeRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +365,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
export class LineIndexSnapshot implements ts.IScriptSnapshot {
|
||||
export class LineIndexSnapshot implements IScriptSnapshot {
|
||||
constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray<TextChange> = emptyArray) {
|
||||
}
|
||||
|
||||
@@ -380,10 +377,10 @@ namespace ts.server {
|
||||
return this.index.root.charCount();
|
||||
}
|
||||
|
||||
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
|
||||
getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
|
||||
if (oldSnapshot instanceof LineIndexSnapshot && this.cache === oldSnapshot.cache) {
|
||||
if (this.version <= oldSnapshot.version) {
|
||||
return ts.unchangedTextChangeRange;
|
||||
return unchangedTextChangeRange;
|
||||
}
|
||||
else {
|
||||
return this.cache.getTextChangesBetweenVersions(oldSnapshot.version, this.version);
|
||||
@@ -397,22 +394,27 @@ namespace ts.server {
|
||||
// set this to true to check each edit for accuracy
|
||||
checkEdits = false;
|
||||
|
||||
charOffsetToLineNumberAndPos(charOffset: number) {
|
||||
return this.root.charOffsetToLineNumberAndPos(1, charOffset);
|
||||
absolutePositionOfStartOfLine(oneBasedLine: number): number {
|
||||
return this.lineNumberToInfo(oneBasedLine).absolutePosition;
|
||||
}
|
||||
|
||||
lineNumberToInfo(lineNumber: number): ILineInfo {
|
||||
positionToLineOffset(position: number): protocol.Location {
|
||||
const { oneBasedLine, zeroBasedColumn } = this.root.charOffsetToLineInfo(1, position);
|
||||
return { line: oneBasedLine, offset: zeroBasedColumn + 1 };
|
||||
}
|
||||
|
||||
private positionToColumnAndLineText(position: number): { zeroBasedColumn: number, lineText: string } {
|
||||
return this.root.charOffsetToLineInfo(1, position);
|
||||
}
|
||||
|
||||
lineNumberToInfo(oneBasedLine: number): AbsolutePositionAndLineText {
|
||||
const lineCount = this.root.lineCount();
|
||||
if (lineNumber <= lineCount) {
|
||||
const lineInfo = this.root.lineNumberToInfo(lineNumber, 0);
|
||||
lineInfo.line = lineNumber;
|
||||
return lineInfo;
|
||||
if (oneBasedLine <= lineCount) {
|
||||
const { position, leaf } = this.root.lineNumberToInfo(oneBasedLine, 0);
|
||||
return { absolutePosition: position, lineText: leaf && leaf.text };
|
||||
}
|
||||
else {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: this.root.charCount()
|
||||
};
|
||||
return { absolutePosition: this.root.charCount(), lineText: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,12 +470,9 @@ namespace ts.server {
|
||||
return !walkFns.done;
|
||||
}
|
||||
|
||||
edit(pos: number, deleteLength: number, newText?: string) {
|
||||
function editFlat(source: string, s: number, dl: number, nt = "") {
|
||||
return source.substring(0, s) + nt + source.substring(s + dl, source.length);
|
||||
}
|
||||
edit(pos: number, deleteLength: number, newText?: string): LineIndex {
|
||||
if (this.root.charCount() === 0) {
|
||||
// TODO: assert deleteLength === 0
|
||||
Debug.assert(deleteLength === 0); // Can't delete from empty document
|
||||
if (newText !== undefined) {
|
||||
this.load(LineIndex.linesFromText(newText).lines);
|
||||
return this;
|
||||
@@ -482,7 +481,8 @@ namespace ts.server {
|
||||
else {
|
||||
let checkText: string;
|
||||
if (this.checkEdits) {
|
||||
checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText);
|
||||
const source = this.getText(0, this.root.charCount());
|
||||
checkText = source.slice(0, pos) + newText + source.slice(pos + deleteLength);
|
||||
}
|
||||
const walker = new EditWalker();
|
||||
let suppressTrailingText = false;
|
||||
@@ -502,63 +502,44 @@ namespace ts.server {
|
||||
else if (deleteLength > 0) {
|
||||
// check whether last characters deleted are line break
|
||||
const e = pos + deleteLength;
|
||||
const lineInfo = this.charOffsetToLineNumberAndPos(e);
|
||||
if ((lineInfo && (lineInfo.offset === 0))) {
|
||||
const { zeroBasedColumn, lineText } = this.positionToColumnAndLineText(e);
|
||||
if (zeroBasedColumn === 0) {
|
||||
// move range end just past line that will merge with previous line
|
||||
deleteLength += lineInfo.text.length;
|
||||
deleteLength += lineText.length;
|
||||
// store text by appending to end of insertedText
|
||||
if (newText) {
|
||||
newText = newText + lineInfo.text;
|
||||
}
|
||||
else {
|
||||
newText = lineInfo.text;
|
||||
}
|
||||
newText = newText ? newText + lineText : lineText;
|
||||
}
|
||||
}
|
||||
if (pos < this.root.charCount()) {
|
||||
this.root.walk(pos, deleteLength, walker);
|
||||
walker.insertLines(newText, suppressTrailingText);
|
||||
}
|
||||
|
||||
this.root.walk(pos, deleteLength, walker);
|
||||
walker.insertLines(newText, suppressTrailingText);
|
||||
|
||||
if (this.checkEdits) {
|
||||
const updatedText = this.getText(0, this.root.charCount());
|
||||
const updatedText = walker.lineIndex.getText(0, walker.lineIndex.getLength());
|
||||
Debug.assert(checkText === updatedText, "buffer edit mismatch");
|
||||
}
|
||||
|
||||
return walker.lineIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
|
||||
const nodeCount = Math.ceil(nodes.length / lineCollectionCapacity);
|
||||
const interiorNodes: LineNode[] = [];
|
||||
private static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
|
||||
if (nodes.length < lineCollectionCapacity) {
|
||||
return new LineNode(nodes);
|
||||
}
|
||||
|
||||
const interiorNodes = new Array<LineNode>(Math.ceil(nodes.length / lineCollectionCapacity));
|
||||
let nodeIndex = 0;
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
interiorNodes[i] = new LineNode();
|
||||
let charCount = 0;
|
||||
let lineCount = 0;
|
||||
for (let j = 0; j < lineCollectionCapacity; j++) {
|
||||
if (nodeIndex < nodes.length) {
|
||||
interiorNodes[i].add(nodes[nodeIndex]);
|
||||
charCount += nodes[nodeIndex].charCount();
|
||||
lineCount += nodes[nodeIndex].lineCount();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
nodeIndex++;
|
||||
}
|
||||
interiorNodes[i].totalChars = charCount;
|
||||
interiorNodes[i].totalLines = lineCount;
|
||||
}
|
||||
if (interiorNodes.length === 1) {
|
||||
return interiorNodes[0];
|
||||
}
|
||||
else {
|
||||
return this.buildTreeFromBottom(interiorNodes);
|
||||
for (let i = 0; i < interiorNodes.length; i++) {
|
||||
const end = Math.min(nodeIndex + lineCollectionCapacity, nodes.length);
|
||||
interiorNodes[i] = new LineNode(nodes.slice(nodeIndex, end));
|
||||
nodeIndex = end;
|
||||
}
|
||||
return this.buildTreeFromBottom(interiorNodes);
|
||||
}
|
||||
|
||||
static linesFromText(text: string) {
|
||||
const lineMap = ts.computeLineStarts(text);
|
||||
const lineMap = computeLineStarts(text);
|
||||
|
||||
if (lineMap.length === 0) {
|
||||
return { lines: <string[]>[], lineMap };
|
||||
@@ -583,7 +564,10 @@ namespace ts.server {
|
||||
export class LineNode implements LineCollection {
|
||||
totalChars = 0;
|
||||
totalLines = 0;
|
||||
children: LineCollection[] = [];
|
||||
|
||||
constructor(private readonly children: LineCollection[] = []) {
|
||||
if (children.length) this.updateCounts();
|
||||
}
|
||||
|
||||
isLeaf() {
|
||||
return false;
|
||||
@@ -598,7 +582,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
|
||||
private execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
|
||||
if (walkFns.pre) {
|
||||
walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType);
|
||||
}
|
||||
@@ -614,7 +598,7 @@ namespace ts.server {
|
||||
return walkFns.done;
|
||||
}
|
||||
|
||||
skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
|
||||
private skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
|
||||
if (walkFns.pre && (!walkFns.done)) {
|
||||
walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType);
|
||||
walkFns.goSubtree = true;
|
||||
@@ -624,16 +608,14 @@ namespace ts.server {
|
||||
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
|
||||
// assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars)
|
||||
let childIndex = 0;
|
||||
let child = this.children[0];
|
||||
let childCharCount = child.charCount();
|
||||
let childCharCount = this.children[childIndex].charCount();
|
||||
// find sub-tree containing start
|
||||
let adjustedStart = rangeStart;
|
||||
while (adjustedStart >= childCharCount) {
|
||||
this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart);
|
||||
adjustedStart -= childCharCount;
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
childCharCount = this.children[childIndex].charCount();
|
||||
}
|
||||
// Case I: both start and end of range in same subtree
|
||||
if ((adjustedStart + rangeLength) <= childCharCount) {
|
||||
@@ -648,7 +630,7 @@ namespace ts.server {
|
||||
}
|
||||
let adjustedLength = rangeLength - (childCharCount - adjustedStart);
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
const child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
while (adjustedLength > childCharCount) {
|
||||
if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) {
|
||||
@@ -656,8 +638,7 @@ namespace ts.server {
|
||||
}
|
||||
adjustedLength -= childCharCount;
|
||||
childIndex++;
|
||||
child = this.children[childIndex];
|
||||
childCharCount = child.charCount();
|
||||
childCharCount = this.children[childIndex].charCount();
|
||||
}
|
||||
if (adjustedLength > 0) {
|
||||
if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) {
|
||||
@@ -676,90 +657,88 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
charOffsetToLineNumberAndPos(lineNumber: number, charOffset: number): ILineInfo {
|
||||
const childInfo = this.childFromCharOffset(lineNumber, charOffset);
|
||||
// Input position is relative to the start of this node.
|
||||
// Output line number is absolute.
|
||||
charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } {
|
||||
const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition);
|
||||
if (!childInfo.child) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: charOffset,
|
||||
oneBasedLine: lineNumberAccumulator,
|
||||
zeroBasedColumn: relativePosition,
|
||||
lineText: undefined,
|
||||
};
|
||||
}
|
||||
else if (childInfo.childIndex < this.children.length) {
|
||||
if (childInfo.child.isLeaf()) {
|
||||
return {
|
||||
line: childInfo.lineNumber,
|
||||
offset: childInfo.charOffset,
|
||||
text: (<LineLeaf>(childInfo.child)).text,
|
||||
leaf: (<LineLeaf>(childInfo.child))
|
||||
oneBasedLine: childInfo.lineNumberAccumulator,
|
||||
zeroBasedColumn: childInfo.relativePosition,
|
||||
lineText: childInfo.child.text,
|
||||
};
|
||||
}
|
||||
else {
|
||||
const lineNode = <LineNode>(childInfo.child);
|
||||
return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset);
|
||||
return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
|
||||
return { line: this.lineCount(), offset: lineInfo.leaf.charCount() };
|
||||
return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
lineNumberToInfo(lineNumber: number, charOffset: number): ILineInfo {
|
||||
const childInfo = this.childFromLineNumber(lineNumber, charOffset);
|
||||
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
|
||||
const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator);
|
||||
if (!childInfo.child) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: charOffset
|
||||
};
|
||||
return { position: positionAccumulator, leaf: undefined };
|
||||
}
|
||||
else if (childInfo.child.isLeaf()) {
|
||||
return {
|
||||
line: lineNumber,
|
||||
offset: childInfo.charOffset,
|
||||
text: (<LineLeaf>(childInfo.child)).text,
|
||||
leaf: (<LineLeaf>(childInfo.child))
|
||||
};
|
||||
return { position: childInfo.positionAccumulator, leaf: childInfo.child };
|
||||
}
|
||||
else {
|
||||
const lineNode = <LineNode>(childInfo.child);
|
||||
return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset);
|
||||
return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator);
|
||||
}
|
||||
}
|
||||
|
||||
childFromLineNumber(lineNumber: number, charOffset: number) {
|
||||
/**
|
||||
* Input line number is relative to the start of this node.
|
||||
* Output line number is relative to the child.
|
||||
* positionAccumulator will be an absolute position once relativeLineNumber reaches 0.
|
||||
*/
|
||||
private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } {
|
||||
let child: LineCollection;
|
||||
let relativeLineNumber = lineNumber;
|
||||
let i: number;
|
||||
let len: number;
|
||||
for (i = 0, len = this.children.length; i < len; i++) {
|
||||
for (i = 0; i < this.children.length; i++) {
|
||||
child = this.children[i];
|
||||
const childLineCount = child.lineCount();
|
||||
if (childLineCount >= relativeLineNumber) {
|
||||
if (childLineCount >= relativeOneBasedLine) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
relativeLineNumber -= childLineCount;
|
||||
charOffset += child.charCount();
|
||||
relativeOneBasedLine -= childLineCount;
|
||||
positionAccumulator += child.charCount();
|
||||
}
|
||||
}
|
||||
return { child, childIndex: i, relativeLineNumber, charOffset };
|
||||
return { child, relativeOneBasedLine, positionAccumulator };
|
||||
}
|
||||
|
||||
childFromCharOffset(lineNumber: number, charOffset: number) {
|
||||
private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number
|
||||
): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } {
|
||||
let child: LineCollection;
|
||||
let i: number;
|
||||
let len: number;
|
||||
for (i = 0, len = this.children.length; i < len; i++) {
|
||||
child = this.children[i];
|
||||
if (child.charCount() > charOffset) {
|
||||
if (child.charCount() > relativePosition) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
charOffset -= child.charCount();
|
||||
lineNumber += child.lineCount();
|
||||
relativePosition -= child.charCount();
|
||||
lineNumberAccumulator += child.lineCount();
|
||||
}
|
||||
}
|
||||
return { child, childIndex: i, charOffset, lineNumber };
|
||||
return { child, childIndex: i, relativePosition, lineNumberAccumulator };
|
||||
}
|
||||
|
||||
private splitAfter(childIndex: number) {
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace ts.server {
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
class Logger implements ts.server.Logger {
|
||||
class Logger implements server.Logger {
|
||||
private fd = -1;
|
||||
private seq = 0;
|
||||
private inGroup = false;
|
||||
@@ -526,12 +526,18 @@ namespace ts.server {
|
||||
if (err) {
|
||||
watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
|
||||
watchedFile.mtime = stats.mtime;
|
||||
const eventKind = watchedFile.mtime.getTime() === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
watchedFile.callback(watchedFile.fileName, eventKind);
|
||||
else {
|
||||
const oldTime = watchedFile.mtime.getTime();
|
||||
const newTime = stats.mtime.getTime();
|
||||
if (oldTime !== newTime) {
|
||||
watchedFile.mtime = stats.mtime;
|
||||
const eventKind = oldTime === 0
|
||||
? FileWatcherEventKind.Created
|
||||
: newTime === 0
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
watchedFile.callback(watchedFile.fileName, eventKind);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace ts.server {
|
||||
|
||||
interface FileStart {
|
||||
file: string;
|
||||
start: ILineInfo;
|
||||
start: protocol.Location;
|
||||
}
|
||||
|
||||
function compareNumber(a: number, b: number) {
|
||||
@@ -72,28 +72,28 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
function formatDiag(fileName: NormalizedPath, project: Project, diag: ts.Diagnostic): protocol.Diagnostic {
|
||||
function formatDiag(fileName: NormalizedPath, project: Project, diag: Diagnostic): protocol.Diagnostic {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(fileName);
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(diag.start),
|
||||
end: scriptInfo.positionToLineOffset(diag.start + diag.length),
|
||||
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
text: flattenDiagnosticMessageText(diag.messageText, "\n"),
|
||||
code: diag.code,
|
||||
category: DiagnosticCategory[diag.category].toLowerCase(),
|
||||
source: diag.source
|
||||
};
|
||||
}
|
||||
|
||||
function convertToILineInfo(lineAndCharacter: LineAndCharacter): ILineInfo {
|
||||
function convertToLocation(lineAndCharacter: LineAndCharacter): protocol.Location {
|
||||
return { line: lineAndCharacter.line + 1, offset: lineAndCharacter.character + 1 };
|
||||
}
|
||||
|
||||
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName;
|
||||
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: false): protocol.Diagnostic;
|
||||
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName {
|
||||
const start = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start));
|
||||
const end = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length));
|
||||
const text = ts.flattenDiagnosticMessageText(diag.messageText, "\n");
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName;
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: false): protocol.Diagnostic;
|
||||
function formatConfigFileDiag(diag: Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName {
|
||||
const start = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start));
|
||||
const end = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length));
|
||||
const text = flattenDiagnosticMessageText(diag.messageText, "\n");
|
||||
const { code, source } = diag;
|
||||
const category = DiagnosticCategory[diag.category].toLowerCase();
|
||||
return includeFileName ? { start, end, text, code, category, source, fileName: diag.file && diag.file.fileName } :
|
||||
@@ -389,8 +389,8 @@ namespace ts.server {
|
||||
this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine));
|
||||
}
|
||||
|
||||
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ts.Diagnostic[]) {
|
||||
const bakedDiags = ts.map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
|
||||
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) {
|
||||
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
|
||||
const ev: protocol.ConfigFileDiagnosticEvent = {
|
||||
seq: 0,
|
||||
type: "event",
|
||||
@@ -547,8 +547,8 @@ namespace ts.server {
|
||||
length: d.length,
|
||||
category: DiagnosticCategory[d.category].toLowerCase(),
|
||||
code: d.code,
|
||||
startLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start)),
|
||||
endLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start + d.length))
|
||||
startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)),
|
||||
endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: def.fileName,
|
||||
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
|
||||
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
|
||||
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -631,7 +631,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: def.fileName,
|
||||
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
|
||||
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
|
||||
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -649,7 +649,7 @@ namespace ts.server {
|
||||
return {
|
||||
file: fileName,
|
||||
start: scriptInfo.positionToLineOffset(textSpan.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan))
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan))
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -673,7 +673,7 @@ namespace ts.server {
|
||||
const { fileName, isWriteAccess, textSpan, isInString } = occurrence;
|
||||
const scriptInfo = project.getScriptInfo(fileName);
|
||||
const start = scriptInfo.positionToLineOffset(textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
|
||||
const result: protocol.OccurrencesResponseItem = {
|
||||
start,
|
||||
end,
|
||||
@@ -723,7 +723,7 @@ namespace ts.server {
|
||||
return documentHighlights;
|
||||
}
|
||||
|
||||
function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem {
|
||||
function convertToDocumentHighlightsItem(documentHighlights: DocumentHighlights): protocol.DocumentHighlightsItem {
|
||||
const { fileName, highlightSpans } = documentHighlights;
|
||||
|
||||
const scriptInfo = project.getScriptInfo(fileName);
|
||||
@@ -732,10 +732,10 @@ namespace ts.server {
|
||||
highlightSpans: highlightSpans.map(convertHighlightSpan)
|
||||
};
|
||||
|
||||
function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan {
|
||||
function convertHighlightSpan(highlightSpan: HighlightSpan): protocol.HighlightSpan {
|
||||
const { textSpan, kind } = highlightSpan;
|
||||
const start = scriptInfo.positionToLineOffset(textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
|
||||
return { start, end, kind };
|
||||
}
|
||||
}
|
||||
@@ -778,7 +778,7 @@ namespace ts.server {
|
||||
const scriptInfo = this.projectService.getScriptInfo(args.file);
|
||||
projects = scriptInfo.containingProjects;
|
||||
}
|
||||
// ts.filter handles case when 'projects' is undefined
|
||||
// filter handles case when 'projects' is undefined
|
||||
projects = filter(projects, p => p.languageServiceEnabled);
|
||||
if (!projects || !projects.length) {
|
||||
return Errors.ThrowNoProject();
|
||||
@@ -831,28 +831,29 @@ namespace ts.server {
|
||||
return <protocol.FileSpan>{
|
||||
file: location.fileName,
|
||||
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
|
||||
end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)),
|
||||
end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)),
|
||||
};
|
||||
});
|
||||
},
|
||||
compareRenameLocation,
|
||||
(a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset
|
||||
);
|
||||
const locs = fileSpans.reduce<protocol.SpanGroup[]>((accum, cur) => {
|
||||
|
||||
const locs: protocol.SpanGroup[] = [];
|
||||
for (const cur of fileSpans) {
|
||||
let curFileAccum: protocol.SpanGroup;
|
||||
if (accum.length > 0) {
|
||||
curFileAccum = accum[accum.length - 1];
|
||||
if (locs.length > 0) {
|
||||
curFileAccum = locs[locs.length - 1];
|
||||
if (curFileAccum.file !== cur.file) {
|
||||
curFileAccum = undefined;
|
||||
}
|
||||
}
|
||||
if (!curFileAccum) {
|
||||
curFileAccum = { file: cur.file, locs: [] };
|
||||
accum.push(curFileAccum);
|
||||
locs.push(curFileAccum);
|
||||
}
|
||||
curFileAccum.locs.push({ start: cur.start, end: cur.end });
|
||||
return accum;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return { info: renameInfo, locs };
|
||||
}
|
||||
@@ -912,10 +913,10 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const displayString = ts.displayPartsToString(nameInfo.displayParts);
|
||||
const displayString = displayPartsToString(nameInfo.displayParts);
|
||||
const nameSpan = nameInfo.textSpan;
|
||||
const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset;
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan));
|
||||
const refs = combineProjectOutput<protocol.ReferencesResponseItem>(
|
||||
projects,
|
||||
(project: Project) => {
|
||||
@@ -928,12 +929,12 @@ namespace ts.server {
|
||||
const refScriptInfo = project.getScriptInfo(ref.fileName);
|
||||
const start = refScriptInfo.positionToLineOffset(ref.textSpan.start);
|
||||
const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1);
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: ref.fileName,
|
||||
start,
|
||||
lineText,
|
||||
end: refScriptInfo.positionToLineOffset(ts.textSpanEnd(ref.textSpan)),
|
||||
end: refScriptInfo.positionToLineOffset(textSpanEnd(ref.textSpan)),
|
||||
isWriteAccess: ref.isWriteAccess,
|
||||
isDefinition: ref.isDefinition
|
||||
};
|
||||
@@ -1056,14 +1057,14 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
if (simplifiedResult) {
|
||||
const displayString = ts.displayPartsToString(quickInfo.displayParts);
|
||||
const docString = ts.displayPartsToString(quickInfo.documentation);
|
||||
const displayString = displayPartsToString(quickInfo.displayParts);
|
||||
const docString = displayPartsToString(quickInfo.documentation);
|
||||
|
||||
return {
|
||||
kind: quickInfo.kind,
|
||||
kindModifiers: quickInfo.kindModifiers,
|
||||
start: scriptInfo.positionToLineOffset(quickInfo.textSpan.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(quickInfo.textSpan)),
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(quickInfo.textSpan)),
|
||||
displayString,
|
||||
documentation: docString,
|
||||
tags: quickInfo.tags || []
|
||||
@@ -1123,32 +1124,29 @@ namespace ts.server {
|
||||
// only to the previous line. If all this is true, then
|
||||
// add edits necessary to properly indent the current line.
|
||||
if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
|
||||
const lineInfo = scriptInfo.getLineInfo(args.line);
|
||||
if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) {
|
||||
const lineText = lineInfo.leaf.text;
|
||||
if (lineText.search("\\S") < 0) {
|
||||
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
|
||||
let hasIndent = 0;
|
||||
let i: number, len: number;
|
||||
for (i = 0, len = lineText.length; i < len; i++) {
|
||||
if (lineText.charAt(i) === " ") {
|
||||
hasIndent++;
|
||||
}
|
||||
else if (lineText.charAt(i) === "\t") {
|
||||
hasIndent += formatOptions.tabSize;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
const { lineText, absolutePosition } = scriptInfo.getLineInfo(args.line);
|
||||
if (lineText && lineText.search("\\S") < 0) {
|
||||
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
|
||||
let hasIndent = 0;
|
||||
let i: number, len: number;
|
||||
for (i = 0, len = lineText.length; i < len; i++) {
|
||||
if (lineText.charAt(i) === " ") {
|
||||
hasIndent++;
|
||||
}
|
||||
// i points to the first non whitespace character
|
||||
if (preferredIndent !== hasIndent) {
|
||||
const firstNoWhiteSpacePosition = lineInfo.offset + i;
|
||||
edits.push({
|
||||
span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition),
|
||||
newText: formatting.getIndentationString(preferredIndent, formatOptions)
|
||||
});
|
||||
else if (lineText.charAt(i) === "\t") {
|
||||
hasIndent += formatOptions.tabSize;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// i points to the first non whitespace character
|
||||
if (preferredIndent !== hasIndent) {
|
||||
const firstNoWhiteSpacePosition = absolutePosition + i;
|
||||
edits.push({
|
||||
span: createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition),
|
||||
newText: formatting.getIndentationString(preferredIndent, formatOptions)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1160,7 +1158,7 @@ namespace ts.server {
|
||||
return edits.map((edit) => {
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(edit.span.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(edit.span)),
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(edit.span)),
|
||||
newText: edit.newText ? edit.newText : ""
|
||||
};
|
||||
});
|
||||
@@ -1178,15 +1176,13 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
if (simplifiedResult) {
|
||||
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
|
||||
return mapDefined(completions.entries, entry => {
|
||||
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
|
||||
const convertedSpan: protocol.TextSpan =
|
||||
replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
|
||||
result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan });
|
||||
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
|
||||
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
|
||||
}
|
||||
return result;
|
||||
}, []).sort((a, b) => ts.compareStrings(a.name, b.name));
|
||||
}).sort((a, b) => compareStrings(a.name, b.name));
|
||||
}
|
||||
else {
|
||||
return completions;
|
||||
@@ -1198,13 +1194,8 @@ namespace ts.server {
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const position = this.getPosition(args, scriptInfo);
|
||||
|
||||
return args.entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
|
||||
const details = project.getLanguageService().getCompletionEntryDetails(file, position, entryName);
|
||||
if (details) {
|
||||
accum.push(details);
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
return mapDefined(args.entryNames, entryName =>
|
||||
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
|
||||
}
|
||||
|
||||
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] {
|
||||
@@ -1269,14 +1260,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
|
||||
const checkList = fileNames.reduce((accum: PendingErrorCheck[], uncheckedFileName: string) => {
|
||||
const checkList = mapDefined(fileNames, uncheckedFileName => {
|
||||
const fileName = toNormalizedPath(uncheckedFileName);
|
||||
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
|
||||
if (project) {
|
||||
accum.push({ fileName, project });
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
return project && { fileName, project };
|
||||
});
|
||||
|
||||
if (checkList.length > 0) {
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
|
||||
@@ -1321,11 +1309,11 @@ namespace ts.server {
|
||||
if (!fileName) {
|
||||
return;
|
||||
}
|
||||
const file = ts.normalizePath(fileName);
|
||||
const file = normalizePath(fileName);
|
||||
this.projectService.closeClientFile(file);
|
||||
}
|
||||
|
||||
private decorateNavigationBarItems(items: ts.NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
|
||||
private decorateNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
|
||||
return map(items, item => ({
|
||||
text: item.text,
|
||||
kind: item.kind,
|
||||
@@ -1346,7 +1334,7 @@ namespace ts.server {
|
||||
: items;
|
||||
}
|
||||
|
||||
private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
|
||||
private decorateNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
|
||||
return {
|
||||
text: tree.text,
|
||||
kind: tree.kind,
|
||||
@@ -1359,7 +1347,7 @@ namespace ts.server {
|
||||
private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
|
||||
return {
|
||||
start: scriptInfo.positionToLineOffset(span.start),
|
||||
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span))
|
||||
end: scriptInfo.positionToLineOffset(textSpanEnd(span))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1389,7 +1377,7 @@ namespace ts.server {
|
||||
return navItems.map((navItem) => {
|
||||
const scriptInfo = project.getScriptInfo(navItem.fileName);
|
||||
const start = scriptInfo.positionToLineOffset(navItem.textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(navItem.textSpan));
|
||||
const end = scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan));
|
||||
const bakedItem: protocol.NavtoItem = {
|
||||
name: navItem.name,
|
||||
kind: navItem.kind,
|
||||
@@ -1454,7 +1442,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getSupportedCodeFixes(): string[] {
|
||||
return ts.getSupportedCodeFixes();
|
||||
return getSupportedCodeFixes();
|
||||
}
|
||||
|
||||
private isLocation(locationOrSpan: protocol.FileLocationOrRangeRequestArgs): locationOrSpan is protocol.FileLocationRequestArgs {
|
||||
@@ -1485,7 +1473,7 @@ namespace ts.server {
|
||||
return project.getLanguageService().getApplicableRefactors(file, position || textRange);
|
||||
}
|
||||
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): ts.RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
|
||||
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const { position, textRange } = this.extractPositionAndRange(args, scriptInfo);
|
||||
@@ -1506,7 +1494,7 @@ namespace ts.server {
|
||||
|
||||
if (simplifiedResult) {
|
||||
const file = result.renameFilename;
|
||||
let location: ILineInfo | undefined = undefined;
|
||||
let location: protocol.Location | undefined;
|
||||
if (file !== undefined && result.renameLocation !== undefined) {
|
||||
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
|
||||
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
|
||||
@@ -1654,7 +1642,7 @@ namespace ts.server {
|
||||
|
||||
getCanonicalFileName(fileName: string) {
|
||||
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
return ts.normalizePath(name);
|
||||
return normalizePath(name);
|
||||
}
|
||||
|
||||
exit() {
|
||||
|
||||
@@ -31,9 +31,9 @@ declare namespace ts.server {
|
||||
|
||||
export interface DiscoverTypings extends TypingInstallerRequest {
|
||||
readonly fileNames: string[];
|
||||
readonly projectRootPath: ts.Path;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly projectRootPath: Path;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly cachePath?: string;
|
||||
readonly kind: "discover";
|
||||
@@ -63,8 +63,8 @@ declare namespace ts.server {
|
||||
}
|
||||
|
||||
export interface SetTypings extends ProjectResponse {
|
||||
readonly typeAcquisition: ts.TypeAcquisition;
|
||||
readonly compilerOptions: ts.CompilerOptions;
|
||||
readonly typeAcquisition: TypeAcquisition;
|
||||
readonly compilerOptions: CompilerOptions;
|
||||
readonly typings: string[];
|
||||
readonly unresolvedImports: SortedReadonlyArray<string>;
|
||||
readonly kind: ActionSet;
|
||||
|
||||
@@ -145,6 +145,7 @@ namespace ts.server.typingsInstaller {
|
||||
|
||||
const discoverTypingsResult = JsTyping.discoverTypings(
|
||||
this.installTypingHost,
|
||||
this.log.isEnabled() ? this.log.writeLine : undefined,
|
||||
req.fileNames,
|
||||
req.projectRootPath,
|
||||
this.safeListPath,
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace ts.server {
|
||||
tabSize: 4,
|
||||
newLineCharacter: host.newLine || "\n",
|
||||
convertTabsToSpaces: true,
|
||||
indentStyle: ts.IndentStyle.Smart,
|
||||
indentStyle: IndentStyle.Smart,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
@@ -196,7 +196,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export function enumerateInsertsAndDeletes<T>(a: SortedReadonlyArray<T>, b: SortedReadonlyArray<T>, inserted: (item: T) => void, deleted: (item: T) => void, compare?: (a: T, b: T) => Comparison) {
|
||||
compare = compare || ts.compareValues;
|
||||
compare = compare || compareValues;
|
||||
let aIndex = 0;
|
||||
let bIndex = 0;
|
||||
const aLen = a.length;
|
||||
|
||||
Reference in New Issue
Block a user