| /* |
| * 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 <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 |