Skip to content

Commit 72973c6

Browse files
Support more documentation comments (#262)
* Support doc comments attached to table function properties * Add more tests * Support comments attached to vars * Improve comment extraction on hover * Update changelog * Allow variable number of equals signs
1 parent 04369ae commit 72973c6

File tree

4 files changed

+327
-21
lines changed

4 files changed

+327
-21
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1010

1111
- Added syntax highlighting support for interpolated strings
1212
- Added color viewers for Color3.new/fromRGB/fromHSV/fromHex
13+
- Support documentation comments on variables:
14+
15+
```lua
16+
--- documentation comment
17+
local x = "string"
18+
19+
--- another doc comment
20+
local y = function()
21+
end
22+
```
23+
24+
- Support documentation comments on table properties, such as the following:
25+
26+
```lua
27+
local tbl = {
28+
--- This is some special information
29+
data = "hello",
30+
--- This is a doc comment
31+
values = function()
32+
end,
33+
}
34+
35+
local x = tbl.values -- Should give "This is a doc comment"
36+
local y = tbl.data -- Should give "This is some special information"
37+
```
1338

1439
### Changed
1540

1641
- Sync to upstream Luau 0.558
1742
- All Luau FFlags are no longer enabled by default. This can be re-enabled by configuring `luau-lsp.fflags.enableByDefault`. It is recommended to keep `luau-lsp.fflags.sync` enabled so that FFlags sync with upstream Luau
43+
- Allow variable number of `=` sign for multiline doc comments, so `--[[` and `--[===[` etc. are valid openers
1844

1945
### Fixed
2046

src/DocumentationParser.cpp

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,74 @@ struct AttachCommentsVisitor : public Luau::AstVisitor
279279
return result;
280280
}
281281

282+
bool visit(Luau::AstExprTable* tbl) override
283+
{
284+
if (tbl->location.begin >= pos)
285+
return false;
286+
if (tbl->location.begin > closestPreviousNode)
287+
closestPreviousNode = tbl->location.begin;
288+
289+
for (Luau::AstExprTable::Item item : tbl->items)
290+
{
291+
if (item.value->location.begin >= pos)
292+
continue;
293+
if (item.value->location.begin > closestPreviousNode)
294+
closestPreviousNode = item.value->location.begin;
295+
item.value->visit(this);
296+
if (item.value->location.end <= pos && item.value->location.end > closestPreviousNode)
297+
closestPreviousNode = item.value->location.end;
298+
}
299+
300+
return false;
301+
}
302+
303+
bool visit(Luau::AstTypeTable* tbl) override
304+
{
305+
if (tbl->location.begin >= pos)
306+
return false;
307+
if (tbl->location.begin > closestPreviousNode)
308+
closestPreviousNode = tbl->location.begin;
309+
310+
for (Luau::AstTableProp item : tbl->props)
311+
{
312+
if (item.type->location.begin >= pos)
313+
continue;
314+
if (item.type->location.begin > closestPreviousNode)
315+
closestPreviousNode = item.type->location.begin;
316+
item.type->visit(this);
317+
if (item.type->location.end <= pos && item.type->location.end > closestPreviousNode)
318+
closestPreviousNode = item.type->location.end;
319+
}
320+
321+
return false;
322+
}
323+
282324
bool visit(Luau::AstStatBlock* block) override
283325
{
326+
// If the position is after the block, then it can be ignored
327+
// If the position is within the block, then we know we can cut anything before the block,
328+
// so set the previous node location to the block entry
329+
if (block->location.begin >= pos)
330+
return false;
331+
if (block->location.begin > closestPreviousNode)
332+
closestPreviousNode = block->location.begin;
333+
284334
for (Luau::AstStat* stat : block->body)
285335
{
286336
if (stat->location.begin >= pos)
287337
continue;
288-
if (stat->location.begin > closestPreviousNode)
289-
closestPreviousNode = stat->location.begin;
290338
stat->visit(this);
291339
if (stat->location.end <= pos && stat->location.end > closestPreviousNode)
292340
closestPreviousNode = stat->location.end;
293341
}
294342

295343
return false;
296344
}
345+
346+
bool visit(Luau::AstType* ty) override
347+
{
348+
return true;
349+
}
297350
};
298351

