From 1e41297d9144c609b6477789acf7e4635e69fb16 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Fri, 7 Jun 2024 15:38:00 +0200 Subject: [PATCH 1/8] add struct for jinja blocks --- Cargo.lock | 23 ++++--- jinja-lsp-nodejs/Cargo.toml | 13 +++- jinja-lsp-nodejs/index.d.ts | 16 ++++- jinja-lsp-nodejs/index.js | 5 +- jinja-lsp-nodejs/src/lib.rs | 95 ++++++++++++++++++++++++++++- jinja-lsp-queries/src/lsp_helper.rs | 22 ++++--- jinja-lsp/Cargo.toml | 1 + jinja-lsp/src/lib.rs | 5 ++ jinja-lsp/src/lsp_files.rs | 47 ++++++++++++-- 9 files changed, 197 insertions(+), 30 deletions(-) create mode 100644 jinja-lsp/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d2b1df4..8ea09ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,6 +320,20 @@ dependencies = [ "walkdir", ] +[[package]] +name = "jinja-lsp-nodejs" +version = "0.1.80" +dependencies = [ + "jinja-lsp", + "jinja-lsp-queries", + "napi", + "napi-build", + "napi-derive", + "ropey", + "tower-lsp", + "tree-sitter", +] + [[package]] name = "jinja-lsp-queries" version = "0.1.80" @@ -976,15 +990,6 @@ dependencies = [ "serde", ] -[[package]] -name = "uros_jinja-lsp-nodejs" -version = "0.0.0" -dependencies = [ - "napi", - "napi-build", - "napi-derive", -] - [[package]] name = "walkdir" version = "2.5.0" diff --git a/jinja-lsp-nodejs/Cargo.toml b/jinja-lsp-nodejs/Cargo.toml index 56c7ba5..80505ee 100644 --- a/jinja-lsp-nodejs/Cargo.toml +++ b/jinja-lsp-nodejs/Cargo.toml @@ -1,7 +1,10 @@ [package] edition = "2021" -name = "uros_jinja-lsp-nodejs" -version = "0.0.0" +name = "jinja-lsp-nodejs" +version = "0.1.80" +license = "MIT" +authors = ["uros-5"] +description = "Bindings for jinja-lsp" [lib] crate-type = ["cdylib"] @@ -10,6 +13,11 @@ crate-type = ["cdylib"] # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix napi = { version = "2.12.2", default-features = false, features = ["napi4"] } napi-derive = "2.12.2" +jinja-lsp-queries = { path = "../jinja-lsp-queries", version = "0.1.80"} +jinja-lsp = { path = "../jinja-lsp", version = "0.1.80"} +tree-sitter = "0.21.0" +tower-lsp = { version = "0.20.0", features = ["proposed"] } +ropey = "1.5.0" [build-dependencies] napi-build = "2.0.1" @@ -17,3 +25,4 @@ napi-build = "2.0.1" [profile.release] lto = true strip = "symbols" +opt-level = 3 diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index 6d85d51..a25ad90 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -3,4 +3,18 @@ /* auto-generated by NAPI-RS */ -export function sum(a: number, b: number): number +export function basic(content: string): number | null +export interface JsPosition { + line: number + character: number +} +export class NodejsLspFiles { + constructor() + /** Actions can come from unsaved context. */ + addGlobalContext(actions?: Array | undefined | null): void + deleteAll(): void + addOne(id: number, content: string, line: number): boolean + hover(position: JsPosition, id: number): void + static complete(position: JsPosition, id: number, content: string): void + gotoDefinition(position: JsPosition, id: number): void +} diff --git a/jinja-lsp-nodejs/index.js b/jinja-lsp-nodejs/index.js index a7ab326..33615e2 100644 --- a/jinja-lsp-nodejs/index.js +++ b/jinja-lsp-nodejs/index.js @@ -310,6 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { sum } = nativeBinding +const { basic, NodejsLspFiles } = nativeBinding -module.exports.sum = sum +module.exports.basic = basic +module.exports.NodejsLspFiles = NodejsLspFiles diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index b479ed7..725ae30 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -1,9 +1,100 @@ #![deny(clippy::all)] +use jinja_lsp::lsp_files::LspFiles; +use jinja_lsp_queries::{ + parsers::Parsers, + search::{objects::objects_query, queries::Queries}, +}; + +use tower_lsp::lsp_types::{DidOpenTextDocumentParams, Position, TextDocumentItem, Url}; +use tree_sitter::Point; + #[macro_use] extern crate napi_derive; #[napi] -pub fn sum(a: i32, b: i32) -> i32 { - a + b +pub fn basic(content: String) -> Option { + let queries = Queries::default(); + let mut parsers = Parsers::default(); + let tree = parsers.parse( + jinja_lsp_queries::tree_builder::LangType::Template, + &content, + None, + )?; + let query = &queries.jinja_objects; + let objects = objects_query(query, &tree, Point::new(0, 0), &content, true); + let count = objects.show(); + Some(count.len() as i32) +} + +#[napi] +#[derive(Default)] +pub struct NodejsLspFiles { + lsp_files: LspFiles, + counter: u32, +} + +#[napi] +impl NodejsLspFiles { + #[napi(constructor)] + pub fn new() -> Self { + Self { + lsp_files: LspFiles::default(), + counter: 0, + } + } + + /// Actions can come from unsaved context. + #[napi] + pub fn add_global_context(&self, actions: Option>) {} + + #[napi] + pub fn delete_all(&mut self) { + self.lsp_files.variables.clear(); + self.lsp_files.delete_documents(); + self.counter = 0; + // self.lsp_files.main_channel + } + + #[napi] + pub fn add_one(&mut self, id: u32, content: String, line: u32) -> bool { + println!("{}", id); + let params: DidOpenTextDocumentParams = DidOpenTextDocumentParams { + text_document: TextDocumentItem::new( + Url::parse(&format!("file:///home/{id}.jinja")).unwrap(), + String::new(), + 0, + content, + ), + }; + let content = self.lsp_files.did_open(params); + if let Some(content) = content { + match content { + jinja_lsp::channels::diagnostics::DiagnosticMessage::Errors(errors) => { + for i in errors { + println!("error: {}", i.1.len()); + } + } + jinja_lsp::channels::diagnostics::DiagnosticMessage::Str(_) => { + println!("str") + } + } + } + true + } + + #[napi] + pub fn hover(&self, position: JsPosition, id: u32) {} + + #[napi] + pub fn complete(position: JsPosition, id: u32, content: String) {} + + #[napi] + pub fn goto_definition(&self, position: JsPosition, id: u32) {} +} + +#[napi(object)] +pub struct JsPosition { + pub line: u32, + pub character: u32, } diff --git a/jinja-lsp-queries/src/lsp_helper.rs b/jinja-lsp-queries/src/lsp_helper.rs index 2a23c6b..5908037 100644 --- a/jinja-lsp-queries/src/lsp_helper.rs +++ b/jinja-lsp-queries/src/lsp_helper.rs @@ -11,6 +11,7 @@ use crate::{ tree_builder::{JinjaDiagnostic, LangType}, }; +#[allow(clippy::too_many_arguments)] pub fn search_errors( root: &Tree, source: &str, @@ -19,6 +20,7 @@ pub fn search_errors( file_name: &String, templates: PathBuf, lang_type: LangType, + ignore_globals: bool, ) -> Option> { let mut diagnostics = vec![]; match lang_type { @@ -57,15 +59,17 @@ pub fn search_errors( to_warn = true; } else if empty { to_warn = true; - for file in variables { - let temp = file - .1 - .iter() - .filter(|variable| variable.name == object.name); - if temp.count() != 0 { - err_type = JinjaDiagnostic::DefinedSomewhere; - to_warn = true; - break; + if !ignore_globals { + for file in variables { + let temp = file + .1 + .iter() + .filter(|variable| variable.name == object.name); + if temp.count() != 0 { + err_type = JinjaDiagnostic::DefinedSomewhere; + to_warn = true; + break; + } } } } diff --git a/jinja-lsp/Cargo.toml b/jinja-lsp/Cargo.toml index 799a3ff..e1ee29f 100644 --- a/jinja-lsp/Cargo.toml +++ b/jinja-lsp/Cargo.toml @@ -18,6 +18,7 @@ opt-level = 3 name = "jinja-lsp" path = "src/main.rs" + [dependencies] env_logger = "0.9.0" ropey = "1.5.0" diff --git a/jinja-lsp/src/lib.rs b/jinja-lsp/src/lib.rs new file mode 100644 index 0000000..81730b2 --- /dev/null +++ b/jinja-lsp/src/lib.rs @@ -0,0 +1,5 @@ +mod backend; +pub mod channels; +mod config; +mod filter; +pub mod lsp_files; diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index 20e621b..cea9448 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -47,11 +47,12 @@ pub struct LspFiles { pub parsers: Parsers, pub queries: Queries, pub config: JinjaConfig, - pub diagnostics_task: JoinHandle<()>, + pub diagnostics_task: Option>, pub main_channel: Option>, pub variables: HashMap>, pub code_actions: HashMap>, pub is_vscode: bool, + pub ignore_globals: bool, } impl LspFiles { @@ -184,14 +185,16 @@ impl LspFiles { text_document: TextDocumentIdentifier::new(params.text_document.uri), text: None, }; - self.diagnostics_task.abort(); + if let Some(task) = &self.diagnostics_task { + task.abort(); + } let channel = self.main_channel.clone(); - self.diagnostics_task = tokio::spawn(async move { + self.diagnostics_task = Some(tokio::spawn(async move { sleep(Duration::from_millis(200)).await; if let Some(channel) = channel { let _ = channel.send(LspMessage::DidSave(param)).await; } - }); + })); None } @@ -211,6 +214,7 @@ impl LspFiles { &name.to_string(), self.config.templates.clone(), lang_type, + self.ignore_globals, ) } @@ -712,6 +716,10 @@ impl LspFiles { } Some(DocumentSymbolResponse::Nested(symbols)) } + + pub fn delete_documents(&mut self) { + self.documents.clear(); + } } impl Default for LspFiles { @@ -719,7 +727,7 @@ impl Default for LspFiles { let mut trees = HashMap::new(); trees.insert(LangType::Template, HashMap::new()); trees.insert(LangType::Backend, HashMap::new()); - let diagnostics_task = tokio::spawn(async move {}); + let diagnostics_task = None; let main_channel = None; Self { trees, @@ -732,6 +740,35 @@ impl Default for LspFiles { variables: HashMap::default(), is_vscode: false, code_actions: HashMap::default(), + ignore_globals: false, + } + } +} + +impl Clone for LspFiles { + fn clone(&self) -> Self { + let trees = self.trees.clone(); + let parsers = Parsers::default(); + let queries = Queries::default(); + let documents = self.documents.clone(); + let main_channel = self.main_channel.clone(); + let variables = self.variables.clone(); + let is_vscode = self.is_vscode; + let code_actions = self.code_actions.clone(); + let config = self.config.clone(); + let task = None; + Self { + trees, + documents, + parsers, + queries, + config, + main_channel, + variables, + code_actions, + is_vscode, + diagnostics_task: task, + ignore_globals: self.ignore_globals, } } } From e4bcadbf1623b12f27205b1ccb14804cb908d59b Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Fri, 7 Jun 2024 20:28:57 +0200 Subject: [PATCH 2/8] finished hover request --- jinja-lsp-nodejs/index.d.ts | 34 +++++- jinja-lsp-nodejs/index.js | 3 +- jinja-lsp-nodejs/src/lib.rs | 201 +++++++++++++++++++++++++++++++++--- jinja-lsp/src/lib.rs | 2 +- 4 files changed, 223 insertions(+), 17 deletions(-) diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index a25ad90..72d2ac5 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -8,13 +8,43 @@ export interface JsPosition { line: number character: number } +export const enum JsIdentifierType { + ForLoopKey = 0, + ForLoopValue = 1, + ForLoopCount = 2, + SetVariable = 3, + WithVariable = 4, + MacroName = 5, + MacroParameter = 6, + TemplateBlock = 7, + BackendVariable = 8, + UndefinedVariable = 9, + JinjaTemplate = 10 +} +export interface JsIdentifier { + start: JsPosition + end: JsPosition + name: string + identifierType: JsIdentifierType + error?: string +} +export interface JsHover { + kind: string + value: string + range?: JsRange +} +export interface JsRange { + start: JsPosition + end: JsPosition +} export class NodejsLspFiles { constructor() /** Actions can come from unsaved context. */ addGlobalContext(actions?: Array | undefined | null): void deleteAll(): void - addOne(id: number, content: string, line: number): boolean - hover(position: JsPosition, id: number): void + addOne(id: number, filename: string, content: string, line: number): Array + getVariables(id: string, line: number): Array | null + hover(id: number, filename: string, content: string, line: number, position: JsPosition): JsHover | null static complete(position: JsPosition, id: number, content: string): void gotoDefinition(position: JsPosition, id: number): void } diff --git a/jinja-lsp-nodejs/index.js b/jinja-lsp-nodejs/index.js index 33615e2..0f2dbf8 100644 --- a/jinja-lsp-nodejs/index.js +++ b/jinja-lsp-nodejs/index.js @@ -310,7 +310,8 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { basic, NodejsLspFiles } = nativeBinding +const { basic, NodejsLspFiles, JsIdentifierType } = nativeBinding module.exports.basic = basic module.exports.NodejsLspFiles = NodejsLspFiles +module.exports.JsIdentifierType = JsIdentifierType diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index 725ae30..a78cdc9 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -1,12 +1,20 @@ #![deny(clippy::all)] -use jinja_lsp::lsp_files::LspFiles; +use jinja_lsp::{ + channels::diagnostics::DiagnosticMessage, + filter::{init_filter_completions, FilterCompletion}, + lsp_files::LspFiles, +}; use jinja_lsp_queries::{ parsers::Parsers, - search::{objects::objects_query, queries::Queries}, + search::{objects::objects_query, queries::Queries, Identifier, IdentifierType}, }; -use tower_lsp::lsp_types::{DidOpenTextDocumentParams, Position, TextDocumentItem, Url}; +use tower_lsp::lsp_types::{ + DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, MarkupContent, MarkupKind, + Position, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, + WorkDoneProgressParams, +}; use tree_sitter::Point; #[macro_use] @@ -32,6 +40,7 @@ pub fn basic(content: String) -> Option { pub struct NodejsLspFiles { lsp_files: LspFiles, counter: u32, + filters: Vec, } #[napi] @@ -41,6 +50,7 @@ impl NodejsLspFiles { Self { lsp_files: LspFiles::default(), counter: 0, + filters: init_filter_completions(), } } @@ -57,34 +67,119 @@ impl NodejsLspFiles { } #[napi] - pub fn add_one(&mut self, id: u32, content: String, line: u32) -> bool { - println!("{}", id); + pub fn add_one( + &mut self, + id: u32, + filename: String, + content: String, + line: u32, + ) -> Vec { let params: DidOpenTextDocumentParams = DidOpenTextDocumentParams { text_document: TextDocumentItem::new( - Url::parse(&format!("file:///home/{id}.jinja")).unwrap(), + Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap(), String::new(), 0, content, ), }; let content = self.lsp_files.did_open(params); + let mut all_errors = vec![]; if let Some(content) = content { match content { - jinja_lsp::channels::diagnostics::DiagnosticMessage::Errors(errors) => { + DiagnosticMessage::Errors(errors) => { for i in errors { - println!("error: {}", i.1.len()); + for error in i.1 { + let diagnostic = error.0.to_string(); + let mut position = error.1; + position.start.row += line as usize; + position.end.row += line as usize; + let mut identifier = JsIdentifier::from(&position); + identifier.error = Some(diagnostic); + all_errors.push(identifier); + } } } - jinja_lsp::channels::diagnostics::DiagnosticMessage::Str(_) => { - println!("str") - } + DiagnosticMessage::Str(_) => {} } } - true + all_errors + } + + #[napi] + pub fn get_variables(&self, id: String, line: u32) -> Option> { + let variables = self.lsp_files.variables.get(&id)?; + let mut converted = vec![]; + for variable in variables { + let mut variable2 = JsIdentifier::from(variable); + variable2.start.line += line; + variable2.end.line += line; + converted.push(variable2); + } + Some(converted) } #[napi] - pub fn hover(&self, position: JsPosition, id: u32) {} + pub fn hover( + &self, + id: u32, + filename: String, + content: String, + line: u32, + mut position: JsPosition, + ) -> Option { + position.line -= line; + let uri = Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap(); + let params: HoverParams = HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + TextDocumentIdentifier::new(uri.clone()), + Position::new(position.line, position.character), + ), + work_done_progress_params: WorkDoneProgressParams { + work_done_token: None, + }, + }; + let hover = self.lsp_files.hover(params)?; + let mut res = None; + if hover.1 { + let filter = self + .filters + .iter() + .find(|name| name.name == hover.0.name && hover.1); + if let Some(filter) = filter { + let markup_content = MarkupContent { + kind: MarkupKind::Markdown, + value: filter.desc.to_string(), + }; + let hover_contents = HoverContents::Markup(markup_content); + let hover = Hover { + contents: hover_contents, + range: None, + }; + res = Some(hover); + } + } else if let Some(data_type) = self.lsp_files.data_type(uri.clone(), hover.0) { + let markup_content = MarkupContent { + kind: MarkupKind::Markdown, + value: data_type.completion_detail().to_owned(), + }; + let hover_contents = HoverContents::Markup(markup_content); + let hover = Hover { + contents: hover_contents, + range: None, + }; + res = Some(hover); + } + if let Some(res) = res { + if let HoverContents::Markup(hover_contents) = res.contents { + return Some(JsHover { + kind: "markdown".to_owned(), + value: hover_contents.value, + range: None, + }); + } + } + None + } #[napi] pub fn complete(position: JsPosition, id: u32, content: String) {} @@ -94,7 +189,87 @@ impl NodejsLspFiles { } #[napi(object)] +#[derive(Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] pub struct JsPosition { pub line: u32, pub character: u32, } + +impl From for JsPosition { + fn from(value: Point) -> Self { + Self { + line: value.row as u32, + character: value.column as u32, + } + } +} + +#[napi] +#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum JsIdentifierType { + ForLoopKey, + ForLoopValue, + ForLoopCount, + SetVariable, + WithVariable, + MacroName, + MacroParameter, + TemplateBlock, + BackendVariable, + #[default] + UndefinedVariable, + JinjaTemplate, +} + +#[napi(object)] +#[derive(Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)] +pub struct JsIdentifier { + pub start: JsPosition, + pub end: JsPosition, + pub name: String, + pub identifier_type: JsIdentifierType, + pub error: Option, +} + +impl From<&Identifier> for JsIdentifier { + fn from(value: &Identifier) -> Self { + Self { + start: JsPosition::from(value.start), + end: JsPosition::from(value.end), + name: value.name.to_string(), + identifier_type: JsIdentifierType::from(&value.identifier_type), + error: None, + } + } +} + +impl From<&IdentifierType> for JsIdentifierType { + fn from(value: &IdentifierType) -> Self { + match value { + IdentifierType::ForLoopKey => JsIdentifierType::ForLoopKey, + IdentifierType::ForLoopValue => JsIdentifierType::ForLoopValue, + IdentifierType::ForLoopCount => JsIdentifierType::ForLoopCount, + IdentifierType::SetVariable => JsIdentifierType::SetVariable, + IdentifierType::WithVariable => JsIdentifierType::WithVariable, + IdentifierType::MacroName => JsIdentifierType::MacroName, + IdentifierType::MacroParameter => JsIdentifierType::MacroParameter, + IdentifierType::TemplateBlock => JsIdentifierType::TemplateBlock, + IdentifierType::BackendVariable => JsIdentifierType::BackendVariable, + IdentifierType::UndefinedVariable => JsIdentifierType::UndefinedVariable, + IdentifierType::JinjaTemplate => JsIdentifierType::JinjaTemplate, + } + } +} + +#[napi(object)] +pub struct JsHover { + pub kind: String, + pub value: String, + pub range: Option, +} + +#[napi(object)] +pub struct JsRange { + pub start: JsPosition, + pub end: JsPosition, +} diff --git a/jinja-lsp/src/lib.rs b/jinja-lsp/src/lib.rs index 81730b2..0d6b1b8 100644 --- a/jinja-lsp/src/lib.rs +++ b/jinja-lsp/src/lib.rs @@ -1,5 +1,5 @@ mod backend; pub mod channels; mod config; -mod filter; +pub mod filter; pub mod lsp_files; From bde8b8cf38c32cf48727420ae4da4869b03bc747 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Sat, 8 Jun 2024 00:02:04 +0200 Subject: [PATCH 3/8] added more types to nodejs --- jinja-lsp-nodejs/index.d.ts | 10 +++- jinja-lsp-nodejs/src/lib.rs | 115 ++++++++++++++++++++++++++++++++---- jinja-lsp/src/lsp_files.rs | 12 ++++ 3 files changed, 122 insertions(+), 15 deletions(-) diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index 72d2ac5..225ed9d 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -37,14 +37,18 @@ export interface JsRange { start: JsPosition end: JsPosition } +export interface JsLocation { + uri: string + range: JsRange +} export class NodejsLspFiles { constructor() /** Actions can come from unsaved context. */ addGlobalContext(actions?: Array | undefined | null): void - deleteAll(): void + deleteAll(filename: string): void addOne(id: number, filename: string, content: string, line: number): Array getVariables(id: string, line: number): Array | null - hover(id: number, filename: string, content: string, line: number, position: JsPosition): JsHover | null + hover(id: number, filename: string, line: number, position: JsPosition): JsHover | null static complete(position: JsPosition, id: number, content: string): void - gotoDefinition(position: JsPosition, id: number): void + gotoDefinition(id: number, filename: string, line: number, position: JsPosition): Array | null } diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index a78cdc9..1ed7f45 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -8,11 +8,13 @@ use jinja_lsp::{ use jinja_lsp_queries::{ parsers::Parsers, search::{objects::objects_query, queries::Queries, Identifier, IdentifierType}, + to_input_edit::to_position2, }; use tower_lsp::lsp_types::{ - DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, MarkupContent, MarkupKind, - Position, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, + DidOpenTextDocumentParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, + HoverParams, Location, MarkupContent, MarkupKind, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, WorkDoneProgressParams, }; use tree_sitter::Point; @@ -59,7 +61,7 @@ impl NodejsLspFiles { pub fn add_global_context(&self, actions: Option>) {} #[napi] - pub fn delete_all(&mut self) { + pub fn delete_all(&mut self, filename: String) { self.lsp_files.variables.clear(); self.lsp_files.delete_documents(); self.counter = 0; @@ -123,7 +125,6 @@ impl NodejsLspFiles { &self, id: u32, filename: String, - content: String, line: u32, mut position: JsPosition, ) -> Option { @@ -140,6 +141,13 @@ impl NodejsLspFiles { }; let hover = self.lsp_files.hover(params)?; let mut res = None; + let mut range = Range { + start: to_position2(hover.0.start), + end: to_position2(hover.0.end), + }; + range.start.line += line; + range.end.line += line; + let range = Some(range); if hover.1 { let filter = self .filters @@ -153,7 +161,7 @@ impl NodejsLspFiles { let hover_contents = HoverContents::Markup(markup_content); let hover = Hover { contents: hover_contents, - range: None, + range, }; res = Some(hover); } @@ -165,17 +173,19 @@ impl NodejsLspFiles { let hover_contents = HoverContents::Markup(markup_content); let hover = Hover { contents: hover_contents, - range: None, + range, }; res = Some(hover); } if let Some(res) = res { if let HoverContents::Markup(hover_contents) = res.contents { - return Some(JsHover { - kind: "markdown".to_owned(), - value: hover_contents.value, - range: None, - }); + if let Some(range) = res.range { + return Some(JsHover { + kind: "markdown".to_owned(), + value: hover_contents.value, + range: Some(JsRange::from(&range)), + }); + } } } None @@ -185,7 +195,55 @@ impl NodejsLspFiles { pub fn complete(position: JsPosition, id: u32, content: String) {} #[napi] - pub fn goto_definition(&self, position: JsPosition, id: u32) {} + pub fn goto_definition( + &self, + id: u32, + filename: String, + line: u32, + mut position: JsPosition, + ) -> Option> { + position.line -= line; + let uri = Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap(); + let params: GotoDefinitionParams = GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + TextDocumentIdentifier::new(uri.clone()), + Position::new(position.line, position.character), + ), + work_done_progress_params: WorkDoneProgressParams { + work_done_token: None, + }, + partial_result_params: PartialResultParams { + ..Default::default() + }, + }; + let defintion = self.lsp_files.goto_definition(params)?; + let mut definitions = vec![]; + println!("here we are, {}", definitions.len()); + match defintion { + GotoDefinitionResponse::Scalar(mut location) => { + let uri2 = location.uri.to_string(); + if uri2.contains(&filename) { + location.uri = Url::parse(&filename).unwrap(); + location.range.start.line += line; + location.range.end.line += line; + definitions.push(JsLocation::from(&location)); + } + } + GotoDefinitionResponse::Array(locations) => { + for mut location in locations { + let uri2 = location.uri.to_string(); + if uri2.contains(&filename) { + location.uri = Url::parse(&filename).unwrap(); + location.range.start.line += line; + location.range.end.line += line; + definitions.push(JsLocation::from(&location)); + } + } + } + _ => (), + } + Some(definitions) + } } #[napi(object)] @@ -204,6 +262,15 @@ impl From for JsPosition { } } +impl From<&Position> for JsPosition { + fn from(value: &Position) -> Self { + Self { + line: value.line, + character: value.character, + } + } +} + #[napi] #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum JsIdentifierType { @@ -261,6 +328,15 @@ impl From<&IdentifierType> for JsIdentifierType { } } +impl From<&Range> for JsRange { + fn from(value: &Range) -> Self { + Self { + start: JsPosition::from(&value.start), + end: JsPosition::from(&value.end), + } + } +} + #[napi(object)] pub struct JsHover { pub kind: String, @@ -273,3 +349,18 @@ pub struct JsRange { pub start: JsPosition, pub end: JsPosition, } + +#[napi(object)] +pub struct JsLocation { + pub uri: String, + pub range: JsRange, +} + +impl From<&Location> for JsLocation { + fn from(value: &Location) -> Self { + Self { + uri: value.uri.to_string(), + range: JsRange::from(&value.range), + } + } +} diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index cea9448..73f5e23 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -720,6 +720,18 @@ impl LspFiles { pub fn delete_documents(&mut self) { self.documents.clear(); } + + pub fn delete_documents_with_id(&mut self, id: String) { + let mut ids = vec![]; + for i in &self.documents { + if i.0.contains(&id) { + ids.push(i.0.clone()); + } + } + for i in ids { + self.documents.remove(&i); + } + } } impl Default for LspFiles { From e90ead68eedb6d96736c04de4f7d2f027c8c1b99 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Sat, 8 Jun 2024 10:44:50 +0200 Subject: [PATCH 4/8] deleted debugging lines --- jinja-lsp-nodejs/index.d.ts | 27 ++- jinja-lsp-nodejs/index.js | 4 +- jinja-lsp-nodejs/src/lib.rs | 162 +++++++++++++++++- .../src/search/snippets_completion.rs | 1 - 4 files changed, 185 insertions(+), 9 deletions(-) diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index 225ed9d..1e8530d 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -32,6 +32,8 @@ export interface JsHover { kind: string value: string range?: JsRange + label?: string + documentaion?: string } export interface JsRange { start: JsPosition @@ -41,6 +43,29 @@ export interface JsLocation { uri: string range: JsRange } +export interface JsCompletionItem { + completionType: JsCompletionType + label: string + kind: Kind2 + description: string + newText?: string + insert?: JsRange + replace?: JsRange +} +export const enum Kind2 { + VARIABLE = 0, + FIELD = 1, + FUNCTION = 2, + MODULE = 3, + CONSTANT = 4, + FILE = 5, + TEXT = 6 +} +export const enum JsCompletionType { + Filter = 0, + Identifier = 1, + Snippets = 2 +} export class NodejsLspFiles { constructor() /** Actions can come from unsaved context. */ @@ -49,6 +74,6 @@ export class NodejsLspFiles { addOne(id: number, filename: string, content: string, line: number): Array getVariables(id: string, line: number): Array | null hover(id: number, filename: string, line: number, position: JsPosition): JsHover | null - static complete(position: JsPosition, id: number, content: string): void + complete(id: number, filename: string, line: number, position: JsPosition): Array | null gotoDefinition(id: number, filename: string, line: number, position: JsPosition): Array | null } diff --git a/jinja-lsp-nodejs/index.js b/jinja-lsp-nodejs/index.js index 0f2dbf8..950e460 100644 --- a/jinja-lsp-nodejs/index.js +++ b/jinja-lsp-nodejs/index.js @@ -310,8 +310,10 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { basic, NodejsLspFiles, JsIdentifierType } = nativeBinding +const { basic, NodejsLspFiles, JsIdentifierType, Kind2, JsCompletionType } = nativeBinding module.exports.basic = basic module.exports.NodejsLspFiles = NodejsLspFiles module.exports.JsIdentifierType = JsIdentifierType +module.exports.Kind2 = Kind2 +module.exports.JsCompletionType = JsCompletionType diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index 1ed7f45..c24cb89 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -7,14 +7,21 @@ use jinja_lsp::{ }; use jinja_lsp_queries::{ parsers::Parsers, - search::{objects::objects_query, queries::Queries, Identifier, IdentifierType}, + search::{ + objects::{objects_query, CompletionType}, + queries::Queries, + snippets_completion::snippets, + Identifier, IdentifierType, + }, to_input_edit::to_position2, }; use tower_lsp::lsp_types::{ - DidOpenTextDocumentParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, - HoverParams, Location, MarkupContent, MarkupKind, PartialResultParams, Position, Range, - TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, + CompletionContext, CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, + CompletionTextEdit, CompletionTriggerKind, DidOpenTextDocumentParams, Documentation, + GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, + InsertReplaceEdit, Location, MarkupContent, MarkupKind, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, WorkDoneProgressParams, }; use tree_sitter::Point; @@ -43,6 +50,7 @@ pub struct NodejsLspFiles { lsp_files: LspFiles, counter: u32, filters: Vec, + snippets: Vec, } #[napi] @@ -53,6 +61,7 @@ impl NodejsLspFiles { lsp_files: LspFiles::default(), counter: 0, filters: init_filter_completions(), + snippets: snippets(), } } @@ -184,6 +193,8 @@ impl NodejsLspFiles { kind: "markdown".to_owned(), value: hover_contents.value, range: Some(JsRange::from(&range)), + label: None, + documentaion: None, }); } } @@ -192,7 +203,96 @@ impl NodejsLspFiles { } #[napi] - pub fn complete(position: JsPosition, id: u32, content: String) {} + pub fn complete( + &self, + id: u32, + filename: String, + line: u32, + mut position: JsPosition, + ) -> Option> { + position.line -= line; + let uri = Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap(); + let position = Position::new(position.line, position.character); + let params: CompletionParams = CompletionParams { + text_document_position: TextDocumentPositionParams::new( + TextDocumentIdentifier::new(uri.clone()), + Position::new(position.line, position.character), + ), + work_done_progress_params: WorkDoneProgressParams { + work_done_token: None, + }, + partial_result_params: PartialResultParams { + ..Default::default() + }, + context: Some(CompletionContext { + trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, + trigger_character: None, + }), + }; + let completion = self.lsp_files.completion(params)?; + let mut items = None; + + match completion { + CompletionType::Filter => { + let completions = self.filters.clone(); + let mut ret = Vec::with_capacity(completions.len()); + for item in completions.into_iter() { + ret.push(JsCompletionItem { + completion_type: JsCompletionType::Filter, + label: item.name, + kind: Kind2::FIELD, + description: item.desc.to_string(), + new_text: None, + insert: None, + replace: None, + }); + } + items = Some(ret); + } + CompletionType::Identifier => { + if let Some(variables) = self.lsp_files.read_variables(&uri, position) { + let mut ret = vec![]; + for item in variables { + ret.push(JsCompletionItem { + completion_type: JsCompletionType::Identifier, + label: item.label, + kind: Kind2::VARIABLE, + description: item.detail.unwrap_or(String::new()), + new_text: None, + insert: None, + replace: None, + }); + } + items = Some(ret); + } + } + CompletionType::IncludedTemplate { .. } => {} + CompletionType::Snippets { .. } => { + // let mut filtered = vec![]; + // for snippet in self.snippets.iter() { + // let mut snippet = snippet.clone(); + // if let Some(CompletionTextEdit::Edit(TextEdit { new_text, .. })) = snippet.text_edit { + // if !self.lsp_files.is_vscode { + // snippet.text_edit = Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { + // new_text, + // insert: range, + // replace: range, + // })); + // } else { + // snippet.text_edit = None; + // } + // } + // filtered.push(snippet); + // } + // } + + // if !filtered.is_empty() { + // items = Some(CompletionResponse::Array(filtered)); + // } + } + }; + items + } #[napi] pub fn goto_definition( @@ -218,7 +318,6 @@ impl NodejsLspFiles { }; let defintion = self.lsp_files.goto_definition(params)?; let mut definitions = vec![]; - println!("here we are, {}", definitions.len()); match defintion { GotoDefinitionResponse::Scalar(mut location) => { let uri2 = location.uri.to_string(); @@ -342,6 +441,8 @@ pub struct JsHover { pub kind: String, pub value: String, pub range: Option, + pub label: Option, + pub documentaion: Option, } #[napi(object)] @@ -364,3 +465,52 @@ impl From<&Location> for JsLocation { } } } + +#[napi(object)] +pub struct JsCompletionItem { + pub completion_type: JsCompletionType, + pub label: String, + pub kind: Kind2, + pub description: String, + pub new_text: Option, + pub insert: Option, + pub replace: Option, +} + +#[napi] +pub enum Kind2 { + VARIABLE, + FIELD, + FUNCTION, + MODULE, + CONSTANT, + FILE, + TEXT, +} + +#[napi] +pub enum JsCompletionType { + Filter, + Identifier, + Snippets, +} + +impl From for Kind2 { + fn from(value: CompletionItemKind) -> Self { + if value == CompletionItemKind::VARIABLE { + Kind2::VARIABLE + } else if value == CompletionItemKind::FIELD { + return Kind2::FIELD; + } else if value == CompletionItemKind::FUNCTION { + return Kind2::FUNCTION; + } else if value == CompletionItemKind::MODULE { + return Kind2::MODULE; + } else if value == CompletionItemKind::CONSTANT { + return Kind2::CONSTANT; + } else if value == CompletionItemKind::FILE { + return Kind2::FILE; + } else { + return Kind2::VARIABLE; + } + } +} diff --git a/jinja-lsp-queries/src/search/snippets_completion.rs b/jinja-lsp-queries/src/search/snippets_completion.rs index d672a09..207f682 100644 --- a/jinja-lsp-queries/src/search/snippets_completion.rs +++ b/jinja-lsp-queries/src/search/snippets_completion.rs @@ -14,7 +14,6 @@ pub struct Snippets { impl Snippets { pub fn check(&mut self, name: &str, capture: &QueryCapture<'_>) -> Option<()> { - dbg!(name); match name { "start" => { let start = capture.node.start_position(); From 3237ba4a689c195abd674be9c44f5cb4f56da044 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Sat, 8 Jun 2024 14:53:26 +0200 Subject: [PATCH 5/8] updated tests --- jinja-lsp-nodejs/src/lib.rs | 20 +++++++++++- jinja-lsp-queries/src/search/mod.rs | 2 +- jinja-lsp-queries/src/search/objects.rs | 22 +++++++++++-- jinja-lsp-queries/src/search/test_queries.rs | 34 +++++++++++++++++--- jinja-lsp/src/channels/lsp.rs | 4 ++- jinja-lsp/src/lsp_files.rs | 17 +++++++++- 6 files changed, 89 insertions(+), 10 deletions(-) diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index c24cb89..c560f2c 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -156,6 +156,7 @@ impl NodejsLspFiles { }; range.start.line += line; range.end.line += line; + let identifier_name = hover.0.name.to_owned(); let range = Some(range); if hover.1 { let filter = self @@ -175,9 +176,11 @@ impl NodejsLspFiles { res = Some(hover); } } else if let Some(data_type) = self.lsp_files.data_type(uri.clone(), hover.0) { + let value = data_type.completion_detail().to_owned(); + let value = format!("{value}\n\n---\n**{}**", identifier_name); let markup_content = MarkupContent { kind: MarkupKind::Markdown, - value: data_type.completion_detail().to_owned(), + value, }; let hover_contents = HoverContents::Markup(markup_content); let hover = Hover { @@ -290,6 +293,21 @@ impl NodejsLspFiles { // items = Some(CompletionResponse::Array(filtered)); // } } + CompletionType::IncompleteIdentifier { name, mut range } => { + range.start.line += line; + range.end.line += line; + let variable = self.lsp_files.get_variable(name, uri.to_string())?; + let ret = vec![JsCompletionItem { + completion_type: JsCompletionType::Identifier, + label: variable.to_string(), + kind: Kind2::VARIABLE, + description: variable.to_string(), + new_text: Some(variable), + insert: Some(JsRange::from(&range)), + replace: Some(JsRange::from(&range)), + }]; + items = Some(ret); + } }; items } diff --git a/jinja-lsp-queries/src/search/mod.rs b/jinja-lsp-queries/src/search/mod.rs index 47c2893..43a7997 100644 --- a/jinja-lsp-queries/src/search/mod.rs +++ b/jinja-lsp-queries/src/search/mod.rs @@ -43,7 +43,7 @@ pub fn completion_start(trigger_point: Point, identifier: &Identifier) -> Option let len = identifier.name.len(); let diff = identifier.end.column - trigger_point.column; if diff == 0 || diff == 1 { - return Some(""); + return Some(&identifier.name); } if diff > len { return None; diff --git a/jinja-lsp-queries/src/search/objects.rs b/jinja-lsp-queries/src/search/objects.rs index be59311..ff75ae7 100644 --- a/jinja-lsp-queries/src/search/objects.rs +++ b/jinja-lsp-queries/src/search/objects.rs @@ -1,6 +1,8 @@ use tower_lsp::lsp_types::Range; use tree_sitter::{Point, Query, QueryCapture, QueryCursor, Tree}; +use super::{completion_start, to_range, Identifier}; + #[derive(Default, Debug, Clone)] pub struct JinjaObject { pub name: String, @@ -104,7 +106,22 @@ impl JinjaObjects { pub fn completion(&self, trigger_point: Point) -> Option { if self.in_pipe(trigger_point) { return Some(CompletionType::Filter); - } else if self.in_expr(trigger_point) { + } + if self.in_expr(trigger_point) { + if trigger_point > self.ident.1 { + return Some(CompletionType::Identifier); + } + if let Some(ident_value) = self.is_ident(trigger_point) { + // if let Some(ident2) = self.objects.last().map(|last| last) { + let identifier = Identifier::new(&ident_value, self.ident.0, self.ident.1); + let start = completion_start(trigger_point, &identifier); + let range = to_range((self.ident.0, self.ident.1)); + return Some(CompletionType::IncompleteIdentifier { + name: start?.to_string(), + range, + }); + // } + } return Some(CompletionType::Identifier); } None @@ -115,7 +132,7 @@ impl JinjaObjects { } pub fn in_expr(&self, trigger_point: Point) -> bool { - trigger_point >= self.expr.0 && trigger_point <= self.expr.1 && trigger_point > self.ident.1 + trigger_point >= self.expr.0 && trigger_point <= self.expr.1 && trigger_point > self.ident.0 } pub fn is_ident(&self, trigger_point: Point) -> Option { @@ -176,6 +193,7 @@ pub enum CompletionType { Identifier, IncludedTemplate { name: String, range: Range }, Snippets { range: Range }, + IncompleteIdentifier { name: String, range: Range }, } static VALID_IDENTIFIERS: [&str; 8] = [ diff --git a/jinja-lsp-queries/src/search/test_queries.rs b/jinja-lsp-queries/src/search/test_queries.rs index b4a8190..cf4b94f 100644 --- a/jinja-lsp-queries/src/search/test_queries.rs +++ b/jinja-lsp-queries/src/search/test_queries.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod query_tests { - use crate::search::{objects::objects_query, snippets_completion::snippets_query}; + use crate::search::{objects::objects_query, snippets_completion::snippets_query, to_range}; use tree_sitter::{Parser, Point}; use crate::{ @@ -195,17 +195,43 @@ mod query_tests { {{ }} {{ "|" }} + {{ identifier }} "#; let cases = [ (Point::new(1, 27), Some(CompletionType::Filter)), - (Point::new(1, 48), None), + ( + Point::new(1, 48), + Some(CompletionType::IncompleteIdentifier { + name: "filter2".to_string(), + range: to_range((Point::new(1, 41), Point::new(1, 48))), + }), + ), (Point::new(1, 40), Some(CompletionType::Filter)), (Point::new(1, 50), Some(CompletionType::Identifier)), - (Point::new(3, 18), None), + ( + Point::new(3, 18), + None, // Some(CompletionType::IncompleteIdentifier { + // name: "something".to_owned(), + // range: to_range((Point::new(3, 18), Point::new(3, 27))), + // }), + ), (Point::new(4, 20), None), - (Point::new(3, 22), None), + ( + Point::new(3, 22), + None, // Some(CompletionType::IncompleteIdentifier { + // name: "something".to_owned(), + // range: to_range((Point::new(3, 18), Point::new(3, 27))), + // }), + ), (Point::new(8, 15), Some(CompletionType::Identifier)), (Point::new(9, 18), Some(CompletionType::Identifier)), + ( + Point::new(10, 18), + Some(CompletionType::IncompleteIdentifier { + name: "iden".to_string(), + range: to_range((Point::new(10, 15), Point::new(10, 25))), + }), + ), ]; for case in cases { let tree = prepare_jinja_tree(source); diff --git a/jinja-lsp/src/channels/lsp.rs b/jinja-lsp/src/channels/lsp.rs index 8ca7e85..0566420 100644 --- a/jinja-lsp/src/channels/lsp.rs +++ b/jinja-lsp/src/channels/lsp.rs @@ -182,7 +182,8 @@ pub fn lsp_task( } } CompletionType::IncludedTemplate { name, range } => { - if let Some(templates) = lsp_data.read_templates(name, range) { + if let Some(templates) = lsp_data.read_templates(name, range, None) + { items = Some(CompletionResponse::Array(templates)); } } @@ -215,6 +216,7 @@ pub fn lsp_task( items = Some(CompletionResponse::Array(filtered)); } } + CompletionType::IncompleteIdentifier { name, range } => {} }; } let _ = sender.send(items); diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index 73f5e23..bfa9386 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -622,7 +622,12 @@ impl LspFiles { Some(items) } - pub fn read_templates(&self, mut prefix: String, range: Range) -> Option> { + pub fn read_templates( + &self, + mut prefix: String, + range: Range, + _: Option, + ) -> Option> { let all_templates = self.trees.get(&LangType::Template)?; if prefix.is_empty() { prefix = String::from("file:///"); @@ -663,6 +668,16 @@ impl LspFiles { Some(abc) } + pub fn get_variable(&self, prefix: String, id: String) -> Option { + let variables = self.variables.get(&id)?; + for variable in variables { + if variable.name.contains(&prefix) { + return Some(variable.name.to_string()); + } + } + None + } + pub fn did_open(&mut self, params: DidOpenTextDocumentParams) -> Option { let name = params.text_document.uri.as_str(); let lang_type = self.config.file_ext(&Path::new(name))?; From b64fb18ed3123265e0d59b2c61fdc86da113da11 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Sat, 8 Jun 2024 18:03:26 +0200 Subject: [PATCH 6/8] updated uri of global variables for customization --- jinja-lsp-nodejs/index.d.ts | 4 +++- jinja-lsp-nodejs/src/lib.rs | 28 +++++++++++++++++++++++----- jinja-lsp/src/lsp_files.rs | 11 ++++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index 1e8530d..66dbf2c 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -42,6 +42,8 @@ export interface JsRange { export interface JsLocation { uri: string range: JsRange + isBackend: boolean + name: string } export interface JsCompletionItem { completionType: JsCompletionType @@ -69,7 +71,7 @@ export const enum JsCompletionType { export class NodejsLspFiles { constructor() /** Actions can come from unsaved context. */ - addGlobalContext(actions?: Array | undefined | null): void + addGlobalContext(uri: string, actions?: Array | undefined | null): void deleteAll(filename: string): void addOne(id: number, filename: string, content: string, line: number): Array getVariables(id: string, line: number): Array | null diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index c560f2c..fd56b12 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -67,12 +67,24 @@ impl NodejsLspFiles { /// Actions can come from unsaved context. #[napi] - pub fn add_global_context(&self, actions: Option>) {} + pub fn add_global_context(&mut self, uri: String, actions: Option>) { + if let Some(actions) = actions { + let mut identifiers = vec![]; + for action in actions { + let mut identifier = action.split('.'); + if let Some(identifier) = identifier.next() { + let mut identifier = Identifier::new(identifier, Point::new(0, 0), Point::new(0, 0)); + identifier.identifier_type = IdentifierType::BackendVariable; + identifiers.push(identifier); + } + } + self.lsp_files.variables.insert(uri, identifiers); + } + } #[napi] pub fn delete_all(&mut self, filename: String) { - self.lsp_files.variables.clear(); - self.lsp_files.delete_documents(); + self.lsp_files.delete_documents_with_id(filename); self.counter = 0; // self.lsp_files.main_channel } @@ -350,10 +362,12 @@ impl NodejsLspFiles { for mut location in locations { let uri2 = location.uri.to_string(); if uri2.contains(&filename) { - location.uri = Url::parse(&filename).unwrap(); + location.uri = Url::parse(&uri2).unwrap(); location.range.start.line += line; location.range.end.line += line; - definitions.push(JsLocation::from(&location)); + let mut js_location = JsLocation::from(&location); + js_location.is_backend = true; + definitions.push(js_location); } } } @@ -473,6 +487,8 @@ pub struct JsRange { pub struct JsLocation { pub uri: String, pub range: JsRange, + pub is_backend: bool, + pub name: String, } impl From<&Location> for JsLocation { @@ -480,6 +496,8 @@ impl From<&Location> for JsLocation { Self { uri: value.uri.to_string(), range: JsRange::from(&value.range), + is_backend: false, + name: String::new(), } } } diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index bfa9386..49bed9b 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -424,7 +424,15 @@ impl LspFiles { } let variables = file.1.iter().filter(|item| item.name == current_ident); for variable in variables { - let uri = Url::parse(file.0).unwrap(); + let uri = { + if variable.start == Point::new(0, 0) + && variable.end == Point::new(0, 0) + { + Url::parse(&format!("{}-{}", &file.0, &variable.name)).unwrap() + } else { + Url::parse(file.0).unwrap() + } + }; let start = to_position2(variable.start); let end = to_position2(variable.end); let range = Range::new(start, end); @@ -745,6 +753,7 @@ impl LspFiles { } for i in ids { self.documents.remove(&i); + self.variables.remove(&i); } } } From 18f5b96d62f3be9fd34aa54a3da74062a2d6e0a7 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Tue, 11 Jun 2024 13:08:55 +0200 Subject: [PATCH 7/8] updated npm version --- .github/workflows/CI-nodejs.yml | 2 +- jinja-lsp-nodejs/index.d.ts | 9 +- .../npm/android-arm-eabi/package.json | 2 +- .../npm/android-arm64/package.json | 2 +- .../npm/darwin-arm64/package.json | 2 +- .../npm/darwin-universal/package.json | 2 +- jinja-lsp-nodejs/npm/freebsd-x64/package.json | 2 +- jinja-lsp-nodejs/npm/gnu-linux/package.json | 2 +- .../npm/linux-arm-gnueabihf/package.json | 2 +- .../npm/linux-arm-musleabihf/package.json | 2 +- .../npm/linux-arm64-gnu/package.json | 2 +- .../npm/linux-arm64-musl/package.json | 2 +- .../npm/linux-riscv64-gnu/package.json | 2 +- .../npm/linux-x64-gnu/package.json | 2 +- .../npm/linux-x64-musl/package.json | 2 +- .../npm/win32-arm64-msvc/package.json | 2 +- .../npm/win32-ia32-msvc/package.json | 2 +- jinja-lsp-nodejs/package-lock.json | 4 +- jinja-lsp-nodejs/package.json | 2 +- jinja-lsp-nodejs/src/lib.rs | 158 ++++++++++++++---- jinja-lsp-queries/src/search/mod.rs | 16 +- jinja-lsp-queries/src/search/objects.rs | 7 +- .../src/search/python_identifiers.rs | 24 +++ jinja-lsp-queries/src/search/queries.rs | 15 ++ jinja-lsp-queries/src/search/test_queries.rs | 20 ++- jinja-lsp/src/backend.rs | 2 +- jinja-lsp/src/channels/lsp.rs | 2 +- jinja-lsp/src/lsp_files.rs | 53 +++++- jinja-lsp/src/main.rs | 2 +- 29 files changed, 277 insertions(+), 69 deletions(-) create mode 100644 jinja-lsp-queries/src/search/python_identifiers.rs diff --git a/.github/workflows/CI-nodejs.yml b/.github/workflows/CI-nodejs.yml index fc9160c..5609b39 100644 --- a/.github/workflows/CI-nodejs.yml +++ b/.github/workflows/CI-nodejs.yml @@ -9,7 +9,7 @@ permissions: 'on': push: branches: - - npm + - nodejs tags-ignore: - '**' paths-ignore: diff --git a/jinja-lsp-nodejs/index.d.ts b/jinja-lsp-nodejs/index.d.ts index 66dbf2c..5f3b749 100644 --- a/jinja-lsp-nodejs/index.d.ts +++ b/jinja-lsp-nodejs/index.d.ts @@ -19,7 +19,8 @@ export const enum JsIdentifierType { TemplateBlock = 7, BackendVariable = 8, UndefinedVariable = 9, - JinjaTemplate = 10 + JinjaTemplate = 10, + Link = 11 } export interface JsIdentifier { start: JsPosition @@ -68,10 +69,14 @@ export const enum JsCompletionType { Identifier = 1, Snippets = 2 } +export interface Action { + name: string + description: string +} export class NodejsLspFiles { constructor() /** Actions can come from unsaved context. */ - addGlobalContext(uri: string, actions?: Array | undefined | null): void + addGlobalContext(uri: string, actions?: Array | undefined | null): void deleteAll(filename: string): void addOne(id: number, filename: string, content: string, line: number): Array getVariables(id: string, line: number): Array | null diff --git a/jinja-lsp-nodejs/npm/android-arm-eabi/package.json b/jinja-lsp-nodejs/npm/android-arm-eabi/package.json index 1ce0c0c..6d31680 100644 --- a/jinja-lsp-nodejs/npm/android-arm-eabi/package.json +++ b/jinja-lsp-nodejs/npm/android-arm-eabi/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-android-arm-eabi", - "version": "0.0.2", + "version": "0.0.3", "os": [ "android" ], diff --git a/jinja-lsp-nodejs/npm/android-arm64/package.json b/jinja-lsp-nodejs/npm/android-arm64/package.json index 5962099..b484a5b 100644 --- a/jinja-lsp-nodejs/npm/android-arm64/package.json +++ b/jinja-lsp-nodejs/npm/android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-android-arm64", - "version": "0.0.2", + "version": "0.0.3", "os": [ "android" ], diff --git a/jinja-lsp-nodejs/npm/darwin-arm64/package.json b/jinja-lsp-nodejs/npm/darwin-arm64/package.json index d7a8c71..b32248e 100644 --- a/jinja-lsp-nodejs/npm/darwin-arm64/package.json +++ b/jinja-lsp-nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-darwin-arm64", - "version": "0.0.2", + "version": "0.0.3", "os": [ "darwin" ], diff --git a/jinja-lsp-nodejs/npm/darwin-universal/package.json b/jinja-lsp-nodejs/npm/darwin-universal/package.json index 936b426..a3736ae 100644 --- a/jinja-lsp-nodejs/npm/darwin-universal/package.json +++ b/jinja-lsp-nodejs/npm/darwin-universal/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-darwin-universal", - "version": "0.0.2", + "version": "0.0.3", "os": [ "darwin" ], diff --git a/jinja-lsp-nodejs/npm/freebsd-x64/package.json b/jinja-lsp-nodejs/npm/freebsd-x64/package.json index f3a8ca4..c383f80 100644 --- a/jinja-lsp-nodejs/npm/freebsd-x64/package.json +++ b/jinja-lsp-nodejs/npm/freebsd-x64/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-freebsd-x64", - "version": "0.0.2", + "version": "0.0.3", "os": [ "freebsd" ], diff --git a/jinja-lsp-nodejs/npm/gnu-linux/package.json b/jinja-lsp-nodejs/npm/gnu-linux/package.json index 86eef40..087d805 100644 --- a/jinja-lsp-nodejs/npm/gnu-linux/package.json +++ b/jinja-lsp-nodejs/npm/gnu-linux/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-gnu-linux", - "version": "0.0.2", + "version": "0.0.3", "os": [ "gnu", "linux" diff --git a/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/package.json b/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/package.json index 42bd33a..6f05124 100644 --- a/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/package.json +++ b/jinja-lsp-nodejs/npm/linux-arm-gnueabihf/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-arm-gnueabihf", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-arm-musleabihf/package.json b/jinja-lsp-nodejs/npm/linux-arm-musleabihf/package.json index 336b84a..2fe366b 100644 --- a/jinja-lsp-nodejs/npm/linux-arm-musleabihf/package.json +++ b/jinja-lsp-nodejs/npm/linux-arm-musleabihf/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-arm-musleabihf", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-arm64-gnu/package.json b/jinja-lsp-nodejs/npm/linux-arm64-gnu/package.json index a888469..a7bd6dc 100644 --- a/jinja-lsp-nodejs/npm/linux-arm64-gnu/package.json +++ b/jinja-lsp-nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-arm64-gnu", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-arm64-musl/package.json b/jinja-lsp-nodejs/npm/linux-arm64-musl/package.json index 92501b4..4a122dd 100644 --- a/jinja-lsp-nodejs/npm/linux-arm64-musl/package.json +++ b/jinja-lsp-nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-arm64-musl", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-riscv64-gnu/package.json b/jinja-lsp-nodejs/npm/linux-riscv64-gnu/package.json index b2e2c48..3875327 100644 --- a/jinja-lsp-nodejs/npm/linux-riscv64-gnu/package.json +++ b/jinja-lsp-nodejs/npm/linux-riscv64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-riscv64-gnu", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-x64-gnu/package.json b/jinja-lsp-nodejs/npm/linux-x64-gnu/package.json index d09db88..7688952 100644 --- a/jinja-lsp-nodejs/npm/linux-x64-gnu/package.json +++ b/jinja-lsp-nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-x64-gnu", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/linux-x64-musl/package.json b/jinja-lsp-nodejs/npm/linux-x64-musl/package.json index 218814a..1442d6f 100644 --- a/jinja-lsp-nodejs/npm/linux-x64-musl/package.json +++ b/jinja-lsp-nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-linux-x64-musl", - "version": "0.0.2", + "version": "0.0.3", "os": [ "linux" ], diff --git a/jinja-lsp-nodejs/npm/win32-arm64-msvc/package.json b/jinja-lsp-nodejs/npm/win32-arm64-msvc/package.json index 85ddd25..2604054 100644 --- a/jinja-lsp-nodejs/npm/win32-arm64-msvc/package.json +++ b/jinja-lsp-nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-win32-arm64-msvc", - "version": "0.0.2", + "version": "0.0.3", "os": [ "win32" ], diff --git a/jinja-lsp-nodejs/npm/win32-ia32-msvc/package.json b/jinja-lsp-nodejs/npm/win32-ia32-msvc/package.json index 6c58a8c..dec91ea 100644 --- a/jinja-lsp-nodejs/npm/win32-ia32-msvc/package.json +++ b/jinja-lsp-nodejs/npm/win32-ia32-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions-win32-ia32-msvc", - "version": "0.0.2", + "version": "0.0.3", "os": [ "win32" ], diff --git a/jinja-lsp-nodejs/package-lock.json b/jinja-lsp-nodejs/package-lock.json index c850fd0..08efa31 100644 --- a/jinja-lsp-nodejs/package-lock.json +++ b/jinja-lsp-nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jinja-lsp/functions", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jinja-lsp/functions", - "version": "0.0.2", + "version": "0.0.3", "license": "MIT", "devDependencies": { "@napi-rs/cli": "^2.18.3", diff --git a/jinja-lsp-nodejs/package.json b/jinja-lsp-nodejs/package.json index 0b2f60f..ec634b9 100644 --- a/jinja-lsp-nodejs/package.json +++ b/jinja-lsp-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@jinja-lsp/functions", - "version": "0.0.2", + "version": "0.0.3", "main": "index.js", "types": "index.d.ts", "napi": { diff --git a/jinja-lsp-nodejs/src/lib.rs b/jinja-lsp-nodejs/src/lib.rs index fd56b12..99b64c3 100644 --- a/jinja-lsp-nodejs/src/lib.rs +++ b/jinja-lsp-nodejs/src/lib.rs @@ -1,14 +1,15 @@ #![deny(clippy::all)] +use std::collections::HashMap; + use jinja_lsp::{ - channels::diagnostics::DiagnosticMessage, filter::{init_filter_completions, FilterCompletion}, lsp_files::LspFiles, }; use jinja_lsp_queries::{ parsers::Parsers, search::{ - objects::{objects_query, CompletionType}, + objects::{objects_query, CompletionType, JinjaObject}, queries::Queries, snippets_completion::snippets, Identifier, IdentifierType, @@ -17,11 +18,10 @@ use jinja_lsp_queries::{ }; use tower_lsp::lsp_types::{ - CompletionContext, CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, - CompletionTextEdit, CompletionTriggerKind, DidOpenTextDocumentParams, Documentation, - GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, - InsertReplaceEdit, Location, MarkupContent, MarkupKind, PartialResultParams, Position, Range, - TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, + CompletionContext, CompletionItem, CompletionItemKind, CompletionParams, CompletionTriggerKind, + DidOpenTextDocumentParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, + HoverParams, Location, MarkupContent, MarkupKind, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, WorkDoneProgressParams, }; use tree_sitter::Point; @@ -50,7 +50,9 @@ pub struct NodejsLspFiles { lsp_files: LspFiles, counter: u32, filters: Vec, - snippets: Vec, + _snippets: Vec, + actions: HashMap>, + action_objects: HashMap>, } #[napi] @@ -61,23 +63,27 @@ impl NodejsLspFiles { lsp_files: LspFiles::default(), counter: 0, filters: init_filter_completions(), - snippets: snippets(), + _snippets: snippets(), + actions: HashMap::new(), + action_objects: HashMap::new(), } } /// Actions can come from unsaved context. #[napi] - pub fn add_global_context(&mut self, uri: String, actions: Option>) { + pub fn add_global_context(&mut self, uri: String, actions: Option>) { if let Some(actions) = actions { let mut identifiers = vec![]; - for action in actions { - let mut identifier = action.split('.'); - if let Some(identifier) = identifier.next() { - let mut identifier = Identifier::new(identifier, Point::new(0, 0), Point::new(0, 0)); - identifier.identifier_type = IdentifierType::BackendVariable; - identifiers.push(identifier); - } + let mut action_objects = vec![]; + for action in &actions { + let mut identifier = Identifier::new(&action.name, Point::new(0, 0), Point::new(0, 0)); + identifier.identifier_type = IdentifierType::BackendVariable; + identifiers.push(identifier); + let action = ActionObject::from(action); + action_objects.push(action); } + self.actions.insert(uri.to_string(), actions); + self.action_objects.insert(uri.to_string(), action_objects); self.lsp_files.variables.insert(uri, identifiers); } } @@ -97,6 +103,7 @@ impl NodejsLspFiles { content: String, line: u32, ) -> Vec { + let mut all_identifiers = vec![]; let params: DidOpenTextDocumentParams = DidOpenTextDocumentParams { text_document: TextDocumentItem::new( Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap(), @@ -105,27 +112,55 @@ impl NodejsLspFiles { content, ), }; - let content = self.lsp_files.did_open(params); - let mut all_errors = vec![]; - if let Some(content) = content { - match content { - DiagnosticMessage::Errors(errors) => { - for i in errors { - for error in i.1 { - let diagnostic = error.0.to_string(); - let mut position = error.1; - position.start.row += line as usize; - position.end.row += line as usize; - let mut identifier = JsIdentifier::from(&position); - identifier.error = Some(diagnostic); - all_errors.push(identifier); + self.lsp_files.did_open(params); + let objects = self + .lsp_files + .read_objects(Url::parse(&format!("file:///home/{filename}.{id}.jinja")).unwrap()); + if let Some(objects) = objects { + if let Some(global_actions) = self.action_objects.get(&filename.to_string()) { + for obj in &objects { + let action_object = ActionObject::from(obj); + for global_action in global_actions { + if global_action.compare(&action_object.fields) { + let mut start = JsPosition::from(obj.location.0); + let mut end = JsPosition::from(obj.last_field_end()); + start.line += line; + end.line += line; + let identifier = JsIdentifier { + start, + end, + name: obj.name.to_owned(), + identifier_type: JsIdentifierType::Link, + error: None, + }; + all_identifiers.push(identifier); } } } - DiagnosticMessage::Str(_) => {} } } - all_errors + // let query = &self.lsp_files.queries.jinja_objects; + // let objects = objects_query(query, &tree, Point::new(0, 0), &content, true); + // let objects = objects.show(); + // if let Some(content) = content { + // match content { + // DiagnosticMessage::Errors(errors) => { + // for i in errors { + // for error in i.1 { + // let diagnostic = error.0.to_string(); + // let mut position = error.1; + // position.start.row += line as usize; + // position.end.row += line as usize; + // let mut identifier = JsIdentifier::from(&position); + // identifier.error = Some(diagnostic); + // all_errors.push(identifier); + // } + // } + // } + // DiagnosticMessage::Str(_) => {} + // } + // } + all_identifiers } #[napi] @@ -168,8 +203,8 @@ impl NodejsLspFiles { }; range.start.line += line; range.end.line += line; - let identifier_name = hover.0.name.to_owned(); let range = Some(range); + let full_name = hover.0.name.to_owned(); if hover.1 { let filter = self .filters @@ -189,10 +224,20 @@ impl NodejsLspFiles { } } else if let Some(data_type) = self.lsp_files.data_type(uri.clone(), hover.0) { let value = data_type.completion_detail().to_owned(); - let value = format!("{value}\n\n---\n**{}**", identifier_name); + let value = format!("{value}\n\n---\n**{}**", &full_name); + let actions = vec![]; + let actions = self.actions.get(&filename).unwrap_or(&actions); + let action = Action { + name: full_name.to_owned(), + description: value.to_owned(), + }; + let value = actions + .iter() + .find(|item| item.name.starts_with(&full_name)) + .unwrap_or(&action); let markup_content = MarkupContent { kind: MarkupKind::Markdown, - value, + value: value.description.to_owned(), }; let hover_contents = HoverContents::Markup(markup_content); let hover = Hover { @@ -360,6 +405,7 @@ impl NodejsLspFiles { } GotoDefinitionResponse::Array(locations) => { for mut location in locations { + print!("{}", location.uri); let uri2 = location.uri.to_string(); if uri2.contains(&filename) { location.uri = Url::parse(&uri2).unwrap(); @@ -417,6 +463,7 @@ pub enum JsIdentifierType { #[default] UndefinedVariable, JinjaTemplate, + Link, } #[napi(object)] @@ -550,3 +597,42 @@ impl From for Kind2 { } } } + +#[napi(object)] +pub struct Action { + pub name: String, + pub description: String, +} + +pub struct ActionObject { + pub fields: Vec, +} + +impl From<&Action> for ActionObject { + fn from(value: &Action) -> Self { + let parts = value.name.split('.'); + let mut fields = vec![]; + for part in parts { + fields.push(part.to_owned()); + } + Self { fields } + } +} + +impl From<&JinjaObject> for ActionObject { + fn from(value: &JinjaObject) -> Self { + let mut fields = vec![]; + fields.push(value.name.to_owned()); + for field in &value.fields { + fields.push(field.0.to_owned()); + } + Self { fields } + } +} + +impl ActionObject { + pub fn compare(&self, fields: &Vec) -> bool { + &self.fields == fields + // self.fields == fields + } +} diff --git a/jinja-lsp-queries/src/search/mod.rs b/jinja-lsp-queries/src/search/mod.rs index 43a7997..2137d7e 100644 --- a/jinja-lsp-queries/src/search/mod.rs +++ b/jinja-lsp-queries/src/search/mod.rs @@ -5,6 +5,7 @@ use self::objects::JinjaObject; pub mod definition; pub mod objects; +mod python_identifiers; pub mod queries; pub mod rust_identifiers; pub mod rust_template_completion; @@ -19,6 +20,7 @@ pub struct Identifier { pub name: String, pub scope_ends: (usize, Point), pub identifier_type: IdentifierType, + pub fields: Vec<(String, (Point, Point))>, } impl Identifier { @@ -29,13 +31,25 @@ impl Identifier { end, scope_ends: (0, Point::default()), identifier_type: IdentifierType::UndefinedVariable, + fields: Vec::new(), } } + + pub fn merge(&self) -> String { + let mut merged = self.name.to_string(); + for field in &self.fields { + merged.push('.'); + merged.push_str(&field.0); + } + merged + } } impl From<&JinjaObject> for Identifier { fn from(value: &JinjaObject) -> Self { - Identifier::new(&value.name, value.location.0, value.location.1) + let mut identifier = Identifier::new(&value.name, value.location.0, value.location.1); + identifier.fields.clone_from(&value.fields); + identifier } } diff --git a/jinja-lsp-queries/src/search/objects.rs b/jinja-lsp-queries/src/search/objects.rs index ff75ae7..f087dd1 100644 --- a/jinja-lsp-queries/src/search/objects.rs +++ b/jinja-lsp-queries/src/search/objects.rs @@ -8,7 +8,7 @@ pub struct JinjaObject { pub name: String, pub location: (Point, Point), pub is_filter: bool, - fields: Vec<(String, (Point, Point))>, + pub fields: Vec<(String, (Point, Point))>, } impl JinjaObject { @@ -24,6 +24,11 @@ impl JinjaObject { pub fn add_field(&mut self, field: String, start: Point, end: Point) { self.fields.push((field, (start, end))); } + + pub fn last_field_end(&self) -> Point { + let last = self.fields.last().map_or(self.location.1, |v| v.1 .1); + last + } } #[derive(Default, Debug)] diff --git a/jinja-lsp-queries/src/search/python_identifiers.rs b/jinja-lsp-queries/src/search/python_identifiers.rs new file mode 100644 index 0000000..0e61728 --- /dev/null +++ b/jinja-lsp-queries/src/search/python_identifiers.rs @@ -0,0 +1,24 @@ +use tree_sitter::{Point, Query, QueryCursor, Tree}; + +pub fn _python_identifiers( + query: &Query, + tree: &Tree, + mut _trigger_point: Point, + text: &str, + all: bool, +) { + let closest_node = tree.root_node(); + let mut cursor_qry = QueryCursor::new(); + let _capture_names = query.capture_names(); + let matches = cursor_qry.matches(query, closest_node, text.as_bytes()); + let captures = matches.into_iter().flat_map(|m| { + m.captures + .iter() + .filter(|capture| all || capture.node.start_position() <= _trigger_point) + }); + for _capture in captures { + // if check.is_none() { + // break; + // } + } +} diff --git a/jinja-lsp-queries/src/search/queries.rs b/jinja-lsp-queries/src/search/queries.rs index a532f3d..10670b1 100644 --- a/jinja-lsp-queries/src/search/queries.rs +++ b/jinja-lsp-queries/src/search/queries.rs @@ -8,6 +8,7 @@ pub struct Queries { pub backend_definitions: Query, pub backend_templates: Query, pub jinja_snippets: Query, + pub python_identifiers: Query, } impl Clone for Queries { @@ -26,6 +27,8 @@ impl Default for Queries { jinja_imports: Query::new(&tree_sitter_jinja2::language(), JINJA_IMPORTS).unwrap(), backend_templates: Query::new(&tree_sitter_rust::language(), RUST_TEMPLATES).unwrap(), jinja_snippets: Query::new(&tree_sitter_jinja2::language(), JINJA_SNIPPETS).unwrap(), + python_identifiers: Query::new(&tree_sitter_python::language(), PYTHON_IDENTIFIERS) + .unwrap(), } } } @@ -37,6 +40,8 @@ impl Queries { Query::new(&tree_sitter_python::language(), PYTHON_TEMPLATES).unwrap(); self.backend_definitions = Query::new(&tree_sitter_python::language(), PYTHON_DEFINITIONS).unwrap(); + self.python_identifiers = + Query::new(&tree_sitter_python::language(), PYTHON_IDENTIFIERS).unwrap(); } } } @@ -272,3 +277,13 @@ pub static PYTHON_DEFINITIONS: &str = r#" (ERROR) @error "#; + +const PYTHON_IDENTIFIERS: &str = r#" +(_ + (identifier) @identifier +) + +(attribute) @attribute + +(ERROR) @error +"#; diff --git a/jinja-lsp-queries/src/search/test_queries.rs b/jinja-lsp-queries/src/search/test_queries.rs index cf4b94f..c475de8 100644 --- a/jinja-lsp-queries/src/search/test_queries.rs +++ b/jinja-lsp-queries/src/search/test_queries.rs @@ -1,6 +1,9 @@ #[cfg(test)] mod query_tests { - use crate::search::{objects::objects_query, snippets_completion::snippets_query, to_range}; + use crate::search::{ + objects::objects_query, python_identifiers::_python_identifiers, + snippets_completion::snippets_query, to_range, + }; use tree_sitter::{Parser, Point}; use crate::{ @@ -384,4 +387,19 @@ mod query_tests { assert_eq!(snippets.is_error, case.2); } } + + #[test] + fn test_python_identifiers() { + let cases = [r#" + [page.text + for page in retrieval.result.abc] + "#]; + + let query = Queries::default(); + let query = query.python_identifiers; + for _case in cases { + let tree = prepare_python_tree(cases[0]); + _python_identifiers(&query, &tree, Point::new(0, 0), cases[0], true); + } + } } diff --git a/jinja-lsp/src/backend.rs b/jinja-lsp/src/backend.rs index 8795484..e33a91a 100644 --- a/jinja-lsp/src/backend.rs +++ b/jinja-lsp/src/backend.rs @@ -154,7 +154,7 @@ impl LanguageServer for Backend { } impl Backend { - pub fn new(client: Client) -> Self { + pub fn _new(client: Client) -> Self { let (lsp_sender, lsp_recv) = mpsc::channel(50); let (diagnostic_sender, diagnostic_recv) = mpsc::channel(20); lsp_task( diff --git a/jinja-lsp/src/channels/lsp.rs b/jinja-lsp/src/channels/lsp.rs index 0566420..69bbeaa 100644 --- a/jinja-lsp/src/channels/lsp.rs +++ b/jinja-lsp/src/channels/lsp.rs @@ -216,7 +216,7 @@ pub fn lsp_task( items = Some(CompletionResponse::Array(filtered)); } } - CompletionType::IncompleteIdentifier { name, range } => {} + CompletionType::IncompleteIdentifier { .. } => {} }; } let _ = sender.send(items); diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index 49bed9b..52c8cfd 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -1,10 +1,15 @@ use jinja_lsp_queries::{ lsp_helper::{path_items, search_errors}, search::{ - completion_start, definition::definition_query, objects::objects_query, queries::Queries, + completion_start, + definition::definition_query, + objects::{objects_query, JinjaObject}, + queries::Queries, rust_identifiers::backend_definition_query, - rust_template_completion::backend_templates_query, snippets_completion::snippets_query, - templates::templates_query, to_range, Identifier, IdentifierType, + rust_template_completion::backend_templates_query, + snippets_completion::snippets_query, + templates::templates_query, + to_range, Identifier, IdentifierType, }, tree_builder::{JinjaDiagnostic, LangType}, }; @@ -422,7 +427,9 @@ impl LspFiles { if file.0 == &uri { continue; } - let variables = file.1.iter().filter(|item| item.name == current_ident); + let variables = file.1.iter().filter(|item| { + item.name.split('.').next().unwrap_or(&item.name) == current_ident + }); for variable in variables { let uri = { if variable.start == Point::new(0, 0) @@ -702,6 +709,25 @@ impl LspFiles { Some(msg) } + pub fn read_objects(&self, uri: Url) -> Option> { + let rope = self.documents.get(uri.as_str())?; + let mut writter = FileContent::default(); + let _ = rope.write_to(&mut writter); + let content = writter.content; + let lang_type = self.config.file_ext(&Path::new(uri.as_str()))?; + let trees = self.trees.get(&lang_type)?; + let tree = trees.get(uri.as_str())?; + let objects = objects_query( + &self.queries.jinja_objects, + tree, + Point::new(0, 0), + &content, + true, + ); + let objects = objects.show(); + Some(objects) + } + pub fn data_type(&self, uri: Url, hover: Identifier) -> Option { let this_file = self.variables.get(uri.as_str())?; let this_file = this_file @@ -713,8 +739,23 @@ impl LspFiles { let same_name = hover.name == variable.name; bigger && in_scope && same_name }) - .max()?; - Some(this_file.identifier_type.clone()) + .max(); + if let Some(this_file) = this_file { + return Some(this_file.identifier_type.clone()); + } + for file in &self.variables { + if file.0 == &uri.to_string() { + continue; + } + let variables = file + .1 + .iter() + .filter(|item| item.name.split('.').next().unwrap_or(&item.name) == hover.name); + if variables.count() > 0 { + return Some(IdentifierType::BackendVariable); + } + } + None } pub fn document_symbols( diff --git a/jinja-lsp/src/main.rs b/jinja-lsp/src/main.rs index 1f6209f..1541114 100644 --- a/jinja-lsp/src/main.rs +++ b/jinja-lsp/src/main.rs @@ -15,7 +15,7 @@ async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let (service, socket) = LspService::build(Backend::new).finish(); + let (service, socket) = LspService::build(Backend::_new).finish(); Server::new(stdin, stdout, socket).serve(service).await; } From 522fc2db9c7c39a18aa6ae6a0bab9e9773973b46 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Tue, 11 Jun 2024 13:09:37 +0200 Subject: [PATCH 8/8] 0.0.3 --- .github/workflows/CI-nodejs.yml | 1 + act_script.sh | 3 +++ jinja-lsp-nodejs/__test__/index.spec.mjs | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 act_script.sh diff --git a/.github/workflows/CI-nodejs.yml b/.github/workflows/CI-nodejs.yml index 5609b39..1f406ec 100644 --- a/.github/workflows/CI-nodejs.yml +++ b/.github/workflows/CI-nodejs.yml @@ -190,6 +190,7 @@ jobs: # name: bindings-freebsd # path: jinja-lsp-nodejs/${{ env.APP_NAME }}.*.node # if-no-files-found: error + test-macOS-windows-binding: defaults: run: diff --git a/act_script.sh b/act_script.sh new file mode 100644 index 0000000..6f957c3 --- /dev/null +++ b/act_script.sh @@ -0,0 +1,3 @@ + +# act -j show -P macos-latest=sickcodes/docker-osx -P windows-latest=dockurr/windows +act --env-file .env -W .github/workflows/CI-nodejs.yaml -P macos-latest=sickcodes/docker-osx -P windows-latest=dockurr/windows diff --git a/jinja-lsp-nodejs/__test__/index.spec.mjs b/jinja-lsp-nodejs/__test__/index.spec.mjs index 1ade4ca..ebbb3b5 100644 --- a/jinja-lsp-nodejs/__test__/index.spec.mjs +++ b/jinja-lsp-nodejs/__test__/index.spec.mjs @@ -1,7 +1,7 @@ import test from 'ava' -import { sum } from '../index.js' +import { NodejsLspFiles } from '../index.js' -test('sum from native', (t) => { - t.is(sum(1, 2), 3) +test('main class', (t) => { + t.is(new NodejsLspFiles().getVariables("id", 11), null); })