Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "protols"
description = "Language server for proto3 files"
version = "0.12.5"
version = "0.12.6"
edition = "2024"
license = "MIT"
homepage = "https://github.com/coder3101/protols"
Expand All @@ -12,8 +12,8 @@ keywords = ["lsp", "proto"]
exclude = ["assets/*", "sample/*"]

[dependencies]
async-lsp = { version = "0.2.0", features = ["tokio"] }
futures = "0.3.30"
async-lsp = { version = "0.2.2", features = ["tokio"] }
futures = "0.3.31"
tokio = { version = "1.38.0", features = ["time", "full"] }
tokio-util = { version = "0.7.11", features = ["compat"] }
tower = "0.5.2"
Expand Down
111 changes: 54 additions & 57 deletions src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,30 @@ use tracing::{error, info};

use async_lsp::lsp_types::{
CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse,
CreateFilesParams, DeleteFilesParams, DidChangeConfigurationParams,
DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentFormattingParams,
DocumentRangeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, Documentation,
FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
FileOperationRegistrationOptions, GotoDefinitionParams, GotoDefinitionResponse, Hover,
HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
Location, MarkupContent, MarkupKind, OneOf, PrepareRenameResponse, ReferenceParams,
RenameFilesParams, RenameOptions, RenameParams, ServerCapabilities, ServerInfo,
TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, Url,
WorkspaceEdit, WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
CreateFilesParams, DeleteFilesParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
DidSaveTextDocumentParams, DocumentFormattingParams, DocumentRangeFormattingParams,
DocumentSymbolParams, DocumentSymbolResponse, Documentation, FileOperationFilter,
FileOperationPattern, FileOperationPatternKind, FileOperationRegistrationOptions,
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
HoverProviderCapability, InitializeParams, InitializeResult, Location, MarkupContent,
MarkupKind, OneOf, PrepareRenameResponse, ReferenceParams, RenameFilesParams, RenameOptions,
RenameParams, ServerCapabilities, ServerInfo, TextDocumentPositionParams,
TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, Url, WorkspaceEdit,
WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
WorkspaceServerCapabilities,
};
use async_lsp::{LanguageClient, LanguageServer, ResponseError};
use async_lsp::{LanguageClient, ResponseError};
use futures::future::BoxFuture;

use crate::docs;
use crate::formatter::ProtoFormatter;
use crate::server::ProtoLanguageServer;

impl LanguageServer for ProtoLanguageServer {
type Error = ResponseError;
type NotifyResult = ControlFlow<async_lsp::Result<()>>;

fn initialize(
impl ProtoLanguageServer {
pub(super) fn initialize(
&mut self,
params: InitializeParams,
) -> BoxFuture<'static, Result<InitializeResult, Self::Error>> {
) -> BoxFuture<'static, Result<InitializeResult, ResponseError>> {
let (cname, version) = params
.client_info
.as_ref()
Expand Down Expand Up @@ -122,10 +118,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn hover(
pub(super) fn hover(
&mut self,
param: HoverParams,
) -> BoxFuture<'static, Result<Option<Hover>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<Hover>, ResponseError>> {
let uri = param.text_document_position_params.text_document.uri;
let pos = param.text_document_position_params.position;

Expand Down Expand Up @@ -154,10 +150,10 @@ impl LanguageServer for ProtoLanguageServer {
})
}

fn completion(
pub(super) fn completion(
&mut self,
params: CompletionParams,
) -> BoxFuture<'static, Result<Option<CompletionResponse>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<CompletionResponse>, ResponseError>> {
let uri = params.text_document_position.text_document.uri;

// All keywords in the language
Expand Down Expand Up @@ -203,10 +199,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(Some(CompletionResponse::Array(completions))) })
}

