Last active
February 2, 2024 23:32
-
-
Save sigmaSd/8147a22054675104c70044964152381b to your computer and use it in GitHub Desktop.
spell check lsp server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [dependencies] | |
// serde_json = "1.0.96" | |
// lsp-types = "=0.94" | |
// lsp-server = "0.7.1" | |
use std::error::Error; | |
use std::io::{Read, Write}; | |
use std::process::Stdio; | |
use lsp_types::{ | |
Diagnostic, DiagnosticSeverity, DidChangeTextDocumentParams, Position, | |
PublishDiagnosticsParams, Range, TextDocumentSyncCapability, TextDocumentSyncKind, | |
}; | |
use lsp_types::{InitializeParams, ServerCapabilities}; | |
use lsp_server::{Connection, Message, Notification}; | |
fn main() -> Result<(), Box<dyn Error + Sync + Send>> { | |
eprintln!("starting generic LSP server"); | |
let (connection, io_threads) = Connection::stdio(); | |
let server_capabilities = serde_json::to_value(ServerCapabilities { | |
text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)), | |
..Default::default() | |
}) | |
.unwrap(); | |
let initialization_params = connection.initialize(server_capabilities)?; | |
main_loop(connection, initialization_params)?; | |
io_threads.join()?; | |
eprintln!("shutting down server"); | |
Ok(()) | |
} | |
fn main_loop( | |
connection: Connection, | |
params: serde_json::Value, | |
) -> Result<(), Box<dyn Error + Sync + Send>> { | |
let _params: InitializeParams = serde_json::from_value(params).unwrap(); | |
eprintln!("starting example main loop"); | |
for msg in &connection.receiver { | |
match msg { | |
Message::Request(req) => { | |
eprintln!("got request: {req:?}"); | |
if connection.handle_shutdown(&req)? { | |
return Ok(()); | |
} | |
} | |
Message::Response(resp) => { | |
eprintln!("got response: {resp:?}"); | |
} | |
Message::Notification(not) => { | |
eprintln!("got notification: {not:?}"); | |
if not.method.as_str() == "textDocument/didChange" { | |
let params: DidChangeTextDocumentParams = serde_json::from_value(not.params)?; | |
for change in params.content_changes { | |
let text = change.text; | |
let mut aspell = std::process::Command::new("aspell") | |
.arg("-a") | |
.stdin(Stdio::piped()) | |
.stdout(Stdio::piped()) | |
.stderr(Stdio::piped()) | |
.spawn() | |
.unwrap(); | |
aspell | |
.stdin | |
.as_mut() | |
.unwrap() | |
.write_all(&text.clone().into_bytes()) | |
.unwrap(); | |
drop(aspell.stdin.take().unwrap()); | |
let mut result = String::new(); | |
aspell | |
.stdout | |
.as_mut() | |
.unwrap() | |
.read_to_string(&mut result) | |
.unwrap(); | |
let mut diagnostics = vec![]; | |
for line in result.lines() { | |
if line.starts_with('&') { | |
let word = line.split_whitespace().nth(1).unwrap(); | |
for (line_index, line) in text.lines().enumerate() { | |
if let Some(column_index) = line.find(word) { | |
let diagnostic = Diagnostic { | |
range: Range { | |
start: Position { | |
line: line_index as _, | |
character: column_index as _, | |
}, | |
end: Position { | |
line: line_index as _, | |
character: (column_index + word.len()) as _, | |
}, | |
}, | |
severity: Some(DiagnosticSeverity::WARNING), | |
// TODO: add suggestions here or in code action | |
message: "misspell".to_owned(), | |
..Default::default() | |
}; | |
diagnostics.push(diagnostic); | |
} | |
} | |
} | |
} | |
let publish_diagnostics = Notification::new( | |
"textDocument/publishDiagnostics".to_owned(), | |
PublishDiagnosticsParams { | |
uri: params.text_document.uri.clone(), | |
diagnostics, | |
version: None, | |
}, | |
); | |
connection | |
.sender | |
.send(Message::Notification(publish_diagnostics))?; | |
} | |
} | |
} | |
} | |
} | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screencast.from.2023-07-04.22-03-17.webm