use crate::sources::*; use crate::completion::keyword::*; use flexi_logger::LoggerHandle; #[allow(unused)] use log::{debug, info, warn}; use once_cell::sync::OnceCell; 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 static GLOBAL_BACKEND: OnceCell> = OnceCell::new(); pub struct LSPServer { pub srcs: Sources, pub key_comps: Vec, pub sys_tasks: Vec, pub directives: Vec, pub conf: RwLock, #[allow(unused)] pub log_handle: Mutex>, } impl LSPServer { pub fn new(log_handle: Option) -> LSPServer { LSPServer { srcs: Sources::new(), key_comps: keyword_completions(KEYWORDS), sys_tasks: other_completions(SYS_TASKS), directives: other_completions(DIRECTIVES), conf: 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 { // 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, // list of directories to recursively search for SystemVerilog/Verilog sources pub source_dirs: Vec, // 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 { 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, } 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, } 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, } 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, _: InitializeParams) -> Result { self.server.srcs.init(); // 申明 LSP 的基本信息和提供的能力 let server_info = Some(ServerInfo { name: "Digital IDE 专用 LSP 后端服务器".to_string(), version: Some("0.4.0".to_string()) }); 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(), ]), work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, }, all_commit_characters: None, // TODO: 检查这里的选项 completion_item: None, }; 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)), document_symbol_provider: Some(OneOf::Left(true)), document_highlight_provider: Some(OneOf::Left(true)), ..ServerCapabilities::default() }; Ok(InitializeResult { server_info, capabilities }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "digital lsp initialized!") .await; // self.client.send_notification::(StringNotification { content: "hello from lsp server".to_string() }).await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { 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) { self.server.did_change(params); } async fn did_save(&self, params: DidSaveTextDocumentParams) { 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> { Ok(self.server.completion(params)) } async fn goto_definition( &self, params: GotoDefinitionParams, ) -> Result> { Ok(self.server.goto_definition(params)) } async fn hover(&self, params: HoverParams) -> Result> { Ok(self.server.hover(params)) } async fn document_symbol( &self, params: DocumentSymbolParams, ) -> Result> { Ok(self.server.document_symbol(params)) } async fn formatting(&self, params: DocumentFormattingParams) -> Result>> { Ok(self.server.formatting(params)) } async fn range_formatting( &self, params: DocumentRangeFormattingParams, ) -> Result>> { Ok(self.server.range_formatting(params)) } async fn document_highlight( &self, params: DocumentHighlightParams, ) -> Result>> { Ok(self.server.document_highlight(params)) } }