package tiny_appkits

  1. Overview
  2. Docs
Application engines from scratch: a spreadsheet, rich text, paint, draw, CAD, editors and more

Install

dune-project
 Dependency

Authors

Maintainers

Sources

0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0

doc/src/tiny_appkits.appkit_editor/Turbo_update.ml.html

Source file Turbo_update.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
(* Claude Code
 *
 * Copyright (C) 2026 Yoann Padioleau
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * (LGPL) as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *)

(* See Turbo_update.mli *)

open Turbo_model

(*****************************************************************************)
(* Update *)
(*****************************************************************************)

(* claude: Esc then a digit, F1 to F10 (0 for F10): the convention of
 * Midnight Commander and the terminals without function keys, for the
 * keyboards whose top row is the volume's and the screen's, the
 * desktop's or the browser's. Two keys typed one after the other, or
 * Alt and the digit at once (Escape and the digit, the same bytes) --
 * though not on a Mac, where Option and a digit types another
 * character (Option-9, "ª"): there, Esc and then the digit *)
let digit (k : string) : bool = String.length k = 1 && k.[0] >= '0' && k.[0] <= '9'

let f_of_digit (d : char) : string = if d = '0' then "F10" else "F" ^ String.make 1 d

(* and Control and a digit (Vt.key's ESC [ 27 ; 5 ; <code> ~), Control
 * and that F key: Ctrl-9, Ctrl-F9, Run *)
let function_key (k : string) : string =
  let n = String.length k in
  if n = 2 && k.[0] = '\x1b' && digit (String.sub k 1 1) then Option.value ~default:k (Vt.key ~ctrl:false (f_of_digit k.[1]))
  else if n = 10 && String.sub k 0 7 = "\x1b[27;5;" && k.[9] = '~' then
    match int_of_string_opt (String.sub k 7 2) with
    | Some c when c >= 48 && c <= 57 -> Option.value ~default:k (Vt.key ~ctrl:true (f_of_digit (Char.chr c)))
    | _ -> k
  else k

let rec key (m : model) (k : string) : model =
  match (m.mode, k) with
  (* in the editor, a lone Esc waits for its digit (else it does
   * nothing, as before) *)
  | Editing, "\x1b" -> { m with escaped = true }
  | Editing, _ when m.escaped && digit k -> key { m with escaped = false } ("\x1b" ^ k)
  | _ -> key_now { m with escaped = false } k

and key_now (m : model) (k : string) : model =
  (* the running program's keys are its own *)
  let k = match m.mode with Executing -> k | _ -> function_key k in
  match m.mode with
  | Executing -> ( match m.session with Some s -> Turbo_debug.executing_key m s k | None -> { m with mode = Editing })
  | Finished (_, err) -> (
      match err with
      | Some (l, msg) -> { m with mode = Editing; error = Some msg; row = l - 1; col = 0 }
      | None -> { m with mode = Editing })
  | Showing _ | Info _ | Stack -> { m with mode = Editing }
  | Listing first -> (
      match k with
      | "\x1b[A" -> { m with mode = Listing (max 0 (first - 1)) }
      | "\x1b[B" -> { m with mode = Listing (first + 1) }
      | "\x1b[5~" -> { m with mode = Listing (max 0 (first - 15)) }
      | "\x1b[6~" -> { m with mode = Listing (first + 15) }
      | _ -> { m with mode = Editing })
  | Menu (bar, sel) -> Turbo_menus.menu_key m bar sel k
  | Open_dialog sel -> (
      let fs = Turbo_menus.files m in
      match k with
      | "\x1b[A" -> { m with mode = Open_dialog (max 0 (sel - 1)) }
      | "\x1b[B" -> { m with mode = Open_dialog (min (List.length fs - 1) (sel + 1)) }
      | "\r" -> Turbo_edit.load { m with mode = Editing } (List.nth fs sel)
      | _ -> { m with mode = Editing })
  | Input { title; label; text; purpose } -> Turbo_menus.input_key m (title, label, text, purpose) k
  | Editing -> (
      (* a key clears the red bar, as in Turbo Pascal *)
      let m = { m with error = None } in
      match k with
      | "\x1bOP" -> Turbo_menus.act m Keys
      | "\x1bOQ" -> Turbo_menus.act m Save
      | "\x1bOR" -> Turbo_menus.act m Open
      | "\x1b[20~" | "\x1b[20;3~" -> Turbo_menus.act m Compile
      | "\x1b[20;5~" -> Turbo_menus.act m Run
      | "\x1b[15;3~" -> Turbo_menus.act m User_screen
      | "\x1b[21~" -> { m with mode = Menu (0, 0) }
      (* the debugger's: F7 F8 F4, Ctrl-F2 Ctrl-F3 Ctrl-F7 Ctrl-F8 *)
      | "\x1b[18~" -> Turbo_menus.act m Trace_into
      | "\x1b[19~" -> Turbo_menus.act m Step_over
      | "\x1bOS" -> Turbo_menus.act m Go_to_cursor
      | "\x1b[1;5Q" -> Turbo_menus.act m Reset
      | "\x1b[1;5R" -> Turbo_menus.act m Call_stack
      | "\x1b[18;5~" -> Turbo_menus.act m Add_watch
      | "\x1b[19;5~" -> Turbo_menus.act m Toggle_breakpoint
      | _ -> (
          match Turbo_menus.alt_letter k with
          | Some 'x' -> Turbo_menus.act m Exit
          | Some c -> ( match Turbo_menus.menu_of_letter c with Some b -> { m with mode = Menu (b, 0) } | None -> m)
          | None ->
              (* the text changed: the program running is another's,
                 reset (Turbo Pascal asked first) *)
              let m' = Turbo_edit.edit_key m k in
              if m'.lines != m.lines then { m' with session = None } else m'))

let update (ev : Tui.event) (m : model) : model =
  match (ev, m.mode) with
  | Tick _, Executing -> ( match m.session with Some s -> Turbo_debug.advance m s | None -> { m with mode = Editing })
  | Tick _, _ -> m
  | Key k, _ -> Turbo_edit.follow (key m k)