mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Added env var configuration for language server timeouts
This commit is contained in:
parent
ff0514aafc
commit
1c7804420a
3 changed files with 71 additions and 16 deletions
|
@ -86,3 +86,13 @@ eg: `ROCLS_LOG=debug`
|
||||||
|
|
||||||
Tests use expect-test, which is a snapshot/expect testing framework.
|
Tests use expect-test, which is a snapshot/expect testing framework.
|
||||||
If a change is made that requires updating the expect tests run `cargo test` confirm that the diff is correct, then run `UPDATE_EXPECT=1 cargo test` to update the contents of the files with the new output.
|
If a change is made that requires updating the expect tests run `cargo test` confirm that the diff is correct, then run `UPDATE_EXPECT=1 cargo test` to update the contents of the files with the new output.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
You can set the environment variables below to control the operation of the language.
|
||||||
|
|
||||||
|
`ROCLS_DEBOUNCE_MS`: Sets the amount of time to delay starting analysis of the document when a change comes in. This prevents starting pointless analysis while you are typing normally.
|
||||||
|
Default: `100`
|
||||||
|
|
||||||
|
`ROCLS_LATEST_DOC_TIMEOUT_MS`: Sets the timeout for waiting for an analysis of the latest document to be complete. If a request is sent that needs the latest version of the document to be analyzed, then it will wait up to this duration before just giving up.
|
||||||
|
Default: `5000`
|
||||||
|
|
|
@ -3,6 +3,7 @@ use log::{debug, info, trace};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::sync::{Mutex, MutexGuard};
|
use tokio::sync::{Mutex, MutexGuard};
|
||||||
|
@ -34,12 +35,33 @@ impl DocumentPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct RegistryConfig {
|
||||||
|
pub(crate) latest_document_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RegistryConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
latest_document_timeout: Duration::from_millis(5000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Registry {
|
pub(crate) struct Registry {
|
||||||
documents: Mutex<HashMap<Url, DocumentPair>>,
|
documents: Mutex<HashMap<Url, DocumentPair>>,
|
||||||
|
config: RegistryConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Registry {
|
impl Registry {
|
||||||
|
pub(crate) fn new(config: RegistryConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
documents: Default::default(),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_latest_version(&self, url: &Url) -> Option<i32> {
|
pub async fn get_latest_version(&self, url: &Url) -> Option<i32> {
|
||||||
self.documents.lock().await.get(url).map(|x| x.info.version)
|
self.documents.lock().await.get(url).map(|x| x.info.version)
|
||||||
}
|
}
|
||||||
|
@ -119,12 +141,12 @@ impl Registry {
|
||||||
|
|
||||||
async fn document_info_by_url(&self, url: &Url) -> Option<DocInfo> {
|
async fn document_info_by_url(&self, url: &Url) -> Option<DocInfo> {
|
||||||
self.documents.lock().await.get(url).map(|a| a.info.clone())
|
self.documents.lock().await.get(url).map(|a| a.info.clone())
|
||||||
}U
|
}
|
||||||
|
|
||||||
///Tries to get the latest document from analysis.
|
///Tries to get the latest document from analysis.
|
||||||
///Gives up and returns none aft 5 seconds.
|
///Gives up and returns none aft 5 seconds.
|
||||||
async fn latest_document_by_url(&self, url: &Url) -> Option<Arc<AnalyzedDocument>> {
|
async fn latest_document_by_url(&self, url: &Url) -> Option<Arc<AnalyzedDocument>> {
|
||||||
let duration = std::time::Duration::from_secs(5);
|
tokio::time::timeout(self.config.latest_document_timeout, async {
|
||||||
tokio::time::timeout(duration, async {
|
|
||||||
//TODO: This should really be a condvar that is triggered by the latest being ready, this will do for now though
|
//TODO: This should really be a condvar that is triggered by the latest being ready, this will do for now though
|
||||||
loop {
|
loop {
|
||||||
let docs = self.documents.lock().await;
|
let docs = self.documents.lock().await;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use analysis::HIGHLIGHT_TOKENS_LEGEND;
|
use analysis::HIGHLIGHT_TOKENS_LEGEND;
|
||||||
|
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use registry::Registry;
|
use registry::{Registry, RegistryConfig};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -15,27 +15,55 @@ mod analysis;
|
||||||
mod convert;
|
mod convert;
|
||||||
mod registry;
|
mod registry;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct RocServer {
|
struct RocServer {
|
||||||
pub state: RocServerState,
|
pub state: RocServerState,
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RocServerConfig {
|
||||||
|
pub debounce_ms: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RocServerConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
debounce_ms: Duration::from_millis(100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///This exists so we can test most of RocLs without anything LSP related
|
///This exists so we can test most of RocLs without anything LSP related
|
||||||
#[derive(Debug)]
|
|
||||||
struct RocServerState {
|
struct RocServerState {
|
||||||
registry: Registry,
|
registry: Registry,
|
||||||
|
config: RocServerConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::panic::RefUnwindSafe for RocServer {}
|
impl std::panic::RefUnwindSafe for RocServer {}
|
||||||
|
|
||||||
|
fn read_env_num(name: &str) -> Option<u64> {
|
||||||
|
std::env::var(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|a| str::parse::<u64>(&a).ok())
|
||||||
|
}
|
||||||
|
|
||||||
impl RocServer {
|
impl RocServer {
|
||||||
pub fn new(client: Client) -> Self {
|
pub fn new(client: Client) -> Self {
|
||||||
|
let registry_config = RegistryConfig {
|
||||||
|
latest_document_timeout: Duration::from_millis(
|
||||||
|
read_env_num("ROCLS_LATEST_DOC_TIMEOUT_MS").unwrap_or_else(|| 5000),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let config = RocServerConfig {
|
||||||
|
debounce_ms: Duration::from_millis(
|
||||||
|
read_env_num("ROCLS_DEBOUNCE_MS").unwrap_or_else(|| 100),
|
||||||
|
),
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
state: RocServerState::new(),
|
state: RocServerState::new(config, Registry::new(registry_config)),
|
||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capabilities() -> ServerCapabilities {
|
pub fn capabilities() -> ServerCapabilities {
|
||||||
let text_document_sync = TextDocumentSyncCapability::Options(
|
let text_document_sync = TextDocumentSyncCapability::Options(
|
||||||
// TODO: later on make this incremental
|
// TODO: later on make this incremental
|
||||||
|
@ -71,7 +99,6 @@ impl RocServer {
|
||||||
let completion_provider = CompletionOptions {
|
let completion_provider = CompletionOptions {
|
||||||
resolve_provider: Some(false),
|
resolve_provider: Some(false),
|
||||||
trigger_characters: Some(vec![".".to_string()]),
|
trigger_characters: Some(vec![".".to_string()]),
|
||||||
//TODO: what is this?
|
|
||||||
all_commit_characters: None,
|
all_commit_characters: None,
|
||||||
work_done_progress_options: WorkDoneProgressOptions {
|
work_done_progress_options: WorkDoneProgressOptions {
|
||||||
work_done_progress: None,
|
work_done_progress: None,
|
||||||
|
@ -108,10 +135,8 @@ impl RocServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RocServerState {
|
impl RocServerState {
|
||||||
pub fn new() -> RocServerState {
|
pub fn new(config: RocServerConfig, registry: Registry) -> RocServerState {
|
||||||
Self {
|
Self { config, registry }
|
||||||
registry: Registry::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn registry(&self) -> &Registry {
|
async fn registry(&self) -> &Registry {
|
||||||
|
@ -141,9 +166,7 @@ impl RocServerState {
|
||||||
let inner_ref = self;
|
let inner_ref = self;
|
||||||
let updating_result = async {
|
let updating_result = async {
|
||||||
//This reduces wasted computation by waiting to allow a new change to come in and update the version before we check, but does delay the final analysis. Ideally this would be replaced with cancelling the analysis when a new one comes in.
|
//This reduces wasted computation by waiting to allow a new change to come in and update the version before we check, but does delay the final analysis. Ideally this would be replaced with cancelling the analysis when a new one comes in.
|
||||||
let debounce_ms = std::env::var("ROCLS_DEBOUNCE")
|
tokio::time::sleep(self.config.debounce_ms).await;
|
||||||
.map_or(100, |a| str::parse::<u64>(&a).unwrap_or(100));
|
|
||||||
tokio::time::sleep(Duration::from_millis(debounce_ms)).await;
|
|
||||||
let is_latest = inner_ref
|
let is_latest = inner_ref
|
||||||
.registry
|
.registry
|
||||||
.get_latest_version(fi)
|
.get_latest_version(fi)
|
||||||
|
@ -388,7 +411,7 @@ mod tests {
|
||||||
info!("Doc is:\n{0}", doc);
|
info!("Doc is:\n{0}", doc);
|
||||||
let url = Url::parse("file:/Test.roc").unwrap();
|
let url = Url::parse("file:/Test.roc").unwrap();
|
||||||
|
|
||||||
let inner = RocServerState::new();
|
let inner = RocServerState::new(RocServerConfig::default(), Registry::default());
|
||||||
//setup the file
|
//setup the file
|
||||||
inner.change(&url, doc, 0).await.unwrap();
|
inner.change(&url, doc, 0).await.unwrap();
|
||||||
(inner, url)
|
(inner, url)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue