Repository: vscode
Issue: Make the `TextDocument.version` properly monotonically increasing during the whole application lifecycle
Labels: under-discussion

<!-- ⚠️⚠️ Do Not Delete This! feature_request_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- Please search existing issues to avoid creating duplicates. -->

Hi VS Code community, this is a proposal for making the `version` property of the text document to be strictly monotonically increasing during the whole VSCode lifecycle. I've prepared a PR implementing this (#223079) and would like to hear your opinions and feedback on this!

## Tl;dr

I propose to provide a guarantee that the document version is never reset within the VSCode lifecycle. This will be beneficial for LSP backends, which would be able to use the document version as a unique pointer into the document evolution timeline.

## Background

The documentation for the `TextDocument.version` property currently says:
> The version number of this document (it will strictly increase after each change, including undo/redo).

(https://code.visualstudio.com/api/references/vscode-api#TextDocument)

In LSP specification many operations (most notably didOpen and didChange) include the text document version, which identifies the document state referenced in these operations. The `version` field is defined as `integer` and described as the following (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#versionedTextDocumentIdentifier):
> The version number of a document will increase after each change, including undo/redo. The number doesn't need to be consecutive.

Basically both specs say that the number increases over time (implying that it never goes down) and it can skip certain values, e.g. go from 4 to 10. The specification **doesn't** provides any clarification on what happens to the document version between the `didClose` and `didOpen` events. This means that in theory the document version can be reset (or decrease) during the LSP lifecycle and cannot be used to identify the document state within the LSP session.

In practice, VSCode maintains the list of all previously closed document models: https://github.com/microsoft/vscode/blob/8d23e6fe2b8a70da4ba57ac527783d88f41471c4/src/vs/editor/common/services/modelService.ts#L103
When the document is re-opened the version number (and the undo history, which is the main reason here if I understand correctly) can be re-used if the content didn't change: https://github.com/microsoft/vscode/blob/8d23e6fe2b8a70da4ba57ac527783d88f41471c4/src/vs/editor/common/services/modelService.ts#L348
In order to prevent the list from growing indefinitely, there's a limit of overal memory consumption, so eventually previously closed document models are removed for good. In this case if the document is re-opened its version will start from 1.

Related issue where the same thing has been requested in the past – https://github.com/microsoft/vscode/issues/160072.

## Proposal

**I propose to provide a guarantee that the document version is never reset within the VSCode lifecycle.**

The document is identified by the URI, so re-opening the file with the same name will always preserve the version. Removing the file and re-creating it with the same name will also preserve the version, since from VSCode perspective this is not distinguishable from a regular close/open.

## Motivation

Document version can be used as a "pointer" into the document editing timeline, uniquely identifying its state. Several LSP features (e.g. diagnostics -- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_publishDiagnostics) already use document versions to refer to the document state, which works fine if always take close/open into the account and "reset" the document history.

This gets worse when we need to correlate "external" events with the editing history. Real life example – tracking the acceptance or rejection of code completion. These events need to point to the specific state of the document within the VSCode and/or LSP session and right now there is no reliable way to do so. Using the monotonically increasing version would be a straightforward way to address this.

## Implementation

This can be done by retaining the information about previously opened documents forever. We only need the subset of data that is stored right now – it would be sufficient to store `(uri, version, sha1)`. We can drop the `sha1` if we're okay with always incrementing the version after re-opening the document, but I think it's a nice property of keeping the version the same if the content didn't change between close/open.

## Caveats

The biggest downside is that we need to keep the list of documents forever, which may become problematic for very long sessions. Assuming one entry is 128 bytes (80 bytes for URI, 8 bytes version, 40 bytes sha1), then 1 million open files will amount to ~128MB. The number is large, but not completely unreasonable and reaching 1 million opened files is quite hard. Since we store the data in the map, this shouldn't affect the performance with the increase of the number of opened files.