Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft concatenative language for demo editor #17

Merged
merged 37 commits into from
Jul 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
287fe93
feat: draft stack-based commands for demo editor
e-matteson Jun 10, 2019
e01125a
feat: support a stack of keymaps
e-matteson Jun 11, 2019
c94b605
feat: print crappy keymap summary, split demo into multiple files
e-matteson Jun 11, 2019
1c0f4e9
fix: don't clone trees, to prevent 'write lock' panic
e-matteson Jun 12, 2019
01c1a04
chore: move demo editor to new crate
e-matteson Jun 12, 2019
7ec506b
refactor: rename Thing->Word
e-matteson Jun 12, 2019
d726f44
refactor: add Stack struct with typed popping methods
e-matteson Jun 12, 2019
a47b593
chore: return Language from make_json_lang(), not LanguageSet
e-matteson Jun 12, 2019
d069326
refactor: clean up lang set and notation set handling
e-matteson Jun 12, 2019
d2c225c
feat: highlight cursor region
e-matteson Jun 12, 2019
ff2b963
fix: don't panic when accessing text
e-matteson Jun 12, 2019
3e792c0
feat: get Doc's mode
e-matteson Jun 12, 2019
93c7cb4
test: update string test (still fails, now a bounds error)
e-matteson Jun 12, 2019
37b0b4e
feat: add text-mode char insertion example to demo editor
e-matteson Jun 12, 2019
74c5beb
refactor: move demo editor Error to new file
e-matteson Jun 12, 2019
71caddb
fix: attempt to correctly implement Clone for Tree
e-matteson Jun 12, 2019
ba807ce
Revert "fix: don't clone trees, to prevent 'write lock' panic"
e-matteson Jun 12, 2019
bdd12da
feat: impl EditorCmd::Cut, return result + optional Ast from execute
e-matteson Jun 12, 2019
1374361
feat: demo basic cut and paste
e-matteson Jun 12, 2019
cad3cc2
feat: implement EditorCmd::Copy
e-matteson Jun 12, 2019
b2407c4
feat: demo copy, plus all paste and insert varieties
e-matteson Jun 12, 2019
323b6f4
feat: impl Debug for Ast
e-matteson Jun 12, 2019
745a086
refactor: derive Debug in more places
e-matteson Jun 12, 2019
247ff61
feat: access Language's keymap
e-matteson Jun 12, 2019
2784554
feat: add Quote, Apply, and Swap
e-matteson Jun 12, 2019
5365e12
feat: implement node selection as a nested keymap
e-matteson Jun 12, 2019
0ed4cd3
feat: print nicer keymap summary
e-matteson Jun 12, 2019
5ee045d
feat: clarify keymap names
e-matteson Jun 12, 2019
865522c
feat: allow cancelling node selection
e-matteson Jun 12, 2019
6703af1
feat: return more results instead of panicking
e-matteson Jun 12, 2019
312c46b
feat: pass clipboard to doc.execute() instead of returning cut Ast
e-matteson Jun 29, 2019
b8f9920
feat: handle paste commands in doc, not in demo editor
e-matteson Jun 29, 2019
fb02dcc
refactor: rename is_tree_mode() and in_tree_mode()
e-matteson Jul 21, 2019
488c975
refactor: use a Clipboard newtype instead of a Vec
e-matteson Jul 21, 2019
170071a
refactor: add Forest::clone_tree(), make children() return owned Ids
e-matteson Jul 21, 2019
a8da9b3
refactor: delete broken & unused to_owned_tree()
e-matteson Jul 21, 2019
d844ca3
refactor: delete unused nightly `iter_copied` feature
e-matteson Jul 21, 2019
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
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "synless"
version = "0.0.1"
authors = ["justinpombrio <[email protected]>"]
authors = ["justinpombrio <[email protected]>", "e-matteson <[email protected]>"]

