emmylua_ls 0.8.2

A language server for emmylua.
use std::{fs::OpenOptions, io::Write, sync::Arc};

use emmylua_code_analysis::{load_configs, DiagnosticCode, FileId};
use lsp_types::{Command, Range};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::sync::RwLock;

use crate::context::{ServerContextSnapshot, WorkspaceManager};

use super::CommandSpec;

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum DisableAction {
    DisableLine,
    DisableFile,
    DisableProject,
}

pub struct DisableCodeCommand;

impl CommandSpec for DisableCodeCommand {
    const COMMAND: &str = "emmy.disable.code";

    async fn handle(context: ServerContextSnapshot, args: Vec<Value>) -> Option<()> {
        let action: DisableAction = serde_json::from_value(args.get(0)?.clone()).ok()?;
        let code: DiagnosticCode = serde_json::from_value(args.get(3)?.clone()).ok()?;

        match action {
            DisableAction::DisableProject => {
                add_disable_project(context.workspace_manager, code).await;
            }
            _ => {}
        }

        Some(())
    }
}

pub fn make_disable_code_command(
    title: &str,
    action: DisableAction,
    code: DiagnosticCode,
    file_id: FileId,
    range: Range,
) -> Command {
    let args = vec![
        serde_json::to_value(action).unwrap(),
        serde_json::to_value(file_id).unwrap(),
        serde_json::to_value(range).unwrap(),
        serde_json::to_value(code.get_name()).unwrap(),
    ];

    Command {
        title: title.to_string(),
        command: DisableCodeCommand::COMMAND.to_string(),
        arguments: Some(args),
    }
}

async fn add_disable_project(
    config_manager: Arc<RwLock<WorkspaceManager>>,
    code: DiagnosticCode,
) -> Option<()> {
    let config_manager = config_manager.read().await;
    let main_workspace = config_manager.workspace_folders.get(0)?;
    let emmyrc_path = main_workspace.join(".emmyrc.json");
    let mut emmyrc = load_configs(vec![emmyrc_path.clone()], None);
    emmyrc.diagnostics.disable.push(code);
    drop(config_manager);

    let emmyrc_json = serde_json::to_string_pretty(&emmyrc).ok()?;
    if let Ok(mut file) = OpenOptions::new()
        .write(true)
        .truncate(true)
        .create(true)
        .open(&emmyrc_path)
    {
        if let Err(err) = file.write_all(emmyrc_json.as_bytes()) {
            log::error!("write emmyrc file failed: {:?}", err);
            return None;
        }
    } else {
        log::error!("Failed to open/create emmyrc file: {:?}", emmyrc_path);
        return None;
    }

    Some(())
}