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
5 years ago
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
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
// line is the current line being entered.
var
line
[]
rune
line
[]
rune
var
pos
int
// pos is the logical position of the cursor in line
var
remainder
[]
byte
pos
int
var
inBuf
[
256
]
byte
// pasteActive is true iff there is a bracketed paste operation in
for
{
// progress.
rest
:=
remainder
pasteActive
bool
lineOk
:=
false
for
!
lineOk
{
// maxLine is the greatest value of cursorY so far.
var
key
rune
maxLine
int
key
,
rest
=
bytesToKey
(
rest
,
pasteActive
)
if
key
==
utf8
.
RuneError
{
// 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
break
}
}
if
!
pasteActive
{
pos
--
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
}
}
continue
for
pos
>
0
{
if
t
.
line
[
pos
]
==
' '
{
pos
++
break
}
}
}
else
if
key
==
keyPasteEnd
{
pos
--
pasteActive
=
false
}
continue
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
{
switch
key
{
case
keyBackspace
:
case
keyBackspace
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
case
keyAltLeft
:
case
keyAltLeft
:
// move left by a word.
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
t
.
pos
-=
t
.
countToLeftWord
(
)
case
keyAltRight
:
case
keyAltRight
:
// move right by a word.
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
t
.
pos
+=
t
.
countToRightWord
(
)
case
keyLeft
:
case
keyLeft
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
pos
--
t
.
pos
--
case
keyRight
:
case
keyRight
:
if
pos
==
len
(
line
)
{
if
t
.
pos
==
len
(
t
.
line
)
{
continue
return
}
}
pos
++
t
.
pos
++
case
keyHome
:
case
keyHome
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
pos
=
0
t
.
pos
=
0
case
keyEnd
:
case
keyEnd
:
if
pos
==
len
(
line
)
{
if
t
.
pos
==
len
(
t
.
line
)
{
continue
return
}
}
pos
=
len
(
line
)
t
.
pos
=
len
(
t
.
line
)
case
keyUp
:
case
keyUp
:
line
=
[]
rune
{}
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
+
1
)
pos
=
0
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
:
case
keyDown
:
line
=
[]
rune
{}
switch
t
.
historyIndex
{
pos
=
0
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
:
case
keyEnter
:
lines
=
append
(
lines
,
string
(
line
))
line
=
string
(
t
.
line
)
line
=
line
[
:
0
]
ok
=
true
pos
=
0
t
.
line
=
t
.
line
[
:
0
]
lineOk
=
true
t
.
pos
=
0
t
.
maxLine
=
0
case
keyDeleteWord
:
case
keyDeleteWord
:
// Delete zero or more spaces and then one or more characters.
// 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
:
case
keyDeleteLine
:
line
=
line
[
:
pos
]
t
.
line
=
t
.
line
[
:
t
.
pos
]
case
keyCtrlD
:
case
keyCtrlD
:
// Erase the character under the current position.
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// The EOF case when the line is empty is handled in
// readLine().
// readLine().
if
pos
<
len
(
line
)
{
if
t
.
pos
<
len
(
t
.
line
)
{
pos
++
t
.
pos
++
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
}
}
case
keyCtrlU
:
case
keyCtrlU
:
line
=
line
[
:
0
]
t
.
eraseNPreviousChars
(
t
.
pos
)
case
keyClearScreen
:
case
keyClearScreen
:
// Erases the screen and moves the cursor to the home position.
t
.
setLine
(
t
.
line
,
t
.
pos
)
default
:
default
:
if
!
isPrintable
(
key
)
{
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
return
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
))
}
}
if
len
(
t
.
line
)
==
maxLineLength
{
return
return
}
}
remainder
=
inBuf
[
:
n
+
len
(
remainder
)]
t
.
addKeyToLine
(
key
)
}
}
return
}
}
func
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
// addKeyToLine inserts the given key at the current position in the current
if
n
==
0
{
// line.
return
line
,
cPos
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
{
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
+
1
]
n
=
cPos
copy
(
t
.
line
[
t
.
pos
+
1
:
],
t
.
line
[
t
.
pos
:
])
}
t
.
line
[
t
.
pos
]
=
key
cPos
-=
n
t
.
pos
++
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
return
line
[
:
len
(
line
)
-
n
],
cPos
}
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
func
(
t
*
TerminalParser
)
ParseLines
(
p
[]
byte
)
(
lines
[]
string
,
err
error
)
{
if
currentPos
==
0
{
lines
=
make
([]
string
,
0
,
3
)
return
0
lineIsPasted
:=
t
.
pasteActive
}
reader
:=
bytes
.
NewBuffer
(
p
)
for
{
pos
:=
currentPos
-
1
rest
:=
t
.
remainder
for
pos
>
0
{
line
:=
""
if
line
[
pos
]
!=
' '
{
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
t
.
pasteActive
)
if
key
==
utf8
.
RuneError
{
break
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
}
}
continue
return
currentPos
-
pos
}
func
CountToRightWord
(
currentPos
int
,
line
[]
rune
)
int
{
pos
:=
currentPos
for
pos
<
len
(
line
)
{
if
line
[
pos
]
==
' '
{
break
}
}
pos
++
}
else
if
key
==
keyPasteEnd
{
t
.
pasteActive
=
false
continue
}
}
for
pos
<
len
(
line
)
{
if
!
t
.
pasteActive
{
if
line
[
pos
]
!=
' '
{
lineIsPasted
=
false
break
}
}
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
)
{
// t.remainder is a slice at the beginning of t.inBuf
if
len
(
line
)
==
cap
(
line
)
{
// containing a partial key sequence
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
readBuf
:=
t
.
inBuf
[
len
(
t
.
remainder
)
:
]
copy
(
newLine
,
line
)
var
n
int
line
=
newLine
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
}
}
This diff is collapsed.
Click to expand it.
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