From b50700a5194672571be88f9b2289a2a784a3a35e Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sat, 11 Oct 2025 17:32:05 +0200 Subject: [PATCH 01/23] :construction: work on lsp setup # Conflicts: # Cargo.lock # Cargo.toml --- Cargo.toml | 4 +++- src/lsp/mod.rs | 59 +++++++++++++++++++++++++++++++++++--------------- src/main.rs | 13 +++++++---- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6897a8f..75db97a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,9 @@ clap = { version = "4.0.23", features = ["derive"] } datex-core = { version = "0.0.6", features = ["default", "debug"] } tokio = { version = "1.17.0", features = ["full"] } -tower-lsp = { version = "0.20.0", features = ["proposed"] } +#tower-lsp = { version = "0.20.0", features = ["proposed"]} +# using fork realhydroper-lsp because tower-lsp does not support local threading (single-threaded tokio runtime), which is required by the DATEX runtime +realhydroper-lsp = { version = "0.22.0", features = ["proposed"]} serde = { version = "1.0", features = ["derive"] } rustyline = "15.0.0" ratatui = "0.29.0" diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 66edf29..90df732 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,19 +1,25 @@ -use tower_lsp::jsonrpc::Result; -use tower_lsp::lsp_types::*; -use tower_lsp::{Client, LanguageServer, LspService, Server}; +use std::cell::RefCell; +use std::sync::Mutex; +use datex_core::compiler::workspace::CompilerWorkspace; +use realhydroper_lsp::jsonrpc::Result; +use realhydroper_lsp::lsp_types::*; +use realhydroper_lsp::{Client, LanguageServer, LspService, Server}; -#[derive(Debug)] -pub struct Backend { +pub struct LanguageServerBackend { pub client: Client, + pub compiler_workspace: RefCell, } -#[tower_lsp::async_trait] -impl LanguageServer for Backend { +#[realhydroper_lsp::async_trait(?Send)] +impl LanguageServer for LanguageServerBackend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), completion_provider: Some(CompletionOptions::default()), + text_document_sync: Some(TextDocumentSyncCapability::Kind( + TextDocumentSyncKind::FULL, + )), ..Default::default() }, ..Default::default() @@ -30,9 +36,35 @@ impl LanguageServer for Backend { Ok(()) } + async fn did_open(&self, params: DidOpenTextDocumentParams) { + self.client + .log_message( + MessageType::INFO, + format!("File opened: {}", params.text_document.uri), + ) + .await; + let mut compiler_workspace = self.compiler_workspace.borrow_mut(); + let file = compiler_workspace.load_file( + params.text_document.uri.to_file_path().unwrap(), + params.text_document.text + ).unwrap(); + self.client + .log_message( + MessageType::INFO, + format!("File compiled, DXB size: {}", file.compiled_dxb.as_ref().map_or(0, |dxb| dxb.len())), + ) + .await; + self.client + .log_message( + MessageType::INFO, + format!("AST: {:#?}", file.ast_with_metadata.ast), + ) + .await; + } + async fn hover(&self, params: HoverParams) -> Result> { self.client - .log_message(MessageType::INFO, "server initialized!") + .log_message(MessageType::INFO, "hover!") .await; Ok(Some(Hover { @@ -43,13 +75,4 @@ impl LanguageServer for Backend { range: None, })) } -} - -#[tokio::main] -async fn main() { - let stdin = tokio::io::stdin(); - let stdout = tokio::io::stdout(); - - let (service, socket) = LspService::new(|client| Backend { client }); - Server::new(stdin, stdout, socket).serve(service).await; -} +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ce5062b..3305b2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use datex_core::crypto::crypto_native::CryptoNative; use datex_core::run_async; use datex_core::runtime::global_context::{DebugFlags, GlobalContext, set_global_context}; @@ -5,6 +6,8 @@ use datex_core::runtime::{Runtime, RuntimeConfig}; use datex_core::utils::time_native::TimeNative; use std::path::PathBuf; use std::sync::Arc; +use datex_core::compiler::workspace::CompilerWorkspace; +use datex_core::values::core_values::endpoint::Endpoint; mod command_line_args; mod lsp; @@ -13,11 +16,11 @@ mod utils; mod workbench; use crate::command_line_args::Repl; -use crate::lsp::Backend; +use crate::lsp::LanguageServerBackend; use crate::repl::{ReplOptions, repl}; use crate::utils::config::{ConfigError, create_runtime_with_config}; use command_line_args::{Subcommands, get_command}; -use tower_lsp::{LspService, Server}; +use realhydroper_lsp::{LspService, Server}; #[tokio::main] async fn main() { @@ -26,11 +29,13 @@ async fn main() { if let Some(cmd) = command { match cmd { Subcommands::Lsp(lsp) => { - // println!("Running LSP"); let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let (service, socket) = LspService::new(|client| Backend { client }); + let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default())); + let compiler_workspace = RefCell::new(CompilerWorkspace::new(runtime)); + + let (service, socket) = LspService::new(|client| LanguageServerBackend { client, compiler_workspace }); Server::new(stdin, stdout, socket).serve(service).await; } Subcommands::Run(run) => { From 67b6576a2e555a1fe6426b7131b27311704330bf Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sat, 11 Oct 2025 17:40:54 +0200 Subject: [PATCH 02/23] update lsp --- src/lsp/mod.rs | 63 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 90df732..30362d0 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -47,21 +47,68 @@ impl LanguageServer for LanguageServerBackend { let file = compiler_workspace.load_file( params.text_document.uri.to_file_path().unwrap(), params.text_document.text - ).unwrap(); - self.client - .log_message( - MessageType::INFO, - format!("File compiled, DXB size: {}", file.compiled_dxb.as_ref().map_or(0, |dxb| dxb.len())), - ) - .await; + ); + if let Ok(file) = file { + self.client + .log_message( + MessageType::INFO, + format!("AST: {:#?}", file.ast_with_metadata.ast), + ) + .await; + self.client + .log_message( + MessageType::INFO, + format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) + ) + .await; + } else { + self.client + .log_message( + MessageType::ERROR, + format!("Failed to compile file: {}", params.text_document.uri), + ) + .await; + } + + } + + async fn did_change(&self, params: DidChangeTextDocumentParams) { self.client .log_message( MessageType::INFO, - format!("AST: {:#?}", file.ast_with_metadata.ast), + format!("File changed: {}", params.text_document.uri), ) .await; + let mut compiler_workspace = self.compiler_workspace.borrow_mut(); + let new_content = params.content_changes.into_iter().next().map(|change| change.text).unwrap_or_default(); + let file = compiler_workspace.load_file( + params.text_document.uri.to_file_path().unwrap(), + new_content + ); + if let Ok(file) = file { + self.client + .log_message( + MessageType::INFO, + format!("AST: {:#?}", file.ast_with_metadata.ast), + ) + .await; + self.client + .log_message( + MessageType::INFO, + format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) + ) + .await; + } else { + self.client + .log_message( + MessageType::ERROR, + format!("Failed to compile file: {}", params.text_document.uri), + ) + .await; + } } + async fn hover(&self, params: HoverParams) -> Result> { self.client .log_message(MessageType::INFO, "hover!") From 873165586a374db2f8fad38346a1f29034cde87b Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Mon, 13 Oct 2025 19:51:11 +0200 Subject: [PATCH 03/23] work on lsp prototype --- src/lsp/mod.rs | 249 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 188 insertions(+), 61 deletions(-) diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 30362d0..184854b 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,5 +1,7 @@ use std::cell::RefCell; -use std::sync::Mutex; +use std::path::PathBuf; +use datex_core::ast::{DatexExpression, DatexExpressionData}; +use datex_core::compiler::precompiler::VariableMetadata; use datex_core::compiler::workspace::CompilerWorkspace; use realhydroper_lsp::jsonrpc::Result; use realhydroper_lsp::lsp_types::*; @@ -10,6 +12,131 @@ pub struct LanguageServerBackend { pub compiler_workspace: RefCell, } +impl LanguageServerBackend { + + async fn update_file_contents( + &self, + path: PathBuf, + content: String, + ) { + let mut compiler_workspace = self.compiler_workspace.borrow_mut(); + let file = compiler_workspace.load_file(path.clone(), content); + if let Ok(file) = file { + self.client + .log_message( + MessageType::INFO, + format!("AST: {:#?}", file.ast_with_metadata.ast), + ) + .await; + self.client + .log_message( + MessageType::INFO, + format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) + ) + .await; + } else { + self.client + .log_message( + MessageType::ERROR, + format!( + "Failed to compile file {}: {}", + path.to_str().unwrap(), + file.err().unwrap() + ), + ) + .await; + } + } + + /// Finds all variables in the workspace whose names start with the given prefix. + fn find_variable_starting_with(&self, prefix: &str) -> Vec { + let compiler_workspace = self.compiler_workspace.borrow(); + let mut results = Vec::new(); + for file in compiler_workspace.files().values() { + let metadata = file.ast_with_metadata.metadata.borrow(); + for var in metadata.variables.iter() { + if var.name.starts_with(prefix) { + results.push(var.clone()); + } + } + } + results + } + + /// Retrieves variable metadata by its unique ID. + fn get_variable_by_id(&self, id: usize) -> Option { + let compiler_workspace = self.compiler_workspace.borrow(); + for file in compiler_workspace.files().values() { + let metadata = file.ast_with_metadata.metadata.borrow(); + if let Some(v) = metadata.variables.get(id).cloned() { + return Some(v); + } + } + None + } + + /// Converts a LSP position (line and character) to a byte offset in the file content. + fn position_to_byte_offset(&self, position: &TextDocumentPositionParams) -> usize { + let workspace = self.compiler_workspace.borrow(); + // first get file contents at position.text_document.uri + // then calculate byte offset from position.position.line and position.position.character + let file_path = position.text_document.uri.to_file_path().unwrap(); + let file_content = &workspace.get_file(&file_path).unwrap().content; + + Self::line_char_to_byte_index(file_content, position.position.line as usize, position.position.character as usize).unwrap_or(0) + } + + fn get_previous_text_at_position(&self, position: &TextDocumentPositionParams) -> String { + let byte_offset = self.position_to_byte_offset(position); + let workspace = self.compiler_workspace.borrow(); + let file_path = position.text_document.uri.to_file_path().unwrap(); + let file_content = &workspace.get_file(&file_path).unwrap().content; + // Get the text before the byte offset, only matching word characters + let previous_text = &file_content[..byte_offset]; + let last_word = previous_text + .rsplit(|c: char| !c.is_alphanumeric() && c != '_') + .next() + .unwrap_or(""); + last_word.to_string() + } + + /// Retrieves the DatexExpression AST node at the given byte offset. + fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> DatexExpression { + let byte_offset = self.position_to_byte_offset(position); + let workspace = self.compiler_workspace.borrow(); + let file_path = position.text_document.uri.to_file_path().unwrap(); + let ast = &workspace.get_file(&file_path).unwrap().ast_with_metadata.ast; + ast.as_ref().cloned().unwrap() + } + + + /// Converts a (line, character) pair to a byte index in the given text. + /// Lines and characters are zero-indexed. + /// Returns None if the line or character is out of bounds. + fn line_char_to_byte_index(text: &str, line: usize, character: usize) -> Option { + let mut lines = text.split('\n'); + + // Get the line + let line_text = lines.nth(line)?; + + // Compute byte index of the start of that line + let byte_offset_to_line_start = text + .lines() + .take(line) + .map(|l| l.len() + 1) // +1 for '\n' + .sum::(); + + // Now find the byte index within that line for the given character offset + let byte_offset_within_line = line_text + .char_indices() + .nth(character) + .map(|(i, _)| i) + .unwrap_or_else(|| line_text.len()); + + Some(byte_offset_to_line_start + byte_offset_within_line) + } +} + #[realhydroper_lsp::async_trait(?Send)] impl LanguageServer for LanguageServerBackend { async fn initialize(&self, _: InitializeParams) -> Result { @@ -43,33 +170,11 @@ impl LanguageServer for LanguageServerBackend { format!("File opened: {}", params.text_document.uri), ) .await; - let mut compiler_workspace = self.compiler_workspace.borrow_mut(); - let file = compiler_workspace.load_file( - params.text_document.uri.to_file_path().unwrap(), - params.text_document.text - ); - if let Ok(file) = file { - self.client - .log_message( - MessageType::INFO, - format!("AST: {:#?}", file.ast_with_metadata.ast), - ) - .await; - self.client - .log_message( - MessageType::INFO, - format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) - ) - .await; - } else { - self.client - .log_message( - MessageType::ERROR, - format!("Failed to compile file: {}", params.text_document.uri), - ) - .await; - } + self.update_file_contents( + params.text_document.uri.to_file_path().unwrap(), + params.text_document.text, + ).await; } async fn did_change(&self, params: DidChangeTextDocumentParams) { @@ -79,47 +184,69 @@ impl LanguageServer for LanguageServerBackend { format!("File changed: {}", params.text_document.uri), ) .await; - let mut compiler_workspace = self.compiler_workspace.borrow_mut(); let new_content = params.content_changes.into_iter().next().map(|change| change.text).unwrap_or_default(); - let file = compiler_workspace.load_file( + self.update_file_contents( params.text_document.uri.to_file_path().unwrap(), - new_content - ); - if let Ok(file) = file { - self.client - .log_message( - MessageType::INFO, - format!("AST: {:#?}", file.ast_with_metadata.ast), - ) - .await; - self.client - .log_message( - MessageType::INFO, - format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) - ) - .await; - } else { - self.client - .log_message( - MessageType::ERROR, - format!("Failed to compile file: {}", params.text_document.uri), - ) - .await; - } + new_content, + ).await; } + async fn completion(&self, params: CompletionParams) -> Result> { + self.client + .log_message(MessageType::INFO, "completion!") + .await; - async fn hover(&self, params: HoverParams) -> Result> { + let position = params.text_document_position; + + // For simplicity, we assume the prefix is the last word before the cursor. + // In a real implementation, you would extract this from the document content. + let prefix = self.get_previous_text_at_position(&position); self.client - .log_message(MessageType::INFO, "hover!") + .log_message(MessageType::INFO, format!("Completion prefix: {}", prefix)) .await; - Ok(Some(Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: "# Example\n123".to_string(), - }), - range: None, - })) + let variables = self.find_variable_starting_with(&prefix); + + let items: Vec = variables.iter().map(|var| { + CompletionItem { + label: var.name.clone(), + kind: Some(CompletionItemKind::VARIABLE), + detail: Some(format!( + "{} {}: {}", + var.shape, + var.name, + var.var_type.as_ref().unwrap()) + ), + documentation: None, + ..Default::default() + } + }).collect(); + + Ok(Some(CompletionResponse::Array(items))) + } + + + async fn hover(&self, params: HoverParams) -> Result> { + let expression = self.get_expression_at_position(¶ms.text_document_position_params); + + match expression { + // show variable type info on hover + DatexExpression { data: DatexExpressionData::VariableDeclaration { name, id: Some(id), .. }, .. } | + DatexExpression { data: DatexExpressionData::VariableAssignment (_, Some(id), name, _), .. } | + DatexExpression { data: DatexExpressionData::Variable (id, name), .. } => { + let variable_metadata = self.get_variable_by_id(id).unwrap(); + let contents = HoverContents::Scalar(MarkedString::LanguageString(LanguageString { + language: "datex".to_string(), + value: format!( + "{} {}: {}", + variable_metadata.shape, + name, + variable_metadata.var_type.as_ref().unwrap() + ) + })); + Ok(Some(Hover { contents, range: None })) + } + _ => Ok(None) + } } } \ No newline at end of file From 4ab663e6dfb4c8afab8d127e99317d803a800299 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Tue, 14 Oct 2025 15:08:02 +0200 Subject: [PATCH 04/23] :recycle: refactoring --- Cargo.lock | 70 +++++++++++++++++++++++++------------------------- src/lsp/mod.rs | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98b9d5a..30256b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -917,10 +917,10 @@ dependencies = [ "datex-core", "home", "ratatui", + "realhydroper-lsp", "rustyline", "serde", "tokio", - "tower-lsp", ] [[package]] @@ -2635,6 +2635,40 @@ dependencies = [ "yasna", ] +[[package]] +name = "realhydroper-lsp" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cf1e9d90671d3f1565fcbcd7c23ff268d325054bb52c101c6a02ab61b9d117" +dependencies = [ + "async-trait", + "auto_impl", + "bytes", + "dashmap", + "futures", + "httparse", + "lsp-types", + "memchr", + "realhydroper-lsp-macros", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "realhydroper-lsp-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc58ab9d2704193ee51a20bb227fc85934ed0be557516d9322b40fa8d7c00ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3656,40 +3690,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" -[[package]] -name = "tower-lsp" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ba052b54a6627628d9b3c34c176e7eda8359b7da9acd497b9f20998d118508" -dependencies = [ - "async-trait", - "auto_impl", - "bytes", - "dashmap", - "futures", - "httparse", - "lsp-types", - "memchr", - "serde", - "serde_json", - "tokio", - "tokio-util", - "tower 0.4.13", - "tower-lsp-macros", - "tracing", -] - -[[package]] -name = "tower-lsp-macros" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "tower-service" version = "0.3.3" diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 184854b..c8f04c9 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,6 +1,6 @@ use std::cell::RefCell; use std::path::PathBuf; -use datex_core::ast::{DatexExpression, DatexExpressionData}; +use datex_core::ast::tree::{DatexExpression, DatexExpressionData}; use datex_core::compiler::precompiler::VariableMetadata; use datex_core::compiler::workspace::CompilerWorkspace; use realhydroper_lsp::jsonrpc::Result; From 5b9d3a58d6e387218b6a82166ee6b4008231959e Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Tue, 14 Oct 2025 16:25:43 +0200 Subject: [PATCH 05/23] :recycle: update version output --- Cargo.lock | 1 + Cargo.toml | 6 ++++++ build.rs | 24 ++++++++++++++++++++++++ src/command_line_args.rs | 10 ++++++---- src/lsp/mod.rs | 2 +- src/main.rs | 10 ++++++++++ 6 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 build.rs diff --git a/Cargo.lock b/Cargo.lock index 30256b3..876eb65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,6 +920,7 @@ dependencies = [ "realhydroper-lsp", "rustyline", "serde", + "serde_json", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 75db97a..a1ec8ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = [ license = "MIT" version = "0.0.5" edition = "2024" +build = "build.rs" [dependencies] clap = { version = "4.0.23", features = ["derive"] } @@ -22,3 +23,8 @@ rustyline = "15.0.0" ratatui = "0.29.0" crossterm = "0.28.1" home = "0.5.11" +serde_json = "1.0.145" + + +[build-dependencies] +serde_json = "1.0.145" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..fd3ffc8 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ +use std::process::Command; + +fn main() { + // Run `cargo metadata` to get dependency information + let output = Command::new("cargo") + .args(["metadata", "--format-version", "1"]) + .output() + .expect("failed to run cargo metadata"); + + let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + // Find the `serde` package version + let serde_version = metadata["packages"] + .as_array() + .unwrap() + .iter() + .find(|pkg| pkg["name"] == "datex-core") + .and_then(|pkg| pkg["version"].as_str()) + .unwrap() + .to_string(); + + // Set environment variable for compile-time use + println!("cargo:rustc-env=DEP_DATEX_CORE_VERSION={}", serde_version); +} \ No newline at end of file diff --git a/src/command_line_args.rs b/src/command_line_args.rs index 0cc1aec..73039d5 100644 --- a/src/command_line_args.rs +++ b/src/command_line_args.rs @@ -3,10 +3,12 @@ use clap::{Args, Parser, Subcommand}; #[derive(Parser)] #[command(author, version, about, long_about = None, bin_name = "datex")] -#[command(propagate_version = true)] +#[command(disable_version_flag = true)] pub struct Cli { #[command(subcommand)] - command: Option, + pub command: Option, + #[arg(short = 'V', long, help = "Print version")] + pub version: bool, } #[derive(Subcommand)] @@ -38,6 +40,6 @@ pub struct Repl { #[derive(Args)] pub struct Workbench {} -pub fn get_command() -> Option { - Cli::parse().command +pub fn get_command() -> Cli { + Cli::parse() } diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index c8f04c9..f0af2cb 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -75,7 +75,7 @@ impl LanguageServerBackend { None } - /// Converts a LSP position (line and character) to a byte offset in the file content. + /// Converts an LSP position (line and character) to a byte offset in the file content. fn position_to_byte_offset(&self, position: &TextDocumentPositionParams) -> usize { let workspace = self.compiler_workspace.borrow(); // first get file contents at position.text_document.uri diff --git a/src/main.rs b/src/main.rs index 3305b2f..a253372 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,16 @@ use realhydroper_lsp::{LspService, Server}; async fn main() { let command = get_command(); + // print version + let command = if command.version { + println!("datex-cli {}", env!("CARGO_PKG_VERSION")); + println!("datex {}", env!("DEP_DATEX_CORE_VERSION")); + return; + } + else { + command.command + }; + if let Some(cmd) = command { match cmd { Subcommands::Lsp(lsp) => { From 0f96baab0bd123819411c1715eb5a6a8130b819c Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Wed, 15 Oct 2025 00:30:58 +0200 Subject: [PATCH 06/23] :construction: work on lsp --- src/lsp/mod.rs | 268 +++++++++++++++++------------------- src/lsp/utils.rs | 343 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +- 3 files changed, 470 insertions(+), 145 deletions(-) create mode 100644 src/lsp/utils.rs diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index f0af2cb..d1609e0 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,145 +1,39 @@ +mod utils; + use std::cell::RefCell; +use std::collections::HashMap; use std::path::PathBuf; -use datex_core::ast::tree::{DatexExpression, DatexExpressionData}; -use datex_core::compiler::precompiler::VariableMetadata; +use datex_core::ast::tree::{DatexExpression, DatexExpressionData, SimpleSpan, VariableAccess, VariableAssignment, VariableDeclaration}; +use datex_core::compiler::error::CompilerError; use datex_core::compiler::workspace::CompilerWorkspace; -use realhydroper_lsp::jsonrpc::Result; use realhydroper_lsp::lsp_types::*; -use realhydroper_lsp::{Client, LanguageServer, LspService, Server}; +use realhydroper_lsp::{Client, LanguageServer}; + +pub struct SpannedCompilerError { + pub range: Range, + pub error: CompilerError, +} pub struct LanguageServerBackend { pub client: Client, pub compiler_workspace: RefCell, + pub spanned_compiler_errors: RefCell>>, } impl LanguageServerBackend { - - async fn update_file_contents( - &self, - path: PathBuf, - content: String, - ) { - let mut compiler_workspace = self.compiler_workspace.borrow_mut(); - let file = compiler_workspace.load_file(path.clone(), content); - if let Ok(file) = file { - self.client - .log_message( - MessageType::INFO, - format!("AST: {:#?}", file.ast_with_metadata.ast), - ) - .await; - self.client - .log_message( - MessageType::INFO, - format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) - ) - .await; - } else { - self.client - .log_message( - MessageType::ERROR, - format!( - "Failed to compile file {}: {}", - path.to_str().unwrap(), - file.err().unwrap() - ), - ) - .await; + pub fn new(client: Client, compiler_workspace: CompilerWorkspace) -> Self { + Self { + client, + compiler_workspace: RefCell::new(compiler_workspace), + spanned_compiler_errors: RefCell::new(HashMap::new()), } } - - /// Finds all variables in the workspace whose names start with the given prefix. - fn find_variable_starting_with(&self, prefix: &str) -> Vec { - let compiler_workspace = self.compiler_workspace.borrow(); - let mut results = Vec::new(); - for file in compiler_workspace.files().values() { - let metadata = file.ast_with_metadata.metadata.borrow(); - for var in metadata.variables.iter() { - if var.name.starts_with(prefix) { - results.push(var.clone()); - } - } - } - results - } - - /// Retrieves variable metadata by its unique ID. - fn get_variable_by_id(&self, id: usize) -> Option { - let compiler_workspace = self.compiler_workspace.borrow(); - for file in compiler_workspace.files().values() { - let metadata = file.ast_with_metadata.metadata.borrow(); - if let Some(v) = metadata.variables.get(id).cloned() { - return Some(v); - } - } - None - } - - /// Converts an LSP position (line and character) to a byte offset in the file content. - fn position_to_byte_offset(&self, position: &TextDocumentPositionParams) -> usize { - let workspace = self.compiler_workspace.borrow(); - // first get file contents at position.text_document.uri - // then calculate byte offset from position.position.line and position.position.character - let file_path = position.text_document.uri.to_file_path().unwrap(); - let file_content = &workspace.get_file(&file_path).unwrap().content; - - Self::line_char_to_byte_index(file_content, position.position.line as usize, position.position.character as usize).unwrap_or(0) - } - - fn get_previous_text_at_position(&self, position: &TextDocumentPositionParams) -> String { - let byte_offset = self.position_to_byte_offset(position); - let workspace = self.compiler_workspace.borrow(); - let file_path = position.text_document.uri.to_file_path().unwrap(); - let file_content = &workspace.get_file(&file_path).unwrap().content; - // Get the text before the byte offset, only matching word characters - let previous_text = &file_content[..byte_offset]; - let last_word = previous_text - .rsplit(|c: char| !c.is_alphanumeric() && c != '_') - .next() - .unwrap_or(""); - last_word.to_string() - } - - /// Retrieves the DatexExpression AST node at the given byte offset. - fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> DatexExpression { - let byte_offset = self.position_to_byte_offset(position); - let workspace = self.compiler_workspace.borrow(); - let file_path = position.text_document.uri.to_file_path().unwrap(); - let ast = &workspace.get_file(&file_path).unwrap().ast_with_metadata.ast; - ast.as_ref().cloned().unwrap() - } - - - /// Converts a (line, character) pair to a byte index in the given text. - /// Lines and characters are zero-indexed. - /// Returns None if the line or character is out of bounds. - fn line_char_to_byte_index(text: &str, line: usize, character: usize) -> Option { - let mut lines = text.split('\n'); - - // Get the line - let line_text = lines.nth(line)?; - - // Compute byte index of the start of that line - let byte_offset_to_line_start = text - .lines() - .take(line) - .map(|l| l.len() + 1) // +1 for '\n' - .sum::(); - - // Now find the byte index within that line for the given character offset - let byte_offset_within_line = line_text - .char_indices() - .nth(character) - .map(|(i, _)| i) - .unwrap_or_else(|| line_text.len()); - - Some(byte_offset_to_line_start + byte_offset_within_line) - } } + #[realhydroper_lsp::async_trait(?Send)] impl LanguageServer for LanguageServerBackend { - async fn initialize(&self, _: InitializeParams) -> Result { + async fn initialize(&self, _: InitializeParams) -> realhydroper_lsp::jsonrpc::Result { Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), @@ -147,6 +41,14 @@ impl LanguageServer for LanguageServerBackend { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::FULL, )), + diagnostic_provider: Some(DiagnosticServerCapabilities::Options( + DiagnosticOptions { + inter_file_dependencies: true, + workspace_diagnostics: false, + identifier: None, + work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None } + } + )), ..Default::default() }, ..Default::default() @@ -159,7 +61,7 @@ impl LanguageServer for LanguageServerBackend { .await; } - async fn shutdown(&self) -> Result<()> { + async fn shutdown(&self) -> realhydroper_lsp::jsonrpc::Result<()> { Ok(()) } @@ -191,7 +93,7 @@ impl LanguageServer for LanguageServerBackend { ).await; } - async fn completion(&self, params: CompletionParams) -> Result> { + async fn completion(&self, params: CompletionParams) -> realhydroper_lsp::jsonrpc::Result> { self.client .log_message(MessageType::INFO, "completion!") .await; @@ -226,27 +128,107 @@ impl LanguageServer for LanguageServerBackend { } - async fn hover(&self, params: HoverParams) -> Result> { + async fn hover(&self, params: HoverParams) -> realhydroper_lsp::jsonrpc::Result> { let expression = self.get_expression_at_position(¶ms.text_document_position_params); - match expression { + Ok(match expression.data { // show variable type info on hover - DatexExpression { data: DatexExpressionData::VariableDeclaration { name, id: Some(id), .. }, .. } | - DatexExpression { data: DatexExpressionData::VariableAssignment (_, Some(id), name, _), .. } | - DatexExpression { data: DatexExpressionData::Variable (id, name), .. } => { + DatexExpressionData::VariableDeclaration(VariableDeclaration { name, id: Some(id), .. }) | + DatexExpressionData::VariableAssignment(VariableAssignment { name, id: Some(id), .. }) | + DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { let variable_metadata = self.get_variable_by_id(id).unwrap(); - let contents = HoverContents::Scalar(MarkedString::LanguageString(LanguageString { - language: "datex".to_string(), - value: format!( - "{} {}: {}", - variable_metadata.shape, - name, - variable_metadata.var_type.as_ref().unwrap() - ) - })); - Ok(Some(Hover { contents, range: None })) + Some(self.get_language_string_hover(&format!( + "{} {}: {}", + variable_metadata.shape, + name, + variable_metadata.var_type.as_ref().unwrap() + ))) + } + + // show value info on hover for literals + DatexExpressionData::Integer(integer) => { + Some(self.get_language_string_hover(&format!("{}", integer))) + }, + DatexExpressionData::TypedInteger(typed_integer) => { + Some(self.get_language_string_hover(&format!("{}", typed_integer))) + }, + DatexExpressionData::Decimal(decimal) => { + Some(self.get_language_string_hover(&format!("{}", decimal))) + }, + DatexExpressionData::TypedDecimal(typed_decimal) => { + Some(self.get_language_string_hover(&format!("{}", typed_decimal))) + }, + DatexExpressionData::Boolean(boolean) => { + Some(self.get_language_string_hover(&format!("{}", boolean))) + }, + DatexExpressionData::Text(text) => { + Some(self.get_language_string_hover(&format!("\"{}\"", text))) + }, + DatexExpressionData::Endpoint(endpoint) => { + Some(self.get_language_string_hover(&format!("{}", endpoint))) + }, + DatexExpressionData::Null => { + Some(self.get_language_string_hover("null")) + }, + + _ => None, + }) + } + + // get error diagnostics + async fn diagnostic(&self, params: DocumentDiagnosticParams) -> realhydroper_lsp::jsonrpc::Result + { + self.client + .log_message(MessageType::INFO, "diagnostics!") + .await; + + let uri = params.text_document.uri; + let file_path = uri.to_file_path().unwrap(); + + let diagnostics = self.get_diagnostics_for_file(&file_path); + let report = FullDocumentDiagnosticReport { + result_id: None, + items: diagnostics, + }; + + Ok(DocumentDiagnosticReportResult::Report( + DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { + related_documents: None, + full_document_diagnostic_report: report, + })) + ) + } +} + + +impl LanguageServerBackend { + fn get_language_string_hover(&self, text: &str) -> Hover { + let contents = HoverContents::Scalar(MarkedString::LanguageString(LanguageString { + language: "datex".to_string(), + value: text.to_string(), + })); + Hover { contents, range: None } + } + + fn get_diagnostics_for_file(&self, file_path: &std::path::Path) -> Vec { + let mut diagnostics = Vec::new(); + let errors = self.spanned_compiler_errors.borrow(); + if let Some(file_errors) = errors.get(file_path) { + for spanned_error in file_errors { + let diagnostic = Diagnostic { + range: spanned_error.range, + severity: Some(DiagnosticSeverity::ERROR), + code: None, + code_description: None, + source: Some("datex".to_string()), + message: format!("{}", spanned_error.error), + related_information: None, + tags: None, + data: None, + }; + diagnostics.push(diagnostic); } - _ => Ok(None) } + diagnostics } } \ No newline at end of file diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs new file mode 100644 index 0000000..cbb381c --- /dev/null +++ b/src/lsp/utils.rs @@ -0,0 +1,343 @@ +use std::path::PathBuf; +use datex_core::ast::tree::{DatexExpression, DatexExpressionData, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; +use datex_core::compiler::error::CompilerError; +use datex_core::compiler::precompiler::VariableMetadata; +use datex_core::values::core_values::decimal::Decimal; +use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; +use datex_core::values::core_values::endpoint::Endpoint; +use datex_core::values::core_values::integer::Integer; +use datex_core::values::core_values::integer::typed_integer::TypedInteger; +use realhydroper_lsp::lsp_types::{MessageType, Position, Range, TextDocumentPositionParams}; +use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; + +impl LanguageServerBackend { + + pub async fn update_file_contents( + &self, + path: PathBuf, + content: String, + ) { + let mut compiler_workspace = self.compiler_workspace.borrow_mut(); + let file = compiler_workspace.load_file(path.clone(), content.clone()); + // Clear previous errors for this file + self.clear_compiler_errors(&path); + if let Ok(file) = file { + self.client + .log_message( + MessageType::INFO, + format!("AST: {:#?}", file.ast_with_metadata.ast), + ) + .await; + self.client + .log_message( + MessageType::INFO, + format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) + ) + .await; + // Clear previous errors for this file + } else { + let error = file.err().unwrap(); + self.client + .log_message( + MessageType::ERROR, + format!( + "Failed to compile file {}: {}", + path.to_str().unwrap(), + error, + ), + ) + .await; + // Collect new errors + self.collect_compiler_errors(error, &path, &content) + } + } + + /// Clears all compiler errors associated with the given file path. + fn clear_compiler_errors(&self, path: &PathBuf) { + let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); + spanned_compiler_errors.remove(path); + } + + /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. + fn collect_compiler_errors(&self, compiler_error: CompilerError, path: &PathBuf, file_content: &String) { + match compiler_error { + CompilerError::Multiple(errors) => { + for err in errors { + self.collect_compiler_errors(err, path, file_content); + } + } + CompilerError::Spanned(err, span) => { + let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); + let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); + file_errors.push(SpannedCompilerError { + range: self.convert_byte_range_to_document_range(span, file_content), + error: *err + }); + } + // workaround for now: if not spanned compiler error, just span the whole file + _ => { + let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); + let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); + file_errors.push(SpannedCompilerError { + range: self.convert_byte_range_to_document_range(0..file_content.len(), file_content), + error: compiler_error + }); + } + } + } + + /// Finds all variables in the workspace whose names start with the given prefix. + pub fn find_variable_starting_with(&self, prefix: &str) -> Vec { + let compiler_workspace = self.compiler_workspace.borrow(); + let mut results = Vec::new(); + for file in compiler_workspace.files().values() { + let metadata = file.ast_with_metadata.metadata.borrow(); + for var in metadata.variables.iter() { + if var.name.starts_with(prefix) { + results.push(var.clone()); + } + } + } + results + } + + /// Retrieves variable metadata by its unique ID. + pub fn get_variable_by_id(&self, id: usize) -> Option { + let compiler_workspace = self.compiler_workspace.borrow(); + for file in compiler_workspace.files().values() { + let metadata = file.ast_with_metadata.metadata.borrow(); + if let Some(v) = metadata.variables.get(id).cloned() { + return Some(v); + } + } + None + } + + /// Converts an LSP position (line and character) to a byte offset in the file content. + fn position_to_byte_offset(&self, position: &TextDocumentPositionParams) -> usize { + let workspace = self.compiler_workspace.borrow(); + // first get file contents at position.text_document.uri + // then calculate byte offset from position.position.line and position.position.character + let file_path = position.text_document.uri.to_file_path().unwrap(); + let file_content = &workspace.get_file(&file_path).unwrap().content; + + Self::line_char_to_byte_index(file_content, position.position.line as usize, position.position.character as usize).unwrap_or(0) + } + + /// Converts a byte range (start, end) to a document Range (start Position, end Position) in the file content. + fn convert_byte_range_to_document_range(&self, span: std::ops::Range, file_content: &String) -> Range { + let start = self.byte_offset_to_position(span.start, file_content).unwrap_or(Position { line: 0, character: 0 }); + let end = self.byte_offset_to_position(span.end, file_content).unwrap_or(Position { line: 0, character: 0 }); + Range { start, end } + } + + /// Converts a byte offset to an LSP position (line and character) in the file content. + /// TODO: check if this is correct, generated with copilot + fn byte_offset_to_position(&self, byte_offset: usize, file_content: &String) -> Option { + let mut current_offset = 0; + for (line_idx, line) in file_content.lines().enumerate() { + let line_length = line.len() + 1; // +1 for the newline character + if current_offset + line_length > byte_offset { + // The byte offset is within this line + let char_offset = line.char_indices() + .find(|(i, _)| current_offset + i >= byte_offset) + .map(|(i, _)| i) + .unwrap_or(line.len()); + return Some(Position { + line: line_idx as u32, + character: char_offset as u32, + }); + } + current_offset += line_length; + } + None + } + + /// Retrieves the text immediately preceding the given position in the document. + /// This is used for autocompletion suggestions. + pub fn get_previous_text_at_position(&self, position: &TextDocumentPositionParams) -> String { + let byte_offset = self.position_to_byte_offset(position); + let workspace = self.compiler_workspace.borrow(); + let file_path = position.text_document.uri.to_file_path().unwrap(); + let file_content = &workspace.get_file(&file_path).unwrap().content; + // Get the text before the byte offset, only matching word characters + let previous_text = &file_content[..byte_offset]; + let last_word = previous_text + .rsplit(|c: char| !c.is_alphanumeric() && c != '_') + .next() + .unwrap_or(""); + last_word.to_string() + } + + /// Retrieves the DatexExpression AST node at the given byte offset. + pub fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> DatexExpression { + let byte_offset = self.position_to_byte_offset(position); + let workspace = self.compiler_workspace.borrow(); + let file_path = position.text_document.uri.to_file_path().unwrap(); + let ast = &workspace.get_file(&file_path).unwrap().ast_with_metadata.ast; + + let mut finder = ExpressionFinder::new(byte_offset); + finder.visit_expression(ast.as_ref().unwrap()); + finder.found_expr.unwrap() + } + + + /// Converts a (line, character) pair to a byte index in the given text. + /// Lines and characters are zero-indexed. + /// Returns None if the line or character is out of bounds. + pub fn line_char_to_byte_index(text: &str, line: usize, character: usize) -> Option { + let mut lines = text.split('\n'); + + // Get the line + let line_text = lines.nth(line)?; + + // Compute byte index of the start of that line + let byte_offset_to_line_start = text + .lines() + .take(line) + .map(|l| l.len() + 1) // +1 for '\n' + .sum::(); + + // Now find the byte index within that line for the given character offset + let byte_offset_within_line = line_text + .char_indices() + .nth(character) + .map(|(i, _)| i) + .unwrap_or_else(|| line_text.len()); + + Some(byte_offset_to_line_start + byte_offset_within_line) + } +} + + +/// Visitor that finds the most specific DatexExpression containing a given byte position. +/// If multiple expressions contain the position, the one with the smallest span is chosen. +struct ExpressionFinder { + pub search_pos: usize, + pub found_expr: Option, +} + +impl ExpressionFinder { + pub fn new(search_pos: usize) -> Self { + Self { + search_pos, + found_expr: None, + } + } + + + /// Checks if the given span includes the search position. + /// If it does, updates found_expr if this expression is more specific (smaller span). + /// Returns true if the span includes the search position, false otherwise. + fn match_span(&mut self, span: SimpleSpan, expr: DatexExpression) -> bool { + if span.start <= self.search_pos && self.search_pos <= span.end { + // If we already found an expression, only replace it if this one is smaller (more specific) + if let Some(existing_expr) = &self.found_expr { + if (span.end - span.start) < (existing_expr.span.end - existing_expr.span.start) { + self.found_expr = Some(expr); + } + } else { + self.found_expr = Some(expr); + } + true + } + else { + false + } + } +} + +impl Visit for ExpressionFinder { + fn visit_statements(&mut self, stmts: &Statements, span: SimpleSpan) { + if self.match_span(span, DatexExpression { + data: DatexExpressionData::Statements(stmts.clone()), + span, + }) { + // Only visit children if the span matched, to find more specific expressions within + stmts.visit_children_with(self); + } + } + + fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: SimpleSpan) { + if self.match_span(span, DatexExpression { + data: DatexExpressionData::VariableDeclaration(var_decl.clone()), + span, + }) { + // Also visit the init expression to find more specific expressions within it + self.visit_expression(&var_decl.init_expression); + } + } + + fn visit_variable_assignment(&mut self, var_assign: &VariableAssignment, span: SimpleSpan) { + if self.match_span(span, DatexExpression { + data: DatexExpressionData::VariableAssignment(var_assign.clone()), + span, + }) { + // Also visit the assigned expression to find more specific expressions within it + self.visit_expression(&var_assign.expression); + } + } + + fn visit_variable_access(&mut self, var_access: &VariableAccess, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::VariableAccess(var_access.clone()), + span, + }); + } + + fn visit_integer(&mut self, value: &Integer, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Integer(value.clone()), + span, + }); + } + + fn visit_typed_integer(&mut self, value: &TypedInteger, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::TypedInteger(value.clone()), + span, + }); + } + + fn visit_decimal(&mut self, value: &Decimal, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Decimal(value.clone()), + span, + }); + } + + fn visit_typed_decimal(&mut self, value: &TypedDecimal, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::TypedDecimal(value.clone()), + span, + }); + } + + fn visit_text(&mut self, value: &String, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Text(value.clone()), + span, + }); + } + + fn visit_boolean(&mut self, value: bool, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Boolean(value), + span, + }); + } + + fn visit_endpoint(&mut self, value: &Endpoint, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Endpoint(value.clone()), + span, + }); + } + + fn visit_null(&mut self, span: SimpleSpan) { + self.match_span(span, DatexExpression { + data: DatexExpressionData::Null, + span, + }); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a253372..95dfb84 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,9 +43,9 @@ async fn main() { let stdout = tokio::io::stdout(); let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default())); - let compiler_workspace = RefCell::new(CompilerWorkspace::new(runtime)); + let compiler_workspace = CompilerWorkspace::new(runtime); - let (service, socket) = LspService::new(|client| LanguageServerBackend { client, compiler_workspace }); + let (service, socket) = LspService::new(|client| LanguageServerBackend::new(client, compiler_workspace)); Server::new(stdin, stdout, socket).serve(service).await; } Subcommands::Run(run) => { From 541a7b487dd4648a7b0b7e0142728da6399c4822 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 17 Oct 2025 20:34:21 +0300 Subject: [PATCH 07/23] :construction: work on lsp --- Cargo.lock | 27 +++++++++++++++------------ Cargo.toml | 2 +- src/lsp/utils.rs | 12 +++++++++++- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 876eb65..658b2a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,9 +483,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -842,9 +842,8 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "datex-core" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b45f844946d7722ef155e91be17f357c1384e2b001cb5820c8c63354da43dd" +version = "0.0.7" +source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-workspace#4d053538e15aef0ef5823074a9d4cc21ad7d2714" dependencies = [ "ariadne", "async-stream", @@ -868,6 +867,7 @@ dependencies = [ "hex", "hyper", "indexmap 2.11.4", + "indoc", "internment", "itertools 0.14.0", "lazy_static", @@ -910,7 +910,7 @@ dependencies = [ [[package]] name = "datex_cli" -version = "0.0.4" +version = "0.0.5" dependencies = [ "clap", "crossterm", @@ -927,8 +927,7 @@ dependencies = [ [[package]] name = "datex_macros" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1922189d0e176d0b815a911357b3c20fc878512496928dc13b72c281aa6ecee" +source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-workspace#4d053538e15aef0ef5823074a9d4cc21ad7d2714" dependencies = [ "proc-macro2", "quote", @@ -2263,9 +2262,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ "bitflags 2.9.4", "cfg-if", @@ -2295,9 +2294,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" dependencies = [ "cc", "libc", @@ -4686,3 +4685,7 @@ dependencies = [ "quote", "syn 2.0.106", ] + +[[patch.unused]] +name = "datex-core" +version = "0.0.7" diff --git a/Cargo.toml b/Cargo.toml index a1ec8ea..6f19791 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ build = "build.rs" [dependencies] clap = { version = "4.0.23", features = ["derive"] } -datex-core = { version = "0.0.6", features = ["default", "debug"] } +datex-core = { version = "0.0.7", git = "https://github.com/unyt-org/datex-core", branch = "feat/compiler-workspace", features = ["default", "debug"] } tokio = { version = "1.17.0", features = ["full"] } #tower-lsp = { version = "0.20.0", features = ["proposed"]} diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index cbb381c..f084501 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,5 +1,5 @@ use std::path::PathBuf; -use datex_core::ast::tree::{DatexExpression, DatexExpressionData, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; +use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; use datex_core::compiler::error::CompilerError; use datex_core::compiler::precompiler::VariableMetadata; use datex_core::values::core_values::decimal::Decimal; @@ -285,6 +285,16 @@ impl Visit for ExpressionFinder { }); } + fn visit_list(&mut self, list: &List, span: SimpleSpan) { + if self.match_span(span, DatexExpression { + data: DatexExpressionData::List(list.clone()), + span, + }) { + // Also visit the assigned expression to find more specific expressions within it + list.visit_children_with(self); + } + } + fn visit_integer(&mut self, value: &Integer, span: SimpleSpan) { self.match_span(span, DatexExpression { data: DatexExpressionData::Integer(value.clone()), From aee307414e8a3f56a2a55d865cf75e72f709d83f Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 17 Oct 2025 22:57:00 +0300 Subject: [PATCH 08/23] :construction: work on lsp --- src/lsp/utils.rs | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index f084501..a47bbf8 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; -use datex_core::compiler::error::CompilerError; +use datex_core::compiler::error::{CompilerError, DetailedCompilerError}; use datex_core::compiler::precompiler::VariableMetadata; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; @@ -59,30 +59,20 @@ impl LanguageServerBackend { } /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. - fn collect_compiler_errors(&self, compiler_error: CompilerError, path: &PathBuf, file_content: &String) { - match compiler_error { - CompilerError::Multiple(errors) => { - for err in errors { - self.collect_compiler_errors(err, path, file_content); - } - } - CompilerError::Spanned(err, span) => { - let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); - let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); - file_errors.push(SpannedCompilerError { - range: self.convert_byte_range_to_document_range(span, file_content), - error: *err - }); - } - // workaround for now: if not spanned compiler error, just span the whole file - _ => { - let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); - let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); - file_errors.push(SpannedCompilerError { - range: self.convert_byte_range_to_document_range(0..file_content.len(), file_content), - error: compiler_error - }); - } + fn collect_compiler_errors(&self, error: DetailedCompilerError, path: &PathBuf, file_content: &String) { + let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); + let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); + + for error in error.errors { + let range = error.span.map(|span| { + self.convert_byte_range_to_document_range(span, file_content) + }).unwrap_or_else(|| { + self.convert_byte_range_to_document_range(0..file_content.len(), file_content) + }); + file_errors.push(SpannedCompilerError { + range, + error: error.error, + }); } } From d699d732f7d897d5f17be10af869a8af131636dd Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sat, 18 Oct 2025 00:30:39 +0300 Subject: [PATCH 09/23] :construction: work on lsp --- src/lsp/mod.rs | 97 +++++++++++++++++++++++++++--------------------- src/lsp/utils.rs | 83 +++++++++++++++++++++++------------------ 2 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index d1609e0..09b3fce 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,13 +1,16 @@ mod utils; +use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; use datex_core::ast::tree::{DatexExpression, DatexExpressionData, SimpleSpan, VariableAccess, VariableAssignment, VariableDeclaration}; use datex_core::compiler::error::CompilerError; use datex_core::compiler::workspace::CompilerWorkspace; +use datex_core::types::type_container::TypeContainer; use realhydroper_lsp::lsp_types::*; use realhydroper_lsp::{Client, LanguageServer}; +use realhydroper_lsp::jsonrpc::ErrorCode; pub struct SpannedCompilerError { pub range: Range, @@ -131,50 +134,60 @@ impl LanguageServer for LanguageServerBackend { async fn hover(&self, params: HoverParams) -> realhydroper_lsp::jsonrpc::Result> { let expression = self.get_expression_at_position(¶ms.text_document_position_params); - Ok(match expression.data { - // show variable type info on hover - DatexExpressionData::VariableDeclaration(VariableDeclaration { name, id: Some(id), .. }) | - DatexExpressionData::VariableAssignment(VariableAssignment { name, id: Some(id), .. }) | - DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { - let variable_metadata = self.get_variable_by_id(id).unwrap(); - Some(self.get_language_string_hover(&format!( - "{} {}: {}", - variable_metadata.shape, - name, - variable_metadata.var_type.as_ref().unwrap() - ))) - } - - // show value info on hover for literals - DatexExpressionData::Integer(integer) => { - Some(self.get_language_string_hover(&format!("{}", integer))) - }, - DatexExpressionData::TypedInteger(typed_integer) => { - Some(self.get_language_string_hover(&format!("{}", typed_integer))) - }, - DatexExpressionData::Decimal(decimal) => { - Some(self.get_language_string_hover(&format!("{}", decimal))) - }, - DatexExpressionData::TypedDecimal(typed_decimal) => { - Some(self.get_language_string_hover(&format!("{}", typed_decimal))) - }, - DatexExpressionData::Boolean(boolean) => { - Some(self.get_language_string_hover(&format!("{}", boolean))) - }, - DatexExpressionData::Text(text) => { - Some(self.get_language_string_hover(&format!("\"{}\"", text))) - }, - DatexExpressionData::Endpoint(endpoint) => { - Some(self.get_language_string_hover(&format!("{}", endpoint))) - }, - DatexExpressionData::Null => { - Some(self.get_language_string_hover("null")) - }, + if let Some(expression) = expression { + Ok(match expression.data { + // show variable type info on hover + DatexExpressionData::VariableDeclaration(VariableDeclaration { name, id: Some(id), .. }) | + DatexExpressionData::VariableAssignment(VariableAssignment { name, id: Some(id), .. }) | + DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { + let variable_metadata = self.get_variable_by_id(id).unwrap(); + Some(self.get_language_string_hover(&format!( + "{} {}: {}", + variable_metadata.shape, + name, + variable_metadata.var_type.unwrap_or(TypeContainer::unknown()) + ))) + } + + // show value info on hover for literals + DatexExpressionData::Integer(integer) => { + Some(self.get_language_string_hover(&format!("{}", integer))) + }, + DatexExpressionData::TypedInteger(typed_integer) => { + Some(self.get_language_string_hover(&format!("{}", typed_integer))) + }, + DatexExpressionData::Decimal(decimal) => { + Some(self.get_language_string_hover(&format!("{}", decimal))) + }, + DatexExpressionData::TypedDecimal(typed_decimal) => { + Some(self.get_language_string_hover(&format!("{}", typed_decimal))) + }, + DatexExpressionData::Boolean(boolean) => { + Some(self.get_language_string_hover(&format!("{}", boolean))) + }, + DatexExpressionData::Text(text) => { + Some(self.get_language_string_hover(&format!("\"{}\"", text))) + }, + DatexExpressionData::Endpoint(endpoint) => { + Some(self.get_language_string_hover(&format!("{}", endpoint))) + }, + DatexExpressionData::Null => { + Some(self.get_language_string_hover("null")) + }, + + _ => None, + }) + } + else { + Err(realhydroper_lsp::jsonrpc::Error { + code: ErrorCode::ParseError, + message: Cow::from("No AST available"), + data: None + }) + } - _ => None, - }) } - + // get error diagnostics async fn diagnostic(&self, params: DocumentDiagnosticParams) -> realhydroper_lsp::jsonrpc::Result { diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index a47bbf8..20a24f5 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,7 +1,8 @@ use std::path::PathBuf; use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; -use datex_core::compiler::error::{CompilerError, DetailedCompilerError}; -use datex_core::compiler::precompiler::VariableMetadata; +use datex_core::compiler::error::DetailedCompilerErrors; +use datex_core::compiler::precompiler::{VariableMetadata}; +use datex_core::serde::error::DetailedCompilerErrorsWithMaybeRichAst; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; use datex_core::values::core_values::endpoint::Endpoint; @@ -21,37 +22,35 @@ impl LanguageServerBackend { let file = compiler_workspace.load_file(path.clone(), content.clone()); // Clear previous errors for this file self.clear_compiler_errors(&path); - if let Ok(file) = file { + if let Some(errors) = &file.errors { self.client .log_message( - MessageType::INFO, - format!("AST: {:#?}", file.ast_with_metadata.ast), + MessageType::ERROR, + format!( + "Failed to compile file {}: {}", + path.to_str().unwrap(), + errors, + ), ) .await; + self.collect_compiler_errors(errors, path, &content) + } + if let Some(rich_ast) = &file.rich_ast { self.client .log_message( MessageType::INFO, - format!("AST metadata: {:#?}", *file.ast_with_metadata.metadata.borrow()) + format!("AST: {:#?}", rich_ast.ast), ) .await; - // Clear previous errors for this file - } else { - let error = file.err().unwrap(); self.client .log_message( - MessageType::ERROR, - format!( - "Failed to compile file {}: {}", - path.to_str().unwrap(), - error, - ), + MessageType::INFO, + format!("AST metadata: {:#?}", *rich_ast.metadata.borrow()) ) .await; - // Collect new errors - self.collect_compiler_errors(error, &path, &content) } } - + /// Clears all compiler errors associated with the given file path. fn clear_compiler_errors(&self, path: &PathBuf) { let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); @@ -59,19 +58,19 @@ impl LanguageServerBackend { } /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. - fn collect_compiler_errors(&self, error: DetailedCompilerError, path: &PathBuf, file_content: &String) { + fn collect_compiler_errors(&self, errors: &DetailedCompilerErrors, path: PathBuf, file_content: &String) { let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); - for error in error.errors { - let range = error.span.map(|span| { + for error in &errors.errors { + let range = error.span.as_ref().map(|span| { self.convert_byte_range_to_document_range(span, file_content) }).unwrap_or_else(|| { - self.convert_byte_range_to_document_range(0..file_content.len(), file_content) + self.convert_byte_range_to_document_range(&(0..file_content.len()), file_content) }); file_errors.push(SpannedCompilerError { range, - error: error.error, + error: error.error.clone(), }); } } @@ -81,10 +80,12 @@ impl LanguageServerBackend { let compiler_workspace = self.compiler_workspace.borrow(); let mut results = Vec::new(); for file in compiler_workspace.files().values() { - let metadata = file.ast_with_metadata.metadata.borrow(); - for var in metadata.variables.iter() { - if var.name.starts_with(prefix) { - results.push(var.clone()); + if let Some(rich_ast) = &file.rich_ast { + let metadata = rich_ast.metadata.borrow(); + for var in metadata.variables.iter() { + if var.name.starts_with(prefix) { + results.push(var.clone()); + } } } } @@ -95,10 +96,13 @@ impl LanguageServerBackend { pub fn get_variable_by_id(&self, id: usize) -> Option { let compiler_workspace = self.compiler_workspace.borrow(); for file in compiler_workspace.files().values() { - let metadata = file.ast_with_metadata.metadata.borrow(); - if let Some(v) = metadata.variables.get(id).cloned() { - return Some(v); + if let Some(rich_ast) = &file.rich_ast { + let metadata = rich_ast.metadata.borrow(); + if let Some(v) = metadata.variables.get(id).cloned() { + return Some(v); + } } + } None } @@ -115,7 +119,7 @@ impl LanguageServerBackend { } /// Converts a byte range (start, end) to a document Range (start Position, end Position) in the file content. - fn convert_byte_range_to_document_range(&self, span: std::ops::Range, file_content: &String) -> Range { + fn convert_byte_range_to_document_range(&self, span: &std::ops::Range, file_content: &String) -> Range { let start = self.byte_offset_to_position(span.start, file_content).unwrap_or(Position { line: 0, character: 0 }); let end = self.byte_offset_to_position(span.end, file_content).unwrap_or(Position { line: 0, character: 0 }); Range { start, end } @@ -160,15 +164,20 @@ impl LanguageServerBackend { } /// Retrieves the DatexExpression AST node at the given byte offset. - pub fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> DatexExpression { + pub fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> Option { let byte_offset = self.position_to_byte_offset(position); let workspace = self.compiler_workspace.borrow(); let file_path = position.text_document.uri.to_file_path().unwrap(); - let ast = &workspace.get_file(&file_path).unwrap().ast_with_metadata.ast; - - let mut finder = ExpressionFinder::new(byte_offset); - finder.visit_expression(ast.as_ref().unwrap()); - finder.found_expr.unwrap() + if let Some(rich_ast) = &workspace.get_file(&file_path).unwrap().rich_ast { + let ast = rich_ast.ast.as_ref(); + let mut finder = ExpressionFinder::new(byte_offset); + finder.visit_expression(ast.as_ref().unwrap()); + finder.found_expr + } + else { + None + } + } From 3dd9635b8ace62d3c0db10c31072970496f3a6fe Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 24 Oct 2025 10:54:10 +0800 Subject: [PATCH 10/23] :bug: fixes --- src/lsp/utils.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 20a24f5..5eb252d 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -2,7 +2,6 @@ use std::path::PathBuf; use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; use datex_core::compiler::error::DetailedCompilerErrors; use datex_core::compiler::precompiler::{VariableMetadata}; -use datex_core::serde::error::DetailedCompilerErrorsWithMaybeRichAst; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; use datex_core::values::core_values::endpoint::Endpoint; From 9cc143f7c4d968ee4f5874fb50be64cff5d9abbb Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 24 Oct 2025 13:03:23 +0800 Subject: [PATCH 11/23] :bug: add visit_map --- src/lsp/utils.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 5eb252d..337c506 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,5 +1,5 @@ use std::path::PathBuf; -use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; +use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, Map, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; use datex_core::compiler::error::DetailedCompilerErrors; use datex_core::compiler::precompiler::{VariableMetadata}; use datex_core::values::core_values::decimal::Decimal; @@ -101,7 +101,7 @@ impl LanguageServerBackend { return Some(v); } } - + } None } @@ -176,7 +176,7 @@ impl LanguageServerBackend { else { None } - + } @@ -293,6 +293,16 @@ impl Visit for ExpressionFinder { } } + fn visit_map(&mut self, map: &Map, span: SimpleSpan) { + if self.match_span(span, DatexExpression { + data: DatexExpressionData::Map(map.clone()), + span, + }) { + // Also visit the assigned expression to find more specific expressions within it + map.visit_children_with(self); + } + } + fn visit_integer(&mut self, value: &Integer, span: SimpleSpan) { self.match_span(span, DatexExpression { data: DatexExpressionData::Integer(value.clone()), From 27463d7110eae1187a90e32054edce730f602eea Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sun, 26 Oct 2025 17:23:45 +0800 Subject: [PATCH 12/23] :bug: improve run command --- src/main.rs | 34 +++++++++++++++++++++++++++++----- src/repl.rs | 2 +- src/utils/config.rs | 17 ++++++++++------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 95dfb84..de6526f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use datex_core::utils::time_native::TimeNative; use std::path::PathBuf; use std::sync::Arc; use datex_core::compiler::workspace::CompilerWorkspace; +use datex_core::decompiler::{decompile_value, DecompileOptions}; use datex_core::values::core_values::endpoint::Endpoint; mod command_line_args; @@ -49,10 +50,7 @@ async fn main() { Server::new(stdin, stdout, socket).serve(service).await; } Subcommands::Run(run) => { - if run.file.is_some() { - println!("File: {}", run.file.unwrap()) - } - let runtime = Runtime::new(RuntimeConfig::default()); + execute_file(run).await; } Subcommands::Repl(Repl { verbose, config }) => { let options = ReplOptions { @@ -72,6 +70,32 @@ async fn main() { } } +async fn execute_file(run: command_line_args::Run) { + run_async! { + if let Some(file) = run.file { + let runtime = create_runtime_with_config(None, false, false).await.unwrap(); + let file_contents = std::fs::read_to_string(file).expect("Could not read file"); + let _result = runtime.execute(&file_contents, &[], None).await; + if let Err(e) = _result { + eprintln!("{}", e); + } + else { + let result = _result.unwrap(); + if let Some(output) = result { + let formatted_output = decompile_value( + &output, + DecompileOptions::colorized() + ); + println!("{}", formatted_output); + } + } + } + else { + eprintln!("No file provided to run."); + } + } +} + async fn workbench(config_path: Option, debug: bool) -> Result<(), ConfigError> { set_global_context(GlobalContext { crypto: Arc::new(CryptoNative), @@ -80,7 +104,7 @@ async fn workbench(config_path: Option, debug: bool) -> Result<(), Conf }); run_async! { - let runtime = create_runtime_with_config(config_path, debug).await?; + let runtime = create_runtime_with_config(config_path, debug, false).await?; workbench::start_workbench(runtime).await?; Ok(()) diff --git a/src/repl.rs b/src/repl.rs index 8c259ad..880bfdd 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -108,7 +108,7 @@ pub async fn repl(options: ReplOptions) -> Result<(), ReplError> { let (response_sender, response_receiver) = tokio::sync::mpsc::channel::(100); run_async! { - let runtime = create_runtime_with_config(options.config_path, options.verbose).await?; + let runtime = create_runtime_with_config(options.config_path, options.verbose, true).await?; repl_loop(cmd_sender, response_receiver)?; diff --git a/src/utils/config.rs b/src/utils/config.rs index 2a0c96b..c13e5a0 100644 --- a/src/utils/config.rs +++ b/src/utils/config.rs @@ -129,6 +129,7 @@ pub fn get_config(custom_config_path: Option) -> Result, force_debug: bool, + print_header: bool, ) -> Result { let mut config = get_config(custom_config_path)?; // overwrite debug mode if force_debug is true @@ -137,14 +138,16 @@ pub async fn create_runtime_with_config( } let runtime = Runtime::create_native(config).await; - let cli_version = env!("CARGO_PKG_VERSION"); + if print_header { + let cli_version = env!("CARGO_PKG_VERSION"); - println!("================================================"); - println!("DATEX REPL v{cli_version}"); - println!("DATEX Core version: {}", runtime.version); - println!("Endpoint: {}", runtime.endpoint()); - println!("\nexit using [CTRL + C]"); - println!("================================================\n"); + println!("================================================"); + println!("DATEX REPL v{cli_version}"); + println!("DATEX Core version: {}", runtime.version); + println!("Endpoint: {}", runtime.endpoint()); + println!("\nexit using [CTRL + C]"); + println!("================================================\n"); + } Ok(runtime) } From 17947d8420debc3b10e01bdad493d6578d3293b4 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sun, 26 Oct 2025 17:32:56 +0800 Subject: [PATCH 13/23] :bug: improve run command --- src/command_line_args.rs | 3 +++ src/main.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/command_line_args.rs b/src/command_line_args.rs index 73039d5..8d1fba9 100644 --- a/src/command_line_args.rs +++ b/src/command_line_args.rs @@ -22,6 +22,9 @@ pub enum Subcommands { #[derive(Args)] pub struct Run { pub file: Option, + /// optional path to dx config file + #[arg(short, long)] + pub config: Option, } #[derive(Args)] diff --git a/src/main.rs b/src/main.rs index de6526f..ea7d34c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,7 +73,7 @@ async fn main() { async fn execute_file(run: command_line_args::Run) { run_async! { if let Some(file) = run.file { - let runtime = create_runtime_with_config(None, false, false).await.unwrap(); + let runtime = create_runtime_with_config(run.config, true, false).await.unwrap(); let file_contents = std::fs::read_to_string(file).expect("Could not read file"); let _result = runtime.execute(&file_contents, &[], None).await; if let Err(e) = _result { From fe2abcc13d2181c0aa8f4eee0a9d747a68cb8590 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sun, 26 Oct 2025 17:38:48 +0800 Subject: [PATCH 14/23] :bug: improve run command --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index ea7d34c..7edcea3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ async fn main() { let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default())); let compiler_workspace = CompilerWorkspace::new(runtime); - + let (service, socket) = LspService::new(|client| LanguageServerBackend::new(client, compiler_workspace)); Server::new(stdin, stdout, socket).serve(service).await; } @@ -73,7 +73,9 @@ async fn main() { async fn execute_file(run: command_line_args::Run) { run_async! { if let Some(file) = run.file { - let runtime = create_runtime_with_config(run.config, true, false).await.unwrap(); + let runtime = create_runtime_with_config(run.config, false, false).await.unwrap(); + // yield to wait for connect. TODO: better way + tokio::task::yield_now().await; let file_contents = std::fs::read_to_string(file).expect("Could not read file"); let _result = runtime.execute(&file_contents, &[], None).await; if let Err(e) = _result { From 4cda92f95a50bc5ec93d911ff7ecc71e7b6a503c Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Tue, 28 Oct 2025 13:40:14 +0800 Subject: [PATCH 15/23] :construction: work on lsp --- Cargo.lock | 24 ++++++ src/lsp/mod.rs | 75 +++++++++++++++- src/lsp/type_hint_collector.rs | 19 ++++ src/lsp/utils.rs | 115 +++++++++++++++++-------- src/lsp/variable_declaration_finder.rs | 26 ++++++ 5 files changed, 221 insertions(+), 38 deletions(-) create mode 100644 src/lsp/type_hint_collector.rs create mode 100644 src/lsp/variable_declaration_finder.rs diff --git a/Cargo.lock b/Cargo.lock index 658b2a1..b877108 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "asn1-rs" version = "0.6.2" @@ -885,6 +891,7 @@ dependencies = [ "openssl", "ordered-float", "pad", + "pretty", "rand", "ringmap", "serde", @@ -2509,6 +2516,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156" +dependencies = [ + "arrayvec", + "typed-arena", + "unicode-width 0.2.0", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3794,6 +3812,12 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.19.0" diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 09b3fce..723326f 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,16 +1,21 @@ mod utils; +mod type_hint_collector; +mod variable_declaration_finder; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; -use datex_core::ast::tree::{DatexExpression, DatexExpressionData, SimpleSpan, VariableAccess, VariableAssignment, VariableDeclaration}; +use datex_core::ast::data::expression::{DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration}; +use datex_core::ast::data::visitor::Visit; use datex_core::compiler::error::CompilerError; +use datex_core::compiler::precompiler::RichAst; use datex_core::compiler::workspace::CompilerWorkspace; use datex_core::types::type_container::TypeContainer; use realhydroper_lsp::lsp_types::*; use realhydroper_lsp::{Client, LanguageServer}; -use realhydroper_lsp::jsonrpc::ErrorCode; +use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; +use crate::lsp::variable_declaration_finder::VariableDeclarationFinder; pub struct SpannedCompilerError { pub range: Range, @@ -52,6 +57,12 @@ impl LanguageServer for LanguageServerBackend { work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None } } )), + inlay_hint_provider: Some(OneOf::Left(true)), + document_link_provider: Some(DocumentLinkOptions { + resolve_provider: Some(true), + work_done_progress_options: Default::default(), + }), + definition_provider: Some(OneOf::Left(true)), ..Default::default() }, ..Default::default() @@ -185,7 +196,67 @@ impl LanguageServer for LanguageServerBackend { data: None }) } + } + + async fn inlay_hint(&self, params: InlayHintParams) -> realhydroper_lsp::jsonrpc::Result>> { + // show type hints for variables + let type_hints = self + .get_type_hints(params.text_document.uri.to_file_path().unwrap()) + .unwrap() + .into_iter() + .map(|hint| { + InlayHint { + position: hint.0, + label: InlayHintLabel::String(format!(": {}", hint.1.unwrap())), + kind: Some(InlayHintKind::TYPE), + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: None, + data: None, + } + }) + .collect(); + + + Ok(Some(type_hints)) + } + + + async fn goto_definition(&self, params: GotoDefinitionParams) -> realhydroper_lsp::jsonrpc::Result> { + let expression = self.get_expression_at_position(¶ms.text_document_position_params); + if let Some(expression) = expression { + match expression.data { + DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { + let uri = params.text_document_position_params.text_document.uri; + let file_path = uri.to_file_path().unwrap(); + let workspace = self.compiler_workspace.borrow(); + let file = workspace.get_file(&file_path).unwrap(); + if let Some(RichAst {ast: Some(ast), ..}) = &file.rich_ast { + let mut finder = VariableDeclarationFinder::new(id); + finder.visit_expression(ast); + Ok( + finder.variable_declaration_position + .map(|position| GotoDefinitionResponse::Scalar( + Location { uri, range: self.convert_byte_range_to_document_range(&position, &file.content)} + )) + ) + } + else { + Ok(None) + } + } + _ => Ok(None) + } + } + else { + Err(Error::internal_error()) + } + } + async fn document_link(&self, params: DocumentLinkParams) -> realhydroper_lsp::jsonrpc::Result>> { + // TODO + Ok(Some(vec![])) } // get error diagnostics diff --git a/src/lsp/type_hint_collector.rs b/src/lsp/type_hint_collector.rs new file mode 100644 index 0000000..3a8b2f9 --- /dev/null +++ b/src/lsp/type_hint_collector.rs @@ -0,0 +1,19 @@ +use std::ops::Range; +use datex_core::ast::binding::VariableId; +use datex_core::ast::data::expression::VariableDeclaration; +use datex_core::ast::data::visitor::{Visit, Visitable}; + +#[derive(Default)] +pub struct TypeHintCollector { + pub type_hints: Vec<(usize, VariableId)> +} + +impl Visit for TypeHintCollector { + fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &Range) { + if var_decl.type_annotation.is_none() { + let expr_start = var_decl.init_expression.span.start; + self.type_hints.push((expr_start - 3, var_decl.id.unwrap())); + } + var_decl.visit_children_with(self); + } +} \ No newline at end of file diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 337c506..c9dbf34 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,7 +1,9 @@ use std::path::PathBuf; -use datex_core::ast::tree::{DatexExpression, DatexExpressionData, List, Map, SimpleSpan, Statements, VariableAccess, VariableAssignment, VariableDeclaration, Visit, Visitable}; +use datex_core::ast::data::expression::{DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, VariableAssignment, VariableDeclaration}; +use datex_core::ast::data::visitor::{Visit, Visitable}; use datex_core::compiler::error::DetailedCompilerErrors; -use datex_core::compiler::precompiler::{VariableMetadata}; +use datex_core::compiler::precompiler::{RichAst, VariableMetadata}; +use datex_core::types::type_container::TypeContainer; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; use datex_core::values::core_values::endpoint::Endpoint; @@ -9,6 +11,7 @@ use datex_core::values::core_values::integer::Integer; use datex_core::values::core_values::integer::typed_integer::TypedInteger; use realhydroper_lsp::lsp_types::{MessageType, Position, Range, TextDocumentPositionParams}; use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; +use crate::lsp::type_hint_collector::TypeHintCollector; impl LanguageServerBackend { @@ -49,6 +52,32 @@ impl LanguageServerBackend { .await; } } + + + pub(crate) fn get_type_hints(&self, file_path: PathBuf) -> Option)>> { + let workspace = self.compiler_workspace.borrow(); + let file = workspace.get_file(&file_path).unwrap(); + if let Some(rich_ast) = &file.rich_ast { + let ast = rich_ast.ast.as_ref().unwrap(); + let mut collector = TypeHintCollector::default(); + collector.visit_expression(ast); + Some( + collector + .type_hints + .into_iter() + .map(|hint| + ( + self.byte_offset_to_position(hint.0, &file.content).unwrap(), + rich_ast.metadata.borrow().variables.get(hint.1).unwrap().var_type.clone() + ) + ) + .collect() + ) + } + else { + None + } + } /// Clears all compiler errors associated with the given file path. fn clear_compiler_errors(&self, path: &PathBuf) { @@ -118,7 +147,7 @@ impl LanguageServerBackend { } /// Converts a byte range (start, end) to a document Range (start Position, end Position) in the file content. - fn convert_byte_range_to_document_range(&self, span: &std::ops::Range, file_content: &String) -> Range { + pub fn convert_byte_range_to_document_range(&self, span: &std::ops::Range, file_content: &String) -> Range { let start = self.byte_offset_to_position(span.start, file_content).unwrap_or(Position { line: 0, character: 0 }); let end = self.byte_offset_to_position(span.end, file_content).unwrap_or(Position { line: 0, character: 0 }); Range { start, end } @@ -126,7 +155,7 @@ impl LanguageServerBackend { /// Converts a byte offset to an LSP position (line and character) in the file content. /// TODO: check if this is correct, generated with copilot - fn byte_offset_to_position(&self, byte_offset: usize, file_content: &String) -> Option { + pub fn byte_offset_to_position(&self, byte_offset: usize, file_content: &String) -> Option { let mut current_offset = 0; for (line_idx, line) in file_content.lines().enumerate() { let line_length = line.len() + 1; // +1 for the newline character @@ -168,9 +197,9 @@ impl LanguageServerBackend { let workspace = self.compiler_workspace.borrow(); let file_path = position.text_document.uri.to_file_path().unwrap(); if let Some(rich_ast) = &workspace.get_file(&file_path).unwrap().rich_ast { - let ast = rich_ast.ast.as_ref(); + let ast = rich_ast.ast.as_ref().unwrap(); let mut finder = ExpressionFinder::new(byte_offset); - finder.visit_expression(ast.as_ref().unwrap()); + finder.visit_expression(ast); finder.found_expr } else { @@ -227,7 +256,7 @@ impl ExpressionFinder { /// Checks if the given span includes the search position. /// If it does, updates found_expr if this expression is more specific (smaller span). /// Returns true if the span includes the search position, false otherwise. - fn match_span(&mut self, span: SimpleSpan, expr: DatexExpression) -> bool { + fn match_span(&mut self, span: &std::ops::Range, expr: DatexExpression) -> bool { if span.start <= self.search_pos && self.search_pos <= span.end { // If we already found an expression, only replace it if this one is smaller (more specific) if let Some(existing_expr) = &self.found_expr { @@ -246,116 +275,130 @@ impl ExpressionFinder { } impl Visit for ExpressionFinder { - fn visit_statements(&mut self, stmts: &Statements, span: SimpleSpan) { + fn visit_statements(&mut self, stmts: &Statements, span: &std::ops::Range) { if self.match_span(span, DatexExpression { data: DatexExpressionData::Statements(stmts.clone()), - span, + span: span.clone(), + wrapped: None, }) { // Only visit children if the span matched, to find more specific expressions within stmts.visit_children_with(self); } } - fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: SimpleSpan) { + fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &std::ops::Range) { if self.match_span(span, DatexExpression { data: DatexExpressionData::VariableDeclaration(var_decl.clone()), - span, + span: span.clone(), + wrapped: None, }) { // Also visit the init expression to find more specific expressions within it self.visit_expression(&var_decl.init_expression); } } - fn visit_variable_assignment(&mut self, var_assign: &VariableAssignment, span: SimpleSpan) { + fn visit_variable_assignment(&mut self, var_assign: &VariableAssignment, span: &std::ops::Range) { if self.match_span(span, DatexExpression { data: DatexExpressionData::VariableAssignment(var_assign.clone()), - span, + span: span.clone(), + wrapped: None, }) { // Also visit the assigned expression to find more specific expressions within it self.visit_expression(&var_assign.expression); } } - fn visit_variable_access(&mut self, var_access: &VariableAccess, span: SimpleSpan) { + fn visit_variable_access(&mut self, var_access: &VariableAccess, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::VariableAccess(var_access.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_list(&mut self, list: &List, span: SimpleSpan) { + fn visit_list(&mut self, list: &List, span: &std::ops::Range) { if self.match_span(span, DatexExpression { data: DatexExpressionData::List(list.clone()), - span, + span: span.clone(), + wrapped: None, }) { // Also visit the assigned expression to find more specific expressions within it list.visit_children_with(self); } } - fn visit_map(&mut self, map: &Map, span: SimpleSpan) { + fn visit_map(&mut self, map: &Map, span: &std::ops::Range) { if self.match_span(span, DatexExpression { data: DatexExpressionData::Map(map.clone()), - span, + span: span.clone(), + wrapped: None, }) { // Also visit the assigned expression to find more specific expressions within it map.visit_children_with(self); } } - fn visit_integer(&mut self, value: &Integer, span: SimpleSpan) { + fn visit_integer(&mut self, value: &Integer, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::Integer(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_typed_integer(&mut self, value: &TypedInteger, span: SimpleSpan) { + fn visit_typed_integer(&mut self, value: &TypedInteger, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::TypedInteger(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_decimal(&mut self, value: &Decimal, span: SimpleSpan) { + fn visit_decimal(&mut self, value: &Decimal, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::Decimal(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_typed_decimal(&mut self, value: &TypedDecimal, span: SimpleSpan) { + fn visit_typed_decimal(&mut self, value: &TypedDecimal, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::TypedDecimal(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_text(&mut self, value: &String, span: SimpleSpan) { + fn visit_text(&mut self, value: &String, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::Text(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_boolean(&mut self, value: bool, span: SimpleSpan) { + fn visit_boolean(&mut self, value: &bool, span: &std::ops::Range) { self.match_span(span, DatexExpression { - data: DatexExpressionData::Boolean(value), - span, + data: DatexExpressionData::Boolean(*value), + span: span.clone(), + wrapped: None, }); } - fn visit_endpoint(&mut self, value: &Endpoint, span: SimpleSpan) { + fn visit_endpoint(&mut self, value: &Endpoint, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::Endpoint(value.clone()), - span, + span: span.clone(), + wrapped: None, }); } - fn visit_null(&mut self, span: SimpleSpan) { + fn visit_null(&mut self, span: &std::ops::Range) { self.match_span(span, DatexExpression { data: DatexExpressionData::Null, - span, + span: span.clone(), + wrapped: None, }); } } \ No newline at end of file diff --git a/src/lsp/variable_declaration_finder.rs b/src/lsp/variable_declaration_finder.rs new file mode 100644 index 0000000..a2c303d --- /dev/null +++ b/src/lsp/variable_declaration_finder.rs @@ -0,0 +1,26 @@ +use std::ops::Range; +use datex_core::ast::data::expression::VariableDeclaration; +use datex_core::ast::data::visitor::{Visit, Visitable}; + +#[derive(Default)] +pub struct VariableDeclarationFinder { + pub var_id: usize, + pub variable_declaration_position: Option> +} + +impl VariableDeclarationFinder { + pub fn new(var_id: usize) -> Self { + VariableDeclarationFinder { var_id, variable_declaration_position: None } + } +} + +impl Visit for VariableDeclarationFinder { + fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &Range) { + if var_decl.id == Some(self.var_id) { + self.variable_declaration_position = Some(span.clone()); + } + else { + var_decl.visit_children_with(self); + } + } +} \ No newline at end of file From e29154b4ee8f8d18632367d0ffb292c3c0d4ffc1 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 31 Oct 2025 14:04:08 +0100 Subject: [PATCH 16/23] :recycle: refactor new datex-core visitor changes --- src/lsp/mod.rs | 14 +- src/lsp/type_hint_collector.rs | 17 ++- src/lsp/utils.rs | 181 ++++++++----------------- src/lsp/variable_declaration_finder.rs | 16 ++- 4 files changed, 89 insertions(+), 139 deletions(-) diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 723326f..883b158 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -6,12 +6,12 @@ use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; -use datex_core::ast::data::expression::{DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration}; -use datex_core::ast::data::visitor::Visit; +use datex_core::ast::structs::expression::{DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration}; use datex_core::compiler::error::CompilerError; -use datex_core::compiler::precompiler::RichAst; use datex_core::compiler::workspace::CompilerWorkspace; +use datex_core::precompiler::precompiled_ast::RichAst; use datex_core::types::type_container::TypeContainer; +use datex_core::visitor::expression::ExpressionVisitor; use realhydroper_lsp::lsp_types::*; use realhydroper_lsp::{Client, LanguageServer}; use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; @@ -230,11 +230,11 @@ impl LanguageServer for LanguageServerBackend { DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { let uri = params.text_document_position_params.text_document.uri; let file_path = uri.to_file_path().unwrap(); - let workspace = self.compiler_workspace.borrow(); - let file = workspace.get_file(&file_path).unwrap(); - if let Some(RichAst {ast: Some(ast), ..}) = &file.rich_ast { + let mut workspace = self.compiler_workspace.borrow_mut(); + let file = workspace.get_file_mut(&file_path).unwrap(); + if let Some(RichAst {ast: Some(ast), ..}) = &mut file.rich_ast { let mut finder = VariableDeclarationFinder::new(id); - finder.visit_expression(ast); + finder.visit_datex_expression(ast); Ok( finder.variable_declaration_position .map(|position| GotoDefinitionResponse::Scalar( diff --git a/src/lsp/type_hint_collector.rs b/src/lsp/type_hint_collector.rs index 3a8b2f9..024badf 100644 --- a/src/lsp/type_hint_collector.rs +++ b/src/lsp/type_hint_collector.rs @@ -1,19 +1,24 @@ use std::ops::Range; -use datex_core::ast::binding::VariableId; -use datex_core::ast::data::expression::VariableDeclaration; -use datex_core::ast::data::visitor::{Visit, Visitable}; +use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; +use datex_core::ast::structs::VariableId; +use datex_core::visitor::expression::ExpressionVisitor; +use datex_core::visitor::type_expression::TypeExpressionVisitor; +use datex_core::visitor::VisitAction; #[derive(Default)] pub struct TypeHintCollector { pub type_hints: Vec<(usize, VariableId)> } -impl Visit for TypeHintCollector { - fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &Range) { +impl TypeExpressionVisitor<()> for TypeHintCollector {} + +impl ExpressionVisitor<()> for TypeHintCollector { + fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &Range) -> Result, ()> { if var_decl.type_annotation.is_none() { let expr_start = var_decl.init_expression.span.start; + // TODO: improve self.type_hints.push((expr_start - 3, var_decl.id.unwrap())); } - var_decl.visit_children_with(self); + Ok(VisitAction::VisitChildren) } } \ No newline at end of file diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index c9dbf34..48ef7f7 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,14 +1,16 @@ use std::path::PathBuf; -use datex_core::ast::data::expression::{DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, VariableAssignment, VariableDeclaration}; -use datex_core::ast::data::visitor::{Visit, Visitable}; +use datex_core::ast::structs::expression::{DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, VariableAssignment, VariableDeclaration}; use datex_core::compiler::error::DetailedCompilerErrors; -use datex_core::compiler::precompiler::{RichAst, VariableMetadata}; +use datex_core::precompiler::precompiled_ast::VariableMetadata; use datex_core::types::type_container::TypeContainer; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; use datex_core::values::core_values::endpoint::Endpoint; use datex_core::values::core_values::integer::Integer; use datex_core::values::core_values::integer::typed_integer::TypedInteger; +use datex_core::visitor::expression::ExpressionVisitor; +use datex_core::visitor::type_expression::TypeExpressionVisitor; +use datex_core::visitor::VisitAction; use realhydroper_lsp::lsp_types::{MessageType, Position, Range, TextDocumentPositionParams}; use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; use crate::lsp::type_hint_collector::TypeHintCollector; @@ -55,12 +57,12 @@ impl LanguageServerBackend { pub(crate) fn get_type_hints(&self, file_path: PathBuf) -> Option)>> { - let workspace = self.compiler_workspace.borrow(); - let file = workspace.get_file(&file_path).unwrap(); - if let Some(rich_ast) = &file.rich_ast { - let ast = rich_ast.ast.as_ref().unwrap(); + let mut workspace = self.compiler_workspace.borrow_mut(); + let file = workspace.get_file_mut(&file_path).unwrap(); + if let Some(rich_ast) = &mut file.rich_ast { + let ast = rich_ast.ast.as_mut().unwrap(); let mut collector = TypeHintCollector::default(); - collector.visit_expression(ast); + collector.visit_datex_expression(ast); Some( collector .type_hints @@ -194,13 +196,19 @@ impl LanguageServerBackend { /// Retrieves the DatexExpression AST node at the given byte offset. pub fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> Option { let byte_offset = self.position_to_byte_offset(position); - let workspace = self.compiler_workspace.borrow(); + let mut workspace = self.compiler_workspace.borrow_mut(); let file_path = position.text_document.uri.to_file_path().unwrap(); - if let Some(rich_ast) = &workspace.get_file(&file_path).unwrap().rich_ast { - let ast = rich_ast.ast.as_ref().unwrap(); + if let Some(rich_ast) = &mut workspace.get_file_mut(&file_path).unwrap().rich_ast { + let ast = rich_ast.ast.as_mut().unwrap(); let mut finder = ExpressionFinder::new(byte_offset); - finder.visit_expression(ast); - finder.found_expr + finder.visit_datex_expression(ast); + finder.found_expr.map(|e| + DatexExpression { + span: e.1, + data: e.0, + wrapped: None + } + ) } else { None @@ -241,7 +249,7 @@ impl LanguageServerBackend { /// If multiple expressions contain the position, the one with the smallest span is chosen. struct ExpressionFinder { pub search_pos: usize, - pub found_expr: Option, + pub found_expr: Option<(DatexExpressionData, std::ops::Range)>, } impl ExpressionFinder { @@ -256,149 +264,80 @@ impl ExpressionFinder { /// Checks if the given span includes the search position. /// If it does, updates found_expr if this expression is more specific (smaller span). /// Returns true if the span includes the search position, false otherwise. - fn match_span(&mut self, span: &std::ops::Range, expr: DatexExpression) -> bool { + fn match_span(&mut self, span: &std::ops::Range, expr_data: DatexExpressionData) -> Result, ()> { if span.start <= self.search_pos && self.search_pos <= span.end { // If we already found an expression, only replace it if this one is smaller (more specific) - if let Some(existing_expr) = &self.found_expr { - if (span.end - span.start) < (existing_expr.span.end - existing_expr.span.start) { - self.found_expr = Some(expr); + if let Some((_, existing_expr_span)) = &self.found_expr { + if (span.end - span.start) < (existing_expr_span.end - existing_expr_span.start) { + self.found_expr = Some((expr_data, span.clone())); } } else { - self.found_expr = Some(expr); + self.found_expr = Some((expr_data, span.clone())); } - true + Ok(VisitAction::VisitChildren) } else { - false + Err(()) } } } -impl Visit for ExpressionFinder { - fn visit_statements(&mut self, stmts: &Statements, span: &std::ops::Range) { - if self.match_span(span, DatexExpression { - data: DatexExpressionData::Statements(stmts.clone()), - span: span.clone(), - wrapped: None, - }) { - // Only visit children if the span matched, to find more specific expressions within - stmts.visit_children_with(self); - } +impl TypeExpressionVisitor<()> for ExpressionFinder {} + +impl ExpressionVisitor<()> for ExpressionFinder { + fn visit_statements(&mut self, stmts: &mut Statements, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Statements(stmts.clone())) } - fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &std::ops::Range) { - if self.match_span(span, DatexExpression { - data: DatexExpressionData::VariableDeclaration(var_decl.clone()), - span: span.clone(), - wrapped: None, - }) { - // Also visit the init expression to find more specific expressions within it - self.visit_expression(&var_decl.init_expression); - } + fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::VariableDeclaration(var_decl.clone())) } - fn visit_variable_assignment(&mut self, var_assign: &VariableAssignment, span: &std::ops::Range) { - if self.match_span(span, DatexExpression { - data: DatexExpressionData::VariableAssignment(var_assign.clone()), - span: span.clone(), - wrapped: None, - }) { - // Also visit the assigned expression to find more specific expressions within it - self.visit_expression(&var_assign.expression); - } + fn visit_variable_assignment(&mut self, var_assign: &mut VariableAssignment, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::VariableAssignment(var_assign.clone())) } - fn visit_variable_access(&mut self, var_access: &VariableAccess, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::VariableAccess(var_access.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_variable_access(&mut self, var_access: &mut VariableAccess, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::VariableAccess(var_access.clone())) } - fn visit_list(&mut self, list: &List, span: &std::ops::Range) { - if self.match_span(span, DatexExpression { - data: DatexExpressionData::List(list.clone()), - span: span.clone(), - wrapped: None, - }) { - // Also visit the assigned expression to find more specific expressions within it - list.visit_children_with(self); - } + fn visit_list(&mut self, list: &mut List, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::List(list.clone())) } - fn visit_map(&mut self, map: &Map, span: &std::ops::Range) { - if self.match_span(span, DatexExpression { - data: DatexExpressionData::Map(map.clone()), - span: span.clone(), - wrapped: None, - }) { - // Also visit the assigned expression to find more specific expressions within it - map.visit_children_with(self); - } + fn visit_map(&mut self, map: &mut Map, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Map(map.clone())) } - fn visit_integer(&mut self, value: &Integer, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Integer(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_integer(&mut self, value: &mut Integer, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Integer(value.clone())) } - fn visit_typed_integer(&mut self, value: &TypedInteger, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::TypedInteger(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_typed_integer(&mut self, value: &mut TypedInteger, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::TypedInteger(value.clone())) } - fn visit_decimal(&mut self, value: &Decimal, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Decimal(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_decimal(&mut self, value: &mut Decimal, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Decimal(value.clone())) } - fn visit_typed_decimal(&mut self, value: &TypedDecimal, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::TypedDecimal(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_typed_decimal(&mut self, value: &mut TypedDecimal, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::TypedDecimal(value.clone())) } - fn visit_text(&mut self, value: &String, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Text(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_text(&mut self, value: &mut String, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Text(value.clone())) } - fn visit_boolean(&mut self, value: &bool, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Boolean(*value), - span: span.clone(), - wrapped: None, - }); + fn visit_boolean(&mut self, value: &mut bool, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Boolean(*value)) } - fn visit_endpoint(&mut self, value: &Endpoint, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Endpoint(value.clone()), - span: span.clone(), - wrapped: None, - }); + fn visit_endpoint(&mut self, value: &mut Endpoint, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Endpoint(value.clone())) } - fn visit_null(&mut self, span: &std::ops::Range) { - self.match_span(span, DatexExpression { - data: DatexExpressionData::Null, - span: span.clone(), - wrapped: None, - }); + fn visit_null(&mut self, span: &std::ops::Range) -> Result, ()> { + self.match_span(span, DatexExpressionData::Null) } } \ No newline at end of file diff --git a/src/lsp/variable_declaration_finder.rs b/src/lsp/variable_declaration_finder.rs index a2c303d..9b601ae 100644 --- a/src/lsp/variable_declaration_finder.rs +++ b/src/lsp/variable_declaration_finder.rs @@ -1,6 +1,8 @@ use std::ops::Range; -use datex_core::ast::data::expression::VariableDeclaration; -use datex_core::ast::data::visitor::{Visit, Visitable}; +use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; +use datex_core::visitor::expression::ExpressionVisitor; +use datex_core::visitor::type_expression::TypeExpressionVisitor; +use datex_core::visitor::VisitAction; #[derive(Default)] pub struct VariableDeclarationFinder { @@ -14,13 +16,17 @@ impl VariableDeclarationFinder { } } -impl Visit for VariableDeclarationFinder { - fn visit_variable_declaration(&mut self, var_decl: &VariableDeclaration, span: &Range) { +impl TypeExpressionVisitor<()> for VariableDeclarationFinder {} + +impl ExpressionVisitor<()> for VariableDeclarationFinder { + fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &Range) -> Result, ()> { if var_decl.id == Some(self.var_id) { self.variable_declaration_position = Some(span.clone()); + // early abort + Err(()) } else { - var_decl.visit_children_with(self); + Ok(VisitAction::VisitChildren) } } } \ No newline at end of file From c5bdc04f70099442ccc8d96d4e8a1256837b483c Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 31 Oct 2025 14:04:42 +0100 Subject: [PATCH 17/23] :art: clippy fixes --- src/lsp/utils.rs | 2 +- src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 48ef7f7..9ef2a93 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -90,7 +90,7 @@ impl LanguageServerBackend { /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. fn collect_compiler_errors(&self, errors: &DetailedCompilerErrors, path: PathBuf, file_content: &String) { let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); - let file_errors = spanned_compiler_errors.entry(path.clone()).or_insert_with(Vec::new); + let file_errors = spanned_compiler_errors.entry(path.clone()).or_default(); for error in &errors.errors { let range = error.span.as_ref().map(|span| { diff --git a/src/main.rs b/src/main.rs index 7edcea3..7ed0b96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use std::cell::RefCell; use datex_core::crypto::crypto_native::CryptoNative; use datex_core::run_async; use datex_core::runtime::global_context::{DebugFlags, GlobalContext, set_global_context}; From b1ebc1cb36cd024e4f55632a747637e2669149b0 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 31 Oct 2025 14:05:20 +0100 Subject: [PATCH 18/23] :art: cargo fmt --- build.rs | 2 +- src/command_line_args.rs | 2 +- src/lsp/mod.rs | 181 ++++++++++-------- src/lsp/type_hint_collector.rs | 16 +- src/lsp/utils.rs | 249 +++++++++++++++++-------- src/lsp/variable_declaration_finder.rs | 22 ++- src/main.rs | 13 +- src/repl.rs | 2 +- src/utils/mod.rs | 2 +- src/workbench/views/comhub.rs | 4 +- src/workbench/views/metadata.rs | 6 +- src/workbench/workbench.rs | 4 +- 12 files changed, 323 insertions(+), 180 deletions(-) diff --git a/build.rs b/build.rs index fd3ffc8..dcd245b 100644 --- a/build.rs +++ b/build.rs @@ -21,4 +21,4 @@ fn main() { // Set environment variable for compile-time use println!("cargo:rustc-env=DEP_DATEX_CORE_VERSION={}", serde_version); -} \ No newline at end of file +} diff --git a/src/command_line_args.rs b/src/command_line_args.rs index 8d1fba9..6a2804c 100644 --- a/src/command_line_args.rs +++ b/src/command_line_args.rs @@ -1,5 +1,5 @@ -use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; +use std::path::PathBuf; #[derive(Parser)] #[command(author, version, about, long_about = None, bin_name = "datex")] diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 883b158..3861238 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -1,21 +1,23 @@ -mod utils; mod type_hint_collector; +mod utils; mod variable_declaration_finder; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::path::PathBuf; -use datex_core::ast::structs::expression::{DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration}; +use crate::lsp::variable_declaration_finder::VariableDeclarationFinder; +use datex_core::ast::structs::expression::{ + DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration, +}; use datex_core::compiler::error::CompilerError; use datex_core::compiler::workspace::CompilerWorkspace; use datex_core::precompiler::precompiled_ast::RichAst; use datex_core::types::type_container::TypeContainer; use datex_core::visitor::expression::ExpressionVisitor; +use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; use realhydroper_lsp::lsp_types::*; use realhydroper_lsp::{Client, LanguageServer}; -use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; -use crate::lsp::variable_declaration_finder::VariableDeclarationFinder; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::path::PathBuf; pub struct SpannedCompilerError { pub range: Range, @@ -38,10 +40,12 @@ impl LanguageServerBackend { } } - #[realhydroper_lsp::async_trait(?Send)] impl LanguageServer for LanguageServerBackend { - async fn initialize(&self, _: InitializeParams) -> realhydroper_lsp::jsonrpc::Result { + async fn initialize( + &self, + _: InitializeParams, + ) -> realhydroper_lsp::jsonrpc::Result { Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), @@ -54,8 +58,10 @@ impl LanguageServer for LanguageServerBackend { inter_file_dependencies: true, workspace_diagnostics: false, identifier: None, - work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None } - } + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, )), inlay_hint_provider: Some(OneOf::Left(true)), document_link_provider: Some(DocumentLinkOptions { @@ -90,7 +96,8 @@ impl LanguageServer for LanguageServerBackend { self.update_file_contents( params.text_document.uri.to_file_path().unwrap(), params.text_document.text, - ).await; + ) + .await; } async fn did_change(&self, params: DidChangeTextDocumentParams) { @@ -100,14 +107,23 @@ impl LanguageServer for LanguageServerBackend { format!("File changed: {}", params.text_document.uri), ) .await; - let new_content = params.content_changes.into_iter().next().map(|change| change.text).unwrap_or_default(); + let new_content = params + .content_changes + .into_iter() + .next() + .map(|change| change.text) + .unwrap_or_default(); self.update_file_contents( params.text_document.uri.to_file_path().unwrap(), new_content, - ).await; + ) + .await; } - async fn completion(&self, params: CompletionParams) -> realhydroper_lsp::jsonrpc::Result> { + async fn completion( + &self, + params: CompletionParams, + ) -> realhydroper_lsp::jsonrpc::Result> { self.client .log_message(MessageType::INFO, "completion!") .await; @@ -123,34 +139,42 @@ impl LanguageServer for LanguageServerBackend { let variables = self.find_variable_starting_with(&prefix); - let items: Vec = variables.iter().map(|var| { - CompletionItem { + let items: Vec = variables + .iter() + .map(|var| CompletionItem { label: var.name.clone(), kind: Some(CompletionItemKind::VARIABLE), detail: Some(format!( "{} {}: {}", var.shape, var.name, - var.var_type.as_ref().unwrap()) - ), + var.var_type.as_ref().unwrap() + )), documentation: None, ..Default::default() - } - }).collect(); + }) + .collect(); Ok(Some(CompletionResponse::Array(items))) } - async fn hover(&self, params: HoverParams) -> realhydroper_lsp::jsonrpc::Result> { let expression = self.get_expression_at_position(¶ms.text_document_position_params); if let Some(expression) = expression { Ok(match expression.data { // show variable type info on hover - DatexExpressionData::VariableDeclaration(VariableDeclaration { name, id: Some(id), .. }) | - DatexExpressionData::VariableAssignment(VariableAssignment { name, id: Some(id), .. }) | - DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { + DatexExpressionData::VariableDeclaration(VariableDeclaration { + name, + id: Some(id), + .. + }) + | DatexExpressionData::VariableAssignment(VariableAssignment { + name, + id: Some(id), + .. + }) + | DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { let variable_metadata = self.get_variable_by_id(id).unwrap(); Some(self.get_language_string_hover(&format!( "{} {}: {}", @@ -163,67 +187,66 @@ impl LanguageServer for LanguageServerBackend { // show value info on hover for literals DatexExpressionData::Integer(integer) => { Some(self.get_language_string_hover(&format!("{}", integer))) - }, + } DatexExpressionData::TypedInteger(typed_integer) => { Some(self.get_language_string_hover(&format!("{}", typed_integer))) - }, + } DatexExpressionData::Decimal(decimal) => { Some(self.get_language_string_hover(&format!("{}", decimal))) - }, + } DatexExpressionData::TypedDecimal(typed_decimal) => { Some(self.get_language_string_hover(&format!("{}", typed_decimal))) - }, + } DatexExpressionData::Boolean(boolean) => { Some(self.get_language_string_hover(&format!("{}", boolean))) - }, + } DatexExpressionData::Text(text) => { Some(self.get_language_string_hover(&format!("\"{}\"", text))) - }, + } DatexExpressionData::Endpoint(endpoint) => { Some(self.get_language_string_hover(&format!("{}", endpoint))) - }, - DatexExpressionData::Null => { - Some(self.get_language_string_hover("null")) - }, + } + DatexExpressionData::Null => Some(self.get_language_string_hover("null")), _ => None, }) - } - else { + } else { Err(realhydroper_lsp::jsonrpc::Error { code: ErrorCode::ParseError, message: Cow::from("No AST available"), - data: None + data: None, }) } } - async fn inlay_hint(&self, params: InlayHintParams) -> realhydroper_lsp::jsonrpc::Result>> { + async fn inlay_hint( + &self, + params: InlayHintParams, + ) -> realhydroper_lsp::jsonrpc::Result>> { // show type hints for variables let type_hints = self .get_type_hints(params.text_document.uri.to_file_path().unwrap()) .unwrap() .into_iter() - .map(|hint| { - InlayHint { - position: hint.0, - label: InlayHintLabel::String(format!(": {}", hint.1.unwrap())), - kind: Some(InlayHintKind::TYPE), - text_edits: None, - tooltip: None, - padding_left: Some(true), - padding_right: None, - data: None, - } + .map(|hint| InlayHint { + position: hint.0, + label: InlayHintLabel::String(format!(": {}", hint.1.unwrap())), + kind: Some(InlayHintKind::TYPE), + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: None, + data: None, }) .collect(); - Ok(Some(type_hints)) } - - async fn goto_definition(&self, params: GotoDefinitionParams) -> realhydroper_lsp::jsonrpc::Result> { + async fn goto_definition( + &self, + params: GotoDefinitionParams, + ) -> realhydroper_lsp::jsonrpc::Result> { let expression = self.get_expression_at_position(¶ms.text_document_position_params); if let Some(expression) = expression { match expression.data { @@ -232,36 +255,40 @@ impl LanguageServer for LanguageServerBackend { let file_path = uri.to_file_path().unwrap(); let mut workspace = self.compiler_workspace.borrow_mut(); let file = workspace.get_file_mut(&file_path).unwrap(); - if let Some(RichAst {ast: Some(ast), ..}) = &mut file.rich_ast { + if let Some(RichAst { ast: Some(ast), .. }) = &mut file.rich_ast { let mut finder = VariableDeclarationFinder::new(id); finder.visit_datex_expression(ast); - Ok( - finder.variable_declaration_position - .map(|position| GotoDefinitionResponse::Scalar( - Location { uri, range: self.convert_byte_range_to_document_range(&position, &file.content)} - )) - ) - } - else { + Ok(finder.variable_declaration_position.map(|position| { + GotoDefinitionResponse::Scalar(Location { + uri, + range: self + .convert_byte_range_to_document_range(&position, &file.content), + }) + })) + } else { Ok(None) } } - _ => Ok(None) + _ => Ok(None), } - } - else { + } else { Err(Error::internal_error()) } } - async fn document_link(&self, params: DocumentLinkParams) -> realhydroper_lsp::jsonrpc::Result>> { - // TODO - Ok(Some(vec![])) + async fn document_link( + &self, + params: DocumentLinkParams, + ) -> realhydroper_lsp::jsonrpc::Result>> { + // TODO + Ok(Some(vec![])) } // get error diagnostics - async fn diagnostic(&self, params: DocumentDiagnosticParams) -> realhydroper_lsp::jsonrpc::Result - { + async fn diagnostic( + &self, + params: DocumentDiagnosticParams, + ) -> realhydroper_lsp::jsonrpc::Result { self.client .log_message(MessageType::INFO, "diagnostics!") .await; @@ -279,19 +306,21 @@ impl LanguageServer for LanguageServerBackend { DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { related_documents: None, full_document_diagnostic_report: report, - })) - ) + }), + )) } } - impl LanguageServerBackend { fn get_language_string_hover(&self, text: &str) -> Hover { let contents = HoverContents::Scalar(MarkedString::LanguageString(LanguageString { language: "datex".to_string(), value: text.to_string(), })); - Hover { contents, range: None } + Hover { + contents, + range: None, + } } fn get_diagnostics_for_file(&self, file_path: &std::path::Path) -> Vec { @@ -315,4 +344,4 @@ impl LanguageServerBackend { } diagnostics } -} \ No newline at end of file +} diff --git a/src/lsp/type_hint_collector.rs b/src/lsp/type_hint_collector.rs index 024badf..05a2e61 100644 --- a/src/lsp/type_hint_collector.rs +++ b/src/lsp/type_hint_collector.rs @@ -1,19 +1,23 @@ -use std::ops::Range; -use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; use datex_core::ast::structs::VariableId; +use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; +use datex_core::visitor::VisitAction; use datex_core::visitor::expression::ExpressionVisitor; use datex_core::visitor::type_expression::TypeExpressionVisitor; -use datex_core::visitor::VisitAction; +use std::ops::Range; #[derive(Default)] pub struct TypeHintCollector { - pub type_hints: Vec<(usize, VariableId)> + pub type_hints: Vec<(usize, VariableId)>, } impl TypeExpressionVisitor<()> for TypeHintCollector {} impl ExpressionVisitor<()> for TypeHintCollector { - fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &Range) -> Result, ()> { + fn visit_variable_declaration( + &mut self, + var_decl: &mut VariableDeclaration, + span: &Range, + ) -> Result, ()> { if var_decl.type_annotation.is_none() { let expr_start = var_decl.init_expression.span.start; // TODO: improve @@ -21,4 +25,4 @@ impl ExpressionVisitor<()> for TypeHintCollector { } Ok(VisitAction::VisitChildren) } -} \ No newline at end of file +} diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 9ef2a93..dc1a274 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -1,5 +1,9 @@ -use std::path::PathBuf; -use datex_core::ast::structs::expression::{DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, VariableAssignment, VariableDeclaration}; +use crate::lsp::type_hint_collector::TypeHintCollector; +use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; +use datex_core::ast::structs::expression::{ + DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, + VariableAssignment, VariableDeclaration, +}; use datex_core::compiler::error::DetailedCompilerErrors; use datex_core::precompiler::precompiled_ast::VariableMetadata; use datex_core::types::type_container::TypeContainer; @@ -8,20 +12,14 @@ use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; use datex_core::values::core_values::endpoint::Endpoint; use datex_core::values::core_values::integer::Integer; use datex_core::values::core_values::integer::typed_integer::TypedInteger; +use datex_core::visitor::VisitAction; use datex_core::visitor::expression::ExpressionVisitor; use datex_core::visitor::type_expression::TypeExpressionVisitor; -use datex_core::visitor::VisitAction; use realhydroper_lsp::lsp_types::{MessageType, Position, Range, TextDocumentPositionParams}; -use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; -use crate::lsp::type_hint_collector::TypeHintCollector; +use std::path::PathBuf; impl LanguageServerBackend { - - pub async fn update_file_contents( - &self, - path: PathBuf, - content: String, - ) { + pub async fn update_file_contents(&self, path: PathBuf, content: String) { let mut compiler_workspace = self.compiler_workspace.borrow_mut(); let file = compiler_workspace.load_file(path.clone(), content.clone()); // Clear previous errors for this file @@ -41,22 +39,21 @@ impl LanguageServerBackend { } if let Some(rich_ast) = &file.rich_ast { self.client - .log_message( - MessageType::INFO, - format!("AST: {:#?}", rich_ast.ast), - ) + .log_message(MessageType::INFO, format!("AST: {:#?}", rich_ast.ast)) .await; self.client .log_message( MessageType::INFO, - format!("AST metadata: {:#?}", *rich_ast.metadata.borrow()) + format!("AST metadata: {:#?}", *rich_ast.metadata.borrow()), ) .await; } } - - - pub(crate) fn get_type_hints(&self, file_path: PathBuf) -> Option)>> { + + pub(crate) fn get_type_hints( + &self, + file_path: PathBuf, + ) -> Option)>> { let mut workspace = self.compiler_workspace.borrow_mut(); let file = workspace.get_file_mut(&file_path).unwrap(); if let Some(rich_ast) = &mut file.rich_ast { @@ -67,16 +64,22 @@ impl LanguageServerBackend { collector .type_hints .into_iter() - .map(|hint| + .map(|hint| { ( self.byte_offset_to_position(hint.0, &file.content).unwrap(), - rich_ast.metadata.borrow().variables.get(hint.1).unwrap().var_type.clone() + rich_ast + .metadata + .borrow() + .variables + .get(hint.1) + .unwrap() + .var_type + .clone(), ) - ) - .collect() + }) + .collect(), ) - } - else { + } else { None } } @@ -88,16 +91,26 @@ impl LanguageServerBackend { } /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. - fn collect_compiler_errors(&self, errors: &DetailedCompilerErrors, path: PathBuf, file_content: &String) { + fn collect_compiler_errors( + &self, + errors: &DetailedCompilerErrors, + path: PathBuf, + file_content: &String, + ) { let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); let file_errors = spanned_compiler_errors.entry(path.clone()).or_default(); for error in &errors.errors { - let range = error.span.as_ref().map(|span| { - self.convert_byte_range_to_document_range(span, file_content) - }).unwrap_or_else(|| { - self.convert_byte_range_to_document_range(&(0..file_content.len()), file_content) - }); + let range = error + .span + .as_ref() + .map(|span| self.convert_byte_range_to_document_range(span, file_content)) + .unwrap_or_else(|| { + self.convert_byte_range_to_document_range( + &(0..file_content.len()), + file_content, + ) + }); file_errors.push(SpannedCompilerError { range, error: error.error.clone(), @@ -132,7 +145,6 @@ impl LanguageServerBackend { return Some(v); } } - } None } @@ -145,25 +157,49 @@ impl LanguageServerBackend { let file_path = position.text_document.uri.to_file_path().unwrap(); let file_content = &workspace.get_file(&file_path).unwrap().content; - Self::line_char_to_byte_index(file_content, position.position.line as usize, position.position.character as usize).unwrap_or(0) + Self::line_char_to_byte_index( + file_content, + position.position.line as usize, + position.position.character as usize, + ) + .unwrap_or(0) } /// Converts a byte range (start, end) to a document Range (start Position, end Position) in the file content. - pub fn convert_byte_range_to_document_range(&self, span: &std::ops::Range, file_content: &String) -> Range { - let start = self.byte_offset_to_position(span.start, file_content).unwrap_or(Position { line: 0, character: 0 }); - let end = self.byte_offset_to_position(span.end, file_content).unwrap_or(Position { line: 0, character: 0 }); + pub fn convert_byte_range_to_document_range( + &self, + span: &std::ops::Range, + file_content: &String, + ) -> Range { + let start = self + .byte_offset_to_position(span.start, file_content) + .unwrap_or(Position { + line: 0, + character: 0, + }); + let end = self + .byte_offset_to_position(span.end, file_content) + .unwrap_or(Position { + line: 0, + character: 0, + }); Range { start, end } } /// Converts a byte offset to an LSP position (line and character) in the file content. /// TODO: check if this is correct, generated with copilot - pub fn byte_offset_to_position(&self, byte_offset: usize, file_content: &String) -> Option { + pub fn byte_offset_to_position( + &self, + byte_offset: usize, + file_content: &String, + ) -> Option { let mut current_offset = 0; for (line_idx, line) in file_content.lines().enumerate() { let line_length = line.len() + 1; // +1 for the newline character if current_offset + line_length > byte_offset { // The byte offset is within this line - let char_offset = line.char_indices() + let char_offset = line + .char_indices() .find(|(i, _)| current_offset + i >= byte_offset) .map(|(i, _)| i) .unwrap_or(line.len()); @@ -194,7 +230,10 @@ impl LanguageServerBackend { } /// Retrieves the DatexExpression AST node at the given byte offset. - pub fn get_expression_at_position(&self, position: &TextDocumentPositionParams) -> Option { + pub fn get_expression_at_position( + &self, + position: &TextDocumentPositionParams, + ) -> Option { let byte_offset = self.position_to_byte_offset(position); let mut workspace = self.compiler_workspace.borrow_mut(); let file_path = position.text_document.uri.to_file_path().unwrap(); @@ -202,21 +241,16 @@ impl LanguageServerBackend { let ast = rich_ast.ast.as_mut().unwrap(); let mut finder = ExpressionFinder::new(byte_offset); finder.visit_datex_expression(ast); - finder.found_expr.map(|e| - DatexExpression { - span: e.1, - data: e.0, - wrapped: None - } - ) - } - else { + finder.found_expr.map(|e| DatexExpression { + span: e.1, + data: e.0, + wrapped: None, + }) + } else { None } - } - /// Converts a (line, character) pair to a byte index in the given text. /// Lines and characters are zero-indexed. /// Returns None if the line or character is out of bounds. @@ -244,7 +278,6 @@ impl LanguageServerBackend { } } - /// Visitor that finds the most specific DatexExpression containing a given byte position. /// If multiple expressions contain the position, the one with the smallest span is chosen. struct ExpressionFinder { @@ -260,11 +293,14 @@ impl ExpressionFinder { } } - /// Checks if the given span includes the search position. /// If it does, updates found_expr if this expression is more specific (smaller span). /// Returns true if the span includes the search position, false otherwise. - fn match_span(&mut self, span: &std::ops::Range, expr_data: DatexExpressionData) -> Result, ()> { + fn match_span( + &mut self, + span: &std::ops::Range, + expr_data: DatexExpressionData, + ) -> Result, ()> { if span.start <= self.search_pos && self.search_pos <= span.end { // If we already found an expression, only replace it if this one is smaller (more specific) if let Some((_, existing_expr_span)) = &self.found_expr { @@ -275,8 +311,7 @@ impl ExpressionFinder { self.found_expr = Some((expr_data, span.clone())); } Ok(VisitAction::VisitChildren) - } - else { + } else { Err(()) } } @@ -285,59 +320,123 @@ impl ExpressionFinder { impl TypeExpressionVisitor<()> for ExpressionFinder {} impl ExpressionVisitor<()> for ExpressionFinder { - fn visit_statements(&mut self, stmts: &mut Statements, span: &std::ops::Range) -> Result, ()> { + fn visit_statements( + &mut self, + stmts: &mut Statements, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Statements(stmts.clone())) } - fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &std::ops::Range) -> Result, ()> { - self.match_span(span, DatexExpressionData::VariableDeclaration(var_decl.clone())) + fn visit_variable_declaration( + &mut self, + var_decl: &mut VariableDeclaration, + span: &std::ops::Range, + ) -> Result, ()> { + self.match_span( + span, + DatexExpressionData::VariableDeclaration(var_decl.clone()), + ) } - fn visit_variable_assignment(&mut self, var_assign: &mut VariableAssignment, span: &std::ops::Range) -> Result, ()> { - self.match_span(span, DatexExpressionData::VariableAssignment(var_assign.clone())) + fn visit_variable_assignment( + &mut self, + var_assign: &mut VariableAssignment, + span: &std::ops::Range, + ) -> Result, ()> { + self.match_span( + span, + DatexExpressionData::VariableAssignment(var_assign.clone()), + ) } - fn visit_variable_access(&mut self, var_access: &mut VariableAccess, span: &std::ops::Range) -> Result, ()> { - self.match_span(span, DatexExpressionData::VariableAccess(var_access.clone())) + fn visit_variable_access( + &mut self, + var_access: &mut VariableAccess, + span: &std::ops::Range, + ) -> Result, ()> { + self.match_span( + span, + DatexExpressionData::VariableAccess(var_access.clone()), + ) } - fn visit_list(&mut self, list: &mut List, span: &std::ops::Range) -> Result, ()> { - self.match_span(span, DatexExpressionData::List(list.clone())) + fn visit_list( + &mut self, + list: &mut List, + span: &std::ops::Range, + ) -> Result, ()> { + self.match_span(span, DatexExpressionData::List(list.clone())) } - fn visit_map(&mut self, map: &mut Map, span: &std::ops::Range) -> Result, ()> { - self.match_span(span, DatexExpressionData::Map(map.clone())) + fn visit_map( + &mut self, + map: &mut Map, + span: &std::ops::Range, + ) -> Result, ()> { + self.match_span(span, DatexExpressionData::Map(map.clone())) } - fn visit_integer(&mut self, value: &mut Integer, span: &std::ops::Range) -> Result, ()> { + fn visit_integer( + &mut self, + value: &mut Integer, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Integer(value.clone())) } - fn visit_typed_integer(&mut self, value: &mut TypedInteger, span: &std::ops::Range) -> Result, ()> { + fn visit_typed_integer( + &mut self, + value: &mut TypedInteger, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::TypedInteger(value.clone())) } - fn visit_decimal(&mut self, value: &mut Decimal, span: &std::ops::Range) -> Result, ()> { + fn visit_decimal( + &mut self, + value: &mut Decimal, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Decimal(value.clone())) } - fn visit_typed_decimal(&mut self, value: &mut TypedDecimal, span: &std::ops::Range) -> Result, ()> { + fn visit_typed_decimal( + &mut self, + value: &mut TypedDecimal, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::TypedDecimal(value.clone())) } - fn visit_text(&mut self, value: &mut String, span: &std::ops::Range) -> Result, ()> { + fn visit_text( + &mut self, + value: &mut String, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Text(value.clone())) } - fn visit_boolean(&mut self, value: &mut bool, span: &std::ops::Range) -> Result, ()> { + fn visit_boolean( + &mut self, + value: &mut bool, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Boolean(*value)) } - fn visit_endpoint(&mut self, value: &mut Endpoint, span: &std::ops::Range) -> Result, ()> { + fn visit_endpoint( + &mut self, + value: &mut Endpoint, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Endpoint(value.clone())) } - fn visit_null(&mut self, span: &std::ops::Range) -> Result, ()> { + fn visit_null( + &mut self, + span: &std::ops::Range, + ) -> Result, ()> { self.match_span(span, DatexExpressionData::Null) } -} \ No newline at end of file +} diff --git a/src/lsp/variable_declaration_finder.rs b/src/lsp/variable_declaration_finder.rs index 9b601ae..36a8fbe 100644 --- a/src/lsp/variable_declaration_finder.rs +++ b/src/lsp/variable_declaration_finder.rs @@ -1,32 +1,38 @@ -use std::ops::Range; use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; +use datex_core::visitor::VisitAction; use datex_core::visitor::expression::ExpressionVisitor; use datex_core::visitor::type_expression::TypeExpressionVisitor; -use datex_core::visitor::VisitAction; +use std::ops::Range; #[derive(Default)] pub struct VariableDeclarationFinder { pub var_id: usize, - pub variable_declaration_position: Option> + pub variable_declaration_position: Option>, } impl VariableDeclarationFinder { pub fn new(var_id: usize) -> Self { - VariableDeclarationFinder { var_id, variable_declaration_position: None } + VariableDeclarationFinder { + var_id, + variable_declaration_position: None, + } } } impl TypeExpressionVisitor<()> for VariableDeclarationFinder {} impl ExpressionVisitor<()> for VariableDeclarationFinder { - fn visit_variable_declaration(&mut self, var_decl: &mut VariableDeclaration, span: &Range) -> Result, ()> { + fn visit_variable_declaration( + &mut self, + var_decl: &mut VariableDeclaration, + span: &Range, + ) -> Result, ()> { if var_decl.id == Some(self.var_id) { self.variable_declaration_position = Some(span.clone()); // early abort Err(()) - } - else { + } else { Ok(VisitAction::VisitChildren) } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 7ed0b96..7d64bb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ +use datex_core::compiler::workspace::CompilerWorkspace; use datex_core::crypto::crypto_native::CryptoNative; +use datex_core::decompiler::{DecompileOptions, decompile_value}; use datex_core::run_async; use datex_core::runtime::global_context::{DebugFlags, GlobalContext, set_global_context}; use datex_core::runtime::{Runtime, RuntimeConfig}; use datex_core::utils::time_native::TimeNative; +use datex_core::values::core_values::endpoint::Endpoint; use std::path::PathBuf; use std::sync::Arc; -use datex_core::compiler::workspace::CompilerWorkspace; -use datex_core::decompiler::{decompile_value, DecompileOptions}; -use datex_core::values::core_values::endpoint::Endpoint; mod command_line_args; mod lsp; @@ -31,8 +31,7 @@ async fn main() { println!("datex-cli {}", env!("CARGO_PKG_VERSION")); println!("datex {}", env!("DEP_DATEX_CORE_VERSION")); return; - } - else { + } else { command.command }; @@ -45,7 +44,9 @@ async fn main() { let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default())); let compiler_workspace = CompilerWorkspace::new(runtime); - let (service, socket) = LspService::new(|client| LanguageServerBackend::new(client, compiler_workspace)); + let (service, socket) = LspService::new(|client| { + LanguageServerBackend::new(client, compiler_workspace) + }); Server::new(stdin, stdout, socket).serve(service).await; } Subcommands::Run(run) => { diff --git a/src/repl.rs b/src/repl.rs index 880bfdd..205599a 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,4 +1,4 @@ -use crate::utils::config::{create_runtime_with_config, ConfigError}; +use crate::utils::config::{ConfigError, create_runtime_with_config}; use datex_core::crypto::crypto_native::CryptoNative; use datex_core::decompiler::{DecompileOptions, apply_syntax_highlighting, decompile_value}; use datex_core::run_async; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index a105933..ef68c36 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1 +1 @@ -pub mod config; \ No newline at end of file +pub mod config; diff --git a/src/workbench/views/comhub.rs b/src/workbench/views/comhub.rs index 0bb3129..7f49b9f 100644 --- a/src/workbench/views/comhub.rs +++ b/src/workbench/views/comhub.rs @@ -59,7 +59,9 @@ impl Widget for &ComHub { (match &socket.endpoint { Some(endpoint) => endpoint.to_string(), None => "unknown".to_string(), - }).to_string().into(), + }) + .to_string() + .into(), ])); } } diff --git a/src/workbench/views/metadata.rs b/src/workbench/views/metadata.rs index e74a54c..2bb2b57 100644 --- a/src/workbench/views/metadata.rs +++ b/src/workbench/views/metadata.rs @@ -20,13 +20,15 @@ impl Widget for &Metadata { .borders(Borders::ALL) .border_style(Style::default().fg(Color::White)); - let lines = vec![ Line::from(vec![ "Endpoint: ".into(), self.runtime.endpoint().to_string().bold(), ]), - Line::from(vec!["Version: ".into(), self.runtime.version.clone().bold()]), + Line::from(vec![ + "Version: ".into(), + self.runtime.version.clone().bold(), + ]), // Line::from(vec![ // "Allocated pointers: ".into(), // self.runtime diff --git a/src/workbench/workbench.rs b/src/workbench/workbench.rs index 352e4e4..5376120 100644 --- a/src/workbench/workbench.rs +++ b/src/workbench/workbench.rs @@ -24,7 +24,7 @@ impl Workbench { runtime: runtime.clone(), }; let comhub = ComHub { - runtime: runtime.clone() + runtime: runtime.clone(), }; Workbench { @@ -40,7 +40,7 @@ impl Workbench { while !self.exit { terminal.draw(|frame| self.draw(frame))?; self.handle_events()?; - + // // add ptr to the runtime // let id = random_bytes_slice::<26>(); // runtime From fd6afde6a1922056ee73c223908ab6cb2bfd906c Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Fri, 31 Oct 2025 18:51:48 +0100 Subject: [PATCH 19/23] :bug: fix ExpressionFinder --- src/lsp/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index dc1a274..98fb3bc 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -312,7 +312,7 @@ impl ExpressionFinder { } Ok(VisitAction::VisitChildren) } else { - Err(()) + Ok(VisitAction::SkipChildren) } } } From 2fe5f02621eadff4544190e0d415f47560e3f0c5 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Tue, 4 Nov 2025 16:31:28 +0100 Subject: [PATCH 20/23] update datex-core changes --- Cargo.lock | 242 +++++++++++++---------------------------------- src/lsp/mod.rs | 2 +- src/lsp/utils.rs | 2 +- src/main.rs | 4 +- 4 files changed, 72 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b877108..e6cd36a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -181,29 +181,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -214,7 +192,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -231,7 +209,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -329,16 +307,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "bigint" -version = "4.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" -dependencies = [ - "byteorder", - "crunchy", -] - [[package]] name = "bincode" version = "1.3.3" @@ -366,10 +334,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "376404e55ec40d0d6f8b4b7df3f87b87954bd987f0cf9a7207ea3b6ea5c9add4" dependencies = [ "either", - "owo-colors", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -565,7 +532,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -693,12 +660,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -754,7 +715,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -788,7 +749,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn", ] [[package]] @@ -802,7 +763,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn", ] [[package]] @@ -813,7 +774,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -824,7 +785,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -852,37 +813,30 @@ version = "0.0.7" source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-workspace#4d053538e15aef0ef5823074a9d4cc21ad7d2714" dependencies = [ "ariadne", - "async-stream", "async-trait", "axum", "bigdecimal", - "bigint", "binrw", - "byteorder", "bytes", "cfg-if", "chumsky", "datex_macros", - "default-args", "flexi_logger", + "foldhash 0.2.0", "futures", "futures-core", - "futures-timer", "futures-util", - "gloo-timers 0.3.0", + "gloo-timers", "hex", "hyper", "indexmap 2.11.4", - "indoc", "internment", "itertools 0.14.0", - "lazy_static", "log", "logos", "modular-bitfield", "mopa", "nostd", - "ntest_timeout", "num", "num-bigint", "num-integer", @@ -890,7 +844,6 @@ dependencies = [ "num_enum", "openssl", "ordered-float", - "pad", "pretty", "rand", "ringmap", @@ -899,6 +852,7 @@ dependencies = [ "serde_json", "serde_with", "serialport", + "spin", "strum 0.27.2", "strum_macros 0.27.2", "syntect", @@ -938,18 +892,7 @@ source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-work dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "default-args" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b278f5a88847c427cdc4c3408916fb2135f179444e095cd207294d4c28c5e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -1007,7 +950,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1170,6 +1113,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1250,7 +1199,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1265,16 +1214,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers 0.2.6", - "send_wrapper", -] - [[package]] name = "futures-util" version = "0.3.31" @@ -1337,18 +1276,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "gloo-timers" version = "0.3.0" @@ -1405,7 +1332,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1722,7 +1649,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1912,7 +1839,7 @@ dependencies = [ "quote", "regex-syntax 0.8.8", "rustc_version", - "syn 2.0.106", + "syn", ] [[package]] @@ -2022,9 +1949,9 @@ dependencies = [ [[package]] name = "modular-bitfield" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0d5274763b5572c8f29dadb5cd0bc59de64c805de433c1b556075f733b0a1a" +checksum = "47a586be3f2f7e70a9d302c621447dba612d42069f3901258b2cf8ce96d855b1" dependencies = [ "modular-bitfield-impl", "static_assertions", @@ -2032,13 +1959,13 @@ dependencies = [ [[package]] name = "modular-bitfield-impl" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eec4327f127d4d18c54c8bfbf7b05d74cc9a1befdcc6283a241238ffbc84c6" +checksum = "8462d3cc74eaf4194f6c0bd7b18c6f3fa6293297f4bdb60fe4c4b022ea366e12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2114,22 +2041,11 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a02781f0897bf3bead576425bbceac9d61ffc7e4696593860e365c8ca941c870" dependencies = [ + "hashbrown 0.15.5", "memchr", "rustversion", ] -[[package]] -name = "ntest_timeout" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc7c92f190c97f79b4a332f5e81dcf68c8420af2045c936c9be0bc9de6f63b5" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2216,6 +2132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2237,7 +2154,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2290,7 +2207,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2320,12 +2237,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "owo-colors" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" - [[package]] name = "p256" version = "0.13.2" @@ -2350,15 +2261,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width 0.1.14", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -2430,7 +2332,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2684,7 +2586,7 @@ checksum = "4bc58ab9d2704193ee51a20bb227fc85934ed0be557516d9322b40fa8d7c00ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2713,7 +2615,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3037,12 +2939,6 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - [[package]] name = "serde" version = "1.0.228" @@ -3079,7 +2975,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3090,7 +2986,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3125,7 +3021,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3168,7 +3064,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3306,6 +3202,15 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -3362,7 +3267,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn", ] [[package]] @@ -3374,7 +3279,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3411,17 +3316,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.106" @@ -3447,7 +3341,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3510,7 +3404,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3521,7 +3415,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3590,7 +3484,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3734,7 +3628,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3768,7 +3662,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn", ] [[package]] @@ -3994,7 +3888,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-shared", ] @@ -4029,7 +3923,7 @@ checksum = "4a518014843a19e2dbbd0ed5dfb6b99b23fb886b14e6192a00803a3e14c552b0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4314,7 +4208,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4325,7 +4219,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4612,7 +4506,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -4633,7 +4527,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4653,7 +4547,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -4674,7 +4568,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4707,7 +4601,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[patch.unused]] diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 3861238..8843ca5 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -8,7 +8,7 @@ use datex_core::ast::structs::expression::{ }; use datex_core::compiler::error::CompilerError; use datex_core::compiler::workspace::CompilerWorkspace; -use datex_core::precompiler::precompiled_ast::RichAst; +use datex_core::compiler::precompiler::precompiled_ast::RichAst; use datex_core::types::type_container::TypeContainer; use datex_core::visitor::expression::ExpressionVisitor; use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index 98fb3bc..cb3d224 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -5,7 +5,7 @@ use datex_core::ast::structs::expression::{ VariableAssignment, VariableDeclaration, }; use datex_core::compiler::error::DetailedCompilerErrors; -use datex_core::precompiler::precompiled_ast::VariableMetadata; +use datex_core::compiler::precompiler::precompiled_ast::VariableMetadata; use datex_core::types::type_container::TypeContainer; use datex_core::values::core_values::decimal::Decimal; use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; diff --git a/src/main.rs b/src/main.rs index 7d64bb3..1fb666d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use datex_core::crypto::crypto_native::CryptoNative; use datex_core::decompiler::{DecompileOptions, decompile_value}; use datex_core::run_async; use datex_core::runtime::global_context::{DebugFlags, GlobalContext, set_global_context}; -use datex_core::runtime::{Runtime, RuntimeConfig}; +use datex_core::runtime::{AsyncContext, Runtime, RuntimeConfig}; use datex_core::utils::time_native::TimeNative; use datex_core::values::core_values::endpoint::Endpoint; use std::path::PathBuf; @@ -41,7 +41,7 @@ async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default())); + let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default()), AsyncContext::new()); let compiler_workspace = CompilerWorkspace::new(runtime); let (service, socket) = LspService::new(|client| { From db9844882ee00b08ca5948c2a50860086c2b4836 Mon Sep 17 00:00:00 2001 From: Benedikt Strehle Date: Sun, 9 Nov 2025 23:02:09 +0100 Subject: [PATCH 21/23] update datex-core changes --- Cargo.lock | 32 +------------------------------- src/lsp/mod.rs | 2 +- src/lsp/utils.rs | 5 +++-- 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6cd36a..1a08e70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -822,7 +822,6 @@ dependencies = [ "chumsky", "datex_macros", "flexi_logger", - "foldhash 0.2.0", "futures", "futures-core", "futures-util", @@ -836,7 +835,6 @@ dependencies = [ "logos", "modular-bitfield", "mopa", - "nostd", "num", "num-bigint", "num-integer", @@ -852,7 +850,6 @@ dependencies = [ "serde_json", "serde_with", "serialport", - "spin", "strum 0.27.2", "strum_macros 0.27.2", "syntect", @@ -1113,12 +1110,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "foreign-types" version = "0.3.2" @@ -1332,7 +1323,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.1.5", + "foldhash", ] [[package]] @@ -2035,17 +2026,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nostd" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a02781f0897bf3bead576425bbceac9d61ffc7e4696593860e365c8ca941c870" -dependencies = [ - "hashbrown 0.15.5", - "memchr", - "rustversion", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2132,7 +2112,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -3202,15 +3181,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs index 8843ca5..3924b93 100644 --- a/src/lsp/mod.rs +++ b/src/lsp/mod.rs @@ -255,7 +255,7 @@ impl LanguageServer for LanguageServerBackend { let file_path = uri.to_file_path().unwrap(); let mut workspace = self.compiler_workspace.borrow_mut(); let file = workspace.get_file_mut(&file_path).unwrap(); - if let Some(RichAst { ast: Some(ast), .. }) = &mut file.rich_ast { + if let Some(RichAst { ast, .. }) = &mut file.rich_ast { let mut finder = VariableDeclarationFinder::new(id); finder.visit_datex_expression(ast); Ok(finder.variable_declaration_position.map(|position| { diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs index cb3d224..968bcd7 100644 --- a/src/lsp/utils.rs +++ b/src/lsp/utils.rs @@ -57,7 +57,7 @@ impl LanguageServerBackend { let mut workspace = self.compiler_workspace.borrow_mut(); let file = workspace.get_file_mut(&file_path).unwrap(); if let Some(rich_ast) = &mut file.rich_ast { - let ast = rich_ast.ast.as_mut().unwrap(); + let ast = &mut rich_ast.ast; let mut collector = TypeHintCollector::default(); collector.visit_datex_expression(ast); Some( @@ -238,13 +238,14 @@ impl LanguageServerBackend { let mut workspace = self.compiler_workspace.borrow_mut(); let file_path = position.text_document.uri.to_file_path().unwrap(); if let Some(rich_ast) = &mut workspace.get_file_mut(&file_path).unwrap().rich_ast { - let ast = rich_ast.ast.as_mut().unwrap(); + let ast = &mut rich_ast.ast; let mut finder = ExpressionFinder::new(byte_offset); finder.visit_datex_expression(ast); finder.found_expr.map(|e| DatexExpression { span: e.1, data: e.0, wrapped: None, + ty: None, }) } else { None From f8de32f4466b10c27e32bb0e8c45353ebbd0daeb Mon Sep 17 00:00:00 2001 From: Jonas Strehle Date: Tue, 11 Nov 2025 21:49:31 +0100 Subject: [PATCH 22/23] update datex-core dependency to branch release/0.0.7 --- Cargo.lock | 239 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 7 +- 2 files changed, 121 insertions(+), 125 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a08e70..a82f84a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -296,9 +296,9 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bigdecimal" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" +checksum = "560f42649de9fa436b73517378a147ec21f6c997a546581df4b4b31677828934" dependencies = [ "autocfg", "libm", @@ -362,9 +362,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "shlex", @@ -503,9 +503,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.49" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -513,9 +513,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.49" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -641,7 +641,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "mio", "parking_lot", @@ -810,7 +810,7 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "datex-core" version = "0.0.7" -source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-workspace#4d053538e15aef0ef5823074a9d4cc21ad7d2714" +source = "git+https://github.com/unyt-org/datex-core?branch=release%2F0.0.7#f02cd8700d7ab8979eb55be925e3c95a3d313ee2" dependencies = [ "ariadne", "async-trait", @@ -828,7 +828,7 @@ dependencies = [ "gloo-timers", "hex", "hyper", - "indexmap 2.11.4", + "indexmap 2.12.0", "internment", "itertools 0.14.0", "log", @@ -885,7 +885,7 @@ dependencies = [ [[package]] name = "datex_macros" version = "0.1.1" -source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Fcompiler-workspace#4d053538e15aef0ef5823074a9d4cc21ad7d2714" +source = "git+https://github.com/unyt-org/datex-core?branch=release%2F0.0.7#f02cd8700d7ab8979eb55be925e3c95a3d313ee2" dependencies = [ "proc-macro2", "quote", @@ -919,9 +919,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", "serde_core", @@ -1077,9 +1077,9 @@ checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -1364,11 +1364,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1419,9 +1419,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f" dependencies = [ "atomic-waker", "bytes", @@ -1480,9 +1480,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1493,9 +1493,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1506,11 +1506,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1521,42 +1520,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1604,9 +1599,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown 0.16.0", @@ -1616,9 +1611,12 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inout" @@ -1690,9 +1688,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -1786,9 +1784,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" @@ -1928,14 +1926,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2010,7 +2008,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -2116,9 +2114,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -2126,9 +2124,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2153,9 +2151,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -2165,11 +2163,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.74" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2197,9 +2195,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.110" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2349,7 +2347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64", - "indexmap 2.11.4", + "indexmap 2.12.0", "quick-xml", "serde", "time", @@ -2375,9 +2373,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2428,18 +2426,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] @@ -2505,7 +2503,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cassowary", "compact_str", "crossterm", @@ -2574,7 +2572,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -2727,7 +2725,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2740,7 +2738,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", @@ -2749,9 +2747,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", @@ -2763,18 +2761,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -2793,7 +2791,7 @@ version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "clipboard-win", "fd-lock", @@ -2847,9 +2845,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -2895,7 +2893,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -3017,17 +3015,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" +checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.12.0", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.1.0", "serde_core", "serde_json", "serde_with_macros", @@ -3036,9 +3034,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" +checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -3052,7 +3050,7 @@ version = "4.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21f60a586160667241d7702c420fc223939fb3c0bb8d3fac84f78768e8970dee" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "core-foundation 0.10.0", "core-foundation-sys", @@ -3106,9 +3104,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -3288,9 +3286,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -3421,9 +3419,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -3495,9 +3493,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -3521,7 +3519,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.0", "toml_datetime", "toml_parser", "winnow", @@ -3699,9 +3697,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -4398,9 +4396,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "x25519-dalek" @@ -4458,11 +4456,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4470,9 +4467,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -4543,9 +4540,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -4554,9 +4551,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -4565,15 +4562,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", "syn", ] - -[[patch.unused]] -name = "datex-core" -version = "0.0.7" diff --git a/Cargo.toml b/Cargo.toml index 6f19791..461eb6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,15 @@ build = "build.rs" [dependencies] clap = { version = "4.0.23", features = ["derive"] } -datex-core = { version = "0.0.7", git = "https://github.com/unyt-org/datex-core", branch = "feat/compiler-workspace", features = ["default", "debug"] } +datex-core = { version = "0.0.7", git = "https://github.com/unyt-org/datex-core", branch = "release/0.0.7", features = [ + "default", + "debug", +] } tokio = { version = "1.17.0", features = ["full"] } #tower-lsp = { version = "0.20.0", features = ["proposed"]} # using fork realhydroper-lsp because tower-lsp does not support local threading (single-threaded tokio runtime), which is required by the DATEX runtime -realhydroper-lsp = { version = "0.22.0", features = ["proposed"]} +realhydroper-lsp = { version = "0.22.0", features = ["proposed"] } serde = { version = "1.0", features = ["derive"] } rustyline = "15.0.0" ratatui = "0.29.0" From 6e7c91e11f655b642e8f4a43f87f3e0b9ac9fdc5 Mon Sep 17 00:00:00 2001 From: Jonas Strehle Date: Wed, 12 Nov 2025 20:37:16 +0100 Subject: [PATCH 23/23] move lsp to DATEX Core --- Cargo.lock | 17 +- Cargo.toml | 2 +- src/lsp/mod.rs | 347 ------------------- src/lsp/type_hint_collector.rs | 28 -- src/lsp/utils.rs | 443 ------------------------- src/lsp/variable_declaration_finder.rs | 38 --- src/main.rs | 17 +- 7 files changed, 19 insertions(+), 873 deletions(-) delete mode 100644 src/lsp/mod.rs delete mode 100644 src/lsp/type_hint_collector.rs delete mode 100644 src/lsp/utils.rs delete mode 100644 src/lsp/variable_declaration_finder.rs diff --git a/Cargo.lock b/Cargo.lock index a82f84a..abb905c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,9 +674,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "rand_core", @@ -810,7 +810,7 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "datex-core" version = "0.0.7" -source = "git+https://github.com/unyt-org/datex-core?branch=release%2F0.0.7#f02cd8700d7ab8979eb55be925e3c95a3d313ee2" +source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Flsp#c560f3172113a16fcefb25e206a929d7ce0f9be4" dependencies = [ "ariadne", "async-trait", @@ -844,6 +844,7 @@ dependencies = [ "ordered-float", "pretty", "rand", + "realhydroper-lsp", "ringmap", "serde", "serde-big-array", @@ -885,7 +886,7 @@ dependencies = [ [[package]] name = "datex_macros" version = "0.1.1" -source = "git+https://github.com/unyt-org/datex-core?branch=release%2F0.0.7#f02cd8700d7ab8979eb55be925e3c95a3d313ee2" +source = "git+https://github.com/unyt-org/datex-core?branch=feat%2Flsp#c560f3172113a16fcefb25e206a929d7ce0f9be4" dependencies = [ "proc-macro2", "quote", @@ -1225,9 +1226,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -4570,3 +4571,7 @@ dependencies = [ "quote", "syn", ] + +[[patch.unused]] +name = "datex-core" +version = "0.0.7" diff --git a/Cargo.toml b/Cargo.toml index 461eb6f..267926e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ build = "build.rs" [dependencies] clap = { version = "4.0.23", features = ["derive"] } -datex-core = { version = "0.0.7", git = "https://github.com/unyt-org/datex-core", branch = "release/0.0.7", features = [ +datex-core = { version = "0.0.7", git = "https://github.com/unyt-org/datex-core", branch = "feat/lsp", features = [ "default", "debug", ] } diff --git a/src/lsp/mod.rs b/src/lsp/mod.rs deleted file mode 100644 index 3924b93..0000000 --- a/src/lsp/mod.rs +++ /dev/null @@ -1,347 +0,0 @@ -mod type_hint_collector; -mod utils; -mod variable_declaration_finder; - -use crate::lsp::variable_declaration_finder::VariableDeclarationFinder; -use datex_core::ast::structs::expression::{ - DatexExpressionData, VariableAccess, VariableAssignment, VariableDeclaration, -}; -use datex_core::compiler::error::CompilerError; -use datex_core::compiler::workspace::CompilerWorkspace; -use datex_core::compiler::precompiler::precompiled_ast::RichAst; -use datex_core::types::type_container::TypeContainer; -use datex_core::visitor::expression::ExpressionVisitor; -use realhydroper_lsp::jsonrpc::{Error, ErrorCode}; -use realhydroper_lsp::lsp_types::*; -use realhydroper_lsp::{Client, LanguageServer}; -use std::borrow::Cow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::path::PathBuf; - -pub struct SpannedCompilerError { - pub range: Range, - pub error: CompilerError, -} - -pub struct LanguageServerBackend { - pub client: Client, - pub compiler_workspace: RefCell, - pub spanned_compiler_errors: RefCell>>, -} - -impl LanguageServerBackend { - pub fn new(client: Client, compiler_workspace: CompilerWorkspace) -> Self { - Self { - client, - compiler_workspace: RefCell::new(compiler_workspace), - spanned_compiler_errors: RefCell::new(HashMap::new()), - } - } -} - -#[realhydroper_lsp::async_trait(?Send)] -impl LanguageServer for LanguageServerBackend { - async fn initialize( - &self, - _: InitializeParams, - ) -> realhydroper_lsp::jsonrpc::Result { - Ok(InitializeResult { - capabilities: ServerCapabilities { - hover_provider: Some(HoverProviderCapability::Simple(true)), - completion_provider: Some(CompletionOptions::default()), - text_document_sync: Some(TextDocumentSyncCapability::Kind( - TextDocumentSyncKind::FULL, - )), - diagnostic_provider: Some(DiagnosticServerCapabilities::Options( - DiagnosticOptions { - inter_file_dependencies: true, - workspace_diagnostics: false, - identifier: None, - work_done_progress_options: WorkDoneProgressOptions { - work_done_progress: None, - }, - }, - )), - inlay_hint_provider: Some(OneOf::Left(true)), - document_link_provider: Some(DocumentLinkOptions { - resolve_provider: Some(true), - work_done_progress_options: Default::default(), - }), - definition_provider: Some(OneOf::Left(true)), - ..Default::default() - }, - ..Default::default() - }) - } - - async fn initialized(&self, _: InitializedParams) { - self.client - .log_message(MessageType::INFO, "server initialized!") - .await; - } - - async fn shutdown(&self) -> realhydroper_lsp::jsonrpc::Result<()> { - Ok(()) - } - - async fn did_open(&self, params: DidOpenTextDocumentParams) { - self.client - .log_message( - MessageType::INFO, - format!("File opened: {}", params.text_document.uri), - ) - .await; - - self.update_file_contents( - params.text_document.uri.to_file_path().unwrap(), - params.text_document.text, - ) - .await; - } - - async fn did_change(&self, params: DidChangeTextDocumentParams) { - self.client - .log_message( - MessageType::INFO, - format!("File changed: {}", params.text_document.uri), - ) - .await; - let new_content = params - .content_changes - .into_iter() - .next() - .map(|change| change.text) - .unwrap_or_default(); - self.update_file_contents( - params.text_document.uri.to_file_path().unwrap(), - new_content, - ) - .await; - } - - async fn completion( - &self, - params: CompletionParams, - ) -> realhydroper_lsp::jsonrpc::Result> { - self.client - .log_message(MessageType::INFO, "completion!") - .await; - - let position = params.text_document_position; - - // For simplicity, we assume the prefix is the last word before the cursor. - // In a real implementation, you would extract this from the document content. - let prefix = self.get_previous_text_at_position(&position); - self.client - .log_message(MessageType::INFO, format!("Completion prefix: {}", prefix)) - .await; - - let variables = self.find_variable_starting_with(&prefix); - - let items: Vec = variables - .iter() - .map(|var| CompletionItem { - label: var.name.clone(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some(format!( - "{} {}: {}", - var.shape, - var.name, - var.var_type.as_ref().unwrap() - )), - documentation: None, - ..Default::default() - }) - .collect(); - - Ok(Some(CompletionResponse::Array(items))) - } - - async fn hover(&self, params: HoverParams) -> realhydroper_lsp::jsonrpc::Result> { - let expression = self.get_expression_at_position(¶ms.text_document_position_params); - - if let Some(expression) = expression { - Ok(match expression.data { - // show variable type info on hover - DatexExpressionData::VariableDeclaration(VariableDeclaration { - name, - id: Some(id), - .. - }) - | DatexExpressionData::VariableAssignment(VariableAssignment { - name, - id: Some(id), - .. - }) - | DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { - let variable_metadata = self.get_variable_by_id(id).unwrap(); - Some(self.get_language_string_hover(&format!( - "{} {}: {}", - variable_metadata.shape, - name, - variable_metadata.var_type.unwrap_or(TypeContainer::unknown()) - ))) - } - - // show value info on hover for literals - DatexExpressionData::Integer(integer) => { - Some(self.get_language_string_hover(&format!("{}", integer))) - } - DatexExpressionData::TypedInteger(typed_integer) => { - Some(self.get_language_string_hover(&format!("{}", typed_integer))) - } - DatexExpressionData::Decimal(decimal) => { - Some(self.get_language_string_hover(&format!("{}", decimal))) - } - DatexExpressionData::TypedDecimal(typed_decimal) => { - Some(self.get_language_string_hover(&format!("{}", typed_decimal))) - } - DatexExpressionData::Boolean(boolean) => { - Some(self.get_language_string_hover(&format!("{}", boolean))) - } - DatexExpressionData::Text(text) => { - Some(self.get_language_string_hover(&format!("\"{}\"", text))) - } - DatexExpressionData::Endpoint(endpoint) => { - Some(self.get_language_string_hover(&format!("{}", endpoint))) - } - DatexExpressionData::Null => Some(self.get_language_string_hover("null")), - - _ => None, - }) - } else { - Err(realhydroper_lsp::jsonrpc::Error { - code: ErrorCode::ParseError, - message: Cow::from("No AST available"), - data: None, - }) - } - } - - async fn inlay_hint( - &self, - params: InlayHintParams, - ) -> realhydroper_lsp::jsonrpc::Result>> { - // show type hints for variables - let type_hints = self - .get_type_hints(params.text_document.uri.to_file_path().unwrap()) - .unwrap() - .into_iter() - .map(|hint| InlayHint { - position: hint.0, - label: InlayHintLabel::String(format!(": {}", hint.1.unwrap())), - kind: Some(InlayHintKind::TYPE), - text_edits: None, - tooltip: None, - padding_left: Some(true), - padding_right: None, - data: None, - }) - .collect(); - - Ok(Some(type_hints)) - } - - async fn goto_definition( - &self, - params: GotoDefinitionParams, - ) -> realhydroper_lsp::jsonrpc::Result> { - let expression = self.get_expression_at_position(¶ms.text_document_position_params); - if let Some(expression) = expression { - match expression.data { - DatexExpressionData::VariableAccess(VariableAccess { id, name }) => { - let uri = params.text_document_position_params.text_document.uri; - let file_path = uri.to_file_path().unwrap(); - let mut workspace = self.compiler_workspace.borrow_mut(); - let file = workspace.get_file_mut(&file_path).unwrap(); - if let Some(RichAst { ast, .. }) = &mut file.rich_ast { - let mut finder = VariableDeclarationFinder::new(id); - finder.visit_datex_expression(ast); - Ok(finder.variable_declaration_position.map(|position| { - GotoDefinitionResponse::Scalar(Location { - uri, - range: self - .convert_byte_range_to_document_range(&position, &file.content), - }) - })) - } else { - Ok(None) - } - } - _ => Ok(None), - } - } else { - Err(Error::internal_error()) - } - } - - async fn document_link( - &self, - params: DocumentLinkParams, - ) -> realhydroper_lsp::jsonrpc::Result>> { - // TODO - Ok(Some(vec![])) - } - - // get error diagnostics - async fn diagnostic( - &self, - params: DocumentDiagnosticParams, - ) -> realhydroper_lsp::jsonrpc::Result { - self.client - .log_message(MessageType::INFO, "diagnostics!") - .await; - - let uri = params.text_document.uri; - let file_path = uri.to_file_path().unwrap(); - - let diagnostics = self.get_diagnostics_for_file(&file_path); - let report = FullDocumentDiagnosticReport { - result_id: None, - items: diagnostics, - }; - - Ok(DocumentDiagnosticReportResult::Report( - DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { - related_documents: None, - full_document_diagnostic_report: report, - }), - )) - } -} - -impl LanguageServerBackend { - fn get_language_string_hover(&self, text: &str) -> Hover { - let contents = HoverContents::Scalar(MarkedString::LanguageString(LanguageString { - language: "datex".to_string(), - value: text.to_string(), - })); - Hover { - contents, - range: None, - } - } - - fn get_diagnostics_for_file(&self, file_path: &std::path::Path) -> Vec { - let mut diagnostics = Vec::new(); - let errors = self.spanned_compiler_errors.borrow(); - if let Some(file_errors) = errors.get(file_path) { - for spanned_error in file_errors { - let diagnostic = Diagnostic { - range: spanned_error.range, - severity: Some(DiagnosticSeverity::ERROR), - code: None, - code_description: None, - source: Some("datex".to_string()), - message: format!("{}", spanned_error.error), - related_information: None, - tags: None, - data: None, - }; - diagnostics.push(diagnostic); - } - } - diagnostics - } -} diff --git a/src/lsp/type_hint_collector.rs b/src/lsp/type_hint_collector.rs deleted file mode 100644 index 05a2e61..0000000 --- a/src/lsp/type_hint_collector.rs +++ /dev/null @@ -1,28 +0,0 @@ -use datex_core::ast::structs::VariableId; -use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; -use datex_core::visitor::VisitAction; -use datex_core::visitor::expression::ExpressionVisitor; -use datex_core::visitor::type_expression::TypeExpressionVisitor; -use std::ops::Range; - -#[derive(Default)] -pub struct TypeHintCollector { - pub type_hints: Vec<(usize, VariableId)>, -} - -impl TypeExpressionVisitor<()> for TypeHintCollector {} - -impl ExpressionVisitor<()> for TypeHintCollector { - fn visit_variable_declaration( - &mut self, - var_decl: &mut VariableDeclaration, - span: &Range, - ) -> Result, ()> { - if var_decl.type_annotation.is_none() { - let expr_start = var_decl.init_expression.span.start; - // TODO: improve - self.type_hints.push((expr_start - 3, var_decl.id.unwrap())); - } - Ok(VisitAction::VisitChildren) - } -} diff --git a/src/lsp/utils.rs b/src/lsp/utils.rs deleted file mode 100644 index 968bcd7..0000000 --- a/src/lsp/utils.rs +++ /dev/null @@ -1,443 +0,0 @@ -use crate::lsp::type_hint_collector::TypeHintCollector; -use crate::lsp::{LanguageServerBackend, SpannedCompilerError}; -use datex_core::ast::structs::expression::{ - DatexExpression, DatexExpressionData, List, Map, Statements, VariableAccess, - VariableAssignment, VariableDeclaration, -}; -use datex_core::compiler::error::DetailedCompilerErrors; -use datex_core::compiler::precompiler::precompiled_ast::VariableMetadata; -use datex_core::types::type_container::TypeContainer; -use datex_core::values::core_values::decimal::Decimal; -use datex_core::values::core_values::decimal::typed_decimal::TypedDecimal; -use datex_core::values::core_values::endpoint::Endpoint; -use datex_core::values::core_values::integer::Integer; -use datex_core::values::core_values::integer::typed_integer::TypedInteger; -use datex_core::visitor::VisitAction; -use datex_core::visitor::expression::ExpressionVisitor; -use datex_core::visitor::type_expression::TypeExpressionVisitor; -use realhydroper_lsp::lsp_types::{MessageType, Position, Range, TextDocumentPositionParams}; -use std::path::PathBuf; - -impl LanguageServerBackend { - pub async fn update_file_contents(&self, path: PathBuf, content: String) { - let mut compiler_workspace = self.compiler_workspace.borrow_mut(); - let file = compiler_workspace.load_file(path.clone(), content.clone()); - // Clear previous errors for this file - self.clear_compiler_errors(&path); - if let Some(errors) = &file.errors { - self.client - .log_message( - MessageType::ERROR, - format!( - "Failed to compile file {}: {}", - path.to_str().unwrap(), - errors, - ), - ) - .await; - self.collect_compiler_errors(errors, path, &content) - } - if let Some(rich_ast) = &file.rich_ast { - self.client - .log_message(MessageType::INFO, format!("AST: {:#?}", rich_ast.ast)) - .await; - self.client - .log_message( - MessageType::INFO, - format!("AST metadata: {:#?}", *rich_ast.metadata.borrow()), - ) - .await; - } - } - - pub(crate) fn get_type_hints( - &self, - file_path: PathBuf, - ) -> Option)>> { - let mut workspace = self.compiler_workspace.borrow_mut(); - let file = workspace.get_file_mut(&file_path).unwrap(); - if let Some(rich_ast) = &mut file.rich_ast { - let ast = &mut rich_ast.ast; - let mut collector = TypeHintCollector::default(); - collector.visit_datex_expression(ast); - Some( - collector - .type_hints - .into_iter() - .map(|hint| { - ( - self.byte_offset_to_position(hint.0, &file.content).unwrap(), - rich_ast - .metadata - .borrow() - .variables - .get(hint.1) - .unwrap() - .var_type - .clone(), - ) - }) - .collect(), - ) - } else { - None - } - } - - /// Clears all compiler errors associated with the given file path. - fn clear_compiler_errors(&self, path: &PathBuf) { - let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); - spanned_compiler_errors.remove(path); - } - - /// Recursively collects spanned compiler errors into the spanned_compiler_errors field. - fn collect_compiler_errors( - &self, - errors: &DetailedCompilerErrors, - path: PathBuf, - file_content: &String, - ) { - let mut spanned_compiler_errors = self.spanned_compiler_errors.borrow_mut(); - let file_errors = spanned_compiler_errors.entry(path.clone()).or_default(); - - for error in &errors.errors { - let range = error - .span - .as_ref() - .map(|span| self.convert_byte_range_to_document_range(span, file_content)) - .unwrap_or_else(|| { - self.convert_byte_range_to_document_range( - &(0..file_content.len()), - file_content, - ) - }); - file_errors.push(SpannedCompilerError { - range, - error: error.error.clone(), - }); - } - } - - /// Finds all variables in the workspace whose names start with the given prefix. - pub fn find_variable_starting_with(&self, prefix: &str) -> Vec { - let compiler_workspace = self.compiler_workspace.borrow(); - let mut results = Vec::new(); - for file in compiler_workspace.files().values() { - if let Some(rich_ast) = &file.rich_ast { - let metadata = rich_ast.metadata.borrow(); - for var in metadata.variables.iter() { - if var.name.starts_with(prefix) { - results.push(var.clone()); - } - } - } - } - results - } - - /// Retrieves variable metadata by its unique ID. - pub fn get_variable_by_id(&self, id: usize) -> Option { - let compiler_workspace = self.compiler_workspace.borrow(); - for file in compiler_workspace.files().values() { - if let Some(rich_ast) = &file.rich_ast { - let metadata = rich_ast.metadata.borrow(); - if let Some(v) = metadata.variables.get(id).cloned() { - return Some(v); - } - } - } - None - } - - /// Converts an LSP position (line and character) to a byte offset in the file content. - fn position_to_byte_offset(&self, position: &TextDocumentPositionParams) -> usize { - let workspace = self.compiler_workspace.borrow(); - // first get file contents at position.text_document.uri - // then calculate byte offset from position.position.line and position.position.character - let file_path = position.text_document.uri.to_file_path().unwrap(); - let file_content = &workspace.get_file(&file_path).unwrap().content; - - Self::line_char_to_byte_index( - file_content, - position.position.line as usize, - position.position.character as usize, - ) - .unwrap_or(0) - } - - /// Converts a byte range (start, end) to a document Range (start Position, end Position) in the file content. - pub fn convert_byte_range_to_document_range( - &self, - span: &std::ops::Range, - file_content: &String, - ) -> Range { - let start = self - .byte_offset_to_position(span.start, file_content) - .unwrap_or(Position { - line: 0, - character: 0, - }); - let end = self - .byte_offset_to_position(span.end, file_content) - .unwrap_or(Position { - line: 0, - character: 0, - }); - Range { start, end } - } - - /// Converts a byte offset to an LSP position (line and character) in the file content. - /// TODO: check if this is correct, generated with copilot - pub fn byte_offset_to_position( - &self, - byte_offset: usize, - file_content: &String, - ) -> Option { - let mut current_offset = 0; - for (line_idx, line) in file_content.lines().enumerate() { - let line_length = line.len() + 1; // +1 for the newline character - if current_offset + line_length > byte_offset { - // The byte offset is within this line - let char_offset = line - .char_indices() - .find(|(i, _)| current_offset + i >= byte_offset) - .map(|(i, _)| i) - .unwrap_or(line.len()); - return Some(Position { - line: line_idx as u32, - character: char_offset as u32, - }); - } - current_offset += line_length; - } - None - } - - /// Retrieves the text immediately preceding the given position in the document. - /// This is used for autocompletion suggestions. - pub fn get_previous_text_at_position(&self, position: &TextDocumentPositionParams) -> String { - let byte_offset = self.position_to_byte_offset(position); - let workspace = self.compiler_workspace.borrow(); - let file_path = position.text_document.uri.to_file_path().unwrap(); - let file_content = &workspace.get_file(&file_path).unwrap().content; - // Get the text before the byte offset, only matching word characters - let previous_text = &file_content[..byte_offset]; - let last_word = previous_text - .rsplit(|c: char| !c.is_alphanumeric() && c != '_') - .next() - .unwrap_or(""); - last_word.to_string() - } - - /// Retrieves the DatexExpression AST node at the given byte offset. - pub fn get_expression_at_position( - &self, - position: &TextDocumentPositionParams, - ) -> Option { - let byte_offset = self.position_to_byte_offset(position); - let mut workspace = self.compiler_workspace.borrow_mut(); - let file_path = position.text_document.uri.to_file_path().unwrap(); - if let Some(rich_ast) = &mut workspace.get_file_mut(&file_path).unwrap().rich_ast { - let ast = &mut rich_ast.ast; - let mut finder = ExpressionFinder::new(byte_offset); - finder.visit_datex_expression(ast); - finder.found_expr.map(|e| DatexExpression { - span: e.1, - data: e.0, - wrapped: None, - ty: None, - }) - } else { - None - } - } - - /// Converts a (line, character) pair to a byte index in the given text. - /// Lines and characters are zero-indexed. - /// Returns None if the line or character is out of bounds. - pub fn line_char_to_byte_index(text: &str, line: usize, character: usize) -> Option { - let mut lines = text.split('\n'); - - // Get the line - let line_text = lines.nth(line)?; - - // Compute byte index of the start of that line - let byte_offset_to_line_start = text - .lines() - .take(line) - .map(|l| l.len() + 1) // +1 for '\n' - .sum::(); - - // Now find the byte index within that line for the given character offset - let byte_offset_within_line = line_text - .char_indices() - .nth(character) - .map(|(i, _)| i) - .unwrap_or_else(|| line_text.len()); - - Some(byte_offset_to_line_start + byte_offset_within_line) - } -} - -/// Visitor that finds the most specific DatexExpression containing a given byte position. -/// If multiple expressions contain the position, the one with the smallest span is chosen. -struct ExpressionFinder { - pub search_pos: usize, - pub found_expr: Option<(DatexExpressionData, std::ops::Range)>, -} - -impl ExpressionFinder { - pub fn new(search_pos: usize) -> Self { - Self { - search_pos, - found_expr: None, - } - } - - /// Checks if the given span includes the search position. - /// If it does, updates found_expr if this expression is more specific (smaller span). - /// Returns true if the span includes the search position, false otherwise. - fn match_span( - &mut self, - span: &std::ops::Range, - expr_data: DatexExpressionData, - ) -> Result, ()> { - if span.start <= self.search_pos && self.search_pos <= span.end { - // If we already found an expression, only replace it if this one is smaller (more specific) - if let Some((_, existing_expr_span)) = &self.found_expr { - if (span.end - span.start) < (existing_expr_span.end - existing_expr_span.start) { - self.found_expr = Some((expr_data, span.clone())); - } - } else { - self.found_expr = Some((expr_data, span.clone())); - } - Ok(VisitAction::VisitChildren) - } else { - Ok(VisitAction::SkipChildren) - } - } -} - -impl TypeExpressionVisitor<()> for ExpressionFinder {} - -impl ExpressionVisitor<()> for ExpressionFinder { - fn visit_statements( - &mut self, - stmts: &mut Statements, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Statements(stmts.clone())) - } - - fn visit_variable_declaration( - &mut self, - var_decl: &mut VariableDeclaration, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span( - span, - DatexExpressionData::VariableDeclaration(var_decl.clone()), - ) - } - - fn visit_variable_assignment( - &mut self, - var_assign: &mut VariableAssignment, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span( - span, - DatexExpressionData::VariableAssignment(var_assign.clone()), - ) - } - - fn visit_variable_access( - &mut self, - var_access: &mut VariableAccess, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span( - span, - DatexExpressionData::VariableAccess(var_access.clone()), - ) - } - - fn visit_list( - &mut self, - list: &mut List, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::List(list.clone())) - } - - fn visit_map( - &mut self, - map: &mut Map, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Map(map.clone())) - } - - fn visit_integer( - &mut self, - value: &mut Integer, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Integer(value.clone())) - } - - fn visit_typed_integer( - &mut self, - value: &mut TypedInteger, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::TypedInteger(value.clone())) - } - - fn visit_decimal( - &mut self, - value: &mut Decimal, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Decimal(value.clone())) - } - - fn visit_typed_decimal( - &mut self, - value: &mut TypedDecimal, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::TypedDecimal(value.clone())) - } - - fn visit_text( - &mut self, - value: &mut String, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Text(value.clone())) - } - - fn visit_boolean( - &mut self, - value: &mut bool, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Boolean(*value)) - } - - fn visit_endpoint( - &mut self, - value: &mut Endpoint, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Endpoint(value.clone())) - } - - fn visit_null( - &mut self, - span: &std::ops::Range, - ) -> Result, ()> { - self.match_span(span, DatexExpressionData::Null) - } -} diff --git a/src/lsp/variable_declaration_finder.rs b/src/lsp/variable_declaration_finder.rs deleted file mode 100644 index 36a8fbe..0000000 --- a/src/lsp/variable_declaration_finder.rs +++ /dev/null @@ -1,38 +0,0 @@ -use datex_core::ast::structs::expression::{DatexExpression, VariableDeclaration}; -use datex_core::visitor::VisitAction; -use datex_core::visitor::expression::ExpressionVisitor; -use datex_core::visitor::type_expression::TypeExpressionVisitor; -use std::ops::Range; - -#[derive(Default)] -pub struct VariableDeclarationFinder { - pub var_id: usize, - pub variable_declaration_position: Option>, -} - -impl VariableDeclarationFinder { - pub fn new(var_id: usize) -> Self { - VariableDeclarationFinder { - var_id, - variable_declaration_position: None, - } - } -} - -impl TypeExpressionVisitor<()> for VariableDeclarationFinder {} - -impl ExpressionVisitor<()> for VariableDeclarationFinder { - fn visit_variable_declaration( - &mut self, - var_decl: &mut VariableDeclaration, - span: &Range, - ) -> Result, ()> { - if var_decl.id == Some(self.var_id) { - self.variable_declaration_position = Some(span.clone()); - // early abort - Err(()) - } else { - Ok(VisitAction::VisitChildren) - } - } -} diff --git a/src/main.rs b/src/main.rs index 1fb666d..e141880 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use datex_core::compiler::workspace::CompilerWorkspace; use datex_core::crypto::crypto_native::CryptoNative; use datex_core::decompiler::{DecompileOptions, decompile_value}; +use datex_core::lsp::create_lsp; use datex_core::run_async; use datex_core::runtime::global_context::{DebugFlags, GlobalContext, set_global_context}; use datex_core::runtime::{AsyncContext, Runtime, RuntimeConfig}; @@ -10,13 +11,11 @@ use std::path::PathBuf; use std::sync::Arc; mod command_line_args; -mod lsp; mod repl; mod utils; mod workbench; use crate::command_line_args::Repl; -use crate::lsp::LanguageServerBackend; use crate::repl::{ReplOptions, repl}; use crate::utils::config::{ConfigError, create_runtime_with_config}; use command_line_args::{Subcommands, get_command}; @@ -37,17 +36,15 @@ async fn main() { if let Some(cmd) = command { match cmd { - Subcommands::Lsp(lsp) => { + Subcommands::Lsp(_) => { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let runtime = Runtime::new(RuntimeConfig::new_with_endpoint(Endpoint::default()), AsyncContext::new()); - let compiler_workspace = CompilerWorkspace::new(runtime); - - let (service, socket) = LspService::new(|client| { - LanguageServerBackend::new(client, compiler_workspace) - }); - Server::new(stdin, stdout, socket).serve(service).await; + let runtime = Runtime::new( + RuntimeConfig::new_with_endpoint(Endpoint::default()), + AsyncContext::new(), + ); + create_lsp(runtime, stdin, stdout).await; } Subcommands::Run(run) => { execute_file(run).await;