Class: Rigor::LanguageServer::BufferTable

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/language_server/buffer_table.rb

Overview

Per-session virtual file table. The LSP server maintains the canonical view of every open buffer here; analysis (slice 4+) reads from this table instead of disk so in-flight edits are reflected immediately.

Keyed by DocumentUri (LSP file://... URIs). Sync is INCREMENTAL (LSP TextDocumentSyncKind::Incremental = 2): a didChange carries range edits which #apply_changes splices into the held text through IncrementalSync. The full-text form (#change, and a contentChanges entry with no range) stays supported — it is still legal under incremental sync and is what didOpen and a client-side resync send.

Defined Under Namespace

Classes: Entry

Instance Method Summary collapse

Constructor Details

#initializeBufferTable

Returns a new instance of BufferTable.



21
22
23
24
# File 'lib/rigor/language_server/buffer_table.rb', line 21

def initialize
  @entries = {}
  @desynchronized = {}
end

Instance Method Details

#[](uri) ⇒ Object



79
80
81
# File 'lib/rigor/language_server/buffer_table.rb', line 79

def [](uri)
  @entries[uri]
end

#apply_changes(uri:, changes:, version:) ⇒ Boolean

Applies a textDocument/didChange payload under INCREMENTAL sync. Each entry of changes is applied in order against the result of the previous one, per the LSP contract.

On success the entry is replaced with the spliced text at the new version. On failure — a malformed change, or a range edit for a URI with no held buffer — the held text is left EXACTLY as it was and the URI is marked desynchronised: the server's view and the editor's view have diverged, and every position computed from the stale text would be wrong. Consumers check #desynchronized? and decline to answer rather than answering wrongly; the mark clears on the next didOpen or full-text change.

Returns:

  • (Boolean)

    true when the changes were applied.



52
53
54
55
56
57
58
59
60
# File 'lib/rigor/language_server/buffer_table.rb', line 52

def apply_changes(uri:, changes:, version:)
  text = IncrementalSync.apply_all(@entries[uri]&.bytes, changes)
  @desynchronized.delete(uri)
  @entries[uri] = Entry.new(uri: uri, bytes: text, version: version)
  true
rescue IncrementalSync::UnappliableChange => e
  @desynchronized[uri] = e.message
  false
end

#change(uri:, bytes:, version:) ⇒ Object

Records a full-text textDocument/didChange. The full new buffer text replaces the entry. If the client sends a didChange for a URI that was never opened (spec violation), the entry is still created — defensive.



37
38
39
40
# File 'lib/rigor/language_server/buffer_table.rb', line 37

def change(uri:, bytes:, version:)
  @desynchronized.delete(uri)
  @entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
end

#close(uri:) ⇒ Object

Records a textDocument/didClose event. The entry is removed. Subsequent reads via #[] return nil.



74
75
76
77
# File 'lib/rigor/language_server/buffer_table.rb', line 74

def close(uri:)
  @desynchronized.delete(uri)
  @entries.delete(uri)
end

#desynchronization_reason(uri) ⇒ String?

Returns why uri is desynchronised, for the log line; nil when it is in sync.

Returns:

  • (String, nil)

    why uri is desynchronised, for the log line; nil when it is in sync.



69
70
71
# File 'lib/rigor/language_server/buffer_table.rb', line 69

def desynchronization_reason(uri)
  @desynchronized[uri]
end

#desynchronized?(uri) ⇒ Boolean

Returns true when the last didChange for uri could not be applied, so the held text no longer matches the editor's.

Returns:

  • (Boolean)

    true when the last didChange for uri could not be applied, so the held text no longer matches the editor's.



64
65
66
# File 'lib/rigor/language_server/buffer_table.rb', line 64

def desynchronized?(uri)
  @desynchronized.key?(uri)
end

#open(uri:, bytes:, version:) ⇒ Object

Records a textDocument/didOpen event. Replaces any existing entry (LSP clients may re-open a previously closed URI; the new version is authoritative) and clears any desynchronised mark — the payload carries the client's full text, so the two views agree again.



29
30
31
32
# File 'lib/rigor/language_server/buffer_table.rb', line 29

def open(uri:, bytes:, version:)
  @desynchronized.delete(uri)
  @entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
end

#open?(uri) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/rigor/language_server/buffer_table.rb', line 83

def open?(uri)
  @entries.key?(uri)
end

#sizeObject



87
88
89
# File 'lib/rigor/language_server/buffer_table.rb', line 87

def size
  @entries.size
end

#urisObject



91
92
93
# File 'lib/rigor/language_server/buffer_table.rb', line 91

def uris
  @entries.keys
end