LCOV - code coverage report
Current view: top level - src/base - MooseServer.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 678 721 94.0 %
Date: 2025-07-17 01:28:37 Functions: 40 40 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "pcrecpp.h"
      25             : #include "hit/hit.h"
      26             : #include "wasphit/HITInterpreter.h"
      27             : #include "waspcore/utils.h"
      28             : #include <algorithm>
      29             : #include <vector>
      30             : #include <sstream>
      31             : #include <iostream>
      32             : #include <functional>
      33             : 
      34           1 : MooseServer::MooseServer(MooseApp & moose_app)
      35           1 :   : _moose_app(moose_app),
      36           1 :     _connection(std::make_shared<wasp::lsp::IOStreamConnection>(this)),
      37           2 :     _formatting_tab_size(0)
      38             : {
      39             :   // set server capabilities to receive full input text when changed
      40           1 :   server_capabilities[wasp::lsp::m_text_doc_sync] = wasp::DataObject();
      41           1 :   server_capabilities[wasp::lsp::m_text_doc_sync][wasp::lsp::m_open_close] = true;
      42           1 :   server_capabilities[wasp::lsp::m_text_doc_sync][wasp::lsp::m_change] = wasp::lsp::m_change_full;
      43             : 
      44             :   // notify completion, symbol, formatting, definition capabilities support
      45           1 :   server_capabilities[wasp::lsp::m_completion_provider] = wasp::DataObject();
      46           1 :   server_capabilities[wasp::lsp::m_completion_provider][wasp::lsp::m_resolve_provider] = false;
      47           1 :   server_capabilities[wasp::lsp::m_doc_symbol_provider] = true;
      48           1 :   server_capabilities[wasp::lsp::m_doc_format_provider] = true;
      49           1 :   server_capabilities[wasp::lsp::m_definition_provider] = true;
      50           1 :   server_capabilities[wasp::lsp::m_references_provider] = true;
      51           1 :   server_capabilities[wasp::lsp::m_hover_provider] = true;
      52           1 : }
      53             : 
      54             : bool
      55           7 : MooseServer::parseDocumentForDiagnostics(wasp::DataArray & diagnosticsList)
      56             : {
      57             :   // strip prefix from document uri if it exists to get parse file path
      58           7 :   std::string parse_file_path = document_path;
      59           7 :   pcrecpp::RE("(.*://)(.*)").Replace("\\2", &parse_file_path);
      60             : 
      61             :   // copy parent application parameters and modify to set up input check
      62           7 :   InputParameters app_params = _moose_app.parameters();
      63          14 :   app_params.set<std::shared_ptr<Parser>>("_parser") =
      64          21 :       std::make_shared<Parser>(parse_file_path, document_text);
      65             : 
      66           7 :   auto command_line = std::make_unique<CommandLine>(_moose_app.commandLine()->getArguments());
      67           7 :   if (command_line->hasArgument("--language-server"))
      68           0 :     command_line->removeArgument("--language-server");
      69           7 :   command_line->addArgument("--check-input");
      70           7 :   command_line->addArgument("--error-unused");
      71           7 :   command_line->addArgument("--error");
      72           7 :   command_line->addArgument("--color=off");
      73           7 :   command_line->addArgument("--disable-perf-graph-live");
      74           7 :   command_line->parse();
      75           7 :   app_params.set<std::shared_ptr<CommandLine>>("_command_line") = std::move(command_line);
      76             : 
      77             :   // create new application with parameters modified for input check run
      78          28 :   _check_apps[document_path] = AppFactory::instance().createShared(
      79          28 :       _moose_app.type(), _moose_app.name(), app_params, _moose_app.getCommunicator()->get());
      80             : 
      81             :   // add updated document text to map associated with current document path
      82           7 :   _path_to_text[document_path] = document_text;
      83             : 
      84             :   // enable exceptions to be thrown for errors and cache initial setting
      85           7 :   bool cached_throw_on_error = Moose::_throw_on_error;
      86           7 :   Moose::_throw_on_error = true;
      87             : 
      88           7 :   bool pass = true;
      89             : 
      90             :   // run input check application converting caught errors to diagnostics
      91             :   try
      92             :   {
      93          12 :     getCheckApp()->run();
      94             :   }
      95           5 :   catch (std::exception & err)
      96             :   {
      97           5 :     int line_number = 1;
      98           5 :     int column_number = 1;
      99             : 
     100           5 :     std::istringstream caught_msg(err.what());
     101             : 
     102             :     // walk over caught message line by line adding each as a diagnostic
     103          30 :     for (std::string error_line; std::getline(caught_msg, error_line);)
     104             :     {
     105             :       // check if this error line already has the input file path prefix
     106          25 :       if (error_line.rfind(parse_file_path + ":", 0) == 0)
     107             :       {
     108             :         // strip input file path and colon prefix off of this error line
     109          16 :         error_line.erase(0, parse_file_path.size() + 1);
     110             : 
     111             :         int match_line_number;
     112             :         int match_column_number;
     113          16 :         std::string match_error_line;
     114             : 
     115             :         // get line and column number from this error line if both exist
     116          32 :         if (pcrecpp::RE("^(\\d+)\\.(\\d+)\\-?\\d*:\\s*(.*)$")
     117          16 :                 .FullMatch(error_line, &match_line_number, &match_column_number, &match_error_line))
     118             :         {
     119          15 :           line_number = match_line_number;
     120          15 :           column_number = match_column_number;
     121          15 :           error_line = match_error_line;
     122             :         }
     123             : 
     124             :         // otherwise get line number off of this error line if it exists
     125           2 :         else if (pcrecpp::RE("^(\\d+):\\s*(.*)$")
     126           1 :                      .FullMatch(error_line, &match_line_number, &match_error_line))
     127             :         {
     128           1 :           line_number = match_line_number;
     129           1 :           column_number = 1;
     130           1 :           error_line = match_error_line;
     131             :         }
     132          16 :       }
     133             : 
     134             :       // skip adding diagnostic when message is empty or only whitespace
     135          25 :       if (error_line.find_first_not_of(" \t") == std::string::npos)
     136           3 :         continue;
     137             : 
     138             :       // build zero based line and column diagnostic and add to the list
     139          22 :       diagnosticsList.push_back(wasp::DataObject());
     140          22 :       wasp::DataObject * diagnostic = diagnosticsList.back().to_object();
     141          22 :       pass &= wasp::lsp::buildDiagnosticObject(*diagnostic,
     142             :                                                errors,
     143             :                                                line_number - 1,
     144             :                                                column_number - 1,
     145             :                                                line_number - 1,
     146             :                                                column_number - 1,
     147             :                                                1,
     148             :                                                "moose_srv",
     149             :                                                "check_inp",
     150             :                                                error_line);
     151           5 :     }
     152           5 :   }
     153             : 
     154             :   // reset behavior of exceptions thrown for errors with initial setting
     155           7 :   Moose::_throw_on_error = cached_throw_on_error;
     156             : 
     157           7 :   return pass;
     158           7 : }
     159             : 
     160             : bool
     161           6 : MooseServer::updateDocumentTextChanges(const std::string & replacement_text,
     162             :                                        int /* start_line */,
     163             :                                        int /* start_character */,
     164             :                                        int /* end_line */,
     165             :                                        int /* end_character*/,
     166             :                                        int /* range_length*/)
     167             : {
     168             :   // replacement text swaps full document as indicated in server capabilities
     169           6 :   document_text = replacement_text;
     170             : 
     171           6 :   return true;
     172             : }
     173             : 
     174             : bool
     175          13 : MooseServer::gatherDocumentCompletionItems(wasp::DataArray & completionItems,
     176             :                                            bool & is_incomplete,
     177             :                                            int line,
     178             :                                            int character)
     179             : {
     180             :   // add only root level blocks to completion list when parser root is null
     181          13 :   if (!rootIsValid())
     182           0 :     return addSubblocksToList(completionItems, "/", line, character, line, character, "", false);
     183             : 
     184             :   // lambdas that will be used for checking completion request context type
     185          39 :   auto is_request_in_open_block = [](wasp::HITNodeView request_context) {
     186          39 :     return request_context.type() == wasp::OBJECT || request_context.type() == wasp::DOCUMENT_ROOT;
     187             :   };
     188          22 :   auto is_request_on_param_decl = [](wasp::HITNodeView request_context)
     189             :   {
     190          30 :     return request_context.type() == wasp::DECL && request_context.has_parent() &&
     191          30 :            (request_context.parent().type() == wasp::KEYED_VALUE ||
     192          48 :             request_context.parent().type() == wasp::ARRAY);
     193             :   };
     194          37 :   auto is_request_on_block_decl = [](wasp::HITNodeView request_context)
     195             :   {
     196          51 :     return request_context.type() == wasp::DECL && request_context.has_parent() &&
     197          51 :            request_context.parent().type() == wasp::OBJECT;
     198             :   };
     199             : 
     200             :   // get document tree root used to find node under request line and column
     201          13 :   wasp::HITNodeView view_root = getRoot().getNodeView();
     202          13 :   wasp::HITNodeView request_context;
     203             : 
     204             :   // find node under request location if it is not past all defined content
     205          14 :   if (line + 1 < (int)view_root.last_line() ||
     206           1 :       (line + 1 == (int)view_root.last_line() && character <= (int)view_root.last_column()))
     207          13 :     request_context = wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
     208             : 
     209             :   // otherwise find last node in document with last line and column of tree
     210             :   else
     211             :   {
     212             :     request_context =
     213           0 :         wasp::findNodeUnderLineColumn(view_root, view_root.last_line(), view_root.last_column());
     214             : 
     215             :     // change context to be parent block or grandparent if block terminator
     216           0 :     wasp::HITNodeView object_context = request_context;
     217           0 :     while (object_context.type() != wasp::OBJECT && object_context.has_parent())
     218           0 :       object_context = object_context.parent();
     219           0 :     if (request_context.type() == wasp::OBJECT_TERM && object_context.has_parent())
     220           0 :       object_context = object_context.parent();
     221           0 :     request_context = object_context;
     222           0 :   }
     223             : 
     224             :   // change context to equal sign if it is preceding node and in open block
     225          13 :   if (is_request_in_open_block(request_context))
     226             :   {
     227           3 :     wasp::HITNodeView backup_context = request_context;
     228           4 :     for (int backup_char = character; backup_context == request_context && --backup_char > 0;)
     229           1 :       backup_context = wasp::findNodeUnderLineColumn(request_context, line + 1, backup_char + 1);
     230           3 :     if (backup_context.type() == wasp::ASSIGN || backup_context.type() == wasp::OVERRIDE_ASSIGN)
     231           1 :       request_context = backup_context;
     232           3 :   }
     233             : 
     234             :   // use request context type to set up replacement range and prefix filter
     235          13 :   int replace_line_beg = line;
     236          13 :   int replace_char_beg = character;
     237          13 :   int replace_line_end = line;
     238          13 :   int replace_char_end = character;
     239          13 :   std::string filtering_prefix;
     240          13 :   if (request_context.type() == wasp::DECL || request_context.type() == wasp::VALUE)
     241             :   {
     242             :     // completion on existing block name, parameter name, or value replaces
     243           9 :     replace_line_beg = request_context.line() - 1;
     244           9 :     replace_char_beg = request_context.column() - 1;
     245           9 :     replace_line_end = request_context.last_line() - 1;
     246           9 :     replace_char_end = request_context.last_column();
     247           9 :     filtering_prefix = request_context.data();
     248             : 
     249             :     // empty block name columns are same as bracket so bump replace columns
     250           9 :     if (is_request_on_block_decl(request_context) && filtering_prefix.empty())
     251             :     {
     252           1 :       replace_char_beg++;
     253           1 :       replace_char_end++;
     254             :     }
     255             :   }
     256             : 
     257             :   // get name of request context direct parent node so it can be used later
     258          13 :   const auto & parent_name = request_context.has_parent() ? request_context.parent().name() : "";
     259             : 
     260             :   // get object context and value of type parameter for request if provided
     261          13 :   wasp::HITNodeView object_context = request_context;
     262          33 :   while (object_context.type() != wasp::OBJECT && object_context.has_parent())
     263          20 :     object_context = object_context.parent();
     264          13 :   if (is_request_on_block_decl(request_context))
     265           2 :     object_context = object_context.parent();
     266          13 :   const std::string & object_path = object_context.path();
     267          13 :   wasp::HITNodeView type_node = object_context.first_child_by_name("type");
     268             :   const std::string & object_type =
     269          13 :       type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
     270             : 
     271             :   // get set of all parameter and subblock names already specified in input
     272          13 :   std::set<std::string> existing_params, existing_subblocks;
     273          13 :   getExistingInput(object_context, existing_params, existing_subblocks);
     274             : 
     275             :   // set used to gather all parameters valid from object context of request
     276          13 :   InputParameters valid_params = emptyInputParameters();
     277             : 
     278             :   // set used to gather MooseObjectAction tasks to verify object parameters
     279          13 :   std::set<std::string> obj_act_tasks;
     280             : 
     281             :   // get set of global parameters, action parameters, and object parameters
     282          13 :   getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
     283             : 
     284          13 :   bool pass = true;
     285             : 
     286             :   // add gathered parameters to completion list with input range and prefix
     287          13 :   if (is_request_in_open_block(request_context) || is_request_on_param_decl(request_context))
     288           4 :     pass &= addParametersToList(completionItems,
     289             :                                 valid_params,
     290             :                                 existing_params,
     291             :                                 replace_line_beg,
     292             :                                 replace_char_beg,
     293             :                                 replace_line_end,
     294             :                                 replace_char_end,
     295             :                                 filtering_prefix);
     296             : 
     297             :   // add all valid subblocks to completion list with input range and prefix
     298          35 :   if (is_request_in_open_block(request_context) || is_request_on_param_decl(request_context) ||
     299          22 :       is_request_on_block_decl(request_context))
     300          12 :     pass &= addSubblocksToList(completionItems,
     301             :                                object_path,
     302             :                                replace_line_beg,
     303             :                                replace_char_beg,
     304             :                                replace_line_end,
     305             :                                replace_char_end,
     306             :                                filtering_prefix,
     307           6 :                                is_request_on_block_decl(request_context));
     308             : 
     309             :   // add valid parameter value options to completion list using input range
     310           8 :   if ((request_context.type() == wasp::VALUE || request_context.type() == wasp::ASSIGN ||
     311          27 :        request_context.type() == wasp::OVERRIDE_ASSIGN) &&
     312          20 :       valid_params.getParametersList().count(parent_name))
     313           7 :     pass &= addValuesToList(completionItems,
     314             :                             valid_params,
     315             :                             existing_params,
     316             :                             existing_subblocks,
     317             :                             parent_name,
     318             :                             obj_act_tasks,
     319             :                             object_path,
     320             :                             replace_line_beg,
     321             :                             replace_char_beg,
     322             :                             replace_line_end,
     323             :                             replace_char_end);
     324             : 
     325          13 :   is_incomplete = !pass;
     326             : 
     327          13 :   return pass;
     328          13 : }
     329             : 
     330             : void
     331          13 : MooseServer::getExistingInput(wasp::HITNodeView parent_node,
     332             :                               std::set<std::string> & existing_params,
     333             :                               std::set<std::string> & existing_subblocks)
     334             : {
     335             :   // gather names of all parameters and subblocks provided in input context
     336         179 :   for (auto itr = parent_node.begin(); itr != parent_node.end(); itr.next())
     337             :   {
     338          83 :     auto child_node = itr.get();
     339             : 
     340             :     // add key value or array type as parameter and object type as subblock
     341          83 :     if (child_node.type() == wasp::KEYED_VALUE || child_node.type() == wasp::ARRAY)
     342          25 :       existing_params.insert(child_node.name());
     343          58 :     else if (child_node.type() == wasp::OBJECT)
     344          12 :       existing_subblocks.insert(child_node.name());
     345          96 :   }
     346          13 : }
     347             : 
     348             : void
     349        1150 : MooseServer::getAllValidParameters(InputParameters & valid_params,
     350             :                                    const std::string & object_path,
     351             :                                    const std::string & object_type,
     352             :                                    std::set<std::string> & obj_act_tasks)
     353             : {
     354             :   // gather global parameters then action parameters then object parameters
     355        1150 :   valid_params += Moose::Builder::validParams();
     356        1150 :   getActionParameters(valid_params, object_path, obj_act_tasks);
     357        1150 :   getObjectParameters(valid_params, object_type, obj_act_tasks);
     358        1150 : }
     359             : 
     360             : void
     361        1150 : MooseServer::getActionParameters(InputParameters & valid_params,
     362             :                                  const std::string & object_path,
     363             :                                  std::set<std::string> & obj_act_tasks)
     364             : {
     365        1150 :   Syntax & syntax = getCheckApp()->syntax();
     366        1150 :   ActionFactory & action_factory = getCheckApp()->getActionFactory();
     367             : 
     368             :   // get registered syntax path identifier using actual object context path
     369             :   bool is_parent;
     370        1150 :   std::string registered_syntax = syntax.isAssociated(object_path, &is_parent);
     371             : 
     372             :   // use is_parent to skip action parameters when not explicitly registered
     373        1150 :   if (!is_parent)
     374             :   {
     375             :     // get action objects associated with registered syntax path identifier
     376        1101 :     auto action_range = syntax.getActions(registered_syntax);
     377             : 
     378             :     // traverse action objects for syntax to gather valid action parameters
     379        2222 :     for (auto action_iter = action_range.first; action_iter != action_range.second; action_iter++)
     380             :     {
     381        1121 :       const std::string & action_name = action_iter->second._action;
     382             : 
     383             :       // use action name to get set of valid parameters from action factory
     384        1121 :       InputParameters action_params = action_factory.getValidParams(action_name);
     385             : 
     386             :       // gather all MooseObjectAction tasks for verifying object parameters
     387        1121 :       if (action_params.have_parameter<bool>("isObjectAction"))
     388             :       {
     389        1088 :         if (action_params.get<bool>("isObjectAction"))
     390             :         {
     391        1088 :           std::set<std::string> tasks_by_actions = action_factory.getTasksByAction(action_name);
     392        1088 :           obj_act_tasks.insert(tasks_by_actions.begin(), tasks_by_actions.end());
     393        1088 :         }
     394             : 
     395             :         // filter parameter from completion list as it is not used in input
     396        1088 :         action_params.remove("isObjectAction");
     397             :       }
     398             : 
     399             :       // add parameters from action to full valid collection being gathered
     400        1121 :       valid_params += action_params;
     401        1121 :     }
     402             :   }
     403        1150 : }
     404             : 
     405             : void
     406        1150 : MooseServer::getObjectParameters(InputParameters & valid_params,
     407             :                                  std::string object_type,
     408             :                                  const std::set<std::string> & obj_act_tasks)
     409             : {
     410        1150 :   Syntax & syntax = getCheckApp()->syntax();
     411        1150 :   Factory & factory = getCheckApp()->getFactory();
     412             : 
     413             :   // use type parameter default if it exists and is not provided from input
     414        1165 :   if (object_type.empty() && valid_params.have_parameter<std::string>("type") &&
     415        1165 :       !valid_params.get<std::string>("type").empty())
     416             :   {
     417           5 :     object_type = valid_params.get<std::string>("type");
     418             : 
     419             :     // make type parameter not required in input since it has default value
     420           5 :     valid_params.makeParamNotRequired("type");
     421             :   }
     422             : 
     423             :   // check if object type has been registered to prevent unregistered error
     424        1150 :   if (factory.isRegistered(object_type))
     425             :   {
     426             :     // use object type to get set of valid parameters registered in factory
     427        1079 :     InputParameters object_params = factory.getValidParams(object_type);
     428             : 
     429             :     // check if object has base associated with any MooseObjectAction tasks
     430        1079 :     if (object_params.have_parameter<std::string>("_moose_base"))
     431             :     {
     432        1079 :       const std::string & moose_base = object_params.get<std::string>("_moose_base");
     433             : 
     434        2128 :       for (const auto & obj_act_task : obj_act_tasks)
     435             :       {
     436        1081 :         if (syntax.verifyMooseObjectTask(moose_base, obj_act_task))
     437             :         {
     438             :           // add parameters from object to valid collection if base matches
     439          32 :           valid_params += object_params;
     440          32 :           break;
     441             :         }
     442             :       }
     443             :     }
     444        1079 :   }
     445             : 
     446             :   // make parameters from list of those set by action not required in input
     447        1150 :   if (valid_params.have_parameter<std::vector<std::string>>("_object_params_set_by_action"))
     448             :   {
     449           6 :     auto names = valid_params.get<std::vector<std::string>>("_object_params_set_by_action");
     450          12 :     for (const auto & name : names)
     451           6 :       valid_params.makeParamNotRequired(name);
     452             : 
     453             :     // filter parameter from completion list since it is not used for input
     454           6 :     valid_params.remove("_object_params_set_by_action");
     455           6 :   }
     456        1150 : }
     457             : 
     458             : bool
     459           4 : MooseServer::addParametersToList(wasp::DataArray & completionItems,
     460             :                                  const InputParameters & valid_params,
     461             :                                  const std::set<std::string> & existing_params,
     462             :                                  int replace_line_beg,
     463             :                                  int replace_char_beg,
     464             :                                  int replace_line_end,
     465             :                                  int replace_char_end,
     466             :                                  const std::string & filtering_prefix)
     467             : {
     468           4 :   bool pass = true;
     469             : 
     470             :   // walk over collection of all valid parameters and build completion list
     471         264 :   for (const auto & valid_params_iter : valid_params)
     472             :   {
     473         260 :     const std::string & param_name = valid_params_iter.first;
     474         260 :     bool deprecated = valid_params.isParamDeprecated(param_name);
     475         260 :     bool is_private = valid_params.isPrivate(param_name);
     476             : 
     477             :     // filter out parameters that are deprecated, private, or already exist
     478         260 :     if (deprecated || is_private || existing_params.count(param_name))
     479         202 :       continue;
     480             : 
     481             :     // filter out parameters that do not begin with prefix if one was given
     482         176 :     if (param_name.rfind(filtering_prefix, 0) != 0)
     483         118 :       continue;
     484             : 
     485             :     // process parameter description and type to use in input default value
     486          58 :     std::string dirty_type = valid_params.type(param_name);
     487          58 :     std::string clean_type = MooseUtils::prettyCppType(dirty_type);
     488          58 :     std::string basic_type = JsonSyntaxTree::basicCppType(clean_type);
     489          58 :     std::string doc_string = valid_params.getDocString(param_name);
     490          58 :     MooseUtils::escape(doc_string);
     491             : 
     492             :     // use basic type to decide if parameter is array and quotes are needed
     493          58 :     bool is_array = basic_type.compare(0, 6, "Array:") == 0;
     494             : 
     495             :     // remove any array prefixes from basic type string and leave base type
     496          58 :     pcrecpp::RE("(Array:)*(.*)").GlobalReplace("\\2", &basic_type);
     497             : 
     498             :     // prepare clean cpp type string to be used for key to find input paths
     499          58 :     pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
     500             : 
     501             :     // decide completion item kind that client may use to display list icon
     502          58 :     int complete_kind = getCompletionItemKind(valid_params, param_name, clean_type, true);
     503             : 
     504             :     // default value for completion to be built using parameter information
     505          58 :     std::string default_value;
     506             : 
     507             :     // first if parameter default is set then use it to build default value
     508          58 :     if (valid_params.isParamValid(param_name))
     509             :     {
     510          32 :       default_value = JsonSyntaxTree::buildOutputString(valid_params_iter);
     511          32 :       default_value = MooseUtils::trim(default_value);
     512             :     }
     513             : 
     514             :     // otherwise if parameter has coupled default then use as default value
     515          26 :     else if (valid_params.hasDefaultCoupledValue(param_name))
     516             :     {
     517           0 :       std::ostringstream oss;
     518           0 :       oss << valid_params.defaultCoupledValue(param_name);
     519           0 :       default_value = oss.str();
     520           0 :     }
     521             : 
     522             :     // switch 1 to true or 0 to false if boolean parameter as default value
     523          58 :     if (basic_type == "Boolean" && default_value == "1")
     524           5 :       default_value = "true";
     525          53 :     else if (basic_type == "Boolean" && default_value == "0")
     526          10 :       default_value = "false";
     527             : 
     528             :     // wrap default value with single quotes if it exists and type is array
     529          58 :     std::string array_quote = is_array && !default_value.empty() ? "'" : "";
     530             : 
     531             :     // choose format of insertion text based on if client supports snippets
     532             :     int text_format;
     533          58 :     std::string insert_text;
     534          58 :     if (client_snippet_support && !default_value.empty())
     535             :     {
     536          25 :       text_format = wasp::lsp::m_text_format_snippet;
     537          25 :       insert_text = param_name + " = " + array_quote + "${1:" + default_value + "}" + array_quote;
     538             :     }
     539             :     else
     540             :     {
     541          33 :       text_format = wasp::lsp::m_text_format_plaintext;
     542          33 :       insert_text = param_name + " = " + array_quote + default_value + array_quote;
     543             :     }
     544             :     // finally build full insertion from parameter name, quote, and default
     545             : 
     546             :     // add parameter label, insert text, and description to completion list
     547          58 :     completionItems.push_back(wasp::DataObject());
     548          58 :     wasp::DataObject * item = completionItems.back().to_object();
     549          58 :     pass &= wasp::lsp::buildCompletionObject(*item,
     550             :                                              errors,
     551             :                                              param_name,
     552             :                                              replace_line_beg,
     553             :                                              replace_char_beg,
     554             :                                              replace_line_end,
     555             :                                              replace_char_end,
     556             :                                              insert_text,
     557             :                                              complete_kind,
     558             :                                              "",
     559             :                                              doc_string,
     560             :                                              false,
     561             :                                              false,
     562             :                                              text_format);
     563          58 :   }
     564             : 
     565           4 :   return pass;
     566             : }
     567             : 
     568             : bool
     569           6 : MooseServer::addSubblocksToList(wasp::DataArray & completionItems,
     570             :                                 const std::string & object_path,
     571             :                                 int replace_line_beg,
     572             :                                 int replace_char_beg,
     573             :                                 int replace_line_end,
     574             :                                 int replace_char_end,
     575             :                                 const std::string & filtering_prefix,
     576             :                                 bool request_on_block_decl)
     577             : {
     578           6 :   Syntax & syntax = getCheckApp()->syntax();
     579             : 
     580             :   // set used to prevent reprocessing syntax paths for more than one action
     581           6 :   std::set<std::string> syntax_paths_processed;
     582             : 
     583             :   // build map of all syntax paths to names for subblocks and save to reuse
     584           6 :   if (_syntax_to_subblocks.empty())
     585             :   {
     586         107 :     for (const auto & syntax_path_iter : syntax.getAssociatedActions())
     587             :     {
     588         106 :       std::string syntax_path = "/" + syntax_path_iter.first;
     589             : 
     590             :       // skip current syntax path if already processed for different action
     591         106 :       if (!syntax_paths_processed.insert(syntax_path).second)
     592          15 :         continue;
     593             : 
     594             :       // walk backward through syntax path adding subblock names to parents
     595         275 :       for (std::size_t last_sep; (last_sep = syntax_path.find_last_of("/")) != std::string::npos;)
     596             :       {
     597         184 :         std::string subblock_name = syntax_path.substr(last_sep + 1);
     598         184 :         syntax_path = syntax_path.substr(0, last_sep);
     599         184 :         _syntax_to_subblocks[syntax_path].insert(subblock_name);
     600         184 :       }
     601         106 :     }
     602             :   }
     603             : 
     604             :   // get registered syntax from object path using map of paths to subblocks
     605           6 :   std::string registered_syntax = syntax.isAssociated(object_path, nullptr, _syntax_to_subblocks);
     606             : 
     607           6 :   bool pass = true;
     608             : 
     609             :   // walk over subblock names if found or at root and build completion list
     610           6 :   if (!registered_syntax.empty() || object_path == "/")
     611             :   {
     612             :     // choose format of insertion text based on if client supports snippets
     613           5 :     int text_format = client_snippet_support ? wasp::lsp::m_text_format_snippet
     614             :                                              : wasp::lsp::m_text_format_plaintext;
     615             : 
     616          81 :     for (const auto & subblock_name : _syntax_to_subblocks[registered_syntax])
     617             :     {
     618             :       // filter subblock if it does not begin with prefix and one was given
     619          76 :       if (subblock_name != "*" && subblock_name.rfind(filtering_prefix, 0) != 0)
     620           5 :         continue;
     621             : 
     622          71 :       std::string doc_string;
     623          71 :       std::string insert_text;
     624             :       int complete_kind;
     625             : 
     626             :       // build required parameter list for each block to use in insert text
     627          71 :       const std::string full_block_path = object_path + "/" + subblock_name;
     628          71 :       const std::string req_params = getRequiredParamsText(full_block_path, "", {}, "  ");
     629             : 
     630             :       // customize description and insert text for star and named subblocks
     631          71 :       if (subblock_name == "*")
     632             :       {
     633           2 :         doc_string = "custom user named block";
     634           4 :         insert_text = (request_on_block_decl ? "" : "[") +
     635           8 :                       (filtering_prefix.size() ? filtering_prefix : "block_name") + "]" +
     636           4 :                       req_params + "\n  " + (client_snippet_support ? "$0" : "") + "\n[]";
     637           2 :         complete_kind = wasp::lsp::m_comp_kind_variable;
     638             :       }
     639             :       else
     640             :       {
     641          69 :         doc_string = "application named block";
     642         138 :         insert_text = (request_on_block_decl ? "" : "[") + subblock_name + "]" + req_params +
     643         138 :                       "\n  " + (client_snippet_support ? "$0" : "") + "\n[]";
     644          69 :         complete_kind = wasp::lsp::m_comp_kind_struct;
     645             :       }
     646             : 
     647             :       // add subblock name, insert text, and description to completion list
     648          71 :       completionItems.push_back(wasp::DataObject());
     649          71 :       wasp::DataObject * item = completionItems.back().to_object();
     650          71 :       pass &= wasp::lsp::buildCompletionObject(*item,
     651             :                                                errors,
     652             :                                                subblock_name,
     653             :                                                replace_line_beg,
     654             :                                                replace_char_beg,
     655             :                                                replace_line_end,
     656             :                                                replace_char_end,
     657             :                                                insert_text,
     658             :                                                complete_kind,
     659             :                                                "",
     660             :                                                doc_string,
     661             :                                                false,
     662             :                                                false,
     663             :                                                text_format);
     664          71 :     }
     665             :   }
     666             : 
     667           6 :   return pass;
     668           6 : }
     669             : 
     670             : bool
     671           7 : MooseServer::addValuesToList(wasp::DataArray & completionItems,
     672             :                              const InputParameters & valid_params,
     673             :                              const std::set<std::string> & existing_params,
     674             :                              const std::set<std::string> & existing_subblocks,
     675             :                              const std::string & param_name,
     676             :                              const std::set<std::string> & obj_act_tasks,
     677             :                              const std::string & object_path,
     678             :                              int replace_line_beg,
     679             :                              int replace_char_beg,
     680             :                              int replace_line_end,
     681             :                              int replace_char_end)
     682             : {
     683           7 :   Syntax & syntax = getCheckApp()->syntax();
     684           7 :   Factory & factory = getCheckApp()->getFactory();
     685             : 
     686             :   // get clean type for path associations and basic type for boolean values
     687           7 :   std::string dirty_type = valid_params.type(param_name);
     688           7 :   std::string clean_type = MooseUtils::prettyCppType(dirty_type);
     689           7 :   std::string basic_type = JsonSyntaxTree::basicCppType(clean_type);
     690             : 
     691             :   // remove any array prefixes from basic type string and replace with base
     692           7 :   pcrecpp::RE("(Array:)*(.*)").GlobalReplace("\\2", &basic_type);
     693             : 
     694             :   // prepare clean cpp type string to be used for a key to find input paths
     695           7 :   pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
     696             : 
     697             :   // decide completion item kind that client may use to display a list icon
     698           7 :   int complete_kind = getCompletionItemKind(valid_params, param_name, clean_type, false);
     699             : 
     700             :   // map used to gather options and descriptions for value completion items
     701           7 :   std::map<std::string, std::string> options_and_descs;
     702             : 
     703             :   // first if parameter name is active or inactive then use input subblocks
     704           7 :   if (param_name == "active" || param_name == "inactive")
     705           3 :     for (const auto & subblock_name : existing_subblocks)
     706           2 :       options_and_descs[subblock_name] = "subblock name";
     707             : 
     708             :   // otherwise if parameter type is boolean then use true and false strings
     709           6 :   else if (basic_type == "Boolean")
     710             :   {
     711           1 :     options_and_descs["true"];
     712           1 :     options_and_descs["false"];
     713             :   }
     714             : 
     715             :   // otherwise if parameter type is one of the enums then use valid options
     716           5 :   else if (valid_params.have_parameter<MooseEnum>(param_name))
     717           2 :     getEnumsAndDocs(valid_params.get<MooseEnum>(param_name), options_and_descs);
     718           3 :   else if (valid_params.have_parameter<MultiMooseEnum>(param_name))
     719           0 :     getEnumsAndDocs(valid_params.get<MultiMooseEnum>(param_name), options_and_descs);
     720           3 :   else if (valid_params.have_parameter<ExecFlagEnum>(param_name))
     721           0 :     getEnumsAndDocs(valid_params.get<ExecFlagEnum>(param_name), options_and_descs);
     722           3 :   else if (valid_params.have_parameter<std::vector<MooseEnum>>(param_name))
     723           0 :     getEnumsAndDocs(valid_params.get<std::vector<MooseEnum>>(param_name)[0], options_and_descs);
     724             : 
     725             :   // otherwise if parameter name is type then use all verified object names
     726           3 :   else if (param_name == "type")
     727             :   {
     728             :     // walk over entire set of objects that have been registered in factory
     729        1055 :     for (const auto & objects_iter : factory.registeredObjects())
     730             :     {
     731        1054 :       const std::string & object_name = objects_iter.first;
     732        1054 :       const InputParameters & object_params = objects_iter.second->buildParameters();
     733             : 
     734             :       // build required parameter list for each block to use in insert text
     735        1054 :       std::string req_params = getRequiredParamsText(object_path, object_name, existing_params, "");
     736        1054 :       req_params += !req_params.empty() ? "\n" : "";
     737             : 
     738             :       // check if object has registered base parameter that can be verified
     739        1054 :       if (!object_params.have_parameter<std::string>("_moose_base"))
     740           0 :         continue;
     741        1054 :       const std::string & moose_base = object_params.get<std::string>("_moose_base");
     742             : 
     743             :       // walk over gathered MooseObjectAction tasks and add if base matches
     744        2101 :       for (const auto & obj_act_task : obj_act_tasks)
     745             :       {
     746        1054 :         if (!syntax.verifyMooseObjectTask(moose_base, obj_act_task))
     747        1047 :           continue;
     748           7 :         std::string type_description = object_params.getClassDescription();
     749           7 :         MooseUtils::escape(type_description);
     750           7 :         options_and_descs[object_name + req_params] = type_description;
     751           7 :         break;
     752           7 :       }
     753        1054 :     }
     754             :   }
     755             : 
     756             :   // otherwise if parameter type has any associated syntax then use lookups
     757             :   else
     758             :   {
     759             :     // build map of parameter types to input lookup paths and save to reuse
     760           2 :     if (_type_to_input_paths.empty())
     761             :     {
     762          27 :       for (const auto & associated_types_iter : syntax.getAssociatedTypes())
     763             :       {
     764          26 :         const std::string & type = associated_types_iter.second;
     765          26 :         const std::string & path = associated_types_iter.first;
     766          26 :         _type_to_input_paths[type].insert(path);
     767             :       }
     768             :     }
     769             : 
     770             :     // check for input lookup paths that are associated with parameter type
     771           2 :     const auto & input_path_iter = _type_to_input_paths.find(clean_type);
     772             : 
     773           2 :     if (input_path_iter != _type_to_input_paths.end())
     774             :     {
     775           2 :       wasp::HITNodeView view_root = getRoot().getNodeView();
     776             : 
     777             :       // walk over all syntax paths that are associated with parameter type
     778           5 :       for (const auto & input_path : input_path_iter->second)
     779             :       {
     780             :         // use wasp siren to gather all input values at current lookup path
     781           3 :         wasp::SIRENInterpreter<> selector;
     782           3 :         if (!selector.parseString(input_path))
     783           0 :           continue;
     784           3 :         wasp::SIRENResultSet<wasp::HITNodeView> results;
     785           3 :         std::size_t count = selector.evaluate(view_root, results);
     786             : 
     787             :         // walk over results and add each input value found at current path
     788          24 :         for (std::size_t i = 0; i < count; i++)
     789          21 :           if (results.adapted(i).type() == wasp::OBJECT)
     790           8 :             options_and_descs[results.adapted(i).name()] = "from /" + input_path;
     791           3 :       }
     792           2 :     }
     793             :   }
     794             : 
     795           7 :   bool pass = true;
     796             : 
     797             :   // walk over pairs of options with descriptions and build completion list
     798          33 :   for (const auto & option_and_desc : options_and_descs)
     799             :   {
     800          26 :     const std::string & insert_text = option_and_desc.first;
     801          26 :     const std::string & option_name = insert_text.substr(0, insert_text.find('\n'));
     802          26 :     const std::string & description = option_and_desc.second;
     803             : 
     804             :     // add option name, insertion range, and description to completion list
     805          26 :     completionItems.push_back(wasp::DataObject());
     806          26 :     wasp::DataObject * item = completionItems.back().to_object();
     807          26 :     pass &= wasp::lsp::buildCompletionObject(*item,
     808             :                                              errors,
     809             :                                              option_name,
     810             :                                              replace_line_beg,
     811             :                                              replace_char_beg,
     812             :                                              replace_line_end,
     813             :                                              replace_char_end,
     814             :                                              insert_text,
     815             :                                              complete_kind,
     816             :                                              "",
     817             :                                              description,
     818             :                                              false,
     819             :                                              false,
     820             :                                              wasp::lsp::m_text_format_plaintext);
     821          26 :   }
     822             : 
     823           7 :   return pass;
     824           7 : }
     825             : 
     826             : template <typename MooseEnumType>
     827             : void
     828           5 : MooseServer::getEnumsAndDocs(MooseEnumType & moose_enum_param,
     829             :                              std::map<std::string, std::string> & options_and_descs)
     830             : {
     831             :   // get map that contains any documentation strings provided for each item
     832           5 :   const auto & enum_docs = moose_enum_param.getItemDocumentation();
     833             : 
     834             :   // walk over enums filling map with options and any provided descriptions
     835          41 :   for (const auto & item : moose_enum_param.items())
     836          36 :     options_and_descs[item.name()] = enum_docs.count(item) ? enum_docs.at(item) : "";
     837           5 : }
     838             : 
     839             : bool
     840           2 : MooseServer::gatherDocumentDefinitionLocations(wasp::DataArray & definitionLocations,
     841             :                                                int line,
     842             :                                                int character)
     843             : {
     844           2 :   Factory & factory = getCheckApp()->getFactory();
     845             : 
     846             :   // return without any definition locations added when parser root is null
     847           2 :   if (!rootIsValid())
     848           0 :     return true;
     849             : 
     850             :   // find hit node for zero based request line and column number from input
     851           2 :   wasp::HITNodeView view_root = getRoot().getNodeView();
     852             :   wasp::HITNodeView request_context =
     853           2 :       wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
     854             : 
     855             :   // return without any definition locations added when node not value type
     856           2 :   if (request_context.type() != wasp::VALUE)
     857           0 :     return true;
     858             : 
     859             :   // get name of parameter node parent of value and value string from input
     860           2 :   std::string param_name = request_context.has_parent() ? request_context.parent().name() : "";
     861           2 :   std::string val_string = request_context.last_as_string();
     862             : 
     863             :   // add source code location if type parameter with registered object name
     864           2 :   if (param_name == "type" && factory.isRegistered(val_string))
     865             :   {
     866             :     // get file path and line number of source code registering object type
     867           1 :     FileLineInfo file_line_info = factory.getLineInfo(val_string);
     868             : 
     869             :     // return without any definition locations added if file cannot be read
     870           2 :     if (!file_line_info.isValid() ||
     871           2 :         !MooseUtils::checkFileReadable(file_line_info.file(), false, false, false))
     872           0 :       return true;
     873             : 
     874             :     // add file scheme prefix to front of file path to build definition uri
     875           1 :     auto location_uri = wasp::lsp::m_uri_prefix + file_line_info.file();
     876             : 
     877             :     // add file uri and zero based line and column range to definition list
     878           1 :     definitionLocations.push_back(wasp::DataObject());
     879           1 :     wasp::DataObject * location = definitionLocations.back().to_object();
     880           3 :     return wasp::lsp::buildLocationObject(*location,
     881             :                                           errors,
     882             :                                           location_uri,
     883           1 :                                           file_line_info.line() - 1,
     884             :                                           0,
     885           1 :                                           file_line_info.line() - 1,
     886           1 :                                           1000);
     887           1 :   }
     888             : 
     889             :   // get object context and value of type parameter for request if provided
     890           1 :   wasp::HITNodeView object_context = request_context;
     891           3 :   while (object_context.type() != wasp::OBJECT && object_context.has_parent())
     892           2 :     object_context = object_context.parent();
     893           1 :   const std::string & object_path = object_context.path();
     894           1 :   wasp::HITNodeView type_node = object_context.first_child_by_name("type");
     895             :   const std::string & object_type =
     896           1 :       type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
     897             : 
     898             :   // set used to gather all parameters valid from object context of request
     899           1 :   InputParameters valid_params = emptyInputParameters();
     900             : 
     901             :   // set used to gather MooseObjectAction tasks to verify object parameters
     902           1 :   std::set<std::string> obj_act_tasks;
     903             : 
     904             :   // get set of global parameters, action parameters, and object parameters
     905           1 :   getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
     906             : 
     907             :   // set used to gather nodes from input lookups custom sorted by locations
     908             :   SortedLocationNodes location_nodes(
     909           0 :       [](const wasp::HITNodeView & l, const wasp::HITNodeView & r)
     910             :       {
     911           7 :         const std::string & l_file = l.node_pool()->stream_name();
     912           7 :         const std::string & r_file = r.node_pool()->stream_name();
     913          17 :         return (l_file < r_file || (l_file == r_file && l.line() < r.line()) ||
     914          17 :                 (l_file == r_file && l.line() == r.line() && l.column() < r.column()));
     915           1 :       });
     916             : 
     917             :   // gather all lookup path nodes matching value if parameter name is valid
     918          28 :   for (const auto & valid_params_iter : valid_params)
     919             :   {
     920          28 :     if (valid_params_iter.first == param_name)
     921             :     {
     922             :       // get cpp type and prepare string for use as key finding input paths
     923           1 :       std::string dirty_type = valid_params.type(param_name);
     924           1 :       std::string clean_type = MooseUtils::prettyCppType(dirty_type);
     925           1 :       pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
     926             : 
     927             :       // get set of nodes from associated path lookups matching input value
     928           1 :       getInputLookupDefinitionNodes(location_nodes, clean_type, val_string);
     929           1 :       break;
     930           1 :     }
     931             :   }
     932             : 
     933             :   // add parameter declarator to set if none were gathered by input lookups
     934           1 :   if (location_nodes.empty() && request_context.has_parent() &&
     935           1 :       request_context.parent().child_count_by_name("decl"))
     936           0 :     location_nodes.insert(request_context.parent().first_child_by_name("decl"));
     937             : 
     938             :   // add locations to definition list using lookups or parameter declarator
     939           1 :   return addLocationNodesToList(definitionLocations, location_nodes);
     940           2 : }
     941             : 
     942             : void
     943           1 : MooseServer::getInputLookupDefinitionNodes(SortedLocationNodes & location_nodes,
     944             :                                            const std::string & clean_type,
     945             :                                            const std::string & val_string)
     946             : {
     947           1 :   Syntax & syntax = getCheckApp()->syntax();
     948             : 
     949             :   // build map from parameter types to input lookup paths and save to reuse
     950           1 :   if (_type_to_input_paths.empty())
     951             :   {
     952           0 :     for (const auto & associated_types_iter : syntax.getAssociatedTypes())
     953             :     {
     954           0 :       const std::string & type = associated_types_iter.second;
     955           0 :       const std::string & path = associated_types_iter.first;
     956           0 :       _type_to_input_paths[type].insert(path);
     957             :     }
     958             :   }
     959             : 
     960             :   // find set of input lookup paths that are associated with parameter type
     961           1 :   const auto & input_path_iter = _type_to_input_paths.find(clean_type);
     962             : 
     963             :   // return without any definition locations added when no paths associated
     964           1 :   if (input_path_iter == _type_to_input_paths.end())
     965           0 :     return;
     966             : 
     967             :   // get root node from input to use in input lookups with associated paths
     968           1 :   wasp::HITNodeView view_root = getRoot().getNodeView();
     969             : 
     970             :   // walk over all syntax paths that are associated with parameter type
     971           3 :   for (const auto & input_path : input_path_iter->second)
     972             :   {
     973             :     // use wasp siren to gather all nodes from current lookup path in input
     974           2 :     wasp::SIRENInterpreter<> selector;
     975           2 :     if (!selector.parseString(input_path))
     976           0 :       continue;
     977           2 :     wasp::SIRENResultSet<wasp::HITNodeView> results;
     978           2 :     std::size_t count = selector.evaluate(view_root, results);
     979             : 
     980             :     // walk over results and add nodes that have name matching value to set
     981          17 :     for (std::size_t i = 0; i < count; i++)
     982          18 :       if (results.adapted(i).type() == wasp::OBJECT && results.adapted(i).name() == val_string &&
     983          18 :           results.adapted(i).child_count_by_name("decl"))
     984           3 :         location_nodes.insert(results.adapted(i).first_child_by_name("decl"));
     985           2 :   }
     986           1 : }
     987             : 
     988             : bool
     989           2 : MooseServer::addLocationNodesToList(wasp::DataArray & defsOrRefsLocations,
     990             :                                     const SortedLocationNodes & location_nodes)
     991             : {
     992           2 :   bool pass = true;
     993             : 
     994             :   // walk over set of sorted nodes provided to add and build locations list
     995          11 :   for (const auto & location_nodes_iter : location_nodes)
     996             :   {
     997             :     // add file scheme prefix onto front of file path to build location uri
     998           9 :     auto location_uri = wasp::lsp::m_uri_prefix + location_nodes_iter.node_pool()->stream_name();
     999             : 
    1000             :     // add file uri with zero based line and column range to locations list
    1001           9 :     defsOrRefsLocations.push_back(wasp::DataObject());
    1002           9 :     wasp::DataObject * location = defsOrRefsLocations.back().to_object();
    1003          45 :     pass &= wasp::lsp::buildLocationObject(*location,
    1004             :                                            errors,
    1005             :                                            location_uri,
    1006           9 :                                            location_nodes_iter.line() - 1,
    1007           9 :                                            location_nodes_iter.column() - 1,
    1008           9 :                                            location_nodes_iter.last_line() - 1,
    1009           9 :                                            location_nodes_iter.last_column());
    1010           9 :   }
    1011             : 
    1012           2 :   return pass;
    1013             : }
    1014             : 
    1015             : bool
    1016           6 : MooseServer::getHoverDisplayText(std::string & display_text, int line, int character)
    1017             : {
    1018           6 :   Factory & factory = getCheckApp()->getFactory();
    1019           6 :   Syntax & syntax = getCheckApp()->syntax();
    1020             : 
    1021             :   // return and leave display text as empty string when parser root is null
    1022           6 :   if (!rootIsValid())
    1023           0 :     return true;
    1024             : 
    1025             :   // find hit node for zero based request line and column number from input
    1026           6 :   wasp::HITNodeView view_root = getRoot().getNodeView();
    1027             :   wasp::HITNodeView request_context =
    1028           6 :       wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
    1029             : 
    1030             :   // return and leave display text as empty string when not on key or value
    1031           4 :   if ((request_context.type() != wasp::DECL && request_context.type() != wasp::VALUE) ||
    1032          16 :       !request_context.has_parent() ||
    1033          12 :       (request_context.parent().type() != wasp::KEYED_VALUE &&
    1034           8 :        request_context.parent().type() != wasp::ARRAY))
    1035           1 :     return true;
    1036             : 
    1037             :   // get name of parameter node and value string that is specified in input
    1038           5 :   std::string paramkey = request_context.parent().name();
    1039           5 :   std::string paramval = request_context.last_as_string();
    1040             : 
    1041             :   // get object context path and object type value for request if it exists
    1042           5 :   wasp::HITNodeView object_context = request_context;
    1043          15 :   while (object_context.type() != wasp::OBJECT && object_context.has_parent())
    1044          10 :     object_context = object_context.parent();
    1045           5 :   const std::string object_path = object_context.path();
    1046           5 :   wasp::HITNodeView type_node = object_context.first_child_by_name("type");
    1047             :   const std::string object_type =
    1048           5 :       type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
    1049             : 
    1050             :   // gather global, action, and object parameters in request object context
    1051           5 :   InputParameters valid_params = emptyInputParameters();
    1052           5 :   std::set<std::string> obj_act_tasks;
    1053           5 :   getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
    1054             : 
    1055             :   // use class description as display text when request is valid type value
    1056           5 :   if (request_context.type() == wasp::VALUE && paramkey == "type" && factory.isRegistered(paramval))
    1057             :   {
    1058           1 :     const InputParameters & object_params = factory.getValidParams(paramval);
    1059           1 :     if (object_params.have_parameter<std::string>("_moose_base"))
    1060             :     {
    1061           1 :       const std::string moose_base = object_params.get<std::string>("_moose_base");
    1062           1 :       for (const auto & obj_act_task : obj_act_tasks)
    1063             :       {
    1064           1 :         if (syntax.verifyMooseObjectTask(moose_base, obj_act_task))
    1065             :         {
    1066           1 :           display_text = object_params.getClassDescription();
    1067           1 :           break;
    1068             :         }
    1069             :       }
    1070           1 :     }
    1071           1 :   }
    1072             : 
    1073             :   // use item documentation as display text when request is enum type value
    1074           4 :   else if (request_context.type() == wasp::VALUE)
    1075             :   {
    1076           3 :     std::map<std::string, std::string> options_and_descs;
    1077           3 :     if (valid_params.have_parameter<MooseEnum>(paramkey))
    1078           1 :       getEnumsAndDocs(valid_params.get<MooseEnum>(paramkey), options_and_descs);
    1079           2 :     else if (valid_params.have_parameter<MultiMooseEnum>(paramkey))
    1080           1 :       getEnumsAndDocs(valid_params.get<MultiMooseEnum>(paramkey), options_and_descs);
    1081           1 :     else if (valid_params.have_parameter<ExecFlagEnum>(paramkey))
    1082           1 :       getEnumsAndDocs(valid_params.get<ExecFlagEnum>(paramkey), options_and_descs);
    1083           0 :     else if (valid_params.have_parameter<std::vector<MooseEnum>>(paramkey))
    1084           0 :       getEnumsAndDocs(valid_params.get<std::vector<MooseEnum>>(paramkey)[0], options_and_descs);
    1085           3 :     if (options_and_descs.count(paramval))
    1086           3 :       display_text = options_and_descs.find(paramval)->second;
    1087           3 :   }
    1088             : 
    1089             :   // use parameter documentation as display text when request is valid name
    1090           1 :   else if (request_context.type() == wasp::DECL && valid_params.getParametersList().count(paramkey))
    1091           1 :     display_text = valid_params.getDocString(paramkey);
    1092             : 
    1093           5 :   MooseUtils::escape(display_text);
    1094           5 :   return true;
    1095           6 : }
    1096             : 
    1097             : bool
    1098           1 : MooseServer::gatherDocumentReferencesLocations(wasp::DataArray & referencesLocations,
    1099             :                                                int line,
    1100             :                                                int character,
    1101             :                                                bool include_declaration)
    1102             : {
    1103           1 :   Syntax & syntax = getCheckApp()->syntax();
    1104             : 
    1105             :   // return without adding any reference locations when parser root is null
    1106           1 :   if (!rootIsValid())
    1107           0 :     return true;
    1108             : 
    1109             :   // find hit node for zero based request line and column number from input
    1110           1 :   wasp::HITNodeView view_root = getRoot().getNodeView();
    1111             :   wasp::HITNodeView request_context =
    1112           1 :       wasp::findNodeUnderLineColumn(view_root, line + 1, character + 1);
    1113             : 
    1114             :   // return without adding any references when request not block declarator
    1115           1 :   if ((request_context.type() != wasp::DECL && request_context.type() != wasp::DOT_SLASH &&
    1116           1 :        request_context.type() != wasp::LBRACKET && request_context.type() != wasp::RBRACKET) ||
    1117           2 :       !request_context.has_parent() || request_context.parent().type() != wasp::OBJECT)
    1118           0 :     return true;
    1119             : 
    1120             :   // get input path and block name of declarator located at request context
    1121           1 :   const std::string & block_path = request_context.parent().path();
    1122           1 :   const std::string & block_name = request_context.parent().name();
    1123             : 
    1124             :   // build map from input lookup paths to parameter types and save to reuse
    1125           1 :   if (_input_path_to_types.empty())
    1126          27 :     for (const auto & associated_types_iter : syntax.getAssociatedTypes())
    1127             :     {
    1128          26 :       const std::string & path = associated_types_iter.first;
    1129          26 :       const std::string & type = associated_types_iter.second;
    1130          26 :       _input_path_to_types[path].insert(type);
    1131             :     }
    1132             : 
    1133             :   // get registered syntax from block path with map of input paths to types
    1134             :   bool is_parent;
    1135           1 :   std::string registered_syntax = syntax.isAssociated(block_path, &is_parent, _input_path_to_types);
    1136             : 
    1137             :   // return without adding any references if syntax has no types associated
    1138           1 :   if (is_parent || !_input_path_to_types.count(registered_syntax))
    1139           0 :     return true;
    1140             : 
    1141             :   // get set of parameter types which are associated with registered syntax
    1142           1 :   const std::set<std::string> & target_types = _input_path_to_types.at(registered_syntax);
    1143             : 
    1144             :   // set used to gather nodes collected by value custom sorted by locations
    1145             :   SortedLocationNodes match_nodes(
    1146           0 :       [](const wasp::HITNodeView & l, const wasp::HITNodeView & r)
    1147             :       {
    1148          19 :         const std::string & l_file = l.node_pool()->stream_name();
    1149          19 :         const std::string & r_file = r.node_pool()->stream_name();
    1150          47 :         return (l_file < r_file || (l_file == r_file && l.line() < r.line()) ||
    1151          47 :                 (l_file == r_file && l.line() == r.line() && l.column() < r.column()));
    1152           1 :       });
    1153             : 
    1154             :   // walk input recursively and gather all nodes that match value and types
    1155           1 :   getNodesByValueAndTypes(match_nodes, view_root, block_name, target_types);
    1156             : 
    1157             :   // return without adding any references if no nodes match value and types
    1158           1 :   if (match_nodes.empty())
    1159           0 :     return true;
    1160             : 
    1161             :   // add request context node to set if declaration inclusion was specified
    1162           1 :   if (include_declaration && request_context.parent().child_count_by_name("decl"))
    1163           1 :     match_nodes.insert(request_context.parent().first_child_by_name("decl"));
    1164             : 
    1165             :   // add locations to references list with nodes that match value and types
    1166           1 :   return addLocationNodesToList(referencesLocations, match_nodes);
    1167           1 : }
    1168             : 
    1169             : void
    1170          26 : MooseServer::getNodesByValueAndTypes(SortedLocationNodes & match_nodes,
    1171             :                                      wasp::HITNodeView view_parent,
    1172             :                                      const std::string & target_value,
    1173             :                                      const std::set<std::string> & target_types)
    1174             : {
    1175             :   // walk over children of context to gather nodes matching value and types
    1176         260 :   for (const auto & view_child : view_parent)
    1177             :   {
    1178             :     // check for parameter type match if node is value matching target data
    1179         117 :     if (view_child.type() == wasp::VALUE && view_child.to_string() == target_value)
    1180             :     {
    1181             :       // get object context path and object type value of node if it exists
    1182           6 :       wasp::HITNodeView object_context = view_child;
    1183          18 :       while (object_context.type() != wasp::OBJECT && object_context.has_parent())
    1184          12 :         object_context = object_context.parent();
    1185           6 :       const std::string object_path = object_context.path();
    1186           6 :       wasp::HITNodeView type_node = object_context.first_child_by_name("type");
    1187             :       const std::string object_type =
    1188           6 :           type_node.is_null() ? "" : wasp::strip_quotes(hit::extractValue(type_node.data()));
    1189             : 
    1190             :       // gather global, action, and object parameters for context of object
    1191           6 :       InputParameters valid_params = emptyInputParameters();
    1192           6 :       std::set<std::string> obj_act_tasks;
    1193           6 :       getAllValidParameters(valid_params, object_path, object_type, obj_act_tasks);
    1194             : 
    1195             :       // get name from parent of current value node which is parameter node
    1196           6 :       std::string param_name = view_child.has_parent() ? view_child.parent().name() : "";
    1197             : 
    1198             :       // get type of parameter and prepare string to check target set match
    1199           6 :       std::string dirty_type = valid_params.type(param_name);
    1200           6 :       std::string clean_type = MooseUtils::prettyCppType(dirty_type);
    1201           6 :       pcrecpp::RE(".+<([A-Za-z0-9_' ':]*)>.*").GlobalReplace("\\1", &clean_type);
    1202             : 
    1203             :       // add input node to collection if its type is also in set of targets
    1204           6 :       if (target_types.count(clean_type))
    1205           5 :         match_nodes.insert(view_child);
    1206           6 :     }
    1207             : 
    1208             :     // recurse deeper into input to search for matches if node has children
    1209         117 :     if (!view_child.is_leaf())
    1210          25 :       getNodesByValueAndTypes(match_nodes, view_child, target_value, target_types);
    1211         143 :   }
    1212          26 : }
    1213             : 
    1214             : bool
    1215           1 : MooseServer::gatherDocumentFormattingTextEdits(wasp::DataArray & formattingTextEdits,
    1216             :                                                int tab_size,
    1217             :                                                bool /* insert_spaces */)
    1218             : {
    1219             :   // strip scheme prefix from document uri if it exists for parse file path
    1220           1 :   std::string parse_file_path = document_path;
    1221           1 :   pcrecpp::RE("(.*://)(.*)").Replace("\\2", &parse_file_path);
    1222             : 
    1223             :   // input check expanded any brace expressions in cached tree so reprocess
    1224           1 :   std::stringstream input_errors, input_stream(getDocumentText());
    1225           1 :   wasp::DefaultHITInterpreter interpreter(input_errors);
    1226             : 
    1227             :   // return without adding any formatting text edits if input parsing fails
    1228           1 :   if (!interpreter.parseStream(input_stream, parse_file_path))
    1229           0 :     return true;
    1230             : 
    1231             :   // return without adding any formatting text edits if parser root is null
    1232           1 :   if (interpreter.root().is_null())
    1233           0 :     return true;
    1234             : 
    1235             :   // get input root node line and column range to represent entire document
    1236           1 :   wasp::HITNodeView view_root = interpreter.root();
    1237           1 :   int document_start_line = view_root.line() - 1;
    1238           1 :   int document_start_char = view_root.column() - 1;
    1239           1 :   int document_last_line = view_root.last_line() - 1;
    1240           1 :   int document_last_char = view_root.last_column();
    1241             : 
    1242             :   // set number of spaces for indentation and build formatted document text
    1243           1 :   _formatting_tab_size = tab_size;
    1244           1 :   std::size_t starting_line = view_root.line() - 1;
    1245           1 :   std::string document_format = formatDocument(view_root, starting_line, 0);
    1246             : 
    1247             :   // add formatted text with whole line and column range to formatting list
    1248           1 :   formattingTextEdits.push_back(wasp::DataObject());
    1249           1 :   wasp::DataObject * item = formattingTextEdits.back().to_object();
    1250           1 :   bool pass = wasp::lsp::buildTextEditObject(*item,
    1251             :                                              errors,
    1252             :                                              document_start_line,
    1253             :                                              document_start_char,
    1254             :                                              document_last_line,
    1255             :                                              document_last_char,
    1256             :                                              document_format);
    1257           1 :   return pass;
    1258           1 : }
    1259             : 
    1260             : std::string
    1261          11 : MooseServer::formatDocument(wasp::HITNodeView parent, std::size_t & prev_line, std::size_t level)
    1262             : {
    1263             :   // build string of newline and indentation spaces from level and tab size
    1264          11 :   std::string newline_indent = "\n" + std::string(level * _formatting_tab_size, ' ');
    1265             : 
    1266             :   // lambda to format include data by replacing consecutive spaces with one
    1267           1 :   auto collapse_spaces = [](std::string string_copy)
    1268             :   {
    1269           1 :     pcrecpp::RE("\\s+").Replace(" ", &string_copy);
    1270           1 :     return string_copy;
    1271             :   };
    1272             : 
    1273             :   // formatted string that will be built recursively by appending each call
    1274          11 :   std::string format_string;
    1275             : 
    1276             :   // walk over all children of this node context and build formatted string
    1277          90 :   for (const auto i : make_range(parent.child_count()))
    1278             :   {
    1279             :     // walk must be index based to catch file include and skip its children
    1280          79 :     wasp::HITNodeView child = parent.child_at(i);
    1281             : 
    1282             :     // add blank line if necessary after previous line and before this line
    1283          79 :     std::string blank = child.line() > prev_line + 1 ? "\n" : "";
    1284             : 
    1285             :     // format include directive with indentation and collapse extra spacing
    1286          79 :     if (child.type() == wasp::FILE)
    1287           1 :       format_string += blank + newline_indent + MooseUtils::trim(collapse_spaces(child.data()));
    1288             : 
    1289             :     // format normal comment with indentation and inline comment with space
    1290          78 :     else if (child.type() == wasp::COMMENT)
    1291          10 :       format_string += (child.line() == prev_line ? " " : blank + newline_indent) +
    1292          15 :                        MooseUtils::trim(child.data());
    1293             : 
    1294             :     // format object recursively with indentation and without legacy syntax
    1295          73 :     else if (child.type() == wasp::OBJECT)
    1296          20 :       format_string += blank + newline_indent + "[" + child.name() + "]" +
    1297          30 :                        formatDocument(child, prev_line, level + 1) + newline_indent + "[]";
    1298             : 
    1299             :     // format keyed value with indentation and calling reusable hit methods
    1300          63 :     else if (child.type() == wasp::KEYED_VALUE || child.type() == wasp::ARRAY)
    1301             :     {
    1302          17 :       const std::string prefix = newline_indent + child.name() + " = ";
    1303             : 
    1304          17 :       const std::string render_val = hit::extractValue(child.data());
    1305          17 :       std::size_t val_column = child.child_count() > 2 ? child.child_at(2).column() : 0;
    1306          17 :       std::size_t prefix_len = prefix.size() - 1;
    1307             : 
    1308          17 :       format_string += blank + prefix + hit::formatValue(render_val, val_column, prefix_len);
    1309          17 :     }
    1310             : 
    1311             :     // set previous line reference used for blank lines and inline comments
    1312          79 :     prev_line = child.last_line();
    1313          79 :   }
    1314             : 
    1315             :   // remove leading newline if this is level zero returning entire document
    1316          22 :   return level != 0 ? format_string : format_string.substr(1);
    1317          11 : }
    1318             : 
    1319             : bool
    1320           2 : MooseServer::gatherDocumentSymbols(wasp::DataArray & documentSymbols)
    1321             : {
    1322             :   // return prior to starting document symbol tree when parser root is null
    1323           2 :   if (!rootIsValid())
    1324           0 :     return true;
    1325             : 
    1326           2 :   wasp::HITNodeView view_root = getRoot().getNodeView();
    1327             : 
    1328           2 :   bool pass = true;
    1329             : 
    1330             :   // walk over all children of root node context and build document symbols
    1331          12 :   for (const auto i : make_range(view_root.child_count()))
    1332             :   {
    1333             :     // walk must be index based to catch file include and skip its children
    1334          10 :     wasp::HITNodeView view_child = view_root.child_at(i);
    1335             : 
    1336             :     // set up name, zero based line and column range, kind, and detail info
    1337          10 :     std::string name = view_child.name();
    1338          10 :     int line = view_child.line() - 1;
    1339          10 :     int column = view_child.column() - 1;
    1340          10 :     int last_line = view_child.last_line() - 1;
    1341          10 :     int last_column = view_child.last_column();
    1342          10 :     int symbol_kind = getDocumentSymbolKind(view_child);
    1343             :     std::string detail =
    1344          20 :         !view_child.first_child_by_name("type").is_null()
    1345          14 :             ? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
    1346          14 :             : "";
    1347             : 
    1348             :     // build document symbol object from node child info and push to array
    1349          10 :     documentSymbols.push_back(wasp::DataObject());
    1350          10 :     wasp::DataObject * data_child = documentSymbols.back().to_object();
    1351          10 :     pass &= wasp::lsp::buildDocumentSymbolObject(*data_child,
    1352             :                                                  errors,
    1353          20 :                                                  (name.empty() ? "void" : name),
    1354             :                                                  detail,
    1355             :                                                  symbol_kind,
    1356             :                                                  false,
    1357             :                                                  line,
    1358             :                                                  column,
    1359             :                                                  last_line,
    1360             :                                                  last_column,
    1361             :                                                  line,
    1362             :                                                  column,
    1363             :                                                  last_line,
    1364             :                                                  last_column);
    1365             : 
    1366             :     // call method to recursively fill document symbols for each node child
    1367          10 :     pass &= traverseParseTreeAndFillSymbols(view_child, *data_child);
    1368          10 :   }
    1369             : 
    1370           2 :   return pass;
    1371           2 : }
    1372             : 
    1373             : bool
    1374         176 : MooseServer::traverseParseTreeAndFillSymbols(wasp::HITNodeView view_parent,
    1375             :                                              wasp::DataObject & data_parent)
    1376             : {
    1377             :   // return without adding any children if parent node is file include type
    1378         176 :   if (wasp::is_nested_file(view_parent))
    1379           0 :     return true;
    1380             : 
    1381         176 :   bool pass = true;
    1382             : 
    1383             :   // walk over all children of this node context and build document symbols
    1384         342 :   for (const auto i : make_range(view_parent.child_count()))
    1385             :   {
    1386             :     // walk must be index based to catch file include and skip its children
    1387         166 :     wasp::HITNodeView view_child = view_parent.child_at(i);
    1388             : 
    1389             :     // set up name, zero based line and column range, kind, and detail info
    1390         166 :     std::string name = view_child.name();
    1391         166 :     int line = view_child.line() - 1;
    1392         166 :     int column = view_child.column() - 1;
    1393         166 :     int last_line = view_child.last_line() - 1;
    1394         166 :     int last_column = view_child.last_column();
    1395         166 :     int symbol_kind = getDocumentSymbolKind(view_child);
    1396             :     std::string detail =
    1397         332 :         !view_child.first_child_by_name("type").is_null()
    1398         168 :             ? wasp::strip_quotes(hit::extractValue(view_child.first_child_by_name("type").data()))
    1399         168 :             : "";
    1400             : 
    1401             :     // build document symbol object from node child info and push to array
    1402         166 :     wasp::DataObject & data_child = wasp::lsp::addDocumentSymbolChild(data_parent);
    1403         166 :     pass &= wasp::lsp::buildDocumentSymbolObject(data_child,
    1404             :                                                  errors,
    1405         332 :                                                  (name.empty() ? "void" : name),
    1406             :                                                  detail,
    1407             :                                                  symbol_kind,
    1408             :                                                  false,
    1409             :                                                  line,
    1410             :                                                  column,
    1411             :                                                  last_line,
    1412             :                                                  last_column,
    1413             :                                                  line,
    1414             :                                                  column,
    1415             :                                                  last_line,
    1416             :                                                  last_column);
    1417             : 
    1418             :     // call method to recursively fill document symbols for each node child
    1419         166 :     pass &= traverseParseTreeAndFillSymbols(view_child, data_child);
    1420         166 :   }
    1421             : 
    1422         176 :   return pass;
    1423             : }
    1424             : 
    1425             : int
    1426          65 : MooseServer::getCompletionItemKind(const InputParameters & valid_params,
    1427             :                                    const std::string & param_name,
    1428             :                                    const std::string & clean_type,
    1429             :                                    bool is_param)
    1430             : {
    1431             :   // set up completion item kind value that client may use for icon in list
    1432          65 :   auto associated_types = getCheckApp()->syntax().getAssociatedTypes();
    1433          66 :   if (is_param && valid_params.isParamRequired(param_name) &&
    1434           1 :       !valid_params.isParamValid(param_name))
    1435           1 :     return wasp::lsp::m_comp_kind_event;
    1436          64 :   else if (param_name == "active" || param_name == "inactive")
    1437           5 :     return wasp::lsp::m_comp_kind_class;
    1438          59 :   else if (clean_type == "bool")
    1439          16 :     return wasp::lsp::m_comp_kind_interface;
    1440          43 :   else if (valid_params.have_parameter<MooseEnum>(param_name) ||
    1441          37 :            valid_params.have_parameter<MultiMooseEnum>(param_name) ||
    1442         116 :            valid_params.have_parameter<ExecFlagEnum>(param_name) ||
    1443          36 :            valid_params.have_parameter<std::vector<MooseEnum>>(param_name))
    1444           7 :     return is_param ? wasp::lsp::m_comp_kind_enum : wasp::lsp::m_comp_kind_enum_member;
    1445          36 :   else if (param_name == "type")
    1446           2 :     return wasp::lsp::m_comp_kind_type_param;
    1447          34 :   else if (std::find_if(associated_types.begin(),
    1448             :                         associated_types.end(),
    1449         860 :                         [&](const auto & entry)
    1450         928 :                         { return entry.second == clean_type; }) != associated_types.end())
    1451           2 :     return wasp::lsp::m_comp_kind_reference;
    1452             :   else
    1453          32 :     return is_param ? wasp::lsp::m_comp_kind_keyword : wasp::lsp::m_comp_kind_value;
    1454          65 : }
    1455             : 
    1456             : int
    1457         176 : MooseServer::getDocumentSymbolKind(wasp::HITNodeView symbol_node)
    1458             : {
    1459             :   // lambdas that check if parameter is a boolean or number for symbol kind
    1460          14 :   auto is_boolean = [](wasp::HITNodeView symbol_node)
    1461             :   {
    1462             :     bool convert;
    1463          14 :     std::istringstream iss(MooseUtils::toLower(symbol_node.last_as_string()));
    1464          28 :     return (iss >> std::boolalpha >> convert && !iss.fail());
    1465          14 :   };
    1466          12 :   auto is_number = [](wasp::HITNodeView symbol_node)
    1467             :   {
    1468             :     double convert;
    1469          12 :     std::istringstream iss(symbol_node.last_as_string());
    1470          24 :     return (iss >> convert && iss.eof());
    1471          12 :   };
    1472             : 
    1473             :   // set up document symbol kind value that client may use for outline icon
    1474         176 :   if (symbol_node.type() == wasp::OBJECT)
    1475          16 :     return wasp::lsp::m_symbol_kind_struct;
    1476         160 :   else if (symbol_node.type() == wasp::FILE)
    1477           0 :     return wasp::lsp::m_symbol_kind_file;
    1478         160 :   else if (symbol_node.type() == wasp::ARRAY)
    1479           2 :     return wasp::lsp::m_symbol_kind_array;
    1480         158 :   else if (symbol_node.type() == wasp::KEYED_VALUE && symbol_node.name() == std::string("type"))
    1481           6 :     return wasp::lsp::m_symbol_kind_type_param;
    1482         152 :   else if (symbol_node.type() == wasp::KEYED_VALUE && is_boolean(symbol_node))
    1483           2 :     return wasp::lsp::m_symbol_kind_boolean;
    1484         150 :   else if (symbol_node.type() == wasp::KEYED_VALUE && is_number(symbol_node))
    1485           2 :     return wasp::lsp::m_symbol_kind_number;
    1486         148 :   else if (symbol_node.type() == wasp::KEYED_VALUE)
    1487          10 :     return wasp::lsp::m_symbol_kind_key;
    1488         138 :   else if (symbol_node.type() == wasp::VALUE)
    1489          26 :     return wasp::lsp::m_symbol_kind_string;
    1490             :   else
    1491         112 :     return wasp::lsp::m_symbol_kind_property;
    1492             : }
    1493             : 
    1494             : std::string
    1495        1125 : MooseServer::getRequiredParamsText(const std::string & subblock_path,
    1496             :                                    const std::string & subblock_type,
    1497             :                                    const std::set<std::string> & existing_params,
    1498             :                                    const std::string & indent_spaces)
    1499             : {
    1500             :   // gather global, action, and object parameters in request object context
    1501        1125 :   InputParameters valid_params = emptyInputParameters();
    1502        1125 :   std::set<std::string> obj_act_tasks;
    1503        1125 :   getAllValidParameters(valid_params, subblock_path, subblock_type, obj_act_tasks);
    1504             : 
    1505             :   // walk over collection of all parameters and build text of ones required
    1506        1125 :   std::string required_param_text;
    1507       15965 :   for (const auto & valid_params_iter : valid_params)
    1508             :   {
    1509             :     // skip parameter if deprecated, private, defaulted, optional, existing
    1510       14840 :     const std::string & param_name = valid_params_iter.first;
    1511       29656 :     if (!valid_params.isParamDeprecated(param_name) && !valid_params.isPrivate(param_name) &&
    1512       30722 :         !valid_params.isParamValid(param_name) && valid_params.isParamRequired(param_name) &&
    1513        1066 :         !existing_params.count(param_name))
    1514          12 :       required_param_text += "\n" + indent_spaces + param_name + " = ";
    1515             :   }
    1516             : 
    1517        2250 :   return required_param_text;
    1518        1125 : }
    1519             : 
    1520             : bool
    1521          24 : MooseServer::rootIsValid() const
    1522             : {
    1523          72 :   return getCheckApp() && getCheckApp()->builder().root() &&
    1524          72 :          !getCheckApp()->builder().root()->getNodeView().is_null();
    1525             : }
    1526             : 
    1527             : hit::Node &
    1528          27 : MooseServer::getRoot()
    1529             : {
    1530             :   mooseAssert(rootIsValid(), "Not valid");
    1531          27 :   return *getCheckApp()->builder().root();
    1532             : }
    1533             : 
    1534             : std::shared_ptr<MooseApp>
    1535        4807 : MooseServer::getCheckApp() const
    1536             : {
    1537             :   mooseAssert(_check_apps.count(document_path), "No check app for path");
    1538        4807 :   return _check_apps.at(document_path);
    1539             : }
    1540             : 
    1541             : const std::string &
    1542           1 : MooseServer::getDocumentText() const
    1543             : {
    1544             :   mooseAssert(_path_to_text.count(document_path), "No text for path");
    1545           1 :   return _path_to_text.at(document_path);
    1546             : }

Generated by: LCOV version 1.14