aboutsummaryrefslogtreecommitdiff
path: root/slof/fs/accept.fs
blob: 22bfb1b81897d1ae18409568bb750557ca6e41c1 (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
\ *****************************************************************************
\ * Copyright (c) 2004, 2008 IBM Corporation
\ * All rights reserved.
\ * This program and the accompanying materials
\ * are made available under the terms of the BSD License
\ * which accompanies this distribution, and is available at
\ * http://www.opensource.org/licenses/bsd-license.php
\ *
\ * Contributors:
\ *     IBM Corporation - initial implementation
\ ****************************************************************************/


\ Implementation of ACCEPT.  Using ECMA-48 for terminal control.

: beep  bell emit ;

: TABLE-EXECUTE
  CREATE DOES> swap cells+ @ ?dup IF execute ELSE beep THEN ;

0 VALUE accept-adr
0 VALUE accept-max
0 VALUE accept-len
0 VALUE accept-cur

: esc  1b emit ;
: csi  esc 5b emit ;

: move-cursor ( -- )
   esc ." 8" accept-cur IF
      csi base @ decimal accept-cur 0 .r base ! ." C"
   THEN
;

: redraw-line ( -- )
   accept-cur accept-len = IF EXIT THEN
   move-cursor
   accept-adr accept-len accept-cur /string type
   csi ." K" move-cursor
;

: full-redraw-line ( -- )
   accept-cur 0 to accept-cur move-cursor
   accept-adr accept-len type
   csi ." K" to accept-cur move-cursor
;

: redraw-prompt ( -- )
   cr depth . [char] > emit
;

: insert-char ( char -- )
   accept-len accept-max = IF drop beep EXIT THEN
   accept-cur accept-len <> IF csi ." @" dup emit
   accept-adr accept-cur + dup 1+ accept-len accept-cur - move
   ELSE dup emit THEN
   accept-adr accept-cur + c!
   accept-cur 1+ to accept-cur
   accept-len 1+ to accept-len redraw-line
;

: delete-char ( -- )
   accept-cur accept-len = IF beep EXIT THEN
   accept-len 1- to accept-len
   accept-adr accept-cur + dup 1+ swap accept-len accept-cur - move
   csi ." P" redraw-line
;

\ *
\ * History handling
\ *

STRUCT
cell FIELD his>next
cell FIELD his>prev
cell FIELD his>len
   0 FIELD his>buf
CONSTANT /his
0 VALUE his-head
0 VALUE his-tail
0 VALUE his-cur

: add-history ( -- )
   accept-len 0= IF EXIT THEN
   /his accept-len + alloc-mem
   his-tail IF dup his-tail his>next ! ELSE dup to his-head THEN
   his-tail over his>prev !  0 over his>next !  dup to his-tail
   accept-len over his>len !  accept-adr swap his>buf accept-len move
;

: history  ( -- )
   his-head BEGIN dup WHILE
   cr dup his>buf over his>len @ type
   his>next @ REPEAT drop
;

: select-history ( his -- )
   dup to his-cur dup IF
   dup his>len @ accept-max min dup to accept-len to accept-cur
   his>buf accept-adr accept-len move ELSE
   drop 0 to accept-len 0 to accept-cur THEN
   full-redraw-line
;


\
\ tab completion
\

\ tab completion state variables
0 value ?tab-pressed
0 value tab-last-adr
0 value tab-last-len

\ compares two strings and returns the longest equal substring.
: $same-string ( addr-1 len-1 addr-2 len-2 -- addr-1 len-1' )
   dup 0= IF    \ The second parameter is not a string.
      2drop EXIT \ bail out
   THEN
   rot min 0 0 -rot ( addr1 addr2 0 len' 0 )
   DO ( addr1 addr2 len-1' )
      2 pick i + c@ lcc
      2 pick i + c@ lcc
      = IF 1 + ELSE leave THEN
   LOOP
   nip
;

: $tab-sift-words    ( text-addr text-len -- sift-count )
   sift-compl-only >r true to sift-compl-only \ save sifting mode

   last BEGIN @ ?dup WHILE \ loop over all words
      $inner-sift IF \ any completions possible?
         \ convert to lower case for user interface sanity
         2dup bounds DO I c@ lcc I c! LOOP
         ?tab-pressed IF 2dup type space THEN  \ <tab><tab> prints possibilities
         tab-last-adr tab-last-len $same-string \ find matching substring ...
         to tab-last-len to tab-last-adr       \ ... and save it
      THEN
   repeat
   2drop

   #sift-count 0 to #sift-count         \ how many words were found?
   r> to sift-compl-only                \ restore sifting completion mode
;

\ 8< node sifting for tab completion on device tree nodes below this line 8<

#include <stack.fs>

10 new-stack device-stack

: (next-dev) ( node -- node' addr len )
   device-stack
   dup (node>path) rot
   dup child IF dup push child -rot EXIT THEN
   dup peer IF peer -rot EXIT THEN
   drop
   BEGIN
      stack-depth
   WHILE
      pop peer ?dup IF -rot EXIT THEN
   REPEAT
   0 -rot
;

: $inner-sift-nodes ( text-addr text-len node -- ... path-addr path-len true | false )
   (next-dev) ( text-addr text-len node' path-addr path-len )
   dup 0= IF drop false EXIT THEN
   2dup 6 pick 6 pick find-isubstr ( text-addr text-len node' path-addr path-len pos )
   0= IF
      #sift-count 1+ to #sift-count \ count completions
      true
   ELSE
      2drop false
   THEN
;

\
\ test function for (next-dev)
: .nodes ( -- )
   s" /" find-node BEGIN dup WHILE
      (next-dev)
      type cr
   REPEAT
   drop
   reset-stack
;

\ node sifting wants its own pockets
create sift-node-buffer 1000 allot
0 value sift-node-num
: sift-node-buffer
   sift-node-buffer sift-node-num 100 * +
   sift-node-num 1+ dup 10 = IF drop 0 THEN
   to sift-node-num
;

: $tab-sift-nodes    ( text-addr text-len -- sift-count )
   s" /" find-node BEGIN dup WHILE
      $inner-sift-nodes IF \ any completions possible?
         sift-node-buffer swap 2>r 2r@ move 2r> \ make an almost permanent copy without strdup
         ?tab-pressed IF 2dup type space THEN  \ <tab><tab> prints possibilities
         tab-last-adr tab-last-len $same-string \ find matching substring ...
         to tab-last-len to tab-last-adr       \ ... and save it
      THEN
   REPEAT
   2drop drop
   #sift-count 0 to #sift-count         \ how many words were found?
   reset-stack
;

: $tab-sift    ( text-addr text-len -- sift-count )
   ?tab-pressed IF beep space THEN \ cosmetical fix for <tab><tab>

   dup IF bl rsplit dup IF 2swap THEN ELSE 0 0 THEN >r >r

   0 dup to tab-last-len to tab-last-adr  \ reset last possible match
   current-node @ IF                      \ if we are in a node?
      2dup 2>r                            \ save text
      $tab-sift-words to #sift-count      \ search in current node first
      2r>                                 \ fetch text to complete, again
   THEN
   2dup 2>r
   current-node @ >r 0 set-node           \ now search in global words
   $tab-sift-words to #sift-count
   r> set-node
   2r> $tab-sift-nodes
   \ concatenate previous commands
   r> r> dup IF s"  " $cat THEN tab-last-adr tab-last-len $cat
   to tab-last-len to tab-last-adr  \ ... and save the whole string
;

\ 8< node sifting for tab completion on device tree nodes above this line 8<

: handle-^A
   0 to accept-cur move-cursor ;
: handle-^B
   accept-cur ?dup IF 1- to accept-cur ( csi ." D" ) move-cursor THEN ;
: handle-^D
   delete-char ( redraw-line ) ;
: handle-^E
   accept-len to accept-cur move-cursor ;
: handle-^F
   accept-cur accept-len <> IF accept-cur 1+ to accept-cur csi ." C" THEN ;
: handle-^H
   accept-cur 0= IF beep EXIT THEN
   handle-^B delete-char
;
: handle-^I
   accept-adr accept-len
   $tab-sift 0 > IF
      ?tab-pressed IF
         redraw-prompt full-redraw-line
         false to ?tab-pressed
      ELSE
         tab-last-adr accept-adr tab-last-len move    \ copy matching substring
         tab-last-len dup to accept-len to accept-cur \ len and cursor position
         full-redraw-line       \ redraw new string
         true to ?tab-pressed   \ second tab will print possible matches
      THEN
   THEN
;

: handle-^K
   BEGIN accept-cur accept-len <> WHILE delete-char REPEAT ;
: handle-^L
   history redraw-prompt full-redraw-line ;
: handle-^N
   his-cur IF his-cur his>next @ ELSE his-head THEN
   dup to his-cur select-history
;
: handle-^P
   his-cur IF his-cur his>prev @ ELSE his-tail THEN
   dup to his-cur select-history
;
: handle-^Q  \ Does not handle terminal formatting yet.
   key insert-char ;
: handle-^R
   full-redraw-line ;
: handle-^U
   0 to accept-len 0 to accept-cur full-redraw-line ;

: handle-fn
   key drop beep
;

TABLE-EXECUTE handle-CSI
0 , ' handle-^P , ' handle-^N , ' handle-^F ,
' handle-^B , 0 , 0 , 0 ,
' handle-^A , 0 , 0 , ' handle-^E ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,

: handle-CSI-key
    key 1f and handle-CSI
;

TABLE-EXECUTE handle-meta
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , ' handle-fn ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
0 , 0 , 0 , ' handle-CSI-key ,
0 , 0 , 0 , 0 ,

: handle-ESC-O
   key
   dup 48 = IF
      handle-^A
   ELSE
      dup 46 = IF
         handle-^E
      THEN
   THEN drop
;

: handle-ESC-5b
   key
   dup 31 = IF \ HOME
      key drop ( drops closing 7e ) handle-^A
   ELSE
      dup 33 = IF \ DEL
         key drop handle-^D
      ELSE
         dup 34 = IF \ END
            key drop handle-^E
         ELSE
            dup 1f and handle-CSI
         THEN
      THEN
   THEN drop
;

: handle-ESC
   key
   dup 5b = IF
      handle-ESC-5b
   ELSE
      dup 4f = IF
         handle-ESC-O
      ELSE
         dup 1f and handle-meta
      THEN
   THEN drop
;

TABLE-EXECUTE handle-control
0 , \ ^@:
' handle-^A ,
' handle-^B ,
0 , \ ^C:
' handle-^D ,
' handle-^E ,
' handle-^F ,
0 , \ ^G:
' handle-^H ,
' handle-^I , \ tab
0 , \ ^J:
' handle-^K ,
' handle-^L ,
0 , \ ^M: enter: handled in main loop
' handle-^N ,
0 , \ ^O:
' handle-^P ,
' handle-^Q ,
' handle-^R ,
0 , \ ^S:
0 , \ ^T:
' handle-^U ,
0 , \ ^V:
0 , \ ^W:
0 , \ ^X:
0 , \ ^Y: insert save buffer
0 , \ ^Z:
' handle-ESC ,
0 , \ ^\:
0 , \ ^]:
0 , \ ^^:
0 , \ ^_:

: (accept) ( adr len -- len' )
   cursor-on
   to accept-max to accept-adr
   0 to accept-len 0 to accept-cur
   0 to his-cur
   1b emit 37 emit
   BEGIN
      key dup 0d <>
   WHILE
      dup 9 <> IF 0 to ?tab-pressed THEN \ reset state machine
      dup 7f = IF drop 8 THEN \ Handle DEL as if it was BS. ??? bogus
      dup bl < IF handle-control ELSE
         dup 80 and IF
            dup a0 < IF 7f and handle-meta ELSE drop beep THEN
         ELSE
            insert-char
         THEN
      THEN
   REPEAT
   drop add-history
   accept-len to accept-cur
   move-cursor space
   accept-len
   cursor-off
;

' (accept) to accept