| // 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. | |
| #ifndef VERIBLE_COMMON_FORMATTING_TOKEN_PARTITION_TREE_H_ | |
| #define VERIBLE_COMMON_FORMATTING_TOKEN_PARTITION_TREE_H_ | |
| #include <cstddef> | |
| #include <iosfwd> | |
| #include <utility> | |
| #include <vector> | |
| #include "common/formatting/basic_format_style.h" | |
| #include "common/formatting/format_token.h" | |
| #include "common/formatting/unwrapped_line.h" | |
| #include "common/strings/position.h" // for ByteOffsetSet | |
| #include "common/util/container_iterator_range.h" | |
| #include "common/util/vector_tree.h" | |
| namespace verible { | |
| // Opaque type alias for a hierarchically partitioned format token stream. | |
| // Objects of this type maintain the following invariants: | |
| // 1) The format token range spanned by any tree node (UnwrappedLine) is | |
| // equal to that of its children. | |
| // 2) Adjacent siblings begin/end iterators are equal (continuity). | |
| // | |
| // TODO(fangism): Promote this to a class that privately inherits the base. | |
| // Methods on this class will preserve invariants. | |
| using TokenPartitionTree = VectorTree<UnwrappedLine>; | |
| using TokenPartitionIterator = TokenPartitionTree::subnodes_type::iterator; | |
| using TokenPartitionRange = container_iterator_range<TokenPartitionIterator>; | |
| // Analyses (non-modifying): | |
| // Verifies the invariant properties of TokenPartitionTree at a single node. | |
| // The 'base' argument is used to calculate iterator distances relative to the | |
| // start of the format token array that is the basis for UnwrappedLine token | |
| // ranges. This function fails with a fatal-error like CHECK() if the | |
| // invariants do not hold true. | |
| void VerifyTreeNodeFormatTokenRanges( | |
| const TokenPartitionTree& node, | |
| std::vector<PreFormatToken>::const_iterator base); | |
| // Verifies TokenPartitionTree invariants at every node in the tree, | |
| // which covers and entire hierarchical partition of format tokens. | |
| void VerifyFullTreeFormatTokenRanges( | |
| const TokenPartitionTree& tree, | |
| std::vector<PreFormatToken>::const_iterator base); | |
| // Returns the largest leaf partitions of tokens, ordered by number of tokens | |
| // spanned. | |
| std::vector<const UnwrappedLine*> FindLargestPartitions( | |
| const TokenPartitionTree& token_partitions, size_t num_partitions); | |
| // Compute (original_spacing - spaces_required) for every format token, | |
| // except the first token in each partition. | |
| // A perfectly formatted (flushed-left) original spacing will return all zeros. | |
| std::vector<std::vector<int>> FlushLeftSpacingDifferences( | |
| const TokenPartitionRange& partitions); | |
| // Custom printer, alternative to the default stream operator<<. | |
| // Modeled after VectorTree<>::PrintTree, but suppresses printing of the | |
| // tokens for non-leaf nodes because a node's token range always spans | |
| // that of all of its children. | |
| // Usage: stream << TokenPartitionTreePrinter(tree) << std::endl; | |
| struct TokenPartitionTreePrinter { | |
| explicit TokenPartitionTreePrinter( | |
| const TokenPartitionTree& n, bool verbose = false, | |
| UnwrappedLine::OriginPrinterFunction origin_printer = | |
| UnwrappedLine::DefaultOriginPrinter) | |
| : node(n), verbose(verbose), origin_printer(std::move(origin_printer)) {} | |
| std::ostream& PrintTree(std::ostream& stream, int indent = 0) const; | |
| // The (sub)tree to display. | |
| const TokenPartitionTree& node; | |
| // If true, display inter-token information. | |
| bool verbose; | |
| UnwrappedLine::OriginPrinterFunction origin_printer; | |
| }; | |
| std::ostream& operator<<(std::ostream& stream, | |
| const TokenPartitionTreePrinter& printer); | |
| // Returns true if any substring range spanned by the token partition 'range' is | |
| // disabled by 'disabled_byte_ranges'. | |
| // 'full_text' is the original string_view that spans the text from which | |
| // tokens were lexed, and is used in byte-offset calculation. | |
| bool AnyPartitionSubRangeIsDisabled(TokenPartitionRange range, | |
| absl::string_view full_text, | |
| const ByteOffsetSet& disabled_byte_ranges); | |
| // Return ranges of subpartitions separated by blank lines. | |
| // This does not modify the partition, but does return ranges of mutable | |
| // iterators of partitions. | |
| std::vector<TokenPartitionRange> GetSubpartitionsBetweenBlankLines( | |
| const TokenPartitionRange&); | |
| // Transformations (modifying): | |
| // Adds or removes a constant amount of indentation from entire token | |
| // partition tree. Relative indentation amount may be positive or negative. | |
| // Final indentation will be at least 0, and never go negative. | |
| void AdjustIndentationRelative(TokenPartitionTree* tree, int amount); | |
| // Adjusts indentation to align root of partition tree to new indentation | |
| // amount. | |
| void AdjustIndentationAbsolute(TokenPartitionTree* tree, int amount); | |
| // Returns the range of text spanned by tokens range. | |
| absl::string_view StringSpanOfTokenRange(const FormatTokenRange& range); | |
| // Mark ranges of tokens (corresponding to formatting-disabled lines) to | |
| // have their original spacing preserved, except allow the first token | |
| // to follow the formatter's calculated indentation. | |
| void IndentButPreserveOtherSpacing(TokenPartitionRange partition_range, | |
| absl::string_view full_text, | |
| std::vector<PreFormatToken>* ftokens); | |
| // Finalizes formatting of a partition with kAlreadyFormatted policy and | |
| // optional kTokenModifer subpartitions. | |
| // Spacing described by the partitions is applied to underlying tokens. All | |
| // subpartitions of the passed node are removed. | |
| void ApplyAlreadyFormattedPartitionPropertiesToTokens( | |
| TokenPartitionTree* already_formatted_partition_node, | |
| std::vector<PreFormatToken>* ftokens); | |
| // Merges the two subpartitions of tree at index pos and pos+1. | |
| void MergeConsecutiveSiblings(TokenPartitionTree* tree, size_t pos); | |
| // Groups this leaf with the leaf partition that preceded it, which could be | |
| // a distant relative. The grouping node is created in place of the preceding | |
| // leaf. | |
| // Both grouped partitions retain their indentation level and partition | |
| // policies. The group partition's indentation level and partition policy is | |
| // copied from the first partition in the group. | |
| // Returns the grouped partition if operation succeeded, else nullptr. | |
| TokenPartitionTree* GroupLeafWithPreviousLeaf(TokenPartitionTree* leaf); | |
| // Merges this leaf into the leaf partition that preceded it, which could be | |
| // a distant relative. The leaf is destroyed in the process. | |
| // The destination partition retains its indentation level and partition | |
| // policies, but those of the leaf are discarded. | |
| // (If you need that information, save it before moving the leaf.) | |
| // Returns the parent of the leaf partition that was moved if the move | |
| // occurred, else nullptr. | |
| TokenPartitionTree* MergeLeafIntoPreviousLeaf(TokenPartitionTree* leaf); | |
| // Merges this leaf into the leaf partition that follows it, which could be | |
| // a distant relative. The leaf is destroyed in the process. | |
| // The destination partition retains its indentation level and partition | |
| // policies, but those of the leaf are discarded. | |
| // (If you need that information, save it before moving the leaf.) | |
| // Returns the parent of the leaf partition that was moved if the move | |
| // occurred, else nullptr. | |
| TokenPartitionTree* MergeLeafIntoNextLeaf(TokenPartitionTree* leaf); | |
| // Evaluates two partitioning schemes wrapped and appended first | |
| // subpartition. Then reshapes node tree according to scheme with less | |
| // grouping nodes (if both have same number of grouping nodes uses one | |
| // with appended first subpartition). | |
| // | |
| // Example input: | |
| // -------------- | |
| // { (>>[<auto>]) @{1,0}, policy: append-fitting-sub-partitions | |
| // { (>>[function fffffffffff (]) } | |
| // { (>>>>>>[<auto>]) @{1,0,1}, policy: fit-else-expand | |
| // { (>>>>>>[type_a aaaa ,]) } | |
| // { (>>>>>>[type_b bbbbb ,]) } | |
| // { (>>>>>>[type_c cccccc ,]) } | |
| // { (>>>>>>[type_d dddddddd ,]) } | |
| // { (>>>>>>[type_e eeeeeeee ,]) } | |
| // { (>>>>>>[type_f ffff ) ;]) } | |
| // } | |
| // } | |
| // | |
| // The special case of a singleton argument being flattened is also supported. | |
| // -------------- | |
| // { (>>[<auto>]) @{1,0}, policy: append-fitting-sub-partitions | |
| // { (>>[function fffffffffff (]) } | |
| // { (>>>>>>[type_a aaaa ) ;]) } | |
| // } | |
| // | |
| // Example outputs: | |
| // ---------------- | |
| // | |
| // style.column_limit = 100: | |
| // | |
| // { (>>[<auto>]) @{1,0}, policy: append-fitting-sub-partitions | |
| // { (>>[<auto>]) @{1,0,0}, policy: fit-else-expand | |
| // { (>>[function fffffffffff (]) } | |
| // { (>>>>>>[type_a aaaa ,]) } | |
| // { (>>>>>>[type_b bbbbb ,]) } | |
| // { (>>>>>>[type_c cccccc ,]) } | |
| // { (>>>>>>[type_d dddddddd ,]) } | |
| // { (>>>>>>[type_e eeeeeeee ,]) } | |
| // } | |
| // { (>>>>>>>>>>>>>>>>>>>>>>>[<auto>]) @{1,0,1}, policy: fit-else-expand | |
| // { (>>>>>>[type_f ffff ) ;]) } | |
| // } | |
| // } | |
| // | |
| // style.column_limit = 56: | |
| // | |
| // { (>>[<auto>]) @{1,0}, policy: append-fitting-sub-partitions | |
| // { (>>[<auto>]) @{1,0,0}, policy: fit-else-expand | |
| // { (>>[function fffffffffff (]) } | |
| // { (>>>>>>[type_a aaaa ,]) } | |
| // { (>>>>>>[type_b bbbbb ,]) } | |
| // } | |
| // { (>>>>>>>>>>>>>>>>>>>>>>>[<auto>]) @{1,0,1}, policy: fit-else-expand | |
| // { (>>>>>>[type_c cccccc ,]) } | |
| // { (>>>>>>[type_d dddddddd ,]) } | |
| // } | |
| // { (>>>>>>>>>>>>>>>>>>>>>>>[<auto>]) @{1,0,2}, policy: fit-else-expand | |
| // { (>>>>>>[type_e eeeeeeee ,]) } | |
| // { (>>>>>>[type_f ffff ) ;]) } | |
| // } | |
| // } | |
| // | |
| // style.column_limit = 40: | |
| // | |
| // { (>>[<auto>]) @{1,0}, policy: append-fitting-sub-partitions | |
| // { (>>[<auto>]) @{1,0,0}, policy: fit-else-expand | |
| // { (>>[function fffffffffff (]) } | |
| // } | |
| // { (>>>>>>[<auto>]) @{1,0,1}, policy: fit-else-expand | |
| // { (>>>>>>[type_a aaaa ,]) } | |
| // { (>>>>>>[type_b bbbbb ,]) } | |
| // } | |
| // { (>>>>>>[<auto>]) @{1,0,2}, policy: fit-else-expand | |
| // { (>>>>>>[type_c cccccc ,]) } | |
| // { (>>>>>>[type_d dddddddd ,]) } | |
| // } | |
| // { (>>>>>>[<auto>]) @{1,0,3}, policy: fit-else-expand | |
| // { (>>>>>>[type_e eeeeeeee ,]) } | |
| // { (>>>>>>[type_f ffff ) ;]) } | |
| // } | |
| // } | |
| void ReshapeFittingSubpartitions(const verible::BasicFormatStyle& style, | |
| TokenPartitionTree* node); | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_FORMATTING_TOKEN_PARTITION_TREE_H_ |