Skip to content

Commit

Permalink
Support documentation comments for table type props
Browse files Browse the repository at this point in the history
Closes #252
  • Loading branch information
JohnnyMorganz committed Nov 5, 2023
1 parent 280b7cc commit 4b78723
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 12 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added

- Added support for documentation comments on table type properties:

```lua
type Foo = {
--- A documentation comment
map: () -> ()
}
```

### Changed

- Sync to upstream Luau 0.602
Expand Down
3 changes: 3 additions & 0 deletions src/include/LSP/Workspace.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <iostream>
#include "Luau/Frontend.h"
#include "Luau/Autocomplete.h"
#include "Protocol/Structures.hpp"
#include "Protocol/LanguageFeatures.hpp"
#include "Protocol/SignatureHelp.hpp"
Expand Down Expand Up @@ -85,6 +86,8 @@ class WorkspaceFolder
public:
std::vector<std::string> getComments(const Luau::ModuleName& moduleName, const Luau::Location& node);
std::optional<std::string> getDocumentationForType(const Luau::TypeId ty);
std::optional<std::string> getDocumentationForAutocompleteEntry(
const Luau::AutocompleteEntry& entry, const std::vector<Luau::AstNode*>& ancestry, const Luau::ModuleName& moduleName);
std::vector<Reference> findAllReferences(const Luau::TypeId ty, std::optional<Luau::Name> property = std::nullopt);
std::vector<Reference> findAllTypeReferences(const Luau::ModuleName& moduleName, const Luau::Name& typeName);

Expand Down
65 changes: 56 additions & 9 deletions src/operations/Completion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,61 @@ static bool canUseSnippets(const lsp::ClientCapabilities& capabilities)
capabilities.textDocument->completion->completionItem->snippetSupport;
}

std::optional<std::string> WorkspaceFolder::getDocumentationForAutocompleteEntry(
const Luau::AutocompleteEntry& entry, const std::vector<Luau::AstNode*>& ancestry, const Luau::ModuleName& moduleName)
{
if (entry.documentationSymbol)
if (auto docs = printDocumentation(client->documentation, *entry.documentationSymbol))
return docs;

if (entry.type.has_value())
if (auto documentation = getDocumentationForType(entry.type.value()))
return documentation;

if (entry.prop)
{
std::optional<Luau::ModuleName> definitionModuleName;

if (entry.containingClass)
{
definitionModuleName = entry.containingClass.value()->definitionModuleName;
}
else
{
// TODO: there is not a nice way to get the containing table type from the entry, so we compute it ourselves
auto module = frontend.moduleResolverForAutocomplete.getModule(moduleName);

if (module)
{
Luau::TypeId* parentTy = nullptr;
if (auto node = ancestry.back())
{
if (auto indexName = node->as<Luau::AstExprIndexName>())
parentTy = module->astTypes.find(indexName->expr);
else if (auto indexExpr = node->as<Luau::AstExprIndexExpr>())
parentTy = module->astTypes.find(indexExpr->expr);
}

if (parentTy)
definitionModuleName = Luau::getDefinitionModuleName(*parentTy);
}
}

if (definitionModuleName)
{
if (auto propLocation = entry.prop.value()->location)
if (auto text = printMoonwaveDocumentation(getComments(definitionModuleName.value(), propLocation.value())); !text.empty())
return text;

if (auto typeLocation = entry.prop.value()->typeLocation)
if (auto text = printMoonwaveDocumentation(getComments(definitionModuleName.value(), typeLocation.value())); !text.empty())
return text;
}
}

return std::nullopt;
}

std::vector<lsp::CompletionItem> WorkspaceFolder::completion(const lsp::CompletionParams& params)
{
auto config = client->getConfiguration(rootUri);
Expand Down Expand Up @@ -660,15 +715,7 @@ std::vector<lsp::CompletionItem> WorkspaceFolder::completion(const lsp::Completi
item.deprecated = entry.deprecated;
item.sortText = SortText::Default;

// Handle documentation
std::optional<std::string> documentationString = std::nullopt;
if (std::optional<std::string> docs;
entry.documentationSymbol && (docs = printDocumentation(client->documentation, *entry.documentationSymbol)) && docs)
documentationString = *docs;
else if (entry.type.has_value())
documentationString = getDocumentationForType(entry.type.value());
// TODO: Handle documentation on properties
if (documentationString)
if (auto documentationString = getDocumentationForAutocompleteEntry(entry, result.ancestry, moduleName))
item.documentation = {lsp::MarkupKind::Markdown, documentationString.value()};

if (entry.wrongIndexType)
Expand Down
31 changes: 29 additions & 2 deletions src/operations/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,28 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
return std::nullopt;
type = typeFun->type;
}
else if (auto typeTable = node->as<Luau::AstTypeTable>())
{
if (auto tableTy = module->astResolvedTypes.find(typeTable))
{
type = *tableTy;

// Check if we are inside one of the properties
for (auto& prop : typeTable->props)
{
if (prop.location.containsClosed(position))
{
auto parentType = Luau::follow(*tableTy);
if (auto definitionModuleName = Luau::getDefinitionModuleName(parentType))
documentationLocation = {definitionModuleName.value(), prop.location};
auto resolvedProperty = lookupProp(parentType, prop.name.value);
if (resolvedProperty)
type = resolvedProperty->type();
break;
}
}
}
}
else if (auto astType = node->asType())
{
if (auto ty = module->astResolvedTypes.find(astType))
Expand Down Expand Up @@ -118,8 +140,13 @@ std::optional<lsp::Hover> WorkspaceFolder::hover(const lsp::HoverParams& params)
if (prop)
{
type = prop->type();
if (auto definitionModuleName = Luau::getDefinitionModuleName(parentType); definitionModuleName && prop->location)
documentationLocation = {definitionModuleName.value(), prop->location.value()};
if (auto definitionModuleName = Luau::getDefinitionModuleName(parentType))
{
if (prop->location)
documentationLocation = {definitionModuleName.value(), prop->location.value()};
else if (prop->typeLocation)
documentationLocation = {definitionModuleName.value(), prop->typeLocation.value()};
}
}
}
}
Expand Down
18 changes: 17 additions & 1 deletion tests/Documentation.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,22 @@ TEST_CASE_FIXTURE(Fixture, "attach_comments_to_variable_2")
CHECK_EQ(1, comments.size());
}

TEST_CASE_FIXTURE(Fixture, "attach_comments_to_table_type_props")
{
auto result = check(R"(
type Foo = {
--- A documentation comment
map: () -> (),
}
)");

REQUIRE_EQ(0, result.errors.size());

// Assume hovering over a var "tbl.data", which would give the position set to the property
auto comments = getCommentLocations(getMainSourceModule(), Luau::Location{{3, 17}, {3, 25}});
CHECK_EQ(1, comments.size());
}

TEST_CASE_FIXTURE(Fixture, "print_comments")
{
auto result = check(R"(
Expand Down Expand Up @@ -467,4 +483,4 @@ TEST_CASE_FIXTURE(Fixture, "ignored_tags")
"\n- `x` number -- Testing");
}

TEST_SUITE_END();
TEST_SUITE_END();

0 comments on commit 4b78723

Please sign in to comment.