feat(completion): snippet insertion for function/method parameters (#412)
## Summary
- Generate LSP snippet placeholders (`${1:param}`, `${2:param}`) for
function and method completions in non-bundle mode
- Controlled by
`CodeCompletionOptions::enable_function_arguments_snippet` (default off)
- No-arg functions produce plain text insertion (no empty snippet)
- Bundle mode is unaffected — snippets only apply when each overload is
a separate item
- Optional chunks (default arguments) are skipped in snippet generation
## Example
```
// Before: typing "fo" and selecting foooo inserts just "foooo"
// After: typing "fo" and selecting foooo inserts "foooo(${1:int x}, ${2:float y})"
```
## Test plan
- [x] `SnippetFunctionArgs` — verifies placeholders are generated
- [x] `SnippetNoArgs` — no-arg functions don't produce snippet
- [x] `SnippetDisabled` — respects the option flag
- [x] `SnippetBundleMode` — bundle mode doesn't generate snippets
- [x] `SnippetMethod` — works for member methods too
- [x] All 494 unit tests pass
- [x] `pixi run format` clean
Stacked on #411.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Code completion now generates function argument snippets with
interactive placeholders, helping users efficiently navigate through
parameters during autocompletion. The feature works with functions and
methods, with configurable options to control behavior for overloaded
scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -237,6 +237,104 @@ int x = fooo$(pos)
|
||||
ASSERT_TRUE(count >= 3);
|
||||
}
|
||||
|
||||
TEST_CASE(SnippetFunctionArgs) {
|
||||
feature::CodeCompletionOptions opts;
|
||||
opts.bundle_overloads = false;
|
||||
opts.enable_function_arguments_snippet = true;
|
||||
code_complete(R"cpp(
|
||||
int foooo(int x, float y);
|
||||
int z = fo$(pos)
|
||||
)cpp",
|
||||
opts);
|
||||
|
||||
auto it = find_item("foooo");
|
||||
ASSERT_TRUE(it != items.end());
|
||||
// Should have snippet format.
|
||||
ASSERT_TRUE(it->insert_text_format.has_value());
|
||||
ASSERT_EQ(*it->insert_text_format, protocol::InsertTextFormat::Snippet);
|
||||
// textEdit should contain placeholders.
|
||||
auto& edit = std::get<protocol::TextEdit>(*it->text_edit);
|
||||
ASSERT_TRUE(edit.new_text.find("${1:") != std::string::npos);
|
||||
ASSERT_TRUE(edit.new_text.find("${2:") != std::string::npos);
|
||||
ASSERT_TRUE(edit.new_text.find("(") != std::string::npos);
|
||||
ASSERT_TRUE(edit.new_text.find(")") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE(SnippetNoArgs) {
|
||||
feature::CodeCompletionOptions opts;
|
||||
opts.bundle_overloads = false;
|
||||
opts.enable_function_arguments_snippet = true;
|
||||
code_complete(R"cpp(
|
||||
void foooo();
|
||||
void bar() { fo$(pos) }
|
||||
)cpp",
|
||||
opts);
|
||||
|
||||
auto it = find_item("foooo");
|
||||
ASSERT_TRUE(it != items.end());
|
||||
// No-arg function should not generate snippet (no placeholders).
|
||||
ASSERT_TRUE(!it->insert_text_format.has_value() ||
|
||||
*it->insert_text_format == protocol::InsertTextFormat::PlainText);
|
||||
}
|
||||
|
||||
TEST_CASE(SnippetDisabled) {
|
||||
feature::CodeCompletionOptions opts;
|
||||
opts.bundle_overloads = false;
|
||||
opts.enable_function_arguments_snippet = false;
|
||||
code_complete(R"cpp(
|
||||
int foooo(int x, float y);
|
||||
int z = fo$(pos)
|
||||
)cpp",
|
||||
opts);
|
||||
|
||||
auto it = find_item("foooo");
|
||||
ASSERT_TRUE(it != items.end());
|
||||
// With snippet disabled, should be plain text.
|
||||
ASSERT_TRUE(!it->insert_text_format.has_value() ||
|
||||
*it->insert_text_format == protocol::InsertTextFormat::PlainText);
|
||||
}
|
||||
|
||||
TEST_CASE(SnippetBundleMode) {
|
||||
// In bundle mode, snippets should NOT be generated even if enabled.
|
||||
feature::CodeCompletionOptions opts;
|
||||
opts.bundle_overloads = true;
|
||||
opts.enable_function_arguments_snippet = true;
|
||||
code_complete(R"cpp(
|
||||
int foooo(int x);
|
||||
int foooo(int x, int y);
|
||||
int z = fo$(pos)
|
||||
)cpp",
|
||||
opts);
|
||||
|
||||
auto it = find_item("foooo");
|
||||
ASSERT_TRUE(it != items.end());
|
||||
ASSERT_TRUE(!it->insert_text_format.has_value() ||
|
||||
*it->insert_text_format == protocol::InsertTextFormat::PlainText);
|
||||
}
|
||||
|
||||
TEST_CASE(SnippetMethod) {
|
||||
feature::CodeCompletionOptions opts;
|
||||
opts.bundle_overloads = false;
|
||||
opts.enable_function_arguments_snippet = true;
|
||||
code_complete(R"cpp(
|
||||
struct Foo {
|
||||
int bazzzz(int a, int b);
|
||||
};
|
||||
void bar() {
|
||||
Foo f;
|
||||
f.ba$(pos);
|
||||
}
|
||||
)cpp",
|
||||
opts);
|
||||
|
||||
auto it = find_item("bazzzz");
|
||||
ASSERT_TRUE(it != items.end());
|
||||
ASSERT_TRUE(it->insert_text_format.has_value());
|
||||
ASSERT_EQ(*it->insert_text_format, protocol::InsertTextFormat::Snippet);
|
||||
auto& edit = std::get<protocol::TextEdit>(*it->text_edit);
|
||||
ASSERT_TRUE(edit.new_text.find("${1:") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE(Unqualified) {
|
||||
code_complete(R"cpp(
|
||||
namespace A {
|
||||
|
||||
Reference in New Issue
Block a user