Skip to content

Commit

Permalink
feat: Many partially implemented features (app, runtime, prog, layers)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpombrio committed Apr 9, 2024
1 parent d152f8f commit 194605d
Show file tree
Hide file tree
Showing 16 changed files with 638 additions and 130 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
ron = "0.8.1"
[dependencies.partial-pretty-printer]
#path = "../ppp/"
git = "https://github.com/justinpombrio/partial-pretty-printer"
version = "0.6.0"
version = "0.7.0"
features = ["serialization"]
[dependencies.no-nonsense-flamegraphs]
version = "0.2.*"
Expand Down
113 changes: 113 additions & 0 deletions src/editor/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use super::runtime::Runtime;
use super::stack::{CallStack, DataStack, Op, Prog};
use super::EditorError;
use crate::engine::Engine;
use crate::frontends::{Event, Frontend, Key, MouseEvent};
use crate::style::Style;
use crate::util::SynlessBug;
use std::time::Duration;

/*
* ## Control flow
*
* To execute a program in the interpreter (w/ call stack, data stack, engine):
*
* ```
* PUSH the program onto the call stack
* REPEAT pop op off call stack and execute it
* IF the op is `block` or the call stack becomes empty:
* THEN suspend the interpreter and return to the server.
* ```
*
* To run the server:
*
* ```
* SET the current keymap:
* IF there's a menu
* THEN use the menu's keymap
* ELSE use the current mode's tree or text keymap
* DISPLAY the screen
* BLOCK until a key is pressed
* THEN execute that key's prog in the interpreter
* ```
*
* To execute a KeyProg:
*
* ```
* IF push_string:
* THEN push selected string onto data stack
* IF exit_menu:
* THEN exit the menu
* EXECUTE program
* IF !exit_menu:
* THEN block
* ```
*/

struct App<F: Frontend<Style = Style>> {
runtime: Runtime<F>,
call_stack: CallStack,
data_stack: DataStack,
}

impl<F: Frontend<Style = Style>> App<F> {
pub fn run_event_loop(&mut self) {
loop {
if let Err(err) = self.runtime.display() {
self.abort(err);
}
let prog = match self.block_on_input() {
Ok(prog) => prog,
Err(err) => self.abort(err),
};
match self.execute(prog) {
Ok(()) => (),
Err(err) => self.runtime.engine.report_error(&err),
}
}
}

fn execute(&mut self, prog: Prog) -> Result<(), EditorError> {
self.call_stack.push(prog);
while let Some(op) = self.call_stack.pop() {
if op == Op::Block {
return Ok(());
} else {
self.call(op)?;
}
}
Ok(())
}

fn block_on_input(&mut self) -> Result<Prog, EditorError> {
use std::str::FromStr;

let ctrl_c = Key::from_str("C-c").bug();

loop {
match self.runtime.next_event()? {
// TODO: Remove Ctrl-c. It's only for testing.
Event::Key(ctrl_c) => return Err(EditorError::KeyboardInterrupt),
Event::Key(key) => {
if let Some(prog) = self.runtime.lookup_key(key) {
return Ok(prog);
}
// wait for a better key press
}
Event::Resize => self.runtime.display()?,
Event::Mouse(_) => (),
Event::Paste(_) => (), // TODO: OS paste support
}
}
}

fn call(&mut self, op: Op) -> Result<(), EditorError> {
todo!()
}

fn abort(&mut self, err: EditorError) -> ! {
self.runtime.engine.report_error(&err);
// TODO: Save the error log, and maybe the docs
panic!("{}", err);
}
}
4 changes: 1 addition & 3 deletions src/editor/keymap.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use super::stack::Prog;
use crate::frontends::Key;
use crate::util::OrderedMap;
use crate::util::SynlessBug;
use std::borrow::Borrow;
use std::collections::HashMap;

#[derive(Clone)]
pub struct Prog {}

/// Key bindings.
///
/// All methods that add bindings will overwrite existing bindings.
Expand Down
63 changes: 63 additions & 0 deletions src/editor/layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use super::keymap::Keymap;
use crate::tree::Mode;
use crate::util::IndexedMap;
use std::collections::HashMap;

