cpp_ls 0.1.1

A C, C++ and CUDA language server.
Documentation
use std::error::Error;

use log::warn;
use lsp_server::Notification;
use lsp_types::{
    CancelParams, DidChangeConfigurationParams, NumberOrString,
    notification::{
        Cancel, DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles,
        DidCloseTextDocument, DidOpenTextDocument, DidSaveTextDocument,
        Notification as LspNotification, SetTrace,
    },
};

use crate::context::ServerContext;

use super::{
    configuration::on_did_change_configuration,
    text_document::{
        on_did_change_text_document, on_did_change_watched_files, on_did_close_document,
        on_did_open_text_document, on_did_save_text_document, on_set_trace,
    },
};

macro_rules! dispatch_notification {
    ($notification:expr, $context:expr, {
        sync: { $($sync_notif:ty => $sync_handler:expr),* $(,)? }
        async: { $($async_notif:ty => $async_handler:expr),* $(,)? }
    }) => {
        match $notification.method.as_str() {
            Cancel::METHOD => {
                if let Ok(params) = $notification.extract::<CancelParams>(Cancel::METHOD) {
                    handle_cancel($context, params).await;
                }
            }
            $(
                <$sync_notif>::METHOD => {
                    if let Ok(params) = $notification.extract::<<$sync_notif as LspNotification>::Params>(<$sync_notif>::METHOD) {
                        let snapshot = $context.snapshot();
                        // Catch panics locally so a handler cannot kill the main loop.
                        let _ = crate::util::catch_unwind($sync_handler(snapshot, params)).await;
                    }
                }
            )*
            $(
                <$async_notif>::METHOD => {
                    if let Ok(params) = $notification.extract::<<$async_notif as LspNotification>::Params>(<$async_notif>::METHOD) {
                        let snapshot = $context.snapshot();
                        let _ = crate::util::catch_unwind($async_handler(snapshot, params)).await;
                    }
                }
            )*
            method => {
                warn!("Unhandled notification method: {}", method);
            }
        }
    };
}

pub async fn on_notification_handler(
    notification: Notification,
    server_context: &mut ServerContext,
) -> Result<(), Box<dyn Error + Sync + Send>> {
    if notification.method == DidChangeConfiguration::METHOD {
        if let Ok(params) =
            notification.extract::<DidChangeConfigurationParams>(DidChangeConfiguration::METHOD)
        {
            let snapshot = server_context.snapshot();
            tokio::spawn(async move {
                on_did_change_configuration(snapshot, params).await;
            });
        }
        return Ok(());
    }

    dispatch_notification!(notification, server_context, {
        sync: {}
        async: {
            DidChangeTextDocument => on_did_change_text_document,
            DidOpenTextDocument => on_did_open_text_document,
            DidSaveTextDocument => on_did_save_text_document,
            DidCloseTextDocument => on_did_close_document,
            DidChangeWatchedFiles => on_did_change_watched_files,
            SetTrace => on_set_trace,
        }
    });

    Ok(())
}

async fn handle_cancel(server_context: &mut ServerContext, params: CancelParams) {
    let req_id = match params.id {
        NumberOrString::Number(i) => i.into(),
        NumberOrString::String(s) => s.into(),
    };

    server_context.cancel(req_id).await;
}