Skip to content

Commit

Permalink
Merge pull request #28310 from brandonlangley/moose_server_find_refer…
Browse files Browse the repository at this point in the history
…ences_22766

Implement full find references capability for MooseServer
  • Loading branch information
loganharbour authored Aug 26, 2024
2 parents 52a1053 + 32d2880 commit 45baf0b
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 44 deletions.
23 changes: 20 additions & 3 deletions framework/include/base/MooseServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ class MooseServer : public wasp::lsp::ServerImpl
const std::string & val_string);

/**
* Add locations of lookups or parameter declarator to definition list.
* @param definitionLocations - data array of locations objects to fill
* Add set of nodes sorted by location to definition or reference list.
* @param defsOrRefsLocations - data array of locations objects to fill
* @param location_nodes - set of nodes that have locations to be added
* @return - true if filling of location objects completed successfully
*/
bool addLocationNodesToList(wasp::DataArray & definitionLocations,
bool addLocationNodesToList(wasp::DataArray & defsOrRefsLocations,
const SortedLocationNodes & location_nodes);

/**
Expand All @@ -253,6 +253,18 @@ class MooseServer : public wasp::lsp::ServerImpl
int character,
bool include_declaration);

/**
* Recursively walk input to gather all nodes matching value and types.
* @param match_nodes - set to fill with nodes matching value and types
* @param view_parent - nodeview used to start recursive tree traversal
* @param target_value -
* @param target_types -
*/
void getNodesByValueAndTypes(SortedLocationNodes & match_nodes,
wasp::HITNodeView view_parent,
const std::string & target_value,
const std::set<std::string> & target_types);

/**
* Gather formatting text edits - specific to this server implemention.
* @param formattingTextEdits - data array of text edit objects to fill
Expand Down Expand Up @@ -378,6 +390,11 @@ class MooseServer : public wasp::lsp::ServerImpl
*/
std::map<std::string, std::set<std::string>> _type_to_input_paths;

/**
* @brief _type_to_input_paths - map of lookup paths to parameter types
*/
std::map<std::string, std::set<std::string>> _input_path_to_types;

/**
* @brief _formatting_tab_size - number of indent spaces for formatting
*/
Expand Down
132 changes: 118 additions & 14 deletions framework/src/base/MooseServer.C
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ MooseServer::MooseServer(MooseApp & moose_app)
server_capabilities[wasp::lsp::m_doc_symbol_provider] = true;
server_capabilities[wasp::lsp::m_doc_format_provider] = true;
server_capabilities[wasp::lsp::m_definition_provider] = true;
server_capabilities[wasp::lsp::m_references_provider] = false;
server_capabilities[wasp::lsp::m_references_provider] = true;
server_capabilities[wasp::lsp::m_hover_provider] = true;
}

Expand Down Expand Up @@ -977,20 +977,20 @@ MooseServer::getInputLookupDefinitionNodes(SortedLocationNodes & location_nodes,
}

