laburnum 1.17.1

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use {
  crate::{
    TRACER,
    database::{
      chunk::RecordWriter,
      storage::Partitions,
    },
    protocol::{
      jsonrpc::{
        self,
        Response,
      },
      lsp::{
        LSPAny,
        LanguageServer,
      },
      macros::{
        match_notification,
        match_request,
      },
    },
    scheduler::{
      lanes::Lane,
      task::TaskContext,
    },
  },
  futures::future::{
    BoxFuture,
    FutureExt,
  },
  std::{
    marker::PhantomData,
    sync::Arc,
  },
};

type RequestHandlerFn<P, T> = Box<
  dyn FnOnce(
      String,
      jsonrpc::Id,
      Option<LSPAny>,
      TaskContext<P, T>,
      RecordWriter<P>,
    ) -> BoxFuture<'static, (Option<Response>, RecordWriter<P>)>
    + Send,
>;

type NotificationHandlerFn<P, T> = Box<
  dyn FnOnce(
      String,
      Option<LSPAny>,
      TaskContext<P, T>,
      RecordWriter<P>,
    ) -> BoxFuture<'static, RecordWriter<P>>
    + Send,
>;

pub struct RequestRouter<P: Partitions, T: LanguageServer<P>> {
  server:   Arc<T>,
  _phantom: PhantomData<P>,
}

impl<P: Partitions, T: LanguageServer<P>> RequestRouter<P, T> {
  pub fn new(server: Arc<T>) -> Self {
    Self {
      server:   server.clone(),
      _phantom: PhantomData,
    }
  }

  fn route<Params, Result, F, Fut>(
    server: Arc<T>,
    method_fn: F,
    lane: Lane,
  ) -> (RequestHandlerFn<P, T>, Lane)
  where
    Params: serde::de::DeserializeOwned,
    Result: serde::Serialize,
    F: Fn(Arc<T>, Params, TaskContext<P, T>, RecordWriter<P>) -> Fut
      + Send
      + 'static,
    Fut: std::future::Future<Output = (jsonrpc::Result<Result>, RecordWriter<P>)>
      + Send
      + 'static,
  {
    let handler: RequestHandlerFn<P, T> =
      Box::new(move |_method, id, params, ctx, writer| {
        let server = server.clone();
        async move {
          let params: Params = match params {
            | Some(p) => {
              match serde_json::from_value(p) {
                | Ok(params) => params,
                | Err(e) => {
                  let response = Response::from_error(
                    id,
                    jsonrpc::Error::invalid_params(e.to_string()),
                  );
                  return (Some(response), writer);
                },
              }
            },
            | None => {
              let response = Response::from_error(
                id,
                jsonrpc::Error::invalid_params("Missing params"),
              );
              return (Some(response), writer);
            },
          };

          let (result, writer) = method_fn(server, params, ctx, writer).await;

          let response = match result {
            | Ok(res) => {
              match serde_json::to_value(res) {
                | Ok(value) => Response::from_ok(id, value),
                | Err(e) => {
                  Response::from_error(id, jsonrpc::Error {
                    code:    jsonrpc::ErrorCode::InternalError,
                    message: e.to_string().into(),
                    data:    None,
                  })
                },
              }
            },
            | Err(e) => Response::from_error(id, e),
          };

          (Some(response), writer)
        }
        .boxed()
      });

    (handler, lane)
  }

  pub fn get_notification_handler(
    &self,
    method: &str,
  ) -> (NotificationHandlerFn<P, T>, Lane) {
    match_notification! {
      match method on self.server => {
        @"notebookDocument/didOpen" => notebook_did_open,
        @"notebookDocument/didChange" => notebook_did_change,
        @"notebookDocument/didSave" => notebook_did_save,
        @"notebookDocument/didClose" => notebook_did_close,
        @"textDocument/didOpen" => did_open,
        @"textDocument/didChange" => did_change,
        @"textDocument/willSave" => will_save,
        @"textDocument/didSave" => did_save,
        @"textDocument/didClose" => did_close,
        @"workspace/didChangeConfiguration" => did_change_configuration,
        @"workspace/didChangeWorkspaceFolders" => did_change_workspace_folders,
        @"workspace/didCreateFiles" => did_create_files,
        @"workspace/didRenameFiles" => did_rename_files,
        @"workspace/didDeleteFiles" => did_delete_files,
        @"workspace/didChangeWatchedFiles" => did_change_watched_files,
      }
    }
  }

