package tiny_libs

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file Text.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
(* 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 Text.mli *)

(*****************************************************************************)
(* Characters, not bytes *)
(*****************************************************************************)

(* a byte whose top bits are 10 continues the character before it *)
let continues s i = Char.code s.[i] land 0xC0 = 0x80

let rec start_of_char s i =
  if i <= 0 then 0 else if continues s i then start_of_char s (i - 1) else i

let prev_char s i = if i <= 0 then 0 else start_of_char s (i - 1)

let next_char s i =
  let n = String.length s in
  let rec go i = if i >= n || not (continues s i) then i else go (i + 1) in
  if i >= n then n else go (i + 1)

(*****************************************************************************)
(* Cells *)
(*****************************************************************************)

let chars s =
  let rec go i acc =
    if i >= String.length s then List.rev acc
    else
      let j = next_char s i in
      go j (String.sub s i (j - i) :: acc)
  in
  go 0 []

let column s i = List.length (chars (String.sub s 0 (min i (String.length s))))

let byte_of_column s col =
  let rec go i col = if col <= 0 || i >= String.length s then i else go (next_char s i) (col - 1) in
  go 0 col

(*****************************************************************************)
(* One frame of typing *)
(*****************************************************************************)

let edit ~typed ~pressed text caret =
  let caret = max 0 (min (String.length text) caret) in
  let text, caret =
    if typed = "" then (text, caret)
    else
      ( String.sub text 0 caret ^ typed ^ String.sub text caret (String.length text - caret),
        caret + String.length typed )
  in
  let text, caret =
    if pressed "Backspace" && caret > 0 then
      let from = prev_char text caret in
      (String.sub text 0 from ^ String.sub text caret (String.length text - caret), from)
    else (text, caret)
  in
  let text, caret =
    if pressed "Delete" && caret < String.length text then
      let upto = next_char text caret in
      (String.sub text 0 caret ^ String.sub text upto (String.length text - upto), caret)
    else (text, caret)
  in
  let caret = if pressed "ArrowLeft" then prev_char text caret else caret in
  let caret = if pressed "ArrowRight" then next_char text caret else caret in
  let caret = if pressed "Home" then 0 else caret in
  let caret = if pressed "End" then String.length text else caret in
  (text, caret)