https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MooseServer.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "MooseServer.h"
11#include "Moose.h"
12#include "AppFactory.h"
13#include "Syntax.h"
14#include "ActionFactory.h"
15#include "Factory.h"
16#include "InputParameters.h"
17#include "MooseUtils.h"
18#include "MooseEnum.h"
19#include "MultiMooseEnum.h"
20#include "ExecFlagEnum.h"
21#include "JsonSyntaxTree.h"
22#include "FileLineInfo.h"
23#include "CommandLine.h"
24#include "Parser.h"
25#include "FEProblemBase.h"
26#include "PiecewiseBase.h"
27#include "Distribution.h"
28#include "ActionWarehouse.h"
30#include "MaterialBase.h"
31#include "MaterialWarehouse.h"
33#include "OutputWarehouse.h"
34#include "Output.h"
35#include "UserObject.h"
36#include "TheWarehouse.h"
37#include "NonlinearSystemBase.h"
38#include "AuxiliarySystem.h"
39#include "pcrecpp.h"
40#include "hit/hit.h"
41#include "wasphit/HITInterpreter.h"
42#include "waspcore/utils.h"
43#include "waspplot/CustomPlotSerialization.h"
44#include <algorithm>
45#include <vector>
46#include <sstream>
47#include <iostream>
48#include <functional>
49#include <filesystem>
50
52 : _moose_app(moose_app),
53 _connection(std::make_shared<wasp::lsp::IOStreamConnection>(this)),
54 _formatting_tab_size(0),
55 _dist_plot_num_points(200),
56 _dist_plot_quantile_bound(1e-3)
57{
58 // add all implemented server capabilities to notify client in initialize
59 enableFullSync();
60 enableSymbols();
61 enableCompletion();
62 enableDefinition();
63 enableReferences();
64 enableFormatting();
65 enableHover();
66 enableExtension("plotting");
67 enableExtension("watcherRegistration");
68}
69
70bool
71MooseServer::parseDocumentForDiagnostics(wasp::DataArray & diagnosticsList)
72{
73 // Reset old parsers and applications if we have them
74 if (const auto it = _check_state.find(document_path); it != _check_state.end())
75 _check_state.erase(it);
76
77 // strip prefix from document uri if it exists to get parse file path
78 std::string parse_file_path = document_path;
79 pcrecpp::RE("(.*://)(.*)").Replace("\\2", &parse_file_path);
80
81 bool pass = true;
82
83 // Adds a single diagnostic
84 const auto diagnostic = [this, &diagnosticsList, &pass](const std::string & message,
85 const int start_line,
86 const int start_column,
87 const std::optional<int> end_line = {},
88 const std::optional<int> end_column = {})
89 {
90 diagnosticsList.push_back(wasp::DataObject());
91 auto & diagnostic = *diagnosticsList.back().to_object();
92 pass &= wasp::lsp::buildDiagnosticObject(diagnostic,
93 errors,
94 start_line,
95 start_column,
96 end_line ? *end_line : start_line,
97 end_column ? *end_column : start_column,
98 1,
99 "moose_srv",
100 "check_inp",
101 message);
102 };
103
104 // Adds a diagnostic on line zero
105 const auto zero_line_diagnostic = [&diagnostic](const std::string & message)
106 { diagnostic(message, 0, 0); };
107
108 // Adds a diagnostic from a hit node, if the context of the hit node is valid
109 const auto hit_node_diagnostic = [&zero_line_diagnostic, &diagnostic, &parse_file_path](
110 const hit::Node * const node, const std::string & message)
111 {
112 // No node, root node, wrong file, or no line information: line zero diagnostic
113 if (!node || node->isRoot() || node->filename() != parse_file_path || !node->line() ||
114 !node->column())
115 zero_line_diagnostic(message);
116 // Have file and line context, diagnostic there
117 else
118 diagnostic(message, node->line() - 1, node->column() - 1);
119 };
120
121 // Adds a diagnostic from a hit::ErrorMessage if the context is valid
122 const auto hit_error_message_diagnostic =
123 [&diagnostic, &zero_line_diagnostic, &parse_file_path](const hit::ErrorMessage & err)
124 {
125 // error has filename that matches document currently getting processed
126 if (err.filename && *err.filename == parse_file_path)
127 {
128 // Has line information that is valid
129 if (err.lineinfo && err.lineinfo->start_line && err.lineinfo->start_column &&
130 err.lineinfo->end_line && err.lineinfo->end_column)
131 {
132 diagnostic(err.message,
133 err.lineinfo->start_line - 1,
134 err.lineinfo->start_column - 1,
135 err.lineinfo->end_line - 1,
136 err.lineinfo->end_column - 1);
137 return;
138 }
139 }
140
141 // no filename, or filename not matching this document, or no line info
142 zero_line_diagnostic(err.prefixed_message);
143 };
144
145 // Runs a try catch loop with the given action, collecting diagnostics
146 // from the known exceptions; returns a bool that is true if we executed
147 // without throwing anything
148 const auto try_catch = [&hit_error_message_diagnostic,
149 &hit_node_diagnostic,
150 &zero_line_diagnostic](const auto & action) -> bool
151 {
152 Moose::ScopedThrowOnError scoped_throw_on_error;
153
154 try
155 {
156 action();
157 }
158 // Will be thrown from the Parser while building the tree or
159 // by the builder while building the input parameters
160 catch (Parser::Error & err)
161 {
162 for (const auto & error_message : err.error_messages)
163 hit_error_message_diagnostic(error_message);
164 }
165 // Will be thrown by mooseError() when _throw_on_error is set
166 // to true, hopefully with hit node context
167 catch (MooseRuntimeError & err)
168 {
169 hit_node_diagnostic(err.getNode(), err.what());
170 }
171 // General catch all for everything else without context
172 catch (std::exception & err)
173 {
174 zero_line_diagnostic(err.what());
175 }
176
177 // continue to build app if parsing fails and run app if building fails
178 // so that problem is there for plotting and warehouse based completion
179 return true;
180 };
181
182 // Setup command line (needed by the Parser)
183 auto command_line = std::make_unique<CommandLine>(_moose_app.commandLine()->getArguments());
184 if (command_line->hasArgument("--language-server"))
185 command_line->removeArgument("--language-server");
186 command_line->addArgument("--check-input");
187 command_line->addArgument("--error-unused");
188 command_line->addArgument("--error");
189 command_line->addArgument("--color=off");
190 command_line->addArgument("--disable-perf-graph-live");
191 command_line->parse();
192
193 // Setup the parser that will be used in the app
194 auto parser = std::make_shared<Parser>(parse_file_path, document_text);
195 mooseAssert(parser->getInputFileNames()[0] == parse_file_path, "Should be consistent");
196 parser->setAppType(_moose_app.type());
197 parser->setCommandLineParams(command_line->buildHitParams());
198 parser->setThrowOnError(true);
199
200 // Try to parse the document
201 const bool parse_success = try_catch([&parser]() { parser->parse(); });
202 // If the Parser has a valid root, store it because we can use it
203 // in the future (hover text etc with a partially complete document)
204 CheckState * state = nullptr;
205 if (auto parser_root_ptr = parser->queryRoot();
206 parser_root_ptr && !parser_root_ptr->getNodeView().is_null())
207 {
208 auto it_inserted_pair = _check_state.emplace(document_path, parser);
209 mooseAssert(it_inserted_pair.second, "Should not already exist");
210 state = &it_inserted_pair.first->second;
211 }
212 // We have no root or an empty document, nothing else to do
213 else
214 return true;
215
216 // Failed to parse, don't bother building the app. But... we might
217 // have a root node at least!
218 if (!parse_success)
219 return pass;
220
221 // Try to instantiate the application
222 std::unique_ptr<MooseApp> app = nullptr;
223 const auto do_build_app = [this, &parse_file_path, &diagnostic, &parser, &command_line, &app]()
224 {
225 // get app type from parser which is Application block type if provided
226 const std::string & app_type = parser->getAppType();
227
228 // error if Application type specified in input has not been registered
229 if (!AppFactory::instance().isRegistered(app_type))
230 {
231 // get line and column range of Application/type/value for diagnostic
232 int error_line_beg = 0, error_char_beg = 0, error_line_end = 0, error_char_end = 0;
233 if (auto app_type_field = parser->getRoot().find("Application/type");
234 app_type_field && app_type_field->filename() == parse_file_path)
235 {
236 auto app_type_value = app_type_field->getNodeView().first_child_by_name("value");
237 if (!app_type_value.is_null())
238 {
239 error_line_beg = app_type_value.line() - 1;
240 error_char_beg = app_type_value.column() - 1;
241 error_line_end = app_type_value.last_line() - 1;
242 error_char_end = app_type_value.last_column();
243 }
244 }
245
246 // build error message string with available app types for diagnostic
247 std::vector<std::string> app_types;
248 for (const auto & apps_iter : AppFactory::instance().registeredObjects())
249 app_types.push_back(apps_iter.first);
250 const auto message = "'" + app_type + "' is not a registered application type. Registered" +
251 " application types are [" + MooseUtils::join(app_types, ", ") + "].";
252
253 // add diagnostic to get reported for Application/type not registered
254 diagnostic(message, error_line_beg, error_char_beg, error_line_end, error_char_end);
255 return;
256 }
257
258 // set up options from app type parameters with parser and command line
259 InputParameters app_params = AppFactory::instance().getValidParams(app_type);
260 app_params.set<std::shared_ptr<Parser>>("_parser") = parser;
261 app_params.set<std::shared_ptr<CommandLine>>("_command_line") = std::move(command_line);
262
263 // create application to use for diagnostic checks and input assistance
265 app_type, AppFactory::main_app_name, app_params, _moose_app.getCommunicator()->get());
266 };
267 if (!try_catch(do_build_app))
268 {
269 if (app)
270 app.reset();
271 return pass;
272 }
273
274 // do not attempt to run application if it is null after build from error
275 if (app)
276 {
277 // store application when it is valid and then run it to invoke builder
278 state->app = std::move(app);
279 const auto do_run_app = [this]() { getCheckApp().run(); };
280 if (!try_catch(do_run_app))
281 state->app.reset();
282 }
283
284 // add all resource files of document that will be registered with client
286
287 return pass;
288}
289
290void
292{
293 // return without any resources added for document if parser root is null
294 auto root_ptr = queryRoot();
295 if (!root_ptr)
296 return;
297 auto & root = *root_ptr;
298
299 // return without document resources added if client does not watch files
300 if (!client_watcher_support)
301 return;
302
303 // get input parse tree root node to be used for gathering resource files
304 wasp::HITNodeView view_root = root.getNodeView();
305 std::set<std::string> include_paths, filename_vals, resource_uris;
306
307 // gather paths of include inputs and add to resource uris if files exist
308 view_root.node_pool()->descendant_include_paths(include_paths);
309 for (const auto & include_path : include_paths)
310 {
311 auto normalized = std::filesystem::path(include_path).lexically_normal().string();
312 if (MooseUtils::checkFileReadable(normalized, false, false, false))
313 resource_uris.insert(wasp::lsp::prefixUriScheme(normalized));
314 }
315
316 // gather paths of FileName types and add to resource uris if files exist
317 getFileNameTypeValues(filename_vals, view_root);
318 for (const auto & filename_val : filename_vals)
319 {
320 auto input_path = wasp::lsp::removeUriScheme(document_path);
321 auto input_base = std::filesystem::path(input_path).parent_path();
322 auto fname_path = std::filesystem::path(filename_val);
323 auto fname_absl = fname_path.is_absolute() ? fname_path : (input_base / fname_path);
324 auto normalized = fname_absl.lexically_normal().string();
325 if (MooseUtils::checkFileReadable(normalized, false, false, false))
326 resource_uris.insert(wasp::lsp::prefixUriScheme(normalized));
327 }
328
329 // add collection of all gathered paths as resources for current document
330 setResourcesForBase(document_path, resource_uris);
331}
332
333void
334MooseServer::getFileNameTypeValues(std::set<std::string> & filename_vals, wasp::HITNodeView parent)
335{
336 // cache set of FileName types for parameters that contain resource files
337 static const std::set<std::string> filename_types = {
338 "FileName", "FileNameNoExtension", "MeshFileName", "MatrixFileName"};
339
340 // walk over children in tree and skip any nodes that are not object type
341 for (const auto & child : parent)
342 {
343 if (child.type() == wasp::OBJECT)
344 {
345 // get object context path and object type value of node if it exists
346 wasp::HITNodeView object_node = child;
347 const std::string object_path = object_node.path();
348 wasp::HITNodeView type_node = object_node.first_child_by_name("type");
349 const std::string object_type =
350 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
351
352 // gather global, action, and object parameters for context of object
353 InputParameters valid_params = emptyInputParameters();
354 std::set<std::string> obj_act_tasks;
355 getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
356
357 // walk over children and skip any nodes that are not parameter types
358 for (const auto & child : object_node)
359 {
360 if (child.type() == wasp::KEYED_VALUE || child.type() == wasp::ARRAY)
361 {
362 // get name of node to use for finding in set of valid parameters
363 wasp::HITNodeView param_node = child;
364 std::string param_name = param_node.name();
365
366 // add parameter values to collection if valid with FileName type
367 if (valid_params.getParametersList().count(param_name))
368 {
369 // get parameter type and prepare to check if in FileName types
370 std::string dirty_type = valid_params.type(param_name);
371 std::string clean_type = MooseUtils::prettyCppType(dirty_type);
372 pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
373
374 // add parameter values to set if type is one of FileName types
375 if (filename_types.count(clean_type))
376 for (const auto & child : param_node)
377 if (child.type() == wasp::VALUE)
378 filename_vals.insert(child.to_string());
379 }
380 }
381 }
382
383 // recurse deeper into input and continue search since node is object
384 getFileNameTypeValues(filename_vals, object_node);
385 }
386 }
387}
388
389bool
390MooseServer::gatherDocumentCompletionItems(wasp::DataArray & completionItems,
391 bool & is_incomplete,
392 int line,
393 int character)
394{
395 auto root_ptr = queryRoot();
396
397 // add only root level blocks to completion list when parser root is null
398 if (!root_ptr)
399 return addSubblocksToList(completionItems, "/", line, character, line, character, "", false);
400 auto & root = *root_ptr;
401
402 // lambdas that will be used for checking completion request context type
403 auto is_request_in_open_block = [](wasp::HITNodeView request_context)
404 {
405 return request_context.type() == wasp::OBJECT || request_context.type() == wasp::DOCUMENT_ROOT;
406 };
407 auto is_request_on_param_decl = [](wasp::HITNodeView request_context)
408 {
409 return request_context.type() == wasp::DECL && request_context.has_parent() &&
410 (request_context.parent().type() == wasp::KEYED_VALUE ||
411 request_context.parent().type() == wasp::ARRAY);
412 };
413 auto is_request_on_block_decl = [](wasp::HITNodeView request_context)
414 {
415 return request_context.type() == wasp::DECL && request_context.has_parent() &&
416 request_context.parent().type() == wasp::OBJECT;
417 };
418
419 // get document tree root used to find node under request line and column
420 wasp::HITNodeView view_root = root.getNodeView();
421 wasp::HITNodeView request_context;
422
423 // find node under request location if it is not past all defined content
424 if (line + 1 < (int)view_root.last_line() ||
425 (line + 1 == (int)view_root.last_line() && character <= (int)view_root.last_column()))
426 request_context = wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
427
428 // otherwise find last node in document with last line and column of tree
429 else
430 {
431 request_context =
432 wasp::findNodeUnderLineColumn(view_root, view_root.last_line(), view_root.last_column());
433
434 // change context to be parent block or grandparent if block terminator
435 wasp::HITNodeView object_context = request_context;
436 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
437 object_context = object_context.parent();
438 if (request_context.type() == wasp::OBJECT_TERM && object_context.has_parent())
439 object_context = object_context.parent();
440 request_context = object_context;
441 }
442
443 // change context to equal sign if it is preceding node and in open block
444 if (is_request_in_open_block(request_context))
445 {
446 wasp::HITNodeView backup_context = request_context;
447 for (int backup_char = character; backup_context == request_context && --backup_char > 0;)
448 backup_context = wasp::findNodeUnderLineColumn(request_context, line + 1, backup_char + 1);
449 if (backup_context.type() == wasp::ASSIGN || backup_context.type() == wasp::OVERRIDE_ASSIGN)
450 request_context = backup_context;
451 }
452
453 // use request context type to set up replacement range and prefix filter
454 int replace_line_beg = line;
455 int replace_char_beg = character;
456 int replace_line_end = line;
457 int replace_char_end = character;
458 std::string filtering_prefix;
459 if (request_context.type() == wasp::DECL || request_context.type() == wasp::VALUE)
460 {
461 // completion on existing block name, parameter name, or value replaces
462 replace_line_beg = request_context.line() - 1;
463 replace_char_beg = request_context.column() - 1;
464 replace_line_end = request_context.last_line() - 1;
465 replace_char_end = request_context.last_column();
466 filtering_prefix = request_context.data();
467
468 // empty block name columns are same as bracket so bump replace columns
469 if (is_request_on_block_decl(request_context) && filtering_prefix.empty())
470 {
471 replace_char_beg++;
472 replace_char_end++;
473 }
474 }
475
476 // get name of request context direct parent node so it can be used later
477 const auto & parent_name = request_context.has_parent() ? request_context.parent().name() : "";
478
479 // get object context and value of type parameter for request if provided
480 wasp::HITNodeView object_context = request_context;
481 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
482 object_context = object_context.parent();
483 if (is_request_on_block_decl(request_context))
484 object_context = object_context.parent();
485 const std::string & object_path = object_context.path();
486 wasp::HITNodeView type_node = object_context.first_child_by_name("type");
487 const std::string & object_type =
488 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
489
490 // get set of all parameter and subblock names already specified in input
491 std::set<std::string> existing_params, existing_subblocks;
492 getExistingInput(object_context, existing_params, existing_subblocks);
493
494 // set used to gather all parameters valid from object context of request
495 InputParameters valid_params = emptyInputParameters();
496
497 // set used to gather MooseObjectAction tasks to verify object parameters
498 std::set<std::string> obj_act_tasks;
499
500 // get set of global parameters, action parameters, and object parameters
501 getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
502
503 bool pass = true;
504
505 // add gathered parameters to completion list with input range and prefix
506 if (is_request_in_open_block(request_context) || is_request_on_param_decl(request_context))
507 pass &= addParametersToList(completionItems,
508 valid_params,
509 existing_params,
510 replace_line_beg,
511 replace_char_beg,
512 replace_line_end,
513 replace_char_end,
514 filtering_prefix);
515
516 // add all valid subblocks to completion list with input range and prefix
517 if (is_request_in_open_block(request_context) || is_request_on_param_decl(request_context) ||
518 is_request_on_block_decl(request_context))
519 pass &= addSubblocksToList(completionItems,
520 object_path,
521 replace_line_beg,
522 replace_char_beg,
523 replace_line_end,
524 replace_char_end,
525 filtering_prefix,
526 is_request_on_block_decl(request_context));
527
528 // add valid parameter value options to completion list using input range
529 if ((request_context.type() == wasp::VALUE || request_context.type() == wasp::ASSIGN ||
530 request_context.type() == wasp::OVERRIDE_ASSIGN) &&
531 valid_params.getParametersList().count(parent_name))
532 pass &= addValuesToList(completionItems,
533 valid_params,
534 existing_params,
535 existing_subblocks,
536 parent_name,
537 obj_act_tasks,
538 object_path,
539 replace_line_beg,
540 replace_char_beg,
541 replace_line_end,
542 replace_char_end);
543
544 is_incomplete = !pass;
545
546 return pass;
547}
548
549void
550MooseServer::getExistingInput(wasp::HITNodeView parent_node,
551 std::set<std::string> & existing_params,
552 std::set<std::string> & existing_subblocks)
553{
554 // gather names of all parameters and subblocks provided in input context
555 for (auto itr = parent_node.begin(); itr != parent_node.end(); itr.next())
556 {
557 auto child_node = itr.get();
558
559 // add key value or array type as parameter and object type as subblock
560 if (child_node.type() == wasp::KEYED_VALUE || child_node.type() == wasp::ARRAY)
561 existing_params.insert(child_node.name());
562 else if (child_node.type() == wasp::OBJECT)
563 existing_subblocks.insert(child_node.name());
564 }
565}
566
567void
569 const std::string & object_path,
570 const std::string & object_type,
571 std::set<std::string> & obj_act_tasks)
572{
573 // gather global parameters then action parameters then object parameters
574 valid_params += Moose::Builder::validParams();
575 getActionParameters(valid_params, object_path, obj_act_tasks);
576 getObjectParameters(valid_params, object_type, obj_act_tasks);
577}
578
579void
581 const std::string & object_path,
582 std::set<std::string> & obj_act_tasks)
583{
584 Syntax & syntax = getRegistrationApp().syntax();
586
587 // get registered syntax path identifier using actual object context path
588 bool is_parent;
589 std::string registered_syntax = syntax.isAssociated(object_path, &is_parent);
590
591 // use is_parent to skip action parameters when not explicitly registered
592 if (!is_parent)
593 {
594 // get action objects associated with registered syntax path identifier
595 auto action_range = syntax.getActions(registered_syntax);
596
597 // traverse action objects for syntax to gather valid action parameters
598 for (auto action_iter = action_range.first; action_iter != action_range.second; action_iter++)
599 {
600 const std::string & action_name = action_iter->second._action;
601
602 // use action name to get set of valid parameters from action factory
603 InputParameters action_params = action_factory.getValidParams(action_name);
604
605 // gather all MooseObjectAction tasks for verifying object parameters
606 if (action_params.have_parameter<bool>("isObjectAction"))
607 {
608 if (action_params.get<bool>("isObjectAction"))
609 {
610 std::set<std::string> tasks_by_actions = action_factory.getTasksByAction(action_name);
611 obj_act_tasks.insert(tasks_by_actions.begin(), tasks_by_actions.end());
612 }
613
614 // filter parameter from completion list as it is not used in input
615 action_params.remove("isObjectAction");
616 }
617
618 // add parameters from action to full valid collection being gathered
619 valid_params += action_params;
620 }
621 }
622}
623
624void
626 std::string object_type,
627 const std::set<std::string> & obj_act_tasks)
628{
629 Syntax & syntax = getRegistrationApp().syntax();
630 Factory & factory = getRegistrationApp().getFactory();
631
632 // use type parameter default if it exists and is not provided from input
633 if (object_type.empty() && valid_params.have_parameter<std::string>("type") &&
634 !valid_params.get<std::string>("type").empty())
635 {
636 object_type = valid_params.get<std::string>("type");
637
638 // make type parameter not required in input since it has default value
639 valid_params.makeParamNotRequired("type");
640 }
641
642 // check if object type has been registered to prevent unregistered error
643 if (factory.isRegistered(object_type))
644 {
645 // use object type to get set of valid parameters registered in factory
646 InputParameters object_params = factory.getValidParams(object_type);
647
648 // check if object has base associated with any MooseObjectAction tasks
649 if (object_params.hasBase())
650 {
651 const std::string & moose_base = object_params.getBase();
652
653 for (const auto & obj_act_task : obj_act_tasks)
654 {
655 if (syntax.verifyMooseObjectTask(moose_base, obj_act_task))
656 {
657 // add parameters from object to valid collection if base matches
658 valid_params += object_params;
659 break;
660 }
661 }
662 }
663 }
664
665 // make parameters from list of those set by action not required in input
666 if (valid_params.have_parameter<std::vector<std::string>>("_object_params_set_by_action"))
667 {
668 auto names = valid_params.get<std::vector<std::string>>("_object_params_set_by_action");
669 for (const auto & name : names)
670 valid_params.makeParamNotRequired(name);
671
672 // filter parameter from completion list since it is not used for input
673 valid_params.remove("_object_params_set_by_action");
674 }
675}
676
677bool
678MooseServer::addParametersToList(wasp::DataArray & completionItems,
679 const InputParameters & valid_params,
680 const std::set<std::string> & existing_params,
681 int replace_line_beg,
682 int replace_char_beg,
683 int replace_line_end,
684 int replace_char_end,
685 const std::string & filtering_prefix)
686{
687 bool pass = true;
688
689 // walk over collection of all valid parameters and build completion list
690 for (const auto & valid_params_iter : valid_params)
691 {
692 const std::string & param_name = valid_params_iter.first;
693 bool deprecated = valid_params.isParamDeprecated(param_name);
694 bool is_private = valid_params.isPrivate(param_name);
695
696 // filter out parameters that are deprecated, private, or already exist
697 if (deprecated || is_private || existing_params.count(param_name))
698 continue;
699
700 // filter out parameters that do not begin with prefix if one was given
701 if (param_name.rfind(filtering_prefix, 0) != 0)
702 continue;
703
704 // process parameter description and type to use in input default value
705 std::string dirty_type = valid_params.type(param_name);
706 std::string clean_type = MooseUtils::prettyCppType(dirty_type);
707 std::string basic_type = JsonSyntaxTree::basicCppType(clean_type);
708 std::string doc_string = valid_params.getDocString(param_name);
709 MooseUtils::escape(doc_string);
710
711 // use basic type to decide if parameter is array and quotes are needed
712 bool is_array = basic_type.compare(0, 6, "Array:") == 0;
713
714 // remove any array prefixes from basic type string and leave base type
715 pcrecpp::RE("(Array:)*(.*)").GlobalReplace("\\2", &basic_type);
716
717 // prepare clean cpp type string to be used for key to find input paths
718 pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
719
720 // decide completion item kind that client may use to display list icon
721 int complete_kind = getCompletionItemKind(valid_params, param_name, clean_type, true);
722
723 // default value for completion to be built using parameter information
724 std::string default_value;
725
726 // first if parameter default is set then use it to build default value
727 if (valid_params.isParamValid(param_name))
728 {
729 default_value = JsonSyntaxTree::buildOutputString(valid_params_iter);
730 default_value = MooseUtils::trim(default_value);
731 }
732
733 // otherwise if parameter has coupled default then use as default value
734 else if (valid_params.hasDefaultCoupledValue(param_name))
735 {
736 std::ostringstream oss;
737 oss << valid_params.defaultCoupledValue(param_name);
738 default_value = oss.str();
739 }
740
741 // switch 1 to true or 0 to false if boolean parameter as default value
742 if (basic_type == "Boolean" && default_value == "1")
743 default_value = "true";
744 else if (basic_type == "Boolean" && default_value == "0")
745 default_value = "false";
746
747 // wrap default value with single quotes if it exists and type is array
748 std::string array_quote = is_array && !default_value.empty() ? "'" : "";
749
750 // choose format of insertion text based on if client supports snippets
751 int text_format;
752 std::string insert_text;
753 if (client_snippet_support && !default_value.empty())
754 {
755 text_format = wasp::lsp::m_text_format_snippet;
756 insert_text = param_name + " = " + array_quote + "${1:" + default_value + "}" + array_quote;
757 }
758 else
759 {
760 text_format = wasp::lsp::m_text_format_plaintext;
761 insert_text = param_name + " = " + array_quote + default_value + array_quote;
762 }
763 // finally build full insertion from parameter name, quote, and default
764
765 // add parameter label, insert text, and description to completion list
766 completionItems.push_back(wasp::DataObject());
767 wasp::DataObject * item = completionItems.back().to_object();
768 pass &= wasp::lsp::buildCompletionObject(*item,
769 errors,
770 param_name,
771 replace_line_beg,
772 replace_char_beg,
773 replace_line_end,
774 replace_char_end,
775 insert_text,
776 complete_kind,
777 "",
778 doc_string,
779 false,
780 false,
781 text_format);
782 }
783
784 return pass;
785}
786
787bool
788MooseServer::addSubblocksToList(wasp::DataArray & completionItems,
789 const std::string & object_path,
790 int replace_line_beg,
791 int replace_char_beg,
792 int replace_line_end,
793 int replace_char_end,
794 const std::string & filtering_prefix,
795 bool request_on_block_decl)
796{
797 Syntax & syntax = getRegistrationApp().syntax();
798
799 // set used to prevent reprocessing syntax paths for more than one action
800 std::set<std::string> syntax_paths_processed;
801
802 // build map of all syntax paths to names for subblocks and save to reuse
803 auto & metadata = getSyntaxMetadata();
804 if (metadata.syntax_to_subblocks.empty())
805 {
806 for (const auto & syntax_path_iter : syntax.getAssociatedActions())
807 {
808 std::string syntax_path = "/" + syntax_path_iter.first;
809
810 // skip current syntax path if already processed for different action
811 if (!syntax_paths_processed.insert(syntax_path).second)
812 continue;
813
814 // walk backward through syntax path adding subblock names to parents
815 for (std::size_t last_sep; (last_sep = syntax_path.find_last_of("/")) != std::string::npos;)
816 {
817 std::string subblock_name = syntax_path.substr(last_sep + 1);
818 syntax_path = syntax_path.substr(0, last_sep);
819 metadata.syntax_to_subblocks[syntax_path].insert(subblock_name);
820 }
821 }
822 }
823
824 // get registered syntax from object path using map of paths to subblocks
825 auto registered_syntax = syntax.isAssociated(object_path, nullptr, metadata.syntax_to_subblocks);
826
827 bool pass = true;
828
829 // walk over subblock names if found or at root and build completion list
830 if (!registered_syntax.empty() || object_path == "/")
831 {
832 // choose format of insertion text based on if client supports snippets
833 int text_format = client_snippet_support ? wasp::lsp::m_text_format_snippet
834 : wasp::lsp::m_text_format_plaintext;
835
836 for (const auto & subblock_name : metadata.syntax_to_subblocks[registered_syntax])
837 {
838 // filter subblock if it does not begin with prefix and one was given
839 if (subblock_name != "*" && subblock_name.rfind(filtering_prefix, 0) != 0)
840 continue;
841
842 std::string doc_string;
843 std::string insert_text;
844 int complete_kind;
845
846 // build required parameter list for each block to use in insert text
847 const std::string full_block_path = object_path + "/" + subblock_name;
848 const std::string req_params = getRequiredParamsText(full_block_path, "", {}, " ");
849
850 // customize description and insert text for star and named subblocks
851 if (subblock_name == "*")
852 {
853 doc_string = "custom user named block";
854 insert_text = (request_on_block_decl ? "" : "[") +
855 std::string(client_snippet_support ? "${1:" : "") +
856 (filtering_prefix.size() ? filtering_prefix : "block_name") +
857 (client_snippet_support ? "}" : "") + "]" + req_params + "\n " +
858 (client_snippet_support ? "$0" : "") + "\n[]";
859 complete_kind = wasp::lsp::m_comp_kind_variable;
860 }
861 else
862 {
863 doc_string = "application named block";
864 insert_text = (request_on_block_decl ? "" : "[") + subblock_name + "]" + req_params +
865 "\n " + (client_snippet_support ? "$0" : "") + "\n[]";
866 complete_kind = wasp::lsp::m_comp_kind_struct;
867 }
868
869 // add subblock name, insert text, and description to completion list
870 completionItems.push_back(wasp::DataObject());
871 wasp::DataObject * item = completionItems.back().to_object();
872 pass &= wasp::lsp::buildCompletionObject(*item,
873 errors,
874 subblock_name,
875 replace_line_beg,
876 replace_char_beg,
877 replace_line_end,
878 replace_char_end,
879 insert_text,
880 complete_kind,
881 "",
882 doc_string,
883 false,
884 false,
885 text_format);
886 }
887 }
888
889 return pass;
890}
891
892bool
893MooseServer::addValuesToList(wasp::DataArray & completionItems,
894 const InputParameters & valid_params,
895 const std::set<std::string> & existing_params,
896 const std::set<std::string> & existing_subblocks,
897 const std::string & param_name,
898 const std::set<std::string> & obj_act_tasks,
899 const std::string & object_path,
900 int replace_line_beg,
901 int replace_char_beg,
902 int replace_line_end,
903 int replace_char_end)
904{
905 Syntax & syntax = getRegistrationApp().syntax();
906 Factory & factory = getRegistrationApp().getFactory();
907
908 // get clean type for path associations and basic type for boolean values
909 std::string dirty_type = valid_params.type(param_name);
910 std::string clean_type = MooseUtils::prettyCppType(dirty_type);
911 std::string basic_type = JsonSyntaxTree::basicCppType(clean_type);
912
913 // remove any array prefixes from basic type string and replace with base
914 pcrecpp::RE("(Array:)*(.*)").GlobalReplace("\\2", &basic_type);
915
916 // prepare clean cpp type string to be used for a key to find input paths
917 pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
918
919 // decide completion item kind that client may use to display a list icon
920 int complete_kind = getCompletionItemKind(valid_params, param_name, clean_type, false);
921
922 // map used to gather options and descriptions for value completion items
923 std::map<std::string, std::string> options_and_descs;
924
925 // first if parameter name is active or inactive then use input subblocks
926 if (param_name == "active" || param_name == "inactive")
927 for (const auto & subblock_name : existing_subblocks)
928 options_and_descs[subblock_name] = "subblock name";
929
930 // otherwise if parameter type is boolean then use true and false strings
931 else if (basic_type == "Boolean")
932 {
933 options_and_descs["true"];
934 options_and_descs["false"];
935 }
936
937 // otherwise if parameter type is one of the enums then use valid options
938 else if (valid_params.have_parameter<MooseEnum>(param_name))
939 getEnumsAndDocs(valid_params.get<MooseEnum>(param_name), options_and_descs);
940 else if (valid_params.have_parameter<MultiMooseEnum>(param_name))
941 getEnumsAndDocs(valid_params.get<MultiMooseEnum>(param_name), options_and_descs);
942 else if (valid_params.have_parameter<ExecFlagEnum>(param_name))
943 getEnumsAndDocs(valid_params.get<ExecFlagEnum>(param_name), options_and_descs);
944 else if (valid_params.have_parameter<std::vector<MooseEnum>>(param_name))
945 getEnumsAndDocs(valid_params.get<std::vector<MooseEnum>>(param_name)[0], options_and_descs);
946
947 // otherwise if parameter is Application type then use all available apps
948 else if (object_path == "/Application" && param_name == "type")
949 {
950 for (const auto & apps_iter : AppFactory::instance().registeredObjects())
951 {
952 const std::string & app_name = apps_iter.first;
953 const InputParameters & app_params = apps_iter.second->buildParameters();
954 std::string app_description = app_params.getClassDescription();
955 MooseUtils::escape(app_description);
956 options_and_descs[app_name] = app_description;
957 }
958 }
959
960 // otherwise if parameter name is type then use all verified object names
961 else if (param_name == "type")
962 {
963 // walk over entire set of objects that have been registered in factory
964 for (const auto & objects_iter : factory.registeredObjects())
965 {
966 const std::string & object_name = objects_iter.first;
967 const InputParameters & object_params = objects_iter.second->buildParameters();
968
969 // build required parameter list for each block to use in insert text
970 std::string req_params = getRequiredParamsText(object_path, object_name, existing_params, "");
971 req_params += req_params.size() ? "\n" + std::string(client_snippet_support ? "$0" : "") : "";
972
973 // check if object has registered base parameter that can be verified
974 if (!object_params.hasBase())
975 continue;
976 const std::string & moose_base = object_params.getBase();
977
978 // walk over gathered MooseObjectAction tasks and add if base matches
979 for (const auto & obj_act_task : obj_act_tasks)
980 {
981 if (!syntax.verifyMooseObjectTask(moose_base, obj_act_task))
982 continue;
983 std::string type_description = object_params.getClassDescription();
984 MooseUtils::escape(type_description);
985 options_and_descs[object_name + req_params] = type_description;
986 break;
987 }
988 }
989 }
990
991 // otherwise if parameter type has any associated syntax then use lookups
992 else
993 {
994 // build map of parameter types to input lookup paths and save to reuse
995 auto & metadata = getSyntaxMetadata();
996 if (metadata.type_to_input_paths.empty())
997 {
998 for (const auto & associated_types_iter : syntax.getAssociatedTypes())
999 {
1000 const std::string & type = associated_types_iter.second;
1001 const std::string & path = associated_types_iter.first;
1002 metadata.type_to_input_paths[type].insert(path);
1003 }
1004 }
1005
1006 // check for input lookup paths that are associated with parameter type
1007 const auto & input_path_iter = metadata.type_to_input_paths.find(clean_type);
1008
1009 if (input_path_iter != metadata.type_to_input_paths.end())
1010 {
1011 wasp::HITNodeView view_root = getRoot().getNodeView();
1012
1013 // walk over all syntax paths that are associated with parameter type
1014 for (const auto & input_path : input_path_iter->second)
1015 {
1016 // use wasp siren to gather all input values at current lookup path
1017 wasp::SIRENInterpreter<> selector;
1018 if (!selector.parseString(input_path))
1019 continue;
1020 wasp::SIRENResultSet<wasp::HITNodeView> results;
1021 std::size_t count = selector.evaluate(view_root, results);
1022
1023 // walk over results and add each input value found at current path
1024 for (std::size_t i = 0; i < count; i++)
1025 if (results.adapted(i).type() == wasp::OBJECT)
1026 options_and_descs[results.adapted(i).name()] = "from /" + input_path;
1027 }
1028 }
1029
1030 // warehouse based completion is unavailable if problem failed to build
1031 // input lookup based completion works even when problem fails to build
1032 // so warehouse completion supplements lookups rather than replacing it
1033 addObjectsFromWarehouses(clean_type, options_and_descs);
1034 }
1035
1036 // choose format of insertion text based on if client has snippet support
1037 int text_format = client_snippet_support ? wasp::lsp::m_text_format_snippet
1038 : wasp::lsp::m_text_format_plaintext;
1039
1040 bool pass = true;
1041
1042 // walk over pairs of options with descriptions and build completion list
1043 for (const auto & option_and_desc : options_and_descs)
1044 {
1045 const std::string & insert_text = option_and_desc.first;
1046 const std::string & option_name = insert_text.substr(0, insert_text.find('\n'));
1047 const std::string & description = option_and_desc.second;
1048
1049 // add option name, insertion range, and description to completion list
1050 completionItems.push_back(wasp::DataObject());
1051 wasp::DataObject * item = completionItems.back().to_object();
1052 pass &= wasp::lsp::buildCompletionObject(*item,
1053 errors,
1054 option_name,
1055 replace_line_beg,
1056 replace_char_beg,
1057 replace_line_end,
1058 replace_char_end,
1059 insert_text,
1060 complete_kind,
1061 "",
1062 description,
1063 false,
1064 false,
1065 text_format);
1066 }
1067
1068 return pass;
1069}
1070
1071template <typename MooseEnumType>
1072void
1073MooseServer::getEnumsAndDocs(MooseEnumType & moose_enum_param,
1074 std::map<std::string, std::string> & options_and_descs)
1075{
1076 // get map that contains any documentation strings provided for each item
1077 const auto & enum_docs = moose_enum_param.getItemDocumentation();
1078
1079 // walk over enums filling map with options and any provided descriptions
1080 for (const auto & item : moose_enum_param.items())
1081 options_and_descs[item.name()] = enum_docs.count(item) ? enum_docs.at(item) : "";
1082}
1083
1084void
1085MooseServer::addObjectsFromWarehouses(const std::string & param_type,
1086 std::map<std::string, std::string> & options_and_descs)
1087{
1088 // get check app of document and return with no items if its build failed
1089 auto app_ptr = queryCheckApp();
1090 if (!app_ptr)
1091 return;
1092
1093 // get problem from action warehouse and return without any items if null
1094 std::shared_ptr<FEProblemBase> & problem = app_ptr->actionWarehouse().problemBase();
1095 if (!problem)
1096 return;
1097
1098 if (param_type == "NonlinearVariableName")
1099 {
1100 for (const auto i : make_range(problem->numNonlinearSystems()))
1101 for (const auto & nls_var_name : problem->getNonlinearSystemBase(i).getVariableNames())
1102 options_and_descs[nls_var_name] = "from NonlinearSystem VariableWarehouse";
1103 }
1104 else if (param_type == "AuxVariableName")
1105 {
1106 for (const auto & aux_var_name : problem->getAuxiliarySystem().getVariableNames())
1107 options_and_descs[aux_var_name] = "from AuxiliarySystem VariableWarehouse";
1108 }
1109 else if (param_type == "VariableName")
1110 {
1111 for (const auto i : make_range(problem->numNonlinearSystems()))
1112 for (const auto & nls_var_name : problem->getNonlinearSystemBase(i).getVariableNames())
1113 options_and_descs[nls_var_name] = "from NonlinearSystem VariableWarehouse";
1114 for (const auto & aux_var_name : problem->getAuxiliarySystem().getVariableNames())
1115 options_and_descs[aux_var_name] = "from AuxiliarySystem VariableWarehouse";
1116 }
1117 else if (param_type == "MaterialPropertyName")
1118 {
1119 const auto & mat_prop_registry = problem->getMaterialPropertyRegistry();
1120 const std::vector<std::string> mat_prop_names(mat_prop_registry.idsToNamesBegin(),
1121 mat_prop_registry.idsToNamesEnd());
1122 for (const auto & mat_prop_name : mat_prop_names)
1123 options_and_descs[mat_prop_name] = "from MaterialPropertyRegistry";
1124 }
1125 else if (param_type == "MaterialName")
1126 {
1127 for (const auto & material : problem->getMaterialWarehouse().getObjects())
1128 options_and_descs[material->name()] = "from MaterialWarehouse";
1129 }
1130 else if (param_type == "FunctionName")
1131 {
1132 for (const auto & function : problem->getFunctionWarehouse().getObjects())
1133 options_and_descs[function->name()] = "from FunctionWarehouse";
1134 }
1135 else if (param_type == "OutputName")
1136 {
1137 for (const auto & output_name : app_ptr->getOutputWarehouse().getOutputNames<Output>())
1138 options_and_descs[output_name] = "from OutputWarehouse";
1139 for (const auto & reserved_name : app_ptr->getOutputWarehouse().getReservedNames())
1140 options_and_descs[reserved_name] = "from reserved names in OutputWarehouse";
1141 }
1142 else if (param_type == "UserObjectName")
1143 {
1144 std::vector<UserObject *> user_objects;
1145 problem->theWarehouse()
1146 .query()
1147 .condition<AttribSystem>("UserObject")
1148 .condition<AttribThread>(0)
1149 .queryIntoUnsorted(user_objects);
1150 for (const auto & user_object : user_objects)
1151 options_and_descs[user_object->name()] = "from UserObjectWarehouse";
1152 }
1153}
1154
1155bool
1156MooseServer::gatherDocumentDefinitionLocations(wasp::DataArray & definitionLocations,
1157 int line,
1158 int character)
1159{
1160 Factory & factory = getRegistrationApp().getFactory();
1161
1162 // return without any definition locations added when parser root is null
1163 auto root_ptr = queryRoot();
1164 if (!root_ptr)
1165 return true;
1166 auto & root = *root_ptr;
1167
1168 // find hit node for zero based request line and column number from input
1169 wasp::HITNodeView view_root = root.getNodeView();
1170 wasp::HITNodeView request_context =
1171 wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
1172
1173 // return without any definition locations added when node not value type
1174 if (request_context.type() != wasp::VALUE)
1175 return true;
1176
1177 // get name of parameter node parent of value and value string from input
1178 std::string param_name = request_context.has_parent() ? request_context.parent().name() : "";
1179 std::string val_string = request_context.last_as_string();
1180
1181 // add source code location if type parameter with registered object name
1182 if (param_name == "type" && factory.isRegistered(val_string))
1183 {
1184 // get file path and line number of source code registering object type
1185 FileLineInfo file_line_info = factory.getLineInfo(val_string);
1186
1187 // return without any definition locations added if file cannot be read
1188 if (!file_line_info.isValid() ||
1189 !MooseUtils::checkFileReadable(file_line_info.file(), false, false, false))
1190 return true;
1191
1192 // add file scheme prefix to front of file path to build definition uri
1193 auto location_uri = wasp::lsp::m_uri_prefix + file_line_info.file();
1194
1195 // add file uri and zero based line and column range to definition list
1196 definitionLocations.push_back(wasp::DataObject());
1197 wasp::DataObject * location = definitionLocations.back().to_object();
1198 return wasp::lsp::buildLocationObject(*location,
1199 errors,
1200 location_uri,
1201 file_line_info.line() - 1,
1202 0,
1203 file_line_info.line() - 1,
1204 1000);
1205 }
1206
1207 // get object context and value of type parameter for request if provided
1208 wasp::HITNodeView object_context = request_context;
1209 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
1210 object_context = object_context.parent();
1211 const std::string & object_path = object_context.path();
1212 wasp::HITNodeView type_node = object_context.first_child_by_name("type");
1213 const std::string & object_type =
1214 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
1215
1216 // set used to gather all parameters valid from object context of request
1217 InputParameters valid_params = emptyInputParameters();
1218
1219 // set used to gather MooseObjectAction tasks to verify object parameters
1220 std::set<std::string> obj_act_tasks;
1221
1222 // get set of global parameters, action parameters, and object parameters
1223 getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
1224
1225 // set used to gather nodes from input lookups custom sorted by locations
1226 SortedLocationNodes location_nodes(
1227 [](const wasp::HITNodeView & l, const wasp::HITNodeView & r)
1228 {
1229 const std::string & l_file = l.node_pool()->stream_name();
1230 const std::string & r_file = r.node_pool()->stream_name();
1231 return (l_file < r_file || (l_file == r_file && l.line() < r.line()) ||
1232 (l_file == r_file && l.line() == r.line() && l.column() < r.column()));
1233 });
1234
1235 // gather all lookup path nodes matching value if parameter name is valid
1236 for (const auto & valid_params_iter : valid_params)
1237 {
1238 if (valid_params_iter.first == param_name)
1239 {
1240 // get cpp type and prepare string for use as key finding input paths
1241 std::string dirty_type = valid_params.type(param_name);
1242 std::string clean_type = MooseUtils::prettyCppType(dirty_type);
1243 pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
1244
1245 // get set of nodes from associated path lookups matching input value
1246 getInputLookupDefinitionNodes(location_nodes, clean_type, val_string);
1247 break;
1248 }
1249 }
1250
1251 // add parameter declarator to set if none were gathered by input lookups
1252 if (location_nodes.empty() && request_context.has_parent() &&
1253 request_context.parent().child_count_by_name("decl"))
1254 location_nodes.insert(request_context.parent().first_child_by_name("decl"));
1255
1256 // add locations to definition list using lookups or parameter declarator
1257 return addLocationNodesToList(definitionLocations, location_nodes);
1258}
1259
1260void
1262 const std::string & clean_type,
1263 const std::string & val_string)
1264{
1265 Syntax & syntax = getRegistrationApp().syntax();
1266
1267 // build map from parameter types to input lookup paths and save to reuse
1268 auto & metadata = getSyntaxMetadata();
1269 if (metadata.type_to_input_paths.empty())
1270 {
1271 for (const auto & associated_types_iter : syntax.getAssociatedTypes())
1272 {
1273 const std::string & type = associated_types_iter.second;
1274 const std::string & path = associated_types_iter.first;
1275 metadata.type_to_input_paths[type].insert(path);
1276 }
1277 }
1278
1279 // find set of input lookup paths that are associated with parameter type
1280 const auto & input_path_iter = metadata.type_to_input_paths.find(clean_type);
1281
1282 // return without any definition locations added when no paths associated
1283 if (input_path_iter == metadata.type_to_input_paths.end())
1284 return;
1285
1286 // get root node from input to use in input lookups with associated paths
1287 wasp::HITNodeView view_root = getRoot().getNodeView();
1288
1289 // walk over all syntax paths that are associated with parameter type
1290 for (const auto & input_path : input_path_iter->second)
1291 {
1292 // use wasp siren to gather all nodes from current lookup path in input
1293 wasp::SIRENInterpreter<> selector;
1294 if (!selector.parseString(input_path))
1295 continue;
1296 wasp::SIRENResultSet<wasp::HITNodeView> results;
1297 std::size_t count = selector.evaluate(view_root, results);
1298
1299 // walk over results and add nodes that have name matching value to set
1300 for (std::size_t i = 0; i < count; i++)
1301 if (results.adapted(i).type() == wasp::OBJECT && results.adapted(i).name() == val_string &&
1302 results.adapted(i).child_count_by_name("decl"))
1303 location_nodes.insert(results.adapted(i).first_child_by_name("decl"));
1304 }
1305}
1306
1307bool
1308MooseServer::addLocationNodesToList(wasp::DataArray & defsOrRefsLocations,
1309 const SortedLocationNodes & location_nodes)
1310{
1311 bool pass = true;
1312
1313 // walk over set of sorted nodes provided to add and build locations list
1314 for (const auto & location_nodes_iter : location_nodes)
1315 {
1316 // add file scheme prefix onto front of file path to build location uri
1317 auto location_uri = wasp::lsp::m_uri_prefix + location_nodes_iter.node_pool()->stream_name();
1318
1319 // add file uri with zero based line and column range to locations list
1320 defsOrRefsLocations.push_back(wasp::DataObject());
1321 wasp::DataObject * location = defsOrRefsLocations.back().to_object();
1322 pass &= wasp::lsp::buildLocationObject(*location,
1323 errors,
1324 location_uri,
1325 location_nodes_iter.line() - 1,
1326 location_nodes_iter.column() - 1,
1327 location_nodes_iter.last_line() - 1,
1328 location_nodes_iter.last_column());
1329 }
1330
1331 return pass;
1332}
1333
1334bool
1335MooseServer::getHoverDisplayText(std::string & display_text, int line, int character)
1336{
1337 Factory & factory = getRegistrationApp().getFactory();
1338 Syntax & syntax = getRegistrationApp().syntax();
1339
1340 // return and leave display text as empty string when parser root is null
1341 auto root_ptr = queryRoot();
1342 if (!root_ptr)
1343 return true;
1344 auto & root = *root_ptr;
1345
1346 // find hit node for zero based request line and column number from input
1347 wasp::HITNodeView view_root = root.getNodeView();
1348 wasp::HITNodeView request_context =
1349 wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
1350
1351 // return and leave display text as empty string when not on key or value
1352 if ((request_context.type() != wasp::DECL && request_context.type() != wasp::VALUE) ||
1353 !request_context.has_parent() ||
1354 (request_context.parent().type() != wasp::KEYED_VALUE &&
1355 request_context.parent().type() != wasp::ARRAY))
1356 return true;
1357
1358 // get name of parameter node and value string that is specified in input
1359 std::string paramkey = request_context.parent().name();
1360 std::string paramval = request_context.last_as_string();
1361
1362 // get object context path and object type value for request if it exists
1363 wasp::HITNodeView object_context = request_context;
1364 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
1365 object_context = object_context.parent();
1366 const std::string object_path = object_context.path();
1367 wasp::HITNodeView type_node = object_context.first_child_by_name("type");
1368 const std::string object_type =
1369 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
1370
1371 // gather global, action, and object parameters in request object context
1372 InputParameters valid_params = emptyInputParameters();
1373 std::set<std::string> obj_act_tasks;
1374 getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
1375
1376 // use class description as display text when request is Application type
1377 if (request_context.type() == wasp::VALUE && paramkey == "type" &&
1378 object_path == "/Application" && AppFactory::instance().isRegistered(paramval))
1379 {
1380 InputParameters app_params = AppFactory::instance().getValidParams(paramval);
1381 display_text = app_params.getClassDescription();
1382 MooseUtils::escape(display_text);
1383 }
1384
1385 // use class description as display text when request is valid type value
1386 else if (request_context.type() == wasp::VALUE && paramkey == "type" &&
1387 factory.isRegistered(paramval))
1388 {
1389 const InputParameters & object_params = factory.getValidParams(paramval);
1390 if (object_params.hasBase())
1391 {
1392 const std::string & moose_base = object_params.getBase();
1393 for (const auto & obj_act_task : obj_act_tasks)
1394 {
1395 if (syntax.verifyMooseObjectTask(moose_base, obj_act_task))
1396 {
1397 display_text = object_params.getClassDescription();
1398 MooseUtils::escape(display_text);
1399 break;
1400 }
1401 }
1402 }
1403 }
1404
1405 // use item documentation as display text when request is enum type value
1406 else if (request_context.type() == wasp::VALUE)
1407 {
1408 std::map<std::string, std::string> options_and_descs;
1409 if (valid_params.have_parameter<MooseEnum>(paramkey))
1410 getEnumsAndDocs(valid_params.get<MooseEnum>(paramkey), options_and_descs);
1411 else if (valid_params.have_parameter<MultiMooseEnum>(paramkey))
1412 getEnumsAndDocs(valid_params.get<MultiMooseEnum>(paramkey), options_and_descs);
1413 else if (valid_params.have_parameter<ExecFlagEnum>(paramkey))
1414 getEnumsAndDocs(valid_params.get<ExecFlagEnum>(paramkey), options_and_descs);
1415 else if (valid_params.have_parameter<std::vector<MooseEnum>>(paramkey))
1416 getEnumsAndDocs(valid_params.get<std::vector<MooseEnum>>(paramkey)[0], options_and_descs);
1417 if (options_and_descs.count(paramval))
1418 {
1419 display_text = options_and_descs.find(paramval)->second;
1420 MooseUtils::escape(display_text);
1421 }
1422 }
1423
1424 // use parameter documentation as display text when request is valid name
1425 else if (request_context.type() == wasp::DECL && valid_params.getParametersList().count(paramkey))
1426 {
1427 display_text = valid_params.getDocString(paramkey);
1428 MooseUtils::escape(display_text);
1429
1430 // add units information to hover text if it is specified for parameter
1431 std::string doc_units = valid_params.getDocUnit(paramkey);
1432 if (!doc_units.empty())
1433 display_text += "\n\nUnits: " + doc_units;
1434
1435 // add range information to hover text if it is specified for parameter
1436 if (valid_params.isRangeChecked(paramkey))
1437 {
1438 std::string doc_range = valid_params.rangeCheckedFunction(paramkey);
1439 if (!doc_range.empty())
1440 display_text += "\n\nRange: " + doc_range;
1441 }
1442 }
1443
1444 return true;
1445}
1446
1447bool
1448MooseServer::gatherDocumentReferencesLocations(wasp::DataArray & referencesLocations,
1449 int line,
1450 int character,
1451 bool include_declaration)
1452{
1453 Syntax & syntax = getRegistrationApp().syntax();
1454
1455 // return without adding any reference locations when parser root is null
1456 auto root_ptr = queryRoot();
1457 if (!root_ptr)
1458 return true;
1459 auto & root = *root_ptr;
1460
1461 // find hit node for zero based request line and column number from input
1462 wasp::HITNodeView view_root = root.getNodeView();
1463 wasp::HITNodeView request_context =
1464 wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
1465
1466 // return without adding any references when request not block declarator
1467 if ((request_context.type() != wasp::DECL && request_context.type() != wasp::DOT_SLASH &&
1468 request_context.type() != wasp::LBRACKET && request_context.type() != wasp::RBRACKET) ||
1469 !request_context.has_parent() || request_context.parent().type() != wasp::OBJECT)
1470 return true;
1471
1472 // get input path and block name of declarator located at request context
1473 const std::string & inp_path = request_context.parent().path();
1474 const std::string & inp_name = request_context.parent().name();
1475
1476 // build map from input lookup paths to parameter types and save to reuse
1477 auto & metadata = getSyntaxMetadata();
1478 if (metadata.input_path_to_types.empty())
1479 for (const auto & associated_types_iter : syntax.getAssociatedTypes())
1480 {
1481 const std::string & path = associated_types_iter.first;
1482 const std::string & type = associated_types_iter.second;
1483 metadata.input_path_to_types[path].insert(type);
1484 }
1485
1486 // get registered syntax from block path with map of input paths to types
1487 bool is_parent;
1488 auto registered_syntax = syntax.isAssociated(inp_path, &is_parent, metadata.input_path_to_types);
1489
1490 // return without adding any references if syntax has no types associated
1491 if (is_parent || !metadata.input_path_to_types.count(registered_syntax))
1492 return true;
1493
1494 // get set of parameter types which are associated with registered syntax
1495 const std::set<std::string> & target_types = metadata.input_path_to_types.at(registered_syntax);
1496
1497 // set used to gather nodes collected by value custom sorted by locations
1498 SortedLocationNodes match_nodes(
1499 [](const wasp::HITNodeView & l, const wasp::HITNodeView & r)
1500 {
1501 const std::string & l_file = l.node_pool()->stream_name();
1502 const std::string & r_file = r.node_pool()->stream_name();
1503 return (l_file < r_file || (l_file == r_file && l.line() < r.line()) ||
1504 (l_file == r_file && l.line() == r.line() && l.column() < r.column()));
1505 });
1506
1507 // walk input recursively and gather all nodes that match value and types
1508 getNodesByValueAndTypes(match_nodes, view_root, inp_name, target_types);
1509
1510 // return without adding any references if no nodes match value and types
1511 if (match_nodes.empty())
1512 return true;
1513
1514 // add request context node to set if declaration inclusion was specified
1515 if (include_declaration && request_context.parent().child_count_by_name("decl"))
1516 match_nodes.insert(request_context.parent().first_child_by_name("decl"));
1517
1518 // add locations to references list with nodes that match value and types
1519 return addLocationNodesToList(referencesLocations, match_nodes);
1520}
1521
1522void
1524 wasp::HITNodeView view_parent,
1525 const std::string & target_value,
1526 const std::set<std::string> & target_types)
1527{
1528 // walk over children of context to gather nodes matching value and types
1529 for (const auto & view_child : view_parent)
1530 {
1531 // check for parameter type match if node is value matching target data
1532 if (view_child.type() == wasp::VALUE && view_child.to_string() == target_value)
1533 {
1534 // get object context path and object type value of node if it exists
1535 wasp::HITNodeView object_context = view_child;
1536 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
1537 object_context = object_context.parent();
1538 const std::string object_path = object_context.path();
1539 wasp::HITNodeView type_node = object_context.first_child_by_name("type");
1540 const std::string object_type =
1541 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
1542
1543 // gather global, action, and object parameters for context of object
1544 InputParameters valid_params = emptyInputParameters();
1545 std::set<std::string> obj_act_tasks;
1546 getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
1547
1548 // get name from parent of current value node which is parameter node
1549 std::string param_name = view_child.has_parent() ? view_child.parent().name() : "";
1550
1551 // get type of parameter and prepare string to check target set match
1552 std::string dirty_type = valid_params.type(param_name);
1553 std::string clean_type = MooseUtils::prettyCppType(dirty_type);
1554 pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
1555
1556 // add input node to collection if its type is also in set of targets
1557 if (target_types.count(clean_type))
1558 match_nodes.insert(view_child);
1559 }
1560
1561 // recurse deeper into input to search for matches if node has children
1562 if (!view_child.is_leaf())
1563 getNodesByValueAndTypes(match_nodes, view_child, target_value, target_types);
1564 }
1565}
1566
1567bool
1568MooseServer::gatherDocumentFormattingTextEdits(wasp::DataArray & formattingTextEdits,
1569 int tab_size,
1570 bool /* insert_spaces */)
1571{
1572 // strip scheme prefix from document uri if it exists for parse file path
1573 std::string parse_file_path = document_path;
1574 pcrecpp::RE("(.*://)(.*)").Replace("\\2", &parse_file_path);
1575
1576 // input check expanded any brace expressions in cached tree so reprocess
1577 std::stringstream input_errors, input_stream(getDocumentText());
1578 wasp::DefaultHITInterpreter interpreter(input_errors);
1579
1580 // return without adding any formatting text edits if input parsing fails
1581 if (!interpreter.parseStream(input_stream, parse_file_path))
1582 return true;
1583
1584 // return without adding any formatting text edits if parser root is null
1585 if (interpreter.root().is_null())
1586 return true;
1587
1588 // get input root node line and column range to represent entire document
1589 wasp::HITNodeView view_root = interpreter.root();
1590 int document_start_line = view_root.line() - 1;
1591 int document_start_char = view_root.column() - 1;
1592 int document_last_line = view_root.last_line() - 1;
1593 int document_last_char = view_root.last_column();
1594
1595 // set number of spaces for indentation and build formatted document text
1596 _formatting_tab_size = tab_size;
1597 std::size_t starting_line = view_root.line() - 1;
1598 std::string document_format = formatDocument(view_root, starting_line, 0);
1599
1600 // remove beginning newline character from formatted document text string
1601 document_format.erase(0, 1);
1602
1603 // add formatted text with whole line and column range to formatting list
1604 formattingTextEdits.push_back(wasp::DataObject());
1605 wasp::DataObject * item = formattingTextEdits.back().to_object();
1606 bool pass = wasp::lsp::buildTextEditObject(*item,
1607 errors,
1608 document_start_line,
1609 document_start_char,
1610 document_last_line,
1611 document_last_char,
1612 document_format);
1613 return pass;
1614}
1615
1616std::string
1617MooseServer::formatDocument(wasp::HITNodeView parent, std::size_t & prev_line, std::size_t level)
1618{
1619 // build string of newline and indentation spaces from level and tab size
1620 std::string newline_indent = "\n" + std::string(level * _formatting_tab_size, ' ');
1621
1622 // lambda to format include data by replacing consecutive spaces with one
1623 auto collapse_spaces = [](std::string string_copy)
1624 {
1625 pcrecpp::RE("\\s+").Replace(" ", &string_copy);
1626 return string_copy;
1627 };
1628
1629 // formatted string that will be built recursively by appending each call
1630 std::string format_string;
1631
1632 // walk over all children of this node context and build formatted string
1633 for (const auto i : make_range(parent.child_count()))
1634 {
1635 // walk must be index based to catch file include and skip its children
1636 wasp::HITNodeView child = parent.child_at(i);
1637
1638 // get declarator to address shorthand syntax object with no declarator
1639 auto decl = child.child_count_by_name("decl") ? child.first_child_by_name("decl").data() : "";
1640
1641 // add blank line if necessary after previous line and before this line
1642 std::string blank = child.line() > prev_line + 1 ? "\n" : "";
1643
1644 // format include directive with indentation and collapse extra spacing
1645 if (child.type() == wasp::FILE)
1646 format_string += blank + newline_indent + MooseUtils::trim(collapse_spaces(child.data()));
1647
1648 // format normal comment with indentation and inline comment with space
1649 else if (child.type() == wasp::COMMENT)
1650 format_string += (child.line() == prev_line ? " " : blank + newline_indent) +
1651 MooseUtils::trim(child.data());
1652
1653 // pass object with no declarator through without increased indentation
1654 else if (child.type() == wasp::OBJECT && decl.empty())
1655 format_string += formatDocument(child, prev_line, level);
1656
1657 // format object recursively with indentation and without legacy syntax
1658 else if (child.type() == wasp::OBJECT)
1659 format_string += blank + newline_indent + "[" + decl + "]" +
1660 formatDocument(child, prev_line, level + 1) + newline_indent + "[]";
1661
1662 // format keyed value with indentation and calling reusable hit methods
1663 else if (child.type() == wasp::KEYED_VALUE || child.type() == wasp::ARRAY)
1664 {
1665 const std::string assign = wasp::is_override(child) ? child.child_at(1).data() : "=";
1666 const std::string prefix = newline_indent + decl + " " + assign + " ";
1667
1668 const std::string render_val = hit::extractValue(child.data());
1669 std::size_t val_column = child.child_count() > 2 ? child.child_at(2).column() : 0;
1670 std::size_t prefix_len = prefix.size() - 1;
1671
1672 format_string += blank + prefix + hit::formatValue(render_val, val_column, prefix_len);
1673 }
1674
1675 // set previous line reference used for blank lines and inline comments
1676 prev_line = child.last_line();
1677 }
1678
1679 // return formatted text string that gets appended to each recursive call
1680 return format_string;
1681}
1682
1683bool
1684MooseServer::gatherDocumentSymbols(wasp::DataArray & documentSymbols)
1685{
1686 // return prior to starting document symbol tree when parser root is null
1687 auto root_ptr = queryRoot();
1688 if (!root_ptr)
1689 return true;
1690 auto & root = *root_ptr;
1691
1692 wasp::HITNodeView view_root = root.getNodeView();
1693
1694 bool pass = true;
1695
1696 // walk over all children of root node context and build document symbols
1697 for (const auto i : make_range(view_root.child_count()))
1698 {
1699 // walk must be index based to catch file include and skip its children
1700 wasp::HITNodeView view_child = view_root.child_at(i);
1701
1702 // set up name, zero based line and column range, kind, and detail info
1703 std::string name = view_child.name();
1704 int line = view_child.line() - 1;
1705 int column = view_child.column() - 1;
1706 int last_line = view_child.last_line() - 1;
1707 int last_column = view_child.last_column();
1708 int symbol_kind = getDocumentSymbolKind(view_child);
1709 std::string detail =
1710 !view_child.first_child_by_name("type").is_null()
1711 ? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
1712 : "";
1713
1714 // build document symbol object from node child info and push to array
1715 documentSymbols.push_back(wasp::DataObject());
1716 wasp::DataObject * data_child = documentSymbols.back().to_object();
1717 pass &= wasp::lsp::buildDocumentSymbolObject(*data_child,
1718 errors,
1719 (name.empty() ? "void" : name),
1720 detail,
1721 symbol_kind,
1722 false,
1723 line,
1724 column,
1725 last_line,
1726 last_column,
1727 line,
1728 column,
1729 last_line,
1730 last_column);
1731
1732 // call method to recursively fill document symbols for each node child
1733 pass &= traverseParseTreeAndFillSymbols(view_child, *data_child);
1734 }
1735
1736 return pass;
1737}
1738
1739bool
1741 wasp::DataObject & data_parent)
1742{
1743 // return without adding any children if parent node is file include type
1744 if (wasp::is_nested_file(view_parent))
1745 return true;
1746
1747 bool pass = true;
1748
1749 // walk over all children of this node context and build document symbols
1750 for (const auto i : make_range(view_parent.child_count()))
1751 {
1752 // walk must be index based to catch file include and skip its children
1753 wasp::HITNodeView view_child = view_parent.child_at(i);
1754
1755 // set up name, zero based line and column range, kind, and detail info
1756 std::string name = view_child.name();
1757 int line = view_child.line() - 1;
1758 int column = view_child.column() - 1;
1759 int last_line = view_child.last_line() - 1;
1760 int last_column = view_child.last_column();
1761 int symbol_kind = getDocumentSymbolKind(view_child);
1762 std::string detail =
1763 !view_child.first_child_by_name("type").is_null()
1764 ? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
1765 : "";
1766
1767 // build document symbol object from node child info and push to array
1768 wasp::DataObject & data_child = wasp::lsp::addDocumentSymbolChild(data_parent);
1769 pass &= wasp::lsp::buildDocumentSymbolObject(data_child,
1770 errors,
1771 (name.empty() ? "void" : name),
1772 detail,
1773 symbol_kind,
1774 false,
1775 line,
1776 column,
1777 last_line,
1778 last_column,
1779 line,
1780 column,
1781 last_line,
1782 last_column);
1783
1784 // call method to recursively fill document symbols for each node child
1785 pass &= traverseParseTreeAndFillSymbols(view_child, data_child);
1786 }
1787
1788 return pass;
1789}
1790
1791int
1793 const std::string & param_name,
1794 const std::string & clean_type,
1795 bool is_param)
1796{
1797 // set up completion item kind value that client may use for icon in list
1798 auto associated_types = getRegistrationApp().syntax().getAssociatedTypes();
1799 if (is_param && valid_params.isParamRequired(param_name) &&
1800 !valid_params.isParamValid(param_name))
1801 return wasp::lsp::m_comp_kind_event;
1802 else if (param_name == "active" || param_name == "inactive")
1803 return wasp::lsp::m_comp_kind_class;
1804 else if (clean_type == "bool")
1805 return wasp::lsp::m_comp_kind_interface;
1806 else if (valid_params.have_parameter<MooseEnum>(param_name) ||
1807 valid_params.have_parameter<MultiMooseEnum>(param_name) ||
1808 valid_params.have_parameter<ExecFlagEnum>(param_name) ||
1809 valid_params.have_parameter<std::vector<MooseEnum>>(param_name))
1810 return is_param ? wasp::lsp::m_comp_kind_enum : wasp::lsp::m_comp_kind_enum_member;
1811 else if (param_name == "type")
1812 return wasp::lsp::m_comp_kind_type_param;
1813 else if (std::find_if(associated_types.begin(),
1814 associated_types.end(),
1815 [&](const auto & entry)
1816 { return entry.second == clean_type; }) != associated_types.end())
1817 return wasp::lsp::m_comp_kind_reference;
1818 else
1819 return is_param ? wasp::lsp::m_comp_kind_keyword : wasp::lsp::m_comp_kind_value;
1820}
1821
1822int
1823MooseServer::getDocumentSymbolKind(wasp::HITNodeView symbol_node)
1824{
1825 // lambdas that check if parameter is a boolean or number for symbol kind
1826 auto is_boolean = [](wasp::HITNodeView symbol_node)
1827 {
1828 bool convert;
1829 std::istringstream iss(MooseUtils::toLower(symbol_node.last_as_string()));
1830 return (iss >> std::boolalpha >> convert && !iss.fail());
1831 };
1832 auto is_number = [](wasp::HITNodeView symbol_node)
1833 {
1834 double convert;
1835 std::istringstream iss(symbol_node.last_as_string());
1836 return (iss >> convert && iss.eof());
1837 };
1838
1839 // set up document symbol kind value that client may use for outline icon
1840 if (symbol_node.type() == wasp::OBJECT)
1841 return wasp::lsp::m_symbol_kind_struct;
1842 else if (symbol_node.type() == wasp::FILE)
1843 return wasp::lsp::m_symbol_kind_file;
1844 else if (symbol_node.type() == wasp::ARRAY)
1845 return wasp::lsp::m_symbol_kind_array;
1846 else if (symbol_node.type() == wasp::KEYED_VALUE && symbol_node.name() == std::string("type"))
1847 return wasp::lsp::m_symbol_kind_type_param;
1848 else if (symbol_node.type() == wasp::KEYED_VALUE && is_boolean(symbol_node))
1849 return wasp::lsp::m_symbol_kind_boolean;
1850 else if (symbol_node.type() == wasp::KEYED_VALUE && is_number(symbol_node))
1851 return wasp::lsp::m_symbol_kind_number;
1852 else if (symbol_node.type() == wasp::KEYED_VALUE)
1853 return wasp::lsp::m_symbol_kind_key;
1854 else if (symbol_node.type() == wasp::VALUE)
1855 return wasp::lsp::m_symbol_kind_string;
1856 else
1857 return wasp::lsp::m_symbol_kind_property;
1858}
1859
1860std::string
1861MooseServer::getRequiredParamsText(const std::string & subblock_path,
1862 const std::string & subblock_type,
1863 const std::set<std::string> & existing_params,
1864 const std::string & indent_spaces)
1865{
1866 // gather global, action, and object parameters in request object context
1867 InputParameters valid_params = emptyInputParameters();
1868 std::set<std::string> obj_act_tasks;
1869 getAllValidParameters(valid_params, subblock_path, subblock_type, obj_act_tasks);
1870
1871 // walk over collection of all parameters and build text of ones required
1872 std::string required_param_text;
1873 std::size_t param_index = 1;
1874
1875 // increment parameter tab stop index when adding custom user named block
1876 // this will allow first stop to be placeholder selecting that block name
1877 if (subblock_type.empty() && !subblock_path.empty() && subblock_path.back() == '*')
1878 param_index++;
1879
1880 for (const auto & valid_params_iter : valid_params)
1881 {
1882 // skip parameter if deprecated, private, defaulted, optional, existing
1883 const std::string & param_name = valid_params_iter.first;
1884 if (!valid_params.isParamDeprecated(param_name) && !valid_params.isPrivate(param_name) &&
1885 !valid_params.isParamValid(param_name) && valid_params.isParamRequired(param_name) &&
1886 !existing_params.count(param_name))
1887 {
1888 std::string tab_stop = client_snippet_support ? "$" + std::to_string(param_index++) : "";
1889 required_param_text += "\n" + indent_spaces + param_name + " = " + tab_stop;
1890 }
1891 }
1892
1893 return required_param_text;
1894}
1895
1896bool
1897MooseServer::gatherExtensionResponses(wasp::DataArray & extensionResponses,
1898 const std::string & extensionMethod,
1899 int line,
1900 int character)
1901{
1902 // use appropriate method to fill response based on extension method name
1903 bool pass = true;
1904 if (extensionMethod == "plotting")
1905 pass = gatherPlottingResponses(extensionResponses, line, character);
1906 return pass;
1907}
1908
1909bool
1910MooseServer::gatherPlottingResponses(wasp::DataArray & plotting_responses, int line, int character)
1911{
1912 // return without adding any plot response objects if parser root is null
1913 auto root_ptr = queryRoot();
1914 if (!root_ptr)
1915 return true;
1916 auto & root = *root_ptr;
1917
1918 // find hit node for zero based request line and column number from input
1919 wasp::HITNodeView view_root = root.getNodeView();
1920 wasp::HITNodeView request_context =
1921 wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
1922
1923 // get object context and value of type parameter for request if provided
1924 wasp::HITNodeView object_context = request_context;
1925 while (object_context.type() != wasp::OBJECT && object_context.has_parent())
1926 object_context = object_context.parent();
1927 const std::string & object_name = object_context.name();
1928 wasp::HITNodeView type_node = object_context.first_child_by_name("type");
1929 const std::string & object_type =
1930 type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
1931
1932 // get check app of document and return with no plots if its build failed
1933 auto app_ptr = queryCheckApp();
1934 if (!app_ptr)
1935 return true;
1936
1937 // get problem from action warehouse and return without any plots if null
1938 std::shared_ptr<FEProblemBase> & problem = app_ptr->actionWarehouse().problemBase();
1939 if (!problem)
1940 return true;
1941
1942 // check problem to build function plot if request is from function block
1943 if (problem->hasFunction(object_name))
1944 buildFuncPlotResponse(plotting_responses, *problem, object_name, object_type);
1945
1946 // check problem to build PDF and CDF plots if request is in distribution
1947 else if (problem->hasDistribution(object_name))
1948 buildDistPlotResponses(plotting_responses, *problem, object_name, object_type);
1949
1950 return true;
1951}
1952
1953void
1954MooseServer::buildFuncPlotResponse(wasp::DataArray & plotting_responses,
1955 FEProblemBase & problem,
1956 const std::string & object_name,
1957 const std::string & object_type)
1958{
1959 // get function from problem and return with no plots added if wrong type
1960 const auto * pw_func = dynamic_cast<const PiecewiseBase *>(&problem.getFunction(object_name));
1961 if (!pw_func)
1962 return;
1963
1964 // return without adding plot response objects when function size is zero
1965 if (pw_func->functionSize() == 0)
1966 return;
1967
1968 // walk over piecewise function and gather keys and values for line graph
1969 std::vector<double> graph_keys, graph_vals;
1970 for (std::size_t i = 0; i < pw_func->functionSize(); i++)
1971 {
1972 graph_keys.push_back(pw_func->domain(i));
1973 graph_vals.push_back(pw_func->range(i));
1974 }
1975
1976 // build CustomPlot object from function data then serialize for response
1977 std::string plot_title = object_name + " " + object_type + " Function";
1978 std::string x_axis_label = "abscissa values";
1979 std::string y_axis_label = "ordinate values";
1980 wasp::CustomPlot plot_object;
1981 buildLineGraphPlot(plot_object, plot_title, x_axis_label, y_axis_label, graph_keys, graph_vals);
1982 plotting_responses.push_back(wasp::serializeCustomPlot(plot_object));
1983}
1984
1985void
1986MooseServer::buildDistPlotResponses(wasp::DataArray & plotting_responses,
1987 FEProblemBase & problem,
1988 const std::string & object_name,
1989 const std::string & object_type)
1990{
1991 // get distribution from problem that is registered for given object name
1992 const Distribution & dist = problem.getDistribution(object_name);
1993
1994 // pick plot x-range using quantiles to be generic for distribution types
1995 const double min_x = dist.quantile(_dist_plot_quantile_bound);
1996 const double max_x = dist.quantile(1.0 - _dist_plot_quantile_bound);
1997 const double del_x = (max_x - min_x) / (_dist_plot_num_points - 1);
1998
1999 // return without any plots added if any calculated values are not finite
2000 if (!std::isfinite(min_x) || !std::isfinite(max_x) || max_x <= min_x || !std::isfinite(del_x))
2001 return;
2002
2003 // use uniform grid of x-axis graph keys to sample plot values for y-axis
2004 std::vector<double> graph_keys(_dist_plot_num_points);
2005 std::vector<double> pdf_values(_dist_plot_num_points);
2006 std::vector<double> cdf_values(_dist_plot_num_points);
2007
2008 // calculate PDF values and CDF values for each key within range of graph
2009 for (std::size_t i = 0; i < _dist_plot_num_points; i++)
2010 {
2011 graph_keys[i] = min_x + (i * del_x);
2012 pdf_values[i] = dist.pdf(graph_keys[i]);
2013 cdf_values[i] = dist.cdf(graph_keys[i]);
2014
2015 // return without any plots added if any PDF or CDF value is not finite
2016 if (!std::isfinite(pdf_values[i]) || !std::isfinite(cdf_values[i]))
2017 return;
2018 }
2019
2020 // lambda to build CustomPlot object for distribution and add to response
2021 auto add_dist_to_plot = [&](const std::string & dist_type, const std::vector<double> & graph_vals)
2022 {
2023 std::string plot_title = object_name + " " + object_type + " " + dist_type + " Distribution";
2024 std::string x_axis_label = "x values";
2025 std::string y_axis_label = dist_type + " values";
2026 wasp::CustomPlot plot_object;
2027 buildLineGraphPlot(plot_object, plot_title, x_axis_label, y_axis_label, graph_keys, graph_vals);
2028 plotting_responses.push_back(wasp::serializeCustomPlot(plot_object));
2029 };
2030
2031 // build CustomPlot object for PDF values, serialize, and add to response
2032 add_dist_to_plot("PDF", pdf_values);
2033
2034 // build CustomPlot object for CDF values, serialize, and add to response
2035 add_dist_to_plot("CDF", cdf_values);
2036}
2037
2038void
2039MooseServer::buildLineGraphPlot(wasp::CustomPlot & plot_object,
2040 const std::string & plot_title,
2041 const std::string & x_axis_label,
2042 const std::string & y_axis_label,
2043 const std::vector<double> & graph_keys,
2044 const std::vector<double> & graph_vals)
2045{
2046 // axis ranges
2047 double min_key = *std::min_element(graph_keys.begin(), graph_keys.end());
2048 double max_key = *std::max_element(graph_keys.begin(), graph_keys.end());
2049 double min_val = *std::min_element(graph_vals.begin(), graph_vals.end());
2050 double max_val = *std::max_element(graph_vals.begin(), graph_vals.end());
2051
2052 // widen extents
2053 double pad_factor = 0.05;
2054 double pad_x_axis = (max_key - min_key) * pad_factor;
2055 double pad_y_axis = (max_val - min_val) * pad_factor;
2056 if (pad_y_axis == 0)
2057 pad_y_axis = pad_factor;
2058 min_key -= pad_x_axis;
2059 max_key += pad_x_axis;
2060 min_val -= pad_y_axis;
2061 max_val += pad_y_axis;
2062
2063 // plot setup
2064 plot_object.title().text(plot_title);
2065 plot_object.title().font().pointsize(18);
2066 plot_object.title().visible(true);
2067 plot_object.legend().visible(false);
2068
2069 // plot x-axis
2070 plot_object.x1Axis().label(x_axis_label);
2071 plot_object.x1Axis().rangeMin(min_key);
2072 plot_object.x1Axis().rangeMax(max_key);
2073 plot_object.x1Axis().scaleType(wasp::CustomPlot::stLinear);
2074 plot_object.x1Axis().labelType(wasp::CustomPlot::ltNumber);
2075 plot_object.x1Axis().labelFont().pointsize(18);
2076 plot_object.x1Axis().tickLabelFont().pointsize(16);
2077
2078 // plot y-axis
2079 plot_object.y1Axis().label(y_axis_label);
2080 plot_object.y1Axis().rangeMin(min_val);
2081 plot_object.y1Axis().rangeMax(max_val);
2082 plot_object.y1Axis().scaleType(wasp::CustomPlot::stLinear);
2083 plot_object.y1Axis().labelType(wasp::CustomPlot::ltNumber);
2084 plot_object.y1Axis().labelFont().pointsize(18);
2085 plot_object.y1Axis().tickLabelFont().pointsize(16);
2086
2087 // graph series
2088 auto line_graph = std::make_shared<wasp::CustomPlot::Graph>();
2089 line_graph->keys() = graph_keys;
2090 line_graph->values() = graph_vals;
2091 line_graph->scatterShape(wasp::CustomPlot::ssDisc);
2092 plot_object.series().push_back(line_graph);
2093}
2094
2095const hit::Node *
2097{
2098 if (const auto parser_ptr = queryCheckParser())
2099 {
2100#ifndef NDEBUG
2101 if (const auto app_ptr = queryCheckApp())
2102 mooseAssert(&app_ptr->parser() == parser_ptr, "App should have this parser");
2103#endif
2104 if (const auto root_ptr = parser_ptr->queryRoot())
2105 if (!root_ptr->getNodeView().is_null())
2106 return root_ptr;
2107 }
2108 return nullptr;
2109}
2110
2113{
2114 const auto it = _check_state.find(document_path);
2115 return it == _check_state.end() ? nullptr : &it->second;
2116}
2117
2120{
2121 return const_cast<MooseServer::CheckState *>(std::as_const(*this).queryCheckState());
2122}
2123
2124const Parser *
2126{
2127 const auto state = queryCheckState();
2128 return state ? state->parser.get() : nullptr;
2129}
2130
2131Parser *
2133{
2134 return const_cast<Parser *>(std::as_const(*this).queryCheckParser());
2135}
2136
2137const MooseApp *
2139{
2140 if (auto state = queryCheckState())
2141 return state->app.get();
2142 return nullptr;
2143}
2144
2145MooseApp *
2147{
2148 return const_cast<MooseApp *>(std::as_const(*this).queryCheckApp());
2149}
2150
2151MooseApp &
2153{
2154 if (auto app_ptr = queryCheckApp())
2155 {
2156 auto & app = *app_ptr;
2157 mooseAssert(queryCheckParser(), "Should have a parser");
2158 mooseAssert(&app.parser() == queryCheckParser(), "Parser should be the app's parser");
2159 return app;
2160 }
2161 mooseError("MooseServer::getCheckApp(): App not available");
2162}
2163
2164MooseApp &
2166{
2167 if (auto * app = queryCheckApp())
2168 return *app;
2169 return _moose_app;
2170}
2171
2177
2178const hit::Node &
2180{
2181 if (auto root_ptr = queryRoot())
2182 return *root_ptr;
2183 mooseError("MooseServer::getRoot(): Root not available");
2184}
InputParameters emptyInputParameters()
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
unsigned int count
Definition MortarUtils.C:53
void ErrorVector unsigned int
Specialized factory for generic Action System objects.
std::set< std::string > getTasksByAction(const std::string &action) const
InputParameters getValidParams(const std::string &name)
bool isRegistered(const std::string &app_name) const
Returns a Boolean indicating whether an application type has been registered.
Definition AppFactory.h:168
InputParameters getValidParams(const std::string &name)
Get valid parameters for the object.
Definition AppFactory.C:35
const auto & registeredObjects() const
Returns a reference to the map from names to AppFactoryBuildInfo pointers.
Definition AppFactory.h:163
static std::unique_ptr< MooseApp > create(const std::string &app_type, const std::vector< std::string > &cli_args={})
Create an app with no input and command line arguments.
Definition AppFactory.C:64
static const std::string main_app_name
The name for the "main" moose application.
Definition AppFactory.h:68
static AppFactory & instance()
Get the instance of the AppFactory.
Definition AppFactory.C:20
All Distributions should inherit from this class.
virtual Real pdf(const Real &x) const =0
Compute the probability with given probability distribution function (PDF) at x.
virtual Real quantile(const Real &y) const =0
Compute the inverse CDF (quantile function) value for given variable value y.
virtual Real cdf(const Real &x) const =0
Compute the cumulative probability with given cumulative probability distribution (CDF) at x.
A MultiMooseEnum object to hold "execute_on" flags.
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
virtual Distribution & getDistribution(const std::string &name)
virtual Function & getFunction(const std::string &name, const THREAD_ID tid=0)
Generic factory class for build all sorts of objects.
Definition Factory.h:29
bool isRegistered(const std::string &obj_name) const
Returns a Boolean indicating whether an object type has been registered.
Definition Factory.h:152
const auto & registeredObjects() const
Returns a reference to the map from names to RegistryEntryBase pointers.
Definition Factory.h:147
InputParameters getValidParams(const std::string &name) const
Get valid parameters for the object.
Definition Factory.C:68
FileLineInfo getLineInfo(const std::string &name) const
Gets file and line information where an object was initially registered.
Definition Factory.C:284
Holds file and line information.
bool isValid() const
int line() const
std::string file() const
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
bool isPrivate(const std::string &name) const
Returns a Boolean indicating whether the specified parameter is private or not.
std::string rangeCheckedFunction(const std::string &name) const
Return the range check function for any parameter (empty string if it is not range checked)
std::string getDocString(const std::string &name) const
Returns the documentation string for the specified parameter name.
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
std::string type(const std::string &name) const
Prints the type of the requested parameter by name.
bool have_parameter(std::string_view name) const
A wrapper around the Parameters base class method.
std::string getDocUnit(const std::string &name) const
Returns the documentation unit string for the specified parameter name.
bool isParamRequired(const std::string &name) const
Returns a boolean indicating whether the specified parameter is required or not.
void makeParamNotRequired(const std::string &name)
Changes the parameter to not be required.
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
std::set< std::string > getParametersList() const
bool hasDefaultCoupledValue(const std::string &coupling_name) const
Return whether or not the requested parameter has a default coupled value.
std::string getClassDescription() const
Returns the class description.
Real defaultCoupledValue(const std::string &coupling_name, unsigned int i=0) const
Get the default value for an optionally coupled variable.
bool isParamDeprecated(const std::string &name) const
Returns True if the parameters is deprecated.
const std::string & getBase() const
bool hasBase() const
bool isRangeChecked(const std::string &param_name) const
Return whether a parameter has a range check.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
static std::string basicCppType(const std::string &cpp_type)
static std::string buildOutputString(const std::iterator_traits< InputParameters::iterator >::value_type &p)
Base class for MOOSE-based applications.
Definition MooseApp.h:110
Syntax & syntax()
Returns a writable reference to the syntax object.
Definition MooseApp.h:231
ActionFactory & getActionFactory()
Retrieve a writable reference to the ActionFactory associated with this App.
Definition MooseApp.h:412
virtual void run()
Run the application.
Definition MooseApp.C:2033
Factory & getFactory()
Retrieve a writable reference to the Factory associated with this App.
Definition MooseApp.h:407
const std::shared_ptr< libMesh::Parallel::Communicator > getCommunicator() const
Definition MooseApp.h:1058
std::shared_ptr< CommandLine > commandLine() const
Get the command line.
Definition MooseApp.h:424
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
Exception to be thrown whenever we have _throw_on_error set and a mooseError() is emitted.
Definition MooseError.h:118
bool gatherPlottingResponses(wasp::DataArray &plotting_responses, int line, int character)
Build CustomPlot extension responses when method name is plotting.
MooseApp & getCheckApp()
Public interface for writable check app reference with error checks.
void getInputLookupDefinitionNodes(SortedLocationNodes &location_nodes, const std::string &clean_type, const std::string &val_string)
Get set of nodes from associated path lookups matching value string.
int getDocumentSymbolKind(wasp::HITNodeView symbol_node)
Get document symbol kind value that client may use for outline icon.
std::set< wasp::HITNodeView, std::function< bool(const wasp::HITNodeView &, const wasp::HITNodeView &)> > SortedLocationNodes
SortedLocationNodes - type alias for set of nodes sorted by location.
Definition MooseServer.h:61
void addObjectsFromWarehouses(const std::string &param_type, std::map< std::string, std::string > &options_and_descs)
Supplement completion list with objects in warehouses if applicable.
bool addSubblocksToList(wasp::DataArray &completionItems, const std::string &object_path, int replace_line_beg, int replace_char_beg, int replace_line_end, int replace_char_end, const std::string &filtering_prefix, bool request_on_block_decl)
Add subblocks to completion list for request path, line, and column.
bool parseDocumentForDiagnostics(wasp::DataArray &diagnosticsList)
Parse document for diagnostics - specific to this server implemention.
Definition MooseServer.C:71
void buildFuncPlotResponse(wasp::DataArray &plotting_responses, FEProblemBase &problem, const std::string &object_name, const std::string &object_type)
Gather function data, build CustomPlot object, and add to responses.
std::size_t _dist_plot_num_points
_dist_plot_num_points - distribution plot sampling resolution
void getExistingInput(wasp::HITNodeView parent_node, std::set< std::string > &existing_params, std::set< std::string > &existing_subblocks)
Get names of parameters and subblocks specified in given input node.
void getActionParameters(InputParameters &valid_params, const std::string &object_path, std::set< std::string > &obj_act_tasks)
Get all action parameters using requested object path to collection.
const CheckState * queryCheckState() const
void getEnumsAndDocs(MooseEnumType &moose_enum_param, std::map< std::string, std::string > &options_and_descs)
Fill map of all options and descriptions if parameter is moose enum.
std::map< std::string, SyntaxMetadata > _app_type_to_syntax_metadata
_app_type_to_syntax_metadata - syntax metadata per app type map
bool gatherDocumentReferencesLocations(wasp::DataArray &referencesLocations, int line, int character, bool include_declaration)
Gather references locations - specific to this server implemention.
bool addParametersToList(wasp::DataArray &completionItems, const InputParameters &valid_params, const std::set< std::string > &existing_params, int replace_line_beg, int replace_char_beg, int replace_line_end, int replace_char_end, const std::string &filtering_prefix)
Add parameters that were previously gathered to list for completion.
void buildDistPlotResponses(wasp::DataArray &plotting_responses, FEProblemBase &problem, const std::string &object_name, const std::string &object_type)
Compute PDF and CDF, build CustomPlot objects, and add to responses.
bool gatherDocumentDefinitionLocations(wasp::DataArray &definitionLocations, int line, int character)
Gather definition locations - specific to this server implemention.
std::size_t _formatting_tab_size
_formatting_tab_size - number of indent spaces for formatting
MooseApp & _moose_app
_moose_app - reference to parent application that owns this server
std::map< std::string, CheckState > _check_state
_check_state - map from document paths to state (parser, app, text)
void getObjectParameters(InputParameters &valid_params, std::string object_type, const std::set< std::string > &obj_act_tasks)
Get all object parameters using requested object path to collection.
bool traverseParseTreeAndFillSymbols(wasp::HITNodeView view_parent, wasp::DataObject &data_parent)
Recursively fill document symbols from the given node.
const hit::Node & getRoot() const
bool addValuesToList(wasp::DataArray &completionItems, const InputParameters &valid_params, const std::set< std::string > &existing_params, const std::set< std::string > &existing_subblocks, const std::string &param_name, const std::set< std::string > &obj_act_tasks, const std::string &object_path, int replace_line_beg, int replace_char_beg, int replace_line_end, int replace_char_end)
Add parameter values to completion list for request line and column.
std::string formatDocument(wasp::HITNodeView parent, std::size_t &prev_line, std::size_t level)
Recursively walk down whole nodeview tree while formatting document.
void addResourcesForDocument()
Add paths from includes and FileName parameters for client to watch.
std::string getRequiredParamsText(const std::string &subblock_path, const std::string &subblock_type, const std::set< std::string > &existing_params, const std::string &indent_spaces)
Get required parameter completion text list for given subblock path.
void buildLineGraphPlot(wasp::CustomPlot &plot_object, const std::string &plot_title, const std::string &x_axis_label, const std::string &y_axis_label, const std::vector< double > &graph_keys, const std::vector< double > &graph_vals)
Build CustomPlot graph with provided keys, values, and plot title.
void getNodesByValueAndTypes(SortedLocationNodes &match_nodes, wasp::HITNodeView view_parent, const std::string &target_value, const std::set< std::string > &target_types)
Recursively walk input to gather all nodes matching value and types.
MooseServer(MooseApp &moose_app)
Definition MooseServer.C:51
int getCompletionItemKind(const InputParameters &valid_params, const std::string &param_name, const std::string &clean_type, bool is_param)
Get completion item kind value that client may use for icon in list.
void getFileNameTypeValues(std::set< std::string > &filename_vals, wasp::HITNodeView parent)
Recursively walk input to gather all FileName type parameter values.
bool gatherExtensionResponses(wasp::DataArray &extensionResponses, const std::string &extensionMethod, int line, int character)
Gather extension responses - specific to this server implemention.
const MooseApp * queryCheckApp() const
const Parser * queryCheckParser() const
bool gatherDocumentFormattingTextEdits(wasp::DataArray &formattingTextEdits, int tab_size, bool insert_spaces)
Gather formatting text edits - specific to this server implemention.
bool gatherDocumentCompletionItems(wasp::DataArray &completionItems, bool &is_incomplete, int line, int character)
Gather document completion items - specific to this server implemention.
bool addLocationNodesToList(wasp::DataArray &defsOrRefsLocations, const SortedLocationNodes &location_nodes)
Add set of nodes sorted by location to definition or reference list.
MooseApp & getRegistrationApp()
double _dist_plot_quantile_bound
_dist_plot_quantile_bound - epsilon to bound plot range tails
SyntaxMetadata & getSyntaxMetadata()
const hit::Node * queryRoot() const
bool getHoverDisplayText(std::string &display_text, int line, int character)
Get hover display text - logic specific to this server implemention.
void getAllValidParameters(InputParameters &valid_params, const std::string &object_path, const std::string &object_type, std::set< std::string > &obj_act_tasks)
Get all global parameters, action parameters, and object parameters.
bool gatherDocumentSymbols(wasp::DataArray &documentSymbols)
Gather document symbols - specific to this server implemention.
static InputParameters validParams()
Parameters that are processed directly by the Parser and are valid anywhere in the input.
Definition Builder.C:121
Scoped helper for setting Moose::_throw_on_error during this scope.
Definition Moose.h:308
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type.
Based class for output objects.
Definition Output.h:52
Class for parsing input files.
Definition Parser.h:102
Function base which provides a piecewise approximation to a specified (x,y) point data set.
Holding syntax for parsing input files.
Definition Syntax.h:22
std::pair< std::multimap< std::string, ActionInfo >::const_iterator, std::multimap< std::string, ActionInfo >::const_iterator > getActions(const std::string &syntax) const
Returns a pair of multimap iterators to all the ActionInfo objects associated with a given piece of s...
Definition Syntax.C:329
const std::multimap< std::string, ActionInfo > & getAssociatedActions() const
Return all Syntax to Action associations.
Definition Syntax.C:375
std::string isAssociated(const std::string &real_id, bool *is_parent, const std::map< std::string, std::set< std::string > > &alt_map={}) const
Method for determining whether a piece of syntax is associated with an Action an optional syntax map ...
Definition Syntax.C:252
bool verifyMooseObjectTask(const std::string &base, const std::string &task) const
Returns a Boolean indicating whether a task is associated with on of the MOOSE pluggable systems (BAS...
Definition Syntax.C:335
const std::multimap< std::string, std::string > & getAssociatedTypes() const
Get a multimap of registered associations of syntax with type.
Definition Syntax.C:369
void remove(std::string_view)
std::string toLower(std::string name)
Convert supplied string to lower case.
void escape(std::string &str)
Definition MooseUtils.C:218
std::string trim(const std::string &str, const std::string &white_space=" \t\n\v\f\r")
Standard scripting language trim function.
std::string prettyCppType(const std::string &cpp_type)
bool checkFileReadable(const std::string &filename, bool check_line_endings, bool throw_on_unreadable, bool check_for_git_lfs_pointer)
Definition MooseUtils.C:265
Helper for storing the state for a single document.
struct to store syntax metadata for each registration application type