1#![warn(missing_docs)]
2#![cfg_attr(not(test), warn(clippy::unwrap_used))]
3
4use std::num::NonZeroUsize;
11
12pub use edit::{DocumentKey, PositionEncoding, TextDocument};
13pub use lint::generate_diagnostics;
14use lsp_types::CodeActionKind;
15pub use server::Server;
16pub use session::{Client, ClientOptions, DocumentQuery, DocumentSnapshot, GlobalOptions, Session};
17pub use workspace::{Workspace, Workspaces};
18
19mod analysis;
20mod call_hierarchy;
21mod edit;
22mod editor;
23mod editor_features;
24mod fix;
25mod format;
26#[cfg(feature = "fuzzing")]
27#[doc(hidden)]
28pub mod fuzzing;
29mod lint;
30mod logging;
31mod resolve;
32mod server;
33mod session;
34mod symbols;
35mod workspace;
36mod workspace_functions;
37
38pub(crate) const SERVER_NAME: &str = "shuck";
39pub(crate) const DIAGNOSTIC_NAME: &str = "shuck";
40
41pub(crate) const SOURCE_FIX_ALL_SHUCK: CodeActionKind = CodeActionKind::new("source.fixAll.shuck");
42
43pub(crate) type Result<T> = anyhow::Result<T>;
44
45pub(crate) fn version() -> &'static str {
46 env!("CARGO_PKG_VERSION")
47}
48
49pub fn run() -> Result<()> {
51 let four = NonZeroUsize::try_from(4usize)
52 .map_err(|_| anyhow::anyhow!("failed to create non-zero worker count"))?;
53 let worker_threads = std::thread::available_parallelism()
54 .unwrap_or(four)
55 .min(four);
56
57 let (connection, io_threads) = server::ConnectionInitializer::stdio();
58 let server_result = match start_server(worker_threads, connection)? {
59 Some(server) => server.run(),
60 None => Ok(()),
61 };
62
63 let io_result = io_threads.join();
64 match (server_result, io_result) {
65 (Ok(()), Ok(())) => Ok(()),
66 (Err(server), Ok(())) => Err(server),
67 (Ok(()), Err(io)) => Err(anyhow::Error::new(io).context("IO thread error")),
68 (Err(server), Err(io)) => Err(server.context(format!("IO thread error: {io}"))),
69 }
70}
71
72#[doc(hidden)]
73pub fn run_connection(connection: lsp_server::Connection) -> Result<()> {
74 let four = NonZeroUsize::try_from(4usize)
75 .map_err(|_| anyhow::anyhow!("failed to create non-zero worker count"))?;
76 let worker_threads = std::thread::available_parallelism()
77 .unwrap_or(four)
78 .min(four);
79 match start_server(
80 worker_threads,
81 server::ConnectionInitializer::from_connection(connection),
82 )? {
83 Some(server) => server.run(),
84 None => Ok(()),
85 }
86}
87
88fn start_server(
89 worker_threads: NonZeroUsize,
90 connection: server::ConnectionInitializer,
91) -> Result<Option<Server>> {
92 match Server::new(worker_threads, connection) {
93 Ok(server) => Ok(Some(server)),
94 Err(error) if is_disconnected(&error) => Ok(None),
95 Err(error) => Err(error.context("Failed to start server")),
96 }
97}
98
99fn is_disconnected(error: &anyhow::Error) -> bool {
100 error
101 .downcast_ref::<lsp_server::ProtocolError>()
102 .is_some_and(lsp_server::ProtocolError::channel_is_disconnected)
103}