GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Updated: Only auto-import from package.json by andrewbranch · Pull Request #32517 · microsoft/TypeScript

Conversation

@andrewbranch

@andrewbranch Andrew Branch (andrewbranch) commented Jul 22, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Restores (and improves upon) the behavior introduced in #31893 without the significant performance regression by adding several caches. (Closes #32441.)

Diff compared to original PR (trying to keep changes to master out of it but will fall behind every now and then)

Note: comments about performance and bugs and changes in the thread below are talking about various states during the draft lifetime of this PR. This PR description reflects the state as of the time I’m changing it from “Draft” to “Ready for Review,” August 26 at 12:00 PM PDT.

Performance impact of this PR

TL;DR: Disregarding the very first completions request made to TS Server, the worst-case completions request is 28% slower than before. But, getting the completions list plus completion details is only 1% slower at worst, and typical requests are 76–80% faster thanks to some added caching.

There are two language service operations to consider:

  1. getCompletionsAtPosition returns an array of available completions without much detailed info about each.
  2. getCompletionEntryDetails is called for a specific entry from the results of the former operation, and gets more details about that completion, like JSDoc, symbol display parts, and CodeActions that will occur.

In VS Code, the completion list UI appears after the former returns. The latter is then called in immediate succession, and its response inserts more details into the same UI. So, the duration of getCompletionsAtPosition is a lower bound of UI response time, but the sum of the two calls affects perceived responsiveness since the UI doesn’t settle until both are complete.

I measured response times before and after this PR in a large project and took the average of 5 samples of each type of measurement reported here. In reporting timings for cached calls, I’ll report “Aug 10 Nightly” as “N/A” to indicate that the original code base performed no caching, but I’ll use the uncached timings to calculate the percent difference, since those would be the timings measured in identical scenarios.

Aug 10 Nightly (ms) PR (ms) Diff
list, no cache 74 116 +56%
details, no cache 51 21 -59%
list, package.json cached 1 N/A 95 +28%
list, cached 2 N/A 18 -76%
details, cached 3 N/A 10 -80%
sum, best-case realistic scenario 125 28 -78%
sum, worst-case realistic scenario 4 125 126 +1%

1. The package.json cache is populated upon the first request that uses it, and future changes to package.json files update the cache eagerly. In other words, this number only ever happens on the very first completions request that a project receives.
2. Cache lives for the duration of editing a single file, except after edge-case edits to that file that modify modules visible to the program.
3. Cache lives while the program is identical, which happens frequently for details, e.g. when navigating the completion list with arrow keys to view details of other items. The only time details requests trigger without a cache is after typing rapidly, such that previous requests get canceled.
4. The worst-case scenario for this PR is a cold cache (nothing in the lifetime of the language service has yet requested package.json information) followed by a cached details call. They can’t both be uncached, because the former populates the cache of the latter. The cache could be invalidated by continuing to type quickly, but at that point the original cold request for the list would be canceled too, and the next call would use the cached package.json.

Implementation

Even the absolute worst measurement of 56% slower is much better than #31893, which was ~12,000% slower. Here’s what made the difference.

Caching symlinks discovery

Getting the best module specifier to use from one module to another requires determining if there are any symlinks hanging around. The original PR tries to get the module specifier for every possible auto-import in a loop, which re-ran the symlink discovery code every time. But, the inputs to that code are constant over that loop, so it needs only be run once. This PR introduces a function moduleSpecifiers.withCachedSymlinks that uses a constant set of symlinks within a callback. This alone brought the performance impact down from ~12,000% slower to ~100% slower.

Caching package.json content

The previous PR searched for and read package.json files from disk during each request. This PR adds:

  1. A cache that keeps track of what folders definitely don’t have a package.json file to avoid reading directories
  2. A cache of package.json dependencies for a folder where a package.json file was previously found
  3. An added function in an existing directory watch that invalidates 1) and 2) when a package.json has been added or removed within the project
  4. An additional watch on any package.json file found (outside of node_modules) to invalidate 2) when the content changes.

Sheetal Nandi (@sheetalkamat) suggested replacing the additional package.json watch with an existing directory watch that detects changes to node_modules. I tried this approach, but it's quite common to run npm install --save dep when dep was already in node_modules required by something else, or npm uninstall --save dep when dep is still required by something else. In these cases, package.json changes, but node_modules does not.

Caching the auto-import list itself

