From c04db217e18d79c8ec609fc18c7e01fdfa0194f3 Mon Sep 17 00:00:00 2001 From: acereca Date: Wed, 12 Apr 2023 21:46:26 +0200 Subject: [PATCH] use tower_lsp and start on caching --- src/cache.rs | 73 ++++++++++++++++++++++ src/main.rs | 173 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 src/cache.rs diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 0000000..9073518 --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,73 @@ +use dashmap::DashMap; +use std::path::{Path, PathBuf}; + +#[derive(PartialEq, Debug)] +struct Position { + line: u32, + character: u16, +} + +#[derive(PartialEq, Debug)] +struct Range { + from: Position, + to: Position, +} + +#[derive(PartialEq, Debug)] +struct CachedScope { + range: Range, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct CachedItems { + global_tokens: Vec, + scopes: Vec, +} + +#[derive(Debug)] +pub struct DocumentCache { + pub documents: DashMap, +} + +#[derive(Debug, Clone)] +struct FileNotInCache; + +impl DocumentCache { + pub fn new() -> DocumentCache { + DocumentCache { + documents: DashMap::new(), + } + } + + pub fn update_document(&self, path: PathBuf, items: CachedItems) { + self.documents.insert(path, items); + } +} + +#[cfg(test)] +mod tests { + use crate::cache::{CachedItems, DocumentCache}; + use std::collections::HashMap; + use std::path::Path; + + #[test] + fn insert() { + let mut d = DocumentCache::new(Path::new("/example/workdir").to_path_buf()); + d.update_document( + Path::new("example_file.ext").to_path_buf(), + CachedItems { + global_tokens: vec![], + scopes: vec![], + }, + ); + let mut comp = HashMap::new(); + comp.insert( + Path::new("example_file.ext").to_path_buf(), + CachedItems { + global_tokens: vec![], + scopes: vec![], + }, + ); + assert_eq!(d.documents, comp) + } +} diff --git a/src/main.rs b/src/main.rs index 628d684..944593f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,172 @@ -use std::{env, fs}; +mod cache; +use cache::DocumentCache; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fs; +use std::path::{Path, PathBuf}; +use tower_lsp::jsonrpc::{Error, ErrorCode, Result}; +use tower_lsp::lsp_types::notification::Notification; +use tower_lsp::lsp_types::*; +use tower_lsp::{Client, LanguageServer, LspService, Server}; + +use crate::pest::Parser; + +extern crate glob; extern crate pest; #[macro_use] extern crate pest_derive; -use pest::Parser; +#[derive(Debug)] +struct Backend { + client: Client, + cache: DocumentCache, +} #[derive(Parser)] -#[grammar = "skill.pest"] -pub struct SkillParser; +#[grammar = "./skill.pest"] +struct SkillParser; -fn main() { - let mut args: Vec = env::args().collect(); - let data = fs::read_to_string(args[1].as_mut_str()).expect("could not read from file"); +#[derive(Debug, Deserialize, Serialize)] +struct CustomNotificationParams { + title: String, + message: String, +} - - let parse = SkillParser::parse(Rule::skill, data.as_str()).expect("ha").next().unwrap(); - for inner in parse.into_inner() { - println!("{:?}: {:?}", inner.as_rule(), inner); +impl CustomNotificationParams { + fn new(title: impl Into, message: impl Into) -> Self { + CustomNotificationParams { + title: title.into(), + message: message.into(), + } } } + +enum CustomNotification {} + +impl Notification for CustomNotification { + type Params = CustomNotificationParams; + + const METHOD: &'static str = "custom/notification"; +} + +#[tower_lsp::async_trait] +impl LanguageServer for Backend { + async fn initialize(&self, init_params: InitializeParams) -> Result { + let root = init_params + .root_uri + .ok_or(Error::new(ErrorCode::InvalidParams))?; + self.client + .log_message( + MessageType::INFO, + format!("server initializing! ({:?})", root.path()), + ) + .await; + + let pattern = root.path().to_string() + "/**/*.il"; + self.client + .log_message(MessageType::INFO, format!("pattern used: {:?}", pattern)) + .await; + for entry in glob::glob(pattern.as_str()).expect("no file to cache in root_dir") { + match entry { + Ok(path) => { + self.client + .log_message(MessageType::INFO, format!("caching {:?}", path.display())) + .await + } + Err(_) => {} + } + } + + Ok(InitializeResult { + server_info: None, + capabilities: ServerCapabilities { + execute_command_provider: Some(ExecuteCommandOptions { + commands: vec!["custom/notification".to_string()], + work_done_progress_options: Default::default(), + }), + completion_provider: Some(CompletionOptions { + resolve_provider: Some(false), + trigger_characters: Some(vec!["(".to_string()]), + work_done_progress_options: Default::default(), + all_commit_characters: None, + ..Default::default() + }), + ..ServerCapabilities::default() + }, + }) + } + + async fn initialized(&self, _: InitializedParams) { + self.client + .log_message(MessageType::INFO, "server initialized!") + .await; + self.client + .send_notification::(CustomNotificationParams::new( + "title", "message", + )) + .await; + } + + async fn shutdown(&self) -> Result<()> { + Ok(()) + } + + async fn execute_command(&self, params: ExecuteCommandParams) -> Result> { + if params.command == "custom.notification" { + self.client + .send_notification::(CustomNotificationParams::new( + "Hello", "Message", + )) + .await; + self.client + .log_message( + MessageType::INFO, + format!("Command executed with params: {params:?}"), + ) + .await; + Ok(None) + } else { + Err(Error::invalid_request()) + } + } + + async fn completion(&self, cparams: CompletionParams) -> Result> { + let doc = cparams.text_document_position.text_document.uri.path(); + // let line = cparams.text_document_position.position.line; + // let character = cparams.text_document_position.position.character; + let content = fs::read_to_string(doc).expect("could not read"); + let file = SkillParser::parse(Rule::skill, &content) + .expect("unsuccessful parse") + .next() + .unwrap(); + + let mut symbols: Vec = vec![]; + + for record in file.into_inner() { + match record.as_rule() { + Rule::assign => symbols.push(CompletionItem { + label: record.into_inner().next().unwrap().as_str().to_string(), + kind: Some(CompletionItemKind::VARIABLE), + ..Default::default() + }), + _ => {} + }; + } + + Ok(Some(CompletionResponse::Array(symbols))) + } +} + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt().init(); + + let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout()); + + let (service, socket) = LspService::new(|client| Backend { + client, + cache: DocumentCache::new(), + }); + Server::new(stdin, stdout, socket).serve(service).await; +}