hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
hover.cpp
Go to the documentation of this file.
1//
2// Hover Provider implementation
3//
4
5#include "hover.h"
6#include <algorithm>
7#include <share/def.hpp>
8
9namespace lsp {
10
11std::optional<Hover> HoverProvider::provide(Document *doc, const Position &pos) {
12 if (!doc || !doc->parseSucceeded) return std::nullopt;
13 return hoverOnSymbol(doc, pos);
14}
15
16static Hover makeHoverForSymbol(const Symbol &sym) {
17 Hover hover;
18 hover.contents.kind = "markdown";
19
20 std::string kindStr;
21 switch (sym.kind) {
22 case HoshiSymbolKind::Function: kindStr = "**Function**"; break;
23 case HoshiSymbolKind::Struct: kindStr = "**Struct**"; break;
24 case HoshiSymbolKind::Interface: kindStr = "**Interface**"; break;
25 case HoshiSymbolKind::Variable: kindStr = "**Variable**"; break;
26 case HoshiSymbolKind::TypeAlias: kindStr = "**Type Alias**"; break;
27 case HoshiSymbolKind::Enum: kindStr = "**Enum**"; break;
29 case HoshiSymbolKind::ModuleAlias: kindStr = "**Module**"; break;
30 case HoshiSymbolKind::Import: kindStr = "**Import**"; break;
31 case HoshiSymbolKind::Export: kindStr = "**Export**"; break;
32 case HoshiSymbolKind::Concept_: kindStr = "**Concept**"; break;
33 case HoshiSymbolKind::Method: kindStr = "**Method**"; break;
34 case HoshiSymbolKind::Field: kindStr = "**Field**"; break;
35 case HoshiSymbolKind::Constructor: kindStr = "**Constructor**"; break;
36 case HoshiSymbolKind::Finalizer: kindStr = "**Finalizer**"; break;
37 case HoshiSymbolKind::EnumMember: kindStr = "**Enum Member**"; break;
38 default: kindStr = "**Symbol**"; break;
39 }
40
41 hover.contents.value = "```hoshi\n" + yoi::wstring2string(sym.detail) + "\n```\n" + kindStr;
42 if (!sym.parentName.empty() && sym.kind != HoshiSymbolKind::ModuleAlias)
43 hover.contents.value += "\n\nParent: `" + yoi::wstring2string(sym.parentName) + "`";
44 if (!sym.typeInfo.empty())
45 hover.contents.value += "\n\nType: `" + yoi::wstring2string(sym.typeInfo) + "`";
46 if (!sym.sourceFile.empty())
47 hover.contents.value += "\n\nDefined in: `" + yoi::wstring2string(sym.sourceFile) + "`";
48 if (!sym.importPath.empty())
49 hover.contents.value += "\n\nModule path: `" + yoi::wstring2string(sym.importPath) + "`";
50
51 return hover;
52}
53
54std::optional<Hover> HoverProvider::hoverOnSymbol(Document *doc, const Position &pos) {
55 int line = static_cast<int>(pos.line);
56 int col = static_cast<int>(pos.character);
57
58 // Extract the word at the cursor position
59 std::string lineText;
60 {
61 int lineIdx = 0;
62 size_t start = 0;
63 std::string text = yoi::wstring2string(doc->text);
64 for (size_t i = 0; i < text.size(); i++) {
65 if (text[i] == '\n') {
66 if (lineIdx == line) {
67 lineText = text.substr(start, i - start);
68 break;
69 }
70 lineIdx++;
71 start = i + 1;
72 }
73 }
74 if (lineText.empty() && lineIdx == line) {
75 lineText = text.substr(start);
76 }
77 }
78
79 if (col > static_cast<int>(lineText.size())) return std::nullopt;
80
81 // Find word boundaries at cursor
82 int wordStart = col;
83 int wordEnd = col;
84 while (wordStart > 0 && (std::isalnum(lineText[wordStart - 1]) || lineText[wordStart - 1] == '_'))
85 wordStart--;
86 while (wordEnd < static_cast<int>(lineText.size()) && (std::isalnum(lineText[wordEnd]) || lineText[wordEnd] == '_'))
87 wordEnd++;
88
89 if (wordStart == wordEnd) return std::nullopt;
90
91 std::string word = lineText.substr(wordStart, wordEnd - wordStart);
92 yoi::wstr wWord = yoi::string2wstring(word);
93
94 // Check for dot-access: if there's an identifier before a dot, it's a module/struct member
95 std::string beforeWord = lineText.substr(0, wordStart);
96 yoi::wstr parentName;
97 size_t lastDot = beforeWord.rfind('.');
98 if (lastDot != std::string::npos) {
99 // Extract the identifier before the dot
100 std::string before = beforeWord.substr(0, lastDot);
101 size_t idEnd = before.size();
102 size_t idStart = idEnd;
103 while (idStart > 0 && (std::isalnum(before[idStart - 1]) || before[idStart - 1] == '_'))
104 idStart--;
105 if (idStart < idEnd) {
106 parentName = yoi::string2wstring(before.substr(idStart, idEnd - idStart));
107 }
108 }
109
110 // Search in document-local symbols
111 for (auto &sym : doc->symbols) {
112 if (sym.name == wWord && static_cast<int>(sym.line) == line &&
113 isSymbolVisibleAt(doc->symbols, sym, static_cast<yoi::indexT>(line))) {
114 return makeHoverForSymbol(sym);
115 }
116 // Check children
117 for (auto &child : sym.children) {
118 if (child.name == wWord && static_cast<int>(child.line) == line) {
119 return makeHoverForSymbol(child);
120 }
121 }
122 }
123
124 // If we have a parent (dot access), search cross-module symbols under that parent
125 if (!parentName.empty()) {
126 for (auto &cross : doc->crossModuleSymbols) {
127 if (cross.isLocal) continue;
128 if (cross.name == wWord && cross.parentName == parentName) {
129 return makeHoverForSymbol(cross);
130 }
131 }
132 }
133
134 // Broader search: any local symbol with matching name (any line)
135 for (auto &sym : doc->symbols) {
136 if (!isSymbolVisibleAt(doc->symbols, sym, static_cast<yoi::indexT>(line))) continue;
137 if (sym.name == wWord) {
138 return makeHoverForSymbol(sym);
139 }
140 for (auto &child : sym.children) {
141 if (child.name == wWord) {
142 return makeHoverForSymbol(child);
143 }
144 }
145 }
146
147 // Cross-module search (any parent)
148 for (auto &cross : doc->crossModuleSymbols) {
149 if (cross.isLocal) continue;
150 if (cross.name == wWord) {
151 return makeHoverForSymbol(cross);
152 }
153 }
154
155 // Search all indexed modules (builtin, etc.)
156 if (doc->projectIndex) {
157 doc->projectIndex->indexAndGet("builtin");
158 yoi::vec<Symbol> allSyms;
159 doc->projectIndex->getAllSymbols(allSyms);
160 for (auto &sym : allSyms) {
161 if (sym.name == wWord) {
162 return makeHoverForSymbol(sym);
163 }
164 for (auto &child : sym.children) {
165 if (child.name == wWord) {
166 return makeHoverForSymbol(child);
167 }
168 }
169 }
170 }
171
172 return std::nullopt;
173}
174
175} // namespace lsp
std::optional< Hover > provide(Document *doc, const Position &pos)
Definition hover.cpp:11
std::optional< Hover > hoverOnSymbol(Document *doc, const Position &pos)
Definition hover.cpp:54
const yoi::vec< Symbol > & indexAndGet(const std::string &absolutePath)
Parse and index a module, then return its symbols.
void getAllSymbols(yoi::vec< Symbol > &out)
Collect all symbols from all indexed modules into flat list.
bool isSymbolVisibleAt(const yoi::vec< Symbol > &symbols, const Symbol &symbol, yoi::indexT line)
static Hover makeHoverForSymbol(const Symbol &sym)
Definition hover.cpp:16
std::string wstring2string(const std::wstring &v)
Definition def.cpp:230
std::vector< t > vec
Definition def.hpp:56
std::wstring string2wstring(const std::string &v)
Definition def.cpp:224
std::wstring wstr
Definition def.hpp:51
uint64_t indexT
Definition def.hpp:54
yoi::vec< Symbol > crossModuleSymbols
Definition document.h:30
yoi::vec< Symbol > symbols
Definition document.h:29
yoi::wstr text
Definition document.h:27
ProjectIndex * projectIndex
Definition document.h:33
bool parseSucceeded
Definition document.h:32
MarkupContent contents
Definition protocol.h:247
std::string value
Definition protocol.h:239
std::string kind
Definition protocol.h:238
yoi::wstr detail
yoi::wstr importPath
yoi::wstr parentName
HoshiSymbolKind kind
yoi::wstr typeInfo
yoi::wstr sourceFile