From 7f407f4dfe6771527605fee7401e8d772136f569 Mon Sep 17 00:00:00 2001 From: CoolLoong Date: Wed, 11 Sep 2024 15:59:25 +0800 Subject: [PATCH] fix: no completions at the end of Binding array fix incorrect region in `get_boundary_indices` fix extra quote in type completion fix color variables like [1,1,1] not showing color picker --- client/package.json | 2 +- out/jsonui_define.json | 72 +++++--------------- package.json | 2 +- src/completion.rs | 140 ++++++++++++++++++++++++++------------- src/completion_helper.rs | 102 ++++++++++++++++------------ src/document.rs | 123 +++++++++++++++++++++------------- 6 files changed, 251 insertions(+), 190 deletions(-) diff --git a/client/package.json b/client/package.json index 62b79ee..618e5e7 100644 --- a/client/package.json +++ b/client/package.json @@ -2,7 +2,7 @@ "name": "jsonui-support-client", "description": "A VSCode extension providing intelligent code completion for Minecraft Bedrock JSON-UI.", "license": "MIT", - "version": "0.0.1", + "version": "0.0.2", "engines": { "vscode": "^1.85.0" }, diff --git a/out/jsonui_define.json b/out/jsonui_define.json index 00fc08d..ea03aca 100644 --- a/out/jsonui_define.json +++ b/out/jsonui_define.json @@ -1943,111 +1943,75 @@ "values": [ { "label": "label", - "description": { "zh-cn": "文本控件", "en-us": "text control" }, - "insert_text_format": 1, - "insert_text": "\"label\"" + "description": { "zh-cn": "文本控件", "en-us": "text control" } }, { "label": "panel", - "description": { "zh-cn": "面板控件", "en-us": "panel" }, - "insert_text_format": 1, - "insert_text": "\"panel\"" + "description": { "zh-cn": "面板控件", "en-us": "panel" } }, { "label": "stack_panel", - "description": { "zh-cn": "堆栈面板控件", "en-us": "stack panel" }, - "insert_text_format": 1, - "insert_text": "\"stack_panel\"" + "description": { "zh-cn": "堆栈面板控件", "en-us": "stack panel" } }, { "label": "input_panel", - "description": { "zh-cn": "输入面板", "en-us": "input panel" }, - "insert_text_format": 1, - "insert_text": "\"input_panel\"" + "description": { "zh-cn": "输入面板", "en-us": "input panel" } }, { "label": "image", - "description": { "zh-cn": "图像", "en-us": "image" }, - "insert_text_format": 1, - "insert_text": "\"image\"" + "description": { "zh-cn": "图像", "en-us": "image" } }, { "label": "button", - "description": { "zh-cn": "按钮", "en-us": "button" }, - "insert_text_format": 1, - "insert_text": "\"button\"" + "description": { "zh-cn": "按钮", "en-us": "button" } }, { "label": "toggle", - "description": { "zh-cn": "切换按钮", "en-us": "toggle" }, - "insert_text_format": 1, - "insert_text": "\"toggle\"" + "description": { "zh-cn": "切换按钮", "en-us": "toggle" } }, { "label": "dropdown", - "description": { "zh-cn": "下拉框", "en-us": "dropdown" }, - "insert_text_format": 1, - "insert_text": "\"dropdown\"" + "description": { "zh-cn": "下拉框", "en-us": "dropdown" } }, { "label": "slider", - "description": { "zh-cn": "滑块", "en-us": "slider" }, - "insert_text_format": 1, - "insert_text": "\"slider\"" + "description": { "zh-cn": "滑块", "en-us": "slider" } }, { "label": "edit_box", - "description": { "zh-cn": "编辑框", "en-us": "edit box" }, - "insert_text_format": 1, - "insert_text": "\"edit_box\"" + "description": { "zh-cn": "编辑框", "en-us": "edit box" } }, { "label": "custom", - "description": { "zh-cn": "custom", "en-us": "custom" }, - "insert_text_format": 1, - "insert_text": "\"custom\"" + "description": { "zh-cn": "custom", "en-us": "custom" } }, { "label": "factory", - "description": { "zh-cn": "工厂", "en-us": "factory" }, - "insert_text_format": 1, - "insert_text": "\"factory\"" + "description": { "zh-cn": "工厂", "en-us": "factory" } }, { "label": "scroll_view", - "description": { "zh-cn": "scroll view", "en-us": "scroll view" }, - "insert_text_format": 1, - "insert_text": "\"scroll_view\"" + "description": { "zh-cn": "scroll view", "en-us": "scroll view" } }, { "label": "scroll_track", - "description": { "zh-cn": "滑块槽", "en-us": "scroll track" }, - "insert_text_format": 1, - "insert_text": "\"scroll_track\"" + "description": { "zh-cn": "滑块槽", "en-us": "scroll track" } }, { "label": "scrollbar_box", - "description": { "zh-cn": "滑块", "en-us": "scrollbar box" }, - "insert_text_format": 1, - "insert_text": "\"scrollbar_box\"" + "description": { "zh-cn": "滑块", "en-us": "scrollbar box" } }, { "label": "selection_wheel", - "description": { "zh-cn": "选择轮盘", "en-us": "selection wheel" }, - "insert_text_format": 1, - "insert_text": "\"selection_wheel\"" + "description": { "zh-cn": "选择轮盘", "en-us": "selection wheel" } }, { "label": "screen", - "description": { "zh-cn": "屏幕", "en-us": "screen" }, - "insert_text_format": 1, - "insert_text": "\"screen\"" + "description": { "zh-cn": "屏幕", "en-us": "screen" } }, { "label": "grid", - "description": { "zh-cn": "网格布局", "en-us": "grid" }, - "insert_text_format": 1, - "insert_text": "\"grid\"" + "description": { "zh-cn": "网格布局", "en-us": "grid" } } ] }, diff --git a/package.json b/package.json index 8884fc8..4727129 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "jsonui support", "publisher": "CoolLoong", "license": "MIT", - "version": "0.0.1", + "version": "0.0.2", "icon": "icon.png", "categories": ["Snippets"], "keywords": [ diff --git a/src/completion.rs b/src/completion.rs index 2e4f365..4d4375e 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -11,10 +11,13 @@ use std::{ }; use tokio::sync::Mutex; use tower_lsp::lsp_types::{ - ColorInformation, CompletionItem, CompletionParams, DidChangeTextDocumentParams, Position, Range, Url, + ColorInformation, CompletionItem, CompletionParams, DidChangeTextDocumentParams, Position, + Range, Url, }; #[derive(Debug, PartialEq, Clone)] +/// Defines an enum named `Token` with variants representing different characters as +/// their integer values. Each variant is assigned a character value casted to an `isize` type pub enum Token { LeftBrace = '{' as isize, RightBrace = '}' as isize, @@ -34,13 +37,22 @@ pub const TYPE_BOL: u8 = 6; pub const TYPE_COL: u8 = 7; pub const TYPE_COM: u8 = 8; +/// The `Value` struct represents a parsed source token +/// +/// Properties: +/// +/// * `l`: represents a left index in `Document` +/// * `r`: represents a right index in `Document` +/// * `type_id`: type constant +/// * `path`: shows the layer of the current `Value` +/// * `v`: parsed value #[derive(Clone, PartialEq, Eq)] pub(crate) struct Value { pub(crate) l: usize, pub(crate) r: usize, pub(crate) type_id: u8, pub(crate) path: Vec, - pub(crate) v: Option, + pub(crate) v: Option, } impl Value { @@ -68,8 +80,12 @@ impl fmt::Debug for Value { } } +/// Enum `ParsedToken` with different variants representing parsed tokens +/// in a data structure. The variants include `Controls` and `Array`, which contain vectors of `Value`, +/// `String` which contains an `Arc`, `Number` which contains a floating-point number, `Bool` which +/// contains a boolean value, and `Colon`, `Comma`. #[derive(Clone)] -pub enum Node { +pub enum ParsedToken { Controls(Vec), Array(Vec), String(Arc), @@ -79,10 +95,10 @@ pub enum Node { Comma, } -impl fmt::Debug for Node { +impl fmt::Debug for ParsedToken { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Node::Controls(controls) => { + ParsedToken::Controls(controls) => { writeln!(f, "{{")?; for (i, control) in controls.iter().enumerate() { if i > 0 { @@ -92,7 +108,7 @@ impl fmt::Debug for Node { } write!(f, "\n}}") } - Node::Array(array) => { + ParsedToken::Array(array) => { writeln!(f, "[")?; for (i, item) in array.iter().enumerate() { if i > 0 { @@ -102,27 +118,27 @@ impl fmt::Debug for Node { } write!(f, "\n]") } - Node::String(s) => write!(f, "\"{}\"", s), - Node::Number(n) => write!(f, "{}", n), - Node::Bool(b) => write!(f, "{}", b), - Node::Colon => write!(f, "Colon"), - Node::Comma => write!(f, "Comma"), + ParsedToken::String(s) => write!(f, "\"{}\"", s), + ParsedToken::Number(n) => write!(f, "{}", n), + ParsedToken::Bool(b) => write!(f, "{}", b), + ParsedToken::Colon => write!(f, "Colon"), + ParsedToken::Comma => write!(f, "Comma"), } } } -impl Eq for Node {} +impl Eq for ParsedToken {} -impl PartialEq for Node { +impl PartialEq for ParsedToken { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Node::Bool(a), Node::Bool(b)) => a.eq(b), - (Node::Number(a), Node::Number(b)) => (a - b).abs() < f32::EPSILON, - (Node::String(a), Node::String(b)) => a.eq(b), - (Node::Array(a), Node::Array(b)) => a.eq(b), - (Node::Controls(a), Node::Controls(b)) => a.eq(b), - (Node::Colon, Node::Colon) => true, - (Node::Comma, Node::Comma) => true, + (ParsedToken::Bool(a), ParsedToken::Bool(b)) => a.eq(b), + (ParsedToken::Number(a), ParsedToken::Number(b)) => (a - b).abs() < f32::EPSILON, + (ParsedToken::String(a), ParsedToken::String(b)) => a.eq(b), + (ParsedToken::Array(a), ParsedToken::Array(b)) => a.eq(b), + (ParsedToken::Controls(a), ParsedToken::Controls(b)) => a.eq(b), + (ParsedToken::Colon, ParsedToken::Colon) => true, + (ParsedToken::Comma, ParsedToken::Comma) => true, _ => false, } } @@ -150,6 +166,8 @@ impl From for char { } impl Token { + /// The function `is_ignore` returns a boolean value indicating whether the input character `c` is a + /// whitespace character (space), carriage return ("\r\n"), or newline ("\n"). pub fn is_ignore(c: &str) -> bool { matches!(c, " " | "\r\n" | "\n") } @@ -181,6 +199,7 @@ impl<'a> CompleteContext<'a> { } /// `PathInfo` is used to record the path information of nodes within the built AST. +/// it shows the layer of the current `Value` struct PathInfo { stack: RefCell>, current: RefCell, @@ -237,6 +256,14 @@ impl PathInfo { } #[derive(Debug)] +/// The `Completer` struct in Rust contains a `Document` and a Node stack call `ast` +/// +/// Properties: +/// +/// * `document`: The `document` property holds the source data and can be updated during editing. +/// * `ast`: The `ast` property in the `Completer` struct is a `Mutex` that contains an `Option` of a +/// vector of `Value` elements. This allows for safe concurrent access to the AST (Abstract Syntax Tree) +/// data stored within the `Completer` struct. pub(crate) struct Completer { document: Document, ast: Mutex>>, @@ -282,12 +309,12 @@ impl Completer { Self::flatten_ast_one_layer(ast, &mut result); for (index, i) in result.iter().enumerate() { if i.type_id == TYPE_STR - && let Some(Node::String(v)) = &i.v + && let Some(ParsedToken::String(v)) = &i.v && v.as_ref() == "type" { let type_node_index = index + 2; if type_node_index < result.len() - && let Some(Node::String(type_v)) = &result[type_node_index].v + && let Some(ParsedToken::String(type_v)) = &result[type_node_index].v { *context.control_type.lock().await = Some(type_v.clone()); } @@ -296,7 +323,7 @@ impl Completer { let mut control_type_mut = context.control_type.lock().await; if control_type_mut.is_none() - && let Some(Node::String(control_name)) = &ast[0].v + && let Some(ParsedToken::String(control_name)) = &ast[0].v { let namespace: Option> = bk.query_namespace(docs_url).await; let control_t = Self::fill_control_type(bk, namespace, control_name).await; @@ -362,7 +389,8 @@ impl Completer { param: &CompletionParams, ) -> Option> { let context: CompleteContext = CompleteContext::empty(); - self.update_ast(&context, ¶m.text_document_position.position).await; + self.update_ast(&context, ¶m.text_document_position.position) + .await; let ast_lock = self.ast.lock().await; if let Some(ast) = ast_lock.as_ref() { @@ -395,14 +423,14 @@ impl Completer { None } }; - if let Some(pos_v) = pos{ + if let Some(pos_v) = pos { let context: CompleteContext = CompleteContext::empty(); self.update_ast(&context, &pos_v).await; - }else{ - trace!("pos is null"); + } else { + trace!("pos is null in complete_color"); return None; } - + let ast = self.ast.lock().await; if ast.is_none() { return None; @@ -415,7 +443,7 @@ impl Completer { let mut iter = results.iter(); while let Some(color_v) = iter .by_ref() - .skip_while(|r| !matches!(&r.v, Some(Node::String(v)) if v.as_ref() == "color")) + .skip_while(|r| !matches!(&r.v, Some(ParsedToken::String(v)) if v.as_ref() == "color")) .nth(2) { if let (Some(left_v), Some(right_v)) = ( @@ -461,6 +489,8 @@ impl Completer { let iter = splice_control.iter().enumerate(); let path_info: &PathInfo = &PathInfo::new(); + // trace!("{:?}",splice_control); + for (index, c) in iter { let i = index + l; let str = c.as_ref(); @@ -486,14 +516,14 @@ impl Completer { r: i + 1, type_id: TYPE_COL, path: path_info.get_path(), - v: Some(Node::Colon), + v: Some(ParsedToken::Colon), }); } Token::RightBrace if Self::not_quote_state(&stack) => { Self::handle_right_brace(&mut stack, i, path_info) } Token::RightBracket if Self::not_quote_state(&stack) => { - Self::handle_right_bracket(&mut stack, i, path_info) + Self::handle_right_bracket(&mut stack, &mut str_builder, i, path_info) } Token::Comma if Self::not_quote_state(&stack) => { Self::handle_comma(&mut stack, &mut str_builder, i, path_info) @@ -521,7 +551,7 @@ impl Completer { if let Some(v) = &i.v { result.push(i); match v { - Node::Array(sub) | Node::Controls(sub) => { + ParsedToken::Array(sub) | ParsedToken::Controls(sub) => { Self::flatten_ast(sub, result); } _ => {} @@ -534,7 +564,7 @@ impl Completer { for i in input { if let Some(v) = &i.v { result.push(i); - if let Node::Array(sub) | Node::Controls(sub) = v { + if let ParsedToken::Array(sub) | ParsedToken::Controls(sub) = v { for sub_value in sub { result.push(sub_value); } @@ -567,7 +597,7 @@ impl Completer { r: index + 1, type_id: TYPE_STR, path: path_info.get_path(), - v: Some(Node::String(Arc::from(str_builder.clone()))), + v: Some(ParsedToken::String(Arc::from(str_builder.clone()))), }); str_builder.clear(); } @@ -584,7 +614,7 @@ impl Completer { r: index + 1, type_id: TYPE_CR, path: f.path, - v: Some(Node::Controls(tmp_vec)), + v: Some(ParsedToken::Controls(tmp_vec)), }); return; } else { @@ -593,7 +623,13 @@ impl Completer { } } - fn handle_right_bracket(stack: &mut VecDeque, index: usize, path_info: &PathInfo) { + fn handle_right_bracket( + stack: &mut VecDeque, + str_builder: &mut String, + index: usize, + path_info: &PathInfo, + ) { + Completer::collect_value(stack, str_builder, index, path_info); let mut tmp_vec: Vec = Vec::new(); while let Some(f) = stack.pop_back() { if TYPE_ARR == f.type_id && f.v.is_none() { @@ -604,7 +640,7 @@ impl Completer { r: index + 1, type_id: TYPE_ARR, path: f.path, - v: Some(Node::Array(tmp_vec)), + v: Some(ParsedToken::Array(tmp_vec)), }); return; } else { @@ -613,11 +649,29 @@ impl Completer { } } + /// The `handle_comma` function is represent comma grammar rule,it parses a string into a comma token and pushes to stack fn handle_comma( stack: &mut VecDeque, str_builder: &mut String, index: usize, path_info: &PathInfo, + ) { + Completer::collect_value(stack, str_builder, index, path_info); + stack.push_back(Value { + l: index, + r: index + 1, + type_id: TYPE_COM, + path: path_info.get_path(), + v: Some(ParsedToken::Comma), + }); + } + + /// The function `collect_value` parses a string into a boolean or float value and pushes to stack + fn collect_value( + stack: &mut VecDeque, + str_builder: &mut String, + index: usize, + path_info: &PathInfo, ) { if !str_builder.is_empty() { let str_c = str_builder.len(); @@ -627,7 +681,7 @@ impl Completer { r: index + str_c + 1, type_id: TYPE_BOL, path: path_info.get_path(), - v: Some(Node::Bool(boolean)), + v: Some(ParsedToken::Bool(boolean)), }); } else if let Ok(float) = str_builder.parse::() { stack.push_back(Value { @@ -635,19 +689,11 @@ impl Completer { r: index + str_c + 1, type_id: TYPE_NUM, path: path_info.get_path(), - v: Some(Node::Number(float)), + v: Some(ParsedToken::Number(float)), }); } str_builder.clear(); } - - stack.push_back(Value { - l: index, - r: index + 1, - type_id: TYPE_COM, - path: path_info.get_path(), - v: Some(Node::Comma), - }); } } diff --git a/src/completion_helper.rs b/src/completion_helper.rs index 417b3fa..b28a5bb 100644 --- a/src/completion_helper.rs +++ b/src/completion_helper.rs @@ -1,11 +1,16 @@ -use crate::completion::{CompleteContext, Node, Value, TYPE_ARR, TYPE_COL, TYPE_CR}; +use crate::completion::{ + CompleteContext, ParsedToken, Value, TYPE_ARR, TYPE_COL, TYPE_COM, TYPE_CR, +}; use log::trace; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use tower_lsp::lsp_types::{ Color, CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, InsertTextFormat, Position, Range, TextEdit, }; +const BINDINGS: &str = "bindings"; +const CONTROLS: &str = "controls"; + fn create_binding_type_input<'a>( ast: &'a Vec, define_map: &'a HashMap, @@ -14,12 +19,12 @@ fn create_binding_type_input<'a>( let mut inputs: Vec<&serde_json::Value> = vec![]; let path = ¤t.path; let binding_type: Option<&Value> = if path.len() > 1 - && let Some(Node::Array(arr)) = &ast[path[0]].v - && let Some(Node::Controls(obj)) = &arr[path[1]].v + && let Some(ParsedToken::Array(arr)) = &ast[path[0]].v + && let Some(ParsedToken::Controls(obj)) = &arr[path[1]].v { obj.iter() .skip_while(|&v| { - if let Some(Node::String(s)) = &v.v + if let Some(ParsedToken::String(s)) = &v.v && s.as_ref() == "binding_type" { return false; @@ -28,11 +33,11 @@ fn create_binding_type_input<'a>( }) .nth(2) } else { + trace!("cant find binding_type"); None }; - trace!("binding_type {:?}", binding_type); if let Some(bt) = binding_type - && let Some(Node::String(bt_n)) = &bt.v + && let Some(ParsedToken::String(bt_n)) = &bt.v && let Some(serde_json::Value::Array(arr)) = define_map.get(bt_n.as_ref()) { inputs.extend(&mut arr.iter()); @@ -56,7 +61,11 @@ pub(crate) async fn create_completion<'a>( context: &CompleteContext<'a>, ast: &Vec, ) -> Option> { - trace!("{:?}\n\n ast {:?}\n--------------------------------", context, ast); + trace!( + "{:?}\n\n AST {:?}\n--------------------------------", + context, + ast + ); let type_c = context.control_type.lock().await; let nodes = context.nodes.lock().await; let input_c = context.input_char.lock().await; @@ -65,27 +74,43 @@ pub(crate) async fn create_completion<'a>( let n2 = nodes[1]; let current = nodes[2]; if let Some(first_ast_v) = ast.first() - && let Some(Node::String(if_bindings)) = &first_ast_v.v - && if_bindings.as_ref() == "bindings" + && let Some(ParsedToken::String(control_name)) = &first_ast_v.v { - if let Some(nv1) = n1 - && let Some(Node::String(pn)) = &nv1.v - && let Some(nv2) = n2 - && nv2.type_id == TYPE_COL - && (current.is_none() || current.unwrap().type_id != TYPE_ARR) - { - trace!("create_bindings_value_completion"); - return create_value_completion(input_c.as_ref(), pn.as_ref(), lang, define_map); - } else if input_c.as_ref() == "\"" - && let Some(current_v) = current - && (current_v.path.len() == 3 || current_v.type_id == TYPE_ARR) - { - trace!("create_bindings_type_completion"); - let inputs = &create_binding_type_input(ast, define_map, current_v); - return create_type_completion(inputs, param, lang, define_map); + match control_name.as_ref() { + BINDINGS => { + if let Some(nv1) = n1 + && let Some(ParsedToken::String(pn)) = &nv1.v + && let Some(nv2) = n2 + && nv2.type_id == TYPE_COL + && (current.is_none() || current.unwrap().type_id != TYPE_ARR) + { + trace!("create_bindings_value_completion"); + return create_value_completion( + input_c.as_ref(), + pn.as_ref(), + lang, + define_map, + ); + } else if let Some(current_v) = current + && let Some(nv2) = n2 + && input_c.as_ref() == "\"" + && (current_v.type_id == TYPE_ARR + || (nv2.type_id == TYPE_COM && current_v.type_id == TYPE_CR)) + { + trace!("create_bindings_type_completion"); + let inputs = &create_binding_type_input(ast, define_map, current_v); + return create_type_completion(inputs, param, lang, define_map); + } + } + CONTROLS => { + return None; + } + _ => {} } - } else if let Some(nv1) = n1 - && let Some(Node::String(pn)) = &nv1.v + } + + if let Some(nv1) = n1 + && let Some(ParsedToken::String(pn)) = &nv1.v && let Some(nv2) = n2 && nv2.type_id == TYPE_COL && (current.is_none() || current.unwrap().type_id != TYPE_CR) @@ -98,19 +123,15 @@ pub(crate) async fn create_completion<'a>( { trace!("create_type_completion"); let mut inputs: Vec<&serde_json::Value> = vec![]; - if let Some(c_type) = type_c.as_ref()//fill type property + //fill type property + if let Some(c_type) = type_c.as_ref() && let Some(serde_json::Value::Array(arr)) = define_map.get(c_type.as_ref()) { inputs.extend(arr.iter()); } inputs.extend( //fill common property - &mut define_map - .get("common") - .unwrap() - .as_array() - .unwrap() - .iter(), + &mut define_map.get("common").unwrap().as_array().unwrap().iter(), ); return create_type_completion(&inputs, param, lang, define_map); } @@ -225,7 +246,7 @@ fn create_value_completion( } pub(crate) fn from_color_value_to_color_arr(v: &Value) -> Option { - if let Some(Node::String(color_str)) = &v.v { + if let Some(ParsedToken::String(color_str)) = &v.v { return match color_str.as_ref() { "white" => Some(Color { red: 1.0, @@ -307,21 +328,20 @@ pub(crate) fn from_color_value_to_color_arr(v: &Value) -> Option { }), _ => None, }; - } else if let Some(Node::Array(color_arr)) = &v.v { + } else if let Some(ParsedToken::Array(color_arr)) = &v.v { if color_arr.len() >= 5 - && let Some(Node::Number(v1)) = color_arr[0].v + && let Some(ParsedToken::Number(v1)) = color_arr[0].v && v1 >= 0.0 && v1 <= 1.0 - && let Some(Node::Number(v2)) = color_arr[2].v + && let Some(ParsedToken::Number(v2)) = color_arr[2].v && v2 >= 0.0 && v2 <= 1.0 - && let Some(Node::Number(v3)) = color_arr[4].v + && let Some(ParsedToken::Number(v3)) = color_arr[4].v && v3 >= 0.0 && v3 <= 1.0 { - trace!("{} {} {}",v1,v2,v3); if color_arr.len() == 7 - && let Some(Node::Number(v4)) = color_arr[6].v + && let Some(ParsedToken::Number(v4)) = color_arr[6].v && v4 >= 0.0 && v4 <= 1.0 { diff --git a/src/document.rs b/src/document.rs index b2c2839..3b7e893 100644 --- a/src/document.rs +++ b/src/document.rs @@ -1,5 +1,6 @@ use std::{collections::VecDeque, sync::Arc}; +use log::trace; use tokio::sync::Mutex; use tower_lsp::lsp_types::{DidChangeTextDocumentParams, Position}; use unicode_segmentation::UnicodeSegmentation; @@ -8,7 +9,7 @@ use unicode_segmentation::UnicodeSegmentation; pub struct Document { pub line_info_cache: Mutex>, pub content_cache: Mutex, - pub content_chars: Mutex>> + pub content_chars: Mutex>>, } #[derive(Debug)] @@ -181,65 +182,96 @@ impl Document { return None; } - let f_len = forward.len(); - let mut left_boundary_char: &str = "{"; - let mut right_boundary_char: &str = "}"; + let mut boundary_char_stack1: Vec<&str> = Vec::new(); //for {} + let mut boundary_char_stack2: Vec<&str> = Vec::new(); //for [] let mut start_index: Option = None; - let mut boundary_char_stack: VecDeque<&str> = VecDeque::new(); + let mut end_index: Option = None; + let mut bound_state = true; + let f_len = forward.len(); let mut forward_iter = forward.iter().rev().peekable().enumerate(); - let mut collected_str = String::new(); + while let Some((_, ch)) = forward_iter.next() { - let str = ch.as_ref(); - if str == left_boundary_char { - if boundary_char_stack.is_empty() { + let char = ch.as_ref(); + match char { + "{" | "}" => { + if !boundary_char_stack1.is_empty() { + boundary_char_stack1.pop(); + } else { + boundary_char_stack1.push(char); + } + bound_state = true; + } + "[" | "]" => { + if !boundary_char_stack2.is_empty() { + boundary_char_stack2.pop(); + } else { + boundary_char_stack2.push(char); + } + bound_state = true; + } + ":" => { + if !(boundary_char_stack1.len() == 1 || boundary_char_stack2.len() == 1) { + continue; + } + let mut p1 = boundary_char_stack1.iter().peekable(); + let mut p2 = boundary_char_stack2.iter().peekable(); + if let Some(&&v1) = p1.peek() + && v1 != "{" + { + continue; + } + if let Some(&&v2) = p2.peek() + && v2 != "[" + { + continue; + } + if !bound_state { + continue; + } + let opt = forward_iter .by_ref() .skip_while(|(_, c)| c.as_ref() != "\"") .skip(1) - .skip_while(|(_, c)| { - if c.as_ref() != "\"" { - collected_str.push_str(c); - true - } else { - false - } - }) + .skip_while(|(_, c)| c.as_ref() != "\"") .nth(1); if let Some((index, _)) = opt { - if collected_str == "sgnidnib" || collected_str == "slortnoc" { - forward_iter = forward.iter().rev().peekable().enumerate(); - left_boundary_char = "["; - right_boundary_char = "]"; - continue; - } start_index = Some(f_len - index); break; - } else { - return None; } - } else { - boundary_char_stack.pop_back(); } - } else if str == right_boundary_char { - boundary_char_stack.push_back(str); + _ if !matches!(char, " " | "\r\n" | "\n") => { + bound_state &= false; + } + _ => {} } } + let start_index_v = start_index?; - boundary_char_stack.clear(); - let mut end_index: Option = None; let backend_iter = backward.iter().peekable().enumerate(); for (index, ch) in backend_iter { - let str = ch.as_ref(); - if str == right_boundary_char { - if boundary_char_stack.is_empty() { - end_index = Some(index + f_len); - break; - } else { - boundary_char_stack.pop_back(); + let char = ch.as_ref(); + match char { + "{" | "}" => { + if !boundary_char_stack1.is_empty() { + boundary_char_stack1.pop(); + } else { + boundary_char_stack1.push(char); + } + } + "[" | "]" => { + if !boundary_char_stack2.is_empty() { + boundary_char_stack2.pop(); + } else { + boundary_char_stack2.push(char); + } } - } else if str == left_boundary_char { - boundary_char_stack.push_back(str); + _ => {} + } + if boundary_char_stack1.is_empty() && boundary_char_stack2.is_empty() { + end_index = Some(index + f_len); + break; } } let end_index_v = end_index?; @@ -332,12 +364,12 @@ mod tests { content_changes: vec![TextDocumentContentChangeEvent { range: Some(Range { start: Position { - line: 24, - character: 25, + line: 23, + character: 23, }, end: Position { - line: 24, - character: 30, + line: 23, + character: 27, }, }), range_length: None, @@ -346,9 +378,8 @@ mod tests { }; document.apply_change(&request).await; let docs = document.content_cache.lock().await; - println!("{}", docs); #[rustfmt::skip] - assert!(docs.contains("\"clip_pixelperfect\": \n },")); + assert!(docs.contains("\"clip_direction\": \"\"")); } #[tokio::test]