bool
MooseServer::addLocationNodesToList(wasp::DataArray & definitionLocations,
MooseServer::addLocationNodesToList(wasp::DataArray & defsOrRefsLocations,
const SortedLocationNodes & location_nodes)
{
bool pass = true;

// walk over set of nodes with locations to add and build definition list
// walk over set of sorted nodes provided to add and build locations list
for (const auto & location_nodes_iter : location_nodes)
{
// add file scheme prefix to front of file path to build definition uri
// add file scheme prefix onto front of file path to build location uri
auto location_uri = wasp::lsp::m_uri_prefix + location_nodes_iter.node_pool()->stream_name();

// add file uri and zero based line and column range to definition list
definitionLocations.push_back(wasp::DataObject());
wasp::DataObject * location = definitionLocations.back().to_object();
// add file uri with zero based line and column range to locations list
defsOrRefsLocations.push_back(wasp::DataObject());
wasp::DataObject * location = defsOrRefsLocations.back().to_object();
pass &= wasp::lsp::buildLocationObject(*location,
errors,
location_uri,
Expand Down Expand Up @@ -1086,16 +1086,120 @@ MooseServer::getHoverDisplayText(std::string & display_text, int line, int chara
}

bool
MooseServer::gatherDocumentReferencesLocations(wasp::DataArray & /* referencesLocations */,
int /* line */,
int /* character */,
bool /* include_declaration */)
MooseServer::gatherDocumentReferencesLocations(wasp::DataArray & referencesLocations,
int line,
int character,
bool include_declaration)
{
bool pass = true;
Syntax & syntax = getCheckApp()->syntax();

// TODO - hook up this capability by adding server specific logic here
// return without adding any reference locations when parser root is null
if (!rootIsValid())
return true;

return pass;
// find hit node for zero based request line and column number from input
wasp::HITNodeView view_root = getRoot().getNodeView();
wasp::HITNodeView request_context =
wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);

// return without adding any references when request not block declarator
if ((request_context.type() != wasp::DECL && request_context.type() != wasp::DOT_SLASH &&
request_context.type() != wasp::LBRACKET && request_context.type() != wasp::RBRACKET) ||
!request_context.has_parent() || request_context.parent().type() != wasp::OBJECT)
return true;

// get input path and block name of declarator located at request context
const std::string & block_path = request_context.parent().path();
const std::string & block_name = request_context.parent().name();

// build map from input lookup paths to parameter types and save to reuse
if (_input_path_to_types.empty())
for (const auto & associated_types_iter : syntax.getAssociatedTypes())
{
const std::string & path = associated_types_iter.first;
const std::string & type = associated_types_iter.second;
_input_path_to_types[path].insert(type);
}

// get registered syntax from block path with map of input paths to types
bool is_parent;
std::string registered_syntax = syntax.isAssociated(block_path, &is_parent, _input_path_to_types);

// return without adding any references if syntax has no types associated
if (is_parent || !_input_path_to_types.count(registered_syntax))
return true;

// get set of parameter types which are associated with registered syntax
const std::set<std::string> & target_types = _input_path_to_types.at(registered_syntax);

// set used to gather nodes collected by value custom sorted by locations
SortedLocationNodes match_nodes(
[](const wasp::HITNodeView & l, const wasp::HITNodeView & r)
{
const std::string & l_file = l.node_pool()->stream_name();
const std::string & r_file = r.node_pool()->stream_name();
return (l_file < r_file || (l_file == r_file && l.line() < r.line()) ||
(l_file == r_file && l.line() == r.line() && l.column() < r.column()));
});

// walk input recursively and gather all nodes that match value and types
getNodesByValueAndTypes(match_nodes, view_root, block_name, target_types);

// return without adding any references if no nodes match value and types
if (match_nodes.empty())
return true;

// add request context node to set if declaration inclusion was specified
if (include_declaration && request_context.parent().child_count_by_name("decl"))
match_nodes.insert(request_context.parent().first_child_by_name("decl"));

// add locations to references list with nodes that match value and types
return addLocationNodesToList(referencesLocations, match_nodes);
}

void
MooseServer::getNodesByValueAndTypes(SortedLocationNodes & match_nodes,
wasp::HITNodeView view_parent,
const std::string & target_value,
const std::set<std::string> & target_types)
{
// walk over children of context to gather nodes matching value and types
for (const auto & view_child : view_parent)
{
// check for parameter type match if node is value matching target data
if (view_child.type() == wasp::VALUE && view_child.to_string() == target_value)
{
// get object context path and object type value of node if it exists
wasp::HITNodeView object_context = view_child;
while (object_context.type() != wasp::OBJECT && object_context.has_parent())
object_context = object_context.parent();
const std::string object_path = object_context.path();
wasp::HITNodeView type_node = object_context.first_child_by_name("type");
const std::string object_type =
type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));

// gather global, action, and object parameters for context of object
InputParameters valid_params = emptyInputParameters();
std::set<std::string> obj_act_tasks;
getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);

// get name from parent of current value node which is parameter node
std::string param_name = view_child.has_parent() ? view_child.parent().name() : "";

// get type of parameter and prepare string to check target set match
std::string dirty_type = valid_params.type(param_name);
std::string clean_type = MooseUtils::prettyCppType(dirty_type);
pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);

// add input node to collection if its type is also in set of targets
if (target_types.count(clean_type))
match_nodes.insert(view_child);
}

// recurse deeper into input to search for matches if node has children
if (!view_child.is_leaf())
getNodesByValueAndTypes(match_nodes, view_child, target_value, target_types);
}
}

