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 { 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 : {:?}", 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::::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 { // 判断点模式,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 { // 判断在不在一个模块内,并获取这个模块 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::::new(); for param in inst_module.params { let label_details = CompletionItemLabelDetails { detail: Some("parameter".to_string()), ..CompletionItemLabelDetails::default() }; let param_desc = make_param_desc(¶m); 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::::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::::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::::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 }