blob: 8d3d33a2c6bcff7d091a2186c223d35befc3f936 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fidl/source_file.h"
#include <assert.h>
#include <zircon/assert.h>
#include <algorithm>
#include <functional>
namespace fidl {
SourceFile::SourceFile(std::string filename, std::string data)
: filename_(std::move(filename)), data_(std::move(data)) {
size_t size = 0u;
auto start_of_line = data_.cbegin();
for (auto it = data_.cbegin(); it != data_.cend(); ++it) {
if (*it == '\n' || *it == '\0') {
auto& position = *start_of_line;
lines_.emplace_back(&position, size);
size = 0u;
start_of_line = it + 1;
} else {
++size;
}
}
// Include the last line if the file does not end in a newline
if (size > 0u) {
ZX_ASSERT(start_of_line != data_.cend());
auto& position = *start_of_line;
lines_.emplace_back(&position, size);
}
}
SourceFile::~SourceFile() = default;
std::string_view SourceFile::LineContaining(std::string_view view, Position* position_out) const {
auto ptr_less_equal = std::less_equal<const char*>();
const char* file_data = data().data();
size_t file_size = data().size();
if (view.data() == file_data + file_size) {
// Gracefully handle a view indicating the end of the file
//
// Assert that this view is either zero-sized or references the null-terminating character
// in the std::string.
ZX_ASSERT_MSG(view.size() <= 1, "the view goes beyond the end of the SourceFile");
if (file_size == 0) {
*position_out = {1, 1};
return std::string_view(view.data(), 0);
}
ZX_ASSERT_MSG(!lines_.empty(), "file size is greater than 0 but no lines were parsed");
auto line = lines_.back();
int line_number = static_cast<int>(lines_.size());
int column_number = static_cast<int>(line.size() + 1);
*position_out = {line_number, column_number};
return line;
}
ZX_ASSERT_MSG(ptr_less_equal(file_data, view.data()), "the view is not part of this SourceFile");
ZX_ASSERT_MSG(ptr_less_equal(view.data() + view.size(), file_data + file_size),
"the view is not part of this SourceFile");
// We are looking from the end of the file backwards (hence
// crbegin and crend), looking for the first line (hence
// upper_bound) to start at or before before the token in
// question.
auto is_in_line = [&ptr_less_equal](const std::string_view& left, const std::string_view& right) {
return ptr_less_equal(right.data(), left.data());
};
auto line = std::upper_bound(lines_.crbegin(), lines_.crend(), view, is_in_line);
ZX_ASSERT(line != lines_.crend());
if (position_out != nullptr) {
// Humans number lines from 1. Calculating this from the end
// accounts for this.
int line_number = static_cast<int>(lines_.crend() - line);
// Columns should also start from 1.
int column_number = static_cast<int>(view.data() - line->data()) + 1;
*position_out = {line_number, column_number};
}
return std::string_view(*line);
}
} // namespace fidl