···4242use tic80_rust::audio::capture as audio_cap;
4343use tic80_rust::audio::fft::{set_global_fft, FFTState};
4444use tic80_rust::core::memory::Memory;
4545+use tic80_rust::editor::code::{Area as CodeArea, CodeBuffer};
4546use tic80_rust::editor::ui::EditorUi;
4647use tic80_rust::gfx::framebuffer::{dimensions, Framebuffer};
4748use tic80_rust::script::lua_runner::LuaRunner;
···299300 return Ok(());
300301 }
301302 tic80_rust::script::lua_runner::set_quiet(args.quiet);
302302- let (lua_runner, mut editor_ui) = if args.editor {
303303- (None, Some(EditorUi::new(window_scale)))
303303+ let (lua_runner, mut editor_ui, mut code_buf) = if args.editor {
304304+ let initial = load_script(args.script_path.as_ref());
305305+ (
306306+ None,
307307+ Some(EditorUi::new(window_scale)),
308308+ Some(CodeBuffer::from_text(&initial)),
309309+ )
304310 } else {
305311 let script = load_script(args.script_path.as_ref());
306312 let lr = match LuaRunner::new(fb.clone(), mem.clone(), &script) {
···310316 None
311317 }
312318 };
313313- (lr, None)
319319+ (lr, None, None)
314320 };
315321 let mut audio_state = init_audio(&args);
316322 let mut warned_no_tic = false;
317323 let mut last_cursor_fb: Option<(i32, i32)> = None;
318324325325+ #[allow(clippy::cognitive_complexity)]
319326 event_loop.run(move |event, _, control_flow| {
320327 *control_flow = ControlFlow::Poll;
321328 match event {
···341348 },
342349 ..
343350 } => *control_flow = ControlFlow::Exit,
351351+ WindowEvent::KeyboardInput { input, .. } => {
352352+ if let (Some(ui), Some(cb)) = (editor_ui.as_ref(), code_buf.as_mut()) {
353353+ if ui.active == tic80_rust::editor::ui::Tab::Code
354354+ && input.state == ElementState::Pressed
355355+ {
356356+ if let Some(key) = input.virtual_keycode {
357357+ match key {
358358+ VirtualKeyCode::Left => cb.move_left(),
359359+ VirtualKeyCode::Right => cb.move_right(),
360360+ VirtualKeyCode::Up => cb.move_up(),
361361+ VirtualKeyCode::Down => cb.move_down(),
362362+ _ => {}
363363+ }
364364+ }
365365+ }
366366+ }
367367+ }
344368 WindowEvent::Resized(size) => {
345369 let _ = pixels.resize_surface(size.width, size.height);
346370 }
···468492 if let Some(ui) = editor_ui.as_ref() {
469493 let mut fbb = fb.borrow_mut();
470494 ui.draw(&mut fbb);
495495+ if ui.active == tic80_rust::editor::ui::Tab::Code {
496496+ if let Some(cb) = code_buf.as_mut() {
497497+ let area = CodeArea { x: 0, y: 12, w: 240, h: 124 };
498498+ cb.draw(&mut fbb, area);
499499+ }
500500+ }
471501 }
472502 fb.borrow().blit_to_rgba(frame);
473503 if let Err(err) = pixels.render() {
+38
tic80_rust/tests/editor_code_view_tests.rs
···11+use std::cell::RefCell;
22+use std::rc::Rc;
33+44+use tic80_rust::editor::code::{Area, CodeBuffer};
55+use tic80_rust::gfx::framebuffer::Framebuffer;
66+77+#[test]
88+fn code_view_renders_lines_and_gutter() {
99+ let text = "line1\nline2\nline3\n";
1010+ let mut cb = CodeBuffer::from_text(text);
1111+ let fb = Rc::new(RefCell::new(Framebuffer::new()));
1212+ let mut fbb = fb.borrow_mut();
1313+ let area = Area {
1414+ x: 0,
1515+ y: 12,
1616+ w: 240,
1717+ h: 40,
1818+ };
1919+ cb.draw(&mut fbb, area);
2020+ // Expect some non-zero pixels in gutter (left side)
2121+ let mut gutter_ink = 0;
2222+ for y in 12..20 {
2323+ for x in 2..22 {
2424+ if fbb.pix(x, y, None).unwrap_or(0) != 0 {
2525+ gutter_ink += 1;
2626+ }
2727+ }
2828+ }
2929+ assert!(gutter_ink > 0);
3030+ // Expect some text pixels in the first line area after gutter (x >= 24)
3131+ let mut line_ink = 0;
3232+ for x in 24..60 {
3333+ if fbb.pix(x, 12, None).unwrap_or(0) != 0 {
3434+ line_ink += 1;
3535+ }
3636+ }
3737+ assert!(line_ink > 0);
3838+}