pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, proto, BracketPair, Grammar, Language, LanguageConfig, LanguageRegistry, LanguageServerConfig, PLAIN_TEXT,};use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, outline::OutlineItem, range_from_lsp, Outline,};use anyhow::{anyhow, Result};use clock::ReplicaId;use futures::FutureExt as _;use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task};use lazy_static::lazy_static;use lsp::LanguageServer;use parking_lot::Mutex;use postage::{prelude::Stream, sink::Sink, watch};use similar::{ChangeTag, TextDiff};use smol::future::yield_now;use std::{ any::Any, cell::RefCell, cmp::{self, Ordering}, collections::{BTreeMap, HashMap}, ffi::OsString, future::Future, iter::{Iterator, Peekable}, ops::{Deref, DerefMut, Range, Sub}, path::{Path, PathBuf}, str, sync::Arc, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, vec,};use sum_tree::TreeMap;use text::{operation_queue::OperationQueue, rope::TextDimension};pub use text::{Buffer as TextBuffer, Operation as _, *};use theme::SyntaxTheme;use tree_sitter::{InputEdit, Parser, QueryCursor, Tree};use util::{post_inc, TryFutureExt as _}; #[cfg(any(test, feature = "test-support"))]pub use tree_sitter_rust; pub use lsp::DiagnosticSeverity; thread_local! { static PARSER: RefCell<Parser> = RefCell::new(Parser::new());} lazy_static! { static ref QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Default::default();} // TODO - Make this configurableconst INDENT_SIZE: u32 = 4; pub struct Buffer { text: TextBuffer, file: Option<Box<dyn File>>, saved_version: clock::Global, saved_mtime: SystemTime, language: Option<Arc<Language>>, autoindent_requests: Vec<Arc<AutoindentRequest>>, pending_autoindent: Option<Task<()>>, sync_parse_timeout: Duration, syntax_tree: Mutex<Option<SyntaxTree>>, parsing_in_background: bool, parse_count: usize, diagnostics: DiagnosticSet, remote_selections: TreeMap<ReplicaId, SelectionSet>, selections_update_count: usize, diagnostics_update_count: usize, language_server: Option<LanguageServerState>, deferred_ops: OperationQueue<Operation>, #[cfg(test)] pub(crate) operations: Vec<Operation>,} pub struct BufferSnapshot { text: text::BufferSnapshot, tree: Option<Tree>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, remote_selections: TreeMap<ReplicaId, SelectionSet>, selections_update_count: usize, is_parsing: bool, language: Option<Arc<Language>>, parse_count: usize,} #[derive(Clone, Debug)]struct SelectionSet { selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport,} #[derive(Clone, Debug, PartialEq, Eq)]pub struct GroupId { source: Arc<str>, id: usize,} #[derive(Clone, Debug, PartialEq, Eq)]pub struct Diagnostic { pub code: Option<String>, pub severity: DiagnosticSeverity, pub message: String, pub group_id: usize, pub is_valid: bool, pub is_primary: bool, pub is_disk_based: bool,} struct LanguageServerState { server: Arc<LanguageServer>, latest_snapshot: watch::Sender<Option<LanguageServerSnapshot>>, pending_snapshots: BTreeMap<usize, LanguageServerSnapshot>, next_version: usize, _maintain_server: Task<Option<()>>,} #[derive(Clone)]struct LanguageServerSnapshot { buffer_snapshot: text::BufferSnapshot, version: usize, path: Arc<Path>,} #[derive(Clone, Debug)]pub enum Operation { Buffer(text::Operation), UpdateDiagnostics { diagnostics: Arc<[DiagnosticEntry<Anchor>]>, lamport_timestamp: clock::Lamport, }, UpdateSelections { replica_id: ReplicaId, selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport, },} #[derive(Clone, Debug, Eq, PartialEq)]pub enum Event { Edited, Dirtied, Saved, FileHandleChanged, Reloaded, Reparsed, DiagnosticsUpdated, Closed,} pub trait File { fn as_local(&self) -> Option<&dyn LocalFile>; fn mtime(&self) -> SystemTime; /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc<Path>; /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &AppContext) -> PathBuf; /// Returns the last component of this handle's absolute path. If this handle refers to the root /// of its worktree, then this method will return the name of the worktree itself. fn file_name(&self, cx: &AppContext) -> OsString; fn is_deleted(&self) -> bool; fn save( &self, buffer_id: u64, text: Rope, version: clock::Global, cx: &mut MutableAppContext, ) -> Task<Result<(clock::Global, SystemTime)>>; fn format_remote(&self, buffer_id: u64, cx: &mut MutableAppContext) -> Option<Task<Result<()>>>; fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext); fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext); fn as_any(&self) -> &dyn Any; fn to_proto(&self) -> rpc::proto::File;} pub trait LocalFile: File { /// Returns the absolute path of this file. fn abs_path(&self, cx: &AppContext) -> PathBuf; fn load(&self, cx: &AppContext) -> Task<Result<String>>; fn buffer_reloaded( &self, buffer_id: u64, version: &clock::Global, mtime: SystemTime, cx: &mut MutableAppContext, );} pub(crate) struct QueryCursorHandle(Option<QueryCursor>); #[derive(Clone)]struct SyntaxTree { tree: Tree, version: clock::Global,} #[derive(Clone)]struct AutoindentRequest { before_edit: BufferSnapshot, edited: Vec<Anchor>, inserted: Option<Vec<Range<Anchor>>>,} #[derive(Debug)]struct IndentSuggestion { basis_row: u32, indent: bool,} struct TextProvider<'a>(&'a Rope); struct BufferChunkHighlights<'a> { captures: tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>, next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>, stack: Vec<(usize, HighlightId)>, highlight_map: HighlightMap, theme: &'a SyntaxTheme, _query_cursor: QueryCursorHandle,} pub struct BufferChunks<'a> { range: Range<usize>, chunks: rope::Chunks<'a>, diagnostic_endpoints: Peekable<vec::IntoIter<DiagnosticEndpoint>>, error_depth: usize, warning_depth: usize, information_depth: usize, hint_depth: usize, highlights: Option<BufferChunkHighlights<'a>>,} #[derive(Clone, Copy, Debug, Default)]pub struct Chunk<'a> { pub text: &'a str, pub highlight_style: Option<HighlightStyle>, pub diagnostic: Option<DiagnosticSeverity>,} pub(crate) struct Diff { base_version: clock::Global, new_text: Arc<str>, changes: Vec<(ChangeTag, usize)>,} #[derive(Clone, Copy)]struct DiagnosticEndpoint { offset: usize, is_start: bool, severity: DiagnosticSeverity,} impl Buffer { pub fn new<T: Into<Arc<str>>>( replica_id: ReplicaId, base_text: T, cx: &mut ModelContext<Self>, ) -> Self { Self::build( TextBuffer::new( replica_id, cx.model_id() as u64, History::new(base_text.into()), ), None, ) } pub fn from_file<T: Into<Arc<str>>>( replica_id: ReplicaId, base_text: T, file: Box<dyn File>, cx: &mut ModelContext<Self>, ) -> Self { Self::build( TextBuffer::new( replica_id, cx.model_id() as u64, History::new(base_text.into()), ), Some(file), ) } pub fn from_proto( replica_id: ReplicaId, message: proto::BufferState, file: Option<Box<dyn File>>, cx: &mut ModelContext<Self>, ) -> Result<Self> { let fragments_len = message.fragments.len(); let buffer = TextBuffer::from_parts( replica_id, message.id, &message.visible_text, &message.deleted_text, message .undo_map .into_iter() .map(proto::deserialize_undo_map_entry), message .fragments .into_iter() .enumerate() .map(|(i, fragment)| { proto::deserialize_buffer_fragment(fragment, i, fragments_len) }), message.lamport_timestamp, From::from(message.version), ); let mut this = Self::build(buffer, file); for selection_set in message.selections { this.remote_selections.insert( selection_set.replica_id as ReplicaId, SelectionSet { selections: proto::deserialize_selections(selection_set.selections), lamport_timestamp: clock::Lamport { replica_id: selection_set.replica_id as ReplicaId, value: selection_set.lamport_timestamp, }, }, ); } let snapshot = this.snapshot(); let entries = proto::deserialize_diagnostics(message.diagnostics); this.apply_diagnostic_update( DiagnosticSet::from_sorted_entries(entries.into_iter().cloned(), &snapshot), cx, ); let deferred_ops = message .deferred_operations .into_iter() .map(proto::deserialize_operation) .collect::<Result<Vec<_>>>()?; this.apply_ops(deferred_ops, cx)?; Ok(this) } pub fn to_proto(&self) -> proto::BufferState { proto::BufferState { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), visible_text: self.text.text(), deleted_text: self.text.deleted_text(), undo_map: self .text .undo_history() .map(proto::serialize_undo_map_entry) .collect(), version: From::from(&self.version), lamport_timestamp: self.lamport_clock.value, fragments: self .text .fragments() .map(proto::serialize_buffer_fragment) .collect(), selections: self .remote_selections .iter() .map(|(replica_id, set)| proto::SelectionSet { replica_id: *replica_id as u32, selections: proto::serialize_selections(&set.selections), lamport_timestamp: set.lamport_timestamp.value, }) .collect(), diagnostics: proto::serialize_diagnostics(self.diagnostics.iter()), deferred_operations: self .deferred_ops .iter() .map(proto::serialize_operation) .chain( self.text .deferred_ops() .map(|op| proto::serialize_operation(&Operation::Buffer(op.clone()))), ) .collect(), } } pub fn with_language(mut self, language: Arc<Language>, cx: &mut ModelContext<Self>) -> Self { self.set_language(Some(language), cx); self } pub fn with_language_server( mut self, server: Arc<LanguageServer>, cx: &mut ModelContext<Self>, ) -> Self { self.set_language_server(Some(server), cx); self } fn build(buffer: TextBuffer, file: Option<Box<dyn File>>) -> Self { let saved_mtime; if let Some(file) = file.as_ref() { saved_mtime = file.mtime(); } else { saved_mtime = UNIX_EPOCH; } Self { saved_mtime, saved_version: buffer.version(), text: buffer, file, syntax_tree: Mutex::new(None), parsing_in_background: false, parse_count: 0, sync_parse_timeout: Duration::from_millis(1), autoindent_requests: Default::default(), pending_autoindent: Default::default(), language: None, remote_selections: Default::default(), selections_update_count: 0, diagnostics: Default::default(), diagnostics_update_count: 0, language_server: None, deferred_ops: OperationQueue::new(), #[cfg(test)] operations: Default::default(), } } pub fn snapshot(&self) -> BufferSnapshot { BufferSnapshot { text: self.text.snapshot(), tree: self.syntax_tree(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, is_parsing: self.parsing_in_background, language: self.language.clone(), parse_count: self.parse_count, selections_update_count: self.selections_update_count, } } pub fn file(&self) -> Option<&dyn File> { self.file.as_deref() } pub fn format(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> { let file = if let Some(file) = self.file.as_ref() { file } else { return Task::ready(Err(anyhow!("buffer has no file"))); }; if let Some(LanguageServerState { server, .. }) = self.language_server.as_ref() { let server = server.clone(); let abs_path = file.as_local().unwrap().abs_path(cx); let version = self.version(); cx.spawn(|this, mut cx| async move { let edits = server .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams { text_document: lsp::TextDocumentIdentifier::new( lsp::Url::from_file_path(&abs_path).unwrap(), ), options: Default::default(), work_done_progress_params: Default::default(), }) .await?; if let Some(edits) = edits { this.update(&mut cx, |this, cx| { if this.version == version { for edit in &edits { let range = range_from_lsp(edit.range); if this.clip_point_utf16(range.start, Bias::Left) != range.start || this.clip_point_utf16(range.end, Bias::Left) != range.end { return Err(anyhow!( "invalid formatting edits received from language server" )); } }Lines 1–500 of 2375. Load more
pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, proto, BracketPair, Grammar, Language, LanguageConfig, LanguageRegistry, LanguageServerConfig, PLAIN_TEXT, }; use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, outline::OutlineItem, range_from_lsp, Outline, }; use anyhow::{anyhow, Result}; use clock::ReplicaId; use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task}; use lazy_static::lazy_static; use lsp::LanguageServer; use parking_lot::Mutex; use postage::{prelude::Stream, sink::Sink, watch}; use similar::{ChangeTag, TextDiff}; use smol::future::yield_now; use std::{ any::Any, cell::RefCell, cmp::{self, Ordering}, collections::{BTreeMap, HashMap}, ffi::OsString, future::Future, iter::{Iterator, Peekable}, ops::{Deref, DerefMut, Range, Sub}, path::{Path, PathBuf}, str, sync::Arc, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, vec, }; use sum_tree::TreeMap; use text::{operation_queue::OperationQueue, rope::TextDimension}; pub use text::{Buffer as TextBuffer, Operation as _, *}; use theme::SyntaxTheme; use tree_sitter::{InputEdit, Parser, QueryCursor, Tree}; use util::{post_inc, TryFutureExt as _}; #[cfg(any(test, feature = "test-support"))] pub use tree_sitter_rust; pub use lsp::DiagnosticSeverity; thread_local! { static PARSER: RefCell<Parser> = RefCell::new(Parser::new()); } lazy_static! { static ref QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Default::default(); } // TODO - Make this configurable const INDENT_SIZE: u32 = 4; pub struct Buffer { text: TextBuffer, file: Option<Box<dyn File>>, saved_version: clock::Global, saved_mtime: SystemTime, language: Option<Arc<Language>>, autoindent_requests: Vec<Arc<AutoindentRequest>>, pending_autoindent: Option<Task<()>>, sync_parse_timeout: Duration, syntax_tree: Mutex<Option<SyntaxTree>>, parsing_in_background: bool, parse_count: usize, diagnostics: DiagnosticSet, remote_selections: TreeMap<ReplicaId, SelectionSet>, selections_update_count: usize, diagnostics_update_count: usize, language_server: Option<LanguageServerState>, deferred_ops: OperationQueue<Operation>, #[cfg(test)] pub(crate) operations: Vec<Operation>, } pub struct BufferSnapshot { text: text::BufferSnapshot, tree: Option<Tree>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, remote_selections: TreeMap<ReplicaId, SelectionSet>, selections_update_count: usize, is_parsing: bool, language: Option<Arc<Language>>, parse_count: usize, } #[derive(Clone, Debug)] struct SelectionSet { selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct GroupId { source: Arc<str>, id: usize, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Diagnostic { pub code: Option<String>, pub severity: DiagnosticSeverity, pub message: String, pub group_id: usize, pub is_valid: bool, pub is_primary: bool, pub is_disk_based: bool, } struct LanguageServerState { server: Arc<LanguageServer>, latest_snapshot: watch::Sender<Option<LanguageServerSnapshot>>, pending_snapshots: BTreeMap<usize, LanguageServerSnapshot>, next_version: usize, _maintain_server: Task<Option<()>>, } #[derive(Clone)] struct LanguageServerSnapshot { buffer_snapshot: text::BufferSnapshot, version: usize, path: Arc<Path>, } #[derive(Clone, Debug)] pub enum Operation { Buffer(text::Operation), UpdateDiagnostics { diagnostics: Arc<[DiagnosticEntry<Anchor>]>, lamport_timestamp: clock::Lamport, }, UpdateSelections { replica_id: ReplicaId, selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport, }, } #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { Edited, Dirtied, Saved, FileHandleChanged, Reloaded, Reparsed, DiagnosticsUpdated, Closed, } pub trait File { fn as_local(&self) -> Option<&dyn LocalFile>; fn mtime(&self) -> SystemTime; /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc<Path>; /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &AppContext) -> PathBuf; /// Returns the last component of this handle's absolute path. If this handle refers to the root /// of its worktree, then this method will return the name of the worktree itself. fn file_name(&self, cx: &AppContext) -> OsString; fn is_deleted(&self) -> bool; fn save( &self, buffer_id: u64, text: Rope, version: clock::Global, cx: &mut MutableAppContext, ) -> Task<Result<(clock::Global, SystemTime)>>; fn format_remote(&self, buffer_id: u64, cx: &mut MutableAppContext) -> Option<Task<Result<()>>>; fn buffer_updated(&self, buffer_id: u64, operation: Operation, cx: &mut MutableAppContext); fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext); fn as_any(&self) -> &dyn Any; fn to_proto(&self) -> rpc::proto::File; } pub trait LocalFile: File { /// Returns the absolute path of this file. fn abs_path(&self, cx: &AppContext) -> PathBuf; fn load(&self, cx: &AppContext) -> Task<Result<String>>; fn buffer_reloaded( &self, buffer_id: u64, version: &clock::Global, mtime: SystemTime, cx: &mut MutableAppContext, ); } pub(crate) struct QueryCursorHandle(Option<QueryCursor>); #[derive(Clone)] struct SyntaxTree { tree: Tree, version: clock::Global, } #[derive(Clone)] struct AutoindentRequest { before_edit: BufferSnapshot, edited: Vec<Anchor>, inserted: Option<Vec<Range<Anchor>>>, } #[derive(Debug)] struct IndentSuggestion { basis_row: u32, indent: bool, } struct TextProvider<'a>(&'a Rope); struct BufferChunkHighlights<'a> { captures: tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>, next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>, stack: Vec<(usize, HighlightId)>, highlight_map: HighlightMap, theme: &'a SyntaxTheme, _query_cursor: QueryCursorHandle, } pub struct BufferChunks<'a> { range: Range<usize>, chunks: rope::Chunks<'a>, diagnostic_endpoints: Peekable<vec::IntoIter<DiagnosticEndpoint>>, error_depth: usize, warning_depth: usize, information_depth: usize, hint_depth: usize, highlights: Option<BufferChunkHighlights<'a>>, } #[derive(Clone, Copy, Debug, Default)] pub struct Chunk<'a> { pub text: &'a str, pub highlight_style: Option<HighlightStyle>, pub diagnostic: Option<DiagnosticSeverity>, } pub(crate) struct Diff { base_version: clock::Global, new_text: Arc<str>, changes: Vec<(ChangeTag, usize)>, } #[derive(Clone, Copy)] struct DiagnosticEndpoint { offset: usize, is_start: bool, severity: DiagnosticSeverity, } impl Buffer { pub fn new<T: Into<Arc<str>>>( replica_id: ReplicaId, base_text: T, cx: &mut ModelContext<Self>, ) -> Self { Self::build( TextBuffer::new( replica_id, cx.model_id() as u64, History::new(base_text.into()), ), None, ) } pub fn from_file<T: Into<Arc<str>>>( replica_id: ReplicaId, base_text: T, file: Box<dyn File>, cx: &mut ModelContext<Self>, ) -> Self { Self::build( TextBuffer::new( replica_id, cx.model_id() as u64, History::new(base_text.into()), ), Some(file), ) } pub fn from_proto( replica_id: ReplicaId, message: proto::BufferState, file: Option<Box<dyn File>>, cx: &mut ModelContext<Self>, ) -> Result<Self> { let fragments_len = message.fragments.len(); let buffer = TextBuffer::from_parts( replica_id, message.id, &message.visible_text, &message.deleted_text, message .undo_map .into_iter() .map(proto::deserialize_undo_map_entry), message .fragments .into_iter() .enumerate() .map(|(i, fragment)| { proto::deserialize_buffer_fragment(fragment, i, fragments_len) }), message.lamport_timestamp, From::from(message.version), ); let mut this = Self::build(buffer, file); for selection_set in message.selections { this.remote_selections.insert( selection_set.replica_id as ReplicaId, SelectionSet { selections: proto::deserialize_selections(selection_set.selections), lamport_timestamp: clock::Lamport { replica_id: selection_set.replica_id as ReplicaId, value: selection_set.lamport_timestamp, }, }, ); } let snapshot = this.snapshot(); let entries = proto::deserialize_diagnostics(message.diagnostics); this.apply_diagnostic_update( DiagnosticSet::from_sorted_entries(entries.into_iter().cloned(), &snapshot), cx, ); let deferred_ops = message .deferred_operations .into_iter() .map(proto::deserialize_operation) .collect::<Result<Vec<_>>>()?; this.apply_ops(deferred_ops, cx)?; Ok(this) } pub fn to_proto(&self) -> proto::BufferState { proto::BufferState { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), visible_text: self.text.text(), deleted_text: self.text.deleted_text(), undo_map: self .text .undo_history() .map(proto::serialize_undo_map_entry) .collect(), version: From::from(&self.version), lamport_timestamp: self.lamport_clock.value, fragments: self .text .fragments() .map(proto::serialize_buffer_fragment) .collect(), selections: self .remote_selections .iter() .map(|(replica_id, set)| proto::SelectionSet { replica_id: *replica_id as u32, selections: proto::serialize_selections(&set.selections), lamport_timestamp: set.lamport_timestamp.value, }) .collect(), diagnostics: proto::serialize_diagnostics(self.diagnostics.iter()), deferred_operations: self .deferred_ops .iter() .map(proto::serialize_operation) .chain( self.text .deferred_ops() .map(|op| proto::serialize_operation(&Operation::Buffer(op.clone()))), ) .collect(), } } pub fn with_language(mut self, language: Arc<Language>, cx: &mut ModelContext<Self>) -> Self { self.set_language(Some(language), cx); self } pub fn with_language_server( mut self, server: Arc<LanguageServer>, cx: &mut ModelContext<Self>, ) -> Self { self.set_language_server(Some(server), cx); self } fn build(buffer: TextBuffer, file: Option<Box<dyn File>>) -> Self { let saved_mtime; if let Some(file) = file.as_ref() { saved_mtime = file.mtime(); } else { saved_mtime = UNIX_EPOCH; } Self { saved_mtime, saved_version: buffer.version(), text: buffer, file, syntax_tree: Mutex::new(None), parsing_in_background: false, parse_count: 0, sync_parse_timeout: Duration::from_millis(1), autoindent_requests: Default::default(), pending_autoindent: Default::default(), language: None, remote_selections: Default::default(), selections_update_count: 0, diagnostics: Default::default(), diagnostics_update_count: 0, language_server: None, deferred_ops: OperationQueue::new(), #[cfg(test)] operations: Default::default(), } } pub fn snapshot(&self) -> BufferSnapshot { BufferSnapshot { text: self.text.snapshot(), tree: self.syntax_tree(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, is_parsing: self.parsing_in_background, language: self.language.clone(), parse_count: self.parse_count, selections_update_count: self.selections_update_count, } } pub fn file(&self) -> Option<&dyn File> { self.file.as_deref() } pub fn format(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> { let file = if let Some(file) = self.file.as_ref() { file } else { return Task::ready(Err(anyhow!("buffer has no file"))); }; if let Some(LanguageServerState { server, .. }) = self.language_server.as_ref() { let server = server.clone(); let abs_path = file.as_local().unwrap().abs_path(cx); let version = self.version(); cx.spawn(|this, mut cx| async move { let edits = server .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams { text_document: lsp::TextDocumentIdentifier::new( lsp::Url::from_file_path(&abs_path).unwrap(), ), options: Default::default(), work_done_progress_params: Default::default(), }) .await?; if let Some(edits) = edits { this.update(&mut cx, |this, cx| { if this.version == version { for edit in &edits { let range = range_from_lsp(edit.range); if this.clip_point_utf16(range.start, Bias::Left) != range.start || this.clip_point_utf16(range.end, Bias::Left) != range.end { return Err(anyhow!( "invalid formatting edits received from language server" )); } }