Module: MilkTea::LSP::Workspace::WorkspaceStore
- Included in:
- MilkTea::LSP::Workspace
- Defined in:
- lib/milk_tea/lsp/workspace/store.rb
Instance Method Summary collapse
- #apply_incremental_change_to_content(content, change) ⇒ Object
- #apply_incremental_changes(uri, changes, warm_facts: true) ⇒ Object
- #apply_incremental_changes_against_snapshot(content, changes) ⇒ Object
- #background_document?(uri) ⇒ Boolean
- #close_document(uri) ⇒ Object
- #document_source(uri) ⇒ Object
-
#index_workspace(root_uri, &progress) ⇒ Object
Index all .mt files under root_uri so they are available for workspace-wide queries.
-
#open_document(uri, content, warm_facts: true) ⇒ Object
── Document lifecycle ──────────────────────────────────────────────────.
- #rename_indexed_file(old_uri, new_uri) ⇒ Object
- #set_document_source(uri, source) ⇒ Object
- #shutdown ⇒ Object
Instance Method Details
#apply_incremental_change_to_content(content, change) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 91 def apply_incremental_change_to_content(content, change) if change['range'] start_pos = change['range']['start'] end_pos = change['range']['end'] start_off = line_char_to_offset(content, start_pos['line'], start_pos['character']) end_off = line_char_to_offset(content, end_pos['line'], end_pos['character']) prefix = content.byteslice(0, start_off).to_s suffix = content.byteslice(end_off..).to_s prefix + change['text'].to_s + suffix else # Full-document fallback within an incremental-sync session change['text'].to_s end end |
#apply_incremental_changes(uri, changes, warm_facts: true) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 59 def apply_incremental_changes(uri, changes, warm_facts: true) content = get_content(uri) edits = Array(changes) new_content = if edits.length > 1 && edits.all? { |change| change['range'] } apply_incremental_changes_against_snapshot(content, edits) else edits.reduce(content) { |acc, change| apply_incremental_change_to_content(acc, change) } end @document_state_mutex.synchronize do @open_documents[uri] = new_content end invalidate_cache(uri) enqueue_definition_warmup(uri) unless background_document?(uri) # The shared module cache is intentionally NOT cleared here. Imported # module analyses only become stale when this file's dependency surface # (imports or exported declarations) changes; a body-only edit leaves # them valid. handle_did_change detects surface changes via # dependency_refresh_required_for_edit? and clears the cache through # refresh_import_dependent_caches. Clearing it on every keystroke forced # a full re-analysis of every transitive module per edit. dependent_uris = @facts_cache_mutex.synchronize do all_open = @document_state_mutex.synchronize { @open_documents.keys } dependent_open_document_uris_for(uri, all_open) end dependent_uris.each { |dep_uri| invalidate_cache(dep_uri) } warm_document_facts(uri, new_content, warm: warm_facts) end |
#apply_incremental_changes_against_snapshot(content, changes) ⇒ Object
106 107 108 109 110 111 112 113 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 106 def apply_incremental_changes_against_snapshot(content, changes) ordered = changes.sort_by do |change| start_pos = change['range']['start'] [start_pos['line'], start_pos['character']] end.reverse ordered.reduce(content) { |acc, change| apply_incremental_change_to_content(acc, change) } end |
#background_document?(uri) ⇒ Boolean
24 25 26 27 28 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 24 def background_document?(uri) @document_state_mutex.synchronize do @document_sources[uri] == 'background-document' end end |
#close_document(uri) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 41 def close_document(uri) # Keep indexed snapshot available for workspace-level features. path = uri_to_path(uri) @document_state_mutex.synchronize do @open_documents.delete(uri) @document_sources.delete(uri) if path && File.file?(path) begin @indexed_documents[uri] = File.read(path) rescue StandardError nil end end end invalidate_cache(uri, clear_last_good: true) enqueue_definition_warmup(uri) unless background_document?(uri) end |
#document_source(uri) ⇒ Object
18 19 20 21 22 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 18 def document_source(uri) @document_state_mutex.synchronize do @document_sources[uri] end end |
#index_workspace(root_uri, &progress) ⇒ Object
Index all .mt files under root_uri so they are available for workspace-wide queries. Skips the per-file sweep when the on-disk file set is unchanged since the last index, so repeated workspace/symbol calls do not re-walk and re-read the tree.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 118 def index_workspace(root_uri, &progress) root_path = uri_to_path(root_uri) return unless root_path && File.directory?(root_path) paths = Dir.glob(File.join(root_path, '**', '*.mt')).sort return if @indexed_workspace_paths == paths @indexed_workspace_paths = paths total = paths.length paths.each_with_index do |path, idx| file_uri = path_to_uri(path) @document_state_mutex.synchronize do @indexed_documents[file_uri] ||= begin File.read(path) rescue StandardError nil end end if progress && total > 0 pct = ((idx + 1) * 100 / total).clamp(0, 100) progress.call(pct, "#{idx + 1}/#{total} files") end end rescue StandardError end |
#open_document(uri, content, warm_facts: true) ⇒ Object
── Document lifecycle ──────────────────────────────────────────────────
32 33 34 35 36 37 38 39 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 32 def open_document(uri, content, warm_facts: true) @document_state_mutex.synchronize do @open_documents[uri] = content end invalidate_cache(uri) enqueue_definition_warmup(uri) unless background_document?(uri) warm_document_facts(uri, content, warm: warm_facts) end |
#rename_indexed_file(old_uri, new_uri) ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 144 def rename_indexed_file(old_uri, new_uri) @document_state_mutex.synchronize do content = @indexed_documents.delete(old_uri) if content @indexed_documents[new_uri] = content end @document_sources.delete(old_uri) if @indexed_documents.key?(new_uri) set_document_source(new_uri, 'workspace-file') end end invalidate_cache(old_uri, clear_last_good: true) invalidate_cache(new_uri) end |
#set_document_source(uri, source) ⇒ Object
7 8 9 10 11 12 13 14 15 16 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 7 def set_document_source(uri, source) normalized = source.to_s raise ArgumentError, "invalid document source #{source.inspect}" unless DOCUMENT_SOURCES.include?(normalized) @document_state_mutex.synchronize do previous = @document_sources[uri] @document_sources[uri] = normalized previous end end |
#shutdown ⇒ Object
159 160 161 |
# File 'lib/milk_tea/lsp/workspace/store.rb', line 159 def shutdown stop_definition_warmup end |