diff --git a/src/core/fast_hdlparam.rs b/src/core/fast_hdlparam.rs index 24aad96..5254eb4 100644 --- a/src/core/fast_hdlparam.rs +++ b/src/core/fast_hdlparam.rs @@ -12,7 +12,7 @@ pub struct Range { pub end: Position } -#[derive(Debug, Serialize, PartialEq, Deserialize)] +#[derive(Debug, Serialize, PartialEq, Deserialize, Clone)] pub struct Port { pub name: String, #[serde(rename = "type")] @@ -23,7 +23,7 @@ pub struct Port { pub range: Range } -#[derive(Debug, Serialize, PartialEq, Deserialize)] +#[derive(Debug, Serialize, PartialEq, Deserialize, Clone)] pub struct Parameter { pub name: String, pub net_type: String, @@ -44,7 +44,7 @@ pub struct Parameter { // pub range: Range // } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Instance { pub name: String, #[serde(rename = "type")] @@ -54,7 +54,7 @@ pub struct Instance { pub range: Range } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Module { pub name: String, pub params: Vec, @@ -63,27 +63,27 @@ pub struct Module { pub range: Range } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct DefineParam { pub name: String, pub value: String } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Define { - name: String, - replacement: String, - range: Range, - params: Vec + pub name: String, + pub replacement: String, + pub range: Range, + pub params: Vec } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Include { pub path: String, pub range: Range } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Error { severity: String, message: String, @@ -93,7 +93,7 @@ pub struct Error { running_phase: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Macro { pub defines: Vec, pub includes: Vec, @@ -101,7 +101,7 @@ pub struct Macro { pub invalid: Vec } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct FastHdlparam { #[serde(rename = "macro")] diff --git a/src/definition/feature.rs b/src/definition/feature.rs index 3679cdb..870c1a5 100644 --- a/src/definition/feature.rs +++ b/src/definition/feature.rs @@ -1,12 +1,13 @@ use std::{path::PathBuf, str::FromStr}; use log::info; +use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url}; -use crate::utils::{resolve_path, to_escape_path}; +use crate::{server::LSPServer, utils::{get_word_range_at_position, resolve_path, to_escape_path}}; -/// 跳转到定义 +/// 跳转到 include 的文件 pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option { let line_text = line.as_str().unwrap_or(""); if line_text.trim().starts_with("`include") { @@ -48,7 +49,7 @@ pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Op let origin_selection_range = Range::new( Position { line: pos.line, character: first_quote_idx as u32 }, - Position { line: pos.line, character: last_quote_idx as u32 } + Position { line: pos.line, character: (last_quote_idx + 1) as u32 } ); let target_position = Position { line: 0, character: 0 }; @@ -68,4 +69,43 @@ pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Op } None +} + + +/// 跳转到宏定义 +pub fn goto_macro_definition(server: &LSPServer, line: &RopeSlice, pos: Position) -> Option { + let macro_text_regex = Regex::new(r"[`0-9a-zA-Z]").unwrap(); + if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) { + if macro_text.starts_with("`") { + if let Some((macro_define, define_path)) = server.find_macros(¯o_text) { + let define_path = PathBuf::from_str(&define_path).unwrap(); + let target_uri = match Url::from_file_path(define_path) { + Ok(uri) => uri, + Err(_) => return None + }; + + let start = macro_define.range.start; + let end = macro_define.range.end; + let target_range = Range::new( + Position { line: start.line, character: start.character }, + Position { line: end.line, character: end.character } + ); + let link = vec![LocationLink { + target_uri, + origin_selection_range: Some(range), + target_range: target_range, + target_selection_range: target_range + }]; + let links = GotoDefinitionResponse::Link(link); + return Some(links); + } + } + } + + None +} + + +pub fn goto_instance_definition() { + } \ No newline at end of file diff --git a/src/definition/mod.rs b/src/definition/mod.rs index 5b60d30..bfe518c 100644 --- a/src/definition/mod.rs +++ b/src/definition/mod.rs @@ -29,11 +29,18 @@ impl LSPServer { let line_text = file.text.line(pos.line as usize); let token: String = get_definition_token(&line_text, pos); - let include_definition = goto_include_definition(&doc, &line_text, pos); - if include_definition.is_some() { - return include_definition; + // match include + if let Some(definition) = goto_include_definition(&doc, &line_text, pos) { + return Some(definition); } + // match macro + if let Some(definition) = goto_macro_definition(self, &line_text, pos) { + return Some(definition); + } + + // match instance + let scope_tree = self.srcs.scope_tree.read().ok()?; let def = scope_tree diff --git a/src/hover/feature.rs b/src/hover/feature.rs index 4b88c50..0b796b1 100644 --- a/src/hover/feature.rs +++ b/src/hover/feature.rs @@ -1,8 +1,13 @@ +use std::{path::PathBuf, str::FromStr}; + +use log::info; use regex::Regex; use ropey::RopeSlice; -use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, Position}; +use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, Position, Range, Url}; -use super::get_word_range_at_position; +use crate::{core::fast_hdlparam::Define, server::LSPServer}; + +use super::{get_word_range_at_position, resolve_path, to_escape_path}; /// 将 4'b0011 分解为 ("b", "0011") fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> { @@ -98,4 +103,102 @@ pub fn match_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> } None -} \ No newline at end of file +} + +pub fn hover_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option { + let line_text = line.as_str().unwrap_or(""); + if line_text.trim().starts_with("`include") { + let character = pos.character as usize; + let first_quote_idx = line_text.find("\""); + let last_quote_idx = line_text.rfind("\""); + + if first_quote_idx.is_none() || last_quote_idx.is_none() { + return None; + } + + let first_quote_idx = first_quote_idx.unwrap(); + let last_quote_idx = last_quote_idx.unwrap(); + if character >= first_quote_idx && character <= last_quote_idx { + let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx]; + if path_string.starts_with("./") || path_string.starts_with(".\\") { + path_string = &path_string[2 ..]; + } + + // 路径转换 + let path = match PathBuf::from_str(uri.path()) { + Ok(path) => path, + Err(error) => { + info!("error happen in : {:?}", error); + return None; + } + }; + let escape_path = to_escape_path(&path); + let escape_path = escape_path.to_str().unwrap_or(""); + if escape_path.len() == 0 { + return None; + } + + if let Some(abs_path) = resolve_path(escape_path, path_string) { + let content = format!("{:?}", abs_path).replace("\\", "/"); + let language_string = LanguageString { + language: language_id.to_string(), + value: content + }; + let markdown = MarkedString::LanguageString(language_string); + let range = Range::new( + Position { line: pos.line, character: first_quote_idx as u32 }, + Position { line: pos.line, character: (last_quote_idx + 1) as u32 } + ); + let hover = Hover { + contents: HoverContents::Scalar(markdown), + range: Some(range) + }; + return Some(hover); + } + } + } + + None +} + +fn make_macro_define_content(macro_define: &Define) -> String { + let macro_name = macro_define.name.trim(); + let macro_value = macro_define.replacement.trim(); + if macro_define.params.len() == 0 { + format!("`define {} {}", macro_name, macro_value) + } else { + let mut macro_sig_vec: Vec = Vec::new(); + for macro_param in ¯o_define.params { + if macro_param.value == "Unknown" { + macro_sig_vec.push(macro_param.name.to_string()); + } else { + macro_sig_vec.push(format!("{}={}", macro_param.name, macro_param.value)); + } + } + + let macro_sig = macro_sig_vec.join(", "); + format!("`define {}({}) {}", macro_name, macro_sig, macro_value) + } +} + +pub fn hover_macro(server: &LSPServer, line: &RopeSlice, pos: Position, language_id: &str) -> Option { + let macro_text_regex = Regex::new(r"[`0-9a-zA-Z]").unwrap(); + if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) { + if macro_text.starts_with("`") { + if let Some((macro_define, _)) = server.find_macros(¯o_text) { + let content = make_macro_define_content(¯o_define); + let language_string = LanguageString { + language: language_id.to_string(), + value: content + }; + let markdown = MarkedString::LanguageString(language_string); + let hover = Hover { + contents: HoverContents::Scalar(markdown), + range: Some(range) + }; + return Some(hover); + } + } + } + None +} diff --git a/src/hover/mod.rs b/src/hover/mod.rs index d43341a..9d5174d 100644 --- a/src/hover/mod.rs +++ b/src/hover/mod.rs @@ -1,12 +1,11 @@ -use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard}; +use std::sync::RwLockReadGuard; use crate::definition::*; use crate::server::LSPServer; use crate::sources::LSPSupport; use crate::utils::*; -use log::info; use regex::Regex; -use ropey::{Rope, RopeSlice}; +use ropey::Rope; use tower_lsp::lsp_types::*; pub mod feature; @@ -26,10 +25,15 @@ impl LSPServer { let language_id = get_language_id_by_uri(&doc); // match `include - if let Some(hover) = match_include(&doc, &line_text, pos, &language_id) { + if let Some(hover) = hover_include(&doc, &line_text, pos, &language_id) { return Some(hover); } + // match macro + if let Some(hover) = hover_macro(self, &line_text, pos, &language_id) { + return Some(hover); + } + let scope_tree: RwLockReadGuard<'_, Option> = self.srcs.scope_tree.read().ok()?; let global_scope = scope_tree.as_ref(); @@ -89,12 +93,10 @@ fn get_hover(doc: &Rope, line: usize) -> String { } } hover.reverse(); - info!("hover array: {:?}", hover); let multi_space_regex = Regex::new(r"\s+").unwrap(); - let line_handler = |line: &str| -> String { - let line = multi_space_regex.replace_all(line.trim(), " "); + let line = multi_space_regex.replace_all(line.trim(), " "); let line = line.into_owned(); return format!("{}\n", line); }; @@ -112,60 +114,6 @@ fn get_hover(doc: &Rope, line: usize) -> String { result.join("").trim_end().to_owned() } - - -fn match_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option { - let line_text = line.as_str().unwrap_or(""); - if line_text.trim().starts_with("`include") { - let character = pos.character as usize; - let first_quote_idx = line_text.find("\""); - let last_quote_idx = line_text.rfind("\""); - - if first_quote_idx.is_none() || last_quote_idx.is_none() { - return None; - } - - let first_quote_idx = first_quote_idx.unwrap(); - let last_quote_idx = last_quote_idx.unwrap(); - if character >= first_quote_idx && character <= last_quote_idx { - let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx]; - if path_string.starts_with("./") || path_string.starts_with(".\\") { - path_string = &path_string[2 ..]; - } - - // 路径转换 - let path = match PathBuf::from_str(uri.path()) { - Ok(path) => path, - Err(error) => { - info!("error happen in : {:?}", error); - return None; - } - }; - let escape_path = to_escape_path(&path); - let escape_path = escape_path.to_str().unwrap_or(""); - if escape_path.len() == 0 { - return None; - } - - if let Some(abs_path) = resolve_path(escape_path, path_string) { - let content = format!("{:?}", abs_path); - let language_string = LanguageString { - language: language_id.to_string(), - value: content - }; - let markdown = MarkedString::LanguageString(language_string); - let hover = Hover { - contents: HoverContents::Scalar(markdown), - range: None - }; - return Some(hover); - } - } - } - - None -} - fn match_common_symbol( scope_tree: &GenericScope, token: &String, diff --git a/src/main.rs b/src/main.rs index 3f0e287..e31c0b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,8 @@ use request::{ CustomParamRequest, CustomRequest, DoFastApi }; -use digital_lsp::server::GLOBAL_FAST_MAP; use log::info; -use std::{collections::HashMap, sync::{Arc, RwLock}}; +use std::sync::Arc; use structopt::StructOpt; use tower_lsp::{LspService, Server}; @@ -40,10 +39,7 @@ async fn main() { let (service, socket) = LspService::build(|client| { let backend = Arc::new(Backend::new(client, log_handle)); - // 初始化全局变量 let _ = GLOBAL_BACKEND.set(backend.clone()); - let _ = GLOBAL_FAST_MAP.set(RwLock::new(HashMap::new())); - backend }) .custom_method("custom/request", CustomRequest) diff --git a/src/request.rs b/src/request.rs index fc0cf5a..be03904 100644 --- a/src/request.rs +++ b/src/request.rs @@ -13,7 +13,7 @@ use crate::core::fast_hdlparam::FastHdlparam; use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::utils::*; -use crate::server::{update_global_fast, Backend, GLOBAL_BACKEND, GLOBAL_FAST_MAP}; +use crate::server::{Backend, GLOBAL_BACKEND}; use crate::sources::recovery_sv_parse; @@ -69,8 +69,8 @@ impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoFastApiRequestParams, ) fn invoke(&self, _server: &'a Arc, _params: (DoFastApiRequestParams, )) -> Self::Future { let request_param = _params.0; let path = request_param.path; - let fut = future::ready(do_fast(path)); - fut + let hdlparam = do_fast(path, _server); + future::ready(hdlparam) } } @@ -85,7 +85,7 @@ fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option Result { +pub fn do_fast<'a>(path: String, _server: &Arc) -> Result { info!("parse fast {}", path); let path_buf = PathBuf::from(&path); @@ -115,12 +115,13 @@ pub fn do_fast(path: String) -> Result { &includes ); - - if let Some(syntax_tree) = parse_result { if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) { - update_global_fast(&hdlparam); - return Ok(hdlparam); + let mut fast_map = _server.server.srcs.fast_map.write().unwrap(); + fast_map.insert(path.to_string(), hdlparam); + if let Some(hdlparam) = fast_map.get(&path) { + return Ok(hdlparam.clone()); + } } } diff --git a/src/server.rs b/src/server.rs index 671882f..4c73dc7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,3 @@ -use crate::core::fast_hdlparam::FastHdlparam; use crate::sources::*; use crate::completion::keyword::*; use flexi_logger::LoggerHandle; @@ -6,7 +5,6 @@ use flexi_logger::LoggerHandle; use log::{debug, info, warn}; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::string::ToString; use std::sync::{Arc, Mutex, RwLock}; use tower_lsp::jsonrpc::Result; @@ -14,7 +12,6 @@ use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer}; pub static GLOBAL_BACKEND: OnceCell> = OnceCell::new(); -pub static GLOBAL_FAST_MAP: OnceCell>> = OnceCell::new(); pub struct LSPServer { pub srcs: Sources, @@ -301,6 +298,3 @@ impl LanguageServer for Backend { Ok(self.server.document_highlight(params)) } } - -pub fn update_global_fast(fast: &FastHdlparam) { -} \ No newline at end of file diff --git a/src/sources.rs b/src/sources.rs index fc5365a..7d6c411 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -1,3 +1,4 @@ +use crate::core::fast_hdlparam::FastHdlparam; use crate::definition::def_types::*; use crate::definition::get_scopes; use crate::diagnostics::{get_diagnostics, is_hidden}; @@ -162,6 +163,8 @@ pub struct Sources { pub include_dirs: Arc>>, // source directories pub source_dirs: Arc>>, + // fast result + pub fast_map: Arc>> } impl std::default::Default for Sources { @@ -179,6 +182,7 @@ impl Sources { scope_tree: Arc::new(RwLock::new(None)), include_dirs: Arc::new(RwLock::new(Vec::new())), source_dirs: Arc::new(RwLock::new(Vec::new())), + fast_map: Arc::new(RwLock::new(HashMap::new())) } } pub fn init(&self) { diff --git a/src/utils/fast.rs b/src/utils/fast.rs new file mode 100644 index 0000000..af0f77e --- /dev/null +++ b/src/utils/fast.rs @@ -0,0 +1,21 @@ + +use crate::{core::fast_hdlparam::Define, server::LSPServer}; + +impl LSPServer { + /// 根据输入的 macro 名字,寻找 fast 中存在的第一个 macro + /// macro 可以以 ` 开头 + pub fn find_macros(&self, macro_name: &str) -> Option<(Define, String)> { + let macro_name = macro_name.replace("`", ""); + let fast_map = self.srcs.fast_map.read().unwrap(); + for path in fast_map.keys() { + if let Some(hdlparam) = fast_map.get(path) { + for define in &hdlparam.fast_macro.defines { + if define.name == macro_name { + return Some((define.clone(), path.to_string())); + } + } + } + } + None + } +} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 08c4caa..fc80ac5 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -5,6 +5,8 @@ use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::*; +pub mod fast; + /// 根据 pos 获取到当前光标所在的字符串,相当于 getWordRangeAtPosition pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String { let mut token = String::new(); @@ -137,5 +139,4 @@ pub fn to_escape_path(path: &PathBuf) -> PathBuf { PathBuf::from(decoded_path_str) }, } -} - +} \ No newline at end of file