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

Generated by: LCOV version 1.14