Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
koko
Commits
7a13be41
Commit
7a13be41
authored
Jan 07, 2020
by
Eric
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
stash
parent
0bcfdc7a
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
273 additions
and
130 deletions
+273
-130
parser.go
pkg/utils/parser.go
+273
-130
No files found.
pkg/utils/parser.go
View file @
7a13be41
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
utils
import
(
"bytes"
"fmt"
"io"
"strconv"
"unicode/utf8"
)
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
)
{
c
:=
bytes
.
NewReader
(
p
)
pasteActive
:=
false
var
line
[]
rune
var
pos
int
var
remainder
[]
byte
var
inBuf
[
256
]
byte
for
{
rest
:=
remainder
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
pasteActive
)
if
key
==
utf8
.
RuneError
{
type
TerminalParser
struct
{
// line is the current line being entered.
line
[]
rune
// pos is the logical position of the cursor in line
pos
int
// pasteActive is true iff there is a bracketed paste operation in
// progress.
pasteActive
bool
// maxLine is the greatest value of cursorY so far.
maxLine
int
// remainder contains the remainder of any partial key sequences after
// a read. It aliases into inBuf.
remainder
[]
byte
inBuf
[
256
]
byte
// history contains previously entered commands so that they can be
// accessed with the up and down keys.
history
stRingBuffer
// historyIndex stores the currently accessed history entry, where zero
// means the immediately previous entry.
historyIndex
int
// When navigating up and down the history it's possible to return to
// the incomplete, initial line. That value is stored in
// historyPending.
historyPending
string
}
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
// a local terminal, that terminal must first have been put into raw mode.
// prompt is a string that is written at the start of each input line (i.e.
// "> ").
func
NewTerminalParser
(
prompt
string
)
*
TerminalParser
{
return
&
TerminalParser
{
historyIndex
:
-
1
,
}
}
func
(
t
*
TerminalParser
)
move
(
up
,
down
,
left
,
right
int
)
{
m
:=
[]
rune
{}
// 1 unit up can be expressed as ^[[A or ^[A
// 5 units up can be expressed as ^[[5A
if
up
==
1
{
m
=
append
(
m
,
keyEscape
,
'['
,
'A'
)
}
else
if
up
>
1
{
m
=
append
(
m
,
keyEscape
,
'['
)
m
=
append
(
m
,
[]
rune
(
strconv
.
Itoa
(
up
))
...
)
m
=
append
(
m
,
'A'
)
}
if
down
==
1
{
m
=
append
(
m
,
keyEscape
,
'['
,
'B'
)
}
else
if
down
>
1
{
m
=
append
(
m
,
keyEscape
,
'['
)
m
=
append
(
m
,
[]
rune
(
strconv
.
Itoa
(
down
))
...
)
m
=
append
(
m
,
'B'
)
}
if
right
==
1
{
m
=
append
(
m
,
keyEscape
,
'['
,
'C'
)
}
else
if
right
>
1
{
m
=
append
(
m
,
keyEscape
,
'['
)
m
=
append
(
m
,
[]
rune
(
strconv
.
Itoa
(
right
))
...
)
m
=
append
(
m
,
'C'
)
}
if
left
==
1
{
m
=
append
(
m
,
keyEscape
,
'['
,
'D'
)
}
else
if
left
>
1
{
m
=
append
(
m
,
keyEscape
,
'['
)
m
=
append
(
m
,
[]
rune
(
strconv
.
Itoa
(
left
))
...
)
m
=
append
(
m
,
'D'
)
}
}
func
(
t
*
TerminalParser
)
setLine
(
newLine
[]
rune
,
newPos
int
)
{
t
.
line
=
newLine
t
.
pos
=
newPos
}
func
(
t
*
TerminalParser
)
eraseNPreviousChars
(
n
int
)
{
if
n
==
0
{
return
}
if
t
.
pos
<
n
{
n
=
t
.
pos
}
t
.
pos
-=
n
copy
(
t
.
line
[
t
.
pos
:
],
t
.
line
[
n
+
t
.
pos
:
])
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
-
n
]
}
// countToLeftWord returns then number of characters from the cursor to the
// start of the previous word.
func
(
t
*
TerminalParser
)
countToLeftWord
()
int
{
if
t
.
pos
==
0
{
return
0
}
pos
:=
t
.
pos
-
1
for
pos
>
0
{
if
t
.
line
[
pos
]
!=
' '
{
break
}
if
!
pasteActive
{
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
pos
--
}
continue
for
pos
>
0
{
if
t
.
line
[
pos
]
==
' '
{
pos
++
break
}
}
else
if
key
==
keyPasteEnd
{
pasteActive
=
false
continue
pos
--
}
return
t
.
pos
-
pos
}
// countToRightWord returns then number of characters from the cursor to the
// start of the next word.
func
(
t
*
TerminalParser
)
countToRightWord
()
int
{
pos
:=
t
.
pos
for
pos
<
len
(
t
.
line
)
{
if
t
.
line
[
pos
]
==
' '
{
break
}
pos
++
}
for
pos
<
len
(
t
.
line
)
{
if
t
.
line
[
pos
]
!=
' '
{
break
}
pos
++
}
return
pos
-
t
.
pos
}
// handleKey processes the given key and, optionally, returns a line of text
// that the user has entered.
func
(
t
*
TerminalParser
)
handleKey
(
key
rune
)
(
line
string
,
ok
bool
)
{
if
t
.
pasteActive
&&
key
!=
keyEnter
{
t
.
addKeyToLine
(
key
)
return
}
switch
key
{
case
keyBackspace
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
case
keyAltLeft
:
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
t
.
pos
-=
t
.
countToLeftWord
(
)
case
keyAltRight
:
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
t
.
pos
+=
t
.
countToRightWord
(
)
case
keyLeft
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
pos
--
t
.
pos
--
case
keyRight
:
if
pos
==
len
(
line
)
{
continue
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
pos
++
t
.
pos
++
case
keyHome
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
pos
=
0
t
.
pos
=
0
case
keyEnd
:
if
pos
==
len
(
line
)
{
continue
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
pos
=
len
(
line
)
t
.
pos
=
len
(
t
.
line
)
case
keyUp
:
line
=
[]
rune
{}
pos
=
0
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
+
1
)
if
!
ok
{
return
""
,
false
}
if
t
.
historyIndex
==
-
1
{
t
.
historyPending
=
string
(
t
.
line
)
}
t
.
historyIndex
++
runes
:=
[]
rune
(
entry
)
t
.
setLine
(
runes
,
len
(
runes
))
case
keyDown
:
line
=
[]
rune
{}
pos
=
0
switch
t
.
historyIndex
{
case
-
1
:
return
case
0
:
runes
:=
[]
rune
(
t
.
historyPending
)
t
.
setLine
(
runes
,
len
(
runes
))
t
.
historyIndex
--
default
:
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
-
1
)
if
ok
{
t
.
historyIndex
--
runes
:=
[]
rune
(
entry
)
t
.
setLine
(
runes
,
len
(
runes
))
}
}
case
keyEnter
:
lines
=
append
(
lines
,
string
(
line
))
line
=
line
[
:
0
]
pos
=
0
lineOk
=
true
line
=
string
(
t
.
line
)
ok
=
true
t
.
line
=
t
.
line
[
:
0
]
t
.
pos
=
0
t
.
maxLine
=
0
case
keyDeleteWord
:
// Delete zero or more spaces and then one or more characters.
line
,
pos
=
EraseNPreviousChars
(
CountToLeftWord
(
pos
,
line
),
pos
,
line
)
t
.
eraseNPreviousChars
(
t
.
countToLeftWord
()
)
case
keyDeleteLine
:
line
=
line
[
:
pos
]
t
.
line
=
t
.
line
[
:
t
.
pos
]
case
keyCtrlD
:
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// readLine().
if
pos
<
len
(
line
)
{
pos
++
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
if
t
.
pos
<
len
(
t
.
line
)
{
t
.
pos
++
t
.
eraseNPreviousChars
(
1
)
}
case
keyCtrlU
:
line
=
line
[
:
0
]
t
.
eraseNPreviousChars
(
t
.
pos
)
case
keyClearScreen
:
// Erases the screen and moves the cursor to the home position.
t
.
setLine
(
t
.
line
,
t
.
pos
)
default
:
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
continue
}
line
,
pos
=
AddKeyToLine
(
key
,
pos
,
line
)
}
}
if
len
(
rest
)
>
0
{
n
:=
copy
(
inBuf
[
:
],
rest
)
remainder
=
inBuf
[
:
n
]
}
else
{
remainder
=
nil
}
// remainder is a slice at the beginning of t.inBuf
// containing a partial key sequence
readBuf
:=
inBuf
[
len
(
remainder
)
:
]
var
n
int
n
,
err
:=
c
.
Read
(
readBuf
)
if
err
!=
nil
{
if
len
(
line
)
>
0
{
lines
=
append
(
lines
,
string
(
line
))
}
else
if
len
(
rest
)
>
0
{
lines
=
append
(
lines
,
string
(
rest
))
return
}
if
len
(
t
.
line
)
==
maxLineLength
{
return
}
remainder
=
inBuf
[
:
n
+
len
(
remainder
)]
t
.
addKeyToLine
(
key
)
}
return
}
func
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
n
==
0
{
return
line
,
cPos
// addKeyToLine inserts the given key at the current position in the current
// line.
func
(
t
*
TerminalParser
)
addKeyToLine
(
key
rune
)
{
if
len
(
t
.
line
)
==
cap
(
t
.
line
)
{
newLine
:=
make
([]
rune
,
len
(
t
.
line
),
2
*
(
1
+
len
(
t
.
line
)))
copy
(
newLine
,
t
.
line
)
t
.
line
=
newLine
}
if
cPos
<
n
{
n
=
cPos
}
cPos
-=
n
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
return
line
[
:
len
(
line
)
-
n
],
cPos
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
+
1
]
copy
(
t
.
line
[
t
.
pos
+
1
:
],
t
.
line
[
t
.
pos
:
])
t
.
line
[
t
.
pos
]
=
key
t
.
pos
++
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
if
currentPos
==
0
{
return
0
}
pos
:=
currentPos
-
1
for
pos
>
0
{
if
line
[
pos
]
!=
' '
{
func
(
t
*
TerminalParser
)
ParseLines
(
p
[]
byte
)
(
lines
[]
string
,
err
error
)
{
lines
=
make
([]
string
,
0
,
3
)
lineIsPasted
:=
t
.
pasteActive
reader
:=
bytes
.
NewBuffer
(
p
)
for
{
rest
:=
t
.
remainder
line
:=
""
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
t
.
pasteActive
)
if
key
==
utf8
.
RuneError
{
break
}
pos
--
if
!
t
.
pasteActive
{
if
key
==
keyCtrlD
{
if
len
(
t
.
line
)
==
0
{
// as key has already handled, we need update remainder data,
t
.
remainder
=
rest
return
""
,
io
.
EOF
}
for
pos
>
0
{
if
line
[
pos
]
==
' '
{
pos
++
break
}
pos
--
if
key
==
keyPasteStart
{
t
.
pasteActive
=
true
if
len
(
t
.
line
)
==
0
{
lineIsPasted
=
true
}
return
currentPos
-
pos
}
func
CountToRightWord
(
currentPos
int
,
line
[]
rune
)
int
{
pos
:=
currentPos
for
pos
<
len
(
line
)
{
if
line
[
pos
]
==
' '
{
break
continue
}
pos
++
}
else
if
key
==
keyPasteEnd
{
t
.
pasteActive
=
false
continue
}
for
pos
<
len
(
line
)
{
if
line
[
pos
]
!=
' '
{
break
if
!
t
.
pasteActive
{
lineIsPasted
=
false
}
pos
++
line
,
lineOk
=
t
.
handleKey
(
key
)
}
if
len
(
rest
)
>
0
{
n
:=
copy
(
t
.
inBuf
[
:
],
rest
)
t
.
remainder
=
t
.
inBuf
[
:
n
]
}
else
{
t
.
remainder
=
nil
}
if
lineOk
{
if
lineIsPasted
{
err
=
ErrPasteIndicator
}
return
}
return
pos
-
currentPos
}
func
AddKeyToLine
(
key
rune
,
pos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
len
(
line
)
==
cap
(
line
)
{
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
copy
(
newLine
,
line
)
line
=
newLine
// t.remainder is a slice at the beginning of t.inBuf
// containing a partial key sequence
readBuf
:=
t
.
inBuf
[
len
(
t
.
remainder
)
:
]
var
n
int
n
,
err
=
reader
.
Read
(
readBuf
)
if
err
!=
nil
{
return
}
t
.
remainder
=
t
.
inBuf
[
:
n
+
len
(
t
.
remainder
)]
}
line
=
line
[
:
len
(
line
)
+
1
]
copy
(
line
[
pos
+
1
:
],
line
[
pos
:
])
line
[
pos
]
=
key
pos
++
return
line
,
pos
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment