Merge pull request #1735 from Microsoft/LSAPICleanup

Language Service host API cleanup
This commit is contained in:
Mohamed Hegazy 2015-02-02 19:04:30 -08:00
commit bbbec22917
26 changed files with 33902 additions and 926 deletions

View File

@ -644,6 +644,10 @@ module ts {
}
}
export function getDefaultLibFilename(options: CompilerOptions): string {
return options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts";
}
export interface ObjectAllocator {
getNodeConstructor(kind: SyntaxKind): new () => Node;
getSymbolConstructor(): new (flags: SymbolFlags, name: string) => Symbol;

View File

@ -64,7 +64,7 @@ module ts {
return {
getSourceFile,
getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts"),
getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), getDefaultLibFilename(options)),
writeFile,
getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
@ -174,7 +174,7 @@ module ts {
}
var diagnostic: DiagnosticMessage;
if (hasExtension(filename)) {
if (!options.allowNonTsExtensions && !fileExtensionIs(filename, ".ts")) {
if (!options.allowNonTsExtensions && !fileExtensionIs(host.getCanonicalFileName(filename), ".ts")) {
diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts;
}
else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) {

View File

@ -237,9 +237,6 @@ module FourSlash {
getLength: () => {
return sourceText.length;
},
getLineStartPositions: () => {
return <number[]>[];
},
getChangeRange: (oldSnapshot: ts.IScriptSnapshot) => {
return <ts.TextChangeRange>undefined;
}
@ -1403,7 +1400,7 @@ module FourSlash {
var content = snapshot.getText(0, snapshot.getLength());
var referenceSourceFile = ts.createLanguageServiceSourceFile(
this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*isOpen:*/ false, /*setNodeParents:*/ false);
this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false);
var referenceSyntaxDiagnostics = referenceSourceFile.getSyntacticDiagnostics();
Utils.assertDiagnosticsEquals(incrementalSyntaxDiagnostics, referenceSyntaxDiagnostics);

View File

@ -930,6 +930,8 @@ module Harness {
settingsCallback(null);
}
var newLine = '\r\n';
var useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
this.settings.forEach(setting => {
switch (setting.flag.toLowerCase()) {
@ -1008,7 +1010,7 @@ module Harness {
case 'newline':
case 'newlines':
ts.sys.newLine = setting.value;
newLine = setting.value;
break;
case 'comments':
@ -1050,7 +1052,7 @@ module Harness {
break;
case 'includebuiltfile':
inputFiles.push({ unitName: setting.value, content: IO.readFile(libFolder + setting.value) });
inputFiles.push({ unitName: setting.value, content: normalizeLineEndings(IO.readFile(libFolder + setting.value), newLine) });
break;
default:
@ -1096,7 +1098,7 @@ module Harness {
onComplete(result, program);
// reset what newline means in case the last test changed it
ts.sys.newLine = '\r\n';
ts.sys.newLine = newLine;
return options;
}
@ -1168,6 +1170,14 @@ module Harness {
}
}
function normalizeLineEndings(text: string, lineEnding: string): string {
var normalized = text.replace(/\r\n?/g, '\n');
if (lineEnding !== '\n') {
normalized = normalized.replace(/\n/g, lineEnding);
}
return normalized;
}
export function getMinimalDiagnostic(err: ts.Diagnostic): HarnessDiagnostic {
var errorLineInfo = err.file ? err.file.getLineAndCharacterFromPosition(err.start) : { line: 0, character: 0 };
return {

View File

@ -7,7 +7,7 @@ module Harness.LanguageService {
public editRanges: { length: number; textChangeRange: ts.TextChangeRange; }[] = [];
public lineMap: number[] = null;
constructor(public fileName: string, public content: string, public isOpen = true) {
constructor(public fileName: string, public content: string) {
this.setContent(content);
}
@ -72,14 +72,6 @@ module Harness.LanguageService {
return this.textSnapshot.length;
}
public getLineStartPositions(): string {
if (this.lineMap === null) {
this.lineMap = ts.computeLineStarts(this.textSnapshot);
}
return JSON.stringify(this.lineMap);
}
public getChangeRange(oldScript: ts.ScriptSnapshotShim): string {
var oldShim = <ScriptSnapshotShim>oldScript;
var range = this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version);
@ -109,11 +101,9 @@ module Harness.LanguageService {
fileName: string,
compilationSettings: ts.CompilerOptions,
scriptSnapshot: ts.IScriptSnapshot,
version: string,
isOpen: boolean): ts.SourceFile {
version: string): ts.SourceFile {
var sourceFile = ts.createSourceFile(fileName, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target);
sourceFile.version = version;
sourceFile.isOpen = isOpen;
return sourceFile;
}
@ -123,10 +113,9 @@ module Harness.LanguageService {
compilationSettings: ts.CompilerOptions,
scriptSnapshot: ts.IScriptSnapshot,
version: string,
isOpen: boolean,
textChangeRange: ts.TextChangeRange
): ts.SourceFile {
return ts.updateLanguageServiceSourceFile(document, scriptSnapshot, version, isOpen, textChangeRange);
return ts.updateLanguageServiceSourceFile(document, scriptSnapshot, version, textChangeRange);
}
public releaseDocument(fileName: string, compilationSettings: ts.CompilerOptions): void {
@ -159,13 +148,17 @@ module Harness.LanguageService {
}
private getScriptInfo(fileName: string): ScriptInfo {
return this.fileNameToScript[fileName];
return ts.lookUp(this.fileNameToScript, fileName);
}
public addScript(fileName: string, content: string) {
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content);
}
private contains(fileName: string): boolean {
return ts.hasProperty(this.fileNameToScript, fileName);
}
public updateScript(fileName: string, content: string) {
var script = this.getScriptInfo(fileName);
if (script !== null) {
@ -223,20 +216,22 @@ module Harness.LanguageService {
public getScriptFileNames(): string {
var fileNames: string[] = [];
ts.forEachKey(this.fileNameToScript, (fileName) => { fileNames.push(fileName); });
ts.forEachKey(this.fileNameToScript,(fileName) => { fileNames.push(fileName); });
return JSON.stringify(fileNames);
}
public getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim {
return new ScriptSnapshotShim(this.getScriptInfo(fileName));
if (this.contains(fileName)) {
return new ScriptSnapshotShim(this.getScriptInfo(fileName));
}
return undefined;
}
public getScriptVersion(fileName: string): string {
return this.getScriptInfo(fileName).version.toString();
}
public getScriptIsOpen(fileName: string): boolean {
return this.getScriptInfo(fileName).isOpen;
if (this.contains(fileName)) {
return this.getScriptInfo(fileName).version.toString();
}
return undefined;
}
public getLocalizedDiagnosticMessages(): string {
@ -272,7 +267,6 @@ module Harness.LanguageService {
public parseSourceText(fileName: string, sourceText: ts.IScriptSnapshot): ts.SourceFile {
var result = ts.createSourceFile(fileName, sourceText.getText(0, sourceText.getLength()), ts.ScriptTarget.Latest);
result.version = "1";
result.isOpen = true;
return result;
}

View File

@ -22,7 +22,7 @@ module ts.formatting {
private activeRules: Rule[];
private rulesMap: RulesMap;
constructor(private logger: Logger) {
constructor() {
this.globalRules = new Rules();
}

View File

@ -9,6 +9,7 @@
/// <reference path='formatting\smartIndenter.ts' />
module ts {
export var servicesVersion = "0.5"
export interface Node {
@ -56,7 +57,6 @@ module ts {
}
export interface SourceFile {
isOpen: boolean;
version: string;
scriptSnapshot: IScriptSnapshot;
nameTable: Map<string>;
@ -75,13 +75,6 @@ module ts {
/** Gets the length of this script snapshot. */
getLength(): number;
/**
* This call returns the array containing the start position of every line.
* i.e."[0, 10, 55]". TODO: consider making this optional. The language service could
* always determine this (albeit in a more expensive manner).
*/
getLineStartPositions(): number[];
/**
* Gets the TextChangeRange that describe how the text changed between this text and
* an older version. This information is used by the incremental parser to determine
@ -107,16 +100,10 @@ module ts {
return this.text.length;
}
public getLineStartPositions(): number[] {
if (!this._lineStartPositions) {
this._lineStartPositions = computeLineStarts(this.text);
}
return this._lineStartPositions;
}
public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
throw new Error("not yet implemented");
// Text-based snapshots do not support incremental parsing. Return undefined
// to signal that to the caller.
return undefined;
}
}
@ -755,7 +742,6 @@ module ts {
public identifierCount: number;
public symbolCount: number;
public version: string;
public isOpen: boolean;
public languageVersion: ScriptTarget;
public identifiers: Map<string>;
public nameTable: Map<string>;
@ -850,26 +836,22 @@ module ts {
}
}
export interface Logger {
log(s: string): void;
trace(s: string): void;
error(s: string): void;
}
//
// Public interface of the host of a language service instance.
//
export interface LanguageServiceHost extends Logger {
export interface LanguageServiceHost {
getCompilationSettings(): CompilerOptions;
getNewLine?(): string;
getScriptFileNames(): string[];
getScriptVersion(fileName: string): string;
getScriptIsOpen(fileName: string): boolean;
getScriptSnapshot(fileName: string): IScriptSnapshot;
getLocalizedDiagnosticMessages?(): any;
getCancellationToken?(): CancellationToken;
getCurrentDirectory(): string;
getDefaultLibFilename(options: CompilerOptions): string;
log? (s: string): void;
trace? (s: string): void;
error? (s: string): void;
}
//
@ -918,6 +900,8 @@ module ts {
getEmitOutput(fileName: string): EmitOutput;
getProgram(): Program;
getSourceFile(filename: string): SourceFile;
dispose(): void;
@ -1172,24 +1156,79 @@ module ts {
getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): ClassificationResult;
}
/**
* The document registry represents a store of SourceFile objects that can be shared between
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
* of files in the context.
* SourceFile objects account for most of the memory usage by the language service. Sharing
* the same DocumentRegistry instance between different instances of LanguageService allow
* for more efficient memory utilization since all projects will share at least the library
* file (lib.d.ts).
*
* A more advanced use of the document registry is to serialize sourceFile objects to disk
* and re-hydrate them when needed.
*
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
* to all subsequent createLanguageService calls.
*/
export interface DocumentRegistry {
/**
* Request a stored SourceFile with a given filename and compilationSettings.
* The first call to acquire will call createLanguageServiceSourceFile to generate
* the SourceFile if was not found in the registry.
*
* @param filename The name of the file requested
* @param compilationSettings Some compilation settings like target affects the
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
* multiple copies of the same file for different compilation settings.
* @parm scriptSnapshot Text of the file. Only used if the file was not found
* in the registry and a new one was created.
* @parm version Current version of the file. Only used if the file was not found
* in the registry and a new one was created.
*/
acquireDocument(
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: IScriptSnapshot,
version: string,
isOpen: boolean): SourceFile;
version: string): SourceFile;
/**
* Request an updated version of an already existing SourceFile with a given filename
* and compilationSettings. The update will intern call updateLanguageServiceSourceFile
* to get an updated SourceFile.
*
* Note: It is not allowed to call update on a SourceFile that was not acquired from this
* registry originally.
*
* @param sourceFile The original sourceFile object to update
* @param filename The name of the file requested
* @param compilationSettings Some compilation settings like target affects the
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
* multiple copies of the same file for different compilation settings.
* @parm scriptSnapshot Text of the file. Only used if the file was not found
* in the registry and a new one was created.
* @parm version Current version of the file. Only used if the file was not found
* in the registry and a new one was created.
* @parm textChangeRange Change ranges since the last snapshot. Only used if the file
* was not found in the registry and a new one was created.
*/
updateDocument(
sourceFile: SourceFile,
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: IScriptSnapshot,
version: string,
isOpen: boolean,
textChangeRange: TextChangeRange
): SourceFile;
textChangeRange: TextChangeRange): SourceFile;
/**
* Informs the DocumentRegistry that a file is not needed any longer.
*
* Note: It is not allowed to call release on a SourceFile that was not acquired from
* this registry originally.
*
* @param filename The name of the file to be released
* @param compilationSettings The compilation settings used to acquire the file
*/
releaseDocument(filename: string, compilationSettings: CompilerOptions): void
}
@ -1327,10 +1366,9 @@ module ts {
// Information about a specific host file.
interface HostFileInformation {
filename: string;
hostFilename: string;
version: string;
isOpen: boolean;
sourceText?: IScriptSnapshot;
scriptSnapshot: IScriptSnapshot;
}
interface DocumentRegistryEntry {
@ -1418,16 +1456,13 @@ module ts {
// script id => script index
this.filenameToEntry = {};
var filenames = host.getScriptFileNames();
for (var i = 0, n = filenames.length; i < n; i++) {
var filename = filenames[i];
this.filenameToEntry[normalizeSlashes(filename)] = {
filename: filename,
version: host.getScriptVersion(filename),
isOpen: host.getScriptIsOpen(filename)
};
// Initialize the list with the root file names
var rootFilenames = host.getScriptFileNames();
for (var i = 0, n = rootFilenames.length; i < n; i++) {
this.createEntry(rootFilenames[i]);
}
// store the compilation settings
this._compilationSettings = host.getCompilationSettings() || getDefaultCompilerOptions();
}
@ -1435,28 +1470,41 @@ module ts {
return this._compilationSettings;
}
private createEntry(filename: string) {
var entry: HostFileInformation;
var scriptSnapshot = this.host.getScriptSnapshot(filename);
if (scriptSnapshot) {
entry = {
hostFilename: filename,
version: this.host.getScriptVersion(filename),
scriptSnapshot: scriptSnapshot
};
}
return this.filenameToEntry[normalizeSlashes(filename)] = entry;
}
public getEntry(filename: string): HostFileInformation {
filename = normalizeSlashes(filename);
return lookUp(this.filenameToEntry, filename);
return lookUp(this.filenameToEntry, normalizeSlashes(filename));
}
public contains(filename: string): boolean {
return !!this.getEntry(filename);
return hasProperty(this.filenameToEntry, normalizeSlashes(filename));
}
public getHostfilename(filename: string) {
var hostCacheEntry = this.getEntry(filename);
if (hostCacheEntry) {
return hostCacheEntry.filename;
public getOrCreateEntry(filename: string): HostFileInformation {
if (this.contains(filename)) {
return this.getEntry(filename);
}
return filename;
return this.createEntry(filename);
}
public getFilenames(): string[] {
public getRootFilenames(): string[] {
var fileNames: string[] = [];
forEachKey(this.filenameToEntry, key => {
if (hasProperty(this.filenameToEntry, key))
if (hasProperty(this.filenameToEntry, key) && this.filenameToEntry[key])
fileNames.push(key);
});
@ -1464,19 +1512,13 @@ module ts {
}
public getVersion(filename: string): string {
return this.getEntry(filename).version;
}
public isOpen(filename: string): boolean {
return this.getEntry(filename).isOpen;
var file = this.getEntry(filename);
return file && file.version;
}
public getScriptSnapshot(filename: string): IScriptSnapshot {
var file = this.getEntry(filename);
if (!file.sourceText) {
file.sourceText = this.host.getScriptSnapshot(file.filename);
}
return file.sourceText;
return file && file.scriptSnapshot;
}
public getChangeRange(filename: string, lastKnownVersion: string, oldScriptSnapshot: IScriptSnapshot): TextChangeRange {
@ -1502,11 +1544,17 @@ module ts {
constructor(private host: LanguageServiceHost) {
}
private log(message: string) {
if (this.host.log) {
this.host.log(message);
}
}
private initialize(filename: string) {
// ensure that both source file and syntax tree are either initialized or not initialized
var start = new Date().getTime();
this.hostCache = new HostCache(this.host);
this.host.log("SyntaxTreeCache.Initialize: new HostCache: " + (new Date().getTime() - start));
this.log("SyntaxTreeCache.Initialize: new HostCache: " + (new Date().getTime() - start));
var version = this.hostCache.getVersion(filename);
var sourceFile: SourceFile;
@ -1515,8 +1563,8 @@ module ts {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
var start = new Date().getTime();
sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, ScriptTarget.Latest, version, /*isOpen*/ true, /*setNodeParents;*/ true);
this.host.log("SyntaxTreeCache.Initialize: createSourceFile: " + (new Date().getTime() - start));
sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents:*/ true);
this.log("SyntaxTreeCache.Initialize: createSourceFile: " + (new Date().getTime() - start));
}
else if (this.currentFileVersion !== version) {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
@ -1524,8 +1572,8 @@ module ts {
var editRange = this.hostCache.getChangeRange(filename, this.currentFileVersion, this.currentSourceFile.scriptSnapshot);
var start = new Date().getTime();
sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, /*isOpen*/ true, editRange);
this.host.log("SyntaxTreeCache.Initialize: updateSourceFile: " + (new Date().getTime() - start));
sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange);
this.log("SyntaxTreeCache.Initialize: updateSourceFile: " + (new Date().getTime() - start));
}
if (sourceFile) {
@ -1546,15 +1594,14 @@ module ts {
}
}
function setSourceFileFields(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, isOpen: boolean) {
function setSourceFileFields(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string) {
sourceFile.version = version;
sourceFile.isOpen = isOpen;
sourceFile.scriptSnapshot = scriptSnapshot;
}
export function createLanguageServiceSourceFile(filename: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, isOpen: boolean, setNodeParents: boolean): SourceFile {
export function createLanguageServiceSourceFile(filename: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile {
var sourceFile = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), scriptTarget, setNodeParents);
setSourceFileFields(sourceFile, scriptSnapshot, version, isOpen);
setSourceFileFields(sourceFile, scriptSnapshot, version);
// after full parsing we can use table with interned strings as name table
sourceFile.nameTable = sourceFile.identifiers;
return sourceFile;
@ -1562,7 +1609,7 @@ module ts {
export var disableIncrementalParsing = false;
export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, isOpen: boolean, textChangeRange: TextChangeRange): SourceFile {
export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange): SourceFile {
if (textChangeRange && Debug.shouldAssert(AssertionLevel.Normal)) {
var oldText = sourceFile.scriptSnapshot;
var newText = scriptSnapshot;
@ -1583,11 +1630,11 @@ module ts {
// If we were given a text change range, and our version or open-ness changed, then
// incrementally parse this file.
if (textChangeRange) {
if (version !== sourceFile.version || isOpen != sourceFile.isOpen) {
if (version !== sourceFile.version) {
// Once incremental parsing is ready, then just call into this function.
if (!disableIncrementalParsing) {
var newSourceFile = sourceFile.update(scriptSnapshot.getText(0, scriptSnapshot.getLength()), textChangeRange);
setSourceFileFields(newSourceFile, scriptSnapshot, version, isOpen);
setSourceFileFields(newSourceFile, scriptSnapshot, version);
// after incremental parsing nameTable might not be up-to-date
// drop it so it can be lazily recreated later
newSourceFile.nameTable = undefined;
@ -1597,7 +1644,7 @@ module ts {
}
// Otherwise, just create a new source file.
return createLanguageServiceSourceFile(sourceFile.filename, scriptSnapshot, sourceFile.languageVersion, version, isOpen, /*setNodeParents:*/ true);
return createLanguageServiceSourceFile(sourceFile.filename, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents:*/ true);
}
export function createDocumentRegistry(): DocumentRegistry {
@ -1641,13 +1688,12 @@ module ts {
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: IScriptSnapshot,
version: string,
isOpen: boolean): SourceFile {
version: string): SourceFile {
var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
var entry = lookUp(bucket, filename);
if (!entry) {
var sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, compilationSettings.target, version, isOpen, /*setNodeParents:*/ false);
var sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, compilationSettings.target, version, /*setNodeParents:*/ false);
bucket[filename] = entry = {
sourceFile: sourceFile,
@ -1666,7 +1712,6 @@ module ts {
compilationSettings: CompilerOptions,
scriptSnapshot: IScriptSnapshot,
version: string,
isOpen: boolean,
textChangeRange: TextChangeRange
): SourceFile {
@ -1675,7 +1720,7 @@ module ts {
var entry = lookUp(bucket, filename);
Debug.assert(entry !== undefined);
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, isOpen, textChangeRange);
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, textChangeRange);
return entry.sourceFile;
}
@ -1926,18 +1971,14 @@ module ts {
});
}
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry): LanguageService {
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry()): LanguageService {
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
var ruleProvider: formatting.RulesProvider;
var hostCache: HostCache; // A cache of all the information about the files on the host side.
var program: Program;
// this checker is used to answer all LS questions except errors
var typeInfoResolver: TypeChecker;
var useCaseSensitivefilenames = false;
var sourceFilesByName: Map<SourceFile> = {};
var documentRegistry = documentRegistry;
var cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
var activeCompletionSession: CompletionSession; // The current active completion session, used to get the completion entry details
@ -1946,12 +1987,22 @@ module ts {
localizedDiagnosticMessages = host.getLocalizedDiagnosticMessages();
}
function log(message: string) {
if (host.log) {
host.log(message);
}
}
function getCanonicalFileName(filename: string) {
return useCaseSensitivefilenames ? filename : filename.toLowerCase();
}
function getSourceFile(filename: string): SourceFile {
return lookUp(sourceFilesByName, getCanonicalFileName(filename));
function getValidSourceFile(filename: string): SourceFile {
var sourceFile = program.getSourceFile(getCanonicalFileName(filename));
if (!sourceFile) {
throw new Error("Could not find file: '" + filename + "'.");
}
return sourceFile;
}
function getDiagnosticsProducingTypeChecker() {
@ -1961,142 +2012,121 @@ module ts {
function getRuleProvider(options: FormatCodeOptions) {
// Ensure rules are initialized and up to date wrt to formatting options
if (!ruleProvider) {
ruleProvider = new formatting.RulesProvider(host);
ruleProvider = new formatting.RulesProvider();
}
ruleProvider.ensureUpToDate(options);
return ruleProvider;
}
function createCompilerHost(): CompilerHost {
return {
getSourceFile: (filename, languageVersion) => {
var sourceFile = getSourceFile(filename);
return sourceFile && sourceFile.getSourceFile();
},
getCancellationToken: () => cancellationToken,
getCanonicalFileName: (filename) => useCaseSensitivefilenames ? filename : filename.toLowerCase(),
useCaseSensitiveFileNames: () => useCaseSensitivefilenames,
getNewLine: () => {
return host.getNewLine ? host.getNewLine() : "\r\n";
},
getDefaultLibFilename: (options): string => {
return host.getDefaultLibFilename(options);
},
writeFile: (filename, data, writeByteOrderMark) => {
},
getCurrentDirectory: (): string => {
return host.getCurrentDirectory();
}
};
}
function sourceFileUpToDate(sourceFile: SourceFile): boolean {
return sourceFile && sourceFile.version === hostCache.getVersion(sourceFile.filename) && sourceFile.isOpen === hostCache.isOpen(sourceFile.filename);
}
function programUpToDate(): boolean {
// If we haven't create a program yet, then it is not up-to-date
if (!program) {
return false;
}
// If number of files in the program do not match, it is not up-to-date
var hostFilenames = hostCache.getFilenames();
if (program.getSourceFiles().length !== hostFilenames.length) {
return false;
}
// If any file is not up-to-date, then the whole program is not up-to-date
for (var i = 0, n = hostFilenames.length; i < n; i++) {
if (!sourceFileUpToDate(program.getSourceFile(hostFilenames[i]))) {
return false;
}
}
// If the compilation settings do no match, then the program is not up-to-date
return compareDataObjects(program.getCompilerOptions(), hostCache.compilationSettings());
}
function synchronizeHostData(): void {
// Reset the cache at start of every refresh
hostCache = new HostCache(host);
// Get a fresh cache of the host information
var hostCache = new HostCache(host);
// If the program is already up-to-date, we can reuse it
if (programUpToDate()) {
return;
}
var compilationSettings = hostCache.compilationSettings();
// Now, remove any files from the compiler that are no longer in the host.
var oldProgram = program;
if (oldProgram) {
var oldSettings = program.getCompilerOptions();
// If the language version changed, then that affects what types of things we parse. So
// we have to dump all syntax trees.
// TODO: handle propagateEnumConstants
// TODO: is module still needed
var settingsChangeAffectsSyntax = oldSettings.target !== compilationSettings.target || oldSettings.module !== compilationSettings.module;
var changesInCompilationSettingsAffectSyntax =
oldSettings && compilationSettings && !compareDataObjects(oldSettings, compilationSettings) && settingsChangeAffectsSyntax;
var oldSourceFiles = program.getSourceFiles();
for (var i = 0, n = oldSourceFiles.length; i < n; i++) {
cancellationToken.throwIfCancellationRequested();
var filename = oldSourceFiles[i].filename;
if (!hostCache.contains(filename) || changesInCompilationSettingsAffectSyntax) {
documentRegistry.releaseDocument(filename, oldSettings);
delete sourceFilesByName[getCanonicalFileName(filename)];
}
}
}
// Now, for every file the host knows about, either add the file (if the compiler
// doesn't know about it.). Or notify the compiler about any changes (if it does
// know about it.)
var hostfilenames = hostCache.getFilenames();
for (var i = 0, n = hostfilenames.length; i < n; i++) {
var filename = hostfilenames[i];
var version = hostCache.getVersion(filename);
var isOpen = hostCache.isOpen(filename);
var scriptSnapshot = hostCache.getScriptSnapshot(filename);
var sourceFile: SourceFile = getSourceFile(filename);
if (sourceFile) {
//
// If the sourceFile is the same, assume no update
//
if (sourceFileUpToDate(sourceFile)) {
continue;
}
// Only perform incremental parsing on open files that are being edited. If a file was
// open, but is now closed, we want to re-parse entirely so we don't have any tokens that
// are holding onto expensive script snapshot instances on the host. Similarly, if a
// file was closed, then we always want to re-parse. This is so our tree doesn't keep
// the old buffer alive that represented the file on disk (as the host has moved to a
// new text buffer).
var textChangeRange: TextChangeRange = null;
if (sourceFile.isOpen && isOpen) {
textChangeRange = hostCache.getChangeRange(filename, sourceFile.version, sourceFile.scriptSnapshot);
}
sourceFile = documentRegistry.updateDocument(sourceFile, filename, compilationSettings, scriptSnapshot, version, isOpen, textChangeRange);
}
else {
sourceFile = documentRegistry.acquireDocument(filename, compilationSettings, scriptSnapshot, version, isOpen);
}
// Remember the new sourceFile
sourceFilesByName[getCanonicalFileName(filename)] = sourceFile;
}
var oldSettings = program && program.getCompilerOptions();
var newSettings = hostCache.compilationSettings();
var changesInCompilationSettingsAffectSyntax = oldSettings && oldSettings.target !== newSettings.target;
// Now create a new compiler
program = createProgram(hostfilenames, compilationSettings, createCompilerHost());
var newProgram = createProgram(hostCache.getRootFilenames(), newSettings, {
getSourceFile: getOrCreateSourceFile,
getCancellationToken: () => cancellationToken,
getCanonicalFileName: (filename) => useCaseSensitivefilenames ? filename : filename.toLowerCase(),
useCaseSensitiveFileNames: () => useCaseSensitivefilenames,
getNewLine: () => host.getNewLine ? host.getNewLine() : "\r\n",
getDefaultLibFilename: (options) => host.getDefaultLibFilename(options),
writeFile: (filename, data, writeByteOrderMark) => { },
getCurrentDirectory: () => host.getCurrentDirectory()
});
// Release any files we have acquired in the old program but are
// not part of the new program.
if (program) {
var oldSourceFiles = program.getSourceFiles();
for (var i = 0, n = oldSourceFiles.length; i < n; i++) {
var filename = oldSourceFiles[i].filename;
if (!newProgram.getSourceFile(filename) || changesInCompilationSettingsAffectSyntax) {
documentRegistry.releaseDocument(filename, oldSettings);
}
}
}
program = newProgram;
typeInfoResolver = program.getTypeChecker(/*produceDiagnostics*/ false);
return;
function getOrCreateSourceFile(filename: string): SourceFile {
cancellationToken.throwIfCancellationRequested();
// The program is asking for this file, check first if the host can locate it.
// If the host can not locate the file, then it does not exist. return undefined
// to the program to allow reporting of errors for missing files.
var hostFileInformation = hostCache.getOrCreateEntry(filename);
if (!hostFileInformation) {
return undefined;
}
// Check if the language version has changed since we last created a program; if they are the same,
// it is safe to reuse the souceFiles; if not, then the shape of the AST can change, and the oldSourceFile
// can not be reused. we have to dump all syntax trees and create new ones.
if (!changesInCompilationSettingsAffectSyntax) {
// Check if the old program had this file already
var oldSourceFile = program && program.getSourceFile(filename);
if (oldSourceFile) {
// This SourceFile is safe to reuse, return it
if (sourceFileUpToDate(oldSourceFile)) {
return oldSourceFile;
}
// We have an older version of the sourceFile, incrementally parse the changes
var textChangeRange = hostCache.getChangeRange(filename, oldSourceFile.version, oldSourceFile.scriptSnapshot);
return documentRegistry.updateDocument(oldSourceFile, filename, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, textChangeRange);
}
}
// Could not find this file in the old program, create a new SourceFile for it.
return documentRegistry.acquireDocument(filename, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version);
}
function sourceFileUpToDate(sourceFile: SourceFile): boolean {
return sourceFile && sourceFile.version === hostCache.getVersion(sourceFile.filename);
}
function programUpToDate(): boolean {
// If we haven't create a program yet, then it is not up-to-date
if (!program) {
return false;
}
// If number of files in the program do not match, it is not up-to-date
var rootFilenames = hostCache.getRootFilenames();
if (program.getSourceFiles().length !== rootFilenames.length) {
return false;
}
// If any file is not up-to-date, then the whole program is not up-to-date
for (var i = 0, n = rootFilenames.length; i < n; i++) {
if (!sourceFileUpToDate(program.getSourceFile(rootFilenames[i]))) {
return false;
}
}
// If the compilation settings do no match, then the program is not up-to-date
return compareDataObjects(program.getCompilerOptions(), hostCache.compilationSettings());
}
}
function getProgram(): Program {
synchronizeHostData();
return program;
}
/**
@ -2123,7 +2153,7 @@ module ts {
filename = normalizeSlashes(filename);
return program.getDiagnostics(getSourceFile(filename));
return program.getDiagnostics(getValidSourceFile(filename));
}
/**
@ -2136,7 +2166,7 @@ module ts {
filename = normalizeSlashes(filename)
var compilerOptions = program.getCompilerOptions();
var checker = getDiagnosticsProducingTypeChecker();
var targetSourceFile = getSourceFile(filename);
var targetSourceFile = getValidSourceFile(filename);
// Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file.
// Therefore only get diagnostics for given file.
@ -2213,19 +2243,19 @@ module ts {
filename = normalizeSlashes(filename);
var syntacticStart = new Date().getTime();
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
var start = new Date().getTime();
var currentToken = getTokenAtPosition(sourceFile, position);
host.log("getCompletionsAtPosition: Get current token: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: Get current token: " + (new Date().getTime() - start));
var start = new Date().getTime();
// Completion not allowed inside comments, bail out if this is the case
var insideComment = isInsideComment(sourceFile, currentToken, position);
host.log("getCompletionsAtPosition: Is inside comment: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: Is inside comment: " + (new Date().getTime() - start));
if (insideComment) {
host.log("Returning an empty list because completion was inside a comment.");
log("Returning an empty list because completion was inside a comment.");
return undefined;
}
@ -2233,19 +2263,19 @@ module ts {
// Note: previousToken can be undefined if we are the beginning of the file
var start = new Date().getTime();
var previousToken = findPrecedingToken(position, sourceFile);
host.log("getCompletionsAtPosition: Get previous token 1: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: Get previous token 1: " + (new Date().getTime() - start));
// The caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS|
// Skip this partial identifier to the previous token
if (previousToken && position <= previousToken.end && previousToken.kind === SyntaxKind.Identifier) {
var start = new Date().getTime();
previousToken = findPrecedingToken(previousToken.pos, sourceFile);
host.log("getCompletionsAtPosition: Get previous token 2: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: Get previous token 2: " + (new Date().getTime() - start));
}
// Check if this is a valid completion location
if (previousToken && isCompletionListBlocker(previousToken)) {
host.log("Returning an empty list because completion was requested in an invalid position.");
log("Returning an empty list because completion was requested in an invalid position.");
return undefined;
}
@ -2274,7 +2304,7 @@ module ts {
symbols: {},
typeChecker: typeInfoResolver
};
host.log("getCompletionsAtPosition: Syntactic work: " + (new Date().getTime() - syntacticStart));
log("getCompletionsAtPosition: Syntactic work: " + (new Date().getTime() - syntacticStart));
var location = getTouchingPropertyName(sourceFile, position);
// Populate the completion list
@ -2348,7 +2378,7 @@ module ts {
if (!isMemberCompletion) {
Array.prototype.push.apply(activeCompletionSession.entries, keywordCompletions);
}
host.log("getCompletionsAtPosition: Semantic work: " + (new Date().getTime() - semanticStart));
log("getCompletionsAtPosition: Semantic work: " + (new Date().getTime() - semanticStart));
return {
isMemberCompletion,
@ -2367,7 +2397,7 @@ module ts {
}
}
});
host.log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
}
function isCompletionListBlocker(previousToken: Node): boolean {
@ -2375,7 +2405,7 @@ module ts {
var result = isInStringOrRegularExpressionOrTemplateLiteral(previousToken) ||
isIdentifierDefinitionLocation(previousToken) ||
isRightOfIllegalDot(previousToken);
host.log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start));
log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start));
return result;
}
@ -2539,7 +2569,7 @@ module ts {
// in the getCompletionsAtPosition earlier
filename = normalizeSlashes(filename);
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
var session = activeCompletionSession;
@ -2557,7 +2587,7 @@ module ts {
// passing the meaning for the node so that we don't report that a suggestion for a value is an interface.
// We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration.
Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol");
var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All);
var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getValidSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All);
return {
name: entryName,
kind: displayPartsDocumentationsAndSymbolKind.symbolKind,
@ -3058,7 +3088,7 @@ module ts {
synchronizeHostData();
fileName = normalizeSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var sourceFile = getValidSourceFile(fileName);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
return undefined;
@ -3104,7 +3134,7 @@ module ts {
synchronizeHostData();
filename = normalizeSlashes(filename);
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
@ -3240,7 +3270,7 @@ module ts {
synchronizeHostData();
filename = normalizeSlashes(filename);
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
var node = getTouchingWord(sourceFile, position);
if (!node) {
@ -3785,7 +3815,7 @@ module ts {
synchronizeHostData();
fileName = normalizeSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var sourceFile = getValidSourceFile(fileName);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
@ -4608,7 +4638,7 @@ module ts {
synchronizeHostData();
filename = normalizeSlashes(filename);
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
var outputFiles: OutputFile[] = [];
@ -4757,7 +4787,7 @@ module ts {
synchronizeHostData();
fileName = normalizeSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var sourceFile = getValidSourceFile(fileName);
return SignatureHelp.getSignatureHelpItems(sourceFile, position, typeInfoResolver, cancellationToken);
}
@ -4840,7 +4870,7 @@ module ts {
synchronizeHostData();
fileName = normalizeSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var sourceFile = getValidSourceFile(fileName);
var result: ClassifiedSpan[] = [];
processNode(sourceFile);
@ -5194,12 +5224,12 @@ module ts {
var start = new Date().getTime();
var sourceFile = getCurrentSourceFile(filename);
host.log("getIndentationAtPosition: getCurrentSourceFile: " + (new Date().getTime() - start));
log("getIndentationAtPosition: getCurrentSourceFile: " + (new Date().getTime() - start));
var start = new Date().getTime();
var result = formatting.SmartIndenter.getIndentation(position, sourceFile, editorOptions);
host.log("getIndentationAtPosition: computeIndentation : " + (new Date().getTime() - start));
log("getIndentationAtPosition: computeIndentation : " + (new Date().getTime() - start));
return result;
}
@ -5246,7 +5276,7 @@ module ts {
filename = normalizeSlashes(filename);
var sourceFile = getSourceFile(filename);
var sourceFile = getValidSourceFile(filename);
cancellationToken.throwIfCancellationRequested();
@ -5392,7 +5422,7 @@ module ts {
synchronizeHostData();
fileName = normalizeSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var sourceFile = getValidSourceFile(fileName);
var node = getTouchingWord(sourceFile, position);
@ -5468,11 +5498,12 @@ module ts {
getFormattingEditsAfterKeystroke,
getEmitOutput,
getSourceFile: getCurrentSourceFile,
getProgram
};
}
/// Classifier
export function createClassifier(host: Logger): Classifier {
export function createClassifier(): Classifier {
var scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false);
/// We do not have a full parser support to know when we should parse a regex or not
@ -5775,6 +5806,23 @@ module ts {
return { getClassificationsForLine };
}
/// getDefaultLibraryFilePath
declare var __dirname: string;
/**
* Get the path of the default library file (lib.d.ts) as distributed with the typescript
* node package.
* The functionality is not supported if the ts module is consumed outside of a node module.
*/
export function getDefaultLibFilePath(options: CompilerOptions): string {
// Check __dirname is defined and that we are on a node.js system.
if (typeof __dirname !== "undefined") {
return __dirname + directorySeparator + getDefaultLibFilename(options);
}
throw new Error("getDefaultLibFilePath is only supported when consumed as a node module. ");
}
function initializeServices() {
objectAllocator = {
getNodeConstructor: kind => {

View File

@ -25,9 +25,6 @@ module ts {
/** Gets the length of this script snapshot. */
getLength(): number;
/** This call returns the JSON-encoded array of the type: number[] */
getLineStartPositions(): string;
/**
* Returns a JSON-encoded value of the type:
* { span: { start: number; length: number }; newLength: number }
@ -37,6 +34,12 @@ module ts {
getChangeRange(oldSnapshot: ScriptSnapshotShim): string;
}
export interface Logger {
log(s: string): void;
trace(s: string): void;
error(s: string): void;
}
/** Public interface of the host of a language service shim instance.*/
export interface LanguageServiceShimHost extends Logger {
getCompilationSettings(): string;
@ -44,7 +47,6 @@ module ts {
/** Returns a JSON-encoded value of the type: string[] */
getScriptFileNames(): string;
getScriptVersion(fileName: string): string;
getScriptIsOpen(fileName: string): boolean;
getScriptSnapshot(fileName: string): ScriptSnapshotShim;
getLocalizedDiagnosticMessages(): string;
getCancellationToken(): CancellationToken;
@ -187,14 +189,6 @@ module ts {
return this.scriptSnapshotShim.getLength();
}
public getLineStartPositions(): number[] {
if (this.lineStartPositions == null) {
this.lineStartPositions = JSON.parse(this.scriptSnapshotShim.getLineStartPositions());
}
return this.lineStartPositions;
}
public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
var oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
var encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
@ -239,17 +233,14 @@ module ts {
}
public getScriptSnapshot(fileName: string): IScriptSnapshot {
return new ScriptSnapshotShimAdapter(this.shimHost.getScriptSnapshot(fileName));
var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot);
}
public getScriptVersion(fileName: string): string {
return this.shimHost.getScriptVersion(fileName);
}
public getScriptIsOpen(fileName: string): boolean {
return this.shimHost.getScriptIsOpen(fileName);
}
public getLocalizedDiagnosticMessages(): any {
var diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
if (diagnosticMessagesJson == null || diagnosticMessagesJson == "") {
@ -269,13 +260,13 @@ module ts {
return this.shimHost.getCancellationToken();
}
public getDefaultLibFilename(options: CompilerOptions): string {
return this.shimHost.getDefaultLibFilename(JSON.stringify(options));
}
public getCurrentDirectory(): string {
return this.shimHost.getCurrentDirectory();
}
public getDefaultLibFilename(options: CompilerOptions): string {
return this.shimHost.getDefaultLibFilename(JSON.stringify(options));
}
}
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any): any {
@ -669,9 +660,9 @@ module ts {
class ClassifierShimObject extends ShimBase implements ClassifierShim {
public classifier: Classifier;
constructor(factory: ShimFactory, public logger: Logger) {
constructor(factory: ShimFactory) {
super(factory);
this.classifier = createClassifier(this.logger);
this.classifier = createClassifier();
}
/// COLORIZATION
@ -761,7 +752,7 @@ module ts {
public createClassifierShim(logger: Logger): ClassifierShim {
try {
return new ClassifierShimObject(this, logger);
return new ClassifierShimObject(this);
}
catch (err) {
logInternalError(logger, err);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,5 @@
tests/cases/compiler/contextualTyping.ts(189,18): error TS2384: Overload signatures must all be ambient or non-ambient.
tests/cases/compiler/contextualTyping.ts(197,15): error TS2300: Duplicate identifier 'Point'.
tests/cases/compiler/contextualTyping.ts(207,10): error TS2300: Duplicate identifier 'Point'.
tests/cases/compiler/contextualTyping.ts(230,5): error TS2322: Type '{}' is not assignable to type 'B'.\n Property 'x' is missing in type '{}'.
==== tests/cases/compiler/contextualTyping.ts (4 errors) ====
tests/cases/compiler/contextualTyping.ts(189,18): error TS2384: Overload signatures must all be ambient or non-ambient.\ntests/cases/compiler/contextualTyping.ts(197,15): error TS2300: Duplicate identifier 'Point'.\ntests/cases/compiler/contextualTyping.ts(207,10): error TS2300: Duplicate identifier 'Point'.\ntests/cases/compiler/contextualTyping.ts(230,5): error TS2322: Type '{}' is not assignable to type 'B'.
Property 'x' is missing in type '{}'.\n\n\n==== tests/cases/compiler/contextualTyping.ts (4 errors) ====
// DEFAULT INTERFACES
interface IFoo {
n: number;
@ -242,5 +237,6 @@ tests/cases/compiler/contextualTyping.ts(230,5): error TS2322: Type '{}' is not
interface B extends A { }
var x: B = { };
~
!!! error TS2322: Type '{}' is not assignable to type 'B'.\n Property 'x' is missing in type '{}'.
!!! error TS2322: Type '{}' is not assignable to type 'B'.
!!! error TS2322: Property 'x' is missing in type '{}'.

View File

@ -232,4 +232,206 @@ var x: B = { };
//// [contextualTyping.js]
// CONTEXT: Class property declaration\nvar C1T5 = (function () {\n function C1T5() {\n this.foo = function (i) {\n return i;\n };\n }\n return C1T5;\n})();\n// CONTEXT: Module property declaration\nvar C2T5;\n(function (C2T5) {\n C2T5.foo = function (i) {\n return i;\n };\n})(C2T5 || (C2T5 = {}));\n// CONTEXT: Variable declaration\nvar c3t1 = (function (s) {\n return s;\n});\nvar c3t2 = ({\n n: 1\n});\nvar c3t3 = [];\nvar c3t4 = function () {\n return ({});\n};\nvar c3t5 = function (n) {\n return ({});\n};\nvar c3t6 = function (n, s) {\n return ({});\n};\nvar c3t7 = function (n) {\n return n;\n};\nvar c3t8 = function (n) {\n return n;\n};\nvar c3t9 = [[], []];\nvar c3t10 = [({}), ({})];\nvar c3t11 = [function (n, s) {\n return s;\n}];\nvar c3t12 = {\n foo: ({})\n};\nvar c3t13 = ({\n f: function (i, s) {\n return s;\n }\n});\nvar c3t14 = ({\n a: []\n});\n// CONTEXT: Class property assignment\nvar C4T5 = (function () {\n function C4T5() {\n this.foo = function (i, s) {\n return s;\n };\n }\n return C4T5;\n})();\n// CONTEXT: Module property assignment\nvar C5T5;\n(function (C5T5) {\n C5T5.foo;\n C5T5.foo = function (i, s) {\n return s;\n };\n})(C5T5 || (C5T5 = {}));\n// CONTEXT: Variable assignment\nvar c6t5;\nc6t5 = function (n) {\n return ({});\n};\n// CONTEXT: Array index assignment\nvar c7t2;\nc7t2[0] = ({ n: 1 });\nvar objc8 = ({});\nobjc8.t1 = (function (s) {\n return s;\n});\nobjc8.t2 = ({\n n: 1\n});\nobjc8.t3 = [];\nobjc8.t4 = function () {\n return ({});\n};\nobjc8.t5 = function (n) {\n return ({});\n};\nobjc8.t6 = function (n, s) {\n return ({});\n};\nobjc8.t7 = function (n) {\n return n;\n};\nobjc8.t8 = function (n) {\n return n;\n};\nobjc8.t9 = [[], []];\nobjc8.t10 = [({}), ({})];\nobjc8.t11 = [function (n, s) {\n return s;\n}];\nobjc8.t12 = {\n foo: ({})\n};\nobjc8.t13 = ({\n f: function (i, s) {\n return s;\n }\n});\nobjc8.t14 = ({\n a: []\n});\n// CONTEXT: Function call\nfunction c9t5(f) {\n}\n;\nc9t5(function (n) {\n return ({});\n});\n// CONTEXT: Return statement\nvar c10t5 = function () {\n return function (n) {\n return ({});\n };\n};\n// CONTEXT: Newing a class\nvar C11t5 = (function () {\n function C11t5(f) {\n }\n return C11t5;\n})();\n;\nvar i = new C11t5(function (n) {\n return ({});\n});\n// CONTEXT: Type annotated expression\nvar c12t1 = (function (s) {\n return s;\n});\nvar c12t2 = ({\n n: 1\n});\nvar c12t3 = [];\nvar c12t4 = function () {\n return ({});\n};\nvar c12t5 = function (n) {\n return ({});\n};\nvar c12t6 = function (n, s) {\n return ({});\n};\nvar c12t7 = function (n) {\n return n;\n};\nvar c12t8 = function (n) {\n return n;\n};\nvar c12t9 = [[], []];\nvar c12t10 = [({}), ({})];\nvar c12t11 = [function (n, s) {\n return s;\n}];\nvar c12t12 = {\n foo: ({})\n};\nvar c12t13 = ({\n f: function (i, s) {\n return s;\n }\n});\nvar c12t14 = ({\n a: []\n});\nfunction EF1(a, b) {\n return a + b;\n}\nvar efv = EF1(1, 2);\nfunction Point(x, y) {\n this.x = x;\n this.y = y;\n return this;\n}\nPoint.origin = new Point(0, 0);\nPoint.prototype.add = function (dx, dy) {\n return new Point(this.x + dx, this.y + dy);\n};\nPoint.prototype = {\n x: 0,\n y: 0,\n add: function (dx, dy) {\n return new Point(this.x + dx, this.y + dy);\n }\n};\nvar x = {};\n//# sourceMappingURL=contextualTyping.js.map
// CONTEXT: Class property declaration
var C1T5 = (function () {
function C1T5() {
this.foo = function (i) {
return i;
};
}
return C1T5;
})();
// CONTEXT: Module property declaration
var C2T5;
(function (C2T5) {
C2T5.foo = function (i) {
return i;
};
})(C2T5 || (C2T5 = {}));
// CONTEXT: Variable declaration
var c3t1 = (function (s) {
return s;
});
var c3t2 = ({
n: 1
});
var c3t3 = [];
var c3t4 = function () {
return ({});
};
var c3t5 = function (n) {
return ({});
};
var c3t6 = function (n, s) {
return ({});
};
var c3t7 = function (n) {
return n;
};
var c3t8 = function (n) {
return n;
};
var c3t9 = [[], []];
var c3t10 = [({}), ({})];
var c3t11 = [function (n, s) {
return s;
}];
var c3t12 = {
foo: ({})
};
var c3t13 = ({
f: function (i, s) {
return s;
}
});
var c3t14 = ({
a: []
});
// CONTEXT: Class property assignment
var C4T5 = (function () {
function C4T5() {
this.foo = function (i, s) {
return s;
};
}
return C4T5;
})();
// CONTEXT: Module property assignment
var C5T5;
(function (C5T5) {
C5T5.foo;
C5T5.foo = function (i, s) {
return s;
};
})(C5T5 || (C5T5 = {}));
// CONTEXT: Variable assignment
var c6t5;
c6t5 = function (n) {
return ({});
};
// CONTEXT: Array index assignment
var c7t2;
c7t2[0] = ({ n: 1 });
var objc8 = ({});
objc8.t1 = (function (s) {
return s;
});
objc8.t2 = ({
n: 1
});
objc8.t3 = [];
objc8.t4 = function () {
return ({});
};
objc8.t5 = function (n) {
return ({});
};
objc8.t6 = function (n, s) {
return ({});
};
objc8.t7 = function (n) {
return n;
};
objc8.t8 = function (n) {
return n;
};
objc8.t9 = [[], []];
objc8.t10 = [({}), ({})];
objc8.t11 = [function (n, s) {
return s;
}];
objc8.t12 = {
foo: ({})
};
objc8.t13 = ({
f: function (i, s) {
return s;
}
});
objc8.t14 = ({
a: []
});
// CONTEXT: Function call
function c9t5(f) {
}
;
c9t5(function (n) {
return ({});
});
// CONTEXT: Return statement
var c10t5 = function () {
return function (n) {
return ({});
};
};
// CONTEXT: Newing a class
var C11t5 = (function () {
function C11t5(f) {
}
return C11t5;
})();
;
var i = new C11t5(function (n) {
return ({});
});
// CONTEXT: Type annotated expression
var c12t1 = (function (s) {
return s;
});
var c12t2 = ({
n: 1
});
var c12t3 = [];
var c12t4 = function () {
return ({});
};
var c12t5 = function (n) {
return ({});
};
var c12t6 = function (n, s) {
return ({});
};
var c12t7 = function (n) {
return n;
};
var c12t8 = function (n) {
return n;
};
var c12t9 = [[], []];
var c12t10 = [({}), ({})];
var c12t11 = [function (n, s) {
return s;
}];
var c12t12 = {
foo: ({})
};
var c12t13 = ({
f: function (i, s) {
return s;
}
});
var c12t14 = ({
a: []
});
function EF1(a, b) {
return a + b;
}
var efv = EF1(1, 2);
function Point(x, y) {
this.x = x;
this.y = y;
return this;
}
Point.origin = new Point(0, 0);
Point.prototype.add = function (dx, dy) {
return new Point(this.x + dx, this.y + dy);
};
Point.prototype = {
x: 0,
y: 0,
add: function (dx, dy) {
return new Point(this.x + dx, this.y + dy);
}
};
var x = {};
//# sourceMappingURL=contextualTyping.js.map

File diff suppressed because it is too large Load Diff

View File

@ -2,4 +2,4 @@
var foo: {id:number;} = {id:4};
//// [contextualTyping1.js]
var foo = { id: 4 };
var foo = { id: 4 };\n

View File

@ -0,0 +1,37 @@
// @module: commonjs
// @includebuiltfile: typescript.d.ts
/*
* Note: This test is a public API sample. The sample sources can be found
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler
* Please log a "breaking change" issue for any API breaking change affecting this issue
*/
declare var process: any;
declare var console: any;
import ts = require("typescript");
export function compile(filenames: string[], options: ts.CompilerOptions): void {
var host = ts.createCompilerHost(options);
var program = ts.createProgram(filenames, options, host);
var checker = ts.createTypeChecker(program, /*produceDiagnostics*/ true);
var result = program.emitFiles();
var allDiagnostics = program.getDiagnostics()
.concat(checker.getDiagnostics())
.concat(result.diagnostics);
allDiagnostics.forEach(diagnostic => {
var lineChar = diagnostic.file.getLineAndCharacterFromPosition(diagnostic.start);
console.log(`${diagnostic.file.filename} (${lineChar.line},${lineChar.character}): ${diagnostic.messageText}`);
});
console.log(`Process exiting with code '${result.emitResultStatus}'.`);
process.exit(result.emitResultStatus);
}
compile(process.argv.slice(2), {
noEmitOnError: true, noImplicitAny: true,
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
});

View File

@ -0,0 +1,65 @@
// @module: commonjs
// @includebuiltfile: typescript.d.ts
/*
* Note: This test is a public API sample. The sample sources can be found
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#traversing-the-ast-with-a-little-linter
* Please log a "breaking change" issue for any API breaking change affecting this issue
*/
declare var process: any;
declare var console: any;
declare var fs: any;
import ts = require("typescript");
export function delint(sourceFile: ts.SourceFile) {
delintNode(sourceFile);
function delintNode(node: ts.Node) {
switch (node.kind) {
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.WhileStatement:
case ts.SyntaxKind.DoStatement:
if ((<ts.IterationStatement>node).statement.kind !== ts.SyntaxKind.Block) {
report(node, "A looping statement's contents should be wrapped in a block body.");
}
break;
case ts.SyntaxKind.IfStatement:
var ifStatement = (<ts.IfStatement>node);
if (ifStatement.thenStatement.kind !== ts.SyntaxKind.Block) {
report(ifStatement.thenStatement, "An if statement's contents should be wrapped in a block body.");
}
if (ifStatement.elseStatement &&
ifStatement.elseStatement.kind !== ts.SyntaxKind.Block && ifStatement.elseStatement.kind !== ts.SyntaxKind.IfStatement) {
report(ifStatement.elseStatement, "An else statement's contents should be wrapped in a block body.");
}
break;
case ts.SyntaxKind.BinaryExpression:
var op = (<ts.BinaryExpression>node).operator;
if (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken) {
report(node, "Use '===' and '!=='.")
}
break;
}
ts.forEachChild(node, delintNode);
}
function report(node: ts.Node, message: string) {
var lineChar = sourceFile.getLineAndCharacterFromPosition(node.getStart());
console.log(`${sourceFile.filename} (${lineChar.line},${lineChar.character}): ${message}`)
}
}
var filenames = process.argv.slice(2);
filenames.forEach(filename => {
// Parse a file
var sourceFile = ts.createSourceFile(filename, fs.readFileSync(filename).toString(), ts.ScriptTarget.ES6, /*setParentNodes */ true);
// delint it
delint(sourceFile);
});

View File

@ -0,0 +1,67 @@
// @module: commonjs
// @includebuiltfile: typescript.d.ts
/*
* Note: This test is a public API sample. The sample sources can be found
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#a-simple-transform-function
* Please log a "breaking change" issue for any API breaking change affecting this issue
*/
declare var process: any;
declare var console: any;
declare var fs: any;
declare var path: any;
import ts = require("typescript");
function transform(contents: string, compilerOptions: ts.CompilerOptions = {}) {
// Sources
var files = {
"file.ts": contents,
"lib.d.ts": fs.readFileSync(ts.getDefaultLibFilePath(compilerOptions)).toString()
};
// Generated outputs
var outputs = [];
// Create a compilerHost object to allow the compiler to read and write files
var compilerHost = {
getSourceFile: (filename, target) => {
return files[filename] !== undefined ?
ts.createSourceFile(filename, files[filename], target) : undefined;
},
writeFile: (name, text, writeByteOrderMark) => {
outputs.push({ name: name, text: text, writeByteOrderMark: writeByteOrderMark });
},
getDefaultLibFilename: () => "lib.d.ts",
useCaseSensitiveFileNames: () => false,
getCanonicalFileName: (filename) => filename,
getCurrentDirectory: () => "",
getNewLine: () => "\n"
};
// Create a program from inputs
var program = ts.createProgram(["file.ts"], compilerOptions, compilerHost);
// Query for early errors
var errors = program.getDiagnostics();
// Do not generate code in the presence of early errors
if (!errors.length) {
// Type check and get semantic errors
var checker = program.getTypeChecker(true);
errors = checker.getDiagnostics();
// Generate output
program.emitFiles();
}
return {
outputs: outputs,
errors: errors.map(function (e) { return e.file.filename + "(" + e.file.getLineAndCharacterFromPosition(e.start).line + "): " + e.messageText; })
};
}
// Calling our transform function using a simple TypeScript variable declarations,
// and loading the default library like:
var source = "var x: number = 'string'";
var result = transform(source);
console.log(JSON.stringify(result));

View File

@ -0,0 +1,104 @@
// @module: commonjs
// @includebuiltfile: typescript.d.ts
/*
* Note: This test is a public API sample. The sample sources can be found
at: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
* Please log a "breaking change" issue for any API breaking change affecting this issue
*/
declare var process: any;
declare var console: any;
declare var fs: any;
declare var path: any;
import ts = require("typescript");
function watch(rootFilenames: string[], options: ts.CompilerOptions) {
var files: ts.Map<{ version: number }> = {};
// initialize the list of files
rootFilenames.forEach(filename => {
files[filename] = { version: 0 };
});
// Create the language service host to allow the LS to communicate with the host
var servicesHost: ts.LanguageServiceHost = {
getScriptFileNames: () => rootFilenames,
getScriptVersion: (filename) => files[filename] && files[filename].version.toString(),
getScriptSnapshot: (filename) => {
if (!fs.existsSync(filename)) {
return undefined;
}
return ts.ScriptSnapshot.fromString(fs.readFileSync(filename).toString());
},
getCurrentDirectory: () => process.cwd(),
getCompilationSettings: () => options,
getDefaultLibFilename: (options) => ts.getDefaultLibFilePath(options),
};
// Create the language service files
var services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry())
// Now let's watch the files
rootFilenames.forEach(filename => {
// First time around, emit all files
emitFile(filename);
// Add a watch on the file to handle next change
fs.watchFile(filename,
{ persistent: true, interval: 250 },
(curr, prev) => {
// Check timestamp
if (+curr.mtime <= +prev.mtime) {
return;
}
// Update the version to signal a change in the file
files[filename].version++;
// write the changes to disk
emitFile(filename);
});
});
function emitFile(filename: string) {
var output = services.getEmitOutput(filename);
if (output.emitOutputStatus === ts.EmitReturnStatus.Succeeded) {
console.log(`Emitting ${filename}`);
}
else {
console.log(`Emitting ${filename} failed`);
logErrors(filename);
}
output.outputFiles.forEach(o => {
fs.writeFileSync(o.name, o.text, "utf8");
});
}
function logErrors(filename: string) {
var allDiagnostics = services.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(filename))
.concat(services.getSemanticDiagnostics(filename));
allDiagnostics.forEach(diagnostic => {
if (diagnostic.file) {
var lineChar = diagnostic.file.getLineAndCharacterFromPosition(diagnostic.start);
console.log(` Error ${diagnostic.file.filename} (${lineChar.line},${lineChar.character}): ${diagnostic.messageText}`);
}
else {
console.log(` Error: ${diagnostic.messageText}`);
}
});
}
}
// Initialize files constituting the program as all .ts files in the current directory
var currentDirectoryFiles = fs.readdirSync(process.cwd()).
filter(filename=> filename.length >= 3 && filename.substr(filename.length - 3, 3) === ".ts");
// Start the watcher
watch(currentDirectoryFiles, { module: ts.ModuleKind.CommonJS });

View File

@ -20,7 +20,7 @@ module ts {
}
function createTree(text: IScriptSnapshot, version: string) {
return createLanguageServiceSourceFile(/*fileName:*/ "", text, ScriptTarget.Latest, version, /*isOpen:*/ true, /*setNodeParents:*/ true)
return createLanguageServiceSourceFile(/*fileName:*/ "", text, ScriptTarget.Latest, version, /*setNodeParents:*/ true)
}
function assertSameDiagnostics(file1: SourceFile, file2: SourceFile) {
@ -56,7 +56,7 @@ module ts {
Utils.assertInvariants(newTree, /*parent:*/ undefined);
// Create a tree for the new text, in an incremental fashion.
var incrementalNewTree = updateLanguageServiceSourceFile(oldTree, newText, oldTree.version + ".", /*isOpen:*/ true, textChangeRange);
var incrementalNewTree = updateLanguageServiceSourceFile(oldTree, newText, oldTree.version + ".", textChangeRange);
Utils.assertInvariants(incrementalNewTree, /*parent:*/ undefined);
// We should get the same tree when doign a full or incremental parse.

View File

@ -5,8 +5,8 @@ describe("DocumentRegistry", () => {
var documentRegistry = ts.createDocumentRegistry();
var defaultCompilerOptions = ts.getDefaultCompilerOptions();
var f1 = documentRegistry.acquireDocument("file1.ts", defaultCompilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f2 = documentRegistry.acquireDocument("file1.ts", defaultCompilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f1 = documentRegistry.acquireDocument("file1.ts", defaultCompilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
var f2 = documentRegistry.acquireDocument("file1.ts", defaultCompilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
assert(f1 === f2, "DocumentRegistry should return the same document for the same name");
});
@ -17,22 +17,22 @@ describe("DocumentRegistry", () => {
// change compilation setting that doesn't affect parsing - should have the same document
compilerOptions.declaration = true;
var f1 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f1 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
compilerOptions.declaration = false;
var f2 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f2 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
assert(f1 === f2, "Expected to have the same document instance");
// change value of compilation setting that is used during production of AST - new document is required
compilerOptions.target = ts.ScriptTarget.ES3;
var f3 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f3 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
assert(f1 !== f3, "Changed target: Expected to have different instances of document");
compilerOptions.module = ts.ModuleKind.CommonJS;
var f4 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), "1", false);
var f4 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
assert(f1 !== f4, "Changed module: Expected to have different instances of document");
assert(f3 === f4, "Changed module: Expected to have the same instance of the document");
});
});