summaryrefslogtreecommitdiff
path: root/lib/command.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command.ml')
-rw-r--r--lib/command.ml115
1 files changed, 115 insertions, 0 deletions
diff --git a/lib/command.ml b/lib/command.ml
new file mode 100644
index 0000000..7dedc6c
--- /dev/null
+++ b/lib/command.ml
@@ -0,0 +1,115 @@
1open Base
2open Key
3
4type register = char option
5type count = int option
6type operation = Noop | Yank | Paste | Delete | Change
7type scope = Line | To_bol | To_eol | Down | Left | Right | Up
8
9type command =
10 | Type of char
11 | Simple of Key.t
12 | Partial of Key.t
13 | Shortcut of register * count * operation * scope
14 | Chord of register * count * operation * count * scope
15
16type t = command
17
18let shortcut ?r ?n c s = Shortcut (r, n, c, s)
19let chord ?r ?n1 c ?n2 m = Chord (r, n1, c, n2, m)
20
21let i_stream =
22 let step s k =
23 let open Sequence.Step in
24 match (s, k) with
25 | `start, Key c -> Yield { value = Type c; state = `start }
26 | `start, _ -> Yield { value = Simple k; state = `start }
27 | _, _ -> Skip { state = `start }
28 in
29 Sequence.unfold_with ~init:`start ~f:step Key.stream
30
31let simple_movements =
32 [
33 Key 'h';
34 Key 'j';
35 Key 'k';
36 Key 'l';
37 Key ' ';
38 Arrow_up;
39 Arrow_down;
40 Arrow_left;
41 Arrow_right;
42 Backspace;
43 ]
44
45let to_scope = function
46 | Key 'j' | Arrow_down -> Down
47 | Key 'h' | Arrow_left | Backspace -> Left
48 | Key 'l' | Key ' ' | Arrow_right -> Right
49 | Key 'k' | Arrow_up -> Up
50 | _ -> failwith "Invalid motion."
51
52let n_stream =
53 let step s k =
54 let open Sequence.Step in
55 let is_chord_op c = String.contains "ydc" (Char.lowercase c) in
56 let is_simple_movement k = List.mem ~equal:Poly.equal simple_movements k in
57 let to_op c =
58 match Char.lowercase c with
59 | 'y' -> Yank
60 | 'p' -> Paste
61 | 'd' -> Delete
62 | 'c' -> Change
63 | _ -> failwith "Invalid operation in chord."
64 in
65 match (s, k) with
66 | `start, Key '"' -> Yield { value = Partial k; state = `chord_reg_pre }
67 | `chord_reg_pre, Key c -> Yield { value = Partial k; state = `chord_reg c }
68 | `chord_reg r, Key n when Char.('1' <= n && n <= '9') ->
69 let n = Char.to_int n - 48 in
70 Yield { value = Partial k; state = `chord_n (Some r, n) }
71 | `start, Key n when Char.('1' <= n && n <= '9') ->
72 let n = Char.to_int n - 48 in
73 Yield { value = Partial k; state = `chord_n (None, n) }
74 | `chord_n (r, m), Key n when Char.('0' <= n && n <= '9') ->
75 let n = (10 * m) + Char.to_int n - 48 in
76 Yield { value = Partial k; state = `chord_n (r, n) }
77 | `start, Key c when is_chord_op c ->
78 if Char.is_uppercase c then
79 Yield { value = shortcut (to_op c) To_eol; state = `start }
80 else
81 Yield { value = Partial k; state = `chord_cmd (None, None, to_op c) }
82 | `chord_reg r, Key c when is_chord_op c ->
83 if Char.is_uppercase c then
84 Yield { value = shortcut ~r (to_op c) To_eol; state = `start }
85 else
86 Yield
87 { value = Partial k; state = `chord_cmd (Some r, None, to_op c) }
88 | `chord_n (r, n), Key c when is_chord_op c ->
89 if Char.is_uppercase c then
90 Yield { value = shortcut ?r ~n (to_op c) To_eol; state = `start }
91 else
92 Yield { value = Partial k; state = `chord_cmd (r, Some n, to_op c) }
93 | `chord_cmd (r, n, c), Key ch when is_chord_op ch && Poly.(c = to_op ch) ->
94 if Char.is_uppercase ch then
95 Yield { value = shortcut ?r ?n c To_bol; state = `start }
96 else Yield { value = shortcut ?r ?n c Line; state = `start }
97 | (`start | `chord_reg _), k when is_simple_movement k ->
98 Yield { value = chord Noop (to_scope k); state = `start }
99 | `chord_n (_, n), k when is_simple_movement k ->
100 Yield { value = chord ~n1:n Noop (to_scope k); state = `start }
101 | `chord_cmd (r, n, c), k when is_simple_movement k ->
102 Yield { value = chord ?r ?n1:n c (to_scope k); state = `start }
103 | `chord_cmd (r, n1, c), Key n when Char.('1' <= n && n <= '9') ->
104 let n = Char.to_int n - 48 in
105 Yield { value = Partial k; state = `chord_mv_n (r, n1, c, n) }
106 | `chord_mv_n (r, n1, c, n2), Key n when Char.('0' <= n && n <= '9') ->
107 let n2 = (10 * n2) + Char.to_int n - 48 in
108 Yield { value = Partial k; state = `chord_mv_n (r, n1, c, n2) }
109 | `chord_mv_n (r, n1, c, n2), k when is_simple_movement k ->
110 Yield { value = chord ?r ?n1 c ~n2 (to_scope k); state = `start }
111 (* Catch-all rules *)
112 | `start, _ -> Yield { value = Simple k; state = `start }
113 | _, _ -> Skip { state = `start }
114 in
115 Sequence.unfold_with ~init:`start ~f:step Key.stream