swap in tower-lsp-server dependency (#100)

This commit is contained in:
Josh Thomas 2025-04-21 13:52:28 -05:00 committed by GitHub
parent 33fb726bdc
commit e098272ea6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 72 additions and 51 deletions

View file

@ -17,8 +17,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "2.0"
tokio = { version = "1.42", features = ["full"] }
tower-lsp = { version = "0.20", features = ["proposed"] }
lsp-types = "0.97"
tower-lsp-server = { version = "0.21", features = ["proposed"] }
[profile.dev.package]
insta.opt-level = 3

View file

@ -5,6 +5,6 @@ edition = "2021"
[dependencies]
pyo3 = { workspace = true }
tower-lsp = { workspace = true }
tower-lsp-server = { workspace = true, features = ["proposed"] }
which = "7.0.1"

View file

@ -5,7 +5,7 @@ pub use templatetags::TemplateTags;
use pyo3::prelude::*;
use std::fmt;
use std::path::{Path, PathBuf};
use tower_lsp::lsp_types::*;
use tower_lsp_server::lsp_types::*;
use which::which;
#[derive(Debug)]
@ -24,21 +24,6 @@ impl DjangoProject {
}
}
pub fn from_initialize_params(params: &InitializeParams) -> Option<Self> {
// Try current directory first
let path = std::env::current_dir()
.ok()
// Fall back to workspace root if provided
.or_else(|| {
params
.root_uri
.as_ref()
.and_then(|uri| uri.to_file_path().ok())
});
path.map(Self::new)
}
pub fn initialize(&mut self) -> PyResult<()> {
let python_env = PythonEnvironment::new().ok_or_else(|| {
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Could not find Python in PATH")

View file

@ -13,4 +13,6 @@ pyo3 = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tower-lsp = { workspace = true }
tower-lsp-server = { workspace = true }
percent-encoding = "2.3"

View file

@ -1,11 +1,7 @@
use anyhow::{anyhow, Result};
use djls_project::TemplateTags;
use std::collections::HashMap;
use tower_lsp::lsp_types::{
CompletionItem, CompletionItemKind, CompletionResponse, DidChangeTextDocumentParams,
DidCloseTextDocumentParams, DidOpenTextDocumentParams, Documentation, InsertTextFormat,
MarkupContent, MarkupKind, Position, Range,
};
use tower_lsp_server::lsp_types::*;
#[derive(Debug)]
pub struct Store {
@ -23,7 +19,7 @@ impl Store {
pub fn handle_did_open(&mut self, params: DidOpenTextDocumentParams) -> Result<()> {
let document = TextDocument::new(
String::from(params.text_document.uri),
params.text_document.uri.to_string(),
params.text_document.text,
params.text_document.version,
params.text_document.language_id,
@ -58,7 +54,7 @@ impl Store {
}
pub fn handle_did_close(&mut self, params: DidCloseTextDocumentParams) -> Result<()> {
self.remove_document(&String::from(params.text_document.uri));
self.remove_document(params.text_document.uri.as_str());
Ok(())
}

View file

@ -1,19 +1,19 @@
mod documents;
mod server;
mod tasks;
mod workspace;
use crate::server::DjangoLanguageServer;
use anyhow::Result;
use tower_lsp_server::{LspService, Server};
pub async fn serve() -> Result<()> {
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
let (service, socket) = tower_lsp::LspService::build(DjangoLanguageServer::new).finish();
let (service, socket) = LspService::build(DjangoLanguageServer::new).finish();
tower_lsp::Server::new(stdin, stdout, socket)
.serve(service)
.await;
Server::new(stdin, stdout, socket).serve(service).await;
Ok(())
}

View file

@ -1,12 +1,13 @@
use crate::documents::Store;
use crate::workspace::get_project_path;
use anyhow::Result;
use djls_project::DjangoProject;
use djls_worker::Worker;
use std::sync::Arc;
use tokio::sync::RwLock;
use tower_lsp::jsonrpc::Result as LspResult;
use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer};
use tower_lsp_server::jsonrpc::Result as LspResult;
use tower_lsp_server::lsp_types::*;
use tower_lsp_server::{Client, LanguageServer};
const SERVER_NAME: &str = "Django Language Server";
const SERVER_VERSION: &str = "0.1.0";
@ -34,12 +35,12 @@ impl DjangoLanguageServer {
}
}
#[tower_lsp::async_trait]
impl LanguageServer for DjangoLanguageServer {
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
let project = DjangoProject::from_initialize_params(&params);
let project_path = get_project_path(&params);
if let Some(mut project) = project {
if let Some(path) = project_path {
let mut project = DjangoProject::new(path);
match project.initialize() {
Ok(()) => {
self.log_message(
@ -109,7 +110,7 @@ impl LanguageServer for DjangoLanguageServer {
self.log_message(
MessageType::INFO,
&format!("Opened document: {}", params.text_document.uri),
&format!("Opened document: {:?}", params.text_document.uri),
)
.await
.ok();
@ -128,7 +129,7 @@ impl LanguageServer for DjangoLanguageServer {
self.log_message(
MessageType::INFO,
&format!("Changed document: {}", params.text_document.uri),
&format!("Changed document: {:?}", params.text_document.uri),
)
.await
.ok();
@ -147,7 +148,7 @@ impl LanguageServer for DjangoLanguageServer {
self.log_message(
MessageType::INFO,
&format!("Closed document: {}", params.text_document.uri),
&format!("Closed document: {:?}", params.text_document.uri),
)
.await
.ok();

View file

@ -0,0 +1,41 @@
use percent_encoding::percent_decode_str;
use std::path::PathBuf;
use tower_lsp_server::lsp_types::{InitializeParams, Uri};
/// Determines the project root path from initialization parameters.
///
/// Tries the current directory first, then falls back to the first workspace folder.
pub fn get_project_path(params: &InitializeParams) -> Option<PathBuf> {
// Try current directory first
std::env::current_dir().ok().or_else(|| {
// Fall back to the first workspace folder URI
params
.workspace_folders
.as_ref()
.and_then(|folders| folders.first())
.and_then(|folder| uri_to_pathbuf(&folder.uri))
})
}
/// Converts a `file:` URI into an absolute `PathBuf`.
fn uri_to_pathbuf(uri: &Uri) -> Option<PathBuf> {
// Check if the scheme is "file"
if uri.scheme().map_or(true, |s| s.as_str() != "file") {
return None;
}
// Get the path part as a string
let encoded_path_str = uri.path().as_str();
// Decode the percent-encoded path string
let decoded_path_cow = percent_decode_str(encoded_path_str).decode_utf8_lossy();
let path_str = decoded_path_cow.as_ref();
#[cfg(windows)]
let path_str = {
// Remove leading '/' for paths like /C:/...
path_str.strip_prefix('/').unwrap_or(path_str)
};
Some(PathBuf::from(path_str))
}

View file

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
anyhow = { workspace = true }
lsp-types = { workspace = true }
tower-lsp-server = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
toml = "0.8"

View file

@ -1,9 +1,9 @@
use crate::ast::{AstError, Span};
use crate::lexer::LexerError;
use crate::parser::ParserError;
use lsp_types;
use serde::Serialize;
use thiserror::Error;
use tower_lsp_server::lsp_types;
#[derive(Debug, Error, Serialize)]
pub enum TemplateError {
@ -71,14 +71,11 @@ impl TemplateError {
}
pub fn to_lsp_diagnostic(error: &TemplateError, _source: &str) -> lsp_types::Diagnostic {
let range = error.span().map_or_else(
|| lsp_types::Range::default(),
|span| {
let start = lsp_types::Position::new(0, *span.start());
let end = lsp_types::Position::new(0, span.start() + span.length());
lsp_types::Range::new(start, end)
},
);
let range = error.span().map_or_else(lsp_types::Range::default, |span| {
let start = lsp_types::Position::new(0, *span.start());
let end = lsp_types::Position::new(0, span.start() + span.length());
lsp_types::Range::new(start, end)
});
lsp_types::Diagnostic {
range,

View file

@ -15,6 +15,6 @@ pyo3 = { workspace = true, features = ["extension-module"] }
pyo3-async-runtimes = { workspace = true, features = ["tokio-runtime"] }
serde_json = { workspace = true }
tokio = { workspace = true }
tower-lsp-server = { workspace = true }
clap = { version = "4.5", features = ["derive"] }
tower-lsp = { version = "0.20", features = ["proposed"] }