Skip to content

Replace api.updateSnapshot - #64204

Open
Andrew Branch (andrewbranch) wants to merge 12 commits into
microsoft:mainfrom
andrewbranch:api/snapshot-state
Open

Replace api.updateSnapshot#64204
Andrew Branch (andrewbranch) wants to merge 12 commits into
microsoft:mainfrom
andrewbranch:api/snapshot-state

Conversation

@andrewbranch

@andrewbranch Andrew Branch (andrewbranch) commented Sep 8, 2026

Copy link
Copy Markdown
Member

There will be a little more to do to integrate #64115 but I think we can say this closes #64154. Read that issue for the big picture overview—here’s the list of changes and decisions:

  • Removes tracking of a "latest snapshot" from the API.
  • api.updateSnapshot() is replaced by:
    • api.getCurrentLanguageServerSnapshot(changes?) available in LSP mode only
    • api.createSnapshot(changes?) available always
  • You can create a new snapshot based on any other with const newSnapshot = snapshot.update(changes)
  • changes takes two new operations:
    • snap.update({ createPrograms: [/* ... */] }) adds programs to any snapshot
    • snap.update({ ensurePrograms: [/* ... */] }) returns a snapshot where projects with the given IDs have up-to-date programs. snap.update({ ensurePrograms: true }) ensures all projects are up to date.
      • You can no longer pass oldProgram as an option in creating a program. Instead, a program can be incrementally updated by notifying the API of its changed files and using ensurePrograms:
        const s0 = api.createSnapshot({ createPrograms: [/* ... */] });
        const p0 = s0.operation.createdPrograms[0];
        // change files on disk or in VFS, then notify:
        const s1 = s0.update({
          fileChanges: { changed: [/* ... */] },
          ensurePrograms: [p0.id],
        });
      • Previously, in LSP mode, all projects were updated automatically as part of any updateSnapshot request. Now, you have to use ensurePrograms. (The exception is that projects returned by openProjects or openFiles are automatically updated without need for a separate ensurePrograms, even if they're already open/created. ensurePrograms is mainly needed in combination with fileChanges, so you can say which projects you care about.)
  • As seen above, a snapshot carries an operation with information about the request that created it. Currently only createPrograms and openFiles contribute to operation, since those result in the creation of projects with an ID that might not be known to the caller ahead of time.
  • project.id now has the type ProjectId = ConfiguredProjectId | InferredProjectId | SyntheticProjectId, each of which is a branded string. ConfiguredProjectId is a subtype of Path. The same ID is also exposed on program.id for convenience.
  • api.createProgram(rootFiles, options) is basically shorthand for api.createSnapshot({ createPrograms: [{ rootFiles, options }] }).operation.createdPrograms[0].

Copilot AI balanced review requested due to automatic review settings September 8, 2026 21:47
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 8, 2026
@typescript-automation typescript-automation Bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Sep 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Snapshot refresh and repeated-open handling contain correctness issues, and removePrograms exposes an overly broad project-ID type.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Replaces latest-snapshot state with explicit snapshot creation and derivation, adding synthetic-program lifecycle support and branded project IDs.

Changes:

  • Adds createSnapshot, Snapshot.update, and LSP snapshot retrieval.
  • Supports creating, removing, and ensuring synthetic programs.
  • Updates protocols, generators, caches, and tests for the new model.
File summaries
File Description
tsc/internal/project/snapshothost.go Adds independent root snapshots.
tsc/internal/project/snapshot.go Adds synthetic-program operations.
tsc/internal/project/snapshot_test.go Tests synthetic lifecycle.
tsc/internal/project/session.go Passes clients explicitly during cloning.
tsc/internal/project/refcountcache_test.go Updates cache tests for synthetic programs.
tsc/internal/project/projectcollectionbuilder.go Manages synthetic projects and program updates.
tsc/internal/project/projectcollection.go Integrates synthetic projects into lookups.
tsc/internal/project/project.go Defines synthetic projects and IDs.
tsc/internal/project/project_stringer_generated.go Adds generated synthetic-kind text.
tsc/internal/api/session.go Implements the redesigned snapshot API.
tsc/internal/api/session_temporary_test.go Tests explicit-base updates.
tsc/internal/api/session_createprogram_test.go Tests snapshot-created programs.
tsc/internal/api/session_completion_test.go Migrates completion setup.
tsc/internal/api/session_apistate_test.go Tests LSP snapshot state and ownership.
tsc/internal/api/proto.go Defines new protocol methods and types.
tsc/internal/api/proto_test.go Tests ensurePrograms decoding.
tools/gen-proto/main.go Generates branded IDs and embedded interfaces.
tools/gen-proto/main_test.go Verifies generated protocol output.
packages/typescript/test/sync/astnav.test.ts Migrates synchronous AST navigation tests.
packages/typescript/test/sync/ast.test.ts Migrates synchronous AST tests.
packages/typescript/test/sync/api.bench.ts Migrates synchronous benchmarks.
packages/typescript/test/sync/api-generators.test.ts Updates generator parity coverage.
packages/typescript/test/diagnosticFormatter.test.ts Migrates diagnostic tests.
packages/typescript/test/async/astnav.test.ts Migrates asynchronous AST navigation tests.
packages/typescript/test/async/api.bench.ts Migrates asynchronous benchmarks.
packages/typescript/src/api/sync/api.ts Exposes the synchronous snapshot model.
packages/typescript/src/api/sourceFileCache.ts Supports branded project IDs in caching.
packages/typescript/src/api/proto.ts Adds snapshot request compatibility conversion.
packages/typescript/src/api/proto.generated.ts Updates generated wire declarations.
packages/typescript/src/api/async/api.ts Exposes the asynchronous snapshot model.
Review details

Files not reviewed (1)

  • tsc/internal/project/project_stringer_generated.go: Generated file
  • Files reviewed: 29/32 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread tsc/internal/project/snapshot.go Outdated
Comment thread tsc/internal/api/proto.go Outdated
Comment thread tsc/internal/api/session.go
@andrewbranch

Copy link
Copy Markdown
Member Author

I have a refactor on top of this to use strongly typed project IDs that are not just tspath.Path, but it was a big diff so I didn't include it in this branch. It's a very nice cleanup though.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

LSP reconciliation can mishandle close-and-reopen requests, and solution-wide operations may consume stale synthetic programs.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Files not reviewed (1)

  • tsc/internal/project/project_stringer_generated.go: Generated file

Suppressed comments (1)

tsc/internal/api/session.go:1245

  • The same pre-request filtering breaks close-and-reopen for files: when a currently owned file appears in both lists, OpenFiles is removed here and CloseFiles is retained below, leaving the file closed. Independent snapshot reconciliation processes closes before opens, so the two APIs now produce different final states for the same change set. Compute both deltas from a temporary open-file state so the reopen wins.
	for uri := range apiRequest.OpenFiles.Keys() {
		path := s.toPath(uri.FileName())
		if s.openFiles.Has(path) {
			apiRequest.OpenFiles.Delete(uri)
		} else {
  • Files reviewed: 31/34 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +1226 to +1230
for configFileName := range apiRequest.OpenProjects.Keys() {
configPath := s.toPath(configFileName)
if s.openProjects.Has(configPath) {
apiRequest.OpenProjects.Delete(configFileName)
} else {
Comment on lines +140 to +147
// Projects returns all configured, synthetic, and inferred projects in a stable order.
func (c *ProjectCollection) Projects() []*Project {
if c.inferredProject == nil {
return c.ConfiguredProjects()
}
projects := make([]*Project, 0, len(c.configuredProjects)+1)
projects := make([]*Project, 0, len(c.configuredProjects)+len(c.syntheticProjects)+core.IfElse(c.inferredProject != nil, 1, 0))
c.fillConfiguredProjects(&projects)
projects = append(projects, c.inferredProject)
projects = append(projects, c.SyntheticProjects()...)
if c.inferredProject != nil {
projects = append(projects, c.inferredProject)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Milestone Bug PRs that fix a bug with a specific milestone

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

[API] Redesign client-side snapshot state model

2 participants