// Copyright 2020 The Tint 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. #include "src/tint/source.h" #include #include #include #include #include "src/tint/text/unicode.h" namespace tint { namespace { bool ParseLineBreak(std::string_view str, size_t i, bool* is_line_break, size_t* line_break_size) { // See https://www.w3.org/TR/WGSL/#blankspace auto* utf8 = reinterpret_cast(&str[i]); auto [cp, n] = text::utf8::Decode(utf8, str.size() - i); if (n == 0) { return false; } static const auto kLF = text::CodePoint(0x000A); // line feed static const auto kVTab = text::CodePoint(0x000B); // vertical tab static const auto kFF = text::CodePoint(0x000C); // form feed static const auto kNL = text::CodePoint(0x0085); // next line static const auto kCR = text::CodePoint(0x000D); // carriage return static const auto kLS = text::CodePoint(0x2028); // line separator static const auto kPS = text::CodePoint(0x2029); // parargraph separator if (cp == kLF || cp == kVTab || cp == kFF || cp == kNL || cp == kPS || cp == kLS) { *is_line_break = true; *line_break_size = n; return true; } // Handle CRLF as one line break, and CR alone as one line break if (cp == kCR) { *is_line_break = true; *line_break_size = n; if (auto next_i = i + n; next_i < str.size()) { auto* next_utf8 = reinterpret_cast(&str[next_i]); auto [next_cp, next_n] = text::utf8::Decode(next_utf8, str.size() - next_i); if (next_n == 0) { return false; } if (next_cp == kLF) { // CRLF as one break *line_break_size = n + next_n; } } return true; } *is_line_break = false; return true; } std::vector SplitLines(std::string_view str) { std::vector lines; size_t lineStart = 0; for (size_t i = 0; i < str.size();) { bool is_line_break{}; size_t line_break_size{}; // We don't handle decode errors from ParseLineBreak. Instead, we rely on // the Lexer to do so. ParseLineBreak(str, i, &is_line_break, &line_break_size); if (is_line_break) { lines.push_back(str.substr(lineStart, i - lineStart)); i += line_break_size; lineStart = i; } else { ++i; } } if (lineStart < str.size()) { lines.push_back(str.substr(lineStart)); } return lines; } std::vector CopyRelativeStringViews( const std::vector& src_list, const std::string_view& src_view, const std::string_view& dst_view) { std::vector out(src_list.size()); for (size_t i = 0; i < src_list.size(); i++) { auto offset = static_cast(&src_list[i].front() - &src_view.front()); auto count = src_list[i].length(); out[i] = dst_view.substr(offset, count); } return out; } } // namespace Source::FileContent::FileContent(const std::string& body) : data(body), data_view(data), lines(SplitLines(data_view)) {} Source::FileContent::FileContent(const FileContent& rhs) : data(rhs.data), data_view(data), lines(CopyRelativeStringViews(rhs.lines, rhs.data_view, data_view)) {} Source::FileContent::~FileContent() = default; Source::File::~File() = default; std::ostream& operator<<(std::ostream& out, const Source& source) { auto rng = source.range; if (source.file) { out << source.file->path << ":"; } if (rng.begin.line) { out << rng.begin.line << ":"; if (rng.begin.column) { out << rng.begin.column; } if (source.file) { out << std::endl << std::endl; auto repeat = [&](char c, size_t n) { while (n--) { out << c; } }; for (size_t line = rng.begin.line; line <= rng.end.line; line++) { if (line < source.file->content.lines.size() + 1) { auto len = source.file->content.lines[line - 1].size(); out << source.file->content.lines[line - 1]; out << std::endl; if (line == rng.begin.line && line == rng.end.line) { // Single line repeat(' ', rng.begin.column - 1); repeat('^', std::max(rng.end.column - rng.begin.column, 1)); } else if (line == rng.begin.line) { // Start of multi-line repeat(' ', rng.begin.column - 1); repeat('^', len - (rng.begin.column - 1)); } else if (line == rng.end.line) { // End of multi-line repeat('^', rng.end.column - 1); } else { // Middle of multi-line repeat('^', len); } out << std::endl; } } } } return out; } } // namespace tint