Skip to content

Commit

Permalink
Merge pull request #26601 from brandonlangley/moose_server_format_upd…
Browse files Browse the repository at this point in the history
…ate_22766

Update MooseServer formatting to not pull in included file contents
  • Loading branch information
dschwen authored Jan 24, 2024
2 parents 1c35054 + e04b928 commit 58d0497
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 138 deletions.
70 changes: 37 additions & 33 deletions framework/contrib/hit/parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -319,18 +319,6 @@ Node::children(NodeType t)
return nodes;
}

std::string
Node::render(int indent, const std::string & indent_text, int maxlen)
{
if (root() != this)
indent = -1;

std::string s;
for (auto child : _children)
s += child->render(indent + 1, indent_text, maxlen) + "\n";
return s;
}

Node *
Node::parent()
{
Expand Down Expand Up @@ -490,7 +478,10 @@ Section::render(int indent, const std::string & indent_text, int maxlen)
{
std::string s;

if (!path().empty() && path() != "-")
// check if this is document root level in order to skip syntax rendering
bool at_root_level = path().empty() || path() == "-";

if (!at_root_level)
{
std::string opening_marks;
if (_hnv.child_count() > 3 && _hnv.child_at(2).type() == wasp::DECL &&
Expand All @@ -501,14 +492,9 @@ Section::render(int indent, const std::string & indent_text, int maxlen)
}

for (auto child : children())
{
if (!path().empty() && path() != "-")
s += child->render(indent + 1, indent_text, maxlen);
else
s += child->render(indent, indent_text, maxlen);
}
s += child->render(indent + (at_root_level ? 0 : 1), indent_text, maxlen);

if (!path().empty() && path() != "-")
if (!at_root_level)
{
wasp::HITNodeView term_node = _hnv.first_child_by_name("term");
std::string term_data = !term_node.is_null() ? term_node.data() : "[]";
Expand Down Expand Up @@ -553,18 +539,32 @@ Field::path()
std::string
Field::render(int indent, const std::string & indent_text, int maxlen)
{
auto render_field = path();
auto render_val = val();
auto render_kind = kind();

std::string s = "\n" + strRepeat(indent_text, indent) + render_field + " = ";
std::string s = "\n" + strRepeat(indent_text, indent) + path() + " = ";

const std::string render_val = val();
std::size_t val_column = _hnv.child_count() > 2 ? _hnv.child_at(2).column() : 0;
size_t prefix_len = s.size() - 1;

s += formatValue(render_val, val_column, prefix_len, maxlen);

for (auto child : children())
s += child->render(indent + 1, indent_text, maxlen);

return s;
}

std::string
formatValue(const std::string & render_val,
std::size_t val_column,
std::size_t prefix_len,
std::size_t max_length)
{
std::string s;
auto quote = quoteChar(render_val);
int max = maxlen - prefix_len - 1;
int max = max_length - prefix_len - 1;

// special rendering logic for double quoted strings that go over maxlen:
if (render_kind == Kind::String && quote == "\"" && max > 0)
if (quote == "\"" && max > 0)
{
if (render_val.find('\n') == std::string::npos)
{
Expand Down Expand Up @@ -612,7 +612,7 @@ Field::render(int indent, const std::string & indent_text, int maxlen)
}
else
{
const int delta_indent = _hnv.child_count() > 2 ? prefix_len - _hnv.child_at(2).column() : 0;
const int delta_indent = prefix_len - val_column;

// first line is always added as is
std::size_t start = 0;
Expand Down Expand Up @@ -655,9 +655,6 @@ Field::render(int indent, const std::string & indent_text, int maxlen)
else
s += render_val;

for (auto child : children())
s += child->render(indent + 1, indent_text, maxlen);

return s;
}

Expand Down Expand Up @@ -745,7 +742,13 @@ Field::setVal(const std::string & value, Kind kind)
std::string
Field::val()
{
std::string value = _hnv.data();
return extractValue(_hnv.data());
}

std::string
extractValue(const std::string & field_data)
{
std::string value = field_data;

size_t equal_index = value.find("=");
if (equal_index != std::string::npos)
Expand Down Expand Up @@ -1208,7 +1211,8 @@ buildHITTree(std::shared_ptr<wasp::DefaultHITInterpreter> interpreter,
hit_parent->addChild(new Blank());

auto hit_child = new Comment(interpreter, hnv_child);
if (hnv_child.line() == previous_line && hit_parent->children().size() > 0)
if (hnv_child.node_pool()->stream_name() == previous_file &&
hnv_child.line() == previous_line && hit_parent->children().size() > 0)
{
hit_child->setInline(true);
hit_parent->children().back()->addChild(hit_child);
Expand Down
11 changes: 10 additions & 1 deletion framework/contrib/hit/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ std::string pathNorm(const std::string & path);
/// pathJoin a joined version of the given hit (relative) paths as single hit path.
std::string pathJoin(const std::vector<std::string> & paths);

/// formatValue reflows double-quoted strings and reindents multi-line data
std::string formatValue(const std::string & render_val,
std::size_t val_column,
std::size_t prefix_len,
std::size_t max_length = 100);

/// extractValue returns after equal with quoted value merged and unescaped
std::string extractValue(const std::string & field_data);

/// Node represents an object in a parsed hit tree. Each node manages the memory for its child
/// nodes. It is safe to delete any node in the tree; doing so will also delete that node's
/// children recursively. It is not safe to place a single node in multiple trees. Instead, use
Expand Down Expand Up @@ -230,7 +239,7 @@ class Node
/// indent string (repeated once for each level). maxlen is the maximum line length before
/// breaking string values.
virtual std::string
render(int indent = 0, const std::string & indent_text = default_indent, int maxlen = 0);
render(int indent = 0, const std::string & indent_text = default_indent, int maxlen = 0) = 0;

/// walk does a depth-first traversal of the hit tree starting at this node (it
/// doesn't visit any nodes that require traversing this node's parent) calling the passed
Expand Down
14 changes: 14 additions & 0 deletions framework/include/base/MooseServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ class MooseServer : public wasp::lsp::ServerImpl
int tab_size,
bool insert_spaces);

/**
* Recursively walk down whole nodeview tree while formatting document.
* @param parent - nodeview for recursive tree traversal starting point
* @param prev_line - line of last print for blanks and inline comments
* @param level - current level in document tree to use for indentation
* @return - formatted string that gets appended to each recursive call
*/
std::string formatDocument(wasp::HITNodeView parent, std::size_t & prev_line, std::size_t level);

/**
* Gather document symbols - specific to this server implemention.
* @param documentSymbols - data array of symbols data objects to fill
Expand Down Expand Up @@ -324,4 +333,9 @@ class MooseServer : public wasp::lsp::ServerImpl
* @brief _type_to_input_paths - map of parameter types to lookup paths
*/
std::map<std::string, std::set<std::string>> _type_to_input_paths;

/**
* @brief _formatting_tab_size - number of indent spaces for formatting
*/
std::size_t _formatting_tab_size;
};
108 changes: 81 additions & 27 deletions framework/src/base/MooseServer.C
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#include <functional>

MooseServer::MooseServer(MooseApp & moose_app)
: _moose_app(moose_app), _connection(std::make_shared<wasp::lsp::IOStreamConnection>(this))
: _moose_app(moose_app),
_connection(std::make_shared<wasp::lsp::IOStreamConnection>(this)),
_formatting_tab_size(0)
{
// set server capabilities to receive full input text when changed
server_capabilities[wasp::lsp::m_text_doc_sync] = wasp::DataObject();
Expand Down Expand Up @@ -188,7 +190,8 @@ MooseServer::gatherDocumentCompletionItems(wasp::DataArray & completionItems,
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() ? "" : type_node.last_as_string();
const std::string & object_type =
type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));

// get set of all parameter and subblock names already specified in input
std::set<std::string> existing_params, existing_subblocks;
Expand Down Expand Up @@ -794,7 +797,8 @@ MooseServer::gatherDocumentDefinitionLocations(wasp::DataArray & definitionLocat
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() ? "" : type_node.last_as_string();
const std::string & object_type =
type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));

// set used to gather all parameters valid from object context of request
InputParameters valid_params = emptyInputParameters();
Expand Down Expand Up @@ -934,30 +938,18 @@ MooseServer::gatherDocumentFormattingTextEdits(wasp::DataArray & formattingTextE
// return without adding any formatting text edits if parser root is null
if (!rootIsValid())
return true;
auto & root = getRoot();

// get input root node line and column range to represent entire document
wasp::HITNodeView view_root = root.getNodeView();
wasp::HITNodeView view_root = getRoot().getNodeView();
int document_start_line = view_root.line() - 1;
int document_start_char = view_root.column() - 1;
int document_last_line = view_root.last_line() - 1;
int document_last_char = view_root.last_column();

// lambda that traverses hit tree clearing legacy markers and blank lines
std::function<void(hit::Section *)> format_document = [&](hit::Section * section)
{
section->clearLegacyMarkers();
for (auto child : section->children())
if (child->type() == hit::NodeType::Blank)
delete child;
else if (child->type() == hit::NodeType::Section)
format_document(static_cast<hit::Section *>(child));
};

// clear legacy markers and blank lines, use tab size to render, and trim
format_document(static_cast<hit::Section *>(&root));
std::string document_format = root.render(0, std::string(tab_size, ' '));
document_format = MooseUtils::trim(document_format);
// set number of spaces for indentation and build formatted document text
_formatting_tab_size = tab_size;
std::size_t starting_line = view_root.line() - 1;
std::string document_format = formatDocument(view_root, starting_line, 0);

// add formatted text with whole line and column range to formatting list
formattingTextEdits.push_back(wasp::DataObject());
Expand All @@ -972,6 +964,65 @@ MooseServer::gatherDocumentFormattingTextEdits(wasp::DataArray & formattingTextE
return pass;
}

std::string
MooseServer::formatDocument(wasp::HITNodeView parent, std::size_t & prev_line, std::size_t level)
{
// build string of newline and indentation spaces from level and tab size
std::string newline_indent = "\n" + std::string(level * _formatting_tab_size, ' ');

// lambda to format include data by replacing consecutive spaces with one
auto collapse_spaces = [](std::string string_copy)
{
pcrecpp::RE("\\s+").Replace(" ", &string_copy);
return string_copy;
};

// formatted string that will be built recursively by appending each call
std::string format_string;

// walk over all children of this node context and build document symbols
for (const auto i : make_range(parent.child_count()))
{
// walk must be index based to catch file include and skip its children
wasp::HITNodeView child = parent.child_at(i);

// add blank line if necessary after previous line and before this line
std::string blank = child.line() > prev_line + 1 ? "\n" : "";

// format include directive with indentation and collapse extra spacing
if (wasp::is_nested_file(child))
format_string += blank + newline_indent + MooseUtils::trim(collapse_spaces(child.data()));

// format normal comment with indentation and inline comment with space
else if (child.type() == wasp::COMMENT)
format_string += (child.line() == prev_line ? " " : blank + newline_indent) +
MooseUtils::trim(child.data());

// format object recursively with indentation and without legacy syntax
else if (child.type() == wasp::OBJECT)
format_string += blank + newline_indent + "[" + child.name() + "]" +
formatDocument(child, prev_line, level + 1) + newline_indent + "[]";

// format keyed value with indentation and calling reusable hit methods
else if (child.type() == wasp::KEYED_VALUE || child.type() == wasp::ARRAY)
{
const std::string prefix = newline_indent + child.name() + " = ";

const std::string render_val = hit::extractValue(child.data());
std::size_t val_column = child.child_count() > 2 ? child.child_at(2).column() : 0;
size_t prefix_len = prefix.size() - 1;

format_string += blank + prefix + hit::formatValue(render_val, val_column, prefix_len);
}

// set previous line reference used for blank lines and inline comments
prev_line = child.last_line();
}

// remove leading newline if this is level zero returning entire document
return level != 0 ? format_string : format_string.substr(1);
}

bool
MooseServer::gatherDocumentSymbols(wasp::DataArray & documentSymbols)
{
Expand All @@ -996,9 +1047,10 @@ MooseServer::gatherDocumentSymbols(wasp::DataArray & documentSymbols)
int last_line = view_child.last_line() - 1;
int last_column = view_child.last_column();
int symbol_kind = getDocumentSymbolKind(view_child);
std::string detail = !view_child.first_child_by_name("type").is_null()
? view_child.first_child_by_name("type").last_as_string()
: "";
std::string detail =
!view_child.first_child_by_name("type").is_null()
? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
: "";

// build document symbol object from node child info and push to array
documentSymbols.push_back(wasp::DataObject());
Expand Down Expand Up @@ -1048,9 +1100,10 @@ MooseServer::traverseParseTreeAndFillSymbols(wasp::HITNodeView view_parent,
int last_line = view_child.last_line() - 1;
int last_column = view_child.last_column();
int symbol_kind = getDocumentSymbolKind(view_child);
std::string detail = !view_child.first_child_by_name("type").is_null()
? view_child.first_child_by_name("type").last_as_string()
: "";
std::string detail =
!view_child.first_child_by_name("type").is_null()
? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
: "";

// build document symbol object from node child info and push to array
wasp::DataObject & data_child = wasp::lsp::addDocumentSymbolChild(data_parent);
Expand Down Expand Up @@ -1084,7 +1137,8 @@ MooseServer::getCompletionItemKind(const InputParameters & valid_params,
{
// set up completion item kind value that client may use for icon in list
auto associated_types = _check_app->syntax().getAssociatedTypes();
if (is_param && valid_params.isParamRequired(param_name))
if (is_param && valid_params.isParamRequired(param_name) &&
!valid_params.isParamValid(param_name))
return wasp::lsp::m_comp_kind_event;
else if (param_name == "active" || param_name == "inactive")
return wasp::lsp::m_comp_kind_class;
Expand Down
Loading

0 comments on commit 58d0497

Please sign in to comment.