mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-07-24 12:53:52 +00:00
switch from runner to ipc and long-running sidecar process (#21)
This commit is contained in:
parent
4c10afb602
commit
235bb4419d
23 changed files with 556 additions and 281 deletions
|
@ -4,6 +4,7 @@ version = "0.0.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
djls-ipc = { workspace = true }
|
||||
djls-python = { workspace = true }
|
||||
|
||||
serde = { workspace = true }
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use djls_python::{Python, RunnerError, ScriptRunner};
|
||||
use djls_ipc::{parse_json_response, JsonResponse, PythonProcess, TransportError};
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
|
||||
use crate::scripts;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct App(String);
|
||||
|
||||
|
@ -27,8 +25,16 @@ struct InstalledAppsCheck {
|
|||
has_app: bool,
|
||||
}
|
||||
|
||||
impl ScriptRunner for InstalledAppsCheck {
|
||||
const SCRIPT: &'static str = scripts::INSTALLED_APPS_CHECK;
|
||||
impl TryFrom<JsonResponse> for InstalledAppsCheck {
|
||||
type Error = TransportError;
|
||||
|
||||
fn try_from(response: JsonResponse) -> Result<Self, Self::Error> {
|
||||
response
|
||||
.data()
|
||||
.clone()
|
||||
.ok_or_else(|| TransportError::Process("No data in response".to_string()))
|
||||
.and_then(|data| serde_json::from_value(data).map_err(TransportError::Json))
|
||||
}
|
||||
}
|
||||
|
||||
impl Apps {
|
||||
|
@ -48,8 +54,10 @@ impl Apps {
|
|||
self.apps().iter()
|
||||
}
|
||||
|
||||
pub fn check_installed(py: &Python, app: &str) -> Result<bool, RunnerError> {
|
||||
let result = InstalledAppsCheck::run_with_py_args(py, app)?;
|
||||
pub fn check_installed(python: &mut PythonProcess, app: &str) -> Result<bool, TransportError> {
|
||||
let response = python.send("installed_apps_check", Some(vec![app.to_string()]))?;
|
||||
let response = parse_json_response(response)?;
|
||||
let result = InstalledAppsCheck::try_from(response)?;
|
||||
Ok(result.has_app)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::apps::Apps;
|
||||
use crate::gis::{check_gis_setup, GISError};
|
||||
use crate::scripts;
|
||||
use crate::templates::TemplateTags;
|
||||
use djls_python::{ImportCheck, Python, RunnerError, ScriptRunner};
|
||||
use djls_ipc::{parse_json_response, JsonResponse, PythonProcess, TransportError};
|
||||
use djls_python::{ImportCheck, Python};
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DjangoProject {
|
||||
py: Python,
|
||||
python: PythonProcess,
|
||||
settings_module: String,
|
||||
installed_apps: Apps,
|
||||
templatetags: TemplateTags,
|
||||
|
@ -20,54 +21,67 @@ struct DjangoSetup {
|
|||
templatetags: TemplateTags,
|
||||
}
|
||||
|
||||
impl ScriptRunner for DjangoSetup {
|
||||
const SCRIPT: &'static str = scripts::DJANGO_SETUP;
|
||||
impl DjangoSetup {
|
||||
pub fn setup(python: &mut PythonProcess) -> Result<JsonResponse, ProjectError> {
|
||||
let response = python.send("django_setup", None)?;
|
||||
let response = parse_json_response(response)?;
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl DjangoProject {
|
||||
fn new(
|
||||
py: Python,
|
||||
python: PythonProcess,
|
||||
settings_module: String,
|
||||
installed_apps: Apps,
|
||||
templatetags: TemplateTags,
|
||||
) -> Self {
|
||||
Self {
|
||||
py,
|
||||
python,
|
||||
settings_module,
|
||||
installed_apps,
|
||||
templatetags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup() -> Result<Self, ProjectError> {
|
||||
pub fn setup(mut python: PythonProcess) -> Result<Self, ProjectError> {
|
||||
let settings_module =
|
||||
std::env::var("DJANGO_SETTINGS_MODULE").expect("DJANGO_SETTINGS_MODULE must be set");
|
||||
|
||||
let py = Python::initialize()?;
|
||||
let py = Python::setup(&mut python)?;
|
||||
|
||||
let has_django = ImportCheck::check(&py, "django")?;
|
||||
let has_django = ImportCheck::check(&mut python, Some(vec!["django".to_string()]))?;
|
||||
|
||||
if !has_django {
|
||||
return Err(ProjectError::DjangoNotFound);
|
||||
}
|
||||
|
||||
if !check_gis_setup(&py)? {
|
||||
if !check_gis_setup(&mut python)? {
|
||||
eprintln!("Warning: GeoDjango detected but GDAL is not available.");
|
||||
eprintln!("Django initialization will be skipped. Some features may be limited.");
|
||||
eprintln!("To enable full functionality, please install GDAL and other GeoDjango prerequisites.");
|
||||
|
||||
return Ok(Self {
|
||||
py,
|
||||
python,
|
||||
settings_module,
|
||||
installed_apps: Apps::default(),
|
||||
templatetags: TemplateTags::default(),
|
||||
});
|
||||
}
|
||||
|
||||
let setup = DjangoSetup::run_with_py(&py)?;
|
||||
let response = DjangoSetup::setup(&mut python)?;
|
||||
let setup: DjangoSetup = response
|
||||
.data()
|
||||
.clone()
|
||||
.ok_or_else(|| TransportError::Process("No data in response".to_string()))
|
||||
.and_then(|data| serde_json::from_value(data).map_err(TransportError::Json))?;
|
||||
|
||||
Ok(Self::new(
|
||||
py,
|
||||
python,
|
||||
settings_module,
|
||||
Apps::from_strings(setup.installed_apps.to_vec()),
|
||||
setup.templatetags,
|
||||
|
@ -110,8 +124,11 @@ pub enum ProjectError {
|
|||
Json(#[from] serde_json::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Python(#[from] djls_python::PythonError),
|
||||
Packaging(#[from] djls_python::PackagingError),
|
||||
|
||||
#[error(transparent)]
|
||||
Runner(#[from] RunnerError),
|
||||
Python(#[from] djls_python::PythonError),
|
||||
|
||||
#[error("Transport error: {0}")]
|
||||
Transport(#[from] TransportError),
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::apps::Apps;
|
||||
use djls_python::{Python, RunnerError};
|
||||
use djls_ipc::{PythonProcess, TransportError};
|
||||
use std::process::Command;
|
||||
|
||||
pub fn check_gis_setup(py: &Python) -> Result<bool, GISError> {
|
||||
let has_geodjango = Apps::check_installed(py, "django.contrib.gis")?;
|
||||
pub fn check_gis_setup(python: &mut PythonProcess) -> Result<bool, GISError> {
|
||||
let has_geodjango = Apps::check_installed(python, "django.contrib.gis")?;
|
||||
let gdal_is_installed = Command::new("gdalinfo")
|
||||
.arg("--version")
|
||||
.output()
|
||||
|
@ -21,6 +21,6 @@ pub enum GISError {
|
|||
#[error("JSON parsing error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Runner(#[from] RunnerError),
|
||||
#[error("Transport error: {0}")]
|
||||
Transport(#[from] TransportError),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
mod apps;
|
||||
mod django;
|
||||
mod gis;
|
||||
mod scripts;
|
||||
mod templates;
|
||||
|
||||
pub use django::DjangoProject;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
use djls_python::include_script;
|
||||
|
||||
pub const DJANGO_SETUP: &str = include_script!("django_setup");
|
||||
pub const INSTALLED_APPS_CHECK: &str = include_script!("installed_apps_check");
|
Loading…
Add table
Add a link
Reference in a new issue