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