[workspace]
members = [
Expand All @@ -10,11 +10,13 @@ members = [
"pretty",
"language",
"frontends",
"editor"]
"editor",
"demo"]
default-members = [
"utility",
"forest",
"pretty",
"language",
"frontends",
"editor"]
"editor",
"demo"]
15 changes: 15 additions & 0 deletions demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "demo"
version = "0.1.0"
authors = ["e-matteson <[email protected]>"]
edition = "2018"

[dependencies]
editor = { path = "../editor" }
forest = { path = "../forest" }
pretty = { path = "../pretty" }
language = { path = "../language" }
frontends = { path = "../frontends"}
utility = { path = "../utility"}
lazy_static = "*"
termion = "1.5"
36 changes: 36 additions & 0 deletions demo/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::io;
use termion::event::Key;

use frontends::terminal;
use language::{ConstructName, LanguageName};

#[derive(Debug)]
pub enum Error {
UnknownKey(Key),
UnknownKeymap(String),
NoKeymap,
UnknownEvent,
KeyboardInterrupt,
UnknownLang(LanguageName),
UnknownConstruct {
construct: ConstructName,
lang: LanguageName,
},
ExpectedWord(String),
EmptyStack,
DocExec(String),
Io(io::Error),
Term(terminal::Error),
}

impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::Io(e)
}
}

impl From<terminal::Error> for Error {
fn from(e: terminal::Error) -> Error {
Error::Term(e)
}
}
212 changes: 212 additions & 0 deletions demo/src/keymap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use std::collections::HashMap;
use std::iter;
use termion::event::Key;

use crate::prog::{Prog, Word};
use language::{Language, LanguageName};

pub struct Keymap<'l>(pub HashMap<Key, Prog<'l>>);

