266 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::{fs, path::PathBuf, str::FromStr};
use log::info;
use ropey::RopeSlice;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionList, Position, Url};
use crate::{server::LSPServer, utils::{resolve_path, to_escape_path}};
pub fn include_path_completion(uri: &Url, line: &RopeSlice, pos: Position) -> Option<CompletionList> {
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) {
// 遍历该路径下所有文件和文件夹,并构建新的自动补全列表
info!("completion active in {:?}", abs_path);
if abs_path.is_dir() {
let mut completion_items = Vec::<CompletionItem>::new();
for entry in fs::read_dir(abs_path).unwrap() {
let entry = entry.unwrap();
let path: PathBuf = entry.path();
let file_name = match path.file_name() {
Some(os_str) => os_str.to_str(),
None => continue
};
if file_name.is_none() {
continue;
}
let file_name = file_name.unwrap();
if path.is_dir() {
completion_items.push(CompletionItem {
label: file_name.to_string(),
kind: Some(CompletionItemKind::FOLDER),
..CompletionItem::default()
});
}
if path.is_file() {
completion_items.push(CompletionItem {
label: file_name.to_string(),
kind: Some(CompletionItemKind::FILE),
..CompletionItem::default()
});
}
}
if !completion_items.is_empty() {
return Some(CompletionList {
is_incomplete: false,
items: completion_items
});
}
}
}
}
}
None
}
pub fn get_dot_completion(
server: &LSPServer,
line: &RopeSlice,
url: &Url,
pos: &Position,
language_id: &str
) -> Option<CompletionList> {
// 判断点模式v 中,点的模式一共两种
// 一种是 port & param position 赋值
// 一种是结构体中的属性访问
info!("current line: {:?}, pos: {:?}", line, pos);
if is_port_completion(line, pos) {
return get_position_port_param_completion(server, line, url, pos, language_id);
}
// TODO: 加入结构体的补全
None
}
fn is_port_completion(line: &RopeSlice, pos: &Position) -> bool {
let character = pos.character as usize;
if character == 0 {
match line.get_char(character) {
Some(char) => {
let char_string = char.to_string();
return char_string == ".";
},
None => return false
}
} else {
let pre_char = line.get_char(character - 1);
let cur_char = line.get_char(character);
match pre_char {
Some(pre_char) => {
let pre_char = pre_char.to_string();
if pre_char == "." {
return true;
}
match cur_char {
Some(cur_char) => {
let cur_char = cur_char.to_string();
return cur_char == ".";
},
None => {}
}
},
None => {}
}
}
false
}
fn get_position_port_param_completion(
server: &LSPServer,
#[allow(unused)]
line: &RopeSlice,
url: &Url,
pos: &Position,
#[allow(unused)]
language_id: &str
) -> Option<CompletionList> {
// 判断在不在一个模块内,并获取这个模块
let hdl_param = &server.srcs.hdl_param;
let fast_map = hdl_param.path_to_hdl_file.read().unwrap();
let path = PathBuf::from_str(url.path()).unwrap();
let path = to_escape_path(&path);
let path_string = path.to_str().unwrap();
info!("enter get_position_port_param_completion, pos: {pos:?}");
if let Some(hdl_file) = fast_map.get(path_string) {
info!("find hdl_file, content: {:?}", hdl_file.fast.content);
// 在当前文件的 fast 中寻找
for module in &hdl_file.fast.content {
for instance in &module.instances {
if let Some(param_range) = &instance.instparams {
let mut param_range = param_range.clone();
param_range.affine(-1, -1);
if param_range.contains(pos) {
// 补全当前 module 的所有 param
let inst_module = hdl_param.find_module_by_name(&instance.inst_type);
if inst_module.is_some() {
let inst_module = inst_module.unwrap();
let mut completion_items = Vec::<CompletionItem>::new();
for param in inst_module.params {
let label_details = CompletionItemLabelDetails {
detail: Some("parameter".to_string()),
..CompletionItemLabelDetails::default()
};
let param_desc = make_param_desc(&param);
let c_item = CompletionItem {
label: param.name,
detail: Some(param_desc),
label_details: Some(label_details),
kind: Some(CompletionItemKind::TYPE_PARAMETER),
..CompletionItem::default()
};
completion_items.push(c_item);
}
return Some(CompletionList {
is_incomplete: false,
items: completion_items
});
}
}
}
if instance.instports.is_some() {
let port_range = instance.gen_dot_completion_port_range();
if port_range.contains(pos) {
let inst_module = hdl_param.find_module_by_name(&instance.inst_type);
if inst_module.is_some() {
let inst_module = inst_module.unwrap();
let mut completion_items = Vec::<CompletionItem>::new();
for port in inst_module.ports {
let label_details = CompletionItemLabelDetails {
detail: Some("port".to_string()),
..CompletionItemLabelDetails::default()
};
let param_desc = make_port_desc(&port);
let c_item = CompletionItem {
label: port.name,
detail: Some(param_desc),
label_details: Some(label_details),
kind: Some(CompletionItemKind::PROPERTY),
..CompletionItem::default()
};
completion_items.push(c_item);
}
return Some(CompletionList {
is_incomplete: false,
items: completion_items
});
}
}
}
}
}
}
None
}
fn make_port_desc(port: &crate::core::hdlparam::Port) -> String {
let mut port_desc_array = Vec::<String>::new();
port_desc_array.push(port.dir_type.to_string());
if port.net_type != "unknown" {
port_desc_array.push(port.net_type.to_string());
}
if port.signed != "unsigned" {
port_desc_array.push("signed".to_string());
}
if port.width != "1" {
port_desc_array.push(port.width.to_string());
}
port_desc_array.push(port.name.to_string());
let port_desc = port_desc_array.join(" ");
port_desc
}
fn make_param_desc(param: &crate::core::hdlparam::Parameter) -> String {
let mut param_desc_array = Vec::<String>::new();
param_desc_array.push(format!("parameter {}", param.name));
if param.init != "unknown" {
param_desc_array.push("=".to_string());
param_desc_array.push(param.init.to_string());
}
let param_desc = param_desc_array.join(" ");
param_desc
}