blob: ba007dc38cefcb35759accb6d420b2e5be36e433 [file] [log] [blame]
// 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 "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"
#include "gtest/gtest.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() final {}
void CollectTrailingFilteredTokens() final {}
// Leaf visit that adds a PreFormatToken from the leaf's TokenInfo
// to the current_unwrapped_line_
void Visit(const verible::SyntaxTreeLeaf& leaf) final {
CatchUpFilteredTokens();
AddTokenToCurrentUnwrappedLine();
}
// Node visit that always starts a new unwrapped line
void Visit(const SyntaxTreeNode& node) final {
StartNewUnwrappedLine(PartitionPolicyEnum::kAlwaysExpand, &node);
TraverseChildren(node);
}
void InterChildNodeHook(const SyntaxTreeNode& node) final {}
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(&current, &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