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
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
290 additions
and
148 deletions
+290
-148
parser.go
pkg/utils/parser.go
+290
-148
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
package
utils
import
(
import
(
"bytes"
"bytes"
"fmt"
"io"
"strconv"
"unicode/utf8"
"unicode/utf8"
)
)
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
)
{
type
TerminalParser
struct
{
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
{
break
}
if
!
pasteActive
{
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
}
continue
}
}
else
if
key
==
keyPasteEnd
{
pasteActive
=
false
continue
}
switch
key
{
// line is the current line being entered.
case
keyBackspace
:
line
[]
rune
if
pos
==
0
{
// pos is the logical position of the cursor in line
continue
pos
int
}
// pasteActive is true iff there is a bracketed paste operation in
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
// progress.
case
keyAltLeft
:
pasteActive
bool
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
case
keyAltRight
:
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
case
keyLeft
:
if
pos
==
0
{
continue
}
pos
--
case
keyRight
:
if
pos
==
len
(
line
)
{
continue
}
pos
++
case
keyHome
:
if
pos
==
0
{
continue
}
pos
=
0
case
keyEnd
:
if
pos
==
len
(
line
)
{
continue
}
pos
=
len
(
line
)
case
keyUp
:
line
=
[]
rune
{}
pos
=
0
case
keyDown
:
line
=
[]
rune
{}
pos
=
0
case
keyEnter
:
lines
=
append
(
lines
,
string
(
line
))
line
=
line
[
:
0
]
pos
=
0
lineOk
=
true
case
keyDeleteWord
:
// Delete zero or more spaces and then one or more characters.
line
,
pos
=
EraseNPreviousChars
(
CountToLeftWord
(
pos
,
line
),
pos
,
line
)
case
keyDeleteLine
:
line
=
line
[
:
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
)
}
case
keyCtrlU
:
line
=
line
[
:
0
]
case
keyClearScreen
:
default
:
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
continue
}
line
,
pos
=
AddKeyToLine
(
key
,
pos
,
line
)
}
}
// maxLine is the greatest value of cursorY so far.
if
len
(
rest
)
>
0
{
maxLine
int
n
:=
copy
(
inBuf
[
:
],
rest
)
remainder
=
inBuf
[
:
n
]
}
else
{
remainder
=
nil
}
// remainder is a slice at the beginning of t.inBuf
// remainder contains the remainder of any partial key sequences after
// containing a partial key sequence
// a read. It aliases into inBuf.
readBuf
:=
inBuf
[
len
(
remainder
)
:
]
remainder
[]
byte
inBuf
[
256
]
byte
var
n
int
// history contains previously entered commands so that they can be
n
,
err
:=
c
.
Read
(
readBuf
)
// accessed with the up and down keys.
if
err
!=
nil
{
history
stRingBuffer
if
len
(
line
)
>
0
{
// historyIndex stores the currently accessed history entry, where zero
lines
=
append
(
lines
,
string
(
line
))
// means the immediately previous entry.
}
else
if
len
(
rest
)
>
0
{
historyIndex
int
lines
=
append
(
lines
,
string
(
rest
))
// 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
}
return
// 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.
remainder
=
inBuf
[
:
n
+
len
(
remainder
)]
// 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
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
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
{
if
n
==
0
{
return
line
,
cPos
return
}
}
if
cPos
<
n
{
n
=
cPos
if
t
.
pos
<
n
{
n
=
t
.
pos
}
}
cPos
-=
n
t
.
pos
-=
n
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
return
line
[
:
len
(
line
)
-
n
],
cPos
copy
(
t
.
line
[
t
.
pos
:
],
t
.
line
[
n
+
t
.
pos
:
])
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
-
n
]
}
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
// countToLeftWord returns then number of characters from the cursor to the
if
currentPos
==
0
{
// start of the previous word.
func
(
t
*
TerminalParser
)
countToLeftWord
()
int
{
if
t
.
pos
==
0
{
return
0
return
0
}
}
pos
:=
currentP
os
-
1
pos
:=
t
.
p
os
-
1
for
pos
>
0
{
for
pos
>
0
{
if
line
[
pos
]
!=
' '
{
if
t
.
line
[
pos
]
!=
' '
{
break
break
}
}
pos
--
pos
--
}
}
for
pos
>
0
{
for
pos
>
0
{
if
line
[
pos
]
==
' '
{
if
t
.
line
[
pos
]
==
' '
{
pos
++
pos
++
break
break
}
}
pos
--
pos
--
}
}
return
currentP
os
-
pos
return
t
.
p
os
-
pos
}
}
func
CountToRightWord
(
currentPos
int
,
line
[]
rune
)
int
{
// countToRightWord returns then number of characters from the cursor to the
pos
:=
currentPos
// start of the next word.
for
pos
<
len
(
line
)
{
func
(
t
*
TerminalParser
)
countToRightWord
()
int
{
if
line
[
pos
]
==
' '
{
pos
:=
t
.
pos
for
pos
<
len
(
t
.
line
)
{
if
t
.
line
[
pos
]
==
' '
{
break
break
}
}
pos
++
pos
++
}
}
for
pos
<
len
(
line
)
{
for
pos
<
len
(
t
.
line
)
{
if
line
[
pos
]
!=
' '
{
if
t
.
line
[
pos
]
!=
' '
{
break
break
}
}
pos
++
pos
++
}
}
return
pos
-
currentPos
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
t
.
pos
==
0
{
return
}
t
.
eraseNPreviousChars
(
1
)
case
keyAltLeft
:
// move left by a word.
t
.
pos
-=
t
.
countToLeftWord
()
case
keyAltRight
:
// move right by a word.
t
.
pos
+=
t
.
countToRightWord
()
case
keyLeft
:
if
t
.
pos
==
0
{
return
}
t
.
pos
--
case
keyRight
:
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
t
.
pos
++
case
keyHome
:
if
t
.
pos
==
0
{
return
}
t
.
pos
=
0
case
keyEnd
:
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
t
.
pos
=
len
(
t
.
line
)
case
keyUp
:
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
:
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
:
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.
t
.
eraseNPreviousChars
(
t
.
countToLeftWord
())
case
keyDeleteLine
:
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
t
.
pos
<
len
(
t
.
line
)
{
t
.
pos
++
t
.
eraseNPreviousChars
(
1
)
}
case
keyCtrlU
:
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
)
{
return
}
if
len
(
t
.
line
)
==
maxLineLength
{
return
}
t
.
addKeyToLine
(
key
)
}
return
}
}
func
AddKeyToLine
(
key
rune
,
pos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
// addKeyToLine inserts the given key at the current position in the current
if
len
(
line
)
==
cap
(
line
)
{
// line.
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
func
(
t
*
TerminalParser
)
addKeyToLine
(
key
rune
)
{
copy
(
newLine
,
line
)
if
len
(
t
.
line
)
==
cap
(
t
.
line
)
{
line
=
newLine
newLine
:=
make
([]
rune
,
len
(
t
.
line
),
2
*
(
1
+
len
(
t
.
line
)))
}
copy
(
newLine
,
t
.
line
)
line
=
line
[
:
len
(
line
)
+
1
]
t
.
line
=
newLine
copy
(
line
[
pos
+
1
:
],
line
[
pos
:
])
}
line
[
pos
]
=
key
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
+
1
]
pos
++
copy
(
t
.
line
[
t
.
pos
+
1
:
],
t
.
line
[
t
.
pos
:
])
return
line
,
pos
t
.
line
[
t
.
pos
]
=
key
}
t
.
pos
++
\ No newline at end of file
}
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
}
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
}
}
if
key
==
keyPasteStart
{
t
.
pasteActive
=
true
if
len
(
t
.
line
)
==
0
{
lineIsPasted
=
true
}
continue
}
}
else
if
key
==
keyPasteEnd
{
t
.
pasteActive
=
false
continue
}
if
!
t
.
pasteActive
{
lineIsPasted
=
false
}
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
}
// 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
)]
}
}
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