From 5d8eb18042198a12624ac09e7127e1166ba771ff Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Fri, 27 Sep 2024 17:06:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20digit=20hover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.toml | 1 - src/completion/mod.rs | 604 ----------------------------------------- src/diagnostics/mod.rs | 117 -------- src/format/mod.rs | 81 ------ src/hover/feature.rs | 94 ++++++- src/hover/mod.rs | 8 +- src/lib.rs | 5 +- src/main.rs | 2 - src/sources.rs | 160 ++--------- src/support.rs | 3 - src/utils/mod.rs | 25 +- 11 files changed, 143 insertions(+), 957 deletions(-) delete mode 100644 src/support.rs diff --git a/config.toml b/config.toml index baa4e8a..a86732d 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,3 @@ [build] -jobs = 16 # 并行构建任务的数量,默认等于 CPU 的核心数 rustc = "rustc" # rust 编译器 target-dir = "target" # 存放编译输出结果的目录 \ No newline at end of file diff --git a/src/completion/mod.rs b/src/completion/mod.rs index b46f980..b697b5d 100644 --- a/src/completion/mod.rs +++ b/src/completion/mod.rs @@ -168,607 +168,3 @@ fn get_completion_token(text: &Rope, line: RopeSlice, pos: Position) -> String { result } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::definition::def_types::Scope; - use crate::definition::get_scopes; - use crate::sources::{recovery_sv_parse, LSPSupport}; - use crate::support::test_init; - use ropey::Rope; - - #[test] - fn test_get_completion_token() { - test_init(); - let text = Rope::from_str("abc abc.cba de_fg cde[4]"); - let mut result = get_completion_token( - &text, - text.line(0), - Position { - line: 0, - character: 3, - }, - ); - assert_eq!(&result, "abc"); - result = get_completion_token( - &text, - text.line(0), - Position { - line: 0, - character: 11, - }, - ); - assert_eq!(&result, "abc.cba"); - result = get_completion_token( - &text, - text.line(0), - Position { - line: 0, - character: 16, - }, - ); - assert_eq!(&result, "de_f"); - result = get_completion_token( - &text, - text.line(0), - Position { - line: 0, - character: 23, - }, - ); - assert_eq!(&result, "cde"); - } - - #[test] - fn test_completion() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"module test; - logic abc; - logic abcd; - -endmodule -"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - server.srcs.wait_parse_ready(fid, true); - - let change_params = DidChangeTextDocumentParams { - text_document: VersionedTextDocumentIdentifier { - uri: uri.clone(), - version: 3, - }, - content_changes: vec![ - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 3, - character: 0, - }, - end: Position { - line: 3, - character: 0, - }, - }), - range_length: None, - text: "\n".to_owned(), - }, - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 4, - character: 0, - }, - end: Position { - line: 4, - character: 0, - }, - }), - range_length: None, - text: " ".to_owned(), - }, - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 4, - character: 2, - }, - end: Position { - line: 4, - character: 2, - }, - }), - range_length: None, - text: "a".to_owned(), - }, - ], - }; - server.did_change(change_params); - server.srcs.wait_parse_ready(fid, true); - - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri }, - position: Position { - line: 4, - character: 3, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::INVOKED, - trigger_character: None, - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - let item1 = CompletionItem { - label: "abc".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("logic".to_string()), - ..CompletionItem::default() - }; - let item2 = CompletionItem { - label: "abcd".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("logic".to_string()), - ..CompletionItem::default() - }; - if let CompletionResponse::List(item) = response { - assert!(item.items.contains(&item1)); - assert!(item.items.contains(&item2)); - } else { - panic!(); - } - } - - #[test] - fn test_nested_completion() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"module test; - logic aouter; - function func1(); - logic abc; - func1 = abc; - endfunction - function func2(); - logic abcd; - func2 = abcd; - endfunction -endmodule -"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - server.srcs.wait_parse_ready(fid, true); - - let change_params = DidChangeTextDocumentParams { - text_document: VersionedTextDocumentIdentifier { - uri: uri.clone(), - version: 3, - }, - content_changes: vec![ - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 4, - character: 0, - }, - end: Position { - line: 4, - character: 0, - }, - }), - range_length: None, - text: "\n".to_owned(), - }, - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 4, - character: 0, - }, - end: Position { - line: 4, - character: 0, - }, - }), - range_length: None, - text: " ".to_owned(), - }, - TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 4, - character: 2, - }, - end: Position { - line: 4, - character: 2, - }, - }), - range_length: None, - text: "a".to_owned(), - }, - ], - }; - server.did_change(change_params); - server.srcs.wait_parse_ready(fid, true); - - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri }, - position: Position { - line: 4, - character: 3, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::INVOKED, - trigger_character: None, - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - let item1 = CompletionItem { - label: "abc".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("logic".to_string()), - ..CompletionItem::default() - }; - let item3 = CompletionItem { - label: "aouter".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("logic".to_string()), - ..CompletionItem::default() - }; - if let CompletionResponse::List(item) = response { - eprintln!("{:#?}", item); - assert!(item.items.contains(&item1)); - for comp in &item.items { - assert!(comp.label != "abcd"); - } - assert!(item.items.contains(&item3)); - } else { - panic!(); - } - } - - #[test] - fn test_dot_completion() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"interface test_inter; - wire abcd; -endinterface -module test( - test_inter abc -); - abc. - test_inter. -endmodule -"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - server.srcs.wait_parse_ready(fid, true); - let file = server.srcs.get_file(fid).unwrap(); - let file = file.read().unwrap(); - eprintln!("{}", file.syntax_tree.as_ref().unwrap()); - eprintln!( - "{:#?}", - server.srcs.scope_tree.read().unwrap().as_ref().unwrap() - ); - - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri: uri.clone() }, - position: Position { - line: 6, - character: 8, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, - trigger_character: Some(".".to_string()), - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - dbg!(&response); - let item1 = CompletionItem { - label: "abcd".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("wire".to_string()), - ..CompletionItem::default() - }; - if let CompletionResponse::List(item) = response { - eprintln!("{:#?}", item); - assert!(item.items.contains(&item1)); - assert!(item.items.len() == 1); - } else { - panic!(); - } - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri }, - position: Position { - line: 7, - character: 14, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, - trigger_character: Some(".".to_string()), - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - if let CompletionResponse::List(item) = response { - eprintln!("{:#?}", item); - assert!(item.items.contains(&item1)); - assert!(item.items.len() == 1); - } else { - panic!(); - } - } - - #[test] - fn test_trigger_dot_nocontext() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"interface test_inter; - wire abcd; -endinterface -module test( - test_inter abc -); - abc. - test_inter. -endmodule -"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - server.srcs.wait_parse_ready(fid, true); - let file = server.srcs.get_file(fid).unwrap(); - let file = file.read().unwrap(); - eprintln!("{}", file.syntax_tree.as_ref().unwrap()); - eprintln!( - "{:#?}", - server.srcs.scope_tree.read().unwrap().as_ref().unwrap() - ); - - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri: uri.clone() }, - position: Position { - line: 6, - character: 8, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: None, - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - dbg!(&response); - let item1 = CompletionItem { - label: "abcd".to_owned(), - kind: Some(CompletionItemKind::VARIABLE), - detail: Some("wire".to_string()), - ..CompletionItem::default() - }; - if let CompletionResponse::List(item) = response { - eprintln!("{:#?}", item); - assert!(item.items.contains(&item1)); - assert!(item.items.len() == 1); - } else { - panic!(); - } - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri }, - position: Position { - line: 7, - character: 14, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, - trigger_character: Some(".".to_string()), - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - if let CompletionResponse::List(item) = response { - eprintln!("{:#?}", item); - assert!(item.items.contains(&item1)); - assert!(item.items.len() == 1); - } else { - panic!(); - } - } - - #[test] - fn test_dot_completion_instantiation() { - test_init(); - let text = r#"interface test_inter; - wire wrong; - logic clk; -endinterface -module test; - logic clk; - test_inter2 t ( - .clk(clk), - . - ) -endmodule -interface test_inter2; - wire abcd; - logic clk; -endinterface -"#; - - let doc = Rope::from_str(&text); - let url = Url::parse("file:///test.sv").unwrap(); - let syntax_tree = recovery_sv_parse(&doc, &url, &None, &Vec::new()).unwrap(); - let scope_tree = get_scopes(&syntax_tree, &url).unwrap(); - let pos = Position::new(8, 9); - let token = get_completion_token(&doc, doc.line(pos.line as usize), pos); - let completions = scope_tree.get_dot_completion( - token.trim_end_matches('.'), - doc.pos_to_byte(&pos), - &url, - &scope_tree, - ); - let labels: Vec = completions.iter().map(|x| x.label.clone()).collect(); - assert_eq!(labels, vec!["abcd", "clk"]); - } - - /* - #[test] - fn test_package_completion() { - test_init(); - let text = r#"package p; - struct {int x;} s1; - struct {int x;} s2; - function void f(); - int x; - endfunction - endpackage - module m; - import p::*; - if (1) begin : s1 - initial begin - s1.x = 1; - f.x = 1; - end - int x; - end - endmodule - "#; - - let doc = Rope::from_str(&text); - let url = Url::parse("file:///test.sv").unwrap(); - let syntax_tree = parse(&doc, &url, &None, &Vec::new()).unwrap(); - let scope_tree = get_scopes(&syntax_tree, &url).unwrap(); - dbg!(&scope_tree); - /* - let pos = Position::new(8, 9); - let token = get_completion_token(&doc, doc.line(pos.line as usize), pos); - let completions = scope_tree.get_dot_completion( - token.trim_end_matches('.'), - doc.pos_to_byte(&pos), - &url, - &scope_tree, - ); - let labels: Vec = completions.iter().map(|x| x.label.clone()).collect(); - assert_eq!(labels, vec!["abcd", "clk"]); - */ - panic!(); - } - */ - - #[test] - fn test_inter_file_completion() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let uri2 = Url::parse("file:///test2.sv").unwrap(); - let text = r#"module test; - s -endmodule -"#; - let text2 = r#"interface simple_bus; - logic clk; -endinterface"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - let open_params2 = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri2.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text2.to_owned(), - }, - }; - server.did_open(open_params); - server.did_open(open_params2); - let fid = server.srcs.get_id(&uri); - let fid2 = server.srcs.get_id(&uri2); - server.srcs.wait_parse_ready(fid, true); - server.srcs.wait_parse_ready(fid2, true); - - let completion_params = CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri }, - position: Position { - line: 1, - character: 5, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - context: Some(CompletionContext { - trigger_kind: CompletionTriggerKind::INVOKED, - trigger_character: None, - }), - }; - let response: CompletionResponse = server.completion(completion_params).unwrap(); - let scope_tree = server.srcs.scope_tree.read().unwrap(); - dbg!(scope_tree.as_ref().unwrap()); - if let CompletionResponse::List(item) = response { - // eprintln!("{:#?}", item); - let names: Vec<&String> = item.items.iter().map(|x| &x.label).collect(); - assert!(names.contains(&&"simple_bus".to_string())); - } else { - panic!(); - } - } -} diff --git a/src/diagnostics/mod.rs b/src/diagnostics/mod.rs index 4c452b8..c9b82a5 100644 --- a/src/diagnostics/mod.rs +++ b/src/diagnostics/mod.rs @@ -199,120 +199,3 @@ fn verible_syntax( None } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::support::test_init; - use std::fs::File; - use std::io::Write; - use tempdir::TempDir; - - #[test] - fn test_unsaved_file() { - test_init(); - let uri = Url::parse("file://test.sv").unwrap(); - get_diagnostics( - uri.clone(), - &Rope::default(), - vec![uri], - &ProjectConfig::default(), - ); - } - - #[test] - fn test_verible_syntax() { - let text = r#"module test; - logic abc; - logic abcd; - - a -endmodule -"#; - let doc = Rope::from_str(text); - let errors = verible_syntax(&doc, "verible-verilog-syntax", &[]) - .expect("verible-verilog-syntax not found, test can not run"); - let expected: Vec = vec![Diagnostic { - range: Range { - start: Position { - line: 5, - character: 0, - }, - end: Position { - line: 5, - character: 8, - }, - }, - severity: Some(DiagnosticSeverity::ERROR), - code: None, - source: Some("verible".to_string()), - message: "syntax error at token".to_string(), - related_information: None, - tags: None, - code_description: None, - data: None, - }]; - assert_eq!(errors, expected); - } - - #[test] - fn test_verilator_syntax() { - let text = r#"module test; - logic abc; - logic abcd; - - a -endmodule -"#; - let doc = Rope::from_str(text); - - // verilator can't read from stdin so we must create a temp dir to place our - // test file - let dir = TempDir::new("verilator_test").unwrap(); - let file_path_1 = dir.path().join("test.sv"); - let mut f = File::create(&file_path_1).unwrap(); - f.write_all(text.as_bytes()).unwrap(); - f.sync_all().unwrap(); - - let errors = verilator_syntax( - &doc, - file_path_1, - "verilator", - &[ - "--lint-only".to_string(), - "--sv".to_string(), - "-Wall".to_string(), - ], - ) - .expect("verilator not found, test can not run"); - - drop(f); - dir.close().unwrap(); - - let expected: Vec = vec![Diagnostic { - range: Range { - start: Position { - line: 5, - character: 0, - }, - end: Position { - line: 5, - character: 0, - }, - }, - severity: Some(DiagnosticSeverity::ERROR), - code: None, - source: Some("verilator".to_string()), - message: "syntax error, unexpected endmodule, expecting IDENTIFIER or randomize" - .to_string(), - related_information: None, - tags: None, - code_description: None, - data: None, - }]; - assert_eq!(errors[0].severity, expected[0].severity); - assert_eq!(errors[0].range.start.line, expected[0].range.start.line); - assert_eq!(errors[0].range.end.line, expected[0].range.end.line); - assert!(errors[0].message.contains("syntax error")); - } -} diff --git a/src/format/mod.rs b/src/format/mod.rs index 9796f35..b6adfd8 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -90,84 +90,3 @@ pub fn format_document( None } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::server::ProjectConfig; - use crate::support::test_init; - use which::which; - - #[test] - fn test_formatting() { - test_init(); - let text = r#" -module test; - logic a; - logic b; -endmodule"#; - let text_fixed = r#" -module test; - logic a; - logic b; -endmodule -"#; - let doc = Rope::from_str(&text); - if which("verible-verilog-format").is_ok() { - assert_eq!( - format_document( - &doc, - None, - &ProjectConfig::default().verible.format.path, - &[] - ) - .unwrap(), - text_fixed.to_string() - ); - } - } - - #[test] - fn test_range_formatting() { - test_init(); - let text = r#"module t1; - logic a; - logic b; - logic c; -endmodule - - -module t2; - logic a; - logic b; - logic c; -endmodule"#; - - let text_fixed = r#"module t1; - logic a; - logic b; - logic c; -endmodule - - -module t2; - logic a; - logic b; - logic c; -endmodule -"#; - let doc = Rope::from_str(&text); - if which("verible-verilog-format").is_ok() { - assert_eq!( - format_document( - &doc, - Some(Range::new(Position::new(0, 0), Position::new(4, 9))), - &ProjectConfig::default().verible.format.path, - &[] - ) - .unwrap(), - text_fixed.to_string() - ); - } - } -} diff --git a/src/hover/feature.rs b/src/hover/feature.rs index 71e0abd..beb3900 100644 --- a/src/hover/feature.rs +++ b/src/hover/feature.rs @@ -1,19 +1,101 @@ use log::info; use regex::Regex; use ropey::RopeSlice; -use tower_lsp::lsp_types::{Hover, Position}; +use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, MarkupContent, Position}; use super::get_word_range_at_position; +/// 将 4'b0011 分解为 ("b", "0011") +fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> { + if digit_string.len() == 0 { + return None; + } + match digit_string.split_once("'") { + Some((width_string, body_string)) => { + let tag = body_string.get(0..1); + let digit = body_string.get(1..); + + if tag.is_none() || digit.is_none() { + return None; + } + + let width = width_string.parse::(); + if width.is_err() { + return None; + } + + return Some((tag.unwrap(), digit.unwrap())); + }, + None => return None + }; +} + +fn convert_tag_to_radix(tag: &str) -> Option { + let tag = &tag.to_string().to_lowercase()[..]; + match tag { + "b" => Some(2), + "o" => Some(8), + "h" => Some(16), + _ => None + } +} + +/// 计算出有符号和无符号下的表示 +fn convert_to_sign_unsign<'a>(tag: &'a str, digit_string: &str) -> Option<(String, String)> { + let radix = convert_tag_to_radix(tag); + if radix.is_none() { + return None; + } + let radix = radix.unwrap(); + let unsigned_decimal = u128::from_str_radix(digit_string, radix); + + if unsigned_decimal.is_err() { + return None; + } + let unsigned_decimal = unsigned_decimal.unwrap(); + + let pow = radix.pow(digit_string.len() as u32) as u128; + + let mut signed_decimal = unsigned_decimal as i128; + if unsigned_decimal >= pow >> 1 { + signed_decimal = (unsigned_decimal as i128) - (pow as i128); + } + + Some(( + signed_decimal.to_string(), + unsigned_decimal.to_string() + )) +} /// 将 1'b1 翻译成 10进制 -pub fn match_format_digit(line: &RopeSlice, pos: Position) -> Option { - let line_text = line.as_str().unwrap_or(""); +pub fn match_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> Option { let regex = Regex::new(r"[0-9'bho]").unwrap(); - let digit_string = get_word_range_at_position(line, pos, regex); - if digit_string.len() > 0 { - info!("current digit: {}", digit_string); + let token_result = get_word_range_at_position(line, pos, regex); + + if token_result.is_none() { + return None; + } + + let ( digit_string, range ) = token_result.unwrap(); + + if let Some((tag, digit)) = parse_digit_string(&digit_string) { + if let Some((signed_string, unsigned_string)) = convert_to_sign_unsign(tag, digit) { + let digit_title = LanguageString { + language: language_id.to_string(), + value: format!("{}'{}{}", digit.len(), tag, digit) + }; + let markdown = HoverContents::Array(vec![ + MarkedString::LanguageString(digit_title), + MarkedString::String(format!("`unsigned` {}\n", unsigned_string)), + MarkedString::String(format!("`signed` {}", signed_string)) + ]); + let hover = Hover { + contents: markdown, + range: Some(range) + }; + return Some(hover); + } } None diff --git a/src/hover/mod.rs b/src/hover/mod.rs index a8216a4..8ab9cb3 100644 --- a/src/hover/mod.rs +++ b/src/hover/mod.rs @@ -40,7 +40,7 @@ impl LSPServer { } // match digit 5'b00110 - if let Some(hover) = match_format_digit(&line_text, pos) { + if let Some(hover) = match_format_digit(&line_text, pos, &language_id) { return Some(hover); } @@ -124,7 +124,11 @@ fn match_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &Strin value: content }; let markdown = MarkedString::LanguageString(language_string); - return Some(Hover { contents: HoverContents::Scalar(markdown), range: None }) + let hover = Hover { + contents: HoverContents::Scalar(markdown), + range: None + }; + return Some(hover); } } } diff --git a/src/lib.rs b/src/lib.rs index 73cb85f..1c86914 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![recursion_limit = "256"] +// 核心 hdlparam pub mod core; // 自动补全 @@ -20,9 +21,11 @@ pub mod format; // 基础工具 pub mod utils; +// LSP 服务器 pub mod server; + +// 管理所有代码 pub mod sources; -pub mod support; // 自定义发送请求 pub mod custom_request; diff --git a/src/main.rs b/src/main.rs index 370d98c..a07d17f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,6 @@ mod server; mod sources; mod custom_request; -#[cfg(test)] -mod support; use server::{Backend, GLOBAL_BACKEND}; #[derive(StructOpt, Debug)] diff --git a/src/sources.rs b/src/sources.rs index c9a5606..1be053d 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -427,48 +427,41 @@ pub fn recovery_sv_parse( &defines, &includes, true, - false + true ) { Ok((syntax_tree, _)) => { return Some(syntax_tree); } Err(err) => { - println!("find err : {:?}", err); - info!("find err : {:?}", err); match err { // 语法错误 - sv_parser::Error::Parse(trace) => { - if trace.is_none() { - return None; - } - - let (_, bpos) = trace.unwrap(); - let mut line_start = text.byte_to_line(bpos); - let mut line_end = text.byte_to_line(bpos) + 1; - // println!("enter Error::Parse, start: {}, end: {}", line_start, line_end); - - if !reverted_change { - if let Some(range) = last_change_range { - line_start = range.start.line as usize; - line_end = range.end.line as usize + 1; - reverted_change = true; + sv_parser::Error::Parse(trace) => match trace { + Some((_, bpos)) => { + let mut line_start = text.byte_to_line(bpos); + let mut line_end = text.byte_to_line(bpos) + 1; + if !reverted_change { + if let Some(range) = last_change_range { + line_start = range.start.line as usize; + line_end = range.end.line as usize + 1; + reverted_change = true; + } } - } - // 把 last_change_range 中的涉及到的所有行内容用等长的空格替代 - for line_idx in line_start..line_end { - let line = text.line(line_idx); - let start_char = text.line_to_char(line_idx); - let line_length = line.len_chars(); - text.remove(start_char..(start_char + line_length - 1)); - text.insert(start_char, &" ".to_owned().repeat(line_length)); + // 把 last_change 处的地方替换成空格 + for line_idx in line_start .. line_end { + let line = text.line(line_idx); + let start_char = text.line_to_char(line_idx); + let line_length = line.len_chars(); + text.remove(start_char..(start_char + line_length - 1)); + text.insert(start_char, &" ".to_owned().repeat(line_length)); + } + parse_iterations += 1; } - parse_iterations += 1; + None => return None, }, // 遇到 include 错误,那就把提示中的 include 加入解析中再次解析 sv_parser::Error::Include { source: x } => { - info!("Error::Include"); if let sv_parser::Error::File { source: _, path: z } = *x { // Include paths have to be relative to the working directory // so we have to convert a source file relative path to a working directory @@ -491,18 +484,13 @@ pub fn recovery_sv_parse( // 宏定义不存在的错误 sv_parser::Error::DefineNotFound(not_found_macro_name) => { - info!("Error::DefineNotFound"); - let range = sv_parser::sv_parser_pp_range::Range { begin: 0, end: 0 }; - let pathbuf = PathBuf::from_str(uri.as_str()).unwrap(); - let origin = Some((pathbuf, range)); let com_define = Define { identifier: not_found_macro_name.to_string(), arguments: Vec::new(), - text: Some(DefineText {text: "undefined".to_string(), origin}) + text: Some(DefineText {text: "undefined".to_string(), origin: None}) }; defines.insert(not_found_macro_name, Some(com_define)); parse_iterations += 1; - info!("parse iteration plus one"); } _ => error!("parse error, {:?}", err), }; @@ -510,6 +498,7 @@ pub fn recovery_sv_parse( } } None + } //TODO: add bounds checking for utf8<->utf16 conversions @@ -597,106 +586,3 @@ impl<'a> LSPSupport for RopeSlice<'a> { panic!("can't edit a rope slice"); } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::support::test_init; - use std::fs::read_to_string; - - #[test] - fn test_open_and_change() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"module test; - logic abc; -endmodule"#; - - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - let file = server.srcs.get_file(fid).unwrap(); - let file = file.read().unwrap(); - assert_eq!(file.text.to_string(), text.to_owned()); - drop(file); - - let change_params = DidChangeTextDocumentParams { - text_document: VersionedTextDocumentIdentifier { uri, version: 1 }, - content_changes: vec![TextDocumentContentChangeEvent { - range: Some(Range { - start: Position { - line: 1, - character: 8, - }, - end: Position { - line: 1, - character: 11, - }, - }), - range_length: None, - text: "var1".to_owned(), - }], - }; - server.did_change(change_params); - let file = server.srcs.get_file(fid).unwrap(); - let file = file.read().unwrap(); - assert_eq!( - file.text.to_string(), - r#"module test; - logic var1; -endmodule"# - .to_owned() - ); - assert_eq!(file.version, 1); - } - - #[test] - fn test_fault_tolerance() { - test_init(); - let server = LSPServer::new(None); - let uri = Url::parse("file:///test.sv").unwrap(); - let text = r#"module test; - logic abc -endmodule"#; - let open_params = DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "systemverilog".to_owned(), - version: 0, - text: text.to_owned(), - }, - }; - server.did_open(open_params); - let fid = server.srcs.get_id(&uri); - - server.srcs.wait_parse_ready(fid, true); - - assert!(server - .srcs - .scope_tree - .read() - .unwrap() - .as_ref() - .unwrap() - .contains_scope("test")); - } - - #[test] - fn test_header() { - test_init(); - let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - d.push("test_data/top_inc.sv"); - let text = read_to_string(&d).unwrap(); - let doc = Rope::from_str(&text); - assert!(recovery_sv_parse(&doc, &Url::from_file_path(d).unwrap(), &None, &Vec::new()).is_some(),); - // TODO: add missing header test - } -} diff --git a/src/support.rs b/src/support.rs deleted file mode 100644 index 1b2df28..0000000 --- a/src/support.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn test_init() { - let _ = flexi_logger::Logger::with(flexi_logger::LogSpecification::info()).start(); -} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5e1ef0b..6e58a2c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -29,29 +29,48 @@ pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String { token } -pub fn get_word_range_at_position(line: &RopeSlice, pos: Position, regex: Regex) -> String { - let mut token = String::new(); +pub fn get_word_range_at_position(line: &RopeSlice, pos: Position, regex: Regex) -> Option<(String, Range)> { let mut line_iter = line.chars(); + + // 找到 character 这个位置 for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) { line_iter.next(); } let mut c = line_iter.prev(); + let mut token = String::new(); + let mut start_character = pos.character; + // 往前找 while c.is_some() && (regex.is_match(&c.unwrap().to_string())) { token.push(c.unwrap()); + start_character -= 1; c = line_iter.prev(); } token = token.chars().rev().collect(); line_iter = line.chars(); + for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) { line_iter.next(); } let mut c = line_iter.next(); + let mut end_character = pos.character; + // 往后找 while c.is_some() && (regex.is_match(&c.unwrap().to_string())) { token.push(c.unwrap()); + end_character += 1; c = line_iter.next(); } - token + + if token.len() == 0 { + return None; + } + + let range = Range::new( + Position { line: pos.line, character: start_character }, + Position { line: pos.line, character: end_character } + ); + + Some((token, range)) } /// 根据 uri 获取 hdl 的 language id