5481: Track document versions in the server r=kjeremy a=kjeremy

This also pushes diagnostics for the correct file version on close so that when it is reopened stale diagnostics are not shown.

Closes #5452 

Co-authored-by: kjeremy <kjeremy@gmail.com>
Co-authored-by: Jeremy Kolb <kjeremy@gmail.com>
This commit is contained in:
bors[bot] 2020-07-22 13:04:30 +00:00 committed by GitHub
commit 1c7d5f513c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 15 deletions

View file

@ -12,7 +12,7 @@ use parking_lot::RwLock;
use ra_db::{CrateId, VfsPath};
use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_hash::FxHashMap;
use crate::{
config::Config,
@ -69,7 +69,7 @@ pub(crate) struct GlobalState {
pub(crate) config: Config,
pub(crate) analysis_host: AnalysisHost,
pub(crate) diagnostics: DiagnosticCollection,
pub(crate) mem_docs: FxHashSet<VfsPath>,
pub(crate) mem_docs: FxHashMap<VfsPath, Option<i64>>,
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
pub(crate) status: Status,
pub(crate) source_root_config: SourceRootConfig,
@ -84,6 +84,7 @@ pub(crate) struct GlobalStateSnapshot {
pub(crate) analysis: Analysis,
pub(crate) check_fixes: CheckFixes,
pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
mem_docs: FxHashMap<VfsPath, Option<i64>>,
vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}
@ -117,7 +118,7 @@ impl GlobalState {
config,
analysis_host,
diagnostics: Default::default(),
mem_docs: FxHashSet::default(),
mem_docs: FxHashMap::default(),
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
status: Status::default(),
source_root_config: SourceRootConfig::default(),
@ -183,6 +184,7 @@ impl GlobalState {
vfs: Arc::clone(&self.vfs),
latest_requests: Arc::clone(&self.latest_requests),
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
mem_docs: self.mem_docs.clone(),
}
}
@ -255,6 +257,11 @@ impl GlobalStateSnapshot {
self.vfs.read().1[&id]
}
pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> {
let path = from_proto::vfs_path(&url).ok()?;
self.mem_docs.get(&path).copied()?
}
pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
let mut base = self.vfs.read().0.file_path(file_id);
base.pop();

View file

@ -210,7 +210,7 @@ impl GlobalState {
let vfs = &mut self.vfs.write().0;
for (path, contents) in files {
let path = VfsPath::from(path);
if !self.mem_docs.contains(&path) {
if !self.mem_docs.contains_key(&path) {
vfs.set_file_contents(path, contents)
}
}
@ -299,7 +299,7 @@ impl GlobalState {
if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) {
let subscriptions = self
.mem_docs
.iter()
.keys()
.map(|path| self.vfs.read().0.file_id(&path).unwrap())
.collect::<Vec<_>>();
@ -310,8 +310,12 @@ impl GlobalState {
for file_id in diagnostic_changes {
let url = file_id_to_url(&self.vfs.read().0, file_id);
let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
let version = from_proto::vfs_path(&url)
.map(|path| self.mem_docs.get(&path).copied().flatten())
.unwrap_or_default();
self.send_notification::<lsp_types::notification::PublishDiagnostics>(
lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None },
lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version },
);
}
}
@ -400,7 +404,11 @@ impl GlobalState {
})?
.on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
if !this.mem_docs.insert(path.clone()) {
if this
.mem_docs
.insert(path.clone(), Some(params.text_document.version))
.is_some()
{
log::error!("duplicate DidOpenTextDocument: {}", path)
}
this.vfs
@ -412,29 +420,38 @@ impl GlobalState {
})?
.on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
assert!(this.mem_docs.contains(&path));
*this.mem_docs.get_mut(&path).unwrap() = params.text_document.version;
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, Some(text.into_bytes()))
vfs.set_file_contents(path.clone(), Some(text.into_bytes()));
this.mem_docs.insert(path, params.text_document.version);
}
Ok(())
})?
.on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
let mut version = None;
if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
if !this.mem_docs.remove(&path) {
log::error!("orphan DidCloseTextDocument: {}", path)
match this.mem_docs.remove(&path) {
Some(entry) => version = entry,
None => log::error!("orphan DidCloseTextDocument: {}", path),
}
if let Some(path) = path.as_path() {
this.loader.handle.invalidate(path.to_path_buf());
}
}
// Clear the diagnostics for the previously known version of the file.
// This prevents stale "cargo check" diagnostics if the file is
// closed, "cargo check" is run and then the file is reopened.
this.send_notification::<lsp_types::notification::PublishDiagnostics>(
lsp_types::PublishDiagnosticsParams {
uri: params.text_document.uri,
diagnostics: Vec::new(),
version: None,
version,
},
);
Ok(())

View file

@ -480,9 +480,10 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
pub(crate) fn versioned_text_document_identifier(
snap: &GlobalStateSnapshot,
file_id: FileId,
version: Option<i64>,
) -> lsp_types::VersionedTextDocumentIdentifier {
lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id), version }
let url = url(snap, file_id);
let version = snap.url_file_version(&url);
lsp_types::VersionedTextDocumentIdentifier { uri: url, version }
}
pub(crate) fn location(
@ -571,7 +572,7 @@ pub(crate) fn snippet_text_document_edit(
is_snippet: bool,
source_file_edit: SourceFileEdit,
) -> Result<lsp_ext::SnippetTextDocumentEdit> {
let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None);
let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id);
let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?;
let line_endings = snap.file_line_endings(source_file_edit.file_id);
let edits = source_file_edit