loalang 0.1.15

Loa is a general-purpose, purely immutable, object-oriented programming language.
Documentation
mod server_error;
pub use self::server_error::*;

pub mod convert;

mod request_handler;
pub use self::request_handler::*;

mod notification_handler;
pub use self::notification_handler::*;

mod handlers;
pub use self::handlers::*;

mod server_context;
pub use self::server_context::*;

mod server_handler;
pub use self::server_handler::*;

mod cancellable;
pub use self::cancellable::*;

pub use loa::*;
pub use lsp_server::*;
pub use lsp_types::*;
pub use serde_json::Value;

pub fn server() {
    let (conn, _threads) = Connection::stdio();
    let conn = Arc::new(conn);

    let sender = Arc::new(NotificationSender { conn: conn.clone() });
    let mut context = ServerContext::new(sender);
    context.server.load_std().expect("failed to load stdlib");
    let initialize_params = init(&conn, &ServerHandler::capabilities()).unwrap();

    conn.sender
        .send(Message::Request(Request::new(
            RequestId::from(loa::Id::new().as_usize() as u64),
            "client/registerCapability".into(),
            RegistrationParams {
                registrations: vec![Registration {
                    id: "workspace-files".into(),
                    method: "workspace/didChangeWatchedFiles".into(),
                    register_options: Some(
                        serde_json::to_value(DidChangeWatchedFilesRegistrationOptions {
                            watchers: vec![FileSystemWatcher {
                                kind: None,
                                glob_pattern: "**/*.loa".into(),
                            }],
                        })
                        .unwrap(),
                    ),
                }],
            },
        )))
        .unwrap();

    if let Some(mut root_path) = initialize_params.root_path.map(std::path::PathBuf::from) {
        root_path.push("**");
        root_path.push("*.loa");

        match root_path.to_str().map(glob::glob) {
            Some(Ok(sources)) => {
                for source in sources.filter_map(|r| r.ok()) {
                    if let Ok(code) = std::fs::read_to_string(&source) {
                        if let Ok(uri) = lsp_types::Url::from_file_path(source)
                            .as_ref()
                            .map(convert::from_lsp::url_to_uri)
                        {
                            context.server.set(uri, code, loa::SourceKind::Module);
                        }
                    }
                }
            }
            _ => (),
        }
    }

    let mut handler = ServerHandler::new(context);

    loop {
        match next(&mut handler, &conn) {
            Err(_) => break,
            Ok(()) => (),
        }
    }
}

pub struct NotificationSender {
    conn: Arc<Connection>,
}

impl NotificationSender {
    fn send(&self, method: &str, params: Value) {
        let _ = self.conn.sender.send(Message::Notification(Notification {
            method: method.into(),
            params,
        }));
    }
}

fn init(
    conn: &Connection,
    capabilities: &ServerCapabilities,
) -> Result<InitializeParams, Box<dyn Error>> {
    Ok(serde_json::from_value::<InitializeParams>(
        conn.initialize(serde_json::to_value(capabilities)?)?,
    )?)
}

fn next(handler: &mut ServerHandler, conn: &Connection) -> Result<(), Box<dyn Error>> {
    let message = conn.receiver.recv()?;

    match message {
        Message::Notification(Notification { method, params }) => {
            match handler.handle(method.as_ref(), params) {
                _ => (),
            }
        }

        Message::Request(Request { method, params, id }) => {
            let mut error = None;
            let mut result = None;

            match handler.handle(method.as_ref(), params) {
                Ok(r) => result = Some(r),
                Err(err) => match err {
                    ServerError::Empty => result = Some(serde_json::Value::Null),
                    err => {
                        error = Some(ResponseError {
                            code: err.code(),
                            message: err.message(),
                            data: None,
                        })
                    }
                },
            }

            conn.sender
                .send(Message::Response(Response { id, error, result }))?;
        }

        _ => (),
    }

    Ok(())
}