mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
POC for odo interactive testing (#5466)
* POC for odo interactive testing
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* add interactive tests to make target test-integration
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* [WIP] use func to pass test instructions
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* cleanup comments/test
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* cleanup minor changes
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* fix unit test failure
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* skip for windows
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* skip for windows by not compiling, cleanup
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* cleanup, and add the make target to test files
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* incorporate review, adding comments, cleanup
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* fix failing make validate
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* update test dependency, and possible fix for windows
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
* Revert "update test dependency, and possible fix for windows"
This reverts commit 55580b7dc5.
* Final cleanup
Signed-off-by: anandrkskd <anandrkskd@gmail.com>
This commit is contained in:
committed by
GitHub
parent
fbdacb2dde
commit
3eb92b6a47
@@ -14,6 +14,7 @@ cleanup_namespaces
|
||||
set -e
|
||||
make install
|
||||
make test-integration-devfile
|
||||
make test-integration-interactive
|
||||
make test-e2e-devfile
|
||||
make test-cmd-project
|
||||
) |& tee "/tmp/${LOGFILE}"
|
||||
|
||||
@@ -12,6 +12,7 @@ cleanup_namespaces
|
||||
set -e
|
||||
make install
|
||||
make test-integration
|
||||
make test-integration-interactive
|
||||
make test-integration-devfile
|
||||
make test-cmd-login-logout
|
||||
make test-cmd-project
|
||||
|
||||
5
Makefile
5
Makefile
@@ -266,6 +266,11 @@ test-cmd-debug: ## Run odo debug command tests
|
||||
test-integration: ## Run command's integration tests irrespective of service catalog status in the cluster.
|
||||
$(RUN_GINKGO) $(GINKGO_FLAGS) tests/integration/
|
||||
|
||||
## Run integration interactive tests
|
||||
.PHONY: test-interactive test
|
||||
test-integration-interactive:
|
||||
$(RUN_GINKGO) $(GINKGO_FLAGS) tests/integration/interactive/
|
||||
|
||||
.PHONY: test-integration-devfile
|
||||
test-integration-devfile: ## Run devfile integration tests
|
||||
$(RUN_GINKGO) $(GINKGO_FLAGS) tests/integration/devfile/
|
||||
|
||||
4
go.mod
4
go.mod
@@ -18,7 +18,8 @@ require (
|
||||
github.com/go-git/go-git/v5 v5.3.0
|
||||
github.com/go-openapi/spec v0.19.5
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c
|
||||
github.com/hinshun/vt10x v0.0.0-20220127042424-3ca73d0126d7
|
||||
github.com/kr/pty v1.1.5
|
||||
github.com/kubernetes-sigs/service-catalog v0.3.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
@@ -42,7 +43,6 @@ require (
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tidwall/gjson v1.9.3
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/zalando/go-keyring v0.1.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -642,8 +642,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk=
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20220127042424-3ca73d0126d7 h1:PoerlCqzob3t6b5/8mjCPkX4QSTYR4/+kB8IzqZE3ug=
|
||||
github.com/hinshun/vt10x v0.0.0-20220127042424-3ca73d0126d7/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package testingutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/Netflix/go-expect"
|
||||
"github.com/hinshun/vt10x"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/AlecAivazis/survey.v1"
|
||||
"gopkg.in/AlecAivazis/survey.v1/terminal"
|
||||
)
|
||||
|
||||
// This whole file copies the testing infrastructure from survey lib since it cannot be imported. This mixes elements from:
|
||||
// vendor/gopkg.in/AlecAivazis/survey.v1/survey_posix_test.go
|
||||
// vendor/gopkg.in/AlecAivazis/survey.v1/survey_test.go
|
||||
// vendor/gopkg.in/AlecAivazis/survey.v1/survey.go
|
||||
|
||||
type wantsStdio interface {
|
||||
WithStdio(terminal.Stdio)
|
||||
}
|
||||
|
||||
// Stdio converts an expect.Console into a survey terminal.Stdio
|
||||
func Stdio(c *expect.Console) terminal.Stdio {
|
||||
return terminal.Stdio{In: c.Tty(), Out: c.Tty(), Err: c.Tty()}
|
||||
}
|
||||
|
||||
// PromptTest encapsulates a survey prompt test
|
||||
type PromptTest struct {
|
||||
// Name of the test
|
||||
Name string
|
||||
// Prompt to test
|
||||
Prompt survey.Prompt
|
||||
// Procedure defines the list of interaction with the console simulating user actions
|
||||
Procedure func(*expect.Console)
|
||||
// Expected result
|
||||
Expected interface{}
|
||||
}
|
||||
|
||||
// RunPromptTest runs the specified PromptTest in the given testing context
|
||||
func RunPromptTest(t *testing.T, test PromptTest) {
|
||||
var answer interface{}
|
||||
RunTest(t, test.Procedure, func(stdio terminal.Stdio) error {
|
||||
var err error
|
||||
if p, ok := test.Prompt.(wantsStdio); ok {
|
||||
p.WithStdio(stdio)
|
||||
}
|
||||
answer, err = test.Prompt.Prompt()
|
||||
return err
|
||||
})
|
||||
require.Equal(t, test.Expected, answer)
|
||||
}
|
||||
|
||||
// RunTest runs the given test using the specified procedure simulating the user interaction with the console
|
||||
func RunTest(t *testing.T, procedure func(*expect.Console), test func(terminal.Stdio) error) {
|
||||
t.Parallel()
|
||||
|
||||
// Multiplex output to a buffer as well for the raw bytes.
|
||||
buf := new(bytes.Buffer)
|
||||
c, state, err := vt10x.NewVT10XConsole(expect.WithStdout(buf))
|
||||
require.Nil(t, err)
|
||||
defer c.Close()
|
||||
|
||||
donec := make(chan struct{})
|
||||
go func() {
|
||||
defer close(donec)
|
||||
procedure(c)
|
||||
}()
|
||||
|
||||
err = test(Stdio(c))
|
||||
require.Nil(t, err)
|
||||
|
||||
// Close the slave end of the pty, and read the remaining bytes from the master end.
|
||||
c.Tty().Close()
|
||||
<-donec
|
||||
|
||||
t.Logf("Raw output: %q", buf.String())
|
||||
|
||||
// Dump the terminal's screen.
|
||||
t.Logf("\n%s", expect.StripTrailingEmptyLines(state.String()))
|
||||
}
|
||||
@@ -29,6 +29,7 @@ oc whoami
|
||||
# Integration tests
|
||||
make test-integration || error=true
|
||||
make test-integration-devfile || error=true
|
||||
make test-integration-interactive || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
make test-cmd-project || error=true
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ elif [ "${ARCH}" == "ppc64le" ]; then
|
||||
else
|
||||
# Integration tests
|
||||
make test-integration || error=true
|
||||
make test-integration-interactive || error=true
|
||||
make test-integration-devfile || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
make test-cmd-project || error=true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package tools
|
||||
|
||||
66
tests/helper/helper_interactive.go
Normal file
66
tests/helper/helper_interactive.go
Normal file
@@ -0,0 +1,66 @@
|
||||
//go:build linux || darwin || dragonfly || solaris || openbsd || netbsd || freebsd
|
||||
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
|
||||
|
||||
package helper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os/exec"
|
||||
|
||||
"github.com/Netflix/go-expect"
|
||||
"github.com/hinshun/vt10x"
|
||||
"github.com/kr/pty"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
// RunInteractive runs the command in interactive mode and returns the output, and error
|
||||
// It takes command as array of strings, and a function `test` that contains steps to run the test as an argument
|
||||
func RunInteractive(command []string, test func(*expect.Console, *bytes.Buffer)) (string, error) {
|
||||
|
||||
ptm, pts, err := pty.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
term := vt10x.New(vt10x.WithWriter(pts))
|
||||
|
||||
c, err := expect.NewConsole(expect.WithStdin(ptm), expect.WithStdout(term), expect.WithCloser(pts, ptm))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// execute the command
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
// setup stdin, stdout and stderr
|
||||
cmd.Stdin = c.Tty()
|
||||
cmd.Stdout = c.Tty()
|
||||
cmd.Stderr = c.Tty()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
test(c, buf)
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Close the slave end of the pty, and read the remaining bytes from the master end.
|
||||
c.Tty().Close()
|
||||
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
func SendLine(c *expect.Console, line string) {
|
||||
_, err := c.SendLine(line)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
}
|
||||
|
||||
func ExpectString(c *expect.Console, line string) string {
|
||||
res, err := c.ExpectString(line)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
return res
|
||||
}
|
||||
62
tests/integration/interactive/cmd_init_test.go
Normal file
62
tests/integration/interactive/cmd_init_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
//go:build linux || darwin || dragonfly || solaris || openbsd || netbsd || freebsd
|
||||
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
|
||||
|
||||
package interactive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/Netflix/go-expect"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/redhat-developer/odo/tests/helper"
|
||||
)
|
||||
|
||||
var _ = Describe("odo init interactive command tests", func() {
|
||||
|
||||
var commonVar helper.CommonVar
|
||||
|
||||
// This is run before every Spec (It)
|
||||
var _ = BeforeEach(func() {
|
||||
commonVar = helper.CommonBeforeEach()
|
||||
helper.Chdir(commonVar.Context)
|
||||
})
|
||||
|
||||
// Clean up after the test
|
||||
// This is run after every Spec (It)
|
||||
var _ = AfterEach(func() {
|
||||
helper.CommonAfterEach(commonVar)
|
||||
})
|
||||
|
||||
It("should download correct devfile", func() {
|
||||
|
||||
command := []string{"odo", "init"}
|
||||
output, err := helper.RunInteractive(command, func(c *expect.Console, output *bytes.Buffer) {
|
||||
|
||||
res := helper.ExpectString(c, "Select language")
|
||||
fmt.Fprintln(output, res)
|
||||
helper.SendLine(c, "go")
|
||||
|
||||
res = helper.ExpectString(c, "Select project type")
|
||||
fmt.Fprintln(output, res)
|
||||
helper.SendLine(c, "\n")
|
||||
|
||||
res = helper.ExpectString(c, "Which starter project do you want to use")
|
||||
fmt.Fprintln(output, res)
|
||||
helper.SendLine(c, "\n")
|
||||
|
||||
res = helper.ExpectString(c, "Enter component name")
|
||||
fmt.Fprintln(output, res)
|
||||
helper.SendLine(c, "my-go-app")
|
||||
|
||||
res = helper.ExpectString(c, "Your new component \"my-go-app\" is ready in the current directory.")
|
||||
fmt.Fprintln(output, res)
|
||||
|
||||
})
|
||||
|
||||
Expect(err).To(BeNil())
|
||||
Expect(output).To(ContainSubstring("Your new component \"my-go-app\" is ready in the current directory."))
|
||||
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
|
||||
})
|
||||
})
|
||||
15
tests/integration/interactive/interactive_suit_test.go
Normal file
15
tests/integration/interactive/interactive_suit_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package interactive
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/tests/helper"
|
||||
)
|
||||
|
||||
func TestInteractive(t *testing.T) {
|
||||
helper.RunTestSpecs(t, "Interactive Suite")
|
||||
}
|
||||
96
vendor/github.com/hinshun/vt10x/Gopkg.lock
generated
vendored
96
vendor/github.com/hinshun/vt10x/Gopkg.lock
generated
vendored
@@ -1,96 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Netflix/go-expect"
|
||||
packages = ["."]
|
||||
revision = "c93bf25de8e869da25cf26bcd2932b36141f61ae"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gdamore/encoding"
|
||||
packages = ["."]
|
||||
revision = "b23993cbb6353f0e6aa98d0ee318a34728f628b9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gdamore/tcell"
|
||||
packages = [
|
||||
".",
|
||||
"terminfo"
|
||||
]
|
||||
revision = "b3cebc399d6f98536af845ed8a5144ab586f6759"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kr/pty"
|
||||
packages = ["."]
|
||||
revision = "282ce0e5322c82529687d609ee670fac7c7d917c"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/lucasb-eyer/go-colorful"
|
||||
packages = ["."]
|
||||
revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
packages = ["."]
|
||||
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"encoding",
|
||||
"encoding/internal/identifier",
|
||||
"internal/gen",
|
||||
"transform",
|
||||
"unicode/cldr"
|
||||
]
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "bd24f29ad753e2f861768c734d71fbbc4e0380b5d7b43b6b101c01274cabcedb"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
38
vendor/github.com/hinshun/vt10x/Gopkg.toml
generated
vendored
38
vendor/github.com/hinshun/vt10x/Gopkg.toml
generated
vendored
@@ -1,38 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kr/pty"
|
||||
version = "1.1.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
5
vendor/github.com/hinshun/vt10x/color.go
generated
vendored
5
vendor/github.com/hinshun/vt10x/color.go
generated
vendored
@@ -24,12 +24,13 @@ const (
|
||||
// For example, a transparent background. Otherwise, the simple case is to
|
||||
// map default colors to another color.
|
||||
const (
|
||||
DefaultFG Color = 0xff80 + iota
|
||||
DefaultFG Color = 1<<24 + iota
|
||||
DefaultBG
|
||||
DefaultCursor
|
||||
)
|
||||
|
||||
// Color maps to the ANSI colors [0, 16) and the xterm colors [16, 256).
|
||||
type Color uint16
|
||||
type Color uint32
|
||||
|
||||
// ANSI returns true if Color is within [0, 16).
|
||||
func (c Color) ANSI() bool {
|
||||
|
||||
40
vendor/github.com/hinshun/vt10x/csi.go
generated
vendored
40
vendor/github.com/hinshun/vt10x/csi.go
generated
vendored
@@ -74,26 +74,26 @@ func (t *State) handleCSI() {
|
||||
case '@': // ICH - insert <n> blank char
|
||||
t.insertBlanks(c.arg(0, 1))
|
||||
case 'A': // CUU - cursor <n> up
|
||||
t.moveTo(t.cur.x, t.cur.y-c.maxarg(0, 1))
|
||||
t.moveTo(t.cur.X, t.cur.Y-c.maxarg(0, 1))
|
||||
case 'B', 'e': // CUD, VPR - cursor <n> down
|
||||
t.moveTo(t.cur.x, t.cur.y+c.maxarg(0, 1))
|
||||
t.moveTo(t.cur.X, t.cur.Y+c.maxarg(0, 1))
|
||||
case 'c': // DA - device attributes
|
||||
if c.arg(0, 0) == 0 {
|
||||
// TODO: write vt102 id
|
||||
}
|
||||
case 'C', 'a': // CUF, HPR - cursor <n> forward
|
||||
t.moveTo(t.cur.x+c.maxarg(0, 1), t.cur.y)
|
||||
t.moveTo(t.cur.X+c.maxarg(0, 1), t.cur.Y)
|
||||
case 'D': // CUB - cursor <n> backward
|
||||
t.moveTo(t.cur.x-c.maxarg(0, 1), t.cur.y)
|
||||
t.moveTo(t.cur.X-c.maxarg(0, 1), t.cur.Y)
|
||||
case 'E': // CNL - cursor <n> down and first col
|
||||
t.moveTo(0, t.cur.y+c.arg(0, 1))
|
||||
t.moveTo(0, t.cur.Y+c.arg(0, 1))
|
||||
case 'F': // CPL - cursor <n> up and first col
|
||||
t.moveTo(0, t.cur.y-c.arg(0, 1))
|
||||
t.moveTo(0, t.cur.Y-c.arg(0, 1))
|
||||
case 'g': // TBC - tabulation clear
|
||||
switch c.arg(0, 0) {
|
||||
// clear current tab stop
|
||||
case 0:
|
||||
t.tabs[t.cur.x] = false
|
||||
t.tabs[t.cur.X] = false
|
||||
// clear all tabs
|
||||
case 3:
|
||||
for i := range t.tabs {
|
||||
@@ -103,7 +103,7 @@ func (t *State) handleCSI() {
|
||||
goto unknown
|
||||
}
|
||||
case 'G', '`': // CHA, HPA - Move to <col>
|
||||
t.moveTo(c.arg(0, 1)-1, t.cur.y)
|
||||
t.moveTo(c.arg(0, 1)-1, t.cur.Y)
|
||||
case 'H', 'f': // CUP, HVP - move to <row> <col>
|
||||
t.moveAbsTo(c.arg(1, 1)-1, c.arg(0, 1)-1)
|
||||
case 'I': // CHT - cursor forward tabulation <n> tab stops
|
||||
@@ -115,15 +115,15 @@ func (t *State) handleCSI() {
|
||||
// TODO: sel.ob.x = -1
|
||||
switch c.arg(0, 0) {
|
||||
case 0: // below
|
||||
t.clear(t.cur.x, t.cur.y, t.cols-1, t.cur.y)
|
||||
if t.cur.y < t.rows-1 {
|
||||
t.clear(0, t.cur.y+1, t.cols-1, t.rows-1)
|
||||
t.clear(t.cur.X, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
if t.cur.Y < t.rows-1 {
|
||||
t.clear(0, t.cur.Y+1, t.cols-1, t.rows-1)
|
||||
}
|
||||
case 1: // above
|
||||
if t.cur.y > 1 {
|
||||
t.clear(0, 0, t.cols-1, t.cur.y-1)
|
||||
if t.cur.Y > 1 {
|
||||
t.clear(0, 0, t.cols-1, t.cur.Y-1)
|
||||
}
|
||||
t.clear(0, t.cur.y, t.cur.x, t.cur.y)
|
||||
t.clear(0, t.cur.Y, t.cur.X, t.cur.Y)
|
||||
case 2: // all
|
||||
t.clear(0, 0, t.cols-1, t.rows-1)
|
||||
default:
|
||||
@@ -132,11 +132,11 @@ func (t *State) handleCSI() {
|
||||
case 'K': // EL - clear line
|
||||
switch c.arg(0, 0) {
|
||||
case 0: // right
|
||||
t.clear(t.cur.x, t.cur.y, t.cols-1, t.cur.y)
|
||||
t.clear(t.cur.X, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
case 1: // left
|
||||
t.clear(0, t.cur.y, t.cur.x, t.cur.y)
|
||||
t.clear(0, t.cur.Y, t.cur.X, t.cur.Y)
|
||||
case 2: // all
|
||||
t.clear(0, t.cur.y, t.cols-1, t.cur.y)
|
||||
t.clear(0, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
}
|
||||
case 'S': // SU - scroll <n> lines up
|
||||
t.scrollUp(t.top, c.arg(0, 1))
|
||||
@@ -149,7 +149,7 @@ func (t *State) handleCSI() {
|
||||
case 'M': // DL - delete <n> lines
|
||||
t.deleteLines(c.arg(0, 1))
|
||||
case 'X': // ECH - erase <n> chars
|
||||
t.clear(t.cur.x, t.cur.y, t.cur.x+c.arg(0, 1)-1, t.cur.y)
|
||||
t.clear(t.cur.X, t.cur.Y, t.cur.X+c.arg(0, 1)-1, t.cur.Y)
|
||||
case 'P': // DCH - delete <n> chars
|
||||
t.deleteChars(c.arg(0, 1))
|
||||
case 'Z': // CBT - cursor backward tabulation <n> tab stops
|
||||
@@ -158,7 +158,7 @@ func (t *State) handleCSI() {
|
||||
t.putTab(false)
|
||||
}
|
||||
case 'd': // VPA - move to <row>
|
||||
t.moveAbsTo(t.cur.x, c.arg(0, 1)-1)
|
||||
t.moveAbsTo(t.cur.X, c.arg(0, 1)-1)
|
||||
case 'h': // SM - set terminal mode
|
||||
t.setMode(c.priv, true, c.args)
|
||||
case 'm': // SGR - terminal attribute (color)
|
||||
@@ -168,7 +168,7 @@ func (t *State) handleCSI() {
|
||||
case 5: // DSR - device status report
|
||||
t.w.Write([]byte("\033[0n"))
|
||||
case 6: // CPR - cursor position report
|
||||
t.w.Write([]byte(fmt.Sprintf("\033[%d;%dR", t.cur.y+1, t.cur.x+1)))
|
||||
t.w.Write([]byte(fmt.Sprintf("\033[%d;%dR", t.cur.Y+1, t.cur.X+1)))
|
||||
}
|
||||
case 'r': // DECSTBM - set scrolling region
|
||||
if c.priv {
|
||||
|
||||
29
vendor/github.com/hinshun/vt10x/expect.go
generated
vendored
29
vendor/github.com/hinshun/vt10x/expect.go
generated
vendored
@@ -1,29 +0,0 @@
|
||||
package vt10x
|
||||
|
||||
import (
|
||||
expect "github.com/Netflix/go-expect"
|
||||
"github.com/kr/pty"
|
||||
)
|
||||
|
||||
// NewVT10XConsole returns a new expect.Console that multiplexes the
|
||||
// Stdin/Stdout to a VT10X terminal, allowing Console to interact with an
|
||||
// application sending ANSI escape sequences.
|
||||
func NewVT10XConsole(opts ...expect.ConsoleOpt) (*expect.Console, *State, error) {
|
||||
ptm, pts, err := pty.Open()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var state State
|
||||
term, err := Create(&state, pts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
c, err := expect.NewConsole(append(opts, expect.WithStdin(ptm), expect.WithStdout(term), expect.WithCloser(pts, ptm, term))...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return c, &state, nil
|
||||
}
|
||||
3
vendor/github.com/hinshun/vt10x/go.mod
generated
vendored
Normal file
3
vendor/github.com/hinshun/vt10x/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/hinshun/vt10x
|
||||
|
||||
go 1.14
|
||||
36
vendor/github.com/hinshun/vt10x/parse.go
generated
vendored
36
vendor/github.com/hinshun/vt10x/parse.go
generated
vendored
@@ -7,27 +7,27 @@ func isControlCode(c rune) bool {
|
||||
func (t *State) parse(c rune) {
|
||||
t.logf("%q", string(c))
|
||||
if isControlCode(c) {
|
||||
if t.handleControlCodes(c) || t.cur.attr.mode&attrGfx == 0 {
|
||||
if t.handleControlCodes(c) || t.cur.Attr.Mode&attrGfx == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: update selection; see st.c:2450
|
||||
|
||||
if t.mode&ModeWrap != 0 && t.cur.state&cursorWrapNext != 0 {
|
||||
t.lines[t.cur.y][t.cur.x].mode |= attrWrap
|
||||
if t.mode&ModeWrap != 0 && t.cur.State&cursorWrapNext != 0 {
|
||||
t.lines[t.cur.Y][t.cur.X].Mode |= attrWrap
|
||||
t.newline(true)
|
||||
}
|
||||
|
||||
if t.mode&ModeInsert != 0 && t.cur.x+1 < t.cols {
|
||||
if t.mode&ModeInsert != 0 && t.cur.X+1 < t.cols {
|
||||
// TODO: move shiz, look at st.c:2458
|
||||
t.logln("insert mode not implemented")
|
||||
}
|
||||
|
||||
t.setChar(c, &t.cur.attr, t.cur.x, t.cur.y)
|
||||
if t.cur.x+1 < t.cols {
|
||||
t.moveTo(t.cur.x+1, t.cur.y)
|
||||
t.setChar(c, &t.cur.Attr, t.cur.X, t.cur.Y)
|
||||
if t.cur.X+1 < t.cols {
|
||||
t.moveTo(t.cur.X+1, t.cur.Y)
|
||||
} else {
|
||||
t.cur.state |= cursorWrapNext
|
||||
t.cur.State |= cursorWrapNext
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,20 +56,20 @@ func (t *State) parseEsc(c rune) {
|
||||
'*', // set tertiary charset G2 (ignored)
|
||||
'+': // set quaternary charset G3 (ignored)
|
||||
case 'D': // IND - linefeed
|
||||
if t.cur.y == t.bottom {
|
||||
if t.cur.Y == t.bottom {
|
||||
t.scrollUp(t.top, 1)
|
||||
} else {
|
||||
t.moveTo(t.cur.x, t.cur.y+1)
|
||||
t.moveTo(t.cur.X, t.cur.Y+1)
|
||||
}
|
||||
case 'E': // NEL - next line
|
||||
t.newline(true)
|
||||
case 'H': // HTS - horizontal tab stop
|
||||
t.tabs[t.cur.x] = true
|
||||
t.tabs[t.cur.X] = true
|
||||
case 'M': // RI - reverse index
|
||||
if t.cur.y == t.top {
|
||||
if t.cur.Y == t.top {
|
||||
t.scrollDown(t.top, 1)
|
||||
} else {
|
||||
t.moveTo(t.cur.x, t.cur.y-1)
|
||||
t.moveTo(t.cur.X, t.cur.Y-1)
|
||||
}
|
||||
case 'Z': // DECID - identify terminal
|
||||
// TODO: write to our writer our id
|
||||
@@ -132,9 +132,9 @@ func (t *State) parseEscAltCharset(c rune) {
|
||||
t.logf("%q", string(c))
|
||||
switch c {
|
||||
case '0': // line drawing set
|
||||
t.cur.attr.mode |= attrGfx
|
||||
t.cur.Attr.Mode |= attrGfx
|
||||
case 'B': // USASCII
|
||||
t.cur.attr.mode &^= attrGfx
|
||||
t.cur.Attr.Mode &^= attrGfx
|
||||
case 'A', // UK (ignored)
|
||||
'<', // multinational (ignored)
|
||||
'5', // Finnish (ignored)
|
||||
@@ -154,7 +154,7 @@ func (t *State) parseEscTest(c rune) {
|
||||
if c == '8' {
|
||||
for y := 0; y < t.rows; y++ {
|
||||
for x := 0; x < t.cols; x++ {
|
||||
t.setChar('E', &t.cur.attr, x, y)
|
||||
t.setChar('E', &t.cur.Attr, x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,10 +171,10 @@ func (t *State) handleControlCodes(c rune) bool {
|
||||
t.putTab(true)
|
||||
// BS
|
||||
case '\b':
|
||||
t.moveTo(t.cur.x-1, t.cur.y)
|
||||
t.moveTo(t.cur.X-1, t.cur.Y)
|
||||
// CR
|
||||
case '\r':
|
||||
t.moveTo(0, t.cur.y)
|
||||
t.moveTo(0, t.cur.Y)
|
||||
// LF, VT, LF
|
||||
case '\f', '\v', '\n':
|
||||
// go to first col if mode is set
|
||||
|
||||
209
vendor/github.com/hinshun/vt10x/state.go
generated
vendored
209
vendor/github.com/hinshun/vt10x/state.go
generated
vendored
@@ -62,18 +62,18 @@ const (
|
||||
ChangedTitle
|
||||
)
|
||||
|
||||
type glyph struct {
|
||||
c rune
|
||||
mode int16
|
||||
fg, bg Color
|
||||
type Glyph struct {
|
||||
Char rune
|
||||
Mode int16
|
||||
FG, BG Color
|
||||
}
|
||||
|
||||
type line []glyph
|
||||
type line []Glyph
|
||||
|
||||
type cursor struct {
|
||||
attr glyph
|
||||
x, y int
|
||||
state uint8
|
||||
type Cursor struct {
|
||||
Attr Glyph
|
||||
X, Y int
|
||||
State uint8
|
||||
}
|
||||
|
||||
type parseState func(c rune)
|
||||
@@ -91,7 +91,7 @@ type State struct {
|
||||
altLines []line
|
||||
dirty []bool // line dirtiness
|
||||
anydirty bool
|
||||
cur, curSaved cursor
|
||||
cur, curSaved Cursor
|
||||
top, bottom int // scroll limits
|
||||
mode ModeFlag
|
||||
state parseState
|
||||
@@ -100,6 +100,14 @@ type State struct {
|
||||
numlock bool
|
||||
tabs []bool
|
||||
title string
|
||||
colorOverride map[Color]Color
|
||||
}
|
||||
|
||||
func newState(w io.Writer) *State {
|
||||
return &State{
|
||||
w: w,
|
||||
colorOverride: make(map[Color]Color),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *State) logf(format string, args ...interface{}) {
|
||||
@@ -133,15 +141,24 @@ func (t *State) Unlock() {
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
// Cell returns the character code, foreground color, and background
|
||||
// color at position (x, y) relative to the top left of the terminal.
|
||||
func (t *State) Cell(x, y int) (ch rune, fg Color, bg Color) {
|
||||
return t.lines[y][x].c, Color(t.lines[y][x].fg), Color(t.lines[y][x].bg)
|
||||
// Cell returns the glyph containing the character code, foreground color, and
|
||||
// background color at position (x, y) relative to the top left of the terminal.
|
||||
func (t *State) Cell(x, y int) Glyph {
|
||||
cell := t.lines[y][x]
|
||||
fg, ok := t.colorOverride[cell.FG]
|
||||
if ok {
|
||||
cell.FG = fg
|
||||
}
|
||||
bg, ok := t.colorOverride[cell.BG]
|
||||
if ok {
|
||||
cell.BG = bg
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
// Cursor returns the current position of the cursor.
|
||||
func (t *State) Cursor() (int, int) {
|
||||
return t.cur.x, t.cur.y
|
||||
func (t *State) Cursor() Cursor {
|
||||
return t.cur
|
||||
}
|
||||
|
||||
// CursorVisible returns the visible state of the cursor.
|
||||
@@ -149,9 +166,9 @@ func (t *State) CursorVisible() bool {
|
||||
return t.mode&ModeHide == 0
|
||||
}
|
||||
|
||||
// Mode tests if mode is currently set.
|
||||
func (t *State) Mode(mode ModeFlag) bool {
|
||||
return t.mode&mode != 0
|
||||
// Mode returns the current terminal mode.
|
||||
func (t *State) Mode() ModeFlag {
|
||||
return t.mode
|
||||
}
|
||||
|
||||
// Title returns the current title set via the tty.
|
||||
@@ -186,7 +203,7 @@ func (t *State) saveCursor() {
|
||||
|
||||
func (t *State) restoreCursor() {
|
||||
t.cur = t.curSaved
|
||||
t.moveTo(t.cur.x, t.cur.y)
|
||||
t.moveTo(t.cur.X, t.cur.Y)
|
||||
}
|
||||
|
||||
func (t *State) put(c rune) {
|
||||
@@ -194,7 +211,7 @@ func (t *State) put(c rune) {
|
||||
}
|
||||
|
||||
func (t *State) putTab(forward bool) {
|
||||
x := t.cur.x
|
||||
x := t.cur.X
|
||||
if forward {
|
||||
if x == t.cols {
|
||||
return
|
||||
@@ -208,11 +225,11 @@ func (t *State) putTab(forward bool) {
|
||||
for x--; x > 0 && !t.tabs[x]; x-- {
|
||||
}
|
||||
}
|
||||
t.moveTo(x, t.cur.y)
|
||||
t.moveTo(x, t.cur.Y)
|
||||
}
|
||||
|
||||
func (t *State) newline(firstCol bool) {
|
||||
y := t.cur.y
|
||||
y := t.cur.Y
|
||||
if y == t.bottom {
|
||||
cur := t.cur
|
||||
t.cur = t.defaultCursor()
|
||||
@@ -224,7 +241,7 @@ func (t *State) newline(firstCol bool) {
|
||||
if firstCol {
|
||||
t.moveTo(0, y)
|
||||
} else {
|
||||
t.moveTo(t.cur.x, y)
|
||||
t.moveTo(t.cur.X, y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +257,8 @@ var gfxCharTable = [62]rune{
|
||||
'│', '≤', '≥', 'π', '≠', '£', '·', // x - ~
|
||||
}
|
||||
|
||||
func (t *State) setChar(c rune, attr *glyph, x, y int) {
|
||||
if attr.mode&attrGfx != 0 {
|
||||
func (t *State) setChar(c rune, attr *Glyph, x, y int) {
|
||||
if attr.Mode&attrGfx != 0 {
|
||||
if c >= 0x41 && c <= 0x7e && gfxCharTable[c-0x41] != 0 {
|
||||
c = gfxCharTable[c-0x41]
|
||||
}
|
||||
@@ -249,21 +266,21 @@ func (t *State) setChar(c rune, attr *glyph, x, y int) {
|
||||
t.changed |= ChangedScreen
|
||||
t.dirty[y] = true
|
||||
t.lines[y][x] = *attr
|
||||
t.lines[y][x].c = c
|
||||
//if t.options.BrightBold && attr.mode&attrBold != 0 && attr.fg < 8 {
|
||||
if attr.mode&attrBold != 0 && attr.fg < 8 {
|
||||
t.lines[y][x].fg = attr.fg + 8
|
||||
t.lines[y][x].Char = c
|
||||
//if t.options.BrightBold && attr.Mode&attrBold != 0 && attr.FG < 8 {
|
||||
if attr.Mode&attrBold != 0 && attr.FG < 8 {
|
||||
t.lines[y][x].FG = attr.FG + 8
|
||||
}
|
||||
if attr.mode&attrReverse != 0 {
|
||||
t.lines[y][x].fg = attr.bg
|
||||
t.lines[y][x].bg = attr.fg
|
||||
if attr.Mode&attrReverse != 0 {
|
||||
t.lines[y][x].FG = attr.BG
|
||||
t.lines[y][x].BG = attr.FG
|
||||
}
|
||||
}
|
||||
|
||||
func (t *State) defaultCursor() cursor {
|
||||
c := cursor{}
|
||||
c.attr.fg = DefaultFG
|
||||
c.attr.bg = DefaultBG
|
||||
func (t *State) defaultCursor() Cursor {
|
||||
c := Cursor{}
|
||||
c.Attr.FG = DefaultFG
|
||||
c.Attr.BG = DefaultBG
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -291,7 +308,7 @@ func (t *State) resize(cols, rows int) bool {
|
||||
if cols < 1 || rows < 1 {
|
||||
return false
|
||||
}
|
||||
slide := t.cur.y - rows + 1
|
||||
slide := t.cur.Y - rows + 1
|
||||
if slide > 0 {
|
||||
copy(t.lines, t.lines[slide:slide+rows])
|
||||
copy(t.altLines, t.altLines[slide:slide+rows])
|
||||
@@ -329,7 +346,7 @@ func (t *State) resize(cols, rows int) bool {
|
||||
t.cols = cols
|
||||
t.rows = rows
|
||||
t.setScroll(0, rows-1)
|
||||
t.moveTo(t.cur.x, t.cur.y)
|
||||
t.moveTo(t.cur.X, t.cur.Y)
|
||||
for i := 0; i < 2; i++ {
|
||||
if mincols < cols && minrows > 0 {
|
||||
t.clear(mincols, 0, cols-1, minrows-1)
|
||||
@@ -357,8 +374,8 @@ func (t *State) clear(x0, y0, x1, y1 int) {
|
||||
for y := y0; y <= y1; y++ {
|
||||
t.dirty[y] = true
|
||||
for x := x0; x <= x1; x++ {
|
||||
t.lines[y][x] = t.cur.attr
|
||||
t.lines[y][x].c = ' '
|
||||
t.lines[y][x] = t.cur.Attr
|
||||
t.lines[y][x].Char = ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +385,7 @@ func (t *State) clearAll() {
|
||||
}
|
||||
|
||||
func (t *State) moveAbsTo(x, y int) {
|
||||
if t.cur.state&cursorOrigin != 0 {
|
||||
if t.cur.State&cursorOrigin != 0 {
|
||||
y += t.top
|
||||
}
|
||||
t.moveTo(x, y)
|
||||
@@ -376,7 +393,7 @@ func (t *State) moveAbsTo(x, y int) {
|
||||
|
||||
func (t *State) moveTo(x, y int) {
|
||||
var miny, maxy int
|
||||
if t.cur.state&cursorOrigin != 0 {
|
||||
if t.cur.State&cursorOrigin != 0 {
|
||||
miny = t.top
|
||||
maxy = t.bottom
|
||||
} else {
|
||||
@@ -386,9 +403,9 @@ func (t *State) moveTo(x, y int) {
|
||||
x = clamp(x, 0, t.cols-1)
|
||||
y = clamp(y, miny, maxy)
|
||||
t.changed |= ChangedScreen
|
||||
t.cur.state &^= cursorWrapNext
|
||||
t.cur.x = x
|
||||
t.cur.y = y
|
||||
t.cur.State &^= cursorWrapNext
|
||||
t.cur.X = x
|
||||
t.cur.Y = y
|
||||
}
|
||||
|
||||
func (t *State) swapScreen() {
|
||||
@@ -492,9 +509,9 @@ func (t *State) setMode(priv bool, set bool, args []int) {
|
||||
}
|
||||
case 6: // DECOM - origin
|
||||
if set {
|
||||
t.cur.state |= cursorOrigin
|
||||
t.cur.State |= cursorOrigin
|
||||
} else {
|
||||
t.cur.state &^= cursorOrigin
|
||||
t.cur.State &^= cursorOrigin
|
||||
}
|
||||
t.moveAbsTo(0, 0)
|
||||
case 7: // DECAWM - auto wrap
|
||||
@@ -594,64 +611,80 @@ func (t *State) setAttr(attr []int) {
|
||||
a := attr[i]
|
||||
switch a {
|
||||
case 0:
|
||||
t.cur.attr.mode &^= attrReverse | attrUnderline | attrBold | attrItalic | attrBlink
|
||||
t.cur.attr.fg = DefaultFG
|
||||
t.cur.attr.bg = DefaultBG
|
||||
t.cur.Attr.Mode &^= attrReverse | attrUnderline | attrBold | attrItalic | attrBlink
|
||||
t.cur.Attr.FG = DefaultFG
|
||||
t.cur.Attr.BG = DefaultBG
|
||||
case 1:
|
||||
t.cur.attr.mode |= attrBold
|
||||
t.cur.Attr.Mode |= attrBold
|
||||
case 3:
|
||||
t.cur.attr.mode |= attrItalic
|
||||
t.cur.Attr.Mode |= attrItalic
|
||||
case 4:
|
||||
t.cur.attr.mode |= attrUnderline
|
||||
t.cur.Attr.Mode |= attrUnderline
|
||||
case 5, 6: // slow, rapid blink
|
||||
t.cur.attr.mode |= attrBlink
|
||||
t.cur.Attr.Mode |= attrBlink
|
||||
case 7:
|
||||
t.cur.attr.mode |= attrReverse
|
||||
t.cur.Attr.Mode |= attrReverse
|
||||
case 21, 22:
|
||||
t.cur.attr.mode &^= attrBold
|
||||
t.cur.Attr.Mode &^= attrBold
|
||||
case 23:
|
||||
t.cur.attr.mode &^= attrItalic
|
||||
t.cur.Attr.Mode &^= attrItalic
|
||||
case 24:
|
||||
t.cur.attr.mode &^= attrUnderline
|
||||
t.cur.Attr.Mode &^= attrUnderline
|
||||
case 25, 26:
|
||||
t.cur.attr.mode &^= attrBlink
|
||||
t.cur.Attr.Mode &^= attrBlink
|
||||
case 27:
|
||||
t.cur.attr.mode &^= attrReverse
|
||||
t.cur.Attr.Mode &^= attrReverse
|
||||
case 38:
|
||||
if i+2 < len(attr) && attr[i+1] == 5 {
|
||||
i += 2
|
||||
if between(attr[i], 0, 255) {
|
||||
t.cur.attr.fg = Color(attr[i])
|
||||
t.cur.Attr.FG = Color(attr[i])
|
||||
} else {
|
||||
t.logf("bad fgcolor %d\n", attr[i])
|
||||
}
|
||||
} else if i+4 < len(attr) && attr[i+1] == 2 {
|
||||
i += 4
|
||||
r, g, b := attr[i-2], attr[i-1], attr[i]
|
||||
if !between(r, 0, 255) || !between(g, 0, 255) || !between(b, 0, 255) {
|
||||
t.logf("bad fg rgb color (%d,%d,%d)\n", r, g, b)
|
||||
} else {
|
||||
t.cur.Attr.FG = Color(r<<16 | g<<8 | b)
|
||||
}
|
||||
} else {
|
||||
t.logf("gfx attr %d unknown\n", a)
|
||||
}
|
||||
case 39:
|
||||
t.cur.attr.fg = DefaultFG
|
||||
t.cur.Attr.FG = DefaultFG
|
||||
case 48:
|
||||
if i+2 < len(attr) && attr[i+1] == 5 {
|
||||
i += 2
|
||||
if between(attr[i], 0, 255) {
|
||||
t.cur.attr.bg = Color(attr[i])
|
||||
t.cur.Attr.BG = Color(attr[i])
|
||||
} else {
|
||||
t.logf("bad bgcolor %d\n", attr[i])
|
||||
}
|
||||
} else if i+4 < len(attr) && attr[i+1] == 2 {
|
||||
i += 4
|
||||
r, g, b := attr[i-2], attr[i-1], attr[i]
|
||||
if !between(r, 0, 255) || !between(g, 0, 255) || !between(b, 0, 255) {
|
||||
t.logf("bad bg rgb color (%d,%d,%d)\n", r, g, b)
|
||||
} else {
|
||||
t.cur.Attr.BG = Color(r<<16 | g<<8 | b)
|
||||
}
|
||||
} else {
|
||||
t.logf("gfx attr %d unknown\n", a)
|
||||
}
|
||||
case 49:
|
||||
t.cur.attr.bg = DefaultBG
|
||||
t.cur.Attr.BG = DefaultBG
|
||||
default:
|
||||
if between(a, 30, 37) {
|
||||
t.cur.attr.fg = Color(a - 30)
|
||||
t.cur.Attr.FG = Color(a - 30)
|
||||
} else if between(a, 40, 47) {
|
||||
t.cur.attr.bg = Color(a - 40)
|
||||
t.cur.Attr.BG = Color(a - 40)
|
||||
} else if between(a, 90, 97) {
|
||||
t.cur.attr.fg = Color(a - 90 + 8)
|
||||
t.cur.Attr.FG = Color(a - 90 + 8)
|
||||
} else if between(a, 100, 107) {
|
||||
t.cur.attr.bg = Color(a - 100 + 8)
|
||||
t.cur.Attr.BG = Color(a - 100 + 8)
|
||||
} else {
|
||||
t.logf("gfx attr %d unknown\n", a)
|
||||
}
|
||||
@@ -660,46 +693,46 @@ func (t *State) setAttr(attr []int) {
|
||||
}
|
||||
|
||||
func (t *State) insertBlanks(n int) {
|
||||
src := t.cur.x
|
||||
src := t.cur.X
|
||||
dst := src + n
|
||||
size := t.cols - dst
|
||||
t.changed |= ChangedScreen
|
||||
t.dirty[t.cur.y] = true
|
||||
t.dirty[t.cur.Y] = true
|
||||
|
||||
if dst >= t.cols {
|
||||
t.clear(t.cur.x, t.cur.y, t.cols-1, t.cur.y)
|
||||
t.clear(t.cur.X, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
} else {
|
||||
copy(t.lines[t.cur.y][dst:dst+size], t.lines[t.cur.y][src:src+size])
|
||||
t.clear(src, t.cur.y, dst-1, t.cur.y)
|
||||
copy(t.lines[t.cur.Y][dst:dst+size], t.lines[t.cur.Y][src:src+size])
|
||||
t.clear(src, t.cur.Y, dst-1, t.cur.Y)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *State) insertBlankLines(n int) {
|
||||
if t.cur.y < t.top || t.cur.y > t.bottom {
|
||||
if t.cur.Y < t.top || t.cur.Y > t.bottom {
|
||||
return
|
||||
}
|
||||
t.scrollDown(t.cur.y, n)
|
||||
t.scrollDown(t.cur.Y, n)
|
||||
}
|
||||
|
||||
func (t *State) deleteLines(n int) {
|
||||
if t.cur.y < t.top || t.cur.y > t.bottom {
|
||||
if t.cur.Y < t.top || t.cur.Y > t.bottom {
|
||||
return
|
||||
}
|
||||
t.scrollUp(t.cur.y, n)
|
||||
t.scrollUp(t.cur.Y, n)
|
||||
}
|
||||
|
||||
func (t *State) deleteChars(n int) {
|
||||
src := t.cur.x + n
|
||||
dst := t.cur.x
|
||||
src := t.cur.X + n
|
||||
dst := t.cur.X
|
||||
size := t.cols - src
|
||||
t.changed |= ChangedScreen
|
||||
t.dirty[t.cur.y] = true
|
||||
t.dirty[t.cur.Y] = true
|
||||
|
||||
if src >= t.cols {
|
||||
t.clear(t.cur.x, t.cur.y, t.cols-1, t.cur.y)
|
||||
t.clear(t.cur.X, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
} else {
|
||||
copy(t.lines[t.cur.y][dst:dst+size], t.lines[t.cur.y][src:src+size])
|
||||
t.clear(t.cols-n, t.cur.y, t.cols-1, t.cur.y)
|
||||
copy(t.lines[t.cur.Y][dst:dst+size], t.lines[t.cur.Y][src:src+size])
|
||||
t.clear(t.cols-n, t.cur.Y, t.cols-1, t.cur.Y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,8 +741,8 @@ func (t *State) setTitle(title string) {
|
||||
t.title = title
|
||||
}
|
||||
|
||||
func (t *State) Size() (rows int, cols int) {
|
||||
return t.rows, t.cols
|
||||
func (t *State) Size() (cols, rows int) {
|
||||
return t.cols, t.rows
|
||||
}
|
||||
|
||||
func (t *State) String() string {
|
||||
@@ -719,8 +752,8 @@ func (t *State) String() string {
|
||||
var view []rune
|
||||
for y := 0; y < t.rows; y++ {
|
||||
for x := 0; x < t.cols; x++ {
|
||||
c, _, _ := t.Cell(x, y)
|
||||
view = append(view, c)
|
||||
attr := t.Cell(x, y)
|
||||
view = append(view, attr.Char)
|
||||
}
|
||||
view = append(view, '\n')
|
||||
}
|
||||
|
||||
242
vendor/github.com/hinshun/vt10x/str.go
generated
vendored
242
vendor/github.com/hinshun/vt10x/str.go
generated
vendored
@@ -1,6 +1,9 @@
|
||||
package vt10x
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -59,20 +62,77 @@ func (t *State) handleSTR() {
|
||||
|
||||
switch s.typ {
|
||||
case ']': // OSC - operating system command
|
||||
var p *string
|
||||
switch d := s.arg(0, 0); d {
|
||||
case 0, 1, 2:
|
||||
title := s.argString(1, "")
|
||||
if title != "" {
|
||||
t.setTitle(title)
|
||||
}
|
||||
case 10:
|
||||
if len(s.args) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
c := s.argString(1, "")
|
||||
p := &c
|
||||
if p != nil && *p == "?" {
|
||||
t.oscColorResponse(int(DefaultFG), 10)
|
||||
} else if err := t.setColorName(int(DefaultFG), p); err != nil {
|
||||
t.logf("invalid foreground color: %s\n", maybe(p))
|
||||
} else {
|
||||
// TODO: redraw
|
||||
}
|
||||
case 11:
|
||||
if len(s.args) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
c := s.argString(1, "")
|
||||
p := &c
|
||||
if p != nil && *p == "?" {
|
||||
t.oscColorResponse(int(DefaultBG), 11)
|
||||
} else if err := t.setColorName(int(DefaultBG), p); err != nil {
|
||||
t.logf("invalid cursor color: %s\n", maybe(p))
|
||||
} else {
|
||||
// TODO: redraw
|
||||
}
|
||||
// case 12:
|
||||
// if len(s.args) < 2 {
|
||||
// break
|
||||
// }
|
||||
|
||||
// c := s.argString(1, "")
|
||||
// p := &c
|
||||
// if p != nil && *p == "?" {
|
||||
// t.oscColorResponse(int(DefaultCursor), 12)
|
||||
// } else if err := t.setColorName(int(DefaultCursor), p); err != nil {
|
||||
// t.logf("invalid background color: %s\n", p)
|
||||
// } else {
|
||||
// // TODO: redraw
|
||||
// }
|
||||
case 4: // color set
|
||||
if len(s.args) < 3 {
|
||||
break
|
||||
}
|
||||
// setcolorname(s.arg(1, 0), s.argString(2, ""))
|
||||
|
||||
c := s.argString(2, "")
|
||||
p = &c
|
||||
fallthrough
|
||||
case 104: // color reset
|
||||
// TODO: complain about invalid color, redraw, etc.
|
||||
// setcolorname(s.arg(1, 0), nil)
|
||||
j := -1
|
||||
if len(s.args) > 1 {
|
||||
j = s.arg(1, 0)
|
||||
}
|
||||
if p != nil && *p == "?" { // report
|
||||
t.osc4ColorResponse(j)
|
||||
} else if err := t.setColorName(j, p); err != nil {
|
||||
if !(d == 104 && len(s.args) <= 1) {
|
||||
t.logf("invalid color j=%d, p=%s\n", j, maybe(p))
|
||||
}
|
||||
} else {
|
||||
// TODO: redraw
|
||||
}
|
||||
default:
|
||||
t.logf("unknown OSC command %d\n", d)
|
||||
// TODO: s.dump()
|
||||
@@ -92,3 +152,179 @@ func (t *State) handleSTR() {
|
||||
// t.str.dump()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *State) setColorName(j int, p *string) error {
|
||||
if !between(j, 0, 1<<24) {
|
||||
return fmt.Errorf("invalid color value %d", j)
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
// restore color
|
||||
delete(t.colorOverride, Color(j))
|
||||
} else {
|
||||
// set color
|
||||
r, g, b, err := parseColor(*p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.colorOverride[Color(j)] = Color(r<<16 | g<<8 | b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *State) oscColorResponse(j, num int) {
|
||||
if j < 0 {
|
||||
t.logf("failed to fetch osc color %d\n", j)
|
||||
return
|
||||
}
|
||||
|
||||
k, ok := t.colorOverride[Color(j)]
|
||||
if ok {
|
||||
j = int(k)
|
||||
}
|
||||
|
||||
r, g, b := rgb(j)
|
||||
t.w.Write([]byte(fmt.Sprintf("\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", num, r, r, g, g, b, b)))
|
||||
}
|
||||
|
||||
func (t *State) osc4ColorResponse(j int) {
|
||||
if j < 0 {
|
||||
t.logf("failed to fetch osc4 color %d\n", j)
|
||||
return
|
||||
}
|
||||
|
||||
k, ok := t.colorOverride[Color(j)]
|
||||
if ok {
|
||||
j = int(k)
|
||||
}
|
||||
|
||||
r, g, b := rgb(j)
|
||||
t.w.Write([]byte(fmt.Sprintf("\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", j, r, r, g, g, b, b)))
|
||||
}
|
||||
|
||||
func rgb(j int) (r, g, b int) {
|
||||
return (j >> 16) & 0xff, (j >> 8) & 0xff, j & 0xff
|
||||
}
|
||||
|
||||
var (
|
||||
RGBPattern = regexp.MustCompile(`^([\da-f]{1})\/([\da-f]{1})\/([\da-f]{1})$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$`)
|
||||
HashPattern = regexp.MustCompile(`[\da-f]`)
|
||||
)
|
||||
|
||||
func parseColor(p string) (r, g, b int, err error) {
|
||||
if len(p) == 0 {
|
||||
err = fmt.Errorf("empty color spec")
|
||||
return
|
||||
}
|
||||
|
||||
low := strings.ToLower(p)
|
||||
if strings.HasPrefix(low, "rgb:") {
|
||||
low = low[4:]
|
||||
sm := RGBPattern.FindAllStringSubmatch(low, -1)
|
||||
if len(sm) != 1 || len(sm[0]) == 0 {
|
||||
err = fmt.Errorf("invalid rgb color spec: %s", p)
|
||||
return
|
||||
}
|
||||
m := sm[0]
|
||||
|
||||
var base float64
|
||||
if len(m[1]) > 0 {
|
||||
base = 15
|
||||
} else if len(m[4]) > 0 {
|
||||
base = 255
|
||||
} else if len(m[7]) > 0 {
|
||||
base = 4095
|
||||
} else {
|
||||
base = 65535
|
||||
}
|
||||
|
||||
r64, err := strconv.ParseInt(firstNonEmpty(m[1], m[4], m[7], m[10]), 16, 0)
|
||||
if err != nil {
|
||||
return r, g, b, err
|
||||
}
|
||||
|
||||
g64, err := strconv.ParseInt(firstNonEmpty(m[2], m[5], m[8], m[11]), 16, 0)
|
||||
if err != nil {
|
||||
return r, g, b, err
|
||||
}
|
||||
|
||||
b64, err := strconv.ParseInt(firstNonEmpty(m[3], m[6], m[9], m[12]), 16, 0)
|
||||
if err != nil {
|
||||
return r, g, b, err
|
||||
}
|
||||
|
||||
r = int(math.Round(float64(r64) / base * 255))
|
||||
g = int(math.Round(float64(g64) / base * 255))
|
||||
b = int(math.Round(float64(b64) / base * 255))
|
||||
return r, g, b, nil
|
||||
} else if strings.HasPrefix(low, "#") {
|
||||
low = low[1:]
|
||||
m := HashPattern.FindAllString(low, -1)
|
||||
if !oneOf(len(m), []int{3, 6, 9, 12}) {
|
||||
err = fmt.Errorf("invalid hash color spec: %s", p)
|
||||
return
|
||||
}
|
||||
|
||||
adv := len(low) / 3
|
||||
for i := 0; i < 3; i++ {
|
||||
c, err := strconv.ParseInt(low[adv*i:adv*i+adv], 16, 0)
|
||||
if err != nil {
|
||||
return r, g, b, err
|
||||
}
|
||||
|
||||
var v int64
|
||||
switch adv {
|
||||
case 1:
|
||||
v = c << 4
|
||||
case 2:
|
||||
v = c
|
||||
case 3:
|
||||
v = c >> 4
|
||||
default:
|
||||
v = c >> 8
|
||||
}
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
r = int(v)
|
||||
case 1:
|
||||
g = int(v)
|
||||
case 2:
|
||||
b = int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
} else {
|
||||
err = fmt.Errorf("invalid color spec: %s", p)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func maybe(p *string) string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return *p
|
||||
}
|
||||
|
||||
func firstNonEmpty(strs ...string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
for _, str := range strs {
|
||||
if len(str) > 0 {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return strs[len(strs)-1]
|
||||
}
|
||||
|
||||
func oneOf(v int, is []int) bool {
|
||||
for _, i := range is {
|
||||
if v == i {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
89
vendor/github.com/hinshun/vt10x/vt.go
generated
vendored
Normal file
89
vendor/github.com/hinshun/vt10x/vt.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package vt10x
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Terminal represents the virtual terminal emulator.
|
||||
type Terminal interface {
|
||||
// View displays the virtual terminal.
|
||||
View
|
||||
|
||||
// Write parses input and writes terminal changes to state.
|
||||
io.Writer
|
||||
|
||||
// Parse blocks on read on pty or io.Reader, then parses sequences until
|
||||
// buffer empties. State is locked as soon as first rune is read, and unlocked
|
||||
// when buffer is empty.
|
||||
Parse(bf *bufio.Reader) error
|
||||
}
|
||||
|
||||
// View represents the view of the virtual terminal emulator.
|
||||
type View interface {
|
||||
// String dumps the virtual terminal contents.
|
||||
fmt.Stringer
|
||||
|
||||
// Size returns the size of the virtual terminal.
|
||||
Size() (cols, rows int)
|
||||
|
||||
// Resize changes the size of the virtual terminal.
|
||||
Resize(cols, rows int)
|
||||
|
||||
// Mode returns the current terminal mode.//
|
||||
Mode() ModeFlag
|
||||
|
||||
// Title represents the title of the console window.
|
||||
Title() string
|
||||
|
||||
// Cell returns the glyph containing the character code, foreground color, and
|
||||
// background color at position (x, y) relative to the top left of the terminal.
|
||||
Cell(x, y int) Glyph
|
||||
|
||||
// Cursor returns the current position of the cursor.
|
||||
Cursor() Cursor
|
||||
|
||||
// CursorVisible returns the visible state of the cursor.
|
||||
CursorVisible() bool
|
||||
|
||||
// Lock locks the state object's mutex.
|
||||
Lock()
|
||||
|
||||
// Unlock resets change flags and unlocks the state object's mutex.
|
||||
Unlock()
|
||||
}
|
||||
|
||||
type TerminalOption func(*TerminalInfo)
|
||||
|
||||
type TerminalInfo struct {
|
||||
w io.Writer
|
||||
cols, rows int
|
||||
}
|
||||
|
||||
func WithWriter(w io.Writer) TerminalOption {
|
||||
return func(info *TerminalInfo) {
|
||||
info.w = w
|
||||
}
|
||||
}
|
||||
|
||||
func WithSize(cols, rows int) TerminalOption {
|
||||
return func(info *TerminalInfo) {
|
||||
info.cols = cols
|
||||
info.rows = rows
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new virtual terminal emulator.
|
||||
func New(opts ...TerminalOption) Terminal {
|
||||
info := TerminalInfo{
|
||||
w: ioutil.Discard,
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&info)
|
||||
}
|
||||
return newTerminal(info)
|
||||
}
|
||||
82
vendor/github.com/hinshun/vt10x/vt_other.go
generated
vendored
82
vendor/github.com/hinshun/vt10x/vt_other.go
generated
vendored
@@ -6,47 +6,34 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// VT represents the virtual terminal emulator.
|
||||
type VT struct {
|
||||
dest *State
|
||||
rwc io.ReadWriteCloser
|
||||
br *bufio.Reader
|
||||
pty *os.File
|
||||
type terminal struct {
|
||||
*State
|
||||
}
|
||||
|
||||
// Create initializes a virtual terminal emulator with the target state
|
||||
// and io.ReadWriteCloser input.
|
||||
func Create(state *State, rwc io.ReadWriteCloser) (*VT, error) {
|
||||
t := &VT{
|
||||
dest: state,
|
||||
rwc: rwc,
|
||||
}
|
||||
t.init()
|
||||
return t, nil
|
||||
func newTerminal(info TerminalInfo) *terminal {
|
||||
t := &terminal{newState(info.w)}
|
||||
t.init(info.cols, info.rows)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *VT) init() {
|
||||
t.br = bufio.NewReader(t.rwc)
|
||||
t.dest.w = t.rwc
|
||||
t.dest.numlock = true
|
||||
t.dest.state = t.dest.parse
|
||||
t.dest.cur.attr.fg = DefaultFG
|
||||
t.dest.cur.attr.bg = DefaultBG
|
||||
t.Resize(80, 24)
|
||||
t.dest.reset()
|
||||
func (t *terminal) init(cols, rows int) {
|
||||
t.numlock = true
|
||||
t.state = t.parse
|
||||
t.cur.attr.fg = DefaultFG
|
||||
t.cur.attr.bg = DefaultBG
|
||||
t.Resize(cols, rows)
|
||||
t.reset()
|
||||
}
|
||||
|
||||
// Write parses input and writes terminal changes to state.
|
||||
func (t *VT) Write(p []byte) (int, error) {
|
||||
func (t *terminal) Write(p []byte) (int, error) {
|
||||
var written int
|
||||
r := bytes.NewReader(p)
|
||||
t.dest.lock()
|
||||
defer t.dest.unlock()
|
||||
t.lock()
|
||||
defer t.unlock()
|
||||
for {
|
||||
c, sz, err := r.ReadRune()
|
||||
if err != nil {
|
||||
@@ -61,51 +48,43 @@ func (t *VT) Write(p []byte) (int, error) {
|
||||
// not enough bytes for a full rune
|
||||
return written - 1, nil
|
||||
}
|
||||
t.dest.logln("invalid utf8 sequence")
|
||||
t.logln("invalid utf8 sequence")
|
||||
continue
|
||||
}
|
||||
t.dest.put(c)
|
||||
t.put(c)
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// Close closes the io.ReadWriteCloser.
|
||||
func (t *VT) Close() error {
|
||||
return t.rwc.Close()
|
||||
}
|
||||
|
||||
// Parse blocks on read on io.ReadWriteCloser, then parses sequences until
|
||||
// buffer empties. State is locked as soon as first rune is read, and unlocked
|
||||
// when buffer is empty.
|
||||
// TODO: add tests for expected blocking behavior
|
||||
func (t *VT) Parse() error {
|
||||
func (t *terminal) Parse(br *bufio.Reader) error {
|
||||
var locked bool
|
||||
defer func() {
|
||||
if locked {
|
||||
t.dest.unlock()
|
||||
t.unlock()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
c, sz, err := t.br.ReadRune()
|
||||
c, sz, err := br.ReadRune()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c == unicode.ReplacementChar && sz == 1 {
|
||||
t.dest.logln("invalid utf8 sequence")
|
||||
t.logln("invalid utf8 sequence")
|
||||
break
|
||||
}
|
||||
if !locked {
|
||||
t.dest.lock()
|
||||
t.lock()
|
||||
locked = true
|
||||
}
|
||||
|
||||
// put rune for parsing and update state
|
||||
t.dest.put(c)
|
||||
t.put(c)
|
||||
|
||||
// break if our buffer is empty, or if buffer contains an
|
||||
// incomplete rune.
|
||||
n := t.br.Buffered()
|
||||
if n == 0 || (n < 4 && !fullRuneBuffered(t.br)) {
|
||||
n := br.Buffered()
|
||||
if n == 0 || (n < 4 && !fullRuneBuffered(br)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -121,9 +100,8 @@ func fullRuneBuffered(br *bufio.Reader) bool {
|
||||
return utf8.FullRune(buf)
|
||||
}
|
||||
|
||||
// Resize reports new size to pty and updates state.
|
||||
func (t *VT) Resize(cols, rows int) {
|
||||
t.dest.lock()
|
||||
defer t.dest.unlock()
|
||||
_ = t.dest.resize(cols, rows)
|
||||
func (t *terminal) Resize(cols, rows int) {
|
||||
t.lock()
|
||||
defer t.unlock()
|
||||
_ = t.resize(cols, rows)
|
||||
}
|
||||
|
||||
79
vendor/github.com/hinshun/vt10x/vt_posix.go
generated
vendored
79
vendor/github.com/hinshun/vt10x/vt_posix.go
generated
vendored
@@ -10,41 +10,31 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// VT represents the virtual terminal emulator.
|
||||
type VT struct {
|
||||
dest *State
|
||||
rwc io.ReadWriteCloser
|
||||
br *bufio.Reader
|
||||
type terminal struct {
|
||||
*State
|
||||
}
|
||||
|
||||
// Create initializes a virtual terminal emulator with the target state
|
||||
// and io.ReadWriteCloser input.
|
||||
func Create(state *State, rwc io.ReadWriteCloser) (*VT, error) {
|
||||
t := &VT{
|
||||
dest: state,
|
||||
rwc: rwc,
|
||||
}
|
||||
t.init()
|
||||
return t, nil
|
||||
func newTerminal(info TerminalInfo) *terminal {
|
||||
t := &terminal{newState(info.w)}
|
||||
t.init(info.cols, info.rows)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *VT) init() {
|
||||
t.br = bufio.NewReader(t.rwc)
|
||||
t.dest.w = t.rwc
|
||||
t.dest.numlock = true
|
||||
t.dest.state = t.dest.parse
|
||||
t.dest.cur.attr.fg = DefaultFG
|
||||
t.dest.cur.attr.bg = DefaultBG
|
||||
t.Resize(80, 24)
|
||||
t.dest.reset()
|
||||
func (t *terminal) init(cols, rows int) {
|
||||
t.numlock = true
|
||||
t.state = t.parse
|
||||
t.cur.Attr.FG = DefaultFG
|
||||
t.cur.Attr.BG = DefaultBG
|
||||
t.Resize(cols, rows)
|
||||
t.reset()
|
||||
}
|
||||
|
||||
// Write parses input and writes terminal changes to state.
|
||||
func (t *VT) Write(p []byte) (int, error) {
|
||||
func (t *terminal) Write(p []byte) (int, error) {
|
||||
var written int
|
||||
r := bytes.NewReader(p)
|
||||
t.dest.lock()
|
||||
defer t.dest.unlock()
|
||||
t.lock()
|
||||
defer t.unlock()
|
||||
for {
|
||||
c, sz, err := r.ReadRune()
|
||||
if err != nil {
|
||||
@@ -59,51 +49,43 @@ func (t *VT) Write(p []byte) (int, error) {
|
||||
// not enough bytes for a full rune
|
||||
return written - 1, nil
|
||||
}
|
||||
t.dest.logln("invalid utf8 sequence")
|
||||
t.logln("invalid utf8 sequence")
|
||||
continue
|
||||
}
|
||||
t.dest.put(c)
|
||||
t.put(c)
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// Close closes the io.ReadWriteCloser.
|
||||
func (t *VT) Close() error {
|
||||
return t.rwc.Close()
|
||||
}
|
||||
|
||||
// Parse blocks on read on pty or io.ReadCloser, then parses sequences until
|
||||
// buffer empties. State is locked as soon as first rune is read, and unlocked
|
||||
// when buffer is empty.
|
||||
// TODO: add tests for expected blocking behavior
|
||||
func (t *VT) Parse() error {
|
||||
func (t *terminal) Parse(br *bufio.Reader) error {
|
||||
var locked bool
|
||||
defer func() {
|
||||
if locked {
|
||||
t.dest.unlock()
|
||||
t.unlock()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
c, sz, err := t.br.ReadRune()
|
||||
c, sz, err := br.ReadRune()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c == unicode.ReplacementChar && sz == 1 {
|
||||
t.dest.logln("invalid utf8 sequence")
|
||||
t.logln("invalid utf8 sequence")
|
||||
break
|
||||
}
|
||||
if !locked {
|
||||
t.dest.lock()
|
||||
t.lock()
|
||||
locked = true
|
||||
}
|
||||
|
||||
// put rune for parsing and update state
|
||||
t.dest.put(c)
|
||||
t.put(c)
|
||||
|
||||
// break if our buffer is empty, or if buffer contains an
|
||||
// incomplete rune.
|
||||
n := t.br.Buffered()
|
||||
if n == 0 || (n < 4 && !fullRuneBuffered(t.br)) {
|
||||
n := br.Buffered()
|
||||
if n == 0 || (n < 4 && !fullRuneBuffered(br)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -119,9 +101,8 @@ func fullRuneBuffered(br *bufio.Reader) bool {
|
||||
return utf8.FullRune(buf)
|
||||
}
|
||||
|
||||
// Resize reports new size to pty and updates state.
|
||||
func (t *VT) Resize(cols, rows int) {
|
||||
t.dest.lock()
|
||||
defer t.dest.unlock()
|
||||
_ = t.dest.resize(cols, rows)
|
||||
func (t *terminal) Resize(cols, rows int) {
|
||||
t.lock()
|
||||
defer t.unlock()
|
||||
_ = t.resize(cols, rows)
|
||||
}
|
||||
|
||||
28
vendor/github.com/stretchr/testify/require/doc.go
generated
vendored
28
vendor/github.com/stretchr/testify/require/doc.go
generated
vendored
@@ -1,28 +0,0 @@
|
||||
// Package require implements the same assertions as the `assert` package but
|
||||
// stops test execution when a test fails.
|
||||
//
|
||||
// Example Usage
|
||||
//
|
||||
// The following is a complete example using require in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// require.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
//
|
||||
// The `require` package have same global functions as in the `assert` package,
|
||||
// but instead of returning a boolean result they call `t.FailNow()`.
|
||||
//
|
||||
// Every assertion function also takes an optional string message as the final argument,
|
||||
// allowing custom error messages to be appended to the message the assertion method outputs.
|
||||
package require
|
||||
16
vendor/github.com/stretchr/testify/require/forward_requirements.go
generated
vendored
16
vendor/github.com/stretchr/testify/require/forward_requirements.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
package require
|
||||
|
||||
// Assertions provides assertion methods around the
|
||||
// TestingT interface.
|
||||
type Assertions struct {
|
||||
t TestingT
|
||||
}
|
||||
|
||||
// New makes a new Assertions object for the specified TestingT.
|
||||
func New(t TestingT) *Assertions {
|
||||
return &Assertions{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs"
|
||||
1879
vendor/github.com/stretchr/testify/require/require.go
generated
vendored
1879
vendor/github.com/stretchr/testify/require/require.go
generated
vendored
File diff suppressed because it is too large
Load Diff
6
vendor/github.com/stretchr/testify/require/require.go.tmpl
generated
vendored
6
vendor/github.com/stretchr/testify/require/require.go.tmpl
generated
vendored
@@ -1,6 +0,0 @@
|
||||
{{.Comment}}
|
||||
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
|
||||
if h, ok := t.(tHelper); ok { h.Helper() }
|
||||
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
|
||||
t.FailNow()
|
||||
}
|
||||
1471
vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
1471
vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
File diff suppressed because it is too large
Load Diff
5
vendor/github.com/stretchr/testify/require/require_forward.go.tmpl
generated
vendored
5
vendor/github.com/stretchr/testify/require/require_forward.go.tmpl
generated
vendored
@@ -1,5 +0,0 @@
|
||||
{{.CommentWithoutT "a"}}
|
||||
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
|
||||
if h, ok := a.t.(tHelper); ok { h.Helper() }
|
||||
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
|
||||
}
|
||||
29
vendor/github.com/stretchr/testify/require/requirements.go
generated
vendored
29
vendor/github.com/stretchr/testify/require/requirements.go
generated
vendored
@@ -1,29 +0,0 @@
|
||||
package require
|
||||
|
||||
// TestingT is an interface wrapper around *testing.T
|
||||
type TestingT interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
FailNow()
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
|
||||
// for table driven tests.
|
||||
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{})
|
||||
|
||||
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
|
||||
// for table driven tests.
|
||||
type ValueAssertionFunc func(TestingT, interface{}, ...interface{})
|
||||
|
||||
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
|
||||
// for table driven tests.
|
||||
type BoolAssertionFunc func(TestingT, bool, ...interface{})
|
||||
|
||||
// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
|
||||
// for table driven tests.
|
||||
type ErrorAssertionFunc func(TestingT, error, ...interface{})
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs"
|
||||
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@@ -286,7 +286,7 @@ github.com/hashicorp/errwrap
|
||||
github.com/hashicorp/go-multierror
|
||||
# github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/go-version
|
||||
# github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c
|
||||
# github.com/hinshun/vt10x v0.0.0-20220127042424-3ca73d0126d7
|
||||
## explicit
|
||||
github.com/hinshun/vt10x
|
||||
# github.com/hpcloud/tail v1.0.0
|
||||
@@ -312,6 +312,7 @@ github.com/kballard/go-shellquote
|
||||
# github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351
|
||||
github.com/kevinburke/ssh_config
|
||||
# github.com/kr/pty v1.1.5
|
||||
## explicit
|
||||
github.com/kr/pty
|
||||
# github.com/kubernetes-sigs/service-catalog v0.3.1
|
||||
## explicit
|
||||
@@ -585,9 +586,7 @@ github.com/spf13/cobra
|
||||
## explicit
|
||||
github.com/spf13/pflag
|
||||
# github.com/stretchr/testify v1.7.0
|
||||
## explicit
|
||||
github.com/stretchr/testify/assert
|
||||
github.com/stretchr/testify/require
|
||||
# github.com/tidwall/gjson v1.9.3
|
||||
## explicit
|
||||
github.com/tidwall/gjson
|
||||
|
||||
Reference in New Issue
Block a user