red: move more logic into lysp
This commit is contained in:
@@ -1,5 +1,16 @@
|
||||
(setq _red/command-table (hash/new))
|
||||
|
||||
;; Command text manipulation
|
||||
(setq _red/current-command nil)
|
||||
|
||||
(defun red/command/append! (text)
|
||||
(setq _red/current-command (+ (if _red/current-command _red/current-command "") text)))
|
||||
(defun red/command/erase-backward! ()
|
||||
(when _red/current-command
|
||||
(setq _red/current-command (string/pop _red/current-command))))
|
||||
(defun red/command/clear! () (setq _red/current-command nil))
|
||||
|
||||
;; Command macros
|
||||
(defun _red/declare-command
|
||||
(command handler)
|
||||
(hash/put! _red/command-table command handler))
|
||||
@@ -21,6 +32,7 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; Command handlers
|
||||
(defun _red/editor-command-hook (command)
|
||||
(when-let
|
||||
(words (filter identity (string/split command)))
|
||||
@@ -37,6 +49,7 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; Root command handler
|
||||
(defun _red/root-command-hook (command)
|
||||
(red/set-top-mode 'normal)
|
||||
(setq command (string/trim command))
|
||||
@@ -49,6 +62,7 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; Command definitions
|
||||
(declare-command "q" () (red/quit))
|
||||
(declare-command "q!" () (red/quit #t))
|
||||
(declare-command
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
;; External API
|
||||
|
||||
(import "util.lysp")
|
||||
|
||||
(defun _red/key-sequence-string
|
||||
(key-seq)
|
||||
(let (strs (map ->string key-seq))
|
||||
(string/join strs "+")
|
||||
)
|
||||
)
|
||||
(defun _red/root-post-render-hook (width height)
|
||||
(unless (nil? _red/key-sequence)
|
||||
(let (keys (_red/key-sequence-string _red/key-sequence))
|
||||
(term/set-cursor (- height 1) (- width 10))
|
||||
(term/write keys)
|
||||
)
|
||||
)
|
||||
)
|
||||
(import "message.lysp")
|
||||
|
||||
;; Bind the hooks
|
||||
(import "render.lysp")
|
||||
(red/bind-post-render-hook _red/root-post-render-hook)
|
||||
|
||||
;; Child modules
|
||||
@@ -30,3 +17,5 @@
|
||||
;; User configuration
|
||||
(try-import "/etc/red/init.lysp")
|
||||
(try-import (+ (fs/home-directory) "/.red.d/init.lysp"))
|
||||
|
||||
(_red/update-render-params)
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
command
|
||||
('escape (red/set-top-mode 'normal))
|
||||
('backspace
|
||||
(let (command (red/command))
|
||||
(if command
|
||||
(red/set-command (string/pop command))
|
||||
(red/set-top-mode 'normal)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if _red/current-command
|
||||
(red/command/erase-backward!)
|
||||
(red/set-top-mode 'normal)
|
||||
))
|
||||
('newline
|
||||
(let (command (red/command))
|
||||
(red/set-command "")
|
||||
(let (command _red/current-command)
|
||||
(red/command/clear!)
|
||||
(_red/root-command-hook command)
|
||||
)
|
||||
)
|
||||
@@ -24,8 +21,4 @@
|
||||
(key-seq)
|
||||
(let (insertable (red/as-insertable-key-seq key-seq))
|
||||
(unless (nil? insertable)
|
||||
(let (command (+ (red/command) insertable))
|
||||
(red/set-command command))
|
||||
)
|
||||
)
|
||||
)
|
||||
(red/command/append! insertable))))
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
(declare-keys
|
||||
normal
|
||||
(': (red/set-top-mode 'command))
|
||||
(':
|
||||
(red/clear-status)
|
||||
(red/clear-message)
|
||||
(red/set-top-mode 'command)
|
||||
)
|
||||
('i (red/buffer/set-mode 'insert-before))
|
||||
('a (red/buffer/set-mode 'insert-after))
|
||||
('I
|
||||
@@ -28,6 +32,8 @@
|
||||
('up (red/buffer/move 'prev-line))
|
||||
('down (red/buffer/move 'next-line))
|
||||
|
||||
('(F F F F F) nil)
|
||||
|
||||
('o
|
||||
(red/buffer/insert-line-after)
|
||||
(red/buffer/move 'next-line)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
(setq _red/current-message nil)
|
||||
(setq _red/current-status nil)
|
||||
|
||||
(defun _red/set-message (var args)
|
||||
(let
|
||||
(value (if (nil? args)
|
||||
nil
|
||||
(string/join (map ->string args))))
|
||||
(set var value)))
|
||||
|
||||
(defun red/message (&rest args) (_red/set-message '_red/current-message args))
|
||||
(defun red/status (&rest args) (_red/set-message '_red/current-status args))
|
||||
|
||||
(defun red/clear-message () (setq _red/current-message nil))
|
||||
(defun red/clear-status () (setq _red/current-status nil))
|
||||
@@ -0,0 +1,112 @@
|
||||
;; Drawing options
|
||||
|
||||
(setq red/render/status-line #T)
|
||||
(setq red/render/status-line/key-sequence #T)
|
||||
|
||||
(setq red/render/mode-line #T)
|
||||
(setq red/render/mode-line/arrow #T)
|
||||
|
||||
;; Drawing functions
|
||||
|
||||
(defun _red/colorize-mode
|
||||
(mode)
|
||||
(cond
|
||||
((= mode 'normal) '(black cyan))
|
||||
((= mode 'insert) '(black yellow))
|
||||
((= mode 'command) '(black green))
|
||||
(&otherwise nil)))
|
||||
|
||||
(defun _red/render-mode-line
|
||||
(row mode name modified)
|
||||
(unless red/render/mode-line (return))
|
||||
(term/set-cursor row 0)
|
||||
(let (color (_red/colorize-mode mode))
|
||||
(when color
|
||||
(when (car color)
|
||||
(term/fg-color (car color)))
|
||||
(when (cadr color)
|
||||
(term/bg-color (cadr color))
|
||||
)
|
||||
)
|
||||
(term/write (+ " " (string/to-upper (->string mode)) " "))
|
||||
;; Invert colors and draw the arrow
|
||||
(when color
|
||||
(when (and red/render/mode-line/arrow (cadr color))
|
||||
(term/fg-color (cadr color))
|
||||
(term/bg-color 'black)
|
||||
(term/write "🭬"))
|
||||
(term/reset)
|
||||
)
|
||||
)
|
||||
;; Render bufer name
|
||||
(when name
|
||||
(term/write (+ " " name)))
|
||||
(when modified
|
||||
(term/write " [+]"))
|
||||
)
|
||||
|
||||
(defun _red/render-status-line
|
||||
(row)
|
||||
(let (message (cond
|
||||
(_red/current-message (list _red/current-message 'red))
|
||||
(_red/current-status (list _red/current-status 'white)))
|
||||
)
|
||||
(unless message (return))
|
||||
(term/set-cursor row 0)
|
||||
(when (cadr message)
|
||||
(term/fg-color (cadr message)))
|
||||
(term/write (car message))))
|
||||
|
||||
(defun _red/render-key-line
|
||||
(row width)
|
||||
(let (keys (string/join (map ->string _red/key-sequence) "+"))
|
||||
(unless keys (return))
|
||||
(term/set-cursor row (- width (+ (string/length keys) 1)))
|
||||
(term/write keys)
|
||||
))
|
||||
|
||||
(defun _red/render-command (row command)
|
||||
(term/set-cursor row 0)
|
||||
(term/write ":")
|
||||
(when command
|
||||
(term/write command)))
|
||||
|
||||
(defun _red/render-bottom-bar
|
||||
(width height)
|
||||
(let* (mode (red/buffer/mode) top-mode (car mode) buffer-mode (cadr mode))
|
||||
(when (= top-mode 'command) (setq buffer-mode 'command))
|
||||
|
||||
;; Display mode line
|
||||
(_red/render-mode-line
|
||||
(- height _red/mode-line-offset)
|
||||
buffer-mode
|
||||
(red/buffer/name)
|
||||
(red/buffer/modified?))
|
||||
|
||||
(when (= top-mode 'normal)
|
||||
;; Status text
|
||||
(_red/render-status-line (- height _red/status-line-offset))
|
||||
;; Current key sequence
|
||||
(_red/render-key-line (- height _red/status-line-offset) width)
|
||||
)
|
||||
(when (= top-mode 'command)
|
||||
;; Render command
|
||||
(_red/render-command (- height _red/status-line-offset) _red/current-command))
|
||||
)
|
||||
)
|
||||
|
||||
(defun _red/root-post-render-hook (width height)
|
||||
(_red/render-bottom-bar width height)
|
||||
(term/reset)
|
||||
;; If not in command mode, set cursor to buffer
|
||||
(when (= 'normal (car (red/buffer/mode)))
|
||||
(red/cursor-to-buffer))
|
||||
)
|
||||
|
||||
;; command line + status line
|
||||
(defun _red/update-render-params ()
|
||||
(setq _red/status-line-offset 1)
|
||||
(setq _red/mode-line-offset 2)
|
||||
(setq red/bottom-margin (+ (and red/render/mode-line 1) 1)))
|
||||
|
||||
(_red/update-render-params)
|
||||
@@ -6,6 +6,7 @@ use std::{
|
||||
pub struct EditorConfig {
|
||||
pub tab_width: usize,
|
||||
pub number: Dirty<bool>,
|
||||
pub bottom_margin: Dirty<usize>,
|
||||
}
|
||||
|
||||
pub struct Dirty<T>(T, bool);
|
||||
@@ -15,6 +16,7 @@ impl Default for EditorConfig {
|
||||
Self {
|
||||
tab_width: 4,
|
||||
number: Dirty::new(false),
|
||||
bottom_margin: Dirty::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ use lysp::{
|
||||
Value,
|
||||
env::{Environment, EnvironmentAccessHook},
|
||||
machine::Machine,
|
||||
value::{IdentifierValue, StringValue, convert::AnyFunction},
|
||||
value::{
|
||||
IdentifierValue, StringValue,
|
||||
convert::{AnyFunction, TryFromValue},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -57,17 +60,28 @@ impl Editor {
|
||||
|
||||
match name {
|
||||
"red/number" => Some((*config.number).into()),
|
||||
"red/bottom-margin" => Some((*config.bottom_margin).into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn write_variable(&mut self, name: IdentifierValue, value: Value) -> bool {
|
||||
fn write_variable(
|
||||
&mut self,
|
||||
name: &IdentifierValue,
|
||||
value: &Value,
|
||||
) -> Result<bool, MachineError> {
|
||||
match name.as_ref() {
|
||||
"red/number" => {
|
||||
let mut config = self.1.borrow_mut();
|
||||
*config.number = value.is_trueish();
|
||||
true
|
||||
Ok(true)
|
||||
}
|
||||
_ => false,
|
||||
"red/bottom-margin" => {
|
||||
let value = usize::try_from_value(value)?;
|
||||
let mut config = self.1.borrow_mut();
|
||||
*config.bottom_margin = value;
|
||||
Ok(true)
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,18 +141,26 @@ impl Editor {
|
||||
pub fn evaluate_file<P: AsRef<Path>>(&mut self, path: P) {
|
||||
let path = path.as_ref();
|
||||
if let Err(error) = self.machine.load_file(Default::default(), &self.env, path) {
|
||||
self.state
|
||||
.borrow_mut()
|
||||
.set_message(format!("{}: {}", path.display(), error));
|
||||
log::error!("{}: {}", path.display(), error);
|
||||
self.env
|
||||
.set_global_value(
|
||||
"_red/current-message",
|
||||
Value::String(format!("{}: {}", path.display(), error).into()),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_callback(&mut self, function: &AnyFunction, args: &[Value]) {
|
||||
let name = function.name();
|
||||
if let Err(error) = function.invoke(&mut self.machine, &self.env, args) {
|
||||
self.state
|
||||
.borrow_mut()
|
||||
.set_message(format!("{name}: {error}"));
|
||||
log::error!("{name}: {error}");
|
||||
self.env
|
||||
.set_global_value(
|
||||
"_red/current-message",
|
||||
Value::String(format!("{name}: {error}").into()),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +177,12 @@ impl Editor {
|
||||
runtime_directory = "/usr/share/red/runtime".into();
|
||||
}
|
||||
|
||||
self.env.set_global_value(
|
||||
"red/runtime-directory",
|
||||
StringValue::from(format!("{}", runtime_directory.display())),
|
||||
);
|
||||
self.env
|
||||
.set_global_value(
|
||||
"red/runtime-directory",
|
||||
StringValue::from(format!("{}", runtime_directory.display())),
|
||||
)
|
||||
.ok();
|
||||
self.evaluate_file(runtime_directory.join("core.lysp"));
|
||||
|
||||
loop {
|
||||
@@ -200,8 +224,6 @@ impl Editor {
|
||||
&key_hook,
|
||||
&[top_mode.into(), buffer_mode.into(), key.into()],
|
||||
);
|
||||
} else {
|
||||
self.state.borrow_mut().set_message("No root key hook set!");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
|
||||
pub mod convert;
|
||||
pub use convert::{AsValue, FromValue, Movement};
|
||||
use libterm::Color;
|
||||
use libterm::{Clear, Color};
|
||||
use lysp::{
|
||||
error::MachineError,
|
||||
vm::{
|
||||
@@ -23,7 +23,7 @@ use lysp::{
|
||||
};
|
||||
|
||||
fn editor_api(editor: &mut Editor) {
|
||||
editor.defun("red/shell-command", |_, _, state, args| {
|
||||
editor.defun("red/shell-command", |_, env, _, args| {
|
||||
let [command, args @ ..] = args else {
|
||||
return Err(MachineError::InvalidArgumentCount.into());
|
||||
};
|
||||
@@ -40,7 +40,9 @@ fn editor_api(editor: &mut Editor) {
|
||||
if let Ok(text) = std::str::from_utf8(&output.stdout[..]) {
|
||||
let text = text.trim();
|
||||
let text = text.rsplit_once('\n').map(|(_, v)| v).unwrap_or(text);
|
||||
state.borrow_mut().set_status(text);
|
||||
|
||||
env.set_global_value("_red/current-status", Value::String(text.into()))
|
||||
.ok();
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
@@ -80,55 +82,11 @@ fn editor_api(editor: &mut Editor) {
|
||||
[] => false,
|
||||
[arg, ..] => arg.is_trueish(),
|
||||
};
|
||||
state.borrow_mut().exit(force);
|
||||
state.borrow_mut().exit(force)?;
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/command", |_, _, state, _| {
|
||||
let command = state.borrow().command.clone();
|
||||
Ok(StringValue::from(command).into())
|
||||
});
|
||||
editor.defun("red/set-command", |_, _, state, args| {
|
||||
let [command] = args else {
|
||||
return Err(MachineError::InvalidArgumentCount.into());
|
||||
};
|
||||
let command = StringValue::try_from_value(command)?;
|
||||
state.borrow_mut().command = (*command).to_owned();
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/message", |_, _, state, args| {
|
||||
let mut message = String::new();
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
message.push(' ');
|
||||
}
|
||||
match arg {
|
||||
Value::String(value) => {
|
||||
message.push_str(value.as_ref());
|
||||
}
|
||||
_ => {
|
||||
message.push_str(&format!("{arg}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
state.borrow_mut().set_message(message);
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/status", |_, _, state, args| {
|
||||
let mut message = String::new();
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
message.push(' ');
|
||||
}
|
||||
match arg {
|
||||
Value::String(value) => {
|
||||
message.push_str(value.as_ref());
|
||||
}
|
||||
_ => {
|
||||
message.push_str(&format!("{arg}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
state.borrow_mut().set_status(message);
|
||||
editor.defun("red/cursor-to-buffer", |_, _, state, _| {
|
||||
state.borrow_mut().cursor_to_buffer().ok();
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
}
|
||||
@@ -288,6 +246,15 @@ fn buffer_api(editor: &mut Editor) {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
});
|
||||
editor.defun("red/buffer/name", |_, _, state, _| {
|
||||
let state = state.borrow();
|
||||
let name = state.buffer().name();
|
||||
if let Some(name) = name {
|
||||
Ok(Value::String((&**name).into()))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
});
|
||||
editor.defun("red/buffer/mode", |_, _, state, _| {
|
||||
let state = state.borrow();
|
||||
let (top_mode, buffer_mode) = state.mode();
|
||||
@@ -304,14 +271,17 @@ fn buffer_api(editor: &mut Editor) {
|
||||
state.set_mode(target_mode);
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/buffer/write", |_, _, state, args| {
|
||||
editor.defun("red/buffer/write", |_, env, state, args| {
|
||||
let path = match args {
|
||||
[] => None,
|
||||
[path] => Some(StringValue::try_from_value(path)?),
|
||||
_ => return Err(MachineError::InvalidArgumentCount.into()),
|
||||
};
|
||||
let mut state = state.borrow_mut();
|
||||
state.write_buffer(path.as_deref())?;
|
||||
if let Some(status) = state.write_buffer(path.as_deref())? {
|
||||
env.set_global_value("_red/current-status", Value::String(status.into()))
|
||||
.ok();
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/buffer/open", |_, _, state, args| {
|
||||
@@ -358,6 +328,10 @@ fn buffer_api(editor: &mut Editor) {
|
||||
state.buffer_mut().newline_after(break_line);
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/buffer/modified?", |_, _, state, _| {
|
||||
let state = state.borrow();
|
||||
Ok(state.buffer().is_modified().into())
|
||||
});
|
||||
editor.defun("red/buffer/kill-line", |_, _, state, _| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.kill_current_line();
|
||||
@@ -378,6 +352,16 @@ fn buffer_api(editor: &mut Editor) {
|
||||
}
|
||||
|
||||
fn term_api(editor: &mut Editor) {
|
||||
editor.defun("term/reset", |_, _, state, _| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.term.reset_style().ok();
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("term/clear-line", |_, _, state, _| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.term.clear(Clear::LineToEnd).ok();
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("term/fg-color", |_, _, state, args| {
|
||||
let [color] = args else {
|
||||
return Err(MachineError::InvalidArgumentCount.into());
|
||||
|
||||
@@ -15,9 +15,6 @@ use crate::{
|
||||
pub struct State {
|
||||
pub(super) term: Term,
|
||||
buffer: Buffer,
|
||||
pub(super) command: String,
|
||||
message: Option<String>,
|
||||
status: Option<String>,
|
||||
top_mode: TopMode,
|
||||
config: Rc<RefCell<EditorConfig>>,
|
||||
running: bool,
|
||||
@@ -43,9 +40,6 @@ impl State {
|
||||
Ok(Self {
|
||||
number_width: buffer.number_width(),
|
||||
top_mode: TopMode::Normal,
|
||||
message: None,
|
||||
status: None,
|
||||
command: String::new(),
|
||||
running: true,
|
||||
buffer,
|
||||
term,
|
||||
@@ -76,26 +70,19 @@ impl State {
|
||||
Some(self.buffer.get_terminal_cursor(&self.config.borrow()))
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, force: bool) {
|
||||
pub fn exit(&mut self, force: bool) -> Result<(), Error> {
|
||||
if self.buffer.is_modified() && !force {
|
||||
self.message = Some("Buffer has unsaved changes. Use :q! to force-exit".into());
|
||||
return;
|
||||
Err(Error::UnsavedBuffer("Use :q! to force-exit"))
|
||||
} else {
|
||||
self.running = false;
|
||||
Ok(())
|
||||
}
|
||||
self.running = false;
|
||||
}
|
||||
|
||||
pub fn exited(&self) -> bool {
|
||||
!self.running
|
||||
}
|
||||
|
||||
pub fn set_status<S: Into<String>>(&mut self, status: S) {
|
||||
self.status.replace(status.into());
|
||||
}
|
||||
|
||||
pub fn set_message<S: Into<String>>(&mut self, message: S) {
|
||||
self.message.replace(message.into());
|
||||
}
|
||||
|
||||
fn display_number(&mut self) -> Result<(), Error> {
|
||||
let start = self.buffer.row_offset();
|
||||
let end = self.buffer.len();
|
||||
@@ -131,85 +118,12 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_modeline(&mut self) -> Result<(), Error> {
|
||||
self.term
|
||||
.set_cursor_position(self.buffer.height(), 0)
|
||||
.map_err(Error::TerminalError)?;
|
||||
|
||||
let bg = match (self.top_mode, self.buffer.mode()) {
|
||||
(TopMode::Normal, Mode::Normal) => Color::Yellow,
|
||||
(TopMode::Normal, Mode::Insert) => Color::Cyan,
|
||||
(TopMode::Command, _) => Color::Green,
|
||||
};
|
||||
|
||||
self.term.set_background(bg).map_err(Error::TerminalError)?;
|
||||
self.term
|
||||
.set_foreground(Color::Black)
|
||||
.map_err(Error::TerminalError)?;
|
||||
|
||||
match self.top_mode {
|
||||
TopMode::Normal => {
|
||||
write!(self.term, " {} ", self.buffer.mode().as_str())
|
||||
.map_err(Error::TerminalFmtError)?;
|
||||
|
||||
if self.buffer.is_modified() {
|
||||
self.term
|
||||
.set_background(Color::Magenta)
|
||||
.map_err(Error::TerminalError)?;
|
||||
self.term
|
||||
.set_foreground(Color::Default)
|
||||
.map_err(Error::TerminalError)?;
|
||||
} else {
|
||||
self.term
|
||||
.set_foreground(Color::Green)
|
||||
.map_err(Error::TerminalError)?;
|
||||
self.term
|
||||
.set_background(Color::Default)
|
||||
.map_err(Error::TerminalError)?;
|
||||
}
|
||||
}
|
||||
TopMode::Command => {
|
||||
write!(self.term, " COMMAND ").map_err(Error::TerminalFmtError)?;
|
||||
|
||||
self.term
|
||||
.set_foreground(Color::Green)
|
||||
.map_err(Error::TerminalError)?;
|
||||
self.term
|
||||
.set_background(Color::Default)
|
||||
.map_err(Error::TerminalError)?;
|
||||
}
|
||||
}
|
||||
|
||||
let name = self
|
||||
.buffer
|
||||
.name()
|
||||
.map(String::as_str)
|
||||
.unwrap_or("<unnamed>");
|
||||
write!(self.term, " {}", name).map_err(Error::TerminalFmtError)?;
|
||||
self.term
|
||||
.clear(Clear::LineToEnd)
|
||||
.map_err(Error::TerminalError)?;
|
||||
|
||||
self.term.reset_style().map_err(Error::TerminalError)?;
|
||||
|
||||
Ok(())
|
||||
pub fn cursor_to_buffer(&mut self) -> Result<(), Error> {
|
||||
let config = self.config.borrow();
|
||||
self.buffer.set_terminal_cursor(&config, &mut self.term)
|
||||
}
|
||||
|
||||
pub fn finish_display(&mut self) -> Result<(), Error> {
|
||||
let config = self.config.borrow();
|
||||
|
||||
match self.top_mode {
|
||||
TopMode::Normal => {
|
||||
self.buffer.set_terminal_cursor(&config, &mut self.term)?;
|
||||
}
|
||||
TopMode::Command => {
|
||||
self.term
|
||||
.set_cursor_position(self.buffer.height() + 1, 0)
|
||||
.map_err(Error::TerminalError)?;
|
||||
write!(self.term, ":{}", self.command.as_str()).map_err(Error::TerminalFmtError)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.term.flush().map_err(Error::TerminalError)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -224,12 +138,14 @@ impl State {
|
||||
|
||||
let (w, h) = self.term.size().map_err(Error::TerminalError)?;
|
||||
|
||||
if config.number.clean() {
|
||||
if config.number.clean() || config.bottom_margin.clean() {
|
||||
if *config.number {
|
||||
let nw = self.buffer.number_width() + 3;
|
||||
self.buffer.resize(&config, nw, w - nw - 1, h - 2);
|
||||
self.buffer
|
||||
.resize(&config, nw, w - nw - 1, h - (*config.bottom_margin + 1));
|
||||
} else {
|
||||
self.buffer.resize(&config, 0, w - 1, h - 2);
|
||||
self.buffer
|
||||
.resize(&config, 0, w - 1, h - (*config.bottom_margin + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,34 +156,11 @@ impl State {
|
||||
self.buffer
|
||||
.display(&config, &mut self.term, &self.highlight)?;
|
||||
|
||||
if self.top_mode != TopMode::Command
|
||||
&& let Some(status) = &self.status
|
||||
{
|
||||
self.term
|
||||
.set_cursor_position(self.buffer().height() + 1, 0)
|
||||
.map_err(Error::TerminalError)?;
|
||||
self.term
|
||||
.write_str(status.as_str())
|
||||
.map_err(Error::TerminalFmtError)?;
|
||||
}
|
||||
|
||||
if let Some(msg) = &self.message {
|
||||
self.term
|
||||
.set_cursor_position(self.buffer.height(), 0)
|
||||
.map_err(Error::TerminalError)?;
|
||||
self.term.write_str(msg).map_err(Error::TerminalFmtError)?;
|
||||
self.term.flush().map_err(Error::TerminalError)?;
|
||||
return Ok((w, h));
|
||||
}
|
||||
|
||||
self.display_modeline()?;
|
||||
|
||||
Ok((w, h))
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, target: SetMode) {
|
||||
self.top_mode = TopMode::Normal;
|
||||
self.message = None;
|
||||
self.buffer.set_mode(&self.config.borrow(), target);
|
||||
}
|
||||
|
||||
@@ -277,8 +170,6 @@ impl State {
|
||||
}
|
||||
|
||||
self.top_mode = target;
|
||||
self.message = None;
|
||||
self.command.clear();
|
||||
self.buffer.set_mode(&self.config.borrow(), SetMode::Normal);
|
||||
}
|
||||
|
||||
@@ -323,7 +214,10 @@ impl State {
|
||||
self.buffer.reopen(path).map_err(Error::OpenError)
|
||||
}
|
||||
|
||||
pub fn write_buffer<P: AsRef<Path>>(&mut self, path: Option<P>) -> Result<(), Error> {
|
||||
pub fn write_buffer<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
path: Option<P>,
|
||||
) -> Result<Option<String>, Error> {
|
||||
if path.is_some() && self.buffer.is_modified() && self.buffer.path().is_some() {
|
||||
return Err(Error::UnsavedBuffer(
|
||||
"Use :w! FILE to force write to another file",
|
||||
@@ -337,10 +231,10 @@ impl State {
|
||||
|
||||
if let Some(name) = self.buffer.name() {
|
||||
let status = format!("{name:?} written");
|
||||
self.set_status(status);
|
||||
Ok(Some(status))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn wait_for_events(&mut self) -> Result<TermKey, Error> {
|
||||
|
||||
Reference in New Issue
Block a user