wat_service 0.10.2

WebAssembly Text Format language service.
Documentation
use crate::{helpers::LineIndexExt, uri::InternUri};
use line_index::LineIndex;
use lspt::{CodeAction, CodeActionKind, TextEdit, WorkspaceEdit};
use rustc_hash::FxBuildHasher;
use std::collections::HashMap;
use wat_syntax::{
    SyntaxKind, SyntaxNode,
    ast::{AstNode, BlockIf, Instr},
};

pub fn act(db: &dyn salsa::Database, uri: InternUri, line_index: &LineIndex, node: &SyntaxNode) -> Option<CodeAction> {
    let block_if = BlockIf::cast(node.clone())?;
    if block_if.else_block().is_some() {
        return None;
    }
    let then_block = block_if.then_block()?;
    let mut then_instrs = then_block.instrs();
    let first_instr = then_instrs.next().and_then(|instr| match instr {
        Instr::Plain(plain) => Some(plain),
        _ => None,
    })?;
    if then_instrs.next().is_some() {
        return None;
    }
    let instr_name = first_instr.instr_name()?;
    if instr_name.text() != "br" {
        return None;
    }
    let mut first_instr_children = first_instr.syntax().children();
    let first_immediate = first_instr_children
        .next()
        .filter(|child| child.kind() == SyntaxKind::IMMEDIATE)?;
    if first_instr_children.next().is_some() {
        return None;
    }

    let mut text_edits = Vec::with_capacity(1);
    if let Some(l_paren) = block_if.l_paren_token() {
        if let Some(keyword) = block_if.keyword() {
            text_edits.push(TextEdit {
                range: line_index.convert(l_paren.text_range().cover(keyword.text_range())),
                new_text: "".into(),
            });
        }
        text_edits.push(TextEdit {
            range: line_index.convert(then_block.syntax().text_range()),
            new_text: format!("(br_if {first_immediate})"),
        });
        if let Some(r_paren) = block_if.r_paren_token() {
            text_edits.push(TextEdit {
                range: line_index.convert(r_paren.text_range()),
                new_text: "".into(),
            });
        }
    } else {
        text_edits.push(TextEdit {
            range: line_index.convert(node.text_range()),
            new_text: format!("br_if {first_immediate}"),
        });
    };

    let mut changes = HashMap::with_capacity_and_hasher(1, FxBuildHasher);
    changes.insert(uri.raw(db), text_edits);
    Some(CodeAction {
        title: "Convert `if` with `br` to `br_if`".into(),
        kind: Some(CodeActionKind::RefactorRewrite),
        edit: Some(WorkspaceEdit {
            changes: Some(changes),
            ..Default::default()
        }),
        ..Default::default()
    })
}