mirror of
https://github.com/helix-editor/helix.git
synced 2025-01-19 21:47:07 +04:00
wip: Getting the new prompt to render in a new layer.
This commit is contained in:
parent
5103dc9617
commit
ada3f92c5b
@ -42,16 +42,15 @@
|
|||||||
const BASE_WIDTH: u16 = 30;
|
const BASE_WIDTH: u16 = 30;
|
||||||
|
|
||||||
pub struct Application<'a> {
|
pub struct Application<'a> {
|
||||||
prompt: Option<Prompt>,
|
|
||||||
|
|
||||||
compositor: Compositor,
|
compositor: Compositor,
|
||||||
|
editor: Editor,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
|
|
||||||
executor: &'a smol::Executor<'a>,
|
executor: &'a smol::Executor<'a>,
|
||||||
language_server: helix_lsp::Client,
|
language_server: helix_lsp::Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Renderer {
|
pub struct Renderer {
|
||||||
size: (u16, u16),
|
size: (u16, u16),
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
surface: Surface,
|
surface: Surface,
|
||||||
@ -263,6 +262,11 @@ pub fn render_statusline(&mut self, view: &View, theme: &Theme) {
|
|||||||
self.surface
|
self.surface
|
||||||
.set_string(1, self.size.1 - 2, mode, self.text_color);
|
.set_string(1, self.size.1 - 2, mode, self.text_color);
|
||||||
|
|
||||||
|
if let Some(path) = view.doc.path() {
|
||||||
|
self.surface
|
||||||
|
.set_string(6, self.size.1 - 2, path.to_string_lossy(), self.text_color);
|
||||||
|
}
|
||||||
|
|
||||||
self.surface.set_string(
|
self.surface.set_string(
|
||||||
self.size.0 - 10,
|
self.size.0 - 10,
|
||||||
self.size.1 - 2,
|
self.size.1 - 2,
|
||||||
@ -271,7 +275,7 @@ pub fn render_statusline(&mut self, view: &View, theme: &Theme) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
|
pub fn render_prompt(&mut self, prompt: &Prompt, theme: &Theme) {
|
||||||
// completion
|
// completion
|
||||||
if !prompt.completion.is_empty() {
|
if !prompt.completion.is_empty() {
|
||||||
// TODO: find out better way of clearing individual lines of the screen
|
// TODO: find out better way of clearing individual lines of the screen
|
||||||
@ -343,15 +347,6 @@ pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport:
|
|||||||
let pos = if let Some(prompt) = prompt {
|
let pos = if let Some(prompt) = prompt {
|
||||||
Position::new(self.size.0 as usize, 2 + prompt.cursor)
|
Position::new(self.size.0 as usize, 2 + prompt.cursor)
|
||||||
} else {
|
} else {
|
||||||
if let Some(path) = view.doc.path() {
|
|
||||||
self.surface.set_string(
|
|
||||||
6,
|
|
||||||
self.size.1 - 1,
|
|
||||||
path.to_string_lossy(),
|
|
||||||
self.text_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cursor = view.doc.state.selection().cursor();
|
let cursor = view.doc.state.selection().cursor();
|
||||||
|
|
||||||
let mut pos = view
|
let mut pos = view
|
||||||
@ -367,146 +362,77 @@ pub fn render_cursor(&mut self, view: &View, prompt: Option<&Prompt>, viewport:
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct EditorView {
|
struct EditorView {
|
||||||
editor: Editor,
|
|
||||||
prompt: Option<Prompt>, // TODO: this is None for now, make a layer
|
|
||||||
keymap: Keymaps,
|
keymap: Keymaps,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorView {
|
impl EditorView {
|
||||||
fn new(editor: Editor) -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
editor,
|
|
||||||
prompt: None,
|
|
||||||
keymap: keymap::default(),
|
keymap: keymap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::compositor::Context;
|
||||||
|
|
||||||
impl Component for EditorView {
|
impl Component for EditorView {
|
||||||
fn handle_event(&mut self, event: Event, executor: &smol::Executor) -> EventResult {
|
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
|
||||||
match event {
|
match event {
|
||||||
Event::Resize(width, height) => {
|
Event::Resize(width, height) => {
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
// TODO: loop over views
|
// TODO: loop over views
|
||||||
if let Some(view) = self.editor.view_mut() {
|
if let Some(view) = cx.editor.view_mut() {
|
||||||
view.size = (width, height);
|
view.size = (width, height);
|
||||||
view.ensure_cursor_in_view()
|
view.ensure_cursor_in_view()
|
||||||
};
|
};
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(event) => {
|
Event::Key(event) => {
|
||||||
if let Some(view) = self.editor.view_mut() {
|
if let Some(view) = cx.editor.view_mut() {
|
||||||
let keys = vec![event];
|
let keys = vec![event];
|
||||||
// TODO: sequences (`gg`)
|
// TODO: sequences (`gg`)
|
||||||
|
let mode = view.doc.mode();
|
||||||
// TODO: handle count other than 1
|
// TODO: handle count other than 1
|
||||||
match view.doc.mode() {
|
let mut cx = commands::Context {
|
||||||
|
view,
|
||||||
|
executor: cx.executor,
|
||||||
|
count: 1,
|
||||||
|
callback: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match mode {
|
||||||
Mode::Insert => {
|
Mode::Insert => {
|
||||||
if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
|
if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
|
||||||
let mut cx = commands::Context {
|
|
||||||
view,
|
|
||||||
executor,
|
|
||||||
count: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
command(&mut cx);
|
command(&mut cx);
|
||||||
} else if let KeyEvent {
|
} else if let KeyEvent {
|
||||||
code: KeyCode::Char(c),
|
code: KeyCode::Char(c),
|
||||||
..
|
..
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
let mut cx = commands::Context {
|
|
||||||
view,
|
|
||||||
executor,
|
|
||||||
count: 1,
|
|
||||||
};
|
|
||||||
commands::insert::insert_char(&mut cx, c);
|
commands::insert::insert_char(&mut cx, c);
|
||||||
}
|
}
|
||||||
view.ensure_cursor_in_view();
|
|
||||||
}
|
}
|
||||||
Mode::Normal => {
|
Mode::Normal => {
|
||||||
if let &[KeyEvent {
|
if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
|
||||||
code: KeyCode::Char(':'),
|
|
||||||
..
|
|
||||||
}] = keys.as_slice()
|
|
||||||
{
|
|
||||||
let prompt = Prompt::new(
|
|
||||||
":".to_owned(),
|
|
||||||
|_input: &str| {
|
|
||||||
// TODO: i need this duplicate list right now to avoid borrow checker issues
|
|
||||||
let command_list = vec![
|
|
||||||
String::from("q"),
|
|
||||||
String::from("aaa"),
|
|
||||||
String::from("bbb"),
|
|
||||||
String::from("ccc"),
|
|
||||||
String::from("ddd"),
|
|
||||||
String::from("eee"),
|
|
||||||
String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
|
|
||||||
String::from("q"),
|
|
||||||
String::from("aaa"),
|
|
||||||
String::from("bbb"),
|
|
||||||
String::from("ccc"),
|
|
||||||
String::from("ddd"),
|
|
||||||
String::from("eee"),
|
|
||||||
String::from("q"),
|
|
||||||
String::from("aaa"),
|
|
||||||
String::from("bbb"),
|
|
||||||
String::from("ccc"),
|
|
||||||
String::from("ddd"),
|
|
||||||
String::from("eee"),
|
|
||||||
String::from("q"),
|
|
||||||
String::from("aaa"),
|
|
||||||
String::from("bbb"),
|
|
||||||
String::from("ccc"),
|
|
||||||
String::from("ddd"),
|
|
||||||
String::from("eee"),
|
|
||||||
String::from("q"),
|
|
||||||
String::from("aaa"),
|
|
||||||
String::from("bbb"),
|
|
||||||
String::from("ccc"),
|
|
||||||
String::from("ddd"),
|
|
||||||
String::from("eee"),
|
|
||||||
];
|
|
||||||
command_list
|
|
||||||
.into_iter()
|
|
||||||
.filter(|command| command.contains(_input))
|
|
||||||
.collect()
|
|
||||||
}, // completion
|
|
||||||
|editor: &mut Editor, input: &str| match input {
|
|
||||||
"q" => editor.should_close = true,
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
self.prompt = Some(prompt);
|
|
||||||
|
|
||||||
// HAXX: special casing for command mode
|
|
||||||
} else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
|
|
||||||
let mut cx = commands::Context {
|
|
||||||
view,
|
|
||||||
executor,
|
|
||||||
count: 1,
|
|
||||||
};
|
|
||||||
command(&mut cx);
|
command(&mut cx);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
view.ensure_cursor_in_view();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mode => {
|
mode => {
|
||||||
if let Some(command) = self.keymap[&mode].get(&keys) {
|
if let Some(command) = self.keymap[&mode].get(&keys) {
|
||||||
let mut cx = commands::Context {
|
|
||||||
view,
|
|
||||||
executor,
|
|
||||||
count: 1,
|
|
||||||
};
|
|
||||||
command(&mut cx);
|
command(&mut cx);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
view.ensure_cursor_in_view();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventResult::Consumed(None)
|
// appease borrowck
|
||||||
|
let callback = cx.callback.take();
|
||||||
|
|
||||||
|
view.ensure_cursor_in_view();
|
||||||
|
|
||||||
|
EventResult::Consumed(callback)
|
||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
@ -514,19 +440,19 @@ fn handle_event(&mut self, event: Event, executor: &smol::Executor) -> EventResu
|
|||||||
Event::Mouse(_) => EventResult::Ignored,
|
Event::Mouse(_) => EventResult::Ignored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn render(&mut self, renderer: &mut Renderer) {
|
fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
|
||||||
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||||
let viewport = Rect::new(OFFSET, 0, renderer.size.0, renderer.size.1 - 2); // - 2 for statusline and prompt
|
let viewport = Rect::new(OFFSET, 0, renderer.size.0, renderer.size.1 - 2); // - 2 for statusline and prompt
|
||||||
|
|
||||||
// SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow
|
// SAFETY: we cheat around the view_mut() borrow because it doesn't allow us to also borrow
|
||||||
// theme. Theme is immutable mutating view won't disrupt theme_ref.
|
// theme. Theme is immutable mutating view won't disrupt theme_ref.
|
||||||
let theme_ref = unsafe { &*(&self.editor.theme as *const Theme) };
|
let theme_ref = unsafe { &*(&cx.editor.theme as *const Theme) };
|
||||||
if let Some(view) = self.editor.view_mut() {
|
if let Some(view) = cx.editor.view_mut() {
|
||||||
renderer.render_view(view, viewport, theme_ref);
|
renderer.render_view(view, viewport, theme_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: drop unwrap
|
// TODO: drop unwrap
|
||||||
renderer.render_cursor(self.editor.view().unwrap(), self.prompt.as_ref(), viewport);
|
renderer.render_cursor(cx.editor.view().unwrap(), None, viewport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,11 +466,12 @@ pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Err
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut compositor = Compositor::new();
|
let mut compositor = Compositor::new();
|
||||||
compositor.push(Box::new(EditorView::new(editor)));
|
compositor.push(Box::new(EditorView::new()));
|
||||||
|
|
||||||
let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
|
let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
|
||||||
|
|
||||||
let mut app = Self {
|
let mut app = Self {
|
||||||
|
editor,
|
||||||
renderer,
|
renderer,
|
||||||
// TODO; move to state
|
// TODO; move to state
|
||||||
compositor,
|
compositor,
|
||||||
@ -558,7 +485,11 @@ pub fn new(mut args: Args, executor: &'a smol::Executor<'a>) -> Result<Self, Err
|
|||||||
|
|
||||||
fn render(&mut self) {
|
fn render(&mut self) {
|
||||||
self.renderer.surface.reset(); // reset is faster than allocating new empty surface
|
self.renderer.surface.reset(); // reset is faster than allocating new empty surface
|
||||||
self.compositor.render(&mut self.renderer); // viewport,
|
let mut cx = crate::compositor::Context {
|
||||||
|
editor: &mut self.editor,
|
||||||
|
executor: &self.executor,
|
||||||
|
};
|
||||||
|
self.compositor.render(&mut self.renderer, &mut cx); // viewport,
|
||||||
self.renderer.draw_and_swap();
|
self.renderer.draw_and_swap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,17 +500,16 @@ pub async fn event_loop(&mut self) {
|
|||||||
self.language_server.initialize().await.unwrap();
|
self.language_server.initialize().await.unwrap();
|
||||||
// TODO: temp
|
// TODO: temp
|
||||||
// self.language_server
|
// self.language_server
|
||||||
// .text_document_did_open(&self.editor.view().unwrap().doc)
|
// .text_document_did_open(&cx.editor.view().unwrap().doc)
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
self.render();
|
self.render();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// TODO:
|
if self.editor.should_close {
|
||||||
// if self.editor.should_close {
|
break;
|
||||||
// break;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
use futures_util::{select, FutureExt};
|
use futures_util::{select, FutureExt};
|
||||||
select! {
|
select! {
|
||||||
@ -594,26 +524,26 @@ pub async fn event_loop(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
|
pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
|
||||||
|
let mut cx = crate::compositor::Context {
|
||||||
|
editor: &mut self.editor,
|
||||||
|
executor: &self.executor,
|
||||||
|
};
|
||||||
// Handle key events
|
// Handle key events
|
||||||
match event {
|
let should_redraw = match event {
|
||||||
Some(Ok(Event::Resize(width, height))) => {
|
Some(Ok(Event::Resize(width, height))) => {
|
||||||
self.renderer.resize(width, height);
|
self.renderer.resize(width, height);
|
||||||
|
|
||||||
// TODO: use the response
|
|
||||||
self.compositor
|
self.compositor
|
||||||
.handle_event(Event::Resize(width, height), self.executor);
|
.handle_event(Event::Resize(width, height), &mut cx)
|
||||||
|
|
||||||
self.render();
|
|
||||||
}
|
|
||||||
Some(Ok(event)) => {
|
|
||||||
// TODO: use the response
|
|
||||||
self.compositor.handle_event(event, self.executor);
|
|
||||||
|
|
||||||
self.render();
|
|
||||||
}
|
}
|
||||||
|
Some(Ok(event)) => self.compositor.handle_event(event, &mut cx),
|
||||||
Some(Err(x)) => panic!(x),
|
Some(Err(x)) => panic!(x),
|
||||||
None => panic!(),
|
None => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if should_redraw {
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_language_server_message(&mut self, call: Option<helix_lsp::Call>) {
|
pub async fn handle_language_server_message(&mut self, call: Option<helix_lsp::Call>) {
|
||||||
|
@ -9,17 +9,21 @@
|
|||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::compositor::Compositor;
|
||||||
use crate::prompt::Prompt;
|
use crate::prompt::Prompt;
|
||||||
|
|
||||||
use helix_view::{
|
use helix_view::{
|
||||||
document::Mode,
|
document::Mode,
|
||||||
view::{View, PADDING},
|
view::{View, PADDING},
|
||||||
|
Editor,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Context<'a, 'b> {
|
pub struct Context<'a, 'b> {
|
||||||
pub count: usize,
|
pub count: usize,
|
||||||
pub view: &'a mut View,
|
pub view: &'a mut View,
|
||||||
pub executor: &'a smol::Executor<'b>,
|
pub executor: &'a smol::Executor<'b>,
|
||||||
|
|
||||||
|
pub callback: Option<crate::compositor::Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A command is a function that takes the current state and a count, and does a side-effect on the
|
/// A command is a function that takes the current state and a count, and does a side-effect on the
|
||||||
@ -333,8 +337,57 @@ pub fn append_mode(cx: &mut Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: I, A, o and O can share a lot of the primitives.
|
// TODO: I, A, o and O can share a lot of the primitives.
|
||||||
pub fn command_mode(_cx: &mut Context) {
|
pub fn command_mode(cx: &mut Context) {
|
||||||
unimplemented!()
|
cx.callback = Some(Box::new(|compositor: &mut Compositor| {
|
||||||
|
let prompt = Prompt::new(
|
||||||
|
":".to_owned(),
|
||||||
|
|_input: &str| {
|
||||||
|
// TODO: i need this duplicate list right now to avoid borrow checker issues
|
||||||
|
let command_list = vec![
|
||||||
|
String::from("q"),
|
||||||
|
String::from("aaa"),
|
||||||
|
String::from("bbb"),
|
||||||
|
String::from("ccc"),
|
||||||
|
String::from("ddd"),
|
||||||
|
String::from("eee"),
|
||||||
|
String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"),
|
||||||
|
String::from("q"),
|
||||||
|
String::from("aaa"),
|
||||||
|
String::from("bbb"),
|
||||||
|
String::from("ccc"),
|
||||||
|
String::from("ddd"),
|
||||||
|
String::from("eee"),
|
||||||
|
String::from("q"),
|
||||||
|
String::from("aaa"),
|
||||||
|
String::from("bbb"),
|
||||||
|
String::from("ccc"),
|
||||||
|
String::from("ddd"),
|
||||||
|
String::from("eee"),
|
||||||
|
String::from("q"),
|
||||||
|
String::from("aaa"),
|
||||||
|
String::from("bbb"),
|
||||||
|
String::from("ccc"),
|
||||||
|
String::from("ddd"),
|
||||||
|
String::from("eee"),
|
||||||
|
String::from("q"),
|
||||||
|
String::from("aaa"),
|
||||||
|
String::from("bbb"),
|
||||||
|
String::from("ccc"),
|
||||||
|
String::from("ddd"),
|
||||||
|
String::from("eee"),
|
||||||
|
];
|
||||||
|
command_list
|
||||||
|
.into_iter()
|
||||||
|
.filter(|command| command.contains(_input))
|
||||||
|
.collect()
|
||||||
|
}, // completion
|
||||||
|
|editor: &mut Editor, input: &str| match input {
|
||||||
|
"q" => editor.should_close = true,
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
compositor.push(Box::new(prompt));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate line numbers for each selection range
|
// calculate line numbers for each selection range
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
use smol::Executor;
|
use smol::Executor;
|
||||||
use tui::buffer::Buffer as Surface;
|
use tui::buffer::Buffer as Surface;
|
||||||
|
|
||||||
pub(crate) type Callback = Box<dyn Fn(&mut Compositor)>;
|
pub type Callback = Box<dyn Fn(&mut Compositor)>;
|
||||||
|
|
||||||
// --> EventResult should have a callback that takes a context with methods like .popup(),
|
// --> EventResult should have a callback that takes a context with methods like .popup(),
|
||||||
// .prompt() etc. That way we can abstract it from the renderer.
|
// .prompt() etc. That way we can abstract it from the renderer.
|
||||||
@ -29,14 +29,21 @@
|
|||||||
// If Compositor was specified in the callback that's then problematic because of
|
// If Compositor was specified in the callback that's then problematic because of
|
||||||
|
|
||||||
// Cursive-inspired
|
// Cursive-inspired
|
||||||
pub(crate) enum EventResult {
|
pub enum EventResult {
|
||||||
Ignored,
|
Ignored,
|
||||||
Consumed(Option<Callback>),
|
Consumed(Option<Callback>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait Component {
|
use helix_view::{Editor, View};
|
||||||
|
// shared with commands.rs
|
||||||
|
pub struct Context<'a, 'b> {
|
||||||
|
pub editor: &'a mut Editor,
|
||||||
|
pub executor: &'a smol::Executor<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Component {
|
||||||
/// Process input events, return true if handled.
|
/// Process input events, return true if handled.
|
||||||
fn handle_event(&mut self, event: Event, executor: &Executor) -> EventResult;
|
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult;
|
||||||
// , args: ()
|
// , args: ()
|
||||||
|
|
||||||
/// Should redraw? Useful for saving redraw cycles if we know component didn't change.
|
/// Should redraw? Useful for saving redraw cycles if we know component didn't change.
|
||||||
@ -44,7 +51,7 @@ fn should_update(&self) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, renderer: &mut Renderer);
|
fn render(&mut self, renderer: &mut Renderer, ctx: &mut Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct Editor { };
|
// struct Editor { };
|
||||||
@ -94,7 +101,7 @@ fn should_update(&self) -> bool {
|
|||||||
// // 2) Alternatively,
|
// // 2) Alternatively,
|
||||||
//}
|
//}
|
||||||
|
|
||||||
pub(crate) struct Compositor {
|
pub struct Compositor {
|
||||||
layers: Vec<Box<dyn Component>>,
|
layers: Vec<Box<dyn Component>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,17 +118,24 @@ pub fn pop(&mut self) {
|
|||||||
self.layers.pop();
|
self.layers.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_event(&mut self, event: Event, executor: &Executor) -> () {
|
pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
|
||||||
// TODO: custom focus
|
// TODO: custom focus
|
||||||
if let Some(layer) = self.layers.last_mut() {
|
if let Some(layer) = self.layers.last_mut() {
|
||||||
layer.handle_event(event, executor);
|
return match layer.handle_event(event, cx) {
|
||||||
// return should_update
|
EventResult::Consumed(Some(callback)) => {
|
||||||
|
callback(self);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
EventResult::Consumed(None) => true,
|
||||||
|
EventResult::Ignored => false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, renderer: &mut Renderer) {
|
pub fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
|
||||||
for layer in &mut self.layers {
|
for layer in &mut self.layers {
|
||||||
layer.render(renderer)
|
layer.render(renderer, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
use crate::Editor;
|
use crate::{
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
application::Renderer,
|
||||||
|
compositor::{Component, Context, EventResult},
|
||||||
|
};
|
||||||
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
||||||
|
use helix_view::Editor;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
pub struct Prompt {
|
pub struct Prompt {
|
||||||
@ -79,9 +83,16 @@ pub fn change_completion_selection(&mut self) {
|
|||||||
pub fn exit_selection(&mut self) {
|
pub fn exit_selection(&mut self) {
|
||||||
self.completion_selection_index = None;
|
self.completion_selection_index = None;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_event(&mut self, key_event: KeyEvent, editor: &mut Editor) {
|
impl Component for Prompt {
|
||||||
match key_event {
|
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
|
||||||
|
let event = match event {
|
||||||
|
Event::Key(event) => event,
|
||||||
|
_ => return EventResult::Ignored,
|
||||||
|
};
|
||||||
|
|
||||||
|
match event {
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Char(c),
|
code: KeyCode::Char(c),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
@ -112,7 +123,7 @@ pub fn handle_event(&mut self, key_event: KeyEvent, editor: &mut Editor) {
|
|||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Enter,
|
code: KeyCode::Enter,
|
||||||
..
|
..
|
||||||
} => (self.callback_fn)(editor, &self.line),
|
} => (self.callback_fn)(cx.editor, &self.line),
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Tab, ..
|
code: KeyCode::Tab, ..
|
||||||
} => self.change_completion_selection(),
|
} => self.change_completion_selection(),
|
||||||
@ -121,6 +132,12 @@ pub fn handle_event(&mut self, key_event: KeyEvent, editor: &mut Editor) {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
} => self.exit_selection(),
|
} => self.exit_selection(),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, renderer: &mut Renderer, cx: &mut Context) {
|
||||||
|
renderer.render_prompt(self, &cx.editor.theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user