-
-
Notifications
You must be signed in to change notification settings - Fork 963
Open
Description
Result from CocInfo
## versions
vim version: VIM - Vi IMproved 9.1 9011362
node version: v23.9.0
coc.nvim version: 0.0.82-911d33a 2025-08-20 20:30:00 +0800
coc.nvim directory: /home/lrne/.vim/plugged/coc.nvim
term: dumb
platform: linux
## Log of coc.nvim
2025-09-03T20:41:21.287 INFO (pid:30821) [plugin] - coc.nvim initialized with node: v23.9.0 after 971
2025-09-03T20:41:21.297 INFO (pid:30821) [services] - LanguageClient Rust Analyzer Language Server state change: stopped => starting
2025-09-03T20:41:21.327 INFO (pid:30821) [language-client-index] - Language server "rust-analyzer" started with 30877
2025-09-03T20:41:21.561 INFO (pid:30821) [services] - LanguageClient Rust Analyzer Language Server state change: starting => running
2025-09-03T20:41:21.580 INFO (pid:30821) [services] - service rust-analyzer started
2025-09-03T20:42:04.805 INFO (pid:30821) [attach] - receive notification: codeAction [ 'V' ]
2025-09-03T20:42:20.004 ERROR (pid:30821) [extension:coc-rust-analyzer] - Applying snippet edit: {
"range": {
"start": {
"line": 154,
"character": 0
},
"end": {
"line": 154,
"character": 0
}
},
"newText": "fn $0fun_name(ctx: &AssistContext<'_>) -> Option<(ast::BinExpr, ast::BinaryOp, syntax::TextRange)> {\n let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;\n let op = bin_expr.op_kind()?;\n let op_range = bin_expr.op_token()?.text_range();\n if !op_range.contains_range(ctx.selection_trimmed()) {\n return None;\n \\}\n Some((bin_expr, op, op_range))\n\\}\n\n",
"insertTextFormat": 2
}
2025-09-03T20:42:26.008 INFO (pid:30821) [attach] - receive notification: showInfo []
2025-09-03T20:42:41.672 INFO (pid:30821) [attach] - receive notification: codeAction [ 'V' ]
2025-09-03T20:42:42.638 ERROR (pid:30821) [extension:coc-rust-analyzer] - Applying snippet edit: {
"range": {
"start": {
"line": 154,
"character": 0
},
"end": {
"line": 154,
"character": 0
}
},
"newText": "fn $0fun_name(ctx: &AssistContext<'_>) -> Option<(ast::BinExpr, ast::BinaryOp, syntax::TextRange)> {\n let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;\n let op = bin_expr.op_kind()?;\n let op_range = bin_expr.op_token()?.text_range();\n if !op_range.contains_range(ctx.selection_trimmed()) {\n return None;\n \\}\n Some((bin_expr, op, op_range))\n\\}\n\n",
"insertTextFormat": 2
}
2025-09-03T20:42:43.022 ERROR (pid:30821) [snippets-session] - Something went wrong with the snippet implementation {
range: {
start: { line: 40, character: 13 },
end: { line: 40, character: 21 }
},
text: 'op, op_range)',
rangeLength: 8
} (mut op, op_range), op, op_range) = fun_name(ctx)?;
editor.add_mappings(make.finish_with_mappings());
while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
match parent_expr.op_kind() {
Some(parent_op) if parent_op == op => {
bin_expr = parent_expr;
}
_ => break,
}
}
let op = bin_expr.op_kind()?;
let (inv_token, prec) = match op {
ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
ast::BinaryOp::LogicOp(ast::LogicOp::Or) => (SyntaxKind::AMP2, ExprPrecedence::LAnd),
_ => return None,
};
let make = SyntaxFactory::with_mappings();
let demorganed = bin_expr.clone_subtree();
let mut editor = SyntaxEditor::new(demorganed.syntax().clone());
editor.replace(demorganed.op_token()?, make.token(inv_token));
let mut exprs = VecDeque::from([
(bin_expr.lhs()?, demorganed.lhs()?, prec),
(bin_expr.rhs()?, demorganed.rhs()?, prec),
]);
while let Some((expr, demorganed, prec)) = exprs.pop_front() {
if let BinExpr(bin_expr) = &expr {
if let BinExpr(cbin_expr) = &demorganed {
if op == bin_expr.op_kind()? {
editor.replace(cbin_expr.op_token()?, make.token(inv_token));
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec));
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec));
} else {
let mut inv = invert_boolean_expression(&make, expr);
if precedence(&inv).needs_parentheses_in(prec) {
inv = make.expr_paren(inv).into();
}
editor.replace(demorganed.syntax(), inv.syntax());
}
} else {
return None;
}
} else {
let mut inv = invert_boolean_expression(&make, demorganed.clone());
if precedence(&inv).needs_parentheses_in(prec) {
inv = make.expr_paren(inv).into();
}
editor.replace(demorganed.syntax(), inv.syntax());
}
}
editor.add_mappings(make.finish_with_mappings());
let edit = editor.finish();
let demorganed = ast::Expr::cast(edit.new_root().clone())?;
acc.add_group(
&GroupLabel("Apply De Morgan's law".to_owned()),
AssistId::refactor_rewrite("apply_demorgan"),
"Apply De Morgan's law",
op_range,
|builder| {
let make = SyntaxFactory::with_mappings();
let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
let neg_expr = paren_expr
.clone()
.and_then(|paren_expr| paren_expr.syntax().parent())
.and_then(ast::PrefixExpr::cast)
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
.map(ast::Expr::PrefixExpr);
let mut editor;
if let Some(paren_expr) = paren_expr {
if let Some(neg_expr) = neg_expr {
cov_mark::hit!(demorgan_double_negation);
let parent = neg_expr.syntax().parent();
editor = builder.make_editor(neg_expr.syntax());
if parent.is_some_and(|parent| {
demorganed.needs_parens_in_place_of(&parent, neg_expr.syntax())
}) {
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
} else {
editor.replace(neg_expr.syntax(), demorganed.syntax());
};
} else {
cov_mark::hit!(demorgan_double_parens);
editor = builder.make_editor(paren_expr.syntax());
editor.replace(paren_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
} else {
editor = builder.make_editor(bin_expr.syntax());
editor.replace(bin_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
fn fun_name(ctx: &AssistContext<'_>) -> Option<(ast::BinExpr, ast::BinaryOp, syntax::TextRange)> {
let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
let op = bin_expr.op_kind()?;
let op_range = bin_expr.op_token()?.text_range();
if !op_range.contains_range(ctx.selection_trimmed()) {
return None;
}
Some((bin_expr, op, op_range))
}
(mut op, op_range), bin_expr = fun_name(ctx)?;
editor.add_mappings(make.finish_with_mappings());
while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
match parent_expr.op_kind() {
Some(parent_op) if parent_op == op => {
bin_expr = parent_expr;
}
_ => break,
}
}
let op = bin_expr.op_kind()?;
let (inv_token, prec) = match op {
ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
ast::BinaryOp::LogicOp(ast::LogicOp::Or) => (SyntaxKind::AMP2, ExprPrecedence::LAnd),
_ => return None,
};
let make = SyntaxFactory::with_mappings();
let demorganed = bin_expr.clone_subtree();
let mut editor = SyntaxEditor::new(demorganed.syntax().clone());
editor.replace(demorganed.op_token()?, make.token(inv_token));
// Walk up the tree while we have the same binary operator
let edit = editor.finish();
let demorganed = ast::Expr::cast(edit.new_root().clone())?;
acc.add_group(
&GroupLabel("Apply De Morgan's law".to_owned()),
AssistId::refactor_rewrite("apply_demorgan"),
"Apply De Morgan's law",
op_range,
|builder| {
let make = SyntaxFactory::with_mappings();
let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
let neg_expr = paren_expr
.clone()
.and_then(|paren_expr| paren_expr.syntax().parent())
.and_then(ast::PrefixExpr::cast)
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
.map(ast::Expr::PrefixExpr);
let mut editor;
if let Some(paren_expr) = paren_expr {
if let Some(neg_expr) = neg_expr {
cov_mark::hit!(demorgan_double_negation);
let parent = neg_expr.syntax().parent();
editor = builder.make_editor(neg_expr.syntax());
if parent.is_some_and(|parent| {
demorganed.needs_parens_in_place_of(&parent, neg_expr.syntax())
}) {
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
} else {
editor.replace(neg_expr.syntax(), demorganed.syntax());
};
} else {
cov_mark::hit!(demorgan_double_parens);
editor = builder.make_editor(paren_expr.syntax());
editor.replace(paren_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
} else {
editor = builder.make_editor(bin_expr.syntax());
editor.replace(bin_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
fn fun_name(ctx: &AssistContext<'_>) -> Option<(ast::BinExpr, ast::BinaryOp, syntax::TextRange)> {
let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
let op = bin_expr.op_kind()?;
let op_range = bin_expr.op_token()?.text_range();
if !op_range.contains_range(ctx.selection_trimmed()) {
return None;
}
Some((bin_expr, op, op_range))
}
// Assist: apply_demorgan_iterator
//
// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
// `Iterator::all` and `Iterator::any`.
//
// This transforms expressions of the form `!iter.any(|x| predicate(x))` into
// `iter.all(|x| !predicate(x))` and vice versa. This also works the other way for
// `Iterator::all` into `Iterator::any`.
//
// ```
// # //- minicore: iterator
// fn main() {
// let arr = [1, 2, 3];
// if !arr.into_iter().$0any(|num| num == 4) {
// println!("foo");
// }
// }
// ```
// ->
// ```
// fn main() {
// let arr = [1, 2, 3];
// if arr.into_iter().all(|num| num != 4) {
// println!("foo");
// }
// }
// ```
pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?;
2025-09-03T20:42:47.829 INFO (pid:30821) [attach] - receive notification: showInfo []
Describe the bug
Code-Action snippets damaged
This may be caused by out of order application code snippets when the device performance is low
Reproduce the bug
-
Clone repo
rust-lang/rust-analyzer
-
Create file
mini.vim
with:colorscheme habamax set nu set laststatus=2 set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P set statusline^=%(%1*[%{coc#status()}%{get(b:,'coc_current_function','')}]%*\ %) call plug#begin('~/.vim/plugged') Plug 'neoclide/coc.nvim' call plug#end() inoremap <silent><expr> <cr> \ coc#pum#visible() ? coc#pum#confirm() \ : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>" inoremap <silent><expr> <tab> coc#pum#visible() \ ? coc#pum#next(0) \ : coc#refresh() xmap <F3> <Plug>(coc-codeaction-selected) nmap <C-k> <Plug>(coc-diagnostic-prev) nmap <C-j> <Plug>(coc-diagnostic-next)
-
Create file
coc-settings.json
with:{ "rust-analyzer.updates.checkOnStartup": false, "rust-analyzer.check.workspace": false, "rust-analyzer.cachePriming.enable": false }
-
Run
cd rust-analyzer/crates/ide-assists
-
Start vim with command:
vim -Nu mini.vim src/handlers/apply_demorgan.rs
-
Operate vim.
- Line-Visual-mode select line 41,48
- Input
<F3>
choose2. Extract into function
- Try a few more times ...
Metadata
Metadata
Assignees
Labels
No labels