299352
std::vector<Luau::Comment> getCommentLocations(const Luau::SourceModule* module, const Luau::Location& node)
@@ -355,8 +408,16 @@ std::vector<std::string> WorkspaceFolder::getComments(const Luau::ModuleName& mo
355408
}
356409
else if (comment.type == Luau::Lexeme::Type::BlockComment)
357410
{
358-
if (Luau::startsWith(commentText, "--[=["))
411+
std::regex comment_regex("^--\\[(=*)\\[");
412+
std::smatch comment_match;
413+
414+
if (std::regex_search(commentText, comment_match, comment_regex))
359415
{
416+
LUAU_ASSERT(comment_match.size() == 2);
417+
// Construct "--[=[" and "--]=]" which will be ignored
418+
std::string start_string = "--[" + std::string(comment_match[1].length(), '=') + "[";
419+
std::string end_string = "]" + std::string(comment_match[1].length(), '=') + "]";
420+
360421
// Parse each line separately
361422
for (auto& line : Luau::split(commentText, '\n'))
362423
{
@@ -365,7 +426,7 @@ std::vector<std::string> WorkspaceFolder::getComments(const Luau::ModuleName& mo
365426

366427
auto trimmedLineText = std::string(line);
367428
trim(trimmedLineText);
368-
if (trimmedLineText == "--[=[" || trimmedLineText == "]=]")
429+
if (trimmedLineText == start_string || trimmedLineText == end_string)
369430
continue;
370431
comments.emplace_back(lineText);
371432
}

src/operations/Hover.cpp

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
#include "LSP/LuauExt.hpp"
77
#include "LSP/DocumentationParser.hpp"
88

9+
struct DocumentationLocation
10+
{
11+
Luau::ModuleName moduleName;
12+
Luau::Location location;
13+
};
14+
915
std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
1016
{
1117
auto config = client->getConfiguration(rootUri);
@@ -40,6 +46,7 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
4046
std::string typeName;
4147
std::optional<Luau::TypeId> type = std::nullopt;
4248
std::optional<std::string> documentationSymbol = getDocumentationSymbolAtPosition(*sourceModule, *module, position);
49+
std::optional<DocumentationLocation> documentationLocation = std::nullopt;
4350

4451
if (auto ref = node->as<Luau::AstTypeReference>())
4552
{
@@ -76,6 +83,7 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
7683
else if (auto local = exprOrLocal.getLocal()) // TODO: can we just use node here instead of also calling exprOrLocal?
7784
{
7885
type = scope->lookup(local);
86+
documentationLocation = {moduleName, local->location};
7987
}
8088
else if (auto expr = exprOrLocal.getExpr())
8189
{
@@ -100,6 +108,29 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
100108
}
101109
}
102110

111+
// Handle table properties (so that we can get documentation info)
112+
if (auto index = expr->as<Luau::AstExprIndexName>())
113+
{
114+
if (auto parentIt = module->astTypes.find(index->expr))
115+
{
116+
auto parentType = Luau::follow(*parentIt);
117+
auto indexName = index->index.value;
118+
auto prop = lookupProp(parentType, indexName);
119+
if (prop)
120+
{
121+
type = prop->type;
122+
if (auto definitionModuleName = Luau::getDefinitionModuleName(parentType); definitionModuleName && prop->location)
123+
documentationLocation = {definitionModuleName.value(), prop->location.value()};
124+
}
125+
}
126+
}
127+
128+
// Handle local variables separately to retrieve documentation location info
129+
if (auto local = expr->as<Luau::AstExprLocal>(); !documentationLocation.has_value() && local && local->local)
130+
{
131+
documentationLocation = {moduleName, local->local->location};
132+
}
133+
103134
if (!type)
104135
{
105136
if (auto it = module->astTypes.find(expr))
@@ -114,17 +145,6 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
114145
{
115146
type = scope->lookup(local->local);
116147
}
117-
else if (auto index = expr->as<Luau::AstExprIndexName>())
118-
{
119-
if (auto parentIt = module->astTypes.find(index->expr))
120-
{
121-
auto parentType = Luau::follow(*parentIt);
122-
auto indexName = index->index.value;
123-
auto prop = lookupProp(parentType, indexName);
124-
if (prop)
125-
type = prop->type;
126-
}
127-
}
128148
}
129149
}
130150

@@ -191,13 +211,20 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
191211
typeString += "\n----------\n";
192212
typeString += printDocumentation(client->documentation, *documentationSymbol);
193213
}
194-
else if (auto ftv = Luau::get<Luau::FunctionType>(*type))
214+
else if (auto ftv = Luau::get<Luau::FunctionType>(*type); ftv && ftv->definition && ftv->definition->definitionModuleName)
195215
{
196-
if (ftv->definition && ftv->definition->definitionModuleName)
197-
{
198-
typeString += "\n----------\n";
199-
typeString += printMoonwaveDocumentation(getComments(ftv->definition->definitionModuleName.value(), ftv->definition->definitionLocation));
200-
}
216+
typeString += "\n----------\n";
217+
typeString += printMoonwaveDocumentation(getComments(ftv->definition->definitionModuleName.value(), ftv->definition->definitionLocation));
218+
}
219+
// else if (auto ttv = Luau::get<Luau::TableType>(*type); ttv && !ttv->definitionModuleName.empty())
220+
// {
221+
// typeString += "\n----------\n";
222+
// typeString += printMoonwaveDocumentation(getComments(ttv->definitionModuleName, ttv->definitionLocation));
223+
// }
224+
else if (documentationLocation)
225+
{
226+
typeString += "\n----------\n";
227+
typeString += printMoonwaveDocumentation(getComments(documentationLocation->moduleName, documentationLocation->location));
201228
}
202229

203230
return lsp::Hover{{lsp::MarkupKind::Markdown, typeString}};

0 commit comments

Comments
 (0)