2024-10-01 21:29:55 +08:00

293 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()),
}
}