├── .gitignore ├── godot-plugin ├── .import │ └── .gdignore ├── .gitignore ├── fonts │ ├── NotoSansMono-Regular.ttf │ └── NotoSansMono-Regular.ttf.import ├── addons │ ├── jlang-rs-gd │ │ ├── jlang_rs_gd.dll │ │ ├── ~jlang_rs_gd.dll │ │ └── jlang_rs_gd.gdextension │ ├── fonts │ │ ├── NotoSansMono-Regular.ttf │ │ ├── FiraSansCondensed-Regular.ttf │ │ ├── noto-font.tres │ │ ├── noto-24.tres │ │ ├── NotoSansMono-Regular.ttf.import │ │ └── FiraSansCondensed-Regular.ttf.import │ └── jprez │ │ ├── plugin.cfg │ │ ├── OrgCommands.gd │ │ ├── plugin.gd │ │ ├── jprez-scene.tscn │ │ ├── jprez-plugin.tscn │ │ ├── JPrezPlayer.tscn │ │ ├── JPrezPlayer.gd │ │ ├── jprez.gd │ │ └── JKVM.gd ├── project.godot └── orgprez.leo ├── readme-screenshot.png ├── syntaxline.ijs ├── etc ├── bbcode.ijs └── db.ijs ├── marquee.ijs ├── macro.ijs ├── jedit.ijs ├── LICENSE ├── ws.ijs ├── docs └── stackwise.org ├── screenplay.org ├── README.md ├── org.ijs ├── jp-websocket.ijs ├── tok.ijs ├── repl.ijs ├── worlds.ijs ├── jprez.ijs └── backlog.org /.gitignore: -------------------------------------------------------------------------------- 1 | waveform 2 | .import 3 | -------------------------------------------------------------------------------- /godot-plugin/.import/.gdignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /godot-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .godot 2 | addons/waveform 3 | -------------------------------------------------------------------------------- /readme-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/readme-screenshot.png -------------------------------------------------------------------------------- /godot-plugin/fonts/NotoSansMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/godot-plugin/fonts/NotoSansMono-Regular.ttf -------------------------------------------------------------------------------- /godot-plugin/addons/jlang-rs-gd/jlang_rs_gd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/godot-plugin/addons/jlang-rs-gd/jlang_rs_gd.dll -------------------------------------------------------------------------------- /godot-plugin/addons/jlang-rs-gd/~jlang_rs_gd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/godot-plugin/addons/jlang-rs-gd/~jlang_rs_gd.dll -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/NotoSansMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/godot-plugin/addons/fonts/NotoSansMono-Regular.ttf -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="jprez" 4 | description="jprez" 5 | author="tangentstorm" 6 | version="" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/FiraSansCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangentstorm/jprez/HEAD/godot-plugin/addons/fonts/FiraSansCondensed-Regular.ttf -------------------------------------------------------------------------------- /godot-plugin/addons/jlang-rs-gd/jlang_rs_gd.gdextension: -------------------------------------------------------------------------------- 1 | [configuration] 2 | entry_symbol = "gdext_rust_init" 3 | compatibility_minimum = 4.2 4 | reloadable = true 5 | 6 | [libraries] 7 | windows.64 = "res://addons/jlang-rs-gd/jlang_rs_gd.dll" 8 | -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/noto-font.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="FontFile" load_steps=2 format=2] 2 | 3 | [sub_resource type="FontFile" id=1] 4 | font_path = "res://fonts/NotoSansMono-Regular.ttf" 5 | 6 | [resource] 7 | use_mipmaps = true 8 | use_filter = true 9 | font_data = SubResource( 1 ) 10 | -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/noto-24.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="FontFile" load_steps=2 format=2] 2 | 3 | [sub_resource type="FontFile" id=1] 4 | font_path = "res://fonts/NotoSansMono-Regular.ttf" 5 | 6 | [resource] 7 | size = 24 8 | use_mipmaps = true 9 | use_filter = true 10 | font_data = SubResource( 1 ) 11 | -------------------------------------------------------------------------------- /syntaxline.ijs: -------------------------------------------------------------------------------- 1 | NB. Syntax-highlighted edit field. 2 | 3 | coclass 'UiSyntaxLine' extends 'UiEditWidget' 4 | 5 | create =: {{ 6 | create_UiEditWidget_ f. y 7 | ted =: '' conew 'TokEd' NB. for syntax highlighting 8 | }} 9 | 10 | render =: {{ 11 | bg BG [ fg FG 12 | try. 13 | B__ted =: jcut_jlex_ B 14 | render__ted y 15 | catch. 16 | render_UiEditWidget_ f. y 17 | end. 18 | render_cursor y }} 19 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/OrgCommands.gd: -------------------------------------------------------------------------------- 1 | # OrgCommands for Jprez 2 | extends Node 3 | 4 | const ANIMATED = true 5 | const IMMEDIATE = false 6 | 7 | func cmd_editor_goxy(x,y, force_visible:bool=false): 8 | var node = $"../jp-editor" 9 | var JI = $"../JLang" 10 | if force_visible: node.show() 11 | print("editor_goxy: ", x, ", ", y) 12 | node.fake_focus = true 13 | JI.cmd("curxy__editor %d %d" % [x,y]) 14 | return IMMEDIATE 15 | 16 | func run_macro(_macro:String): 17 | # !! we ignore OrgPrez's copy of the macro because jprez 18 | # already knows it from when goix was called. 19 | pass 20 | -------------------------------------------------------------------------------- /etc/bbcode.ijs: -------------------------------------------------------------------------------- 1 | NB. example script for converting jprez syntax highlighting 2 | NB. to bbcode markup. 3 | 4 | load 'tangentstorm/j-lex' 5 | load 'tok.ijs' NB. run from .. 6 | coinsert 'tok' 7 | line =: 0 : 0 8 | (16b221111*i.8) viewmat |:+/2<|(+*:)^:(<9)~_1+j./~ 0.01*i:175 [ load'viewmat' 9 | ) 10 | 11 | bbcolor =: {{ NB. colorize a line of j using vt escape codes 12 | res =. '' 13 | for_tt. {.>{.> jlex y do. 14 | if. -.*#tt do. continue. end. 15 | res =. res,('[color=#',(hfd jcolor tt),']'),'[/color]',~,(>}.tt) 16 | end. res }} 17 | 18 | 19 | 'bbcode' fwrites~ bbcolor line 20 | echo 'look in file: "bbcode"' 21 | exit'' 22 | -------------------------------------------------------------------------------- /marquee.ijs: -------------------------------------------------------------------------------- 1 | NB. scratch code that makes a marquee / ticker tape display 2 | NB. at some point I will clean this up and make it a base widget 3 | NB. for j-kvm 4 | cocurrent 'UiMarquee' extends 'UiWidget' 5 | 6 | create =: {{ 7 | 'w s' =. y 8 | S =: ' _.,oO( )Oo,._ '"_ ^:(s-:'') s 9 | W =: w 10 | A =: 1 11 | T =: 0 NB. timer (in seconds) 12 | FPS =: 20 NB. frames per second 13 | }} 14 | 15 | update =: {{ 16 | T =: T + y 17 | if. T > % FPS do. 18 | T =: 0 [ R=:1 [ S=:1|.S NB. rotate the text 19 | end. }} 20 | 21 | render =: {{ 22 | bg _4 [ fg _9 23 | puts '[' 24 | fg _15 25 | puts (W-2){. S 26 | fg _9 27 | puts']' }} 28 | 29 | -------------------------------------------------------------------------------- /macro.ijs: -------------------------------------------------------------------------------- 1 | NB. macro editing widget 2 | require 'tangentstorm/j-kvm/ui' 3 | coinsert 'kvm' 4 | 5 | coclass 'MacroWidget' extends 'UiEditWidget' 6 | 7 | create =: {{ 8 | create_UiEditWidget_ f. y 9 | O =: 0$a: NB. observers 10 | }} 11 | 12 | register =: {{ O =: O,y }} 13 | 14 | NB. rather than recording macros for *this* editor, 15 | NB. we are editng a macro for *another* editor. To 16 | NB. debug, we want to tell the other editor to play 17 | NB. the macro up to this editor's cursor. Since we don't 18 | NB. need logging for ourselves, we can override that to 19 | NB. just tell the second editor (observer O) to play the 20 | NB. macro up to the cursor. 21 | log =: {{ for_o. O do. notify__o C {. B end. }} 22 | -------------------------------------------------------------------------------- /godot-plugin/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="jprez godot plugin" 14 | run/main_scene="res://addons/jprez/jprez-scene.tscn" 15 | config/features=PackedStringArray("4.0") 16 | 17 | [audio] 18 | 19 | default_bus_layout="res://addons/waveform/waveform_bus_layout.tres" 20 | enable_audio_input=true 21 | 22 | [importer_defaults] 23 | 24 | org.jprez={} 25 | 26 | [physics] 27 | 28 | common/enable_pause_aware_picking=true 29 | 30 | [rendering] 31 | 32 | quality/driver/driver_name="GLES2" 33 | -------------------------------------------------------------------------------- /godot-plugin/fonts/NotoSansMono-Regular.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://b0wa37h07dguo" 6 | path="res://.godot/imported/NotoSansMono-Regular.ttf-abc0ca93a7fdd72922b7fb02e4b6d50c.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://fonts/NotoSansMono-Regular.ttf" 11 | dest_files=["res://.godot/imported/NotoSansMono-Regular.ttf-abc0ca93a7fdd72922b7fb02e4b6d50c.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/NotoSansMono-Regular.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://cmvemxi28sukl" 6 | path="res://.godot/imported/NotoSansMono-Regular.ttf-eee3e061872858cbbca1bd2949c4c10c.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://addons/fonts/NotoSansMono-Regular.ttf" 11 | dest_files=["res://.godot/imported/NotoSansMono-Regular.ttf-eee3e061872858cbbca1bd2949c4c10c.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /godot-plugin/addons/fonts/FiraSansCondensed-Regular.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://euqt1sdsn5p" 6 | path="res://.godot/imported/FiraSansCondensed-Regular.ttf-698a8348fad116e5d0250db79fb39059.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://addons/fonts/FiraSansCondensed-Regular.ttf" 11 | dest_files=["res://.godot/imported/FiraSansCondensed-Regular.ttf-698a8348fad116e5d0250db79fb39059.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /jedit.ijs: -------------------------------------------------------------------------------- 1 | NB. syntax aware code editor widget for j 2 | require 'tangentstorm/j-kvm/ui' 3 | require 'tok.ijs syntaxline.ijs' 4 | 5 | coclass 'JCodeEditor' extends 'UiList' 6 | 7 | create =: {{ 8 | create_UiList_ f. y 9 | ed =: '' conew 'UiSyntaxLine' NB. syntax highlighted line editor 10 | on_accept__ed =: 'accept'of_self 11 | on_up__ed =: 'bak'of_self 12 | on_dn__ed =: 'fwd'of_self 13 | 0 0$0 }} 14 | 15 | setval =: {{ curxy 0 0 [ L =: y }} 16 | 17 | curxy =: {{ 18 | 'cx cy' =. y 19 | setval__ed > (C=:cy) { L 20 | C__ed =: cx 21 | R =: 1 }} 22 | 23 | 24 | render =: {{ 25 | NB. draw the code editor 26 | cscr'' [ bg 16b101010 [ reset'' 27 | if. -. a: -: L do. 28 | for_line. >jlex L do. 29 | goxy 0,line_index 30 | if. line ~: a: do. (put_tok_TokEd_ :: ]) L:1 "1 > line end. 31 | end. 32 | NB. draw the cursor on current line C using is_focused flag y 33 | end. 34 | C render_cursor__ed y 35 | R =: 0 }} 36 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/plugin.gd: -------------------------------------------------------------------------------- 1 | # jprez godot plugin 2 | @tool extends EditorPlugin 3 | 4 | var jprez 5 | var wav_dir = 'res://' 6 | 7 | func _enter_tree(): 8 | jprez = preload("res://addons/jprez/jprez-plugin.tscn").instantiate() 9 | add_control_to_bottom_panel(jprez, "jprez") 10 | 11 | func _exit_tree(): 12 | remove_control_from_bottom_panel(jprez); jprez.queue_free() 13 | 14 | func edit_sample(path): 15 | var samp:AudioStreamWAV = ResourceLoader.load(path) 16 | get_editor_interface().edit_resource(samp) 17 | 18 | func _on_audio_chunk_selected(chunk:OrgChunk): 19 | var path = wav_dir + chunk.suggest_path() 20 | if ResourceLoader.exists(path): edit_sample(path) 21 | else: 22 | # !! make a stub resource and edit that instead. this is so we don't 23 | # have to make some hard-coded reference to the godot-waveform plugin, 24 | # though I admit it still feels rather clunky. 25 | var samp = AudioStreamWAV.new() 26 | samp.resource_path = path 27 | samp.format = AudioStreamWAV.FORMAT_16_BITS 28 | samp.stereo = true 29 | get_editor_interface().edit_resource(samp) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 tangentstorm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ws.ijs: -------------------------------------------------------------------------------- 1 | load 'net/websocket' 2 | 3 | NB. make websocket server non-blocking. 4 | NB. combined with ws_onstep, this will allow us to 5 | NB. run the j-kvm loop 'inside' the websocket event 6 | NB. loop. 7 | WaitTimeout_jws_ =: 0.2 8 | 9 | {{ NB. monkeypatch initrun_jws_ 10 | src =. '-' 11 | oldsrc =. (5!:5)a:)`',f, ' =: ', src 20 | ". src 21 | 0 22 | }}'' 23 | 24 | OLDT_jws_=:0 25 | ws_onstep_jws_ =: {{ if. 0.2<:(t=.6!:1'')-OLDT do. echo <.OLDT=:t end.}} 26 | 27 | ws_onmessage_jws_ =: {{ 28 | logcmd y 29 | echo 'in here. encoding:' 30 | echo encoding 31 | r =. '' 32 | if. encoding=2 do. NB. "binary" message 33 | echo y 34 | if. y -: 'stop!' do. interrupt_jws_'' end. 35 | if. y -: 'STOP!' do. shutdown_jws_'' end. 36 | r =. |.y 37 | else. end. NB. (TODO: encoding=1 for text message) 38 | ws_send r 39 | }} 40 | 41 | 42 | 9!:27 'init_jws_ 8081 0' 43 | 9!:29]1 44 | -------------------------------------------------------------------------------- /docs/stackwise.org: -------------------------------------------------------------------------------- 1 | #+title: features i want for repl in future 2 | 3 | * j token stack 4 | 5 | push and pop items from left side of j line here 6 | (use a stack the way the j parser uses a stack) 7 | basically a two-line repl(??) 8 | 9 | 10 | "come back to this point" 11 | > NB. find the alphabet 12 | > NB. find positions of vowels 13 | > NB. given a letter, which vowel precedes it in alphabet? 14 | 15 | 16 | * stackwise 17 | ** keep a stack for where the heck i am in a narrative 18 | especially after i'm done with an example, and 19 | 'popping off the stack' 20 | the ui for this is to be called "stackwise" 21 | 22 | ** stackwise refinement 23 | start with final thing you want to say 24 | and perform stepwise refinement 25 | using this stack-based ui 26 | 27 | ** stackwise UI 28 | - (use time-travelling repl to go back to that point) 29 | - visual display of your "goal stack" 30 | - derived from the word you're defining 31 | - *or* you can manually enter goals like: 32 | - "give this example" 33 | - "write this other function i need" 34 | - go off on a tangent describing the ui you want to build in future videos 35 | - you notice a bug and want to fix it(since it's blocking you) 36 | - you want to refactor something 37 | 38 | * something like "git rebase" 39 | mid task, you realize the current task would be simpler if some other task had been done first 40 | push your work-in-progress onto a stack 41 | go back in time 42 | change / refactor something (or make it right from the start) 43 | play hit history to "new present" 44 | restart your current task from the newly improved start point 45 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/jprez-scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://b814tmrtq1geo"] 2 | 3 | [ext_resource type="Script" path="res://addons/jprez/JKVM.gd" id="1"] 4 | [ext_resource type="Script" path="res://addons/jprez/jprez.gd" id="2"] 5 | 6 | [node name="JPrez" type="Control"] 7 | layout_mode = 3 8 | anchors_preset = 15 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | offset_right = 40.0 12 | offset_bottom = 40.0 13 | grow_horizontal = 2 14 | grow_vertical = 2 15 | script = ExtResource("2") 16 | 17 | [node name="JLang" type="JLang" parent="."] 18 | 19 | [node name="jp-repl" type="Control" parent="."] 20 | custom_minimum_size = Vector2(1080, 480) 21 | anchors_preset = 0 22 | offset_right = 1080.0 23 | offset_bottom = 480.0 24 | focus_mode = 1 25 | script = ExtResource("1") 26 | font_base = Vector2(0, 15) 27 | font_size = 16 28 | j_widget = "repl" 29 | grid_wh = Vector2(120, 24) 30 | cell_wh = Vector2(9, 20) 31 | 32 | [node name="jp-editor" type="Control" parent="."] 33 | custom_minimum_size = Vector2(630, 480) 34 | anchors_preset = 0 35 | offset_left = 1115.0 36 | offset_right = 1745.0 37 | offset_bottom = 480.0 38 | focus_mode = 1 39 | script = ExtResource("1") 40 | font_base = Vector2(0, 15) 41 | font_size = 16 42 | j_widget = "editor" 43 | grid_wh = Vector2(70, 24) 44 | cell_wh = Vector2(9, 20) 45 | 46 | [connection signal="focus_entered" from="jp-repl" to="." method="_on_jprepl_focus_entered"] 47 | [connection signal="keypress" from="jp-repl" to="." method="_on_JKVM_keypress"] 48 | [connection signal="focus_entered" from="jp-editor" to="." method="_on_jpeditor_focus_entered"] 49 | [connection signal="keypress" from="jp-editor" to="." method="_on_JKVM_keypress"] 50 | -------------------------------------------------------------------------------- /screenplay.org: -------------------------------------------------------------------------------- 1 | #+title: demo screenplay 2 | 3 | * j demo 4 | #+begin_src j 5 | NB. Hello, world! Welcome to JPrez! 6 | NB. https://github.com/tangentstorm/jprez 7 | 8 | NB. This pane is the editor. 9 | 10 | +/p: i.10 NB. sum of the first 10 primes. 11 | 12 | #+end_src 13 | 14 | : . ?i.10 NB. first n?X?ten natural numbers? 15 | : i.10 NB. first ten natural numbers 16 | : . ^bbbwwbK?primes?<<<<<|(z+*:)^:(<8) z =: (}:sy*i:-:h) j./~ 1-~ sx * (<.w%_3)+i.w 31 | pal =: (3#256)#: dfh;.1' 0 ffffff ffd21d b28f00 400fe8 1d2799 000055 000033' 32 | pal vm mb 33 | #+end_src 34 | 35 | ** line-by-line demo 36 | #+begin_src j 37 | NB. having installed J from http://jsoftware.com/ 38 | install'github:tangentstorm/j-kvm@work' 39 | #+end_src 40 | 41 | # Text without : is the spoken part 42 | 43 | Hello, and welcome to my demo. 44 | 45 | ** once it's loaded 46 | 47 | Here's how you load the "viewmat" part of j-kvm. 48 | 49 | Viewmat is short for "view matrix" and comes with J, but 50 | here we are adapting it to work in the terminal. 51 | 52 | # text with : shows up in the REPL 53 | : load 'tangentstorm/j-kvm/vm' 54 | 55 | Here's a boolean matrix: 56 | 57 | : vm ~:/\^:(<32) 32#1 NB. sierpinski triangle 58 | 59 | Here's one with multiple values: 60 | 61 | : vm i. 5 5 NB. normal viewmat colors 62 | 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jprez 2 | 3 | This is a tool for making presentations about programming, with an initial focus on J programming language, (in which it is written). 4 | 5 | # current status 6 | 7 | This software is still in development and not really usable on its own yet. 8 | 9 | It is also currently the subject of the **Ridiculously Early J Morning Show** on twitch, where I spend an hour each morning (8am-9am Eastern Time) livecoding this thing. 10 | 11 | (Past episodes are available at https://youtube.com/c/tangentstorm/, along with my scripted programming videos.) 12 | 13 | # orientation 14 | 15 | 16 | There are four sections to the screen above. 17 | 18 | - Top left: a code editor, as it will appear in the presentation. 19 | - Top right: a J repl (read-eval-print loop), as it will appear in the presentation. 20 | - Bottom left: an outline of the presentation 21 | - Bottom right: the script ("screenplay") for this section of the presentation. 22 | 23 | The screenplay is stored in an org-mode formatted file. The one from the screenshot is sandpiles-j.org, and is the script for the upcoming code review / code golf followup to my sandpiles video. 24 | 25 | # requirements 26 | 27 | ( Do not expect to be able to run this without changing some hard-coded paths right now. ) 28 | 29 | J version 9.02 (or greater) from http://jsoftware.com/ 30 | 31 | Run the following code from J to install the two required libraries: 32 | 33 | ``` 34 | install'github:tangentstorm/j-lex@main' 35 | install'github:tangentstorm/j-kvm@main' 36 | ``` 37 | 38 | Code for the dependencies are at: 39 | 40 | * http://github.com/tangentstorm/j-lex/ 41 | * https://github.com/tangentstorm/j-kvm/tree/work 42 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/jprez-plugin.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://6ddxmusg1p8l"] 2 | 3 | [ext_resource type="Script" path="res://addons/jprez/JKVM.gd" id="1"] 4 | [ext_resource type="Script" path="res://addons/jprez/jprez.gd" id="2"] 5 | 6 | [node name="JPrez" type="Control"] 7 | layout_mode = 3 8 | anchors_preset = 15 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | offset_right = 40.0 12 | offset_bottom = -280.0 13 | script = ExtResource("2") 14 | 15 | [node name="jp-list" type="Control" parent="."] 16 | custom_minimum_size = Vector2(288, 240) 17 | anchors_preset = 0 18 | offset_top = 58.0 19 | offset_right = 288.0 20 | offset_bottom = 298.0 21 | focus_mode = 1 22 | script = ExtResource("1") 23 | font_base = Vector2(0, 15) 24 | j_widget = "list" 25 | grid_wh = Vector2(32, 12) 26 | cell_wh = Vector2(9, 20) 27 | 28 | [node name="jp-cmds" type="Control" parent="."] 29 | custom_minimum_size = Vector2(1116, 240) 30 | anchors_preset = 0 31 | offset_left = 297.0 32 | offset_top = 58.0 33 | offset_right = 1413.0 34 | offset_bottom = 298.0 35 | focus_mode = 1 36 | script = ExtResource("1") 37 | font_base = Vector2(0, 15) 38 | j_widget = "cmds" 39 | grid_wh = Vector2(124, 12) 40 | cell_wh = Vector2(9, 20) 41 | 42 | [node name="jp-led" type="Control" parent="."] 43 | custom_minimum_size = Vector2(1116, 20) 44 | anchors_preset = 0 45 | anchor_right = 1.0 46 | anchor_bottom = 1.0 47 | offset_left = 18.0 48 | offset_right = 1134.0 49 | offset_bottom = 20.0 50 | focus_mode = 1 51 | script = ExtResource("1") 52 | font_base = Vector2(0, 15) 53 | j_widget = "led" 54 | grid_wh = Vector2(124, 1) 55 | cell_wh = Vector2(9, 20) 56 | 57 | [connection signal="focus_entered" from="jp-list" to="." method="_on_jplist_focus_entered"] 58 | [connection signal="keypress" from="jp-list" to="." method="_on_JKVM_keypress"] 59 | [connection signal="focus_entered" from="jp-cmds" to="." method="_on_jpcmds_focus_entered"] 60 | [connection signal="keypress" from="jp-cmds" to="." method="_on_JKVM_keypress"] 61 | [connection signal="keypress" from="jp-led" to="." method="_on_JKVM_keypress"] 62 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/JPrezPlayer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://bdvpq8fotx3fy"] 2 | 3 | [ext_resource type="Script" path="res://addons/jprez/JKVM.gd" id="1"] 4 | [ext_resource type="Script" path="res://addons/jprez/JPrezPlayer.gd" id="2"] 5 | [ext_resource type="Script" path="res://addons/jprez/OrgCommands.gd" id="4"] 6 | [ext_resource type="FontFile" uid="uid://0ddyd3wbehih" path="res://fonts/noto-24.tres" id="5"] 7 | [ext_resource type="PackedScene" uid="uid://b4eolliry53vi" path="res://assets/SceneTitle.tscn" id="10"] 8 | 9 | [sub_resource type="AudioStreamWAV" id="1"] 10 | 11 | [node name="JPrezPlayer" type="Control"] 12 | layout_mode = 3 13 | anchors_preset = 15 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | grow_horizontal = 2 17 | grow_vertical = 2 18 | script = ExtResource("2") 19 | 20 | [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] 21 | stream = SubResource("1") 22 | 23 | [node name="jp-repl" type="Control" parent="."] 24 | custom_minimum_size = Vector2(3080, 1080) 25 | anchors_preset = 0 26 | offset_right = 3080.0 27 | offset_bottom = 1080.0 28 | focus_mode = 1 29 | script = ExtResource("1") 30 | font = ExtResource("5") 31 | font_base = Vector2(0, 20) 32 | j_widget = "repl" 33 | grid_wh = Vector2(220, 36) 34 | cell_wh = Vector2(14, 30) 35 | 36 | [node name="jp-editor" type="Control" parent="."] 37 | custom_minimum_size = Vector2(1120, 480) 38 | anchors_preset = 0 39 | offset_left = 38.1406 40 | offset_top = 570.697 41 | offset_right = 1158.14 42 | offset_bottom = 1050.7 43 | focus_mode = 1 44 | script = ExtResource("1") 45 | font = ExtResource("5") 46 | font_base = Vector2(0, 24) 47 | j_widget = "editor" 48 | grid_wh = Vector2(80, 16) 49 | cell_wh = Vector2(14, 30) 50 | 51 | [node name="OrgSceneTitle" parent="." instance=ExtResource("10")] 52 | text_len = 0 53 | 54 | [node name="OrgCommands" type="Node" parent="."] 55 | script = ExtResource("4") 56 | 57 | [node name="JLang" type="JLang" parent="."] 58 | 59 | [connection signal="keypress" from="jp-repl" to="." method="_on_JKVM_keypress"] 60 | [connection signal="keypress" from="jp-editor" to="." method="_on_JKVM_keypress"] 61 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/JPrezPlayer.gd: -------------------------------------------------------------------------------- 1 | class_name JPrezPlayer extends Node 2 | # This component renders jprez directly from the J interpreter. 3 | # It knows nothing about org files except to pass the path to jprez. 4 | 5 | signal macro_finished # when a macro finishes playing 6 | 7 | @onready var JI = $JLang # J interpreter 8 | @onready var jprez_repl = get_node('jp-repl') 9 | @onready var jprez_editor = get_node('jp-editor') 10 | 11 | var jprez_ready = true # false when macro is playing 12 | 13 | func _ready(): 14 | jprez_repl.JI = JI 15 | jprez_repl.fake_focus = true 16 | $AudioStreamPlayer.connect("finished",Callable(self,"_on_audio_finished")) 17 | # print("J_HOME:", OS.get_environment('J_HOME')) 18 | JI.cmd("9!:7 [ (16+i.11){a. NB. box drawing characters") 19 | JI.cmd("ARGV_z_=:,<''") 20 | JI.cmd("load 'tangentstorm/j-kvm/vid'") 21 | JI.cmd("coinsert 'kvm'") 22 | JI.cmd("1!:44 'd:/ver/jprez'") 23 | JI.cmd('gethw_vt_ =: {{ 45 220 }}') # TODO: parameterize this? 24 | JI.cmd("load 'jprez.ijs'") 25 | 26 | func set_org_path(org_path): 27 | JI.cmd("cocurrent'base'") 28 | JI.cmd("MACROS_ONLY =: 0") 29 | JI.cmd("open '%s'" % [org_path]) 30 | JI.cmd("reopen_base_=:reopen_outkeys_ f.") 31 | JI.cmd("reopen''") 32 | JI.cmd("goto 0") 33 | 34 | func _process(_dt): 35 | # we do this checked every tick so we can watch macros play. 36 | JI.cmd("update__app''") 37 | # !! these '* 2' bits are so the flags are non-boolean (because j-rs-gd only does ints atm) 38 | if JI.cmd('R__repl * 2'): jprez_repl.refresh() 39 | if JI.cmd('R__editor * 2'): jprez_editor.refresh() 40 | # we have to run update_app /before/ checking A__red 41 | if not jprez_ready: 42 | jprez_ready = not JI.cmd('A__red * 2') # TODO: return bools as ints 43 | # signal checked the next frame so that macro lines always take at least one frame. 44 | # (this is so the stepper doesn't play both an input line and an audio in one step) 45 | if jprez_ready: call_deferred('emit_signal', 'macro_finished') 46 | 47 | func goix(scene, cmd): 48 | jprez_ready = false 49 | JI.cmd("cocurrent'base'") 50 | JI.cmd('goix %d %d' % [scene, cmd]) 51 | JI.cmd("advance''") 52 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/jprez.gd: -------------------------------------------------------------------------------- 1 | @tool extends Control 2 | 3 | @export var jlang_nodepath = 'JLang' 4 | @onready var JI = get_node(jlang_nodepath) # J interpreter 5 | 6 | func set_org(absolute_org_path): # :OrgNode): 7 | var org_path = ProjectSettings.globalize_path(absolute_org_path) # org.resource_path) 8 | JI.cmd("ORG_PATH =: '%s'" % [org_path]) 9 | JI.cmd("reopen''") 10 | update_editor() 11 | 12 | func _ready(): 13 | var hw = '36 157' 14 | print("J_HOME:", OS.get_environment('J_HOME')) 15 | JI.cmd("9!:7 [ (16+i.11){a. NB. box drawing characters") 16 | JI.cmd("ARGV_z_=:,<''") 17 | JI.cmd("load 'tangentstorm/j-kvm/vid'") 18 | JI.cmd("coinsert 'kvm'") 19 | JI.cmd("1!:44 'd:/ver/jprez'") 20 | JI.cmd('gethw_vt_ =: {{ ' +hw+ '}}') 21 | JI.cmd("load 'jprez.ijs'") 22 | if Engine.is_editor_hint(): 23 | var cmds = get_node_or_null("jp-cmds") 24 | if cmds:cmds.grab_focus() 25 | else: 26 | var repl = $"jp-repl" 27 | if repl: repl.grab_focus() 28 | 29 | func _on_JKVM_keypress(code, ch, fns): 30 | print('keypress('+str({'code':code, 'ch':ch, 'fns':fns})+')') 31 | var s = "" 32 | for fn in fns: s += "'"+fn+"';" 33 | s = "("+s+"'k_any')" 34 | JI.cmd("fn =: > {. (#~ 3 = 4!:0) "+s) 35 | JI.cmd("(fn~)'"+ch+"'") 36 | update_editor() 37 | 38 | func update_editor(): 39 | if true: # Engine.is_editor_hint(): 40 | for np in ['jp-cmds', 'jp-list']: 41 | var n = get_node_or_null(np) 42 | if n: n.refresh() 43 | 44 | func refresh_jkvm_node(node_path): 45 | var node = get_node_or_null(node_path) 46 | if node: 47 | node.refresh() 48 | 49 | func refresh_repl(): 50 | var r = JI.cmd('R__repl * 2') # so it's not a boolean 51 | if r: refresh_jkvm_node('jp-repl') 52 | func refresh_editor(): 53 | var r = JI.cmd('R__editor * 2') # so it's not a boolean 54 | if r: refresh_jkvm_node('jp-editor') 55 | 56 | func _process(_dt): 57 | if true: #Engine.is_editor_hint(): 58 | if not JI: return 59 | if not JI.has_method('cmd'): return 60 | JI.cmd("cocurrent'base'") 61 | JI.cmd("update__app''") 62 | refresh_repl() 63 | refresh_editor() 64 | 65 | func _on_jplist_focus_entered(): 66 | JI.cmd("keymode'outkeys'") 67 | 68 | func _on_jpcmds_focus_entered(): 69 | JI.cmd("keymode'outkeys'") 70 | 71 | func _on_jprepl_focus_entered(): 72 | JI.cmd("keymode'replkeys'") 73 | 74 | func _on_jpeditor_focus_entered(): 75 | JI.cmd("keymode'edkeys'") 76 | -------------------------------------------------------------------------------- /org.ijs: -------------------------------------------------------------------------------- 1 | NB. jprez-specific org-mode parser 2 | NB. the jprez format is a subset of org-mode: 3 | NB. - #+title: at the top 4 | NB. - any number of nested headlines 5 | NB. - body text only appears in leaf nodes ("slides") 6 | NB. - zero to one #+begin_src j ... #+end_src per slide 7 | NB. - lines starting with # indicate comments 8 | NB. - [[path/to/file.xyz]] indicates an audio file 9 | NB. - verbatim lines (starting with ":") indicate repl input or macros 10 | NB. - ": ." indicates that the line is a jprez keyboard macro 11 | NB. - ": " (not followed by " .") indicates verbatim repl input 12 | 13 | MACROS_ONLY =: 0 NB. toggled by godot-plugin to ignore non-macro steps 14 | 15 | between =: (>:@[ + i.@<:@-~)/ NB. between 3 7 -> 4 5 6 16 | parse =: monad define 17 | NB. parse a single slide 18 | NB. returns (head; text; src) triple 19 | head =. (2+I.'* 'E.h) }. h=.>{. y NB. strip any number of leading '*'s, up to ' ' 20 | depth =. h <:@-&# head NB. record the number of leading stars 21 | text =. }. y 22 | srcd =. '#+begin_src j';'#+end_src' NB. source code delimiters 23 | src =. , |: si=.I. y ="1 0 srcd NB. indices of start and end delimiters 24 | if. #src do. 25 | code =. y {~ between 2$src NB. only take the first source block 26 | text =. (1{src) }. text 27 | else. code =. a: end. 28 | if. MACROS_ONLY do. text =. text#~':'={.&> text end. 29 | ( org NB. 1 if org line starts with '*' (a headline) 37 | slide0 =. headbits <;.1 org NB. group lines: each headline starts a new slide 38 | title =: {.org 39 | slides =: > parse each slide0 }} 40 | 41 | 42 | slide_lines =: {{ 43 | 'd h t c' =. (depth;head;text;<@code) y 44 | r =. <(d#'*'),' ',h 45 | if. #>c do. r =. r,'#+begin_src j';c,<'#+end_src' end. 46 | r,t }} 47 | 48 | NB. turn the arrays back into org text: 49 | org_text =: {{ ;(,&LF)L:0 title,'';;slide_lines each i.#slides }} 50 | 51 | head =: verb : '> ( str 52 | text =: verb : '> ( [box(str)] 53 | code =: verb : '> ( [box(str)] 54 | depth=: verb : '> ( [box(str)] 55 | -------------------------------------------------------------------------------- /jp-websocket.ijs: -------------------------------------------------------------------------------- 1 | NB. minimal example for talking to JQt over a websocket 2 | (1!:44)'d:/ver/jprez' 3 | load'tangentstorm/j-kvm/vt' 4 | gethw_vt_=:{{ 24 80 }} 5 | load'jprez.ijs' 6 | 7 | open ORG_PATH=:'d:/ver/j-talks/wip/dealing-cards/dealing-cards.org' 8 | goix (hcur=:0), [ ccur=:0 9 | 10 | port =: 4444 11 | 12 | wssvr_handler_z_ =: {{ 13 | NB. global handler for all websocket events 14 | 'e sock' =: y 15 | if. e = jws_onMessage_jqtide_ do. wss1_jrx_ on_wsmsg_base_ wss0_jrx_ 16 | elseif. e = jws_onOpen_jqtide_ do. on_wsopen_base_'' 17 | end. }} 18 | 19 | 20 | ws_send =: {{ wd 'ws send ',(":sock),' *',":y }} 21 | ws_state =: {{ wd 'ws state ',":sock }} 22 | 23 | NB. echo websocket parameters (uri, port, etc) to console 24 | on_wsopen =: {{ NB. smoutput ws_state'' 25 | smoutput <'ws connected' }} 26 | 27 | 28 | rebuild'' 29 | 30 | load'convert/pjson' 31 | ischar_pjson_ =: (2 131072 262144 e.~ 3!:0) 32 | 33 | make_vbuf =: {{ 34 | if. 2=#y do. 'W__repl H__repl' =: y end. 35 | buf=. tobuf__repl'' 36 | res =. (;:'w h bgb fgb chb') ,. W__repl;H__repl;(;BGB__buf);(;FGB__buf);(;CHB__buf ) 37 | codestroy__buf'' 38 | res }} 39 | 40 | 41 | NB. there's a bug in websockets with double quotes: https://github.com/jsoftware/jsource/issues/187 42 | enc =: {{ (';';'')stringreplace enc_pjson_ y }} 43 | send_patch =: {{ 44 | hcur=.cur [ ccur=.C__cmds [ chunks =.L__cmds [ heads =. heads 45 | code =. code cur [ wpath =. wavpath'' 46 | wps =. calc_wavpath each chunks 47 | vbuf =. make_vbuf'' 48 | keys =. 'hcur ccur opath chunks heads code wpath wps vbuf' 49 | ws_send enc k,.". each k=.;:keys }} 50 | 51 | apply_patch =: {{)v 52 | olds =. orgs 53 | for_row. dec_pjson_ y do. (k) =: v [ 'k v' =. row end. 54 | if. -. orgs -: olds do. 55 | reorg orgs 56 | send_patch'' 57 | else. 58 | goix hcur,ccur 59 | end. }} 60 | 61 | on_wsmsg =: {{ 62 | NB. ws_send y NB. echo request back to the socket 63 | NB. smoutput y NB. echo request payload to console 64 | smoutput 'msg:';y 65 | if. x -: 'text' do. NB. what to do if x -: 'text' 66 | if. '.' = {. y do. 67 | NB. ".init main" is only message sent this way for now 68 | send_patch'' 69 | else. 70 | apply_patch y 71 | send_patch'' 72 | end. 73 | else. NB. what to do if x -: 'binary' 74 | end. }} 75 | 76 | wd 'ws listen ',":port 77 | -------------------------------------------------------------------------------- /tok.ijs: -------------------------------------------------------------------------------- 1 | NB. token-centric editor component. 2 | NB. (currently this file is not a standalone application, 3 | NB. but is called by mje.ijs) 4 | load 'tangentstorm/j-kvm/ui tangentstorm/j-lex' 5 | 6 | cocurrent 'tok' extends 'vt' 7 | 8 | jcolor =: {{ 9 | select. tag [ 'tag tok'=. y 10 | case. '' do. 16b999999 NB. ?? 11 | case. 'S' do. 16b999999 NB. space 12 | case. 'A' do. 16b008b8b NB. assignment 13 | case. 'i' do. 16bffffff NB. identifier 14 | case. 'v' do. 16bffd700 NB. verb 15 | case. 'a' do. 16bc78243 NB. adverb 16 | case. 'D' do. 16bf95c54 NB. def braces 17 | case. 'c' do. 16bff4500 NB. conjunction 18 | case. 'P' do. 16b555555 NB. parens 19 | case. 'p' do. 16b32cd32 NB. proname 20 | case. 'N' do. 16bf984e5 NB. numeric 21 | case. 'L' do. 16b2dabfc NB. literal 22 | case. 'C' do. 16b666666 NB. comment 23 | case. 'K' do. 16bf95c54 NB. control word 24 | case. do. 16b0000cd end. }} 25 | 26 | vtcolor =: {{ NB. colorize a line of j using vt escape codes 27 | res =. '' 28 | for_tt. {.>{.> jlex y do. 29 | if. -.*#tt do. continue. end. 30 | res =. res,(FG24B jcolor tt),,>}.tt 31 | end. res }} 32 | 33 | coclass 'TokEd' extends 'UiEditWidget';'tok' 34 | 35 | create =: {{ 36 | create_UiEditWidget_ f. 0$a: 37 | BG =: _1 38 | NB. ced = character editor for new tokens 39 | ced =: '' conew 'UiEditWidget' 40 | EX__ced =: 0 [ FG__ced =: _7 [ BG__ced =: _234 }} 41 | 42 | 43 | kvm_init =: {{ curs 0 }} 44 | kvm_done =: {{ curs 1 }} 45 | 46 | run =: draw loop_kvm_ 47 | jcut =: jcut_jlex_ 48 | save =: curxy@raw@1 49 | rest =: raw@0 @ reset@'' @ goxy 50 | kpxy =: {{ 51 | NB. execute u and then restore the cursor position. 52 | res [ rest xy [ res =. u y [ xy =. save'' 53 | : 54 | res [ rest xy [ res =. x u y [ xy =. save'' }} 55 | 56 | 57 | put_tok =: {{ 58 | if. -.*#y do. return. end. 59 | fg jcolor 'tag tok' =. y 60 | puts tok }} 61 | 62 | jtype =: jtype_jlex_ &.> 63 | 64 | render =: {{ 65 | for_tok. C {. B do. put_tok (jtype,]) tok end. 66 | if. MODE e. 'iq' do. puts B__ced [ fg FG__ced [ bg BG__ced end. 67 | reset'' 68 | for_tok. C }. B do. put_tok (jtype,]) tok end. }} 69 | 70 | emit =: {{ 71 | ins jcut B__ced 72 | C__ced =: 0 [ B__ced =: '' [ XY__ced =: 0,~4++/# S:0 C {. B }} 73 | 74 | eval =: {{ 75 | try. err =. 0 [ res =. ":".;B 76 | catch. err =. 1 [ res =. 'error' end. NB. how to get error message? 77 | reset'' 78 | for_i. 1+i.#res do. ceol goxy 0,i end. 79 | goxy 0,1 80 | puts res ,"1 CRLF }} 81 | 82 | 83 | vid =: conew'vid' 84 | 85 | -------------------------------------------------------------------------------- /repl.ijs: -------------------------------------------------------------------------------- 1 | NB. repl ui widget, including history display 2 | load 'tangentstorm/j-kvm tangentstorm/j-kvm/ui tangentstorm/j-lex' 3 | require 'tok.ijs worlds.ijs syntaxline.ijs' 4 | 5 | coclass 'UiRepl' extends 'UiWidget' 6 | coinsert 'kvm' 7 | 8 | create =: {{ 9 | 'H W' =: gethw_vt_'' 10 | hist =: (,line 39 | end. 40 | NB. draw line editor / prompt on the last line, with 3-space prompt 41 | NB. but hide special lines like @cls (valid j never starts with '@') 42 | if. '@'-:{.B__ed do. C__ed=:0 [ B__ed =: '' end. 43 | XY__ed =: 3 0 + (0, # hist) 44 | termdraw__ed y 45 | R =: R__ed =: 0 }} 46 | 47 | 48 | bak =: {{ 49 | if. (atz__hist'') *. #B__ed do. 50 | NB. save work-in-progress so arrow up, arrow down restores it. 51 | ins__hist B__ed [ fwd__hist'' 52 | end. 53 | bak__hist'' 54 | setval__ed >val__hist'' 55 | R =: 1 }} 56 | 57 | fwd =: {{ 58 | fwd__hist'' 59 | setval__ed >val__hist'' 60 | R =: 1 }} 61 | 62 | 63 | NB. k_arup =: bak 64 | NB. k_ardn =: fwd 65 | 66 | NB. event handler for accepting the finished input line 67 | accept =: {{ 68 | exec_world_ B__ed 69 | goz__hist'' 70 | ins__hist B__ed 71 | goz__hist'' 72 | L__hist =: L__hist -. ed__repl }} 87 | repl^:('repl.ijs' {.@E.&.|. >{.}.ARGV)'' 88 | -------------------------------------------------------------------------------- /etc/db.ijs: -------------------------------------------------------------------------------- 1 | NB. program to write a jprez file to a sqlite database. 2 | NB. the schema is not (yet?) public as of this writing so 3 | NB. this file is probably not useful to anyone else 4 | NB. without major modifications. 5 | load 'jprez.ijs' 6 | load 'data/sqlite' 7 | 8 | 9 | NB. --- additions to api.ijs in data/sqlite 10 | cocurrent'psqlite' 11 | 12 | NB. https://sqlite.org/c3ref/db_config.html 13 | NB. int sqlite3_db_config(sqlite3*, int op, ...); 14 | NB. !! this is a variadic function. not sure how to handle with cd 15 | NB. but I only need to pass in two arguments... 16 | lib=. '"',libsqlite,'"' 17 | sqlite3_db_config=:(lib,' sqlite3_db_config > ',(IFWIN#'+'),' i x i i i') &cd 18 | 19 | 20 | NB. https://sqlite.org/c3ref/load_extension.html 21 | NB. int sqlite3_load_extension(*db, *zFile, *zProc, **pzErrMsg) 22 | NB. x sqlite3 *db, /* Load the extension into this database connection */ 23 | NB. *c const char *zFile, /* Name of the shared library containing extension */ 24 | NB. *c const char *zProc, /* Entry point. Derived from zFile if 0 */ 25 | NB. *x char **pzErrMsg /* Put error message here if not 0 */ 26 | sqlite3_load_extension=:(lib,' sqlite3_load_extension > ',(IFWIN#'+'),' i x &c x *x ')&cd 27 | 28 | NB. https://sqlite.org/c3ref/c_dbconfig_defensive.html 29 | SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION =: 1005 30 | NB. https://sqlite.org/c3ref/load_extension.html 31 | sqlconfig =: {{ sqlite3_db_config CH;y }} 32 | sqlload =: {{ 33 | flag =.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION_psqlite_ 34 | sqlconfig flag;1;0 35 | sqlite3_load_extension CH;y;0;,0 }} 36 | cocurrent'base' 37 | 38 | 39 | db =: sqlopen_psqlite_'/home/michal/db/tree.sdb' 40 | sqlload__db '/home/michal/db/define' 41 | 42 | MAX =: ->: NIL =: SQLITE_NULL_INTEGER_psqlite_ 43 | tree_ins =: {{ sqlinsert__db '_tree';(;:'pi ix t v');{:stack }} 59 | 60 | 61 | imp_org =: {{ 62 | echo 'imp_org';y 63 | assert. (pi = TOP), stack-:,a: 64 | 'f p' =. (>@{: ; '/' joinstring '';3 }. }:) '/'splitstring y 65 | node 'org' noix f 66 | 'path' attr p 67 | echo 'pi:'; pi =: ltri'' 68 | d =: 0 [ open y 69 | for_i. i.#heads do. 70 | goix i,0 71 | while. (depth cur) <: d do. done [ d=: d - 1 end. 72 | node 'hdl' item head cur [ d =: d + 1 73 | if. #c =. LF joinstring code cur do. 'src' item c end. 74 | for_row. (text cur)-.a: do. s=.>row 75 | if. ': . ' -: 4 {. s do. 'mac' item s 76 | elseif. ': '-: 2 {. s do. 'cmd' item s 77 | elseif. '#'=0{s do. 'rem' item s 78 | elseif. '@'=0{s do. 'anm' item s 79 | else. 'txt' item s end. 80 | end. 81 | end. 82 | echo 'done with';y;'depth:';d 83 | done^:(d+1)''}} 84 | 85 | {{ 86 | vid =: sqlopen_psqlite_'/home/michal/db/videos.sdb' 87 | dat =: sqlread__vid 'select path||''/''||name as p from wip where p like ''%org''' 88 | for_p0. (1;0){::dat do. 89 | echo p =. ('/home/michal',>p0) 90 | imp_org p 91 | end. 92 | }}'' -------------------------------------------------------------------------------- /worlds.ijs: -------------------------------------------------------------------------------- 1 | NB. This implements something like the 'world' concept by Allesandro Warth 2 | NB. (used in his OMeta system). 3 | NB. 4 | NB. A world is just a locale (chained namespace), but when a line of code is executed 5 | NB. through the 'exec' function, /and/ that line contains an assignment, then a new 6 | NB. world is created on the fly to hold the results of the assignment. 7 | NB. 8 | NB. The result is that you can 'time travel' back and forth between states. 9 | NB. 10 | NB. Limitations: 11 | NB. - does not track global assignments that happen inside verbs 12 | NB. - assigmnents using locatives operate independently of the 'timeline' 13 | NB. 14 | NB. Basically, this is just enough magic to power my presentation tool and probably 15 | NB. should not be used for anything else without careful consideration. 16 | 17 | cocurrent 'world' 18 | require 'tok.ijs' NB. for colorization 19 | 20 | init =: {{ 21 | ii =: 0 NB. world counter 22 | ihist =: '' NB. input history 23 | ehist =: '' NB. echo history 24 | coerase (#~ #@('WORLD\d+'&rxmatches)&>) conl'' 25 | NB. EHISTL0 and EHISTL1 are length of buffer. EHISTCS is last clear screen. 26 | EHISTCS_WORLD0_=: EHISTL0_WORLD0_=: EHISTL1_WORLD0_=: ii_world_=:0 }} 27 | 28 | NB. m in_world_ : name builder: append locative for current world to m 29 | in =: {{ m,'_',(this_world_''),'_' }} 30 | 31 | lines =: {{ 32 | if. 1 = # $ y do. y =. ,:y end. 33 | ,(LF cut CR -.~ ])"1 y }} 34 | 35 | boxc =: ,(,.<"0 a.{~ 16+i.11),.(8 u: dfh) each cut'250c 252c 2510 251c 253c 2524 2514 2534 2518 2502 2500' 36 | boxe =: {{ y rplc"1 boxc }} 37 | echo =: {{ 38 | for_line. lines boxe y do. ehist =: ehist,line end. 39 | ('EHISTL1'in_world_) =: #ehist }} 40 | cscr =: {{ ('EHISTCS'in_world_)=:#ehist }} 41 | 42 | this =: {{ 'WORLD',":ii_world_ }} 43 | next =: {{ 44 | ni =. ii_world_ + 1 45 | cocurrent nw =. 'WORLD',":ni 46 | coinsert this_world_'' 47 | if. EHISTL1 ~: EHISTL0 do. EHISTL0 =: EHISTL1 end. 48 | ii_world_ =: ni 49 | this_world_'' }} 50 | 51 | NB. nesting depth of tokens 52 | depth =: {{ +/\ 1 _1 0 {~ ('{{';'}}')&i. L:1 y }} 53 | 54 | exec =: {{ NB. run code in the current 'world' 55 | NB. you get a domain error if you try to perform (".'y=:1') 56 | NB. inside a verb, because it conflicts with the locally defined 'y'. 57 | NB. (In fact, this is true for any locally defined name, hence the 58 | NB. 'exec' prefix on all the locals here.) 59 | NB. so.. we will rewrite 'x','y', etc to ('y'in_world_)... 60 | NB. BUT we do NOT want to do this inside a direct definition, so 61 | NB. we use 'depth' as a mask. 62 | y =. 8 u: y 63 | ihist =: ihist,execslot{exec_toks),'_',(this_world_''),'_' 75 | exec_toks =. exec_fix execslot } exec_toks 76 | end. 77 | end. 78 | if. # exec_toks do. exec_toks =. exec_toks #~ -. 'NB.' {.@E. S:0 exec_toks end. 79 | exec_str =. ' ' joinstring exec_toks 80 | if. # exec_str -. ' ' do. 81 | (<'do'in_world_)`:0 'JRESULT=:', exec_str 82 | if. (type 'JRESULT'in_world_) -: <'noun' do. exec_res =. ": 'JRESULT'in_world_~ 83 | else. exec_res =. 5!:5 <'JRESULT'in_world_ end. 84 | if. (*# exec_res) > '=:'-:>1{exec_toks,a:,a: do. echo_world_ exec_res end. 85 | end. 86 | catch. 87 | echo_world_ ('do_WORLD\d+_|JRESULT=:';'') rxrplc dberm'' 88 | end. }} 89 | -------------------------------------------------------------------------------- /godot-plugin/addons/jprez/JKVM.gd: -------------------------------------------------------------------------------- 1 | @tool class_name JKVM extends Control 2 | 3 | signal keypress(code, ch, fns) 4 | 5 | @export var font : Font = preload("res://fonts/noto-font.tres") 6 | @export var font_base : Vector2 = Vector2(1,12) 7 | @export var font_size : int = 24 8 | 9 | @export var jlang_nodepath = "../JLang" 10 | @export var j_widget : String = "app" 11 | @export var j_locale : String = "base" 12 | 13 | @export var rng_seed : int = 82076 : set = _set_rng_seed 14 | @export var grid_wh : Vector2 = Vector2(80, 25) : set = _set_grid_wh 15 | @export var cell_wh : Vector2 = Vector2(12,14) : set = _set_cell_wh 16 | @export var cursor_visible: bool = false : set = _set_cursor_visible 17 | @export var fake_focus: bool = false 18 | 19 | @onready var JI = get_node(jlang_nodepath) 20 | 21 | var cursor_xy : Vector2 = Vector2.ZERO 22 | var fg : Color = Color.GRAY 23 | var panel : Color = Color.BLACK 24 | 25 | var rng : RandomNumberGenerator = RandomNumberGenerator.new() 26 | 27 | var FGB : PackedColorArray 28 | var BGB : PackedColorArray 29 | var CHB : PackedInt32Array # unicode code points 30 | 31 | func _set_rng_seed(v): 32 | rng_seed = v 33 | rng.seed = v 34 | 35 | func _set_cursor_visible(v): 36 | cursor_visible = v 37 | #if $cursor: $cursor.visible = v 38 | 39 | func _set_grid_wh(v): 40 | grid_wh = v 41 | recalc_size() 42 | 43 | func _set_cell_wh(v): 44 | cell_wh =v 45 | recalc_size() 46 | 47 | func recalc_size(): 48 | custom_minimum_size = grid_wh * cell_wh 49 | size = custom_minimum_size 50 | queue_redraw() 51 | 52 | func _ready(): 53 | focus_mode = FOCUS_CLICK 54 | var cursor = ColorRect.new() 55 | cursor.color = Color.TRANSPARENT 56 | cursor.size = cell_wh 57 | cursor.name = "cursor" 58 | add_child(cursor) 59 | _set_cursor_visible(cursor_visible) 60 | _reset() 61 | _cscr() 62 | if j_widget: 63 | call_deferred('refresh') 64 | 65 | func _reset(): 66 | fg = Color.GRAY 67 | panel = Color.BLACK 68 | 69 | func _cscr(): 70 | FGB = PackedColorArray() 71 | BGB = PackedColorArray() 72 | CHB = PackedInt32Array() 73 | for i in range(grid_wh.x * grid_wh.y): 74 | FGB.append(fg) 75 | BGB.append(panel) 76 | CHB.append(32) 77 | 78 | func _rnd(): 79 | # fill buffer with random colored characters 80 | # ("screen saver" for debugging / fps checks) 81 | for i in range(grid_wh.x * grid_wh.y): 82 | FGB[i] = 0x333333ff + (rng.randi_range(0, 0xcccccc) << 8) 83 | BGB[i] = Color.BLACK 84 | CHB[i] = rng.randi_range(33,126) 85 | queue_redraw() 86 | 87 | func _draw(): 88 | var w = 80; var h = 16 89 | for y in range(grid_wh.y): 90 | for x in range(grid_wh.x): 91 | var xy = Vector2(x,y)*cell_wh 92 | var p = y * grid_wh.x + x 93 | draw_rect(Rect2(xy, cell_wh), BGB[p]) 94 | draw_char(font, xy+font_base, char(CHB[p]), font_size, FGB[p]) 95 | 96 | @onready var pal : PackedColorArray = _make_palette() 97 | func _make_palette(): 98 | var res = PackedColorArray() 99 | var ansi = [ # -- ansi colors -- 100 | 0x000000, # black 101 | 0xaa0000, # red 102 | 0x00aa00, # green 103 | 0xaaaa00, # dark yellow ( note: not vga brown! ) 104 | 0x0000aa, # blue 105 | 0xaa00aa, # magenta 106 | 0x00aaaa, # cyan 107 | 0xaaaaaa, # gray 108 | 0x555555, # dark gray 109 | 0xff5555, # light red 110 | 0x55ff55, # light green 111 | 0xffff55, # yellow 112 | 0x5555ff, # light blue 113 | 0xff55ff, # light magenta 114 | 0x55ffff, # light cyan 115 | 0xffffff ]# white 116 | for a in ansi: res.append(Color(a * 0x100 + 0xff)) 117 | 118 | # colors 16..231 are a color cube: 119 | var ramp = [ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF ] 120 | for r in ramp: 121 | for g in ramp: 122 | for b in ramp: 123 | res.append(Color(((r << 16 ) + ( g << 8 ) + b) * 0x100 + 0xff)) 124 | 125 | # 232..255 are a black to gray gradiant: 126 | var grays = [ 127 | 0x00, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 128 | 0x58, 0x62, 0x6C, 0x76, 0x80, 0x8A, 0x94, 0x9E, 129 | 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE ] 130 | for v in grays: 131 | res.append(Color((( v << 16 ) + ( v << 8 ) + v) * 0x100 + 0xff)) 132 | 133 | return res 134 | 135 | func _gui_input(e): 136 | if e is InputEventKey and e.pressed: 137 | var code = e.keycode 138 | var ch = char(e.unicode) 139 | var fns = [] 140 | if code >= 32 and code < 127: 141 | var fn = 'k' 142 | var asc = true 143 | if e.ctrl_pressed: fn +='c' 144 | if e.alt_pressed: fn += 'a' 145 | if e.alt_pressed or e.ctrl_pressed: 146 | asc = false 147 | ch = char(code).to_lower() 148 | match ch: 149 | "'": # need to escape this char in j 150 | fn += '_quote'; ch="''" 151 | ' ': fn += '_space' 152 | '+': fn += '_plus' 153 | _: fn += '_' + ch 154 | fns.append(fn) 155 | if asc: fns.append('k_asc') 156 | else: 157 | match code: 158 | KEY_SHIFT, KEY_ALT, KEY_CTRL: return 159 | KEY_UP: fns.append('k_arup') 160 | KEY_DOWN: fns.append('k_ardn') 161 | KEY_RIGHT: fns.append('k_arrt') 162 | KEY_LEFT: fns.append('k_arlf') 163 | KEY_TAB: fns.append('k_tab') 164 | KEY_ENTER: fns.append('kc_m') 165 | KEY_BACKSPACE: fns.append('k_bsp') 166 | emit_signal('keypress', code, ch, fns) 167 | 168 | func _to_colors(ints): 169 | var res = PackedColorArray() 170 | for i in ints: 171 | if i < 0: res.append(pal[-i]) 172 | else: res.append(Color(i*0x100+0xFF)) 173 | return res 174 | 175 | func j_hw(): 176 | return str(grid_wh.y) + ' ' + str(grid_wh.x) 177 | 178 | func J(cmd:String): 179 | #print(' '+cmd) 180 | #print(JI.cmd_s(cmd)) 181 | JI.cmd_s(cmd) 182 | 183 | func Jv(cmd)->Variant: 184 | #print(' '+cmd) 185 | var res = JI.cmd(cmd) 186 | return res 187 | 188 | func refresh(): 189 | var vid = 'scratch' 190 | J("cocurrent '" + j_locale + "'") 191 | J(vid + "=: 'vid' conew~ |." + j_hw()) 192 | J("pushterm_kvm_ "+ vid) 193 | J("'H__" + j_widget + " W__"+j_widget+"' =: "+j_hw()) 194 | J("render__" + j_widget + " " + str(int(fake_focus or has_focus()))) 195 | J("popterm_kvm_ ''") 196 | CHB = Jv("3 u:,CHB__" + vid) 197 | FGB = _to_colors(Jv(",FGB__" + vid)) 198 | BGB = _to_colors(Jv(",BGB__" + vid)) 199 | J('codestroy__' + vid + "''") 200 | queue_redraw() 201 | -------------------------------------------------------------------------------- /godot-plugin/orgprez.leo: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | godot presentation tool ("orgprez") 10 | creating a plugin 11 | @clean orgprez/plugin.cfg 12 | 13 | creating a new top-level tab in godot 14 | declare app and scene variables 15 | methods to place the tab label at top 16 | _enter_tree(): instantiate the editor tab's scene 17 | _exit_tree(): cleanup scene if plugin is uninstalled 18 | 19 | org file resources 20 | org importer 21 | declare org_importer plugin 22 | _enter_tree(): load the importer 23 | _exit_tree(): remove the importer 24 | 25 | click org file to edit 26 | handles() method lets plugin opt-in to editing a resource 27 | edit() says what to actually do when it's clicked 28 | 29 | 30 | TODO 31 | introduce decks and slides 32 | slides panel to manage editor text, linked scene 33 | restore nav panels (outline/chunklist) 34 | outline panel to manage headline tree 35 | preview panel in middle shows the linked scene? 36 | do some QA and make sure audio recorder actually works 37 | introduce the .wav subfolder to avoid clutter 38 | decouple from jprez repl 39 | move the "JPrezApp" to OrgPrezPlayer scene 40 | someday/maybe 41 | explain the editor tab 42 | 43 | how to use it 44 | 45 | @path addons 46 | @clean orgprez/plugin.gd 47 | declarations 48 | 49 | 50 | 51 | _enter_tree() 52 | 53 | 54 | 55 | 56 | 57 | 58 | _exit_tree() 59 | 60 | 61 | 62 | 63 | 64 | @clean orgprez/plugin.tscn 65 | 66 | notes 67 | 68 | 69 | 70 | 71 | 72 | @tabwidth 8 73 | @language config 74 | [plugin] 75 | 76 | name="orgprez" 77 | description="Godot tool for creating presentations from emacs org-mode documents." 78 | author="tangentstorm" 79 | version="0.1" 80 | script="plugin.gd" 81 | 82 | @language gdscript 83 | # presentation tool for emacs org files 84 | tool 85 | extends EditorPlugin 86 | 87 | @others 88 | 89 | 90 | @language unknown_language 91 | [gd_scene load_steps=2 format=2] 92 | 93 | [ext_resource path="res://addons/jprez/JPrezAudioTab.tscn" type="PackedScene" id=1] 94 | 95 | [node name="Control" type="Control"] 96 | anchor_right = 1.0 97 | anchor_bottom = 1.0 98 | margin_right = -1820.0 99 | margin_bottom = -980.0 100 | __meta__ = { 101 | "_edit_use_anchors_": false 102 | } 103 | 104 | [node name="JPrezAudioTab" parent="." instance=ExtResource( 1 )] 105 | margin_right = 1808.0 106 | margin_bottom = 926.0 107 | 108 | var scene = preload('res://addons/orgprez/OrgPrezAudioTab.tscn') 109 | var app # member variable holding instance of scene 110 | 111 | 112 | func _enter_tree(): 113 | @others 114 | 115 | 116 | func has_main_screen(): 117 | return true 118 | 119 | func get_plugin_name(): 120 | return "OrgPrez" # used in top-level tab name 121 | 122 | func get_plugin_icon(): 123 | return get_editor_interface().get_base_control().get_icon("AutoKey", "EditorIcons") 124 | 125 | func make_visible(x): # called at startup and when the tab is changed 126 | if app: app.visible = x 127 | 128 | 129 | app = scene.instance() 130 | get_editor_interface().get_editor_viewport().add_child(app) 131 | make_visible(false) # otherwise it shows up on-screen no matter what tab is active 132 | 133 | if app: app.queue_free() 134 | 135 | func _exit_tree(): 136 | @others 137 | 138 | 139 | @ 140 | search for references to REPL 141 | move this code into a signal 142 | the repl can just register to receive those signals 143 | 144 | maybe that would be something that lives in a custom version of the main scene, 145 | alongside the player object? 146 | 147 | 148 | @ 149 | 150 | Here's the tutorial for editing these scenes: 151 | 152 | https://docs.godotengine.org/en/stable/tutorials/plugins/editor/making_main_screen_plugins.html 153 | 154 | Important is the Vertical Expand checkbox for the top level Control. 155 | 156 | things i did in this step: 157 | 158 | - set up the default org file (TODO: maybe change this when you open an org file) 159 | 160 | 161 | each org file has one associated deck. 162 | (either specified or just same name but .tscn) 163 | this gets loaded INTO the top-level jprez scene 164 | (so orgprez has some generic player logic and run-time debugging tools like the stepper) 165 | 166 | 167 | 168 | @ 169 | For godot 4, maybe we just want to use the "keep" importer. 170 | (we may not actually need a resource) 171 | 172 | - probably get rid of timestamp sequence in org files (??) 173 | 174 | - Org.gd has begin_src j... probably should just be begin_src 175 | OrgNode.gd has same in output 176 | 177 | 178 | 179 | make_visible: 180 | #var efp = EditorFeatureProfile.new() 181 | #efp.set_disable_feature(EditorFeatureProfile.FEATURE_IMPORT_DOCK, not v) 182 | 183 | _enter_tree: 184 | var efp = EditorFeatureProfile.new() 185 | efp.set_disable_feature(EditorFeatureProfile.FEATURE_3D, true) 186 | 187 | 188 | 189 | res://wip/dealing-cards/dealing-cards.org 190 | res://wip/mandelbrot/mandelbrot.org 191 | @ 192 | this works by loading an importer class from the plugin.gd 193 | 194 | 195 | @ 196 | - the editor uses both 197 | - consider making one panel for debugging animations in the editor 198 | (basically do as much of the "stepping" stuff as possible at edit time) 199 | 200 | - merge into one panel (use dropdown to select the slide/section) 201 | - should be used to control animations 202 | - may or may not still directly edit. 203 | 204 | org_import = preload("res://addons/orgprez/org_import.gd").new() 205 | add_import_plugin(org_import) 206 | 207 | 208 | var org_import 209 | 210 | remove_import_plugin(org_import); org_import = null 211 | 212 | 213 | func handles(object): 214 | return object is OrgNode 215 | 216 | 217 | func edit(org): 218 | # remember directory for saving wave files 219 | # TODO: make this wav_dir 220 | # wav_dir = org.resource_path.get_base_dir() 221 | # if not wav_dir.ends_with('/'): wav_dir += '/' 222 | # chunks.org_dir = wav_dir # !! only need for waves, so update chunks code 223 | # wav_dir += '.wav' 224 | 225 | # tell the app to load that org-file 226 | # chunks.set_org(org) # outln will override this with first node (if one exists) 227 | # outln.set_org(org) 228 | # jprez.set_org(org) 229 | 230 | app.set_org(org) 231 | 232 | 233 | - for now still create and edit the org file in a text editor. 234 | 235 | 236 | - set main scene to be the org player 237 | - each org file should have a corresponding deck 238 | - based on which org file you click to edit, the project setting is changed 239 | - both the editor and main scene check the project settings to remember this decision 240 | - keep all your talks in different directories in the same git repot 241 | (they can be but do not have to be separate godot projects) 242 | 243 | 244 | 245 | @ 246 | actually create org files and add headings and add/edit lines frOm godot 247 | instead of using an outside editor. 248 | 249 | this becomes the main scene for your project (as a user). 250 | - need to move the Outline and Chunklist controls over 251 | - rename them to OrgOutline and OrgChunklist (??) 252 | 253 | 254 | 255 | @ 256 | could step through the animations as well? 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /jprez.ijs: -------------------------------------------------------------------------------- 1 | NB. minimal j editor 2 | NB. 3 | NB. This file my attempt to port my presentation tool 4 | NB. over to my new console-based libraries, token editor 5 | NB. components, etc. It is something of a scratchpad and 6 | NB. not well organized at the moment, but important enough 7 | NB. that I should probably have it under version control. 8 | (<'z') copath 'base' NB. clear previous path 9 | load'tangentstorm/j-kvm tangentstorm/j-kvm/ui tangentstorm/j-lex' 10 | load'worlds.ijs org.ijs tok.ijs repl.ijs jedit.ijs macro.ijs' 11 | load'convert/misc/md5' NB. for wavpath (filename calculation) 12 | coinsert 'kvm' 13 | dbg 0 NB. cannot debug from pascal shell at the moment 14 | copush_z_ =: {{ 18!:4 y [ BASE__y =: coname'' [ y=.{:ARGV do. ORG_PATH =:arg 21 | else. ORG_PATH =:'./screenplay.org' end. }}'' 22 | 23 | NB. org-mode stuff 24 | open =: {{ 25 | if. -. y -: '' do. ORG_PATH =: y end. 26 | reorg freads ORG_PATH 27 | L__list =: heads}} 28 | 29 | goix =: ] NB. stub because we call 'open' before drawing widgets :/ 30 | reorg =: {{ 31 | if. -. y -: '' do. org =: org_from_str orgs =: y end. 32 | NB. heads is the indented outline that shows up on the left 33 | heads =: <@;"1((' '#~+:@<:) each 3 {"1 slides),.(0{"1 slides) 34 | goix 0 0 35 | rebuild''}} 36 | 37 | rebuild =: {{ 38 | emit_vm_ =: ] NB. so j-kvm/vm outputs string we can capture (vs puts) 39 | NB. (index I. (C__list, C__cmd) { olw) says which world we are in 40 | index =: 0 0 $ 0 NB. one entry per line that starts with : (slide,line no) 41 | olw =: ,0 NB. outline worlds. 'now'=EHISTL0/EHISTL1 in (i{olw) 42 | init_world_'' 43 | tmp =. ''conew 'UiEditWidget' NB. for tracking start states per macro 44 | KPS__tmp =: _ [ TSV__tmp =: 0 NB. infinite speed, no random variation 45 | olr =: , line 50 | if. ': ' {.@E. line do. line =. 2}.line NB. : marks code line 51 | if. '. ' {.@E. line do. line =. 2}.line NB. : . is editor macro 52 | do__tmp line while. A__tmp do. update__tmp 1 end. NB. run macro 53 | elseif. '@cls' -: line do. cscr_world_@exec_world_'' 54 | else. 55 | setval__tmp line 56 | exec_world_ line NB. execute code in repl 57 | setval__tmp'' 58 | end. 59 | index =: index, i,j 60 | olr =: olr,:ymax'')-H_SPLIT 77 | XY__list =: 0,H_SPLIT 78 | TX_BG__list =: 16b111122 79 | 80 | NB. the detailed text of the screenplay (also macro commands) 81 | cmds =: 'UiList' conew~ a: 82 | W__cmds =: (xmax'')-(W__list) 83 | H__cmds =: H__list 84 | XY__cmds =: ((W__list+2),0) + XY__list 85 | TX_BG__cmds =: 16b111122 86 | render_item__cmds =: {{ 87 | NB. x is current line number 88 | s =. >y 89 | select. {. s 90 | case. ':' do. 91 | if. ': .' -: 3 {. s do. 92 | if. C=x do. fg _4 [ bg _12 else. fg _12 end. 93 | else. if. C=x do. fg _14 [ bg _6 else. fg _6 end. end. 94 | case. '#' do. 95 | fg _1 96 | case. do. 97 | if. C~:x do. NB. if not current line, show whether .wav exist 98 | if. fexist 'wav/',calc_wavpath_base_ s do. fg _15 end. 99 | end. 100 | end. 101 | puts W{.s }} 102 | 103 | 104 | 105 | NB. led is the line editor for editing a line of text in the outline 106 | led =: 'MacroWidget' conew~ '' 107 | XY__led =: XY__cmds 108 | W__led =: W__cmds 109 | V__led =: 0 110 | 111 | repl =: 'UiRepl' conew~ '' NB. W,XY of repl are calculated in show/hide editor 112 | H__repl =: H_SPLIT 113 | A__repl =: 1 114 | OLD__repl =: '' 115 | red =: ed__repl 116 | 117 | editor =: 'JCodeEditor' conew~ '' 118 | XY__editor =: 0 0 119 | H__editor =: H_SPLIT 120 | W__editor =: 70 121 | 122 | show_editor =: {{ V__editor =: 1 [ XY__repl =: (W__editor+2), 0 [ W__red =: W__repl =: W__editor -~ xmax'' }} 123 | hide_editor =: {{ V__editor =: 0 [ XY__repl =: 0 0 [ W__red =: W__repl =: 1+xmax'' }} 124 | hide_editor'' 125 | 126 | app =: (list,editor,cmds,led,repl) conew 'UiApp' 127 | smudge__app BG__app =: 16b112233 128 | 129 | NB. -- widget modifications --------------------------------------- 130 | 131 | NB. allow changing the repl line as we navigate through the outline 132 | new_repl_line =: {{ 133 | if. ': ' {.@E. val =. >val__cmds'' do. 134 | if. ': . ' {.@E. val do. '' 135 | else. 2}.val end. 136 | else. '' end. }} 137 | 138 | update__repl =: {{ 139 | if. A__ed do. update__ed y end. 140 | R =: R +. R__ed 141 | new =. new_repl_line_base_'' 142 | if. -. new -: OLD do. 143 | setval__ed OLD =: new 144 | R =: 1 145 | end. }} 146 | 147 | inscmd =: put_text [ ins__cmds 148 | 149 | accept__repl =: {{ 150 | inscmd_base_ ': ', B__ed 151 | inscmd_base_ ': . ', getlog__ed'' 152 | cmds =. cmds_base_ 153 | fwd__cmds^:2'' 154 | accept_UiRepl_ f.'' 155 | rebuild_base_'' }} 156 | 157 | cmdix =: {{ index I. C__list, C__cmds }} 158 | 159 | worldnum =: {{ olw_base_ pick~ cmdix_base_'' }} 160 | getworld__repl =: {{ 'WORLD',": worldnum_base_'' }} 161 | 162 | 163 | NB. keyboard control 164 | 165 | goix =: {{ 166 | NB. go to y =: slide,line 167 | setval__red'' [ A__red =: 0 NB. halt macro 168 | setval__editor code cur =: C__list =: 0{y 169 | fwd__cmds^:(1{y)'' [go0__cmds''[ L__cmds =: text cur 170 | smudge__app''}} 171 | 172 | goto =: {{ goix y,0 }} 173 | 174 | put_text =: {{ 0 0 $ slides =: (val__cmds'' do. playmacro'' 227 | else. fwd_cmd'' [ keymode'replkeys' end. }} 228 | 229 | FLIP =: 1 230 | flipped =: {{ FLIP }} 231 | flip =: {{ 232 | FLIP =: -FLIP 233 | H__list =: H__cmds =: H__editor [ th =. H__cmds 234 | H__editor =: H__repl =: th 235 | ty1 =. 1{XY__editor [ ty0 =. 1{XY__cmds 236 | XY__editor =: ty0 (1) } XY__editor 237 | XY__repl =: ty0 (1) } XY__repl 238 | XY__list =: ty1 (1) } XY__list 239 | XY__cmds =: ty1 (1) } XY__cmds 240 | move_splitter 0 241 | }} 242 | 243 | move_splitter =: {{ 244 | if. *./ 1 < (H__list-y),H__repl + y do. 245 | if. FLIP = 1 do. 246 | XY__list =: XY__list + 0,y [ H__list =: H__list - y 247 | XY__cmds =: XY__cmds + 0,y [ H__cmds =: H__cmds - y 248 | H__editor =: H__editor + y 249 | H__repl =: H__repl + y 250 | else. 251 | XY__editor =: XY__editor + 0,y [ H__editor =: H__editor - y 252 | XY__repl =: XY__repl + 0,y [ H__repl =: H__repl - y 253 | H__cmds =: H__cmds + y 254 | H__list =: H__list + y 255 | end. 256 | smudge__app'' 257 | end. }} 258 | 259 | toggle_editor =: {{ 260 | if. V__editor do. hide_editor'' else. show_editor'' end. 261 | smudge__app'' }} 262 | 263 | 264 | save =: {{ (org_text'') fwrites ORG_PATH }} 265 | halt =: {{ curs@1 @ reset@'' [ break_kvm_=: 1 }} 266 | insline =: edline@'' @ inscmd@'' 267 | delline =: rebuild @ put_text@'' @ del__cmds 268 | 269 | bak_cmd =: {{ 270 | if. (at0__cmds > at0__list)'' do. 271 | goto bak__list'' 272 | goz__cmds'' 273 | else. bak__cmds'' end. }} 274 | 275 | fwd_cmd =: {{ 276 | if. atz__cmds'' do. goto@fwd__list^:(-.@atz__list)'' 277 | else. fwd__cmds'' end. }} 278 | 279 | playmacro =: {{ 280 | NB. play macro we currently looking at in the outline 281 | if. a: ~: cmd =. val__cmds'' do. 282 | cmd =. >cmd 283 | if. ': . ' -: 4{.cmd do. 284 | setstate__red olr pick~ cmdix'' 285 | reset_rhist'' NB. this includes the completed line... 286 | set__hist__repl'' NB. so delete it. (TODO: handle multi-line macros) 287 | on_macro_end__red =: fwd_cmd_base_ 288 | do__red 4}.cmd 289 | end. 290 | end. }} 291 | 292 | 293 | (copush [ coinsert) 'outkeys' 294 | NB. ----------------------------------------------------------- 295 | coinsert BASE 296 | FOCUS =: list__BASE [ ''`inscmd =: inscmd__BASE 297 | 298 | k_any =: {{ 299 | if. 0=#y do. return. end. 300 | select. 0{y 301 | case.'9'do. goto bak__list'' 302 | case.'0'do. goto fwd__list'' 303 | case.'('do. bak__cmds'' 304 | case.')'do. fwd__cmds'' 305 | end. }} 306 | k_E =: edrepl 307 | k_N =: playmacro 308 | k_O =: insline 309 | k_d =: delline 310 | k_e =: edline 311 | k_j =: k_n =: fwd_cmd 312 | k_k =: k_p =: bak_cmd 313 | k_o =: insline@fwd__cmds 314 | kc_i =: focus_on_repl 315 | 316 | focus_on_repl =: {{ 317 | R__list =: R__repl =: 1 NB. to redraw focus 318 | keymode__BASE 'replkeys' }} 319 | 320 | 321 | NB. outline / macro editor 322 | 323 | register__led red NB. red listens for changes to led .. 324 | notify__led =: ] NB. .. but ignores them by default. 325 | edline =: {{ 326 | R__led =: V__led =: 1 [ XY__led =: XY__cmds + 0,C__cmds-S__cmds 327 | C__led =: 0 [ B__led =: b=. '',>val__cmds__BASE'' 328 | ed_edkeys_ =: led 329 | keymode__BASE 'edkeys' 330 | if. ': ' -: 2 {. b do. 331 | if. ': . ' -: 4{.b do. 332 | notify__red =: instaplay @ (4&}. [ reset_rhist_base_@'') 333 | setval__red '' 334 | else. 335 | notify__red =: setval @ (2&}.) 336 | setval__red 2}.b 337 | end. 338 | else. notify__red =: ] end. 339 | 0 0$0 }} 340 | 341 | edrepl =: {{ 342 | V__red =: R__red =: 1 343 | C__red =: 0 [ B__red =: 2}.>val__cmds__BASE'' 344 | keymode__BASE 'replkeys' }} 345 | 346 | copop'' 347 | 348 | copush 'edkeys' 349 | NB. ----------------------------------------------------------- 350 | FOCUS =: led =: led__BASE [ cmds =: cmds__BASE 351 | 352 | stop =: {{ 353 | keymode__BASE 'outkeys' 354 | V__led =: 0 [ R__repl =: R__red =: R__led =: R__cmds =: 1 355 | L__cmds =: ( C__cmds { L__cmds 397 | if. ':' -: {.line do. '' 398 | elseif. line -: '' do. '' 399 | else. calc_wavpath line end. }} 400 | 401 | NB. ----------------------------------------------------------- 402 | rl =: {{ load'jprez.ijs' }} NB. reload command (for development) 403 | 404 | NB. event loop 405 | mje =: {{ 406 | 9!:29]0 NB. disable infinite loop on error 407 | curs 0 408 | goto 0 NB. slide 0 409 | NB. main loop 410 | step__app loop_kvm_'base' 411 | reset'' 412 | 0$0}} 413 | 414 | resume =: {{ step__app loop_kvm_'base' [ smudge__app''}} 415 | 416 | open'' 417 | 418 | NB. only run if directly invoked from command line 419 | {{9!:29]1[9!:27 'mje _'}}^:('jprez.ijs' {.@E.&.|. >{.}.ARGV)'' 420 | -------------------------------------------------------------------------------- /backlog.org: -------------------------------------------------------------------------------- 1 | #+TITLE: backlog for jprez ( https://github.com/tangentstorm/jprez/ ) 2 | 3 | * inbox (for triage) 4 | ** TODO bug with wrapping on big screen: (+*:)^:(<15)~ 0.3 5 | ** TODO further improve render__app by omitting the cursor moves for adjacent characters 6 | ** TODO general purpose hbox/vbox widgets 7 | ** TODO general purpose grid widget 8 | - for macro debugger 9 | - for keymap editing 10 | - for file browser? (tree-grid) 11 | 12 | ** TODO [#A] fix blank line in history 13 | from outliner: 14 | - tab to go to repl 15 | - press up 16 | - you should see the previous line from history 17 | - instead you see a blank line 18 | 19 | ** TODO [#A] multi-line macros are broken because of this line in jprez.ijs :bug: 20 | : set__hist__repl__BASE'' NB. so delete it. (TODO: handle multi-line macros) 21 | 22 | ** TODO bug: pressing n at end of last slide takes you to top of last slide :jprez:bug: 23 | it should just do nothing. 24 | ** TODO allow binding keys to multi-character macros 25 | NB. TODO: 'd$' is the "correct" vim macro for kc_k/macro 'K' 26 | ** TODO group syntax to record a macro :ui:edit:macros: 27 | : (abcd) would execute a,b,c,d in 4 ticks 28 | ** TODO block syntax to run a macro at max speed :ui:edit:macros: 29 | : [abcd] would execute a,b,c,d in 1 tick 30 | - for typing long strings at speed 31 | - for grouping primitive operations into a single "command" 32 | ** TODO give blocks a name 33 | : :go[abcd] would define 'go' 34 | : {go} would call 'go' (??) 35 | - there needs to be an end delimiter because 36 | 37 | ** TODO character to invoke a named macro 38 | - right now, every macro instruction is a single character 39 | - therefore, many ascii characters are used up. 40 | - so: we need an escape character to indicate that we want to call a macro, 41 | rather than execute each character in its name. 42 | - we also need an end-escape character to indicate the end of the name. 43 | 44 | ** TODO lines in the screenplay that are too long are unreadable. 45 | "what good is the nub sieve?" 46 | 47 | ** TODO [#C] tokenize org-mode text 48 | ** TODO need actual outlining capabilities 49 | - insert headline (actually works but requires save/reload) 50 | - re-indenting is not possible without external editor 51 | 52 | ** TODO reopen verb should refresh the outline widgets :jprez: 53 | 54 | ** TODO adding line on blank slide breaks jprez :jprez:bug: 55 | i have a last slide with one blank line 56 | i try to edit the line, and get this: 57 | : |index error: stop 58 | : | L__cmds=:(/)"1 rct 208 | xys,.<"0 xys { b 209 | 210 | NB. each row is x,y,val 211 | ;@|."1(;"0~{&b) xys 212 | *** TODO redraw the changes: 213 | generate list of attributes of the changed cells. 214 | ideally you'd have rank 2 list: fg and bg. 215 | turn it into 2 boxes. 216 | 217 | anywhere the color changes from box to box, you issue a color change, 218 | otherwise ''. 219 | 220 | likewise, for the coordinates, if they're right next to each other, 221 | you don't need to issue a cursor move 222 | ** TODO [#A] use numeric prefix for multi-commands :jkvm:edit: 223 | especially important for pauses 224 | ** TODO [#A] set base tempo for playback (in kps) :jkvm:edit: 225 | ** TODO [#A] general undo system for all widgets :jkvm:ui: 226 | ** TODO [#B] pick a new default panic/break key 227 | maybe ^/ or ^] or whatever 228 | because ^space is used in emacs to set the mark (enter selection mode) 229 | ** TODO [#B] ctrl-space should enter selection mode 230 | ** TODO [#B] command to insert a line from repl into the editor at cursor :jprez:lsed: 231 | ** TODO [#B] command to evaluate the editor in the repl :jprez:lsed: 232 | - probably don't want to dump the whole buffer 233 | - maybe say '<>' in the repl 234 | ** TODO [#B] draw selection :jprez:lsed: 235 | ** TODO [#B] ^c should not break out of the application 236 | ** TODO [#B] cut, copy, paste :jkvm:edit: 237 | requires a selection 238 | ** TODO [#B] make worlds optional :jrepl: 239 | Some people will just prefer a regular repl. 240 | 241 | ** TODO [#B] speed up the escape code parsers (vputs, onkey) :jkvm: 242 | :PROPERTIES: 243 | :Effort: 2d 244 | :END: 245 | ** TODO [#B] extract UiComponent from UiApp (component=widget+container) (??) :jkvm: 246 | have a list of children and auto-provide the ability to draw all of them with extra code. 247 | (probably can factor this out of ui/app.ijs) 248 | ** TODO [#B] app: define applications' widget in a table with x,y,class,args 249 | ** TODO [#B] in the repl, if i print out a non-noun, syntax highlight it. 250 | ** TODO [#B] make =vputs_vid_= table-driven 251 | the current code is a horrifying recursive descent parser 252 | derive the state machine from a list of the escape code patterns 253 | ** TODO [#B] [5/12] have =vputs_vid_= recognize escape codes 254 | https://www2.ccs.neu.edu/research/gpc/VonaUtils/vona/terminal/vtansi.htm 255 | *** DONE home/goxy: ~CSI (row? ; col?)? H~ 256 | *** DONE erase screen: ~CSI 2J~ 257 | *** DONE clear to eol: ~CSI K~ 258 | *** DONE show cursor: ~CSI ?25 h~ 259 | *** DONE hide cursor: ~CSI ?25 l~ 260 | *** TODO vt code: cursor shift: ~CSI count? A|B|C|D~ # A=up B=dn C=rt D=lf 261 | *** TODO erase down: ~CSI J~ 262 | *** TODO bold: ( CSI 1m ? or is that just "bright"?) 263 | *** TODO italic? 264 | *** TODO enable line wrap: ~CSI 7h~ 265 | *** TODO disable line wrap: ~CSI 7l~ 266 | *** TODO query cursor position: ~CSI 6n~ (responds with =CSI ROW;COL R=) 267 | '0123456789' e.~ s=:'1234;1234234x42342' 268 | ** TODO [#B] add real =on_focus= handlers :jkvm:ui: 269 | (after i do real keyboard focus handling solution) 270 | ** TODO [#B] check for multiline input. (direct defs) 271 | - we already have =depth_world_= and we just need to check depth of last token 272 | - double check that parens cannot span lines inside direct definitions 273 | ** TODO [#B] visual indication that the file actually saved when you press ^s :jprez: 274 | status line widget? 275 | (right now we define a key and its macro logging character in one area, far away from the update method) 276 | ** TODO [#B] default tab-key handler (=kc_i=) should be to call 'next-widget' on main app :jkvm: 277 | - requires somehow having reference to the app in which we are running 278 | ** TODO [#B] =render_UiApp_= (and compound widgets) should clear R flag on each widget it renders :jkvm:ui: 279 | ** TODO [#B] look for =kc_spc= instead of =k_nul= :jkvm: 280 | - =k_nul= works, but it should probably be named =kc_spc= 281 | - (maybe this is vt100 thing?) actual ascii character is called ^@ 282 | - double check that control-space actually sends ascii 0. 283 | ** TODO [#C] remove =vtcolor_tok_= call from worlds :jprez:techdebt: 284 | - right now, =exec= calls =vtcolor= to color the history 285 | - maybe the repl widget itself should just know to color history lines 286 | - then we don't need vtcolor (which actually stores escape characters in the history) 287 | ** TODO [#C] on accept: remove consecutive duplicates from history 288 | - maybe: if B != as last line in history, add it to the history 289 | - maybe: if last two items in history are same, delete one 290 | 291 | ** TODO [#C] re-arrange mje.ijs so that open'' isn't in the middle of the file :jprez:techdebt: 292 | ** TODO [#C] fix j-kvm on osx 293 | :PROPERTIES: 294 | :Effort: 3d 295 | :END: 296 | ** TODO [#C] test that the macro actually produces the next line of code in the script. 297 | examples: manually edited macros might break. 298 | using "future" completion history is not allowed. 299 | ** TODO [#C] detect and "bake" usage of "future command line history" :jprez: 300 | this when you have a full future history from loading a presentation, 301 | and you use that history to complete a line in the past. 302 | This makes no sense from a narrative point of view. 303 | ** TODO [#C] show world for line, with content :jrepl:debug: 304 | on screen, show the world number as you move the outline cursor 305 | also have a display of the variables in scope that changes as you move the cursor 306 | ** TODO [#C] fix =loop_kvm= so left argument does not need to be in the z locale :jkvm: 307 | :PROPERTIES: 308 | :Effort: 3d 309 | :END: 310 | ** TODO [#C] decide whether curs 0 should be part of loop_kvm_, and if so, how to use cursors? 311 | maybe this is just a flag. 312 | ** TODO [#C] allow setting vim or emacs keys :jkvm:edit: 313 | ** TODO [#C] add word-wrap mode to list control :jkvm:list: 314 | - not just wrapping the characters, but breaking on spaces or hyphens or something 315 | - would need to track the height of each entry 316 | ** TODO [#C] file browser widget :widget: 317 | ** TODO [#C] "goal stack" widget in timeline :widget: 318 | is this even different from a list widget? color coding, maybe? 319 | the goal is different: it's to show the current state of the narrative. 320 | (it's an "on-screen", in-presentation widget that changes as you navigate through time) 321 | 322 | ** TODO [#C] demonstrate mouse events :jkvm: 323 | ** TODO [#C] elastic tabstops for editor 324 | https://nickgravgaard.com/elastic-tabstops/ 325 | ** TODO [#D] add ability to run arbitrary verbs on every frame 326 | - The idea here was to have a general-purpose task runner, that was not necessarily tied to a widget. 327 | - I don't remember why I wanted this, 328 | - But it's easy to simulate with an invisible widget. 329 | - Is there any need for more than this? 330 | 331 | ** TODO [#D] document and port cwio 332 | cwio = 'colorwrite' 333 | https://github.com/tangentstorm/xpl/blob/master/demo/cwio_eg.pas 334 | 335 | ** TODO [#D] git status widget :jkvm:files: 336 | (after we have a file browser) 337 | ** TODO [#D] paging in text editor 338 | - insert page 339 | - delete page 340 | - join pages 341 | 342 | ** TODO [#D] =render_UiListWidget_= could use some golfing 343 | 344 | 345 | * someday / maybe 346 | ** [#D] show (os) console in jqt 347 | : jshowconsole_j_ 1 NB. doesn't seem to work in jqt 348 | qt terminal doesn't support vt escape codes 349 | not sure i even care about this. 350 | (would have to be done in jqt front-end itself) 351 | (better idea would probably be make terminal emulator in jqt, or opengl, or SDL) 352 | 353 | ** [#D] integrate with JOD? 354 | * design work needed 355 | ** in =exec_world_=, decide what to do when an error happens and the debugger is on. :unclear: 356 | ** better idiom for expresssing 'method___self' (see =create= in repl.ijs) :unclear: 357 | maybe this ties in with the '::' concept for nested spaces 358 | (but: conames and names don't currently occupy the same namespace) 359 | maybe ::x is x in current namespace? 360 | 361 | 362 | 363 | * finished 364 | ** [3/3] app framework 365 | *** DONE render multiple widgets to buffer 366 | *** DONE emit only changed lines 367 | *** DONE handle unicode vid buffers properly 368 | ** [5/5] basic line editor 369 | *** DONE fix broken fwd/bwd commands 370 | *** DONE syntax highlighting in the editor (proof of concept) 371 | *** DONE fix bug: space key does not work 372 | *** DONE record keystrokes as macros 373 | *** DONE concatenate the inserted characters without redundant escapes 374 | *** DONE remove spurious color codes 375 | *** DONE move macros from token editor to plain editor 376 | *** DONE set aside "token editor" concept for now 377 | *** DONE restore syntax highlighting 378 | ** [4/4] macro timing 379 | *** DONE record and quantize keystroke timestamps 380 | *** DONE encode timing in the macros itself 381 | *** DONE [7/7] make macro animations asynchronous 382 | (get them out of the while loop) 383 | 384 | - [X] each widget needs an 'update' verb and an 'A' flag for whether it's active/animated. 385 | - [X] update app should call update on every active widget on each tick, *before* it re-renders. 386 | - [X] implement step ( just render @ update ) 387 | - [X] main loop should call app step instead of render. 388 | - [X] argument to step should be the time delta since last step (j-kvm.ijs) 389 | - [X] editor needs a flag/mode that indicates it's playing (maybe the A flag does this) 390 | - [X] editor's update method should play the next character in the macro if it's animating. 391 | 392 | *** DONE allow speed control per keystroke in the editor. 393 | initially got this for free because it pauses after each keystroke 394 | 395 | ** [4/4] screenplay editor ui 396 | *** DONE [3/3] implement a scrolling list widget 397 | **** DONE visible range 398 | **** DONE current highlight 399 | **** DONE scroll 400 | *** DONE show slides and steps in separate panes at bottom 401 | *** DONE roundtrip to/from org-mode 402 | *** DONE make kvm a library so syndir can import it 403 | ** [6/6] extract repl widget 404 | *** DONE add history widget to repl 405 | *** DONE implement solution for composite widgets 406 | *** DONE allow widgets to draw and blit themselves to current terminal 407 | *** DONE implement blit for vt 408 | *** DONE make repl a composite widget 409 | *** DONE draw history whether it's part of MJE or not 410 | ** previously 411 | *** DONE finish parser for xterm color codes (vputs) 412 | *** DONE widgetize repl history 413 | *** DONE evaluate and show output 414 | *** DONE implement ^K -> clear to end of line (d$ in vim?) 415 | *** DONE don't hardcode the script path 416 | *** DONE 'pre-render' the repl interactions for all slides 417 | - history can just be the list of lines on the screen 418 | - at each step, store which one is the bottom-most on screen. 419 | - then to render, take a window of lines the same size as the terminal 420 | - for each input there should/could also be an animation of how we arrived at it 421 | *** DONE implement 'worlds' so I can track the state of the system at each point 422 | *** DONE pre-determine the height of the repl window (=H_REPL=) for the presentation. 423 | *** DONE use an in-world variable to track the state of the editor 424 | *** DONE parse repl inputs from the org file 425 | - lines starting with ': . ' are editor animations (macros) 426 | - lines starting with ':' are repl input 427 | - If a editor animaiton precedes the repl input, it should be treated as a derivation of the input, and an alarm should be triggered if it doesn't actually produce the expected input. 428 | (this might happen if the editor macro modifies previous inputs and the input history changes due to modifying the narrative) 429 | *** DONE handle local definitions 430 | I see three alternatives: 431 | - [X] rewrite the code before it is evaluated (replace =. with =:) 432 | - execute the code in a separate j process 433 | - execute the code as part of an immex expression 434 | *** DONE execute every line starting with ':' (but not ': .') on load 435 | *** DONE execute each line using the world concept 436 | *** DONE append output to the echo history 437 | *** DONE track the repl history length at each step (before and after) 438 | *** DONE when navigating to a step, render the repl in its 'before' state 439 | *** DONE map each step in the slide to a world 440 | *** DONE handle box-drawing characters 441 | *** DONE rewrite special names 442 | 443 | ** ep-10: repl recorder 444 | *** DONE insert new commands into screenplay 445 | *** DONE insert keylog macro into screenplay 446 | *** DONE clear macro after each input 447 | ** ep-11: macro playback in repl 448 | *** bugs 449 | **** DONE fix ctrl-o so it re-opens the file 450 | **** DONE bug: history is messed up when you press ctrl-o 451 | (needed to fix =init_world_=) 452 | **** DONE bug: text added from repl gets discarded 453 | (fix was to use =insline= instead of =ins__cmd=) 454 | **** DONE do not show macros in the repl 455 | (fix was change to =new_repl_line=) 456 | **** DONE fix the right side of outline so that it scrolls 457 | height (H) was just set wrong 458 | **** DONE =goz_UiList_= does not scroll correctly (cursor hidden when entering from bottom) 459 | fixed by adding bounds checking to =goz= 460 | 461 | *** features 462 | **** DONE get simple macro playback working (using empty start state for now) 463 | - Play macro when cmd cursor is on macro and you press 'N'. 464 | **** DONE track the mark/selection on each line as we load (part of repl state) 465 | - maybe answer here is to have UiEditWidget produce and consume a state memo 466 | 467 | **** DONE play macros (without pauses) when loading and keep start states for each line 468 | - =tmp= is temporary editor object (no need to render) 469 | - set =KPS__tmp= to _ for infinite speed 470 | - set =TSV__tmp= to 0 to turn off random variation 471 | - call =do__tmp= with the macro 472 | - just call =update_tmp 1= until =A__tmp= is 0 473 | - state for next iteration is =B__tmp= 474 | - save start states in =olr= 475 | 476 | **** DONE play macros from the line's starting state when 'N' is pressed 477 | ** rejms-14 478 | *** DONE "focus color" for cursors in list, edit, repl :feature:jprez:jkvm: 479 | ** rejms-15 : 5 easy changes 480 | *** DONE make sure R=:0 in =render__repl= (in update, copy R from ed) 481 | *** DONE I set TSV=0 in macro player. it should be 1. :jprez: 482 | actually i don't use ?TSV, i use TSV*?0 so it's in seconds. 483 | *** DONE [#A] make A=:1 the default for widgets :jkvm: 484 | *** DONE [#A] backspace key in editor :jprez: 485 | needed to manually copy all keyboard handlers. 486 | *** DONE don't break on =kc_m= :jkvm:edit: 487 | *** DONE reorganize mje so that all keybindings map to named verb 488 | (instead of verb definition) 489 | ** rejms-16 : repl enhancements 490 | *** DONE [#A] colorize input history for standalone repl :jrepl: 491 | *** DONE start repl cursor at top of the screen :jprez: 492 | ** rejms-17 : working repl in jprez 493 | *** DONE inserting repl->script mis-manages start state :bug:jprez: 494 | part of this was clearing the worlds, and part was =setval__tmp''= 495 | *** DONE [#C] clear "future" worlds on input :jprez: 496 | *** DONE [#C] rebuild worlds on delete :jprez: 497 | *** DONE run the command and show the output in the repl :jprez: 498 | *** DONE tab to switch between outline and repl 499 | ** rejms-18 : repl command history 500 | *** DONE [4/4] [#A] tie repl to the command history :jrepl: 501 | Pressing up or down should let you navigate the input history. 502 | This history is provided by worlds.ijs. 503 | 504 | - [X] maintain an internal (invisible) list widget =hist= with input history 505 | - [X] last item in =hist= is the current edit buffer (set by up-arrow) 506 | - [X] on up arrow: 507 | - [X] if i'm at the end of list, then update the list else ok 508 | - [X] call =bak__hist= to move the history cursor 509 | - [X] set B to =val__hist= 510 | - [X] on down arrow: 511 | - call =fwd__hist= 512 | - set B to =val__hist= 513 | ** rejms-19 : history fixes 514 | *** DONE fix length error: =getlog__ed= when the log is empty. :ui:edit: 515 | 516 | *** DONE fix length error in exec when input is empty. :worlds: 517 | *** DONE pressing up, and then down should restore the line :jrepl: 518 | *** DONE create pluggable =on_arup= and =on_ardn= event handlers :ui:edit: 519 | *** DONE record the history navigation in the macro :jprez: 520 | *** DONE command history for standalone repl (keybinding issue) :jrepl: 521 | *** DONE playback of j/k keys in macro player (up/down arrows) :ui:edit: 522 | *** DONE stopgap method to get input history into repl widget 523 | right now it just sets the history when the focus changes, 524 | and it includes the entire input history. 525 | 526 | *** DONE properly handle history in playback :jprez: 527 | this is handled by =reset_rhist= when focus changes or you play macro. 528 | this way we get the exact history at that point in time. 529 | ** rejms-20 : backlog grooming 2 530 | *** DONE rename =mje.ijs= to =jprez.ijs= (or at least =main.ijs=) 531 | 532 | *** DONE ^c / in repl/line-editor should not break whole application 533 | one exit key is enough. 534 | (fix was to remove the 3 in j-kvm.ijs) 535 | ** rejms-21 : start on table-driven keybindings (tag only in j-kvm) 536 | ** rejms-22 : table-driven keybindings part 2* 537 | *** DONE [#A] make keybindings table-driven (so people can choose which keys they use for input) :jkvm: 538 | - edit: refactor so that keys, macro recording, and verb to execute (for live binding or macro playback) are all defined in one table 539 | - maybe the quick answer for keyboard layouts is to just put that in a separate file? 540 | - or have two tables: one mapping macro code <-> function name, and another for key <-> macro or key <-> function 541 | ** rejms-23 : start multi-line editor 542 | *** DONE [#A] Define Text Editor Component :widget: 543 | (right now, the jprez editor is just a plain UiWidget) 544 | *** DONE code editor should extend the UiList 545 | we can start with the idea that the editor is just a list of lines 546 | therefore, extend the UiListWidget 547 | *** DONE create =of_self= adverb 548 | *** DONE [#B] draw the editor cursor(s) :jprez:lsed: 549 | render the tokenized LIST with an extra cursor drawn on top. 550 | --------------------------------------------------------------------------------