From f99ab90c29758cebe936bca5ded389f0a841bda5 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 22 May 2023 12:29:21 +0200 Subject: [PATCH 01/27] add feat load imports --- uvls/src/main.rs | 77 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/uvls/src/main.rs b/uvls/src/main.rs index d34d319c..1121bd38 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -44,6 +44,35 @@ impl Backend { load_blocking(uri, &pipeline); }); } + + fn async load_all_imports(&self, uri: Url) { + let pipeline = self.pipeline.clone(); + let uri_clone = uri.clone(); + info!("load path :{}", uri); + tokio::task::spawn_blocking(move || { + load_blocking(uri, &pipeline); + }).await; + tokio::task::spawn_blocking(|| { + let piplineAfterLoad = self.pipeline.clone(); + let root = pipeline.root(); + let broot = root.borrow(); + let document = broot.file_by_uri(&uri_clone); + + match document { + Some(ast) => { + let imports = ast.imports(); + info!("imports amount {}", imports.len()); + for import in imports { + info!("import : {:?}", import.path); + } + } + None => { + info!("test") + } + } + }); + } + async fn snapshot(&self, uri: &Url, sync: bool) -> Result)>> { self.pipeline .snapshot(uri, sync) @@ -88,6 +117,7 @@ fn load_blocking(uri: Url, pipeline: &AsyncPipeline) { info!("Failed to load file {} : {}", uri, e); } } +//TODO remove method //load all files under given a path fn load_all_blocking(path: &Path, pipeline: AsyncPipeline) { for e in walkdir::WalkDir::new(path) @@ -121,17 +151,17 @@ impl LanguageServer for Backend { .as_deref() .or_else(|| init_params.root_uri.as_ref().map(|p| p.path())) .map(PathBuf::from); - if let Some(root_folder) = root_folder { - let semantic = self.pipeline.clone(); - //cheap fix for better intial load, we should really use priority model to prefer - //editor owned files - spawn(async move { - tokio::task::spawn_blocking(move || { - load_all_blocking(&root_folder, semantic); - }) - .await - }); - } + // if let Some(root_folder) = root_folder { + // let semantic = self.pipeline.clone(); + // //cheap fix for better intial load, we should really use priority model to prefer + // //editor owned files + // spawn(async move { + // tokio::task::spawn_blocking(move || { + // load_all_blocking(&root_folder, semantic); + // }) + // .await + // }); + // } if init_params .client_info .map(|info| matches!(info.name.as_str(), "Visual Studio Code")) @@ -219,11 +249,14 @@ impl LanguageServer for Backend { } async fn did_open(&self, params: DidOpenTextDocumentParams) { info!("received did_open {:?}", params.text_document.uri); - self.pipeline.open( - params.text_document.uri, - params.text_document.text, - DocumentState::OwnedByEditor, - ); + let state = self.pipeline.root(); + let uri = params.text_document.uri; + if state.borrow().contains(&uri) { + self.pipeline + .open(uri, params.text_document.text, DocumentState::OwnedByEditor); + } else { + self.load_all_imports(uri); + } info!("done did_open"); } async fn did_change(&self, params: DidChangeTextDocumentParams) { @@ -411,12 +444,9 @@ impl LanguageServer for Backend { character: 0, }, }, - command: if self - .pipeline - .inlay_state() - .is_active(ide::inlays::InlaySource::File(semantic::FileID::new( - uri.as_str(), - ))) { + command: if self.pipeline.inlay_state().is_active( + ide::inlays::InlaySource::File(semantic::FileID::new(uri.as_str())), + ) { Some(Command { title: "hide".into(), command: "uvls/hide_config".into(), @@ -487,12 +517,11 @@ fn main() { } async fn server_main() { std::env::set_var("RUST_BACKTRACE", "1"); - + log_panics::Config::new() .backtrace_mode(log_panics::BacktraceMode::Unresolved) .install_panic_hook(); - let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); //only needed for vscode auto update From 21c6a9dc6c886b4fcbf34463bf12bdc9a8eef023 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 22 May 2023 18:15:44 +0200 Subject: [PATCH 02/27] add feat simple file loading --- uvls/src/core/ast/def.rs | 5 +++ uvls/src/core/pipeline.rs | 30 +++++++++++++++-- uvls/src/main.rs | 68 +++++++++++---------------------------- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index d4e1197f..8f6ec489 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -41,6 +41,11 @@ impl Path { pub fn to_string(&self) -> String { self.names.iter().map(|i| i.as_str()).join(".") } + pub fn to_file(&self,root_folder: String) -> String { + let absoultpath = self.names.iter().map(|i| i.as_str()).join("/"); + let path = root_folder+ "/"+ &absoultpath+".uvl"; + path + } } //Type definitions for symbols diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 25aea98e..eedf58fd 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -1,10 +1,13 @@ -use crate::{core::*,smt,ide::inlays::InlayHandler}; +use crate::ROOT_FILE; +use crate::{core::*,smt,ide::inlays::InlayHandler,load_blocking}; use document::*; use check::*; use dashmap::DashMap; use hashbrown::HashMap; use log::info; use ropey::Rope; +use tokio::net::unix::pipe; +use std::path::Path; use std::{ sync::{ atomic::{AtomicU64, Ordering}, @@ -486,14 +489,35 @@ impl AsyncPipeline { let time = Instant::now(); if let Some(draft) = self.snapshot_draft(uri).await? { info!("waited {:?} for draft", time.elapsed()); - Ok(Some(if sync { + let result = Ok(Some(if sync { let timestamp = draft.timestamp(); (draft, self.snapshot_root_sync(uri, timestamp).await?) } else { (draft, self.snapshot_root(uri).await?) - })) + })); + self.load_imports(uri); + result } else { Ok(None) } } + fn load_imports(&self, uri: &Url){ + let rootBinding = self.rx_root.borrow(); + let doc = rootBinding.file_by_uri(&uri); + match doc { + Some(ast) =>{ + let imports = ast.imports(); + for import in imports { + let relative_path_string = import.path.to_file( ROOT_FILE.try_lock().unwrap().as_mut().to_string()); + let url_import = Url::from_file_path(&relative_path_string).unwrap(); + if !rootBinding.contains(&url_import) { + info!("load import{}",url_import); + load_blocking(url_import, self) + } + } + } + None =>{ + } + } + } } diff --git a/uvls/src/main.rs b/uvls/src/main.rs index 1121bd38..0e4c9aec 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -2,15 +2,17 @@ #![forbid(unsafe_code)] use flexi_logger::FileSpec; +use futures_util::lock::Mutex; use get_port::Ops; +use lazy_static::lazy_static; -use serde::Serialize; -use tokio::{join, spawn}; use log::info; +use serde::Serialize; use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::SystemTime; +use tokio::{join, spawn}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; @@ -29,6 +31,11 @@ impl Default for Settings { Settings { has_webview: false } } } + +lazy_static! { + pub static ref ROOT_FILE: Mutex = Mutex::new("".to_string()); +} + //The LSP struct Backend { client: Client, @@ -44,35 +51,6 @@ impl Backend { load_blocking(uri, &pipeline); }); } - - fn async load_all_imports(&self, uri: Url) { - let pipeline = self.pipeline.clone(); - let uri_clone = uri.clone(); - info!("load path :{}", uri); - tokio::task::spawn_blocking(move || { - load_blocking(uri, &pipeline); - }).await; - tokio::task::spawn_blocking(|| { - let piplineAfterLoad = self.pipeline.clone(); - let root = pipeline.root(); - let broot = root.borrow(); - let document = broot.file_by_uri(&uri_clone); - - match document { - Some(ast) => { - let imports = ast.imports(); - info!("imports amount {}", imports.len()); - for import in imports { - info!("import : {:?}", import.path); - } - } - None => { - info!("test") - } - } - }); - } - async fn snapshot(&self, uri: &Url, sync: bool) -> Result)>> { self.pipeline .snapshot(uri, sync) @@ -151,17 +129,9 @@ impl LanguageServer for Backend { .as_deref() .or_else(|| init_params.root_uri.as_ref().map(|p| p.path())) .map(PathBuf::from); - // if let Some(root_folder) = root_folder { - // let semantic = self.pipeline.clone(); - // //cheap fix for better intial load, we should really use priority model to prefer - // //editor owned files - // spawn(async move { - // tokio::task::spawn_blocking(move || { - // load_all_blocking(&root_folder, semantic); - // }) - // .await - // }); - // } + if let Some(root_folder) = root_folder { + *ROOT_FILE.lock().await = root_folder.to_str().unwrap().to_string(); + } if init_params .client_info .map(|info| matches!(info.name.as_str(), "Visual Studio Code")) @@ -249,14 +219,12 @@ impl LanguageServer for Backend { } async fn did_open(&self, params: DidOpenTextDocumentParams) { info!("received did_open {:?}", params.text_document.uri); - let state = self.pipeline.root(); - let uri = params.text_document.uri; - if state.borrow().contains(&uri) { - self.pipeline - .open(uri, params.text_document.text, DocumentState::OwnedByEditor); - } else { - self.load_all_imports(uri); - } + self.pipeline.open( + params.text_document.uri, + params.text_document.text, + DocumentState::OwnedByEditor, + ); + info!("done did_open"); } async fn did_change(&self, params: DidChangeTextDocumentParams) { From 8b2fc724ca7e48aff784bc0e2d9aa3610b4ec4fc Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 30 May 2023 13:02:55 +0200 Subject: [PATCH 03/27] update load files from uvl.json --- uvls/src/core/pipeline.rs | 40 ++++++++++++++++++++++++++++----------- uvls/src/main.rs | 37 +++++++++++++++--------------------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index eedf58fd..3aefd373 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -3,6 +3,7 @@ use crate::{core::*,smt,ide::inlays::InlayHandler,load_blocking}; use document::*; use check::*; use dashmap::DashMap; +use document::*; use hashbrown::HashMap; use log::info; use ropey::Rope; @@ -501,22 +502,39 @@ impl AsyncPipeline { Ok(None) } } - fn load_imports(&self, uri: &Url){ - let rootBinding = self.rx_root.borrow(); - let doc = rootBinding.file_by_uri(&uri); + //load import if not already loaded and if the file is a uvl.json then load the corresponding uvl file + // this method is called after the complete red tree of the uri is created + fn load_imports(&self, uri: &Url) { + let root_binding = self.rx_root.borrow(); + let doc = root_binding.file_by_uri(&uri); match doc { - Some(ast) =>{ + // it is a uvl + Some(ast) => { + // get all imports let imports = ast.imports(); - for import in imports { - let relative_path_string = import.path.to_file( ROOT_FILE.try_lock().unwrap().as_mut().to_string()); + for import in imports { + let relative_path_string = import + .path + .to_file(ROOT_FILE.try_lock().unwrap().as_mut().to_string()); let url_import = Url::from_file_path(&relative_path_string).unwrap(); - if !rootBinding.contains(&url_import) { - info!("load import{}",url_import); - load_blocking(url_import, self) + //check if import is already loaded, if not, load + if !root_binding.contains(&url_import) { + load_blocking(url_import, self); } - } + } } - None =>{ + // it is a uvl.json + None => { + let config = root_binding.config_by_uri(&uri); + match config { + Some(config_doc) => { + let url_file = config_doc.config.as_ref().unwrap().file.url(); + if !root_binding.contains(&url_file) { + load_blocking(url_file, self); + } + } + None => {} + } } } } diff --git a/uvls/src/main.rs b/uvls/src/main.rs index 0e4c9aec..fd4610a9 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -95,23 +95,7 @@ fn load_blocking(uri: Url, pipeline: &AsyncPipeline) { info!("Failed to load file {} : {}", uri, e); } } -//TODO remove method -//load all files under given a path -fn load_all_blocking(path: &Path, pipeline: AsyncPipeline) { - for e in walkdir::WalkDir::new(path) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.path().is_file()) - .filter(|e| { - e.path() - .extension() - .map(|e| e == std::ffi::OsStr::new("uvl")) - .unwrap_or(false) - }) - { - load_blocking(Url::from_file_path(e.path()).unwrap(), &pipeline) - } -} + fn shutdown_error() -> tower_lsp::jsonrpc::Error { tower_lsp::jsonrpc::Error { code: tower_lsp::jsonrpc::ErrorCode::InternalError, @@ -219,11 +203,20 @@ impl LanguageServer for Backend { } async fn did_open(&self, params: DidOpenTextDocumentParams) { info!("received did_open {:?}", params.text_document.uri); - self.pipeline.open( - params.text_document.uri, - params.text_document.text, - DocumentState::OwnedByEditor, - ); + if self + .pipeline + .root() + .borrow() + .contains(¶ms.text_document.uri) + { + self.pipeline.open( + params.text_document.uri, + params.text_document.text, + DocumentState::OwnedByEditor, + ); + } else { + load_blocking(params.text_document.uri, &self.pipeline); + } info!("done did_open"); } From 5da1ad83bee259e65f92c8750d04a74d99b554fb Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 30 May 2023 13:03:25 +0200 Subject: [PATCH 04/27] update auto completion import --- uvls/src/core/cache.rs | 85 ++++++++++++++++++++++++++++++++++++++ uvls/src/ide/completion.rs | 15 +++++++ 2 files changed, 100 insertions(+) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index edbcb301..17351791 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -8,6 +8,7 @@ use petgraph::prelude::*; use resolve::*; use std::sync::Arc; use ustr::Ustr; +use std::path; #[derive(Debug, Clone, PartialEq)] enum FSEdge { Path(Ustr), @@ -251,6 +252,7 @@ impl FileSystem { .find(|n| matches!(self.graph[*n], FSNode::Dir)) .unwrap() } + //all subfiles from origin under path, returns (prefix,filename,filenode) pub fn sub_files<'a>( &'a self, @@ -264,8 +266,91 @@ impl FileSystem { _ => true, }) } + + // find all files and subfiles of one file which are not loaded + pub fn all_sub_files<'a>( + &self, + origin_unc: FileID, + prefix: &[Ustr], + ) -> impl Iterator + 'a { + // + let origin = origin_unc.as_str().strip_prefix("file://").unwrap(); + + let mut suffix_helper: Vec<&str> = origin.split("/").collect(); + let suffix = suffix_helper.pop().unwrap(); + let root_dir = origin.strip_suffix(suffix).unwrap(); + + + let mut stack: Vec<(compact_str::CompactString, Ustr, FSNode)> = Vec::new(); + let mut dirs: Vec = Vec::new(); + for entry in walkdir::WalkDir::new(path::Path::new(root_dir)) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.path().is_file()) + .filter(|e| { + e.path() + .extension() + .map(|e| e == std::ffi::OsStr::new("uvl")) + .unwrap_or(false) + }) + { + let path = entry.path().to_str().unwrap(); + + if path != origin && !self.file2node.contains_key(&FileID::new(path)){ + info!("paht not loaded {}",path); + let mut valid_path = true; + let mut check_path = origin.clone(); + for i in prefix.iter() { + let mut check_prefix = "/".to_owned(); + check_prefix.push_str(&i.as_str().to_owned()); + if check_path.starts_with(check_prefix.as_str()) { + let strip_prefix = check_path.strip_prefix(check_prefix.as_str()); + check_path = strip_prefix.unwrap(); + } else { + valid_path = false; + break; + } + } + if valid_path { + let name_op = path + .strip_prefix(root_dir); + match name_op { + Some(name) => { + let new_name = name.replace("/", ".").replace(".uvl", ""); + stack.push(( + new_name.as_str().into(), + Ustr::from(new_name.as_str()), + FSNode::File(FileID::new(&path)), + )); + let mut is_dir = true; + let mut dir_names: Vec<&str> = new_name.split(".").collect(); + while is_dir { + let dir_name = dir_names.join("."); + if dir_name.is_empty() || dirs.contains(&dir_name) { + is_dir = false; + } else { + stack.push(( + dir_name.as_str().into(), + Ustr::from(&dir_name.as_str()), + FSNode::Dir, + )); + dirs.push(dir_name); + let _ = dir_names.pop(); + } + } + } + _ => {} + } + } + } + } + std::iter::from_fn(move || stack.pop()) + } + } + + #[derive(Debug, Clone)] pub struct LinkedAstDocument { pub content: Arc, diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index 8a12eaec..da03ad7e 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -990,6 +990,21 @@ fn compute_completions_impl( &ctx, )) } + for (path, name, node) in snapshot.fs().all_sub_files(origin, &ctx.prefix) { + let len = path.as_str().chars().filter(|c| c == &'.').count(); + top.push(CompletionOpt::new( + match node { + FSNode::Dir => CompletionKind::Folder, + _ => CompletionKind::File, + }, + name, + path.clone(), + len, + TextOP::Put(path), + &ctx, + )) + } + is_incomplete = true } CompletionEnv::Include => { From fdaed842d2d07e6c2c1b9d46ea80ae3c5983eb9a Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 6 Jun 2023 13:07:44 +0200 Subject: [PATCH 05/27] update auto completion imports and load uvl.json --- uvls/Cargo.toml | 1 + uvls/src/core/cache.rs | 12 +++++++++--- uvls/src/core/pipeline.rs | 19 +++++++++++++------ uvls/src/ide/completion.rs | 2 ++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/uvls/Cargo.toml b/uvls/Cargo.toml index b4b1b7c7..b271b56c 100644 --- a/uvls/Cargo.toml +++ b/uvls/Cargo.toml @@ -48,3 +48,4 @@ get-port = "4.0.0" open = "4.0.1" nom = "7.1.3" percent-encoding = "2.2.0" +filetime = "0.2" diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index 17351791..bec6111e 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -294,10 +294,13 @@ impl FileSystem { .unwrap_or(false) }) { - let path = entry.path().to_str().unwrap(); + let path= entry.path().to_str().unwrap(); - if path != origin && !self.file2node.contains_key(&FileID::new(path)){ - info!("paht not loaded {}",path); + if path != origin && None == self.file2node.keys().find(|&&ele| { + let check_path = ele.as_str().strip_prefix("file://").unwrap(); + check_path.eq(path) + + }){ let mut valid_path = true; let mut check_path = origin.clone(); for i in prefix.iter() { @@ -324,6 +327,9 @@ impl FileSystem { )); let mut is_dir = true; let mut dir_names: Vec<&str> = new_name.split(".").collect(); + if ! dir_names.is_empty() { + let _ = dir_names.pop(); + } while is_dir { let dir_name = dir_names.join("."); if dir_name.is_empty() || dirs.contains(&dir_name) { diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 3aefd373..c1c24094 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -1,14 +1,11 @@ use crate::ROOT_FILE; -use crate::{core::*,smt,ide::inlays::InlayHandler,load_blocking}; -use document::*; +use crate::{core::*, ide::inlays::InlayHandler, load_blocking, smt}; use check::*; use dashmap::DashMap; use document::*; use hashbrown::HashMap; use log::info; use ropey::Rope; -use tokio::net::unix::pipe; -use std::path::Path; use std::{ sync::{ atomic::{AtomicU64, Ordering}, @@ -16,6 +13,7 @@ use std::{ }, time::SystemTime, }; +use filetime::{set_file_mtime, FileTime}; use tokio::{ select, spawn, sync::{broadcast, mpsc, oneshot, watch}, @@ -519,7 +517,10 @@ impl AsyncPipeline { let url_import = Url::from_file_path(&relative_path_string).unwrap(); //check if import is already loaded, if not, load if !root_binding.contains(&url_import) { - load_blocking(url_import, self); + let pipeline = self.clone(); + tokio::task::spawn_blocking(move || { + load_blocking(url_import, &pipeline); + }); } } } @@ -530,7 +531,13 @@ impl AsyncPipeline { Some(config_doc) => { let url_file = config_doc.config.as_ref().unwrap().file.url(); if !root_binding.contains(&url_file) { - load_blocking(url_file, self); + let pipeline = self.clone(); + let open_url = uri.clone(); + tokio::task::spawn_blocking(move || { + load_blocking(url_file, &pipeline); + let _ = set_file_mtime(open_url.path(), FileTime::now()); + load_blocking(open_url, &pipeline); + }); } } None => {} diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index da03ad7e..666b02f4 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -976,6 +976,7 @@ fn compute_completions_impl( is_incomplete = true } CompletionEnv::Import => { + // complete all files and dic which are already loaded for (path, name, node) in snapshot.fs().sub_files(origin, &ctx.prefix) { let len = path.as_str().chars().filter(|c| c == &'.').count(); top.push(CompletionOpt::new( @@ -990,6 +991,7 @@ fn compute_completions_impl( &ctx, )) } + // complete all files and dic which are not loaded for (path, name, node) in snapshot.fs().all_sub_files(origin, &ctx.prefix) { let len = path.as_str().chars().filter(|c| c == &'.').count(); top.push(CompletionOpt::new( From 3abedc044779c03db5f3db854b183e776c590f30 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 6 Jun 2023 18:29:54 +0200 Subject: [PATCH 06/27] add comments for auto completion imports --- uvls/src/core/cache.rs | 236 ++++++++++++++++++-------------------- uvls/src/core/pipeline.rs | 1 + 2 files changed, 114 insertions(+), 123 deletions(-) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index bec6111e..ad706bb1 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -6,9 +6,9 @@ use log::info; use module::{ConfigModule, Module}; use petgraph::prelude::*; use resolve::*; +use std::path; use std::sync::Arc; use ustr::Ustr; -use std::path; #[derive(Debug, Clone, PartialEq)] enum FSEdge { Path(Ustr), @@ -33,11 +33,7 @@ pub struct FileSystem { file2node: HashMap, } impl FileSystem { - fn goto_dir( - graph: &DiGraph, - mut root: NodeIndex, - path: &[Ustr], - ) -> Option { + fn goto_dir(graph: &DiGraph, mut root: NodeIndex, path: &[Ustr]) -> Option { if !graph[root].is_dir() { root = graph .neighbors_directed(root, Incoming) @@ -63,18 +59,10 @@ impl FileSystem { None } } - fn goto_file( - graph: &DiGraph, - root: NodeIndex, - path: &[Ustr], - ) -> Option { + fn goto_file(graph: &DiGraph, root: NodeIndex, path: &[Ustr]) -> Option { Self::goto_dir(graph, root, &path[0..path.len() - 1]).and_then(|dir| { graph.edges(dir).find_map(|e| match e.weight() { - FSEdge::Path(name) - if name == &path[path.len() - 1] && !graph[e.target()].is_dir() => - { - Some(e.target()) - } + FSEdge::Path(name) if name == &path[path.len() - 1] && !graph[e.target()].is_dir() => Some(e.target()), _ => None, }) }) @@ -130,12 +118,12 @@ impl FileSystem { } //all imports from a pub fn imports(&self, a: FileID) -> impl Iterator + '_ { - self.graph.edges(self.file2node[&a]).filter_map(|e| { - match (e.weight(), &self.graph[e.target()]) { + self.graph + .edges(self.file2node[&a]) + .filter_map(|e| match (e.weight(), &self.graph[e.target()]) { (FSEdge::Import(sym), FSNode::File(name)) => Some((*sym, *name)), _ => None, - } - }) + }) } pub fn recursive_imports(&self, src: FileID) -> Vec { let mut out = HashSet::new(); @@ -198,11 +186,9 @@ impl FileSystem { return None; } } - Self::goto_file(&self.graph, self.file2node[&origin], path).and_then(|node| { - match &self.graph[node] { - FSNode::File(id) => Some(*id), - _ => None, - } + Self::goto_file(&self.graph, self.file2node[&origin], path).and_then(|node| match &self.graph[node] { + FSNode::File(id) => Some(*id), + _ => None, }) } pub fn dir_files<'a>( @@ -234,11 +220,9 @@ impl FileSystem { stack.pop().map(|(path, name, node)| { for i in self.graph.edges(node) { match i.weight() { - FSEdge::Path(name) => stack.push(( - [path.as_str(), name.as_str()].join_compact("."), - *name, - i.target(), - )), + FSEdge::Path(name) => { + stack.push(([path.as_str(), name.as_str()].join_compact("."), *name, i.target())) + } _ => {} } } @@ -260,88 +244,101 @@ impl FileSystem { path: &[Ustr], ) -> impl Iterator + 'a { let dir = self.dir_of(origin); - self.dir_files(dir, path) - .filter(move |(_, _, node)| match node { - FSNode::File(tgt) => tgt != &origin, - _ => true, - }) + self.dir_files(dir, path).filter(move |(_, _, node)| match node { + FSNode::File(tgt) => tgt != &origin, + _ => true, + }) } - - // find all files and subfiles of one file which are not loaded + /** + * find all subfiles, subdirectories and files on the same level as the currently opened file, + * but only those that have not been loaded yet + */ pub fn all_sub_files<'a>( &self, origin_unc: FileID, prefix: &[Ustr], ) -> impl Iterator + 'a { - // - let origin = origin_unc.as_str().strip_prefix("file://").unwrap(); - - let mut suffix_helper: Vec<&str> = origin.split("/").collect(); - let suffix = suffix_helper.pop().unwrap(); - let root_dir = origin.strip_suffix(suffix).unwrap(); - - let mut stack: Vec<(compact_str::CompactString, Ustr, FSNode)> = Vec::new(); let mut dirs: Vec = Vec::new(); - for entry in walkdir::WalkDir::new(path::Path::new(root_dir)) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.path().is_file()) - .filter(|e| { - e.path() - .extension() - .map(|e| e == std::ffi::OsStr::new("uvl")) - .unwrap_or(false) - }) - { - let path= entry.path().to_str().unwrap(); - - if path != origin && None == self.file2node.keys().find(|&&ele| { - let check_path = ele.as_str().strip_prefix("file://").unwrap(); - check_path.eq(path) - - }){ - let mut valid_path = true; - let mut check_path = origin.clone(); - for i in prefix.iter() { - let mut check_prefix = "/".to_owned(); - check_prefix.push_str(&i.as_str().to_owned()); - if check_path.starts_with(check_prefix.as_str()) { - let strip_prefix = check_path.strip_prefix(check_prefix.as_str()); - check_path = strip_prefix.unwrap(); - } else { - valid_path = false; - break; - } - } - if valid_path { - let name_op = path - .strip_prefix(root_dir); - match name_op { - Some(name) => { - let new_name = name.replace("/", ".").replace(".uvl", ""); - stack.push(( - new_name.as_str().into(), - Ustr::from(new_name.as_str()), - FSNode::File(FileID::new(&path)), - )); - let mut is_dir = true; - let mut dir_names: Vec<&str> = new_name.split(".").collect(); - if ! dir_names.is_empty() { - let _ = dir_names.pop(); - } - while is_dir { - let dir_name = dir_names.join("."); - if dir_name.is_empty() || dirs.contains(&dir_name) { - is_dir = false; - } else { - stack.push(( - dir_name.as_str().into(), - Ustr::from(&dir_name.as_str()), - FSNode::Dir, - )); - dirs.push(dir_name); - let _ = dir_names.pop(); + //remove "file://"" from uri, because only the path is needed + match origin_unc.as_str().strip_prefix("file://") { + Some(origin) => { + let mut suffix_helper: Vec<&str> = origin.split("/").collect(); + let suffix = suffix_helper.pop().unwrap(); + let root_dir = origin.strip_suffix(suffix).unwrap(); + //Retrieve all uvl files and subfiles from the current directory + for entry in walkdir::WalkDir::new(path::Path::new(root_dir)) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.path().is_file()) + .filter(|e| { + e.path() + .extension() + .map(|e| e == std::ffi::OsStr::new("uvl")) + .unwrap_or(false) + }) + { + match entry.path().to_str() { + Some(path) => { + // check if the file has not been loaded yet and if it is not an open file + if path != origin + && None + == self + .file2node + .keys() + .find(|&&ele| match ele.as_str().strip_prefix("file://") { + Some(check_path) => check_path.eq(path), + None => false, + }) + { + // checks if already written text is prefix of the path + let mut valid_path = true; + let mut check_path = origin.clone(); + for i in prefix.iter() { + let mut check_prefix = "/".to_owned(); + check_prefix.push_str(&i.as_str().to_owned()); + if check_path.starts_with(check_prefix.as_str()) { + let strip_prefix = check_path.strip_prefix(check_prefix.as_str()); + check_path = strip_prefix.unwrap(); + } else { + valid_path = false; + break; + } + } + if valid_path { + let name_op = path.strip_prefix(root_dir); + match name_op { + Some(name) => { + let new_name = name.replace("/", ".").replace(".uvl", ""); + // safe file for autoCompletion + stack.push(( + new_name.as_str().into(), + Ustr::from(new_name.as_str()), + FSNode::File(FileID::new(&path)), + )); + let mut is_dir = true; + let mut dir_names: Vec<&str> = new_name.split(".").collect(); + if !dir_names.is_empty() { + let _ = dir_names.pop(); + } + // add all parent directories, for autocompletion, but only if they are not yet added + while is_dir { + let dir_name = dir_names.join("."); + if dir_name.is_empty() || dirs.contains(&dir_name) { + is_dir = false; + } else { + stack.push(( + dir_name.as_str().into(), + Ustr::from(&dir_name.as_str()), + FSNode::Dir, + )); + dirs.push(dir_name); + let _ = dir_names.pop(); + } + } + } + _ => {} + } } } } @@ -349,14 +346,15 @@ impl FileSystem { } } } + _ => { + info!("uri has wrong form: {} ", origin_unc.as_str()); + } } + std::iter::from_fn(move || stack.pop()) } - } - - #[derive(Debug, Clone)] pub struct LinkedAstDocument { pub content: Arc, @@ -372,10 +370,7 @@ fn find_root_files<'a>(fs: &FileSystem, files: &'a AstFiles) -> impl Iterator = find_root_files(&fs, files) .map(|root| { let imports = fs.recursive_imports(root); - if imports.iter().any(|i| trans_dirty.contains(i)) - || !old.modules.contains_key(&root) - { + if imports.iter().any(|i| trans_dirty.contains(i)) || !old.modules.contains_key(&root) { (root, Arc::new(Module::new(root, &fs, &linked_ast))) } else { (root, old.modules[&root].clone()) @@ -450,9 +443,8 @@ impl Cache { if let Some(content) = v.config.as_ref() { info!("uri {}", content.file.as_str()); if files.contains_key(&content.file) { - let dirty = trans_dirty.contains(&content.file) - || dirty.contains(k) - || !old.config_modules.contains_key(k); + let dirty = + trans_dirty.contains(&content.file) || dirty.contains(k) || !old.config_modules.contains_key(k); if dirty { //recreate let mut module = Module::new(content.file, &fs, &linked_ast); @@ -461,15 +453,13 @@ impl Cache { *k, Arc::new(ConfigModule { module: Arc::new(module), - values:Default::default(), - source_map:Default::default(), + values: Default::default(), + source_map: Default::default(), }), ); } else { - let (values, source_map) = module - .resolve_config(&content.config, |span, err| { - errors.span(span, *k, 20, err) - }); + let (values, source_map) = + module.resolve_config(&content.config, |span, err| errors.span(span, *k, 20, err)); module.ok &= !errors.has_error(*k); config_modules.insert( *k, diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index c1c24094..7f9f79ff 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -535,6 +535,7 @@ impl AsyncPipeline { let open_url = uri.clone(); tokio::task::spawn_blocking(move || { load_blocking(url_file, &pipeline); + //update modified so uvl.json can be loaded let _ = set_file_mtime(open_url.path(), FileTime::now()); load_blocking(open_url, &pipeline); }); From 278e8bb094ed862f0c21677cebd9b95c6bc03354 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 20 Jun 2023 10:26:29 +0200 Subject: [PATCH 07/27] fix auto completion for not loaded files and dir --- uvls/src/core/cache.rs | 134 +++++++++++++++++++++++-------------- uvls/src/ide/completion.rs | 2 +- uvls/src/main.rs | 2 +- 3 files changed, 86 insertions(+), 52 deletions(-) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index ad706bb1..7b78091c 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -1,6 +1,6 @@ use crate::core::*; use check::ErrorsAcc; -use compact_str::CompactStringExt; +use compact_str::{CompactString, CompactStringExt}; use hashbrown::{HashMap, HashSet}; use log::info; use module::{ConfigModule, Module}; @@ -33,7 +33,11 @@ pub struct FileSystem { file2node: HashMap, } impl FileSystem { - fn goto_dir(graph: &DiGraph, mut root: NodeIndex, path: &[Ustr]) -> Option { + fn goto_dir( + graph: &DiGraph, + mut root: NodeIndex, + path: &[Ustr], + ) -> Option { if !graph[root].is_dir() { root = graph .neighbors_directed(root, Incoming) @@ -59,10 +63,18 @@ impl FileSystem { None } } - fn goto_file(graph: &DiGraph, root: NodeIndex, path: &[Ustr]) -> Option { + fn goto_file( + graph: &DiGraph, + root: NodeIndex, + path: &[Ustr], + ) -> Option { Self::goto_dir(graph, root, &path[0..path.len() - 1]).and_then(|dir| { graph.edges(dir).find_map(|e| match e.weight() { - FSEdge::Path(name) if name == &path[path.len() - 1] && !graph[e.target()].is_dir() => Some(e.target()), + FSEdge::Path(name) + if name == &path[path.len() - 1] && !graph[e.target()].is_dir() => + { + Some(e.target()) + } _ => None, }) }) @@ -118,12 +130,12 @@ impl FileSystem { } //all imports from a pub fn imports(&self, a: FileID) -> impl Iterator + '_ { - self.graph - .edges(self.file2node[&a]) - .filter_map(|e| match (e.weight(), &self.graph[e.target()]) { + self.graph.edges(self.file2node[&a]).filter_map(|e| { + match (e.weight(), &self.graph[e.target()]) { (FSEdge::Import(sym), FSNode::File(name)) => Some((*sym, *name)), _ => None, - }) + } + }) } pub fn recursive_imports(&self, src: FileID) -> Vec { let mut out = HashSet::new(); @@ -186,9 +198,11 @@ impl FileSystem { return None; } } - Self::goto_file(&self.graph, self.file2node[&origin], path).and_then(|node| match &self.graph[node] { - FSNode::File(id) => Some(*id), - _ => None, + Self::goto_file(&self.graph, self.file2node[&origin], path).and_then(|node| { + match &self.graph[node] { + FSNode::File(id) => Some(*id), + _ => None, + } }) } pub fn dir_files<'a>( @@ -220,9 +234,11 @@ impl FileSystem { stack.pop().map(|(path, name, node)| { for i in self.graph.edges(node) { match i.weight() { - FSEdge::Path(name) => { - stack.push(([path.as_str(), name.as_str()].join_compact("."), *name, i.target())) - } + FSEdge::Path(name) => stack.push(( + [path.as_str(), name.as_str()].join_compact("."), + *name, + i.target(), + )), _ => {} } } @@ -244,10 +260,11 @@ impl FileSystem { path: &[Ustr], ) -> impl Iterator + 'a { let dir = self.dir_of(origin); - self.dir_files(dir, path).filter(move |(_, _, node)| match node { - FSNode::File(tgt) => tgt != &origin, - _ => true, - }) + self.dir_files(dir, path) + .filter(move |(_, _, node)| match node { + FSNode::File(tgt) => tgt != &origin, + _ => true, + }) } /** * find all subfiles, subdirectories and files on the same level as the currently opened file, @@ -257,6 +274,7 @@ impl FileSystem { &self, origin_unc: FileID, prefix: &[Ustr], + postfix: CompactString, ) -> impl Iterator + 'a { let mut stack: Vec<(compact_str::CompactString, Ustr, FSNode)> = Vec::new(); let mut dirs: Vec = Vec::new(); @@ -283,33 +301,40 @@ impl FileSystem { // check if the file has not been loaded yet and if it is not an open file if path != origin && None - == self - .file2node - .keys() - .find(|&&ele| match ele.as_str().strip_prefix("file://") { + == self.file2node.keys().find(|&&ele| { + match ele.as_str().strip_prefix("file://") { Some(check_path) => check_path.eq(path), None => false, - }) + } + }) { - // checks if already written text is prefix of the path - let mut valid_path = true; - let mut check_path = origin.clone(); - for i in prefix.iter() { - let mut check_prefix = "/".to_owned(); - check_prefix.push_str(&i.as_str().to_owned()); - if check_path.starts_with(check_prefix.as_str()) { - let strip_prefix = check_path.strip_prefix(check_prefix.as_str()); - check_path = strip_prefix.unwrap(); - } else { - valid_path = false; - break; - } - } - if valid_path { - let name_op = path.strip_prefix(root_dir); - match name_op { - Some(name) => { - let new_name = name.replace("/", ".").replace(".uvl", ""); + let name_op = path.strip_prefix(root_dir); + match name_op { + Some(name) => { + // checks if already written text is prefix of the path + let mut valid_path = true; + let mut check_path: Vec<&str> = name.clone().split("/").collect(); + for i in prefix.iter() { + let check_prefix = i.as_str(); + + match check_path.iter().position(|&ele| ele == check_prefix) { + Some(0) =>{ + let _ = check_path.remove(0); + } + _ => { + valid_path = false; + break; + } + } + } + match check_path.iter().position(|&ele| ele.starts_with(postfix.as_str())) { + Some(0) => {} + _ => valid_path = false, + } + let name_up = check_path.join("/"); + if valid_path { + let new_name = + name_up.replace("/", ".").replace(".uvl", ""); // safe file for autoCompletion stack.push(( new_name.as_str().into(), @@ -317,7 +342,8 @@ impl FileSystem { FSNode::File(FileID::new(&path)), )); let mut is_dir = true; - let mut dir_names: Vec<&str> = new_name.split(".").collect(); + let mut dir_names: Vec<&str> = + new_name.split(".").collect(); if !dir_names.is_empty() { let _ = dir_names.pop(); } @@ -337,8 +363,8 @@ impl FileSystem { } } } - _ => {} } + _ => {} } } } @@ -370,7 +396,10 @@ fn find_root_files<'a>(fs: &FileSystem, files: &'a AstFiles) -> impl Iterator = find_root_files(&fs, files) .map(|root| { let imports = fs.recursive_imports(root); - if imports.iter().any(|i| trans_dirty.contains(i)) || !old.modules.contains_key(&root) { + if imports.iter().any(|i| trans_dirty.contains(i)) + || !old.modules.contains_key(&root) + { (root, Arc::new(Module::new(root, &fs, &linked_ast))) } else { (root, old.modules[&root].clone()) @@ -443,8 +474,9 @@ impl Cache { if let Some(content) = v.config.as_ref() { info!("uri {}", content.file.as_str()); if files.contains_key(&content.file) { - let dirty = - trans_dirty.contains(&content.file) || dirty.contains(k) || !old.config_modules.contains_key(k); + let dirty = trans_dirty.contains(&content.file) + || dirty.contains(k) + || !old.config_modules.contains_key(k); if dirty { //recreate let mut module = Module::new(content.file, &fs, &linked_ast); @@ -458,8 +490,10 @@ impl Cache { }), ); } else { - let (values, source_map) = - module.resolve_config(&content.config, |span, err| errors.span(span, *k, 20, err)); + let (values, source_map) = module + .resolve_config(&content.config, |span, err| { + errors.span(span, *k, 20, err) + }); module.ok &= !errors.has_error(*k); config_modules.insert( *k, diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index 666b02f4..9b09d013 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -992,7 +992,7 @@ fn compute_completions_impl( )) } // complete all files and dic which are not loaded - for (path, name, node) in snapshot.fs().all_sub_files(origin, &ctx.prefix) { + for (path, name, node) in snapshot.fs().all_sub_files(origin, & ctx.prefix,ctx.postfix.clone()) { let len = path.as_str().chars().filter(|c| c == &'.').count(); top.push(CompletionOpt::new( match node { diff --git a/uvls/src/main.rs b/uvls/src/main.rs index fd4610a9..1ce2f726 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -9,7 +9,7 @@ use lazy_static::lazy_static; use log::info; use serde::Serialize; use std::io::Read; -use std::path::{Path, PathBuf}; +use std::path::{PathBuf}; use std::sync::Arc; use std::time::SystemTime; use tokio::{join, spawn}; From f7db1d4b185b307b0b1de92f4928d81c225e0fe8 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 20 Jun 2023 12:47:27 +0200 Subject: [PATCH 08/27] fix copy create problem --- uvls/src/main.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/uvls/src/main.rs b/uvls/src/main.rs index 29cb6116..bc2c080e 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -9,13 +9,11 @@ use lazy_static::lazy_static; use log::info; use serde::Serialize; use tokio::{join, spawn}; -use log::info; use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; +use std::path::{PathBuf}; use percent_encoding::percent_decode_str; use std::sync::Arc; use std::time::SystemTime; -use tokio::{join, spawn}; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; @@ -337,10 +335,14 @@ impl LanguageServer for Backend { } async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { info!("file change {:?}", params); + let mut created_flag = true; for i in params.changes { match i.typ { FileChangeType::CREATED => { - self.load(i.uri); + if created_flag { + self.load(i.uri); + created_flag = false; + } } FileChangeType::CHANGED => { self.load(i.uri); From 808363c01eb32179dcff288b4da62eb79120ab93 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Fri, 23 Jun 2023 14:49:48 +0200 Subject: [PATCH 09/27] remove nameapce by imports --- uvls/src/core/ast/transform.rs | 13 +++++++------ uvls/src/main.rs | 5 +---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/uvls/src/core/ast/transform.rs b/uvls/src/core/ast/transform.rs index ff2d1c63..086706d1 100644 --- a/uvls/src/core/ast/transform.rs +++ b/uvls/src/core/ast/transform.rs @@ -1174,12 +1174,13 @@ pub fn visit_root(source: Rope, tree: Tree, uri: Url, timestamp: Instant) -> Ast state.connect(); (state.ast, state.errors) }; - let mut path = uri_to_path(&uri).unwrap(); - if let Some(ns) = ast.namespace.as_ref() { - let len = path.len().saturating_sub(ns.names.len()); - path.truncate(len); - path.extend_from_slice(&ns.names); - } + let path = uri_to_path(&uri).unwrap(); + // without this Code namespaces are ignored for imports and the normal path is used + // if let Some(ns) = ast.namespace.as_ref() { + // let len = path.len().saturating_sub(ns.names.len()); + // path.truncate(len); + // path.extend_from_slice(&ns.names); + // } AstDocument { id: FileID::from_uri(&uri), path, diff --git a/uvls/src/main.rs b/uvls/src/main.rs index bc2c080e..c9b5e733 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -335,14 +335,11 @@ impl LanguageServer for Backend { } async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { info!("file change {:?}", params); - let mut created_flag = true; for i in params.changes { match i.typ { FileChangeType::CREATED => { - if created_flag { self.load(i.uri); - created_flag = false; - } + break; } FileChangeType::CHANGED => { self.load(i.uri); From c615e302eeaa2650146d1426b03cabcf14bcf3da Mon Sep 17 00:00:00 2001 From: Tobias Schrull Date: Fri, 30 Jun 2023 15:15:37 +0200 Subject: [PATCH 10/27] add windows support.. (again) Universal-Variability-Language/uvl-lsp#13 --- uvls/src/core/cache.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index 7b78091c..a0199fd3 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -283,9 +283,17 @@ impl FileSystem { Some(origin) => { let mut suffix_helper: Vec<&str> = origin.split("/").collect(); let suffix = suffix_helper.pop().unwrap(); - let root_dir = origin.strip_suffix(suffix).unwrap(); + let mut root_dir = origin.strip_suffix(suffix).unwrap(); + // Handling for Windows systems + if std::env::consts::OS == "windows" + && !path::Path::new(root_dir).is_dir() + && root_dir.starts_with("/") + { + root_dir = root_dir.strip_prefix("/").unwrap(); + } + let path = path::Path::new(root_dir); //Retrieve all uvl files and subfiles from the current directory - for entry in walkdir::WalkDir::new(path::Path::new(root_dir)) + for entry in walkdir::WalkDir::new(path) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.path().is_file()) @@ -313,12 +321,15 @@ impl FileSystem { Some(name) => { // checks if already written text is prefix of the path let mut valid_path = true; - let mut check_path: Vec<&str> = name.clone().split("/").collect(); + let mut check_path: Vec<&str> = name.clone().split("/").collect(); for i in prefix.iter() { - let check_prefix = i.as_str(); + let check_prefix = i.as_str(); - match check_path.iter().position(|&ele| ele == check_prefix) { - Some(0) =>{ + match check_path + .iter() + .position(|&ele| ele == check_prefix) + { + Some(0) => { let _ = check_path.remove(0); } _ => { @@ -327,12 +338,15 @@ impl FileSystem { } } } - match check_path.iter().position(|&ele| ele.starts_with(postfix.as_str())) { + match check_path + .iter() + .position(|&ele| ele.starts_with(postfix.as_str())) + { Some(0) => {} - _ => valid_path = false, + _ => valid_path = false, } let name_up = check_path.join("/"); - if valid_path { + if valid_path { let new_name = name_up.replace("/", ".").replace(".uvl", ""); // safe file for autoCompletion From ea4b341944d9648377aece80e9bcfeb3b4e7ebb6 Mon Sep 17 00:00:00 2001 From: Tobias Schrull Date: Fri, 30 Jun 2023 16:31:10 +0200 Subject: [PATCH 11/27] add (ugly) windows support for configurations (again) Universal-Variability-Language/uvl-lsp#13 --- uvls/src/core/cache.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index a0199fd3..4c462121 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -487,13 +487,28 @@ impl Cache { for (k, v) in configs.iter() { if let Some(content) = v.config.as_ref() { info!("uri {}", content.file.as_str()); - if files.contains_key(&content.file) { - let dirty = trans_dirty.contains(&content.file) + let fileid = if files.contains_key(&content.file) { + content.file + } else { + // Windows inconvenience (we now select the files-key which equals out uri): + *files + .keys() + .find(|key| { + key.as_str().strip_prefix("file:///").unwrap_or("") + == content.file.as_str().strip_prefix("file://").unwrap_or("").replace("\\", "/") + }) + .unwrap_or(&FileID::new("X")) + }; + if files.contains_key(&fileid) { + let dirty = trans_dirty.contains(&fileid) + || trans_dirty.contains(&FileID::from_uri( + &tower_lsp::lsp_types::Url::parse(fileid.as_str()).unwrap(), + )) || dirty.contains(k) || !old.config_modules.contains_key(k); if dirty { //recreate - let mut module = Module::new(content.file, &fs, &linked_ast); + let mut module = Module::new(fileid, &fs, &linked_ast); if !module.ok { config_modules.insert( *k, @@ -523,7 +538,12 @@ impl Cache { config_modules.insert(*k, old.config_modules[k].clone()); } } else { - errors.span(content.file_span.clone(), *k, 100, "file no found"); + errors.span( + content.file_span.clone(), + *k, + 100, + "File not found, please open it explicitly in your editor", + ); } } } From 3256ecbc1b503cc1fc52cf9b152c84b5def5b46d Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 3 Jul 2023 13:05:51 +0200 Subject: [PATCH 12/27] add windows support for completion --- uvls/src/core/cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index 4c462121..1eacbbfd 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -348,7 +348,7 @@ impl FileSystem { let name_up = check_path.join("/"); if valid_path { let new_name = - name_up.replace("/", ".").replace(".uvl", ""); + name_up.replace("/", ".").replace("\\", ".").replace(".uvl", ""); // safe file for autoCompletion stack.push(( new_name.as_str().into(), From aa06d2cc24f6159b46b12e4ff4b9f60be1a4183e Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 4 Jul 2023 13:05:53 +0200 Subject: [PATCH 13/27] fix namspace problem --- uvls/src/core/semantic.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/uvls/src/core/semantic.rs b/uvls/src/core/semantic.rs index 8fa61e09..7d4ad234 100644 --- a/uvls/src/core/semantic.rs +++ b/uvls/src/core/semantic.rs @@ -2,13 +2,13 @@ use crate::core::*; use hashbrown::{HashMap, HashSet}; use log::info; +use percent_encoding::percent_decode_str; use std::fmt::Debug; use std::sync::Arc; use tokio::time::Instant; use tokio_util::sync::CancellationToken; use tower_lsp::lsp_types::*; use ustr::Ustr; -use percent_encoding::percent_decode_str; pub type Snapshot = Arc; #[derive(Hash, PartialEq, Eq, Clone, Copy)] @@ -192,9 +192,15 @@ impl RootGraph { { let mut file_paths = HashSet::new(); for file in files.values() { - if !file_paths.insert(file.path.as_slice()) { - //info!("{:?}", file.namespace()); - if let Some(ns) = file.namespace() { + if let Some(ns) = file.namespace() { + //create path with namespace if namespace exists and check if it is already defined + let mut path : Vec = file.path.clone().iter().map(|s| s.to_string()).collect(); + let ns_path : Vec = ns.names.iter().map(|s| s.to_string()).collect(); + let len = file.path.len().saturating_sub(ns.names.len()); + path.truncate(len); + path.extend_from_slice(&ns_path); + let path_str = path.join("."); + if !file_paths.insert(path_str) { if err.errors.contains_key(&file.id) { err.span(ns.range(), file.id, 100, "namespace already defined"); } From b2f87cadd7d9c80b1d7181027177618a68b4fe22 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Wed, 5 Jul 2023 14:00:56 +0200 Subject: [PATCH 14/27] remove unused code --- uvls/src/core/ast/def.rs | 16 ++++++--- uvls/src/core/pipeline.rs | 75 +++++++++++++++++++-------------------- uvls/src/core/semantic.rs | 1 + uvls/src/main.rs | 16 +-------- 4 files changed, 51 insertions(+), 57 deletions(-) diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index 79577b53..66a3f6d9 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -40,10 +40,18 @@ impl Path { } pub fn to_string(&self) -> String { self.names.iter().map(|i| i.as_str()).join(".") - } - pub fn to_file(&self,root_folder: String) -> String { - let absoultpath = self.names.iter().map(|i| i.as_str()).join("/"); - let path = root_folder+ "/"+ &absoultpath+".uvl"; + } + + //creates from root_path and the path an absolute path + pub fn to_file(&self,root_path: &str ) -> String { + let mut slash = "/"; + if std::env::consts::OS == "windows" { + slash = "\\" + } + let mut dir:Vec<&str> = root_path.split(slash).collect(); + let absoultpath = self.names.iter().map(|i| i.as_str()).join(slash); + let root_dir = root_path.replace(dir.pop().unwrap(), ""); + let path = root_dir+ slash+ &absoultpath+".uvl"; path } } diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 0b369d19..4931cffb 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -1,8 +1,8 @@ -use crate::ROOT_FILE; use crate::{core::*, ide::inlays::InlayHandler, load_blocking, smt}; use check::*; use dashmap::DashMap; use document::*; +use filetime::{set_file_mtime, FileTime}; use hashbrown::HashMap; use log::info; use ropey::Rope; @@ -13,7 +13,6 @@ use std::{ }, time::SystemTime, }; -use filetime::{set_file_mtime, FileTime}; use tokio::{ select, spawn, sync::{broadcast, mpsc, oneshot, watch}, @@ -503,48 +502,48 @@ impl AsyncPipeline { } //load import if not already loaded and if the file is a uvl.json then load the corresponding uvl file // this method is called after the complete red tree of the uri is created - fn load_imports(&self, uri: &Url) { - let root_binding = self.rx_root.borrow(); - let doc = root_binding.file_by_uri(&uri); - match doc { - // it is a uvl - Some(ast) => { - // get all imports - let imports = ast.imports(); - for import in imports { - let relative_path_string = import - .path - .to_file(ROOT_FILE.try_lock().unwrap().as_mut().to_string()); - let url_import = Url::from_file_path(&relative_path_string).unwrap(); - //check if import is already loaded, if not, load - if !root_binding.contains(&url_import) { - let pipeline = self.clone(); - tokio::task::spawn_blocking(move || { - load_blocking(url_import, &pipeline); - }); - } - } - } - // it is a uvl.json - None => { - let config = root_binding.config_by_uri(&uri); - match config { - Some(config_doc) => { - let url_file = config_doc.config.as_ref().unwrap().file.url(); - if !root_binding.contains(&url_file) { + pub fn load_imports(&self, uri: &Url) { + let root_binding = self.rx_root.borrow(); + let doc = root_binding.file_by_uri(&uri); + match doc { + // it is a uvl + Some(ast) => { + // get all imports + let imports = ast.imports(); + for import in imports { + let relative_path_string = import.path.to_file(uri.path()); + let url_import = Url::from_file_path(&relative_path_string).unwrap(); + //check if import is already loaded, if not, load + if !root_binding.contains(&url_import) { + info!("update"); let pipeline = self.clone(); - let open_url = uri.clone(); tokio::task::spawn_blocking(move || { - load_blocking(url_file, &pipeline); - //update modified so uvl.json can be loaded - let _ = set_file_mtime(open_url.path(), FileTime::now()); - load_blocking(open_url, &pipeline); + load_blocking(url_import, &pipeline); }); } } - None => {} + } + // it is a uvl.json + None => { + let config = root_binding.config_by_uri(&uri); + match config { + Some(config_doc) => { + let url_file = config_doc.config.as_ref().unwrap().file.url(); + if !root_binding.contains(&url_file) { + let pipeline = self.clone(); + let open_url = uri.clone(); + tokio::task::spawn_blocking(move || { + load_blocking(url_file, &pipeline); + //update modified so uvl.json can be loaded + let _ = set_file_mtime(open_url.path(), FileTime::now()); + load_blocking(open_url, &pipeline); + }); + } + } + None => {} + } } } } } -} + diff --git a/uvls/src/core/semantic.rs b/uvls/src/core/semantic.rs index 7d4ad234..a3575114 100644 --- a/uvls/src/core/semantic.rs +++ b/uvls/src/core/semantic.rs @@ -81,6 +81,7 @@ impl RootGraph { .or(self.file_by_uri(uri).map(|i| i.timestamp)) } pub fn contains_id(&self, id: FileID) -> bool { + info!("file id: {:?}", id); self.files.contains_key(&id) || self.configs.contains_key(&id) } pub fn type_of(&self, sym: RootSymbol) -> Option { diff --git a/uvls/src/main.rs b/uvls/src/main.rs index c9b5e733..61e94259 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -2,15 +2,11 @@ #![forbid(unsafe_code)] use flexi_logger::FileSpec; -use futures_util::lock::Mutex; use get_port::Ops; -use lazy_static::lazy_static; - use log::info; use serde::Serialize; use tokio::{join, spawn}; use std::io::{Read, Write}; -use std::path::{PathBuf}; use percent_encoding::percent_decode_str; use std::sync::Arc; use std::time::SystemTime; @@ -33,9 +29,6 @@ impl Default for Settings { } } -lazy_static! { - pub static ref ROOT_FILE: Mutex = Mutex::new("".to_string()); -} //The LSP struct Backend { @@ -86,6 +79,7 @@ fn load_blocking(uri: Url, pipeline: &AsyncPipeline) { let modified = meta.modified()?; if !pipeline.should_load(&uri, modified) { + info!("load problem"); return Ok(()); } let mut data = String::new(); @@ -109,14 +103,6 @@ fn shutdown_error() -> tower_lsp::jsonrpc::Error { impl LanguageServer for Backend { async fn initialize(&self, init_params: InitializeParams) -> Result { #[allow(deprecated)] - let root_folder = init_params - .root_path - .as_deref() - .or_else(|| init_params.root_uri.as_ref().map(|p| p.path())) - .map(PathBuf::from); - if let Some(root_folder) = root_folder { - *ROOT_FILE.lock().await = root_folder.to_str().unwrap().to_string(); - } if init_params .client_info .map(|info| matches!(info.name.as_str(), "Visual Studio Code")) From 10cd659e82926ff2f9b38bfcf8c92ccdac85612c Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 17 Jul 2023 14:56:13 +0200 Subject: [PATCH 15/27] fix client bug --- editors/vscode/src/extension.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/editors/vscode/src/extension.ts b/editors/vscode/src/extension.ts index aa664656..32ba36b9 100644 --- a/editors/vscode/src/extension.ts +++ b/editors/vscode/src/extension.ts @@ -32,6 +32,8 @@ import { info } from 'console'; let client: LanguageClient | null = null; let outputChannel: vscode.OutputChannel | null = null; const SOURCE_URI = "https://api.github.com/repos/Universal-Variability-Language/uvl-lsp/releases/latest"; +let rangeOrOptions: Map>> = new Map(); +const decorators: Array = new Array(4); function getDefaultInstallationName(): string | null { @@ -316,7 +318,6 @@ async function startClient(context: ExtensionContext) { }; // Decorator for dead features - const decorators: Array = new Array(4); decorators[0] = vscode.window.createTextEditorDecorationType({ gutterIconPath: context.asAbsolutePath("assets/deadfeature.svg"), gutterIconSize: "90%", @@ -342,7 +343,7 @@ async function startClient(context: ExtensionContext) { gutterIconSize: "90%", backgroundColor: { id: 'color.voidfeature' } }); - let rangeOrOptions: Map>> = new Map(); + rangeOrOptions = new Map(); //If we change the textEditor, the Decoration remains intact window.onDidChangeActiveTextEditor((editor) =>{ @@ -411,6 +412,13 @@ async function startClient(context: ExtensionContext) { client.start(); } async function stopClient(): Promise { + for (const editor of window.visibleTextEditors) { + let range = rangeOrOptions.get(editor.document.fileName); + if (range !== undefined ){ + decorators.forEach((decorator) => editor.setDecorations(decorator, [])); + } + } + rangeOrOptions = new Map(); if (client) client.stop(); client = null; } \ No newline at end of file From 4e2cd5f0beb63ce7eed40e9e08296aea2f8c8b6e Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 17 Jul 2023 15:02:43 +0200 Subject: [PATCH 16/27] add new import load technique --- uvls/src/core/ast.rs | 11 +++- uvls/src/core/pipeline.rs | 128 +++++++++++++++++++++++++------------- uvls/src/main.rs | 2 + 3 files changed, 95 insertions(+), 46 deletions(-) diff --git a/uvls/src/core/ast.rs b/uvls/src/core/ast.rs index 8917ce6d..1b682339 100644 --- a/uvls/src/core/ast.rs +++ b/uvls/src/core/ast.rs @@ -59,7 +59,7 @@ impl TreeMap { } //Ast container each symbol kind lives in its own vector #[derive(Clone, Debug, Default)] -struct Ast { +pub struct Ast { keywords: Vec, namespace: Option, includes: Vec, @@ -170,7 +170,7 @@ impl Ast { .flat_map(|v| v.iter().cloned()) } //utility iterators over different elements of interest - fn all_imports(&self) -> impl Iterator + DoubleEndedIterator { + pub fn all_imports(&self) -> impl Iterator + DoubleEndedIterator { (0..self.import.len()).map(Symbol::Import) } fn all_features(&self) -> impl Iterator { @@ -188,6 +188,9 @@ impl Ast { fn all_lang_lvls(&self) -> impl Iterator { (0..self.includes.len()).map(Symbol::LangLvl) } + pub fn imports(&self) -> &[Import] { + &self.import + } //Search a symbol by byte offset in O(N) fn find(&self, offset: usize) -> Option { self.all_imports() @@ -209,6 +212,7 @@ pub struct AstDocument { pub uri: Url, pub id: FileID, } + impl AstDocument { pub fn parent(&self, sym: Symbol, merge_root_features: bool) -> Option { if merge_root_features && matches!(sym, Symbol::Feature(..)) { @@ -299,6 +303,9 @@ impl AstDocument { pub fn get_reference(&self, index: usize) -> Option<&Reference> { self.ast.references.get(index) } + pub fn get_ast (&self) -> Ast { + self.ast.clone() + } pub fn lsp_range(&self, sym: Symbol) -> Option { self.ast.lsp_range(sym, &self.source) } diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 4931cffb..db7dbcb6 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -161,6 +161,8 @@ async fn link_handler( mut rx: mpsc::Receiver, tx_cache: watch::Sender>, tx_err: mpsc::Sender, + tx_ast: watch::Sender>, + tx_root_imports: watch::Sender>, ) { //First we gather changes to avoid redundant recomputation let mut latest_configs: HashMap> = HashMap::new(); @@ -168,9 +170,9 @@ async fn link_handler( let mut timestamps: HashMap = HashMap::new(); let (tx_execute, rx_execute) = watch::channel((latest_ast.clone(), latest_configs.clone(), 0)); let mut dirty = false; - let mut revision = 0;//Each change is one revision + let mut revision = 0; //Each change is one revision info!("started link handler"); - spawn(link_executor(rx_execute, tx_cache, tx_err)); + spawn(link_executor(rx_execute, tx_cache, tx_err, tx_root_imports)); let mut timer = tokio::time::interval(tokio::time::Duration::from_millis(100)); loop { select! { @@ -187,6 +189,8 @@ async fn link_handler( dirty=true; } LinkMsg::UpdateAst(ast)=>{ + let ast_clone = ast.get_ast().clone(); + let uri_str = ast.uri.clone().to_string(); if timestamps.get(&ast.uri).map(|old|old < &ast.timestamp).unwrap_or(true){ timestamps.insert(ast.uri.clone(),ast.timestamp); let id = FileID::new(ast.uri.as_str()); @@ -195,6 +199,7 @@ async fn link_handler( revision +=1; dirty=true; + let _ = tx_ast.send(Arc::new((ast_clone,uri_str))); } LinkMsg::UpdateConfig(conf)=>{ @@ -235,6 +240,7 @@ async fn link_handler( )>, tx_cache: watch::Sender>, tx_err: mpsc::Sender, + tx_root_imports: watch::Sender> ) { let mut timestamps: HashMap = HashMap::new(); info!("started link execute"); @@ -255,13 +261,47 @@ async fn link_handler( //link files incrementally let root = RootGraph::new(&ast, &configs, revision, &old, &mut err, &mut timestamps); - let _ = tx_cache.send(Arc::new(root)); + let _ = tx_cache.send(Arc::new(root.clone())); let _ = tx_err .send(DiagnosticUpdate { timestamp: revision, error_state: err.errors, }) .await; + let _= tx_root_imports.send(Arc::new(root)); + } + } +} + +//handler which takes care that all imported files are loaded recursively +async fn import_handler(pipeline: AsyncPipeline) { + let mut rx = pipeline.rx_ast.clone(); + let mut rx_root = pipeline.rx_root_imports.clone(); + loop { + //wait that Ast Document is updated + if rx.changed().await.is_err() { + break; + } + let arc = pipeline.rx_ast.borrow().clone(); + let ast = arc.0.clone(); + if let Ok(uri) = Url::parse(arc.1.as_str()) { + for import in ast.imports() { + let relative_path_string = import.path.to_file(uri.path()); + if let Ok(url_import) = Url::from_file_path(&relative_path_string) { + //check if import is already loaded, if not, load + let pip = pipeline.clone(); + //wait that rootGraph is update + if rx_root.changed().await.is_err() { + break; + } + //only load import if it isn't loaded yet + if !rx_root.borrow().contains(&url_import) { + tokio::task::spawn_blocking(move || { + load_blocking(url_import, &pip); + }); + } + } + } } } } @@ -274,25 +314,30 @@ pub struct AsyncPipeline { tx_link: mpsc::Sender, //error publisher tx_err: mpsc::Sender, - //latest version of the linked files + //latest version of the linked files rx_root: watch::Receiver>, + //latest version of the linkded files, but this receiver is used for loading imported files + rx_root_imports: watch::Receiver>, //fires when a file changed tx_dirty_tree: broadcast::Sender<()>, revision_counter: Arc, client: tower_lsp::Client, //code inlays are managed globally inlay_handler: InlayHandler, + //fires when astDocument get updated + rx_ast: watch::Receiver>, } impl AsyncPipeline { pub fn new(client: tower_lsp::Client) -> Self { - + let (tx_ast, rx_ast) = watch::channel(Arc::new((Ast::default(), "".to_string()))); let (tx_link, rx_link) = mpsc::channel(1024); let (tx_root, rx_root) = watch::channel(Arc::new(RootGraph::default())); + let (tx_root_imports, rx_root_imports) = watch::channel(Arc::new(RootGraph::default())); let (tx_err, rx_err) = mpsc::channel(1024); let revision_counter = Arc::new(AtomicU64::new(0)); let (tx_dirty, _) = broadcast::channel(1024); let inlay_handler = InlayHandler::new(client.clone()); - spawn(link_handler(rx_link, tx_root, tx_err.clone())); + spawn(link_handler(rx_link, tx_root, tx_err.clone(), tx_ast, tx_root_imports)); spawn(check::diagnostic_handler(rx_err, client.clone())); spawn(smt::check_handler( rx_root.clone(), @@ -309,6 +354,8 @@ impl AsyncPipeline { tx_link, tx_err, rx_root, + rx_root_imports, + rx_ast, } } pub fn touch(&self, uri: &Url) { @@ -431,6 +478,10 @@ impl AsyncPipeline { pub fn root(&self) -> watch::Receiver> { self.rx_root.clone() } + + pub fn root_imports(&self) -> watch::Receiver> { + self.rx_root_imports.clone() + } //wait until uri newer than timestamp in the root graph pub async fn snapshot_root_sync( &self, @@ -494,56 +545,45 @@ impl AsyncPipeline { } else { (draft, self.snapshot_root(uri).await?) })); - self.load_imports(uri); + self.load_uvl_after_json(uri); result } else { Ok(None) } } - //load import if not already loaded and if the file is a uvl.json then load the corresponding uvl file + // if the file is a uvl.json then load the corresponding uvl file // this method is called after the complete red tree of the uri is created - pub fn load_imports(&self, uri: &Url) { - let root_binding = self.rx_root.borrow(); - let doc = root_binding.file_by_uri(&uri); - match doc { - // it is a uvl - Some(ast) => { - // get all imports - let imports = ast.imports(); - for import in imports { - let relative_path_string = import.path.to_file(uri.path()); - let url_import = Url::from_file_path(&relative_path_string).unwrap(); - //check if import is already loaded, if not, load - if !root_binding.contains(&url_import) { - info!("update"); + pub fn load_uvl_after_json(&self, uri: &Url) { + let root_binding = self.rx_root.borrow(); + let doc = root_binding.file_by_uri(&uri); + match doc { + // it is a uvl, can be ignored + Some(_) => (), + // it is a uvl.json + None => { + let config = root_binding.config_by_uri(&uri); + match config { + Some(config_doc) => { + let url_file = config_doc.config.as_ref().unwrap().file.url(); + if !root_binding.contains(&url_file) { let pipeline = self.clone(); + let open_url = uri.clone(); tokio::task::spawn_blocking(move || { - load_blocking(url_import, &pipeline); + load_blocking(url_file, &pipeline); + //update modified so uvl.json can be loaded + let _ = set_file_mtime(open_url.path(), FileTime::now()); + load_blocking(open_url, &pipeline); }); } } - } - // it is a uvl.json - None => { - let config = root_binding.config_by_uri(&uri); - match config { - Some(config_doc) => { - let url_file = config_doc.config.as_ref().unwrap().file.url(); - if !root_binding.contains(&url_file) { - let pipeline = self.clone(); - let open_url = uri.clone(); - tokio::task::spawn_blocking(move || { - load_blocking(url_file, &pipeline); - //update modified so uvl.json can be loaded - let _ = set_file_mtime(open_url.path(), FileTime::now()); - load_blocking(open_url, &pipeline); - }); - } - } - None => {} - } + None => {} } } } } - + //start the import handler + pub fn import_handler(&self) { + let pipeline = self.clone(); + spawn(import_handler(pipeline)); + } +} diff --git a/uvls/src/main.rs b/uvls/src/main.rs index 61e94259..d8ac1b6b 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -110,6 +110,8 @@ impl LanguageServer for Backend { { self.settings.lock().has_webview = true; } + + self.pipeline.import_handler(); Ok(InitializeResult { server_info: Some(ServerInfo { From 36c7923418a6dc1131508e9483aca1fb3b893d88 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 17 Jul 2023 16:17:52 +0200 Subject: [PATCH 17/27] fix merg errors --- uvls/src/core/pipeline.rs | 2 +- uvls/src/main.rs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 9ea8b006..e2253ffc 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -1,4 +1,4 @@ -use crate::{core::*, ide::inlays::InlayHandler, smt}; +use crate::{core::*, ide::inlays::InlayHandler, smt, load_blocking}; use check::*; use dashmap::DashMap; use document::*; diff --git a/uvls/src/main.rs b/uvls/src/main.rs index ce3a4fa2..5e5d5f1a 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -8,7 +8,6 @@ use log::info; use percent_encoding::percent_decode_str; use serde::Serialize; use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::SystemTime; use tokio::{join, spawn}; @@ -75,7 +74,7 @@ impl Backend { } } //load a file, this is tricky because the editor can also load it at the same time -fn load_blocking(uri: Url, pipeline: &AsyncPipeline) { +pub fn load_blocking(uri: Url, pipeline: &AsyncPipeline) { if let Err(e) = std::fs::File::open(uri.to_file_path().unwrap()).and_then(|mut f| { let meta = f.metadata()?; let modified = meta.modified()?; @@ -467,9 +466,6 @@ impl LanguageServer for Backend { character: 0, }, }, - command: if self.pipeline.inlay_state().is_active( - ide::inlays::InlaySource::File(semantic::FileID::new(uri.as_str())), - ) { command: if self.pipeline.inlay_state().is_active( ide::inlays::InlaySource::File(semantic::FileID::new(uri.as_str())), ) { From 99a05eb0b10c1f4e53b118d899d182d2c4cdad25 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Thu, 20 Jul 2023 15:33:03 +0200 Subject: [PATCH 18/27] run formatter --- uvls/src/core/ast.rs | 2 +- uvls/src/core/ast/def.rs | 10 +++++----- uvls/src/core/ast/transform.rs | 2 +- uvls/src/core/cache.rs | 16 ++++++++++++---- uvls/src/core/pipeline.rs | 14 ++++++++++---- uvls/src/core/semantic.rs | 7 ++++--- uvls/src/ide/completion.rs | 10 +++++++--- uvls/src/main.rs | 8 +++----- 8 files changed, 43 insertions(+), 26 deletions(-) diff --git a/uvls/src/core/ast.rs b/uvls/src/core/ast.rs index ea9520e3..1e67edff 100644 --- a/uvls/src/core/ast.rs +++ b/uvls/src/core/ast.rs @@ -317,7 +317,7 @@ impl AstDocument { pub fn get_reference(&self, index: usize) -> Option<&Reference> { self.ast.references.get(index) } - pub fn get_ast (&self) -> Ast { + pub fn get_ast(&self) -> Ast { self.ast.clone() } pub fn lsp_range(&self, sym: Symbol) -> Option { diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index 00e73d93..c5a321de 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -40,18 +40,18 @@ impl Path { } pub fn to_string(&self) -> String { self.names.iter().map(|i| i.as_str()).join(".") - } + } //creates from root_path and the path an absolute path - pub fn to_file(&self,root_path: &str ) -> String { + pub fn to_file(&self, root_path: &str) -> String { let mut slash = "/"; - if std::env::consts::OS == "windows" { + if std::env::consts::OS == "windows" { slash = "\\" } - let mut dir:Vec<&str> = root_path.split(slash).collect(); + let mut dir: Vec<&str> = root_path.split(slash).collect(); let absoultpath = self.names.iter().map(|i| i.as_str()).join(slash); let root_dir = root_path.replace(dir.pop().unwrap(), ""); - let path = root_dir+ slash+ &absoultpath+".uvl"; + let path = root_dir + slash + &absoultpath + ".uvl"; path } } diff --git a/uvls/src/core/ast/transform.rs b/uvls/src/core/ast/transform.rs index 4d10b50e..07aed7c5 100644 --- a/uvls/src/core/ast/transform.rs +++ b/uvls/src/core/ast/transform.rs @@ -1313,7 +1313,7 @@ pub fn visit_root(source: Rope, tree: Tree, uri: Url, timestamp: Instant) -> Ast (state.ast, state.errors) }; let path = uri_to_path(&uri).unwrap(); - // without this Code namespaces are ignored for imports and the normal path is used + // without this Code namespaces are ignored for imports and the normal path is used // if let Some(ns) = ast.namespace.as_ref() { // let len = path.len().saturating_sub(ns.names.len()); // path.truncate(len); diff --git a/uvls/src/core/cache.rs b/uvls/src/core/cache.rs index 1eacbbfd..56b34c44 100644 --- a/uvls/src/core/cache.rs +++ b/uvls/src/core/cache.rs @@ -321,7 +321,8 @@ impl FileSystem { Some(name) => { // checks if already written text is prefix of the path let mut valid_path = true; - let mut check_path: Vec<&str> = name.clone().split("/").collect(); + let mut check_path: Vec<&str> = + name.clone().split("/").collect(); for i in prefix.iter() { let check_prefix = i.as_str(); @@ -347,8 +348,10 @@ impl FileSystem { } let name_up = check_path.join("/"); if valid_path { - let new_name = - name_up.replace("/", ".").replace("\\", ".").replace(".uvl", ""); + let new_name = name_up + .replace("/", ".") + .replace("\\", ".") + .replace(".uvl", ""); // safe file for autoCompletion stack.push(( new_name.as_str().into(), @@ -495,7 +498,12 @@ impl Cache { .keys() .find(|key| { key.as_str().strip_prefix("file:///").unwrap_or("") - == content.file.as_str().strip_prefix("file://").unwrap_or("").replace("\\", "/") + == content + .file + .as_str() + .strip_prefix("file://") + .unwrap_or("") + .replace("\\", "/") }) .unwrap_or(&FileID::new("X")) }; diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index e2253ffc..b6ca583c 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -1,4 +1,4 @@ -use crate::{core::*, ide::inlays::InlayHandler, smt, load_blocking}; +use crate::{core::*, ide::inlays::InlayHandler, load_blocking, smt}; use check::*; use dashmap::DashMap; use document::*; @@ -241,7 +241,7 @@ async fn link_handler( )>, tx_cache: watch::Sender>, tx_err: mpsc::Sender, - tx_root_imports: watch::Sender> + tx_root_imports: watch::Sender>, ) { let mut timestamps: HashMap = HashMap::new(); info!("started link execute"); @@ -269,7 +269,7 @@ async fn link_handler( error_state: err.errors, }) .await; - let _= tx_root_imports.send(Arc::new(root)); + let _ = tx_root_imports.send(Arc::new(root)); } } } @@ -338,7 +338,13 @@ impl AsyncPipeline { let revision_counter = Arc::new(AtomicU64::new(0)); let (tx_dirty, _) = broadcast::channel(1024); let inlay_handler = InlayHandler::new(client.clone()); - spawn(link_handler(rx_link, tx_root, tx_err.clone(), tx_ast, tx_root_imports)); + spawn(link_handler( + rx_link, + tx_root, + tx_err.clone(), + tx_ast, + tx_root_imports, + )); spawn(check::diagnostic_handler(rx_err, client.clone())); spawn(smt::check_handler( rx_root.clone(), diff --git a/uvls/src/core/semantic.rs b/uvls/src/core/semantic.rs index 98d7754f..94bf05f9 100644 --- a/uvls/src/core/semantic.rs +++ b/uvls/src/core/semantic.rs @@ -194,9 +194,10 @@ impl RootGraph { let mut file_paths = HashSet::new(); for file in files.values() { if let Some(ns) = file.namespace() { - //create path with namespace if namespace exists and check if it is already defined - let mut path : Vec = file.path.clone().iter().map(|s| s.to_string()).collect(); - let ns_path : Vec = ns.names.iter().map(|s| s.to_string()).collect(); + //create path with namespace if namespace exists and check if it is already defined + let mut path: Vec = + file.path.clone().iter().map(|s| s.to_string()).collect(); + let ns_path: Vec = ns.names.iter().map(|s| s.to_string()).collect(); let len = file.path.len().saturating_sub(ns.names.len()); path.truncate(len); path.extend_from_slice(&ns_path); diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index d6670e80..1a83893d 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -996,7 +996,7 @@ fn compute_completions_impl( is_incomplete = true } CompletionEnv::Import => { - // complete all files and dic which are already loaded + // complete all files and dic which are already loaded for (path, name, node) in snapshot.fs().sub_files(origin, &ctx.prefix) { let len = path.as_str().chars().filter(|c| c == &'.').count(); top.push(CompletionOpt::new( @@ -1011,8 +1011,12 @@ fn compute_completions_impl( &ctx, )) } - // complete all files and dic which are not loaded - for (path, name, node) in snapshot.fs().all_sub_files(origin, & ctx.prefix,ctx.postfix.clone()) { + // complete all files and dic which are not loaded + for (path, name, node) in + snapshot + .fs() + .all_sub_files(origin, &ctx.prefix, ctx.postfix.clone()) + { let len = path.as_str().chars().filter(|c| c == &'.').count(); top.push(CompletionOpt::new( match node { diff --git a/uvls/src/main.rs b/uvls/src/main.rs index 5e5d5f1a..7c1233d5 100644 --- a/uvls/src/main.rs +++ b/uvls/src/main.rs @@ -30,7 +30,6 @@ impl Default for Settings { } } - //The LSP struct Backend { client: Client, @@ -111,7 +110,7 @@ impl LanguageServer for Backend { { self.settings.lock().has_webview = true; } - + self.pipeline.import_handler(); Ok(InitializeResult { @@ -345,8 +344,8 @@ impl LanguageServer for Backend { for i in params.changes { match i.typ { FileChangeType::CREATED => { - self.load(i.uri); - break; + self.load(i.uri); + break; } FileChangeType::CHANGED => { self.load(i.uri); @@ -600,7 +599,6 @@ fn main() { async fn server_main() { std::env::set_var("RUST_BACKTRACE", "1"); - log_panics::Config::new() .backtrace_mode(log_panics::BacktraceMode::Unresolved) .install_panic_hook(); From 455316597f964b138215a5e482b339478e23e0d9 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 14 Aug 2023 13:07:53 +0200 Subject: [PATCH 19/27] fix import bug windows --- uvls/src/core/ast/def.rs | 20 +++++++++++++------- uvls/src/core/pipeline.rs | 7 ++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index c5a321de..0c743881 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -44,15 +44,21 @@ impl Path { //creates from root_path and the path an absolute path pub fn to_file(&self, root_path: &str) -> String { - let mut slash = "/"; - if std::env::consts::OS == "windows" { - slash = "\\" - } + let slash = "/"; let mut dir: Vec<&str> = root_path.split(slash).collect(); let absoultpath = self.names.iter().map(|i| i.as_str()).join(slash); - let root_dir = root_path.replace(dir.pop().unwrap(), ""); - let path = root_dir + slash + &absoultpath + ".uvl"; - path + if let Some(name) = dir.pop() { + if std::env::consts::OS == "windows" { + let root_dir = root_path.replace(name, ""); + let path = "file://".to_string() + &root_dir + &absoultpath + ".uvl"; + return path; + } else { + let root_dir = root_path.replace(name, ""); + let path = "file://".to_string() + &root_dir + slash + &absoultpath + ".uvl"; + return path; + } + } + absoultpath } } diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index b6ca583c..4bb502f7 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -288,13 +288,10 @@ async fn import_handler(pipeline: AsyncPipeline) { if let Ok(uri) = Url::parse(arc.1.as_str()) { for import in ast.imports() { let relative_path_string = import.path.to_file(uri.path()); - if let Ok(url_import) = Url::from_file_path(&relative_path_string) { + if let Ok(url_import) = Url::parse(relative_path_string.as_str()) { //check if import is already loaded, if not, load let pip = pipeline.clone(); - //wait that rootGraph is update - if rx_root.changed().await.is_err() { - break; - } + //only load import if it isn't loaded yet if !rx_root.borrow().contains(&url_import) { tokio::task::spawn_blocking(move || { From b58057197fa46b3510fe4a09135a8013de5560df Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 28 Aug 2023 11:57:03 +0200 Subject: [PATCH 20/27] fix linux autocompletion bug --- uvls/src/core/ast/def.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/uvls/src/core/ast/def.rs b/uvls/src/core/ast/def.rs index 0c743881..cb4ec3da 100644 --- a/uvls/src/core/ast/def.rs +++ b/uvls/src/core/ast/def.rs @@ -48,15 +48,9 @@ impl Path { let mut dir: Vec<&str> = root_path.split(slash).collect(); let absoultpath = self.names.iter().map(|i| i.as_str()).join(slash); if let Some(name) = dir.pop() { - if std::env::consts::OS == "windows" { - let root_dir = root_path.replace(name, ""); - let path = "file://".to_string() + &root_dir + &absoultpath + ".uvl"; - return path; - } else { - let root_dir = root_path.replace(name, ""); - let path = "file://".to_string() + &root_dir + slash + &absoultpath + ".uvl"; - return path; - } + let root_dir = root_path.replace(name, ""); + let path = "file://".to_string() + &root_dir + &absoultpath + ".uvl"; + return path; } absoultpath } From 874c3ce167de46978fec4886079b8c894a40f549 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 28 Aug 2023 12:12:59 +0200 Subject: [PATCH 21/27] remove mut --- uvls/src/core/pipeline.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 4bb502f7..07b6bdc1 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -277,7 +277,7 @@ async fn link_handler( //handler which takes care that all imported files are loaded recursively async fn import_handler(pipeline: AsyncPipeline) { let mut rx = pipeline.rx_ast.clone(); - let mut rx_root = pipeline.rx_root_imports.clone(); + let rx_root = pipeline.rx_root_imports.clone(); loop { //wait that Ast Document is updated if rx.changed().await.is_err() { From 6e2a868e0929191c1da0597ba88c8891c1879b78 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 28 Aug 2023 14:57:02 +0200 Subject: [PATCH 22/27] fix load json.uvl bug for windows --- uvls/src/core/pipeline.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index 07b6bdc1..d8c06e45 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -575,7 +575,10 @@ impl AsyncPipeline { tokio::task::spawn_blocking(move || { load_blocking(url_file, &pipeline); //update modified so uvl.json can be loaded - let _ = set_file_mtime(open_url.path(), FileTime::now()); + let e = set_file_mtime( + open_url.to_file_path().unwrap(), + FileTime::now(), + ); load_blocking(open_url, &pipeline); }); } From 56216ecb2f2c95e2b768fecdb7353692546fc0cc Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 18 Sep 2023 15:29:01 +0200 Subject: [PATCH 23/27] run cargo fmt --- uvls/src/core/module.rs | 4 +++- uvls/src/smt/parse.rs | 24 ++++++++++++++++------ uvls/src/smt/smt_lib.rs | 40 +++++++++++++++++++++--------------- uvls/src/webview/frontend.rs | 14 +++++++++---- 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/uvls/src/core/module.rs b/uvls/src/core/module.rs index c66d3b43..a59b20b5 100644 --- a/uvls/src/core/module.rs +++ b/uvls/src/core/module.rs @@ -439,7 +439,9 @@ impl ConfigModule { //Turns a the set of linear configuration values of this module into theire recusive from //used in json pub fn serialize(&self) -> Vec { - let ConfigEntry::Import(_,v) = self.serialize_rec(&[],InstanceID(0)) else {unreachable!()}; + let ConfigEntry::Import(_, v) = self.serialize_rec(&[], InstanceID(0)) else { + unreachable!() + }; v } } diff --git a/uvls/src/smt/parse.rs b/uvls/src/smt/parse.rs index ba64fd60..aa236962 100644 --- a/uvls/src/smt/parse.rs +++ b/uvls/src/smt/parse.rs @@ -176,17 +176,29 @@ mod tests { _ => Type::Real, }, }; - let (i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; + let (i, (_, ConfigValue::Number(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_approx_eq!(n, -1.0); - let (i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; + let (i, (_, ConfigValue::Number(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_approx_eq!(n, -1.0 / 103.0); - let (i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; + let (i, (_, ConfigValue::Number(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_approx_eq!(n, 100.0 - 1.333 / 102.0); - let (i,(_,ConfigValue::String(n))) = parser.parse(i).unwrap() else {panic!()}; + let (i, (_, ConfigValue::String(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_eq!(n, "test"); - let (i,(_,ConfigValue::Bool(n))) = parser.parse(i).unwrap() else {panic!()}; + let (i, (_, ConfigValue::Bool(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_eq!(n, true); - let (_i,(_,ConfigValue::Number(n))) = parser.parse(i).unwrap() else {panic!()}; + let (_i, (_, ConfigValue::Number(n))) = parser.parse(i).unwrap() else { + panic!() + }; assert_approx_eq!(n, 1.0); } } diff --git a/uvls/src/smt/smt_lib.rs b/uvls/src/smt/smt_lib.rs index 3d681d0c..dc8e00f2 100644 --- a/uvls/src/smt/smt_lib.rs +++ b/uvls/src/smt/smt_lib.rs @@ -570,29 +570,37 @@ pub fn uvl2smt(module: &Module, config: &HashMap) -> return true; } let ms = m.sym(a); - let Some((val, n)) = config.get(&ms).map(|v|( v.clone().into() ,AssertName::Config )).or_else(|| { - file.value(a) - .and_then(|v| match v { - ast::Value::Bool(x) => Some(Expr::Bool(*x)), - ast::Value::Number(x) => Some(Expr::Real(*x)), - ast::Value::String(x) => Some(Expr::String(x.clone())), - _ => None, - }) - .map(|v| (v, AssertName::Attribute)) - }) else {return true} ; + let Some((val, n)) = config + .get(&ms) + .map(|v| (v.clone().into(), AssertName::Config)) + .or_else(|| { + file.value(a) + .and_then(|v| match v { + ast::Value::Bool(x) => Some(Expr::Bool(*x)), + ast::Value::Number(x) => Some(Expr::Real(*x)), + ast::Value::String(x) => Some(Expr::String(x.clone())), + _ => None, + }) + .map(|v| (v, AssertName::Attribute)) + }) + else { + return true; + }; let zero = match val { Expr::Bool(..) => Expr::Bool(false), Expr::Real(..) => Expr::Real(0.0), Expr::String(..) => Expr::String("".into()), - _=>unreachable!() + _ => unreachable!(), }; let attrib_var = builder.push_var(ms); let feat_var = builder.pseudo_bool(m.sym(f)); - builder.assert.push(Assert(Some(AssertInfo(ms, n)),Expr::Equal(vec![ - Expr::Ite(feat_var.into(), - val.into(), - zero.into()), - attrib_var] ) )); + builder.assert.push(Assert( + Some(AssertInfo(ms, n)), + Expr::Equal(vec![ + Expr::Ite(feat_var.into(), val.into(), zero.into()), + attrib_var, + ]), + )); true }); } diff --git a/uvls/src/webview/frontend.rs b/uvls/src/webview/frontend.rs index b479d38d..9ee28db6 100644 --- a/uvls/src/webview/frontend.rs +++ b/uvls/src/webview/frontend.rs @@ -422,8 +422,15 @@ where .map(|(_, vn)| vn.depth <= v.depth) .unwrap_or(true); //Resolve link - if let UIEntryValue::Link{name,tgt,..} = &v.value{ - let UIEntryValue::Feature { config, smt_value, ty, unsat,.. } = &state.entries[tgt].value else{ + if let UIEntryValue::Link { name, tgt, .. } = &v.value { + let UIEntryValue::Feature { + config, + smt_value, + ty, + unsat, + .. + } = &state.entries[tgt].value + else { panic!() }; @@ -441,8 +448,7 @@ where } },leaf:leaf,sym:*tgt,key:"{k:?}",tag:tag} } - } - else{ + } else { rsx! { FileEntry{node:v.clone(),leaf:leaf,sym:*k,key:"{k:?}",tag:tag} } From 9b4da070a129484d7fed7243636ce80dfffa5b19 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Mon, 25 Sep 2023 15:27:26 +0200 Subject: [PATCH 24/27] add syncronisation for import handler --- uvls/src/core/ast/graph.rs | 2 +- uvls/src/core/pipeline.rs | 10 +++++++++- uvls/src/smt.rs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/uvls/src/core/ast/graph.rs b/uvls/src/core/ast/graph.rs index b48f4809..c6120a92 100644 --- a/uvls/src/core/ast/graph.rs +++ b/uvls/src/core/ast/graph.rs @@ -600,7 +600,7 @@ fn visit_ref(graph: &mut VisitorGraph, _: &mut GraphNode, _: Path) { } } } -fn visit_group(graph: &mut VisitorGraph, mut parent: &mut GraphNode, mode: GroupMode) { +fn visit_group(graph: &mut VisitorGraph, parent: &mut GraphNode, mode: GroupMode) { //let sym = Symbol::Group(graph.ast.groups.len()); //graph.push_child(parent, sym); parent.group_mode = Some(mode); diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index d8c06e45..ff1d9ff5 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -164,6 +164,7 @@ async fn link_handler( tx_err: mpsc::Sender, tx_ast: watch::Sender>, tx_root_imports: watch::Sender>, + mut rx_sync: mpsc::Receiver, ) { //First we gather changes to avoid redundant recomputation let mut latest_configs: HashMap> = HashMap::new(); @@ -200,6 +201,7 @@ async fn link_handler( revision +=1; dirty=true; + let _= rx_sync.recv().await; let _ = tx_ast.send(Arc::new((ast_clone,uri_str))); } @@ -279,6 +281,7 @@ async fn import_handler(pipeline: AsyncPipeline) { let mut rx = pipeline.rx_ast.clone(); let rx_root = pipeline.rx_root_imports.clone(); loop { + let _ = pipeline.tx_sync.send(true).await; //wait that Ast Document is updated if rx.changed().await.is_err() { break; @@ -324,10 +327,13 @@ pub struct AsyncPipeline { inlay_handler: InlayHandler, //fires when astDocument get updated rx_ast: watch::Receiver>, + //fires to inform rx_ast Receiver that it can send + tx_sync: mpsc::Sender, } impl AsyncPipeline { pub fn new(client: tower_lsp::Client) -> Self { let (tx_ast, rx_ast) = watch::channel(Arc::new((Ast::default(), "".to_string()))); + let (tx_sync, rx_sync) = mpsc::channel(1); let (tx_link, rx_link) = mpsc::channel(1024); let (tx_root, rx_root) = watch::channel(Arc::new(RootGraph::default())); let (tx_root_imports, rx_root_imports) = watch::channel(Arc::new(RootGraph::default())); @@ -341,6 +347,7 @@ impl AsyncPipeline { tx_err.clone(), tx_ast, tx_root_imports, + rx_sync, )); spawn(check::diagnostic_handler(rx_err, client.clone())); spawn(smt::check_handler( @@ -360,6 +367,7 @@ impl AsyncPipeline { rx_root, rx_root_imports, rx_ast, + tx_sync, } } pub fn touch(&self, uri: &Url) { @@ -575,7 +583,7 @@ impl AsyncPipeline { tokio::task::spawn_blocking(move || { load_blocking(url_file, &pipeline); //update modified so uvl.json can be loaded - let e = set_file_mtime( + let _ = set_file_mtime( open_url.to_file_path().unwrap(), FileTime::now(), ); diff --git a/uvls/src/smt.rs b/uvls/src/smt.rs index c8fcbd54..1e6a7bb2 100644 --- a/uvls/src/smt.rs +++ b/uvls/src/smt.rs @@ -342,6 +342,7 @@ async fn check_base_sat( }); let models = join_all(active.map(|(_, v)| { let module = v.clone(); + info!("module {:?}", module); async move { let smt_module = uvl2smt(&module, &HashMap::new()); let source = smt_module.to_source(&module); From bd1df22eb820d106d8072f36e36177dc5739fe95 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 26 Sep 2023 11:33:24 +0200 Subject: [PATCH 25/27] clean up --- uvls/src/core/pipeline.rs | 3 +++ uvls/src/smt.rs | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/uvls/src/core/pipeline.rs b/uvls/src/core/pipeline.rs index ff1d9ff5..5e1c3cb3 100644 --- a/uvls/src/core/pipeline.rs +++ b/uvls/src/core/pipeline.rs @@ -281,6 +281,9 @@ async fn import_handler(pipeline: AsyncPipeline) { let mut rx = pipeline.rx_ast.clone(); let rx_root = pipeline.rx_root_imports.clone(); loop { + // Send message to notify rx sender to send the last edited file URI. + // This synchronization is important to ensure that rx sender does not send + // two or more messages and the import handler gets only one processed let _ = pipeline.tx_sync.send(true).await; //wait that Ast Document is updated if rx.changed().await.is_err() { diff --git a/uvls/src/smt.rs b/uvls/src/smt.rs index 1e6a7bb2..c8fcbd54 100644 --- a/uvls/src/smt.rs +++ b/uvls/src/smt.rs @@ -342,7 +342,6 @@ async fn check_base_sat( }); let models = join_all(active.map(|(_, v)| { let module = v.clone(); - info!("module {:?}", module); async move { let smt_module = uvl2smt(&module, &HashMap::new()); let source = smt_module.to_source(&module); From 91511da92bb014e43288c40781b093dac4effffd Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 17 Oct 2023 14:42:55 +0200 Subject: [PATCH 26/27] fix merge fail --- uvls/src/ide/completion.rs | 39 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index 5f53a0e9..119df084 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -1032,28 +1032,27 @@ fn compute_completions_impl( &ctx, )) } + // complete all files and dic which are not loaded + for (path, name, node) in + snapshot + .fs() + .all_sub_files(origin, &ctx.prefix, ctx.postfix.clone()) + { + let len = path.as_str().chars().filter(|c| c == &'.').count(); + top.push(CompletionOpt::new( + match node { + FSNode::Dir => CompletionKind::Folder, + _ => CompletionKind::File, + }, + name, + path.clone(), + len, + TextOP::Put(path), + &ctx, + )) + } } } - // complete all files and dic which are not loaded - for (path, name, node) in - snapshot - .fs() - .all_sub_files(origin, &ctx.prefix, ctx.postfix.clone()) - { - let len = path.as_str().chars().filter(|c| c == &'.').count(); - top.push(CompletionOpt::new( - match node { - FSNode::Dir => CompletionKind::Folder, - _ => CompletionKind::File, - }, - name, - path.clone(), - len, - TextOP::Put(path), - &ctx, - )) - } - is_incomplete = true } CompletionEnv::Include => { From e39911e596f30103c123b5412513e2ac3e3b0154 Mon Sep 17 00:00:00 2001 From: pascalfoerster Date: Tue, 17 Oct 2023 16:04:22 +0200 Subject: [PATCH 27/27] fix bug after merge --- uvls/src/ide/completion.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uvls/src/ide/completion.rs b/uvls/src/ide/completion.rs index 119df084..2d99dbbe 100644 --- a/uvls/src/ide/completion.rs +++ b/uvls/src/ide/completion.rs @@ -338,7 +338,9 @@ fn estimate_env(node: Node, source: &Rope, pos: &Position) -> Option Some(CompletionEnv::Import), - "ref" if node.kind() == "path" => Some(CompletionEnv::Import), + "ref" if node.kind() == "path" || node.kind() == "name" => { + Some(CompletionEnv::Import) + } _ => None, } }