Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use arc_swap::{access::Map, ArcSwap};
use futures_util::Stream;
use futures_util::{future::OptionFuture, Stream};
use helix_core::{
diagnostic::{DiagnosticTag, NumberOrString},
path::get_relative_path,
Expand All @@ -11,7 +11,7 @@ use helix_view::{
document::DocumentSavedEventResult,
editor::{ConfigEvent, EditorEvent},
graphics::Rect,
theme,
theme::{self, Modifier, Style},
tree::Layout,
Align, Editor,
};
Expand All @@ -23,6 +23,7 @@ use crate::{
commands::apply_workspace_edit,
compositor::{Compositor, Event},
config::Config,
ctrl,
job::Jobs,
keymap::Keymaps,
ui::{self, overlay::overlayed},
Expand Down Expand Up @@ -284,6 +285,13 @@ impl Application {
// reset cursor cache
self.editor.cursor_cache.set(None);

if self.jobs.blocking_job.is_some() {
surface.set_style(
area.clip_bottom(1),
Style::default().add_modifier(Modifier::DIM),
);
}

let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));
self.terminal.draw(pos, kind).unwrap();
}
Expand Down Expand Up @@ -316,6 +324,13 @@ impl Application {
tokio::select! {
biased;

Some(callback) = OptionFuture::from(self.jobs.blocking_job.as_mut()), if self.jobs.blocking_job.is_some() => {
self.jobs.blocking_job = None;
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
self.editor.clear_status();
self.render().await;
}

Some(signal) = self.signals.next() => {
self.handle_signals(signal).await;
}
Expand Down Expand Up @@ -633,7 +648,24 @@ impl Application {
kind: crossterm::event::KeyEventKind::Release,
..
}) => false,
event => self.compositor.handle_event(&event.into(), &mut cx),

CrosstermEvent::Key(event)
if cx.jobs.blocking_job.is_some()
&& helix_view::input::KeyEvent::from(event) == ctrl!('c') =>
{
cx.editor.clear_status();
cx.jobs.blocking_job = None;

true
}

event => {
if cx.jobs.blocking_job.is_some() {
false
} else {
self.compositor.handle_event(&event.into(), &mut cx)
}
}
};

if should_redraw && !self.editor.should_close() {
Expand Down Expand Up @@ -874,7 +906,9 @@ impl Application {
if !self.lsp_progress.is_progressing(server_id) {
editor_view.spinners_mut().get_or_create(server_id).stop();
}
self.editor.clear_status();
if self.config.load().editor.lsp.display_messages {
self.editor.clear_status();
}

// we want to render to clear any leftover spinners or messages
return;
Expand Down
34 changes: 33 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use crate::{
args,
compositor::{self, Component, Compositor},
filter_picker_entry,
job::Callback,
job::{cancelation, BlockingJob, Callback, CancelSender},
keymap::ReverseKeymap,
ui::{
self, editor::InsertEvent, overlay::overlayed, FilePicker, Picker, Popup, Prompt,
Expand Down Expand Up @@ -85,6 +85,7 @@ pub struct Context<'a> {
pub editor: &'a mut Editor,

pub callback: Option<crate::compositor::Callback>,
pub blocking_callback: Option<BlockingJob>,
pub on_next_key_callback: Option<OnKeyCallback>,
pub jobs: &'a mut Jobs,
}
Expand All @@ -97,6 +98,15 @@ impl<'a> Context<'a> {
}));
}

pub fn push_layer_blocking(
&mut self,
layer: impl Future<Output = anyhow::Result<Box<dyn Component>>> + 'static,
cancel: CancelSender,
msg: &'static str,
) {
self.blocking_callback = Some(BlockingJob::push_layer(layer, cancel, msg))
}

