[tintd] Handle multi-line node spans
Bug: tint:2127
Change-Id: I6d065f91aca4f069cd8b4aa0a366a9503c896d11
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/180485
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/wgsl/ls/file.h b/src/tint/lang/wgsl/ls/file.h
index 7e6c367..7f31ad9 100644
--- a/src/tint/lang/wgsl/ls/file.h
+++ b/src/tint/lang/wgsl/ls/file.h
@@ -28,6 +28,8 @@
#ifndef SRC_TINT_LANG_WGSL_LS_FILE_H_
#define SRC_TINT_LANG_WGSL_LS_FILE_H_
+#include <cstddef>
+#include <cstdint>
#include <limits>
#include <memory>
#include <string>
@@ -98,17 +100,16 @@
template <typename T = sem::Node, UnwrapMode UNWRAP_MODE = DefaultUnwrapMode<T>>
const T* NodeAt(Source::Location l) const {
// TODO(bclayton): This is a brute-force search. Optimize.
- size_t best_len = std::numeric_limits<uint32_t>::max();
+ size_t best_len = std::numeric_limits<size_t>::max();
const T* best_node = nullptr;
for (auto* node : nodes) {
- if (node->source.range.begin.line == node->source.range.end.line &&
- node->source.range.begin <= l && node->source.range.end >= l) {
+ if (node->source.range.begin <= l && node->source.range.end >= l) {
auto* sem = program.Sem().Get(node);
if constexpr (UNWRAP_MODE == UnwrapMode::kUnwrap) {
sem = Unwrap(sem);
}
if (auto* cast = As<T, CastFlags::kDontErrorOnImpossibleCast>(sem)) {
- size_t len = node->source.range.end.column - node->source.range.begin.column;
+ size_t len = node->source.range.Length(source->content);
if (len < best_len) {
best_len = len;
best_node = cast;
diff --git a/src/tint/lang/wgsl/ls/signature_help.cc b/src/tint/lang/wgsl/ls/signature_help.cc
index 401e621..9e0de3e 100644
--- a/src/tint/lang/wgsl/ls/signature_help.cc
+++ b/src/tint/lang/wgsl/ls/signature_help.cc
@@ -27,6 +27,7 @@
#include "src/tint/lang/wgsl/ls/server.h"
+#include "langsvr/lsp/comparators.h"
#include "src/tint/lang/core/intrinsic/table.h"
#include "src/tint/lang/wgsl/intrinsic/dialect.h"
#include "src/tint/lang/wgsl/ls/utils.h"
@@ -56,18 +57,22 @@
return params;
}
-size_t CalcParamIndex(const Source& call_source, const Source::Location& carat) {
+/// @returns the zero-based index of the parameter at with the cursor at @p position, for a call
+/// with the source @p call_source.
+size_t CalcParamIndex(const Source& call_source, const Source::Location& position) {
size_t index = 0;
int depth = 0;
- auto start = call_source.range.begin;
- auto end = std::min(call_source.range.end, carat);
+ auto range = Conv(call_source.range);
+ auto start = range.start;
+ auto end = std::min(range.end, Conv(position));
auto& lines = call_source.file->content.lines;
- for (auto line = start.line; line <= end.line; line++) {
- auto start_column = line == start.line ? start.column : 0;
- auto end_column = line == end.line ? end.column : 0;
- auto text = lines[line - 1].substr(start_column - 1, end_column - start_column);
+ for (auto line_idx = start.line; line_idx <= end.line; line_idx++) {
+ auto& line = lines[line_idx];
+ auto start_character = (line_idx == start.line) ? start.character : 0;
+ auto end_character = (line_idx == end.line) ? end.character : line.size();
+ auto text = line.substr(start_character, end_character - start_character);
for (char c : text) {
switch (c) {
case '(':
diff --git a/src/tint/lang/wgsl/ls/signature_help_test.cc b/src/tint/lang/wgsl/ls/signature_help_test.cc
index ceaf52a..31fcaba 100644
--- a/src/tint/lang/wgsl/ls/signature_help_test.cc
+++ b/src/tint/lang/wgsl/ls/signature_help_test.cc
@@ -271,6 +271,28 @@
}, // =========================================
{
R"(
+const C = max(1
+ ⧘
+ ,
+ 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(1
+ ,
+ ⧘
+ 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
const C = max(1, 2) ⧘;
)",
{},
diff --git a/src/tint/utils/diagnostic/source.cc b/src/tint/utils/diagnostic/source.cc
index 3dc439b..b183f8a 100644
--- a/src/tint/utils/diagnostic/source.cc
+++ b/src/tint/utils/diagnostic/source.cc
@@ -31,6 +31,7 @@
#include <string_view>
#include <utility>
+#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/text/string_stream.h"
#include "src/tint/utils/text/unicode.h"
@@ -190,4 +191,25 @@
return out.str();
}
+size_t Source::Range::Length(const FileContent& content) const {
+ TINT_ASSERT_OR_RETURN_VALUE(begin <= end, 0);
+ TINT_ASSERT_OR_RETURN_VALUE(begin.column > 0, 0);
+ TINT_ASSERT_OR_RETURN_VALUE(begin.line > 0, 0);
+ TINT_ASSERT_OR_RETURN_VALUE(end.line <= 1 + content.lines.size(), 0);
+ TINT_ASSERT_OR_RETURN_VALUE(end.column <= 1 + content.lines[end.line - 1].size(), 0);
+
+ if (end.line == begin.line) {
+ return end.column - begin.column;
+ }
+
+ size_t len = (content.lines[begin.line - 1].size() + 1 - begin.column) + // first line
+ (end.column - 1) + // last line
+ end.line - begin.line; // newlines
+
+ for (size_t line = begin.line + 1; line < end.line; line++) {
+ len += content.lines[line - 1].size(); // whole-lines
+ }
+ return len;
+}
+
} // namespace tint
diff --git a/src/tint/utils/diagnostic/source.h b/src/tint/utils/diagnostic/source.h
index afef58c..e644122 100644
--- a/src/tint/utils/diagnostic/source.h
+++ b/src/tint/utils/diagnostic/source.h
@@ -42,7 +42,7 @@
/// Source describes a range of characters within a source file.
class Source {
public:
- /// FileContent describes the content of a source file encoded using utf-8.
+ /// FileContent describes the content of a source file encoded using UTF-8.
class FileContent {
public:
/// Constructs the FileContent with the given file content.
@@ -151,7 +151,7 @@
inline constexpr Range(const Location& b, const Location& e) : begin(b), end(e) {}
/// Return a column-shifted Range
- /// @param n the number of characters to shift by
+ /// @param n the number of UTF-8 codepoint to shift by
/// @returns a Range with a #begin and #end column shifted by `n`
inline Range operator+(uint32_t n) const {
return Range{{begin.line, begin.column + n}, {end.line, end.column + n}};
@@ -169,9 +169,16 @@
/// @returns true if `this` == `rhs`
inline bool operator!=(const Range& rhs) const { return !(*this == rhs); }
- /// The location of the first character in the range.
+ /// @param content the file content that this range belongs to
+ /// @returns the length of the range in UTF-8 codepoints, treating all line-break sequences
+ /// as a single code-point.
+ /// @see https://www.w3.org/TR/WGSL/#blankspace-and-line-breaks for the definition of a line
+ /// break.
+ size_t Length(const FileContent& content) const;
+
+ /// The location of the first UTF-8 codepoint in the range.
Location begin;
- /// The location of one-past the last character in the range.
+ /// The location of one-past the last UTF-8 codepoint in the range.
Location end;
};
diff --git a/src/tint/utils/diagnostic/source_test.cc b/src/tint/utils/diagnostic/source_test.cc
index cb50053..42b4e01 100644
--- a/src/tint/utils/diagnostic/source_test.cc
+++ b/src/tint/utils/diagnostic/source_test.cc
@@ -28,6 +28,7 @@
#include "src/tint/utils/diagnostic/source.h"
#include <memory>
+#include <string_view>
#include <utility>
#include "gtest/gtest.h"
@@ -35,7 +36,7 @@
namespace tint {
namespace {
-static constexpr const char* kSource = R"(line one
+static constexpr std::string_view kSource = R"(line one
line two
line three)";
@@ -81,7 +82,7 @@
#define kLS "\xE2\x80\xA8"
#define kPS "\xE2\x80\xA9"
-using LineBreakTest = testing::TestWithParam<const char*>;
+using LineBreakTest = testing::TestWithParam<std::string_view>;
TEST_P(LineBreakTest, Single) {
std::string src = "line one";
src += GetParam();
@@ -108,5 +109,50 @@
LineBreakTest,
testing::Values(kVTab, kFF, kNL, kLS, kPS, kLF, kCR, kCR kLF));
+using RangeLengthTest = testing::TestWithParam<std::pair<Source::Range, size_t>>;
+TEST_P(RangeLengthTest, Test) {
+ Source::FileContent fc("X" kLF // 1
+ "XX" kCR kLF // 2
+ "X" kCR // 3
+ kLS // 4
+ "XX" // 5
+ );
+ auto& range = GetParam().first;
+ auto expected_length = GetParam().second;
+ EXPECT_EQ(range.Length(fc), expected_length);
+}
+
+INSTANTIATE_TEST_SUITE_P(SingleLine,
+ RangeLengthTest,
+ testing::Values( //
+
+ std::make_pair(Source::Range{{1, 1}, {1, 1}}, 0),
+ std::make_pair(Source::Range{{2, 1}, {2, 1}}, 0),
+ std::make_pair(Source::Range{{3, 1}, {3, 1}}, 0),
+ std::make_pair(Source::Range{{4, 1}, {4, 1}}, 0),
+ std::make_pair(Source::Range{{5, 1}, {5, 1}}, 0),
+
+ std::make_pair(Source::Range{{1, 1}, {1, 2}}, 1),
+ std::make_pair(Source::Range{{2, 1}, {2, 3}}, 2),
+ std::make_pair(Source::Range{{3, 1}, {3, 2}}, 1),
+ std::make_pair(Source::Range{{5, 1}, {5, 3}}, 2),
+
+ std::make_pair(Source::Range{{1, 2}, {1, 2}}, 0),
+ std::make_pair(Source::Range{{2, 2}, {2, 3}}, 1),
+ std::make_pair(Source::Range{{3, 2}, {3, 2}}, 0),
+ std::make_pair(Source::Range{{5, 2}, {5, 3}}, 1)));
+
+INSTANTIATE_TEST_SUITE_P(MultiLine,
+ RangeLengthTest,
+ testing::Values( //
+
+ std::make_pair(Source::Range{{1, 1}, {2, 1}}, 2),
+ std::make_pair(Source::Range{{2, 1}, {3, 1}}, 3),
+ std::make_pair(Source::Range{{3, 1}, {4, 1}}, 2),
+ std::make_pair(Source::Range{{4, 1}, {5, 1}}, 1),
+
+ std::make_pair(Source::Range{{1, 1}, {5, 3}}, 10),
+ std::make_pair(Source::Range{{2, 2}, {5, 2}}, 6)));
+
} // namespace
} // namespace tint