diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index 431cf460180..e5eaa46c8e5 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -86,7 +86,7 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: '/// \r\n' + '/* @internal */\r\n' + 'namespace ts {\r\n' + - ' export var Diagnostics = {\r\n'; + ' export const Diagnostics = {\r\n'; var names = Utilities.getObjectKeys(messageTable); for (var i = 0; i < names.length; i++) { var name = names[i]; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 7d705acc5de..bc98d411b82 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -212,7 +212,7 @@ namespace ts { * true for all elements, otherwise returns a new array instance containing the filtered subset. */ export function filter(array: T[], f: (x: T) => x is U): U[]; - export function filter(array: T[], f: (x: T) => boolean): T[] + export function filter(array: T[], f: (x: T) => boolean): T[]; export function filter(array: T[], f: (x: T) => boolean): T[] { if (array) { const len = array.length; @@ -1879,10 +1879,10 @@ namespace ts { declare var process: any; declare var require: any; - let currentAssertionLevel: AssertionLevel; + export let currentAssertionLevel = AssertionLevel.None; export function shouldAssert(level: AssertionLevel): boolean { - return getCurrentAssertionLevel() >= level; + return currentAssertionLevel >= level; } export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void { @@ -1899,35 +1899,6 @@ namespace ts { export function fail(message?: string): void { Debug.assert(/*expression*/ false, message); } - - function getCurrentAssertionLevel() { - if (currentAssertionLevel !== undefined) { - return currentAssertionLevel; - } - - if (sys === undefined) { - return AssertionLevel.None; - } - - const developmentMode = /^development$/i.test(getEnvironmentVariable("NODE_ENV")); - currentAssertionLevel = developmentMode - ? AssertionLevel.Normal - : AssertionLevel.None; - - return currentAssertionLevel; - } - } - - export function getEnvironmentVariable(name: string, host?: CompilerHost) { - if (host && host.getEnvironmentVariable) { - return host.getEnvironmentVariable(name); - } - - if (sys && sys.getEnvironmentVariable) { - return sys.getEnvironmentVariable(name); - } - - return ""; } /** Remove an item from an array, moving everything to its right one space left. */ diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 91adb09c402..5d46038b2b1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -206,7 +206,7 @@ namespace ts { readFile: fileName => sys.readFile(fileName), trace: (s: string) => sys.write(s + newLine), directoryExists: directoryName => sys.directoryExists(directoryName), - getEnvironmentVariable: name => getEnvironmentVariable(name, /*host*/ undefined), + getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "", getDirectories: (path: string) => sys.getDirectories(path), realpath }; diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 2d4fcd241aa..7cf7d75bc02 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -83,7 +83,7 @@ namespace ts { getEnvironmentVariable?(name: string): string; }; - export var sys: System = (function() { + export let sys: System = (function() { function getWScriptSystem(): System { @@ -637,4 +637,10 @@ namespace ts { } return sys; })(); + + if (sys && sys.getEnvironmentVariable) { + Debug.currentAssertionLevel = /^development$/i.test(sys.getEnvironmentVariable("NODE_ENV")) + ? AssertionLevel.Normal + : AssertionLevel.None; + } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 0b4418b1236..49f834963ee 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -754,6 +754,13 @@ namespace FourSlash { } } + public verifyCompletionListIsGlobal(expected: boolean) { + const completions = this.getCompletionListAtCaret(); + if (completions && completions.isGlobalCompletion !== expected) { + this.raiseError(`verifyCompletionListIsGlobal failed - expected result to be ${completions.isGlobalCompletion}`); + } + } + public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) { const completions = this.getCompletionListAtCaret(); if (completions) { @@ -3046,6 +3053,10 @@ namespace FourSlashInterface { this.state.verifyCompletionListIsEmpty(this.negative); } + public completionListIsGlobal(expected: boolean) { + this.state.verifyCompletionListIsGlobal(expected); + } + public completionListAllowsNewIdentifier() { this.state.verifyCompletionListAllowsNewIdentifier(this.negative); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index c6b6b460fca..7c82aeece78 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -509,7 +509,7 @@ namespace Harness { tryEnableSourceMapsForHost?(): void; getEnvironmentVariable?(name: string): string; } - export var IO: IO; + export let IO: IO; // harness always uses one kind of new line const harnessNewLine = "\r\n"; diff --git a/src/server/client.ts b/src/server/client.ts index 5032056c2f3..688408dfb88 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -214,6 +214,7 @@ namespace ts.server { const response = this.processResponse(request); return { + isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries: response.body.map(entry => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 2b28e66bf88..51310c90a6b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -12,7 +12,7 @@ namespace ts.server { export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024; export type ProjectServiceEvent = - { eventName: "context", data: { project: Project, fileName: NormalizedPath } } | { eventName: "configFileDiag", data: { triggerFile?: string, configFileName: string, diagnostics: Diagnostic[] } } + { eventName: "context", data: { project: Project, fileName: NormalizedPath } } | { eventName: "configFileDiag", data: { triggerFile?: string, configFileName: string, diagnostics: Diagnostic[] } }; export interface ProjectServiceEventHandler { (event: ProjectServiceEvent): void; diff --git a/src/services/completions.ts b/src/services/completions.ts index a9395bd665b..071cac27bc6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -14,11 +14,11 @@ namespace ts.Completions { return undefined; } - const { symbols, isMemberCompletion, isNewIdentifierLocation, location, isJsDocTagName } = completionData; + const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isJsDocTagName } = completionData; if (isJsDocTagName) { // If the current position is a jsDoc tag name, only tag names should be provided for completion - return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: JsDoc.getAllJsDocCompletionEntries() }; + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries: JsDoc.getAllJsDocCompletionEntries() }; } const entries: CompletionEntry[] = []; @@ -56,7 +56,7 @@ namespace ts.Completions { addRange(entries, keywordCompletions); } - return { isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation, entries }; + return { isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation, entries }; function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map): CompletionEntry[] { const entries: CompletionEntry[] = []; @@ -190,7 +190,7 @@ namespace ts.Completions { if (type) { getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, element, /*performCharacterChecks*/false); if (entries.length) { - return { isMemberCompletion: true, isNewIdentifierLocation: true, entries }; + return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } } } @@ -209,7 +209,7 @@ namespace ts.Completions { } if (entries.length) { - return { isMemberCompletion: false, isNewIdentifierLocation: true, entries }; + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries }; } return undefined; @@ -221,7 +221,7 @@ namespace ts.Completions { if (type) { getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false); if (entries.length) { - return { isMemberCompletion: true, isNewIdentifierLocation: true, entries }; + return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } } return undefined; @@ -233,7 +233,7 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; addStringLiteralCompletionsFromType(type, entries); if (entries.length) { - return { isMemberCompletion: false, isNewIdentifierLocation: false, entries }; + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } } return undefined; @@ -281,6 +281,7 @@ namespace ts.Completions { entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span); } return { + isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries @@ -549,13 +550,18 @@ namespace ts.Completions { } const completionInfo: CompletionInfo = { - isMemberCompletion: false, - /** - * The user may type in a path that doesn't yet exist, creating a "new identifier" - * with respect to the collection of identifiers the server is aware of. - */ - isNewIdentifierLocation: true, - entries: [] + /** + * We don't want the editor to offer any other completions, such as snippets, inside a comment. + */ + isGlobalCompletion: false, + isMemberCompletion: false, + /** + * The user may type in a path that doesn't yet exist, creating a "new identifier" + * with respect to the collection of identifiers the server is aware of. + */ + isNewIdentifierLocation: true, + + entries: [] }; const text = sourceFile.text.substr(range.pos, position - range.pos); @@ -828,7 +834,7 @@ namespace ts.Completions { } if (isJsDocTagName) { - return { symbols: undefined, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, isJsDocTagName }; + return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, isJsDocTagName }; } if (!insideJsDocTagExpression) { @@ -900,6 +906,7 @@ namespace ts.Completions { } const semanticStart = timestamp(); + let isGlobalCompletion = false; let isMemberCompletion: boolean; let isNewIdentifierLocation: boolean; let symbols: Symbol[] = []; @@ -935,14 +942,16 @@ namespace ts.Completions { if (!tryGetGlobalSymbols()) { return undefined; } + isGlobalCompletion = true; } log("getCompletionData: Semantic work: " + (timestamp() - semanticStart)); - return { symbols, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), isJsDocTagName }; + return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), isJsDocTagName }; function getTypeScriptMemberSymbols(): void { // Right of dot member completion list + isGlobalCompletion = false; isMemberCompletion = true; isNewIdentifierLocation = false; diff --git a/src/services/types.ts b/src/services/types.ts index fc9a3da5331..ac81a87daf5 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -503,6 +503,7 @@ namespace ts { } export interface CompletionInfo { + isGlobalCompletion: boolean; isMemberCompletion: boolean; /** diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts new file mode 100644 index 00000000000..3a7adb465a1 --- /dev/null +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -0,0 +1,42 @@ +/// + +/////// // no globals in reference paths +////import { /*2*/ } from "./file.ts"; // no globals in imports +////var test = "/*3*/"; // no globals in strings +/////*4*/class A { // insert globals +//// foo(): string { return ''; } +////} +//// +////class /*5*/B extends A { // no globals after class keyword +//// bar(): string { +//// /*6*/ // insert globals +//// return ''; +//// } +////} +//// +////class C { // no globals at beginning of generics +//// x: U; +//// y = this./*8*/x; // no globals inserted for member completions +//// /*9*/ // insert globals +////} +/////*10*/ // insert globals +goTo.marker("1"); +verify.completionListIsGlobal(false); +goTo.marker("2"); +verify.completionListIsGlobal(false); +goTo.marker("3"); +verify.completionListIsGlobal(false); +goTo.marker("4"); +verify.completionListIsGlobal(true); +goTo.marker("5"); +verify.completionListIsGlobal(false); +goTo.marker("6"); +verify.completionListIsGlobal(true); +goTo.marker("7"); +verify.completionListIsGlobal(false); +goTo.marker("8"); +verify.completionListIsGlobal(false); +goTo.marker("9"); +verify.completionListIsGlobal(true); +goTo.marker("10"); +verify.completionListIsGlobal(true);