@hydranium/core 1.0.0-next.205 → 1.0.0-next.206
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/documents/ast-document-manager.js +4 -4
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/client-ids.d.ts +1 -1
- package/lib/documents/client-ids.d.ts.map +1 -1
- package/lib/documents/client-ids.js +1 -1
- package/lib/documents/client-ids.js.map +1 -1
- package/lib/documents/client-session-registry.d.ts +24 -17
- package/lib/documents/client-session-registry.d.ts.map +1 -1
- package/lib/documents/client-session-registry.js +49 -29
- package/lib/documents/client-session-registry.js.map +1 -1
- package/lib/documents/dirty-state-tracker.d.ts +79 -0
- package/lib/documents/dirty-state-tracker.d.ts.map +1 -0
- package/lib/documents/dirty-state-tracker.js +69 -0
- package/lib/documents/dirty-state-tracker.js.map +1 -0
- package/lib/documents/document-release-handler.d.ts +174 -0
- package/lib/documents/document-release-handler.d.ts.map +1 -0
- package/lib/documents/document-release-handler.js +270 -0
- package/lib/documents/document-release-handler.js.map +1 -0
- package/lib/documents/document-release-scheduler.d.ts +67 -0
- package/lib/documents/document-release-scheduler.d.ts.map +1 -0
- package/lib/documents/document-release-scheduler.js +80 -0
- package/lib/documents/document-release-scheduler.js.map +1 -0
- package/lib/documents/hydranium-text-documents.d.ts +130 -455
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +362 -835
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/index.d.ts +5 -1
- package/lib/documents/index.d.ts.map +1 -1
- package/lib/documents/index.js +5 -1
- package/lib/documents/index.js.map +1 -1
- package/lib/documents/language-client-shadow.d.ts +221 -0
- package/lib/documents/language-client-shadow.d.ts.map +1 -0
- package/lib/documents/language-client-shadow.js +288 -0
- package/lib/documents/language-client-shadow.js.map +1 -0
- package/lib/documents/text-ledger.d.ts +64 -0
- package/lib/documents/text-ledger.d.ts.map +1 -0
- package/lib/documents/text-ledger.js +63 -0
- package/lib/documents/text-ledger.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +8 -7
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +8 -7
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts +1 -1
- package/lib/langium/integrity/integrity-service.js +4 -4
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts +4 -4
- package/lib/langium/model-service/client-session.js +2 -2
- package/lib/langium/model-service/client-session.js.map +1 -1
- package/lib/langium/model-service/model-events.d.ts +8 -11
- package/lib/langium/model-service/model-events.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +12 -4
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +1 -1
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +7 -0
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +3 -1
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/workspace/file-not-found.d.ts +7 -1
- package/lib/langium/workspace/file-not-found.d.ts.map +1 -1
- package/lib/langium/workspace/file-not-found.js +14 -3
- package/lib/langium/workspace/file-not-found.js.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.d.ts +6 -6
- package/lib/lsp/hydranium-document-update-handler.js +10 -10
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/testing/make-test-services.d.ts +3 -0
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +5 -2
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.d.ts +9 -6
- package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.js +19 -11
- package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
- package/package.json +5 -5
- package/src/documents/ast-document-manager.ts +4 -4
- package/src/documents/client-ids.ts +1 -1
- package/src/documents/client-session-registry.ts +55 -34
- package/src/documents/dirty-state-tracker.ts +130 -0
- package/src/documents/document-release-handler.ts +351 -0
- package/src/documents/document-release-scheduler.ts +121 -0
- package/src/documents/hydranium-text-documents.ts +395 -1034
- package/src/documents/index.ts +5 -1
- package/src/documents/language-client-shadow.ts +463 -0
- package/src/documents/text-ledger.ts +112 -0
- package/src/langium/document-builder/document-builder.ts +8 -7
- package/src/langium/integrity/integrity-service.ts +4 -4
- package/src/langium/model-service/client-session.ts +6 -6
- package/src/langium/model-service/model-events.ts +12 -11
- package/src/langium/model-service/model-service.ts +13 -4
- package/src/langium/module.ts +9 -1
- package/src/langium/workspace/file-not-found.ts +15 -3
- package/src/lsp/hydranium-document-update-handler.ts +10 -10
- package/src/testing/make-test-services.ts +7 -2
- package/src/testing/stub-hydranium-text-documents.ts +34 -21
- package/lib/documents/language-client-text-shadow.d.ts +0 -133
- package/lib/documents/language-client-text-shadow.d.ts.map +0 -1
- package/lib/documents/language-client-text-shadow.js +0 -201
- package/lib/documents/language-client-text-shadow.js.map +0 -1
- package/src/documents/language-client-text-shadow.ts +0 -223
|
@@ -12,10 +12,14 @@ import { type URI } from '@hydranium/langium';
|
|
|
12
12
|
import { type ServerSharedServices } from '../langium/module.js';
|
|
13
13
|
import { type ApplyWorkspaceEditResult, type CancellationToken, type Connection, type DidChangeTextDocumentParams, type DidCloseTextDocumentParams, type DidOpenTextDocumentParams, type DidSaveTextDocumentParams, type Disposable, Emitter, type Event, type HandlerResult, type RequestHandler, type TextDocumentChangeEvent, type TextDocumentWillSaveEvent, type TextDocumentsConfiguration, type TextEdit, type WillSaveTextDocumentParams } from 'vscode-languageserver';
|
|
14
14
|
import { type DocumentUri, TextDocument, type TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
|
|
15
|
-
import { type CanonicalUri, type LanguageClientUri, type
|
|
15
|
+
import { type CanonicalUri, type LanguageClientUri, type TextState, type TextVersion, type Tracer } from '@hydranium/protocol';
|
|
16
16
|
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
17
17
|
import { type ClientSessionClosedEvent, ClientSessionRegistry, type SessionEndCause } from './client-session-registry.js';
|
|
18
|
-
import {
|
|
18
|
+
import { type LanguageClientShadow } from './language-client-shadow.js';
|
|
19
|
+
import { type TextLedger } from './text-ledger.js';
|
|
20
|
+
import { type ReleasedDocument } from './document-release-handler.js';
|
|
21
|
+
import { type DocumentReleaseScheduler } from './document-release-scheduler.js';
|
|
22
|
+
import { type CleanAnnouncement, type DirtyStateTracker, type DocumentDirtyChangedEvent } from './dirty-state-tracker.js';
|
|
19
23
|
export interface ClientTextDocumentChangeEvent<T> extends TextDocumentChangeEvent<T> {
|
|
20
24
|
clientId: string;
|
|
21
25
|
}
|
|
@@ -32,123 +36,29 @@ export interface HydraniumTextDocumentsOptions<T extends TextDocument = TextDocu
|
|
|
32
36
|
readonly configuration?: TextDocumentsConfiguration<T>;
|
|
33
37
|
/**
|
|
34
38
|
* How long a document whose last open closed because its client's
|
|
35
|
-
* connection was lost keeps its text before
|
|
36
|
-
* a client lost from the document, within this time of its own loss,
|
|
37
|
-
* cancels the
|
|
39
|
+
* connection was lost keeps its text before the store releases it. An open
|
|
40
|
+
* by a client lost from the document, within this time of its own loss,
|
|
41
|
+
* cancels the release, so a client that registers again under its id after
|
|
38
42
|
* a dropped connection finds its unsaved edits. Any other open releases the
|
|
39
43
|
* document first and then opens it as a first open does, from the text the
|
|
40
44
|
* opener supplies (an editor's own) or else from the file. Meanwhile the
|
|
41
45
|
* document counts as open for the integrity service, which therefore writes
|
|
42
46
|
* none of its unsaved text to disk. A close the client makes itself, or
|
|
43
|
-
* ending its session,
|
|
47
|
+
* ending its session, releases at once whatever this is.
|
|
44
48
|
*
|
|
45
|
-
* Defaults to 10 s. `0`
|
|
46
|
-
*
|
|
49
|
+
* Defaults to 10 s. `0` releases such a document at once as well, in the
|
|
50
|
+
* close itself rather than on a timer.
|
|
47
51
|
*/
|
|
48
|
-
readonly
|
|
52
|
+
readonly releaseGraceMs?: number;
|
|
49
53
|
}
|
|
50
54
|
/** Delivered by {@link HydraniumTextDocuments.onDidSaveInLanguageClient}. */
|
|
51
55
|
export interface LanguageClientSavedEvent {
|
|
52
56
|
readonly uri: string;
|
|
53
57
|
}
|
|
54
|
-
/** Delivered by {@link HydraniumTextDocuments.
|
|
55
|
-
export interface
|
|
58
|
+
/** Delivered by {@link HydraniumTextDocuments.onDidReleaseDocument}. */
|
|
59
|
+
export interface DocumentReleasedEvent {
|
|
56
60
|
readonly uri: CanonicalUri;
|
|
57
61
|
}
|
|
58
|
-
/** Delivered by {@link HydraniumTextDocuments.onDidChangeDirty}. */
|
|
59
|
-
export interface DocumentDirtyChangedEvent {
|
|
60
|
-
readonly uri: CanonicalUri;
|
|
61
|
-
/**
|
|
62
|
-
* The text the new answer of {@link HydraniumTextDocuments.isDirty} was
|
|
63
|
-
* decided on. The answer changes with the text, before any build, so it can
|
|
64
|
-
* name text whose model has not been sent yet. Absent when the document no
|
|
65
|
-
* longer exists, or when the build that follows its release failed.
|
|
66
|
-
*/
|
|
67
|
-
readonly text?: TextState;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* The language client's open of a document under one URI, from its didOpen to
|
|
71
|
-
* its didClose. Each URI is its own editor buffer with its own version counter,
|
|
72
|
-
* so a file reached through a symlink and its real path has one of these each.
|
|
73
|
-
*/
|
|
74
|
-
export interface LanguageClientDocumentState {
|
|
75
|
-
/** The version the client last declared for this URI. */
|
|
76
|
-
declaredVersion: number;
|
|
77
|
-
/**
|
|
78
|
-
* The version an applied versioned push moved this URI to, ahead of its
|
|
79
|
-
* echo. Apart from `declaredVersion`, whose staleness guard would drop that
|
|
80
|
-
* echo and strand its pending push.
|
|
81
|
-
*/
|
|
82
|
-
pushedVersion?: number;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* All per-URI client-facing tracking the manager keys by normalized URI,
|
|
86
|
-
* collapsed into one record so a URI's full state lives in one place and the
|
|
87
|
-
* last-client close clears every axis in a single delete. (Parallel per-axis
|
|
88
|
-
* maps are the substrate of a close-on-stale-state desync — one map can be
|
|
89
|
-
* cleared while another lingers.)
|
|
90
|
-
*
|
|
91
|
-
* Two neighbours deliberately stay separate:
|
|
92
|
-
* - The inherited `__syncedDocuments` (Langium's parsed `TextDocument` store).
|
|
93
|
-
* - {@link LanguageClientTextShadow} (`__shadow`), a self-contained,
|
|
94
|
-
* separately-tested diff/apply-verify abstraction that owns its own baseline
|
|
95
|
-
* text; folding its storage here would couple a clean utility to this record
|
|
96
|
-
* for no real gain.
|
|
97
|
-
*
|
|
98
|
-
* Returned by the `protected` {@link HydraniumTextDocuments.trackingFor}, so an
|
|
99
|
-
* override has to name it. Restating the shape structurally instead compiles
|
|
100
|
-
* until a field is added here, and then fails at the adopter rather than at the
|
|
101
|
-
* change that caused it.
|
|
102
|
-
*/
|
|
103
|
-
export interface DocumentTrackingRecord {
|
|
104
|
-
/** Author of each version, sparse-indexed by the SHARED (server-assigned) version number. */
|
|
105
|
-
readonly versionAuthors: string[];
|
|
106
|
-
/**
|
|
107
|
-
* Last version id each client declared for this document (didOpen baseline,
|
|
108
|
-
* advanced by every accepted didChange), for the per-client staleness guard.
|
|
109
|
-
* Client version ids are CLIENT-owned per LSP (Monaco numbers its own
|
|
110
|
-
* buffer) — they never leak into the shared version sequence, which the
|
|
111
|
-
* server assigns (see {@link HydraniumTextDocuments.__versionSequences}).
|
|
112
|
-
* The language client's entry is the latest any of its URIs declared; the
|
|
113
|
-
* store checks and addresses it per URI, through
|
|
114
|
-
* {@link DocumentTrackingRecord.languageClientDocuments}.
|
|
115
|
-
*/
|
|
116
|
-
readonly clientVersions: Map<string, number>;
|
|
117
|
-
/** Content staged by integrity rules for a closed document. Consumed on next open. */
|
|
118
|
-
pendingContent?: string;
|
|
119
|
-
/**
|
|
120
|
-
* Each URI the LSP textual language client opened this (canonically-keyed)
|
|
121
|
-
* document under, with that open's state. Usually one; more when the same
|
|
122
|
-
* file is opened under a symlink path and its real path. These are the
|
|
123
|
-
* egress addresses: the document is *keyed* by its canonical identity, but
|
|
124
|
-
* Monaco holds it under the URI it opened. The language client holds the
|
|
125
|
-
* document while any entry remains.
|
|
126
|
-
*/
|
|
127
|
-
languageClientDocuments?: Map<LanguageClientUri, LanguageClientDocumentState>;
|
|
128
|
-
/**
|
|
129
|
-
* The clients whose close of this document was caused by a lost connection
|
|
130
|
-
* and that have not opened it again, each with a stopwatch started at its
|
|
131
|
-
* loss on the store's `Clock`. While the document waits out the revert grace, only an
|
|
132
|
-
* open by one of them within its own
|
|
133
|
-
* {@link HydraniumTextDocumentsOptions.revertGraceMs} of that loss cancels
|
|
134
|
-
* the revert. Every such client counts, not only the last to close: one
|
|
135
|
-
* connection's sessions all end lost together, and any of them may reopen
|
|
136
|
-
* first. Without the time limit, a client that returns late inherits the
|
|
137
|
-
* unsaved text of a holder lost after it. A stopwatch rather than a `now()`
|
|
138
|
-
* reading, because a wall-clock step would otherwise expire a claim early or
|
|
139
|
-
* revive one past its grace, against a grace timer that the step leaves
|
|
140
|
-
* alone. An entry past its grace counts as any other client's and is pruned
|
|
141
|
-
* at the next open of the document.
|
|
142
|
-
*/
|
|
143
|
-
lostClients?: Map<string, Stopwatch>;
|
|
144
|
-
/**
|
|
145
|
-
* The text the server last knew the file to hold, `undefined` for no file.
|
|
146
|
-
* Set by the first open and moved by {@link HydraniumTextDocuments.updateDiskBaseline}.
|
|
147
|
-
*/
|
|
148
|
-
diskBaseline?: string;
|
|
149
|
-
/** The last answer {@link HydraniumTextDocuments.onDidChangeDirty} announced. */
|
|
150
|
-
dirty?: boolean;
|
|
151
|
-
}
|
|
152
62
|
/**
|
|
153
63
|
* A document held open by at least one client, with the client ids holding it —
|
|
154
64
|
* one entry of {@link HydraniumTextDocuments.openDocuments}.
|
|
@@ -179,199 +89,72 @@ export type RepairCommit<T extends TextDocument> = {
|
|
|
179
89
|
readonly status: 'not-open';
|
|
180
90
|
};
|
|
181
91
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* One text pushed to the LSP textual language client by
|
|
197
|
-
* {@link HydraniumTextDocuments.applyEditToLanguageClient} whose echo has not
|
|
198
|
-
* come back yet. One entry of {@link HydraniumTextDocuments.__pendingPushes} —
|
|
199
|
-
* a `protected` field, so a subclass reading the queue has to name what it holds.
|
|
200
|
-
*/
|
|
201
|
-
export interface PendingLanguageClientPush {
|
|
202
|
-
/**
|
|
203
|
-
* The text the client held BEFORE this push, and therefore the text its
|
|
204
|
-
* echo addresses with its ranges. Wider than the baseline the push's edits
|
|
205
|
-
* were diffed against: a full replace is sent with no diff baseline and
|
|
206
|
-
* still lands on a buffer the echo is keyed to.
|
|
207
|
-
*
|
|
208
|
-
* `undefined` only when that buffer is unknown — the client never declared
|
|
209
|
-
* one, or a rejection invalidated what was tracked. The echo is then
|
|
210
|
-
* reconstructed against the synced text, which is sound only for a
|
|
211
|
-
* position-independent (full-text) change.
|
|
212
|
-
*/
|
|
213
|
-
readonly before: string | undefined;
|
|
214
|
-
/** {@link textHash} of the text this push moves the client to. */
|
|
215
|
-
readonly afterHash: string;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* What an incoming language-client change turns out to be once reconstructed
|
|
219
|
-
* against the buffer its ranges address — the return of
|
|
220
|
-
* {@link HydraniumTextDocuments.classifyLanguageClientChange}. That method is
|
|
221
|
-
* `protected`, so an override has to name every arm it can return.
|
|
222
|
-
*/
|
|
223
|
-
export type LanguageClientChangeOrigin =
|
|
224
|
-
/** The client is reporting a text we pushed it. The synced document is already there. */
|
|
225
|
-
{
|
|
226
|
-
readonly kind: 'echo';
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* The client's buffer holds a text we did not push it — a keystroke that
|
|
230
|
-
* raced a push, or an edit to a buffer the store has already been written
|
|
231
|
-
* past. The reconstructed text is what it now holds, and is authoritative.
|
|
232
|
-
*/
|
|
233
|
-
| {
|
|
234
|
-
readonly kind: 'divergent';
|
|
235
|
-
readonly text: string;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* The change carries ranges and no known text addresses them, so no
|
|
239
|
-
* reconstruction is offered. Adopting one anyway splices the document and
|
|
240
|
-
* stores an edit nobody made; dropping costs at most the one keystroke the
|
|
241
|
-
* client still holds and the next push contradicts.
|
|
242
|
-
*/
|
|
243
|
-
| {
|
|
244
|
-
readonly kind: 'unreconstructable';
|
|
245
|
-
};
|
|
246
|
-
/**
|
|
247
|
-
* Multi-client text-document tracking on top of Langium's `NormalizedTextDocuments`.
|
|
92
|
+
* The one text store every head writes to, on top of Langium's
|
|
93
|
+
* `NormalizedTextDocuments`, and the LSP text-sync endpoint: every
|
|
94
|
+
* `textDocument/*` notification and every `workspace/applyEdit` push goes
|
|
95
|
+
* through here.
|
|
96
|
+
*
|
|
97
|
+
* Each open, change, close and save is one synchronous transition over the
|
|
98
|
+
* held document and the collaborators its `create…` methods build. A
|
|
99
|
+
* collaborator that defers its part lets a listener of the transition's event
|
|
100
|
+
* read state from before it. A document no client holds any more is
|
|
101
|
+
* released to the `DocumentReleaseHandler` slot, after
|
|
102
|
+
* {@link HydraniumTextDocumentsOptions.releaseGraceMs} when its last client's
|
|
103
|
+
* connection was lost.
|
|
248
104
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* root that records a version, starts at its opener's declared id: no version
|
|
260
|
-
* was handed out for it.
|
|
261
|
-
* - Version-author history so each edit is attributable to its originating client.
|
|
262
|
-
* - The revert to disk once no client has a document open, for every head
|
|
263
|
-
* ({@link revertToDisk}), deferred by
|
|
264
|
-
* {@link HydraniumTextDocumentsOptions.revertGraceMs} after a lost connection.
|
|
265
|
-
* - A disk baseline per open document, and whether its text differs from it
|
|
266
|
-
* ({@link isDirty}).
|
|
267
|
-
* - Pending-content staging used by the integrity service to thread corrections
|
|
268
|
-
* through `workspace/applyEdit` cycles for currently-closed documents.
|
|
269
|
-
* - `didOpen` notifications arriving over the LSP connection wait on the
|
|
270
|
-
* workspace-ready promise, so a client's first open cannot race workspace
|
|
271
|
-
* discovery. Direct {@link notifyDidOpenTextDocument} calls (the non-LSP
|
|
272
|
-
* heads) do not pass that gate — their caller owns the ordering.
|
|
105
|
+
* Client-declared version ids feed only the per-client staleness guard and
|
|
106
|
+
* never a running shared sequence: the two count different things, and
|
|
107
|
+
* splicing them lets versions drift past base-version gate holders. A URI
|
|
108
|
+
* with no ledger record and no built root starts at its opener's declared id:
|
|
109
|
+
* no version was handed out for it.
|
|
110
|
+
*
|
|
111
|
+
* `didOpen` notifications arriving over the LSP connection wait on the
|
|
112
|
+
* workspace-ready promise, so a client's first open cannot race workspace
|
|
113
|
+
* discovery. Direct {@link notifyDidOpenTextDocument} calls (the non-LSP
|
|
114
|
+
* heads) do not pass that gate — their caller owns the ordering.
|
|
273
115
|
*/
|
|
274
116
|
export declare class HydraniumTextDocuments<T extends TextDocument = TextDocument> extends NormalizedTextDocuments<T> {
|
|
275
117
|
protected services: ServerSharedServices;
|
|
118
|
+
protected readonly options: HydraniumTextDocumentsOptions<T>;
|
|
119
|
+
/** Content staged by integrity rules for a document no client holds, consumed by its first open. */
|
|
120
|
+
protected readonly __pendingContent: Map<CanonicalUri, string>;
|
|
276
121
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* {@link __sessions}.
|
|
281
|
-
*/
|
|
282
|
-
protected __documents: Map<CanonicalUri, DocumentTrackingRecord>;
|
|
283
|
-
/**
|
|
284
|
-
* Which client has which document open, and which client ids are registered
|
|
285
|
-
* sessions. Every open-state predicate on this class reads it, so an open
|
|
122
|
+
* Which client has which document open, which client ids are registered
|
|
123
|
+
* sessions, and each client's declared version, the staleness guard's
|
|
124
|
+
* baseline. Every open-state predicate on this class reads it, so an open
|
|
286
125
|
* recorded anywhere else is invisible to the last-close transition.
|
|
287
126
|
*/
|
|
288
127
|
protected readonly __sessions: ClientSessionRegistry;
|
|
289
|
-
/**
|
|
290
|
-
* Per-URI shared-version continuity across close/reopen cycles
|
|
291
|
-
* ({@link VersionSequence}), kept for every document a build or a client
|
|
292
|
-
* gave the store, and consulted by the next first-client open. A URI
|
|
293
|
-
* with none, and no root that records a version, starts at its opener's
|
|
294
|
-
* declared version.
|
|
295
|
-
* DELIBERATELY outside {@link DocumentTrackingRecord}: that record is deleted on last close, while the version sequence must
|
|
296
|
-
* survive it — the shared version is a server-owned, monotonic,
|
|
297
|
-
* advances-iff-content-changes counter that never resets while the server
|
|
298
|
-
* lives. That invariant is what makes an optimistic base-version gate
|
|
299
|
-
* sound: "version unchanged ⇔ content unchanged", with no false conflicts
|
|
300
|
-
* from close/reopen version resets and no false passes from a reopened
|
|
301
|
-
* sequence coincidentally landing on a stale writer's number.
|
|
302
|
-
*
|
|
303
|
-
* Never pruned, not even when the file is deleted: a recreated file
|
|
304
|
-
* restarting at `0` would let a write based on the deleted text pass. Two
|
|
305
|
-
* small values per URI ever built, however often it changes.
|
|
306
|
-
*/
|
|
307
|
-
protected readonly __versionSequences: Map<CanonicalUri, VersionSequence>;
|
|
308
|
-
/**
|
|
309
|
-
* Released documents last announced dirty. Their clean flip waits for the
|
|
310
|
-
* revert, so it carries the reverted text's version; a first open before the
|
|
311
|
-
* revert takes the entry over, and its own dirty answer decides the flip.
|
|
312
|
-
* Each release enters a token of its own, so the revert of an earlier
|
|
313
|
-
* release cannot announce a later one clean before that one's revert.
|
|
314
|
-
*/
|
|
315
|
-
protected readonly __releasedDirty: Map<CanonicalUri, object>;
|
|
316
|
-
/** Per held document, the {@link textHash} of its text at the version it was taken. */
|
|
317
|
-
protected readonly __textHashes: WeakMap<TextDocument, {
|
|
318
|
-
readonly version: number;
|
|
319
|
-
readonly hash: string;
|
|
320
|
-
}>;
|
|
321
|
-
/**
|
|
322
|
-
* Texts pushed to the LSP textual language client via
|
|
323
|
-
* {@link applyEditToLanguageClient} whose echoes have not come back yet
|
|
324
|
-
* ({@link PendingLanguageClientPush}), keyed like the shadow by language-client URI.
|
|
325
|
-
* Outbound pushes and inbound echoes are uncorrelated on the wire; this
|
|
326
|
-
* FIFO is the explicit correlation, and it is what
|
|
327
|
-
* {@link classifyLanguageClientChange} reconstructs against.
|
|
328
|
-
*
|
|
329
|
-
* **Each entry keeps the client's PRE-push text, not only a hash of the
|
|
330
|
-
* post-push one.** A hash alone can classify a full-text echo, whose
|
|
331
|
-
* application is a no-op either way — and that is all it ever classified,
|
|
332
|
-
* because a conforming client echoes INCREMENTAL ranges keyed to its
|
|
333
|
-
* previous buffer. Those ranges cannot be applied to the synced text (which
|
|
334
|
-
* the authored write already advanced) and cannot be reconstructed without
|
|
335
|
-
* the baseline: the line they insert lands twice, validates cleanly, and
|
|
336
|
-
* compounds on every later edit.
|
|
337
|
-
*
|
|
338
|
-
* Lifecycle: entries are consumed by the matching echo (together with any
|
|
339
|
-
* older entries it supersedes), and the whole queue drops when the client
|
|
340
|
-
* stops being a pure mirror — a divergent change, an `applyEdit`
|
|
341
|
-
* failure/rejection (shadow invalidation), a close, or an explicit
|
|
342
|
-
* shadow (re)baseline. {@link PENDING_ECHO_CAP} bounds the queue against
|
|
343
|
-
* a pathological echo that never arrives; the memory cost until then is
|
|
344
|
-
* one pre-push text per in-flight push, for milliseconds.
|
|
345
|
-
*/
|
|
346
|
-
protected readonly __pendingPushes: Map<LanguageClientUri, PendingLanguageClientPush[]>;
|
|
347
|
-
/**
|
|
348
|
-
* Tracked text content per URI for the LSP textual language client (Monaco / VS Code).
|
|
349
|
-
* Owned here so {@link applyEditToLanguageClient} can compute minimal `workspace/applyEdit`
|
|
350
|
-
* diffs instead of full-document replaces (5–20 s → <500 ms on 20-30 KB YAML diagrams).
|
|
351
|
-
*
|
|
352
|
-
* Auto-tracked from the multi-client text-document events: open / change / close of
|
|
353
|
-
* the language client (re)baseline or invalidate the shadow. Other client ids
|
|
354
|
-
* (form editor, GLSP, integrity) do NOT touch the shadow — only what Monaco believes
|
|
355
|
-
* it has matters for the diff.
|
|
356
|
-
*
|
|
357
|
-
* Apply-verify safety net is built in: if the diff doesn't reconstruct `newText`
|
|
358
|
-
* exactly, {@link LanguageClientTextShadow.computeEdits} falls back to a full-range
|
|
359
|
-
* replace and invokes the `onFallback` callback — a diff regression becomes log
|
|
360
|
-
* noise, not a 0-byte save.
|
|
361
|
-
*
|
|
362
|
-
* Assigned in the constructor body so the `onFallback` callback can capture
|
|
363
|
-
* `this.logger` after the parameter-property assignment has run (field
|
|
364
|
-
* initializers fire BEFORE parameter-property assignment in TS).
|
|
365
|
-
*/
|
|
366
|
-
protected readonly __shadow: LanguageClientTextShadow;
|
|
367
128
|
protected readonly tracer: Tracer;
|
|
368
129
|
protected readonly configuration: TextDocumentsConfiguration<T>;
|
|
369
|
-
|
|
370
|
-
protected
|
|
371
|
-
protected
|
|
130
|
+
protected __textLedger: TextLedger | undefined;
|
|
131
|
+
protected __languageClientShadow: LanguageClientShadow | undefined;
|
|
132
|
+
protected __dirtyStateTracker: DirtyStateTracker | undefined;
|
|
133
|
+
protected __documentReleaseScheduler: DocumentReleaseScheduler | undefined;
|
|
134
|
+
protected readonly documentReleasedEmitter: Emitter<DocumentReleasedEvent>;
|
|
372
135
|
protected readonly languageClientSavedEmitter: Emitter<LanguageClientSavedEvent>;
|
|
373
|
-
protected readonly dirtyChangedEmitter: Emitter<DocumentDirtyChangedEvent>;
|
|
374
136
|
constructor(services: ServerSharedServices, options?: HydraniumTextDocumentsOptions<T>);
|
|
137
|
+
protected get textLedger(): TextLedger;
|
|
138
|
+
protected get languageClientShadow(): LanguageClientShadow;
|
|
139
|
+
protected get dirtyStateTracker(): DirtyStateTracker;
|
|
140
|
+
protected get documentReleaseScheduler(): DocumentReleaseScheduler;
|
|
141
|
+
protected createTextLedger(): TextLedger;
|
|
142
|
+
protected createLanguageClientShadow(): LanguageClientShadow;
|
|
143
|
+
protected createDirtyStateTracker(): DirtyStateTracker;
|
|
144
|
+
protected createDocumentReleaseScheduler(): DocumentReleaseScheduler;
|
|
145
|
+
/** Hold `document` as the text of `key`, a new version authored by `author`. */
|
|
146
|
+
protected commitText(key: CanonicalUri, document: T, author: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* Apply `changes` to `document`, the held text of `key`, and hold the
|
|
149
|
+
* result. The version steps only when the text changes, which is what a
|
|
150
|
+
* base-version gate relies on: unchanged text keeps its version and its
|
|
151
|
+
* author. The new text is known only once the changes are applied, so they
|
|
152
|
+
* go in at a tentative step that an identical result rolls back.
|
|
153
|
+
*/
|
|
154
|
+
protected commitChange(key: CanonicalUri, document: T, changes: TextDocumentContentChangeEvent[], author: string): {
|
|
155
|
+
document: T;
|
|
156
|
+
changed: boolean;
|
|
157
|
+
};
|
|
375
158
|
create(uri: string, languageId: string, version: number, content: string): T;
|
|
376
159
|
update(document: T, changes: TextDocumentContentChangeEvent[], version: number): T;
|
|
377
160
|
protected get __syncedDocuments(): Map<string, T>;
|
|
@@ -407,64 +190,38 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
407
190
|
applyContentChange(uri: DocumentUri, text: string, clientId: string): TextVersion;
|
|
408
191
|
/**
|
|
409
192
|
* Close `clientId`'s open of the document. When it was the last open, the
|
|
410
|
-
* document is released
|
|
411
|
-
* close, after {@link HydraniumTextDocumentsOptions.
|
|
193
|
+
* document is released, at once or, for a `'lost'`
|
|
194
|
+
* close, after {@link HydraniumTextDocumentsOptions.releaseGraceMs}.
|
|
412
195
|
*/
|
|
413
196
|
notifyDidCloseTextDocument(event: DidCloseTextDocumentParams, clientId?: string, cause?: SessionEndCause): void;
|
|
414
197
|
/**
|
|
415
|
-
* Keep the document, text and all, for the
|
|
198
|
+
* Keep the document, text and all, for the release grace, then release it.
|
|
416
199
|
* The document stays in the store meanwhile, so a lost client that opens it
|
|
417
200
|
* again within its own grace attaches to it and finds its unsaved text
|
|
418
|
-
* rather than reading disk; see {@link
|
|
201
|
+
* rather than reading disk; see {@link resolveDeferredRelease} for any other
|
|
419
202
|
* open.
|
|
420
203
|
*/
|
|
421
204
|
protected deferRelease(uri: CanonicalUri): void;
|
|
205
|
+
/** Resolve a deferred release of `uri` for an open by `clientId`: a release the scheduler decides on runs now. */
|
|
206
|
+
protected resolveDeferredRelease(uri: CanonicalUri, clientId: string): void;
|
|
422
207
|
/**
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* editor, with nothing marking it unsaved.
|
|
429
|
-
*/
|
|
430
|
-
protected resolvePendingRevert(uri: CanonicalUri, clientId: string): void;
|
|
431
|
-
/** Drop the entries of {@link DocumentTrackingRecord.lostClients} whose grace has run out. */
|
|
432
|
-
protected pruneLostClients(record: DocumentTrackingRecord): void;
|
|
433
|
-
/**
|
|
434
|
-
* Drop the document no client has open any more, then announce it on
|
|
435
|
-
* {@link onDidCloseLastOpen}, which is what reverts it to disk.
|
|
208
|
+
* Drop the document no client has open any more, announce it on
|
|
209
|
+
* {@link onDidReleaseDocument}, then hand it to the
|
|
210
|
+
* `DocumentReleaseHandler` slot: its listeners, such as the update
|
|
211
|
+
* handler dropping a change it still holds back, act before any build the
|
|
212
|
+
* handler runs.
|
|
436
213
|
*/
|
|
437
214
|
protected releaseDocument(uri: CanonicalUri): void;
|
|
438
215
|
/**
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
* its last client's text, and keeping it read-only is the client's job.
|
|
449
|
-
*
|
|
450
|
-
* The answer is read in the document's disk queue, so the rebuild follows
|
|
451
|
-
* any save still queued rather than reverting past it; a file that goes
|
|
452
|
-
* after that read is removed when its rebuild finds none.
|
|
453
|
-
*
|
|
454
|
-
* Whether to revert at all is decided inside the write lock, as its holder:
|
|
455
|
-
* decided before waiting for the lock, a client that opens or re-creates the
|
|
456
|
-
* document meanwhile would have its text rebuilt over, or the document
|
|
457
|
-
* removed. A document some client has open again, or that waits out a new
|
|
458
|
-
* grace, is left to that client.
|
|
459
|
-
*
|
|
460
|
-
* A document released dirty is announced clean, so a watcher is never left
|
|
461
|
-
* holding it dirty: once the revert has parsed the file, even if it is then
|
|
462
|
-
* cancelled, or else once the build it requests in its place has parsed it,
|
|
463
|
-
* as the announcement names the store's text. One the revert or that build
|
|
464
|
-
* removed, or that build failed, is announced without text. A reopen before
|
|
465
|
-
* that takes the announcement over.
|
|
466
|
-
*/
|
|
467
|
-
protected revertToDisk(uri: CanonicalUri): Promise<void>;
|
|
216
|
+
* Call the `DocumentReleaseHandler` slot, and announce a document released
|
|
217
|
+
* dirty clean once the promise it returns settles, which is after the
|
|
218
|
+
* release event. A failure is logged rather than thrown: the store has let
|
|
219
|
+
* go of the document by now, and a throw would abort the transition that
|
|
220
|
+
* released it, a session's close of its other documents included.
|
|
221
|
+
*/
|
|
222
|
+
protected handOverRelease(released: ReleasedDocument, cleanAnnouncement: CleanAnnouncement | undefined): void;
|
|
223
|
+
/** `uri` as the `DocumentReleaseHandler` slot receives it. */
|
|
224
|
+
protected toReleasedDocument(uri: CanonicalUri): ReleasedDocument;
|
|
468
225
|
notifyWillSaveTextDocument(event: WillSaveTextDocumentParams): void;
|
|
469
226
|
notifyWillSaveTextDocumentWaitUntil(event: WillSaveTextDocumentParams, token: CancellationToken): HandlerResult<TextEdit[], void>;
|
|
470
227
|
/**
|
|
@@ -493,24 +250,12 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
493
250
|
* and refreshing per attach turns each into a Langium rebuild and dependent
|
|
494
251
|
* relink cascade.
|
|
495
252
|
*
|
|
496
|
-
* The staleness guard baselines at the SYNCED version, not a declared buffer
|
|
497
|
-
* version, because a client arriving this way holds no buffer of its own.
|
|
498
|
-
* Unifying the two routes therefore rebaselines a textual client's guard to a
|
|
499
|
-
* version it never declared.
|
|
500
|
-
*
|
|
501
253
|
* Returns whether a hold was added — `false` when `uri` is not open,
|
|
502
|
-
* `clientId` already holds it, or the document was waiting out the
|
|
254
|
+
* `clientId` already holds it, or the document was waiting out the release
|
|
503
255
|
* grace for other clients and was released instead (see
|
|
504
|
-
* {@link
|
|
256
|
+
* {@link resolveDeferredRelease}); the caller then opens it anew.
|
|
505
257
|
*/
|
|
506
258
|
attachClient(uri: DocumentUri, clientId: string): boolean;
|
|
507
|
-
/**
|
|
508
|
-
* The built root's recorded version, one on when `text` differs from the root's;
|
|
509
|
-
* `declared` when the root records no store version. Seeded from `declared`, a write based
|
|
510
|
-
* on the root passes the gate over other text, and the same text looks newer than its model.
|
|
511
|
-
* A built root has no sequence only under a `LangiumDocuments` that does not reconcile at registration.
|
|
512
|
-
*/
|
|
513
|
-
protected firstOpenVersion(uri: CanonicalUri, text: string, declared: number): number;
|
|
514
259
|
protected logClientJoined(uri: DocumentUri, clientId: string, version: number, existingClients: readonly string[]): void;
|
|
515
260
|
refreshContent(uri: DocumentUri, clientId: string): void;
|
|
516
261
|
/**
|
|
@@ -520,7 +265,7 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
520
265
|
* two URIs for one physical file (a symlink path and its real path) into a
|
|
521
266
|
* single registration — dedup at the editor layer, not just in
|
|
522
267
|
* `LangiumDocuments`. The URI the client opened under is preserved separately
|
|
523
|
-
* for egress addressing
|
|
268
|
+
* for egress addressing, by the {@link LanguageClientShadow}. The
|
|
524
269
|
* policy is always bound (the framework defaults it to
|
|
525
270
|
* `DefaultDocumentUriPolicy`, where canonical ≡ syntactic normalize).
|
|
526
271
|
*/
|
|
@@ -534,8 +279,6 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
534
279
|
* from its real path (symlink / `..` / case).
|
|
535
280
|
*/
|
|
536
281
|
protected toLanguageClientUri(uri: string): LanguageClientUri;
|
|
537
|
-
/** Get-or-create the per-URI tracking record. `uri` must already be a {@link documentKey}. */
|
|
538
|
-
protected trackingFor(uri: CanonicalUri): DocumentTrackingRecord;
|
|
539
282
|
setAuthor(uri: DocumentUri, version: number, author: string): void;
|
|
540
283
|
/**
|
|
541
284
|
* Resolve the synced `TextDocument` for `uri`. The store keys documents by
|
|
@@ -572,14 +315,18 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
572
315
|
*/
|
|
573
316
|
textState(uri: DocumentUri): TextState | undefined;
|
|
574
317
|
/**
|
|
575
|
-
*
|
|
576
|
-
*
|
|
318
|
+
* The built root's recorded version for a first open of `key` with `text`,
|
|
319
|
+
* one on when the text differs from the root's; `undefined` when the root
|
|
320
|
+
* records no store version. Seeded from the opener's declared version
|
|
321
|
+
* instead, a write based on the built root passes the gate over other text,
|
|
322
|
+
* and the same text looks newer than its model. A built root records none
|
|
323
|
+
* only under a `LangiumDocuments` that does not reconcile at registration.
|
|
577
324
|
*/
|
|
578
|
-
protected
|
|
325
|
+
protected builtRootOpeningVersion(key: CanonicalUri, text: string): TextVersion | undefined;
|
|
579
326
|
/**
|
|
580
327
|
* Reconcile the persisted version sequence with content that reached the
|
|
581
328
|
* build OUTSIDE the store's write paths — a closed document rebuilt from
|
|
582
|
-
* disk
|
|
329
|
+
* disk after its release, or replaced by a watched-file change. Steps the
|
|
583
330
|
* sequence iff `text` differs from the sequence's last-known content and
|
|
584
331
|
* returns the resulting sequence version so the caller can re-stamp the
|
|
585
332
|
* rebuilt document (`VersionSyncService.modelProduced`) —
|
|
@@ -633,8 +380,10 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
633
380
|
* `onDidClose` event fires for the last client, so `isOpen` returns `true`
|
|
634
381
|
* during the close event itself. `isOpenInAnyClient` reads the open table
|
|
635
382
|
* ({@link __sessions}), which is updated BEFORE the fire, so an `onDidClose`
|
|
636
|
-
* subscriber that finds this `false` knows the last client just closed
|
|
637
|
-
*
|
|
383
|
+
* subscriber that finds this `false` knows the last client just closed.
|
|
384
|
+
* What the build keeps is the release's: a subscriber that re-read or
|
|
385
|
+
* rebuilt the document would race the `DocumentReleaseHandler`, and after a
|
|
386
|
+
* lost connection the release waits out its grace.
|
|
638
387
|
*/
|
|
639
388
|
isOpenInAnyClient(uri: DocumentUri): boolean;
|
|
640
389
|
isOpenInClient(uri: DocumentUri, client: string): boolean;
|
|
@@ -657,11 +406,11 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
657
406
|
get onDidCloseSession(): Event<ClientSessionClosedEvent>;
|
|
658
407
|
/**
|
|
659
408
|
* Fires once a document no client has open is released: at its last close,
|
|
660
|
-
* or, for a lost client's last close, when its
|
|
661
|
-
* another client opens it, or it is deleted
|
|
662
|
-
*
|
|
409
|
+
* or, for a lost client's last close, when its release grace runs out,
|
|
410
|
+
* another client opens it, or it is deleted, before the
|
|
411
|
+
* `DocumentReleaseHandler` slot is handed the document.
|
|
663
412
|
*/
|
|
664
|
-
get
|
|
413
|
+
get onDidReleaseDocument(): Event<DocumentReleasedEvent>;
|
|
665
414
|
/**
|
|
666
415
|
* Fires for every save the language client reports of a document it has
|
|
667
416
|
* open: the editor has written the file. {@link onDidSave} follows only
|
|
@@ -669,16 +418,16 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
669
418
|
*/
|
|
670
419
|
get onDidSaveInLanguageClient(): Event<LanguageClientSavedEvent>;
|
|
671
420
|
/**
|
|
672
|
-
* Whether `uri` is waiting out the
|
|
421
|
+
* Whether `uri` is waiting out the release grace: its last open closed with a
|
|
673
422
|
* lost connection, and it still holds its unsaved text. Such a document is
|
|
674
423
|
* open for no client, yet not closed either, so a caller that would persist
|
|
675
424
|
* a closed document's text to disk treats it as open.
|
|
676
425
|
*/
|
|
677
|
-
|
|
426
|
+
isReleaseDeferred(uri: DocumentUri): boolean;
|
|
678
427
|
/**
|
|
679
428
|
* Whether the store holds `uri` with text that differs from its disk
|
|
680
429
|
* baseline: what the server last knew the file to hold. `false` for a URI
|
|
681
|
-
* the store does not hold; a document waiting out the
|
|
430
|
+
* the store does not hold; a document waiting out the release grace is still
|
|
682
431
|
* held.
|
|
683
432
|
*
|
|
684
433
|
* The baseline is the text a first open brought, or what the server wrote,
|
|
@@ -688,10 +437,11 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
688
437
|
*/
|
|
689
438
|
isDirty(uri: DocumentUri): boolean;
|
|
690
439
|
/**
|
|
691
|
-
* Fires each time the answer of {@link isDirty} changes.
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
440
|
+
* Fires each time the answer of {@link isDirty} changes. For a document
|
|
441
|
+
* released dirty it fires once the `DocumentReleaseHandler` slot reports the
|
|
442
|
+
* release settled: at the version of the text the build then holds, or
|
|
443
|
+
* without text when the document is gone or the handler failed, though
|
|
444
|
+
* {@link isDirty} answers clean from the release on.
|
|
695
445
|
*/
|
|
696
446
|
get onDidChangeDirty(): Event<DocumentDirtyChangedEvent>;
|
|
697
447
|
/**
|
|
@@ -699,15 +449,13 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
699
449
|
* `undefined`. A no-op for a URI the store does not hold: the next first
|
|
700
450
|
* open sets the baseline from its own text.
|
|
701
451
|
*/
|
|
702
|
-
|
|
452
|
+
setDiskBaseline(uri: DocumentUri, text: string | undefined): void;
|
|
703
453
|
/**
|
|
704
454
|
* Read the file behind `uri` through its disk queue and take it as the
|
|
705
455
|
* baseline. A file that cannot be read counts as none, the side that
|
|
706
456
|
* leaves the document dirty.
|
|
707
457
|
*/
|
|
708
458
|
reloadDiskBaseline(uri: DocumentUri): Promise<void>;
|
|
709
|
-
/** Compare the held text of `uri` with its baseline, and announce a changed answer. */
|
|
710
|
-
protected refreshDirty(uri: CanonicalUri): void;
|
|
711
459
|
/**
|
|
712
460
|
* Start a client session under `clientId`. Throws where
|
|
713
461
|
* {@link ClientSessionRegistry.register} refuses the id.
|
|
@@ -722,7 +470,7 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
722
470
|
closeSession(clientId: string, cause?: SessionEndCause): void;
|
|
723
471
|
/**
|
|
724
472
|
* Close every document the language client has open, as a `didClose` for
|
|
725
|
-
* each would, so each last close
|
|
473
|
+
* each would, so each last close releases its document. For a host whose editor connection
|
|
726
474
|
* can end while the process lives on, such as a worker whose port's peer
|
|
727
475
|
* closed: the language client is no session, so nothing else closes them.
|
|
728
476
|
*
|
|
@@ -740,13 +488,6 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
740
488
|
* The workspace manager logs a failed build.
|
|
741
489
|
*/
|
|
742
490
|
protected initialBuildFinished(): Promise<void>;
|
|
743
|
-
/**
|
|
744
|
-
* Stop tracking every URI the language client holds the document `key`
|
|
745
|
-
* under, with its shadow and pending pushes. Call before a close by
|
|
746
|
-
* canonical key that means all of them: a close for one URI while others
|
|
747
|
-
* remain drops only that URI's and keeps the client's hold.
|
|
748
|
-
*/
|
|
749
|
-
protected untrackLanguageClientDocuments(key: CanonicalUri): void;
|
|
750
491
|
/**
|
|
751
492
|
* The file behind `uri` was deleted: close every open of it except the
|
|
752
493
|
* language client's.
|
|
@@ -786,10 +527,9 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
786
527
|
* Only a FIRST open consumes it, so stage only for a URI no client holds
|
|
787
528
|
* ({@link isOpenInAnyClient} is `false`). A URI held only through another
|
|
788
529
|
* head is not closed: an editor attaching to it joins the existing entry and
|
|
789
|
-
* never reads the stage, and the
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
* URI.
|
|
530
|
+
* never reads the stage, and the release discards it. An entry lingers
|
|
531
|
+
* only if `workspace/applyEdit` fails and the file is never opened — the
|
|
532
|
+
* memory cost is one serialised string per URI.
|
|
793
533
|
*/
|
|
794
534
|
stagePendingContent(uri: DocumentUri, text: string): void;
|
|
795
535
|
/**
|
|
@@ -809,20 +549,19 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
809
549
|
*
|
|
810
550
|
* The diff path is apply-verify-safe: the shadow internally checks that
|
|
811
551
|
* `TextDocument.applyEdits(old, edits) === newText` and falls back to a
|
|
812
|
-
* full replace on mismatch
|
|
813
|
-
*
|
|
552
|
+
* full replace on mismatch, logged, so a diff regression becomes log noise,
|
|
553
|
+
* not data loss.
|
|
814
554
|
*
|
|
815
555
|
* That safety net verifies the diff against the SHADOW, which is what the
|
|
816
556
|
* client is *believed* to hold — so it cannot see the client's buffer moving
|
|
817
557
|
* underneath a push. A line-keyed edit is position-dependent: if a genuine
|
|
818
|
-
* client keystroke lands between
|
|
819
|
-
*
|
|
820
|
-
*
|
|
558
|
+
* client keystroke lands between computing the edits and the client
|
|
559
|
+
* applying them, the ranges address the wrong lines and splice the buffer
|
|
560
|
+
* (observed as a duplicated declaration, which the integrity tier then
|
|
821
561
|
* "repairs" into a suffixed name and persists). The edit is therefore
|
|
822
|
-
* addressed at the language client's last known version for that URI
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
* reject a push its buffer has outrun. On rejection the shadow is invalidated,
|
|
562
|
+
* addressed at the language client's last known version for that URI rather
|
|
563
|
+
* than at `null` ("version intentionally unknown"), which is what lets the
|
|
564
|
+
* client reject a push its buffer has outrun. On rejection the shadow is invalidated,
|
|
826
565
|
* so the caller's retry is a full-range replace — position-independent, and
|
|
827
566
|
* safe to apply to whatever the client now holds.
|
|
828
567
|
*/
|
|
@@ -830,71 +569,7 @@ export declare class HydraniumTextDocuments<T extends TextDocument = TextDocumen
|
|
|
830
569
|
label?: string;
|
|
831
570
|
}): Promise<ApplyWorkspaceEditResult | undefined>;
|
|
832
571
|
/**
|
|
833
|
-
*
|
|
834
|
-
* `targetUri`, for addressing an outgoing `workspace/applyEdit`.
|
|
835
|
-
*
|
|
836
|
-
* Client version ids are CLIENT-owned per LSP, so this is the id the client
|
|
837
|
-
* itself stamped on its last `didOpen` / `didChange` for `targetUri`, or the
|
|
838
|
-
* one an applied push moved it to ahead of the push's echo
|
|
839
|
-
* ({@link LanguageClientDocumentState}) — never the shared server version,
|
|
840
|
-
* which advances on authored writes the client knows nothing about and would
|
|
841
|
-
* therefore reject every push.
|
|
842
|
-
*
|
|
843
|
-
* Falls back to {@link UNKNOWN_CLIENT_VERSION} when the client has not opened
|
|
844
|
-
* the document under `targetUri`, which is the honest answer. That is also
|
|
845
|
-
* the case in which there is no shadow, so the push is already a
|
|
846
|
-
* position-independent full replace and has nothing to gain from a gate.
|
|
847
|
-
*/
|
|
848
|
-
protected languageClientVersion(uri: DocumentUri, targetUri?: LanguageClientUri): number;
|
|
849
|
-
/**
|
|
850
|
-
* Append a push to the in-flight queue for `targetUri`
|
|
851
|
-
* (see {@link __pendingPushes}). Bounded: beyond
|
|
852
|
-
* {@link PENDING_ECHO_CAP} the oldest entry drops with a debug log — an
|
|
853
|
-
* echo that far outstanding means the client is not echoing at all, and
|
|
854
|
-
* an unbounded queue must not become the leak.
|
|
855
|
-
*/
|
|
856
|
-
protected recordPendingPush(targetUri: LanguageClientUri, before: string | undefined, newText: string): void;
|
|
857
|
-
/**
|
|
858
|
-
* Decide what an incoming language-client change actually is, by
|
|
859
|
-
* reconstructing the client's resulting buffer against the text its ranges
|
|
860
|
-
* address — the pre-push buffer of the OLDEST push still in flight for
|
|
861
|
-
* `clientFacing`, else whatever that client is believed to hold.
|
|
862
|
-
*
|
|
863
|
-
* `undefined` when the client's ranges address the synced text itself —
|
|
864
|
-
* nothing in flight, and no evidence the client holds anything else. That
|
|
865
|
-
* is the ordinary path, and the caller then applies the ranges directly.
|
|
866
|
-
* The two texts part company without a push in flight whenever a client
|
|
867
|
-
* attaches to a document another client has already written: it opened
|
|
868
|
-
* from disk, so applying its ranges to the synced text splices lines they
|
|
869
|
-
* never addressed.
|
|
870
|
-
*
|
|
871
|
-
* **Why the oldest, and why reconstruct at all.** The client applies our
|
|
872
|
-
* pushes in order and echoes each against the buffer it held before that
|
|
873
|
-
* push, so the first echo to arrive belongs to the oldest entry — a FIFO
|
|
874
|
-
* correspondence the queue preserves by consuming from the front. Comparing
|
|
875
|
-
* the reconstruction against every pending hash, not only the oldest, is
|
|
876
|
-
* what recognises an echo that a newer write already superseded: a rapid
|
|
877
|
-
* write sequence can deliver the echo of push N after the store applied
|
|
878
|
-
* push N+1, and everything older is then accounted for too.
|
|
879
|
-
*
|
|
880
|
-
* A reconstruction matching NO pending push means the client's buffer went
|
|
881
|
-
* somewhere we did not send it — it coalesced a keystroke into the echo, or
|
|
882
|
-
* typed before the push landed. That text is authoritative, and the queue
|
|
883
|
-
* drops: the client has stopped being a pure mirror, so no outstanding echo
|
|
884
|
-
* can match again. (A push still in flight at that point will be refused by
|
|
885
|
-
* the client's own version gate, which invalidates the shadow and makes the
|
|
886
|
-
* next sync a position-independent full replace.)
|
|
887
|
-
*
|
|
888
|
-
* Content equality is a sound echo proof because entries live only between a
|
|
889
|
-
* push and its echo — a milliseconds window, never history — and the
|
|
890
|
-
* client's `didChange` stream is ordered, so every buffer state arrives in
|
|
891
|
-
* mutation order. An UNDO returning the buffer to previously-pushed text is
|
|
892
|
-
* therefore never swallowed: the edit that preceded it already emptied the
|
|
893
|
-
* queue, so undo revisits PAST states while the queue holds IN-FLIGHT ones.
|
|
894
|
-
*/
|
|
895
|
-
protected classifyLanguageClientChange(clientFacing: LanguageClientUri, document: T, changes: TextDocumentContentChangeEvent[]): LanguageClientChangeOrigin | undefined;
|
|
896
|
-
/**
|
|
897
|
-
* Explicitly baseline the language-client text shadow for a URI. Useful in
|
|
572
|
+
* Explicitly baseline the language-client shadow for a URI. Useful in
|
|
898
573
|
* tests and for adopters that need to seed the shadow without going through
|
|
899
574
|
* a `didOpen` event (e.g. after a sideband save). Normal didOpen / didChange
|
|
900
575
|
* paths from the LSP language client already auto-track the shadow.
|