use std::{env::consts::OS, path::{Path, PathBuf}}; use percent_encoding::percent_decode_str; use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::*; use vhdl_lang::{ast::ObjectClass, AnyEntKind, Concurrent, Object, Overloaded, SrcPos, Type}; pub mod fast; pub mod file; pub use file::*; /// 根据 pos 获取到当前光标所在的字符串,相当于 getWordRangeAtPosition pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String { let mut token = String::new(); let mut line_iter = line.chars(); for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) { line_iter.next(); } let mut c = line_iter.prev(); while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') { token.push(c.unwrap()); 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(); while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') { token.push(c.unwrap()); c = line_iter.next(); } token } 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(); } 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 /// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext" /// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值 pub fn get_language_id_by_uri(uri: &Url) -> String { let path = uri.path(); let ext_name = std::path::Path::new(path) .extension() .and_then(std::ffi::OsStr::to_str) .unwrap_or(""); get_language_id_by_extname(ext_name) } /// 根据路径字符串获取 hdl 的 language id /// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext" /// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值 pub fn get_language_id_by_pathbuf(pathbuf: &PathBuf) -> String { let ext_name = pathbuf.as_path() .extension() .and_then(std::ffi::OsStr::to_str) .unwrap_or(""); get_language_id_by_extname(ext_name) } /// 根据路径字符串获取 hdl 的 language id /// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext" /// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值 pub fn get_language_id_by_path_str(path_str: &str) -> String { let ext_name = std::path::Path::new(path_str) .extension() .and_then(std::ffi::OsStr::to_str) .unwrap_or(""); get_language_id_by_extname(ext_name) } fn get_language_id_by_extname(ext_name: &str) -> String { match ext_name { "vhd" | "vhdl" | "vho" | "vht" => "vhdl".to_string(), "v" | "V" | "vh" | "vl" => "verilog".to_string(), "sv" | "svh" => "systemverilog".to_string(), _ => "plaintext".to_string() } } /// 根据基础路径和给出的 path 路径,计算出绝对路径 /// /// 如果 path 是绝对路径直接返回 /// 会进行存在性检查,不存在的路径直接返回 None /// /// 例子: /// /// resolve_path("/home/ubuntu/hello.v", "./control.v") -> Some("/home/ubuntu/control.v") /// /// resolve_path("/home/ubuntu/hello.v", "/opt/control.v") -> Some("/opt/control.v") /// pub fn resolve_path(base: &str, path_string: &str) -> Option { let path = Path::new(path_string); let abs_path: PathBuf; if path.is_absolute() { abs_path = PathBuf::from(path); } else { let base = Path::new(base).parent()?; abs_path = base.join(path); } if abs_path.exists() { let path = abs_path.as_path().to_owned(); return Some(path); } None } /// 把 uri 路径转换成合法的操作系统文件路径 /// /// windows 中 /// /// /c%3A/Users/MyCpu.v 转换成 c:/Users/MyCpu.v pub fn to_escape_path(path: &PathBuf) -> PathBuf { let decoded_path = percent_decode_str(path.to_str().unwrap()).decode_utf8_lossy(); let decoded_path_str = decoded_path.as_ref(); match OS { "windows" => { // 去掉开头的斜杠 let trimmed_path_str = decoded_path_str.trim_start_matches('/'); PathBuf::from(trimmed_path_str) }, _ => { // 其他操作系统(如 Linux)保持原样 PathBuf::from(decoded_path_str) }, } } pub fn to_lsp_pos(position: vhdl_lang::Position) -> Position { Position { line: position.line, character: position.character, } } pub fn to_lsp_range(range: vhdl_lang::Range) -> Range { Range { start: to_lsp_pos(range.start), end: to_lsp_pos(range.end), } } pub fn to_symbol_kind(kind: &AnyEntKind) -> SymbolKind { match kind { AnyEntKind::ExternalAlias { class, .. } => object_class_kind(ObjectClass::from(*class)), AnyEntKind::ObjectAlias { base_object, .. } => object_kind(base_object.object()), AnyEntKind::Object(o) => object_kind(o), AnyEntKind::LoopParameter(_) => SymbolKind::CONSTANT, AnyEntKind::PhysicalLiteral(_) => SymbolKind::CONSTANT, AnyEntKind::DeferredConstant(_) => SymbolKind::CONSTANT, AnyEntKind::File { .. } => SymbolKind::FILE, AnyEntKind::InterfaceFile { .. } => SymbolKind::INTERFACE, AnyEntKind::Component(_) => SymbolKind::CLASS, AnyEntKind::Attribute(_) => SymbolKind::PROPERTY, AnyEntKind::Overloaded(o) => overloaded_kind(o), AnyEntKind::Type(t) => type_kind(t), AnyEntKind::ElementDeclaration(_) => SymbolKind::FIELD, AnyEntKind::Sequential(_) => SymbolKind::NAMESPACE, AnyEntKind::Concurrent(Some(Concurrent::Instance)) => SymbolKind::MODULE, AnyEntKind::Concurrent(_) => SymbolKind::NAMESPACE, AnyEntKind::Library => SymbolKind::NAMESPACE, AnyEntKind::View(_) => SymbolKind::INTERFACE, AnyEntKind::Design(d) => match d { vhdl_lang::Design::Entity(_, _) => SymbolKind::MODULE, vhdl_lang::Design::Architecture(..) => SymbolKind::MODULE, vhdl_lang::Design::Configuration => SymbolKind::MODULE, vhdl_lang::Design::Package(_, _) => SymbolKind::PACKAGE, vhdl_lang::Design::PackageBody(..) => SymbolKind::PACKAGE, vhdl_lang::Design::UninstPackage(_, _) => SymbolKind::PACKAGE, vhdl_lang::Design::PackageInstance(_) => SymbolKind::PACKAGE, vhdl_lang::Design::InterfacePackageInstance(_) => SymbolKind::PACKAGE, vhdl_lang::Design::Context(_) => SymbolKind::NAMESPACE, }, } } pub fn type_kind(t: &Type) -> SymbolKind { match t { vhdl_lang::Type::Array { .. } => SymbolKind::ARRAY, vhdl_lang::Type::Enum(_) => SymbolKind::ENUM, vhdl_lang::Type::Integer => SymbolKind::NUMBER, vhdl_lang::Type::Real => SymbolKind::NUMBER, vhdl_lang::Type::Physical => SymbolKind::NUMBER, vhdl_lang::Type::Access(_) => SymbolKind::ENUM, vhdl_lang::Type::Record(_) => SymbolKind::STRUCT, vhdl_lang::Type::Incomplete => SymbolKind::NULL, vhdl_lang::Type::Subtype(t) => type_kind(t.type_mark().kind()), vhdl_lang::Type::Protected(_, _) => SymbolKind::CLASS, vhdl_lang::Type::File => SymbolKind::FILE, vhdl_lang::Type::Interface => SymbolKind::TYPE_PARAMETER, vhdl_lang::Type::Alias(t) => type_kind(t.kind()), vhdl_lang::Type::Universal(_) => SymbolKind::NUMBER, } } pub fn overloaded_kind(overloaded: &Overloaded) -> SymbolKind { match overloaded { Overloaded::SubprogramDecl(_) => SymbolKind::FUNCTION, Overloaded::Subprogram(_) => SymbolKind::FUNCTION, Overloaded::UninstSubprogramDecl(..) => SymbolKind::FUNCTION, Overloaded::UninstSubprogram(..) => SymbolKind::FUNCTION, Overloaded::InterfaceSubprogram(_) => SymbolKind::FUNCTION, Overloaded::EnumLiteral(_) => SymbolKind::ENUM_MEMBER, Overloaded::Alias(o) => overloaded_kind(o.kind()), } } pub fn object_kind(object: &Object) -> SymbolKind { if matches!(object.subtype.type_mark().kind(), Type::Protected(..)) { SymbolKind::OBJECT } else if object.iface.is_some() { SymbolKind::INTERFACE } else { object_class_kind(object.class) } } pub fn object_class_kind(class: ObjectClass) -> SymbolKind { match class { ObjectClass::Signal => SymbolKind::EVENT, ObjectClass::Constant => SymbolKind::CONSTANT, ObjectClass::Variable => SymbolKind::VARIABLE, ObjectClass::SharedVariable => SymbolKind::VARIABLE, } } pub fn from_lsp_pos(position: Position) -> vhdl_lang::Position { vhdl_lang::Position { line: position.line, character: position.character, } } pub fn from_lsp_range(range: Range) -> vhdl_lang::Range { vhdl_lang::Range { start: from_lsp_pos(range.start), end: from_lsp_pos(range.end), } } pub fn srcpos_to_location(pos: &SrcPos) -> Location { let uri = Url::from_file_path(pos.source.file_name()).unwrap(); Location { uri, range: to_lsp_range(pos.range()), } }