Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/emmylua_ls/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use client_id::{ClientId, get_client_id};
use emmylua_code_analysis::EmmyLuaAnalysis;
pub use file_diagnostic::FileDiagnostic;
use lsp_server::{Connection, ErrorCode, Message, RequestId, Response};
use lsp_types::ClientCapabilities;
pub use snapshot::ServerContextSnapshot;
pub use status_bar::ProgressTask;
pub use status_bar::StatusBar;
Expand All @@ -29,10 +30,11 @@ pub struct ServerContext {
file_diagnostic: Arc<FileDiagnostic>,
workspace_manager: Arc<RwLock<WorkspaceManager>>,
status_bar: Arc<StatusBar>,
client_capabilities: Arc<ClientCapabilities>,
}

impl ServerContext {
pub fn new(conn: Connection) -> Self {
pub fn new(conn: Connection, client_capabilities: Arc<ClientCapabilities>) -> Self {
let client = Arc::new(ClientProxy::new(Connection {
sender: conn.sender.clone(),
receiver: conn.receiver.clone(),
Expand Down Expand Up @@ -60,6 +62,7 @@ impl ServerContext {
cancllations: Arc::new(Mutex::new(HashMap::new())),
workspace_manager,
status_bar,
client_capabilities,
}
}

Expand All @@ -70,6 +73,7 @@ impl ServerContext {
file_diagnostic: self.file_diagnostic.clone(),
workspace_manager: self.workspace_manager.clone(),
status_bar: self.status_bar.clone(),
client_capabilities: self.client_capabilities.clone(),
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/emmylua_ls/src/context/snapshot.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use lsp_types::ClientCapabilities;
use std::sync::Arc;
use tokio::sync::RwLock;

Expand All @@ -15,4 +16,5 @@ pub struct ServerContextSnapshot {
pub file_diagnostic: Arc<FileDiagnostic>,
pub workspace_manager: Arc<RwLock<WorkspaceManager>>,
pub status_bar: Arc<StatusBar>,
pub client_capabilities: Arc<ClientCapabilities>,
}
9 changes: 8 additions & 1 deletion crates/emmylua_ls/src/handlers/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ pub async fn on_did_change_configuration(
let client_id = workspace_manager.client_config.client_id;
drop(workspace_manager);

let supports_config_request = context
.client_capabilities
.workspace
.as_ref()?
.configuration
.unwrap_or_default();

log::info!("change config client_id: {:?}", client_id);
let new_client_config = get_client_config(&context, client_id).await;
let new_client_config = get_client_config(&context, client_id, supports_config_request).await;
let mut config_manager = context.workspace_manager.write().await;
config_manager.client_config = new_client_config;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::time::Duration;

use log::info;
use serde_json::Value;

use crate::{context::ServerContextSnapshot, util::time_cancel_token};
use emmylua_code_analysis::file_path_to_uri;

use super::ClientConfig;

pub async fn get_client_config_default(
context: &ServerContextSnapshot,
config: &mut ClientConfig,
scopes: Option<&[&str]>,
) -> Option<()> {
let workspace_folders = context
.workspace_manager
.read()
.await
.workspace_folders
.clone();
let main_workspace_folder = workspace_folders.get(0);
let client = &context.client;
let scope_uri = main_workspace_folder.map(|p| file_path_to_uri(p).unwrap());
Comment on lines +22 to +24
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this rolls back a change made in previous commit. This is because NeoVim doesn't require workspace root to run EmmyLua. In this case, workspace_folders will be empty, but we should still request configs from the client.

Let me know if I've missed something and I shouldn't change this.


let mut configs = Vec::new();
let mut used_scope = None;
for scope in scopes.unwrap_or(&["emmylua"]) {
let params = lsp_types::ConfigurationParams {
items: vec![lsp_types::ConfigurationItem {
scope_uri: scope_uri.clone(),
section: Some(scope.to_string()),
}],
};
let cancel_token = time_cancel_token(Duration::from_secs(5));
let fetched_configs: Vec<_> = client
.get_configuration::<Value>(params, cancel_token)
.await?
.into_iter()
.filter(|config| !config.is_null())
.collect();
if !fetched_configs.is_empty() {
info!("found client config in scope {scope:?}");
configs = fetched_configs;
used_scope = Some(scope.to_string());
}
}

if let Some(used_scope) = used_scope {
info!(
"using client config from scope {used_scope:?}: {}",
serde_json::to_string_pretty(&configs)
.as_deref()
.unwrap_or("<failed to serialize json>")
);
} else {
info!("no client config found");
}

for config in &mut configs {
// VSCode always sends default values for all options, even those that weren't
// explicitly configured by user. This results in `null`s being sent for
// every option. Naturally, serde chokes on these nulls when applying partial
// configuration.
//
// Because of this, we have to ignore them here.
skip_nulls(config);
}

config.partial_emmyrcs = Some(configs);

Some(())
}

fn skip_nulls(v: &mut Value) {
if let Value::Object(obj) = v {
obj.retain(|_, v| !v.is_null());
for (_, v) in obj {
skip_nulls(v);
}
}
}
18 changes: 13 additions & 5 deletions crates/emmylua_ls/src/handlers/initialized/client_config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod neovim_config;
mod default_config;
mod vscode_config;

use neovim_config::get_client_config_neovim;
use default_config::get_client_config_default;
use serde_json::Value;
use vscode_config::get_client_config_vscode;

Expand All @@ -20,6 +20,7 @@ pub struct ClientConfig {
pub async fn get_client_config(
context: &ServerContextSnapshot,
client_id: ClientId,
supports_config_request: bool,
) -> ClientConfig {
let mut config = ClientConfig {
client_id,
Expand All @@ -29,9 +30,16 @@ pub async fn get_client_config(
partial_emmyrcs: None,
};
match client_id {
ClientId::VSCode => get_client_config_vscode(context, &mut config).await,
ClientId::Neovim => get_client_config_neovim(context, &mut config).await,
_ => Some(()),
ClientId::VSCode => {
get_client_config_vscode(context, &mut config).await;
}
ClientId::Neovim => {
get_client_config_default(context, &mut config, Some(&["Lua", "emmylua"])).await;
}
_ if supports_config_request => {
get_client_config_default(context, &mut config, None).await;
}
_ => {}
};

config
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use std::{collections::HashMap, time::Duration};

use serde::{Deserialize, Serialize};

use crate::{context::ServerContextSnapshot, util::time_cancel_token};

use super::ClientConfig;
use crate::handlers::initialized::client_config::default_config::get_client_config_default;
use crate::{context::ServerContextSnapshot, util::time_cancel_token};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, time::Duration};

#[derive(Debug, Deserialize, Serialize)]
struct VscodeFilesConfig {
Expand All @@ -17,6 +15,8 @@ pub async fn get_client_config_vscode(
context: &ServerContextSnapshot,
config: &mut ClientConfig,
) -> Option<()> {
get_client_config_default(context, config, None).await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why request configuration? The existing configuration in EmmyLua is unrelated to the language server itself; it’s mainly for color settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to provide a way to change non-project-related settings (like which inlay hints to display, whether to render markdown in comments or not, etc.) from VSCode interface. This approach repeats what rust-analyzer and several other LSPs do.

Current WIP on VSCode settings:

image

Plus, I want to provide a standard way for all editors to change EmmyLua's config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual maintenance is very difficult. LuaLS generates its package.json, so if this is to be implemented, I hope it can be generated automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With schema export already present, this shouldn't be hard to implement. I'll add it when sending PR to the VSCode extension.


let client = &context.client;
let params = lsp_types::ConfigurationParams {
items: vec![lsp_types::ConfigurationItem {
Expand Down
8 changes: 7 additions & 1 deletion crates/emmylua_ls/src/handlers/initialized/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub async fn initialized_handler(
log::info!("main root: {:?}", main_root);

let client_id = get_client_id(&params.client_info);
let supports_config_request = params
.capabilities
.workspace
.as_ref()?
.configuration
.unwrap_or_default();
log::info!("client_id: {:?}", client_id);

{
Expand All @@ -51,7 +57,7 @@ pub async fn initialized_handler(
log::info!("workspace folders set");
}

let client_config = get_client_config(&context, client_id).await;
let client_config = get_client_config(&context, client_id, supports_config_request).await;
log::info!("client_config: {:?}", client_config);

let params_json = serde_json::to_string_pretty(&params).unwrap();
Expand Down
19 changes: 11 additions & 8 deletions crates/emmylua_ls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ mod logger;
mod meta_text;
mod util;

use crate::handlers::{
initialized_handler, on_notification_handler, on_req_handler, on_response_handler,
};
pub use clap::Parser;
pub use cmd_args::*;
use handlers::server_capabilities;
use lsp_server::{Connection, Message};
use lsp_types::InitializeParams;
use std::sync::Arc;
use std::{env, error::Error};

use crate::handlers::{
initialized_handler, on_notification_handler, on_req_handler, on_response_handler,
};

#[macro_use]
extern crate rust_i18n;
rust_i18n::i18n!("./locales", fallback = "en");
Expand Down Expand Up @@ -60,10 +60,13 @@ async fn main_loop(
params: InitializeParams,
cmd_args: CmdArgs,
) -> Result<(), Box<dyn Error + Sync + Send>> {
let mut server_context = context::ServerContext::new(Connection {
sender: connection.sender.clone(),
receiver: connection.receiver.clone(),
});
let mut server_context = context::ServerContext::new(
Connection {
sender: connection.sender.clone(),
receiver: connection.receiver.clone(),
},
Arc::new(params.capabilities.clone()),
);

let server_context_snapshot = server_context.snapshot();
tokio::spawn(async move {
Expand Down
Loading