  pub fn get_request_handler(
    &self,
    method: &str,
  ) -> (RequestHandlerFn<P, T>, Lane) {
    match_request! {
      match method on self.server => {
        "initialize" => initialize,
        "textDocument/hover" => hover,
        "textDocument/completion" => completion,
        "completionItem/resolve" => completion_resolve,
        "textDocument/declaration" => goto_declaration,
        "textDocument/definition" => goto_definition,
        "textDocument/typeDefinition" => goto_type_definition,
        "textDocument/implementation" => goto_implementation,
        "textDocument/references" => references,
        "textDocument/prepareCallHierarchy" => prepare_call_hierarchy,
        "callHierarchy/incomingCalls" => incoming_calls,
        "callHierarchy/outgoingCalls" => outgoing_calls,
        "textDocument/codeAction" => code_action,
        "codeAction/resolve" => code_action_resolve,
        "textDocument/codeLens" => code_lens,
        "codeLens/resolve" => code_lens_resolve,
        "textDocument/documentColor" => document_color,
        "textDocument/colorPresentation" => color_presentation,
        "textDocument/diagnostic" => diagnostic,
        "textDocument/documentHighlight" => document_highlight,
        "textDocument/documentLink" => document_link,
        "documentLink/resolve" => document_link_resolve,
        "textDocument/documentSymbol" => document_symbol,
        "textDocument/foldingRange" => folding_range,
        "textDocument/formatting" => formatting,
        "textDocument/rangeFormatting" => range_formatting,
        "textDocument/onTypeFormatting" => on_type_formatting,
        "textDocument/inlayHint" => inlay_hint,
        "inlayHint/resolve" => inlay_hint_resolve,
        "textDocument/inlineValue" => inline_value,
        "textDocument/linkedEditingRange" => linked_editing_range,
        "textDocument/moniker" => moniker,
        "textDocument/rename" => rename,
        "textDocument/prepareRename" => prepare_rename,
        "textDocument/selectionRange" => selection_range,
        "textDocument/semanticTokens/full" => semantic_tokens_full,
        "textDocument/semanticTokens/full/delta" => semantic_tokens_full_delta,
        "textDocument/semanticTokens/range" => semantic_tokens_range,
        "textDocument/signatureHelp" => signature_help,
        "textDocument/willSaveWaitUntil" => will_save_wait_until,
        "textDocument/prepareTypeHierarchy" => prepare_type_hierarchy,
        "typeHierarchy/supertypes" => supertypes,
        "typeHierarchy/subtypes" => subtypes,
        "workspace/willCreateFiles" => will_create_files,
        "workspace/willRenameFiles" => will_rename_files,
        "workspace/willDeleteFiles" => will_delete_files,
        "workspace/executeCommand" => execute_command,
        "workspace/diagnostic" => workspace_diagnostic,
        "workspace/symbol" => symbol,
        "workspaceSymbol/resolve" => symbol_resolve,
      }
    }
  }
}

// "$/cancelRequest" => ,
//   "$/setTrace" => ,
//   "$/logTrace" => ,
//   "initialized" => ,
//   "exit" => ,

//   "window/showMessage" => ,
//   "window/logMessage" => ,
//   "window/workDoneProgress/cancel" => ,

//   "telemetry/event" => ,

//   "textDocument/didOpen" => ,
//   "textDocument/didChange" => ,
//   "textDocument/willSave" => ,
//   "textDocument/didSave" => ,
//   "textDocument/didClose" => ,
//   "textDocument/publishDiagnostics" => ,

//   "notebookDocument/didOpen" => ,
//   "notebookDocument/didChange" => ,
//   "notebookDocument/didSave" => ,
//   "notebookDocument/didClose" => ,

//   "workspace/didChangeConfiguration" => ,
//   "workspace/didChangeWatchedFiles" => ,
//   "workspace/didChangeWorkspaceFolders" => ,
//   "$/progress" => ,
//   "workspace/didCreateFiles" => ,
//   "workspace/didRenameFiles" => ,
//   "workspace/didDeleteFiles" => ,