/* * 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 * * 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 "wabt/lexer-source-line-finder.h" #include #include "wabt/lexer-source.h" namespace wabt { LexerSourceLineFinder::LexerSourceLineFinder( std::unique_ptr 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 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(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 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