Generally, the auto-imports available to a particular file don’t change while editing that file alone. So, we now cache auto-imports, and invalidate them when:

  1. A different file requests auto-imports
  2. The same file adds or removes an import or triple-slash reference that changes the modules in the program
  3. The same file adds or removes an ambient module or module augmentation
  4. The same file edits the contents of an ambient module or module augmentation
  5. The same file has ATA enabled, has acquired @types/node via ATA, and makes an edit that causes it to have imports of node core modules when it didn’t before, or vice versa. The same logic introduced in Proposal: If there’s a package.json, only auto-import things in it, more or less #31893 about inferring whether to offer node core module auto-imports still exists, so the cache needs to be invalidated when the input to that inference changes. (Note: I plan to make this inference smarter in a later PR—didn’t want this one to balloon in scope more than it already has.)

Caching auto-imports for completion entry details

Completion details display type information, so they can’t be as aggressively cached as the list of auto-imports available. But, completion details are commonly requested when the program hasn’t changed at all, so the same cache used for the auto-import list described above can be used for completion details requests if the project version hasn’t changed.

Avoiding computing the full specifier for a node_modules module

When determining whether a module that can be referred to by some node_modules-style path e.g. import 'foo/lib/whatever' should be filtered out by a package.json file or not, the path after foo/ is irrelevant. The previous PR used an existing function that calculated the entire path, which requires FS operations (to look for a dependency’s package.json and find a types entry). To avoid unnecessary FS operations, this PR adds an option to that function (tryGetModuleNameAsNodeModule) that prevents it from calculating the full path.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

weswigham, IllusionMH, OliverJAsh, DanceParty, and ExE-Boss reacted with thumbs up emoji
All reactions
  • 👍 5 reactions
@andrewbranch

Andrew Branch (andrewbranch) commented Jul 22, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Here’s a durable link to the diff between this and the original PR: https://github.com/andrewbranch/TypeScript/compare/old-package-json-pr..enhancement/only-import-from-package-json

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andrewbranch

Andrew Branch (andrewbranch) commented Jul 22, 2019

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) pack this

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jul 22, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Andrew Branch (@andrewbranch), I've started to run the tarball bundle task on this PR at 9cdad91. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jul 22, 2019

Copy link
Copy Markdown
Contributor

Hey Andrew Branch (@andrewbranch), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/37384/artifacts?artifactName=tgz&fileId=9A8AD91D5BBF9F0DABFC6D51B7BC1CF00FE422AAE5882E164C454C39B7E9BA0802&fileName=/typescript-3.6.0-insiders.20190722.tgz"
    }
}

and then running npm install.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@IllusionMH

Andrii Dieiev (IllusionMH) commented Jul 22, 2019

Copy link
Copy Markdown
Contributor

Andrew Branch (@andrewbranch) thanks for the ping. I will try this PR tomorrow on PC with that project.

andrewbranch reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@IllusionMH

Andrii Dieiev (IllusionMH) commented Jul 23, 2019

Copy link
Copy Markdown
Contributor

Andrew Branch (@andrewbranch) perf looks good. There is some delay, but it is barely noticeable (in comparison with latest nightly, and huuuuge difference if compared with problematic builds).
Great work! 🎉

As for completions: looks like second problem where completion list was missing auto imports is resolved, but i will diff returned list a bit later to see if there are any unexpectedly filtered items.

These are lines from tsserver.log.
I tried several times and one completion is for local name and one that should provide auto import.

Version: 3.6.0-dev.20190723

Perf 4187 [16:44:35.849] 13::completionInfo: elapsed time (in milliseconds) 102.2542
Perf 4246 [16:44:39.224] 24::completionInfo: elapsed time (in milliseconds) 71.3568

Perf 4187 [16:47:7.496] 13::completionInfo: elapsed time (in milliseconds) 108.9400
Perf 4235 [16:47:9.842] 21::completionInfo: elapsed time (in milliseconds) 73.0632

Perf 4196 [16:50:49.439] 16::completionInfo: elapsed time (in milliseconds) 89.8442
Perf 4262 [16:50:53.953] 30::completionInfo: elapsed time (in milliseconds) 87.2158


Version: 3.6.0-insiders.20190722 (used link from typescript-bot)
Perf 4199 [16:59:40.582] 17::completionInfo: elapsed time (in milliseconds) 219.0684
Perf 4250 [16:59:44.153] 26::completionInfo: elapsed time (in milliseconds) 187.5221

Perf 4190 [17:2:2.300] 14::completionInfo: elapsed time (in milliseconds) 222.2024
Perf 4244 [17:2:6.296] 24::completionInfo: elapsed time (in milliseconds) 188.8708

