diff options
author | vitya-smirnov <[email protected]> | 2025-06-23 14:56:28 +0300 |
---|---|---|
committer | vitya-smirnov <[email protected]> | 2025-06-23 15:43:07 +0300 |
commit | 8174698515f702eddb4e32490cd10cd65d0bc936 (patch) | |
tree | 2fe32c6880723cc129d1e13e9cf86632ed130aed | |
parent | 0133b00bb26b7f9040d1ba71da735b68e4e2a2c0 (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
-rw-r--r-- | yql/essentials/sql/v1/complete/sql_complete.cpp | 62 | ||||
-rw-r--r-- | yql/essentials/sql/v1/complete/sql_complete_ut.cpp | 212 |
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); } |