380 lines
12 KiB
Rust
380 lines
12 KiB
Rust
use crate::core::cache_storage::CacheManager;
|
|
use crate::sources::*;
|
|
use crate::completion::keyword::*;
|
|
use flexi_logger::LoggerHandle;
|
|
#[allow(unused)]
|
|
use log::{debug, info, warn};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::string::ToString;
|
|
use std::sync::{Arc, Mutex, RwLock};
|
|
use tower_lsp::jsonrpc::Result;
|
|
use tower_lsp::lsp_types::*;
|
|
use tower_lsp::{Client, LanguageServer};
|
|
pub struct LSPServer {
|
|
pub srcs: Sources,
|
|
pub cache: CacheManager,
|
|
pub vlog_keyword_completion_items: Vec<CompletionItem>,
|
|
pub vhdl_keyword_completiom_items: Vec<CompletionItem>,
|
|
pub sys_tasks: Vec<CompletionItem>,
|
|
pub directives: Vec<CompletionItem>,
|
|
pub conf: Arc<RwLock<ProjectConfig>>,
|
|
#[allow(unused)]
|
|
pub log_handle: Mutex<Option<LoggerHandle>>,
|
|
}
|
|
|
|
impl LSPServer {
|
|
pub fn new(log_handle: Option<LoggerHandle>) -> LSPServer {
|
|
let user_home = dirs_next::home_dir().unwrap();
|
|
let dide_home = user_home.join(".digital-ide");
|
|
LSPServer {
|
|
srcs: Sources::new(),
|
|
cache: CacheManager::new(dide_home),
|
|
vlog_keyword_completion_items: keyword_completions(VLOG_KEYWORDS),
|
|
vhdl_keyword_completiom_items: keyword_completions(VHDL_KEYWORDS),
|
|
sys_tasks: other_completions(SYS_TASKS),
|
|
directives: other_completions(DIRECTIVES),
|
|
conf: Arc::new(RwLock::new(ProjectConfig::default())),
|
|
log_handle: Mutex::new(log_handle),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Backend {
|
|
pub client: Client,
|
|
pub server: LSPServer
|
|
}
|
|
|
|
|
|
impl Backend {
|
|
pub fn new(client: Client, log_handle: LoggerHandle) -> Backend {
|
|
Backend {
|
|
client,
|
|
server: LSPServer::new(Some(log_handle)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(strum_macros::Display, Debug, Serialize, Deserialize)]
|
|
pub enum LogLevel {
|
|
#[strum(serialize = "error")]
|
|
Error,
|
|
#[strum(serialize = "warn")]
|
|
Warn,
|
|
#[strum(serialize = "info")]
|
|
Info,
|
|
#[strum(serialize = "debug")]
|
|
Debug,
|
|
#[strum(serialize = "trace")]
|
|
Trace,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct ProjectConfig {
|
|
// 用户工作目录的路径
|
|
pub workspace_folder: Option<Url>,
|
|
// 插件安装的根路径
|
|
pub extension_path: String,
|
|
// 当前工具链
|
|
pub tool_chain: String,
|
|
// if true, recursively search the working directory for files to run diagnostics on
|
|
pub auto_search_workdir: bool,
|
|
// list of directories with header files
|
|
pub include_dirs: Vec<String>,
|
|
// list of directories to recursively search for SystemVerilog/Verilog sources
|
|
pub source_dirs: Vec<String>,
|
|
// config options for verible tools
|
|
pub verible: Verible,
|
|
// config options for verilator tools
|
|
pub verilator: Verilator,
|
|
// log level
|
|
pub log_level: LogLevel
|
|
}
|
|
|
|
impl Default for ProjectConfig {
|
|
fn default() -> Self {
|
|
ProjectConfig {
|
|
workspace_folder: None,
|
|
extension_path: "".to_string(),
|
|
tool_chain: "xilinx".to_string(),
|
|
auto_search_workdir: true,
|
|
include_dirs: Vec::new(),
|
|
source_dirs: Vec::new(),
|
|
verible: Verible::default(),
|
|
verilator: Verilator::default(),
|
|
log_level: LogLevel::Info,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Debug, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct Verible {
|
|
pub syntax: VeribleSyntax,
|
|
pub format: VeribleFormat,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct VeribleSyntax {
|
|
pub enabled: bool,
|
|
pub path: String,
|
|
pub args: Vec<String>,
|
|
}
|
|
|
|
impl Default for VeribleSyntax {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: true,
|
|
path: "verible-verilog-syntax".to_string(),
|
|
args: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct Verilator {
|
|
pub syntax: VerilatorSyntax,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct VerilatorSyntax {
|
|
pub enabled: bool,
|
|
pub path: String,
|
|
pub args: Vec<String>,
|
|
}
|
|
|
|
impl Default for VerilatorSyntax {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: true,
|
|
path: "verilator".to_string(),
|
|
args: vec![
|
|
"--lint-only".to_string(),
|
|
"--sv".to_string(),
|
|
"-Wall".to_string(),
|
|
],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct VeribleFormat {
|
|
pub enabled: bool,
|
|
pub path: String,
|
|
pub args: Vec<String>,
|
|
}
|
|
|
|
impl Default for VeribleFormat {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: true,
|
|
path: "verible-verilog-format".to_string(),
|
|
args: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[tower_lsp::async_trait]
|
|
impl LanguageServer for Backend {
|
|
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
|
|
// 申明 LSP 的基本信息和提供的能力
|
|
let server_info = Some(ServerInfo {
|
|
name: "Digital IDE 专用 LSP 后端服务器".to_string(),
|
|
version: Some("0.4.0".to_string())
|
|
});
|
|
|
|
let root_uri = ¶ms.root_uri;
|
|
|
|
let mut configure = self.server.conf.write().unwrap();
|
|
configure.workspace_folder = root_uri.clone();
|
|
|
|
if let Some(serde_json::Value::Object(options)) = params.initialization_options {
|
|
let extension_path = options.get("extensionPath").unwrap().as_str().unwrap();
|
|
let tool_chain = options.get("toolChain").unwrap().as_str().unwrap();
|
|
configure.tool_chain = tool_chain.to_string();
|
|
configure.extension_path = extension_path.to_string();
|
|
}
|
|
|
|
info!("当前客户端初始化结果");
|
|
info!("workspaceFolder: {:?}", configure.workspace_folder);
|
|
info!("extensionPath: {:?}", configure.extension_path);
|
|
info!("toolChain: {:?}", configure.tool_chain);
|
|
|
|
self.server.srcs.init_primitive(
|
|
&configure.tool_chain,
|
|
&configure.extension_path
|
|
);
|
|
|
|
let text_document_sync = TextDocumentSyncCapability::Options(
|
|
TextDocumentSyncOptions {
|
|
open_close: Some(true),
|
|
change: Some(TextDocumentSyncKind::INCREMENTAL),
|
|
will_save: None,
|
|
will_save_wait_until: None,
|
|
save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
|
|
include_text: None,
|
|
})),
|
|
}
|
|
);
|
|
|
|
let completion_provider = CompletionOptions {
|
|
resolve_provider: Some(false),
|
|
trigger_characters: Some(vec![
|
|
".".to_string(),
|
|
"$".to_string(),
|
|
"`".to_string(),
|
|
"\"".to_string(),
|
|
"/".to_string()
|
|
]),
|
|
work_done_progress_options: WorkDoneProgressOptions {
|
|
work_done_progress: None,
|
|
},
|
|
all_commit_characters: None,
|
|
// TODO: 检查这里的选项
|
|
completion_item: None,
|
|
};
|
|
|
|
let workspace = WorkspaceServerCapabilities {
|
|
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
|
|
supported: Some(true),
|
|
change_notifications: Some(OneOf::Left(true)),
|
|
}),
|
|
..Default::default()
|
|
};
|
|
|
|
let capabilities = ServerCapabilities {
|
|
text_document_sync: Some(text_document_sync),
|
|
completion_provider: Some(completion_provider),
|
|
definition_provider: Some(OneOf::Left(true)),
|
|
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
|
inlay_hint_provider: Some(OneOf::Left(true)),
|
|
document_symbol_provider: Some(OneOf::Left(true)),
|
|
document_highlight_provider: Some(OneOf::Left(true)),
|
|
workspace: Some(workspace),
|
|
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
|
|
..ServerCapabilities::default()
|
|
};
|
|
|
|
Ok(InitializeResult {
|
|
server_info,
|
|
capabilities,
|
|
offset_encoding: None
|
|
})
|
|
}
|
|
|
|
async fn initialized(&self, _: InitializedParams) {
|
|
self.client
|
|
.log_message(MessageType::INFO, "Digital LSP initialized!")
|
|
.await;
|
|
}
|
|
|
|
async fn shutdown(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
async fn did_open(&self, params: DidOpenTextDocumentParams) {
|
|
// // 如果文件太大则显示错误
|
|
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
|
// self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
|
// .await;
|
|
// } else {
|
|
let diagnostics = self.server.did_open(params);
|
|
self.client
|
|
.publish_diagnostics(
|
|
diagnostics.uri,
|
|
diagnostics.diagnostics,
|
|
diagnostics.version,
|
|
)
|
|
.await;
|
|
// }
|
|
}
|
|
|
|
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
|
// // 如果文件太大则显示错误
|
|
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
|
// // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
|
// // .await;
|
|
// } else {
|
|
self.server.did_change(params);
|
|
// }
|
|
}
|
|
|
|
async fn did_delete_files(&self, params: DeleteFilesParams) {
|
|
self.server.did_delete_files(params);
|
|
}
|
|
|
|
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
|
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
|
|
|
// } else {
|
|
let diagnostics = self.server.did_save(params);
|
|
self.client
|
|
.publish_diagnostics(
|
|
diagnostics.uri,
|
|
diagnostics.diagnostics,
|
|
diagnostics.version,
|
|
)
|
|
.await;
|
|
// }
|
|
}
|
|
|
|
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
|
|
Ok(self.server.completion(params))
|
|
}
|
|
|
|
async fn goto_definition(
|
|
&self,
|
|
params: GotoDefinitionParams,
|
|
) -> Result<Option<GotoDefinitionResponse>> {
|
|
Ok(self.server.goto_definition(params))
|
|
}
|
|
|
|
async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
|
|
Ok(self.server.hover(params))
|
|
}
|
|
|
|
async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
|
|
Ok(self.server.formatting(params))
|
|
}
|
|
|
|
async fn range_formatting(
|
|
&self,
|
|
params: DocumentRangeFormattingParams,
|
|
) -> Result<Option<Vec<TextEdit>>> {
|
|
Ok(self.server.range_formatting(params))
|
|
}
|
|
|
|
async fn document_symbol(
|
|
&self,
|
|
params: DocumentSymbolParams,
|
|
) -> Result<Option<DocumentSymbolResponse>> {
|
|
Ok(self.server.document_symbol(params))
|
|
}
|
|
|
|
async fn document_highlight(
|
|
&self,
|
|
params: DocumentHighlightParams,
|
|
) -> Result<Option<Vec<DocumentHighlight>>> {
|
|
Ok(self.server.document_highlight(params))
|
|
}
|
|
|
|
async fn inlay_hint(
|
|
&self,
|
|
params: InlayHintParams
|
|
) -> Result<Option<Vec<InlayHint>>> {
|
|
Ok(self.server.inlay_hint(params))
|
|
}
|
|
|
|
async fn code_lens(
|
|
&self,
|
|
params: CodeLensParams
|
|
) -> Result<Option<Vec<CodeLens>>> {
|
|
Ok(self.server.code_lens(params))
|
|
}
|
|
}
|