Module: MilkTea::LSP::Workspace::WorkspaceStore

Included in:
MilkTea::LSP::Workspace
Defined in:
lib/milk_tea/lsp/workspace/store.rb

Instance Method Summary collapse

Instance Method Details

#apply_incremental_change_to_content(content, change) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/milk_tea/lsp/workspace/store.rb', line 85

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) ⇒ 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
# File 'lib/milk_tea/lsp/workspace/store.rb', line 59

def apply_incremental_changes(uri, changes)
  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)

  @shared_module_cache.clear
  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)
end

#apply_incremental_changes_against_snapshot(content, changes) ⇒ Object



100
101
102
103
104
105
106
107
# File 'lib/milk_tea/lsp/workspace/store.rb', line 100

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

Returns:

  • (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.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/milk_tea/lsp/workspace/store.rb', line 110

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
  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) ⇒ 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)
  @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)
end

#rename_indexed_file(old_uri, new_uri) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/milk_tea/lsp/workspace/store.rb', line 133

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

Raises:

  • (ArgumentError)


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

#shutdownObject



148
149
150
# File 'lib/milk_tea/lsp/workspace/store.rb', line 148

def shutdown
  stop_definition_warmup
end