type MenuName = String;
type LayerIndex = usize;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum KeymapLabel {
Menu(MenuName),
Mode(Mode),
}

pub struct Layer {
name: String,
keymaps: HashMap<KeymapLabel, Keymap>,
}

impl Layer {
pub fn new(name: String) -> Layer {
Layer {
name,
keymaps: HashMap::new(),
}
}

pub fn add_menu(&mut self, menu_name: MenuName, keymap: Keymap) {
self.keymaps.insert(KeymapLabel::Menu(menu_name), keymap);
}

pub fn add_mode(&mut self, mode: Mode, keymap: Keymap) {
self.keymaps.insert(KeymapLabel::Mode(mode), keymap);
}
}

// TODO: Have LayerManager track DocName -> local_layers?
//
// local: Buffer -> Vec<LayerIndex>
// global: Vec<LayerIndex>
//
// order: buffer, global

pub struct LayerManager {
active_local_layers: Vec<LayerIndex>,
active_global_layers: Vec<LayerIndex>,
active_menu: Option<MenuName>,
layers: IndexedMap<Layer>,
}

impl LayerManager {
pub fn add_layer(&mut self, layer: Layer) {
self.layers.insert(layer.name.clone(), layer);
}

pub fn enter_menu(&mut self, menu_name: String) {
self.active_menu = Some(menu_name);
}

pub fn exit_menu(&mut self) {
self.active_menu = None;
}
}
25 changes: 25 additions & 0 deletions src/editor/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
mod app;
mod keymap;
mod layer;
mod runtime;
mod stack;

use crate::engine::EngineError;
use crate::frontends::Frontend;
use std::error::Error;
use std::fmt;

#[derive(thiserror::Error, fmt::Debug)]
pub enum EditorError {
#[error("Error from the document engine")]
EngineError(#[from] EngineError),
#[error("Attempted to pop an empty data stack")]
EmptyDataStack,
#[error("Expected type '{expected}' but found '{actual}'")]
TypeMismatch { actual: String, expected: String },
#[error("I was rudely interrupted by Ctrl-C")]
KeyboardInterrupt,
#[error("Frontend error")]
FrontendError(#[source] Box<dyn Error>),
#[error("Pane error")]
PaneError(#[source] Box<dyn Error>),
}
47 changes: 47 additions & 0 deletions src/editor/runtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use super::stack::Prog;
use super::EditorError;
use crate::engine::{DocDisplayLabel, Engine};
use crate::frontends::{Event, Frontend, Key};
use crate::style::Style;
use partial_pretty_printer as ppp;
use partial_pretty_printer::pane;
use std::error::Error;
use std::time::Duration;

pub struct Runtime<F: Frontend<Style = Style>> {
pub engine: Engine,
pane_notation: pane::PaneNotation<DocDisplayLabel, Style>,
frontend: F,
}

impl<F: Frontend<Style = Style>> Runtime<F> {
/// Block until the next input event.
pub fn next_event(&mut self) -> Result<Event, EditorError> {
loop {
match self.frontend.next_event(Duration::from_secs(1)) {
Ok(None) => (), // continue waiting
Ok(Some(event)) => return Ok(event),
Err(err) => return Err(EditorError::FrontendError(Box::new(err))),
}
}
}

pub fn display(&mut self) -> Result<(), EditorError> {
self.frontend
.start_frame()
.map_err(|err| EditorError::FrontendError(Box::new(err)))?;

let get_content = |doc_label| self.engine.get_content(doc_label);
pane::display_pane(&mut self.frontend, &self.pane_notation, &get_content)
.map_err(|err| EditorError::PaneError(Box::new(err)))?;

self.frontend
.end_frame()
.map_err(|err| EditorError::FrontendError(Box::new(err)))
}

pub fn lookup_key(&self, key: Key) -> Option<Prog> {
// TODO: must handle char insertion in text mode
todo!()
}
}
Loading

0 comments on commit 194605d

Please sign in to comment.