mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 18:41:56 +03:00
Make LspStore more responsible (#17318)
It now handles more of the buffer language work that project used to have to. Release Notes: - N/A
This commit is contained in:
parent
452272e5df
commit
be657377a2
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
buffer_store::BufferStore,
|
buffer_store::{BufferStore, BufferStoreEvent},
|
||||||
environment::ProjectEnvironment,
|
environment::ProjectEnvironment,
|
||||||
lsp_command::{self, *},
|
lsp_command::{self, *},
|
||||||
lsp_ext_command,
|
lsp_ext_command,
|
||||||
@ -108,6 +108,7 @@ pub struct LspStore {
|
|||||||
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
|
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
|
||||||
active_entry: Option<ProjectEntryId>,
|
active_entry: Option<ProjectEntryId>,
|
||||||
_maintain_workspace_config: Task<Result<()>>,
|
_maintain_workspace_config: Task<Result<()>>,
|
||||||
|
_maintain_buffer_languages: Task<()>,
|
||||||
next_diagnostic_group_id: usize,
|
next_diagnostic_group_id: usize,
|
||||||
diagnostic_summaries:
|
diagnostic_summaries:
|
||||||
HashMap<WorktreeId, HashMap<Arc<Path>, HashMap<LanguageServerId, DiagnosticSummary>>>,
|
HashMap<WorktreeId, HashMap<Arc<Path>, HashMap<LanguageServerId, DiagnosticSummary>>>,
|
||||||
@ -134,6 +135,10 @@ pub enum LspStoreEvent {
|
|||||||
},
|
},
|
||||||
LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
|
LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
|
||||||
LanguageServerPrompt(LanguageServerPromptRequest),
|
LanguageServerPrompt(LanguageServerPromptRequest),
|
||||||
|
LanguageDetected {
|
||||||
|
buffer: Model<Buffer>,
|
||||||
|
new_language: Option<Arc<Language>>,
|
||||||
|
},
|
||||||
Notification(String),
|
Notification(String),
|
||||||
RefreshInlayHints,
|
RefreshInlayHints,
|
||||||
DiagnosticsUpdated {
|
DiagnosticsUpdated {
|
||||||
@ -218,6 +223,8 @@ impl LspStore {
|
|||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let yarn = YarnPathStore::new(fs.clone(), cx);
|
let yarn = YarnPathStore::new(fs.clone(), cx);
|
||||||
|
cx.subscribe(&buffer_store, Self::on_buffer_store_event)
|
||||||
|
.detach();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
downstream_client,
|
downstream_client,
|
||||||
@ -227,7 +234,7 @@ impl LspStore {
|
|||||||
project_id: remote_id.unwrap_or(0),
|
project_id: remote_id.unwrap_or(0),
|
||||||
buffer_store,
|
buffer_store,
|
||||||
worktree_store,
|
worktree_store,
|
||||||
languages,
|
languages: languages.clone(),
|
||||||
environment,
|
environment,
|
||||||
nonce: StdRng::from_entropy().gen(),
|
nonce: StdRng::from_entropy().gen(),
|
||||||
buffer_snapshots: Default::default(),
|
buffer_snapshots: Default::default(),
|
||||||
@ -244,10 +251,214 @@ impl LspStore {
|
|||||||
active_entry: None,
|
active_entry: None,
|
||||||
yarn,
|
yarn,
|
||||||
_maintain_workspace_config: Self::maintain_workspace_config(cx),
|
_maintain_workspace_config: Self::maintain_workspace_config(cx),
|
||||||
|
_maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
|
||||||
_subscription: cx.on_app_quit(Self::shutdown_language_servers),
|
_subscription: cx.on_app_quit(Self::shutdown_language_servers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_buffer_store_event(
|
||||||
|
&mut self,
|
||||||
|
_: Model<BufferStore>,
|
||||||
|
event: &BufferStoreEvent,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
BufferStoreEvent::BufferAdded(buffer) => {
|
||||||
|
self.register_buffer(buffer, cx).log_err();
|
||||||
|
}
|
||||||
|
BufferStoreEvent::BufferChangedFilePath { buffer, old_file } => {
|
||||||
|
if let Some(old_file) = File::from_dyn(old_file.as_ref()) {
|
||||||
|
self.unregister_buffer_from_language_servers(&buffer, old_file, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.detect_language_for_buffer(&buffer, cx);
|
||||||
|
self.register_buffer_with_language_servers(&buffer, cx);
|
||||||
|
}
|
||||||
|
BufferStoreEvent::BufferDropped(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_buffer_event(
|
||||||
|
&mut self,
|
||||||
|
buffer: Model<Buffer>,
|
||||||
|
event: &language::Event,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
language::Event::Edited { .. } => {
|
||||||
|
self.on_buffer_edited(buffer, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
language::Event::Saved => {
|
||||||
|
self.on_buffer_saved(buffer, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_buffer(
|
||||||
|
&mut self,
|
||||||
|
buffer: &Model<Buffer>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Result<()> {
|
||||||
|
buffer.update(cx, |buffer, _| {
|
||||||
|
buffer.set_language_registry(self.languages.clone())
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.subscribe(buffer, |this, buffer, event, cx| {
|
||||||
|
this.on_buffer_event(buffer, event, cx);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
self.detect_language_for_buffer(buffer, cx);
|
||||||
|
self.register_buffer_with_language_servers(buffer, cx);
|
||||||
|
cx.observe_release(buffer, |this, buffer, cx| {
|
||||||
|
if let Some(file) = File::from_dyn(buffer.file()) {
|
||||||
|
if file.is_local() {
|
||||||
|
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
||||||
|
for server in this.language_servers_for_buffer(buffer, cx) {
|
||||||
|
server
|
||||||
|
.1
|
||||||
|
.notify::<lsp::notification::DidCloseTextDocument>(
|
||||||
|
lsp::DidCloseTextDocumentParams {
|
||||||
|
text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maintain_buffer_languages(
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Task<()> {
|
||||||
|
let mut subscription = languages.subscribe();
|
||||||
|
let mut prev_reload_count = languages.reload_count();
|
||||||
|
cx.spawn(move |this, mut cx| async move {
|
||||||
|
while let Some(()) = subscription.next().await {
|
||||||
|
if let Some(this) = this.upgrade() {
|
||||||
|
// If the language registry has been reloaded, then remove and
|
||||||
|
// re-assign the languages on all open buffers.
|
||||||
|
let reload_count = languages.reload_count();
|
||||||
|
if reload_count > prev_reload_count {
|
||||||
|
prev_reload_count = reload_count;
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.buffer_store.clone().update(cx, |buffer_store, cx| {
|
||||||
|
for buffer in buffer_store.buffers() {
|
||||||
|
if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned()
|
||||||
|
{
|
||||||
|
this.unregister_buffer_from_language_servers(
|
||||||
|
&buffer, &f, cx,
|
||||||
|
);
|
||||||
|
buffer
|
||||||
|
.update(cx, |buffer, cx| buffer.set_language(None, cx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
let mut plain_text_buffers = Vec::new();
|
||||||
|
let mut buffers_with_unknown_injections = Vec::new();
|
||||||
|
for handle in this.buffer_store.read(cx).buffers() {
|
||||||
|
let buffer = handle.read(cx);
|
||||||
|
if buffer.language().is_none()
|
||||||
|
|| buffer.language() == Some(&*language::PLAIN_TEXT)
|
||||||
|
{
|
||||||
|
plain_text_buffers.push(handle);
|
||||||
|
} else if buffer.contains_unknown_injections() {
|
||||||
|
buffers_with_unknown_injections.push(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for buffer in plain_text_buffers {
|
||||||
|
this.detect_language_for_buffer(&buffer, cx);
|
||||||
|
this.register_buffer_with_language_servers(&buffer, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for buffer in buffers_with_unknown_injections {
|
||||||
|
buffer.update(cx, |buffer, cx| buffer.reparse(cx));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_language_for_buffer(
|
||||||
|
&mut self,
|
||||||
|
buffer_handle: &Model<Buffer>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
// If the buffer has a language, set it and start the language server if we haven't already.
|
||||||
|
let buffer = buffer_handle.read(cx);
|
||||||
|
let Some(file) = buffer.file() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let content = buffer.as_rope();
|
||||||
|
let Some(new_language_result) = self
|
||||||
|
.languages
|
||||||
|
.language_for_file(file, Some(content), cx)
|
||||||
|
.now_or_never()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match new_language_result {
|
||||||
|
Err(e) => {
|
||||||
|
if e.is::<language::LanguageNotFound>() {
|
||||||
|
cx.emit(LspStoreEvent::LanguageDetected {
|
||||||
|
buffer: buffer_handle.clone(),
|
||||||
|
new_language: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(new_language) => {
|
||||||
|
self.set_language_for_buffer(buffer_handle, new_language, cx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_language_for_buffer(
|
||||||
|
&mut self,
|
||||||
|
buffer: &Model<Buffer>,
|
||||||
|
new_language: Arc<Language>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
if buffer.language().map_or(true, |old_language| {
|
||||||
|
!Arc::ptr_eq(old_language, &new_language)
|
||||||
|
}) {
|
||||||
|
buffer.set_language(Some(new_language.clone()), cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let buffer_file = buffer.read(cx).file().cloned();
|
||||||
|
let buffer_file = File::from_dyn(buffer_file.as_ref());
|
||||||
|
|
||||||
|
if let Some(file) = buffer_file {
|
||||||
|
let worktree = file.worktree.clone();
|
||||||
|
if worktree.read(cx).is_local() {
|
||||||
|
self.start_language_servers(&worktree, new_language.clone(), cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.emit(LspStoreEvent::LanguageDetected {
|
||||||
|
buffer: buffer.clone(),
|
||||||
|
new_language: Some(new_language),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buffer_store(&self) -> Model<BufferStore> {
|
pub fn buffer_store(&self) -> Model<BufferStore> {
|
||||||
self.buffer_store.clone()
|
self.buffer_store.clone()
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ use language::{
|
|||||||
},
|
},
|
||||||
Buffer, CachedLspAdapter, Capability, CodeLabel, ContextProvider, DiagnosticEntry, Diff,
|
Buffer, CachedLspAdapter, Capability, CodeLabel, ContextProvider, DiagnosticEntry, Diff,
|
||||||
Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName,
|
Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName,
|
||||||
LocalFile, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||||
};
|
};
|
||||||
use lsp::{CompletionContext, DocumentHighlightKind, LanguageServer, LanguageServerId};
|
use lsp::{CompletionContext, DocumentHighlightKind, LanguageServer, LanguageServerId};
|
||||||
use lsp_command::*;
|
use lsp_command::*;
|
||||||
@ -161,7 +161,6 @@ pub struct Project {
|
|||||||
buffers_needing_diff: HashSet<WeakModel<Buffer>>,
|
buffers_needing_diff: HashSet<WeakModel<Buffer>>,
|
||||||
git_diff_debouncer: DebouncedDelay<Self>,
|
git_diff_debouncer: DebouncedDelay<Self>,
|
||||||
remotely_created_buffers: Arc<Mutex<RemotelyCreatedBuffers>>,
|
remotely_created_buffers: Arc<Mutex<RemotelyCreatedBuffers>>,
|
||||||
_maintain_buffer_languages: Task<()>,
|
|
||||||
terminals: Terminals,
|
terminals: Terminals,
|
||||||
node: Option<Arc<dyn NodeRuntime>>,
|
node: Option<Arc<dyn NodeRuntime>>,
|
||||||
default_prettier: DefaultPrettier,
|
default_prettier: DefaultPrettier,
|
||||||
@ -661,7 +660,6 @@ impl Project {
|
|||||||
cx.observe_global::<SettingsStore>(Self::on_settings_changed),
|
cx.observe_global::<SettingsStore>(Self::on_settings_changed),
|
||||||
cx.on_release(Self::release),
|
cx.on_release(Self::release),
|
||||||
],
|
],
|
||||||
_maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
|
|
||||||
active_entry: None,
|
active_entry: None,
|
||||||
snippets,
|
snippets,
|
||||||
languages,
|
languages,
|
||||||
@ -847,7 +845,6 @@ impl Project {
|
|||||||
active_entry: None,
|
active_entry: None,
|
||||||
collaborators: Default::default(),
|
collaborators: Default::default(),
|
||||||
join_project_response_message_id: response.message_id,
|
join_project_response_message_id: response.message_id,
|
||||||
_maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
|
|
||||||
languages,
|
languages,
|
||||||
user_store: user_store.clone(),
|
user_store: user_store.clone(),
|
||||||
snippets,
|
snippets,
|
||||||
@ -1869,60 +1866,15 @@ impl Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.request_buffer_diff_recalculation(buffer, cx);
|
self.request_buffer_diff_recalculation(buffer, cx);
|
||||||
buffer.update(cx, |buffer, _| {
|
|
||||||
buffer.set_language_registry(self.languages.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.subscribe(buffer, |this, buffer, event, cx| {
|
cx.subscribe(buffer, |this, buffer, event, cx| {
|
||||||
this.on_buffer_event(buffer, event, cx);
|
this.on_buffer_event(buffer, event, cx);
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
self.detect_language_for_buffer(buffer, cx);
|
|
||||||
self.register_buffer_with_language_servers(buffer, cx);
|
|
||||||
cx.observe_release(buffer, |this, buffer, cx| {
|
|
||||||
if let Some(file) = File::from_dyn(buffer.file()) {
|
|
||||||
if file.is_local() {
|
|
||||||
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
|
||||||
for server in this.language_servers_for_buffer(buffer, cx) {
|
|
||||||
server
|
|
||||||
.1
|
|
||||||
.notify::<lsp::notification::DidCloseTextDocument>(
|
|
||||||
lsp::DidCloseTextDocumentParams {
|
|
||||||
text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_buffer_with_language_servers(
|
|
||||||
&mut self,
|
|
||||||
buffer_handle: &Model<Buffer>,
|
|
||||||
cx: &mut ModelContext<Self>,
|
|
||||||
) {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
|
||||||
lsp_store.register_buffer_with_language_servers(buffer_handle, cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unregister_buffer_from_language_servers(
|
|
||||||
&mut self,
|
|
||||||
buffer: &Model<Buffer>,
|
|
||||||
old_file: &File,
|
|
||||||
cx: &mut AppContext,
|
|
||||||
) {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
|
||||||
lsp_store.unregister_buffer_from_language_servers(buffer, old_file, cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_buffer_ordered_messages(
|
async fn send_buffer_ordered_messages(
|
||||||
this: WeakModel<Self>,
|
this: WeakModel<Self>,
|
||||||
rx: UnboundedReceiver<BufferOrderedMessage>,
|
rx: UnboundedReceiver<BufferOrderedMessage>,
|
||||||
@ -2041,14 +1993,7 @@ impl Project {
|
|||||||
BufferStoreEvent::BufferAdded(buffer) => {
|
BufferStoreEvent::BufferAdded(buffer) => {
|
||||||
self.register_buffer(buffer, cx).log_err();
|
self.register_buffer(buffer, cx).log_err();
|
||||||
}
|
}
|
||||||
BufferStoreEvent::BufferChangedFilePath { buffer, old_file } => {
|
BufferStoreEvent::BufferChangedFilePath { .. } => {}
|
||||||
if let Some(old_file) = File::from_dyn(old_file.as_ref()) {
|
|
||||||
self.unregister_buffer_from_language_servers(&buffer, old_file, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.detect_language_for_buffer(&buffer, cx);
|
|
||||||
self.register_buffer_with_language_servers(&buffer, cx);
|
|
||||||
}
|
|
||||||
BufferStoreEvent::BufferDropped(buffer_id) => {
|
BufferStoreEvent::BufferDropped(buffer_id) => {
|
||||||
if let Some(ref ssh_session) = self.ssh_session {
|
if let Some(ref ssh_session) = self.ssh_session {
|
||||||
ssh_session
|
ssh_session
|
||||||
@ -2085,6 +2030,29 @@ impl Project {
|
|||||||
LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
|
LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
|
||||||
Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
|
Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
|
||||||
),
|
),
|
||||||
|
LspStoreEvent::LanguageDetected {
|
||||||
|
buffer,
|
||||||
|
new_language,
|
||||||
|
} => {
|
||||||
|
let Some(new_language) = new_language else {
|
||||||
|
cx.emit(Event::LanguageNotFound(buffer.clone()));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let buffer_file = buffer.read(cx).file().cloned();
|
||||||
|
let settings =
|
||||||
|
language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
|
||||||
|
let buffer_file = File::from_dyn(buffer_file.as_ref());
|
||||||
|
let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
|
||||||
|
if let Some(prettier_plugins) =
|
||||||
|
prettier_support::prettier_plugins_for_language(&settings)
|
||||||
|
{
|
||||||
|
self.install_default_prettier(
|
||||||
|
worktree,
|
||||||
|
prettier_plugins.iter().map(|s| Arc::from(s.as_str())),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
LspStoreEvent::RefreshInlayHints => cx.emit(Event::RefreshInlayHints),
|
LspStoreEvent::RefreshInlayHints => cx.emit(Event::RefreshInlayHints),
|
||||||
LspStoreEvent::LanguageServerPrompt(prompt) => {
|
LspStoreEvent::LanguageServerPrompt(prompt) => {
|
||||||
cx.emit(Event::LanguageServerPrompt(prompt.clone()))
|
cx.emit(Event::LanguageServerPrompt(prompt.clone()))
|
||||||
@ -2326,19 +2294,6 @@ impl Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferEvent::Edited { .. } => {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
|
||||||
lsp_store.on_buffer_edited(buffer, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEXT STEP have the lsp_store register for these things!
|
|
||||||
BufferEvent::Saved => {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
|
||||||
lsp_store.on_buffer_saved(buffer, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2412,134 +2367,15 @@ impl Project {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain_buffer_languages(
|
|
||||||
languages: Arc<LanguageRegistry>,
|
|
||||||
cx: &mut ModelContext<Project>,
|
|
||||||
) -> Task<()> {
|
|
||||||
let mut subscription = languages.subscribe();
|
|
||||||
let mut prev_reload_count = languages.reload_count();
|
|
||||||
cx.spawn(move |project, mut cx| async move {
|
|
||||||
while let Some(()) = subscription.next().await {
|
|
||||||
if let Some(project) = project.upgrade() {
|
|
||||||
// If the language registry has been reloaded, then remove and
|
|
||||||
// re-assign the languages on all open buffers.
|
|
||||||
let reload_count = languages.reload_count();
|
|
||||||
if reload_count > prev_reload_count {
|
|
||||||
prev_reload_count = reload_count;
|
|
||||||
project
|
|
||||||
.update(&mut cx, |this, cx| {
|
|
||||||
this.buffer_store.clone().update(cx, |buffer_store, cx| {
|
|
||||||
for buffer in buffer_store.buffers() {
|
|
||||||
if let Some(f) =
|
|
||||||
File::from_dyn(buffer.read(cx).file()).cloned()
|
|
||||||
{
|
|
||||||
this.unregister_buffer_from_language_servers(
|
|
||||||
&buffer, &f, cx,
|
|
||||||
);
|
|
||||||
buffer.update(cx, |buffer, cx| {
|
|
||||||
buffer.set_language(None, cx)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
project
|
|
||||||
.update(&mut cx, |project, cx| {
|
|
||||||
let mut plain_text_buffers = Vec::new();
|
|
||||||
let mut buffers_with_unknown_injections = Vec::new();
|
|
||||||
for handle in project.buffer_store.read(cx).buffers() {
|
|
||||||
let buffer = handle.read(cx);
|
|
||||||
if buffer.language().is_none()
|
|
||||||
|| buffer.language() == Some(&*language::PLAIN_TEXT)
|
|
||||||
{
|
|
||||||
plain_text_buffers.push(handle);
|
|
||||||
} else if buffer.contains_unknown_injections() {
|
|
||||||
buffers_with_unknown_injections.push(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for buffer in plain_text_buffers {
|
|
||||||
project.detect_language_for_buffer(&buffer, cx);
|
|
||||||
project.register_buffer_with_language_servers(&buffer, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for buffer in buffers_with_unknown_injections {
|
|
||||||
buffer.update(cx, |buffer, cx| buffer.reparse(cx));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn detect_language_for_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer_handle: &Model<Buffer>,
|
|
||||||
cx: &mut ModelContext<Self>,
|
|
||||||
) {
|
|
||||||
// If the buffer has a language, set it and start the language server if we haven't already.
|
|
||||||
let buffer = buffer_handle.read(cx);
|
|
||||||
let Some(file) = buffer.file() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let content = buffer.as_rope();
|
|
||||||
let Some(new_language_result) = self
|
|
||||||
.languages
|
|
||||||
.language_for_file(file, Some(content), cx)
|
|
||||||
.now_or_never()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
match new_language_result {
|
|
||||||
Err(e) => {
|
|
||||||
if e.is::<language::LanguageNotFound>() {
|
|
||||||
cx.emit(Event::LanguageNotFound(buffer_handle.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(new_language) => {
|
|
||||||
self.set_language_for_buffer(buffer_handle, new_language, cx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_language_for_buffer(
|
pub fn set_language_for_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &Model<Buffer>,
|
buffer: &Model<Buffer>,
|
||||||
new_language: Arc<Language>,
|
new_language: Arc<Language>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
buffer.update(cx, |buffer, cx| {
|
|
||||||
if buffer.language().map_or(true, |old_language| {
|
|
||||||
!Arc::ptr_eq(old_language, &new_language)
|
|
||||||
}) {
|
|
||||||
buffer.set_language(Some(new_language.clone()), cx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let buffer_file = buffer.read(cx).file().cloned();
|
|
||||||
let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
|
|
||||||
let buffer_file = File::from_dyn(buffer_file.as_ref());
|
|
||||||
let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
|
|
||||||
if let Some(prettier_plugins) = prettier_support::prettier_plugins_for_language(&settings) {
|
|
||||||
self.install_default_prettier(
|
|
||||||
worktree,
|
|
||||||
prettier_plugins.iter().map(|s| Arc::from(s.as_str())),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
if let Some(file) = buffer_file {
|
|
||||||
let worktree = file.worktree.clone();
|
|
||||||
if worktree.read(cx).is_local() {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.start_language_servers(&worktree, new_language, cx);
|
lsp_store.set_language_for_buffer(buffer, new_language, cx)
|
||||||
});
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restart_language_servers_for_buffers(
|
pub fn restart_language_servers_for_buffers(
|
||||||
|
Loading…
Reference in New Issue
Block a user