Appearance
| 1 | namespace Semantic is | |
| 2 | use IO.Std | |
| 3 | ||
| 4 | use Source | |
| 5 | ||
| 6 | trait SymbolDefinitionListener is | |
| 7 | add_symbol_definition(location: LOCATION, symbol: Symbols.Symbol) | |
| 8 | si | |
| 9 | ||
| 10 | class SYMBOL_DEFINITION_LOCATIONS(_symbol_use_listener: SymbolUseListener): SymbolDefinitionListener is | |
| 11 | ||
| 12 | _symbol_definition_map: Collections.MAP[string,Collections.LIST[Symbols.Symbol]] | |
| 13 | ||
| 14 | // The location each definition above was recorded at, in step | |
| 15 | // with it: what the replay checks a re-declaration against. | |
| 16 | _definition_locations: Collections.MAP[string,Collections.LIST[LOCATION]] | |
| 17 | _workspace_definition_map: Collections.MAP[string,Collections.LIST[Symbols.Symbol]] | |
| 18 | ||
| 19 | init(..) is | |
| 20 | clear() | |
| 21 | si | |
| 22 | ||
| 23 | dump_counts() is | |
| 24 | Std.error.write_line("symbol definition map: {_symbol_definition_map.count}") | |
| 25 | Std.error.write_line("workspace definition map: {_workspace_definition_map.count}") | |
| 26 | si | |
| 27 | ||
| 28 | clear() is | |
| 29 | _symbol_definition_map = Collections.MAP[string,Collections.LIST[Symbols.Symbol]]() | |
| 30 | _definition_locations = Collections.MAP[string,Collections.LIST[LOCATION]]() | |
| 31 | _workspace_definition_map = Collections.MAP[string,Collections.LIST[Symbols.Symbol]]() | |
| 32 | si | |
| 33 | ||
| 34 | ||
| 35 | // One declaration of the previous generation, in the order it was | |
| 36 | // recorded, for the replay below. | |
| 37 | _replay: Collections.MAP[string, Collections.LIST[(location: LOCATION, name: string, id: int)]]? | |
| 38 | _replay_cursor: Collections.MAP[string, int]? | |
| 39 | ||
| 40 | // Forget the recorded definitions ahead of a rebuild, keeping | |
| 41 | // each unedited file's sequence of (location, name, id) so its | |
| 42 | // re-declaration can adopt the ids. A file re-declared from | |
| 43 | // its retained tree records the same declarations in the same | |
| 44 | // order; the location and name of each are checked as they | |
| 45 | // arrive, and a file whose sequence diverges gets fresh ids from | |
| 46 | // that point - the behaviour without any replay. | |
| 47 | clear_and_replay_ids(edited_files: Collections.Iterable[string]) is | |
| 48 | let edited = Collections.SET[string]() | |
| 49 | ||
| 50 | for file_name in edited_files do | |
| 51 | edited.add(file_name) | |
| 52 | od | |
| 53 | ||
| 54 | let replay = Collections.MAP[string, Collections.LIST[(location: LOCATION, name: string, id: int)]]() | |
| 55 | ||
| 56 | for entry in _symbol_definition_map do | |
| 57 | if edited.contains(entry.key) then | |
| 58 | continue | |
| 59 | fi | |
| 60 | ||
| 61 | let sequence = Collections.LIST[(location: LOCATION, name: string, id: int)]() | |
| 62 | let locations = _definition_locations[entry.key] | |
| 63 | ||
| 64 | for i in 0..entry.value.count do | |
| 65 | let symbol = entry.value[i] | |
| 66 | ||
| 67 | sequence.add((location = locations[i], name = symbol.name, id = symbol.id)) | |
| 68 | od | |
| 69 | ||
| 70 | replay[entry.key] = sequence | |
| 71 | od | |
| 72 | ||
| 73 | clear() | |
| 74 | ||
| 75 | _replay = replay | |
| 76 | _replay_cursor = Collections.MAP[string, int]() | |
| 77 | si | |
| 78 | ||
| 79 | _adopt_replayed_id(location: LOCATION, symbol: Symbols.Symbol) is | |
| 80 | if let replay = _replay, cursor = _replay_cursor then | |
| 81 | let file_name = location.file_name | |
| 82 | ||
| 83 | let sequence: Collections.LIST[(location: LOCATION, name: string, id: int)] mut | |
| 84 | ||
| 85 | if !replay.try_get_value(file_name, sequence ref) then | |
| 86 | return | |
| 87 | fi | |
| 88 | ||
| 89 | let index = if cursor.contains_key(file_name) then cursor[file_name] else 0 fi | |
| 90 | ||
| 91 | if index < sequence.count then | |
| 92 | let expected = sequence[index] | |
| 93 | ||
| 94 | if expected.location =~ location /\ expected.name =~ symbol.name then | |
| 95 | symbol.adopt_id(expected.id) | |
| 96 | cursor[file_name] = index + 1 | |
| 97 | ||
| 98 | return | |
| 99 | fi | |
| 100 | fi | |
| 101 | ||
| 102 | replay.remove(file_name) | |
| 103 | fi | |
| 104 | si | |
| 105 | add_symbol_definition(location: LOCATION, symbol: Symbols.Symbol) is | |
| 106 | _adopt_replayed_id(location, symbol) | |
| 107 | ||
| 108 | get_symbol_list_for_file_name(location.file_name).add(symbol) | |
| 109 | _definition_locations[location.file_name].add(location) | |
| 110 | ||
| 111 | if !symbol.is_internal then | |
| 112 | _symbol_use_listener.add_symbol_use(location, symbol) | |
| 113 | fi | |
| 114 | si | |
| 115 | ||
| 116 | // Reconcile one file's recorded definitions after an | |
| 117 | // interface-preserving incremental EDIT, before the body re-walk | |
| 118 | // re-declares its bodies. The body-local definitions are dropped — | |
| 119 | // the re-walk re-records them — and the interface definitions are | |
| 120 | // kept (the re-walk does not re-declare the interface; the | |
| 121 | // interface symbols' own locations are refreshed by | |
| 122 | // SYMBOL_USE_LOCATIONS.refresh_edited_file). The derived workspace | |
| 123 | // list is discarded so it rebuilds on next query. | |
| 124 | refresh_edited_file(file_name: string, body_spans: Source.BODY_SPANS) is | |
| 125 | if _symbol_definition_map.contains_key(file_name) then | |
| 126 | let kept = Collections.LIST[Symbols.Symbol]() | |
| 127 | let kept_locations = Collections.LIST[LOCATION]() | |
| 128 | let symbols = _symbol_definition_map[file_name] | |
| 129 | let locations = _definition_locations[file_name] | |
| 130 | ||
| 131 | for i in 0..symbols.count do | |
| 132 | let symbol = symbols[i] | |
| 133 | ||
| 134 | if !body_spans.contains(symbol.location.start) then | |
| 135 | kept.add(symbol) | |
| 136 | kept_locations.add(locations[i]) | |
| 137 | fi | |
| 138 | od | |
| 139 | ||
| 140 | _symbol_definition_map[file_name] = kept | |
| 141 | _definition_locations[file_name] = kept_locations | |
| 142 | fi | |
| 143 | ||
| 144 | _workspace_definition_map.remove(file_name) | |
| 145 | si | |
| 146 | ||
| 147 | find_definitions_from_file(file_name: string, workspace_search: bool) -> Collections.Iterable[Symbols.Symbol] => | |
| 148 | if workspace_search then | |
| 149 | get_workspace_list_for_file_name(file_name) | |
| 150 | else | |
| 151 | get_symbol_list_for_file_name(file_name) | |
| 152 | fi | |
| 153 | ||
| 154 | // True iff at least one definition is recorded for the file — | |
| 155 | // distinguishes "this file was already compiled and genuinely | |
| 156 | // has no visible definitions" from "no compile has run yet". | |
| 157 | // `get_symbol_list_for_file_name` lazily creates an empty list, | |
| 158 | // so `contains_key` after-the-fact isn't a safe test; check | |
| 159 | // count instead. | |
| 160 | // Every recorded definition, over every file. | |
| 161 | all_definitions() -> Ghul.Pipes.Pipe[Symbols.Symbol] is | |
| 162 | for symbols in _symbol_definition_map.values do | |
| 163 | for symbol in symbols do | |
| 164 | yield symbol | |
| 165 | od | |
| 166 | od | |
| 167 | si | |
| 168 | ||
| 169 | has_definitions_for(file_name: string) -> bool => | |
| 170 | _symbol_definition_map.contains_key(file_name) /\ _symbol_definition_map[file_name].count > 0 | |
| 171 | ||
| 172 | get_symbol_list_for_file_name(file_name: string) -> Collections.LIST[Symbols.Symbol] is | |
| 173 | if _symbol_definition_map.contains_key(file_name) then | |
| 174 | return _symbol_definition_map[file_name] | |
| 175 | fi | |
| 176 | ||
| 177 | let result = Collections.LIST[Symbols.Symbol]() | |
| 178 | ||
| 179 | _symbol_definition_map[file_name] = result | |
| 180 | _definition_locations[file_name] = Collections.LIST[LOCATION]() | |
| 181 | ||
| 182 | return result | |
| 183 | si | |
| 184 | ||
| 185 | get_workspace_list_for_file_name(file_name: string) -> Collections.LIST[Symbols.Symbol] is | |
| 186 | if _workspace_definition_map.contains_key(file_name) then | |
| 187 | return _workspace_definition_map[file_name] | |
| 188 | fi | |
| 189 | ||
| 190 | let result = Collections.LIST[Symbols.Symbol]() | |
| 191 | ||
| 192 | _workspace_definition_map[file_name] = result | |
| 193 | ||
| 194 | for symbol in get_symbol_list_for_file_name(file_name) do | |
| 195 | if symbol.is_workspace_visible then | |
| 196 | result.add(symbol) | |
| 197 | fi | |
| 198 | od | |
| 199 | ||
| 200 | return result | |
| 201 | si | |
| 202 | si | |
| 203 | si |