blob: 6a10de523d9eb83730b7ed1a3285759405034022 [file] [log] [blame]
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001// Copyright 2020 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "src/tint/source.h"
16
17#include <algorithm>
18#include <sstream>
19#include <string_view>
20#include <utility>
21
Antonio Maiorano25775302022-04-25 19:49:01 +000022#include "src/tint/text/unicode.h"
23
Ryan Harrisondbc13af2022-02-21 15:19:07 +000024namespace tint {
25namespace {
Antonio Maiorano25775302022-04-25 19:49:01 +000026
dan sinclair41e4d9a2022-05-01 14:40:55 +000027bool ParseLineBreak(std::string_view str, size_t i, bool* is_line_break, size_t* line_break_size) {
28 // See https://www.w3.org/TR/WGSL/#blankspace
Antonio Maiorano25775302022-04-25 19:49:01 +000029
dan sinclair41e4d9a2022-05-01 14:40:55 +000030 auto* utf8 = reinterpret_cast<const uint8_t*>(&str[i]);
31 auto [cp, n] = text::utf8::Decode(utf8, str.size() - i);
Antonio Maiorano25775302022-04-25 19:49:01 +000032
dan sinclair41e4d9a2022-05-01 14:40:55 +000033 if (n == 0) {
Antonio Maiorano25775302022-04-25 19:49:01 +000034 return false;
Antonio Maiorano25775302022-04-25 19:49:01 +000035 }
36
dan sinclair41e4d9a2022-05-01 14:40:55 +000037 static const auto kLF = text::CodePoint(0x000A); // line feed
38 static const auto kVTab = text::CodePoint(0x000B); // vertical tab
39 static const auto kFF = text::CodePoint(0x000C); // form feed
40 static const auto kNL = text::CodePoint(0x0085); // next line
41 static const auto kCR = text::CodePoint(0x000D); // carriage return
42 static const auto kLS = text::CodePoint(0x2028); // line separator
43 static const auto kPS = text::CodePoint(0x2029); // parargraph separator
Antonio Maiorano25775302022-04-25 19:49:01 +000044
dan sinclair41e4d9a2022-05-01 14:40:55 +000045 if (cp == kLF || cp == kVTab || cp == kFF || cp == kNL || cp == kPS || cp == kLS) {
46 *is_line_break = true;
47 *line_break_size = n;
48 return true;
49 }
50
51 // Handle CRLF as one line break, and CR alone as one line break
52 if (cp == kCR) {
53 *is_line_break = true;
54 *line_break_size = n;
55
56 if (auto next_i = i + n; next_i < str.size()) {
57 auto* next_utf8 = reinterpret_cast<const uint8_t*>(&str[next_i]);
58 auto [next_cp, next_n] = text::utf8::Decode(next_utf8, str.size() - next_i);
59
60 if (next_n == 0) {
61 return false;
62 }
63
64 if (next_cp == kLF) {
65 // CRLF as one break
66 *line_break_size = n + next_n;
67 }
68 }
69
70 return true;
71 }
72
73 *is_line_break = false;
74 return true;
Antonio Maiorano25775302022-04-25 19:49:01 +000075}
76
Ryan Harrisondbc13af2022-02-21 15:19:07 +000077std::vector<std::string_view> SplitLines(std::string_view str) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000078 std::vector<std::string_view> lines;
Ryan Harrisondbc13af2022-02-21 15:19:07 +000079
dan sinclair41e4d9a2022-05-01 14:40:55 +000080 size_t lineStart = 0;
81 for (size_t i = 0; i < str.size();) {
82 bool is_line_break{};
83 size_t line_break_size{};
84 // We don't handle decode errors from ParseLineBreak. Instead, we rely on
85 // the Lexer to do so.
86 ParseLineBreak(str, i, &is_line_break, &line_break_size);
87 if (is_line_break) {
88 lines.push_back(str.substr(lineStart, i - lineStart));
89 i += line_break_size;
90 lineStart = i;
91 } else {
92 ++i;
93 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +000094 }
dan sinclair41e4d9a2022-05-01 14:40:55 +000095 if (lineStart < str.size()) {
96 lines.push_back(str.substr(lineStart));
97 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +000098
dan sinclair41e4d9a2022-05-01 14:40:55 +000099 return lines;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000100}
101
dan sinclair41e4d9a2022-05-01 14:40:55 +0000102std::vector<std::string_view> CopyRelativeStringViews(const std::vector<std::string_view>& src_list,
103 const std::string_view& src_view,
104 const std::string_view& dst_view) {
105 std::vector<std::string_view> out(src_list.size());
106 for (size_t i = 0; i < src_list.size(); i++) {
107 auto offset = static_cast<size_t>(&src_list[i].front() - &src_view.front());
108 auto count = src_list[i].length();
109 out[i] = dst_view.substr(offset, count);
110 }
111 return out;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000112}
113
114} // namespace
115
Ben Claytone412c8d2022-11-17 19:16:24 +0000116Source::FileContent::FileContent(const std::string& body) : data(body), lines(SplitLines(data)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000117
118Source::FileContent::FileContent(const FileContent& rhs)
Ben Claytone412c8d2022-11-17 19:16:24 +0000119 : data(rhs.data), lines(CopyRelativeStringViews(rhs.lines, rhs.data, data)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000120
121Source::FileContent::~FileContent() = default;
122
123Source::File::~File() = default;
124
125std::ostream& operator<<(std::ostream& out, const Source& source) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000126 auto rng = source.range;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000127
128 if (source.file) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000129 out << source.file->path << ":";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000130 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000131 if (rng.begin.line) {
132 out << rng.begin.line << ":";
133 if (rng.begin.column) {
134 out << rng.begin.column;
135 }
136
137 if (source.file) {
138 out << std::endl << std::endl;
139
140 auto repeat = [&](char c, size_t n) {
141 while (n--) {
142 out << c;
143 }
144 };
145
146 for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
147 if (line < source.file->content.lines.size() + 1) {
148 auto len = source.file->content.lines[line - 1].size();
149
150 out << source.file->content.lines[line - 1];
151
152 out << std::endl;
153
154 if (line == rng.begin.line && line == rng.end.line) {
155 // Single line
156 repeat(' ', rng.begin.column - 1);
157 repeat('^', std::max<size_t>(rng.end.column - rng.begin.column, 1));
158 } else if (line == rng.begin.line) {
159 // Start of multi-line
160 repeat(' ', rng.begin.column - 1);
161 repeat('^', len - (rng.begin.column - 1));
162 } else if (line == rng.end.line) {
163 // End of multi-line
164 repeat('^', rng.end.column - 1);
165 } else {
166 // Middle of multi-line
167 repeat('^', len);
168 }
169
170 out << std::endl;
171 }
172 }
173 }
174 }
175 return out;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000176}
177
178} // namespace tint