Repository: rust-analyzer/rust-analyzer
Pull request #5511 (2020-07-23 21:49:13)

Set the document version after changes are applied but before vfs


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 0a35eb7932..007b0d6a55 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1326,6 +1326,15 @@ version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
 
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "rowan"
 version = "0.10.0"
@@ -1377,6 +1386,7 @@ dependencies = [
  "serde",
  "serde_json",
  "stdx",
+ "tempfile",
  "test_utils",
  "threadpool",
  "vfs",
@@ -1596,6 +1606,20 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.0"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 3f9c820c5d..758aa1c5d7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -58,6 +58,7 @@ ra_proc_macro_srv = { path = "../ra_proc_macro_srv" }
 winapi = "0.3.8"
 
 [dev-dependencies]
+tempfile = "3.1.0"
 expect = { path = "../expect" }
 test_utils = { path = "../test_utils" }
 mbe = { path = "../ra_mbe", package = "ra_mbe" }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ac95e428eb..e95d4157c0 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -420,14 +420,17 @@ impl GlobalState {
             })?
             .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
-                    *this.mem_docs.get_mut(&path).unwrap() = params.text_document.version;
+                    let doc = this.mem_docs.get_mut(&path).unwrap();
                     let vfs = &mut this.vfs.write().0;
                     let file_id = vfs.file_id(&path).unwrap();
                     let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
                     apply_document_changes(&mut text, params.content_changes);
-                    vfs.set_file_contents(path.clone(), Some(text.into_bytes()));
 
-                    this.mem_docs.insert(path, params.text_document.version);
+                    // The version passed in DidChangeTextDocument is the version after all edits are applied
+                    // so we should apply it before the vfs is notified.
+                    *doc = params.text_document.version;
+
+                    vfs.set_file_contents(path.clone(), Some(text.into_bytes()));
                 }
                 Ok(())
             })?


Test patch:
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 28e8966487..93448834f7 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -1,4 +1,3 @@
-mod testdir;
 mod support;
 
 use std::{collections::HashMap, path::PathBuf, time::Instant};
@@ -13,12 +12,10 @@ use lsp_types::{
 };
 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
 use serde_json::json;
+use tempfile::TempDir;
 use test_utils::skip_slow_tests;
 
-use crate::{
-    support::{project, Project},
-    testdir::TestDir,
-};
+use crate::support::{project, Project};
 
 const PROFILE: &str = "";
 // const PROFILE: &'static str = "*@3>100";
@@ -311,7 +308,7 @@ fn test_missing_module_code_action_in_json_project() {
         return;
     }
 
-    let tmp_dir = TestDir::new();
+    let tmp_dir = TempDir::new().unwrap();
 
     let path = tmp_dir.path();
 
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index e152264d36..e51796d36e 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -19,15 +19,14 @@ use rust_analyzer::{
 };
 use serde::Serialize;
 use serde_json::{to_string_pretty, Value};
+use tempfile::TempDir;
 use test_utils::{find_mismatch, Fixture};
 use vfs::AbsPathBuf;
 
-use crate::testdir::TestDir;
-
 pub struct Project<'a> {
     fixture: &'a str,
     with_sysroot: bool,
-    tmp_dir: Option<TestDir>,
+    tmp_dir: Option<TempDir>,
     roots: Vec<PathBuf>,
     config: Option<Box<dyn Fn(&mut Config)>>,
 }
@@ -37,7 +36,7 @@ impl<'a> Project<'a> {
         Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None }
     }
 
-    pub fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> {
+    pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> {
         self.tmp_dir = Some(tmp_dir);
         self
     }
@@ -58,7 +57,7 @@ impl<'a> Project<'a> {
     }
 
     pub fn server(self) -> Server {
-        let tmp_dir = self.tmp_dir.unwrap_or_else(|| TestDir::new());
+        let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
         static INIT: Once = Once::new();
         INIT.call_once(|| {
             env_logger::builder().is_test(true).try_init().unwrap();
@@ -113,11 +112,11 @@ pub struct Server {
     _thread: jod_thread::JoinHandle<()>,
     client: Connection,
     /// XXX: remove the tempdir last
-    dir: TestDir,
+    dir: TempDir,
 }
 
 impl Server {
-    fn new(dir: TestDir, config: Config) -> Server {
+    fn new(dir: TempDir, config: Config) -> Server {
         let (connection, client) = Connection::memory();
 
         let _thread = jod_thread::Builder::new()
diff --git a/crates/rust-analyzer/tests/heavy_tests/testdir.rs b/crates/rust-analyzer/tests/heavy_tests/testdir.rs
deleted file mode 100644
index 7487e74294..0000000000
--- a/crates/rust-analyzer/tests/heavy_tests/testdir.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use std::{
-    fs, io,
-    path::{Path, PathBuf},
-    sync::atomic::{AtomicUsize, Ordering},
-};
-
-pub struct TestDir {
-    path: PathBuf,
-    keep: bool,
-}
-
-impl TestDir {
-    pub fn new() -> TestDir {
-        let base = std::env::temp_dir().join("testdir");
-        let pid = std::process::id();
-
-        static CNT: AtomicUsize = AtomicUsize::new(0);
-        for _ in 0..100 {
-            let cnt = CNT.fetch_add(1, Ordering::Relaxed);
-            let path = base.join(format!("{}_{}", pid, cnt));
-            if path.is_dir() {
-                continue;
-            }
-            fs::create_dir_all(&path).unwrap();
-            return TestDir { path, keep: false };
-        }
-        panic!("Failed to create a temporary directory")
-    }
-    #[allow(unused)]
-    pub fn keep(mut self) -> TestDir {
-        self.keep = true;
-        self
-    }
-    pub fn path(&self) -> &Path {
-        &self.path
-    }
-}
-
-impl Drop for TestDir {
-    fn drop(&mut self) {
-        if self.keep {
-            return;
-        }
-        remove_dir_all(&self.path).unwrap()
-    }
-}
-
-#[cfg(not(windows))]
-fn remove_dir_all(path: &Path) -> io::Result<()> {
-    fs::remove_dir_all(path)
-}
-
-#[cfg(windows)]
-fn remove_dir_all(path: &Path) -> io::Result<()> {
-    for _ in 0..99 {
-        if fs::remove_dir_all(path).is_ok() {
-            return Ok(());
-        }
-        std::thread::sleep(std::time::Duration::from_millis(10))
-    }
-    fs::remove_dir_all(path)
-}