293 lines
10 KiB
Rust
293 lines
10 KiB
Rust
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<PathBuf> {
|
||
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()),
|
||
}
|
||
} |