summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-06-23 14:56:28 +0300
committervitya-smirnov <[email protected]>2025-06-23 15:43:07 +0300
commit8174698515f702eddb4e32490cd10cd65d0bc936 (patch)
tree2fe32c6880723cc129d1e13e9cf86632ed130aed /yql/essentials/sql
parent0133b00bb26b7f9040d1ba71da735b68e4e2a2c0 (diff)
YQL-19747: Set cursor shift at completion item
Set `TCandidate::Shift` for functions and generic types. So now brackets are balanced and UI should adopt it. YDB CLI is ready for the update and just cut off symbols after an expected cursor position. commit_hash:9efc1110869af7be618b841c6c132572b61046a1
Diffstat (limited to 'yql/essentials/sql')
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp62
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp212
2 files changed, 158 insertions, 116 deletions
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 187756c6975..8b732bd3913 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -209,7 +209,18 @@ namespace NSQLComplete {
if constexpr (std::is_base_of_v<TKeyword, T>) {
TVector<TString>& seq = context.Keywords[name.Content];
seq.insert(std::begin(seq), name.Content);
- return {ECandidateKind::Keyword, FormatKeywords(seq)};
+
+ TCandidate candidate = {
+ .Kind = ECandidateKind::Keyword,
+ .Content = FormatKeywords(seq),
+ };
+
+ if (candidate.Content.EndsWith('(')) {
+ candidate.Content += ')';
+ candidate.CursorShift = 1;
+ }
+
+ return candidate;
}
if constexpr (std::is_base_of_v<TPragmaName, T>) {
@@ -217,22 +228,37 @@ namespace NSQLComplete {
}
if constexpr (std::is_base_of_v<TTypeName, T>) {
+ TCandidate candidate = {
+ .Kind = ECandidateKind::TypeName,
+ .Content = std::move(name.Indentifier),
+ };
+
switch (name.Kind) {
case TTypeName::EKind::Simple: {
} break;
case TTypeName::EKind::Container: {
- name.Indentifier += "<";
+ candidate.Content += "<>";
+ candidate.CursorShift = 1;
} break;
case TTypeName::EKind::Parameterized: {
- name.Indentifier += "(";
+ candidate.Content += "()";
+ candidate.CursorShift = 1;
} break;
}
- return {ECandidateKind::TypeName, std::move(name.Indentifier)};
+
+ return candidate;
}
if constexpr (std::is_base_of_v<TFunctionName, T>) {
- name.Indentifier += "(";
- return {ECandidateKind::FunctionName, std::move(name.Indentifier)};
+ TCandidate candidate = {
+ .Kind = ECandidateKind::FunctionName,
+ .Content = std::move(name.Indentifier),
+ };
+
+ candidate.Content += "()";
+ candidate.CursorShift = 1;
+
+ return candidate;
}
if constexpr (std::is_base_of_v<THintName, T>) {
@@ -240,11 +266,23 @@ namespace NSQLComplete {
}
if constexpr (std::is_base_of_v<TFolderName, T>) {
- name.Indentifier.append('/');
+ TCandidate candidate = {
+ .Kind = ECandidateKind::FolderName,
+ .Content = std::move(name.Indentifier),
+ };
+
if (!context.IsQuoted.AtLhs) {
- name.Indentifier.prepend('`');
+ candidate.Content.prepend('`');
+ }
+
+ candidate.Content.append('/');
+
+ if (!context.IsQuoted.AtRhs) {
+ candidate.Content.append('`');
+ candidate.CursorShift = 1;
}
- return {ECandidateKind::FolderName, std::move(name.Indentifier)};
+
+ return candidate;
}
if constexpr (std::is_base_of_v<TTableName, T>) {
@@ -396,5 +434,9 @@ void Out<NSQLComplete::ECandidateKind>(IOutputStream& out, NSQLComplete::ECandid
template <>
void Out<NSQLComplete::TCandidate>(IOutputStream& out, const NSQLComplete::TCandidate& value) {
- out << "{" << value.Kind << ", \"" << value.Content << "\"}";
+ out << "{" << value.Kind << ", \"" << value.Content << "\"";
+ if (value.CursorShift != 0) {
+ out << ", " << value.CursorShift;
+ }
+ out << "}";
}
diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
index e9dfad58282..d18cac6305b 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -233,10 +233,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {FolderName, "`.sys/"},
- {FolderName, "`local/"},
- {FolderName, "`prod/"},
- {FolderName, "`test/"},
+ {FolderName, "`.sys/`", 1},
+ {FolderName, "`local/`", 1},
+ {FolderName, "`prod/`", 1},
+ {FolderName, "`test/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -298,10 +298,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {FolderName, "`.sys/"},
- {FolderName, "`local/"},
- {FolderName, "`prod/"},
- {FolderName, "`test/"},
+ {FolderName, "`.sys/`", 1},
+ {FolderName, "`local/`", 1},
+ {FolderName, "`prod/`", 1},
+ {FolderName, "`test/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "IF NOT EXISTS"},
@@ -347,10 +347,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
Y_UNIT_TEST(DropObject) {
TVector<TCandidate> expected = {
- {FolderName, "`.sys/"},
- {FolderName, "`local/"},
- {FolderName, "`prod/"},
- {FolderName, "`test/"},
+ {FolderName, "`.sys/`", 1},
+ {FolderName, "`local/`", 1},
+ {FolderName, "`prod/`", 1},
+ {FolderName, "`test/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "IF EXISTS"},
@@ -500,41 +500,41 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
Y_UNIT_TEST(Select) {
TVector<TCandidate> expected = {
{Keyword, "ALL"},
- {Keyword, "BITCAST("},
+ {Keyword, "BITCAST()", 1},
{Keyword, "CASE"},
- {Keyword, "CAST("},
+ {Keyword, "CAST()", 1},
{Keyword, "CURRENT_DATE"},
{Keyword, "CURRENT_TIME"},
{Keyword, "CURRENT_TIMESTAMP"},
- {TypeName, "Callable<"},
+ {TypeName, "Callable<>", 1},
{Keyword, "DISTINCT"},
- {FunctionName, "DateTime::Split("},
- {TypeName, "Decimal("},
- {TypeName, "Dict<"},
+ {FunctionName, "DateTime::Split()", 1},
+ {TypeName, "Decimal()", 1},
+ {TypeName, "Dict<>", 1},
{Keyword, "EMPTY_ACTION"},
- {Keyword, "EXISTS("},
- {TypeName, "Enum<"},
+ {Keyword, "EXISTS()", 1},
+ {TypeName, "Enum<>", 1},
{Keyword, "FALSE"},
- {TypeName, "Flow<"},
- {Keyword, "JSON_EXISTS("},
- {Keyword, "JSON_QUERY("},
- {Keyword, "JSON_VALUE("},
- {TypeName, "List<"},
+ {TypeName, "Flow<>", 1},
+ {Keyword, "JSON_EXISTS()", 1},
+ {Keyword, "JSON_QUERY()", 1},
+ {Keyword, "JSON_VALUE()", 1},
+ {TypeName, "List<>", 1},
{Keyword, "NOT"},
{Keyword, "NULL"},
- {TypeName, "Optional<"},
- {FunctionName, "Python::__private("},
- {TypeName, "Resource<"},
+ {TypeName, "Optional<>", 1},
+ {FunctionName, "Python::__private()", 1},
+ {TypeName, "Resource<>", 1},
{Keyword, "STREAM"},
- {TypeName, "Set<"},
- {FunctionName, "StartsWith("},
- {TypeName, "Stream<"},
- {TypeName, "Struct<"},
+ {TypeName, "Set<>", 1},
+ {FunctionName, "StartsWith()", 1},
+ {TypeName, "Stream<>", 1},
+ {TypeName, "Struct<>", 1},
{Keyword, "TRUE"},
- {TypeName, "Tagged<"},
- {TypeName, "Tuple<"},
+ {TypeName, "Tagged<>", 1},
+ {TypeName, "Tuple<>", 1},
{TypeName, "Uint64"},
- {TypeName, "Variant<"},
+ {TypeName, "Variant<>", 1},
};
auto engine = MakeSqlCompletionEngineUT();
@@ -546,10 +546,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {FolderName, "`.sys/"},
- {FolderName, "`local/"},
- {FolderName, "`prod/"},
- {FolderName, "`test/"},
+ {FolderName, "`.sys/`", 1},
+ {FolderName, "`local/`", 1},
+ {FolderName, "`prod/`", 1},
+ {FolderName, "`test/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -559,7 +559,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
{
TString input = "SELECT * FROM pr";
TVector<TCandidate> expected = {
- {FolderName, "`prod/"},
+ {FolderName, "`prod/`", 1},
};
TCompletion actual = engine->Complete(SharpedInput(input));
UNIT_ASSERT_VALUES_EQUAL(actual.Candidates, expected);
@@ -567,10 +567,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {FolderName, ".sys/"},
- {FolderName, "local/"},
- {FolderName, "prod/"},
- {FolderName, "test/"},
+ {FolderName, ".sys/`", 1},
+ {FolderName, "local/`", 1},
+ {FolderName, "prod/`", 1},
+ {FolderName, "test/`", 1},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `#"), expected);
}
@@ -631,17 +631,17 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {FolderName, ".sys/"},
- {FolderName, "local/"},
- {FolderName, "prod/"},
- {FolderName, "test/"},
+ {FolderName, ".sys/`", 1},
+ {FolderName, "local/`", 1},
+ {FolderName, "prod/`", 1},
+ {FolderName, "test/`", 1},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `#"), expected);
}
{
TVector<TCandidate> expected = {
{TableName, "meta`"},
- {FolderName, "service/"},
+ {FolderName, "service/`", 1},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `test/"), expected);
}
@@ -696,7 +696,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
{
TVector<TCandidate> expected = {
{TableName, "`people`"},
- {FolderName, "`yql/"},
+ {FolderName, "`yql/`", 1},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "USE yt:saurus; SELECT * FROM example."), expected);
}
@@ -729,7 +729,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
TVector<TCandidate> expected = {
{BindingName, "$action"},
{TableName, "`people`"},
- {FolderName, "`yql/"},
+ {FolderName, "`yql/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -749,39 +749,39 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
Y_UNIT_TEST(SelectWhere) {
TVector<TCandidate> expected = {
- {Keyword, "BITCAST("},
+ {Keyword, "BITCAST()", 1},
{Keyword, "CASE"},
- {Keyword, "CAST("},
+ {Keyword, "CAST()", 1},
{Keyword, "CURRENT_DATE"},
{Keyword, "CURRENT_TIME"},
{Keyword, "CURRENT_TIMESTAMP"},
- {TypeName, "Callable<"},
- {FunctionName, "DateTime::Split("},
- {TypeName, "Decimal("},
- {TypeName, "Dict<"},
+ {TypeName, "Callable<>", 1},
+ {FunctionName, "DateTime::Split()", 1},
+ {TypeName, "Decimal()", 1},
+ {TypeName, "Dict<>", 1},
{Keyword, "EMPTY_ACTION"},
- {Keyword, "EXISTS("},
- {TypeName, "Enum<"},
+ {Keyword, "EXISTS()", 1},
+ {TypeName, "Enum<>", 1},
{Keyword, "FALSE"},
- {TypeName, "Flow<"},
- {Keyword, "JSON_EXISTS("},
- {Keyword, "JSON_QUERY("},
- {Keyword, "JSON_VALUE("},
- {TypeName, "List<"},
+ {TypeName, "Flow<>", 1},
+ {Keyword, "JSON_EXISTS()", 1},
+ {Keyword, "JSON_QUERY()", 1},
+ {Keyword, "JSON_VALUE()", 1},
+ {TypeName, "List<>", 1},
{Keyword, "NOT"},
{Keyword, "NULL"},
- {TypeName, "Optional<"},
- {FunctionName, "Python::__private("},
- {TypeName, "Resource<"},
- {TypeName, "Set<"},
- {FunctionName, "StartsWith("},
- {TypeName, "Stream<"},
- {TypeName, "Struct<"},
+ {TypeName, "Optional<>", 1},
+ {FunctionName, "Python::__private()", 1},
+ {TypeName, "Resource<>", 1},
+ {TypeName, "Set<>", 1},
+ {FunctionName, "StartsWith()", 1},
+ {TypeName, "Stream<>", 1},
+ {TypeName, "Struct<>", 1},
{Keyword, "TRUE"},
- {TypeName, "Tagged<"},
- {TypeName, "Tuple<"},
+ {TypeName, "Tagged<>", 1},
+ {TypeName, "Tuple<>", 1},
{TypeName, "Uint64"},
- {TypeName, "Variant<"},
+ {TypeName, "Variant<>", 1},
};
auto engine = MakeSqlCompletionEngineUT();
@@ -802,10 +802,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {FolderName, "`.sys/"},
- {FolderName, "`local/"},
- {FolderName, "`prod/"},
- {FolderName, "`test/"},
+ {FolderName, "`.sys/`", 1},
+ {FolderName, "`local/`", 1},
+ {FolderName, "`prod/`", 1},
+ {FolderName, "`test/`", 1},
{ClusterName, "example"},
{ClusterName, "saurus"},
};
@@ -822,21 +822,21 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
Y_UNIT_TEST(TypeName) {
TVector<TCandidate> expected = {
- {TypeName, "Callable<"},
- {TypeName, "Decimal("},
- {TypeName, "Dict<"},
- {TypeName, "Enum<"},
- {TypeName, "Flow<"},
- {TypeName, "List<"},
- {TypeName, "Optional<"},
- {TypeName, "Resource<"},
- {TypeName, "Set<"},
- {TypeName, "Stream<"},
- {TypeName, "Struct<"},
- {TypeName, "Tagged<"},
- {TypeName, "Tuple<"},
+ {TypeName, "Callable<>", 1},
+ {TypeName, "Decimal()", 1},
+ {TypeName, "Dict<>", 1},
+ {TypeName, "Enum<>", 1},
+ {TypeName, "Flow<>", 1},
+ {TypeName, "List<>", 1},
+ {TypeName, "Optional<>", 1},
+ {TypeName, "Resource<>", 1},
+ {TypeName, "Set<>", 1},
+ {TypeName, "Stream<>", 1},
+ {TypeName, "Struct<>", 1},
+ {TypeName, "Tagged<>", 1},
+ {TypeName, "Tuple<>", 1},
{TypeName, "Uint64"},
- {TypeName, "Variant<"},
+ {TypeName, "Variant<>", 1},
};
auto engine = MakeSqlCompletionEngineUT();
@@ -856,7 +856,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {TypeName, "Optional<"},
+ {TypeName, "Optional<>", 1},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT Nothing(Option"), expected);
}
@@ -866,7 +866,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {FunctionName, "DateTime::Split("},
+ {FunctionName, "DateTime::Split()", 1},
};
auto completion = engine->CompleteAsync({"SELECT Date"}).GetValueSync();
UNIT_ASSERT_VALUES_EQUAL(completion.Candidates, expected);
@@ -874,14 +874,14 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {FunctionName, "Split("},
+ {FunctionName, "Split()", 1},
};
auto completion = engine->CompleteAsync({"SELECT DateTime:"}).GetValueSync();
UNIT_ASSERT(completion.Candidates.empty());
}
{
TVector<TCandidate> expected = {
- {FunctionName, "Split("},
+ {FunctionName, "Split()", 1},
};
auto completion = engine->CompleteAsync({"SELECT DateTime::"}).GetValueSync();
UNIT_ASSERT_VALUES_EQUAL(completion.Candidates, expected);
@@ -889,7 +889,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {FunctionName, "Split("},
+ {FunctionName, "Split()", 1},
};
auto completion = engine->CompleteAsync({"SELECT DateTime::s"}).GetValueSync();
UNIT_ASSERT_VALUES_EQUAL(completion.Candidates, expected);
@@ -989,7 +989,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT 21#21"), empty);
UNIT_ASSERT(FindPtr(Complete(engine, "SELECT `name`#"), TCandidate{Keyword, "FROM"}) != nullptr);
- UNIT_ASSERT(FindPtr(Complete(engine, "SELECT #`name`"), TCandidate{FunctionName, "StartsWith("}) != nullptr);
+ UNIT_ASSERT(FindPtr(Complete(engine, "SELECT #`name`"), TCandidate{FunctionName, "StartsWith()", 1}) != nullptr);
UNIT_ASSERT_GT_C(Complete(engine, "SELECT \"a\"#\"b\"").size(), 0, "Between tokens");
UNIT_ASSERT_VALUES_EQUAL_C(Complete(engine, "SELECT `a`#`b`"), empty, "Solid ID_QUOTED");
@@ -1314,14 +1314,14 @@ JOIN yt:$cluster_name.test;
}
{
TVector<TCandidate> expected = {
- {FunctionName, "Min("},
- {FunctionName, "Max("},
- {FunctionName, "MaxOf("},
- {FunctionName, "MaxBy("},
- {FunctionName, "MinBy("},
- {FunctionName, "Math::Abs("},
- {FunctionName, "Math::Acos("},
- {FunctionName, "Math::Asin("},
+ {FunctionName, "Min()", 1},
+ {FunctionName, "Max()", 1},
+ {FunctionName, "MaxOf()", 1},
+ {FunctionName, "MaxBy()", 1},
+ {FunctionName, "MinBy()", 1},
+ {FunctionName, "Math::Abs()", 1},
+ {FunctionName, "Math::Acos()", 1},
+ {FunctionName, "Math::Asin()", 1},
};
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, "SELECT m"), expected);
}