-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
use egui::{text_edit::CCursorRange, *}; | ||
|
||
#[derive(serde::Deserialize, serde::Serialize)] | ||
pub struct CodeEditor { | ||
code: String, | ||
highlight_editor: bool, | ||
show_rendered: bool, | ||
} | ||
|
||
impl PartialEq for CodeEditor { | ||
fn eq(&self, other: &Self) -> bool { | ||
(&self.code, self.highlight_editor, self.show_rendered) | ||
== (&other.code, other.highlight_editor, other.show_rendered) | ||
} | ||
} | ||
|
||
impl Default for CodeEditor { | ||
fn default() -> Self { | ||
Self { | ||
code: DEFAULT_CODE.trim().to_owned(), | ||
highlight_editor: true, | ||
show_rendered: true, | ||
} | ||
} | ||
} | ||
|
||
impl CodeEditor { | ||
pub fn panels(&mut self, ctx: &egui::Context) { | ||
egui::TopBottomPanel::bottom("easy_mark_bottom").show(ctx, |ui| { | ||
Check warning on line 29 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 29 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 29 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true); | ||
Check warning on line 30 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 30 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 30 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
}); | ||
|
||
egui::CentralPanel::default().show(ctx, |ui| { | ||
self.ui(ui); | ||
}); | ||
} | ||
|
||
pub fn ui(&mut self, ui: &mut egui::Ui) { | ||
egui::Grid::new("controls").show(ui, |ui| { | ||
let _ = ui.button("Hotkeys").on_hover_ui(nested_hotkeys_ui); | ||
ui.checkbox(&mut self.show_rendered, "Show rendered"); | ||
ui.checkbox(&mut self.highlight_editor, "Highlight editor"); | ||
egui::reset_button(ui, self); | ||
ui.end_row(); | ||
}); | ||
ui.separator(); | ||
|
||
if self.show_rendered { | ||
ui.columns(2, |columns| { | ||
ScrollArea::vertical() | ||
.id_source("source") | ||
.show(&mut columns[0], |ui| self.editor_ui(ui)); | ||
ScrollArea::vertical() | ||
.id_source("rendered") | ||
.show(&mut columns[1], |ui| {}); | ||
Check warning on line 55 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 55 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 55 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
}); | ||
} else { | ||
ScrollArea::vertical() | ||
.id_source("source") | ||
.show(ui, |ui| self.editor_ui(ui)); | ||
} | ||
} | ||
|
||
fn editor_ui(&mut self, ui: &mut egui::Ui) {} | ||
Check warning on line 64 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 64 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 64 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
} | ||
|
||
pub const SHORTCUT_BOLD: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::B); | ||
pub const SHORTCUT_CODE: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::N); | ||
pub const SHORTCUT_ITALICS: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::I); | ||
pub const SHORTCUT_SUBSCRIPT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::L); | ||
pub const SHORTCUT_SUPERSCRIPT: KeyboardShortcut = | ||
KeyboardShortcut::new(Modifiers::COMMAND, Key::Y); | ||
pub const SHORTCUT_STRIKETHROUGH: KeyboardShortcut = | ||
KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::Q); | ||
pub const SHORTCUT_UNDERLINE: KeyboardShortcut = | ||
KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::W); | ||
pub const SHORTCUT_INDENT: KeyboardShortcut = | ||
KeyboardShortcut::new(Modifiers::CTRL.plus(Modifiers::SHIFT), Key::E); | ||
|
||
fn nested_hotkeys_ui(ui: &mut egui::Ui) { | ||
egui::Grid::new("shortcuts").striped(true).show(ui, |ui| { | ||
let mut label = |shortcut, what| { | ||
ui.label(what); | ||
ui.weak(ui.ctx().format_shortcut(&shortcut)); | ||
ui.end_row(); | ||
}; | ||
|
||
label(SHORTCUT_BOLD, "*bold*"); | ||
label(SHORTCUT_CODE, "`code`"); | ||
label(SHORTCUT_ITALICS, "/italics/"); | ||
label(SHORTCUT_SUBSCRIPT, "$subscript$"); | ||
label(SHORTCUT_SUPERSCRIPT, "^superscript^"); | ||
label(SHORTCUT_STRIKETHROUGH, "~strikethrough~"); | ||
label(SHORTCUT_UNDERLINE, "_underline_"); | ||
label(SHORTCUT_INDENT, "two spaces"); // Placeholder for tab indent | ||
}); | ||
} | ||
|
||
fn shortcuts(ui: &Ui, code: &mut dyn TextBuffer, ccursor_range: &mut CCursorRange) -> bool { | ||
Check warning on line 99 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 99 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 99 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
let mut any_change = false; | ||
|
||
if ui.input_mut(|i| i.consume_shortcut(&SHORTCUT_INDENT)) { | ||
// This is a placeholder till we can indent the active line | ||
any_change = true; | ||
let [primary, _secondary] = ccursor_range.sorted(); | ||
|
||
let advance = code.insert_text(" ", primary.index); | ||
ccursor_range.primary.index += advance; | ||
ccursor_range.secondary.index += advance; | ||
} | ||
|
||
for (shortcut, surrounding) in [ | ||
(SHORTCUT_BOLD, "*"), | ||
(SHORTCUT_CODE, "`"), | ||
(SHORTCUT_ITALICS, "/"), | ||
(SHORTCUT_SUBSCRIPT, "$"), | ||
(SHORTCUT_SUPERSCRIPT, "^"), | ||
(SHORTCUT_STRIKETHROUGH, "~"), | ||
(SHORTCUT_UNDERLINE, "_"), | ||
] { | ||
if ui.input_mut(|i| i.consume_shortcut(&shortcut)) { | ||
any_change = true; | ||
toggle_surrounding(code, ccursor_range, surrounding); | ||
}; | ||
} | ||
|
||
any_change | ||
} | ||
|
||
/// E.g. toggle *strong* with `toggle_surrounding(&mut text, &mut cursor, "*")` | ||
fn toggle_surrounding( | ||
Check warning on line 131 in src/code_editor/editor.rs GitHub Actions / Check wasm32
Check warning on line 131 in src/code_editor/editor.rs GitHub Actions / Check
Check warning on line 131 in src/code_editor/editor.rs GitHub Actions / Test Suite
|
||
code: &mut dyn TextBuffer, | ||
ccursor_range: &mut CCursorRange, | ||
surrounding: &str, | ||
) { | ||
let [primary, secondary] = ccursor_range.sorted(); | ||
|
||
let surrounding_ccount = surrounding.chars().count(); | ||
|
||
let prefix_crange = primary.index.saturating_sub(surrounding_ccount)..primary.index; | ||
let suffix_crange = secondary.index..secondary.index.saturating_add(surrounding_ccount); | ||
let already_surrounded = code.char_range(prefix_crange.clone()) == surrounding | ||
&& code.char_range(suffix_crange.clone()) == surrounding; | ||
|
||
if already_surrounded { | ||
code.delete_char_range(suffix_crange); | ||
code.delete_char_range(prefix_crange); | ||
ccursor_range.primary.index -= surrounding_ccount; | ||
ccursor_range.secondary.index -= surrounding_ccount; | ||
} else { | ||
code.insert_text(surrounding, secondary.index); | ||
let advance = code.insert_text(surrounding, primary.index); | ||
|
||
ccursor_range.primary.index += advance; | ||
ccursor_range.secondary.index += advance; | ||
} | ||
} | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
const DEFAULT_CODE: &str = r#" | ||
# EasyMark | ||
EasyMark is a markup language, designed for extreme simplicity. | ||
``` | ||
WARNING: EasyMark is still an evolving specification, | ||
and is also missing some features. | ||
``` | ||
---------------- | ||
# At a glance | ||
- inline text: | ||
- normal, `code`, *strong*, ~strikethrough~, _underline_, /italics/, ^raised^, $small$ | ||
- `\` escapes the next character | ||
- [hyperlink](https://github.com/emilk/egui) | ||
- Embedded URL: <https://github.com/emilk/egui> | ||
- `# ` header | ||
- `---` separator (horizontal line) | ||
- `> ` quote | ||
- `- ` bullet list | ||
- `1. ` numbered list | ||
- \`\`\` code fence | ||
- a^2^ + b^2^ = c^2^ | ||
- $Remember to read the small print$ | ||
# Design | ||
> /"Why do what everyone else is doing, when everyone else is already doing it?" | ||
> \- Emil | ||
Goals: | ||
1. easy to parse | ||
2. easy to learn | ||
3. similar to markdown | ||
[The reference parser](https://github.com/emilk/egui/blob/master/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs) is \~250 lines of code, using only the Rust standard library. The parser uses no look-ahead or recursion. | ||
There is never more than one way to accomplish the same thing, and each special character is only used for one thing. For instance `*` is used for *strong* and `-` is used for bullet lists. There is no alternative way to specify the *strong* style or getting a bullet list. | ||
Similarity to markdown is kept when possible, but with much less ambiguity and some improvements (like _underlining_). | ||
# Details | ||
All style changes are single characters, so it is `*strong*`, NOT `**strong**`. Style is reset by a matching character, or at the end of the line. | ||
Style change characters and escapes (`\`) work everywhere except for in inline code, code blocks and in URLs. | ||
You can mix styles. For instance: /italics _underline_/ and *strong `code`*. | ||
You can use styles on URLs: ~my webpage is at <http://www.example.com>~. | ||
Newlines are preserved. If you want to continue text on the same line, just do so. Alternatively, escape the newline by ending the line with a backslash (`\`). \ | ||
Escaping the newline effectively ignores it. | ||
The style characters are chosen to be similar to what they are representing: | ||
`_` = _underline_ | ||
`~` = ~strikethrough~ (`-` is used for bullet points) | ||
`/` = /italics/ | ||
`*` = *strong* | ||
`$` = $small$ | ||
`^` = ^raised^ | ||
# TODO | ||
- Sub-headers (`## h2`, `### h3` etc) | ||
- Hotkey Editor | ||
- International keyboard algorithm for non-letter keys | ||
- ALT+SHIFT+Num1 is not a functioning hotkey | ||
- Tab Indent Increment/Decrement CTRL+], CTRL+[ | ||
- Images | ||
- we want to be able to optionally specify size (width and\/or height) | ||
- centering of images is very desirable | ||
- captioning (image with a text underneath it) | ||
- `![caption=My image][width=200][center](url)` ? | ||
- Nicer URL:s | ||
- `<url>` and `[url](url)` do the same thing yet look completely different. | ||
- let's keep similarity with images | ||
- Tables | ||
- Inspiration: <https://mycorrhiza.lesarbr.es/page/mycomarkup> | ||
"#; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod editor; | ||
|
||
pub use editor::CodeEditor; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
#![warn(clippy::all, rust_2018_idioms)] | ||
|
||
mod wrap_app; | ||
pub use wrap_app::CodeChallengeApp; | ||
pub use wrap_app::WrapApp; | ||
|
||
pub mod apps; | ||
pub mod code_editor; | ||
pub mod components; | ||
pub mod helpers; |