swap from IPC architecture to PyO3 library (#45)

This commit is contained in:
Josh Thomas 2024-12-23 10:12:10 -06:00 committed by GitHub
parent df30aafde5
commit a73e912e0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 136 additions and 2224 deletions

113
crates/djls/src/lib.rs Normal file
View file

@ -0,0 +1,113 @@
mod commands;
use crate::commands::Serve;
use anyhow::Result;
use clap::{Parser, Subcommand};
use pyo3::prelude::*;
use std::env;
use std::process::ExitCode;
#[derive(Parser)]
#[command(name = "djls")]
#[command(version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
command: Command,
#[command(flatten)]
args: Args,
}
#[derive(Debug, Subcommand)]
enum Command {
/// Start the LSP server
Serve(Serve),
}
#[derive(Parser)]
pub struct Args {
#[command(flatten)]
global: GlobalArgs,
}
#[derive(Parser, Debug, Clone)]
struct GlobalArgs {
/// Do not print any output.
#[arg(global = true, long, short, conflicts_with = "verbose")]
pub quiet: bool,
/// Use verbose output.
#[arg(global = true, action = clap::ArgAction::Count, long, short, conflicts_with = "quiet")]
pub verbose: u8,
}
#[pyfunction]
fn cli_entrypoint(_py: Python) -> PyResult<()> {
// Skip python interpreter and script path, add command name
let args: Vec<String> = std::iter::once("djls".to_string())
.chain(env::args().skip(2))
.collect();
let runtime = tokio::runtime::Runtime::new().unwrap();
let local = tokio::task::LocalSet::new();
local.block_on(&runtime, async move {
tokio::select! {
// The main CLI program
result = cli_main(args) => {
match result {
Ok(code) => {
if code != ExitCode::SUCCESS {
std::process::exit(1);
}
Ok::<(), PyErr>(())
}
Err(e) => {
eprintln!("Error: {}", e);
if let Some(source) = e.source() {
eprintln!("Caused by: {}", source);
}
std::process::exit(1);
}
}
}
// Ctrl+C handling
_ = tokio::signal::ctrl_c() => {
println!("\nReceived Ctrl+C, shutting down...");
// Cleanup code here if needed
std::process::exit(130); // Standard Ctrl+C exit code
}
// SIGTERM handling (Unix only)
_ = async {
#[cfg(unix)]
{
use tokio::signal::unix::{signal, SignalKind};
let mut term = signal(SignalKind::terminate()).unwrap();
term.recv().await;
}
} => {
println!("\nReceived termination signal, shutting down...");
std::process::exit(143); // Standard SIGTERM exit code
}
}
})?;
Ok(())
}
async fn cli_main(args: Vec<String>) -> Result<ExitCode> {
let cli = Cli::try_parse_from(args).unwrap_or_else(|e| {
e.exit();
});
match cli.command {
Command::Serve(_serve) => djls_server::serve().await?,
}
Ok(ExitCode::SUCCESS)
}
#[pymodule]
fn djls(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(cli_entrypoint, m)?)?;
Ok(())
}

View file

@ -1,54 +0,0 @@
mod commands;
use crate::commands::Serve;
use anyhow::Result;
use clap::{Parser, Subcommand};
use djls_ipc::PythonProcess;
use std::ffi::OsStr;
use std::process::ExitCode;
#[derive(Parser)]
#[command(name = "djls")]
#[command(version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
command: Command,
#[command(flatten)]
args: Args,
}
#[derive(Debug, Subcommand)]
enum Command {
/// Start the LSP server
Serve(Serve),
}
#[derive(Parser)]
pub struct Args {
#[command(flatten)]
global: GlobalArgs,
}
#[derive(Parser, Debug, Clone)]
struct GlobalArgs {
/// Do not print any output.
#[arg(global = true, long, short, conflicts_with = "verbose")]
pub quiet: bool,
/// Use verbose output.
#[arg(global = true, action = clap::ArgAction::Count, long, short, conflicts_with = "quiet")]
pub verbose: u8,
}
#[tokio::main]
async fn main() -> Result<ExitCode> {
let cli = Cli::parse();
match cli.command {
Command::Serve(_serve) => {
let python = PythonProcess::new::<Vec<&OsStr>, &OsStr>("djls_agent", None, None)?;
djls_server::serve(python).await?
}
}
Ok(ExitCode::SUCCESS)
}