Source code

Revision control

Copy as Markdown

Other Tools

/*
* Copyright 2017 WebAssembly Community Group participants
*
* 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
*
*
* 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 "wabt/lexer-source-line-finder.h"
#include <algorithm>
#include "wabt/lexer-source.h"
namespace wabt {
LexerSourceLineFinder::LexerSourceLineFinder(
std::unique_ptr<LexerSource> source)
: source_(std::move(source)),
next_line_start_(0),
last_cr_(false),
eof_(false) {
source_->Seek(0);
// Line 0 should not be used; but it makes indexing simpler.
line_ranges_.emplace_back(0, 0);
}
Result LexerSourceLineFinder::GetSourceLine(const Location& loc,
Offset max_line_length,
SourceLine* out_source_line) {
ColumnRange column_range(loc.first_column, loc.last_column);
OffsetRange original;
CHECK_RESULT(GetLineOffsets(loc.line, &original));
OffsetRange clamped =
ClampSourceLineOffsets(original, column_range, max_line_length);
bool has_start_ellipsis = original.start != clamped.start;
bool has_end_ellipsis = original.end != clamped.end;
out_source_line->column_offset = clamped.start - original.start;
if (has_start_ellipsis) {
out_source_line->line += "...";
clamped.start += 3;
}
if (has_end_ellipsis) {
clamped.end -= 3;
}
std::vector<char> read_line;
CHECK_RESULT(source_->ReadRange(clamped, &read_line));
out_source_line->line.append(read_line.begin(), read_line.end());
if (has_end_ellipsis) {
out_source_line->line += "...";
}
return Result::Ok;
}
bool LexerSourceLineFinder::IsLineCached(int line) const {
return static_cast<size_t>(line) < line_ranges_.size();
}
OffsetRange LexerSourceLineFinder::GetCachedLine(int line) const {
assert(IsLineCached(line));
return line_ranges_[line];
}
Result LexerSourceLineFinder::GetLineOffsets(int find_line,
OffsetRange* out_range) {
if (IsLineCached(find_line)) {
*out_range = GetCachedLine(find_line);
return Result::Ok;
}
const size_t kBufferSize = 1 << 16;
std::vector<char> buffer(kBufferSize);
assert(!line_ranges_.empty());
Offset buffer_file_offset = 0;
while (!IsLineCached(find_line) && !eof_) {
CHECK_RESULT(source_->Tell(&buffer_file_offset));
size_t read_size = source_->Fill(buffer.data(), buffer.size());
if (read_size < buffer.size()) {
eof_ = true;
}
for (auto iter = buffer.begin(), end = iter + read_size; iter < end;
++iter) {
if (*iter == '\n') {
// Don't include \n or \r in the line range.
Offset line_offset =
buffer_file_offset + (iter - buffer.begin()) - last_cr_;
line_ranges_.emplace_back(next_line_start_, line_offset);
next_line_start_ = line_offset + last_cr_ + 1;
}
last_cr_ = *iter == '\r';
}
if (eof_) {
// Add the final line as an empty range.
Offset end = buffer_file_offset + read_size;
line_ranges_.emplace_back(next_line_start_, end);
}
}
if (IsLineCached(find_line)) {
*out_range = GetCachedLine(find_line);
return Result::Ok;
} else {
assert(eof_);
return Result::Error;
}
}
// static
OffsetRange LexerSourceLineFinder::ClampSourceLineOffsets(
OffsetRange offset_range,
ColumnRange column_range,
Offset max_line_length) {
Offset line_length = offset_range.size();
if (line_length > max_line_length) {
size_t column_count = column_range.size();
size_t center_on;
if (column_count > max_line_length) {
// The column range doesn't fit, just center on first_column.
center_on = column_range.start - 1;
} else {
// the entire range fits, display it all in the center.
center_on = (column_range.start + column_range.end) / 2 - 1;
}
if (center_on > max_line_length / 2) {
offset_range.start += center_on - max_line_length / 2;
}
offset_range.start =
std::min(offset_range.start, offset_range.end - max_line_length);
offset_range.end = offset_range.start + max_line_length;
}
return offset_range;
}
} // namespace wabt