use std::convert::TryFrom;
use serde::{de::DeserializeOwned, Serialize};
pub use config::{Config, FilesWatcher};
pub use main_loop::main_loop;
use mun_paths::AbsPathBuf;
use mun_project::ProjectManifest;
use mun_syntax::{TextRange, TextSize};
pub(crate) use state::LanguageServerState;
pub(crate) use symbol_kind::SymbolKind;
mod analysis;
mod cancelation;
mod capabilities;
mod change;
#[cfg(test)]
mod change_fixture;
mod completion;
mod config;
mod db;
mod diagnostics;
mod file_structure;
mod from_lsp;
mod handlers;
mod lsp_utils;
mod main_loop;
mod state;
mod symbol_kind;
mod to_lsp;
#[derive(Clone, Copy, Debug)]
pub struct FilePosition {
pub file_id: mun_hir::FileId,
pub offset: TextSize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileRange {
pub file_id: mun_hir::FileId,
pub range: TextRange,
}
pub fn from_json<T: DeserializeOwned>(
what: &'static str,
json: serde_json::Value,
) -> anyhow::Result<T> {
T::deserialize(&json)
.map_err(|e| anyhow::anyhow!("could not deserialize {}: {}: {}", what, e, json))
}
pub fn to_json<T: Serialize>(value: T) -> anyhow::Result<serde_json::Value> {
serde_json::to_value(value).map_err(|e| anyhow::anyhow!("could not serialize to json: {}", e))
}
pub fn run_server() -> anyhow::Result<()> {
log::info!("language server started");
let (connection, io_threads) = lsp_server::Connection::stdio();
let (initialize_id, initialize_params) = connection.initialize_start()?;
let initialize_params =
from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
let server_capabilities = capabilities::server_capabilities(&initialize_params.capabilities);
let initialize_result = lsp_types::InitializeResult {
capabilities: server_capabilities,
server_info: Some(lsp_types::ServerInfo {
name: String::from("mun-language-server"),
version: Some(String::from(env!("CARGO_PKG_VERSION"))),
}),
};
let initialize_result = serde_json::to_value(initialize_result).unwrap();
connection.initialize_finish(initialize_id, initialize_result)?;
if let Some(client_info) = initialize_params.client_info {
log::info!(
"client '{}' {}",
client_info.name,
client_info.version.unwrap_or_default()
);
}
let config = {
let root_dir = match initialize_params
.root_uri
.and_then(|it| it.to_file_path().ok())
.and_then(|path| AbsPathBuf::try_from(path).ok())
{
Some(path) => path,
None => {
let cwd = std::env::current_dir()?;
AbsPathBuf::try_from(cwd)
.expect("could not convert current directory to an absolute path")
}
};
let mut config = Config::new(root_dir);
let supports_file_watcher_dynamic_registration = initialize_params
.capabilities
.workspace
.and_then(|c| c.did_change_watched_files)
.map(|c| c.dynamic_registration.unwrap_or(false))
.unwrap_or(false);
if supports_file_watcher_dynamic_registration {
config.watcher = FilesWatcher::Client;
}
let workspace_roots = initialize_params
.workspace_folders
.map(|workspaces| {
workspaces
.into_iter()
.filter_map(|it| it.uri.to_file_path().ok())
.filter_map(|path| AbsPathBuf::try_from(path).ok())
.collect::<Vec<_>>()
})
.filter(|workspaces| !workspaces.is_empty())
.unwrap_or_else(|| vec![config.root_dir.clone()]);
let discovered = ProjectManifest::discover_all(workspace_roots.iter().cloned());
log::info!("discovered projects: {:?}", discovered);
if discovered.is_empty() {
log::error!("failed to find any projects in {:?}", workspace_roots);
}
config.discovered_projects = Some(discovered);
config
};
main_loop(connection, config)?;
io_threads.join()?;
Ok(())
}