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

Generated by: LCOV version 1.14