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

Expand auto-import to all package.json dependencies #38923

Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f67fb5b
Start experiment
andrewbranch May 12, 2020
8e128d8
Add logging
andrewbranch May 13, 2020
23f72c7
Go back to a single program
andrewbranch May 14, 2020
913c380
Fix forEachExternalModuleToImportFrom
andrewbranch May 18, 2020
c1d1721
Move auxiliary program to language service
andrewbranch May 19, 2020
feb719a
Add logging
andrewbranch May 19, 2020
3bad9ee
Don’t use resolution cache
andrewbranch May 20, 2020
d0c2d50
Fix(?) containingProjects for ScriptInfo in auxiliary program
andrewbranch May 21, 2020
3554044
Fix ScriptInfo project inclusion
andrewbranch May 26, 2020
3d03027
Add test for default project of auto-importable ScriptInfo
andrewbranch May 26, 2020
bba64e2
Add fourslash server test
andrewbranch May 26, 2020
304b8a3
Don’t create auto import provider inside node_modules
andrewbranch May 26, 2020
4fed93d
Add monorepo-like test
andrewbranch May 26, 2020
958adc0
WIP
andrewbranch May 28, 2020
1ff2db5
Naively ensure autoImportProvider is up to date after package.json ch…
andrewbranch May 29, 2020
af64cd2
Start limiting when auto update provider gets updated
andrewbranch Jun 1, 2020
3d07e97
Respond to changes in node_modules
andrewbranch Jun 2, 2020
936763a
Don’t create auto-import provider until a file is open that would use it
andrewbranch Jun 3, 2020
c0066b3
Merge branch 'master' into experiment/auto-import/program-per-package…
andrewbranch Jun 3, 2020
86151e1
Clean up naming, @internal marking, and fix empty project creation bug
andrewbranch Jun 3, 2020
9c946d6
Drop devDependencies, include peerDependencies
andrewbranch Jun 5, 2020
d3c6785
Add additional compiler options
andrewbranch Jun 5, 2020
9882aeb
Merge branch 'master' into experiment/auto-import/program-per-package…
andrewbranch Jun 5, 2020
6b0441e
Fix interaction with importSuggestionsCache
andrewbranch Jun 5, 2020
40d6363
Move option to UserPreferences, allow inclusion of devDependencies
andrewbranch Jun 8, 2020
d4800ab
Don’t filter out peerDependencies
andrewbranch Jun 9, 2020
dd7240e
Watch unparseable package.jsons
andrewbranch Jun 9, 2020
8823473
But don’t filter packages out due to an invalid package.json
andrewbranch Jun 9, 2020
00b57e3
Update test
andrewbranch Jun 9, 2020
242127d
Merge branch 'master' into experiment/auto-import/program-per-package…
andrewbranch Jun 9, 2020
8d59f50
Don’t use autoImportProvider in codefixes where it can never be used …
andrewbranch Jun 9, 2020
0d4fff9
Add CompletionEntry property for telemetry
andrewbranch Jun 11, 2020
e9c7f6a
Add assertion for isPackageJsonImport to fourslash
andrewbranch Jun 11, 2020
6448144
Fix missing pushSymbol argument
andrewbranch Jun 11, 2020
955edbc
Add isPackageJsonImport to tests and API baselines
andrewbranch Jun 11, 2020
fb8fb38
Fix unit test
andrewbranch Jun 11, 2020
75f12f5
Host auto import provider in new Project kind
andrewbranch Jun 16, 2020
327efa9
Fix InferredProject attaching on AutoImportProvider-included files, l…
andrewbranch Jun 16, 2020
f638d46
Update Public APIs
andrewbranch Jun 16, 2020
b788b05
Simplify PackageJsonCache host
andrewbranch Jun 16, 2020
01a0e44
Merge branch 'master' into experiment/auto-import/program-per-package…
andrewbranch Jun 18, 2020
b6ac999
Remove unneeded markAsDirty
andrewbranch Jun 18, 2020
6e6a71d
Defer project finished event until after AutoImportProvider is created
andrewbranch Jun 18, 2020
c11a701
Make AutoImportProviderProject always report isOrphan = true
andrewbranch Jun 18, 2020
4dc2ed2
Close and remove AutoImportProviderProject when host project closes
andrewbranch Jun 18, 2020
e360fa1
Don’t set pendingEnsureProjectForOpenFiles
andrewbranch Jun 18, 2020
0c05e3a
Use hasAddedOrRemovedFiles instead of hasNewProgram
andrewbranch Jun 18, 2020
e480830
Use host-wide watchOptions for package.json watching
andrewbranch Jun 18, 2020
0cf3cf6
Add to `printProjects`
andrewbranch Jun 18, 2020
83fc465
Clean up
andrewbranch Jun 18, 2020
00e1707
Get autoImportProvider directly from LanguageServiceHost
andrewbranch Jun 18, 2020
737f2ed
Clean up
andrewbranch Jun 18, 2020
f957bd4
Clean up
andrewbranch Jun 22, 2020
8f0ad77
Close auto import provider on disableLanguageService
andrewbranch Jun 22, 2020
f7b6946
Move AutoImportProvider preload to project updateGraph
andrewbranch Jun 22, 2020
e9557ff
Clear auto import suggestion cache when provider program changes
andrewbranch Jun 22, 2020
bd15ac4
Merge branch 'master' into experiment/auto-import/program-per-package…
andrewbranch Jun 22, 2020
3f1821e
Fix tests
andrewbranch Jun 22, 2020
b2d8e34
Revert yet-unneeded change
andrewbranch Jun 22, 2020
4a667e3
Use projectService host for module resolution host
andrewbranch Jun 22, 2020
3ed641d
Don’t re-resolve type directives if nothing has changed
andrewbranch Jun 22, 2020
7b1aaf4
Update src/server/project.ts
andrewbranch Jun 22, 2020
85c6938
Use ts.emptyArray
andrewbranch Jun 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2361,7 +2361,7 @@ namespace ts {
}

