实现 macro 跳转

This commit is contained in:
锦恢 2024-09-28 21:42:04 +08:00
parent 0040a4eb28
commit b3d7a9faa1
11 changed files with 220 additions and 105 deletions

View File

@ -12,7 +12,7 @@ pub struct Range {
pub end: Position pub end: Position
} }
#[derive(Debug, Serialize, PartialEq, Deserialize)] #[derive(Debug, Serialize, PartialEq, Deserialize, Clone)]
pub struct Port { pub struct Port {
pub name: String, pub name: String,
#[serde(rename = "type")] #[serde(rename = "type")]
@ -23,7 +23,7 @@ pub struct Port {
pub range: Range pub range: Range
} }
#[derive(Debug, Serialize, PartialEq, Deserialize)] #[derive(Debug, Serialize, PartialEq, Deserialize, Clone)]
pub struct Parameter { pub struct Parameter {
pub name: String, pub name: String,
pub net_type: String, pub net_type: String,
@ -44,7 +44,7 @@ pub struct Parameter {
// pub range: Range // pub range: Range
// } // }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Instance { pub struct Instance {
pub name: String, pub name: String,
#[serde(rename = "type")] #[serde(rename = "type")]
@ -54,7 +54,7 @@ pub struct Instance {
pub range: Range pub range: Range
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Module { pub struct Module {
pub name: String, pub name: String,
pub params: Vec<Parameter>, pub params: Vec<Parameter>,
@ -63,27 +63,27 @@ pub struct Module {
pub range: Range pub range: Range
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DefineParam { pub struct DefineParam {
pub name: String, pub name: String,
pub value: String pub value: String
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Define { pub struct Define {
name: String, pub name: String,
replacement: String, pub replacement: String,
range: Range, pub range: Range,
params: Vec<DefineParam> pub params: Vec<DefineParam>
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Include { pub struct Include {
pub path: String, pub path: String,
pub range: Range pub range: Range
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Error { pub struct Error {
severity: String, severity: String,
message: String, message: String,
@ -93,7 +93,7 @@ pub struct Error {
running_phase: Option<String>, running_phase: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Macro { pub struct Macro {
pub defines: Vec<Define>, pub defines: Vec<Define>,
pub includes: Vec<Include>, pub includes: Vec<Include>,
@ -101,7 +101,7 @@ pub struct Macro {
pub invalid: Vec<Range> pub invalid: Vec<Range>
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct FastHdlparam { pub struct FastHdlparam {
#[serde(rename = "macro")] #[serde(rename = "macro")]

View File

@ -1,12 +1,13 @@
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
use log::info; use log::info;
use regex::Regex;
use ropey::RopeSlice; use ropey::RopeSlice;
use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url}; 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<GotoDefinitionResponse> { pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
let line_text = line.as_str().unwrap_or(""); let line_text = line.as_str().unwrap_or("");
if line_text.trim().starts_with("`include") { 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( let origin_selection_range = Range::new(
Position { line: pos.line, character: first_quote_idx as u32 }, 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 }; let target_position = Position { line: 0, character: 0 };
@ -69,3 +70,42 @@ pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Op
None None
} }
/// 跳转到宏定义
pub fn goto_macro_definition(server: &LSPServer, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
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(&macro_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() {
}

View File

@ -29,11 +29,18 @@ impl LSPServer {
let line_text = file.text.line(pos.line as usize); let line_text = file.text.line(pos.line as usize);
let token: String = get_definition_token(&line_text, pos); let token: String = get_definition_token(&line_text, pos);
let include_definition = goto_include_definition(&doc, &line_text, pos); // match include
if include_definition.is_some() { if let Some(definition) = goto_include_definition(&doc, &line_text, pos) {
return include_definition; 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 scope_tree = self.srcs.scope_tree.read().ok()?;
let def = scope_tree let def = scope_tree

View File

@ -1,8 +1,13 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
use regex::Regex; use regex::Regex;
use ropey::RopeSlice; 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") /// 将 4'b0011 分解为 ("b", "0011")
fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> { fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> {
@ -99,3 +104,101 @@ pub fn match_format_digit(line: &RopeSlice, pos: Position, language_id: &str) ->
None None
} }
pub fn hover_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option<Hover> {
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 <goto_include_definition>: {:?}", 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<String> = Vec::new();
for macro_param in &macro_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<Hover> {
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(&macro_text) {
let content = make_macro_define_content(&macro_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
}

View File

@ -1,12 +1,11 @@
use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard}; use std::sync::RwLockReadGuard;
use crate::definition::*; use crate::definition::*;
use crate::server::LSPServer; use crate::server::LSPServer;
use crate::sources::LSPSupport; use crate::sources::LSPSupport;
use crate::utils::*; use crate::utils::*;
use log::info;
use regex::Regex; use regex::Regex;
use ropey::{Rope, RopeSlice}; use ropey::Rope;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
pub mod feature; pub mod feature;
@ -26,7 +25,12 @@ impl LSPServer {
let language_id = get_language_id_by_uri(&doc); let language_id = get_language_id_by_uri(&doc);
// match `include // 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); return Some(hover);
} }
@ -89,10 +93,8 @@ fn get_hover(doc: &Rope, line: usize) -> String {
} }
} }
hover.reverse(); hover.reverse();
info!("hover array: {:?}", hover);
let multi_space_regex = Regex::new(r"\s+").unwrap(); let multi_space_regex = Regex::new(r"\s+").unwrap();
let line_handler = |line: &str| -> String { 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(); let line = line.into_owned();
@ -112,60 +114,6 @@ fn get_hover(doc: &Rope, line: usize) -> String {
result.join("").trim_end().to_owned() result.join("").trim_end().to_owned()
} }
fn match_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option<Hover> {
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 <goto_include_definition>: {:?}", 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( fn match_common_symbol(
scope_tree: &GenericScope, scope_tree: &GenericScope,
token: &String, token: &String,

View File

@ -2,9 +2,8 @@
use request::{ CustomParamRequest, CustomRequest, DoFastApi }; use request::{ CustomParamRequest, CustomRequest, DoFastApi };
use digital_lsp::server::GLOBAL_FAST_MAP;
use log::info; use log::info;
use std::{collections::HashMap, sync::{Arc, RwLock}}; use std::sync::Arc;
use structopt::StructOpt; use structopt::StructOpt;
use tower_lsp::{LspService, Server}; use tower_lsp::{LspService, Server};
@ -40,10 +39,7 @@ async fn main() {
let (service, socket) = LspService::build(|client| { let (service, socket) = LspService::build(|client| {
let backend = Arc::new(Backend::new(client, log_handle)); let backend = Arc::new(Backend::new(client, log_handle));
// 初始化全局变量
let _ = GLOBAL_BACKEND.set(backend.clone()); let _ = GLOBAL_BACKEND.set(backend.clone());
let _ = GLOBAL_FAST_MAP.set(RwLock::new(HashMap::new()));
backend backend
}) })
.custom_method("custom/request", CustomRequest) .custom_method("custom/request", CustomRequest)

View File

@ -13,7 +13,7 @@ use crate::core::fast_hdlparam::FastHdlparam;
use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::core::sv_parser::make_fast_from_syntaxtree;
use crate::utils::*; 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; use crate::sources::recovery_sv_parse;
@ -69,8 +69,8 @@ impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (DoFastApiRequestParams, )
fn invoke(&self, _server: &'a Arc<Backend>, _params: (DoFastApiRequestParams, )) -> Self::Future { fn invoke(&self, _server: &'a Arc<Backend>, _params: (DoFastApiRequestParams, )) -> Self::Future {
let request_param = _params.0; let request_param = _params.0;
let path = request_param.path; let path = request_param.path;
let fut = future::ready(do_fast(path)); let hdlparam = do_fast(path, _server);
fut future::ready(hdlparam)
} }
} }
@ -85,7 +85,7 @@ fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option<TextDocumentItem
} }
/// 前端交互接口: do_fast输入文件路径计算出对应的 fast 结构 /// 前端交互接口: do_fast输入文件路径计算出对应的 fast 结构
pub fn do_fast(path: String) -> Result<FastHdlparam> { pub fn do_fast<'a>(path: String, _server: &Arc<Backend>) -> Result<FastHdlparam> {
info!("parse fast {}", path); info!("parse fast {}", path);
let path_buf = PathBuf::from(&path); let path_buf = PathBuf::from(&path);
@ -115,12 +115,13 @@ pub fn do_fast(path: String) -> Result<FastHdlparam> {
&includes &includes
); );
if let Some(syntax_tree) = parse_result { if let Some(syntax_tree) = parse_result {
if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) { if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) {
update_global_fast(&hdlparam); let mut fast_map = _server.server.srcs.fast_map.write().unwrap();
return Ok(hdlparam); fast_map.insert(path.to_string(), hdlparam);
if let Some(hdlparam) = fast_map.get(&path) {
return Ok(hdlparam.clone());
}
} }
} }

View File

@ -1,4 +1,3 @@
use crate::core::fast_hdlparam::FastHdlparam;
use crate::sources::*; use crate::sources::*;
use crate::completion::keyword::*; use crate::completion::keyword::*;
use flexi_logger::LoggerHandle; use flexi_logger::LoggerHandle;
@ -6,7 +5,6 @@ use flexi_logger::LoggerHandle;
use log::{debug, info, warn}; use log::{debug, info, warn};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::string::ToString; use std::string::ToString;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use tower_lsp::jsonrpc::Result; use tower_lsp::jsonrpc::Result;
@ -14,7 +12,6 @@ use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer}; use tower_lsp::{Client, LanguageServer};
pub static GLOBAL_BACKEND: OnceCell<Arc<Backend>> = OnceCell::new(); pub static GLOBAL_BACKEND: OnceCell<Arc<Backend>> = OnceCell::new();
pub static GLOBAL_FAST_MAP: OnceCell<RwLock<HashMap<String, FastHdlparam>>> = OnceCell::new();
pub struct LSPServer { pub struct LSPServer {
pub srcs: Sources, pub srcs: Sources,
@ -301,6 +298,3 @@ impl LanguageServer for Backend {
Ok(self.server.document_highlight(params)) Ok(self.server.document_highlight(params))
} }
} }
pub fn update_global_fast(fast: &FastHdlparam) {
}

View File

@ -1,3 +1,4 @@
use crate::core::fast_hdlparam::FastHdlparam;
use crate::definition::def_types::*; use crate::definition::def_types::*;
use crate::definition::get_scopes; use crate::definition::get_scopes;
use crate::diagnostics::{get_diagnostics, is_hidden}; use crate::diagnostics::{get_diagnostics, is_hidden};
@ -162,6 +163,8 @@ pub struct Sources {
pub include_dirs: Arc<RwLock<Vec<PathBuf>>>, pub include_dirs: Arc<RwLock<Vec<PathBuf>>>,
// source directories // source directories
pub source_dirs: Arc<RwLock<Vec<PathBuf>>>, pub source_dirs: Arc<RwLock<Vec<PathBuf>>>,
// fast result
pub fast_map: Arc<RwLock<HashMap<String, FastHdlparam>>>
} }
impl std::default::Default for Sources { impl std::default::Default for Sources {
@ -179,6 +182,7 @@ impl Sources {
scope_tree: Arc::new(RwLock::new(None)), scope_tree: Arc::new(RwLock::new(None)),
include_dirs: Arc::new(RwLock::new(Vec::new())), include_dirs: Arc::new(RwLock::new(Vec::new())),
source_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) { pub fn init(&self) {

21
src/utils/fast.rs Normal file
View File

@ -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
}
}

View File

@ -5,6 +5,8 @@ use regex::Regex;
use ropey::RopeSlice; use ropey::RopeSlice;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
pub mod fast;
/// 根据 pos 获取到当前光标所在的字符串,相当于 getWordRangeAtPosition /// 根据 pos 获取到当前光标所在的字符串,相当于 getWordRangeAtPosition
pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String { pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String {
let mut token = String::new(); let mut token = String::new();
@ -138,4 +140,3 @@ pub fn to_escape_path(path: &PathBuf) -> PathBuf {
}, },
} }
} }