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::{
    Partitions, Uri,
    protocol::{
      jsonrpc,
      lsp::{LSPAny, LanguageServer},
    },
    scheduler::task::TaskContext,
  },
  serde_json::Value,
};

pub async fn handle_query_source_cache<P: Partitions, T: LanguageServer<P>>(
  arguments: &[Value],
  ctx: &mut TaskContext<P, T>,
) -> jsonrpc::Result<Option<LSPAny>> {
  let arg = arguments.first().ok_or_else(|| {
    otel::exception!(
      "invalid_params",
      jsonrpc::Error::invalid_params("Missing arguments object")
    )
  })?;

  let uri_str = arg.get("uri").and_then(|v| v.as_str()).ok_or_else(|| {
    otel::exception!(
      "invalid_params",
      jsonrpc::Error::invalid_params("Missing 'uri' field")
    )
  })?;

  let uri = Uri::parse(uri_str).map_err(|e| {
    otel::exception!(
      "invalid_uri",
      jsonrpc::Error::invalid_params(format!("Invalid URI: {}", e)),
      "uri_str" = uri_str.to_string()
    )
  })?;

  let properties =
    arg.get("properties").and_then(|v| v.as_array()).map(|arr| {
      arr
        .iter()
        .filter_map(|v| v.as_str().map(|s| s.to_string()))
        .collect::<Vec<_>>()
    });

  otel::event!(
    "query_source_cache.start",
    "uri" = uri.to_string(),
    "property_count" = properties.as_ref().map(|p| p.len() as i64).unwrap_or(0)
  );

  let source_cache = ctx.source_cache();
  let cache = source_cache.read();

  if let Some(source) = cache.get_source_by_uri(&uri) {
    otel::event!("cache.hit", "uri" = uri.to_string());
    let mut response = serde_json::Map::new();

    let include_all = properties.is_none();
    let props = properties.as_ref();

    let should_include = |prop: &str| -> bool {
      include_all || props.is_some_and(|p| p.contains(&prop.to_string()))
    };

    if should_include("uri") {
      response.insert(
        "uri".to_string(),
        serde_json::json!(source.uri().to_string()),
      );
    }

    if should_include("content")
      && let Some(content) = source.reify()
    {
      response.insert("content".to_string(), serde_json::json!(content));
    }

    if should_include("version") {
      let version = cache
        .get_latest_lsp_version(&uri)
        .unwrap_or(source.version() as i32);
      response.insert("version".to_string(), serde_json::json!(version));
    }

    if should_include("isOpen") {
      let is_open = cache.is_file_open_in_editor(&uri);
      response.insert("isOpen".to_string(), serde_json::json!(is_open));
    }

    Ok(Some(serde_json::Value::Object(response)))
  } else {
    otel::event!("cache.miss", "uri" = uri.to_string());

    Err(otel::exception!(
      "source_not_found",
      jsonrpc::Error {
        code: jsonrpc::ErrorCode::ServerError(10404),
        message: format!("Source not found for URI: {}", uri).into(),
        data: None,
      },
      "uri" = uri.to_string()
    ))
  }
}