Skip to content

Commit

Permalink
implemented config functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
ascandone committed Jun 25, 2024
1 parent 9f46fff commit 2d52e9e
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 30 deletions.
1 change: 1 addition & 0 deletions compiler-core/src/language_server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod code_action;
mod compiler;
mod completer;
mod configuration;
mod engine;
mod feedback;
mod files;
Expand Down
32 changes: 32 additions & 0 deletions compiler-core/src/language_server/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use serde::Deserialize;
use std::sync::{Arc, RwLock};

pub type SharedConfig = Arc<RwLock<Configuration>>;

#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Configuration {
#[serde(default = "InlayHintsConfig::default")]
pub inlay_hints: InlayHintsConfig,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintsConfig {
#[serde(default = "InlayHintsConfig::default_pipelines")]
pub pipelines: bool,
}

impl Default for InlayHintsConfig {
fn default() -> Self {
Self {
pipelines: Self::default_pipelines(),
}
}
}

impl InlayHintsConfig {
fn default_pipelines() -> bool {
false
}
}
15 changes: 15 additions & 0 deletions compiler-core/src/language_server/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::sync::Arc;
use super::{
code_action::{CodeActionBuilder, RedundantTupleInCaseSubject},
completer::Completer,
configuration::SharedConfig,
src_offset_to_lsp_position, src_span_to_lsp_range, DownloadDependencies, MakeLocker,
};

Expand Down Expand Up @@ -63,6 +64,9 @@ pub struct LanguageServerEngine<IO, Reporter> {
/// Used to know if to show the "View on HexDocs" link
/// when hovering on an imported value
hex_deps: std::collections::HashSet<EcoString>,

/// Configuration the user has set in their editor.
pub(crate) user_config: SharedConfig,
}

impl<'a, IO, Reporter> LanguageServerEngine<IO, Reporter>
Expand All @@ -82,6 +86,7 @@ where
progress_reporter: Reporter,
io: FileSystemProxy<IO>,
paths: ProjectPaths,
user_config: SharedConfig,
) -> Result<Self> {
let locker = io.inner().make_locker(&paths, config.target)?;

Expand Down Expand Up @@ -117,6 +122,7 @@ where
compiler,
paths,
hex_deps,
user_config,
})
}

Expand Down Expand Up @@ -268,6 +274,15 @@ where