bool
Expand Down
115 changes: 88 additions & 27 deletions unit/src/MooseServerTest.C
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ protected:
pcrecpp::RE(uri_pattern).Replace(uri_replace, &location_uri);

locations_stream << "document_uri: \"" << location_uri << "\""
<< " definition_start: [" << location_start_line << "."
<< location_start_character << "] definition_end: [" << location_end_line
<< " location_start: [" << location_start_line << "."
<< location_start_character << "] location_end: [" << location_end_line
<< "." << location_end_character << "]"
<< "\n";
}
Expand Down Expand Up @@ -469,7 +469,7 @@ TEST_F(MooseServerTest, InitializeAndInitialized)
EXPECT_TRUE(server_capabilities[wasp::lsp::m_definition_provider].to_bool());

EXPECT_TRUE(server_capabilities[wasp::lsp::m_references_provider].is_bool());
EXPECT_FALSE(server_capabilities[wasp::lsp::m_references_provider].to_bool());
EXPECT_TRUE(server_capabilities[wasp::lsp::m_references_provider].to_bool());

EXPECT_TRUE(server_capabilities[wasp::lsp::m_hover_provider].is_bool());
EXPECT_TRUE(server_capabilities[wasp::lsp::m_hover_provider].to_bool());
Expand Down Expand Up @@ -1262,7 +1262,7 @@ TEST_F(MooseServerTest, DefinitionObjectTypeSource)
// expected locations with zero-based lines and columns

std::string locations_expect = R"INPUT(
document_uri: "file://...absolute.../framework/src/executioners/Transient.C" definition_start: [38.0] definition_end: [38.1000]
document_uri: "file://...absolute.../framework/src/executioners/Transient.C" location_start: [38.0] location_end: [38.1000]
)INPUT";

EXPECT_EQ(locations_expect, "\n" + locations_actual.str());
Expand Down Expand Up @@ -1317,9 +1317,9 @@ TEST_F(MooseServerTest, DefinitionInputFileLookups)
// expected locations with zero-based lines and columns

std::string locations_expect = R"INPUT(
document_uri: "file:///test/input/path" definition_start: [16.4] definition_end: [16.10]
document_uri: "file:///test/input/path" definition_start: [17.7] definition_end: [17.13]
document_uri: "file:///test/input/path" definition_start: [19.5] definition_end: [19.11]
document_uri: "file:///test/input/path" location_start: [16.4] location_end: [16.10]
document_uri: "file:///test/input/path" location_start: [17.7] location_end: [17.13]
document_uri: "file:///test/input/path" location_start: [19.5] location_end: [19.11]
)INPUT";

EXPECT_EQ(locations_expect, "\n" + locations_actual.str());
Expand Down Expand Up @@ -1492,38 +1492,99 @@ label: linear_residual_start_time text: linear_residual_start_time = d

TEST_F(MooseServerTest, DocumentReferencesRequest)
{
// references test parameters
// didchange test parameters - update input to set up document references
std::string doc_uri = wasp::lsp::m_uri_prefix + std::string("/test/input/path");
int doc_version = 5;
std::string doc_text_change = R"INPUT(
[Mesh]
type = GeneratedMesh
dim = 1
[]
[Variables]
[u][]
[v][]
[]
[Kernels]
[diff]
type = Diffusion
variable = "u"
[]
[]
[BCs]
[left]
type = VacuumBC
boundary = left
variable = u
prop_getter_suffix = u
[]
[right]
type = VacuumBC
boundary = right
variable = v
displacements = 'u v u v u'
[]
[]
[Executioner]
type = Transient
[]
[Problem]
solve = false
[]
)INPUT";

int request_id = 25;
std::string document_uri = wasp::lsp::m_uri_prefix + std::string("/test/input/path");
int line = 0;
int character = 0;
bool include_declaration = false;
// build didchange notification from parameters and handle it with server
wasp::DataObject didchange_notification, diagnostics_notification;
std::stringstream errors;
EXPECT_TRUE(wasp::lsp::buildDidChangeNotification(
didchange_notification, errors, doc_uri, doc_version, -1, -1, -1, -1, -1, doc_text_change));
EXPECT_TRUE(
moose_server->handleDidChangeNotification(didchange_notification, diagnostics_notification));

// build references request with the test parameters
// references test parameters - on subblock declarator of variable name u
int request_id = 25;
int request_line = 6;
int request_char = 4;
bool incl_decl = true;

// build references request with the test parameters for the moose_server
wasp::DataObject references_request;
std::stringstream references_errors;

EXPECT_TRUE(wasp::lsp::buildReferencesRequest(references_request,
references_errors,
request_id,
document_uri,
line,
character,
include_declaration));

doc_uri,
request_line,
request_char,
incl_decl));
EXPECT_TRUE(references_errors.str().empty());

// handle the built references request with the moose_server

// handle references request built from parameters using the moose_server
wasp::DataObject references_response;

EXPECT_TRUE(moose_server->handleReferencesRequest(references_request, references_response));

EXPECT_TRUE(moose_server->getErrors().empty());

// references response will be checked when capability is implemented
// check dissected values of references response sent by the moose_server
std::stringstream response_errors;
int response_id;
wasp::DataArray locations_array;
EXPECT_TRUE(wasp::lsp::dissectLocationsResponse(
references_response, response_errors, response_id, locations_array));
EXPECT_TRUE(response_errors.str().empty());
EXPECT_EQ(request_id, response_id);
EXPECT_EQ(6u, locations_array.size());

// make formatted list of response references and check it is as expected
std::ostringstream locations_actual;
format_locations(locations_array, locations_actual);
std::string locations_expect = R"INPUT(
document_uri: "file:///test/input/path" location_start: [6.3] location_end: [6.4]
document_uri: "file:///test/input/path" location_start: [12.15] location_end: [12.18]
document_uri: "file:///test/input/path" location_start: [19.15] location_end: [19.16]
document_uri: "file:///test/input/path" location_start: [26.21] location_end: [26.22]
document_uri: "file:///test/input/path" location_start: [26.25] location_end: [26.26]
document_uri: "file:///test/input/path" location_start: [26.29] location_end: [26.30]
)INPUT";
EXPECT_EQ(locations_expect, "\n" + locations_actual.str());
}

TEST_F(MooseServerTest, DocumentFormattingRequest)
Expand All @@ -1537,7 +1598,7 @@ TEST_F(MooseServerTest, DocumentFormattingRequest)
// didchange test parameters - update input to set up document formatting

std::string doc_uri = wasp::lsp::m_uri_prefix + std::string("/test/input/path");
int doc_version = 5;
int doc_version = 6;
std::string doc_text_change = R"INPUT(
num_dim = 2
Expand Down Expand Up @@ -1735,7 +1796,7 @@ TEST_F(MooseServerTest, DiagnosticsEmptyMessageSkip)
{
// didchange test parameters - create empty diagnostic which is not added
std::string doc_uri = wasp::lsp::m_uri_prefix + std::string("/test/input/path");
int doc_version = 6;
int doc_version = 7;
std::string doc_text_change = R"INPUT(
[Mesh]
type = GeneratedMesh
Expand Down

0 comments on commit 45baf0b

Please sign in to comment.