fn prepare_rename(
pub(super) fn prepare_rename(
&mut self,
params: TextDocumentPositionParams,
) -> BoxFuture<'static, Result<Option<PrepareRenameResponse>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<PrepareRenameResponse>, ResponseError>> {
let uri = params.text_document.uri;
let pos = params.position;

Expand All @@ -220,10 +216,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn rename(
pub(super) fn rename(
&mut self,
params: RenameParams,
) -> BoxFuture<'static, Result<Option<WorkspaceEdit>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<WorkspaceEdit>, ResponseError>> {
let uri = params.text_document_position.text_document.uri;
let pos = params.text_document_position.position;

Expand Down Expand Up @@ -271,7 +267,7 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn references(
pub(super) fn references(
&mut self,
param: ReferenceParams,
) -> BoxFuture<'static, Result<Option<Vec<Location>>, ResponseError>> {
Expand Down Expand Up @@ -318,7 +314,7 @@ impl LanguageServer for ProtoLanguageServer {
})
}

fn definition(
pub(super) fn definition(
&mut self,
param: GotoDefinitionParams,
) -> BoxFuture<'static, Result<Option<GotoDefinitionResponse>, ResponseError>> {
Expand Down Expand Up @@ -353,10 +349,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn document_symbol(
pub(super) fn document_symbol(
&mut self,
params: DocumentSymbolParams,
) -> BoxFuture<'static, Result<Option<DocumentSymbolResponse>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<DocumentSymbolResponse>, ResponseError>> {
let uri = params.text_document.uri;

let Some(tree) = self.state.get_tree(&uri) else {
Expand All @@ -371,10 +367,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(Some(response)) })
}

fn formatting(
pub(super) fn formatting(
&mut self,
params: DocumentFormattingParams,
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, ResponseError>> {
let uri = params.text_document.uri;
let content = self.state.get_content(&uri);

Expand All @@ -386,10 +382,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn range_formatting(
pub(super) fn range_formatting(
&mut self,
params: DocumentRangeFormattingParams,
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, Self::Error>> {
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, ResponseError>> {
let uri = params.text_document.uri;
let content = self.state.get_content(&uri);

Expand All @@ -401,7 +397,10 @@ impl LanguageServer for ProtoLanguageServer {
Box::pin(async move { Ok(response) })
}

fn did_save(&mut self, params: DidSaveTextDocumentParams) -> Self::NotifyResult {
pub(super) fn did_save(
&mut self,
params: DidSaveTextDocumentParams,
) -> ControlFlow<async_lsp::Result<()>> {
let uri = params.text_document.uri;
let content = self.state.get_content(&uri);

Expand All @@ -424,11 +423,10 @@ impl LanguageServer for ProtoLanguageServer {
ControlFlow::Continue(())
}

fn did_close(&mut self, _params: DidCloseTextDocumentParams) -> Self::NotifyResult {
ControlFlow::Continue(())
}

fn did_open(&mut self, params: DidOpenTextDocumentParams) -> Self::NotifyResult {
pub(super) fn did_open(
&mut self,
params: DidOpenTextDocumentParams,
) -> ControlFlow<async_lsp::Result<()>> {
let uri = params.text_document.uri;
let content = params.text_document.text;

Expand All @@ -451,7 +449,10 @@ impl LanguageServer for ProtoLanguageServer {
ControlFlow::Continue(())
}

fn did_change(&mut self, params: DidChangeTextDocumentParams) -> Self::NotifyResult {
pub(super) fn did_change(
&mut self,
params: DidChangeTextDocumentParams,
) -> ControlFlow<async_lsp::Result<()>> {
let uri = params.text_document.uri;
let content = params.content_changes[0].text.clone();

Expand All @@ -474,7 +475,10 @@ impl LanguageServer for ProtoLanguageServer {
ControlFlow::Continue(())
}

fn did_create_files(&mut self, params: CreateFilesParams) -> Self::NotifyResult {
pub(super) fn did_create_files(
&mut self,
params: CreateFilesParams,
) -> ControlFlow<async_lsp::Result<()>> {
for file in params.files {
if let Ok(uri) = Url::from_file_path(&file.uri) {
// Safety: The uri is always a file type
Expand All @@ -488,7 +492,10 @@ impl LanguageServer for ProtoLanguageServer {
ControlFlow::Continue(())
}

fn did_rename_files(&mut self, params: RenameFilesParams) -> Self::NotifyResult {
pub(super) fn did_rename_files(
&mut self,
params: RenameFilesParams,
) -> ControlFlow<async_lsp::Result<()>> {
for file in params.files {
let Ok(new_uri) = Url::from_file_path(&file.new_uri) else {
error!(uri = file.new_uri, "failed to parse uri");
Expand All @@ -505,7 +512,10 @@ impl LanguageServer for ProtoLanguageServer {
ControlFlow::Continue(())
}

fn did_delete_files(&mut self, params: DeleteFilesParams) -> Self::NotifyResult {
pub(super) fn did_delete_files(
&mut self,
params: DeleteFilesParams,
) -> ControlFlow<async_lsp::Result<()>> {
for file in params.files {
if let Ok(uri) = Url::from_file_path(&file.uri) {
self.state.delete_file(&uri);
Expand All @@ -515,17 +525,4 @@ impl LanguageServer for ProtoLanguageServer {
}
ControlFlow::Continue(())
}

// Required because of: https://github.com/coder3101/protols/issues/32
fn did_change_configuration(&mut self, _: DidChangeConfigurationParams) -> Self::NotifyResult {
ControlFlow::Continue(())
}

// Required because when jumping to outside the workspace; this is triggered
fn did_change_workspace_folders(
&mut self,
_: DidChangeWorkspaceFoldersParams,
) -> Self::NotifyResult {
ControlFlow::Continue(())
}
}
7 changes: 4 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async fn main() {
let cli = Cli::parse();

let dir = std::env::temp_dir();
eprintln!("Rolling file based logging at directory: {dir:?}");
eprintln!("file logging at directory: {dir:?}");

let file_appender = tracing_appender::rolling::daily(dir.clone(), "protols.log");
let file_appender = tracing_appender::non_blocking(file_appender);
Expand All @@ -48,9 +48,10 @@ async fn main() {
.with_writer(file_appender.0)
.init();

tracing::info!("server version: {}", env!("CARGO_PKG_VERSION"));
let (server, _) = async_lsp::MainLoop::new_server(|client| {
tracing::info!("Using CLI options: {:?}", cli);
let server = ProtoLanguageServer::new_router(
let router = ProtoLanguageServer::new_router(
client.clone(),
cli.include_paths
.map(|ic| ic.into_iter().map(std::path::PathBuf::from).collect())
Expand All @@ -76,7 +77,7 @@ async fn main() {
.layer(CatchUnwindLayer::default())
.layer(ConcurrencyLayer::default())
.layer(ClientProcessMonitorLayer::new(client.clone()))
.service(server)
.service(router)
});

// Prefer truly asynchronous piped stdin/stdout without blocking tasks.
Expand Down
52 changes: 44 additions & 8 deletions src/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
use async_lsp::{
ClientSocket, LanguageClient,
lsp_types::{NumberOrString, ProgressParams, ProgressParamsValue},
lsp_types::{
NumberOrString, ProgressParams, ProgressParamsValue,
notification::{
DidChangeTextDocument, DidCreateFiles, DidDeleteFiles, DidOpenTextDocument,
DidRenameFiles, DidSaveTextDocument,
},
request::{
Completion, DocumentSymbolRequest, Formatting, GotoDefinition, HoverRequest,
Initialize, PrepareRenameRequest, RangeFormatting, References, Rename,
},
},
router::Router,
};
use std::{
Expand All @@ -22,19 +32,45 @@ pub struct ProtoLanguageServer {

impl ProtoLanguageServer {
pub fn new_router(client: ClientSocket, cli_include_paths: Vec<PathBuf>) -> Router<Self> {
let mut router = Router::from_language_server(Self {
let mut router = Router::new(Self {
client,
counter: 0,
state: ProtoLanguageState::new(),
configs: WorkspaceProtoConfigs::new(cli_include_paths),
});
router.event(Self::on_tick);
router
}

fn on_tick(&mut self, _: TickEvent) -> ControlFlow<async_lsp::Result<()>> {
self.counter += 1;
ControlFlow::Continue(())
router.event::<TickEvent>(|st, _| {
st.counter += 1;
ControlFlow::Continue(())
});

// Ignore any unknown notification.
router.unhandled_notification(|_, notif| {
tracing::info!(notif.method, "ignored unknown notification");
ControlFlow::Continue(())
});

// Handling request
router.request::<Initialize, _>(|st, params| st.initialize(params));
router.request::<HoverRequest, _>(|st, params| st.hover(params));
router.request::<Completion, _>(|st, params| st.completion(params));
router.request::<PrepareRenameRequest, _>(|st, params| st.prepare_rename(params));
router.request::<Rename, _>(|st, params| st.rename(params));
router.request::<References, _>(|st, params| st.references(params));
router.request::<GotoDefinition, _>(|st, params| st.definition(params));
router.request::<DocumentSymbolRequest, _>(|st, params| st.document_symbol(params));
router.request::<Formatting, _>(|st, params| st.formatting(params));
router.request::<RangeFormatting, _>(|st, params| st.range_formatting(params));

// Handling notification
router.notification::<DidSaveTextDocument>(|st, params| st.did_save(params));
router.notification::<DidOpenTextDocument>(|st, params| st.did_open(params));
router.notification::<DidChangeTextDocument>(|st, params| st.did_change(params));
router.notification::<DidCreateFiles>(|st, params| st.did_create_files(params));
router.notification::<DidRenameFiles>(|st, params| st.did_rename_files(params));
router.notification::<DidDeleteFiles>(|st, params| st.did_delete_files(params));

router
}

pub fn with_report_progress(&self, token: NumberOrString) -> Sender<ProgressParamsValue> {
Expand Down