This commit is contained in:
Kanchalai Tanglertsampan 2016-09-06 17:14:36 -07:00
commit 4685646281
71 changed files with 1829 additions and 131 deletions

View file

@ -2,6 +2,8 @@
/* @internal */
namespace ts {
const ambientModuleSymbolRegex = /^".+"$/;
let nextSymbolId = 1;
let nextNodeId = 1;
let nextMergeId = 1;
@ -100,6 +102,7 @@ namespace ts {
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
getAmbientModules,
getJsxElementAttributesType,
getJsxIntrinsicTagNames,
@ -20195,5 +20198,15 @@ namespace ts {
return true;
}
}
function getAmbientModules(): Symbol[] {
const result: Symbol[] = [];
for (const sym in globals) {
if (ambientModuleSymbolRegex.test(sym)) {
result.push(globals[sym]);
}
}
return result;
}
}
}

View file

@ -8,9 +8,9 @@ namespace ts {
const emptyArray: any[] = [];
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string {
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string {
while (true) {
const fileName = combinePaths(searchPath, "tsconfig.json");
const fileName = combinePaths(searchPath, configName);
if (fileExists(fileName)) {
return fileName;
}
@ -166,7 +166,7 @@ namespace ts {
const typeReferenceExtensions = [".d.ts"];
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost): string[] | undefined {
export function getEffectiveTypeRoots(options: CompilerOptions, host: { directoryExists?: (directoryName: string) => boolean, getCurrentDirectory?: () => string }): string[] | undefined {
if (options.typeRoots) {
return options.typeRoots;
}
@ -186,7 +186,7 @@ namespace ts {
* Returns the path to every node_modules/@types directory from some ancestor directory.
* Returns undefined if there are none.
*/
function getDefaultTypeRoots(currentDirectory: string, host: ModuleResolutionHost): string[] | undefined {
function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined {
if (!host.directoryExists) {
return [combinePaths(currentDirectory, nodeModulesAtTypes)];
// And if it doesn't exist, tough.

View file

@ -1965,6 +1965,7 @@ namespace ts {
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
getJsxIntrinsicTagNames(): Symbol[];
isOptionalParameter(node: ParameterDeclaration): boolean;
getAmbientModules(): Symbol[];
// Should not be called directly. Should only be accessed through the Program instance.
/* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];

View file

@ -263,22 +263,31 @@ namespace FourSlash {
constructor(private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
// Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
const compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
let compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
compilationOptions.skipDefaultLibCheck = true;
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
this.languageService = languageServiceAdapter.getLanguageService();
// Initialize the language service with all the scripts
let startResolveFileRef: FourSlashFile;
ts.forEach(testData.files, file => {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles[file.fileName] = file.content;
if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") {
const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content);
assert.isTrue(configJson.config !== undefined);
// Extend our existing compiler options so that we can also support tsconfig only options
if (configJson.config.compilerOptions) {
const baseDirectory = ts.normalizePath(ts.getDirectoryPath(file.fileName));
const tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDirectory, file.fileName);
if (!tsConfig.errors || !tsConfig.errors.length) {
compilationOptions = ts.extend(compilationOptions, tsConfig.options);
}
}
}
if (!startResolveFileRef && file.fileOptions[metadataOptionNames.resolveReference] === "true") {
startResolveFileRef = file;
}
@ -288,6 +297,15 @@ namespace FourSlash {
}
});
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
this.languageService = languageServiceAdapter.getLanguageService();
if (startResolveFileRef) {
// Add the entry-point file itself into the languageServiceShimHost
this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content, /*isRootFile*/ true);
@ -342,6 +360,7 @@ namespace FourSlash {
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
PlaceOpenBraceOnNewLineForFunctions: false,
@ -730,10 +749,10 @@ namespace FourSlash {
}
}
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
const completions = this.getCompletionListAtCaret();
if (completions) {
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind);
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex);
}
else {
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`);
@ -749,25 +768,32 @@ namespace FourSlash {
* @param expectedText the text associated with the symbol
* @param expectedDocumentation the documentation text associated with the symbol
* @param expectedKind the kind of symbol (see ScriptElementKind)
* @param spanIndex the index of the range that the completion item's replacement text span should match
*/
public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string) {
public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) {
const that = this;
let replacementSpan: ts.TextSpan;
if (spanIndex !== undefined) {
replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex);
}
function filterByTextOrDocumentation(entry: ts.CompletionEntry) {
const details = that.getCompletionEntryDetails(entry.name);
const documentation = ts.displayPartsToString(details.documentation);
const text = ts.displayPartsToString(details.displayParts);
if (expectedText && expectedDocumentation) {
return (documentation === expectedDocumentation && text === expectedText) ? true : false;
// If any of the expected values are undefined, assume that users don't
// care about them.
if (replacementSpan && !TestState.textSpansEqual(replacementSpan, entry.replacementSpan)) {
return false;
}
else if (expectedText && !expectedDocumentation) {
return text === expectedText ? true : false;
else if (expectedText && text !== expectedText) {
return false;
}
else if (expectedDocumentation && !expectedText) {
return documentation === expectedDocumentation ? true : false;
else if (expectedDocumentation && documentation !== expectedDocumentation) {
return false;
}
// Because expectedText and expectedDocumentation are undefined, we assume that
// users don"t care to compare them so we will treat that entry as if the entry has matching text and documentation
// and keep it in the list of filtered entry.
return true;
}
@ -791,6 +817,10 @@ namespace FourSlash {
if (expectedKind) {
error += "Expected kind: " + expectedKind + " to equal: " + filterCompletions[0].kind + ".";
}
if (replacementSpan) {
const spanText = filterCompletions[0].replacementSpan ? stringify(filterCompletions[0].replacementSpan) : undefined;
error += "Expected replacement span: " + stringify(replacementSpan) + " to equal: " + spanText + ".";
}
this.raiseError(error);
}
}
@ -2188,7 +2218,7 @@ namespace FourSlash {
return text.substring(startPos, endPos);
}
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string) {
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.name === name) {
@ -2207,6 +2237,11 @@ namespace FourSlash {
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + name));
}
if (spanIndex !== undefined) {
const span = this.getTextSpanForRangeAtIndex(spanIndex);
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + name));
}
return;
}
}
@ -2263,6 +2298,17 @@ namespace FourSlash {
return `line ${(pos.line + 1)}, col ${pos.character}`;
}
private getTextSpanForRangeAtIndex(index: number): ts.TextSpan {
const ranges = this.getRanges();
if (ranges && ranges.length > index) {
const range = ranges[index];
return { start: range.start, length: range.end - range.start };
}
else {
this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + (ranges ? 0 : ranges.length));
}
}
public getMarkerByName(markerName: string) {
const markerPos = this.testData.markerPositions[markerName];
if (markerPos === undefined) {
@ -2286,6 +2332,10 @@ namespace FourSlash {
public resetCancelled(): void {
this.cancellationToken.resetCancelled();
}
private static textSpansEqual(a: ts.TextSpan, b: ts.TextSpan) {
return a && b && a.start === b.start && a.length === b.length;
}
}
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
@ -2294,12 +2344,16 @@ namespace FourSlash {
}
export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void {
// Give file paths an absolute path for the virtual file system
const absoluteBasePath = ts.combinePaths(Harness.virtualFileSystemRoot, basePath);
const absoluteFileName = ts.combinePaths(Harness.virtualFileSystemRoot, fileName);
// Parse out the files and their metadata
const testData = parseTestData(basePath, content, fileName);
const state = new TestState(basePath, testType, testData);
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteBasePath, testType, testData);
const output = ts.transpileModule(content, { reportDiagnostics: true });
if (output.diagnostics.length > 0) {
throw new Error(`Syntax error in ${basePath}: ${output.diagnostics[0].messageText}`);
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics[0].messageText}`);
}
runCode(output.outputText, state);
}
@ -2852,12 +2906,12 @@ namespace FourSlashInterface {
// Verifies the completion list contains the specified symbol. The
// completion list is brought up if necessary
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
if (this.negative) {
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind);
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind, spanIndex);
}
else {
this.state.verifyCompletionListContains(symbol, text, documentation, kind);
this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex);
}
}

View file

@ -514,6 +514,9 @@ namespace Harness {
// harness always uses one kind of new line
const harnessNewLine = "\r\n";
// Root for file paths that are stored in a virtual file system
export const virtualFileSystemRoot = "/";
namespace IOImpl {
declare class Enumerator {
public atEnd(): boolean;

View file

@ -123,7 +123,7 @@ namespace Harness.LanguageService {
}
export class LanguageServiceAdapterHost {
protected fileNameToScript = ts.createMap<ScriptInfo>();
protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false);
constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
protected settings = ts.getDefaultCompilerOptions()) {
@ -135,22 +135,24 @@ namespace Harness.LanguageService {
public getFilenames(): string[] {
const fileNames: string[] = [];
ts.forEachProperty(this.fileNameToScript, (scriptInfo) => {
for (const virtualEntry of this.virtualFileSystem.getAllFileEntries()){
const scriptInfo = virtualEntry.content;
if (scriptInfo.isRootFile) {
// only include root files here
// usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir.
fileNames.push(scriptInfo.fileName);
}
});
}
return fileNames;
}
public getScriptInfo(fileName: string): ScriptInfo {
return this.fileNameToScript[fileName];
const fileEntry = this.virtualFileSystem.traversePath(fileName);
return fileEntry && fileEntry.isFile() ? (<Utils.VirtualFile>fileEntry).content : undefined;
}
public addScript(fileName: string, content: string, isRootFile: boolean): void {
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content, isRootFile);
this.virtualFileSystem.addFile(fileName, new ScriptInfo(fileName, content, isRootFile));
}
public editScript(fileName: string, start: number, end: number, newText: string) {
@ -171,7 +173,7 @@ namespace Harness.LanguageService {
* @param col 0 based index
*/
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
const script: ScriptInfo = this.fileNameToScript[fileName];
const script: ScriptInfo = this.getScriptInfo(fileName);
assert.isOk(script);
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
@ -182,8 +184,14 @@ namespace Harness.LanguageService {
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
getCompilationSettings() { return this.settings; }
getCancellationToken() { return this.cancellationToken; }
getDirectories(path: string): string[] { return []; }
getCurrentDirectory(): string { return ""; }
getDirectories(path: string): string[] {
const dir = this.virtualFileSystem.traversePath(path);
if (dir && dir.isDirectory()) {
return ts.map((<Utils.VirtualDirectory>dir).getDirectories(), (d) => ts.combinePaths(path, d.name));
}
return [];
}
getCurrentDirectory(): string { return virtualFileSystemRoot; }
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
getScriptFileNames(): string[] { return this.getFilenames(); }
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
@ -196,6 +204,22 @@ namespace Harness.LanguageService {
return script ? script.version.toString() : undefined;
}
fileExists(fileName: string): boolean {
const script = this.getScriptSnapshot(fileName);
return script !== undefined;
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
return ts.matchFiles(path, extensions, exclude, include,
/*useCaseSensitiveFileNames*/false,
this.getCurrentDirectory(),
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
}
readFile(path: string, encoding?: string): string {
const snapshot = this.getScriptSnapshot(path);
return snapshot.getText(0, snapshot.getLength());
}
log(s: string): void { }
trace(s: string): void { }
error(s: string): void { }

View file

@ -16,7 +16,7 @@ namespace Utils {
}
export class VirtualFile extends VirtualFileSystemEntry {
content: string;
content?: Harness.LanguageService.ScriptInfo;
isFile() { return true; }
}
@ -73,7 +73,7 @@ namespace Utils {
}
}
addFile(name: string, content?: string): VirtualFile {
addFile(name: string, content?: Harness.LanguageService.ScriptInfo): VirtualFile {
const entry = this.getFileSystemEntry(name);
if (entry === undefined) {
const file = new VirtualFile(this.fileSystem, name);
@ -111,6 +111,7 @@ namespace Utils {
getFileSystemEntries() { return this.root.getFileSystemEntries(); }
addDirectory(path: string) {
path = ts.normalizePath(path);
const components = ts.getNormalizedPathComponents(path, this.currentDirectory);
let directory: VirtualDirectory = this.root;
for (const component of components) {
@ -123,8 +124,8 @@ namespace Utils {
return directory;
}
addFile(path: string, content?: string) {
const absolutePath = ts.getNormalizedAbsolutePath(path, this.currentDirectory);
addFile(path: string, content?: Harness.LanguageService.ScriptInfo) {
const absolutePath = ts.normalizePath(ts.getNormalizedAbsolutePath(path, this.currentDirectory));
const fileName = ts.getBaseFileName(path);
const directoryPath = ts.getDirectoryPath(absolutePath);
const directory = this.addDirectory(directoryPath);
@ -141,6 +142,7 @@ namespace Utils {
}
traversePath(path: string) {
path = ts.normalizePath(path);
let directory: VirtualDirectory = this.root;
for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) {
const entry = directory.getFileSystemEntry(component);
@ -157,6 +159,40 @@ namespace Utils {
return directory;
}
/**
* Reads the directory at the given path and retrieves a list of file names and a list
* of directory names within it. Suitable for use with ts.matchFiles()
* @param path The path to the directory to be read
*/
getAccessibleFileSystemEntries(path: string) {
const entry = this.traversePath(path);
if (entry && entry.isDirectory()) {
const directory = <VirtualDirectory>entry;
return {
files: ts.map(directory.getFiles(), f => f.name),
directories: ts.map(directory.getDirectories(), d => d.name)
};
}
return { files: [], directories: [] };
}
getAllFileEntries() {
const fileEntries: VirtualFile[] = [];
getFilesRecursive(this.root, fileEntries);
return fileEntries;
function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) {
const files = dir.getFiles();
const dirs = dir.getDirectories();
for (const file of files) {
result.push(file);
}
for (const subDir of dirs) {
getFilesRecursive(subDir, result);
}
}
}
}
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
@ -170,17 +206,5 @@ namespace Utils {
readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) {
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path));
}
getAccessibleFileSystemEntries(path: string) {
const entry = this.traversePath(path);
if (entry && entry.isDirectory()) {
const directory = <VirtualDirectory>entry;
return {
files: ts.map(directory.getFiles(), f => f.name),
directories: ts.map(directory.getDirectories(), d => d.name)
};
}
return { files: [], directories: [] };
}
}
}

6
src/lib/es5.d.ts vendored
View file

@ -1060,6 +1060,12 @@ interface ReadonlyArray<T> {
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
filter<S extends T>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S, thisArg?: any): S[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.

View file

@ -216,7 +216,18 @@ namespace ts.server {
return {
isMemberCompletion: false,
isNewIdentifierLocation: false,
entries: response.body
entries: response.body.map(entry => {
if (entry.replacementSpan !== undefined) {
const { name, kind, kindModifiers, sortText, replacementSpan} = entry;
const convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start),
this.lineOffsetToPosition(fileName, replacementSpan.end));
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
}
return entry as { name: string, kind: string, kindModifiers: string, sortText: string };
})
};
}

View file

@ -306,11 +306,6 @@ namespace ts.server {
throw new Error("No script with name '" + filename + "'");
}
resolvePath(path: string): string {
const result = this.host.resolvePath(path);
return result;
}
fileExists(path: string): boolean {
const result = this.host.fileExists(path);
return result;
@ -324,6 +319,14 @@ namespace ts.server {
return this.host.getDirectories(path);
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
return this.host.readDirectory(path, extensions, exclude, include);
}
readFile(path: string, encoding?: string): string {
return this.host.readFile(path, encoding);
}
/**
* @param line 1 based index
*/
@ -1591,6 +1594,7 @@ namespace ts.server {
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
PlaceOpenBraceOnNewLineForFunctions: false,

View file

@ -773,6 +773,11 @@ declare namespace ts.server.protocol {
* is often the same as the name but may be different in certain circumstances.
*/
sortText: string;
/**
* An optional span that indicates the text to be replaced by this completion item. If present,
* this span should be used instead of the default one.
*/
replacementSpan?: TextSpan;
}
/**

View file

@ -778,7 +778,17 @@ namespace ts.server {
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
result.push(entry);
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
let convertedSpan: protocol.TextSpan = undefined;
if (replacementSpan) {
convertedSpan = {
start: compilerService.host.positionToLineOffset(fileName, replacementSpan.start),
end: compilerService.host.positionToLineOffset(fileName, replacementSpan.start + replacementSpan.length)
};
}
result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan });
}
return result;
}, []).sort((a, b) => a.name.localeCompare(b.name));

View file

@ -50,6 +50,8 @@ namespace ts.formatting {
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
public SpaceAfterOpenBrace: Rule;
public SpaceBeforeCloseBrace: Rule;
public NoSpaceAfterOpenBrace: Rule;
public NoSpaceBeforeCloseBrace: Rule;
public NoSpaceBetweenEmptyBraceBrackets: Rule;
// Insert new line after { and before } in multi-line contexts.
@ -287,6 +289,8 @@ namespace ts.formatting {
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Space));
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Space));
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Delete));
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Delete));
this.NoSpaceBetweenEmptyBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsObjectContext), RuleAction.Delete));
// Insert new line after { and before } in multi-line contexts.
@ -414,7 +418,7 @@ namespace ts.formatting {
this.SpaceAfterPostdecrementWhenFollowedBySubtract,
this.SpaceAfterSubtractWhenFollowedByUnaryMinus, this.SpaceAfterSubtractWhenFollowedByPredecrement,
this.NoSpaceAfterCloseBrace,
this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NewLineBeforeCloseBraceInBlockContext,
this.NewLineBeforeCloseBraceInBlockContext,
this.SpaceAfterCloseBrace, this.SpaceBetweenCloseBraceAndElse, this.SpaceBetweenCloseBraceAndWhile, this.NoSpaceBetweenEmptyBraceBrackets,
this.NoSpaceBetweenFunctionKeywordAndStar, this.SpaceAfterStarInGeneratorDeclaration,
this.SpaceAfterFunctionInFuncDecl, this.NewLineAfterOpenBraceInBlockContext, this.SpaceAfterGetSetInMember,

View file

@ -81,6 +81,19 @@ namespace ts.formatting {
rules.push(this.globalRules.NoSpaceBetweenBrackets);
}
// The default value of InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces is true
// so if the option is undefined, we should treat it as true as well
if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
rules.push(this.globalRules.SpaceAfterOpenBrace);
rules.push(this.globalRules.SpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
else {
rules.push(this.globalRules.NoSpaceAfterOpenBrace);
rules.push(this.globalRules.NoSpaceBeforeCloseBrace);
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
}
if (options.InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);

View file

@ -1049,6 +1049,11 @@ namespace ts {
owners: string[];
}
interface VisibleModuleInfo {
moduleName: string;
moduleDir: string;
}
export interface DisplayPartsSymbolWriter extends SymbolWriter {
displayParts(): SymbolDisplayPart[];
}
@ -1245,7 +1250,15 @@ namespace ts {
sourceMapText?: string;
}
/**
* Matches a triple slash reference directive with an incomplete string literal for its path. Used
* to determine if the caret is currently within the string literal and capture the literal fragment
* for completions.
* For example, this matches /// <reference path="fragment
*/
const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|"))([^\3]*)$/;
const nodeModulesDependencyKeys = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
@ -2097,13 +2110,17 @@ namespace ts {
function isNameOfExternalModuleImportOrDeclaration(node: Node): boolean {
if (node.kind === SyntaxKind.StringLiteral) {
return isNameOfModuleDeclaration(node) ||
(isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node);
return isNameOfModuleDeclaration(node) || isExpressionOfExternalModuleImportEqualsDeclaration(node);
}
return false;
}
function isExpressionOfExternalModuleImportEqualsDeclaration(node: Node) {
return isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
}
/** Returns true if the position is within a comment */
function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean {
// The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment
@ -3436,6 +3453,10 @@ namespace ts {
const sourceFile = getValidSourceFile(fileName);
if (isInReferenceComment(sourceFile, position)) {
return getTripleSlashReferenceCompletion(sourceFile, position);
}
if (isInString(sourceFile, position)) {
return getStringLiteralCompletionEntries(sourceFile, position);
}
@ -3605,6 +3626,13 @@ namespace ts {
// a['/*completion position*/']
return getStringLiteralCompletionEntriesFromElementAccess(node.parent);
}
else if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) {
// Get all known external module names or complete a path to a module
// i.e. import * as ns from "/*completion position*/";
// import x = require("/*completion position*/");
// var y = require("/*completion position*/");
return getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node);
}
else {
const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile);
if (argumentInfo) {
@ -3697,6 +3725,446 @@ namespace ts {
}
}
}
function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral): CompletionInfo {
const literalValue = normalizeSlashes(node.text);
const scriptPath = node.getSourceFile().path;
const scriptDirectory = getDirectoryPath(scriptPath);
const span = getDirectoryFragmentTextSpan((<StringLiteral>node).text, node.getStart() + 1);
let entries: CompletionEntry[];
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
const compilerOptions = program.getCompilerOptions();
if (compilerOptions.rootDirs) {
entries = getCompletionEntriesForDirectoryFragmentWithRootDirs(
compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath);
}
else {
entries = getCompletionEntriesForDirectoryFragment(
literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath);
}
}
else {
// Check for node modules
entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span);
}
return {
isMemberCompletion: false,
isNewIdentifierLocation: true,
entries
};
}
/**
* Takes a script path and returns paths for all potential folders that could be merged with its
* containing folder via the "rootDirs" compiler option
*/
function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean): string[] {
// Make all paths absolute/normalized if they are not already
rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory)));
// Determine the path to the directory containing the script relative to the root directory it is contained within
let relativeDirectory: string;
for (const rootDirectory of rootDirs) {
if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) {
relativeDirectory = scriptPath.substr(rootDirectory.length);
break;
}
}
// Now find a path for each potential directory that is to be merged with the one containing the script
return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
}
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string): CompletionEntry[] {
const basePath = program.getCompilerOptions().project || host.getCurrentDirectory();
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
const result: CompletionEntry[] = [];
for (const baseDirectory of baseDirectories) {
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, exclude, result);
}
return result;
}
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
fragment = getDirectoryPath(fragment);
if (!fragment) {
fragment = "./";
}
else {
fragment = ensureTrailingDirectorySeparator(fragment);
}
const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment));
const baseDirectory = getDirectoryPath(absolutePath);
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
if (directoryProbablyExists(baseDirectory, host)) {
if (host.readDirectory) {
// Enumerate the available files if possible
const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]);
const foundFiles = createMap<boolean>();
for (let filePath of files) {
filePath = normalizePath(filePath);
if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) {
continue;
}
const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath));
if (!foundFiles[foundFileName]) {
foundFiles[foundFileName] = true;
}
}
for (const foundFile in foundFiles) {
result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span));
}
}
// If possible, get folder completion as well
if (host.getDirectories) {
const directories = host.getDirectories(baseDirectory);
for (const directory of directories) {
const directoryName = getBaseFileName(normalizePath(directory));
result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span));
}
}
}
return result;
}
/**
* Check all of the declared modules and those in node modules. Possible sources of modules:
* Modules that are found by the type checker
* Modules found relative to "baseUrl" compliler options (including patterns from "paths" compiler option)
* Modules from node_modules (i.e. those listed in package.json)
* This includes all files that are found in node_modules/moduleName/ with acceptable file extensions
*/
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan): CompletionEntry[] {
const options = program.getCompilerOptions();
const { baseUrl, paths } = options;
let result: CompletionEntry[];
if (baseUrl) {
const fileExtensions = getSupportedExtensions(options);
const projectDir = options.project || host.getCurrentDirectory();
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span);
if (paths) {
for (const path in paths) {
if (paths.hasOwnProperty(path)) {
if (path === "*") {
if (paths[path]) {
for (const pattern of paths[path]) {
for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions)) {
result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span));
}
}
}
}
else if (startsWith(path, fragment)) {
const entry = paths[path] && paths[path].length === 1 && paths[path][0];
if (entry) {
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
}
}
}
}
}
}
else {
result = [];
}
getCompletionEntriesFromTypings(host, options, scriptPath, span, result);
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, options)) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
}
return result;
}
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] {
if (host.readDirectory) {
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
if (parsed) {
// The prefix has two effective parts: the directory path and the base component after the filepath that is not a
// full directory component. For example: directory/path/of/prefix/base*
const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix);
const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix);
const normalizedPrefixBase = getBaseFileName(normalizedPrefix);
const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1;
// Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory;
const normalizedSuffix = normalizePath(parsed.suffix);
const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory);
const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase;
// If we have a suffix, then we need to read the directory all the way down. We could create a glob
// that encodes the suffix, but we would have to escape the character "?" which readDirectory
// doesn't support. For now, this is safer but slower
const includeGlob = normalizedSuffix ? "**/*" : "./*";
const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]);
const result: string[] = [];
// Trim away prefix and suffix
for (const match of matches) {
const normalizedMatch = normalizePath(match);
if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) {
continue;
}
const start = completePrefix.length;
const length = normalizedMatch.length - start - normalizedSuffix.length;
result.push(removeFileExtension(normalizedMatch.substr(start, length)));
}
return result;
}
}
return undefined;
}
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions): string[] {
// Check If this is a nested module
const isNestedModule = fragment.indexOf(directorySeparator) !== -1;
const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined;
// Get modules that the type checker picked up
const ambientModules = map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name));
let nonRelativeModules = filter(ambientModules, moduleName => startsWith(moduleName, fragment));
// Nested modules of the form "module-name/sub" need to be adjusted to only return the string
// after the last '/' that appears in the fragment because that's where the replacement span
// starts
if (isNestedModule) {
const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment);
nonRelativeModules = map(nonRelativeModules, moduleName => {
if (startsWith(fragment, moduleNameWithSeperator)) {
return moduleName.substr(moduleNameWithSeperator.length);
}
return moduleName;
});
}
if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) {
for (const visibleModule of enumerateNodeModulesVisibleToScript(host, scriptPath)) {
if (!isNestedModule) {
nonRelativeModules.push(visibleModule.moduleName);
}
else if (host.readDirectory && startsWith(visibleModule.moduleName, moduleNameFragment)) {
const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
for (let f of nestedFiles) {
f = normalizePath(f);
const nestedModule = removeFileExtension(getBaseFileName(f));
nonRelativeModules.push(nestedModule);
}
}
}
}
return deduplicate(nonRelativeModules);
}
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): CompletionInfo {
const token = getTokenAtPosition(sourceFile, position);
if (!token) {
return undefined;
}
const commentRanges: CommentRange[] = getLeadingCommentRanges(sourceFile.text, token.pos);
if (!commentRanges || !commentRanges.length) {
return undefined;
}
const range = forEach(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end && commentRange);
if (!range) {
return undefined;
}
const text = sourceFile.text.substr(range.pos, position - range.pos);
const match = tripleSlashDirectiveFragmentRegex.exec(text);
if (match) {
const prefix = match[1];
const kind = match[2];
const toComplete = match[3];
const scriptPath = getDirectoryPath(sourceFile.path);
let entries: CompletionEntry[];
if (kind === "path") {
// Give completions for a relative path
const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
entries = getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span, sourceFile.path);
}
else {
// Give completions based on the typings available
const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length };
entries = getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath, span);
}
return {
isMemberCompletion: false,
isNewIdentifierLocation: true,
entries
};
}
return undefined;
}
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
// Check for typings specified in compiler options
if (options.types) {
for (const moduleName of options.types) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
}
}
else if (host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
for (const root of typeRoots) {
getCompletionEntriesFromDirectories(host, options, root, span, result);
}
}
if (host.getDirectories) {
// Also get all @types typings installed in visible node_modules directories
for (const package of findPackageJsons(scriptPath)) {
const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types");
getCompletionEntriesFromDirectories(host, options, typesDir, span, result);
}
}
return result;
}
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: CompletionEntry[]) {
if (host.getDirectories && directoryProbablyExists(directory, host)) {
for (let typeDirectory of host.getDirectories(directory)) {
typeDirectory = normalizePath(typeDirectory);
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
}
}
}
function findPackageJsons(currentDir: string): string[] {
const paths: string[] = [];
let currentConfigPath: string;
while (true) {
currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json");
if (currentConfigPath) {
paths.push(currentConfigPath);
currentDir = getDirectoryPath(currentConfigPath);
const parent = getDirectoryPath(currentDir);
if (currentDir === parent) {
break;
}
currentDir = parent;
}
else {
break;
}
}
return paths;
}
function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) {
const result: VisibleModuleInfo[] = [];
if (host.readFile && host.fileExists) {
for (const packageJson of findPackageJsons(scriptPath)) {
const package = tryReadingPackageJson(packageJson);
if (!package) {
return;
}
const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules");
const foundModuleNames: string[] = [];
// Provide completions for all non @types dependencies
for (const key of nodeModulesDependencyKeys) {
addPotentialPackageNames(package[key], foundModuleNames);
}
for (const moduleName of foundModuleNames) {
const moduleDir = combinePaths(nodeModulesDir, moduleName);
result.push({
moduleName,
moduleDir
});
}
}
}
return result;
function tryReadingPackageJson(filePath: string) {
try {
const fileText = host.readFile(filePath);
return JSON.parse(fileText);
}
catch (e) {
return undefined;
}
}
function addPotentialPackageNames(dependencies: any, result: string[]) {
if (dependencies) {
for (const dep in dependencies) {
if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) {
result.push(dep);
}
}
}
}
}
function createCompletionEntryForModule(name: string, kind: string, replacementSpan: TextSpan): CompletionEntry {
return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan };
}
// Replace everything after the last directory seperator that appears
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
const index = text.lastIndexOf(directorySeparator);
const offset = index !== -1 ? index + 1 : 0;
return { start: textStart + offset, length: text.length - offset };
}
// Returns true if the path is explicitly relative to the script (i.e. relative to . or ..)
function isPathRelativeToScript(path: string) {
if (path && path.length >= 2 && path.charCodeAt(0) === CharacterCodes.dot) {
const slashIndex = path.length >= 3 && path.charCodeAt(1) === CharacterCodes.dot ? 2 : 1;
const slashCharCode = path.charCodeAt(slashIndex);
return slashCharCode === CharacterCodes.slash || slashCharCode === CharacterCodes.backslash;
}
return false;
}
function normalizeAndPreserveTrailingSlash(path: string) {
return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalizePath(path)) : normalizePath(path);
}
}
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
@ -5627,7 +6095,6 @@ namespace ts {
symbolToIndex: number[]): void {
const sourceFile = container.getSourceFile();
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
const start = findInComments ? container.getFullStart() : container.getStart();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, start, container.getEnd());
@ -5792,15 +6259,6 @@ namespace ts {
return result[index];
}
function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isNonReferenceComment);
function isNonReferenceComment(c: CommentRange): boolean {
const commentText = sourceFile.text.substring(c.pos, c.end);
return !tripleSlashDirectivePrefixRegex.test(commentText);
}
}
}
function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] {

View file

@ -67,6 +67,10 @@ namespace ts {
getProjectVersion?(): string;
useCaseSensitiveFileNames?(): boolean;
readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string;
readFile(path: string, encoding?: string): string;
fileExists(path: string): boolean;
getModuleResolutionsForFile?(fileName: string): string;
getTypeReferenceDirectiveResolutionsForFile?(fileName: string): string;
directoryExists(directoryName: string): boolean;
@ -421,6 +425,28 @@ namespace ts {
public getDefaultLibFileName(options: CompilerOptions): string {
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
}
public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
const pattern = getFileMatcherPatterns(path, extensions, exclude, include,
this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory());
return JSON.parse(this.shimHost.readDirectory(
path,
JSON.stringify(extensions),
JSON.stringify(pattern.basePaths),
pattern.excludePattern,
pattern.includeFilePattern,
pattern.includeDirectoryPattern,
depth
));
}
public readFile(path: string, encoding?: string): string {
return this.shimHost.readFile(path, encoding);
}
public fileExists(path: string): boolean {
return this.shimHost.fileExists(path);
}
}
/** A cancellation that throttles calls to the host */

View file

@ -140,6 +140,14 @@ namespace ts {
error?(s: string): void;
useCaseSensitiveFileNames?(): boolean;
/*
* LS host can optionally implement these methods to support completions for module specifiers.
* Without these methods, only completions for ambient modules will be provided.
*/
readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
readFile?(path: string, encoding?: string): string;
fileExists?(path: string): boolean;
/*
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
* if implementation is omitted then language service will use built-in module resolution logic and get answers to
@ -148,6 +156,11 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
directoryExists?(directoryName: string): boolean;
/*
* getDirectories is also required for full import and type reference completions. Without it defined, certain
* completions will not be provided
*/
getDirectories?(directoryName: string): string[];
}
@ -336,6 +349,7 @@ namespace ts {
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
PlaceOpenBraceOnNewLineForFunctions: boolean;
@ -454,6 +468,12 @@ namespace ts {
kind: string; // see ScriptElementKind
kindModifiers: string; // see ScriptElementKindModifier, comma separated
sortText: string;
/**
* An optional span that indicates the text to be replaced by this completion item. It will be
* set if the required span differs from the one generated by the default replacement behavior and should
* be used in that case
*/
replacementSpan?: TextSpan;
}
export interface CompletionEntryDetails {
@ -736,6 +756,10 @@ namespace ts {
export const constElement = "const";
export const letElement = "let";
export const directory = "directory";
export const externalModuleName = "external module name";
}
export namespace ScriptElementKindModifier {

View file

@ -1,6 +1,9 @@
// These utilities are common to multiple language service features.
/* @internal */
namespace ts {
// Matches the beginning of a triple slash directive
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
export interface ListItemInfo {
listItemIndex: number;
list: Node;
@ -704,6 +707,29 @@ namespace ts {
return false;
}
export function hasTrailingDirectorySeparator(path: string) {
const lastCharacter = path.charAt(path.length - 1);
return lastCharacter === "/" || lastCharacter === "\\";
}
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isReferenceComment);
function isReferenceComment(c: CommentRange): boolean {
const commentText = sourceFile.text.substring(c.pos, c.end);
return tripleSlashDirectivePrefixRegex.test(commentText);
}
}
export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isNonReferenceComment);
function isNonReferenceComment(c: CommentRange): boolean {
const commentText = sourceFile.text.substring(c.pos, c.end);
return !tripleSlashDirectivePrefixRegex.test(commentText);
}
}
}
// Display-part writer helpers

View file

@ -1,8 +1,8 @@
EmitSkipped: true
Diagnostics:
Cannot write file 'tests/cases/fourslash/b.js' because it would overwrite input file.
Cannot write file '/tests/cases/fourslash/b.js' because it would overwrite input file.
EmitSkipped: false
FileName : tests/cases/fourslash/a.js
FileName : /tests/cases/fourslash/a.js
function foo2() { return 30; } // no error - should emit a.js

View file

@ -1,12 +1,12 @@
EmitSkipped: false
FileName : tests/cases/fourslash/shims-pp/inputFile1.js
FileName : /tests/cases/fourslash/shims-pp/inputFile1.js
var x = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
}());
FileName : tests/cases/fourslash/shims-pp/inputFile1.d.ts
FileName : /tests/cases/fourslash/shims-pp/inputFile1.d.ts
declare var x: number;
declare class Bar {
x: string;
@ -14,14 +14,14 @@ declare class Bar {
}
EmitSkipped: false
FileName : tests/cases/fourslash/shims-pp/inputFile2.js
FileName : /tests/cases/fourslash/shims-pp/inputFile2.js
var x1 = "hello world";
var Foo = (function () {
function Foo() {
}
return Foo;
}());
FileName : tests/cases/fourslash/shims-pp/inputFile2.d.ts
FileName : /tests/cases/fourslash/shims-pp/inputFile2.d.ts
declare var x1: string;
declare class Foo {
x: string;

View file

@ -1,12 +1,12 @@
EmitSkipped: false
FileName : tests/cases/fourslash/shims/inputFile1.js
FileName : /tests/cases/fourslash/shims/inputFile1.js
var x = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
}());
FileName : tests/cases/fourslash/shims/inputFile1.d.ts
FileName : /tests/cases/fourslash/shims/inputFile1.d.ts
declare var x: number;
declare class Bar {
x: string;
@ -14,14 +14,14 @@ declare class Bar {
}
EmitSkipped: false
FileName : tests/cases/fourslash/shims/inputFile2.js
FileName : /tests/cases/fourslash/shims/inputFile2.js
var x1 = "hello world";
var Foo = (function () {
function Foo() {
}
return Foo;
}());
FileName : tests/cases/fourslash/shims/inputFile2.d.ts
FileName : /tests/cases/fourslash/shims/inputFile2.d.ts
declare var x1: string;
declare class Foo {
x: string;

View file

@ -1,12 +1,12 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js
var x = 5;
var Bar = (function () {
function Bar() {
}
return Bar;
}());
FileName : tests/cases/fourslash/inputFile1.d.ts
FileName : /tests/cases/fourslash/inputFile1.d.ts
declare var x: number;
declare class Bar {
x: string;
@ -14,14 +14,14 @@ declare class Bar {
}
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js
var x1 = "hello world";
var Foo = (function () {
function Foo() {
}
return Foo;
}());
FileName : tests/cases/fourslash/inputFile2.d.ts
FileName : /tests/cases/fourslash/inputFile2.d.ts
declare var x1: string;
declare class Foo {
x: string;

View file

@ -1,5 +1,5 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
var x;
var M = (function () {
function M() {

View file

@ -1,5 +1,5 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js
var x;
var Foo = (function () {
function Foo() {

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js.map
{"version":3,"file":"inputFile.js","sourceRoot":"","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js.map
{"version":3,"file":"inputFile.js","sourceRoot":"","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile.js
var x = 109;
var foo = "hello world";
var M = (function () {

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js.map
{"version":3,"file":"inputFile.js","sourceRoot":"sourceRootDir/","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js.map
{"version":3,"file":"inputFile.js","sourceRoot":"sourceRootDir/","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile.js
var x = 109;
var foo = "hello world";
var M = (function () {

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"sourceRootDir/","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"sourceRootDir/","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js
var x = 109;
var foo = "hello world";
var M = (function () {
@ -10,8 +10,8 @@ var M = (function () {
}());
//# sourceMappingURL=inputFile1.js.map
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js.map
{"version":3,"file":"inputFile2.js","sourceRoot":"sourceRootDir/","sources":["inputFile2.ts"],"names":[],"mappings":"AAAA,IAAI,GAAG,GAAG,wBAAwB,CAAC;AACnC;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js.map
{"version":3,"file":"inputFile2.js","sourceRoot":"sourceRootDir/","sources":["inputFile2.ts"],"names":[],"mappings":"AAAA,IAAI,GAAG,GAAG,wBAAwB,CAAC;AACnC;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile2.js
var bar = "hello world Typescript";
var C = (function () {
function C() {

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js
// regular ts file
var t = 5;
var Bar = (function () {
@ -8,7 +8,7 @@ var Bar = (function () {
}
return Bar;
}());
//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts
//# sourceMappingURL=inputFile1.js.mapFileName : /tests/cases/fourslash/inputFile1.d.ts
declare var t: number;
declare class Bar {
x: string;
@ -16,11 +16,11 @@ declare class Bar {
}
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.jsx.map
{"version":3,"file":"inputFile2.jsx","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAG,CAAA"}FileName : tests/cases/fourslash/inputFile2.jsx
FileName : /tests/cases/fourslash/inputFile2.jsx.map
{"version":3,"file":"inputFile2.jsx","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAG,CAAA"}FileName : /tests/cases/fourslash/inputFile2.jsx
var y = "my div";
var x = <div name={y}/>;
//# sourceMappingURL=inputFile2.jsx.mapFileName : tests/cases/fourslash/inputFile2.d.ts
//# sourceMappingURL=inputFile2.jsx.mapFileName : /tests/cases/fourslash/inputFile2.d.ts
declare var y: string;
declare var x: any;

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js.map
{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js
// regular ts file
var t = 5;
var Bar = (function () {
@ -8,7 +8,7 @@ var Bar = (function () {
}
return Bar;
}());
//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts
//# sourceMappingURL=inputFile1.js.mapFileName : /tests/cases/fourslash/inputFile1.d.ts
declare var t: number;
declare class Bar {
x: string;
@ -16,11 +16,11 @@ declare class Bar {
}
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js.map
{"version":3,"file":"inputFile2.js","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AACA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,6BAAK,IAAI,EAAG,CAAC,GAAI,CAAA"}FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js.map
{"version":3,"file":"inputFile2.js","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AACA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,6BAAK,IAAI,EAAG,CAAC,GAAI,CAAA"}FileName : /tests/cases/fourslash/inputFile2.js
var y = "my div";
var x = React.createElement("div", { name: y });
//# sourceMappingURL=inputFile2.js.mapFileName : tests/cases/fourslash/inputFile2.d.ts
//# sourceMappingURL=inputFile2.js.mapFileName : /tests/cases/fourslash/inputFile2.d.ts
declare var React: any;
declare var y: string;
declare var x: any;

View file

@ -1,7 +1,7 @@
EmitSkipped: false
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js
var x1 = "hello world";
var Foo = (function () {
function Foo() {

View file

@ -1,7 +1,7 @@
EmitSkipped: false
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile2.js
FileName : /tests/cases/fourslash/inputFile2.js
"use strict";
var Foo = (function () {
function Foo() {
@ -11,6 +11,6 @@ var Foo = (function () {
exports.Foo = Foo;
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile3.js
FileName : /tests/cases/fourslash/inputFile3.js
var x = "hello";

View file

@ -1,5 +1,5 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js
// File contains early errors. All outputs should be skipped.
var uninitialized_const_error;

View file

@ -1,7 +1,7 @@
EmitSkipped: true
Diagnostics:
Exported variable 'foo' has or is using private name 'C'.
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
var M;
(function (M) {
var C = (function () {

View file

@ -1,7 +1,7 @@
EmitSkipped: true
Diagnostics:
Exported variable 'foo' has or is using private name 'C'.
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
define(["require", "exports"], function (require, exports) {
"use strict";
var C = (function () {

View file

@ -1,4 +1,4 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
var x = "hello world";

View file

@ -1,6 +1,6 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
var x = "hello world";
FileName : tests/cases/fourslash/inputFile.d.ts
FileName : /tests/cases/fourslash/inputFile.d.ts
declare var x: number;

View file

@ -1,8 +1,8 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js
// File to emit, does not contain semantic errors
// expected to be emitted correctelly regardless of the semantic errors in the other file
var noErrors = true;
FileName : tests/cases/fourslash/inputFile1.d.ts
FileName : /tests/cases/fourslash/inputFile1.d.ts
declare var noErrors: boolean;

View file

@ -1,5 +1,5 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile1.js
FileName : /tests/cases/fourslash/inputFile1.js
// File to emit, does not contain syntactic errors
// expected to be emitted correctelly regardless of the syntactic errors in the other file
var noErrors = true;

View file

@ -1,4 +1,4 @@
EmitSkipped: false
FileName : tests/cases/fourslash/inputFile.js
FileName : /tests/cases/fourslash/inputFile.js
var x;

View file

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// Should define spans for replacement that appear after the last directory seperator in import statements
// @typeRoots: my_typings
// @Filename: test.ts
//// import * as foo0 from "./[|some|]/*0*/
//// import * as foo1 from "./sub/[|some|]/*1*/
//// import * as foo2 from "[|some-|]/*2*/"
//// import * as foo3 from "../[||]/*3*/";
// @Filename: someFile1.ts
//// /*someFile1*/
// @Filename: sub/someFile2.ts
//// /*someFile2*/
// @Filename: my_typings/some-module/index.d.ts
//// export var x = 9;
goTo.marker("0");
verify.completionListContains("someFile1", undefined, undefined, undefined, 0);
goTo.marker("1");
verify.completionListContains("someFile2", undefined, undefined, undefined, 1);
goTo.marker("2");
verify.completionListContains("some-module", undefined, undefined, undefined, 2);
goTo.marker("3");
verify.completionListContains("fourslash", undefined, undefined, undefined, 3);

View file

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// Should define spans for replacement that appear after the last directory seperator in triple slash references
// @typeRoots: my_typings
// @Filename: test.ts
//// /// <reference path="./[|some|]/*0*/
//// /// <reference types="[|some|]/*1*/
//// /// <reference path="./sub/[|some|]/*2*/" />
//// /// <reference types="[|some|]/*3*/" />
// @Filename: someFile.ts
//// /*someFile*/
// @Filename: sub/someOtherFile.ts
//// /*someOtherFile*/
// @Filename: my_typings/some-module/index.d.ts
//// export var x = 9;
goTo.marker("0");
verify.completionListContains("someFile.ts", undefined, undefined, undefined, 0);
goTo.marker("1");
verify.completionListContains("some-module", undefined, undefined, undefined, 1);
goTo.marker("2");
verify.completionListContains("someOtherFile.ts", undefined, undefined, undefined, 2);
goTo.marker("3");
verify.completionListContains("some-module", undefined, undefined, undefined, 3);

View file

@ -0,0 +1,63 @@
/// <reference path='fourslash.ts' />
// Should give completions for node modules and files within those modules with ts file extensions
// @Filename: tests/test0.ts
//// import * as foo1 from "f/*import_as0*/
//// import * as foo2 from "fake-module//*import_as1*/
//// import * as foo3 from "fake-module/*import_as2*/
//// import foo4 = require("f/*import_equals0*/
//// import foo5 = require("fake-module//*import_equals1*/
//// import foo6 = require("fake-module/*import_equals2*/
//// var foo7 = require("f/*require0*/
//// var foo8 = require("fake-module//*require1*/
//// var foo9 = require("fake-module/*require2*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } }
// @Filename: node_modules/fake-module/index.js
//// /*fake-module*/
// @Filename: node_modules/fake-module/index.d.ts
//// /*fakemodule-d-ts*/
// @Filename: node_modules/fake-module/ts.ts
//// /*ts*/
// @Filename: node_modules/fake-module/dts.d.ts
//// /*dts*/
// @Filename: node_modules/fake-module/tsx.tsx
//// /*tsx*/
// @Filename: node_modules/fake-module/js.js
//// /*js*/
// @Filename: node_modules/fake-module/jsx.jsx
//// /*jsx*/
// @Filename: node_modules/fake-module-dev/index.js
//// /*fakemodule-dev*/
// @Filename: node_modules/fake-module-dev/index.d.ts
//// /*fakemodule-dev-d-ts*/
// @Filename: node_modules/unlisted-module/index.ts
//// /*unlisted-module*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("fake-module");
verify.completionListContains("fake-module-dev");
verify.not.completionListItemsCountIsGreaterThan(2);
goTo.marker(kind + "1");
verify.completionListContains("index");
verify.completionListContains("ts");
verify.completionListContains("dts");
verify.completionListContains("tsx");
verify.not.completionListItemsCountIsGreaterThan(4);
goTo.marker(kind + "2");
verify.completionListContains("fake-module");
verify.completionListContains("fake-module-dev");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,35 @@
/// <reference path='fourslash.ts' />
// Should not give node module completions if classic module resolution is enabled
// @moduleResolution: classic
// @Filename: dir1/dir2/dir3/dir4/test0.ts
//// import * as foo1 from "f/*import_as0*/
//// import * as foo3 from "fake-module/*import_as1*/
//// import foo4 = require("f/*import_equals0*/
//// import foo6 = require("fake-module/*import_equals1*/
//// var foo7 = require("f/*require0*/
//// var foo9 = require("fake-module/*require1*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" } }
// @Filename: node_modules/fake-module/ts.ts
//// /*module1*/
// @Filename: dir1/dir2/dir3/package.json
//// { "dependencies": { "fake-module3": "latest" } }
// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts
//// /*module3*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListIsEmpty();
goTo.marker(kind + "1");
verify.completionListIsEmpty();
}

View file

@ -0,0 +1,38 @@
/// <reference path='fourslash.ts' />
// Should handle nested files in folders discovered via the baseUrl compiler option
// @baseUrl: tests/cases/fourslash/modules
// @Filename: tests/test0.ts
//// import * as foo1 from "subfolder//*import_as0*/
//// import foo2 = require("subfolder//*import_equals0*/
//// var foo3 = require("subfolder//*require0*/
//// import * as foo1 from "module-from-node//*import_as1*/
//// import foo2 = require("module-from-node//*import_equals1*/
//// var foo3 = require("module-from-node//*require1*/
// @Filename: modules/subfolder/module.ts
//// export var x = 5;
// @Filename: package.json
//// { "dependencies": { "module-from-node": "latest" } }
// @Filename: node_modules/module-from-node/index.ts
//// /*module1*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker(kind + "1");
verify.completionListContains("index");
verify.not.completionListItemsCountIsGreaterThan(1);
}

View file

@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// Should give completions for all dependencies in package.json
// @Filename: tests/test0.ts
//// import * as foo1 from "m/*import_as0*/
//// import foo2 = require("m/*import_equals0*/
//// var foo3 = require("m/*require0*/
// @Filename: package.json
//// {
//// "dependencies": { "module": "latest" },
//// "devDependencies": { "dev-module": "latest" },
//// "optionalDependencies": { "optional-module": "latest" },
//// "peerDependencies": { "peer-module": "latest" }
//// }
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module");
verify.completionListContains("dev-module");
verify.completionListContains("optional-module");
verify.completionListContains("peer-module");
verify.not.completionListItemsCountIsGreaterThan(4);
}

View file

@ -0,0 +1,37 @@
/// <reference path='fourslash.ts' />
// Should not give duplicate entries for similarly named files with different extensions
// @Filename: tests/test0.ts
//// import * as foo1 from "fake-module//*import_as0*/
//// import foo2 = require("fake-module//*import_equals0*/
//// var foo3 = require("fake-module//*require0*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } }
// @Filename: node_modules/fake-module/repeated.ts
//// /*repeatedts*/
// @Filename: node_modules/fake-module/repeated.tsx
//// /*repeatedtsx*/
// @Filename: node_modules/fake-module/repeated.d.ts
//// /*repeateddts*/
// @Filename: node_modules/fake-module/other.js
//// /*other*/
// @Filename: node_modules/fake-module/other2.js
//// /*other2*/
// @Filename: node_modules/unlisted-module/index.js
//// /*unlisted-module*/
// @Filename: ambient.ts
//// declare module "fake-module/other"
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("repeated");
verify.completionListContains("other");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,38 @@
/// <reference path='fourslash.ts' />
// Should give completions for js files in node modules when allowJs is set to true
// @allowJs: true
// @Filename: tests/test0.ts
//// import * as foo1 from "fake-module//*import_as0*/
//// import foo2 = require("fake-module//*import_equals0*/
//// var foo3 = require("fake-module//*require0*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" } }
// @Filename: node_modules/fake-module/ts.ts
//// /*ts*/
// @Filename: node_modules/fake-module/tsx.tsx
//// /*tsx*/
// @Filename: node_modules/fake-module/dts.d.ts
//// /*dts*/
// @Filename: node_modules/fake-module/js.js
//// /*js*/
// @Filename: node_modules/fake-module/jsx.jsx
//// /*jsx*/
// @Filename: node_modules/fake-module/repeated.js
//// /*repeatedjs*/
// @Filename: node_modules/fake-module/repeated.jsx
//// /*repeatedjsx*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("ts");
verify.completionListContains("tsx");
verify.completionListContains("dts");
verify.not.completionListItemsCountIsGreaterThan(3);
}

View file

@ -0,0 +1,34 @@
/// <reference path='fourslash.ts' />
// Should give completions for all node modules visible to the script
// @Filename: dir1/dir2/dir3/dir4/test0.ts
//// import * as foo1 from "f/*import_as0*/
//// import foo4 = require("f/*import_equals0*/
//// var foo7 = require("f/*require0*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" } }
// @Filename: node_modules/fake-module/ts.ts
//// /*module1*/
// @Filename: dir1/package.json
//// { "dependencies": { "fake-module2": "latest" } }
// @Filename: dir1/node_modules/fake-module2/index.ts
//// /*module2*/
// @Filename: dir1/dir2/dir3/package.json
//// { "dependencies": { "fake-module3": "latest" } }
// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts
//// /*module3*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("fake-module");
verify.completionListContains("fake-module2");
verify.completionListContains("fake-module3");
verify.not.completionListItemsCountIsGreaterThan(3);
}

View file

@ -0,0 +1,39 @@
/// <reference path='fourslash.ts' />
// Should give completions for ambiently declared modules
// @Filename: test0.ts
//// /// <reference path="./ambientModules.d.ts" />
//// /// <reference path="./ambientModules2.d.ts" />
//// import * as foo1 from "/*import_as0*/
//// import * as foo2 from "a/*import_as1*/
//// import foo3 = require("/*import_equals0*/
//// import foo4 = require("a/*import_equals1*/
//// var foo5 = require("/*require0*/
//// var foo6 = require("a/*require1*/
// @Filename: ambientModules.d.ts
//// declare module "ambientModule" {}
//// declare module "otherAmbientModule" {} /*dummy0*/
// @Filename: ambientModules2.d.ts
//// declare module "otherOtherAmbientModule" {} /*dummy1*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("ambientModule");
verify.completionListContains("otherAmbientModule");
verify.completionListContains("otherOtherAmbientModule");
verify.not.completionListItemsCountIsGreaterThan(3);
goTo.marker(kind + "1");
verify.completionListContains("ambientModule");
verify.not.completionListItemsCountIsGreaterThan(1);
}

View file

@ -0,0 +1,30 @@
/// <reference path='fourslash.ts' />
// Should give completions for files that are discovered via the baseUrl compiler option
// @baseUrl: tests/cases/fourslash/modules
// @Filename: tests/test0.ts
//// import * as foo1 from "mod/*import_as0*/
//// import foo2 = require("mod/*import_equals0*/
//// var foo3 = require("mod/*require0*/
// @Filename: modules/module.ts
//// export var x = 5;
// @Filename: package.json
//// { "dependencies": { "module-from-node": "latest" } }
// @Filename: node_modules/module-from-node/index.ts
//// /*module1*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module");
verify.completionListContains("module-from-node");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,55 @@
/// <reference path='fourslash.ts' />
// Should give completions for modules referenced via baseUrl and paths compiler options with wildcards
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": "./modules",
//// "paths": {
//// "*": [
//// "prefix/0*/suffix.ts",
//// "prefix-only/*",
//// "*/suffix-only.ts"
//// ]
//// }
//// }
//// }
// @Filename: tests/test0.ts
//// import * as foo1 from "0/*import_as0*/
//// import foo2 = require("0/*import_equals0*/
//// var foo3 = require("0/*require0*/
//// import * as foo1 from "1/*import_as1*/
//// import foo2 = require("1/*import_equals1*/
//// var foo3 = require("1/*require1*/
//// import * as foo1 from "2/*import_as2*/
//// import foo2 = require("2/*import_equals2*/
//// var foo3 = require("2/*require2*/
// @Filename: modules/prefix/00test/suffix.ts
//// export var x = 5;
// @Filename: modules/prefix-only/1test.ts
//// export var y = 5;
// @Filename: modules/2test/suffix-only.ts
//// export var z = 5;
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("0test");
goTo.marker(kind + "1");
verify.completionListContains("1test");
goTo.marker(kind + "2");
verify.completionListContains("2test");
}

View file

@ -0,0 +1,36 @@
/// <reference path='fourslash.ts' />
// Should give completions for modules referenced via baseUrl and paths compiler options with explicit name mappings
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": "./modules",
//// "paths": {
//// "module1": ["some/path/whatever.ts"],
//// "module2": ["some/other/path.ts"]
//// }
//// }
//// }
// @Filename: tests/test0.ts
//// import * as foo1 from "m/*import_as0*/
//// import foo2 = require("m/*import_equals0*/
//// var foo3 = require("m/*require0*/
// @Filename: some/path/whatever.ts
//// export var x = 9;
// @Filename: some/other/path.ts
//// export var y = 10;
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module1");
verify.completionListContains("module2");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,35 @@
/// <reference path='fourslash.ts' />
// Should give completions for typings discovered via the typeRoots compiler option
// @typeRoots: my_typings,my_other_typings
// @Filename: tests/test0.ts
//// /// <reference types="m/*types_ref0*/" />
//// import * as foo1 from "m/*import_as0*/
//// import foo2 = require("m/*import_equals0*/
//// var foo3 = require("m/*require0*/
// @Filename: my_typings/module-x/index.d.ts
//// export var x = 9;
// @Filename: my_typings/module-x/whatever.d.ts
//// export var w = 9;
// @Filename: my_typings/module-y/index.d.ts
//// export var y = 9;
// @Filename: my_other_typings/module-z/index.d.ts
//// export var z = 9;
const kinds = ["types_ref", "import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module-x");
verify.completionListContains("module-y");
verify.completionListContains("module-z");
verify.not.completionListItemsCountIsGreaterThan(3);
}

View file

@ -0,0 +1,32 @@
/// <reference path='fourslash.ts' />
// Should respect the types compiler option when giving completions
// @typeRoots: my_typings,my_other_typings
// @types: module-x,module-z
// @Filename: tests/test0.ts
//// /// <reference types="m/*types_ref0*/" />
//// import * as foo1 from "m/*import_as0*/
//// import foo2 = require("m/*import_equals0*/
//// var foo3 = require("m/*require0*/
// @Filename: my_typings/module-x/index.d.ts
//// export var x = 9;
// @Filename: my_typings/module-y/index.d.ts
//// export var y = 9;
// @Filename: my_other_typings/module-z/index.d.ts
//// export var z = 9;
const kinds = ["types_ref", "import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module-x");
verify.completionListContains("module-z");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// Should give completions for typings discovered in all visible @types directories
// @Filename: subdirectory/test0.ts
//// /// <reference types="m/*types_ref0*/" />
//// import * as foo1 from "m/*import_as0*/
//// import foo2 = require("m/*import_equals0*/
//// var foo3 = require("m/*require0*/
// @Filename: subdirectory/node_modules/@types/module-x/index.d.ts
//// export var x = 9;
// @Filename: subdirectory/package.json
//// { "dependencies": { "@types/module-x": "latest" } }
// @Filename: node_modules/@types/module-y/index.d.ts
//// export var y = 9;
// @Filename: package.json
//// { "dependencies": { "@types/module-y": "latest" } }
const kinds = ["types_ref", "import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module-x");
verify.completionListContains("module-y");
verify.not.completionListItemsCountIsGreaterThan(2);
}

View file

@ -0,0 +1,70 @@
/// <reference path='fourslash.ts' />
// Should give completions for ts files when allowJs is false
// @Filename: test0.ts
//// import * as foo1 from "./*import_as0*/
//// import * as foo2 from ".//*import_as1*/
//// import * as foo4 from "./folder//*import_as2*/
//// import foo6 = require("./*import_equals0*/
//// import foo7 = require(".//*import_equals1*/
//// import foo9 = require("./folder//*import_equals2*/
//// var foo11 = require("./*require0*/
//// var foo12 = require(".//*require1*/
//// var foo14 = require("./folder//*require2*/
// @Filename: parentTest/sub/test5.ts
//// import * as foo16 from "../g/*import_as3*/
//// import foo17 = require("../g/*import_equals3*/
//// var foo18 = require("../g/*require3*/
// @Filename: f1.ts
//// /*f1*/
// @Filename: f1.js
//// /*f1j*/
// @Filename: f1.d.ts
//// /*f1d*/
// @Filename: f2.tsx
//// /f2*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: folder/f3.ts
//// /*subf1*/
// @Filename: folder/h1.ts
//// /*subh1*/
// @Filename: parentTest/f4.ts
//// /*parentf1*/
// @Filename: parentTest/g1.ts
//// /*parentg1*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListIsEmpty();
goTo.marker(kind + "1");
verify.completionListContains("f1");
verify.completionListContains("f2");
verify.completionListContains("e1");
verify.completionListContains("folder");
verify.completionListContains("parentTest");
verify.not.completionListItemsCountIsGreaterThan(5);
goTo.marker(kind + "2");
verify.completionListContains("f3");
verify.completionListContains("h1");
verify.not.completionListItemsCountIsGreaterThan(2);
goTo.marker(kind + "3");
verify.completionListContains("f4");
verify.completionListContains("g1");
verify.completionListContains("sub");
verify.not.completionListItemsCountIsGreaterThan(3);
}

View file

@ -0,0 +1,53 @@
/// <reference path='fourslash.ts' />
// Should give completions for ts and js files when allowJs is true
// @allowJs: true
// @Filename: test0.ts
//// import * as foo1 from ".//*import_as0*/
//// import * as foo2 from "./f/*import_as1*/
//// import foo3 = require(".//*import_equals0*/
//// import foo4 = require("./f/*import_equals1*/
//// var foo5 = require(".//*require0*/
//// var foo6 = require("./f/*require1*/
// @Filename: f1.ts
//// /f1*/
// @Filename: f1.js
//// /*f1j*/
// @Filename: f1.d.ts
//// /*f1d*/
// @Filename: f2.tsx
//// /*f2*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: e2.js
//// /*e2*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("f1");
verify.completionListContains("f2");
verify.completionListContains("f3");
verify.completionListContains("f4");
verify.completionListContains("e1");
verify.completionListContains("e2");
verify.not.completionListItemsCountIsGreaterThan(6);
goTo.marker(kind + "1");
verify.completionListContains("f1");
verify.completionListContains("f2");
verify.completionListContains("f3");
verify.completionListContains("f4");
verify.completionListContains("e1");
verify.completionListContains("e2");
verify.not.completionListItemsCountIsGreaterThan(6);
}

View file

@ -0,0 +1,51 @@
/// <reference path='fourslash.ts' />
// Should give completions for absolute paths
// @Filename: tests/test0.ts
//// import * as foo1 from "/tests/cases/f/*import_as0*/
//// import * as foo2 from "/tests/cases/fourslash/*import_as1*/
//// import * as foo3 from "/tests/cases/fourslash//*import_as2*/
//// import foo4 = require("/tests/cases/f/*import_equals0*/
//// import foo5 = require("/tests/cases/fourslash/*import_equals1*/
//// import foo6 = require("/tests/cases/fourslash//*import_equals2*/
//// var foo7 = require("/tests/cases/f/*require0*/
//// var foo8 = require("/tests/cases/fourslash/*require1*/
//// var foo9 = require("/tests/cases/fourslash//*require2*/
// @Filename: f1.ts
//// /*f1*/
// @Filename: f2.tsx
//// /*f2*/
// @Filename: folder/f1.ts
//// /*subf1*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: e2.js
//// /*e2*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("fourslash");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker(kind + "1");
verify.completionListContains("fourslash");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker(kind + "2");
verify.completionListContains("f1");
verify.completionListContains("f2");
verify.completionListContains("e1");
verify.completionListContains("folder");
verify.completionListContains("tests");
verify.not.completionListItemsCountIsGreaterThan(5);
}

View file

@ -0,0 +1,52 @@
/// <reference path='fourslash.ts' />
// Should give completions for directories that are merged via the rootDirs compiler option
// @rootDirs: tests/cases/fourslash/sub/src1,tests/cases/fourslash/src2
// @Filename: src2/test0.ts
//// import * as foo1 from "./mo/*import_as0*/
//// import foo2 = require("./mo/*import_equals0*/
//// var foo3 = require("./mo/*require0*/
// @Filename: src2/module0.ts
//// export var w = 0;
// @Filename: sub/src1/module1.ts
//// export var x = 0;
// @Filename: sub/src1/module2.ts
//// export var y = 0;
// @Filename: sub/src1/more/module3.ts
//// export var z = 0;
// @Filename: f1.ts
//// /*f1*/
// @Filename: f2.tsx
//// /*f2*/
// @Filename: folder/f1.ts
//// /*subf1*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: e2.js
//// /*e2*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListContains("module0");
verify.completionListContains("module1");
verify.completionListContains("module2");
verify.completionListContains("more");
// Should not contain itself
verify.not.completionListItemsCountIsGreaterThan(4);
}

View file

@ -0,0 +1,51 @@
/// <reference path='fourslash.ts' />
// Should give completions for relative references to ts files when allowJs is false
// @Filename: test0.ts
//// /// <reference path="/*0*/
//// /// <reference path="./*1*/
//// /// <reference path="../*2*/
//// /// <reference path=".//*3*/
//// /// <reference path="./f/*4*/" />
//// /// <reference path="./parentTest//*5*/
// @Filename: parentTest/sub/test1.ts
//// /// <reference path="../g/*6*/
// @Filename: f1.ts
//// /*f1*/
// @Filename: f1.js
//// /*f1j*/
// @Filename: f1.d.ts
//// /*f1d*/
// @Filename: f2.tsx
//// /f2*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: parentTest/g1.ts
//// /*parentg1*/
for (let i = 0; i < 5; i++) {
goTo.marker("" + i);
verify.completionListContains("f1.ts");
verify.completionListContains("f1.d.ts");
verify.completionListContains("f2.tsx");
verify.completionListContains("e1.ts");
verify.completionListContains("parentTest");
verify.not.completionListItemsCountIsGreaterThan(5);
}
goTo.marker("5");
verify.completionListContains("g1.ts");
verify.completionListContains("sub");
verify.not.completionListItemsCountIsGreaterThan(2);
goTo.marker("6");
verify.completionListContains("g1.ts");
verify.completionListContains("sub");
verify.not.completionListItemsCountIsGreaterThan(2);

View file

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// Should give completions for relative references to js and ts files when allowJs is true
// @allowJs: true
// @Filename: test0.ts
//// /// <reference path="/*0*/
//// /// <reference path="./*1*/
//// /// <reference path="../*2*/
//// /// <reference path=".//*3*/
//// /// <reference path="./f/*4*/" />
// @Filename: f1.ts
//// /*f1*/
// @Filename: f1.js
//// /*f1j*/
// @Filename: f1.d.ts
//// /*f1d*/
// @Filename: f2.tsx
//// /f2*/
// @Filename: f4.jsx
//// /*f4*/
for (let i = 0; i < 5; i++) {
goTo.marker("" + i);
verify.completionListContains("f1.ts");
verify.completionListContains("f1.js");
verify.completionListContains("f1.d.ts");
verify.completionListContains("f2.tsx");
verify.completionListContains("f4.jsx");
verify.not.completionListItemsCountIsGreaterThan(5);
}

View file

@ -0,0 +1,43 @@
/// <reference path='fourslash.ts' />
// Should give completions for absolute paths
// @Filename: tests/test0.ts
//// /// <reference path="/tests/cases/f/*0*/
// @Filename: tests/test1.ts
//// /// <reference path="/tests/cases/fourslash/*1*/
// @Filename: tests/test2.ts
//// /// <reference path="/tests/cases/fourslash//*2*/
// @Filename: f1.ts
//// /*f1*/
// @Filename: f2.tsx
//// /*f2*/
// @Filename: folder/f1.ts
//// /*subf1*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: e2.js
//// /*e2*/
goTo.marker("0");
verify.completionListContains("fourslash");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker("1");
verify.completionListContains("fourslash");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker("2");
verify.completionListContains("f1.ts");
verify.completionListContains("f2.tsx");
verify.completionListContains("e1.ts");
verify.completionListContains("folder");
verify.completionListContains("tests");
verify.not.completionListItemsCountIsGreaterThan(5);

View file

@ -0,0 +1,43 @@
/// <reference path='fourslash.ts' />
// Should NOT give completions for directories that are merged via the rootDirs compiler option
// @rootDirs: sub/src1,src2
// @Filename: src2/test0.ts
//// /// <reference path="./mo/*0*/
// @Filename: src2/module0.ts
//// export var w = 0;
// @Filename: sub/src1/module1.ts
//// export var x = 0;
// @Filename: sub/src1/module2.ts
//// export var y = 0;
// @Filename: sub/src1/more/module3.ts
//// export var z = 0;
// @Filename: f1.ts
//// /*f1*/
// @Filename: f2.tsx
//// /*f2*/
// @Filename: folder/f1.ts
//// /*subf1*/
// @Filename: f3.js
//// /*f3*/
// @Filename: f4.jsx
//// /*f4*/
// @Filename: e1.ts
//// /*e1*/
// @Filename: e2.js
//// /*e2*/
goTo.marker("0");
verify.completionListContains("module0.ts");
verify.not.completionListItemsCountIsGreaterThan(1);

View file

@ -12,6 +12,7 @@
////}
/////*PlaceOpenBraceOnNewLineForControlBlocks*/if (true) {
////}
/////*InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces*/{ var t = 1};
runTest("InsertSpaceAfterCommaDelimiter", "[1, 2, 3];[72,];", "[1,2,3];[72,];");
runTest("InsertSpaceAfterSemicolonInForStatements", "for (i = 0; i; i++);", "for (i = 0;i;i++);");
@ -23,6 +24,7 @@ runTest("InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets", "[ 1 ];[];[];
runTest("InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces", "`${ 1 }`; `${ 1 }`", "`${1}`; `${1}`");
runTest("PlaceOpenBraceOnNewLineForFunctions", "class foo", "class foo {");
runTest("PlaceOpenBraceOnNewLineForControlBlocks", "if ( true )", "if ( true ) {");
runTest("InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces", "{ var t = 1 };", "{var t = 1};");
function runTest(propertyName: string, expectedStringWhenTrue: string, expectedStringWhenFalse: string) {

View file

@ -121,7 +121,7 @@ declare namespace FourSlashInterface {
constructor(negative?: boolean);
memberListContains(symbol: string, text?: string, documenation?: string, kind?: string): void;
memberListCount(expectedCount: number): void;
completionListContains(symbol: string, text?: string, documentation?: string, kind?: string): void;
completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number): void;
completionListItemsCountIsGreaterThan(count: number): void;
completionListIsEmpty(): void;
completionListAllowsNewIdentifier(): void;

View file

@ -4,7 +4,7 @@
// @module: CommonJS
// @declaration: true
// @out: declSingleFile.js
// @outDir: tests/cases/fourslash/
// @outDir: /tests/cases/fourslash/
// @Filename: inputFile1.ts
//// var x: number = 5;

View file

@ -12,4 +12,4 @@
////var y = new DefaultExportedClass;
goTo.marker();
verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass');
verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass');

View file

@ -12,4 +12,4 @@
////var y = new DefaultExportedClass;
goTo.marker();
verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass');
verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass');

View file

@ -12,4 +12,4 @@
////var y = new /**/[|DefaultExportedClass|];
goTo.marker();
verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass');
verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass');

View file

@ -13,4 +13,4 @@
////var y = DefaultExportedFunction();
goTo.marker();
verify.renameInfoSucceeded("DefaultExportedFunction", '"tests/cases/fourslash/foo".DefaultExportedFunction');
verify.renameInfoSucceeded("DefaultExportedFunction", '"/tests/cases/fourslash/foo".DefaultExportedFunction');

View file

@ -13,4 +13,4 @@
////var y = DefaultExportedFunction();
goTo.marker();
verify.renameInfoSucceeded("DefaultExportedFunction", '"tests/cases/fourslash/foo".DefaultExportedFunction');
verify.renameInfoSucceeded("DefaultExportedFunction", '"/tests/cases/fourslash/foo".DefaultExportedFunction');