Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -81,6 +81,12 @@ /// Include completions that require small corrections, e.g. change '.' to /// '->' on member access etc. bool IncludeFixIts = false; + + /// Enables cursor to be moved around according to completions needs even when + /// snippets are disabled. For example selecting a function with parameters as + /// completion item moves the cursor within the parameters, whereas without + /// parameters moves to after closing parantheses. + bool EnableCursorRelocation = false; }; // Semi-structured representation of a code-complete suggestion for our C++ API. Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -1373,11 +1373,21 @@ } if (Opts.EnableSnippets) LSP.textEdit->newText += SnippetSuffix; + // Check if we have cursor relocation enabled and completing a function. + else if (SnippetSuffix.size() && Opts.EnableCursorRelocation) { + // Check whether function has any parameters or not. + if (SnippetSuffix.size() > 2) + LSP.textEdit->newText += "(${0})"; + else + LSP.textEdit->newText += "()"; + } + // FIXME(kadircet): Do not even fill insertText after making sure textEdit is // compatible with most of the editors. LSP.insertText = LSP.textEdit->newText; - LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet - : InsertTextFormat::PlainText; + LSP.insertTextFormat = Opts.EnableSnippets || Opts.EnableCursorRelocation + ? InsertTextFormat::Snippet + : InsertTextFormat::PlainText; if (HeaderInsertion) LSP.additionalTextEdits.push_back(*HeaderInsertion); return LSP; Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -1590,6 +1590,44 @@ ElementsAre(Sig("foo(T, U) -> void", {"T", "U"}))); } +TEST(CompletionTest, RenderWithCursorRelocation) { + CodeCompleteOptions Opts; + Opts.EnableCursorRelocation = true; + + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = ""; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } + + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = "()"; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x()"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } + + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = "(${0:bool})"; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x(${0})"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } +} + } // namespace } // namespace clangd } // namespace clang