Skip to content

Commit cccb87b

Browse files
tomsmedingautozimu
authored andcommitted
Wrap notify::Watcher to handle vim's write-via-rename (#1054)
The language server can request a watch on a file that is currently open in vim. If it does so, LanguageClient-neovim adds a file system watcher on that file. Doing this directly via `notify::Watcher` as it did before, however, goes wrong on systems where a file watch is really an inode watch, and vim writes to files by writing to a temporary file and then renaming over the original file. These systems include Linux and macOS.
1 parent f10377c commit cccb87b

File tree

4 files changed

+342
-12
lines changed

4 files changed

+342
-12
lines changed

src/language_server_protocol.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
vim_cmd_args_to_value, Canonicalize, Combine, ToUrl,
1111
},
1212
viewport,
13+
watcher::FSWatch,
1314
};
1415
use anyhow::{anyhow, Context, Error, Result};
1516
use glob::glob;
@@ -43,7 +44,6 @@ use lsp_types::{
4344
WorkDoneProgressParams, WorkspaceClientCapabilities, WorkspaceEdit, WorkspaceSymbolParams,
4445
};
4546
use maplit::hashmap;
46-
use notify::Watcher;
4747
use serde::de::Deserialize;
4848
use serde_json::json;
4949
use std::{
@@ -2764,7 +2764,7 @@ impl LanguageClient {
27642764
if !self.get(|state| state.watchers.contains_key(language_id))? {
27652765
let (watcher_tx, watcher_rx) = mpsc::channel();
27662766
// TODO: configurable duration.
2767-
let watcher = notify::watcher(watcher_tx, Duration::from_secs(2))?;
2767+
let watcher = FSWatch::new(watcher_tx, Duration::from_secs(2))?;
27682768
self.update(|state| {
27692769
state.watchers.insert(language_id.to_owned(), watcher);
27702770
state.watcher_rxs.insert(language_id.to_owned(), watcher_rx);
@@ -2779,17 +2779,15 @@ impl LanguageClient {
27792779
for entry in glob(&w.glob_pattern)? {
27802780
match entry {
27812781
Ok(path) => {
2782-
let mode = if path.is_dir() {
2783-
notify::RecursiveMode::Recursive
2782+
if path.is_dir() {
2783+
watcher.watch_dir(
2784+
&path,
2785+
notify::RecursiveMode::Recursive,
2786+
)?;
27842787
} else {
2785-
notify::RecursiveMode::NonRecursive
2788+
watcher.watch_file(&path)?;
27862789
};
2787-
debug!(
2788-
"Watching path {} with mode {:?}",
2789-
path.display(),
2790-
mode
2791-
);
2792-
watcher.watch(path, mode)?;
2790+
info!("Start watching path {:?}", path);
27932791
}
27942792
Err(e) => {
27952793
warn!("Error globbing for {}: {}", w.glob_pattern, e)

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod utils;
1010
mod viewport;
1111
mod vim;
1212
mod vimext;
13+
mod watcher;
1314

1415
use anyhow::Result;
1516
use std::collections::HashMap;

src/types.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{
55
language_client::LanguageClient,
66
utils::{code_action_kind_as_str, ToUrl},
77
vim::Vim,
8+
watcher::FSWatch,
89
};
910
use anyhow::{anyhow, Result};
1011
use jsonrpc_core::Params;
@@ -171,7 +172,7 @@ pub struct State {
171172
pub document_highlight_source: Option<HighlightSource>,
172173
pub user_handlers: HashMap<String, String>,
173174
#[serde(skip_serializing)]
174-
pub watchers: HashMap<String, notify::RecommendedWatcher>,
175+
pub watchers: HashMap<String, FSWatch>,
175176
#[serde(skip_serializing)]
176177
pub watcher_rxs: HashMap<String, mpsc::Receiver<notify::DebouncedEvent>>,
177178

0 commit comments

Comments
 (0)