const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile);
if (shouldReportNoInputFiles(result, canJsonReportNoInutFiles(raw), resolutionStack)) {
if (shouldReportNoInputFiles(result, canJsonReportNoInputFiles(raw), resolutionStack)) {
errors.push(getErrorForNoInputFiles(result.spec, configFileName));
}

Expand Down Expand Up @@ -2413,7 +2413,7 @@ namespace ts {
}

/*@internal*/
export function canJsonReportNoInutFiles(raw: any) {
export function canJsonReportNoInputFiles(raw: any) {
return !hasProperty(raw, "files") && !hasProperty(raw, "references");
}

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ namespace ts {
else if (reloadLevel === ConfigFileProgramReloadLevel.Partial) {
// Update file names
const result = getFileNamesFromConfigSpecs(config.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost);
updateErrorForNoInputFiles(result, project, config.configFileSpecs!, config.errors, canJsonReportNoInutFiles(config.raw));
updateErrorForNoInputFiles(result, project, config.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw));
config.fileNames = result.fileNames;
watchInputFiles(state, project, projectPath, config);
}
Expand Down Expand Up @@ -1371,7 +1371,7 @@ namespace ts {
}

// Container if no files are specified in the project
if (!project.fileNames.length && !canJsonReportNoInutFiles(project.raw)) {
if (!project.fileNames.length && !canJsonReportNoInputFiles(project.raw)) {
return {
type: UpToDateStatusType.ContainerOnly
};
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8024,6 +8024,7 @@ namespace ts {
readonly importModuleSpecifierEnding?: "auto" | "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
readonly providePrefixAndSuffixTextForRename?: boolean;
readonly includePackageJsonAutoImports?: "exclude-dev" | "all" | "none";
readonly provideRefactorNotApplicableReason?: boolean;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ namespace ts {
configFileSpecs = configFileParseResult.configFileSpecs!; // TODO: GH#18217
projectReferences = configFileParseResult.projectReferences;
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice();
canConfigFileJsonReportNoInputFiles = canJsonReportNoInutFiles(configFileParseResult.raw);
canConfigFileJsonReportNoInputFiles = canJsonReportNoInputFiles(configFileParseResult.raw);
hasChangedConfigFileParsingErrors = true;
}

Expand Down
13 changes: 12 additions & 1 deletion src/harness/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ namespace ts.server {
this.processResponse(request, /*expectEmptyBody*/ true);
}

/*@internal*/
setFormattingOptions(formatOptions: FormatCodeSettings) {
const args: protocol.ConfigureRequestArguments = { formatOptions };
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 Expand Up @@ -791,7 +798,11 @@ namespace ts.server {
}

getProgram(): Program {
throw new Error("SourceFile objects are not serializable through the server protocol.");
throw new Error("Program objects are not serializable through the server protocol.");
}

getAutoImportProvider(): Program | undefined {
throw new Error("Program objects are not serializable through the server protocol.");
}

getNonBoundSourceFile(_fileName: string): SourceFile {
Expand Down
53 changes: 25 additions & 28 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -871,53 +871,47 @@ namespace FourSlash {
}

private verifyCompletionEntry(actual: ts.CompletionEntry, expected: FourSlashInterface.ExpectedCompletionEntry) {
const { insertText, replacementSpan, hasAction, isRecommended, isFromUncheckedFile, kind, kindModifiers, text, documentation, tags, source, sourceDisplay, sortText } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, isFromUncheckedFile: undefined, kind: undefined, kindModifiers: undefined, text: undefined, documentation: undefined, tags: undefined, source: undefined, sourceDisplay: undefined, sortText: undefined }
: expected;
expected = typeof expected === "string" ? { name: expected } : expected;

if (actual.insertText !== insertText) {
this.raiseError(`Expected completion insert text to be ${insertText}, got ${actual.insertText}`);
if (actual.insertText !== expected.insertText) {
this.raiseError(`Expected completion insert text to be ${expected.insertText}, got ${actual.insertText}`);
}
const convertedReplacementSpan = replacementSpan && ts.createTextSpanFromRange(replacementSpan);
const convertedReplacementSpan = expected.replacementSpan && ts.createTextSpanFromRange(expected.replacementSpan);
try {
assert.deepEqual(actual.replacementSpan, convertedReplacementSpan);
}
catch {
this.raiseError(`Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`);
}

if (kind !== undefined || kindModifiers !== undefined) {
if (actual.kind !== kind) {
this.raiseError(`Unexpected kind for ${actual.name}: Expected '${kind}', actual '${actual.kind}'`);
}
if (actual.kindModifiers !== (kindModifiers || "")) {
this.raiseError(`Bad kindModifiers for ${actual.name}: Expected ${kindModifiers || ""}, actual ${actual.kindModifiers}`);
}
if (expected.kind !== undefined || expected.kindModifiers !== undefined) {
assert.equal(actual.kind, expected.kind, `Expected 'kind' for ${actual.name} to match`);
assert.equal(actual.kindModifiers, expected.kindModifiers || "", `Expected 'kindModifiers' for ${actual.name} to match`);
}

if (isFromUncheckedFile !== undefined) {
if (actual.isFromUncheckedFile !== isFromUncheckedFile) {
this.raiseError(`Expected 'isFromUncheckedFile' value '${actual.isFromUncheckedFile}' to equal '${isFromUncheckedFile}'`);
}
if (expected.isFromUncheckedFile !== undefined) {
assert.equal<boolean | undefined>(actual.isFromUncheckedFile, expected.isFromUncheckedFile, "Expected 'isFromUncheckedFile' properties to match");
}
if (expected.isPackageJsonImport !== undefined) {
assert.equal<boolean | undefined>(actual.isPackageJsonImport, expected.isPackageJsonImport, "Expected 'isPackageJsonImport' properties to match");
}

assert.equal(actual.hasAction, hasAction, `Expected 'hasAction' properties to match`);
assert.equal(actual.isRecommended, isRecommended, `Expected 'isRecommended' properties to match'`);
assert.equal(actual.source, source, `Expected 'source' values to match`);
assert.equal(actual.sortText, sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
assert.equal(actual.hasAction, expected.hasAction, `Expected 'hasAction' properties to match`);
assert.equal(actual.isRecommended, expected.isRecommended, `Expected 'isRecommended' properties to match'`);
assert.equal(actual.source, expected.source, `Expected 'source' values to match`);
assert.equal(actual.sortText, expected.sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));

if (text !== undefined) {
if (expected.text !== undefined) {
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
assert.equal(ts.displayPartsToString(actualDetails.displayParts), text, "Expected 'text' property to match 'displayParts' string");
assert.equal(ts.displayPartsToString(actualDetails.documentation), documentation || "", "Expected 'documentation' property to match 'documentation' display parts string");
assert.equal(ts.displayPartsToString(actualDetails.displayParts), expected.text, "Expected 'text' property to match 'displayParts' string");
assert.equal(ts.displayPartsToString(actualDetails.documentation), expected.documentation || "", "Expected 'documentation' property to match 'documentation' display parts string");
// TODO: GH#23587
// assert.equal(actualDetails.kind, actual.kind);
assert.equal(actualDetails.kindModifiers, actual.kindModifiers, "Expected 'kindModifiers' properties to match");
assert.equal(actualDetails.source && ts.displayPartsToString(actualDetails.source), sourceDisplay, "Expected 'sourceDisplay' property to match 'source' display parts string");
assert.deepEqual(actualDetails.tags, tags);
assert.equal(actualDetails.source && ts.displayPartsToString(actualDetails.source), expected.sourceDisplay, "Expected 'sourceDisplay' property to match 'source' display parts string");
assert.deepEqual(actualDetails.tags, expected.tags);
}
else {
assert(documentation === undefined && tags === undefined && sourceDisplay === undefined, "If specifying completion details, should specify 'text'");
assert(expected.documentation === undefined && expected.tags === undefined && expected.sourceDisplay === undefined, "If specifying completion details, should specify 'text'");
}
}

Expand Down Expand Up @@ -2122,6 +2116,9 @@ namespace FourSlash {
public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions | ts.FormatCodeSettings): ts.FormatCodeSettings {
const oldFormatCodeOptions = this.formatCodeSettings;
this.formatCodeSettings = ts.toEditorSettings(formatCodeOptions);
if (this.testType === FourSlashTestType.Server) {
(this.languageService as ts.server.SessionClient).setFormattingOptions(this.formatCodeSettings);
}
return oldFormatCodeOptions;
}

Expand Down
3 changes: 2 additions & 1 deletion src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ namespace FourSlashInterface {
}

public setOption(name: keyof ts.FormatCodeSettings, value: number | string | boolean): void {
this.state.formatCodeSettings = { ...this.state.formatCodeSettings, [name]: value };
this.state.setFormatOptions({ ...this.state.formatCodeSettings, [name]: value });
}
}

Expand Down Expand Up @@ -1495,6 +1495,7 @@ namespace FourSlashInterface {
readonly isRecommended?: boolean; // If not specified, will assert that this is false.
readonly isFromUncheckedFile?: boolean; // If not specified, won't assert about this
readonly kind?: string; // If not specified, won't assert about this
readonly isPackageJsonImport?: boolean; // If not specified, won't assert about this
readonly kindModifiers?: string; // Must be paired with 'kind'
readonly text?: string;
readonly documentation?: string;
Expand Down
3 changes: 3 additions & 0 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@ namespace Harness.LanguageService {
getProgram(): ts.Program {
throw new Error("Program can not be marshaled across the shim layer.");
}
getAutoImportProvider(): ts.Program | undefined {
throw new Error("Program can not be marshaled across the shim layer.");
}
getNonBoundSourceFile(): ts.SourceFile {
throw new Error("SourceFile can not be marshaled across the shim layer.");
}
Expand Down
Loading