Detailed changes
@@ -176,12 +176,17 @@ impl<T> AnchorRangeMap<T> {
self.entries.len()
}
- pub fn from_raw(version: clock::Global, entries: Vec<(Range<(FullOffset, Bias)>, T)>) -> Self {
+ pub fn from_full_offset_ranges(
+ version: clock::Global,
+ entries: Vec<(Range<(FullOffset, Bias)>, T)>,
+ ) -> Self {
Self { version, entries }
}
- pub fn raw_entries(&self) -> &[(Range<(FullOffset, Bias)>, T)] {
- &self.entries
+ pub fn full_offset_ranges(&self) -> impl Iterator<Item = (Range<FullOffset>, &T)> {
+ self.entries
+ .iter()
+ .map(|(range, value)| (range.start.0..range.end.0, value))
}
pub fn point_ranges<'a>(
@@ -270,6 +275,10 @@ impl<T: Clone> Default for AnchorRangeMultimap<T> {
}
impl<T: Clone> AnchorRangeMultimap<T> {
+ pub fn version(&self) -> &clock::Global {
+ &self.version
+ }
+
pub fn intersecting_ranges<'a, I, O>(
&'a self,
range: Range<I>,
@@ -336,6 +345,35 @@ impl<T: Clone> AnchorRangeMultimap<T> {
}
})
}
+
+ pub fn from_full_offset_ranges(
+ version: clock::Global,
+ start_bias: Bias,
+ end_bias: Bias,
+ entries: impl Iterator<Item = (Range<FullOffset>, T)>,
+ ) -> Self {
+ Self {
+ version,
+ start_bias,
+ end_bias,
+ entries: SumTree::from_iter(
+ entries.map(|(range, value)| AnchorRangeMultimapEntry {
+ range: FullOffsetRange {
+ start: range.start,
+ end: range.end,
+ },
+ value,
+ }),
+ &(),
+ ),
+ }
+ }
+
+ pub fn full_offset_ranges(&self) -> impl Iterator<Item = (Range<FullOffset>, &T)> {
+ self.entries
+ .cursor::<()>()
+ .map(|entry| (entry.range.start..entry.range.end, &entry.value))
+ }
}
impl<T: Clone> sum_tree::Item for AnchorRangeMultimapEntry<T> {
@@ -9,7 +9,7 @@ pub use self::{
language::{BracketPair, Language, LanguageConfig, LanguageRegistry},
};
use anyhow::{anyhow, Result};
-pub use buffer::{Buffer as TextBuffer, *};
+pub use buffer::{Buffer as TextBuffer, Operation as _, *};
use clock::ReplicaId;
use futures::FutureExt as _;
use gpui::{AppContext, Entity, ModelContext, MutableAppContext, Task};
@@ -99,6 +99,12 @@ struct LanguageServerSnapshot {
path: Arc<Path>,
}
+#[derive(Clone)]
+pub enum Operation {
+ Buffer(buffer::Operation),
+ UpdateDiagnostics(AnchorRangeMultimap<Diagnostic>),
+}
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Event {
Edited,
@@ -256,7 +262,7 @@ impl Buffer {
let ops = message
.history
.into_iter()
- .map(|op| Operation::Edit(proto::deserialize_edit_operation(op)));
+ .map(|op| buffer::Operation::Edit(proto::deserialize_edit_operation(op)));
buffer.apply_ops(ops)?;
for set in message.selections {
let set = proto::deserialize_selection_set(set);
@@ -278,6 +284,7 @@ impl Buffer {
.selection_sets()
.map(|(_, set)| proto::serialize_selection_set(set))
.collect(),
+ diagnostics: Some(proto::serialize_diagnostics(&self.diagnostics)),
}
}
@@ -761,6 +768,7 @@ impl Buffer {
}
self.diagnostics_update_count += 1;
+ self.send_operation(Operation::UpdateDiagnostics(self.diagnostics.clone()), cx);
cx.notify();
Ok(())
}
@@ -1240,7 +1248,7 @@ impl Buffer {
}
self.end_transaction(None, cx).unwrap();
- self.send_operation(Operation::Edit(edit), cx);
+ self.send_operation(Operation::Buffer(buffer::Operation::Edit(edit)), cx);
}
fn did_edit(
@@ -1269,10 +1277,10 @@ impl Buffer {
cx: &mut ModelContext<Self>,
) -> SelectionSetId {
let operation = self.text.add_selection_set(selections);
- if let Operation::UpdateSelections { set_id, .. } = &operation {
+ if let buffer::Operation::UpdateSelections { set_id, .. } = &operation {
let set_id = *set_id;
cx.notify();
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
set_id
} else {
unreachable!()
@@ -1287,7 +1295,7 @@ impl Buffer {
) -> Result<()> {
let operation = self.text.update_selection_set(set_id, selections)?;
cx.notify();
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
@@ -1297,7 +1305,7 @@ impl Buffer {
cx: &mut ModelContext<Self>,
) -> Result<()> {
let operation = self.text.set_active_selection_set(set_id)?;
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
@@ -1308,7 +1316,7 @@ impl Buffer {
) -> Result<()> {
let operation = self.text.remove_selection_set(set_id)?;
cx.notify();
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
@@ -1320,7 +1328,17 @@ impl Buffer {
self.pending_autoindent.take();
let was_dirty = self.is_dirty();
let old_version = self.version.clone();
- self.text.apply_ops(ops)?;
+ let buffer_ops = ops
+ .into_iter()
+ .filter_map(|op| match op {
+ Operation::Buffer(op) => Some(op),
+ Operation::UpdateDiagnostics(diagnostics) => {
+ self.apply_diagnostic_update(diagnostics, cx);
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ self.text.apply_ops(buffer_ops)?;
self.did_edit(&old_version, was_dirty, cx);
// Notify independently of whether the buffer was edited as the operations could include a
// selection update.
@@ -1328,6 +1346,15 @@ impl Buffer {
Ok(())
}
+ fn apply_diagnostic_update(
+ &mut self,
+ diagnostics: AnchorRangeMultimap<Diagnostic>,
+ cx: &mut ModelContext<Self>,
+ ) {
+ self.diagnostics = diagnostics;
+ cx.notify();
+ }
+
#[cfg(not(test))]
pub fn send_operation(&mut self, operation: Operation, cx: &mut ModelContext<Self>) {
if let Some(file) = &self.file {
@@ -1350,7 +1377,7 @@ impl Buffer {
let old_version = self.version.clone();
for operation in self.text.undo() {
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
}
self.did_edit(&old_version, was_dirty, cx);
@@ -1361,7 +1388,7 @@ impl Buffer {
let old_version = self.version.clone();
for operation in self.text.redo() {
- self.send_operation(operation, cx);
+ self.send_operation(Operation::Buffer(operation), cx);
}
self.did_edit(&old_version, was_dirty, cx);
@@ -1,8 +1,12 @@
use std::sync::Arc;
+use crate::Diagnostic;
+
+use super::Operation;
use anyhow::{anyhow, Result};
use buffer::*;
use clock::ReplicaId;
+use lsp::DiagnosticSeverity;
use rpc::proto;
pub use proto::Buffer;
@@ -10,13 +14,13 @@ pub use proto::Buffer;
pub fn serialize_operation(operation: &Operation) -> proto::Operation {
proto::Operation {
variant: Some(match operation {
- Operation::Edit(edit) => {
+ Operation::Buffer(buffer::Operation::Edit(edit)) => {
proto::operation::Variant::Edit(serialize_edit_operation(edit))
}
- Operation::Undo {
+ Operation::Buffer(buffer::Operation::Undo {
undo,
lamport_timestamp,
- } => proto::operation::Variant::Undo(proto::operation::Undo {
+ }) => proto::operation::Variant::Undo(proto::operation::Undo {
replica_id: undo.id.replica_id as u32,
local_timestamp: undo.id.value,
lamport_timestamp: lamport_timestamp.value,
@@ -39,44 +43,46 @@ pub fn serialize_operation(operation: &Operation) -> proto::Operation {
.collect(),
version: From::from(&undo.version),
}),
- Operation::UpdateSelections {
+ Operation::Buffer(buffer::Operation::UpdateSelections {
set_id,
selections,
lamport_timestamp,
- } => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
+ }) => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
replica_id: set_id.replica_id as u32,
local_timestamp: set_id.value,
lamport_timestamp: lamport_timestamp.value,
version: selections.version().into(),
selections: selections
- .raw_entries()
- .iter()
+ .full_offset_ranges()
.map(|(range, state)| proto::Selection {
id: state.id as u64,
- start: range.start.0 .0 as u64,
- end: range.end.0 .0 as u64,
+ start: range.start.0 as u64,
+ end: range.end.0 as u64,
reversed: state.reversed,
})
.collect(),
}),
- Operation::RemoveSelections {
+ Operation::Buffer(buffer::Operation::RemoveSelections {
set_id,
lamport_timestamp,
- } => proto::operation::Variant::RemoveSelections(proto::operation::RemoveSelections {
+ }) => proto::operation::Variant::RemoveSelections(proto::operation::RemoveSelections {
replica_id: set_id.replica_id as u32,
local_timestamp: set_id.value,
lamport_timestamp: lamport_timestamp.value,
}),
- Operation::SetActiveSelections {
+ Operation::Buffer(buffer::Operation::SetActiveSelections {
set_id,
lamport_timestamp,
- } => proto::operation::Variant::SetActiveSelections(
+ }) => proto::operation::Variant::SetActiveSelections(
proto::operation::SetActiveSelections {
replica_id: lamport_timestamp.replica_id as u32,
local_timestamp: set_id.map(|set_id| set_id.value),
lamport_timestamp: lamport_timestamp.value,
},
),
+ Operation::UpdateDiagnostics(diagnostic_set) => {
+ proto::operation::Variant::UpdateDiagnostics(serialize_diagnostics(diagnostic_set))
+ }
}),
}
}
@@ -102,24 +108,44 @@ pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::
pub fn serialize_selection_set(set: &SelectionSet) -> proto::SelectionSet {
let version = set.selections.version();
- let entries = set.selections.raw_entries();
+ let entries = set.selections.full_offset_ranges();
proto::SelectionSet {
replica_id: set.id.replica_id as u32,
lamport_timestamp: set.id.value as u32,
is_active: set.active,
version: version.into(),
selections: entries
- .iter()
.map(|(range, state)| proto::Selection {
id: state.id as u64,
- start: range.start.0 .0 as u64,
- end: range.end.0 .0 as u64,
+ start: range.start.0 as u64,
+ end: range.end.0 as u64,
reversed: state.reversed,
})
.collect(),
}
}
+pub fn serialize_diagnostics(map: &AnchorRangeMultimap<Diagnostic>) -> proto::DiagnosticSet {
+ proto::DiagnosticSet {
+ version: map.version().into(),
+ diagnostics: map
+ .full_offset_ranges()
+ .map(|(range, diagnostic)| proto::Diagnostic {
+ start: range.start.0 as u64,
+ end: range.end.0 as u64,
+ message: diagnostic.message.clone(),
+ severity: match diagnostic.severity {
+ DiagnosticSeverity::ERROR => proto::diagnostic::Severity::Error,
+ DiagnosticSeverity::WARNING => proto::diagnostic::Severity::Warning,
+ DiagnosticSeverity::INFORMATION => proto::diagnostic::Severity::Information,
+ DiagnosticSeverity::HINT => proto::diagnostic::Severity::Hint,
+ _ => proto::diagnostic::Severity::None,
+ } as i32,
+ })
+ .collect(),
+ }
+}
+
pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
Ok(
match message
@@ -127,9 +153,9 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
.ok_or_else(|| anyhow!("missing operation variant"))?
{
proto::operation::Variant::Edit(edit) => {
- Operation::Edit(deserialize_edit_operation(edit))
+ Operation::Buffer(buffer::Operation::Edit(deserialize_edit_operation(edit)))
}
- proto::operation::Variant::Undo(undo) => Operation::Undo {
+ proto::operation::Variant::Undo(undo) => Operation::Buffer(buffer::Operation::Undo {
lamport_timestamp: clock::Lamport {
replica_id: undo.replica_id as ReplicaId,
value: undo.lamport_timestamp,
@@ -159,7 +185,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
.collect(),
version: undo.version.into(),
},
- },
+ }),
proto::operation::Variant::UpdateSelections(message) => {
let version = message.version.into();
let entries = message
@@ -176,9 +202,9 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
(range, state)
})
.collect();
- let selections = AnchorRangeMap::from_raw(version, entries);
+ let selections = AnchorRangeMap::from_full_offset_ranges(version, entries);
- Operation::UpdateSelections {
+ Operation::Buffer(buffer::Operation::UpdateSelections {
set_id: clock::Lamport {
replica_id: message.replica_id as ReplicaId,
value: message.local_timestamp,
@@ -188,20 +214,22 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
value: message.lamport_timestamp,
},
selections: Arc::from(selections),
- }
+ })
+ }
+ proto::operation::Variant::RemoveSelections(message) => {
+ Operation::Buffer(buffer::Operation::RemoveSelections {
+ set_id: clock::Lamport {
+ replica_id: message.replica_id as ReplicaId,
+ value: message.local_timestamp,
+ },
+ lamport_timestamp: clock::Lamport {
+ replica_id: message.replica_id as ReplicaId,
+ value: message.lamport_timestamp,
+ },
+ })
}
- proto::operation::Variant::RemoveSelections(message) => Operation::RemoveSelections {
- set_id: clock::Lamport {
- replica_id: message.replica_id as ReplicaId,
- value: message.local_timestamp,
- },
- lamport_timestamp: clock::Lamport {
- replica_id: message.replica_id as ReplicaId,
- value: message.lamport_timestamp,
- },
- },
proto::operation::Variant::SetActiveSelections(message) => {
- Operation::SetActiveSelections {
+ Operation::Buffer(buffer::Operation::SetActiveSelections {
set_id: message.local_timestamp.map(|value| clock::Lamport {
replica_id: message.replica_id as ReplicaId,
value,
@@ -210,7 +238,10 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
replica_id: message.replica_id as ReplicaId,
value: message.lamport_timestamp,
},
- }
+ })
+ }
+ proto::operation::Variant::UpdateDiagnostics(message) => {
+ Operation::UpdateDiagnostics(deserialize_diagnostics(message))
}
},
)
@@ -241,7 +272,7 @@ pub fn deserialize_selection_set(set: proto::SelectionSet) -> SelectionSet {
value: set.lamport_timestamp,
},
active: set.is_active,
- selections: Arc::new(AnchorRangeMap::from_raw(
+ selections: Arc::new(AnchorRangeMap::from_full_offset_ranges(
set.version.into(),
set.selections
.into_iter()
@@ -259,3 +290,26 @@ pub fn deserialize_selection_set(set: proto::SelectionSet) -> SelectionSet {
)),
}
}
+
+pub fn deserialize_diagnostics(message: proto::DiagnosticSet) -> AnchorRangeMultimap<Diagnostic> {
+ AnchorRangeMultimap::from_full_offset_ranges(
+ message.version.into(),
+ Bias::Left,
+ Bias::Right,
+ message.diagnostics.into_iter().filter_map(|diagnostic| {
+ Some((
+ FullOffset(diagnostic.start as usize)..FullOffset(diagnostic.end as usize),
+ Diagnostic {
+ severity: match proto::diagnostic::Severity::from_i32(diagnostic.severity)? {
+ proto::diagnostic::Severity::Error => DiagnosticSeverity::ERROR,
+ proto::diagnostic::Severity::Warning => DiagnosticSeverity::WARNING,
+ proto::diagnostic::Severity::Information => DiagnosticSeverity::INFORMATION,
+ proto::diagnostic::Severity::Hint => DiagnosticSeverity::HINT,
+ proto::diagnostic::Severity::None => return None,
+ },
+ message: diagnostic.message,
+ },
+ ))
+ }),
+ )
+}
@@ -228,6 +228,7 @@ message Buffer {
string content = 2;
repeated Operation.Edit history = 3;
repeated SelectionSet selections = 4;
+ DiagnosticSet diagnostics = 5;
}
message SelectionSet {
@@ -245,6 +246,27 @@ message Selection {
bool reversed = 4;
}
+message DiagnosticSet {
+ repeated VectorClockEntry version = 1;
+ repeated Diagnostic diagnostics = 2;
+}
+
+message Diagnostic {
+ uint64 start = 1;
+ uint64 end = 2;
+ Severity severity = 3;
+ string message = 4;
+ enum Severity {
+ None = 0;
+ Error = 1;
+ Warning = 2;
+ Information = 3;
+ Hint = 4;
+ }
+}
+
+
+
message Operation {
oneof variant {
Edit edit = 1;
@@ -252,6 +274,7 @@ message Operation {
UpdateSelections update_selections = 3;
RemoveSelections remove_selections = 4;
SetActiveSelections set_active_selections = 5;
+ DiagnosticSet update_diagnostics = 6;
}
message Edit {
@@ -398,6 +398,7 @@ mod tests {
content: "path/one content".to_string(),
history: vec![],
selections: vec![],
+ diagnostics: None,
}),
}
);
@@ -419,6 +420,7 @@ mod tests {
content: "path/two content".to_string(),
history: vec![],
selections: vec![],
+ diagnostics: None,
}),
}
);
@@ -449,6 +451,7 @@ mod tests {
content: "path/one content".to_string(),
history: vec![],
selections: vec![],
+ diagnostics: None,
}),
}
}
@@ -460,6 +463,7 @@ mod tests {
content: "path/two content".to_string(),
history: vec![],
selections: vec![],
+ diagnostics: None,
}),
}
}
@@ -2,5 +2,5 @@
set -e
-cd server
+cd crates/server
cargo run $@