wat_service 0.7.0

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

pub fn act(
    service: &LanguageService,
    uri: InternUri,
    line_index: &LineIndex,
    node: &SyntaxNode,
    context: &CodeActionContext,
) -> Option<Vec<CodeAction>> {
    let node_lsp_range = helpers::rowan_range_to_lsp_range(line_index, 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.siblings_with_tokens(Direction::Prev)
            .find_map(|element| match element {
                SyntaxElement::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, service, uri, line_index))
                .collect(),
        ),
        "struct.get_s" | "struct.get_u" => Some(vec![build_action(
            "struct.get",
            range,
            diagnostic,
            service,
            uri,
            line_index,
        )]),
        "array.get" => Some(
            ["array.get_s", "array.get_u"]
                .iter()
                .map(|new_text| build_action(new_text, range, diagnostic, service, uri, line_index))
                .collect(),
        ),
        "array.get_s" | "array.get_u" => Some(vec![build_action(
            "array.get",
            range,
            diagnostic,
            service,
            uri,
            line_index,
        )]),
        _ => None,
    }
}

fn build_action(
    new_text: &str,
    range: TextRange,
    diagnostic: &Diagnostic,
    service: &LanguageService,
    uri: InternUri,
    line_index: &LineIndex,
) -> CodeAction {
    let text_edits = vec![TextEdit {
        range: helpers::rowan_range_to_lsp_range(line_index, range),
        new_text: new_text.into(),
    }];
    let mut changes = HashMap::with_capacity_and_hasher(1, FxBuildHasher);
    changes.insert(uri.raw(service), 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()
    }
}