Skip to content

Replace api.updateSnapshot - #64204

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

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

Conversation

@andrewbranch

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

Copy link
Copy Markdown
Member

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 three new operations:
    • snap.update({ createPrograms: [/* ... */] }) adds programs to any snapshot
    • snap.update({ reconfigurePrograms: [/* ... */] }) changes the root files, options, or references of a program created with createPrograms
    • 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].
  • Renamed fileChanges to fileNotifications and APIFileChanges to FileNotifications

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 Outdated
@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 thread tsc/internal/api/session.go Outdated
Comment thread tsc/internal/project/projectcollection.go

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

Malformed or unknown program identifiers can currently cause incorrect success, unintended removal, or a server panic.

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)

Previously missed (1) — in code that hasn't changed since the last review.

tsc/internal/project/projectcollectionbuilder.go:310

  • Unknown project IDs are silently ignored because DidRequestProject returns without indicating whether it found anything. Consequently, ensurePrograms can report success even though the requested program is absent (for example, when an ID from an unrelated snapshot is supplied). Validate every requested ID against this snapshot and return a client error when one is missing.
  • Files reviewed: 33/36 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread tsc/internal/api/session.go
Comment thread tsc/internal/project/project.go
# Conflicts:
#	tsc/internal/api/session_requestfilesystem_test.go
#	tsc/internal/project/projectcollectionbuilder.go
#	tsc/internal/project/refcountcache_test.go
#	tsc/internal/project/session.go
#	tsc/internal/project/snapshot.go
#	tsc/internal/project/snapshothost.go

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

Failed LSP updates can desynchronize ownership state, and reconstructed snapshots lose stable project ordering.

Get a fresh assessment by requesting another Copilot review.

Review details

Files not reviewed (1)

  • tsc/internal/project/project_stringer_generated.go: Generated file
  • Files reviewed: 34/37 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread tsc/internal/project/projectcollectionbuilder.go
Comment thread packages/typescript/src/api/async/api.ts Outdated

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

Canonical LSP updates can report failure after mutating state, and snapshot cleanup currently runs under the global snapshot lock.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 38/40 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread tsc/internal/api/session.go Outdated
Comment thread tsc/internal/api/session.go
Comment thread tsc/internal/project/api.go

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

Temporary updates can omit project discovery, and legacy file-change fields are silently ignored.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

packages/typescript/src/api/async/api.ts:511

  • ensurePrograms: true only updates projects already present in the base snapshot; it does not perform the file-to-project discovery that the removed temporary-snapshot endpoint performed via Documents. Consequently, calling this helper for a file whose configured/inferred project is not already loaded leaves the callback snapshot with no project for that file. Open the file in this derived snapshot (which is disposed immediately afterward), or add a targeted ensure-file operation.
  • Files reviewed: 38/40 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread packages/typescript/src/api/async/api.ts Outdated

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 request validation and public overload compatibility issues must be addressed.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

tsc/internal/project/projectcollectionbuilder.go:360

  • An unknown or stale ID in ensurePrograms is silently ignored because DidRequestProject has no failure result. The request then succeeds without ensuring the named program, contrary to this operation's contract; validate that each ID resolves in this snapshot and return a client error when it does not.

tsc/internal/api/session.go:1253

  • createPrograms is decoded as a slice of pointers, so a wire request containing createPrograms: [null] reaches this dereference and panics instead of returning a client error. Reject nil entries before reading RootFiles.
	for i, programParams := range changes.CreatePrograms {
		rootFileNames := make([]string, len(programParams.RootFiles))
  • Files reviewed: 38/40 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread tsc/internal/api/session.go
Comment thread packages/typescript/src/api/async/api.ts Outdated

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

Request validation and public overload issues can currently cause a server panic or violate the new API contract.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

tsc/internal/project/projectcollectionbuilder.go:360

  • Unknown IDs in ensurePrograms are silently ignored by DidRequestProject, so updating an unrelated/root snapshot with an ID from another snapshot succeeds even though the requested program is absent and cannot be ensured. Reject IDs not present in this builder, consistent with reconfigure/remove validation.
    packages/typescript/src/api/async/api.ts:375
  • The overloads do not accept an explicit undefined or a CreateSnapshotParams | undefined variable, although the implementation and the documented changes? API make the parameter optional. Expose an optional-parameter overload rather than only a zero-argument overload.
  • Files reviewed: 38/40 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread tsc/internal/api/proto.go
// closed once every API client that opened it closes it.
CloseFiles []DocumentIdentifier `json:"closeFiles,omitempty"`
// CreatePrograms describes synthetic programs to create in the snapshot.
CreatePrograms []*CreateSnapshotProgramParams `json:"createPrograms,omitempty"`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Copilot you know it's very rude to keep requesting changes one at a time when you could just batch all your feedback

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