更加完善的宏相关的自动补全
This commit is contained in:
parent
7cad6176b6
commit
20500a55ca
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1 +1,3 @@
|
|||||||
{}
|
{
|
||||||
|
"rust-analyzer.trace.server": "verbose"
|
||||||
|
}
|
@ -4,9 +4,14 @@ use log::info;
|
|||||||
use ropey::RopeSlice;
|
use ropey::RopeSlice;
|
||||||
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionList, Position, Url};
|
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionList, Position, Url};
|
||||||
|
|
||||||
use crate::{server::LspServer, utils::{resolve_path, to_escape_path}};
|
use crate::{server::LspServer, utils::{is_character_ordered_match, resolve_path, to_escape_path}};
|
||||||
|
|
||||||
pub fn include_path_completion(uri: &Url, line: &RopeSlice, pos: Position) -> Option<CompletionList> {
|
/// 补全 include 内部的系统路径
|
||||||
|
pub fn include_path_completion(
|
||||||
|
uri: &Url,
|
||||||
|
line: &RopeSlice,
|
||||||
|
pos: Position
|
||||||
|
) -> Option<CompletionList> {
|
||||||
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") {
|
||||||
let character = pos.character as usize;
|
let character = pos.character as usize;
|
||||||
@ -87,6 +92,39 @@ pub fn include_path_completion(uri: &Url, line: &RopeSlice, pos: Position) -> Op
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 补全宏
|
||||||
|
pub fn vlog_directives_completion(
|
||||||
|
token: &str,
|
||||||
|
server: &LspServer
|
||||||
|
) -> Option<CompletionList> {
|
||||||
|
// 先把固定宏定义比如 include, define 这种的放入其中
|
||||||
|
let mut completion_items = server.vlog_directives.clone();
|
||||||
|
|
||||||
|
// 再从每个文件中搜集定义的宏
|
||||||
|
let hdl_param = server.srcs.hdl_param.clone();
|
||||||
|
let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap();
|
||||||
|
|
||||||
|
// 遍历所有的 defines
|
||||||
|
for hdl_file in path_to_hdl_file.values() {
|
||||||
|
for define in &hdl_file.fast.fast_macro.defines {
|
||||||
|
if is_character_ordered_match(token, &define.name) {
|
||||||
|
completion_items.push(CompletionItem {
|
||||||
|
label: define.name.to_string(),
|
||||||
|
kind: Some(CompletionItemKind::CONSTANT),
|
||||||
|
// 用户定义的宏默认最高优先级
|
||||||
|
sort_text: Some("0001".to_string()),
|
||||||
|
..CompletionItem::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(CompletionList {
|
||||||
|
is_incomplete: false,
|
||||||
|
items: completion_items
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_dot_completion(
|
pub fn get_dot_completion(
|
||||||
server: &LspServer,
|
server: &LspServer,
|
||||||
line: &RopeSlice,
|
line: &RopeSlice,
|
||||||
|
@ -136,8 +136,8 @@ pub const VLOG_KEYWORDS: &[(&str, &str)] = &[
|
|||||||
("incdir", ""),
|
("incdir", ""),
|
||||||
("include", "`include \"$1\""),
|
("include", "`include \"$1\""),
|
||||||
("initial", "initial begin\n\t$1\nend"),
|
("initial", "initial begin\n\t$1\nend"),
|
||||||
("inout", ""),
|
("inout", "inout $1"),
|
||||||
("input", ""),
|
("input", "input $1"),
|
||||||
("inside", ""),
|
("inside", ""),
|
||||||
("instance", ""),
|
("instance", ""),
|
||||||
("int", ""),
|
("int", ""),
|
||||||
@ -174,7 +174,7 @@ pub const VLOG_KEYWORDS: &[(&str, &str)] = &[
|
|||||||
("notif1", ""),
|
("notif1", ""),
|
||||||
("null", ""),
|
("null", ""),
|
||||||
("or", ""),
|
("or", ""),
|
||||||
("output", ""),
|
("output", "output $1"),
|
||||||
("package", "package $1;\nendpackage"),
|
("package", "package $1;\nendpackage"),
|
||||||
("packed", ""),
|
("packed", ""),
|
||||||
("parameter", ""),
|
("parameter", ""),
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LspServer, sources::LSPSupport, utils::get_language_id_by_uri};
|
use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LspServer, sources::LSPSupport, utils::{get_definition_token, get_language_id_by_uri, is_character_ordered_match}};
|
||||||
use log::info;
|
use log::info;
|
||||||
use ropey::{Rope, RopeSlice};
|
use ropey::{Rope, RopeSlice};
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
|
use super::feature::vlog_directives_completion;
|
||||||
|
|
||||||
|
|
||||||
pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<CompletionResponse> {
|
pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<CompletionResponse> {
|
||||||
let doc = ¶ms.text_document_position;
|
let doc = ¶ms.text_document_position;
|
||||||
@ -15,46 +17,48 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
|
|||||||
let file = server.srcs.get_file(file_id)?;
|
let file = server.srcs.get_file(file_id)?;
|
||||||
let file = file.read().ok()?;
|
let file = file.read().ok()?;
|
||||||
let line_text = file.text.line(doc.position.line as usize);
|
let line_text = file.text.line(doc.position.line as usize);
|
||||||
let token = get_completion_token(
|
let token = get_definition_token(&line_text, doc.position);
|
||||||
&file.text,
|
|
||||||
line_text.clone(),
|
|
||||||
doc.position,
|
|
||||||
);
|
|
||||||
|
|
||||||
// info!("trigger completion token: {}", token);
|
|
||||||
let line_text = file.text.line(pos.line as usize);
|
let line_text = file.text.line(pos.line as usize);
|
||||||
|
|
||||||
let response = match ¶ms.context {
|
let response = match ¶ms.context {
|
||||||
Some(context) => match context.trigger_kind {
|
Some(context) => match context.trigger_kind {
|
||||||
// 特殊字符触发
|
// 特殊字符触发
|
||||||
CompletionTriggerKind::TRIGGER_CHARACTER => {
|
CompletionTriggerKind::TRIGGER_CHARACTER => {
|
||||||
// info!("trigger character");
|
|
||||||
let trigger_character = context.trigger_character.clone().unwrap();
|
let trigger_character = context.trigger_character.clone().unwrap();
|
||||||
match trigger_character.as_str() {
|
match trigger_character.as_str() {
|
||||||
"." => {
|
"." => {
|
||||||
info!("trigger dot completion");
|
// 用户按下 . 如果是在例化 scope 中,则补全对应的 port
|
||||||
get_dot_completion(server, &line_text, uri, &pos, &language_id)
|
get_dot_completion(server, &line_text, uri, &pos, &language_id)
|
||||||
},
|
},
|
||||||
"$" => Some(CompletionList {
|
"$" => {
|
||||||
|
// 用户按下 $ 补全系统函数
|
||||||
|
Some(CompletionList {
|
||||||
is_incomplete: false,
|
is_incomplete: false,
|
||||||
items: server.vlog_sys_tasks_completion_items.clone(),
|
items: server.vlog_sys_tasks_completion_items.clone(),
|
||||||
}),
|
})
|
||||||
"`" => Some(CompletionList {
|
},
|
||||||
is_incomplete: false,
|
"`" => {
|
||||||
items: server.vlog_directives.clone(),
|
// 用户按下 ` , 补全系统宏定义和用户自己的宏定义
|
||||||
}),
|
vlog_directives_completion(&token, server)
|
||||||
|
},
|
||||||
"/" => {
|
"/" => {
|
||||||
info!("trigger include");
|
// 用户按下 / ,如果在 "" 内触发,路径的自动补全
|
||||||
include_path_completion(&doc.text_document.uri, &line_text, pos)
|
include_path_completion(&doc.text_document.uri, &line_text, pos)
|
||||||
},
|
},
|
||||||
"\"" => {
|
"\"" => {
|
||||||
info!("trigger include");
|
// 用户按下 " ,如果开头有 include,则自动补全
|
||||||
include_path_completion(&doc.text_document.uri, &line_text, pos)
|
include_path_completion(&doc.text_document.uri, &line_text, pos)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对于上一次自动补全结果 is_incomplete: true 的操作,还会额外触发一次自动补全,
|
||||||
|
// 它的逻辑在这个分支里面
|
||||||
CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS => None,
|
CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS => None,
|
||||||
|
|
||||||
// 常规触发
|
// 常规触发
|
||||||
CompletionTriggerKind::INVOKED => {
|
CompletionTriggerKind::INVOKED => {
|
||||||
// 1. 先根据 AST 获取上下文补全项
|
// 1. 先根据 AST 获取上下文补全项
|
||||||
@ -71,7 +75,7 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
|
|||||||
completion_items.items.extend::<Vec<CompletionItem>>(
|
completion_items.items.extend::<Vec<CompletionItem>>(
|
||||||
server.vlog_keyword_completion_items
|
server.vlog_keyword_completion_items
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.label.starts_with(&token))
|
.filter(|x| is_character_ordered_match(&token, &x.label))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
@ -81,7 +85,7 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
|
|||||||
completion_items.items.extend::<Vec<CompletionItem>>(
|
completion_items.items.extend::<Vec<CompletionItem>>(
|
||||||
server.vlog_sys_tasks_completion_items
|
server.vlog_sys_tasks_completion_items
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.label.starts_with(&token))
|
.filter(|x| is_character_ordered_match(&token, &x.label))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
@ -91,126 +95,22 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
|
|||||||
make_module_completions(server, &token, &language_id)
|
make_module_completions(server, &token, &language_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 不知道为什么会有重复,去重就完事
|
||||||
completion_items.items.dedup_by_key(|i| i.label.clone());
|
completion_items.items.dedup_by_key(|i| i.label.clone());
|
||||||
|
|
||||||
|
|
||||||
// info!("invoked return completion_items {:?}", completion_items);
|
// info!("invoked return completion_items {:?}", completion_items);
|
||||||
Some(completion_items)
|
Some(completion_items)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let trigger = prev_char(&file.text, &doc.position);
|
return None;
|
||||||
match trigger {
|
|
||||||
'.' => Some(server.srcs.get_dot_completions(
|
|
||||||
token.trim_end_matches('.'),
|
|
||||||
file.text.pos_to_byte(&doc.position),
|
|
||||||
&doc.text_document.uri,
|
|
||||||
)?),
|
|
||||||
'$' => Some(CompletionList {
|
|
||||||
is_incomplete: false,
|
|
||||||
items: server.vlog_sys_tasks_completion_items.clone(),
|
|
||||||
}),
|
|
||||||
'`' => Some(CompletionList {
|
|
||||||
is_incomplete: false,
|
|
||||||
items: server.vlog_directives.clone(),
|
|
||||||
}),
|
|
||||||
_ => {
|
|
||||||
let mut completion_items = server.srcs.get_completions(
|
|
||||||
&token,
|
|
||||||
file.text.pos_to_byte(&doc.position),
|
|
||||||
&doc.text_document.uri,
|
|
||||||
)?;
|
|
||||||
// info!("current completion token: {}", token);
|
|
||||||
|
|
||||||
completion_items.items.extend::<Vec<CompletionItem>>(
|
|
||||||
server.vlog_keyword_completion_items
|
|
||||||
.iter()
|
|
||||||
.filter(|x| x.label.starts_with(&token))
|
|
||||||
.cloned()
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
completion_items.items.dedup_by_key(|i| i.label.clone());
|
|
||||||
|
|
||||||
Some(completion_items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// eprintln!("comp response: {}", now.elapsed().as_millis());
|
|
||||||
Some(CompletionResponse::List(response?))
|
Some(CompletionResponse::List(response?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// get the previous non-whitespace character
|
|
||||||
fn prev_char(text: &Rope, pos: &Position) -> char {
|
|
||||||
let char_idx = text.pos_to_char(pos);
|
|
||||||
if char_idx > 0 {
|
|
||||||
for i in (0..char_idx).rev() {
|
|
||||||
let res = text.char(i);
|
|
||||||
if !res.is_whitespace() {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
' '
|
|
||||||
} else {
|
|
||||||
' '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// attempt to get the token the user was trying to complete, by
|
|
||||||
/// filtering out characters unneeded for name resolution
|
|
||||||
fn get_completion_token(text: &Rope, 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();
|
|
||||||
//TODO: make this a regex
|
|
||||||
while c.is_some()
|
|
||||||
&& (c.unwrap().is_alphanumeric()
|
|
||||||
|| c.unwrap() == '_'
|
|
||||||
|| c.unwrap() == '.'
|
|
||||||
|| c.unwrap() == '['
|
|
||||||
|| c.unwrap() == ']')
|
|
||||||
{
|
|
||||||
token.push(c.unwrap());
|
|
||||||
c = line_iter.prev();
|
|
||||||
}
|
|
||||||
let mut result: String = token.chars().rev().collect();
|
|
||||||
if result.contains('[') {
|
|
||||||
let l_bracket_offset = result.find('[').unwrap_or(result.len());
|
|
||||||
result.replace_range(l_bracket_offset.., "");
|
|
||||||
}
|
|
||||||
if &result == "." {
|
|
||||||
// probably a instantiation, the token should be what we're instatiating
|
|
||||||
let mut char_iter = text.chars();
|
|
||||||
let mut token = String::new();
|
|
||||||
for _ in 0..text.pos_to_char(&pos) {
|
|
||||||
char_iter.next();
|
|
||||||
}
|
|
||||||
let mut c = char_iter.prev();
|
|
||||||
|
|
||||||
// go to the last semicolon
|
|
||||||
while c.is_some() && (c.unwrap() != ';') {
|
|
||||||
c = char_iter.prev();
|
|
||||||
}
|
|
||||||
// go the the start of the next symbol
|
|
||||||
while c.is_some() && !(c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
|
|
||||||
c = char_iter.next();
|
|
||||||
}
|
|
||||||
// then extract the next symbol
|
|
||||||
while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
|
|
||||||
token.push(c.unwrap());
|
|
||||||
c = char_iter.next();
|
|
||||||
}
|
|
||||||
token
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_primitives_instantiation_code(text: &str) -> String {
|
fn make_primitives_instantiation_code(text: &str) -> String {
|
||||||
let mut instantiations = text.lines()
|
let mut instantiations = text.lines()
|
||||||
.filter(|line| !line.trim().is_empty() && !line.trim().starts_with("//"))
|
.filter(|line| !line.trim().is_empty() && !line.trim().starts_with("//"))
|
||||||
|
@ -311,9 +311,16 @@ pub struct DefineParam {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Define {
|
pub struct Define {
|
||||||
|
/// 宏的名字
|
||||||
|
/// <code>`define {name} {replacement}</code>
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// 宏的名字
|
||||||
|
/// <code>`define {name} {replacement}</code>
|
||||||
pub replacement: String,
|
pub replacement: String,
|
||||||
|
/// 宏的范围
|
||||||
pub range: Range,
|
pub range: Range,
|
||||||
|
/// 宏的参数(如果为函数宏才会有这个选项)
|
||||||
|
/// <code>`define {name} {replacement}({...params})</code>
|
||||||
pub params: Vec<DefineParam>
|
pub params: Vec<DefineParam>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +141,7 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
|
|||||||
completions
|
completions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// return a dot completion from the scope tree, this function should be called on the global
|
/// return a dot completion from the scope tree, this function should be called on the global
|
||||||
/// scope
|
/// scope
|
||||||
fn get_dot_completion(
|
fn get_dot_completion(
|
||||||
@ -176,6 +177,7 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// 根据输入的 token,计算出这个 token 在 scope 中的定义
|
/// 根据输入的 token,计算出这个 token 在 scope 中的定义
|
||||||
/// 比如输入 clock,则返回 clock 这个变量在哪里被定义,没有则返回 None
|
/// 比如输入 clock,则返回 clock 这个变量在哪里被定义,没有则返回 None
|
||||||
/// - `token`: 需要查找定义的完整的单词
|
/// - `token`: 需要查找定义的完整的单词
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::common::*;
|
use super::common::*;
|
||||||
use super::match_definitions;
|
use super::match_definitions;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
use sv_parser::*;
|
use sv_parser::*;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
@ -45,6 +46,8 @@ macro_rules! advance_until_leave {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 遍历 $tree,直到找到 $node
|
||||||
|
/// 中间遍历遇到的所有 node 的字面量会被存储进入 $tokens 中
|
||||||
macro_rules! advance_until_enter {
|
macro_rules! advance_until_enter {
|
||||||
($tokens:ident, $tree:ident, $event_iter:ident, $node:path, $type:ty) => {{
|
($tokens:ident, $tree:ident, $event_iter:ident, $node:path, $type:ty) => {{
|
||||||
let mut result: Option<$type> = None;
|
let mut result: Option<$type> = None;
|
||||||
@ -2352,6 +2355,8 @@ pub fn text_macro_def(
|
|||||||
&TextMacroIdentifier
|
&TextMacroIdentifier
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 最终渲染的基本字面量
|
||||||
|
text_macro.type_str = "`define".to_string();
|
||||||
// 自动补全用的
|
// 自动补全用的
|
||||||
text_macro.completion_kind = CompletionItemKind::CONSTANT;
|
text_macro.completion_kind = CompletionItemKind::CONSTANT;
|
||||||
// document 用的
|
// document 用的
|
||||||
|
@ -458,7 +458,7 @@ impl Sources {
|
|||||||
*self.names.read().unwrap().get(uri).unwrap()
|
*self.names.read().unwrap().get(uri).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// compute identifier completions
|
/// 根据输入 token,计算当前
|
||||||
pub fn get_completions(
|
pub fn get_completions(
|
||||||
&self,
|
&self,
|
||||||
token: &str,
|
token: &str,
|
||||||
|
@ -449,7 +449,7 @@ mod test_scope_tree {
|
|||||||
mod test_file {
|
mod test_file {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use crate::utils::{file_size_in_kb, get_language_id_by_pathbuf, RecursiveFileIterator};
|
use crate::utils::{file_size_in_kb, get_language_id_by_pathbuf, is_character_ordered_match, RecursiveFileIterator};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -476,4 +476,14 @@ mod test_file {
|
|||||||
fn test_cache() {
|
fn test_cache() {
|
||||||
let _ = fs::create_dir_all("/home/dide/project/digital-lsp-server/.cache");
|
let _ = fs::create_dir_all("/home/dide/project/digital-lsp-server/.cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utils() {
|
||||||
|
println!("enter function");
|
||||||
|
assert!(is_character_ordered_match("parm", "param"));
|
||||||
|
assert!(is_character_ordered_match("param", "param"));
|
||||||
|
assert!(is_character_ordered_match("prm", "PARAM"));
|
||||||
|
assert!(is_character_ordered_match("car", "careful"));
|
||||||
|
assert!(!is_character_ordered_match("suprt", "super"));
|
||||||
|
}
|
||||||
}
|
}
|
@ -83,6 +83,7 @@ pub fn get_word_range_at_position(line: &RopeSlice, pos: Position, regex: Regex)
|
|||||||
/// 根据 uri 获取 hdl 的 language id
|
/// 根据 uri 获取 hdl 的 language id
|
||||||
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
||||||
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
||||||
|
#[allow(unused)]
|
||||||
pub fn get_language_id_by_uri(uri: &Url) -> String {
|
pub fn get_language_id_by_uri(uri: &Url) -> String {
|
||||||
let path = uri.path();
|
let path = uri.path();
|
||||||
let ext_name = std::path::Path::new(path)
|
let ext_name = std::path::Path::new(path)
|
||||||
@ -96,6 +97,7 @@ pub fn get_language_id_by_uri(uri: &Url) -> String {
|
|||||||
/// 根据路径字符串获取 hdl 的 language id
|
/// 根据路径字符串获取 hdl 的 language id
|
||||||
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
||||||
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
||||||
|
#[allow(unused)]
|
||||||
pub fn get_language_id_by_pathbuf(pathbuf: &PathBuf) -> String {
|
pub fn get_language_id_by_pathbuf(pathbuf: &PathBuf) -> String {
|
||||||
let ext_name = pathbuf.as_path()
|
let ext_name = pathbuf.as_path()
|
||||||
.extension()
|
.extension()
|
||||||
@ -107,6 +109,7 @@ pub fn get_language_id_by_pathbuf(pathbuf: &PathBuf) -> String {
|
|||||||
/// 根据路径字符串获取 hdl 的 language id
|
/// 根据路径字符串获取 hdl 的 language id
|
||||||
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
/// 返回值为 "vhdl" | "verilog" | "systemverilog" | "plaintext"
|
||||||
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
/// 不采用枚举是因为需要在 lsp 中使用到它们的字符串值
|
||||||
|
#[allow(unused)]
|
||||||
pub fn get_language_id_by_path_str(path_str: &str) -> String {
|
pub fn get_language_id_by_path_str(path_str: &str) -> String {
|
||||||
let ext_name = std::path::Path::new(path_str)
|
let ext_name = std::path::Path::new(path_str)
|
||||||
.extension()
|
.extension()
|
||||||
@ -338,3 +341,33 @@ impl BracketMatcher {
|
|||||||
BracketMatchResult::Valid
|
BracketMatchResult::Valid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 基于字符顺序判断是否匹配
|
||||||
|
/// 检查是否可以从 `candidate` 中提取出与 `input` 顺序一致的字符
|
||||||
|
/// input 忽略大小写
|
||||||
|
pub fn is_character_ordered_match(
|
||||||
|
input: &str,
|
||||||
|
candidate: &str
|
||||||
|
) -> bool {
|
||||||
|
let mut input_chars = input.chars().peekable();
|
||||||
|
let mut candidate_chars = candidate.chars().peekable();
|
||||||
|
|
||||||
|
while let Some(input_char) = input_chars.next() {
|
||||||
|
// 在 candidate 中找到与 input_char 匹配的字符
|
||||||
|
let mut matched = false;
|
||||||
|
while let Some(candidate_char) = candidate_chars.next() {
|
||||||
|
if candidate_char.to_lowercase().eq(input_char.to_lowercase()) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未找到匹配的字符,返回 false
|
||||||
|
if !matched {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 input 的所有字符都匹配完成,返回 true
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user