Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated: Only auto-import from package.json #32517

Merged
Show file tree
Hide file tree
Changes from 117 commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
3e95a35
Move package.json related utils to utilities
andrewbranch Jun 10, 2019
c33bf88
Add failing test
andrewbranch Jun 10, 2019
52a0a00
Make first test pass
andrewbranch Jun 11, 2019
528f873
Don’t filter when there’s no package.json, fix scoped package imports
andrewbranch Jun 11, 2019
5e60836
Use type acquisition as a heuristic for whether a JS project is using…
andrewbranch Jun 12, 2019
9ca24b1
Make same fix in getCompletionDetails
andrewbranch Jun 12, 2019
6d582c9
Fix re-exporting
andrewbranch Jun 13, 2019
c60d078
Change JS node core module heuristic to same-file utilization
andrewbranch Jun 13, 2019
372265c
Remove unused method
andrewbranch Jun 13, 2019
e907863
Remove other unused method
andrewbranch Jun 13, 2019
410ade0
Remove unused triple-slash ref
andrewbranch Jun 13, 2019
b646317
Update comment
andrewbranch Jun 13, 2019
8b87aea
Refactor findAlias to forEachAlias to reduce iterations
andrewbranch Jun 13, 2019
d02d691
Really fix re-exporting
andrewbranch Jun 13, 2019
341fb3a
Use getModuleSpecifier instead of custom hack
andrewbranch Jun 14, 2019
426c28b
Fix offering auto imports to paths within node modules
andrewbranch Jun 14, 2019
60478c6
Rename things and make comments better
andrewbranch Jun 26, 2019
af96ac4
Add another reexport test
andrewbranch Jun 26, 2019
c3437d1
Inline `symbolHasBeenSeen`
andrewbranch Jul 11, 2019
8813f02
Simplify forEachAlias to findAlias
andrewbranch Jul 11, 2019
a5f2269
Add note that symbols is mutated
andrewbranch Jul 11, 2019
5c2421d
Symbol order doesn’t matter here
andrewbranch Jul 11, 2019
1f6e58d
Style nits
andrewbranch Jul 11, 2019
670c8f6
Add test with nested package.jsons
andrewbranch Jul 11, 2019
23daffe
Fix and add tests for export * re-exports
andrewbranch Jul 12, 2019
19f829a
Don’t fail when alias isn’t found
andrewbranch Jul 22, 2019
f953b4d
Make some easy optimizations
andrewbranch Jul 22, 2019
a1ae6e2
Clean up memoization when done
andrewbranch Jul 22, 2019
ebbe996
Remove unnecessary semicolon
andrewbranch Jul 22, 2019
d483f5f
Make getSymbolsFromOtherSourceFileExports pure
andrewbranch Jul 29, 2019
8ea4829
Cache auto imports
andrewbranch Jul 29, 2019
7eaa6c6
Revert "Cache auto imports"
andrewbranch Jul 30, 2019
46f605c
Handle merged symbols through cache
andrewbranch Jul 31, 2019
7cb6a5e
Be safer with symbol declarations, add logging
andrewbranch Jul 31, 2019
eb342dd
Improve cache invalidation for imports and exports
andrewbranch Jul 31, 2019
b805651
Check symbol presence first
andrewbranch Jul 31, 2019
3559193
Only run cache invalidation logic if there’s something to clear
andrewbranch Jul 31, 2019
a1f8c6f
Consolidate cache invalidation logic
andrewbranch Aug 1, 2019
eaa6709
Fix reuseProgramStructure test
andrewbranch Aug 1, 2019
a0ae197
Add more logging
andrewbranch Aug 1, 2019
47bf2d5
Only clear cache if symbols are different
andrewbranch Aug 1, 2019
3d8d03d
Refactor ambient module handling
andrewbranch Aug 3, 2019
16b9dbb
Start caching package.json stuff
andrewbranch Aug 6, 2019
f14754b
Support package.json searching in fourslash
andrewbranch Aug 6, 2019
9a15542
Move import suggestions cache to Project
andrewbranch Aug 6, 2019
c613772
Start making more module specifier work available without having the …
andrewbranch Aug 7, 2019
51adf97
Going to backtrack some from here
andrewbranch Aug 12, 2019
916abae
Get rid of dumb cache, fix node core modules stuff
andrewbranch Aug 12, 2019
585f1c7
Start determining changes to a file have invalidated its own auto imp…
andrewbranch Aug 13, 2019
655cb6d
Move package.json related utils to utilities
andrewbranch Jun 10, 2019
9fe59a7
Add failing test
andrewbranch Jun 10, 2019
3da34f8
Make first test pass
andrewbranch Jun 11, 2019
50e4a4e
Don’t filter when there’s no package.json, fix scoped package imports
andrewbranch Jun 11, 2019
e125ba4
Use type acquisition as a heuristic for whether a JS project is using…
andrewbranch Jun 12, 2019
d3efd46
Make same fix in getCompletionDetails
andrewbranch Jun 12, 2019
4059d74
Fix re-exporting
andrewbranch Jun 13, 2019
06dc6ca
Change JS node core module heuristic to same-file utilization
andrewbranch Jun 13, 2019
3b7caf9
Remove unused method
andrewbranch Jun 13, 2019
b6d7c38
Remove other unused method
andrewbranch Jun 13, 2019
04eacbf
Remove unused triple-slash ref
andrewbranch Jun 13, 2019
916b536
Update comment
andrewbranch Jun 13, 2019
c938d04
Refactor findAlias to forEachAlias to reduce iterations
andrewbranch Jun 13, 2019
ba6ff46
Really fix re-exporting
andrewbranch Jun 13, 2019
f266c5c
Use getModuleSpecifier instead of custom hack
andrewbranch Jun 14, 2019
b90e466
Fix offering auto imports to paths within node modules
andrewbranch Jun 14, 2019
adbce72
Rename things and make comments better
andrewbranch Jun 26, 2019
186af80
Add another reexport test
andrewbranch Jun 26, 2019
45a3b3e
Inline `symbolHasBeenSeen`
andrewbranch Jul 11, 2019
6c4cf54
Simplify forEachAlias to findAlias
andrewbranch Jul 11, 2019
47b3bfc
Add note that symbols is mutated
andrewbranch Jul 11, 2019
5b4e9ed
Symbol order doesn’t matter here
andrewbranch Jul 11, 2019
87cb6bc
Style nits
andrewbranch Jul 11, 2019
34888d5
Add test with nested package.jsons
andrewbranch Jul 11, 2019
67d332a
Fix and add tests for export * re-exports
andrewbranch Jul 12, 2019
24d080e
Don’t fail when alias isn’t found
andrewbranch Jul 22, 2019
fb180ec
Make some easy optimizations
andrewbranch Jul 22, 2019
8c5f227
Clean up memoization when done
andrewbranch Jul 22, 2019
c91a175
Remove unnecessary semicolon
andrewbranch Jul 22, 2019
5a1c450
Make getSymbolsFromOtherSourceFileExports pure
andrewbranch Jul 29, 2019
3d0b9ee
Cache auto imports
andrewbranch Jul 29, 2019
5271962
Revert "Cache auto imports"
andrewbranch Jul 30, 2019
325df3c
Handle merged symbols through cache
andrewbranch Jul 31, 2019
74989aa
Be safer with symbol declarations, add logging
andrewbranch Jul 31, 2019
e318b1b
Improve cache invalidation for imports and exports
andrewbranch Jul 31, 2019
ae21c64
Check symbol presence first
andrewbranch Jul 31, 2019
8680641
Only run cache invalidation logic if there’s something to clear
andrewbranch Jul 31, 2019
738c8db
Consolidate cache invalidation logic
andrewbranch Aug 1, 2019
8f0c929
Fix reuseProgramStructure test
andrewbranch Aug 1, 2019
adf3489
Add more logging
andrewbranch Aug 1, 2019
3e114b7
Only clear cache if symbols are different
andrewbranch Aug 1, 2019
975c7b8
Refactor ambient module handling
andrewbranch Aug 3, 2019
b521d9e
Merge branch 'enhancement/only-import-from-package-json' into more-ca…
andrewbranch Aug 16, 2019
8d002fe
Finish(?) sourceFileHasChangedOwnImportSuggestions
andrewbranch Aug 16, 2019
4e3322a
Make package.json info model better
andrewbranch Aug 16, 2019
7cecbe0
Fix misplaced paren
andrewbranch Aug 16, 2019
b57ed78
Merge branch 'more-caching' into enhancement/only-import-from-package…
andrewbranch Aug 19, 2019
0b53c08
Use file structure cache for package.json detection when possible
andrewbranch Aug 20, 2019
d77d4f2
Revert unnecessary changes in moduleSpecifiers
andrewbranch Aug 20, 2019
f7c8d35
Revert more unnecessary changes
andrewbranch Aug 20, 2019
51164a9
Don’t watch package.jsons inside node_modules, fix tests
andrewbranch Aug 20, 2019
0ad9dd8
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Aug 20, 2019
3d5eb7a
Work around declaration emit bug
andrewbranch Aug 20, 2019
b6409f4
Sync submodules?
andrewbranch Aug 20, 2019
e23ed56
Delete unused type
andrewbranch Aug 20, 2019
81b2e3d
Add server cache tests
andrewbranch Aug 21, 2019
3e79dbf
Fix server fourslash editing
andrewbranch Aug 22, 2019
81f5ca6
Fix packageJsonInfo tests
andrewbranch Aug 22, 2019
04b4602
Add node core modules cache test and fix more fourslash
andrewbranch Aug 22, 2019
779015c
Clean up symlink caching
andrewbranch Aug 23, 2019
2f09159
Improve logging
andrewbranch Aug 23, 2019
a188425
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Aug 23, 2019
28551bb
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Aug 26, 2019
97f778a
Function name doesn’t make any sense anymore
andrewbranch Aug 26, 2019
5ddd273
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Sep 13, 2019
a9b6369
Move symlinks cache to host
andrewbranch Sep 13, 2019
dcd8339
Fix markFileAsDirty from ScriptInfo
andrewbranch Sep 13, 2019
2afb54e
Mark new Project members internal
andrewbranch Sep 13, 2019
65d0255
Use Path instead of fileName
andrewbranch Sep 23, 2019
0d8568b
Rename AutoImportSuggestionsCache
andrewbranch Sep 23, 2019
ba7a5c5
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Sep 25, 2019
8ba6376
Improve WatchType description
andrewbranch Sep 26, 2019
0d34bfe
Remove entries() from packageJsonCache
andrewbranch Sep 26, 2019
368b31d
Merge branch 'master' into enhancement/only-import-from-package-json
andrewbranch Sep 27, 2019
b95cbc1
Fix path/fileName bug
andrewbranch Sep 27, 2019
6bb70f2
Also cache symlinks on Program for benefit of d.ts emit
andrewbranch Sep 27, 2019
213a370
Let language service use Program’s symlink cache
andrewbranch Sep 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 40 additions & 21 deletions src/compiler/moduleSpecifiers.ts
Expand Up @@ -64,6 +64,20 @@ namespace ts.moduleSpecifiers {
return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, files, redirectTargetsMap, getPreferences(preferences, compilerOptions, importingSourceFile));
}