impl<'l> Keymap<'l> {
pub fn node(lang: &Language) -> Self {
Keymap(
lang.keymap()
.iter()
.map(|(&ch, construct_name)| {
(
Key::Char(ch),
Prog::named(
construct_name,
&[
// Push the new node onto the stack, and then
// apply the quoted command (eg. InsertAfter)
// that was already on the top of the stack.
Word::LangConstruct(lang.name().into(), construct_name.to_owned()),
Word::NodeByName,
Word::Swap,
Word::Apply,
Word::PopMap,
],
),
)
})
.chain(iter::once((
Key::Esc,
Prog::named(
"Cancel",
&[
Word::Pop, // get rid of the quoted command on the stack
Word::PopMap,
],
),
)))
.collect(),
)
}

pub fn tree() -> Self {
let map = vec![
(Key::Char('d'), Prog::single(Word::Cut)),
(Key::Char('y'), Prog::single(Word::Copy)),
(Key::Char('p'), Prog::single(Word::PasteAfter)),
(Key::Char('P'), Prog::single(Word::PasteBefore)),
(Key::Ctrl('p'), Prog::single(Word::PastePrepend)),
(Key::Alt('p'), Prog::single(Word::PastePostpend)),
(Key::Char('R'), Prog::single(Word::PasteReplace)),
(
Key::Char('a'),
Prog::named("TypeA", &[Word::Char('a'), Word::InsertChar]),
),
(Key::Char('u'), Prog::single(Word::Undo)),
(Key::Ctrl('r'), Prog::single(Word::Redo)),
(Key::Right, Prog::single(Word::Right)),
(Key::Left, Prog::single(Word::Left)),
(Key::Up, Prog::single(Word::Parent)),
(Key::Backspace, Prog::single(Word::Remove)),
(
Key::Down,
Prog::named("Child", &[Word::Usize(0), Word::Child]),
),
(
Key::Char('i'),
Prog::named(
"InsertAfter",
&[
Word::InsertAfter.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('I'),
Prog::named(
"InsertBefore",
&[
Word::InsertBefore.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('o'),
Prog::named(
"InsertPostpend",
&[
Word::InsertPostpend.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('O'),
Prog::named(
"InsertPrepend",
&[
Word::InsertPrepend.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('r'),
Prog::named(
"Replace",
&[
Word::Replace.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char(' '),
Prog::named(
"SpeedBoolMode",
&[Word::MapName("speed_bool".into()), Word::PushMap],
),
),
]
.into_iter()
.collect();

Keymap(map)
}

pub fn speed_bool() -> Self {
let lang: LanguageName = "json".into();
let map = vec![
(
Key::Char('t'),
Prog::named(
"True",
&[
Word::LangConstruct(lang.clone(), "true".into()),
Word::NodeByName,
Word::InsertAfter,
],
),
),
(
Key::Char('f'),
Prog::named(
"False",
&[
Word::LangConstruct(lang, "false".into()),
Word::NodeByName,
Word::InsertAfter,
],
),
),
(Key::Esc, Prog::named("Exit", &[Word::PopMap])),
]
.into_iter()
.collect();

Keymap(map)
}

pub fn summary(&self) -> String {
let mut s = String::new();
for (key, prog) in &self.0 {
let prog_name = if let Some(ref name) = prog.name {
name.to_string()
} else if prog.words.len() == 1 {
format!("{:?}", prog.words[0])
} else {
"...".into()
};
s += &format!("{}:{}, ", self.format_key(key), prog_name);
}
s
}

fn format_key(&self, key: &Key) -> String {
match key {
Key::Backspace => "Bksp".to_string(),
Key::Left => "←".to_string(),
Key::Right => "→".to_string(),
Key::Up => "↑".to_string(),
Key::Down => "↓".to_string(),
Key::Home => "Home".to_string(),
Key::End => "End".to_string(),
Key::PageUp => "PgUp".to_string(),
Key::PageDown => "PgDn".to_string(),
Key::Delete => "Del".to_string(),
Key::Insert => "Ins".to_string(),
Key::F(num) => format!("F{}", num),
Key::Char(' ') => "Spc".to_string(),
Key::Char(c) => c.to_string(),
Key::Alt(' ') => "A-Spc".to_string(),
Key::Alt(c) => format!("A-{}", c),
Key::Ctrl(' ') => "C-Spc".to_string(),
Key::Ctrl(c) => format!("C-{}", c),
Key::Null => "Null".to_string(),
Key::Esc => "Esc".to_string(),
_ => "(unknown)".to_string(),
}
}
}
70 changes: 70 additions & 0 deletions demo/src/keymap_lang.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::NotationSet;
use language::{Arity, Construct, Language};
use pretty::{child, literal, no_wrap, repeat, text, Notation, Repeat, Style};

pub fn make_keymap_lang() -> (Language, NotationSet) {
let notations = vec![
("key".into(), key()),
("prog".into(), prog()),
("entry".into(), entry()),
("dict".into(), dict()),
];
let constructs = vec![
Construct::new("key", "Key", Arity::Text, Some('k')),
Construct::new("prog", "Value", Arity::Text, Some('p')),
Construct::new(
"dict",
"Dict",
Arity::Flexible("Entry".to_string()),
Some('d'),
),
Construct::new(
"entry",
"Entry",
Arity::Fixed(vec!["Key".to_string(), "Value".to_string()]),
Some('e'),
),
];
// TODO: some of this boilerplate should get abstracted out
let mut lang = Language::new("keymap");
for construct in constructs {
lang.add(construct);
}
let note_set = NotationSet::new(&lang, notations);
(lang, note_set)
}

fn key() -> Notation {
let style = Style::plain();
text(style)
}

fn prog() -> Notation {
let style = Style::plain();
text(style)
}

/// Try putting the key and value on the same line.
/// If they don't fit, wrap after the colon, and indent the value.
fn entry() -> Notation {
no_wrap(child(0) + punct(": ") + child(1)) | (child(0) + punct(":") ^ indent() + child(1))
}

/// Put all entries on separate lines.
/// If there is more than one entry, put the opening and closing delimiters on separate lines too.
fn dict() -> Notation {
repeat(Repeat {
empty: punct("{}"),
lone: punct("{") + child(0) + punct("}"),
join: child(0) + punct(",") ^ child(1),
surround: punct("{") ^ indent() + child(0) ^ punct("}"),
})
}

fn punct(text: &str) -> Notation {
literal(text, Style::plain())
}

fn indent() -> Notation {
literal(" ", Style::plain())
}
Loading