Compare commits

...

3 commits

Author SHA1 Message Date
Arthur Ozga 7a67cf1796 Mid edits 2016-11-22 16:46:09 -08:00
Arthur Ozga 2131d5727e Add incomplete test 2016-11-21 14:07:01 -08:00
Arthur Ozga 70885ed301 Rename modulenameresolver 2016-11-21 14:06:13 -08:00
14 changed files with 367 additions and 27 deletions

View file

@ -318,7 +318,7 @@ namespace ts {
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
result = classicModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
@ -749,7 +749,7 @@ namespace ts {
}
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
export function classicModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state: ModuleResolutionState = { compilerOptions, host, traceEnabled };
const failedLookupLocations: string[] = [];

View file

@ -400,49 +400,64 @@ namespace ts.Completions {
/**
* 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
* * 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)
*
* We include all files that are found in node_modules/moduleName/ with acceptable file extensions.
* Note that we do not traverse the path through all of its ancestors and node_modules subdirectories as that is too
* slow and the above should include all intended packages.
*/
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan): CompletionEntry[] {
const { baseUrl, paths } = compilerOptions;
let result: CompletionEntry[];
// TODO: (arozga) do completions by path fragment
const dirPathFragment = getDirectoryPath(normalizeSlashes(fragment));
if (baseUrl) {
const fileExtensions = getSupportedExtensions(compilerOptions);
const projectDir = compilerOptions.project || host.getCurrentDirectory();
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span);
if (paths) {
if (!paths) {
result = getCompletionEntriesForDirectoryFragment(dirPathFragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span);
}
else {
result = [];
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));
}
if (!paths.hasOwnProperty(path)) {
continue;
}
const patterns = paths[path];
if (!patterns) {
continue;
}
const pathPattern = hasZeroOrOneAsteriskCharacter(path) ? tryParsePattern(path) : undefined;
if (pathPattern) {
for (const pattern of patterns) {
// TODO: (arozga) Need to match fragment against path-pattern to see if we have already partially-completed the path.
const matches = getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions);
// TODO: (arozga)
if (matches) {
for (const match of matches) {
// TODO: (arozga) need to figure out which fragment to add based on
result.push(createCompletionEntryForModule(pathPattern.prefix + match + pathPattern.suffix, 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 {
// TODO: (arozga) Check if target of pattern exists.
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
}
}
}
}
else {
result = [];
}
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
result = getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions)) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
@ -451,7 +466,14 @@ namespace ts.Completions {
return result;
}
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] {
/**
* ???
*
* TODO: (arozga) Make the fragment a path (as an argument)
* TODO: (arozga) Make the result returned be the only the last bit of the path (like in getDirFragment).
* TODO: (arozga) Draw pictures of what we think the various components look like.
*/
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] | undefined {
if (host.readDirectory) {
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
if (parsed) {
@ -486,6 +508,7 @@ namespace ts.Completions {
continue;
}
// TODO: (arozga) simplify.
const start = completePrefix.length;
const length = normalizedMatch.length - start - normalizedSuffix.length;

View file

@ -0,0 +1,34 @@
/// <reference path='fourslash.ts' />
// We don't walk the filesystem to search for modules. Instead we use cheap heuristics.
// @moduleResolution: node
// @Filename: src/test.ts
//// import {I as I0} from "/*0*/
//// import * as IModule0 from "/*1*/
//// import IRequireModule0 from "/*2*/
//// import foo2 = require("/*3*/
//// var foo3 = require("/*4*/
// @Filename: src/not_present.ts
// @Filename: src/node_modules/1.ts
// @Filename: src/node_modules/2.tsx
// @Filename: src/node_modules/3.d.ts
// @Filename: src/node_modules/4/package.json
// @Filename: src/node_modules/5/index.ts
// @Filename: src/node_modules/6/index.tsx
// @Filename: src/node_modules/7/index.d.ts
// @Filename: src/node_modules/@types/8.ts
// @Filename: src/node_modules/@types/9.tsx
// @Filename: src/node_modules/@types/10.d.ts
// @Filename: src/node_modules/@types/11/package.json
// @Filename: src/node_modules/@types/12/index.ts
// @Filename: src/node_modules/@types/13/index.tsx
// @Filename: src/node_modules/@types/14/index.d.ts
for (let marker = 0; marker < 5; marker++) {
goTo.marker(marker.toString());
verify.completionListIsEmpty();
}

View file

@ -0,0 +1,35 @@
/// <reference path='fourslash.ts' />
// if baseURL is specified but paths is not, then we offer completions for non-relative imports
// relative to the baseURL.
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": "./modules"
//// }
//// }
// @Filename: test.ts
//// import * as a from "/*0*/
//// import * as b from "dir//*1*/"
// @Filename: node_modules/bad.ts
// @Filename: modules/a.ts
//// /*marker_a*/
// @Filename: modules/a.d.ts
//// /*marker_a_d*/
// @Filename: modules/dir/b.ts
//// /*marker_b*/
goTo.marker("0");
verify.completionListContains("a");
verify.completionListContains("dir");
verify.not.completionListItemsCountIsGreaterThan(2);
goTo.marker("1");
verify.completionListContains("b");
verify.not.completionListItemsCountIsGreaterThan(1);

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1,95 @@
/// <reference path='fourslash.ts' />
// If baseURL and paths is specified, we compare against the baseUrl/paths rule.
// We do NOT do a raw comparison against baseUrl unless we specified such a paths rule.
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": "./modules"
//// }
//// }
// @Filename: tsconfig.json
//// {
//// "compilerOptions": {
//// "baseUrl": "./modules",
//// "paths": {
//// "a": ["a.ts"],
//// "b": ["b.ts"],
//// "c": ["c.ts", "c.d.ts"],
//// "d": ["dir/d.ts"],
//// "e/e": ["dir/e.ts"],
//// "*": ["dir_star/*"],
//// "prefix_*_suffix": ["dir_star/*"],
//// "i": ["i.ts"], // Target does not exist.
//// "j/j": ["dir/j.ts"] // Target does not exist.
//// }
//// }
//// }
// @Filename: test.ts
//// import * as a from "/*0*/
//// import * as b from "e//*1*/
//// import * as c from "g//*2*/
//// import * as d from "prefix_dir2//*3*/_suffix
// @Filename: node_modules/bad.ts
//// /*marker_bad*/
// @Filename: modules/also_bad.ts
//// /*marker_also_bad*/
// @Filename: modules/a.ts
//// /*marker_a*/
// @Filename: modules/b.ts
//// /*marker_b*/
// @Filename: modules/c.ts
//// /*marker_c*/
// @Filename: modules/c.d.ts
//// /*marker_c_d*/
// @Filename: modules/dir/d.ts
//// /*marker_d*/
// @Filename: modules/dir/e.ts
//// /*marker_e*/
// @Filename: modules/dir_star/f.ts
//// /*marker_f*/
// @Filename: modules/dir_star/g.ts
//// /*marker_g*/
// @Filename: modules/dir_star/dir2/h.ts
//// /*marker_h*/
goTo.marker("0");
verify.completionListContains("a");
verify.completionListContains("b");
verify.completionListContains("c");
verify.completionListContains("d");
verify.completionListContains("e");
verify.completionListContains("f");
verify.completionListContains("g");
verify.completionListContains("dir2");
verify.completionListContains("prefix_f_suffix");
verify.completionListContains("prefix_g_suffix");
verify.completionListContains("prefix_dir2_suffix");
verify.completionListContains("h");
verify.completionListContains("i");
verify.not.completionListItemsCountIsGreaterThan(5);
/*
goTo.marker("1");
verify.completionListContains("d");
verify.not.completionListItemsCountIsGreaterThan(1);
goTo.marker("2");
verify.completionListIsEmpty();
goTo.marker("2");
verify.completionListContains("h");
*/

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1 @@
/// <reference path='fourslash.ts' />

View file

@ -0,0 +1,73 @@
/// <reference path='fourslash.ts' />
// Should give completions for ts files only when allowJs is false.
// @Filename: d/exportInterface.ts
//// export interface I { x: number; }
// @Filename: d/dd/onlyHereToCreateDir.ts
//// /*dummyMarker*/
// @Filename: d/importInterface.ts
//// import {I as I0} from "/*named0*/"
//// import {I as I1} from "./*named1*/"
//// import {I as I2} from ".//*named2*/"
//// import {I as I3} from "../*named3*/"
//// import {I as I4} from "..//*named4*/"
//// import {I as I5} from "../d/*named5*/"
//// import {I as I6} from "../d//*named6*/"
//// import {I as I7} from "/tests/cases/fourslash/d//*named7*/"
//// import {I as I8} from "/tests/cases/fourslash/d/../d/../../fourslash/d//*named8*/"
////
//// import * as IModule0 from "/*module0*/"
//// import * as IModule1 from "./*module1*/"
//// import * as IModule2 from ".//*module2*/"
//// import * as IModule3 from "../*module3*/"
//// import * as IModule4 from "..//*module4*/"
//// import * as IModule5 from "../d/*module5*/"
//// import * as IModule6 from "../d//*module6*/"
//// import * as IModule7 from "/tests/cases/fourslash/d//*module7*/"
//// import * as IModule8 from "/tests/cases/fourslash/d/../d/../../fourslash/d//*module8*/"
////
//// import IRequireModule0 from "/*requireModule0*/"
//// import IRequireModule1 from "./*requireModule1*/"
//// import IRequireModule2 from ".//*requireModule2*/"
//// import IRequireModule3 from "../*requireModule3*/"
//// import IRequireModule4 from "..//*requireModule4*/"
//// import IRequireModule5 from "../d/*requireModule5*/"
//// import IRequireModule6 from "../d//*requireModule6*/"
//// import IRequireModule7 from "/tests/cases/fourslash/d//*requireModule7*/"
//// import IRequireModule8 from "/tests/cases/fourslash/d/../d/../../fourslash/d//*requireModule8*/"
const kinds = ["named", "module", "requireModule"];
function hasCurDirCompletions() {
verify.completionListContains("dd");
verify.completionListContains("exportInterface");
verify.not.completionListItemsCountIsGreaterThan(2);
}
function hasParentCompletions() {
verify.completionListContains("d");
verify.not.completionListItemsCountIsGreaterThan(1);
}
for (const kind of kinds) {
goTo.marker(`${kind}0`);
verify.completionListIsEmpty();
goTo.marker(`${kind}1`);
goTo.marker(`${kind}2`);
hasCurDirCompletions();
goTo.marker(`${kind}3`);
goTo.marker(`${kind}4`);
hasParentCompletions();
goTo.marker(`${kind}5`);
hasParentCompletions();
goTo.marker(`${kind}6`);
hasCurDirCompletions();
goTo.marker(`${kind}7`);
hasCurDirCompletions();
goTo.marker(`${kind}8`);
hasCurDirCompletions();
}

View file

@ -0,0 +1,73 @@
/// <reference path='fourslash.ts' />
// Should give completions for ts files only when allowJs is false.
// @Filename: d/exportInterface.ts
//// export interface I { x: number; }
// @Filename: d/dd/onlyHereToCreateDir.ts
//// /*dummyMarker*/
// @Filename: d/importInterface.ts
//// import {I as I0} from "/*named0*/
//// import {I as I1} from "./*named1*/
//// import {I as I2} from ".//*named2*/
//// import {I as I3} from "../*named3*/
//// import {I as I4} from "..//*named4*/
//// import {I as I5} from "../d/*named5*/
//// import {I as I6} from "../d//*named6*/
//// import {I as I7} from "/tests/cases/fourslash/d//*named7*/
//// import {I as I8} from "/tests/cases/fourslash/d/../d/../../fourslash/d//*named8*/
////
//// import * as IModule0 from "/*module0*/
//// import * as IModule1 from "./*module1*/
//// import * as IModule2 from ".//*module2*/
//// import * as IModule3 from "../*module3*/
//// import * as IModule4 from "..//*module4*/
//// import * as IModule5 from "../d/*module5*/
//// import * as IModule6 from "../d//*module6*/
//// import * as IModule7 from "/tests/cases/fourslash/d//*module7*/
//// import * as IModule8 from "/tests/cases/fourslash/d/../d/../../fourslash/d//*module8*/
////
//// import IRequireModule0 from "/*requireModule0*/
//// import IRequireModule1 from "./*requireModule1*/
//// import IRequireModule2 from ".//*requireModule2*/
//// import IRequireModule3 from "../*requireModule3*/
//// import IRequireModule4 from "..//*requireModule4*/
//// import IRequireModule5 from "../d/*requireModule5*/
//// import IRequireModule6 from "../d//*requireModule6*/
//// import IRequireModule7 from "/tests/cases/fourslash/d//*requireModule7*/
//// import IRequireModule8 from "/tests/cases/fourslash/d/../d/../../fourslash/d//*requireModule8*/
const kinds = ["named", "module", "requireModule"];
function hasCurDirCompletions() {
verify.completionListContains("dd");
verify.completionListContains("exportInterface");
verify.not.completionListItemsCountIsGreaterThan(2);
}
function hasParentCompletions() {
verify.completionListContains("d");
verify.not.completionListItemsCountIsGreaterThan(1);
}
for (const kind of kinds) {
goTo.marker(`${kind}0`);
verify.completionListIsEmpty();
goTo.marker(`${kind}1`);
goTo.marker(`${kind}2`);
hasCurDirCompletions();
goTo.marker(`${kind}3`);
goTo.marker(`${kind}4`);
hasParentCompletions();
goTo.marker(`${kind}5`);
hasParentCompletions();
goTo.marker(`${kind}6`);
hasCurDirCompletions();
goTo.marker(`${kind}7`);
hasCurDirCompletions();
goTo.marker(`${kind}8`);
hasCurDirCompletions();
}