Skip to content

Commit

Permalink
Can publish diagnostics on save
Browse files Browse the repository at this point in the history
  • Loading branch information
elijah-potter committed Jan 18, 2024
1 parent dd0e4de commit 5ff1b34
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 84 deletions.
92 changes: 8 additions & 84 deletions harper-ls/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use lsp_types::{
request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities,
};
use lsp_types::{Location, OneOf, Position};
mod server;

use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
use tracing::{info, Level};
use lsp_server::Connection;
use tracing::Level;

use crate::server::Server;

fn main() -> anyhow::Result<()> {
let subscriber = tracing_subscriber::FmtSubscriber::builder()
Expand All @@ -14,84 +13,9 @@ fn main() -> anyhow::Result<()> {
tracing::subscriber::set_global_default(subscriber)?;

let (connection, io_threads) = Connection::listen("127.0.0.1:4000")?;

let server_capabilities = serde_json::to_value(ServerCapabilities {
definition_provider: Some(OneOf::Left(true)),
..Default::default()
})
.unwrap();
let initialization_params = match connection.initialize(server_capabilities) {
Ok(it) => it,
Err(e) => {
if e.channel_is_disconnected() {
io_threads.join()?;
}
return Err(e.into());
}
};
main_loop(connection, initialization_params)?;
io_threads.join()?;

info!("Shutting down server.c");
Ok(())
}

fn main_loop(connection: Connection, params: serde_json::Value) -> anyhow::Result<()> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();
info!("Starting example main loop");
for msg in &connection.receiver {
info!("Got msg: {msg:?}");
match msg {
Message::Request(req) => {
if connection.handle_shutdown(&req)? {
return Ok(());
}
info!("Got request: {req:?}");
match cast::<GotoDefinition>(req) {
Ok((id, params)) => {
info!("Got gotoDefinition request #{id}: {params:?}");
let result = Some(GotoDefinitionResponse::Array(vec![Location {
uri: params.text_document_position_params.text_document.uri,
range: lsp_types::Range {
start: Position {
line: 0,
character: 0,
},
end: Position {
line: 0,
character: 0,
},
},
}]));
let result = serde_json::to_value(&result).unwrap();
let resp = Response {
id,
result: Some(result),
error: None,
};
connection.sender.send(Message::Response(resp))?;
continue;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
Err(ExtractError::MethodMismatch(req)) => req,
};
}
Message::Response(resp) => {
info!("Got response: {resp:?}");
}
Message::Notification(not) => {
info!("Got notification: {not:?}");
}
}
}
let mut server = Server::new(connection, io_threads)?;
server.main_loop()?;
server.join()?;

Ok(())
}

fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}
181 changes: 181 additions & 0 deletions harper-ls/src/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use anyhow::Ok;
use lsp_server::{
Connection, ExtractError, IoThreads, Message, Notification, Request, RequestId, Response,
};
use lsp_types::{
notification::{
DidOpenTextDocument, DidSaveTextDocument, Notification as NotificationTrait,
PublishDiagnostics,
},
request::GotoDefinition,
Diagnostic, GotoDefinitionResponse, InitializedParams, Location, OneOf, Position,
PublishDiagnosticsParams, Range, ServerCapabilities,
};
use tracing::{error, info};

pub struct Server {
connection: Connection,
io_threads: IoThreads,
params: InitializedParams,
}

type RequestHandler = fn(server: &Server, req: &Request) -> anyhow::Result<()>;
type NotificationHandler = fn(server: &Server, notif: &Notification) -> anyhow::Result<()>;

impl Server {
pub fn new(connection: Connection, io_threads: IoThreads) -> anyhow::Result<Self> {
let server_capabilities = serde_json::to_value(ServerCapabilities {
definition_provider: Some(OneOf::Left(true)),
..Default::default()
})
.unwrap();
let initialization_params =
serde_json::from_value(connection.initialize(server_capabilities)?)?;

Ok(Self {
connection,
io_threads,
params: initialization_params,
})
}

pub fn main_loop(&mut self) -> anyhow::Result<()> {
info!("Starting example main loop");
for msg in &self.connection.receiver {
info!("Got msg: {msg:?}");
match msg {
Message::Request(req) => {
if self.connection.handle_shutdown(&req)? {
return Ok(());
}
info!("Got request: {req:?}");

let handlers: [RequestHandler; 1] = [Self::handle_goto];

for handler in handlers {
let res = handler(self, &req);

if let Err(err) = res {
error!("{}", err.to_string());
}
}
}
Message::Response(resp) => {
info!("Got response: {resp:?}");
}
Message::Notification(not) => {
info!("Got notification: {not:?}");

let handlers: [NotificationHandler; 2] = [Self::handle_open, Self::handle_save];

for handler in handlers {
let res = handler(self, &not);

if let Err(err) = res {
error!("{}", err.to_string());
}
}
}
};
}

Ok(())
}

pub fn join(self) -> anyhow::Result<()> {
Ok(self.io_threads.join()?)
}

fn handle_open(&self, req: &Notification) -> anyhow::Result<()> {
let params = cast_notif::<DidOpenTextDocument>(req.clone())?;

dbg!(params);

Ok(())
}

fn handle_save(&self, req: &Notification) -> anyhow::Result<()> {
let params = cast_notif::<DidSaveTextDocument>(req.clone())?;

dbg!(&params);

let result = PublishDiagnosticsParams {
uri: params.text_document.uri,
diagnostics: vec![Diagnostic {
range: Range {
start: Position {
line: 0,
character: 0,
},
end: Position {
line: 0,
character: 0,
},
},
severity: None,
code: None,
code_description: None,
source: Some("Harper".to_string()),
message: "Testing testing 123".to_string(),
related_information: None,
tags: None,
data: None,
}],
version: None,
};

let result = serde_json::to_value(result)?;
self.connection
.sender
.send(Message::Notification(Notification {
method: PublishDiagnostics::METHOD.to_string(),
params: result,
}))?;

Ok(())
}

fn handle_goto(&self, req: &Request) -> anyhow::Result<()> {
let (id, params) = cast_request::<GotoDefinition>(req.clone())?;

info!("Got gotoDefinition request #{id}: {params:?}");
let result = Some(GotoDefinitionResponse::Array(vec![Location {
uri: params.text_document_position_params.text_document.uri,
range: lsp_types::Range {
start: Position {
line: 0,
character: 0,
},
end: Position {
line: 0,
character: 0,
},
},
}]));
let result = serde_json::to_value(&result).unwrap();
let resp = Response {
id,
result: Some(result),
error: None,
};
self.connection.sender.send(Message::Response(resp))?;

Ok(())
}
}

fn cast_request<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}

fn cast_notif<R>(notif: Notification) -> Result<R::Params, ExtractError<Notification>>
where
R: lsp_types::notification::Notification,
R::Params: serde::de::DeserializeOwned,
{
notif.extract(R::METHOD)
}

0 comments on commit 5ff1b34

Please sign in to comment.