Skip to content

Commit b589f58

Browse files
Add indexed include definition navigation
1 parent b01a982 commit b589f58

14 files changed

Lines changed: 304 additions & 2 deletions

File tree

src/index/merged_index.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "index/serialization.h"
77
#include "support/filesystem.h"
8+
#include "syntax/lexer.h"
89

910
#include "llvm/ADT/DenseSet.h"
1011
#include "llvm/Support/raw_os_ostream.h"
@@ -443,6 +444,44 @@ void MergedIndex::lookup(this const Self& self,
443444
}
444445
}
445446

447+
std::optional<std::uint32_t> MergedIndex::find_include_definition(this const Self& self,
448+
std::uint32_t offset) {
449+
if(self.impl) {
450+
auto& index = *self.impl;
451+
auto argument = find_directive_argument_at(index.content, offset);
452+
if(!argument) {
453+
return std::nullopt;
454+
}
455+
for(auto& [_, context]: index.compilation_contexts) {
456+
for(auto& location: context.include_locations) {
457+
if(location.include == static_cast<std::uint32_t>(-1) &&
458+
location.line == argument->line) {
459+
return location.path_id;
460+
}
461+
}
462+
}
463+
} else if(self.buffer) {
464+
auto index = fbs::GetRoot<binary::MergedIndex>(self.buffer->getBufferStart());
465+
llvm::StringRef content;
466+
if(auto* stored_content = index->content()) {
467+
content = stored_content->string_view();
468+
}
469+
auto argument = find_directive_argument_at(content, offset);
470+
if(!argument) {
471+
return std::nullopt;
472+
}
473+
for(auto context: *index->compilation_contexts()) {
474+
for(auto location: *context->include_locations()) {
475+
if(location->include_id() == static_cast<std::uint32_t>(-1) &&
476+
location->line() == argument->line) {
477+
return location->path_id();
478+
}
479+
}
480+
}
481+
}
482+
return std::nullopt;
483+
}
484+
446485
void MergedIndex::lookup(this const Self& self,
447486
SymbolHash symbol,
448487
RelationKind kind,

src/index/merged_index.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <chrono>
44
#include <cstdint>
55
#include <memory>
6+
#include <optional>
67
#include <vector>
78

89
#include "index/tu_index.h"
@@ -48,6 +49,10 @@ class MergedIndex {
4849
std::uint32_t offset,
4950
llvm::function_ref<bool(const Occurrence&)> callback);
5051

52+
/// Find the included path id for an include directive argument at `offset`.
53+
std::optional<std::uint32_t> find_include_definition(this const Self& self,
54+
std::uint32_t offset);
55+
5156
/// Lookup the relations of given symbol.
5257
void lookup(this const Self& self,
5358
SymbolHash symbol,

src/server/compiler/compiler.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ kota::task<> Compiler::run_compile(std::uint32_t pid, std::shared_ptr<Session::P
761761
OpenFileIndex ofi;
762762
ofi.file_index = std::move(tu_index.main_file_index);
763763
ofi.symbols = std::move(tu_index.symbols);
764+
ofi.include_paths = std::move(tu_index.graph.paths);
765+
ofi.include_locations = std::move(tu_index.graph.locations);
764766
ofi.content = sess->text;
765767
ofi.mapper.emplace(ofi.content, lsp::PositionEncoding::UTF16);
766768
sess->file_index = std::move(ofi);

src/server/compiler/indexer.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ namespace clice {
2626

2727
namespace lsp = kota::ipc::lsp;
2828

29+
namespace {
30+
31+
auto zero_range() -> protocol::Range {
32+
auto pos = protocol::Position{.line = 0, .character = 0};
33+
return protocol::Range{.start = pos, .end = pos};
34+
}
35+
36+
auto file_location(llvm::StringRef path) -> std::optional<protocol::Location> {
37+
auto uri = lsp::URI::from_file_path(std::string(path));
38+
if(!uri) {
39+
return std::nullopt;
40+
}
41+
return protocol::Location{.uri = uri->str(), .range = zero_range()};
42+
}
43+
44+
} // namespace
45+
2946
void Indexer::merge(const void* tu_index_data, std::size_t size) {
3047
auto tu_index = index::TUIndex::from(tu_index_data);
3148
if(tu_index.graph.paths.empty()) {
@@ -243,10 +260,57 @@ Indexer::CursorHit Indexer::resolve_cursor(llvm::StringRef path,
243260
return {};
244261
}
245262

263+
std::optional<protocol::Location>
264+
Indexer::find_include_definition(llvm::StringRef path,
265+
const protocol::Position& position,
266+
Session* session) {
267+
if(session && session->file_index && session->file_index->mapper) {
268+
auto offset = session->file_index->mapper->to_offset(position);
269+
if(!offset) {
270+
return std::nullopt;
271+
}
272+
auto target = session->file_index->find_include_definition(*offset);
273+
if(target && *target < session->file_index->include_paths.size()) {
274+
return file_location(session->file_index->include_paths[*target]);
275+
}
276+
}
277+
278+
const std::string* doc_text = session ? &session->text : nullptr;
279+
if(!doc_text) {
280+
return std::nullopt;
281+
}
282+
lsp::PositionMapper doc_mapper(*doc_text, lsp::PositionEncoding::UTF16);
283+
auto offset = doc_mapper.to_offset(position);
284+
if(!offset) {
285+
return std::nullopt;
286+
}
287+
288+
auto proj_it = workspace.project_index.path_pool.find(path);
289+
if(proj_it == workspace.project_index.path_pool.cache.end()) {
290+
return std::nullopt;
291+
}
292+
auto shard_it = workspace.merged_indices.find(proj_it->second);
293+
if(shard_it == workspace.merged_indices.end()) {
294+
return std::nullopt;
295+
}
296+
297+
auto target = shard_it->second.find_include_definition(*offset);
298+
if(!target || *target >= workspace.project_index.path_pool.paths.size()) {
299+
return std::nullopt;
300+
}
301+
return file_location(workspace.project_index.path_pool.path(*target));
302+
}
303+
246304
std::vector<protocol::Location> Indexer::query_relations(llvm::StringRef path,
247305
const protocol::Position& position,
248306
RelationKind kind,
249307
Session* session) {
308+
if(kind.value() == RelationKind::Definition) {
309+
if(auto include = find_include_definition(path, position, session)) {
310+
return {*include};
311+
}
312+
}
313+
250314
auto hit = resolve_cursor(path, position, session);
251315
if(hit.hash == 0)
252316
return {};

src/server/compiler/indexer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ class Indexer {
224224
const protocol::Position& position,
225225
Session* session);
226226

227+
/// Resolve an include directive argument at (position), if any.
228+
std::optional<protocol::Location> find_include_definition(llvm::StringRef path,
229+
const protocol::Position& position,
230+
Session* session);
231+
227232
/// Collect relations grouped by target symbol, across all index sources.
228233
void collect_grouped_relations(
229234
index::SymbolHash hash,

src/server/worker/stateful_worker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ void StatefulWorker::register_handlers() {
241241
return result ? to_raw(*result) : kota::codec::RawValue{"null"};
242242
});
243243
case K::GoToDefinition:
244-
// TODO: Implement go-to-definition
244+
// Indexer handles go-to-definition; worker fallback is intentionally empty.
245245
co_return kota::codec::RawValue{"[]"};
246246
case K::SemanticTokens:
247247
co_return co_await with_ast(params.path, [&](DocumentEntry& doc) {

src/server/workspace/workspace.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "support/filesystem.h"
77
#include "support/logging.h"
8+
#include "syntax/lexer.h"
89
#include "syntax/scan.h"
910

1011
#include "kota/codec/json/json.h"
@@ -36,6 +37,24 @@ const static index::Occurrence* lookup_occurrence(const std::vector<index::Occur
3637
return best;
3738
}
3839

40+
std::optional<std::uint32_t>
41+
lookup_include_definition(llvm::StringRef content,
42+
llvm::ArrayRef<index::IncludeLocation> includes,
43+
std::uint32_t offset) {
44+
auto argument = find_directive_argument_at(content, offset);
45+
if(!argument) {
46+
return std::nullopt;
47+
}
48+
49+
for(auto& include: includes) {
50+
if(include.include == static_cast<std::uint32_t>(-1) &&
51+
include.line == argument->line) {
52+
return include.path_id;
53+
}
54+
}
55+
return std::nullopt;
56+
}
57+
3958
std::optional<std::pair<index::SymbolHash, protocol::Range>>
4059
OpenFileIndex::find_occurrence(std::uint32_t offset) const {
4160
if(!mapper)
@@ -53,6 +72,10 @@ std::optional<std::pair<index::SymbolHash, protocol::Range>>
5372
};
5473
}
5574

75+
std::optional<std::uint32_t> OpenFileIndex::find_include_definition(std::uint32_t offset) const {
76+
return lookup_include_definition(content, include_locations, offset);
77+
}
78+
5679
std::optional<std::pair<index::SymbolHash, protocol::Range>>
5780
MergedIndexShard::find_occurrence(std::uint32_t offset) const {
5881
auto* m = mapper();
@@ -73,6 +96,10 @@ std::optional<std::pair<index::SymbolHash, protocol::Range>>
7396
return result;
7497
}
7598

99+
std::optional<std::uint32_t> MergedIndexShard::find_include_definition(std::uint32_t offset) const {
100+
return index.find_include_definition(offset);
101+
}
102+
76103
llvm::SmallVector<std::uint32_t> Workspace::on_file_saved(std::uint32_t path_id) {
77104
llvm::SmallVector<std::uint32_t> dirtied;
78105

src/server/workspace/workspace.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <string>
77
#include <unordered_map>
88
#include <utility>
9+
#include <vector>
910

1011
#include "command/command.h"
1112
#include "command/toolchain.h"
@@ -56,6 +57,8 @@ struct HeaderFileContext {
5657
struct OpenFileIndex {
5758
index::FileIndex file_index;
5859
index::SymbolTable symbols;
60+
std::vector<std::string> include_paths;
61+
std::vector<index::IncludeLocation> include_locations;
5962
std::string content; ///< Buffer text at index time (for position mapping).
6063

6164
/// Cached PositionMapper built from `content`. Avoids re-scanning line
@@ -67,6 +70,9 @@ struct OpenFileIndex {
6770
std::optional<std::pair<index::SymbolHash, protocol::Range>>
6871
find_occurrence(std::uint32_t offset) const;
6972

73+
/// Find an include definition target path id at byte offset.
74+
std::optional<std::uint32_t> find_include_definition(std::uint32_t offset) const;
75+
7076
/// Iterate relations matching `kind`, calling back with pre-converted ranges.
7177
/// Callback: (const index::Relation&, protocol::Range) -> bool (true = continue).
7278
template <typename Fn>
@@ -115,6 +121,9 @@ struct MergedIndexShard {
115121
std::optional<std::pair<index::SymbolHash, protocol::Range>>
116122
find_occurrence(std::uint32_t offset) const;
117123

124+
/// Find an include definition target path id at byte offset.
125+
std::optional<std::uint32_t> find_include_definition(std::uint32_t offset) const;
126+
118127
/// Iterate relations matching `kind`, calling back with pre-converted ranges.
119128
/// Callback: (const index::Relation&, protocol::Range) -> bool (true = continue).
120129
template <typename Fn>

src/syntax/lexer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,33 @@ std::optional<LocalSourceRange> find_directive_argument(llvm::StringRef content,
162162
return std::nullopt;
163163
}
164164

165+
std::optional<DirectiveArgument> find_directive_argument_at(llvm::StringRef content,
166+
std::uint32_t offset,
167+
const clang::LangOptions* lang_opts) {
168+
if(offset >= content.size()) {
169+
return std::nullopt;
170+
}
171+
172+
std::uint32_t line_start = 0;
173+
if(offset > 0) {
174+
auto pos = content.rfind('\n', offset - 1);
175+
if(pos != llvm::StringRef::npos) {
176+
line_start = static_cast<std::uint32_t>(pos + 1);
177+
}
178+
}
179+
180+
auto range = find_directive_argument(content, line_start, lang_opts);
181+
if(!range || !range->contains(offset)) {
182+
return std::nullopt;
183+
}
184+
185+
std::uint32_t line = 1;
186+
for(char c: content.take_front(line_start)) {
187+
if(c == '\n') {
188+
++line;
189+
}
190+
}
191+
return DirectiveArgument{*range, line};
192+
}
193+
165194
} // namespace clice

src/syntax/lexer.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,17 @@ std::optional<LocalSourceRange>
8282
std::uint32_t offset,
8383
const clang::LangOptions* lang_opts = nullptr);
8484

85+
struct DirectiveArgument {
86+
LocalSourceRange range;
87+
std::uint32_t line = 0;
88+
};
89+
90+
/// Find the directive argument containing `offset`.
91+
/// Returns its source range and 1-based line number, or nullopt if `offset`
92+
/// is not inside a directive argument.
93+
std::optional<DirectiveArgument>
94+
find_directive_argument_at(llvm::StringRef content,
95+
std::uint32_t offset,
96+
const clang::LangOptions* lang_opts = nullptr);
97+
8598
} // namespace clice

0 commit comments

Comments
 (0)