mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-27 12:54:42 +00:00
Remove duplication when assigning diagnostics and hardcode provider names
This commit is contained in:
parent
4f774e2bde
commit
11e3874b4a
8 changed files with 198 additions and 178 deletions
|
@ -579,7 +579,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use client::{http::ServerResponse, test::FakeHttpClient, Client, UserStore};
|
use client::{http::ServerResponse, test::FakeHttpClient, Client, UserStore};
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry, PointUtf16};
|
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry};
|
||||||
use project::FakeFs;
|
use project::FakeFs;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -637,13 +637,11 @@ mod tests {
|
||||||
|
|
||||||
worktree.update(&mut cx, |worktree, cx| {
|
worktree.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree
|
||||||
.update_point_utf16_diagnostics(
|
.update_diagnostics_from_provider(
|
||||||
"lsp".into(),
|
|
||||||
Arc::from("/test/main.rs".as_ref()),
|
Arc::from("/test/main.rs".as_ref()),
|
||||||
None,
|
|
||||||
vec![
|
vec![
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(1, 8)..PointUtf16::new(1, 9),
|
range: 20..21,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message:
|
message:
|
||||||
"move occurs because `x` has type `Vec<char>`, which does not implement the `Copy` trait"
|
"move occurs because `x` has type `Vec<char>`, which does not implement the `Copy` trait"
|
||||||
|
@ -655,7 +653,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(2, 8)..PointUtf16::new(2, 9),
|
range: 40..41,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message:
|
message:
|
||||||
"move occurs because `y` has type `Vec<char>`, which does not implement the `Copy` trait"
|
"move occurs because `y` has type `Vec<char>`, which does not implement the `Copy` trait"
|
||||||
|
@ -667,7 +665,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(3, 6)..PointUtf16::new(3, 7),
|
range: 58..59,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message: "value moved here".to_string(),
|
message: "value moved here".to_string(),
|
||||||
severity: DiagnosticSeverity::INFORMATION,
|
severity: DiagnosticSeverity::INFORMATION,
|
||||||
|
@ -677,7 +675,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(4, 6)..PointUtf16::new(4, 7),
|
range: 68..69,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message: "value moved here".to_string(),
|
message: "value moved here".to_string(),
|
||||||
severity: DiagnosticSeverity::INFORMATION,
|
severity: DiagnosticSeverity::INFORMATION,
|
||||||
|
@ -687,7 +685,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(7, 6)..PointUtf16::new(7, 7),
|
range: 112..113,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message: "use of moved value\nvalue used here after move".to_string(),
|
message: "use of moved value\nvalue used here after move".to_string(),
|
||||||
severity: DiagnosticSeverity::ERROR,
|
severity: DiagnosticSeverity::ERROR,
|
||||||
|
@ -697,7 +695,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DiagnosticEntry {
|
DiagnosticEntry {
|
||||||
range: PointUtf16::new(8, 6)..PointUtf16::new(8, 7),
|
range: 122..123,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message: "use of moved value\nvalue used here after move".to_string(),
|
message: "use of moved value\nvalue used here after move".to_string(),
|
||||||
severity: DiagnosticSeverity::ERROR,
|
severity: DiagnosticSeverity::ERROR,
|
||||||
|
@ -764,12 +762,10 @@ mod tests {
|
||||||
|
|
||||||
worktree.update(&mut cx, |worktree, cx| {
|
worktree.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree
|
||||||
.update_point_utf16_diagnostics(
|
.update_diagnostics_from_provider(
|
||||||
"lsp".into(),
|
|
||||||
Arc::from("/test/a.rs".as_ref()),
|
Arc::from("/test/a.rs".as_ref()),
|
||||||
None,
|
|
||||||
vec![DiagnosticEntry {
|
vec![DiagnosticEntry {
|
||||||
range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15),
|
range: 15..15,
|
||||||
diagnostic: Diagnostic {
|
diagnostic: Diagnostic {
|
||||||
message: "mismatched types\nexpected `usize`, found `char`".to_string(),
|
message: "mismatched types\nexpected `usize`, found `char`".to_string(),
|
||||||
severity: DiagnosticSeverity::ERROR,
|
severity: DiagnosticSeverity::ERROR,
|
||||||
|
|
|
@ -23,7 +23,7 @@ use std::{
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
future::Future,
|
future::Future,
|
||||||
iter::{Iterator, Peekable},
|
iter::{Iterator, Peekable},
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Add, Deref, DerefMut, Range, Sub},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str,
|
str,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -31,7 +31,7 @@ use std::{
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
use sum_tree::TreeMap;
|
use sum_tree::TreeMap;
|
||||||
use text::operation_queue::OperationQueue;
|
use text::{operation_queue::OperationQueue, rope::TextDimension};
|
||||||
pub use text::{Buffer as TextBuffer, Operation as _, *};
|
pub use text::{Buffer as TextBuffer, Operation as _, *};
|
||||||
use theme::SyntaxTheme;
|
use theme::SyntaxTheme;
|
||||||
use tree_sitter::{InputEdit, Parser, QueryCursor, Tree};
|
use tree_sitter::{InputEdit, Parser, QueryCursor, Tree};
|
||||||
|
@ -85,6 +85,12 @@ pub struct BufferSnapshot {
|
||||||
parse_count: usize,
|
parse_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct GroupId {
|
||||||
|
source: Arc<str>,
|
||||||
|
id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Diagnostic {
|
pub struct Diagnostic {
|
||||||
pub code: Option<String>,
|
pub code: Option<String>,
|
||||||
|
@ -740,13 +746,16 @@ impl Buffer {
|
||||||
self.diagnostic_sets.iter().flat_map(|set| set.iter())
|
self.diagnostic_sets.iter().flat_map(|set| set.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_diagnostics(
|
pub fn update_diagnostics<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider_name: Arc<str>,
|
provider_name: Arc<str>,
|
||||||
version: Option<i32>,
|
version: Option<i32>,
|
||||||
mut diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
|
mut diagnostics: Vec<DiagnosticEntry<T>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Result<Operation> {
|
) -> Result<Operation>
|
||||||
|
where
|
||||||
|
T: ToPoint + Ord + Clip + TextDimension + Add<Output = T> + Sub<Output = T> + Copy,
|
||||||
|
{
|
||||||
fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
|
fn compare_diagnostics(a: &Diagnostic, b: &Diagnostic) -> Ordering {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
.then_with(|| b.is_primary.cmp(&a.is_primary))
|
.then_with(|| b.is_primary.cmp(&a.is_primary))
|
||||||
|
@ -755,13 +764,6 @@ impl Buffer {
|
||||||
.then_with(|| a.message.cmp(&b.message))
|
.then_with(|| a.message.cmp(&b.message))
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics.sort_unstable_by(|a, b| {
|
|
||||||
Ordering::Equal
|
|
||||||
.then_with(|| a.range.start.cmp(&b.range.start))
|
|
||||||
.then_with(|| b.range.end.cmp(&a.range.end))
|
|
||||||
.then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
|
|
||||||
});
|
|
||||||
|
|
||||||
let version = version.map(|version| version as usize);
|
let version = version.map(|version| version as usize);
|
||||||
let content = if let Some(version) = version {
|
let content = if let Some(version) = version {
|
||||||
let language_server = self.language_server.as_mut().unwrap();
|
let language_server = self.language_server.as_mut().unwrap();
|
||||||
|
@ -777,14 +779,18 @@ impl Buffer {
|
||||||
self.deref()
|
self.deref()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut edits_since_save = content
|
diagnostics.sort_unstable_by(|a, b| {
|
||||||
.edits_since::<PointUtf16>(&self.saved_version)
|
Ordering::Equal
|
||||||
.peekable();
|
.then_with(|| a.range.start.cmp(&b.range.start))
|
||||||
let mut last_edit_old_end = PointUtf16::zero();
|
.then_with(|| b.range.end.cmp(&a.range.end))
|
||||||
let mut last_edit_new_end = PointUtf16::zero();
|
.then_with(|| compare_diagnostics(&a.diagnostic, &b.diagnostic))
|
||||||
let mut ix = 0;
|
});
|
||||||
'outer: while ix < diagnostics.len() {
|
|
||||||
let entry = &mut diagnostics[ix];
|
let mut sanitized_diagnostics = Vec::new();
|
||||||
|
let mut edits_since_save = content.edits_since::<T>(&self.saved_version).peekable();
|
||||||
|
let mut last_edit_old_end = T::default();
|
||||||
|
let mut last_edit_new_end = T::default();
|
||||||
|
'outer: for entry in diagnostics {
|
||||||
let mut start = entry.range.start;
|
let mut start = entry.range.start;
|
||||||
let mut end = entry.range.end;
|
let mut end = entry.range.end;
|
||||||
|
|
||||||
|
@ -798,7 +804,6 @@ impl Buffer {
|
||||||
last_edit_new_end = edit.new.end;
|
last_edit_new_end = edit.new.end;
|
||||||
edits_since_save.next();
|
edits_since_save.next();
|
||||||
} else if edit.old.start <= end && edit.old.end >= start {
|
} else if edit.old.start <= end && edit.old.end >= start {
|
||||||
diagnostics.remove(ix);
|
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -809,23 +814,26 @@ impl Buffer {
|
||||||
end = last_edit_new_end + (end - last_edit_old_end);
|
end = last_edit_new_end + (end - last_edit_old_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.range = content.clip_point_utf16(start, Bias::Left)
|
let range = start.clip(Bias::Left, content)..end.clip(Bias::Right, content);
|
||||||
..content.clip_point_utf16(end, Bias::Right);
|
let mut range = range.start.to_point(content)..range.end.to_point(content);
|
||||||
|
|
||||||
// Expand empty ranges by one character
|
// Expand empty ranges by one character
|
||||||
if entry.range.start == entry.range.end {
|
if range.start == range.end {
|
||||||
entry.range.end.column += 1;
|
range.end.column += 1;
|
||||||
entry.range.end = content.clip_point_utf16(entry.range.end, Bias::Right);
|
range.end = content.clip_point(range.end, Bias::Right);
|
||||||
if entry.range.start == entry.range.end && entry.range.end.column > 0 {
|
if range.start == range.end && range.end.column > 0 {
|
||||||
entry.range.start.column -= 1;
|
range.start.column -= 1;
|
||||||
entry.range.start = content.clip_point_utf16(entry.range.start, Bias::Left);
|
range.start = content.clip_point(range.start, Bias::Left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ix += 1;
|
|
||||||
|
sanitized_diagnostics.push(DiagnosticEntry {
|
||||||
|
range,
|
||||||
|
diagnostic: entry.diagnostic,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
drop(edits_since_save);
|
drop(edits_since_save);
|
||||||
|
|
||||||
let set = DiagnosticSet::new(provider_name, diagnostics, content);
|
let set = DiagnosticSet::new(provider_name, sanitized_diagnostics, content);
|
||||||
self.apply_diagnostic_update(set.clone(), cx);
|
self.apply_diagnostic_update(set.clone(), cx);
|
||||||
Ok(Operation::UpdateDiagnostics {
|
Ok(Operation::UpdateDiagnostics {
|
||||||
provider_name: set.provider_name().to_string(),
|
provider_name: set.provider_name().to_string(),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use sum_tree::{self, Bias, SumTree};
|
use sum_tree::{self, Bias, SumTree};
|
||||||
use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
|
use text::{Anchor, FromAnchor, Point, ToOffset};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DiagnosticSet {
|
pub struct DiagnosticSet {
|
||||||
|
@ -56,7 +56,7 @@ impl DiagnosticSet {
|
||||||
|
|
||||||
pub fn new<I>(provider_name: Arc<str>, iter: I, buffer: &text::BufferSnapshot) -> Self
|
pub fn new<I>(provider_name: Arc<str>, iter: I, buffer: &text::BufferSnapshot) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
|
I: IntoIterator<Item = DiagnosticEntry<Point>>,
|
||||||
{
|
{
|
||||||
let mut entries = iter.into_iter().collect::<Vec<_>>();
|
let mut entries = iter.into_iter().collect::<Vec<_>>();
|
||||||
entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end)));
|
entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end)));
|
||||||
|
|
|
@ -65,9 +65,7 @@ pub struct BracketPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait DiagnosticSource: 'static + Send + Sync {
|
pub trait DiagnosticProvider: 'static + Send + Sync {
|
||||||
fn name(&self) -> Arc<str>;
|
|
||||||
|
|
||||||
async fn diagnose(
|
async fn diagnose(
|
||||||
&self,
|
&self,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
|
@ -77,7 +75,7 @@ pub trait DiagnosticSource: 'static + Send + Sync {
|
||||||
pub struct Language {
|
pub struct Language {
|
||||||
pub(crate) config: LanguageConfig,
|
pub(crate) config: LanguageConfig,
|
||||||
pub(crate) grammar: Option<Arc<Grammar>>,
|
pub(crate) grammar: Option<Arc<Grammar>>,
|
||||||
pub(crate) diagnostic_source: Option<Arc<dyn DiagnosticSource>>,
|
pub(crate) diagnostic_provider: Option<Arc<dyn DiagnosticProvider>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Grammar {
|
pub struct Grammar {
|
||||||
|
@ -142,7 +140,7 @@ impl Language {
|
||||||
highlight_map: Default::default(),
|
highlight_map: Default::default(),
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
diagnostic_source: None,
|
diagnostic_provider: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +174,8 @@ impl Language {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_diagnostic_source(mut self, source: impl DiagnosticSource) -> Self {
|
pub fn with_diagnostic_provider(mut self, source: impl DiagnosticProvider) -> Self {
|
||||||
self.diagnostic_source = Some(Arc::new(source));
|
self.diagnostic_provider = Some(Arc::new(source));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,8 +212,8 @@ impl Language {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostic_source(&self) -> Option<&Arc<dyn DiagnosticSource>> {
|
pub fn diagnostic_provider(&self) -> Option<&Arc<dyn DiagnosticProvider>> {
|
||||||
self.diagnostic_source.as_ref()
|
self.diagnostic_provider.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disk_based_diagnostic_sources(&self) -> Option<&HashSet<String>> {
|
pub fn disk_based_diagnostic_sources(&self) -> Option<&HashSet<String>> {
|
||||||
|
|
|
@ -507,18 +507,16 @@ impl Project {
|
||||||
for worktree_handle in &self.worktrees {
|
for worktree_handle in &self.worktrees {
|
||||||
if let Some(worktree) = worktree_handle.read(cx).as_local() {
|
if let Some(worktree) = worktree_handle.read(cx).as_local() {
|
||||||
for language in worktree.languages() {
|
for language in worktree.languages() {
|
||||||
if let Some(diagnostic_source) = language.diagnostic_source().cloned() {
|
if let Some(provider) = language.diagnostic_provider().cloned() {
|
||||||
let worktree_path = worktree.abs_path().clone();
|
let worktree_path = worktree.abs_path().clone();
|
||||||
let worktree_handle = worktree_handle.downgrade();
|
let worktree_handle = worktree_handle.downgrade();
|
||||||
cx.spawn_weak(|_, mut cx| async move {
|
cx.spawn_weak(|_, mut cx| async move {
|
||||||
let diagnostics =
|
let diagnostics = provider.diagnose(worktree_path).await.log_err()?;
|
||||||
diagnostic_source.diagnose(worktree_path).await.log_err()?;
|
|
||||||
let worktree_handle = worktree_handle.upgrade(&cx)?;
|
let worktree_handle = worktree_handle.upgrade(&cx)?;
|
||||||
worktree_handle.update(&mut cx, |worktree, cx| {
|
worktree_handle.update(&mut cx, |worktree, cx| {
|
||||||
for (path, diagnostics) in diagnostics {
|
for (path, diagnostics) in diagnostics {
|
||||||
worktree
|
worktree
|
||||||
.update_offset_diagnostics(
|
.update_diagnostics_from_provider(
|
||||||
diagnostic_source.name(),
|
|
||||||
path.into(),
|
path.into(),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -50,6 +50,8 @@ use util::{post_inc, ResultExt, TryFutureExt};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
|
static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
|
||||||
|
static ref DIAGNOSTIC_PROVIDER_NAME: Arc<str> = Arc::from("diagnostic_source");
|
||||||
|
static ref LSP_PROVIDER_NAME: Arc<str> = Arc::from("lsp");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -672,9 +674,8 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_lsp_diagnostics(
|
pub fn update_diagnostics_from_lsp(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider_name: Arc<str>,
|
|
||||||
mut params: lsp::PublishDiagnosticsParams,
|
mut params: lsp::PublishDiagnosticsParams,
|
||||||
disk_based_sources: &HashSet<String>,
|
disk_based_sources: &HashSet<String>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
|
@ -743,18 +744,42 @@ impl Worktree {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.update_point_utf16_diagnostics(
|
let this = self.as_local_mut().unwrap();
|
||||||
provider_name,
|
for buffer in this.open_buffers.values() {
|
||||||
worktree_path,
|
if let Some(buffer) = buffer.upgrade(cx) {
|
||||||
params.version,
|
if buffer
|
||||||
diagnostics,
|
.read(cx)
|
||||||
cx,
|
.file()
|
||||||
)
|
.map_or(false, |file| *file.path() == worktree_path)
|
||||||
|
{
|
||||||
|
let (remote_id, operation) = buffer.update(cx, |buffer, cx| {
|
||||||
|
(
|
||||||
|
buffer.remote_id(),
|
||||||
|
buffer.update_diagnostics(
|
||||||
|
LSP_PROVIDER_NAME.clone(),
|
||||||
|
params.version,
|
||||||
|
diagnostics.clone(),
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
self.send_buffer_update(remote_id, operation?, cx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let this = self.as_local_mut().unwrap();
|
||||||
|
this.diagnostic_summaries
|
||||||
|
.insert(worktree_path.clone(), DiagnosticSummary::new(&diagnostics));
|
||||||
|
this.lsp_diagnostics
|
||||||
|
.insert(worktree_path.clone(), diagnostics);
|
||||||
|
cx.emit(Event::DiagnosticsUpdated(worktree_path.clone()));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_offset_diagnostics(
|
pub fn update_diagnostics_from_provider(
|
||||||
&mut self,
|
&mut self,
|
||||||
provider_name: Arc<str>,
|
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
diagnostics: Vec<DiagnosticEntry<usize>>,
|
diagnostics: Vec<DiagnosticEntry<usize>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
|
@ -771,56 +796,8 @@ impl Worktree {
|
||||||
(
|
(
|
||||||
buffer.remote_id(),
|
buffer.remote_id(),
|
||||||
buffer.update_diagnostics(
|
buffer.update_diagnostics(
|
||||||
provider_name,
|
DIAGNOSTIC_PROVIDER_NAME.clone(),
|
||||||
None,
|
None,
|
||||||
diagnostics
|
|
||||||
.iter()
|
|
||||||
.map(|entry| DiagnosticEntry {
|
|
||||||
range: buffer.offset_to_point_utf16(entry.range.start)
|
|
||||||
..buffer.offset_to_point_utf16(entry.range.end),
|
|
||||||
diagnostic: entry.diagnostic.clone(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
cx,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
self.send_buffer_update(remote_id, operation?, cx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let this = self.as_local_mut().unwrap();
|
|
||||||
this.diagnostic_summaries
|
|
||||||
.insert(path.clone(), DiagnosticSummary::new(&diagnostics));
|
|
||||||
this.offset_diagnostics.insert(path.clone(), diagnostics);
|
|
||||||
cx.emit(Event::DiagnosticsUpdated(path.clone()));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_point_utf16_diagnostics(
|
|
||||||
&mut self,
|
|
||||||
provider_name: Arc<str>,
|
|
||||||
path: Arc<Path>,
|
|
||||||
version: Option<i32>,
|
|
||||||
diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
|
|
||||||
cx: &mut ModelContext<Worktree>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let this = self.as_local_mut().unwrap();
|
|
||||||
for buffer in this.open_buffers.values() {
|
|
||||||
if let Some(buffer) = buffer.upgrade(cx) {
|
|
||||||
if buffer
|
|
||||||
.read(cx)
|
|
||||||
.file()
|
|
||||||
.map_or(false, |file| *file.path() == path)
|
|
||||||
{
|
|
||||||
let (remote_id, operation) = buffer.update(cx, |buffer, cx| {
|
|
||||||
(
|
|
||||||
buffer.remote_id(),
|
|
||||||
buffer.update_diagnostics(
|
|
||||||
provider_name,
|
|
||||||
version,
|
|
||||||
diagnostics.clone(),
|
diagnostics.clone(),
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
|
@ -835,26 +812,11 @@ impl Worktree {
|
||||||
let this = self.as_local_mut().unwrap();
|
let this = self.as_local_mut().unwrap();
|
||||||
this.diagnostic_summaries
|
this.diagnostic_summaries
|
||||||
.insert(path.clone(), DiagnosticSummary::new(&diagnostics));
|
.insert(path.clone(), DiagnosticSummary::new(&diagnostics));
|
||||||
this.point_utf16_diagnostics
|
this.provider_diagnostics.insert(path.clone(), diagnostics);
|
||||||
.insert(path.clone(), diagnostics);
|
|
||||||
cx.emit(Event::DiagnosticsUpdated(path.clone()));
|
cx.emit(Event::DiagnosticsUpdated(path.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_diagnostics(
|
|
||||||
diagnostics: &[DiagnosticEntry<usize>],
|
|
||||||
buffer: &Buffer,
|
|
||||||
) -> Vec<DiagnosticEntry<PointUtf16>> {
|
|
||||||
diagnostics
|
|
||||||
.iter()
|
|
||||||
.map(|entry| DiagnosticEntry {
|
|
||||||
range: buffer.offset_to_point_utf16(entry.range.start)
|
|
||||||
..buffer.offset_to_point_utf16(entry.range.end),
|
|
||||||
diagnostic: entry.diagnostic.clone(),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_buffer_update(
|
fn send_buffer_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer_id: u64,
|
buffer_id: u64,
|
||||||
|
@ -925,8 +887,8 @@ pub struct LocalWorktree {
|
||||||
loading_buffers: LoadingBuffers,
|
loading_buffers: LoadingBuffers,
|
||||||
open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
|
open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
|
||||||
shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
|
shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
|
||||||
point_utf16_diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<PointUtf16>>>,
|
lsp_diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<PointUtf16>>>,
|
||||||
offset_diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<usize>>>,
|
provider_diagnostics: HashMap<Arc<Path>, Vec<DiagnosticEntry<usize>>>,
|
||||||
diagnostic_summaries: BTreeMap<Arc<Path>, DiagnosticSummary>,
|
diagnostic_summaries: BTreeMap<Arc<Path>, DiagnosticSummary>,
|
||||||
queued_operations: Vec<(u64, Operation)>,
|
queued_operations: Vec<(u64, Operation)>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
@ -1034,8 +996,8 @@ impl LocalWorktree {
|
||||||
loading_buffers: Default::default(),
|
loading_buffers: Default::default(),
|
||||||
open_buffers: Default::default(),
|
open_buffers: Default::default(),
|
||||||
shared_buffers: Default::default(),
|
shared_buffers: Default::default(),
|
||||||
point_utf16_diagnostics: Default::default(),
|
lsp_diagnostics: Default::default(),
|
||||||
offset_diagnostics: Default::default(),
|
provider_diagnostics: Default::default(),
|
||||||
diagnostic_summaries: Default::default(),
|
diagnostic_summaries: Default::default(),
|
||||||
queued_operations: Default::default(),
|
queued_operations: Default::default(),
|
||||||
language_registry: languages,
|
language_registry: languages,
|
||||||
|
@ -1104,8 +1066,6 @@ impl LocalWorktree {
|
||||||
return Some(server.clone());
|
return Some(server.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let name: Arc<str> = language.name().into();
|
|
||||||
|
|
||||||
if let Some(language_server) = language
|
if let Some(language_server) = language
|
||||||
.start_server(self.abs_path(), cx)
|
.start_server(self.abs_path(), cx)
|
||||||
.log_err()
|
.log_err()
|
||||||
|
@ -1121,23 +1081,15 @@ impl LocalWorktree {
|
||||||
smol::block_on(diagnostics_tx.send(params)).ok();
|
smol::block_on(diagnostics_tx.send(params)).ok();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
cx.spawn_weak(|this, mut cx| {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
let provider_name = name.clone();
|
while let Ok(diagnostics) = diagnostics_rx.recv().await {
|
||||||
async move {
|
if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
|
||||||
while let Ok(diagnostics) = diagnostics_rx.recv().await {
|
handle.update(&mut cx, |this, cx| {
|
||||||
if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
|
this.update_diagnostics_from_lsp(diagnostics, &disk_based_sources, cx)
|
||||||
handle.update(&mut cx, |this, cx| {
|
|
||||||
this.update_lsp_diagnostics(
|
|
||||||
provider_name.clone(),
|
|
||||||
diagnostics,
|
|
||||||
&disk_based_sources,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.log_err();
|
.log_err();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1184,11 +1136,11 @@ impl LocalWorktree {
|
||||||
.update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
|
.update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (point_utf16_diagnostics, offset_diagnostics, language, language_server) = this
|
let (lsp_diagnostics, provider_diagnostics, language, language_server) =
|
||||||
.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
let this = this.as_local_mut().unwrap();
|
let this = this.as_local_mut().unwrap();
|
||||||
let point_utf16_diagnostics = this.point_utf16_diagnostics.remove(&path);
|
let lsp_diagnostics = this.lsp_diagnostics.remove(&path);
|
||||||
let offset_diagnostics = this.offset_diagnostics.remove(&path);
|
let provider_diagnostics = this.provider_diagnostics.remove(&path);
|
||||||
let language = this
|
let language = this
|
||||||
.language_registry
|
.language_registry
|
||||||
.select_language(file.full_path())
|
.select_language(file.full_path())
|
||||||
|
@ -1196,31 +1148,32 @@ impl LocalWorktree {
|
||||||
let server = language
|
let server = language
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|language| this.register_language(language, cx));
|
.and_then(|language| this.register_language(language, cx));
|
||||||
(
|
(lsp_diagnostics, provider_diagnostics, language, server)
|
||||||
point_utf16_diagnostics,
|
|
||||||
offset_diagnostics,
|
|
||||||
language,
|
|
||||||
server,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut buffer_operations = Vec::new();
|
||||||
let buffer = cx.add_model(|cx| {
|
let buffer = cx.add_model(|cx| {
|
||||||
let mut buffer = Buffer::from_file(0, contents, Box::new(file), cx);
|
let mut buffer = Buffer::from_file(0, contents, Box::new(file), cx);
|
||||||
buffer.set_language(language, language_server, cx);
|
buffer.set_language(language, language_server, cx);
|
||||||
if let Some(diagnostics) = point_utf16_diagnostics {
|
if let Some(diagnostics) = lsp_diagnostics {
|
||||||
buffer
|
let op = buffer
|
||||||
.update_diagnostics(todo!(), None, diagnostics, cx)
|
.update_diagnostics(LSP_PROVIDER_NAME.clone(), None, diagnostics, cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
buffer_operations.push(op);
|
||||||
}
|
}
|
||||||
if let Some(diagnostics) = offset_diagnostics {
|
if let Some(diagnostics) = provider_diagnostics {
|
||||||
buffer
|
let op = buffer
|
||||||
.update_offset_diagnostics(todo!(), None, diagnostics, cx)
|
.update_diagnostics(DIAGNOSTIC_PROVIDER_NAME.clone(), None, diagnostics, cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
buffer_operations.push(op);
|
||||||
}
|
}
|
||||||
buffer
|
buffer
|
||||||
});
|
});
|
||||||
|
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, cx| {
|
||||||
|
for op in buffer_operations {
|
||||||
|
this.send_buffer_update(buffer.read(cx).remote_id(), op, cx);
|
||||||
|
}
|
||||||
let this = this.as_local_mut().unwrap();
|
let this = this.as_local_mut().unwrap();
|
||||||
this.open_buffers.insert(buffer.id(), buffer.downgrade());
|
this.open_buffers.insert(buffer.id(), buffer.downgrade());
|
||||||
});
|
});
|
||||||
|
@ -3936,7 +3889,7 @@ mod tests {
|
||||||
|
|
||||||
worktree
|
worktree
|
||||||
.update(&mut cx, |tree, cx| {
|
.update(&mut cx, |tree, cx| {
|
||||||
tree.update_lsp_diagnostics("lsp".into(), message, &Default::default(), cx)
|
tree.update_diagnostics_from_lsp(message, &Default::default(), cx)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
|
let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
|
||||||
|
|
|
@ -205,6 +205,19 @@ impl Rope {
|
||||||
.map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot))
|
.map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
|
||||||
|
if point >= self.summary().lines_utf16 {
|
||||||
|
return self.summary().lines;
|
||||||
|
}
|
||||||
|
let mut cursor = self.chunks.cursor::<(PointUtf16, Point)>();
|
||||||
|
cursor.seek(&point, Bias::Left, &());
|
||||||
|
let overshoot = point - cursor.start().0;
|
||||||
|
cursor.start().1
|
||||||
|
+ cursor
|
||||||
|
.item()
|
||||||
|
.map_or(Point::zero(), |chunk| chunk.point_utf16_to_point(overshoot))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clip_offset(&self, mut offset: usize, bias: Bias) -> usize {
|
pub fn clip_offset(&self, mut offset: usize, bias: Bias) -> usize {
|
||||||
let mut cursor = self.chunks.cursor::<usize>();
|
let mut cursor = self.chunks.cursor::<usize>();
|
||||||
cursor.seek(&offset, Bias::Left, &());
|
cursor.seek(&offset, Bias::Left, &());
|
||||||
|
@ -583,6 +596,28 @@ impl Chunk {
|
||||||
offset
|
offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn point_utf16_to_point(&self, target: PointUtf16) -> Point {
|
||||||
|
let mut point = Point::zero();
|
||||||
|
let mut point_utf16 = PointUtf16::zero();
|
||||||
|
for ch in self.0.chars() {
|
||||||
|
if point_utf16 >= target {
|
||||||
|
if point_utf16 > target {
|
||||||
|
panic!("point {:?} is inside of character {:?}", target, ch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == '\n' {
|
||||||
|
point_utf16 += PointUtf16::new(1, 0);
|
||||||
|
point += Point::new(1, 0);
|
||||||
|
} else {
|
||||||
|
point_utf16 += PointUtf16::new(0, ch.len_utf16() as u32);
|
||||||
|
point += Point::new(0, ch.len_utf8() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
point
|
||||||
|
}
|
||||||
|
|
||||||
fn clip_point(&self, target: Point, bias: Bias) -> Point {
|
fn clip_point(&self, target: Point, bias: Bias) -> Point {
|
||||||
for (row, line) in self.0.split('\n').enumerate() {
|
for (row, line) in self.0.split('\n').enumerate() {
|
||||||
if row == target.row as usize {
|
if row == target.row as usize {
|
||||||
|
|
|
@ -1307,6 +1307,10 @@ impl BufferSnapshot {
|
||||||
self.visible_text.point_utf16_to_offset(point)
|
self.visible_text.point_utf16_to_offset(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
|
||||||
|
self.visible_text.point_utf16_to_point(point)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn offset_to_point(&self, offset: usize) -> Point {
|
pub fn offset_to_point(&self, offset: usize) -> Point {
|
||||||
self.visible_text.offset_to_point(offset)
|
self.visible_text.offset_to_point(offset)
|
||||||
}
|
}
|
||||||
|
@ -2045,12 +2049,40 @@ impl ToPoint for usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToPoint for PointUtf16 {
|
||||||
|
fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point {
|
||||||
|
snapshot.point_utf16_to_point(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToPoint for Point {
|
impl ToPoint for Point {
|
||||||
fn to_point<'a>(&self, _: &BufferSnapshot) -> Point {
|
fn to_point<'a>(&self, _: &BufferSnapshot) -> Point {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Clip {
|
||||||
|
fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clip for usize {
|
||||||
|
fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self {
|
||||||
|
snapshot.clip_offset(*self, bias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clip for Point {
|
||||||
|
fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self {
|
||||||
|
snapshot.clip_point(*self, bias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clip for PointUtf16 {
|
||||||
|
fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self {
|
||||||
|
snapshot.clip_point_utf16(*self, bias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FromAnchor {
|
pub trait FromAnchor {
|
||||||
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self;
|
fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue