| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "common/formatting/tree_unwrapper.h" | |
| #include <algorithm> | |
| #include <iterator> | |
| #include <memory> | |
| #include <vector> | |
| #include "gtest/gtest.h" | |
| #include "absl/strings/ascii.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/formatting/format_token.h" | |
| #include "common/formatting/unwrapped_line.h" | |
| #include "common/text/concrete_syntax_leaf.h" | |
| #include "common/text/concrete_syntax_tree.h" | |
| #include "common/text/text_structure.h" | |
| #include "common/text/text_structure_test_utils.h" | |
| #include "common/text/token_info.h" | |
| #include "common/text/token_stream_view.h" | |
| #include "common/util/container_iterator_range.h" | |
| #include "common/util/range.h" | |
| namespace verible { | |
| static bool KeepNonWhitespace(const TokenInfo& token) { | |
| const absl::string_view text(absl::StripAsciiWhitespace(token.text())); | |
| return !text.empty(); | |
| } | |
| class TreeUnwrapperData { | |
| public: | |
| explicit TreeUnwrapperData(const verible::TokenSequence& tokens) { | |
| verible::InitTokenStreamView(tokens, &tokens_view_); | |
| verible::FilterTokenStreamViewInPlace(KeepNonWhitespace, &tokens_view_); | |
| preformatted_tokens_.reserve(tokens_view_.size()); | |
| std::transform(tokens_view_.begin(), tokens_view_.end(), | |
| std::back_inserter(preformatted_tokens_), | |
| [](const TokenSequence::const_iterator iter) { | |
| return PreFormatToken(&*iter); | |
| }); | |
| } | |
| protected: | |
| verible::TokenStreamView tokens_view_; | |
| std::vector<verible::PreFormatToken> preformatted_tokens_; | |
| }; | |
| class FakeTreeUnwrapper : public TreeUnwrapperData, public TreeUnwrapper { | |
| public: | |
| explicit FakeTreeUnwrapper(const TextStructureView& view) | |
| : TreeUnwrapperData(view.TokenStream()), | |
| TreeUnwrapper(view, TreeUnwrapperData::preformatted_tokens_) {} | |
| // There are no filtered-out tokens in this fake. | |
| void CollectLeadingFilteredTokens() override {} | |
| void CollectTrailingFilteredTokens() override {} | |
| // Leaf visit that adds a PreFormatToken from the leaf's TokenInfo | |
| // to the current_unwrapped_line_ | |
| void Visit(const verible::SyntaxTreeLeaf& leaf) override { | |
| CatchUpFilteredTokens(); | |
| AddTokenToCurrentUnwrappedLine(); | |
| } | |
| // Node visit that always starts a new unwrapped line | |
| void Visit(const SyntaxTreeNode& node) override { | |
| StartNewUnwrappedLine(PartitionPolicyEnum::kAlwaysExpand, &node); | |
| TraverseChildren(node); | |
| } | |
| void InterChildNodeHook(const SyntaxTreeNode& node) override {} | |
| using TreeUnwrapper::StartNewUnwrappedLine; | |
| protected: | |
| void CatchUpFilteredTokens() { | |
| const auto iter = CurrentFormatTokenIterator(); | |
| SkipUnfilteredTokens( | |
| [=](const verible::TokenInfo& token) { return &token != iter->token; }); | |
| } | |
| }; | |
| // Test that no new UnwrappedLines are created when current_unwrapped_line_ | |
| // is empty. | |
| TEST(TreeUnwrapperTest, EmptyStartNewUnwrappedLine) { | |
| std::unique_ptr<TextStructureView> view = MakeTextStructureViewWithNoLeaves(); | |
| FakeTreeUnwrapper tree_unwrapper(*view); | |
| // const reference forces use of const method | |
| const auto& const_tree_unwrapper(tree_unwrapper); | |
| // Do not call .Unwrap() for this test. | |
| const auto& current = const_tree_unwrapper.CurrentUnwrappedLine(); | |
| tree_unwrapper.StartNewUnwrappedLine(PartitionPolicyEnum::kAlwaysExpand, | |
| &*view->SyntaxTree()); | |
| const auto& next = const_tree_unwrapper.CurrentUnwrappedLine(); | |
| EXPECT_EQ(¤t, &next); | |
| } | |
| // Test that StartNewUnwrappedLine properly handles current_unwrapped_line_ by | |
| // creating new unwrapped lines at each Node and appending the | |
| // current_unwrapped_line to UnwrappedLines. | |
| TEST(TreeUnwrapperTest, NonEmptyUnwrap) { | |
| std::unique_ptr<TextStructureView> view = MakeTextStructureViewHelloWorld(); | |
| FakeTreeUnwrapper tree_unwrapper(*view); | |
| EXPECT_TRUE( | |
| verible::BoundsEqual(tree_unwrapper.FullText(), view->Contents())); | |
| tree_unwrapper.Unwrap(); | |
| const auto unwrapped_lines = tree_unwrapper.FullyPartitionedUnwrappedLines(); | |
| ASSERT_EQ(unwrapped_lines.size(), 2); | |
| const UnwrappedLine& first_unwrapped_line = unwrapped_lines[0]; | |
| const UnwrappedLine& second_unwrapped_line = unwrapped_lines[1]; | |
| EXPECT_EQ(first_unwrapped_line.Size(), 2); | |
| EXPECT_EQ(second_unwrapped_line.Size(), 1); | |
| const auto first_range = first_unwrapped_line.TokensRange(); | |
| const auto second_range = second_unwrapped_line.TokensRange(); | |
| EXPECT_EQ(first_range.front().TokenEnum(), 0); | |
| EXPECT_EQ(first_range.back().TokenEnum(), 1); | |
| EXPECT_EQ(second_range.front().TokenEnum(), 3); | |
| EXPECT_EQ(second_range.back().TokenEnum(), 3); | |
| } | |
| // Test that TreeUnwrapper does not gain any UnwrappedLines after calling | |
| // StartNewUnwrappedLine when visiting a tree with no leaves. | |
| TEST(TreeUnwrapperTest, UnwrapNoLeaves) { | |
| std::unique_ptr<TextStructureView> view = MakeTextStructureViewWithNoLeaves(); | |
| FakeTreeUnwrapper tree_unwrapper(*view); | |
| tree_unwrapper.Unwrap(); | |
| const auto unwrapped_lines = tree_unwrapper.FullyPartitionedUnwrappedLines(); | |
| EXPECT_TRUE(unwrapped_lines.empty()); // Blank line removed. | |
| } | |
| TEST(TreeUnwrapperTest, StreamPrinting) { | |
| std::unique_ptr<TextStructureView> view = MakeTextStructureViewHelloWorld(); | |
| FakeTreeUnwrapper tree_unwrapper(*view); | |
| EXPECT_TRUE( | |
| verible::BoundsEqual(tree_unwrapper.FullText(), view->Contents())); | |
| tree_unwrapper.Unwrap(); | |
| std::ostringstream stream; | |
| stream << tree_unwrapper; | |
| EXPECT_EQ(stream.str(), // | |
| "[hello ,], policy: always-expand, (origin: \"hello, world\")\n" | |
| "[world], policy: always-expand, (origin: \"world\")\n"); | |
| } | |
| } // namespace verible |