blob: 199ce5dc3cd63de1037de29f9f2dd5932ecbb7dc [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.
// Implementation for LineColumnMap.
#include "common/strings/line_column_map.h"
#include <algorithm> // for binary search
#include <cstddef>
#include <iostream>
#include <iterator>
#include <vector>
#include "absl/strings/string_view.h"
#include "common/strings/utf8.h"
namespace verible {
// Print to the user as 1-based index because that is how lines
// and columns are indexed in every file diagnostic tool.
std::ostream& operator<<(std::ostream& out, const LineColumn& line_column) {
return out << line_column.line + 1 << ':' << line_column.column + 1;
}
std::ostream& operator<<(std::ostream& out, const LineColumnRange& r) {
// Unlike 'technical' representation where we point the end pos one past
// the relevant range, for human consumption we want to point to the last
// character.
LineColumn right = r.end;
right.column--;
out << r.start;
// Only if we cover more than a single character, print range of columns.
if (r.start.line == right.line) {
if (right.column > r.start.column) out << '-' << right.column + 1;
} else {
// TODO(hzeller): what is a good format to mark a range of multiple lines
// that is understood by common editors ?
out << ':' << right;
}
out << ':';
return out;
}
// Records locations of line breaks, which can then be used to translate
// offsets into line:column numbers.
// Offsets are guaranteed to be monotonically increasing (sorted), and
// thus, are binary-searchable.
LineColumnMap::LineColumnMap(absl::string_view text) {
// The column number after every line break is 0.
// The first line always starts at offset 0.
beginning_of_line_offsets_.push_back(0);
auto offset = text.find('\n');
while (offset != absl::string_view::npos) {
beginning_of_line_offsets_.push_back(offset + 1);
offset = text.find('\n', offset + 1);
}
// If the text does not end with a \n (POSIX), don't implicitly behave as if
// there were one.
}
// Constructor that calculates line break offsets given an already-split
// set of lines for a body of text.
LineColumnMap::LineColumnMap(const std::vector<absl::string_view>& lines) {
size_t offset = 0;
for (const auto& line : lines) {
beginning_of_line_offsets_.push_back(offset);
offset += line.length() + 1;
}
}
LineColumn LineColumnMap::GetLineColAtOffset(absl::string_view base,
int bytes_offset) const {
const auto begin = beginning_of_line_offsets_.begin();
const auto end = beginning_of_line_offsets_.end();
// std::upper_bound is a binary search.
const auto line_at_offset = std::upper_bound(begin, end, bytes_offset) - 1;
const int line_number = std::distance(begin, line_at_offset);
const int len_within_line = bytes_offset - *line_at_offset;
absl::string_view line = base.substr(*line_at_offset, len_within_line);
return LineColumn{line_number, utf8_len(line)};
}
int LineColumnMap::LineAtOffset(int bytes_offset) const {
const auto begin = beginning_of_line_offsets_.begin();
const auto end = beginning_of_line_offsets_.end();
// std::upper_bound is a binary search.
const auto line_at_offset = std::upper_bound(begin, end, bytes_offset) - 1;
return std::distance(begin, line_at_offset);
}
} // namespace verible