#[inline]
pub fn on_next_key(
&mut self,
Expand All @@ -117,6 +127,27 @@ impl<'a> Context<'a> {
self.jobs.callback(make_job_callback(call, callback));
}

pub fn callback_blocking<T, F>(
&mut self,
// The editor status message while awaiting the callback.
msg: &'static str,
call: impl Future<Output = helix_lsp::Result<serde_json::Value>> + 'static + Send,
callback: F,
) where
T: for<'de> serde::Deserialize<'de> + Send + 'static,
F: FnOnce(&mut Editor, &mut Compositor, T) + Send + 'static,
{
// No cancelation subscribers -- the only cleanup we need to do is to clear the
// status message, which is handled by BlockingJob's integration into the UI
// thread.
let (sender, _) = cancelation();
self.blocking_callback = Some(BlockingJob::new(
make_job_callback(call, callback),
sender,
msg,
))
}

/// Returns 1 if no explicit count was provided
#[inline]
pub fn count(&self) -> usize {
Expand Down Expand Up @@ -2647,6 +2678,7 @@ pub fn command_palette(cx: &mut Context) {
callback: None,
on_next_key_callback: None,
jobs: cx.jobs,
blocking_callback: None,
};
let focus = view!(ctx.editor).id;

Expand Down
58 changes: 26 additions & 32 deletions helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,22 +1007,19 @@ pub fn goto_definition(cx: &mut Context) {

let pos = doc.position(view.id, offset_encoding);

let future = match language_server.goto_definition(doc.identifier(), pos, None) {
Some(future) => future,
None => {
cx.editor
.set_error("Language server does not support goto-definition");
return;
}
};

cx.callback(
future,
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let items = to_locations(response);
goto_impl(editor, compositor, items, offset_encoding);
},
);
if let Some(future) = language_server.goto_definition(doc.identifier(), pos, None) {
cx.callback_blocking(
"goto_definition: Waiting on LSP...",
future,
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
let items = to_locations(response);
goto_impl(editor, compositor, items, offset_encoding);
},
);
} else {
cx.editor
.set_error("Language server does not support goto-definition");
}
}

pub fn goto_type_definition(cx: &mut Context) {
Expand Down Expand Up @@ -1082,22 +1079,19 @@ pub fn goto_reference(cx: &mut Context) {

let pos = doc.position(view.id, offset_encoding);

let future = match language_server.goto_reference(doc.identifier(), pos, None) {
Some(future) => future,
None => {
cx.editor
.set_error("Language server does not support goto-reference");
return;
}
};

cx.callback(
future,
move |editor, compositor, response: Option<Vec<lsp::Location>>| {
let items = response.unwrap_or_default();
goto_impl(editor, compositor, items, offset_encoding);
},
);
if let Some(future) = language_server.goto_reference(doc.identifier(), pos, None) {
cx.callback_blocking(
"goto_reference: Waiting on LSP...",
future,
move |editor, compositor, response: Option<Vec<lsp::Location>>| {
let items = response.unwrap_or_default();
goto_impl(editor, compositor, items, offset_encoding);
},
);
} else {
cx.editor
.set_error("Language server does not support goto-reference");
}
}

#[derive(PartialEq, Eq)]
Expand Down
7 changes: 6 additions & 1 deletion helix-term/src/compositor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Each component declares its own size constraints and gets fitted based on its parent.
// Q: how does this work with popups?
// cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
use helix_core::diagnostic::Severity;
use helix_core::Position;
use helix_view::graphics::{CursorKind, Rect};

Expand All @@ -15,7 +16,7 @@ pub enum EventResult {
Consumed(Option<Callback>),
}

use crate::job::Jobs;
use crate::job::{BlockingJob, Jobs};
use helix_view::Editor;

pub use helix_view::input::Event;
Expand All @@ -27,6 +28,10 @@ pub struct Context<'a> {
}

impl<'a> Context<'a> {
pub fn blocking_job(&mut self, blocking_callback: BlockingJob) {
self.editor.status_msg = Some((blocking_callback.msg.into(), Severity::Error));
self.jobs.blocking_job = Some(blocking_callback);
}
/// Waits on all pending jobs, and then tries to flush all pending write
/// operations for all documents.
pub fn block_try_flush_writes(&mut self) -> anyhow::Result<()> {
Expand Down
Loading