mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 11:35:42 -06:00
Adding completions for import and reference directives
This commit is contained in:
parent
2aa1d718ab
commit
c06b02ac34
@ -2,6 +2,8 @@
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
const ambientModuleSymbolRegex = /^".+"$/;
|
||||
|
||||
let nextSymbolId = 1;
|
||||
let nextNodeId = 1;
|
||||
let nextMergeId = 1;
|
||||
@ -101,6 +103,7 @@ namespace ts {
|
||||
getAliasedSymbol: resolveAlias,
|
||||
getEmitResolver,
|
||||
getExportsOfModule: getExportsOfModuleAsArray,
|
||||
getAmbientModules,
|
||||
|
||||
getJsxElementAttributesType,
|
||||
getJsxIntrinsicTagNames,
|
||||
@ -19104,5 +19107,15 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getAmbientModules(): Symbol[] {
|
||||
const result: Symbol[] = [];
|
||||
for (const sym in globals) {
|
||||
if (globals.hasOwnProperty(sym) && ambientModuleSymbolRegex.test(sym)) {
|
||||
result.push(globals[sym]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,9 @@ namespace ts {
|
||||
|
||||
const defaultTypeRoots = ["node_modules/@types"];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1854,6 +1854,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[];
|
||||
|
||||
@ -742,14 +742,14 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) {
|
||||
const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames());
|
||||
const fs = new Utils.VirtualFileSystem<string>(path, useCaseSensitiveFileNames());
|
||||
for (const file in listFiles(path)) {
|
||||
fs.addFile(file);
|
||||
}
|
||||
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => {
|
||||
const entry = fs.traversePath(path);
|
||||
if (entry && entry.isDirectory()) {
|
||||
const directory = <Utils.VirtualDirectory>entry;
|
||||
const directory = <Utils.VirtualDirectory<string>>entry;
|
||||
return {
|
||||
files: ts.map(directory.getFiles(), f => f.name),
|
||||
directories: ts.map(directory.getDirectories(), d => d.name)
|
||||
|
||||
@ -123,7 +123,7 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
export class LanguageServiceAdapterHost {
|
||||
protected fileNameToScript: ts.Map<ScriptInfo> = {};
|
||||
protected virtualFileSystem: Utils.VirtualFileSystem<ScriptInfo> = new Utils.VirtualFileSystem<ScriptInfo>(/*root*/"c:", /*useCaseSensitiveFilenames*/false);
|
||||
|
||||
constructor(protected cancellationToken = DefaultHostCancellationToken.Instance,
|
||||
protected settings = ts.getDefaultCompilerOptions()) {
|
||||
@ -135,7 +135,8 @@ namespace Harness.LanguageService {
|
||||
|
||||
public getFilenames(): string[] {
|
||||
const fileNames: string[] = [];
|
||||
ts.forEachValue(this.fileNameToScript, (scriptInfo) => {
|
||||
this.virtualFileSystem.getAllFileEntries().forEach((virtualEntry) => {
|
||||
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.
|
||||
@ -146,11 +147,12 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
public getScriptInfo(fileName: string): ScriptInfo {
|
||||
return ts.lookUp(this.fileNameToScript, fileName);
|
||||
const fileEntry = this.virtualFileSystem.traversePath(fileName);
|
||||
return fileEntry && fileEntry.isFile() ? (<Utils.VirtualFile<ScriptInfo>>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,7 +184,13 @@ namespace Harness.LanguageService {
|
||||
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
|
||||
getCompilationSettings() { return this.settings; }
|
||||
getCancellationToken() { return this.cancellationToken; }
|
||||
getDirectories(path: string): string[] { return []; }
|
||||
getDirectories(path: string): string[] {
|
||||
const dir = this.virtualFileSystem.traversePath(path);
|
||||
if (dir && dir.isDirectory()) {
|
||||
return ts.map((<Utils.VirtualDirectory<ScriptInfo>>dir).getDirectories(), (d) => ts.combinePaths(path, d.name));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
getCurrentDirectory(): string { return ""; }
|
||||
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
|
||||
getScriptFileNames(): string[] { return this.getFilenames(); }
|
||||
@ -196,6 +204,39 @@ 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,
|
||||
/*currentDirectory*/"/",
|
||||
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
|
||||
}
|
||||
readFile(path: string, encoding?: string): string {
|
||||
const snapshot = this.getScriptSnapshot(path);
|
||||
return snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
resolvePath(path: string): string {
|
||||
// Reduce away "." and ".."
|
||||
const parts = path.split("/");
|
||||
const res: string[] = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
if (parts[i] === ".") {
|
||||
continue;
|
||||
}
|
||||
else if (parts[i] === ".." && res.length > 0) {
|
||||
res.splice(res.length - 1, 1);
|
||||
}
|
||||
else {
|
||||
res.push(parts[i]);
|
||||
}
|
||||
}
|
||||
return res.join("/");
|
||||
}
|
||||
|
||||
|
||||
log(s: string): void { }
|
||||
trace(s: string): void { }
|
||||
error(s: string): void { }
|
||||
@ -299,6 +340,9 @@ namespace Harness.LanguageService {
|
||||
const snapshot = this.nativeHost.getScriptSnapshot(fileName);
|
||||
return snapshot && snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
resolvePath(path: string): string {
|
||||
return this.nativeHost.resolvePath(path);
|
||||
}
|
||||
log(s: string): void { this.nativeHost.log(s); }
|
||||
trace(s: string): void { this.nativeHost.trace(s); }
|
||||
error(s: string): void { this.nativeHost.error(s); }
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="..\compiler\commandLineParser.ts"/>
|
||||
namespace Utils {
|
||||
export class VirtualFileSystemEntry {
|
||||
fileSystem: VirtualFileSystem;
|
||||
export class VirtualFileSystemEntry<T> {
|
||||
fileSystem: VirtualFileSystem<T>;
|
||||
name: string;
|
||||
|
||||
constructor(fileSystem: VirtualFileSystem, name: string) {
|
||||
constructor(fileSystem: VirtualFileSystem<T>, name: string) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.name = name;
|
||||
}
|
||||
@ -15,15 +15,15 @@ namespace Utils {
|
||||
isFileSystem() { return false; }
|
||||
}
|
||||
|
||||
export class VirtualFile extends VirtualFileSystemEntry {
|
||||
content: string;
|
||||
export class VirtualFile<T> extends VirtualFileSystemEntry<T> {
|
||||
content: T;
|
||||
isFile() { return true; }
|
||||
}
|
||||
|
||||
export abstract class VirtualFileSystemContainer extends VirtualFileSystemEntry {
|
||||
abstract getFileSystemEntries(): VirtualFileSystemEntry[];
|
||||
export abstract class VirtualFileSystemContainer<T> extends VirtualFileSystemEntry<T> {
|
||||
abstract getFileSystemEntries(): VirtualFileSystemEntry<T>[];
|
||||
|
||||
getFileSystemEntry(name: string): VirtualFileSystemEntry {
|
||||
getFileSystemEntry(name: string): VirtualFileSystemEntry<T> {
|
||||
for (const entry of this.getFileSystemEntries()) {
|
||||
if (this.fileSystem.sameName(entry.name, name)) {
|
||||
return entry;
|
||||
@ -32,57 +32,57 @@ namespace Utils {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getDirectories(): VirtualDirectory[] {
|
||||
return <VirtualDirectory[]>ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory());
|
||||
getDirectories(): VirtualDirectory<T>[] {
|
||||
return <VirtualDirectory<T>[]>ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory());
|
||||
}
|
||||
|
||||
getFiles(): VirtualFile[] {
|
||||
return <VirtualFile[]>ts.filter(this.getFileSystemEntries(), entry => entry.isFile());
|
||||
getFiles(): VirtualFile<T>[] {
|
||||
return <VirtualFile<T>[]>ts.filter(this.getFileSystemEntries(), entry => entry.isFile());
|
||||
}
|
||||
|
||||
getDirectory(name: string): VirtualDirectory {
|
||||
getDirectory(name: string): VirtualDirectory<T> {
|
||||
const entry = this.getFileSystemEntry(name);
|
||||
return entry.isDirectory() ? <VirtualDirectory>entry : undefined;
|
||||
return entry.isDirectory() ? <VirtualDirectory<T>>entry : undefined;
|
||||
}
|
||||
|
||||
getFile(name: string): VirtualFile {
|
||||
getFile(name: string): VirtualFile<T> {
|
||||
const entry = this.getFileSystemEntry(name);
|
||||
return entry.isFile() ? <VirtualFile>entry : undefined;
|
||||
return entry.isFile() ? <VirtualFile<T>>entry : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualDirectory extends VirtualFileSystemContainer {
|
||||
private entries: VirtualFileSystemEntry[] = [];
|
||||
export class VirtualDirectory<T> extends VirtualFileSystemContainer<T> {
|
||||
private entries: VirtualFileSystemEntry<T>[] = [];
|
||||
|
||||
isDirectory() { return true; }
|
||||
|
||||
getFileSystemEntries() { return this.entries.slice(); }
|
||||
|
||||
addDirectory(name: string): VirtualDirectory {
|
||||
addDirectory(name: string): VirtualDirectory<T> {
|
||||
const entry = this.getFileSystemEntry(name);
|
||||
if (entry === undefined) {
|
||||
const directory = new VirtualDirectory(this.fileSystem, name);
|
||||
const directory = new VirtualDirectory<T>(this.fileSystem, name);
|
||||
this.entries.push(directory);
|
||||
return directory;
|
||||
}
|
||||
else if (entry.isDirectory()) {
|
||||
return <VirtualDirectory>entry;
|
||||
return <VirtualDirectory<T>>entry;
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
addFile(name: string, content?: string): VirtualFile {
|
||||
addFile(name: string, content?: T): VirtualFile<T> {
|
||||
const entry = this.getFileSystemEntry(name);
|
||||
if (entry === undefined) {
|
||||
const file = new VirtualFile(this.fileSystem, name);
|
||||
const file = new VirtualFile<T>(this.fileSystem, name);
|
||||
file.content = content;
|
||||
this.entries.push(file);
|
||||
return file;
|
||||
}
|
||||
else if (entry.isFile()) {
|
||||
const file = <VirtualFile>entry;
|
||||
const file = <VirtualFile<T>>entry;
|
||||
file.content = content;
|
||||
return file;
|
||||
}
|
||||
@ -92,8 +92,8 @@ namespace Utils {
|
||||
}
|
||||
}
|
||||
|
||||
export class VirtualFileSystem extends VirtualFileSystemContainer {
|
||||
private root: VirtualDirectory;
|
||||
export class VirtualFileSystem<T> extends VirtualFileSystemContainer<T> {
|
||||
private root: VirtualDirectory<T>;
|
||||
|
||||
currentDirectory: string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
@ -101,7 +101,7 @@ namespace Utils {
|
||||
constructor(currentDirectory: string, useCaseSensitiveFileNames: boolean) {
|
||||
super(undefined, "");
|
||||
this.fileSystem = this;
|
||||
this.root = new VirtualDirectory(this, "");
|
||||
this.root = new VirtualDirectory<T>(this, "");
|
||||
this.currentDirectory = currentDirectory;
|
||||
this.useCaseSensitiveFileNames = useCaseSensitiveFileNames;
|
||||
}
|
||||
@ -112,7 +112,7 @@ namespace Utils {
|
||||
|
||||
addDirectory(path: string) {
|
||||
const components = ts.getNormalizedPathComponents(path, this.currentDirectory);
|
||||
let directory: VirtualDirectory = this.root;
|
||||
let directory: VirtualDirectory<T> = this.root;
|
||||
for (const component of components) {
|
||||
directory = directory.addDirectory(component);
|
||||
if (directory === undefined) {
|
||||
@ -123,7 +123,7 @@ namespace Utils {
|
||||
return directory;
|
||||
}
|
||||
|
||||
addFile(path: string, content?: string) {
|
||||
addFile(path: string, content?: T) {
|
||||
const absolutePath = ts.getNormalizedAbsolutePath(path, this.currentDirectory);
|
||||
const fileName = ts.getBaseFileName(path);
|
||||
const directoryPath = ts.getDirectoryPath(absolutePath);
|
||||
@ -141,14 +141,14 @@ namespace Utils {
|
||||
}
|
||||
|
||||
traversePath(path: string) {
|
||||
let directory: VirtualDirectory = this.root;
|
||||
let directory: VirtualDirectory<T> = this.root;
|
||||
for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) {
|
||||
const entry = directory.getFileSystemEntry(component);
|
||||
if (entry === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
else if (entry.isDirectory()) {
|
||||
directory = <VirtualDirectory>entry;
|
||||
directory = <VirtualDirectory<T>>entry;
|
||||
}
|
||||
else {
|
||||
return entry;
|
||||
@ -157,9 +157,34 @@ namespace Utils {
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
getAccessibleFileSystemEntries(path: string) {
|
||||
const entry = this.traversePath(path);
|
||||
if (entry && entry.isDirectory()) {
|
||||
const directory = <VirtualDirectory<T>>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<T>[] = [];
|
||||
getFilesRecursive(this.root, fileEntries);
|
||||
return fileEntries;
|
||||
|
||||
function getFilesRecursive(dir: VirtualDirectory<T>, result: VirtualFile<T>[]) {
|
||||
dir.getFiles().forEach((e) => result.push(e));
|
||||
dir.getDirectories().forEach((subDir) => getFilesRecursive(subDir, result));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
|
||||
export class MockParseConfigHost extends VirtualFileSystem<string> implements ts.ParseConfigHost {
|
||||
constructor(currentDirectory: string, ignoreCase: boolean, files: string[]) {
|
||||
super(currentDirectory, ignoreCase);
|
||||
for (const file of files) {
|
||||
@ -170,17 +195,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: [] };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,6 +321,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
|
||||
*/
|
||||
|
||||
@ -1070,6 +1070,11 @@ namespace ts {
|
||||
error?(s: string): void;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
|
||||
resolvePath(path: 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
|
||||
@ -1943,6 +1948,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
|
||||
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
|
||||
const tripleSlashDirectiveFragmentRegex = /^\/\/\/\s*<reference\s+path\s*=\s*(?:'|")([^'"]+)$/;
|
||||
|
||||
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
|
||||
|
||||
@ -2806,6 +2814,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const enum SemanticMeaning {
|
||||
None = 0x0,
|
||||
Value = 0x1,
|
||||
@ -4094,6 +4111,10 @@ namespace ts {
|
||||
return getStringLiteralCompletionEntries(sourceFile, position);
|
||||
}
|
||||
|
||||
if (isInReferenceComment(sourceFile, position)) {
|
||||
return getTripleSlashReferenceCompletion(sourceFile, position);
|
||||
}
|
||||
|
||||
const completionData = getCompletionData(fileName, position);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
@ -4243,6 +4264,10 @@ namespace ts {
|
||||
// Get all names of properties on the expression
|
||||
return getStringLiteralCompletionEntriesFromElementAccess(node.parent);
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ImportDeclaration) {
|
||||
// Get all known module names
|
||||
return getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node);
|
||||
}
|
||||
else {
|
||||
// Otherwise, get the completions from the contextual type if one exists
|
||||
return getStringLiteralCompletionEntriesFromContextualType(<StringLiteral>node);
|
||||
@ -4314,6 +4339,258 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral) {
|
||||
const literalValue = node.text;
|
||||
let result: CompletionEntry[];
|
||||
|
||||
const isRelativePath = startsWith(literalValue, ".");
|
||||
const scriptDir = getDirectoryPath(node.getSourceFile().path);
|
||||
if (isRelativePath || isRootedDiskPath(literalValue)) {
|
||||
result = getCompletionEntriesForDirectoryFragment(literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false);
|
||||
}
|
||||
else {
|
||||
// Check for node modules
|
||||
result = getCompletionEntriesForNonRelativeModules(literalValue, scriptDir);
|
||||
}
|
||||
|
||||
return {
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: false,
|
||||
entries: result
|
||||
};
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean): CompletionEntry[] {
|
||||
// Complete the path by looking for source files and directories
|
||||
const result: CompletionEntry[] = [];
|
||||
|
||||
const toComplete = getBaseFileName(fragment);
|
||||
const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)));
|
||||
const baseDir = getDirectoryPath(absolutePath);
|
||||
|
||||
|
||||
if (directoryProbablyExists(baseDir, host)) {
|
||||
// Enumerate the available files
|
||||
const files = host.readDirectory(baseDir, extensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
files.forEach((f) => {
|
||||
const fName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f));
|
||||
|
||||
if (startsWith(fName, toComplete)) {
|
||||
result.push({
|
||||
name: fName,
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
sortText: fName
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// If possible, get folder completion as well
|
||||
if (host.getDirectories) {
|
||||
const directories = host.getDirectories(baseDir);
|
||||
directories.forEach((d) => {
|
||||
const dName = getBaseFileName(removeTrailingDirectorySeparator(d));
|
||||
|
||||
if (startsWith(dName, toComplete)) {
|
||||
result.push({
|
||||
name: ensureTrailingDirectorySeparator(dName),
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
sortText: dName
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return includeExtensions ? result : deduplicate(result, (a, b) => a.name === b.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all of the declared modules and those in node modules. Possible sources of modules:
|
||||
* Modules declared in the program
|
||||
* Modules from node_modules (i.e. those listed in package.json)
|
||||
* This includes all files that are found in node_modules/moduleName/ and node_modules/@types/moduleName/
|
||||
* with acceptable file extensions
|
||||
*/
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): CompletionEntry[] {
|
||||
return ts.map(enumeratePotentialNonRelativeModules(fragment, scriptPath), (moduleName) => {
|
||||
return {
|
||||
name: moduleName,
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
sortText: moduleName
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string) {
|
||||
const ambientSymbolNameRegex = /^"(.+?)"$/;
|
||||
|
||||
// If this is a nested module, get the module name
|
||||
const firstSeparator = fragment.indexOf(directorySeparator);
|
||||
const moduleNameFragment = firstSeparator !== -1 ? fragment.substr(0, firstSeparator) : fragment;
|
||||
const isNestedModule = fragment !== moduleNameFragment;
|
||||
|
||||
// Get modules that the type checker picked up
|
||||
const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), (sym) => {
|
||||
const match = ambientSymbolNameRegex.exec(sym.name);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
// This should never happen
|
||||
return sym.name;
|
||||
});
|
||||
let nonRelativeModules = ts.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 editors insert the completion
|
||||
// only after that character
|
||||
nonRelativeModules = ts.map(nonRelativeModules, (moduleName) => {
|
||||
if (moduleName.indexOf(directorySeparator) !== -1) {
|
||||
if (isNestedModule) {
|
||||
return moduleName.substr(fragment.lastIndexOf(directorySeparator) + 1);
|
||||
}
|
||||
}
|
||||
return moduleName;
|
||||
});
|
||||
|
||||
// Check for node_modules. We only offer completions for modules that are listed in the
|
||||
// package.json for a project for efficiency and to ensure that the completion list is
|
||||
// not polluted with sub-dependencies
|
||||
findPackageJsons(scriptPath).forEach((packageJson) => {
|
||||
const package = tryReadingPackageJson(packageJson);
|
||||
if (!package) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules");
|
||||
const foundModuleNames: string[] = [];
|
||||
|
||||
if (package.dependencies) {
|
||||
addPotentialPackageNames(package.dependencies, moduleNameFragment, foundModuleNames);
|
||||
}
|
||||
if (package.devDependencies) {
|
||||
addPotentialPackageNames(package.devDependencies, moduleNameFragment, foundModuleNames);
|
||||
}
|
||||
|
||||
foundModuleNames.forEach((moduleName) => {
|
||||
if (isNestedModule && moduleName === moduleNameFragment) {
|
||||
const moduleDir = combinePaths(nodeModulesDir, moduleName);
|
||||
if (directoryProbablyExists(moduleDir, host)) {
|
||||
// Get all possible nested module names from files with all extensions
|
||||
const nestedFiles = host.readDirectory(moduleDir, allSupportedExtensions, /*exclude*/undefined, /*include*/["./*"]);
|
||||
|
||||
// Add those with typings to the completion list
|
||||
nestedFiles.forEach((f) => {
|
||||
const nestedModule = removeFileExtension(getBaseFileName(f));
|
||||
if (hasTypeScriptFileExtension(f)) {
|
||||
nonRelativeModules.push(nestedModule);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (startsWith(moduleName, fragment)) {
|
||||
if (moduleCanBeImported(combinePaths(nodeModulesDir, moduleName))) {
|
||||
nonRelativeModules.push(moduleName);
|
||||
}
|
||||
else {
|
||||
nonRelativeModules.push(ensureTrailingDirectorySeparator(moduleName));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deduplicate(nonRelativeModules);
|
||||
}
|
||||
|
||||
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 tryReadingPackageJson(filePath: string) {
|
||||
try {
|
||||
const fileText = host.readFile(filePath);
|
||||
return JSON.parse(fileText);
|
||||
}
|
||||
catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) {
|
||||
for (const dep in dependencies) {
|
||||
if (dependencies.hasOwnProperty(dep) && startsWith(dep, prefix)) {
|
||||
result.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A module can be imported by name alone if one of the following is true:
|
||||
* It defines the "typings" property in its package.json
|
||||
* The module has a "main" export and an index.d.ts file
|
||||
* The module has an index.ts
|
||||
*/
|
||||
function moduleCanBeImported(modulePath: string): boolean {
|
||||
const packagePath = combinePaths(modulePath, "package.json");
|
||||
|
||||
let hasMainExport = false;
|
||||
if (host.fileExists(packagePath)) {
|
||||
const package = tryReadingPackageJson(packagePath);
|
||||
if (package) {
|
||||
if (package.typings) {
|
||||
return true;
|
||||
}
|
||||
hasMainExport = !!package.main;
|
||||
}
|
||||
}
|
||||
|
||||
hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js"));
|
||||
|
||||
return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts"));
|
||||
}
|
||||
|
||||
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number) {
|
||||
const node = getTokenAtPosition(sourceFile, position);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const text = sourceFile.text.substr(node.pos, position);
|
||||
const match = tripleSlashDirectiveFragmentRegex.exec(text);
|
||||
if (match) {
|
||||
const fragment = match[1];
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
return {
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: false,
|
||||
entries: getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true)
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
@ -6189,7 +6466,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());
|
||||
|
||||
@ -67,6 +67,11 @@ namespace ts {
|
||||
getProjectVersion?(): string;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
readDirectory(path: string, extensions?: string, exclude?: string, include?: string): string;
|
||||
readFile(path: string, encoding?: string): string;
|
||||
resolvePath(path: string): string;
|
||||
fileExists(path: string): boolean;
|
||||
|
||||
getModuleResolutionsForFile?(fileName: string): string;
|
||||
getTypeReferenceDirectiveResolutionsForFile?(fileName: string): string;
|
||||
directoryExists(directoryName: string): boolean;
|
||||
@ -409,6 +414,27 @@ namespace ts {
|
||||
public getDefaultLibFileName(options: CompilerOptions): string {
|
||||
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
|
||||
}
|
||||
|
||||
public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
return JSON.parse(this.shimHost.readDirectory(
|
||||
path,
|
||||
extensions ? JSON.stringify(extensions) : undefined,
|
||||
exclude ? JSON.stringify(exclude) : undefined,
|
||||
include ? JSON.stringify(include) : undefined
|
||||
));
|
||||
}
|
||||
|
||||
public readFile(path: string, encoding?: string): string {
|
||||
return this.shimHost.readFile(path, encoding);
|
||||
}
|
||||
|
||||
public resolvePath(path: string): string {
|
||||
return this.shimHost.resolvePath(path);
|
||||
}
|
||||
|
||||
public fileExists(path: string): boolean {
|
||||
return this.shimHost.fileExists(path);
|
||||
}
|
||||
}
|
||||
|
||||
/** A cancellation that throttles calls to the host */
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// import * as foo from "f/*0*/
|
||||
|
||||
// @Filename: tests/test1.ts
|
||||
//// import * as foo from "fake-module//*1*/
|
||||
|
||||
// @Filename: tests/test2.ts
|
||||
//// import * as foo from "fake-module/*2*/
|
||||
|
||||
// @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*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListContains("fake-module");
|
||||
verify.completionListContains("fake-module-dev");
|
||||
verify.not.completionListItemsCountIsGreaterThan(2);
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("index");
|
||||
verify.completionListContains("ts");
|
||||
verify.completionListContains("dts");
|
||||
verify.completionListContains("tsx");
|
||||
verify.not.completionListItemsCountIsGreaterThan(4);
|
||||
|
||||
goTo.marker("2");
|
||||
verify.completionListContains("fake-module");
|
||||
verify.completionListContains("fake-module-dev");
|
||||
verify.not.completionListItemsCountIsGreaterThan(2);
|
||||
@ -0,0 +1,32 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// import * as foo from "fake-module//*0*/
|
||||
|
||||
// @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: node_modules/@types/fake-module/other.d.ts
|
||||
//// declare module "fake-module/other" {}
|
||||
|
||||
// @Filename: node_modules/@types/unlisted-module/index.d.ts
|
||||
//// /*unlisted-types*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListContains("repeated");
|
||||
verify.completionListContains("other");
|
||||
verify.not.completionListItemsCountIsGreaterThan(2);
|
||||
@ -0,0 +1,30 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
// @allowJs: true
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// import * as foo from "fake-module//*0*/
|
||||
|
||||
// @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*/
|
||||
|
||||
goTo.marker("0");
|
||||
|
||||
verify.completionListContains("ts");
|
||||
verify.completionListContains("tsx");
|
||||
verify.completionListContains("dts");
|
||||
verify.not.completionListItemsCountIsGreaterThan(3);
|
||||
@ -0,0 +1,45 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: dir1/dir2/dir3/dir4/test0.ts
|
||||
//// import * as foo from "f/*0*/
|
||||
|
||||
// @Filename: dir1/dir2/dir3/dir4/test1.ts
|
||||
//// import * as foo from "a/*1*/
|
||||
|
||||
// @Filename: dir1/dir2/dir3/dir4/test2.ts
|
||||
//// import * as foo from "fake-module/*2*/
|
||||
|
||||
// @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/@types/fake-module2/js.d.ts
|
||||
//// declare module "ambient-module-test" {}
|
||||
|
||||
// @Filename: dir1/dir2/dir3/package.json
|
||||
//// { "dependencies": { "fake-module3": "latest" } }
|
||||
// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts
|
||||
//// /*module3*/
|
||||
|
||||
|
||||
goTo.marker("0");
|
||||
|
||||
verify.completionListContains("fake-module/");
|
||||
verify.completionListContains("fake-module2/");
|
||||
verify.completionListContains("fake-module3/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(3);
|
||||
|
||||
goTo.marker("1");
|
||||
|
||||
verify.completionListContains("ambient-module-test");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
|
||||
goTo.marker("2");
|
||||
|
||||
verify.completionListContains("fake-module/");
|
||||
verify.completionListContains("fake-module2/");
|
||||
verify.completionListContains("fake-module3/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(3);
|
||||
@ -0,0 +1,28 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: test0.ts
|
||||
//// import * as foo from "/*0*/
|
||||
|
||||
// @Filename: test1.ts
|
||||
//// import * as foo from "a/*1*/
|
||||
|
||||
// @Filename: ambientModules.d.ts
|
||||
//// declare module "ambientModule" {}
|
||||
//// declare module "otherAmbientModule" {}
|
||||
|
||||
// @Filename: ambientModules2.d.ts
|
||||
//// declare module "otherOtherAmbientModule" {}
|
||||
|
||||
|
||||
goTo.marker("0");
|
||||
|
||||
verify.completionListContains("ambientModule");
|
||||
verify.completionListContains("otherAmbientModule");
|
||||
verify.completionListContains("otherOtherAmbientModule");
|
||||
verify.not.completionListItemsCountIsGreaterThan(3);
|
||||
|
||||
goTo.marker("1");
|
||||
|
||||
verify.completionListContains("ambientModule");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// import * as foo from "module-/*0*/
|
||||
|
||||
// @Filename: package.json
|
||||
//// { "dependencies": {
|
||||
//// "module-no-main": "latest",
|
||||
//// "module-no-main-index-d-ts": "latest",
|
||||
//// "module-index-ts": "latest",
|
||||
//// "module-index-d-ts-explicit-main": "latest",
|
||||
//// "module-index-d-ts-default-main": "latest",
|
||||
//// "module-typings": "latest"
|
||||
//// } }
|
||||
|
||||
// @Filename: node_modules/module-no-main/package.json
|
||||
//// { }
|
||||
|
||||
// @Filename: node_modules/module-no-main-index-d-ts/package.json
|
||||
//// { }
|
||||
// @Filename: node_modules/module-no-main-index-d-ts/index.d.ts
|
||||
//// /*module-no-main-index-d-ts*/
|
||||
|
||||
// @Filename: node_modules/module-index-ts/package.json
|
||||
//// { }
|
||||
// @Filename: node_modules/module-index-ts/index.ts
|
||||
//// /*module-index-ts*/
|
||||
|
||||
// @Filename: node_modules/module-index-d-ts-explicit-main/package.json
|
||||
//// { "main":"./notIndex.js" }
|
||||
// @Filename: node_modules/module-index-d-ts-explicit-main/notIndex.js
|
||||
//// /*module-index-d-ts-explicit-main*/
|
||||
// @Filename: node_modules/module-index-d-ts-explicit-main/index.d.ts
|
||||
//// /*module-index-d-ts-explicit-main2*/
|
||||
|
||||
// @Filename: node_modules/module-index-d-ts-default-main/package.json
|
||||
//// { }
|
||||
// @Filename: node_modules/module-index-d-ts-default-main/index.js
|
||||
//// /*module-index-d-ts-default-main*/
|
||||
// @Filename: node_modules/module-index-d-ts-default-main/index.d.ts
|
||||
//// /*module-index-d-ts-default-main2*/
|
||||
|
||||
// @Filename: node_modules/module-typings/package.json
|
||||
//// { "typings":"./types.d.ts" }
|
||||
// @Filename: node_modules/module-typings/types.d.ts
|
||||
//// /*module-typings*/
|
||||
|
||||
|
||||
goTo.marker("0");
|
||||
|
||||
verify.completionListContains("module-no-main/");
|
||||
verify.completionListContains("module-no-main-index-d-ts/");
|
||||
verify.completionListContains("module-index-ts");
|
||||
verify.completionListContains("module-index-d-ts-explicit-main");
|
||||
verify.completionListContains("module-index-d-ts-default-main");
|
||||
verify.completionListContains("module-typings");
|
||||
verify.not.completionListItemsCountIsGreaterThan(6);
|
||||
@ -0,0 +1,77 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: test0.ts
|
||||
//// import * as foo from "./*0*/
|
||||
|
||||
// @Filename: test1.ts
|
||||
//// import * as foo from ".//*1*/
|
||||
|
||||
// @Filename: test2.ts
|
||||
//// import * as foo from "./f/*2*/
|
||||
|
||||
// @Filename: test3.ts
|
||||
//// import * as foo from "./folder//*3*/
|
||||
|
||||
// @Filename: test4.ts
|
||||
//// import * as foo from "./folder/h/*4*/
|
||||
|
||||
// @Filename: parentTest/sub/test5.ts
|
||||
//// import * as foo from "../g/*5*/
|
||||
|
||||
// @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/f1.ts
|
||||
//// /*subf1*/
|
||||
// @Filename: folder/h1.ts
|
||||
//// /*subh1*/
|
||||
// @Filename: parentTest/f1.ts
|
||||
//// /*parentf1*/
|
||||
// @Filename: parentTest/g1.ts
|
||||
//// /*parentg1*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListIsEmpty();
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("f1");
|
||||
verify.completionListContains("f2");
|
||||
verify.completionListContains("e1");
|
||||
verify.completionListContains("test0");
|
||||
verify.completionListContains("test1");
|
||||
verify.completionListContains("test2");
|
||||
verify.completionListContains("test3");
|
||||
verify.completionListContains("test4");
|
||||
verify.completionListContains("folder/");
|
||||
verify.completionListContains("parentTest/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(10);
|
||||
|
||||
goTo.marker("2");
|
||||
verify.completionListContains("f1");
|
||||
verify.completionListContains("f2");
|
||||
verify.completionListContains("folder/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(3);
|
||||
|
||||
goTo.marker("3");
|
||||
verify.completionListContains("f1");
|
||||
verify.completionListContains("h1");
|
||||
verify.not.completionListItemsCountIsGreaterThan(2);
|
||||
|
||||
goTo.marker("4");
|
||||
verify.completionListContains("h1");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
|
||||
goTo.marker("5");
|
||||
verify.completionListContains("g1");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
@ -0,0 +1,43 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
// @allowJs: true
|
||||
|
||||
// @Filename: test0.ts
|
||||
//// import * as foo from ".//*0*/
|
||||
|
||||
// @Filename: test1.ts
|
||||
//// import * as foo from "./f/*1*/
|
||||
|
||||
// @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*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListContains("f1");
|
||||
verify.completionListContains("f2");
|
||||
verify.completionListContains("f3");
|
||||
verify.completionListContains("f4");
|
||||
verify.completionListContains("e1");
|
||||
verify.completionListContains("e2");
|
||||
verify.completionListContains("test0");
|
||||
verify.completionListContains("test1");
|
||||
verify.not.completionListItemsCountIsGreaterThan(8);
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("f1");
|
||||
verify.completionListContains("f2");
|
||||
verify.completionListContains("f3");
|
||||
verify.completionListContains("f4");
|
||||
verify.not.completionListItemsCountIsGreaterThan(4);
|
||||
@ -0,0 +1,41 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// import * as foo from "c:/tests/cases/f/*0*/
|
||||
|
||||
// @Filename: tests/test1.ts
|
||||
//// import * as foo from "c:/tests/cases/fourslash/*1*/
|
||||
|
||||
// @Filename: tests/test2.ts
|
||||
//// import * as foo from "c:/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");
|
||||
verify.completionListContains("f2");
|
||||
verify.completionListContains("e1");
|
||||
verify.completionListContains("folder/");
|
||||
verify.completionListContains("tests/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(5);
|
||||
79
tests/cases/fourslash/completionForTripleSlashReference1.ts
Normal file
79
tests/cases/fourslash/completionForTripleSlashReference1.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: test0.ts
|
||||
//// /// <reference path="./*0*/
|
||||
|
||||
// @Filename: test1.ts
|
||||
//// /// <reference path=".//*1*/
|
||||
|
||||
// @Filename: test2.ts
|
||||
//// /// <reference path="./f/*2*/" />
|
||||
|
||||
// @Filename: test3.ts
|
||||
//// /// <reference path="./folder//*3*/
|
||||
|
||||
// @Filename: test4.ts
|
||||
//// /// <reference path="./folder/h/*4*/
|
||||
|
||||
// @Filename: parentTest/sub/test5.ts
|
||||
//// /// <reference path="../g/*5*/
|
||||
|
||||
// @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/f1.ts
|
||||
//// /*subf1*/
|
||||
// @Filename: folder/h1.ts
|
||||
//// /*subh1*/
|
||||
// @Filename: parentTest/f1.ts
|
||||
//// /*parentf1*/
|
||||
// @Filename: parentTest/g1.ts
|
||||
//// /*parentg1*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListIsEmpty();
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("f1.ts");
|
||||
verify.completionListContains("f1.d.ts");
|
||||
verify.completionListContains("f2.tsx");
|
||||
verify.completionListContains("e1.ts");
|
||||
verify.completionListContains("test0.ts");
|
||||
verify.completionListContains("test1.ts");
|
||||
verify.completionListContains("test2.ts");
|
||||
verify.completionListContains("test3.ts");
|
||||
verify.completionListContains("test4.ts");
|
||||
verify.completionListContains("folder/");
|
||||
verify.completionListContains("parentTest/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(11);
|
||||
|
||||
goTo.marker("2");
|
||||
verify.completionListContains("f1.ts");
|
||||
verify.completionListContains("f1.d.ts");
|
||||
verify.completionListContains("f2.tsx");
|
||||
verify.completionListContains("folder/");
|
||||
verify.not.completionListItemsCountIsGreaterThan(4);
|
||||
|
||||
goTo.marker("3");
|
||||
verify.completionListContains("f1.ts");
|
||||
verify.completionListContains("h1.ts");
|
||||
verify.not.completionListItemsCountIsGreaterThan(2);
|
||||
|
||||
goTo.marker("4");
|
||||
verify.completionListContains("h1.ts");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
|
||||
goTo.marker("5");
|
||||
verify.completionListContains("g1.ts");
|
||||
verify.not.completionListItemsCountIsGreaterThan(1);
|
||||
47
tests/cases/fourslash/completionForTripleSlashReference2.ts
Normal file
47
tests/cases/fourslash/completionForTripleSlashReference2.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
// @allowJs: true
|
||||
|
||||
// @Filename: test0.ts
|
||||
//// /// <reference path=".//*0*/
|
||||
|
||||
// @Filename: test1.ts
|
||||
//// /// <reference path="./f/*1*/
|
||||
|
||||
// @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*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListContains("f1.ts");
|
||||
verify.completionListContains("f1.js");
|
||||
verify.completionListContains("f1.d.ts");
|
||||
verify.completionListContains("f2.tsx");
|
||||
verify.completionListContains("f3.js");
|
||||
verify.completionListContains("f4.jsx");
|
||||
verify.completionListContains("e1.ts");
|
||||
verify.completionListContains("e2.js");
|
||||
verify.completionListContains("test0.ts");
|
||||
verify.completionListContains("test1.ts");
|
||||
verify.not.completionListItemsCountIsGreaterThan(10);
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("f1.ts");
|
||||
verify.completionListContains("f1.js");
|
||||
verify.completionListContains("f1.d.ts");
|
||||
verify.completionListContains("f2.tsx");
|
||||
verify.completionListContains("f3.js");
|
||||
verify.completionListContains("f4.jsx");
|
||||
verify.not.completionListItemsCountIsGreaterThan(6);
|
||||
41
tests/cases/fourslash/completionForTripleSlashReference3.ts
Normal file
41
tests/cases/fourslash/completionForTripleSlashReference3.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: tests/test0.ts
|
||||
//// /// <reference path="c:/tests/cases/f/*0*/
|
||||
|
||||
// @Filename: tests/test1.ts
|
||||
//// /// <reference path="c:/tests/cases/fourslash/*1*/
|
||||
|
||||
// @Filename: tests/test2.ts
|
||||
//// /// <reference path="c:/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);
|
||||
Loading…
x
Reference in New Issue
Block a user