mirror of https://github.com/lapce/lapce.git
Handle LSP requests for hearing about file saving of specific files
This commit is contained in:
parent
5646bdb235
commit
a904656f10
|
@ -2302,6 +2302,7 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"directories",
|
||||
"git2",
|
||||
"globset",
|
||||
"grep-matcher",
|
||||
"grep-regex",
|
||||
"grep-searcher",
|
||||
|
|
|
@ -11,6 +11,7 @@ grep-searcher = "0.1.8"
|
|||
grep-matcher = "0.1.5"
|
||||
grep-regex = "0.1.9"
|
||||
ignore = "0.4.18"
|
||||
globset = "0.4.9"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json", "socks"] }
|
||||
wasmer = "2.1.1"
|
||||
wasmer-wasi = "2.1.1"
|
||||
|
|
|
@ -629,11 +629,13 @@ fn handle_request(&self, id: RequestId, rpc: ProxyRequest) {
|
|||
}
|
||||
}
|
||||
Save { rev, buffer_id } => {
|
||||
let mut buffers = self.buffers.lock();
|
||||
let buffer = buffers.get_mut(&buffer_id).unwrap();
|
||||
let resp = buffer.save(rev).map(|_r| json!({}));
|
||||
self.lsp.lock().save_buffer(buffer);
|
||||
self.respond(id, resp);
|
||||
if let Some(workspace) = self.workspace.lock().as_ref() {
|
||||
let mut buffers = self.buffers.lock();
|
||||
let buffer = buffers.get_mut(&buffer_id).unwrap();
|
||||
let resp = buffer.save(rev).map(|_r| json!({}));
|
||||
self.lsp.lock().save_buffer(buffer, workspace);
|
||||
self.respond(id, resp);
|
||||
}
|
||||
}
|
||||
SaveBufferAs {
|
||||
buffer_id,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
collections::HashMap,
|
||||
io::BufRead,
|
||||
io::{BufReader, BufWriter, Write},
|
||||
path::Path,
|
||||
process::{self, Child, ChildStdout, Command, Stdio},
|
||||
sync::{mpsc::channel, Arc},
|
||||
thread,
|
||||
|
@ -52,6 +53,37 @@ pub struct LspState {
|
|||
pub server_capabilities: Option<ServerCapabilities>,
|
||||
pub opened_documents: HashMap<BufferId, Url>,
|
||||
pub is_initialized: bool,
|
||||
pub did_save_capabilities: Vec<DidSaveCapability>,
|
||||
}
|
||||
|
||||
pub struct DocumentFilter {
|
||||
/// The document must have this language id, if it exists
|
||||
pub language_id: Option<String>,
|
||||
/// The document's path must match this glob, if it exists
|
||||
pub pattern: Option<globset::GlobMatcher>,
|
||||
// TODO: URI Scheme from lsp-types document filter
|
||||
}
|
||||
impl DocumentFilter {
|
||||
/// Constructs a document filter from the LSP version
|
||||
/// This ignores any fields that are badly constructed
|
||||
fn from_lsp_filter_loose(filter: lsp_types::DocumentFilter) -> DocumentFilter {
|
||||
DocumentFilter {
|
||||
language_id: filter.language,
|
||||
// TODO: clean this up
|
||||
pattern: filter
|
||||
.pattern
|
||||
.as_deref()
|
||||
.map(globset::Glob::new)
|
||||
.and_then(Result::ok)
|
||||
.map(|x| globset::Glob::compile_matcher(&x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct DidSaveCapability {
|
||||
/// A filter on what documents this applies to
|
||||
filter: DocumentFilter,
|
||||
/// Whether we are supposed to include the text when sending a didSave event
|
||||
include_text: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -135,10 +167,53 @@ pub fn new_buffer(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn save_buffer(&self, buffer: &Buffer) {
|
||||
if let Some(client) = self.clients.get(&buffer.language_id) {
|
||||
let uri = client.get_uri(buffer);
|
||||
client.send_did_save(uri);
|
||||
pub fn save_buffer(&self, buffer: &Buffer, workspace_path: &Path) {
|
||||
for (client_language_id, client) in self.clients.iter() {
|
||||
// Get rid of the workspace path prefix so that it can be used with the filters
|
||||
let buffer_path = buffer
|
||||
.path
|
||||
.strip_prefix(workspace_path)
|
||||
.unwrap_or(&buffer.path);
|
||||
|
||||
let mut passed_filter = client_language_id == &buffer.language_id;
|
||||
let mut include_text = false;
|
||||
if !passed_filter {
|
||||
let lsp_state = client.state.lock();
|
||||
|
||||
// TODO: Should we iterate in reverse order so that later capabilities
|
||||
// can overwrite old ones?
|
||||
// Find the first capability that wants this file, if any.
|
||||
for cap in &lsp_state.did_save_capabilities {
|
||||
if let Some(language_id) = &cap.filter.language_id {
|
||||
if language_id != &buffer.language_id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pattern) = &cap.filter.pattern {
|
||||
if !pattern.is_match(buffer_path) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
passed_filter = true;
|
||||
include_text = cap.include_text;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get rid of the mutex guard
|
||||
drop(lsp_state);
|
||||
}
|
||||
|
||||
if passed_filter {
|
||||
let uri = client.get_uri(buffer);
|
||||
let text = if include_text {
|
||||
Some(buffer.get_document())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
client.send_did_save(uri, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,6 +532,7 @@ pub fn new(
|
|||
server_capabilities: None,
|
||||
opened_documents: HashMap::new(),
|
||||
is_initialized: false,
|
||||
did_save_capabilities: Vec::new(),
|
||||
})),
|
||||
});
|
||||
|
||||
|
@ -577,7 +653,7 @@ pub fn handle_message(&self, message: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_request(&self, method: &str, id: Id, _params: Params) {
|
||||
pub fn handle_request(&self, method: &str, id: Id, params: Params) {
|
||||
match method {
|
||||
"window/workDoneProgress/create" => {
|
||||
// Token is ignored as the workProgress Widget is always working
|
||||
|
@ -585,6 +661,41 @@ pub fn handle_request(&self, method: &str, id: Id, _params: Params) {
|
|||
// probably store the token
|
||||
self.send_success_response(id, &json!({}));
|
||||
}
|
||||
"client/registerCapability" => {
|
||||
if let Ok(registrations) =
|
||||
serde_json::from_value::<RegistrationParams>(json!(params))
|
||||
{
|
||||
for registration in registrations.registrations {
|
||||
match registration.method.as_str() {
|
||||
"textDocument/didSave" => {
|
||||
if let Some(options) = registration.register_options {
|
||||
if let Ok(options) = serde_json::from_value::<TextDocumentSaveRegistrationOptions>(options) {
|
||||
if let Some(selectors) = options.text_document_registration_options.document_selector {
|
||||
// TODO: is false a reasonable default?
|
||||
let include_text = options.include_text.unwrap_or(false);
|
||||
|
||||
let mut lsp_state = self.state.lock();
|
||||
|
||||
// Add each selector our did save filtering
|
||||
for selector in selectors {
|
||||
let filter = DocumentFilter::from_lsp_filter_loose(selector);
|
||||
let cap = DidSaveCapability {
|
||||
filter,
|
||||
include_text,
|
||||
};
|
||||
|
||||
lsp_state.did_save_capabilities.push(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: report error?
|
||||
}
|
||||
_ => println!("Received unhandled client/registerCapability request {}", registration.method),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
method => {
|
||||
println!("Received unhandled request {method}");
|
||||
}
|
||||
|
@ -740,10 +851,10 @@ pub fn send_did_open(
|
|||
self.send_notification("textDocument/didOpen", params);
|
||||
}
|
||||
|
||||
pub fn send_did_save(&self, uri: Url) {
|
||||
pub fn send_did_save(&self, uri: Url, text: Option<String>) {
|
||||
let params = DidSaveTextDocumentParams {
|
||||
text_document: TextDocumentIdentifier { uri },
|
||||
text: None,
|
||||
text,
|
||||
};
|
||||
let params = Params::from(serde_json::to_value(params).unwrap());
|
||||
self.send_notification("textDocument/didSave", params);
|
||||
|
@ -759,6 +870,11 @@ pub fn send_initialize<CB>(&self, root_uri: Option<Url>, on_init: CB)
|
|||
{
|
||||
let client_capabilities = ClientCapabilities {
|
||||
text_document: Some(TextDocumentClientCapabilities {
|
||||
synchronization: Some(TextDocumentSyncClientCapabilities {
|
||||
did_save: Some(true),
|
||||
dynamic_registration: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
completion: Some(CompletionClientCapabilities {
|
||||
completion_item: Some(CompletionItemCapability {
|
||||
snippet_support: Some(true),
|
||||
|
|
Loading…
Reference in New Issue