blob: cb6f2fa8f3ac90f20f92ab8bf719b752230ebf6b [file] [log] [blame]
\ *****************************************************************************
\ * 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