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

Generated by: LCOV version 1.14