mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 12:51:30 -05:00
Merge branch 'master' into map5
This commit is contained in:
@@ -107,6 +107,7 @@ namespace ts.server {
|
||||
export interface HostConfiguration {
|
||||
formatCodeOptions: FormatCodeSettings;
|
||||
hostInfo: string;
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
||||
interface ConfigFileConversionResult {
|
||||
@@ -131,13 +132,16 @@ namespace ts.server {
|
||||
interface FilePropertyReader<T> {
|
||||
getFileName(f: T): string;
|
||||
getScriptKind(f: T): ScriptKind;
|
||||
hasMixedContent(f: T): boolean;
|
||||
hasMixedContent(f: T, extraFileExtensions: FileExtensionInfo[]): boolean;
|
||||
}
|
||||
|
||||
const fileNamePropertyReader: FilePropertyReader<string> = {
|
||||
getFileName: x => x,
|
||||
getScriptKind: _ => undefined,
|
||||
hasMixedContent: _ => false
|
||||
hasMixedContent: (fileName, extraFileExtensions) => {
|
||||
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
|
||||
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension))
|
||||
}
|
||||
};
|
||||
|
||||
const externalFilePropertyReader: FilePropertyReader<protocol.ExternalFile> = {
|
||||
@@ -282,7 +286,8 @@ namespace ts.server {
|
||||
|
||||
this.hostConfiguration = {
|
||||
formatCodeOptions: getDefaultFormatCodeSettings(this.host),
|
||||
hostInfo: "Unknown host"
|
||||
hostInfo: "Unknown host",
|
||||
extraFileExtensions: []
|
||||
};
|
||||
|
||||
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames, host.getCurrentDirectory());
|
||||
@@ -424,7 +429,7 @@ namespace ts.server {
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
else {
|
||||
if (info && (!info.isOpen)) {
|
||||
if (info && (!info.isScriptOpen())) {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.reloadFromFile();
|
||||
@@ -440,7 +445,7 @@ namespace ts.server {
|
||||
|
||||
// TODO: handle isOpen = true case
|
||||
|
||||
if (!info.isOpen) {
|
||||
if (!info.isScriptOpen()) {
|
||||
this.filenameToScriptInfo.remove(info.path);
|
||||
this.lastDeletedFile = info;
|
||||
|
||||
@@ -486,7 +491,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())) {
|
||||
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -634,15 +639,17 @@ namespace ts.server {
|
||||
// Closing file should trigger re-reading the file content from disk. This is
|
||||
// because the user may chose to discard the buffer content before saving
|
||||
// to the disk, and the server's version of the file can be out of sync.
|
||||
info.reloadFromFile();
|
||||
info.close();
|
||||
|
||||
removeItemFromSet(this.openFiles, info);
|
||||
info.isOpen = false;
|
||||
|
||||
// collect all projects that should be removed
|
||||
let projectsToRemove: Project[];
|
||||
for (const p of info.containingProjects) {
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
if (info.hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
// last open file in configured project - close it
|
||||
if ((<ConfiguredProject>p).deleteOpenRef() === 0) {
|
||||
(projectsToRemove || (projectsToRemove = [])).push(p);
|
||||
@@ -811,7 +818,9 @@ namespace ts.server {
|
||||
this.host,
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
configFilename);
|
||||
configFilename,
|
||||
/*resolutionStack*/ [],
|
||||
this.hostConfiguration.extraFileExtensions);
|
||||
|
||||
if (parsedCommandLine.errors.length) {
|
||||
errors = concatenate(errors, parsedCommandLine.errors);
|
||||
@@ -915,7 +924,7 @@ namespace ts.server {
|
||||
for (const f of files) {
|
||||
const rootFilename = propertyReader.getFileName(f);
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
if (this.host.fileExists(rootFilename)) {
|
||||
const info = this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(rootFilename), /*openedByClient*/ clientFileName == rootFilename, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
project.addRoot(info);
|
||||
@@ -961,7 +970,7 @@ namespace ts.server {
|
||||
rootFilesChanged = true;
|
||||
if (!scriptInfo) {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent);
|
||||
}
|
||||
}
|
||||
@@ -989,7 +998,7 @@ namespace ts.server {
|
||||
}
|
||||
if (toAdd) {
|
||||
for (const f of toAdd) {
|
||||
if (f.isOpen && isRootFileInInferredProject(f)) {
|
||||
if (f.isScriptOpen() && isRootFileInInferredProject(f)) {
|
||||
// if file is already root in some inferred project
|
||||
// - remove the file from that project and delete the project if necessary
|
||||
const inferredProject = f.containingProjects[0];
|
||||
@@ -1089,32 +1098,34 @@ namespace ts.server {
|
||||
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) {
|
||||
let info = this.getScriptInfoForNormalizedPath(fileName);
|
||||
if (!info) {
|
||||
let content: string;
|
||||
if (this.host.fileExists(fileName)) {
|
||||
// by default pick whatever content was supplied as the argument
|
||||
// if argument was not given - then for mixed content files assume that its content is empty string
|
||||
content = fileContent || (hasMixedContent ? "" : this.host.readFile(fileName));
|
||||
}
|
||||
if (!content) {
|
||||
if (openedByClient) {
|
||||
content = "";
|
||||
}
|
||||
}
|
||||
if (content !== undefined) {
|
||||
info = new ScriptInfo(this.host, fileName, content, scriptKind, openedByClient, hasMixedContent);
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (openedByClient || this.host.fileExists(fileName)) {
|
||||
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent);
|
||||
|
||||
this.filenameToScriptInfo.set(info.path, info);
|
||||
if (!info.isOpen && !hasMixedContent) {
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
|
||||
if (openedByClient) {
|
||||
if (fileContent === undefined) {
|
||||
// if file is opened by client and its content is not specified - use file text
|
||||
fileContent = this.host.readFile(fileName) || "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// do not watch files with mixed content - server doesn't know how to interpret it
|
||||
if (!hasMixedContent) {
|
||||
info.setWatcher(this.host.watchFile(fileName, _ => this.onSourceFileChanged(fileName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
if (fileContent !== undefined) {
|
||||
info.reload(fileContent);
|
||||
if (openedByClient && !info.isScriptOpen()) {
|
||||
info.open(fileContent);
|
||||
if (hasMixedContent) {
|
||||
info.registerFileUpdate();
|
||||
}
|
||||
}
|
||||
if (openedByClient) {
|
||||
info.isOpen = true;
|
||||
else if (fileContent !== undefined) {
|
||||
info.reload(fileContent);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
@@ -1146,6 +1157,10 @@ namespace ts.server {
|
||||
mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
|
||||
this.logger.info("Format host information updated");
|
||||
}
|
||||
if (args.extraFileExtensions) {
|
||||
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
|
||||
this.logger.info("Host file extension mappings updated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,7 +1245,6 @@ namespace ts.server {
|
||||
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName));
|
||||
if (info) {
|
||||
this.closeOpenFile(info);
|
||||
info.isOpen = false;
|
||||
}
|
||||
this.printProjects();
|
||||
}
|
||||
@@ -1255,7 +1269,7 @@ namespace ts.server {
|
||||
if (openFiles) {
|
||||
for (const file of openFiles) {
|
||||
const scriptInfo = this.getScriptInfo(file.fileName);
|
||||
Debug.assert(!scriptInfo || !scriptInfo.isOpen);
|
||||
Debug.assert(!scriptInfo || !scriptInfo.isScriptOpen());
|
||||
const normalizedPath = scriptInfo ? scriptInfo.fileName : toNormalizedPath(file.fileName);
|
||||
this.openClientFileWithNormalizedPath(normalizedPath, file.content, tryConvertScriptKindName(file.scriptKind), file.hasMixedContent);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace ts.server {
|
||||
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
|
||||
const scriptInfo = this.project.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.snap();
|
||||
return scriptInfo.getSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="utilities.ts"/>
|
||||
/// <reference path="scriptInfo.ts"/>
|
||||
/// <reference path="lsHost.ts"/>
|
||||
@@ -187,6 +187,10 @@ namespace ts.server {
|
||||
public languageServiceEnabled = true;
|
||||
|
||||
builder: Builder;
|
||||
/**
|
||||
* Set of files names that were updated since the last call to getChangesSinceVersion.
|
||||
*/
|
||||
private updatedFileNames: Map<string>;
|
||||
/**
|
||||
* Set of files that was returned from the last call to getChangesSinceVersion.
|
||||
*/
|
||||
@@ -448,7 +452,7 @@ namespace ts.server {
|
||||
|
||||
containsFile(filename: NormalizedPath, requireOpen?: boolean) {
|
||||
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
|
||||
if (info && (info.isOpen || !requireOpen)) {
|
||||
if (info && (info.isScriptOpen() || !requireOpen)) {
|
||||
return this.containsScriptInfo(info);
|
||||
}
|
||||
}
|
||||
@@ -480,6 +484,10 @@ namespace ts.server {
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
registerFileUpdate(fileName: string) {
|
||||
(this.updatedFileNames || (this.updatedFileNames = createMap<string>())).set(fileName, fileName);
|
||||
}
|
||||
|
||||
markAsDirty() {
|
||||
this.projectStateVersion++;
|
||||
}
|
||||
@@ -667,10 +675,12 @@ namespace ts.server {
|
||||
isInferred: this.projectKind === ProjectKind.Inferred,
|
||||
options: this.getCompilerOptions()
|
||||
};
|
||||
const updatedFileNames = this.updatedFileNames;
|
||||
this.updatedFileNames = undefined;
|
||||
// check if requested version is the same that we have reported last time
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info witout any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info without any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion && !updatedFileNames) {
|
||||
return { info, projectErrors: this.projectErrors };
|
||||
}
|
||||
// compute and return the difference
|
||||
@@ -679,6 +689,8 @@ namespace ts.server {
|
||||
|
||||
const added: string[] = [];
|
||||
const removed: string[] = [];
|
||||
const updated: string[] = keysOfMap(updatedFileNames);
|
||||
|
||||
forEachKeyInMap(currentFiles, id => {
|
||||
if (!lastReportedFileNames.has(id)) {
|
||||
added.push(id);
|
||||
@@ -691,7 +703,7 @@ namespace ts.server {
|
||||
});
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, changes: { added, removed }, projectErrors: this.projectErrors };
|
||||
return { info, changes: { added, removed, updated }, projectErrors: this.projectErrors };
|
||||
}
|
||||
else {
|
||||
// unknown version - return everything
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Declaration module describing the TypeScript Server protocol
|
||||
*/
|
||||
namespace ts.server.protocol {
|
||||
@@ -918,6 +918,10 @@ namespace ts.server.protocol {
|
||||
* List of removed files
|
||||
*/
|
||||
removed: string[];
|
||||
/**
|
||||
* List of updated files
|
||||
*/
|
||||
updated: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -990,6 +994,11 @@ namespace ts.server.protocol {
|
||||
* The format options to use during formatting and other code editing features.
|
||||
*/
|
||||
formatOptions?: FormatCodeSettings;
|
||||
|
||||
/**
|
||||
* The host's additional supported file extensions
|
||||
*/
|
||||
extraFileExtensions?: FileExtensionInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,161 @@
|
||||
|
||||
namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
export class TextStorage {
|
||||
private svc: ScriptVersionCache | undefined;
|
||||
private svcVersion = 0;
|
||||
|
||||
private text: string;
|
||||
private lineMap: number[];
|
||||
private textVersion = 0;
|
||||
|
||||
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath) {
|
||||
}
|
||||
|
||||
public getVersion() {
|
||||
return this.svc
|
||||
? `SVC-${this.svcVersion}-${this.svc.getSnapshot().version}`
|
||||
: `Text-${this.textVersion}`;
|
||||
}
|
||||
|
||||
public hasScriptVersionCache() {
|
||||
return this.svc !== undefined;
|
||||
}
|
||||
|
||||
public useScriptVersionCache(newText?: string) {
|
||||
this.switchToScriptVersionCache(newText);
|
||||
}
|
||||
|
||||
public useText(newText?: string) {
|
||||
this.svc = undefined;
|
||||
this.setText(newText);
|
||||
}
|
||||
|
||||
public edit(start: number, end: number, newText: string) {
|
||||
this.switchToScriptVersionCache().edit(start, end - start, newText);
|
||||
}
|
||||
|
||||
public reload(text: string) {
|
||||
if (this.svc) {
|
||||
this.svc.reload(text);
|
||||
}
|
||||
else {
|
||||
this.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
public reloadFromFile(tempFileName?: string) {
|
||||
if (this.svc || (tempFileName !== this.fileName)) {
|
||||
this.reload(this.getFileText(tempFileName))
|
||||
}
|
||||
else {
|
||||
this.setText(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public getSnapshot(): IScriptSnapshot {
|
||||
return this.svc
|
||||
? this.svc.getSnapshot()
|
||||
: ScriptSnapshot.fromString(this.getOrLoadText());
|
||||
}
|
||||
|
||||
public getLineInfo(line: number) {
|
||||
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
|
||||
}
|
||||
/**
|
||||
* @param line 0 based index
|
||||
*/
|
||||
lineToTextSpan(line: number) {
|
||||
if (!this.svc) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1 based index
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
if (!this.svc) {
|
||||
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1-based index
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
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 };
|
||||
}
|
||||
|
||||
private getFileText(tempFileName?: string) {
|
||||
return this.host.readFile(tempFileName || this.fileName) || "";
|
||||
}
|
||||
|
||||
private ensureNoScriptVersionCache() {
|
||||
Debug.assert(!this.svc, "ScriptVersionCache should not be set");
|
||||
}
|
||||
|
||||
private switchToScriptVersionCache(newText?: string): ScriptVersionCache {
|
||||
if (!this.svc) {
|
||||
this.svc = ScriptVersionCache.fromString(this.host, newText !== undefined ? newText : this.getOrLoadText());
|
||||
this.svcVersion++;
|
||||
this.text = undefined;
|
||||
}
|
||||
return this.svc;
|
||||
}
|
||||
|
||||
private getOrLoadText() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (this.text === undefined) {
|
||||
this.setText(this.getFileText());
|
||||
}
|
||||
return this.text;
|
||||
}
|
||||
|
||||
private getLineMap() {
|
||||
this.ensureNoScriptVersionCache();
|
||||
return this.lineMap || (this.lineMap = computeLineStarts(this.getOrLoadText()));
|
||||
}
|
||||
|
||||
private setText(newText: string) {
|
||||
this.ensureNoScriptVersionCache();
|
||||
if (newText === undefined || this.text !== newText) {
|
||||
this.text = newText;
|
||||
this.lineMap = undefined;
|
||||
this.textVersion++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ScriptInfo {
|
||||
/**
|
||||
* All projects that include this file
|
||||
@@ -11,24 +166,46 @@ namespace ts.server {
|
||||
readonly path: Path;
|
||||
|
||||
private fileWatcher: FileWatcher;
|
||||
private svc: ScriptVersionCache;
|
||||
private textStorage: TextStorage;
|
||||
|
||||
private isOpen: boolean;
|
||||
|
||||
// TODO: allow to update hasMixedContent from the outside
|
||||
constructor(
|
||||
private readonly host: ServerHost,
|
||||
readonly fileName: NormalizedPath,
|
||||
content: string,
|
||||
readonly scriptKind: ScriptKind,
|
||||
public isOpen = false,
|
||||
public hasMixedContent = false) {
|
||||
|
||||
this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames));
|
||||
this.svc = ScriptVersionCache.fromString(host, content);
|
||||
this.textStorage = new TextStorage(host, fileName);
|
||||
if (hasMixedContent) {
|
||||
this.textStorage.reload("");
|
||||
}
|
||||
this.scriptKind = scriptKind
|
||||
? scriptKind
|
||||
: getScriptKindFromFileName(fileName);
|
||||
}
|
||||
|
||||
public isScriptOpen() {
|
||||
return this.isOpen;
|
||||
}
|
||||
|
||||
public open(newText: string) {
|
||||
this.isOpen = true;
|
||||
this.textStorage.useScriptVersionCache(newText);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isOpen = false;
|
||||
this.textStorage.useText(this.hasMixedContent ? "" : undefined);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
public getSnapshot() {
|
||||
return this.textStorage.getSnapshot();
|
||||
}
|
||||
|
||||
getFormatCodeSettings() {
|
||||
return this.formatCodeSettings;
|
||||
}
|
||||
@@ -90,6 +267,12 @@ namespace ts.server {
|
||||
return this.containingProjects[0];
|
||||
}
|
||||
|
||||
registerFileUpdate(): void {
|
||||
for (const p of this.containingProjects) {
|
||||
p.registerFileUpdate(this.path);
|
||||
}
|
||||
}
|
||||
|
||||
setFormatOptions(formatSettings: FormatCodeSettings): void {
|
||||
if (formatSettings) {
|
||||
if (!this.formatCodeSettings) {
|
||||
@@ -112,16 +295,16 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getLatestVersion() {
|
||||
return this.svc.latestVersion().toString();
|
||||
return this.textStorage.getVersion();
|
||||
}
|
||||
|
||||
reload(script: string) {
|
||||
this.svc.reload(script);
|
||||
this.textStorage.reload(script);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
saveTo(fileName: string) {
|
||||
const snap = this.snap();
|
||||
const snap = this.textStorage.getSnapshot();
|
||||
this.host.writeFile(fileName, snap.getText(0, snap.getLength()));
|
||||
}
|
||||
|
||||
@@ -130,22 +313,17 @@ namespace ts.server {
|
||||
this.reload("");
|
||||
}
|
||||
else {
|
||||
this.svc.reloadFromFile(tempFileName || this.fileName);
|
||||
this.textStorage.reloadFromFile(tempFileName);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
snap() {
|
||||
return this.svc.getSnapshot();
|
||||
}
|
||||
|
||||
getLineInfo(line: number) {
|
||||
const snap = this.snap();
|
||||
return snap.index.lineNumberToInfo(line);
|
||||
return this.textStorage.getLineInfo(line);
|
||||
}
|
||||
|
||||
editContent(start: number, end: number, newText: string): void {
|
||||
this.svc.edit(start, end - start, newText);
|
||||
this.textStorage.edit(start, end, newText);
|
||||
this.markContainingProjectsAsDirty();
|
||||
}
|
||||
|
||||
@@ -159,17 +337,7 @@ namespace ts.server {
|
||||
* @param line 1 based index
|
||||
*/
|
||||
lineToTextSpan(line: number) {
|
||||
const index = this.snap().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);
|
||||
return this.textStorage.lineToTextSpan(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,11 +345,7 @@ namespace ts.server {
|
||||
* @param offset 1 based index
|
||||
*/
|
||||
lineOffsetToPosition(line: number, offset: number): number {
|
||||
const index = this.snap().index;
|
||||
|
||||
const lineInfo = index.lineNumberToInfo(line);
|
||||
// TODO: assert this offset is actually on the line
|
||||
return (lineInfo.offset + offset - 1);
|
||||
return this.textStorage.lineOffsetToPosition(line, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,9 +353,7 @@ namespace ts.server {
|
||||
* @param offset 1-based index
|
||||
*/
|
||||
positionToLineOffset(position: number): ILineInfo {
|
||||
const index = this.snap().index;
|
||||
const lineOffset = index.charOffsetToLineNumberAndPos(position);
|
||||
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
|
||||
return this.textStorage.positionToLineOffset(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,8 +438,9 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
|
||||
const oldSnap = <LineIndexSnapshot>oldSnapshot;
|
||||
return this.getTextChangeRangeSinceVersion(oldSnap.version);
|
||||
if (oldSnapshot instanceof LineIndexSnapshot) {
|
||||
return this.getTextChangeRangeSinceVersion(oldSnapshot.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -709,7 +709,7 @@ namespace ts.server {
|
||||
const displayString = ts.displayPartsToString(nameInfo.displayParts);
|
||||
const nameSpan = nameInfo.textSpan;
|
||||
const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset;
|
||||
const nameText = scriptInfo.snap().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
|
||||
const refs = combineProjectOutput<protocol.ReferencesResponseItem>(
|
||||
projects,
|
||||
(project: Project) => {
|
||||
@@ -722,7 +722,7 @@ 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.snap().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: ref.fileName,
|
||||
start: start,
|
||||
@@ -1326,7 +1326,7 @@ namespace ts.server {
|
||||
highPriorityFiles.push(fileNameInProject);
|
||||
else {
|
||||
const info = this.projectService.getScriptInfo(fileNameInProject);
|
||||
if (!info.isOpen) {
|
||||
if (!info.isScriptOpen()) {
|
||||
if (fileNameInProject.indexOf(".d.ts") > 0)
|
||||
veryLowPriorityFiles.push(fileNameInProject);
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="types.d.ts" />
|
||||
/// <reference path="types.d.ts" />
|
||||
/// <reference path="shared.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
|
||||
Reference in New Issue
Block a user