2024-09-29 22:08:18 +08:00

205 lines
7.0 KiB
Rust

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, Range, Url};
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)> {
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::<u32>();
if width.is_err() {
return None;
}
return Some((tag.unwrap(), digit.unwrap()));
},
None => return None
};
}
fn convert_tag_to_radix(tag: &str) -> Option<u32> {
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 hover_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> {
let regex = Regex::new(r"[0-9'bho]").unwrap();
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
}
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
}