@@ -375,6 +375,8 @@ Node::type() const
375375 return NodeType::Section;
376376 else if (_hnv.type () == wasp::KEYED_VALUE || _hnv.type () == wasp::ARRAY)
377377 return NodeType::Field;
378+ else if (_hnv.type () == wasp::FILE)
379+ return NodeType::Include;
378380 return NodeType::Other;
379381}
380382
@@ -554,6 +556,26 @@ Comment::setInline(bool is_inline)
554556 _isinline = is_inline;
555557}
556558
559+ Include::Include (std::shared_ptr<wasp::DefaultHITInterpreter> dhi, wasp::HITNodeView hnv)
560+ : Node(dhi, hnv)
561+ {
562+ }
563+
564+ std::string
565+ Include::render (int indent, const std::string & indent_text, int /* maxlen*/ ) const
566+ {
567+ return " \n " + strRepeat (indent_text, indent) + _hnv.data ();
568+ }
569+
570+ Node *
571+ Include::clone (bool absolute_path)
572+ {
573+ auto n = new Include (_dhi, _hnv);
574+ if (absolute_path)
575+ n->setOverridePath (fullpath ());
576+ return n;
577+ }
578+
557579Section::Section (const std::string & path) : Node(path) {}
558580
559581Section::Section (std::shared_ptr<wasp::DefaultHITInterpreter> dhi, wasp::HITNodeView hnv)
@@ -1024,7 +1046,8 @@ Field::strVal() const
10241046Node *
10251047parse (const std::string & fname,
10261048 const std::string & input,
1027- std::vector<ErrorMessage> * syntax_errors /* = nullptr */ )
1049+ std::vector<ErrorMessage> * syntax_errors /* = nullptr */ ,
1050+ const ParseOptions & options /* = ParseOptions{} */ )
10281051{
10291052 std::stringstream input_errors;
10301053 std::shared_ptr<wasp::DefaultHITInterpreter> interpreter =
@@ -1056,7 +1079,8 @@ parse(const std::string & fname,
10561079 {
10571080 std::string starting_file = interpreter->root ().node_pool ()->stream_name ();
10581081 std::size_t starting_line = interpreter->root ().line ();
1059- buildHITTree (interpreter, interpreter->root (), root.get (), starting_file, starting_line);
1082+ buildHITTree (
1083+ interpreter, interpreter->root (), root.get (), starting_file, starting_line, options);
10601084 }
10611085
10621086 return root.release ();
@@ -1186,7 +1210,10 @@ Formatter::Formatter(const std::string & fname, const std::string & hit_config)
11861210std::string
11871211Formatter::format (const std::string & fname, const std::string & input)
11881212{
1189- std::unique_ptr<hit::Node> root (hit::parse (fname, input));
1213+ // formatting should parse the document without included content expanded
1214+ ParseOptions options;
1215+ options.expand_includes = false ;
1216+ std::unique_ptr<hit::Node> root (hit::parse (fname, input, nullptr , options));
11901217 format (root.get ());
11911218 return root->render (0 , indent_string, line_length);
11921219}
@@ -1311,112 +1338,138 @@ buildHITTree(std::shared_ptr<wasp::DefaultHITInterpreter> interpreter,
13111338 wasp::HITNodeView hnv_parent,
13121339 Node * hit_parent,
13131340 std::string & previous_file,
1314- std::size_t & previous_line)
1341+ std::size_t & previous_line,
1342+ const ParseOptions & options)
13151343{
13161344 if (hnv_parent.is_null ())
13171345 return ;
13181346
1319- for (const auto & hnv_child : hnv_parent)
1347+ // do range-based traversal in normal conditions to expand included files
1348+ // do index-based traversal for format to not descend into included files
1349+ auto walk_children = [](wasp::HITNodeView hnv_parent, bool expand_includes, auto visit)
13201350 {
1321- // create and add comment node as terminal leaf but do not recurse deeper
1322- if (hnv_child.type () == wasp::COMMENT)
1323- {
1324- // add blank line if needed between previous node line and this node line
1325- if (hnv_child.node_pool ()->stream_name () == previous_file &&
1326- hnv_child.line () > previous_line + 1 )
1327- hit_parent->addChild (new Blank ());
1328-
1329- auto hit_child = new Comment (interpreter, hnv_child);
1330- if (hnv_child.node_pool ()->stream_name () == previous_file &&
1331- hnv_child.line () == previous_line && hit_parent->children ().size () > 0 )
1332- {
1333- hit_child->setInline (true );
1334- hit_parent->children ().back ()->addChild (hit_child);
1335- }
1336- else
1337- {
1338- hit_child->setInline (false );
1339- hit_parent->addChild (hit_child);
1340- }
1341- previous_file = hnv_child.node_pool ()->stream_name ();
1342- previous_line = hnv_child.last_line ();
1343- }
1344-
1345- // create and add field node depending on conflicts but recurse no deeper
1346- else if (hnv_child.type () == wasp::KEYED_VALUE || hnv_child.type () == wasp::ARRAY)
1347- {
1348- // check if tree has field conflict and overrides need to be considered
1349- if (auto found_field = hit_parent->find (hnv_child.name ());
1350- found_field && found_field->type () == NodeType::Field)
1351+ if (expand_includes)
1352+ for (const auto & hnv_child : hnv_parent)
1353+ visit (hnv_child);
1354+ else
1355+ for (std::size_t i = 0 , count = hnv_parent.child_count (); i < count; i++)
1356+ visit (hnv_parent.child_at (i));
1357+ };
1358+
1359+ walk_children (
1360+ hnv_parent,
1361+ options.expand_includes ,
1362+ [&](const auto & hnv_child)
13511363 {
1352- // capture any override settings used for both existing and new field
1353- bool override_for_old_node = wasp::is_override (found_field->getNodeView ());
1354- bool override_for_new_node = wasp::is_override (hnv_child);
1355-
1356- // use overrides of conflicting fields to decide which has precedence
1357- if (!override_for_old_node && !override_for_new_node)
1364+ // create and add comment node as terminal leaf but do not recurse deeper
1365+ if (hnv_child.type () == wasp::COMMENT)
13581366 {
1359- // neither has override so add new next to existing for error later
1360- hit_parent->addChild (new Field (interpreter, hnv_child));
1367+ // add blank line if needed between previous node line and this node line
1368+ if (hnv_child.node_pool ()->stream_name () == previous_file &&
1369+ hnv_child.line () > previous_line + 1 )
1370+ hit_parent->addChild (new Blank ());
1371+
1372+ auto hit_child = new Comment (interpreter, hnv_child);
1373+ if (hnv_child.node_pool ()->stream_name () == previous_file &&
1374+ hnv_child.line () == previous_line && hit_parent->children ().size () > 0 )
1375+ {
1376+ hit_child->setInline (true );
1377+ hit_parent->children ().back ()->addChild (hit_child);
1378+ }
1379+ else
1380+ {
1381+ hit_child->setInline (false );
1382+ hit_parent->addChild (hit_child);
1383+ }
13611384 previous_file = hnv_child.node_pool ()->stream_name ();
13621385 previous_line = hnv_child.last_line ();
13631386 }
1364- else if (!override_for_old_node && override_for_new_node)
1387+
1388+ // create and add field node depending on conflicts but recurse no deeper
1389+ else if (hnv_child.type () == wasp::KEYED_VALUE || hnv_child.type () == wasp::ARRAY)
13651390 {
1366- // only new field has override so remove existing field and replace
1367- const auto & hit_siblings = hit_parent->children ();
1368- for (std::size_t index = 0 ; index < hit_siblings.size (); index++)
1369- if (hit_siblings[index] == found_field)
1391+ // check if tree has field conflict and overrides need to be considered
1392+ if (auto found_field = hit_parent->find (hnv_child.name ());
1393+ found_field && found_field->type () == NodeType::Field)
1394+ {
1395+ // capture any override settings used for both existing and new field
1396+ bool override_for_old_node = wasp::is_override (found_field->getNodeView ());
1397+ bool override_for_new_node = wasp::is_override (hnv_child);
1398+
1399+ // use overrides of conflicting fields to decide which has precedence
1400+ if (!override_for_old_node && !override_for_new_node)
1401+ {
1402+ // neither has override so add new next to existing for error later
1403+ hit_parent->addChild (new Field (interpreter, hnv_child));
1404+ previous_file = hnv_child.node_pool ()->stream_name ();
1405+ previous_line = hnv_child.last_line ();
1406+ }
1407+ else if (!override_for_old_node && override_for_new_node)
13701408 {
1371- hit_parent->insertChild (index, new Field (interpreter, hnv_child));
1372- delete found_field;
1373- break ;
1409+ // only new field has override so remove existing field and replace
1410+ const auto & hit_siblings = hit_parent->children ();
1411+ for (std::size_t index = 0 ; index < hit_siblings.size (); index++)
1412+ if (hit_siblings[index] == found_field)
1413+ {
1414+ hit_parent->insertChild (index, new Field (interpreter, hnv_child));
1415+ delete found_field;
1416+ break ;
1417+ }
13741418 }
1419+ else if (override_for_old_node && override_for_new_node)
1420+ // both fields have override and this is not allowed so throw error
1421+ throw Error (" '" + found_field->fullpath () +
1422+ " ' specified more than once with override syntax" ,
1423+ found_field);
1424+ }
1425+ else
1426+ {
1427+ // no conflict so add blank line if necessary then add new field node
1428+ if (hnv_child.node_pool ()->stream_name () == previous_file &&
1429+ hnv_child.line () > previous_line + 1 )
1430+ hit_parent->addChild (new Blank ());
1431+ hit_parent->addChild (new Field (interpreter, hnv_child));
1432+ previous_file = hnv_child.node_pool ()->stream_name ();
1433+ previous_line = hnv_child.last_line ();
1434+ }
13751435 }
1376- else if (override_for_old_node && override_for_new_node)
1377- // both fields have override and this is not allowed so throw error
1378- throw Error (" '" + found_field->fullpath () +
1379- " ' specified more than once with override syntax" ,
1380- found_field);
1381- }
1382- else
1383- {
1384- // no conflict so add blank line if necessary then add new field node
1385- if (hnv_child.node_pool ()->stream_name () == previous_file &&
1386- hnv_child.line () > previous_line + 1 )
1387- hit_parent->addChild (new Blank ());
1388- hit_parent->addChild (new Field (interpreter, hnv_child));
1389- previous_file = hnv_child.node_pool ()->stream_name ();
1390- previous_line = hnv_child.last_line ();
1391- }
1392- }
13931436
1394- // create and add section node if not found in tree then recurse children
1395- else if (hnv_child.type () == wasp::OBJECT || hnv_child.type () == wasp::DOCUMENT_ROOT)
1396- {
1397- // recurse using section if found and do not create or add anything new
1398- if (auto hit_child = hit_parent->find (hnv_child.name ());
1399- hit_child && hit_child->type () == NodeType::Section)
1400- buildHITTree (interpreter, hnv_child, hit_child, previous_file, previous_line);
1437+ else if (hnv_child.type () == wasp::FILE)
1438+ {
1439+ if (hnv_child.node_pool ()->stream_name () == previous_file &&
1440+ hnv_child.line () > previous_line + 1 )
1441+ hit_parent->addChild (new Blank ());
1442+ hit_parent->addChild (new Include (interpreter, hnv_child));
1443+ previous_file = hnv_child.node_pool ()->stream_name ();
1444+ previous_line = hnv_child.last_line ();
1445+ }
14011446
1402- // create and add new section node if not found then recurse using node
1403- else
1404- {
1405- // add blank line if needed between previous node line and this node line
1406- if (hnv_child.node_pool ()->stream_name () == previous_file &&
1407- hnv_child.line () > previous_line + 1 )
1408- hit_parent->addChild (new Blank ());
1409-
1410- hit_child = new Section (interpreter, hnv_child);
1411- hit_parent->addChild (hit_child);
1412- previous_file = hnv_child.node_pool ()->stream_name ();
1413- previous_line = hnv_child.line ();
1414- buildHITTree (interpreter, hnv_child, hit_child, previous_file, previous_line);
1415- previous_file = hnv_child.node_pool ()->stream_name ();
1416- previous_line = hnv_child.last_line ();
1417- }
1418- }
1419- }
1447+ // create and add section node if not found in tree then recurse children
1448+ else if (hnv_child.type () == wasp::OBJECT || hnv_child.type () == wasp::DOCUMENT_ROOT)
1449+ {
1450+ // recurse using section if found and do not create or add anything new
1451+ if (auto hit_child = hit_parent->find (hnv_child.name ());
1452+ hit_child && hit_child->type () == NodeType::Section)
1453+ buildHITTree (interpreter, hnv_child, hit_child, previous_file, previous_line, options);
1454+
1455+ // create and add new section node if not found then recurse using node
1456+ else
1457+ {
1458+ // add blank line if needed between previous node line and this node line
1459+ if (hnv_child.node_pool ()->stream_name () == previous_file &&
1460+ hnv_child.line () > previous_line + 1 )
1461+ hit_parent->addChild (new Blank ());
1462+
1463+ hit_child = new Section (interpreter, hnv_child);
1464+ hit_parent->addChild (hit_child);
1465+ previous_file = hnv_child.node_pool ()->stream_name ();
1466+ previous_line = hnv_child.line ();
1467+ buildHITTree (interpreter, hnv_child, hit_child, previous_file, previous_line, options);
1468+ previous_file = hnv_child.node_pool ()->stream_name ();
1469+ previous_line = hnv_child.last_line ();
1470+ }
1471+ }
1472+ });
14201473}
14211474
14221475} // namespace hit
0 commit comments