export function getNodeModulesPackageName(
compilerOptions: CompilerOptions,
importingSourceFileName: Path,
nodeModulesFileName: string,
host: ModuleSpecifierResolutionHost,
files: readonly SourceFile[],
redirectTargetsMap: RedirectTargetsMap,
): string | undefined {
const info = getInfo(importingSourceFileName, host);
const modulePaths = getAllModulePaths(files, importingSourceFileName, nodeModulesFileName, info.getCanonicalFileName, host, redirectTargetsMap);
return firstDefined(modulePaths,
moduleFileName => tryGetModuleNameAsNodeModule(moduleFileName, info, host, compilerOptions, /*packageNameOnly*/ true));
}

function getModuleSpecifierWorker(
compilerOptions: CompilerOptions,
importingSourceFileName: Path,
Expand All @@ -79,7 +93,7 @@ namespace ts.moduleSpecifiers {
getLocalModuleSpecifier(toFileName, info, compilerOptions, preferences);
}

// Returns an import for each symlink and for the realpath.
/** Returns an import for each symlink and for the realpath. */
export function getModuleSpecifiers(
moduleSymbol: Symbol,
compilerOptions: CompilerOptions,
Expand Down Expand Up @@ -174,7 +188,7 @@ namespace ts.moduleSpecifiers {
return [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)];
}

function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
export function discoverProbableSymlinks(files: readonly SourceFile[], getCanonicalFileName: GetCanonicalFileName, cwd: string): ReadonlyMap<string> {
const result = createMap<string>();
const symlinks = flatten<readonly [string, string]>(mapDefined(files, sf =>
sf.resolvedModules && compact(arrayFrom(mapIterator(sf.resolvedModules.values(), res =>
Expand All @@ -195,7 +209,9 @@ namespace ts.moduleSpecifiers {
const importedFileNames = redirects ? [...redirects, importedFileName] : [importedFileName];
const cwd = host.getCurrentDirectory ? host.getCurrentDirectory() : "";
const targets = importedFileNames.map(f => getNormalizedAbsolutePath(f, cwd));
const links = discoverProbableSymlinks(files, getCanonicalFileName, cwd);
const links = host.getProbableSymlinks
? host.getProbableSymlinks(files)
: discoverProbableSymlinks(files, getCanonicalFileName, cwd);

const result: string[] = [];
const compareStrings = (!host.useCaseSensitiveFileNames || host.useCaseSensitiveFileNames()) ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
Expand Down Expand Up @@ -262,7 +278,7 @@ namespace ts.moduleSpecifiers {
: removeFileExtension(relativePath);
}

function tryGetModuleNameAsNodeModule(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions): string | undefined {
function tryGetModuleNameAsNodeModule(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions, packageNameOnly?: boolean): string | undefined {
if (!host.fileExists || !host.readFile) {
return undefined;
}
Expand All @@ -271,30 +287,33 @@ namespace ts.moduleSpecifiers {
return undefined;
}

let packageJsonContent: any | undefined;
const packageRootPath = moduleFileName.substring(0, parts.packageRootIndex);
const packageJsonPath = combinePaths(packageRootPath, "package.json");
const packageJsonContent = host.fileExists(packageJsonPath)
? JSON.parse(host.readFile(packageJsonPath)!)
: undefined;
const versionPaths = packageJsonContent && packageJsonContent.typesVersions
? getPackageJsonTypesVersionsPaths(packageJsonContent.typesVersions)
: undefined;
if (versionPaths) {
const subModuleName = moduleFileName.slice(parts.packageRootIndex + 1);
const fromPaths = tryGetModuleNameFromPaths(
removeFileExtension(subModuleName),
removeExtensionAndIndexPostFix(subModuleName, Ending.Minimal, options),
versionPaths.paths
);
if (fromPaths !== undefined) {
moduleFileName = combinePaths(moduleFileName.slice(0, parts.packageRootIndex), fromPaths);
if (!packageNameOnly) {
const packageJsonPath = combinePaths(packageRootPath, "package.json");
packageJsonContent = host.fileExists(packageJsonPath)
? JSON.parse(host.readFile(packageJsonPath)!)
: undefined;
const versionPaths = packageJsonContent && packageJsonContent.typesVersions
? getPackageJsonTypesVersionsPaths(packageJsonContent.typesVersions)
: undefined;
if (versionPaths) {
const subModuleName = moduleFileName.slice(parts.packageRootIndex + 1);
const fromPaths = tryGetModuleNameFromPaths(
removeFileExtension(subModuleName),
removeExtensionAndIndexPostFix(subModuleName, Ending.Minimal, options),
versionPaths.paths
);
if (fromPaths !== undefined) {
moduleFileName = combinePaths(moduleFileName.slice(0, parts.packageRootIndex), fromPaths);
}
}
}

// Simplify the full file path to something that can be resolved by Node.

// If the module could be imported by a directory name, use that directory's name
const moduleSpecifier = getDirectoryOrExtensionlessFileName(moduleFileName);
const moduleSpecifier = packageNameOnly ? moduleFileName : getDirectoryOrExtensionlessFileName(moduleFileName);
// Get a path that's relative to node_modules or the importing file's path
// if node_modules folder is in this folder or any of its parent folders, no need to keep it.
if (!startsWith(sourceDirectory, getCanonicalFileName(moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex)))) return undefined;
Expand Down
7 changes: 5 additions & 2 deletions src/compiler/types.ts
Expand Up @@ -4808,7 +4808,8 @@ namespace ts {
}

export interface TypeAcquisition {
/* @deprecated typingOptions.enableAutoDiscovery
/**
* @deprecated typingOptions.enableAutoDiscovery
* Use typeAcquisition.enable instead.
*/
enableAutoDiscovery?: boolean;
Expand Down Expand Up @@ -5921,11 +5922,13 @@ namespace ts {
directoryExists?(directoryName: string): boolean;
getCurrentDirectory?(): string;
}
/** @internal */

export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost {
useCaseSensitiveFileNames?(): boolean;
fileExists?(path: string): boolean;
readFile?(path: string): string | undefined;
/* @internal */
getProbableSymlinks?(files: readonly SourceFile[]): ReadonlyMap<string>;
}

// Note: this used to be deprecated in our public API, but is still used internally
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/utilities.ts
Expand Up @@ -7580,7 +7580,7 @@ namespace ts {

/**
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
* except that we support URL's as well.
* except that we support URLs as well.
*
* ```ts
* getDirectoryPath("/path/to/file.ext") === "/path/to"
Expand All @@ -7591,7 +7591,7 @@ namespace ts {
export function getDirectoryPath(path: Path): Path;
/**
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
* except that we support URL's as well.
* except that we support URLs as well.
*
* ```ts
* getDirectoryPath("/path/to/file.ext") === "/path/to"
Expand Down
7 changes: 7 additions & 0 deletions src/harness/client.ts
Expand Up @@ -124,6 +124,13 @@ namespace ts.server {
return response;
}

/*@internal*/
configure(preferences: UserPreferences) {
const args: protocol.ConfigureRequestArguments = { preferences };
const request = this.processRequest(CommandNames.Configure, args);
this.processResponse(request, /*expectEmptyBody*/ true);
}

openFile(file: string, fileContent?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
const args: protocol.OpenRequestArgs = { file, fileContent, scriptKindName };
this.processRequest(CommandNames.Open, args);
Expand Down
64 changes: 56 additions & 8 deletions src/harness/fourslash.ts
Expand Up @@ -421,7 +421,10 @@ namespace FourSlash {
})!;
}

public goToPosition(pos: number) {
public goToPosition(positionOrLineAndCharacter: number | ts.LineAndCharacter) {
const pos = typeof positionOrLineAndCharacter === "number"
? positionOrLineAndCharacter
: this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, positionOrLineAndCharacter);
this.currentCaretPosition = pos;
this.selectionEnd = -1;
}
Expand All @@ -447,6 +450,12 @@ namespace FourSlash {
this.selectionEnd = range.end;
}

public selectLine(index: number) {
const lineStart = this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, { line: index, character: 0 });
const lineEnd = lineStart + this.getLineContent(index).length;
this.selectRange({ fileName: this.activeFile.fileName, pos: lineStart, end: lineEnd });
}

public moveCaretRight(count = 1) {
this.currentCaretPosition += count;
this.currentCaretPosition = Math.min(this.currentCaretPosition, this.getFileContent(this.activeFile.fileName).length);
Expand Down Expand Up @@ -803,7 +812,7 @@ namespace FourSlash {
const name = typeof include === "string" ? include : include.name;
const found = nameToEntries.get(name);
if (!found) throw this.raiseError(`Includes: completion '${name}' not found.`);
assert(found.length === 1); // Must use 'exact' for multiple completions with same name
assert(found.length === 1, `Must use 'exact' for multiple completions with same name: '${name}'`);
this.verifyCompletionEntry(ts.first(found), include);
}
}
Expand Down Expand Up @@ -1081,11 +1090,23 @@ namespace FourSlash {
TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts"));
}

private configure(preferences: ts.UserPreferences) {
if (this.testType === FourSlashTestType.Server) {
(this.languageService as ts.server.SessionClient).configure(preferences);
}
}

private getCompletionListAtCaret(options?: ts.GetCompletionsAtPositionOptions): ts.CompletionInfo | undefined {
if (options) {
this.configure(options);
}
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
}

private getCompletionEntryDetails(entryName: string, source?: string, preferences?: ts.UserPreferences): ts.CompletionEntryDetails | undefined {
if (preferences) {
this.configure(preferences);
}
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, preferences);
}

Expand Down Expand Up @@ -1696,6 +1717,12 @@ namespace FourSlash {
this.checkPostEditInvariants();
}

public deleteLineRange(startIndex: number, endIndexInclusive: number) {
const startPos = this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, { line: startIndex, character: 0 });
const endPos = this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, { line: endIndexInclusive + 1, character: 0 });
this.replace(startPos, endPos - startPos, "");
}

public deleteCharBehindMarker(count = 1) {
let offset = this.currentCaretPosition;
const ch = "";
Expand All @@ -1721,6 +1748,8 @@ namespace FourSlash {
let offset = this.currentCaretPosition;
const prevChar = " ";
const checkCadence = (text.length >> 2) + 1;
const selection = this.getSelection();
this.replace(selection.pos, selection.end - selection.pos, "");

for (let i = 0; i < text.length; i++) {
const ch = text.charAt(i);
Expand Down Expand Up @@ -3059,11 +3088,9 @@ namespace FourSlash {
Harness.IO.log(stringify(codeFixes));
}

// Get the text of the entire line the caret is currently at
private getCurrentLineContent() {
private getLineContent(index: number) {
const text = this.getFileContent(this.activeFile.fileName);

const pos = this.currentCaretPosition;
const pos = this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, { line: index, character: 0 });
let startPos = pos, endPos = pos;

while (startPos > 0) {
Expand All @@ -3088,6 +3115,14 @@ namespace FourSlash {
return text.substring(startPos, endPos);
}

// Get the text of the entire line the caret is currently at
private getCurrentLineContent() {
return this.getLineContent(this.languageServiceAdapterHost.positionToLineAndCharacter(
this.activeFile.fileName,
this.currentCaretPosition,
).line);
}

private findFile(indexOrName: string | number): FourSlashFile {
if (typeof indexOrName === "number") {
const index = indexOrName;
Expand Down Expand Up @@ -3822,11 +3857,11 @@ namespace FourSlashInterface {
this.state.goToImplementation();
}

public position(position: number, fileNameOrIndex?: string | number): void {
public position(positionOrLineAndCharacter: number | ts.LineAndCharacter, fileNameOrIndex?: string | number): void {
if (fileNameOrIndex !== undefined) {
this.file(fileNameOrIndex);
}
this.state.goToPosition(position);
this.state.goToPosition(positionOrLineAndCharacter);
}

// Opens a file, given either its index as it
Expand Down Expand Up @@ -4299,6 +4334,19 @@ namespace FourSlashInterface {
this.state.type(lines.join("\n"));
}

public deleteLine(index: number) {
this.deleteLineRange(index, index);
}

public deleteLineRange(startIndex: number, endIndexInclusive: number) {
this.state.deleteLineRange(startIndex, endIndexInclusive);
}

public replaceLine(index: number, text: string) {
this.state.selectLine(index);
this.state.type(text);
}

public moveRight(count?: number) {
this.state.moveCaretRight(count);
}
Expand Down
6 changes: 6 additions & 0 deletions src/harness/harnessLanguageService.ts
Expand Up @@ -203,6 +203,12 @@ namespace Harness.LanguageService {
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
}

public lineAndCharacterToPosition(fileName: string, lineAndCharacter: ts.LineAndCharacter): number {
const script: ScriptInfo = this.getScriptInfo(fileName)!;
assert.isOk(script);
return ts.computePositionOfLineAndCharacter(script.getLineMap(), lineAndCharacter.line, lineAndCharacter.character);
}

useCaseSensitiveFileNames() {
return !this.vfs.ignoreCase;
}
Expand Down
9 changes: 8 additions & 1 deletion src/server/editorServices.ts
Expand Up @@ -1005,7 +1005,7 @@ namespace ts.server {
directory,
fileOrDirectory => {
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
const fsResult = project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);

// don't trigger callback on open, existing files
if (project.fileIsOpen(fileOrDirectoryPath)) {
Expand All @@ -1015,6 +1015,13 @@ namespace ts.server {
if (isPathIgnored(fileOrDirectoryPath)) return;
const configFilename = project.getConfigFilePath();

if (getBaseFileName(fileOrDirectoryPath) === "package.json" && !isInsideNodeModules(fileOrDirectoryPath) &&
(fsResult && fsResult.fileExists || !fsResult && this.host.fileExists(fileOrDirectoryPath))
) {
this.logger.info(`Project: ${configFilename} Detected new package.json: ${fileOrDirectory}`);
project.onAddPackageJson(fileOrDirectoryPath);
}

// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, project.getCompilationSettings(), this.hostConfiguration.extraFileExtensions)) {
Expand Down