use tower_lsp and start on caching

This commit is contained in:
acereca 2023-04-12 21:46:26 +02:00
parent 5b12d75c47
commit c04db217e1
2 changed files with 235 additions and 11 deletions

73
src/cache.rs Normal file
View File

@ -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<String>,
scopes: Vec<CachedScope>,
}
#[derive(Debug)]
pub struct DocumentCache {
pub documents: DashMap<PathBuf, CachedItems>,
}
#[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)
}
}

View File

@ -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; extern crate pest;
#[macro_use] #[macro_use]
extern crate pest_derive; extern crate pest_derive;
use pest::Parser; #[derive(Debug)]
struct Backend {
client: Client,
cache: DocumentCache,
}
#[derive(Parser)] #[derive(Parser)]
#[grammar = "skill.pest"] #[grammar = "./skill.pest"]
pub struct SkillParser; struct SkillParser;
fn main() { #[derive(Debug, Deserialize, Serialize)]
let mut args: Vec<String> = env::args().collect(); struct CustomNotificationParams {
let data = fs::read_to_string(args[1].as_mut_str()).expect("could not read from file"); title: String,
message: String,
}
impl CustomNotificationParams {
let parse = SkillParser::parse(Rule::skill, data.as_str()).expect("ha").next().unwrap(); fn new(title: impl Into<String>, message: impl Into<String>) -> Self {
for inner in parse.into_inner() { CustomNotificationParams {
println!("{:?}: {:?}", inner.as_rule(), inner); 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<InitializeResult> {
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::<CustomNotification>(CustomNotificationParams::new(
"title", "message",
))
.await;
}
async fn shutdown(&self) -> Result<()> {
Ok(())
}
async fn execute_command(&self, params: ExecuteCommandParams) -> Result<Option<Value>> {
if params.command == "custom.notification" {
self.client
.send_notification::<CustomNotification>(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<Option<CompletionResponse>> {
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<CompletionItem> = 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;
}