pub fn inlay_hints(&mut self, params: lsp::InlayHintParams) -> Response<Vec<InlayHint>> {
self.respond(|this| {
let Ok(config) = this.user_config.read() else {
// TODO trace?
return Ok(vec![]);
};

if !config.inlay_hints.pipelines {
return Ok(vec![]);
}

let Some(module) = this.module_for_uri(&params.text_document.uri) else {
return Ok(vec![]);
};
Expand Down
80 changes: 77 additions & 3 deletions compiler-core/src/language_server/messages.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::language_server::configuration::Configuration;
use camino::Utf8PathBuf;
use lsp::{
notification::{DidChangeWatchedFiles, DidOpenTextDocument},
Expand All @@ -8,11 +9,12 @@ use lsp_types::{
notification::{DidChangeTextDocument, DidCloseTextDocument, DidSaveTextDocument},
request::{CodeActionRequest, Completion, Formatting, HoverRequest, InlayHintRequest},
};
use std::time::Duration;
use std::{collections::HashMap, time::Duration};

#[derive(Debug)]
pub enum Message {
Request(lsp_server::RequestId, Request),
Response(Response),
Notification(Notification),
}

Expand All @@ -26,6 +28,11 @@ pub enum Request {
ShowInlayHints(lsp::InlayHintParams),
}

#[derive(Debug)]
pub enum Response {
Configuration(Configuration),
}

impl Request {
fn extract(request: lsp_server::Request) -> Option<Message> {
let id = request.id.clone();
Expand Down Expand Up @@ -67,6 +74,8 @@ pub enum Notification {
SourceFileMatchesDisc { path: Utf8PathBuf },
/// gleam.toml has changed.
ConfigFileChanged { path: Utf8PathBuf },
/// The user edited a client config option
ConfigChanged,
/// It's time to compile all open projects.
CompilePlease,
}
Expand Down Expand Up @@ -113,6 +122,11 @@ impl Notification {
};
Some(Message::Notification(notification))
}

"workspace/didChangeConfiguration" => {
Some(Message::Notification(Notification::ConfigChanged))
}

_ => None,
}
}
Expand All @@ -130,15 +144,19 @@ pub enum Next {
/// - A short pause in messages is detected, indicating the programmer has
/// stopped typing for a moment and would benefit from feedback.
/// - A request type message is received, which requires an immediate response.
///
#[derive(Debug)]
pub struct MessageBuffer {
messages: Vec<Message>,
next_request_id: i32,
response_handlers: HashMap<lsp_server::RequestId, ResponseHandler>,
}

impl MessageBuffer {
pub fn new() -> Self {
Self {
messages: Vec::new(),
next_request_id: 1,
response_handlers: Default::default(),
}
}

Expand Down Expand Up @@ -198,7 +216,58 @@ impl MessageBuffer {
Next::MorePlease
}

fn response(&mut self, _: lsp_server::Response) -> Next {
pub fn make_request(
&mut self,
method: impl Into<String>,
params: impl serde::Serialize,
handler: Option<ResponseHandler>,
) -> lsp_server::Request {
let id = self.next_request_id;
self.next_request_id += 1;
let request = lsp_server::Request {
id: id.into(),
method: method.into(),
params: serde_json::value::to_value(params).expect("serialisation should never fail"),
};

if let Some(handler) = handler {
_ = self.response_handlers.insert(id.into(), handler);
}

request
}

fn configuration_update_received(&mut self, result: serde_json::Value) -> Next {
let Some(first_el) = result.as_array().and_then(|a| a.first()) else {
return Next::MorePlease;
};

let parsed_config_result: Result<Configuration, _> =
serde_json::from_value(first_el.clone());

let Ok(parsed_config) = parsed_config_result else {
return Next::MorePlease;
};

let message = Message::Response(Response::Configuration(parsed_config));
self.messages.push(message);

Next::Handle(self.take_messages())
}

fn handle_response(&mut self, handler: ResponseHandler, result: serde_json::Value) -> Next {
match handler {
ResponseHandler::UpdateConfiguration => self.configuration_update_received(result),
}
}

fn response(&mut self, response: lsp_server::Response) -> Next {
if let Some(handler) = self.response_handlers.remove(&response.id) {
if let Some(result) = response.result {
return self.handle_response(handler, result);
}
}

// We do not use or expect responses from the client currently.
Next::MorePlease
}
Expand Down Expand Up @@ -243,3 +312,8 @@ where
.extract::<N::Params>(N::METHOD)
.expect("cast notification")
}

#[derive(Debug)]
pub enum ResponseHandler {
UpdateConfiguration,
}
28 changes: 19 additions & 9 deletions compiler-core/src/language_server/router.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::{configuration::SharedConfig, feedback::FeedbackBookKeeper};
use crate::{
build::SourceFingerprint,
error::{FileIoAction, FileKind},
Expand All @@ -9,15 +10,12 @@ use crate::{
paths::ProjectPaths,
Error, Result,
};
use camino::{Utf8Path, Utf8PathBuf};
use std::{
collections::{hash_map::Entry, HashMap},
time::SystemTime,
};

use camino::{Utf8Path, Utf8PathBuf};

use super::feedback::FeedbackBookKeeper;

/// The language server instance serves a language client, typically a text
/// editor. The editor could have multiple Gleam projects open at once, so run
/// an instance of the language server engine for each project.
Expand All @@ -30,6 +28,7 @@ pub(crate) struct Router<IO, Reporter> {
io: FileSystemProxy<IO>,
engines: HashMap<Utf8PathBuf, Project<IO, Reporter>>,
progress_reporter: Reporter,
user_config: SharedConfig,
}

impl<'a, IO, Reporter> Router<IO, Reporter>
Expand All @@ -44,11 +43,16 @@ where
// IO to be supplied from inside of gleam-core
Reporter: ProgressReporter + Clone + 'a,
{
pub fn new(progress_reporter: Reporter, io: FileSystemProxy<IO>) -> Self {
pub fn new(
progress_reporter: Reporter,
io: FileSystemProxy<IO>,
user_config: SharedConfig,
) -> Self {
Self {
io,
engines: HashMap::new(),
progress_reporter,
user_config,
}
}

Expand Down Expand Up @@ -84,8 +88,12 @@ where
Ok(Some(match self.engines.entry(path.clone()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
let project =
Self::new_project(path, self.io.clone(), self.progress_reporter.clone())?;
let project = Self::new_project(
path,
self.io.clone(),
self.progress_reporter.clone(),
self.user_config.clone(),
)?;
entry.insert(project)
}
}))
Expand Down Expand Up @@ -123,19 +131,21 @@ where
path: Utf8PathBuf,
io: FileSystemProxy<IO>,
progress_reporter: Reporter,
user_config: SharedConfig,
) -> Result<Project<IO, Reporter>, Error> {
tracing::info!(?path, "creating_new_language_server_engine");
let paths = ProjectPaths::new(path);
let config_path = paths.root_config();
let modification_time = io.modification_time(&config_path)?;
let toml = io.read(&config_path)?;
let config = toml::from_str(&toml).map_err(|e| Error::FileIo {
let package_config = toml::from_str(&toml).map_err(|e| Error::FileIo {
action: FileIoAction::Parse,
kind: FileKind::File,
path: config_path,
err: Some(e.to_string()),
})?;
let engine = LanguageServerEngine::new(config, progress_reporter, io, paths)?;
let engine =
LanguageServerEngine::new(package_config, progress_reporter, io, paths, user_config)?;
let project = Project {
engine,
feedback: FeedbackBookKeeper::default(),
Expand Down
Loading

0 comments on commit 2d52e9e

Please sign in to comment.