Get editor showing (not yet usable) on #editor
This commit is contained in:
parent
56edc3d484
commit
4585db0433
40
index.html
40
index.html
@ -1,11 +1,51 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"codemirror": "./codemirror/index.js",
|
||||
"@codemirror/view": "./@codemirror/view/index.js",
|
||||
"@codemirror/state": "./@codemirror/state/index.js",
|
||||
"@codemirror/language": "./@codemirror/language/index.js",
|
||||
"@codemirror/legacy-modes/mode/lua": "./@codemirror/legacy-modes/mode/lua.js",
|
||||
"@codemirror/commands": "./@codemirror/commands/index.js",
|
||||
"@codemirror/search": "./@codemirror/search/index.js",
|
||||
"@codemirror/autocomplete": "./@codemirror/autocomplete/index.js",
|
||||
"@codemirror/lint": "./@codemirror/lint/index.js",
|
||||
"@lezer/common": "./@lezer/common/index.js",
|
||||
"@lezer/highlight": "./@lezer/highlight/index.js",
|
||||
"@lezer/lr": "./@lezer/lr/index.js",
|
||||
"style-mod": "./style-mod/style-mod.js",
|
||||
"w3c-keyname": "./w3c-keyname/index.js",
|
||||
"crelt": "./crelt/index.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<link data-trunk rel="css" href="node_modules/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<link data-trunk rel="css" href="node_modules/bootstrap-icons/font/bootstrap-icons.min.css"/>
|
||||
<link data-trunk rel="css" href="node_modules/@xterm/xterm/css/xterm.css"/>
|
||||
<script data-trunk src="node_modules/@xterm/xterm/lib/xterm.js"></script>
|
||||
<script data-trunk src="node_modules/@xterm/addon-fit/lib/addon-fit.js"></script>
|
||||
<link data-trunk rel="css" href="styles.css"/>
|
||||
<link data-trunk rel="copy-file" href="assets/fonts/JetBrainsMono-Regular.woff2"/>
|
||||
<link data-trunk rel="copy-file" href="node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff" data-target-path="fonts"/>
|
||||
<link data-trunk rel="copy-file" href="node_modules/codemirror/dist/index.js" data-target-path="codemirror"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/view/dist/index.js" data-target-path="@codemirror/view"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/state/dist/index.js" data-target-path="@codemirror/state"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/language/dist/index.js" data-target-path="@codemirror/language"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/legacy-modes/mode/lua.js" data-target-path="@codemirror/legacy-modes/mode"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/commands/dist/index.js" data-target-path="@codemirror/commands"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/autocomplete/dist/index.js" data-target-path="@codemirror/autocomplete"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/search/dist/index.js" data-target-path="@codemirror/search"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@codemirror/lint/dist/index.js" data-target-path="@codemirror/lint"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@lezer/common/dist/index.js" data-target-path="@lezer/common"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@lezer/highlight/dist/index.js" data-target-path="@lezer/highlight"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/@lezer/lr/dist/index.js" data-target-path="@lezer/lr"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/style-mod/src/style-mod.js" data-target-path="style-mod"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/crelt/index.js" data-target-path="crelt"></link>
|
||||
<link data-trunk rel="copy-file" href="node_modules/w3c-keyname/index.js" data-target-path="w3c-keyname"></link>
|
||||
<script type="module" src="codemirror/index.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
184
package-lock.json
generated
184
package-lock.json
generated
@ -9,8 +9,128 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0"
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"codemirror": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz",
|
||||
"integrity": "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.2.tgz",
|
||||
"integrity": "sha512-Fq7eWOl1Rcbrfn6jD8FPCj9Auaxdm5nIK5RYOeW7ughnd/rY5AmPg6b+CfsG39ZHdwiwe8lde3q8uR7CF5S0yQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.27.0",
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz",
|
||||
"integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@lezer/common": "^1.1.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0",
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/legacy-modes": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.1.tgz",
|
||||
"integrity": "sha512-vdg3XY7OAs5uLDx2Iw+cGfnwtd7kM+Et/eMsqAGTfT/JKiVBQZXosTzjEbWAi/FrY6DcQIz8mQjBozFHZEUWQA==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.2.tgz",
|
||||
"integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
|
||||
"integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
|
||||
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.1.tgz",
|
||||
"integrity": "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.2.tgz",
|
||||
"integrity": "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
|
||||
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
|
||||
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-fit": {
|
||||
@ -25,6 +145,68 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.11.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
|
||||
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,12 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0"
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"codemirror": "^6.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
echo_to_term_frame,
|
||||
lua_engine::LuaState,
|
||||
parsing::{parse_commands, ParsedCommand},
|
||||
GlobalMemoCell, TermFrame,
|
||||
FrameId, GlobalMemoCell,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use wasm_bindgen::JsValue;
|
||||
@ -20,7 +20,7 @@ pub fn debrace(inp: &str) -> &str {
|
||||
fn reentrant_command_handler(
|
||||
lua_state: &mut LuaState,
|
||||
globals: &GlobalMemoCell,
|
||||
term_frame: &TermFrame,
|
||||
term_frame: &FrameId,
|
||||
commands_in: &[ParsedCommand],
|
||||
) {
|
||||
lua_state.set_current_frame(term_frame);
|
||||
@ -68,7 +68,7 @@ fn reentrant_command_handler(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_handler(globals: &GlobalMemoCell, term_frame: &TermFrame, command_in: &str) {
|
||||
pub fn command_handler(globals: &GlobalMemoCell, term_frame: &FrameId, command_in: &str) {
|
||||
echo_to_term_frame(globals, term_frame, "\r").unwrap_or(());
|
||||
{
|
||||
let mut cq = globals.command_queue.borrow_mut();
|
||||
|
164
src/editor_view.rs
Normal file
164
src/editor_view.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use std::{ops::Deref, rc::Rc};
|
||||
|
||||
use anyhow::Error;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
use web_sys::{window, Node};
|
||||
use yew::{
|
||||
function_component, html, use_effect_with, use_node_ref, use_state_eq, AttrValue, Callback,
|
||||
Html, Properties, UseStateHandle,
|
||||
};
|
||||
|
||||
use crate::{FrameId, GlobalLayoutState, GlobalMemoCell, PanelDirection, SplitPanel};
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct EditorViewProps {
|
||||
pub frame: FrameId,
|
||||
pub global_memo: GlobalMemoCell,
|
||||
pub global_layout: UseStateHandle<Rc<GlobalLayoutState>>,
|
||||
}
|
||||
|
||||
fn close_editor(frame: &FrameId, global_layout: &UseStateHandle<Rc<GlobalLayoutState>>) {
|
||||
let mut gl_new: GlobalLayoutState = global_layout.as_ref().clone();
|
||||
gl_new.frame_views.remove(frame);
|
||||
global_layout.set(gl_new.into());
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct EditorState {
|
||||
error_msg: Option<AttrValue>,
|
||||
available_files: Vec<AttrValue>,
|
||||
}
|
||||
|
||||
fn fetch_initial_editor_state_or_fail() -> anyhow::Result<EditorState> {
|
||||
let win = window().ok_or_else(|| Error::msg("Can't get window"))?;
|
||||
win.local_storage()
|
||||
.map_err(|_| Error::msg("Error retrieving localStorage"))?
|
||||
.ok_or_else(|| Error::msg("Local storage not available"))?;
|
||||
Ok(EditorState {
|
||||
error_msg: None,
|
||||
available_files: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_initial_editor_state() -> EditorState {
|
||||
match fetch_initial_editor_state_or_fail() {
|
||||
Ok(s) => s,
|
||||
Err(e) => EditorState {
|
||||
error_msg: Some(format!("Can't load editor data: {}", e.to_string()).into()),
|
||||
available_files: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(EditorNav)]
|
||||
fn editor_nav(props: &EditorViewProps) -> Html {
|
||||
let global_layout = props.global_layout.clone();
|
||||
let frame = props.frame.clone();
|
||||
html! {
|
||||
<div class="editornav">
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<button class="btn" aria-label="New file" title="New file"><i class="bi bi-file-earmark-plus"></i></button>
|
||||
<div class="flex-fill"/>
|
||||
<button class="btn" onclick={move |_ev| close_editor(&frame, &global_layout)} aria-label="Close Editor" title="Close editor"><i class="bi bi-arrow-return-left"></i></button>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
struct EditorViewConfig {
|
||||
pub doc: String,
|
||||
pub extensions: Vec<CMExtension>,
|
||||
pub parent: Node,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = codemirror)]
|
||||
extern "C" {
|
||||
type EditorView;
|
||||
#[derive(Clone)]
|
||||
type CMExtension;
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
fn new(settings: EditorViewConfig) -> EditorView;
|
||||
|
||||
#[wasm_bindgen(js_name = basicSetup, thread_local)]
|
||||
static BASIC_SETUP: CMExtension;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = "@codemirror/legacy-modes/mode/lua")]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = lua, thread_local)]
|
||||
static LUA: StreamLanguage;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = "@codemirror/language")]
|
||||
extern "C" {
|
||||
#[derive(Clone)]
|
||||
type StreamLanguage;
|
||||
|
||||
#[wasm_bindgen(js_namespace = StreamLanguage, js_name = define)]
|
||||
fn streamlanguage_define(input: StreamLanguage) -> CMExtension;
|
||||
}
|
||||
|
||||
#[function_component(EditorArea)]
|
||||
fn editor_area(props: &EditorViewProps) -> Html {
|
||||
let node_ref = use_node_ref();
|
||||
use_effect_with(node_ref.clone(), |node_ref| match node_ref.get() {
|
||||
None => {}
|
||||
Some(node) => {
|
||||
EditorView::new(EditorViewConfig {
|
||||
doc: "Hello World".to_owned(),
|
||||
extensions: vec![
|
||||
BASIC_SETUP.with(CMExtension::clone),
|
||||
StreamLanguage::streamlanguage_define(LUA.with(StreamLanguage::clone)),
|
||||
],
|
||||
parent: node,
|
||||
});
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div class="editorarea" ref={node_ref.clone()}>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct ErrorBarProps {
|
||||
pub msg: AttrValue,
|
||||
pub dismiss: Callback<()>,
|
||||
}
|
||||
|
||||
#[function_component(ErrorBar)]
|
||||
fn error_bar(props: &ErrorBarProps) -> Html {
|
||||
let props = props.clone();
|
||||
html! {
|
||||
<div class="errorbar alert alert-danger alert-dismissible" role="alert">
|
||||
<span class="errorbar-msg">{props.msg.clone()}</span>
|
||||
<button class="btn-close" aria-label="Close" onclick={move |_ev| props.dismiss.emit(())}></button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(CodeEditorView)]
|
||||
pub fn editor_view(props: &EditorViewProps) -> Html {
|
||||
let editor_state: UseStateHandle<EditorState> = use_state_eq(fetch_initial_editor_state);
|
||||
|
||||
html! {
|
||||
<>
|
||||
{match editor_state.error_msg.as_ref() {
|
||||
None => { html! { <></> }}
|
||||
Some(msg) => html! {
|
||||
<ErrorBar msg={msg.clone()}
|
||||
dismiss={move |()| editor_state.set(EditorState {
|
||||
error_msg: None,
|
||||
..(editor_state.deref().clone())
|
||||
})
|
||||
}/> }
|
||||
}}
|
||||
<SplitPanel direction={PanelDirection::Horizontal}
|
||||
first={html!{<EditorNav ..props.clone()/>}}
|
||||
second={html!{<EditorArea ..props.clone()/>}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ use yew::prelude::*;
|
||||
|
||||
use crate::{
|
||||
command_handler::command_handler,
|
||||
editor_view::CodeEditorView,
|
||||
lineengine::line::{Readline, ReadlineEvent},
|
||||
term_split::TermSplit,
|
||||
timer_host::TimerHost,
|
||||
@ -69,11 +70,17 @@ extern "C" {
|
||||
|
||||
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Debug, Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct TermFrame(pub u64);
|
||||
pub struct FrameId(pub u64);
|
||||
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
|
||||
pub enum FrameViewType {
|
||||
Terminal,
|
||||
Editor,
|
||||
}
|
||||
|
||||
#[derive(Properties)]
|
||||
pub struct TermFrameData {
|
||||
pub id: TermFrame,
|
||||
pub struct FrameData {
|
||||
pub id: FrameId,
|
||||
pub term: Terminal,
|
||||
pub fit: FitAddon,
|
||||
pub node: Node,
|
||||
@ -86,21 +93,21 @@ pub struct TermFrameData {
|
||||
)>,
|
||||
}
|
||||
|
||||
impl PartialEq for TermFrameData {
|
||||
impl PartialEq for FrameData {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Only the ID matters, the rest are just reference data.
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
pub type RegisteredTermFrames = HashMap<TermFrame, TermFrameData>;
|
||||
pub type RegisteredTermFrames = HashMap<FrameId, FrameData>;
|
||||
|
||||
fn get_or_make_term_frame<'a>(
|
||||
frame: &TermFrame,
|
||||
frame: &FrameId,
|
||||
frames: &'a mut RegisteredTermFrames,
|
||||
// Only used for callbacks, expected frames already borrowed mut!
|
||||
globals: &GlobalMemoCell,
|
||||
) -> &'a TermFrameData {
|
||||
) -> &'a mut FrameData {
|
||||
frames.entry(frame.clone()).or_insert_with(|| {
|
||||
let term = Terminal::new();
|
||||
let fit = FitAddon::new();
|
||||
@ -124,7 +131,7 @@ fn get_or_make_term_frame<'a>(
|
||||
fit.fit();
|
||||
let term_for_readline: Terminal = Terminal { obj: term.clone() };
|
||||
let initial_size = (term.cols(), term.rows());
|
||||
let mut new_data: TermFrameData = TermFrameData {
|
||||
let mut new_data: FrameData = FrameData {
|
||||
id: frame.clone(),
|
||||
term: Terminal { obj: term.clone() },
|
||||
fit,
|
||||
@ -142,7 +149,7 @@ fn get_or_make_term_frame<'a>(
|
||||
retained_closures: None,
|
||||
};
|
||||
|
||||
let frame_for_resize: TermFrame = frame.clone();
|
||||
let frame_for_resize: FrameId = frame.clone();
|
||||
let globals_for_resize: GlobalMemoCell = globals.clone();
|
||||
let resize_closure = Closure::new(move |dims: Dims| {
|
||||
globals_for_resize
|
||||
@ -161,7 +168,7 @@ fn get_or_make_term_frame<'a>(
|
||||
});
|
||||
term.onResize(&resize_closure);
|
||||
|
||||
let frame_for_ondata: TermFrame = frame.clone();
|
||||
let frame_for_ondata: FrameId = frame.clone();
|
||||
let globals_for_ondata: GlobalMemoCell = globals.clone();
|
||||
let data_closure = Closure::new(move |d: String| {
|
||||
let new_lines: Vec<String> = match globals_for_ondata
|
||||
@ -200,8 +207,9 @@ fn get_or_make_term_frame<'a>(
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct TermViewProps {
|
||||
pub terminal: TermFrame,
|
||||
pub terminal: FrameId,
|
||||
pub global_memo: GlobalMemoCell,
|
||||
pub global_layout: UseStateHandle<GlobalLayoutCell>,
|
||||
}
|
||||
|
||||
#[function_component(TermView)]
|
||||
@ -209,8 +217,20 @@ pub fn term_view(props: &TermViewProps) -> Html {
|
||||
let mut frame_reg = props.global_memo.frame_registry.borrow_mut();
|
||||
let term = get_or_make_term_frame(&props.terminal, &mut frame_reg, &props.global_memo);
|
||||
term.fit.fit();
|
||||
html! {
|
||||
{Html::VRef(term.node.clone())}
|
||||
match props
|
||||
.global_layout
|
||||
.frame_views
|
||||
.get(&props.terminal)
|
||||
.cloned()
|
||||
.unwrap_or(FrameViewType::Terminal)
|
||||
{
|
||||
FrameViewType::Terminal => Html::VRef(term.node.clone()),
|
||||
FrameViewType::Editor => html! {
|
||||
<CodeEditorView
|
||||
frame={props.terminal.clone()}
|
||||
global_memo={props.global_memo.clone()}
|
||||
global_layout={props.global_layout.clone()}/>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,35 +242,43 @@ pub struct TermViewTreeProps {
|
||||
|
||||
#[function_component(TermViewTree)]
|
||||
pub fn term_view_tree(props: &TermViewTreeProps) -> Html {
|
||||
fn mk_term_view_tree(global: &GlobalMemoCell, split: &TermSplit) -> Html {
|
||||
fn mk_term_view_tree(
|
||||
global: &GlobalMemoCell,
|
||||
layout: UseStateHandle<GlobalLayoutCell>,
|
||||
split: &TermSplit,
|
||||
) -> Html {
|
||||
use TermSplit::*;
|
||||
match split {
|
||||
Term { frame } => html! {
|
||||
<TermView global_memo={global.clone()} terminal={frame.clone()}/>
|
||||
<TermView global_memo={global.clone()} global_layout={layout} terminal={frame.clone()}/>
|
||||
},
|
||||
Horizontal { left, right } => html! {
|
||||
<SplitPanel
|
||||
direction={PanelDirection::Horizontal}
|
||||
first={mk_term_view_tree(global, left)}
|
||||
second={mk_term_view_tree(global, right)}
|
||||
first={mk_term_view_tree(global, layout.clone(), left)}
|
||||
second={mk_term_view_tree(global, layout, right)}
|
||||
/>
|
||||
},
|
||||
Vertical { top, bottom } => html! {
|
||||
<SplitPanel
|
||||
direction={PanelDirection::Vertical}
|
||||
first={mk_term_view_tree(global, top)}
|
||||
second={mk_term_view_tree(global, bottom)}
|
||||
first={mk_term_view_tree(global, layout.clone(), top)}
|
||||
second={mk_term_view_tree(global, layout, bottom)}
|
||||
/>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
mk_term_view_tree(&props.global_memo, &props.global_layout.term_splits)
|
||||
mk_term_view_tree(
|
||||
&props.global_memo,
|
||||
props.global_layout.clone(),
|
||||
&props.global_layout.term_splits,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn echo_to_term_frame(
|
||||
global: &GlobalMemoCell,
|
||||
frame_id: &TermFrame,
|
||||
frame_id: &FrameId,
|
||||
message: &str,
|
||||
) -> Result<(), &'static str> {
|
||||
global
|
@ -22,19 +22,19 @@ use web_sys::{
|
||||
IdbTransactionMode, Url,
|
||||
};
|
||||
|
||||
use crate::{echo_to_term_frame, GlobalMemoCell, TermFrame};
|
||||
use crate::{echo_to_term_frame, FrameId, GlobalMemoCell};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum QueuedAction {
|
||||
LogEvent(LogEvent),
|
||||
ReportOnLogStreams(TermFrame),
|
||||
ReportOnLogStreams(FrameId),
|
||||
DeleteLogs(DeleteLogs),
|
||||
DownloadLogs(DownloadLogs),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteLogs {
|
||||
pub reply_to: TermFrame,
|
||||
pub reply_to: FrameId,
|
||||
pub stream: String,
|
||||
pub min_date: String,
|
||||
pub max_date: String,
|
||||
@ -42,7 +42,7 @@ pub struct DeleteLogs {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DownloadLogs {
|
||||
pub reply_to: TermFrame,
|
||||
pub reply_to: FrameId,
|
||||
pub stream: String,
|
||||
pub min_date: String,
|
||||
pub max_date: String,
|
||||
@ -114,7 +114,7 @@ async fn async_init_logging() -> Result<LoggingEngine, DomException> {
|
||||
Ok(LoggingEngine::Ready { db: db.into() })
|
||||
}
|
||||
|
||||
fn logging_broken_message(global: &GlobalMemoCell, frame: &TermFrame) {
|
||||
fn logging_broken_message(global: &GlobalMemoCell, frame: &FrameId) {
|
||||
let _ = echo_to_term_frame(global, frame, "Couldn't enable logging. This sometimes happens if your browser doesn't enable indexed storage, or is in a mode (e.g. private browsing) where such storage is not permitted.\r\n");
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ fn init_logging(global: &GlobalMemoCell) {
|
||||
}
|
||||
Err(_) => {
|
||||
if let LoggingEngine::Initialising { .. } = global.log_engine.borrow().deref() {
|
||||
logging_broken_message(&global, &TermFrame(1));
|
||||
logging_broken_message(&global, &FrameId(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@ fn queue_immediate_log(global: &GlobalMemoCell, event: LogEvent) {
|
||||
async fn immediate_report_log_frame_refutable(
|
||||
global: GlobalMemoCell,
|
||||
db: &IdbDatabase,
|
||||
frame: TermFrame,
|
||||
frame: FrameId,
|
||||
) -> Result<(), DomException> {
|
||||
let trans = db.transaction_on_one("logs")?;
|
||||
let store = trans.object_store("logs")?;
|
||||
@ -216,7 +216,7 @@ async fn immediate_report_log_frame_refutable(
|
||||
async fn immediate_report_log_frame_async(
|
||||
global: GlobalMemoCell,
|
||||
db: Arc<IdbDatabase>,
|
||||
frame: TermFrame,
|
||||
frame: FrameId,
|
||||
) {
|
||||
match immediate_report_log_frame_refutable(global, &db, frame).await {
|
||||
Ok(()) => {}
|
||||
@ -229,7 +229,7 @@ async fn immediate_report_log_frame_async(
|
||||
}
|
||||
}
|
||||
|
||||
fn immediate_report_log_frame(global: &GlobalMemoCell, frame: TermFrame) {
|
||||
fn immediate_report_log_frame(global: &GlobalMemoCell, frame: FrameId) {
|
||||
let log_engine = global.log_engine.borrow();
|
||||
if let LoggingEngine::Ready { db } = log_engine.deref() {
|
||||
spawn_local(immediate_report_log_frame_async(
|
||||
@ -450,7 +450,7 @@ pub fn log(global: &GlobalMemoCell, event: &LogEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_listing_logs(global: &GlobalMemoCell, send_to: &TermFrame) {
|
||||
pub fn start_listing_logs(global: &GlobalMemoCell, send_to: &FrameId) {
|
||||
let mut engine_borrow = global.log_engine.borrow_mut();
|
||||
match engine_borrow.deref_mut() {
|
||||
LoggingEngine::Uninitialised => {
|
||||
@ -475,7 +475,7 @@ pub fn start_listing_logs(global: &GlobalMemoCell, send_to: &TermFrame) {
|
||||
|
||||
pub fn start_deleting_logs(
|
||||
global: &GlobalMemoCell,
|
||||
reply_to: &TermFrame,
|
||||
reply_to: &FrameId,
|
||||
stream: &str,
|
||||
min_date: &str,
|
||||
max_date: &str,
|
||||
@ -510,7 +510,7 @@ pub fn start_deleting_logs(
|
||||
|
||||
pub fn start_downloading_logs(
|
||||
global: &GlobalMemoCell,
|
||||
reply_to: &TermFrame,
|
||||
reply_to: &FrameId,
|
||||
stream: &str,
|
||||
min_date: &str,
|
||||
max_date: &str,
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
||||
match_table_try_run_sub,
|
||||
},
|
||||
parsing::ParsedCommand,
|
||||
GlobalLayoutCell, GlobalMemoCell, TermFrame,
|
||||
FrameId, GlobalLayoutCell, GlobalMemoCell,
|
||||
};
|
||||
|
||||
pub struct LuaState {
|
||||
@ -38,19 +38,19 @@ impl LuaState {
|
||||
Ok(LuaState { interp, exec })
|
||||
}
|
||||
|
||||
fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), ExternError> {
|
||||
fn try_set_current_frame(&mut self, frame: &FrameId) -> Result<(), ExternError> {
|
||||
self.interp.try_enter(|ctx| {
|
||||
let info_table = Table::from_value(ctx, ctx.get_global("info")?)?;
|
||||
info_table.set(
|
||||
ctx,
|
||||
ctx.intern_static(b"current_frame"),
|
||||
intern_id::<TermFrame>(ctx, frame.clone()),
|
||||
intern_id::<FrameId>(ctx, frame.clone()),
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_current_frame(&mut self, frame: &TermFrame) {
|
||||
pub fn set_current_frame(&mut self, frame: &FrameId) {
|
||||
// We silently ignore errors here. Failure can happen if the Lua code does weird things
|
||||
// like messes with the info global, so better to just ignore.
|
||||
self.try_set_current_frame(frame).unwrap_or(())
|
||||
@ -104,7 +104,7 @@ impl LuaState {
|
||||
|
||||
pub(crate) fn dispatch_normal_command(
|
||||
&mut self,
|
||||
frame: &TermFrame,
|
||||
frame: &FrameId,
|
||||
command: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
self.interp.try_enter(|ctx| {
|
||||
@ -187,6 +187,7 @@ pub fn install_lua_globals(
|
||||
register_command!(echo);
|
||||
register_command!(echo_frame);
|
||||
register_command!(echo_frame_raw);
|
||||
register_command!(editor);
|
||||
register_command!(hsplit);
|
||||
register_command!(cmd_list_logs, "listlogs");
|
||||
register_command!(mud_log, "log");
|
||||
@ -341,7 +342,7 @@ fn lua_global_initialisation(global_memo: &GlobalMemoCell) -> Result<(), String>
|
||||
lua_engine_ref.interp.enter(|ctx| {
|
||||
ctx.fetch(&lua_engine_ref.exec).restart(
|
||||
ctx,
|
||||
Function::Callback(ensure_frame_instance(ctx, &TermFrame(1))),
|
||||
Function::Callback(ensure_frame_instance(ctx, &FrameId(1))),
|
||||
(),
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
use piccolo::{Callback, CallbackReturn, Context, FromValue, Table};
|
||||
|
||||
use crate::{echo_to_term_frame, id_intern::intern_id, GlobalMemoCell, TermFrame};
|
||||
use crate::{echo_to_term_frame, id_intern::intern_id, FrameId, GlobalMemoCell};
|
||||
|
||||
use super::try_unwrap_frame;
|
||||
|
||||
@ -15,7 +15,7 @@ pub(super) fn new_frameroute<'gc>(
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("classes.frameroute:new missing object!"))?,
|
||||
)?;
|
||||
let frame: TermFrame = try_unwrap_frame(
|
||||
let frame: FrameId = try_unwrap_frame(
|
||||
ctx,
|
||||
&stack
|
||||
.pop_front()
|
||||
@ -48,7 +48,7 @@ pub(super) fn frameroute_route<'gc>(
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("frameroute:route called without line!"))?,
|
||||
)?;
|
||||
let frame: TermFrame =
|
||||
let frame: FrameId =
|
||||
try_unwrap_frame(ctx, &frameroute.get(ctx, ctx.intern_static(b"frame"))?)?;
|
||||
|
||||
// We ignore errors with the term frame to avoid breaking the entire client for one closed frame.
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
id_intern::intern_id,
|
||||
match_table::{create_match_table, match_table_add, match_table_remove},
|
||||
timer_host::TimerHostAccessContext,
|
||||
GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame,
|
||||
FrameId, FrameViewType, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell,
|
||||
};
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use itertools::Itertools;
|
||||
@ -19,7 +19,7 @@ use super::call_checking_metatable;
|
||||
pub fn alias(ctx: Context<'_>) -> Callback<'_> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
||||
@ -65,7 +65,7 @@ pub fn alias(ctx: Context<'_>) -> Callback<'_> {
|
||||
pub fn unalias(ctx: Context<'_>) -> Callback<'_> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
||||
@ -98,7 +98,7 @@ pub fn unalias(ctx: Context<'_>) -> Callback<'_> {
|
||||
}
|
||||
|
||||
pub fn list_match_tab<'gc>(
|
||||
frame: TermFrame,
|
||||
frame: FrameId,
|
||||
aliases: UserData<'gc>,
|
||||
ctx: Context<'gc>,
|
||||
) -> Result<CallbackReturn<'gc>, Error<'gc>> {
|
||||
@ -147,7 +147,7 @@ pub fn echo_frame_raw<'gc, 'a>(
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame: TermFrame = try_unwrap_frame(
|
||||
let frame: FrameId = try_unwrap_frame(
|
||||
ctx,
|
||||
&stack
|
||||
.pop_front()
|
||||
@ -214,7 +214,7 @@ pub fn vsplit<'gc>(
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let frame: TermFrame = TermFrame(stack.from_front(ctx)?);
|
||||
let frame: FrameId = FrameId(stack.from_front(ctx)?);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
@ -223,6 +223,7 @@ pub fn vsplit<'gc>(
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
@ -242,7 +243,7 @@ pub fn hsplit<'gc>(
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let frame: TermFrame = TermFrame(stack.from_front(ctx)?);
|
||||
let frame: FrameId = FrameId(stack.from_front(ctx)?);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
@ -251,6 +252,7 @@ pub fn hsplit<'gc>(
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
@ -278,6 +280,7 @@ pub fn panel_merge<'gc>(
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
..(*(global_memo.layout.borrow().as_ref())).clone()
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
@ -288,17 +291,17 @@ pub fn panel_merge<'gc>(
|
||||
pub fn try_unwrap_frame<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
value: &Value<'gc>,
|
||||
) -> Result<TermFrame, piccolo::Error<'gc>> {
|
||||
) -> Result<FrameId, piccolo::Error<'gc>> {
|
||||
match u64::from_value(ctx, *value) {
|
||||
Ok(v) => Ok(TermFrame(v)),
|
||||
Ok(v) => Ok(FrameId(v)),
|
||||
Err(_) => Ok(UserData::from_value(ctx, *value)?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, TermFrame>]>()?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, FrameId>]>()?
|
||||
.as_ref()
|
||||
.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_frame_instance<'gc>(ctx: Context<'gc>, frame: &TermFrame) -> Callback<'gc> {
|
||||
pub fn ensure_frame_instance<'gc>(ctx: Context<'gc>, frame: &FrameId) -> Callback<'gc> {
|
||||
let frame = frame.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, _stack| {
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
@ -313,7 +316,7 @@ pub fn ensure_frame_instance<'gc>(ctx: Context<'gc>, frame: &TermFrame) -> Callb
|
||||
frames.set(ctx, frame.0 as i64, frame_tab)?;
|
||||
|
||||
// Call frame_tab:new(frame) to setup.
|
||||
let frame = intern_id::<TermFrame>(ctx, frame.clone());
|
||||
let frame = intern_id::<FrameId>(ctx, frame.clone());
|
||||
let seq = async_sequence(&ctx, move |locals, mut seq| {
|
||||
let frame_tab = locals.stash(&ctx, frame_tab);
|
||||
let frame = locals.stash(&ctx, frame);
|
||||
@ -444,7 +447,7 @@ pub(super) fn frame_input<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell)
|
||||
fn list_timers<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
cur_frame_id: TermFrame,
|
||||
cur_frame_id: FrameId,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let timer_val = match global_memo.frame_registry.borrow().get(&cur_frame_id) {
|
||||
None => Err(anyhow::Error::msg("Frame no longer exists"))?,
|
||||
@ -486,7 +489,7 @@ fn list_timers<'gc>(
|
||||
|
||||
struct GlobalCellTermFrameTimerHostAccess {
|
||||
global_memo: GlobalMemoCell,
|
||||
frame: TermFrame,
|
||||
frame: FrameId,
|
||||
}
|
||||
impl TimerHostAccessContext for GlobalCellTermFrameTimerHostAccess {
|
||||
fn with_ref<F>(&self, f: F)
|
||||
@ -512,7 +515,7 @@ pub(super) fn tick<'gc>(
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
|
||||
if stack.is_empty() {
|
||||
@ -586,7 +589,7 @@ pub(super) fn delay<'gc>(
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
|
||||
if stack.is_empty() {
|
||||
@ -660,7 +663,7 @@ pub(super) fn untick<'gc>(
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
let name = stack.consume::<piccolo::String>(ctx)?.to_str()?.to_owned();
|
||||
|
||||
@ -675,3 +678,32 @@ pub(super) fn untick<'gc>(
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn editor<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
let global_layout = global_layout.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, _stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
match global_memo
|
||||
.frame_registry
|
||||
.borrow_mut()
|
||||
.get_mut(&cur_frame_id)
|
||||
{
|
||||
None => Err(anyhow::Error::msg("Frame no longer exists"))?,
|
||||
Some(_frame_dat) => {
|
||||
let mut new_gl: GlobalLayoutState = (*global_memo.layout.borrow().as_ref()).clone();
|
||||
new_gl
|
||||
.frame_views
|
||||
.insert(cur_frame_id, FrameViewType::Editor);
|
||||
global_layout.set(new_gl.into());
|
||||
}
|
||||
};
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
match_table::{create_match_table, match_table_add, match_table_remove},
|
||||
telnet::{parse_telnet_buf, TelnetOutput},
|
||||
websocket::{connect_websocket, send_message_to_mud, WebSocketId},
|
||||
GlobalLayoutCell, GlobalMemoCell, TermFrame,
|
||||
FrameId, GlobalLayoutCell, GlobalMemoCell,
|
||||
};
|
||||
|
||||
use super::{call_checking_metatable, list_match_tab, try_unwrap_frame, LuaState};
|
||||
@ -537,7 +537,7 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
||||
pub(super) fn mud_trigger<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
||||
@ -592,7 +592,7 @@ pub(super) fn mud_trigger<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
||||
pub(super) fn mud_untrigger(ctx: Context<'_>) -> Callback<'_> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let info: Table = ctx.get_global("info")?;
|
||||
let cur_frame_id: TermFrame =
|
||||
let cur_frame_id: FrameId =
|
||||
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
||||
|
17
src/main.rs
17
src/main.rs
@ -8,6 +8,8 @@ use term_split::TermSplit;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub mod command_handler;
|
||||
pub mod editor_view;
|
||||
pub mod frame_view;
|
||||
pub mod id_intern;
|
||||
pub mod lineengine;
|
||||
pub mod logging;
|
||||
@ -17,12 +19,11 @@ pub mod parsing;
|
||||
pub mod split_panel;
|
||||
pub mod telnet;
|
||||
pub mod term_split;
|
||||
pub mod term_view;
|
||||
pub mod timer_host;
|
||||
pub mod websocket;
|
||||
use crate::frame_view::*;
|
||||
use crate::lua_engine::{install_lua_globals, LuaState};
|
||||
use crate::split_panel::*;
|
||||
use crate::term_view::*;
|
||||
use crate::websocket::RegisteredWebSockets;
|
||||
|
||||
#[derive(Properties)]
|
||||
@ -31,7 +32,7 @@ pub struct GlobalMemoState {
|
||||
frame_registry: RefCell<RegisteredTermFrames>,
|
||||
lua_engine: RefCell<LuaState>,
|
||||
ws_registry: RefCell<RegisteredWebSockets>,
|
||||
command_queue: RefCell<VecDeque<(TermFrame, ParsedCommand)>>,
|
||||
command_queue: RefCell<VecDeque<(FrameId, ParsedCommand)>>,
|
||||
log_engine: RefCell<LoggingEngine>,
|
||||
|
||||
// A cache of the latest layout info (separate from the state).
|
||||
@ -51,9 +52,10 @@ impl PartialEq for GlobalMemoState {
|
||||
|
||||
// Used for state that impacts the entire layout. Changes here will
|
||||
// cause a re-render
|
||||
#[derive(PartialEq, Properties)]
|
||||
#[derive(PartialEq, Properties, Clone)]
|
||||
pub struct GlobalLayoutState {
|
||||
term_splits: TermSplit,
|
||||
frame_views: im::OrdMap<FrameId, FrameViewType>,
|
||||
}
|
||||
// Note: Despite interior mutability, you still should always call the
|
||||
// setter to force a re-render. Interior mutability is used to allow this
|
||||
@ -65,9 +67,8 @@ type GlobalLayoutCell = Rc<GlobalLayoutState>;
|
||||
fn app() -> Html {
|
||||
let global_layout: UseStateHandle<GlobalLayoutCell> = use_state(|| {
|
||||
Rc::new(GlobalLayoutState {
|
||||
term_splits: TermSplit::Term {
|
||||
frame: TermFrame(1),
|
||||
},
|
||||
term_splits: TermSplit::Term { frame: FrameId(1) },
|
||||
frame_views: im::OrdMap::new(),
|
||||
})
|
||||
});
|
||||
let global_memo = use_memo((), |_| GlobalMemoState {
|
||||
@ -84,7 +85,7 @@ fn app() -> Html {
|
||||
});
|
||||
|
||||
html! {
|
||||
<div class="toplevel">
|
||||
<div class="toplevel" data-bs-theme="dark">
|
||||
<TermViewTree global_memo={global_memo.clone()}
|
||||
global_layout={global_layout.clone()}/>
|
||||
</div>
|
||||
|
@ -1,12 +1,12 @@
|
||||
use itertools::Itertools;
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
|
||||
use crate::TermFrame;
|
||||
use crate::FrameId;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum TermSplit {
|
||||
Term {
|
||||
frame: TermFrame,
|
||||
frame: FrameId,
|
||||
},
|
||||
Horizontal {
|
||||
left: Rc<TermSplit>,
|
||||
@ -19,7 +19,7 @@ pub enum TermSplit {
|
||||
}
|
||||
|
||||
impl TermSplit {
|
||||
fn collect_term_frames(&self, into: &mut BTreeMap<TermFrame, usize>) {
|
||||
fn collect_term_frames(&self, into: &mut BTreeMap<FrameId, usize>) {
|
||||
match self {
|
||||
TermSplit::Term { frame } => {
|
||||
into.entry(frame.clone())
|
||||
@ -38,7 +38,7 @@ impl TermSplit {
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
let mut frame_count: BTreeMap<TermFrame, usize> = BTreeMap::new();
|
||||
let mut frame_count: BTreeMap<FrameId, usize> = BTreeMap::new();
|
||||
self.collect_term_frames(&mut frame_count);
|
||||
let duplicate_terminal = frame_count
|
||||
.iter()
|
||||
@ -103,7 +103,7 @@ impl TermSplit {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hsplit(&self, pathstr: &str, new_frame: TermFrame) -> Result<TermSplit, String> {
|
||||
pub fn hsplit(&self, pathstr: &str, new_frame: FrameId) -> Result<TermSplit, String> {
|
||||
let new = self.modify_at_pathstr(pathstr, move |n| {
|
||||
Ok(TermSplit::Horizontal {
|
||||
left: n.clone().into(),
|
||||
@ -114,7 +114,7 @@ impl TermSplit {
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn vsplit(&self, pathstr: &str, new_frame: TermFrame) -> Result<TermSplit, String> {
|
||||
pub fn vsplit(&self, pathstr: &str, new_frame: FrameId) -> Result<TermSplit, String> {
|
||||
let new = self.modify_at_pathstr(pathstr, move |n| {
|
||||
Ok(TermSplit::Vertical {
|
||||
top: n.clone().into(),
|
||||
@ -145,19 +145,10 @@ mod tests {
|
||||
use TermSplit::*;
|
||||
assert_eq!(
|
||||
(Vertical {
|
||||
top: Term {
|
||||
frame: TermFrame(1)
|
||||
}
|
||||
.into(),
|
||||
top: Term { frame: FrameId(1) }.into(),
|
||||
bottom: Horizontal {
|
||||
left: Term {
|
||||
frame: TermFrame(2)
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(3)
|
||||
}
|
||||
.into(),
|
||||
left: Term { frame: FrameId(2) }.into(),
|
||||
right: Term { frame: FrameId(3) }.into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@ -171,19 +162,10 @@ mod tests {
|
||||
use TermSplit::*;
|
||||
assert_eq!(
|
||||
(Vertical {
|
||||
top: Term {
|
||||
frame: TermFrame(1)
|
||||
}
|
||||
.into(),
|
||||
top: Term { frame: FrameId(1) }.into(),
|
||||
bottom: Horizontal {
|
||||
left: Term {
|
||||
frame: TermFrame(1)
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(3)
|
||||
}
|
||||
.into(),
|
||||
left: Term { frame: FrameId(1) }.into(),
|
||||
right: Term { frame: FrameId(3) }.into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@ -192,19 +174,10 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
(Vertical {
|
||||
top: Term {
|
||||
frame: TermFrame(42)
|
||||
}
|
||||
.into(),
|
||||
top: Term { frame: FrameId(42) }.into(),
|
||||
bottom: Horizontal {
|
||||
left: Term {
|
||||
frame: TermFrame(1)
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(42)
|
||||
}
|
||||
.into(),
|
||||
left: Term { frame: FrameId(1) }.into(),
|
||||
right: Term { frame: FrameId(42) }.into(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@ -216,99 +189,49 @@ mod tests {
|
||||
#[test]
|
||||
fn modify_at_pathstr_works() {
|
||||
use TermSplit::*;
|
||||
let t = Term {
|
||||
frame: TermFrame(1),
|
||||
};
|
||||
let t = Term { frame: FrameId(1) };
|
||||
assert_eq!(
|
||||
t.modify_at_pathstr("", |_v| {
|
||||
Ok(Term {
|
||||
frame: TermFrame(2),
|
||||
})
|
||||
}),
|
||||
Ok(Term {
|
||||
frame: TermFrame(2),
|
||||
})
|
||||
t.modify_at_pathstr("", |_v| { Ok(Term { frame: FrameId(2) }) }),
|
||||
Ok(Term { frame: FrameId(2) })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
t.modify_at_pathstr("tlr", |_v| {
|
||||
Ok(Term {
|
||||
frame: TermFrame(2),
|
||||
})
|
||||
}),
|
||||
t.modify_at_pathstr("tlr", |_v| { Ok(Term { frame: FrameId(2) }) }),
|
||||
Err("In split path, found trailing junk tlr after addressing terminal".to_owned())
|
||||
);
|
||||
|
||||
let t = Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term {
|
||||
frame: TermFrame(42),
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(64),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(42),
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(64) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(42) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term {
|
||||
frame: TermFrame(43),
|
||||
}
|
||||
.into(),
|
||||
bottom: Term {
|
||||
frame: TermFrame(44),
|
||||
}
|
||||
.into(),
|
||||
top: Term { frame: FrameId(43) }.into(),
|
||||
bottom: Term { frame: FrameId(44) }.into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
assert_eq!(
|
||||
t.modify_at_pathstr("tlr", |_v| {
|
||||
Ok(Term {
|
||||
frame: TermFrame(2),
|
||||
})
|
||||
})
|
||||
.and_then(|t| t.modify_at_pathstr("bb", |_v| {
|
||||
Ok(Term {
|
||||
frame: TermFrame(3),
|
||||
})
|
||||
})),
|
||||
t.modify_at_pathstr("tlr", |_v| { Ok(Term { frame: FrameId(2) }) })
|
||||
.and_then(|t| t.modify_at_pathstr("bb", |_v| { Ok(Term { frame: FrameId(3) }) })),
|
||||
Ok(Vertical {
|
||||
top: Horizontal {
|
||||
left: Horizontal {
|
||||
left: Term {
|
||||
frame: TermFrame(42),
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(2),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term {
|
||||
frame: TermFrame(42),
|
||||
left: Term { frame: FrameId(42) }.into(),
|
||||
right: Term { frame: FrameId(2) }.into(),
|
||||
}
|
||||
.into(),
|
||||
right: Term { frame: FrameId(42) }.into(),
|
||||
}
|
||||
.into(),
|
||||
bottom: Vertical {
|
||||
top: Term {
|
||||
frame: TermFrame(43),
|
||||
}
|
||||
.into(),
|
||||
bottom: Term {
|
||||
frame: TermFrame(3),
|
||||
}
|
||||
.into(),
|
||||
top: Term { frame: FrameId(43) }.into(),
|
||||
bottom: Term { frame: FrameId(3) }.into(),
|
||||
}
|
||||
.into(),
|
||||
})
|
||||
|
@ -5,12 +5,12 @@ use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use crate::{
|
||||
command_handler::execute_queue,
|
||||
parsing::{parse_commands, ParsedCommand},
|
||||
GlobalMemoCell, TermFrame,
|
||||
FrameId, GlobalMemoCell,
|
||||
};
|
||||
|
||||
pub struct TimerHost {
|
||||
timers: Vec<TimerRecord>,
|
||||
frame_id: TermFrame,
|
||||
frame_id: FrameId,
|
||||
next_id: u64,
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ impl Drop for TimerHost {
|
||||
}
|
||||
|
||||
impl TimerHost {
|
||||
pub fn new(frame_id: TermFrame) -> Self {
|
||||
pub fn new(frame_id: FrameId) -> Self {
|
||||
Self {
|
||||
timers: vec![],
|
||||
frame_id,
|
||||
|
@ -67,3 +67,9 @@ body {
|
||||
font-family: "JetBrainsMono-Regular", monospace, serif;
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
.editornav {
|
||||
color: white;
|
||||
}
|
||||
.editorarea {
|
||||
color: white;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user