Perf 4241 [17:4:24.938] 17::completionInfo: elapsed time (in milliseconds) 273.6976
Perf 4353 [17:4:27.964] 33::completionInfo: elapsed time (in milliseconds) 188.3860
Perf 4378 [17:4:28.463] 37::completionInfo: elapsed time (in milliseconds) 144.7915

Perf 4238 [17:12:6.739] 33::completionInfo: elapsed time (in milliseconds) 201.9130
Perf 4181 [17:11:58.180] 21::completionInfo: elapsed time (in milliseconds) 210.6140


Version: 3.6.0-dev.20190723 (again)
Perf 4229 [17:15:54.644] 23::completionInfo: elapsed time (in milliseconds) 115.0832
Perf 4289 [17:15:57.245] 36::completionInfo: elapsed time (in milliseconds) 74.5518

Version: 3.6.0-insiders.20190722 (used link from typescript-bot) (again)
Perf 4187 [17:27:1.306] 13::completionInfo: elapsed time (in milliseconds) 222.7731
Perf 4259 [17:27:6.808] 27::completionInfo: elapsed time (in milliseconds) 190.7949
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andrewbranch

Andrew Branch (andrewbranch) commented Jul 23, 2019

Copy link
Copy Markdown
Member Author

Thanks for checking, Andrii Dieiev (@IllusionMH)! Your absolute times are actually a little better than mine now, but the diff is similar—even with the fix, the time is almost double what it was before. ~200ms isn’t awful in itself, but I don’t feel great about doubling the time.

The problem is, almost everything that’s happening to get the full list is now fairly important and unavoidable. However, when gathering auto imports, the same exact stuff happens every time you keydown, always producing the same results. Making edits in one file is highly unlikely to change the availability of auto-imports to that file. And in fact, making edits to any of your own source files is quite unlikely to change the availability of auto-imports of node_modules into your project. I think there may be an opportunity to cache auto-import completions for at least a session of editing a single file. I would be ok if the first getCompletionsAtPosition for a file goes from 100ms to 200ms, and then all the subsequent ones are potentially even faster than before.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@falsandtru

falsandtru (falsandtru) commented Jul 24, 2019

Copy link
Copy Markdown
Contributor

Andrew Branch (@andrewbranch) BTW, can you also fix #32273 like #31893?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andrewbranch

Andrew Branch (andrewbranch) commented Jul 24, 2019

Copy link
Copy Markdown
Member Author

That’s not really related to what’s happening here. What I’m doing is filtering auto-imports from all the things in the program down to ones that are relevant to you. What you seem to be asking for is for things that you don’t explicitly depend on to be excluded from the program entirely, even though other things depend on them. Which auto-imports are surfaced doesn’t fundamentally change how type checking works; excluding hard dependencies from the program does. There are other threads (what you’ve linked to) that already discuss the problems with that, so this PR isn’t a good place to get into it. But I’d also point you to #31894, if you haven’t yet seen it—that looks more relevant to your concerns.

RyanCavanaugh reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@falsandtru

falsandtru (falsandtru) commented Jul 25, 2019

Copy link
Copy Markdown
Contributor

