wat_service 0.10.2

WebAssembly Text Format language service.
Documentation
use crate::{helpers::LineIndexExt, uri::InternUri};
use line_index::LineIndex;
use lspt::{CodeAction, CodeActionContext, CodeActionKind, Diagnostic, TextEdit, Union2, WorkspaceEdit};
use rustc_hash::FxBuildHasher;
use std::collections::HashMap;
use wat_syntax::{NodeOrToken, SyntaxKind, SyntaxNode, TextRange};

pub fn act(
    db: &dyn salsa::Database,
    uri: InternUri,
    line_index: &LineIndex,
    node: &SyntaxNode,
    context: &CodeActionContext,
) -> Option<Vec<CodeAction>> {
    let node_lsp_range = line_index.convert(node.text_range());
    let diagnostic = context.diagnostics.iter().find(|diagnostic| match &diagnostic.code {
        Some(Union2::B(code)) => code == "packing" && diagnostic.range == node_lsp_range,
        _ => false,
    })?;
    let instr_name = node
        .prev_siblings_with_tokens()
        .find_map(|node_or_token| match node_or_token {
            NodeOrToken::Token(token) if token.kind() == SyntaxKind::INSTR_NAME => Some(token),
            _ => None,
        })?;

    let range = instr_name.text_range();
    match instr_name.text() {
        "struct.get" => Some(
            ["struct.get_s", "struct.get_u"]
                .iter()
                .map(|new_text| build_action(new_text, range, diagnostic, db, uri, line_index))
                .collect(),
        ),
        "struct.get_s" | "struct.get_u" => {
            Some(vec![build_action("struct.get", range, diagnostic, db, uri, line_index)])
        }
        "array.get" => Some(
            ["array.get_s", "array.get_u"]
                .iter()
                .map(|new_text| build_action(new_text, range, diagnostic, db, uri, line_index))
                .collect(),
        ),
        "array.get_s" | "array.get_u" => Some(vec![build_action("array.get", range, diagnostic, db, uri, line_index)]),
        _ => None,
    }
}

fn build_action(
    new_text: &str,
    range: TextRange,
    diagnostic: &Diagnostic,
    db: &dyn salsa::Database,
    uri: InternUri,
    line_index: &LineIndex,
) -> CodeAction {
    let text_edits = vec![TextEdit {
        range: line_index.convert(range),
        new_text: new_text.into(),
    }];
    let mut changes = HashMap::with_capacity_and_hasher(1, FxBuildHasher);
    changes.insert(uri.raw(db), text_edits);
    CodeAction {
        title: format!("Replace instruction with `{new_text}`"),
        kind: Some(CodeActionKind::QuickFix),
        edit: Some(WorkspaceEdit {
            changes: Some(changes),
            ..Default::default()
        }),
        is_preferred: Some(true),
        diagnostics: Some(vec![diagnostic.clone()]),
        ..Default::default()
    }
}