I see, thanks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Andrew Branch (andrewbranch) added 20 commits July 29, 2019 10:21
@andrewbranch
Move package.json related utils to utilities
3e95a35
@andrewbranch
Add failing test
c33bf88
@andrewbranch
Make first test pass
52a0a00
@andrewbranch
Don’t filter when there’s no package.json, fix scoped package imports
528f873
@andrewbranch
Use type acquisition as a heuristic for whether a JS project is using…
5e60836
… node core
@andrewbranch
Make same fix in getCompletionDetails
9ca24b1
@andrewbranch
Fix re-exporting
6d582c9
@andrewbranch
Change JS node core module heuristic to same-file utilization
c60d078
@andrewbranch
Remove unused method
372265c
@andrewbranch
Remove other unused method
e907863
@andrewbranch
Remove unused triple-slash ref
410ade0
@andrewbranch
Update comment
b646317
@andrewbranch
Refactor findAlias to forEachAlias to reduce iterations
8b87aea
@andrewbranch
Really fix re-exporting
d02d691
@andrewbranch
Use getModuleSpecifier instead of custom hack
341fb3a
@andrewbranch
Fix offering auto imports to paths within node modules
426c28b
@andrewbranch
Rename things and make comments better
60478c6
@andrewbranch
Add another reexport test
af96ac4
@andrewbranch
Inline symbolHasBeenSeen
c3437d1
@andrewbranch
Simplify forEachAlias to findAlias
8813f02
Andrew Branch (andrewbranch) added 2 commits September 13, 2019 14:23
@andrewbranch
Fix markFileAsDirty from ScriptInfo
dcd8339
@andrewbranch
Mark new Project members internal
2afb54e
@andrewbranch
Andrew Branch (andrewbranch) requested a review from Sheetal Nandi (sheetalkamat) September 13, 2019 21:34
sheetalkamat
Sheetal Nandi (sheetalkamat) requested changes Sep 23, 2019
View reviewed changes
Comment thread src/server/project.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/server/project.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/server/packageJsonCache.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/services/completions.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/services/completions.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/services/completions.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/server/project.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Andrew Branch (andrewbranch) added 3 commits September 23, 2019 11:21
@andrewbranch
Use Path instead of fileName
65d0255
@andrewbranch
Rename AutoImportSuggestionsCache
0d8568b
@andrewbranch
Merge branch 'master' into enhancement/only-import-from-package-json
ba7a5c5
sheetalkamat
Sheetal Nandi (sheetalkamat) requested changes Sep 26, 2019
View reviewed changes
Comment thread src/server/project.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/server/utilities.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/server/packageJsonCache.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Andrew Branch (andrewbranch) added 5 commits September 26, 2019 10:41
@andrewbranch
Improve WatchType description
8ba6376
@andrewbranch
Remove entries() from packageJsonCache
0d34bfe
@andrewbranch
Merge branch 'master' into enhancement/only-import-from-package-json
368b31d
@andrewbranch
Fix path/fileName bug
b95cbc1
@andrewbranch
Also cache symlinks on Program for benefit of d.ts emit
6bb70f2
@andrewbranch
Andrew Branch (andrewbranch) requested a review from Sheetal Nandi (sheetalkamat) September 27, 2019 16:56
sheetalkamat
Sheetal Nandi (sheetalkamat) reviewed Sep 27, 2019
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

@andrewbranch
Let language service use Program’s symlink cache
213a370
sheetalkamat
Sheetal Nandi (sheetalkamat) approved these changes Sep 27, 2019
View reviewed changes
@andrewbranch
Andrew Branch (andrewbranch) merged commit 304fcee into microsoft:master Sep 27, 2019
@andrewbranch
Andrew Branch (andrewbranch) deleted the enhancement/only-import-from-package-json branch September 27, 2019 20:38
@orta Orta Therox (orta) added the Update Docs on Next Release Indicates that this PR affects docs label Sep 30, 2019
@orta Orta Therox (orta) mentioned this pull request Oct 15, 2019
Autocompletion doesn't suggest global variables exposed from dependent packages (regression) #32675
Closed
@fatcerberus Bruce Pascoe (fatcerberus) mentioned this pull request Nov 10, 2019
Auto-import not working for modules that are indirect dependencies #35023
Closed
@mjbvz Matt Bierner (mjbvz) mentioned this pull request Dec 3, 2019
Intellisense stops working when TS Server is higher version #35458
Closed
@andrewbranch Andrew Branch (andrewbranch) mentioned this pull request Dec 4, 2019
Don’t auto-import undefined #35504
Merged
@AviVahl Avi Vahl (AviVahl) mentioned this pull request Dec 7, 2019
No import suggestions for transitive dependencies in [v3.7.3] #35560
Closed
@andrewbranch Andrew Branch (andrewbranch) mentioned this pull request Jan 22, 2020
Automatic imports don't work for packages that provide their own types #36042
Closed
@andrewbranch Andrew Branch (andrewbranch) mentioned this pull request Feb 24, 2020
Auto-importing modules as namespaces? #23830
Closed
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Apr 6, 2020
Expand auto-import to all dependencies in package.json #37812
Closed
@mjbvz Matt Bierner (mjbvz) mentioned this pull request Apr 25, 2020
Auto-imports from builder-util-runtime #38176
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@sheetalkamat Sheetal Nandi (sheetalkamat) sheetalkamat approved these changes
@sandersn Nathan Shively-Sanders (sandersn) Awaiting requested review from sandersn
@rbuckton Ron Buckton (rbuckton) Awaiting requested review from rbuckton
@orta Orta Therox (orta) Awaiting requested review from orta
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Awaiting requested review from RyanCavanaugh
@weswigham Wesley Wigham (weswigham) Awaiting requested review from weswigham

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@andrewbranch Andrew Branch (andrewbranch)

Labels

Update Docs on Next Release Indicates that this PR affects docs

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Suggestions regression since 3.6.0-dev.20190713

Uh oh!

There was an error while loading. Please reload this page.

7 participants

@andrewbranch @typescript-bot @IllusionMH @falsandtru @weswigham @sheetalkamat @orta
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.