From 18f6c065e1c553c388a016b22a205dbde8882d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Fri, 21 Sep 2018 14:59:45 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E6=94=AF=E6=8C=81ssh=20webconsole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/climc/shell/webconsole.go | 9 +++ pkg/mcclient/modules/mod_webconsole.go | 4 ++ pkg/mcclient/options/webconsole.go | 5 ++ pkg/webconsole/command/ssh_command.go | 84 ++++++++++++++++++++++++++ pkg/webconsole/handlers.go | 29 ++++++++- pkg/webconsole/options/options.go | 8 ++- 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 pkg/webconsole/command/ssh_command.go diff --git a/cmd/climc/shell/webconsole.go b/cmd/climc/shell/webconsole.go index 0a784dc555..7658e8e158 100644 --- a/cmd/climc/shell/webconsole.go +++ b/cmd/climc/shell/webconsole.go @@ -65,4 +65,13 @@ func init() { handleResult(args.WebConsoleOptions, ret) return nil }) + + R(&o.WebConsoleServerOptions{}, "webconsole-server", "Connect server webconsole", func(s *mcclient.ClientSession, args *o.WebConsoleServerOptions) error { + ret, err := modules.WebConsole.DoServerConnect(s, args.ID) + if err != nil { + return err + } + handleResult(args.WebConsoleOptions, ret) + return nil + }) } diff --git a/pkg/mcclient/modules/mod_webconsole.go b/pkg/mcclient/modules/mod_webconsole.go index 0c1b79f985..ad0509a21b 100644 --- a/pkg/mcclient/modules/mod_webconsole.go +++ b/pkg/mcclient/modules/mod_webconsole.go @@ -69,3 +69,7 @@ func (m WebConsoleManager) DoK8sLogConnect( func (m WebConsoleManager) DoBaremetalConnect(s *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { return m.DoConnect(s, "baremetal", id, "", nil) } + +func (m WebConsoleManager) DoServerConnect(s *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { + return m.DoConnect(s, "server", id, "", nil) +} diff --git a/pkg/mcclient/options/webconsole.go b/pkg/mcclient/options/webconsole.go index 723c03da6f..d01e521985 100644 --- a/pkg/mcclient/options/webconsole.go +++ b/pkg/mcclient/options/webconsole.go @@ -36,3 +36,8 @@ type WebConsoleBaremetalOptions struct { func (opt *WebConsoleBaremetalOptions) Params() (*jsonutils.JSONDict, error) { return StructToParams(opt) } + +type WebConsoleServerOptions struct { + WebConsoleOptions + ID string `help:"Server id or name"` +} diff --git a/pkg/webconsole/command/ssh_command.go b/pkg/webconsole/command/ssh_command.go new file mode 100644 index 0000000000..1d6423e454 --- /dev/null +++ b/pkg/webconsole/command/ssh_command.go @@ -0,0 +1,84 @@ +package command + +import ( + "fmt" + "net" + "os/exec" + "strings" + + o "yunion.io/x/onecloud/pkg/webconsole/options" + "yunion.io/x/pkg/utils" +) + +type Metadata struct { + LoginAccount string `json:"login_account"` + LoginKey string `json:"login_key"` +} + +type SSHInfo struct { + ID string `json:"id"` + Metadata Metadata `json:"metadata"` + Eip string `json:"eip"` + IPs string `json:"ips"` + Keypaire string `json:"keypair"` + OsType string `json:"os_type"` +} + +type SSHtoolSol struct { + *BaseCommand + Info *SSHInfo +} + +func NewSSHtoolSolCommand(info *SSHInfo) (*SSHtoolSol, error) { + if info.IPs == "" { + return nil, fmt.Errorf("Empty server ip address") + } + if info.Metadata.LoginAccount == "" { + return nil, fmt.Errorf("Empty username") + } + if len(info.Keypaire) != 0 { + return nil, fmt.Errorf("Not support private_key login") + } + if info.OsType != "Linux" { + return nil, fmt.Errorf("Not support login for %s", info.OsType) + } + args := "" + if info.Eip != "" { + args = fmt.Sprintf("%s@%s", info.Metadata.LoginAccount, info.Eip) + } else { + for _, ip := range strings.Split(info.IPs, ",") { + conn, err := net.Dial("tcp", fmt.Sprintf("%s:22", ip)) + if err == nil { + args = fmt.Sprintf("%s@%s", info.Metadata.LoginAccount, ip) + break + } + defer conn.Close() + } + } + if len(args) == 0 { + return nil, fmt.Errorf("failed find usable connection ip address") + } + cmd := NewBaseCommand(o.Options.SSHtoolPath) + if info.Metadata.LoginKey != "" { + cmd := NewBaseCommand(o.Options.SSHtoolPath) + if passwd, err := utils.DescryptAESBase64(info.ID, info.Metadata.LoginKey); err != nil { + return nil, err + } else { + cmd.AppendArgs("-p", passwd) + } + } + cmd.AppendArgs(args) + tool := &SSHtoolSol{ + BaseCommand: cmd, + Info: info, + } + return tool, nil +} + +func (c *SSHtoolSol) GetCommand() *exec.Cmd { + return c.BaseCommand.GetCommand() +} + +func (c SSHtoolSol) GetProtocol() string { + return PROTOCOL_TTY +} diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index cd44f4f0c0..18eb95c89b 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -31,6 +31,7 @@ func InitHandlers(app *appsrv.Application) { app.AddHandler("POST", ApiPathPrefix+"k8s//shell", auth.Authenticate(handleK8sShell)) app.AddHandler("POST", ApiPathPrefix+"k8s//log", auth.Authenticate(handleK8sLog)) app.AddHandler("POST", ApiPathPrefix+"baremetal/", auth.Authenticate(handleBaremetalShell)) + app.AddHandler("POST", ApiPathPrefix+"server/", auth.Authenticate(handleServerShell)) } func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) { @@ -122,7 +123,7 @@ func fetchCloudEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) if userCred == nil { return nil, httperrors.NewUnauthorizedError("No token founded") } - s := auth.Client().NewSession(o.Options.Region, "", "internal", userCred, "") + s := auth.Client().NewSession(o.Options.Region, "", "internal", userCred, "v2") return &CloudEnv{ ClientSessin: s, Params: params, @@ -156,6 +157,32 @@ func handleK8sLog(ctx context.Context, w http.ResponseWriter, r *http.Request) { handleK8sCommand(ctx, w, r, command.NewPodLogCommand) } +func handleServerShell(ctx context.Context, w http.ResponseWriter, r *http.Request) { + env, err := fetchCloudEnv(ctx, w, r) + if err != nil { + httperrors.GeneralServerError(w, err) + return + } + serverId := env.Params[""] + ret, err := modules.Servers.Get(env.ClientSessin, serverId, jsonutils.Marshal(map[string]bool{"with_meta": true})) + if err != nil { + httperrors.GeneralServerError(w, err) + return + } + info := command.SSHInfo{} + err = ret.Unmarshal(&info) + if err != nil { + httperrors.GeneralServerError(w, err) + return + } + cmd, err := command.NewSSHtoolSolCommand(&info) + if err != nil { + httperrors.GeneralServerError(w, err) + return + } + handleCommandSession(cmd, w) +} + func handleBaremetalShell(ctx context.Context, w http.ResponseWriter, r *http.Request) { env, err := fetchCloudEnv(ctx, w, r) if err != nil { diff --git a/pkg/webconsole/options/options.go b/pkg/webconsole/options/options.go index 45e9235f00..b504dfa7af 100644 --- a/pkg/webconsole/options/options.go +++ b/pkg/webconsole/options/options.go @@ -11,7 +11,9 @@ var ( type WebConsoleOptions struct { cloudcommon.Options - ApiServer string `help:"API server url to handle websocket connection, usually with public access" default:"http://webconsole.yunion.io"` - KubectlPath string `help:"kubectl binary path used to connect k8s cluster" default:"/usr/bin/kubectl"` - IpmitoolPath string `help:"ipmitool binary path used to connect baremetal sol" default:"/usr/bin/ipmitool"` + ApiServer string `help:"API server url to handle websocket connection, usually with public access" default:"http://webconsole.yunion.io"` + KubectlPath string `help:"kubectl binary path used to connect k8s cluster" default:"/usr/bin/kubectl"` + IpmitoolPath string `help:"ipmitool binary path used to connect baremetal sol" default:"/usr/bin/ipmitool"` + SSHtoolPath string `help:"sshtool binary path used to connect server sol" default:"/usr/bin/ssh"` + SSHPasstoolPath string `help:"sshpass tool binary path used to connect server sol" default:"/usr/local/bin/sshpass"` } From 9673892de03c4e66b4917bcd2564cdb8f8f59281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Fri, 21 Sep 2018 15:12:59 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0admin=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/webconsole/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index 18eb95c89b..b470820f57 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -164,7 +164,7 @@ func handleServerShell(ctx context.Context, w http.ResponseWriter, r *http.Reque return } serverId := env.Params[""] - ret, err := modules.Servers.Get(env.ClientSessin, serverId, jsonutils.Marshal(map[string]bool{"with_meta": true})) + ret, err := modules.Servers.Get(env.ClientSessin, serverId, jsonutils.Marshal(map[string]bool{"with_meta": true, "admin": true})) if err != nil { httperrors.GeneralServerError(w, err) return From 03653febc708d5337024dc88aaaf2165c0d9c43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Thu, 27 Sep 2018 11:57:56 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E8=A1=A5=E5=85=85modules=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/mcclient/modules/modules.go | 1 + pkg/mcclient/modules/resource.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/pkg/mcclient/modules/modules.go b/pkg/mcclient/modules/modules.go index 1ba4171f98..571712a41e 100644 --- a/pkg/mcclient/modules/modules.go +++ b/pkg/mcclient/modules/modules.go @@ -102,6 +102,7 @@ type Manager interface { BatchPatchInContext(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctx Manager, ctxid string) []SubmitResult BatchPatchInContexts(session *mcclient.ClientSession, idlist []string, params jsonutils.JSONObject, ctxs []ManagerContext) []SubmitResult PerformAction(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) + PerformClassAction(session *mcclient.ClientSession, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) PerformActionInContext(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject, ctx Manager, ctxid string) (jsonutils.JSONObject, error) PerformActionInContexts(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) BatchPerformAction(session *mcclient.ClientSession, idlist []string, action string, params jsonutils.JSONObject) []SubmitResult diff --git a/pkg/mcclient/modules/resource.go b/pkg/mcclient/modules/resource.go index 473e38b67a..cb343484b2 100644 --- a/pkg/mcclient/modules/resource.go +++ b/pkg/mcclient/modules/resource.go @@ -351,6 +351,15 @@ func (this *ResourceManager) PerformActionInContexts(session *mcclient.ClientSes return this._post(session, path, this.params2Body(params), this.Keyword) } +func (this *ResourceManager) PerformClassAction(session *mcclient.ClientSession, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return this.PerformClassActionInContexts(session, action, params, nil) +} + +func (this *ResourceManager) PerformClassActionInContexts(session *mcclient.ClientSession, action string, params jsonutils.JSONObject, ctxs []ManagerContext) (jsonutils.JSONObject, error) { + path := fmt.Sprintf("/%s/%s", this.ContextPath(ctxs), url.PathEscape(action)) + return this._post(session, path, params, this.Keyword) +} + func (this *ResourceManager) BatchPerformAction(session *mcclient.ClientSession, idlist []string, action string, params jsonutils.JSONObject) []SubmitResult { return this.BatchPerformActionInContexts(session, idlist, action, params, nil) } From f80ebd9f91c9e2a167533cf73996795b4ee997ea Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 27 Sep 2018 17:07:33 +0800 Subject: [PATCH 04/13] Add baremetal agent support --- cmd/climc/shell/baremetalagents.go | 31 +++++++++++++++++++++ pkg/mcclient/modules/mod_baremetalagents.go | 15 ++++++++++ 2 files changed, 46 insertions(+) create mode 100644 cmd/climc/shell/baremetalagents.go create mode 100644 pkg/mcclient/modules/mod_baremetalagents.go diff --git a/cmd/climc/shell/baremetalagents.go b/cmd/climc/shell/baremetalagents.go new file mode 100644 index 0000000000..e5ff84b2cd --- /dev/null +++ b/cmd/climc/shell/baremetalagents.go @@ -0,0 +1,31 @@ +package shell + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + type BaremetalAgentListOptions struct { + options.BaseListOptions + } + R(&BaremetalAgentListOptions{}, "baremetal-agent-list", "List baremetal agent", func(s *mcclient.ClientSession, args *BaremetalAgentListOptions) error { + var params *jsonutils.JSONDict + { + var err error + params, err = args.BaseListOptions.Params() + if err != nil { + return err + + } + } + result, err := modules.Baremetalagents.List(s, params) + if err != nil { + return err + } + printList(result, modules.Baremetalagents.GetColumns(s)) + return nil + }) +} diff --git a/pkg/mcclient/modules/mod_baremetalagents.go b/pkg/mcclient/modules/mod_baremetalagents.go new file mode 100644 index 0000000000..7050e8f65d --- /dev/null +++ b/pkg/mcclient/modules/mod_baremetalagents.go @@ -0,0 +1,15 @@ +package modules + +var ( + Baremetalagents ResourceManager +) + +func init() { + Baremetalagents = NewComputeManager( + "baremetalagent", + "baremetalagents", + []string{"ID", "Name", "Access_ip", "Manager_URI", "Status"}, + []string{}, + ) + registerCompute(&Baremetalagents) +} From 3ff40b817808b437863c2532a70fcb54cf8e5baa Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 27 Sep 2018 18:02:14 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=EF=BC=9A=E4=BC=AA?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=99=9A=E6=8B=9F=E6=9C=BA=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99=EF=BC=8C=E5=A6=82=E6=9E=9C=E8=99=9A=E6=8B=9F=E6=9C=BA?= =?UTF-8?q?=E5=81=9C=E6=9C=BA=E5=A4=B1=E8=B4=A5=EF=BC=8C=E4=BC=9A=E7=9C=9F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=99=9A=E6=8B=9F=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/tasks/guest_delete_task.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/compute/tasks/guest_delete_task.go b/pkg/compute/tasks/guest_delete_task.go index 7b21109cda..5b5a8e5d51 100644 --- a/pkg/compute/tasks/guest_delete_task.go +++ b/pkg/compute/tasks/guest_delete_task.go @@ -2,6 +2,7 @@ package tasks import ( "context" + "fmt" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -42,12 +43,19 @@ func (self *GuestDeleteTask) OnGuestStopComplete(ctx context.Context, obj db.ISt } } log.Debugf("XXXXXXX Do real delete on guest ... XXXXXXX") - self.OnGuestStopCompleteFailed(ctx, guest, data) + self.doStartDeleteGuest(ctx, guest) } -func (self *GuestDeleteTask) OnGuestStopCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { +func (self *GuestDeleteTask) OnGuestStopCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) { guest := obj.(*models.SGuest) - guest.SetStatus(self.UserCred, models.VM_DELETING, "delete anyway") + guest.SetStatus(self.UserCred, models.VM_DELETE_FAIL, err.String()) + db.OpsLog.LogEvent(guest, db.ACT_DELOCATE_FAIL, err, self.UserCred) + self.SetStageFailed(ctx, fmt.Sprintf("stop failed %s", err.String())) +} + +func (self *GuestDeleteTask) doStartDeleteGuest(ctx context.Context, obj db.IStandaloneModel) { + guest := obj.(*models.SGuest) + guest.SetStatus(self.UserCred, models.VM_DELETING, "delete server after stop") db.OpsLog.LogEvent(guest, db.ACT_DELOCATING, nil, self.UserCred) self.StartDeleteGuest(ctx, guest) } @@ -92,6 +100,7 @@ func (self *GuestDeleteTask) OnGuestDeleteCompleteFailed(ctx context.Context, ob guest := obj.(*models.SGuest) guest.SetStatus(self.UserCred, models.VM_DELETE_FAIL, err.String()) db.OpsLog.LogEvent(guest, db.ACT_DELOCATE_FAIL, err, self.UserCred) + self.SetStageFailed(ctx, fmt.Sprintf("delete failed %s", err.String())) } func (self *GuestDeleteTask) OnGuestDeleteComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { From 873724b1fbdef79890367951896768b24fd849e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Thu, 27 Sep 2018 18:20:44 +0800 Subject: [PATCH 06/13] =?UTF-8?q?cloudprovider=E8=BF=94=E5=9B=9E=E8=99=9A?= =?UTF-8?q?=E6=9C=BA=E6=95=B0=E9=87=8F=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/cloudproviders.go | 9 ++++++++- pkg/mcclient/modules/mod_cloudproviders.go | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index d894456712..ec6905900d 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -70,6 +70,11 @@ func (self *SCloudprovider) ValidateDeleteCondition(ctx context.Context) error { return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) } +func (self *SCloudprovider) GetGuestCount() int { + sq := HostManager.Query("id").Equals("manager_id", self.Id) + return GuestManager.Query().In("host_id", sq).Count() +} + func (self *SCloudprovider) GetHostCount() int { return HostManager.Query().Equals("manager_id", self.Id).Count() } @@ -396,11 +401,12 @@ func (manager *SCloudproviderManager) FetchCloudproviderByIdOrName(providerId st } type SCloudproviderUsage struct { + GuestCount int HostCount int VpcCount int StorageCount int StorageCacheCount int - EipCount int + EipCount int } func (usage *SCloudproviderUsage) isEmpty() bool { @@ -424,6 +430,7 @@ func (usage *SCloudproviderUsage) isEmpty() bool { func (self *SCloudprovider) getUsage() *SCloudproviderUsage { usage := SCloudproviderUsage{} + usage.GuestCount = self.GetGuestCount() usage.HostCount = self.GetHostCount() usage.VpcCount = self.getVpcCount() usage.StorageCount = self.getStorageCount() diff --git a/pkg/mcclient/modules/mod_cloudproviders.go b/pkg/mcclient/modules/mod_cloudproviders.go index 7a38ba7b05..df24b3cc3a 100644 --- a/pkg/mcclient/modules/mod_cloudproviders.go +++ b/pkg/mcclient/modules/mod_cloudproviders.go @@ -6,7 +6,9 @@ var ( func init() { Cloudproviders = NewComputeManager("cloudprovider", "cloudproviders", - []string{"ID", "Name", "Enabled", "Status", "Access_url", "Account", "Last_sync", "Provider"}, + []string{"ID", "Name", "Enabled", "Status", "Access_url", "Account", + "Last_sync", "Provider", "guest_count", "host_count", "vpc_count", + "storage_count", "storage_cache_count", "eip_count"}, []string{}) registerCompute(&Cloudproviders) From 8eac303dbfa6c89a8b5ecb86bd7ffee764306ae6 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 27 Sep 2018 02:53:34 +0000 Subject: [PATCH 07/13] validators: ValidatorStruct: update input data with validated struct --- pkg/cloudcommon/validators/validators.go | 1 + pkg/cloudcommon/validators/validators_test.go | 116 ++++++++++-------- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/pkg/cloudcommon/validators/validators.go b/pkg/cloudcommon/validators/validators.go index 4348106f62..163f04a549 100644 --- a/pkg/cloudcommon/validators/validators.go +++ b/pkg/cloudcommon/validators/validators.go @@ -493,6 +493,7 @@ func (v *ValidatorStruct) Validate(data *jsonutils.JSONDict) error { return err } } + data.Set(v.Key, jsonutils.Marshal(v.Value)) return nil } diff --git a/pkg/cloudcommon/validators/validators_test.go b/pkg/cloudcommon/validators/validators_test.go index 508e4ea2e4..6045aa5ed1 100644 --- a/pkg/cloudcommon/validators/validators_test.go +++ b/pkg/cloudcommon/validators/validators_test.go @@ -546,19 +546,22 @@ func TestIPv4Validator(t *testing.T) { } type TestStruct struct { - F0 string - F1 int - F2 bool + Name string + F0 string + F1 int + F2 bool } type TestVStruct TestStruct func (v *TestVStruct) Validate(data *jsonutils.JSONDict) error { - if v.F2 { - return nil - } else { - return newInvalidValueError("F2", v.F0) + switch v.Name { + case "bad": + return newInvalidValueError("Name", v.Name) + case "setDefault": + v.Name = "defaultVal" } + return nil } func TestStructValidator(t *testing.T) { @@ -566,29 +569,7 @@ func TestStructValidator(t *testing.T) { *C Value interface{} } - defaultVal := &TestStruct{ - F0: "holy", - F1: 100, - F2: true, - } - var defaultValMiss *TestStruct - { - defaultValMissCopy := *defaultVal - defaultValMissCopy.F2 = false - defaultValMiss = &defaultValMissCopy - } - defaultValJsonStr := `{s: {"F0": "holy", "F1": 100, "F2": true}}` - defaultValJsonStrMiss := `{s: {"F0": "holy", "F1": 100}}` - defaultValJsonStrMore := `{s: {"F0": "holy", "F1": 100, "F2": true, "Foo": "bar"}}` - defaultVVal := (*TestVStruct)(defaultVal) - var defaultVValBad *TestVStruct - { - defaultVValBadCopy := *defaultVVal - defaultVValBadCopy.F2 = false - defaultVValBad = &defaultVValBadCopy - } - defaultVValJsonStrBad := `{s: {"F0": "holy", "F1": 100, "F2": false}}` cases := []*StructC{ { C: &C{ @@ -612,47 +593,82 @@ func TestStructValidator(t *testing.T) { }, { C: &C{ - Name: "valid", - In: defaultValJsonStr, - Out: defaultValJsonStr, - ValueWant: defaultVal, + Name: "valid", + In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`, + Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`, + ValueWant: &TestStruct{ + F0: "holy", + F1: 100, + F2: true, + }, }, Value: &TestStruct{}, }, { C: &C{ - Name: "valid (missing fields)", - In: defaultValJsonStrMiss, - Out: defaultValJsonStrMiss, - ValueWant: defaultValMiss, + Name: "valid (missing fields)", + In: `{s: {"F0": "holy", "F1": 100}}`, + Out: `{s: {"f0": "holy", "f1": 100, "f2": false}}`, + ValueWant: &TestStruct{ + F0: "holy", + F1: 100, + F2: false, + }, }, Value: &TestStruct{}, }, { C: &C{ - Name: "valid (more fields)", - In: defaultValJsonStrMore, - Out: defaultValJsonStrMore, - ValueWant: defaultVal, + Name: "valid (more fields)", + In: `{s: {"F0": "holy", "F1": 100, "F2": true, "Foo": "bar"}}`, + Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`, + ValueWant: &TestStruct{ + F0: "holy", + F1: 100, + F2: true, + }, }, Value: &TestStruct{}, }, { C: &C{ - Name: "valid (struct says valid)", - In: defaultValJsonStr, - Out: defaultValJsonStr, - ValueWant: defaultVVal, + Name: "valid (struct says valid)", + In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`, + Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`, + ValueWant: &TestVStruct{ + F0: "holy", + F1: 100, + F2: true, + }, }, Value: &TestVStruct{}, }, { C: &C{ - Name: "invalid (struct says invalid)", - In: defaultVValJsonStrBad, - Out: defaultVValJsonStrBad, - ValueWant: defaultVValBad, - Err: ERR_INVALID_VALUE, + Name: "valid (with default initialized)", + In: `{s: {Name: "setDefault", "F0": "holy", "F1": 100, "F2": false}}`, + Out: `{s: {name: "defaultVal", "f0": "holy", "f1": 100, "f2": false}}`, + ValueWant: &TestVStruct{ + Name: "defaultVal", + F0: "holy", + F1: 100, + F2: false, + }, + }, + Value: &TestVStruct{}, + }, + { + C: &C{ + Name: "invalid (struct says invalid)", + In: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`, + Out: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`, + ValueWant: &TestVStruct{ + Name: "bad", + F0: "holy", + F1: 100, + F2: false, + }, + Err: ERR_INVALID_VALUE, }, Value: &TestVStruct{}, }, From eb95ee71562c287c915cf4f4402bc71604bce3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Sat, 29 Sep 2018 11:50:13 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0region=E5=AF=B9guest?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/cloudregions.go | 20 ++++++++++++++++++++ pkg/mcclient/modules/mod_cloudregions.go | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkg/compute/models/cloudregions.go b/pkg/compute/models/cloudregions.go index 44a1901655..6cdf658d3e 100644 --- a/pkg/compute/models/cloudregions.go +++ b/pkg/compute/models/cloudregions.go @@ -3,6 +3,7 @@ package models import ( "context" "database/sql" + "time" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -70,6 +71,23 @@ func (self *SCloudregion) GetZoneCount() int { } } +func (self *SCloudregion) GetGuestCount(increment bool) int { + zoneTable := ZoneManager.Query("id").Equals("cloudregion_id", self.Id) + if self.Id == "default" { + zoneTable = ZoneManager.Query("id").Filter(sqlchemy.OR(sqlchemy.IsNull(zoneTable.Field("cloudregion_id")), + sqlchemy.IsEmpty(zoneTable.Field("cloudregion_id")), + sqlchemy.Equals(zoneTable.Field("cloudregion_id"), self.Id))) + } + sq := HostManager.Query("id").In("zone_id", zoneTable) + query := GuestManager.Query().In("host_id", sq) + if increment { + year, month, _ := time.Now().UTC().Date() + startOfMonth := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) + query.GE("created_at", startOfMonth) + } + return query.Count() +} + func (self *SCloudregion) GetVpcCount() int { vpcs := VpcManager.Query() if self.Id == "default" { @@ -84,6 +102,8 @@ func (self *SCloudregion) GetVpcCount() int { func (self *SCloudregion) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { extra.Add(jsonutils.NewInt(int64(self.GetVpcCount())), "vpc_count") extra.Add(jsonutils.NewInt(int64(self.GetZoneCount())), "zone_count") + extra.Add(jsonutils.NewInt(int64(self.GetGuestCount(false))), "guest_count") + extra.Add(jsonutils.NewInt(int64(self.GetGuestCount(true))), "guest_increment_count") return extra } diff --git a/pkg/mcclient/modules/mod_cloudregions.go b/pkg/mcclient/modules/mod_cloudregions.go index 6b3be4119d..ae41321e22 100644 --- a/pkg/mcclient/modules/mod_cloudregions.go +++ b/pkg/mcclient/modules/mod_cloudregions.go @@ -6,7 +6,8 @@ var ( func init() { Cloudregions = NewComputeManager("cloudregion", "cloudregions", - []string{"ID", "Name", "Enabled", "Status", "Provider", "Latitude", "Longitude"}, + []string{"ID", "Name", "Enabled", "Status", "Provider", "Latitude", "Longitude", + "vpc_count", "zone_count", "guest_count", "guest_increment_count"}, []string{}) registerCompute(&Cloudregions) From 1f13848418c2f7eb342beab06eb63193caf879aa Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Sat, 29 Sep 2018 12:19:48 +0800 Subject: [PATCH 09/13] fix: climc prompt for macos --- Gopkg.lock | 6 +- Gopkg.toml | 2 +- .../github.com/c-bata/go-prompt/CHANGELOG.md | 23 +- .../c-bata/go-prompt/DEVELOPER_GUIDE.md | 155 ++++++++ vendor/github.com/c-bata/go-prompt/Gopkg.lock | 14 +- vendor/github.com/c-bata/go-prompt/Gopkg.toml | 4 - vendor/github.com/c-bata/go-prompt/Makefile | 5 - vendor/github.com/c-bata/go-prompt/README.md | 62 ++-- vendor/github.com/c-bata/go-prompt/bisect.go | 27 ++ vendor/github.com/c-bata/go-prompt/buffer.go | 60 ++-- .../github.com/c-bata/go-prompt/completion.go | 47 +-- .../{output.go => console_interface.go} | 69 ++-- .../github.com/c-bata/go-prompt/document.go | 309 ++-------------- vendor/github.com/c-bata/go-prompt/emacs.go | 1 - vendor/github.com/c-bata/go-prompt/input.go | 186 ++-------- .../c-bata/go-prompt/input_posix.go | 128 ------- .../c-bata/go-prompt/input_windows.go | 94 ----- vendor/github.com/c-bata/go-prompt/key.go | 3 - .../github.com/c-bata/go-prompt/key_bind.go | 39 +- .../c-bata/go-prompt/key_bind_func.go | 48 --- vendor/github.com/c-bata/go-prompt/option.go | 34 +- .../c-bata/go-prompt/output_posix.go | 35 -- .../c-bata/go-prompt/output_vt100.go | 333 ----------------- .../c-bata/go-prompt/output_windows.go | 36 -- .../c-bata/go-prompt/posix_input.go | 265 ++++++++++++++ .../c-bata/go-prompt/posix_output.go | 335 ++++++++++++++++++ vendor/github.com/c-bata/go-prompt/prompt.go | 98 ++--- vendor/github.com/c-bata/go-prompt/render.go | 94 ++--- .../github.com/c-bata/go-prompt/shortcut.go | 43 --- .../c-bata/go-prompt/windows_input.go | 214 +++++++++++ .../c-bata/go-prompt/windows_output.go | 333 +++++++++++++++++ 31 files changed, 1591 insertions(+), 1511 deletions(-) create mode 100644 vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md create mode 100644 vendor/github.com/c-bata/go-prompt/bisect.go rename vendor/github.com/c-bata/go-prompt/{output.go => console_interface.go} (58%) delete mode 100644 vendor/github.com/c-bata/go-prompt/input_posix.go delete mode 100644 vendor/github.com/c-bata/go-prompt/input_windows.go delete mode 100644 vendor/github.com/c-bata/go-prompt/key_bind_func.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_posix.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_vt100.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_windows.go create mode 100644 vendor/github.com/c-bata/go-prompt/posix_input.go create mode 100644 vendor/github.com/c-bata/go-prompt/posix_output.go delete mode 100644 vendor/github.com/c-bata/go-prompt/shortcut.go create mode 100644 vendor/github.com/c-bata/go-prompt/windows_input.go create mode 100644 vendor/github.com/c-bata/go-prompt/windows_output.go diff --git a/Gopkg.lock b/Gopkg.lock index 465f2beb19..a7bb1a3f9f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -79,12 +79,12 @@ version = "v0.5.0" [[projects]] - digest = "1:a2486f94bda33b301614ff5cfce102b7eed4c923fa650c2b83dba1fcf2117466" + digest = "1:1659cb76cbd08a29826688d006e7a3d9279d6ca8a12155acb7b20164958987d3" name = "github.com/c-bata/go-prompt" packages = ["."] pruneopts = "UT" - revision = "c52492ff1b386e5c0ba5271b5eaad165fab09eca" - version = "v0.2.2" + revision = "e99fbc797b795e0a7a94affc8d44f6a0350d85f0" + version = "v0.2.1" [[projects]] digest = "1:40098afbdd06a76dee4b6bcb85a22fed81ac9d2ebaf775c91e558c80f57aaff1" diff --git a/Gopkg.toml b/Gopkg.toml index 1762493a14..d81d0ee99f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,7 +27,7 @@ [[constraint]] name = "github.com/c-bata/go-prompt" - version = "0.2.2" + version = "=0.2.1" [[constraint]] branch = "master" diff --git a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md index a1fcb273be..ac4b02dc1f 100644 --- a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md +++ b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md @@ -1,31 +1,10 @@ # Change Log -## v0.3.0 (2018/??/??) - -next release. - -## v0.2.2 (2018/06/28) - -### What's new? - -* Support CJK(Chinese, Japanese and Korean) and Cyrillic characters. -* Add OptionCompletionWordSeparator(x string) to customize insertion points for completions. - * To support this, text query functions by arbitrary word separator are added in Document (please see [here](https://github.com/c-bata/go-prompt/pull/79) for more details). -* Add FilePathCompleter to complete file path on your system. -* Add option to customize ascii code key bindings. -* Add GetWordAfterCursor method in Document. - -### Removed or Deprecated - -* SetColor method in ConsoleWriter is deprecated. Please use SetDisplayAttributes instead. -* prompt.Choose shortcut function is deprecated. - ## v0.2.1 (2018/02/14) ### What's New? -* ~~It seems that windows support is almost perfect.~~ - * A critical bug is found :( When you change a terminal window size, the layout will be broken because current implementation cannot catch signal for updating window size on Windows. +* It seems that windows support is almost perfect. ### Fixed diff --git a/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md b/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md new file mode 100644 index 0000000000..44f2c55bd2 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md @@ -0,0 +1,155 @@ +# Developer Guide + +## Getting Started + +The most simple example is below. + +```go +package main + +import ( + "fmt" + + "github.com/c-bata/go-prompt" +) + +// executor executes command and print the output. +func executor(in string) { + fmt.Println("Your input: " + in) +} + +// completer returns the completion items from user input. +func completer(d prompt.Document) []prompt.Suggest { + s := []prompt.Suggest{ + {Text: "users", Description: "user table"}, + {Text: "sites", Description: "sites table"}, + {Text: "articles", Description: "articles table"}, + {Text: "comments", Description: "comments table"}, + } + return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) +} + +func main() { + p := prompt.New( + executor, + completer, + prompt.OptionPrefix(">>> "), + prompt.OptionTitle("sql-prompt"), + ) + p.Run() +} +``` + +If you want to create CLI using go-prompt, I recommend you to look at the [source code of kube-prompt](https://github.com/c-bata/kube-prompt). +It is the most practical example. + + +## Options + +go-prompt has many color options. +It is difficult to describe by text. So please see below figure: + +![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png) + +* **OptionPrefixTextColor(prompt.Color)** : default `prompt.Blue` +* **OptionPrefixBackgroundColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionInputTextColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionInputBGColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionPreviewSuggestionTextColor(prompt.Color)** : default `prompt.Green` +* **OptionPreviewSuggestionBGColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionSuggestionTextColor(prompt.Color)** : default `prompt.White` +* **OptionSuggestionBGColor(prompt.Color)** : default `prompt.Cyan` +* **OptionSelectedSuggestionTextColor(prompt.Color)** : `default prompt.Black` +* **OptionSelectedSuggestionBGColor(prompt.Color)** : `default prompt.DefaultColor` +* **OptionDescriptionTextColor(prompt.Color)** : default `prompt.Black` +* **OptionDescriptionBGColor(prompt.Color)** : default `prompt.Turquoise` +* **OptionSelectedDescriptionTextColor(prompt.Color)** : default `prompt.White` +* **OptionSelectedDescriptionBGColor(prompt.Color)** : default `prompt.Cyan` +* **OptionScrollbarThumbColor** : `prompt.DarkGray` +* **OptionScrollbarBGColor** : `prompt.Cyan` + +**Other Options** + +#### `OptionTitle(string)` : default `""` +Option to set a title that wll be displayed on the header bar of terminal. + +#### `OptionHistory([]string)` : default `[]string{}` +Option to set history. + +#### `OptionPrefix(string)` : default `"> "` +Option to set prefix string. + +#### `OptionLivePrefix(func() (prefix string, useLivePrefix bool))` : default `nil` +Option to set a callback function for updating prefix string dynamically. + +#### `OptionMaxSuggestions(x uint16)` : default `6` +The max number of displayed suggestions. + +#### `OptionParser(prompt.ConsoleParser)` : default `VT100Parser` +To set a custom ConsoleParser object. +The argument should implement ConsoleParser interface. + +#### `OptionWriter(prompt.ConsoleWriter)` : default `VT100Writer` +To set a custom ConsoleWriter object. +The argument should implement ConsoleWriter interface. + +#### `SwitchKeyBindMode(prompt.KeyBindMode)` : default `prompt.EmacsKeyBindMode` +To set a key bind mode. + +#### `OptionAddKeyBind(...KeyBind)` : default `[]KeyBind{}` +To set a custom key bind. + +## Architecture of go-prompt + +*Caution: This section is WIP.* + +This is a short description of go-prompt implementation. +go-prompt consists of three parts. + +1. Input parser +2. Emulate user input with Buffer object. +3. Render buffer object. + +### Input Parser + +![input-parser animation](https://github.com/c-bata/assets/raw/master/go-prompt/input-parser.gif) + +Input Parser only supports vt100-compatible console now. + +* Set as the raw mode. +* Read a standard input. +* Parse to byte array + +### Emulate user input + +go-prompt contains Buffer class. +It represents input state by handling a key input by user. + +`Buffer` object has text and cursor position. + +**TODO prepare the sample of buffer** + +```go +package main + +import "github.com/c-bata/go-prompt" + +func main() { + b := prompt.NewBuffer() + ... wip +} +``` + +### Renderer + +`Renderer` object renders a buffer object. + +**TODO prepare the sample of brender** + +```go +package main +``` + +the output is below: + +**TODO prepare a screen shot** diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.lock b/vendor/github.com/c-bata/go-prompt/Gopkg.lock index 1b6866bfcb..0228094527 100644 --- a/vendor/github.com/c-bata/go-prompt/Gopkg.lock +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.lock @@ -13,33 +13,27 @@ revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" -[[projects]] - branch = "master" - name = "github.com/mattn/go-runewidth" - packages = ["."] - revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" - [[projects]] branch = "master" name = "github.com/mattn/go-tty" packages = ["."] - revision = "931426f7535ac39720c8909d70ece5a41a2502a6" + revision = "c1750293025292316a611ae8f632d9791515e51c" [[projects]] branch = "master" name = "github.com/pkg/term" packages = ["termios"] - revision = "cda20d4ac917ad418d86e151eff439648b06185b" + revision = "b1f72af2d63057363398bec5873d16a98b453312" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1" + revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d6a0ea9e49092cfd8cb3d6077c97a938de7c39195b83828dae2a0befdd207ffd" + inputs-digest = "6c442f55617e93df75aa1ee2fd2b36cfab23003fded4723be56c6b6fd0545c56" solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.toml b/vendor/github.com/c-bata/go-prompt/Gopkg.toml index 903f53b251..fe31c575ad 100644 --- a/vendor/github.com/c-bata/go-prompt/Gopkg.toml +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.toml @@ -32,7 +32,3 @@ [[constraint]] branch = "master" name = "github.com/pkg/term" - -[[constraint]] - branch = "master" - name = "github.com/mattn/go-runewidth" diff --git a/vendor/github.com/c-bata/go-prompt/Makefile b/vendor/github.com/c-bata/go-prompt/Makefile index 3cb6edfe63..caf263cabc 100644 --- a/vendor/github.com/c-bata/go-prompt/Makefile +++ b/vendor/github.com/c-bata/go-prompt/Makefile @@ -21,11 +21,6 @@ lint: ## Run golint and go vet. test: ## Run the tests. @go test . -.PHONY: coverage -cover: ## Run the tests. - @go test -coverprofile=coverage.o - @go tool cover -func=coverage.o - .PHONY: race-test race-test: ## Checking the race condition. @go test -race . diff --git a/vendor/github.com/c-bata/go-prompt/README.md b/vendor/github.com/c-bata/go-prompt/README.md index a949b8b9d8..e66ae425e0 100644 --- a/vendor/github.com/c-bata/go-prompt/README.md +++ b/vendor/github.com/c-bata/go-prompt/README.md @@ -1,10 +1,7 @@ # go-prompt -[![Go Report Card](https://goreportcard.com/badge/github.com/c-bata/go-prompt)](https://goreportcard.com/report/github.com/c-bata/go-prompt) -![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square) - -A library for building powerful interactive prompts inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit), -making it easier to build cross-platform command line tools using Go. +Library for building a powerful interactive prompt, inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit). +Easy building a multi-platform binary of the command line tools because written in Golang. ```go package main @@ -30,15 +27,15 @@ func main() { } ``` + #### Projects using go-prompt * [c-bata/kube-prompt : An interactive kubernetes client featuring auto-complete written in Go.](https://github.com/c-bata/kube-prompt) * [rancher/cli : The Rancher Command Line Interface (CLI)is a unified tool to manage your Rancher server](https://github.com/rancher/cli) -* [kubicorn/kubicorn : Simple, cloud native infrastructure for Kubernetes.](https://github.com/kubicorn/kubicorn) +* [kris-nova/kubicorn : Simple. Cloud Native. Kubernetes. Infrastructure.](https://github.com/kris-nova/kubicorn) * [cch123/asm-cli : Interactive shell of assembly language(X86/X64) based on unicorn and rasm2](https://github.com/cch123/asm-cli) * [ktr0731/evans : more expressive universal gRPC client](https://github.com/ktr0731/evans) -* [CrushedPixel/moshpit: A Command-line tool for datamoshing.](https://github.com/CrushedPixel/moshpit) -* (If you create a CLI utility using go-prompt and want your own project to be listed here, please submit a GitHub issue.) +* (If you create a CLI using go-prompt and want your own project to be listed here, Please submit a Github Issue.) ## Features @@ -50,52 +47,54 @@ func main() { ### Flexible options -go-prompt provides many options. Please check [option section of GoDoc](https://godoc.org/github.com/c-bata/go-prompt#Option) for more details. +go-prompt provides many options. All options are listed in [Developer Guide](./DEVELOPER_GUIDE.md). [![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png)](#flexible-options) ### Keyboard Shortcuts -Emacs-like keyboard shortcuts are available by default (these also are the default shortcuts in Bash shell). +Emacs-like keyboard shortcut is available by default (it's also default shortcuts in Bash shell). You can customize and expand these shortcuts. [![keyboard shortcuts](https://github.com/c-bata/assets/raw/master/go-prompt/keyboard-shortcuts.gif)](#keyboard-shortcuts) -Key Binding | Description ----------------------|--------------------------------------------------------- -Ctrl + A | Go to the beginning of the line (Home) -Ctrl + E | Go to the end of the line (End) -Ctrl + P | Previous command (Up arrow) -Ctrl + N | Next command (Down arrow) -Ctrl + F | Forward one character -Ctrl + B | Backward one character -Ctrl + D | Delete character under the cursor -Ctrl + H | Delete character before the cursor (Backspace) -Ctrl + W | Cut the word before the cursor to the clipboard -Ctrl + K | Cut the line after the cursor to the clipboard -Ctrl + U | Cut the line before the cursor to the clipboard -Ctrl + L | Clear the screen +KeyBinding | Description +--------------------|--------------------------------------------------------- +Ctrl + A | Go to the beginning of the line (Home) +Ctrl + E | Go to the End of the line (End) +Ctrl + P | Previous command (Up arrow) +Ctrl + N | Next command (Down arrow) +Ctrl + F | Forward one character +Ctrl + B | Backward one character +Ctrl + D | Delete character under the cursor +Ctrl + H | Delete character before the cursor (Backspace) +Ctrl + W | Cut the Word before the cursor to the clipboard. +Ctrl + K | Cut the Line after the cursor to the clipboard. +Ctrl + U | Cut/delete the Line before the cursor to the clipboard. +Ctrl + L | Clear the screen ### History -You can use Up arrow and Down arrow to walk through the history of commands executed. +You can use up-arrow and down-arrow to walk through the history of commands executed. [![History](https://github.com/c-bata/assets/raw/master/go-prompt/history.gif)](#history) + ### Multiple platform support -We have confirmed go-prompt works fine in the following terminals: +We confirmed following terminals * iTerm2 (macOS) * Terminal.app (macOS) * Command Prompt (Windows) -* gnome-terminal (Ubuntu) +* GNU Terminal (Ubuntu) + ## Links +* [Developer Guide](./DEVELOPER_GUIDE.md). * [Change Log](./CHANGELOG.md) -* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt) -* [gocover.io](https://gocover.io/github.com/c-bata/go-prompt) +* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt). ## Author @@ -105,6 +104,7 @@ Masashi Shibata * Github: [@c-bata](https://github.com/c-bata/) * Facebook: [Masashi Shibata](https://www.facebook.com/masashi.cbata) -## License +## LICENSE + +This software is licensed under the MIT License (See [LICENSE](./LICENSE) ). -This software is licensed under the MIT license, see [LICENSE](./LICENSE) for more information. diff --git a/vendor/github.com/c-bata/go-prompt/bisect.go b/vendor/github.com/c-bata/go-prompt/bisect.go new file mode 100644 index 0000000000..2bcef3a5be --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/bisect.go @@ -0,0 +1,27 @@ +package prompt + +import "sort" + +// BisectLeft to Locate the insertion point for v in a to maintain sorted order. +func BisectLeft(a []int, v int) int { + return bisectLeftRange(a, v, 0, len(a)) +} + +func bisectLeftRange(a []int, v int, lo, hi int) int { + s := a[lo:hi] + return sort.Search(len(s), func(i int) bool { + return s[i] >= v + }) +} + +// BisectRight to Locate the insertion point for v in a to maintain sorted order. +func BisectRight(a []int, v int) int { + return bisectRightRange(a, v, 0, len(a)) +} + +func bisectRightRange(a []int, v int, lo, hi int) int { + s := a[lo:hi] + return sort.Search(len(s), func(i int) bool { + return s[i] > v + }) +} diff --git a/vendor/github.com/c-bata/go-prompt/buffer.go b/vendor/github.com/c-bata/go-prompt/buffer.go index 6c29b461e3..929d2fcecb 100644 --- a/vendor/github.com/c-bata/go-prompt/buffer.go +++ b/vendor/github.com/c-bata/go-prompt/buffer.go @@ -9,7 +9,7 @@ import ( type Buffer struct { workingLines []string // The working lines. Similar to history workingIndex int - cursorPosition int + CursorPosition int cacheDocument *Document preferredColumn int // Remember the original column for the next up/down movement. } @@ -23,25 +23,19 @@ func (b *Buffer) Text() string { func (b *Buffer) Document() (d *Document) { if b.cacheDocument == nil || b.cacheDocument.Text != b.Text() || - b.cacheDocument.cursorPosition != b.cursorPosition { + b.cacheDocument.CursorPosition != b.CursorPosition { b.cacheDocument = &Document{ Text: b.Text(), - cursorPosition: b.cursorPosition, + CursorPosition: b.CursorPosition, } } return b.cacheDocument } -// DisplayCursorPosition returns the cursor position on rendered text on terminal emulators. -// So if Document is "日本(cursor)語", DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. -func (b *Buffer) DisplayCursorPosition() int { - return b.Document().DisplayCursorPosition() -} - // InsertText insert string from current line. func (b *Buffer) InsertText(v string, overwrite bool, moveCursor bool) { or := []rune(b.Text()) - oc := b.cursorPosition + oc := b.CursorPosition if overwrite { overwritten := string(or[oc : oc+len(v)]) @@ -55,15 +49,15 @@ func (b *Buffer) InsertText(v string, overwrite bool, moveCursor bool) { } if moveCursor { - b.cursorPosition += len([]rune(v)) + b.CursorPosition += len([]rune(v)) } } -// SetText method to set text and update cursorPosition. +// SetText method to set text and update CursorPosition. // (When doing this, make sure that the cursor_position is valid for this text. // text/cursor_position should be consistent at any time, otherwise set a Document instead.) func (b *Buffer) setText(v string) { - if b.cursorPosition > len([]rune(v)) { + if b.CursorPosition > len([]rune(v)) { log.Print("[ERROR] The length of input value should be shorter than the position of cursor.") } o := b.workingLines[b.workingIndex] @@ -78,11 +72,11 @@ func (b *Buffer) setText(v string) { // Set cursor position. Return whether it changed. func (b *Buffer) setCursorPosition(p int) { - o := b.cursorPosition + o := b.CursorPosition if p > 0 { - b.cursorPosition = p + b.CursorPosition = p } else { - b.cursorPosition = 0 + b.CursorPosition = 0 } if p != o { // Cursor position is changed. @@ -92,21 +86,21 @@ func (b *Buffer) setCursorPosition(p int) { func (b *Buffer) setDocument(d *Document) { b.cacheDocument = d - b.setCursorPosition(d.cursorPosition) // Call before setText because setText check the relation between cursorPosition and line length. + b.setCursorPosition(d.CursorPosition) // Call before setText because setText check the relation between cursorPosition and line length. b.setText(d.Text) } // CursorLeft move to left on the current line. func (b *Buffer) CursorLeft(count int) { l := b.Document().GetCursorLeftPosition(count) - b.cursorPosition += l + b.CursorPosition += l return } // CursorRight move to right on the current line. func (b *Buffer) CursorRight(count int) { l := b.Document().GetCursorRightPosition(count) - b.cursorPosition += l + b.CursorPosition += l return } @@ -117,7 +111,7 @@ func (b *Buffer) CursorUp(count int) { if b.preferredColumn == -1 { // -1 means nil orig = b.Document().CursorPositionCol() } - b.cursorPosition += b.Document().GetCursorUpPosition(count, orig) + b.CursorPosition += b.Document().GetCursorUpPosition(count, orig) // Remember the original column for the next up/down movement. b.preferredColumn = orig @@ -130,7 +124,7 @@ func (b *Buffer) CursorDown(count int) { if b.preferredColumn == -1 { // -1 means nil orig = b.Document().CursorPositionCol() } - b.cursorPosition += b.Document().GetCursorDownPosition(count, orig) + b.CursorPosition += b.Document().GetCursorDownPosition(count, orig) // Remember the original column for the next up/down movement. b.preferredColumn = orig @@ -143,15 +137,15 @@ func (b *Buffer) DeleteBeforeCursor(count int) (deleted string) { } r := []rune(b.Text()) - if b.cursorPosition > 0 { - start := b.cursorPosition - count + if b.CursorPosition > 0 { + start := b.CursorPosition - count if start < 0 { start = 0 } - deleted = string(r[start:b.cursorPosition]) + deleted = string(r[start:b.CursorPosition]) b.setDocument(&Document{ - Text: string(r[:start]) + string(r[b.cursorPosition:]), - cursorPosition: b.cursorPosition - len([]rune(deleted)), + Text: string(r[:start]) + string(r[b.CursorPosition:]), + CursorPosition: b.CursorPosition - len([]rune(deleted)), }) } return @@ -169,9 +163,9 @@ func (b *Buffer) NewLine(copyMargin bool) { // Delete specified number of characters and Return the deleted text. func (b *Buffer) Delete(count int) (deleted string) { r := []rune(b.Text()) - if b.cursorPosition < len(r) { + if b.CursorPosition < len(r) { deleted = b.Document().TextAfterCursor()[:count] - b.setText(string(r[:b.cursorPosition]) + string(r[b.cursorPosition+len(deleted):])) + b.setText(string(r[:b.CursorPosition]) + string(r[b.CursorPosition+len(deleted):])) } return } @@ -179,7 +173,7 @@ func (b *Buffer) Delete(count int) (deleted string) { // JoinNextLine joins the next line to the current one by deleting the line ending after the current line. func (b *Buffer) JoinNextLine(separator string) { if !b.Document().OnLastLine() { - b.cursorPosition += b.Document().GetEndOfLinePosition() + b.CursorPosition += b.Document().GetEndOfLinePosition() b.Delete(1) // Remove spaces b.setText(b.Document().TextBeforeCursor() + separator + strings.TrimLeft(b.Document().TextAfterCursor(), " ")) @@ -188,10 +182,10 @@ func (b *Buffer) JoinNextLine(separator string) { // SwapCharactersBeforeCursor swaps the last two characters before the cursor. func (b *Buffer) SwapCharactersBeforeCursor() { - if b.cursorPosition >= 2 { - x := b.Text()[b.cursorPosition-2 : b.cursorPosition-1] - y := b.Text()[b.cursorPosition-1 : b.cursorPosition] - b.setText(b.Text()[:b.cursorPosition-2] + y + x + b.Text()[b.cursorPosition:]) + if b.CursorPosition >= 2 { + x := b.Text()[b.CursorPosition-2 : b.CursorPosition-1] + y := b.Text()[b.CursorPosition-1 : b.CursorPosition] + b.setText(b.Text()[:b.CursorPosition-2] + y + x + b.Text()[b.CursorPosition:]) } } diff --git a/vendor/github.com/c-bata/go-prompt/completion.go b/vendor/github.com/c-bata/go-prompt/completion.go index 5f698034bf..0c0f54bf81 100644 --- a/vendor/github.com/c-bata/go-prompt/completion.go +++ b/vendor/github.com/c-bata/go-prompt/completion.go @@ -3,21 +3,16 @@ package prompt import ( "log" "strings" - - "github.com/mattn/go-runewidth" ) const ( - shortenSuffix = "..." - leftPrefix = " " - leftSuffix = " " - rightPrefix = " " - rightSuffix = " " -) - -var ( - leftMargin = runewidth.StringWidth(leftPrefix + leftSuffix) - rightMargin = runewidth.StringWidth(rightPrefix + rightSuffix) + shortenSuffix = "..." + leftPrefix = " " + leftSuffix = " " + rightPrefix = " " + rightSuffix = " " + leftMargin = len(leftPrefix + leftSuffix) + rightMargin = len(rightPrefix + rightSuffix) completionMargin = leftMargin + rightMargin ) @@ -35,7 +30,6 @@ type CompletionManager struct { completer Completer verticalScroll int - wordSeparator string } // GetSelectedSuggestion returns the selected item. @@ -108,26 +102,17 @@ func (c *CompletionManager) update() { } } -func deleteBreakLineCharacters(s string) string { - s = strings.Replace(s, "\n", "", -1) - s = strings.Replace(s, "\r", "", -1) - return s -} - func formatTexts(o []string, max int, prefix, suffix string) (new []string, width int) { l := len(o) n := make([]string, l) - lenPrefix := runewidth.StringWidth(prefix) - lenSuffix := runewidth.StringWidth(suffix) - lenShorten := runewidth.StringWidth(shortenSuffix) + lenPrefix := len([]rune(prefix)) + lenSuffix := len([]rune(suffix)) + lenShorten := len(shortenSuffix) min := lenPrefix + lenSuffix + lenShorten for i := 0; i < l; i++ { - o[i] = deleteBreakLineCharacters(o[i]) - - w := runewidth.StringWidth(o[i]) - if width < w { - width = w + if width < len([]rune(o[i])) { + width = len([]rune(o[i])) } } @@ -143,15 +128,13 @@ func formatTexts(o []string, max int, prefix, suffix string) (new []string, widt } for i := 0; i < l; i++ { - x := runewidth.StringWidth(o[i]) + r := []rune(o[i]) + x := len(r) if x <= width { spaces := strings.Repeat(" ", width-x) n[i] = prefix + o[i] + spaces + suffix } else if x > width { - x := runewidth.Truncate(o[i], width, shortenSuffix) - // When calling runewidth.Truncate("您好xxx您好xxx", 11, "...") returns "您好xxx..." - // But the length of this result is 10. So we need fill right using runewidth.FillRight. - n[i] = prefix + runewidth.FillRight(x, width) + suffix + n[i] = prefix + string(r[:width-lenShorten]) + shortenSuffix + suffix } } return n, lenPrefix + width + lenSuffix diff --git a/vendor/github.com/c-bata/go-prompt/output.go b/vendor/github.com/c-bata/go-prompt/console_interface.go similarity index 58% rename from vendor/github.com/c-bata/go-prompt/output.go rename to vendor/github.com/c-bata/go-prompt/console_interface.go index 5afb81c090..1215359a77 100644 --- a/vendor/github.com/c-bata/go-prompt/output.go +++ b/vendor/github.com/c-bata/go-prompt/console_interface.go @@ -1,90 +1,63 @@ package prompt -// DisplayAttribute represents display attributes like Blinking, Bold, Italic and so on. -type DisplayAttribute int - -const ( - // DisplayReset reset all display attributes. - DisplayReset DisplayAttribute = iota - // DisplayBold set bold or increases intensity. - DisplayBold - // DisplayLowIntensity decreases intensity. Not widely supported. - DisplayLowIntensity - // DisplayItalic set italic. Not widely supported. - DisplayItalic - // DisplayUnderline set underline - DisplayUnderline - // DisplayBlink set blink (less than 150 per minute). - DisplayBlink - // DisplayRapidBlink set blink (more than 150 per minute). Not widely supported. - DisplayRapidBlink - // DisplayReverse swap foreground and background colors. - DisplayReverse - // DisplayInvisible set invisible. Not widely supported. - DisplayInvisible - // DisplayCrossedOut set characters legible, but marked for deletion. Not widely supported. - DisplayCrossedOut - // DisplayDefaultFont set primary(default) font - DisplayDefaultFont -) +// WinSize represents the width and height of terminal. +type WinSize struct { + Row uint16 + Col uint16 +} // Color represents color on terminal. type Color int const ( - // DefaultColor represents a default color. DefaultColor Color = iota // Low intensity - - // Black represents a black. Black - // DarkRed represents a dark red. DarkRed - // DarkGreen represents a dark green. DarkGreen - // Brown represents a brown. Brown - // DarkBlue represents a dark blue. DarkBlue - // Purple represents a purple. Purple - // Cyan represents a cyan. Cyan - // LightGray represents a light gray. LightGray // High intensity - - // DarkGray represents a dark gray. DarkGray - // Red represents a red. Red - // Green represents a green. Green - // Yellow represents a yellow. Yellow - // Blue represents a blue. Blue - // Fuchsia represents a fuchsia. Fuchsia - // Turquoise represents a turquoise. Turquoise - // White represents a white. White ) +// ConsoleParser is an interface to abstract input layer. +type ConsoleParser interface { + // Setup should be called before starting input + Setup() error + // TearDown should be called after stopping input + TearDown() error + // GetKey returns Key correspond to input byte codes. + GetKey(b []byte) Key + // GetWinSize returns WinSize object to represent width and height of terminal. + GetWinSize() *WinSize + // Read returns byte array. + Read() ([]byte, error) +} + // ConsoleWriter is an interface to abstract output layer. type ConsoleWriter interface { /* Write */ // WriteRaw to write raw byte array. WriteRaw(data []byte) - // Write to write safety byte array by removing control sequences. + // Write to write byte array without control sequences. Write(data []byte) // WriteStr to write raw string. WriteRawStr(data string) - // WriteStr to write safety string by removing control sequences. + // WriteStr to write string without control sequences. WriteStr(data string) // Flush to flush buffer. Flush() error diff --git a/vendor/github.com/c-bata/go-prompt/document.go b/vendor/github.com/c-bata/go-prompt/document.go index 06b436a21d..94a410e229 100644 --- a/vendor/github.com/c-bata/go-prompt/document.go +++ b/vendor/github.com/c-bata/go-prompt/document.go @@ -1,41 +1,24 @@ package prompt import ( - "sort" "strings" "unicode/utf8" - - "github.com/mattn/go-runewidth" ) // Document has text displayed in terminal and cursor position. type Document struct { - Text string - // This represents a index in a rune array of Document.Text. - // So if Document is "日本(cursor)語", cursorPosition is 2. - // But DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. - cursorPosition int + Text string + CursorPosition int } // NewDocument return the new empty document. func NewDocument() *Document { return &Document{ Text: "", - cursorPosition: 0, + CursorPosition: 0, } } -// DisplayCursorPosition returns the cursor position on rendered text on terminal emulators. -// So if Document is "日本(cursor)語", DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. -func (d *Document) DisplayCursorPosition() int { - var position int - runes := []rune(d.Text)[:d.cursorPosition] - for i := range runes { - position += runewidth.RuneWidth(runes[i]) - } - return position -} - // GetCharRelativeToCursor return character relative to cursor position, or empty string func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { s := d.Text @@ -44,7 +27,7 @@ func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { for len(s) > 0 { cnt++ r, size := utf8.DecodeRuneInString(s) - if cnt == d.cursorPosition+offset { + if cnt == d.CursorPosition+offset { return r } s = s[size:] @@ -55,13 +38,13 @@ func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { // TextBeforeCursor returns the text before the cursor. func (d *Document) TextBeforeCursor() string { r := []rune(d.Text) - return string(r[:d.cursorPosition]) + return string(r[:d.CursorPosition]) } // TextAfterCursor returns the text after the cursor. func (d *Document) TextAfterCursor() string { r := []rune(d.Text) - return string(r[d.cursorPosition:]) + return string(r[d.CursorPosition:]) } // GetWordBeforeCursor returns the word before the cursor. @@ -71,13 +54,6 @@ func (d *Document) GetWordBeforeCursor() string { return x[d.FindStartOfPreviousWord():] } -// GetWordAfterCursor returns the word after the cursor. -// If we have whitespace after the cursor this returns an empty string. -func (d *Document) GetWordAfterCursor() string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWord()] -} - // GetWordBeforeCursorWithSpace returns the word before the cursor. // Unlike GetWordBeforeCursor, it returns string containing space func (d *Document) GetWordBeforeCursorWithSpace() string { @@ -85,46 +61,16 @@ func (d *Document) GetWordBeforeCursorWithSpace() string { return x[d.FindStartOfPreviousWordWithSpace():] } -// GetWordAfterCursorWithSpace returns the word after the cursor. -// Unlike GetWordAfterCursor, it returns string containing space -func (d *Document) GetWordAfterCursorWithSpace() string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordWithSpace()] -} - -// GetWordBeforeCursorUntilSeparator returns the text before the cursor until next separator. -func (d *Document) GetWordBeforeCursorUntilSeparator(sep string) string { - x := d.TextBeforeCursor() - return x[d.FindStartOfPreviousWordUntilSeparator(sep):] -} - -// GetWordAfterCursorUntilSeparator returns the text after the cursor until next separator. -func (d *Document) GetWordAfterCursorUntilSeparator(sep string) string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordUntilSeparator(sep)] -} - -// GetWordBeforeCursorUntilSeparatorIgnoreNextToCursor returns the word before the cursor. -// Unlike GetWordBeforeCursor, it returns string containing space -func (d *Document) GetWordBeforeCursorUntilSeparatorIgnoreNextToCursor(sep string) string { - x := d.TextBeforeCursor() - return x[d.FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor(sep):] -} - -// GetWordAfterCursorUntilSeparatorIgnoreNextToCursor returns the word after the cursor. -// Unlike GetWordAfterCursor, it returns string containing space -func (d *Document) GetWordAfterCursorUntilSeparatorIgnoreNextToCursor(sep string) string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor(sep)] -} - // FindStartOfPreviousWord returns an index relative to the cursor position -// pointing to the start of the previous word. Return 0 if nothing was found. +// pointing to the start of the previous word. Return `None` if nothing was found. func (d *Document) FindStartOfPreviousWord() int { + // Reverse the text before the cursor, in order to do an efficient backwards search. x := d.TextBeforeCursor() - i := strings.LastIndexByte(x, ' ') - if i != -1 { - return i + 1 + l := len(x) + for i := l; i > 0; i-- { + if x[i-1:i] == " " { + return i + } } return 0 } @@ -132,119 +78,21 @@ func (d *Document) FindStartOfPreviousWord() int { // FindStartOfPreviousWordWithSpace is almost the same as FindStartOfPreviousWord. // The only difference is to ignore contiguous spaces. func (d *Document) FindStartOfPreviousWordWithSpace() int { + // Reverse the text before the cursor, in order to do an efficient backwards search. x := d.TextBeforeCursor() - end := lastIndexByteNot(x, ' ') - if end == -1 { - return 0 - } - - start := strings.LastIndexByte(x[:end], ' ') - if start == -1 { - return 0 - } - return start + 1 -} - -// FindStartOfPreviousWordUntilSeparator is almost the same as FindStartOfPreviousWord. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindStartOfPreviousWordUntilSeparator(sep string) int { - if sep == "" { - return d.FindStartOfPreviousWord() - } - - x := d.TextBeforeCursor() - i := strings.LastIndexAny(x, sep) - if i != -1 { - return i + 1 + l := len(x) + appear := false + for i := l; i > 0; i-- { + if x[i-1:i] != " " { + appear = true + } + if x[i-1:i] == " " && appear { + return i + } } return 0 } -// FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor is almost the same as FindStartOfPreviousWordWithSpace. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor(sep string) int { - if sep == "" { - return d.FindStartOfPreviousWordWithSpace() - } - - x := d.TextBeforeCursor() - end := lastIndexAnyNot(x, sep) - if end == -1 { - return 0 - } - start := strings.LastIndexAny(x[:end], sep) - if start == -1 { - return 0 - } - return start + 1 -} - -// FindEndOfCurrentWord returns an index relative to the cursor position. -// pointing to the end of the current word. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWord() int { - x := d.TextAfterCursor() - i := strings.IndexByte(x, ' ') - if i != -1 { - return i - } - return len(x) -} - -// FindEndOfCurrentWordWithSpace is almost the same as FindEndOfCurrentWord. -// The only difference is to ignore contiguous spaces. -func (d *Document) FindEndOfCurrentWordWithSpace() int { - x := d.TextAfterCursor() - - start := indexByteNot(x, ' ') - if start == -1 { - return len(x) - } - - end := strings.IndexByte(x[start:], ' ') - if end == -1 { - return len(x) - } - - return start + end -} - -// FindEndOfCurrentWordUntilSeparator is almost the same as FindEndOfCurrentWord. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWordUntilSeparator(sep string) int { - if sep == "" { - return d.FindEndOfCurrentWord() - } - - x := d.TextAfterCursor() - i := strings.IndexAny(x, sep) - if i != -1 { - return i - } - return len(x) -} - -// FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor is almost the same as FindEndOfCurrentWordWithSpace. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor(sep string) int { - if sep == "" { - return d.FindEndOfCurrentWordWithSpace() - } - - x := d.TextAfterCursor() - - start := indexAnyNot(x, sep) - if start == -1 { - return len(x) - } - - end := strings.IndexAny(x[start:], sep) - if end == -1 { - return len(x) - } - - return start + end -} - // CurrentLineBeforeCursor returns the text from the start of the line until the cursor. func (d *Document) CurrentLineBeforeCursor() string { s := strings.Split(d.TextBeforeCursor(), "\n") @@ -292,14 +140,14 @@ func (d *Document) lineStartIndexes() []int { // the first character on that line. func (d *Document) findLineStartIndex(index int) (pos int, lineStartIndex int) { indexes := d.lineStartIndexes() - pos = bisectRight(indexes, index) - 1 + pos = BisectRight(indexes, index) - 1 lineStartIndex = indexes[pos] return } // CursorPositionRow returns the current row. (0-based.) func (d *Document) CursorPositionRow() (row int) { - row, _ = d.findLineStartIndex(d.cursorPosition) + row, _ = d.findLineStartIndex(d.CursorPosition) return } @@ -307,8 +155,8 @@ func (d *Document) CursorPositionRow() (row int) { func (d *Document) CursorPositionCol() (col int) { // Don't use self.text_before_cursor to calculate this. Creating substrings // and splitting is too expensive for getting the cursor position. - _, index := d.findLineStartIndex(d.cursorPosition) - col = d.cursorPosition - index + _, index := d.findLineStartIndex(d.CursorPosition) + col = d.CursorPosition - index return } @@ -348,7 +196,7 @@ func (d *Document) GetCursorUpPosition(count int, preferredColumn int) int { if row < 0 { row = 0 } - return d.TranslateRowColToIndex(row, col) - d.cursorPosition + return d.TranslateRowColToIndex(row, col) - d.CursorPosition } // GetCursorDownPosition return the relative cursor position (character index) where we would be if the @@ -361,7 +209,7 @@ func (d *Document) GetCursorDownPosition(count int, preferredColumn int) int { col = preferredColumn } row := d.CursorPositionRow() + count - return d.TranslateRowColToIndex(row, col) - d.cursorPosition + return d.TranslateRowColToIndex(row, col) - d.CursorPosition } // Lines returns the array of all the lines. @@ -432,102 +280,3 @@ func (d *Document) leadingWhitespaceInCurrentLine() (margin string) { margin = d.CurrentLine()[:len(d.CurrentLine())-len(trimmed)] return } - -// bisectRight to Locate the insertion point for v in a to maintain sorted order. -func bisectRight(a []int, v int) int { - return bisectRightRange(a, v, 0, len(a)) -} - -func bisectRightRange(a []int, v int, lo, hi int) int { - s := a[lo:hi] - return sort.Search(len(s), func(i int) bool { - return s[i] > v - }) -} - -func indexByteNot(s string, c byte) int { - n := len(s) - for i := 0; i < n; i++ { - if s[i] != c { - return i - } - } - return -1 -} - -func lastIndexByteNot(s string, c byte) int { - for i := len(s) - 1; i >= 0; i-- { - if s[i] != c { - return i - } - } - return -1 -} - -type asciiSet [8]uint32 - -func (as *asciiSet) notContains(c byte) bool { - return (as[c>>5] & (1 << uint(c&31))) == 0 -} - -func makeASCIISet(chars string) (as asciiSet, ok bool) { - for i := 0; i < len(chars); i++ { - c := chars[i] - if c >= utf8.RuneSelf { - return as, false - } - as[c>>5] |= 1 << uint(c&31) - } - return as, true -} - -func indexAnyNot(s, chars string) int { - if len(chars) > 0 { - if len(s) > 8 { - if as, isASCII := makeASCIISet(chars); isASCII { - for i := 0; i < len(s); i++ { - if as.notContains(s[i]) { - return i - } - } - return -1 - } - } - for i := 0; i < len(s); { - // I don't know why strings.IndexAny doesn't add rune count here. - r, size := utf8.DecodeRuneInString(s[i:]) - i += size - for _, c := range chars { - if r != c { - return i - } - } - } - } - return -1 -} - -func lastIndexAnyNot(s, chars string) int { - if len(chars) > 0 { - if len(s) > 8 { - if as, isASCII := makeASCIISet(chars); isASCII { - for i := len(s) - 1; i >= 0; i-- { - if as.notContains(s[i]) { - return i - } - } - return -1 - } - } - for i := len(s); i > 0; { - r, size := utf8.DecodeLastRuneInString(s[:i]) - i -= size - for _, c := range chars { - if r != c { - return i - } - } - } - } - return -1 -} diff --git a/vendor/github.com/c-bata/go-prompt/emacs.go b/vendor/github.com/c-bata/go-prompt/emacs.go index 9dc71edcab..f38d3f7302 100644 --- a/vendor/github.com/c-bata/go-prompt/emacs.go +++ b/vendor/github.com/c-bata/go-prompt/emacs.go @@ -113,7 +113,6 @@ var emacsKeyBindings = []KeyBind{ out := NewStandardOutputWriter() out.EraseScreen() out.CursorGoTo(0, 0) - out.Flush() }, }, } diff --git a/vendor/github.com/c-bata/go-prompt/input.go b/vendor/github.com/c-bata/go-prompt/input.go index 4c90b0f309..70837103cc 100644 --- a/vendor/github.com/c-bata/go-prompt/input.go +++ b/vendor/github.com/c-bata/go-prompt/input.go @@ -1,158 +1,42 @@ package prompt -// WinSize represents the width and height of terminal. -type WinSize struct { - Row uint16 - Col uint16 +func dummyExecutor(in string) { return } + +// Input get the input data from the user and return it. +func Input(prefix string, completer Completer, opts ...Option) string { + pt := New(dummyExecutor, completer) + pt.renderer.prefixTextColor = DefaultColor + pt.renderer.prefix = prefix + + for _, opt := range opts { + if err := opt(pt); err != nil { + panic(err) + } + } + return pt.Input() } -// ConsoleParser is an interface to abstract input layer. -type ConsoleParser interface { - // Setup should be called before starting input - Setup() error - // TearDown should be called after stopping input - TearDown() error - // GetKey returns Key correspond to input byte codes. - GetKey(b []byte) Key - // GetWinSize returns WinSize object to represent width and height of terminal. - GetWinSize() *WinSize - // Read returns byte array. - Read() ([]byte, error) +// Choose to the shortcut of input function to select from string array. +func Choose(prefix string, choices []string, opts ...Option) string { + completer := newChoiceCompleter(choices, FilterHasPrefix) + pt := New(dummyExecutor, completer) + pt.renderer.prefixTextColor = DefaultColor + pt.renderer.prefix = prefix + + for _, opt := range opts { + if err := opt(pt); err != nil { + panic(err) + } + } + return pt.Input() } -var asciiSequences = []*ASCIICode{ - {Key: Escape, ASCIICode: []byte{0x1b}}, - - {Key: ControlSpace, ASCIICode: []byte{0x00}}, - {Key: ControlA, ASCIICode: []byte{0x1}}, - {Key: ControlB, ASCIICode: []byte{0x2}}, - {Key: ControlC, ASCIICode: []byte{0x3}}, - {Key: ControlD, ASCIICode: []byte{0x4}}, - {Key: ControlE, ASCIICode: []byte{0x5}}, - {Key: ControlF, ASCIICode: []byte{0x6}}, - {Key: ControlG, ASCIICode: []byte{0x7}}, - {Key: ControlH, ASCIICode: []byte{0x8}}, - //{Key: ControlI, ASCIICode: []byte{0x9}}, - //{Key: ControlJ, ASCIICode: []byte{0xa}}, - {Key: ControlK, ASCIICode: []byte{0xb}}, - {Key: ControlL, ASCIICode: []byte{0xc}}, - {Key: ControlM, ASCIICode: []byte{0xd}}, - {Key: ControlN, ASCIICode: []byte{0xe}}, - {Key: ControlO, ASCIICode: []byte{0xf}}, - {Key: ControlP, ASCIICode: []byte{0x10}}, - {Key: ControlQ, ASCIICode: []byte{0x11}}, - {Key: ControlR, ASCIICode: []byte{0x12}}, - {Key: ControlS, ASCIICode: []byte{0x13}}, - {Key: ControlT, ASCIICode: []byte{0x14}}, - {Key: ControlU, ASCIICode: []byte{0x15}}, - {Key: ControlV, ASCIICode: []byte{0x16}}, - {Key: ControlW, ASCIICode: []byte{0x17}}, - {Key: ControlX, ASCIICode: []byte{0x18}}, - {Key: ControlY, ASCIICode: []byte{0x19}}, - {Key: ControlZ, ASCIICode: []byte{0x1a}}, - - {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, - {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, - {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, - {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, - {Key: Backspace, ASCIICode: []byte{0x7f}}, - - {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, - {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, - {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, - {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x30, 0x48}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, - {Key: End, ASCIICode: []byte{0x1b, 0x30, 0x46}}, - - {Key: Enter, ASCIICode: []byte{0xa}}, - {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, - {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e}}, - {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, - {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x7e}}, - {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x36, 0x7e}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x37, 0x7e}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x38, 0x7e}}, - {Key: Tab, ASCIICode: []byte{0x9}}, - {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, - {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, - - {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, - {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, - {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, - {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, - - {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console - {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console - {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console - {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console - {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console - - {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode - {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode - {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode - {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode - - {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, - {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, - {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, - {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, - {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, - {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, - {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, - {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, - {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, - {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, - {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, - {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, - {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, - {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, - {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, - {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, - - // Xterm - {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x50}}, - {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51}}, - // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, // Conflicts with CPR response - {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, - {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x32, 0x7e}}, - {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x32, 0x7e}}, - {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x32, 0x7e}}, - {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x32, 0x7e}}, - {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x32, 0x7e}}, - {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x32, 0x7e}}, - {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x32, 0x7e}}, - {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x32, 0x7e}}, - - {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41}}, - {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42}}, - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43}}, - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44}}, - - {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41}}, - {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x42}}, - {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43}}, - {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x44}}, - - // Tmux sends following keystrokes when control+arrow is pressed, but for - // Emacs ansi-term sends the same sequences for normal arrow keys. Consider - // it a normal arrow press, because that's more important. - {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, - {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, - {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, - {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, - - {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x41}}, - {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x42}}, - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x43}}, - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x44}}, - - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt - - {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm - {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +func newChoiceCompleter(choices []string, filter Filter) Completer { + s := make([]Suggest, len(choices)) + for i := range choices { + s[i] = Suggest{Text: choices[i]} + } + return func(x Document) []Suggest { + return filter(s, x.GetWordBeforeCursor(), true) + } } diff --git a/vendor/github.com/c-bata/go-prompt/input_posix.go b/vendor/github.com/c-bata/go-prompt/input_posix.go deleted file mode 100644 index 46ecb511a1..0000000000 --- a/vendor/github.com/c-bata/go-prompt/input_posix.go +++ /dev/null @@ -1,128 +0,0 @@ -// +build !windows - -package prompt - -import ( - "bytes" - "log" - "syscall" - "unsafe" - - "github.com/pkg/term/termios" -) - -const maxReadBytes = 1024 - -// PosixParser is a ConsoleParser implementation for POSIX environment. -type PosixParser struct { - fd int - origTermios syscall.Termios -} - -// Setup should be called before starting input -func (t *PosixParser) Setup() error { - // Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh. - if err := syscall.SetNonblock(t.fd, true); err != nil { - log.Println("[ERROR] Cannot set non blocking mode.") - return err - } - if err := t.setRawMode(); err != nil { - log.Println("[ERROR] Cannot set raw mode.") - return err - } - return nil -} - -// TearDown should be called after stopping input -func (t *PosixParser) TearDown() error { - if err := syscall.SetNonblock(t.fd, false); err != nil { - log.Println("[ERROR] Cannot set blocking mode.") - return err - } - if err := t.resetRawMode(); err != nil { - log.Println("[ERROR] Cannot reset from raw mode.") - return err - } - return nil -} - -// Read returns byte array. -func (t *PosixParser) Read() ([]byte, error) { - buf := make([]byte, maxReadBytes) - n, err := syscall.Read(syscall.Stdin, buf) - if err != nil { - return []byte{}, err - } - return buf[:n], nil -} - -func (t *PosixParser) setRawMode() error { - x := t.origTermios.Lflag - if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag { - // fd is already raw mode - return nil - } - var n syscall.Termios - if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil { - return err - } - n = t.origTermios - // "&^=" used like: https://play.golang.org/p/8eJw3JxS4O - n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG - n.Cc[syscall.VMIN] = 1 - n.Cc[syscall.VTIME] = 0 - termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n) - return nil -} - -func (t *PosixParser) resetRawMode() error { - if t.origTermios.Lflag == 0 { - return nil - } - return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios) -} - -// GetKey returns Key correspond to input byte codes. -func (t *PosixParser) GetKey(b []byte) Key { - for _, k := range asciiSequences { - if bytes.Equal(k.ASCIICode, b) { - return k.Key - } - } - return NotDefined -} - -// winsize is winsize struct got from the ioctl(2) system call. -type ioctlWinsize struct { - Row uint16 - Col uint16 - X uint16 // pixel value - Y uint16 // pixel value -} - -// GetWinSize returns WinSize object to represent width and height of terminal. -func (t *PosixParser) GetWinSize() *WinSize { - ws := &ioctlWinsize{} - retCode, _, errno := syscall.Syscall( - syscall.SYS_IOCTL, - uintptr(t.fd), - uintptr(syscall.TIOCGWINSZ), - uintptr(unsafe.Pointer(ws))) - - if int(retCode) == -1 { - panic(errno) - } - return &WinSize{ - Row: ws.Row, - Col: ws.Col, - } -} - -var _ ConsoleParser = &PosixParser{} - -// NewStandardInputParser returns ConsoleParser object to read from stdin. -func NewStandardInputParser() *PosixParser { - return &PosixParser{ - fd: syscall.Stdin, - } -} diff --git a/vendor/github.com/c-bata/go-prompt/input_windows.go b/vendor/github.com/c-bata/go-prompt/input_windows.go deleted file mode 100644 index f52b538977..0000000000 --- a/vendor/github.com/c-bata/go-prompt/input_windows.go +++ /dev/null @@ -1,94 +0,0 @@ -// +build windows - -package prompt - -import ( - "bytes" - "errors" - "syscall" - "unicode/utf8" - "unsafe" - - "github.com/mattn/go-tty" -) - -const maxReadBytes = 1024 - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents") - -// WindowsParser is a ConsoleParser implementation for Win32 console. -type WindowsParser struct { - tty *tty.TTY -} - -// Setup should be called before starting input -func (p *WindowsParser) Setup() error { - t, err := tty.Open() - if err != nil { - return err - } - p.tty = t - return nil -} - -// TearDown should be called after stopping input -func (p *WindowsParser) TearDown() error { - return p.tty.Close() -} - -// GetKey returns Key correspond to input byte codes. -func (p *WindowsParser) GetKey(b []byte) Key { - for _, k := range asciiSequences { - if bytes.Compare(k.ASCIICode, b) == 0 { - return k.Key - } - } - return NotDefined -} - -// Read returns byte array. -func (p *WindowsParser) Read() ([]byte, error) { - var ev uint32 - r0, _, err := procGetNumberOfConsoleInputEvents.Call(p.tty.Input().Fd(), uintptr(unsafe.Pointer(&ev))) - if r0 == 0 { - return nil, err - } - if ev == 0 { - return nil, errors.New("EAGAIN") - } - - r, err := p.tty.ReadRune() - if err != nil { - return nil, err - } - - buf := make([]byte, maxReadBytes) - n := utf8.EncodeRune(buf[:], r) - for p.tty.Buffered() && n < maxReadBytes { - r, err := p.tty.ReadRune() - if err != nil { - break - } - n += utf8.EncodeRune(buf[n:], r) - } - return buf[:n], nil -} - -// GetWinSize returns WinSize object to represent width and height of terminal. -func (p *WindowsParser) GetWinSize() *WinSize { - w, h, err := p.tty.Size() - if err != nil { - panic(err) - } - return &WinSize{ - Row: uint16(h), - Col: uint16(w), - } -} - -// NewStandardInputParser returns ConsoleParser object to read from stdin. -func NewStandardInputParser() *WindowsParser { - return &WindowsParser{} -} diff --git a/vendor/github.com/c-bata/go-prompt/key.go b/vendor/github.com/c-bata/go-prompt/key.go index 068b70e9f7..9f79185f50 100644 --- a/vendor/github.com/c-bata/go-prompt/key.go +++ b/vendor/github.com/c-bata/go-prompt/key.go @@ -1,6 +1,3 @@ -// Code generated "This is a fake comment to avoid golint errors"; DO NOT EDIT. -// FIXME: This is a little bit stupid, but there are many public constants which is no value for writing godoc comment. - package prompt // Key is the type express the key inserted from user. diff --git a/vendor/github.com/c-bata/go-prompt/key_bind.go b/vendor/github.com/c-bata/go-prompt/key_bind.go index 42669e7f94..0cfcab44aa 100644 --- a/vendor/github.com/c-bata/go-prompt/key_bind.go +++ b/vendor/github.com/c-bata/go-prompt/key_bind.go @@ -1,59 +1,62 @@ package prompt -// KeyBindFunc receives buffer and processed it. type KeyBindFunc func(*Buffer) -// KeyBind represents which key should do what operation. type KeyBind struct { Key Key Fn KeyBindFunc } -// ASCIICodeBind represents which []byte should do what operation -type ASCIICodeBind struct { - ASCIICode []byte - Fn KeyBindFunc -} - -// KeyBindMode to switch a key binding flexibly. type KeyBindMode string const ( - // CommonKeyBind is a mode without any keyboard shortcut CommonKeyBind KeyBindMode = "common" - // EmacsKeyBind is a mode to use emacs-like keyboard shortcut - EmacsKeyBind KeyBindMode = "emacs" + EmacsKeyBind KeyBindMode = "emacs" ) var commonKeyBindings = []KeyBind{ // Go to the End of the line { Key: End, - Fn: GoLineEnd, + Fn: func(buf *Buffer) { + x := []rune(buf.Document().TextAfterCursor()) + buf.CursorRight(len(x)) + }, }, // Go to the beginning of the line { Key: Home, - Fn: GoLineBeginning, + Fn: func(buf *Buffer) { + x := []rune(buf.Document().TextBeforeCursor()) + buf.CursorLeft(len(x)) + }, }, // Delete character under the cursor { Key: Delete, - Fn: DeleteChar, + Fn: func(buf *Buffer) { + buf.Delete(1) + }, }, // Backspace { Key: Backspace, - Fn: DeleteBeforeChar, + Fn: func(buf *Buffer) { + buf.DeleteBeforeCursor(1) + }, }, // Right allow: Forward one character { Key: Right, - Fn: GoRightChar, + Fn: func(buf *Buffer) { + buf.CursorRight(1) + }, }, // Left allow: Backward one character { Key: Left, - Fn: GoLeftChar, + Fn: func(buf *Buffer) { + buf.CursorLeft(1) + }, }, } diff --git a/vendor/github.com/c-bata/go-prompt/key_bind_func.go b/vendor/github.com/c-bata/go-prompt/key_bind_func.go deleted file mode 100644 index 7b2ecdf631..0000000000 --- a/vendor/github.com/c-bata/go-prompt/key_bind_func.go +++ /dev/null @@ -1,48 +0,0 @@ -package prompt - -// GoLineEnd Go to the End of the line -func GoLineEnd(buf *Buffer) { - x := []rune(buf.Document().TextAfterCursor()) - buf.CursorRight(len(x)) -} - -// GoLineBeginning Go to the beginning of the line -func GoLineBeginning(buf *Buffer) { - x := []rune(buf.Document().TextBeforeCursor()) - buf.CursorLeft(len(x)) -} - -// DeleteChar Delete character under the cursor -func DeleteChar(buf *Buffer) { - buf.Delete(1) -} - -// DeleteWord Delete word before the cursor -func DeleteWord(buf *Buffer) { - buf.DeleteBeforeCursor(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace()) -} - -// DeleteBeforeChar Go to Backspace -func DeleteBeforeChar(buf *Buffer) { - buf.DeleteBeforeCursor(1) -} - -// GoRightChar Forward one character -func GoRightChar(buf *Buffer) { - buf.CursorRight(1) -} - -// GoLeftChar Backward one character -func GoLeftChar(buf *Buffer) { - buf.CursorLeft(1) -} - -// GoRightWord Forward one word -func GoRightWord(buf *Buffer) { - buf.CursorRight(buf.Document().FindEndOfCurrentWordWithSpace()) -} - -// GoLeftWord Backward one word -func GoLeftWord(buf *Buffer) { - buf.CursorLeft(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace()) -} diff --git a/vendor/github.com/c-bata/go-prompt/option.go b/vendor/github.com/c-bata/go-prompt/option.go index 9a7f386da3..72c6de3103 100644 --- a/vendor/github.com/c-bata/go-prompt/option.go +++ b/vendor/github.com/c-bata/go-prompt/option.go @@ -12,7 +12,7 @@ func OptionParser(x ConsoleParser) Option { } } -// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interface. +// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interace. func OptionWriter(x ConsoleWriter) Option { return func(p *Prompt) error { p.renderer.out = x @@ -36,14 +36,6 @@ func OptionPrefix(x string) Option { } } -// OptionCompletionWordSeparator to set word separators. Enable only ' ' if empty. -func OptionCompletionWordSeparator(x string) Option { - return func(p *Prompt) error { - p.completion.wordSeparator = x - return nil - } -} - // OptionLivePrefix to change the prefix dynamically by callback function func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option { return func(p *Prompt) error { @@ -52,7 +44,6 @@ func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option { } } -// OptionPrefixTextColor change a text color of prefix string func OptionPrefixTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.prefixTextColor = x @@ -60,7 +51,6 @@ func OptionPrefixTextColor(x Color) Option { } } -// OptionPrefixBackgroundColor to change a background color of prefix string func OptionPrefixBackgroundColor(x Color) Option { return func(p *Prompt) error { p.renderer.prefixBGColor = x @@ -68,7 +58,6 @@ func OptionPrefixBackgroundColor(x Color) Option { } } -// OptionInputTextColor to change a color of text which is input by user func OptionInputTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.inputTextColor = x @@ -76,7 +65,6 @@ func OptionInputTextColor(x Color) Option { } } -// OptionInputBGColor to change a color of background which is input by user func OptionInputBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.inputBGColor = x @@ -84,7 +72,6 @@ func OptionInputBGColor(x Color) Option { } } -// OptionPreviewSuggestionTextColor to change a text color which is completed func OptionPreviewSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.previewSuggestionTextColor = x @@ -92,7 +79,6 @@ func OptionPreviewSuggestionTextColor(x Color) Option { } } -// OptionPreviewSuggestionBGColor to change a background color which is completed func OptionPreviewSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.previewSuggestionBGColor = x @@ -100,7 +86,6 @@ func OptionPreviewSuggestionBGColor(x Color) Option { } } -// OptionSuggestionTextColor to change a text color in drop down suggestions. func OptionSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.suggestionTextColor = x @@ -108,7 +93,6 @@ func OptionSuggestionTextColor(x Color) Option { } } -// OptionSuggestionBGColor change a background color in drop down suggestions. func OptionSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.suggestionBGColor = x @@ -116,7 +100,6 @@ func OptionSuggestionBGColor(x Color) Option { } } -// OptionSelectedSuggestionTextColor to change a text color for completed text which is selected inside suggestions drop down box. func OptionSelectedSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedSuggestionTextColor = x @@ -124,7 +107,6 @@ func OptionSelectedSuggestionTextColor(x Color) Option { } } -// OptionSelectedSuggestionBGColor to change a background color for completed text which is selected inside suggestions drop down box. func OptionSelectedSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedSuggestionBGColor = x @@ -132,7 +114,6 @@ func OptionSelectedSuggestionBGColor(x Color) Option { } } -// OptionDescriptionTextColor to change a background color of description text in drop down suggestions. func OptionDescriptionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.descriptionTextColor = x @@ -140,7 +121,6 @@ func OptionDescriptionTextColor(x Color) Option { } } -// OptionDescriptionBGColor to change a background color of description text in drop down suggestions. func OptionDescriptionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.descriptionBGColor = x @@ -148,7 +128,6 @@ func OptionDescriptionBGColor(x Color) Option { } } -// OptionSelectedDescriptionTextColor to change a text color of description which is selected inside suggestions drop down box. func OptionSelectedDescriptionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedDescriptionTextColor = x @@ -156,7 +135,6 @@ func OptionSelectedDescriptionTextColor(x Color) Option { } } -// OptionSelectedDescriptionBGColor to change a background color of description which is selected inside suggestions drop down box. func OptionSelectedDescriptionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedDescriptionBGColor = x @@ -164,7 +142,6 @@ func OptionSelectedDescriptionBGColor(x Color) Option { } } -// OptionScrollbarThumbColor to change a thumb color on scrollbar. func OptionScrollbarThumbColor(x Color) Option { return func(p *Prompt) error { p.renderer.scrollbarThumbColor = x @@ -172,7 +149,6 @@ func OptionScrollbarThumbColor(x Color) Option { } } -// OptionScrollbarBGColor to change a background color of scrollbar. func OptionScrollbarBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.scrollbarBGColor = x @@ -217,14 +193,6 @@ func OptionAddKeyBind(b ...KeyBind) Option { } } -// OptionAddASCIICodeBind to set a custom key bind. -func OptionAddASCIICodeBind(b ...ASCIICodeBind) Option { - return func(p *Prompt) error { - p.ASCIICodeBindings = append(p.ASCIICodeBindings, b...) - return nil - } -} - // New returns a Prompt with powerful auto-completion. func New(executor Executor, completer Completer, opts ...Option) *Prompt { pt := &Prompt{ diff --git a/vendor/github.com/c-bata/go-prompt/output_posix.go b/vendor/github.com/c-bata/go-prompt/output_posix.go deleted file mode 100644 index 4c2ccf63b1..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_posix.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build !windows - -package prompt - -import ( - "syscall" -) - -// PosixWriter is a ConsoleWriter implementation for POSIX environment. -// To control terminal emulator, this outputs VT100 escape sequences. -type PosixWriter struct { - VT100Writer - fd int -} - -// Flush to flush buffer -func (w *PosixWriter) Flush() error { - _, err := syscall.Write(w.fd, w.buffer) - if err != nil { - return err - } - w.buffer = []byte{} - return nil -} - -var _ ConsoleWriter = &PosixWriter{} - -// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. -// This generates VT100 escape sequences because almost terminal emulators -// in POSIX OS built on top of a VT100 specification. -func NewStandardOutputWriter() *PosixWriter { - return &PosixWriter{ - fd: syscall.Stdout, - } -} diff --git a/vendor/github.com/c-bata/go-prompt/output_vt100.go b/vendor/github.com/c-bata/go-prompt/output_vt100.go deleted file mode 100644 index 3b3031d70b..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_vt100.go +++ /dev/null @@ -1,333 +0,0 @@ -package prompt - -import ( - "bytes" - "strconv" -) - -// VT100Writer generates VT100 escape sequences. -type VT100Writer struct { - buffer []byte -} - -// WriteRaw to write raw byte array -func (w *VT100Writer) WriteRaw(data []byte) { - w.buffer = append(w.buffer, data...) - return -} - -// Write to write safety byte array by removing control sequences. -func (w *VT100Writer) Write(data []byte) { - w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1)) - return -} - -// WriteRawStr to write raw string -func (w *VT100Writer) WriteRawStr(data string) { - w.WriteRaw([]byte(data)) - return -} - -// WriteStr to write safety string by removing control sequences. -func (w *VT100Writer) WriteStr(data string) { - w.Write([]byte(data)) - return -} - -/* Erase */ - -// EraseScreen erases the screen with the background colour and moves the cursor to home. -func (w *VT100Writer) EraseScreen() { - w.WriteRaw([]byte{0x1b, '[', '2', 'J'}) - return -} - -// EraseUp erases the screen from the current line up to the top of the screen. -func (w *VT100Writer) EraseUp() { - w.WriteRaw([]byte{0x1b, '[', '1', 'J'}) - return -} - -// EraseDown erases the screen from the current line down to the bottom of the screen. -func (w *VT100Writer) EraseDown() { - w.WriteRaw([]byte{0x1b, '[', 'J'}) - return -} - -// EraseStartOfLine erases from the current cursor position to the start of the current line. -func (w *VT100Writer) EraseStartOfLine() { - w.WriteRaw([]byte{0x1b, '[', '1', 'K'}) - return -} - -// EraseEndOfLine erases from the current cursor position to the end of the current line. -func (w *VT100Writer) EraseEndOfLine() { - w.WriteRaw([]byte{0x1b, '[', 'K'}) - return -} - -// EraseLine erases the entire current line. -func (w *VT100Writer) EraseLine() { - w.WriteRaw([]byte{0x1b, '[', '2', 'K'}) - return -} - -/* Cursor */ - -// ShowCursor stops blinking cursor and show. -func (w *VT100Writer) ShowCursor() { - w.WriteRaw([]byte{0x1b, '[', '?', '1', '2', 'l', 0x1b, '[', '?', '2', '5', 'h'}) -} - -// HideCursor hides cursor. -func (w *VT100Writer) HideCursor() { - w.WriteRaw([]byte{0x1b, '[', '?', '2', '5', 'l'}) - return -} - -// CursorGoTo sets the cursor position where subsequent text will begin. -func (w *VT100Writer) CursorGoTo(row, col int) { - if row == 0 && col == 0 { - // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. - w.WriteRaw([]byte{0x1b, '[', 'H'}) - return - } - r := strconv.Itoa(row) - c := strconv.Itoa(col) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(r)) - w.WriteRaw([]byte{';'}) - w.WriteRaw([]byte(c)) - w.WriteRaw([]byte{'H'}) - return -} - -// CursorUp moves the cursor up by 'n' rows; the default count is 1. -func (w *VT100Writer) CursorUp(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorDown(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'A'}) - return -} - -// CursorDown moves the cursor down by 'n' rows; the default count is 1. -func (w *VT100Writer) CursorDown(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorUp(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'B'}) - return -} - -// CursorForward moves the cursor forward by 'n' columns; the default count is 1. -func (w *VT100Writer) CursorForward(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorBackward(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'C'}) - return -} - -// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. -func (w *VT100Writer) CursorBackward(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorForward(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'D'}) - return -} - -// AskForCPR asks for a cursor position report (CPR). -func (w *VT100Writer) AskForCPR() { - // CPR: Cursor Position Request. - w.WriteRaw([]byte{0x1b, '[', '6', 'n'}) - return -} - -// SaveCursor saves current cursor position. -func (w *VT100Writer) SaveCursor() { - w.WriteRaw([]byte{0x1b, '[', 's'}) - return -} - -// UnSaveCursor restores cursor position after a Save Cursor. -func (w *VT100Writer) UnSaveCursor() { - w.WriteRaw([]byte{0x1b, '[', 'u'}) - return -} - -/* Scrolling */ - -// ScrollDown scrolls display down one line. -func (w *VT100Writer) ScrollDown() { - w.WriteRaw([]byte{0x1b, 'D'}) - return -} - -// ScrollUp scroll display up one line. -func (w *VT100Writer) ScrollUp() { - w.WriteRaw([]byte{0x1b, 'M'}) - return -} - -/* Title */ - -// SetTitle sets a title of terminal window. -func (w *VT100Writer) SetTitle(title string) { - titleBytes := []byte(title) - patterns := []struct { - from []byte - to []byte - }{ - { - from: []byte{0x13}, - to: []byte{}, - }, - { - from: []byte{0x07}, - to: []byte{}, - }, - } - for i := range patterns { - titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1) - } - - w.WriteRaw([]byte{0x1b, ']', '2', ';'}) - w.WriteRaw(titleBytes) - w.WriteRaw([]byte{0x07}) - return -} - -// ClearTitle clears a title of terminal window. -func (w *VT100Writer) ClearTitle() { - w.WriteRaw([]byte{0x1b, ']', '2', ';', 0x07}) - return -} - -/* Font */ - -// SetColor sets text and background colors. and specify whether text is bold. -func (w *VT100Writer) SetColor(fg, bg Color, bold bool) { - if bold { - w.SetDisplayAttributes(fg, bg, DisplayBold) - } else { - w.SetDisplayAttributes(fg, bg, DisplayDefaultFont) - } - return -} - -// SetDisplayAttributes to set VT100 display attributes. -func (w *VT100Writer) SetDisplayAttributes(fg, bg Color, attrs ...DisplayAttribute) { - w.WriteRaw([]byte{0x1b, '['}) // control sequence introducer - defer w.WriteRaw([]byte{'m'}) // final character - - var separator byte = ';' - for i := range attrs { - p, ok := displayAttributeParameters[attrs[i]] - if !ok { - continue - } - w.WriteRaw(p) - w.WriteRaw([]byte{separator}) - } - - f, ok := foregroundANSIColors[fg] - if !ok { - f = foregroundANSIColors[DefaultColor] - } - w.WriteRaw(f) - w.WriteRaw([]byte{separator}) - b, ok := backgroundANSIColors[bg] - if !ok { - b = backgroundANSIColors[DefaultColor] - } - w.WriteRaw(b) - return -} - -var displayAttributeParameters = map[DisplayAttribute][]byte{ - DisplayReset: {'0'}, - DisplayBold: {'1'}, - DisplayLowIntensity: {'2'}, - DisplayItalic: {'3'}, - DisplayUnderline: {'4'}, - DisplayBlink: {'5'}, - DisplayRapidBlink: {'6'}, - DisplayReverse: {'7'}, - DisplayInvisible: {'8'}, - DisplayCrossedOut: {'9'}, - DisplayDefaultFont: {'1', '0'}, -} - -var foregroundANSIColors = map[Color][]byte{ - DefaultColor: {'3', '9'}, - - // Low intensity. - Black: {'3', '0'}, - DarkRed: {'3', '1'}, - DarkGreen: {'3', '2'}, - Brown: {'3', '3'}, - DarkBlue: {'3', '4'}, - Purple: {'3', '5'}, - Cyan: {'3', '6'}, - LightGray: {'3', '7'}, - - // High intensity. - DarkGray: {'9', '0'}, - Red: {'9', '1'}, - Green: {'9', '2'}, - Yellow: {'9', '3'}, - Blue: {'9', '4'}, - Fuchsia: {'9', '5'}, - Turquoise: {'9', '6'}, - White: {'9', '7'}, -} - -var backgroundANSIColors = map[Color][]byte{ - DefaultColor: {'4', '9'}, - - // Low intensity. - Black: {'4', '0'}, - DarkRed: {'4', '1'}, - DarkGreen: {'4', '2'}, - Brown: {'4', '3'}, - DarkBlue: {'4', '4'}, - Purple: {'4', '5'}, - Cyan: {'4', '6'}, - LightGray: {'4', '7'}, - - // High intensity - DarkGray: {'1', '0', '0'}, - Red: {'1', '0', '1'}, - Green: {'1', '0', '2'}, - Yellow: {'1', '0', '3'}, - Blue: {'1', '0', '4'}, - Fuchsia: {'1', '0', '5'}, - Turquoise: {'1', '0', '6'}, - White: {'1', '0', '7'}, -} diff --git a/vendor/github.com/c-bata/go-prompt/output_windows.go b/vendor/github.com/c-bata/go-prompt/output_windows.go deleted file mode 100644 index 7418af3491..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_windows.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build windows - -package prompt - -import ( - "io" - - "github.com/mattn/go-colorable" -) - -// WindowsWriter is a ConsoleWriter implementation for Win32 console. -// Output is converted from VT100 escape sequences by mattn/go-colorable. -type WindowsWriter struct { - VT100Writer - out io.Writer -} - -// Flush to flush buffer -func (w *WindowsWriter) Flush() error { - _, err := w.out.Write(w.buffer) - if err != nil { - return err - } - w.buffer = []byte{} - return nil -} - -var _ ConsoleWriter = &WindowsWriter{} - -// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. -// This generates win32 control sequences. -func NewStandardOutputWriter() *WindowsWriter { - return &WindowsWriter{ - out: colorable.NewColorableStdout(), - } -} diff --git a/vendor/github.com/c-bata/go-prompt/posix_input.go b/vendor/github.com/c-bata/go-prompt/posix_input.go new file mode 100644 index 0000000000..ead496d97e --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/posix_input.go @@ -0,0 +1,265 @@ +// +build !windows + +package prompt + +import ( + "bytes" + "log" + "syscall" + "unsafe" + + "github.com/pkg/term/termios" +) + +const maxReadBytes = 1024 + +// PosixParser is a ConsoleParser implementation for POSIX environment. +type PosixParser struct { + fd int + origTermios syscall.Termios +} + +// Setup should be called before starting input +func (t *PosixParser) Setup() error { + // Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh. + if err := syscall.SetNonblock(t.fd, true); err != nil { + log.Println("[ERROR] Cannot set non blocking mode.") + return err + } + if err := t.setRawMode(); err != nil { + log.Println("[ERROR] Cannot set raw mode.") + return err + } + return nil +} + +// TearDown should be called after stopping input +func (t *PosixParser) TearDown() error { + if err := syscall.SetNonblock(t.fd, false); err != nil { + log.Println("[ERROR] Cannot set blocking mode.") + return err + } + if err := t.resetRawMode(); err != nil { + log.Println("[ERROR] Cannot reset from raw mode.") + return err + } + return nil +} + +// Read returns byte array. +func (t *PosixParser) Read() ([]byte, error) { + buf := make([]byte, maxReadBytes) + n, err := syscall.Read(syscall.Stdin, buf) + if err != nil { + return []byte{}, err + } + return buf[:n], nil +} + +func (t *PosixParser) setRawMode() error { + x := t.origTermios.Lflag + if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag { + // fd is already raw mode + return nil + } + var n syscall.Termios + if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil { + return err + } + n = t.origTermios + // "&^=" used like: https://play.golang.org/p/8eJw3JxS4O + n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG + n.Cc[syscall.VMIN] = 1 + n.Cc[syscall.VTIME] = 0 + termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n) + return nil +} + +func (t *PosixParser) resetRawMode() error { + if t.origTermios.Lflag == 0 { + return nil + } + return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios) +} + +// GetKey returns Key correspond to input byte codes. +func (t *PosixParser) GetKey(b []byte) Key { + for _, k := range asciiSequences { + if bytes.Equal(k.ASCIICode, b) { + return k.Key + } + } + return NotDefined +} + +// winsize is winsize struct got from the ioctl(2) system call. +type ioctlWinsize struct { + Row uint16 + Col uint16 + X uint16 // pixel value + Y uint16 // pixel value +} + +// GetWinSize returns WinSize object to represent width and height of terminal. +func (t *PosixParser) GetWinSize() *WinSize { + ws := &ioctlWinsize{} + retCode, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + uintptr(t.fd), + uintptr(syscall.TIOCGWINSZ), + uintptr(unsafe.Pointer(ws))) + + if int(retCode) == -1 { + panic(errno) + } + return &WinSize{ + Row: ws.Row, + Col: ws.Col, + } +} + +var asciiSequences = []*ASCIICode{ + {Key: Escape, ASCIICode: []byte{0x1b}}, + + {Key: ControlSpace, ASCIICode: []byte{0x00}}, + {Key: ControlA, ASCIICode: []byte{0x1}}, + {Key: ControlB, ASCIICode: []byte{0x2}}, + {Key: ControlC, ASCIICode: []byte{0x3}}, + {Key: ControlD, ASCIICode: []byte{0x4}}, + {Key: ControlE, ASCIICode: []byte{0x5}}, + {Key: ControlF, ASCIICode: []byte{0x6}}, + {Key: ControlG, ASCIICode: []byte{0x7}}, + {Key: ControlH, ASCIICode: []byte{0x8}}, + //{Key: ControlI, ASCIICode: []byte{0x9}}, + //{Key: ControlJ, ASCIICode: []byte{0xa}}, + {Key: ControlK, ASCIICode: []byte{0xb}}, + {Key: ControlL, ASCIICode: []byte{0xc}}, + {Key: ControlM, ASCIICode: []byte{0xd}}, + {Key: ControlN, ASCIICode: []byte{0xe}}, + {Key: ControlO, ASCIICode: []byte{0xf}}, + {Key: ControlP, ASCIICode: []byte{0x10}}, + {Key: ControlQ, ASCIICode: []byte{0x11}}, + {Key: ControlR, ASCIICode: []byte{0x12}}, + {Key: ControlS, ASCIICode: []byte{0x13}}, + {Key: ControlT, ASCIICode: []byte{0x14}}, + {Key: ControlU, ASCIICode: []byte{0x15}}, + {Key: ControlV, ASCIICode: []byte{0x16}}, + {Key: ControlW, ASCIICode: []byte{0x17}}, + {Key: ControlX, ASCIICode: []byte{0x18}}, + {Key: ControlY, ASCIICode: []byte{0x19}}, + {Key: ControlZ, ASCIICode: []byte{0x1a}}, + + {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, + {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, + {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, + {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, + {Key: Backspace, ASCIICode: []byte{0x7f}}, + + {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x30, 0x48}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, + {Key: End, ASCIICode: []byte{0x1b, 0x30, 0x46}}, + + {Key: Enter, ASCIICode: []byte{0xa}}, + {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e}}, + {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x7e}}, + {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x36, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x37, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x38, 0x7e}}, + {Key: Tab, ASCIICode: []byte{0x9}}, + {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, + {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, + {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, + {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, + {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console + + {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode + + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, + {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, + {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, + {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, + {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, + {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, + {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, + {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, + {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + + // Xterm + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x50}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51}}, + // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, // Conflicts with CPR response + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x32, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x32, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x32, 0x7e}}, + {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x32, 0x7e}}, + {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x32, 0x7e}}, + {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x32, 0x7e}}, + {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x32, 0x7e}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44}}, + + {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41}}, + {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x42}}, + {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43}}, + {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x44}}, + + // Tmux sends following keystrokes when control+arrow is pressed, but for + // Emacs ansi-term sends the same sequences for normal arrow keys. Consider + // it a normal arrow press, because that's more important. + {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x44}}, + + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt + + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +} + +var _ ConsoleParser = &PosixParser{} + +// NewStandardInputParser returns ConsoleParser object to read from stdin. +func NewStandardInputParser() *PosixParser { + return &PosixParser{ + fd: syscall.Stdin, + } +} diff --git a/vendor/github.com/c-bata/go-prompt/posix_output.go b/vendor/github.com/c-bata/go-prompt/posix_output.go new file mode 100644 index 0000000000..52c69d9465 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/posix_output.go @@ -0,0 +1,335 @@ +// +build !windows + +package prompt + +import ( + "strconv" + "syscall" +) + +// PosixWriter is a ConsoleWriter implementation for POSIX environment. +// To control terminal emulator, this outputs VT100 escape sequences. +type PosixWriter struct { + fd int + buffer []byte +} + +// WriteRaw to write raw byte array +func (w *PosixWriter) WriteRaw(data []byte) { + w.buffer = append(w.buffer, data...) + // Flush because sometimes the render is broken when a large amount data in buffer. + w.Flush() + return +} + +// Write to write byte array without control sequences +func (w *PosixWriter) Write(data []byte) { + w.WriteRaw(byteFilter(data, writeFilter)) + return +} + +// WriteRawStr to write raw string +func (w *PosixWriter) WriteRawStr(data string) { + w.WriteRaw([]byte(data)) + return +} + +// WriteStr to write string without control sequences +func (w *PosixWriter) WriteStr(data string) { + w.Write([]byte(data)) + return +} + +// Flush to flush buffer +func (w *PosixWriter) Flush() error { + _, err := syscall.Write(w.fd, w.buffer) + if err != nil { + return err + } + w.buffer = []byte{} + return nil +} + +/* Erase */ + +// EraseScreen erases the screen with the background colour and moves the cursor to home. +func (w *PosixWriter) EraseScreen() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a}) + return +} + +// EraseUp erases the screen from the current line up to the top of the screen. +func (w *PosixWriter) EraseUp() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a}) + return +} + +// EraseDown erases the screen from the current line down to the bottom of the screen. +func (w *PosixWriter) EraseDown() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4a}) + return +} + +// EraseStartOfLine erases from the current cursor position to the start of the current line. +func (w *PosixWriter) EraseStartOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b}) + return +} + +// EraseEndOfLine erases from the current cursor position to the end of the current line. +func (w *PosixWriter) EraseEndOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4b}) + return +} + +// EraseLine erases the entire current line. +func (w *PosixWriter) EraseLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b}) + return +} + +/* Cursor */ + +// ShowCursor stops blinking cursor and show. +func (w *PosixWriter) ShowCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68}) +} + +// HideCursor hides cursor. +func (w *PosixWriter) HideCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c}) + return +} + +// CursorGoTo sets the cursor position where subsequent text will begin. +func (w *PosixWriter) CursorGoTo(row, col int) { + if row == 0 && col == 0 { + // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. + w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48}) + return + } + r := strconv.Itoa(row) + c := strconv.Itoa(col) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(r)) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw([]byte(c)) + w.WriteRaw([]byte{0x48}) + return +} + +// CursorUp moves the cursor up by 'n' rows; the default count is 1. +func (w *PosixWriter) CursorUp(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorDown(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x41}) + return +} + +// CursorDown moves the cursor down by 'n' rows; the default count is 1. +func (w *PosixWriter) CursorDown(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorUp(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x42}) + return +} + +// CursorForward moves the cursor forward by 'n' columns; the default count is 1. +func (w *PosixWriter) CursorForward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorBackward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x43}) + return +} + +// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. +func (w *PosixWriter) CursorBackward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorForward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x44}) + return +} + +// AskForCPR asks for a cursor position report (CPR). +func (w *PosixWriter) AskForCPR() { + // CPR: Cursor Position Request. + w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e}) + w.Flush() + return +} + +// SaveCursor saves current cursor position. +func (w *PosixWriter) SaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x73}) + return +} + +// UnSaveCursor restores cursor position after a Save Cursor. +func (w *PosixWriter) UnSaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x75}) + return +} + +/* Scrolling */ + +// ScrollDown scrolls display down one line. +func (w *PosixWriter) ScrollDown() { + w.WriteRaw([]byte{0x1b, 0x44}) + return +} + +// ScrollUp scroll display up one line. +func (w *PosixWriter) ScrollUp() { + w.WriteRaw([]byte{0x1b, 0x4d}) + return +} + +/* Title */ + +// SetTitle sets a title of terminal window. +func (w *PosixWriter) SetTitle(title string) { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b}) + w.WriteRaw(byteFilter([]byte(title), setTextFilter)) + w.WriteRaw([]byte{0x07}) + return +} + +// ClearTitle clears a title of terminal window. +func (w *PosixWriter) ClearTitle() { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07}) + return +} + +/* Font */ + +// SetColor sets text and background colors. and specify whether text is bold. +func (w *PosixWriter) SetColor(fg, bg Color, bold bool) { + f, ok := foregroundANSIColors[fg] + if !ok { + f = foregroundANSIColors[DefaultColor] + } + b, ok := backgroundANSIColors[bg] + if !ok { + b = backgroundANSIColors[DefaultColor] + } + syscall.Write(syscall.Stdout, []byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d}) + w.WriteRaw([]byte{0x1b, 0x5b}) + if !bold { + w.WriteRaw([]byte{0x30, 0x3b}) + } + w.WriteRaw(f) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw(b) + if bold { + w.WriteRaw([]byte{0x3b, 0x31}) + } + w.WriteRaw([]byte{0x6d}) + return +} + +var foregroundANSIColors = map[Color][]byte{ + DefaultColor: {0x33, 0x39}, // 39 + + // Low intensity. + Black: {0x33, 0x30}, // 30 + DarkRed: {0x33, 0x31}, // 31 + DarkGreen: {0x33, 0x32}, // 32 + Brown: {0x33, 0x33}, // 33 + DarkBlue: {0x33, 0x34}, // 34 + Purple: {0x33, 0x35}, // 35 + Cyan: {0x33, 0x36}, //36 + LightGray: {0x33, 0x37}, //37 + + // High intensity. + DarkGray: {0x39, 0x30}, // 90 + Red: {0x39, 0x31}, // 91 + Green: {0x39, 0x32}, // 92 + Yellow: {0x39, 0x33}, // 93 + Blue: {0x39, 0x34}, // 94 + Fuchsia: {0x39, 0x35}, // 95 + Turquoise: {0x39, 0x36}, // 96 + White: {0x39, 0x37}, // 97 +} + +var backgroundANSIColors = map[Color][]byte{ + DefaultColor: {0x34, 0x39}, // 49 + + // Low intensity. + Black: {0x34, 0x30}, // 40 + DarkRed: {0x34, 0x31}, // 41 + DarkGreen: {0x34, 0x32}, // 42 + Brown: {0x34, 0x33}, // 43 + DarkBlue: {0x34, 0x34}, // 44 + Purple: {0x34, 0x35}, // 45 + Cyan: {0x34, 0x36}, // 46 + LightGray: {0x34, 0x37}, // 47 + + // High intensity + DarkGray: {0x31, 0x30, 0x30}, // 100 + Red: {0x31, 0x30, 0x31}, // 101 + Green: {0x31, 0x30, 0x32}, // 102 + Yellow: {0x31, 0x30, 0x33}, // 103 + Blue: {0x31, 0x30, 0x34}, // 104 + Fuchsia: {0x31, 0x30, 0x35}, // 105 + Turquoise: {0x31, 0x30, 0x36}, // 106 + White: {0x31, 0x30, 0x37}, // 107 +} + +func writeFilter(buf byte) bool { + return buf != 0x1b && buf != 0x3f +} + +func setTextFilter(buf byte) bool { + return buf != 0x1b && buf != 0x07 +} + +func byteFilter(buf []byte, fn ...func(b byte) bool) []byte { + if len(fn) == 0 { + return buf + } + ret := make([]byte, 0, len(buf)) + f := fn[0] + for i, n := range buf { + if f(n) { + ret = append(ret, buf[i]) + } + } + return byteFilter(ret, fn[1:]...) +} + +var _ ConsoleWriter = &PosixWriter{} + +// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. +func NewStandardOutputWriter() *PosixWriter { + return &PosixWriter{ + fd: syscall.Stdout, + } +} diff --git a/vendor/github.com/c-bata/go-prompt/prompt.go b/vendor/github.com/c-bata/go-prompt/prompt.go index 6f72944c34..74ce03b616 100644 --- a/vendor/github.com/c-bata/go-prompt/prompt.go +++ b/vendor/github.com/c-bata/go-prompt/prompt.go @@ -1,7 +1,6 @@ package prompt import ( - "bytes" "io/ioutil" "log" "os" @@ -20,15 +19,14 @@ type Completer func(Document) []Suggest // Prompt is core struct of go-prompt. type Prompt struct { - in ConsoleParser - buf *Buffer - renderer *Render - executor Executor - history *History - completion *CompletionManager - keyBindings []KeyBind - ASCIICodeBindings []ASCIICodeBind - keyBindMode KeyBindMode + in ConsoleParser + buf *Buffer + renderer *Render + executor Executor + history *History + completion *CompletionManager + keyBindings []KeyBind + keyBindMode KeyBindMode } // Exec is the struct contains user input context. @@ -67,8 +65,6 @@ func (p *Prompt) Run() { case b := <-bufCh: if shouldExit, e := p.feed(b); shouldExit { p.renderer.BreakLine(p.buf) - stopReadBufCh <- struct{}{} - stopHandleSignalCh <- struct{}{} return } else if e != nil { // Stop goroutine to run readBuffer function @@ -109,7 +105,31 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) { // completion completing := p.completion.Completing() - p.handleCompletionKeyBinding(key, completing) + switch key { + case Down: + if completing { + p.completion.Next() + } + case Tab, ControlI: + p.completion.Next() + case Up: + if completing { + p.completion.Previous() + } + case BackTab: + p.completion.Previous() + case ControlSpace: + return + default: + if s, ok := p.completion.GetSelectedSuggestion(); ok { + w := p.buf.Document().GetWordBeforeCursor() + if w != "" { + p.buf.DeleteBeforeCursor(len([]rune(w))) + } + p.buf.InsertText(s.Text, false, true) + } + p.completion.Reset() + } switch key { case Enter, ControlJ, ControlM: @@ -144,43 +164,10 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) { return } case NotDefined: - if p.handleASCIICodeBinding(b) { - return - } p.buf.InsertText(string(b), false, true) } - p.handleKeyBinding(key) - return -} - -func (p *Prompt) handleCompletionKeyBinding(key Key, completing bool) { - switch key { - case Down: - if completing { - p.completion.Next() - } - case Tab, ControlI: - p.completion.Next() - case Up: - if completing { - p.completion.Previous() - } - case BackTab: - p.completion.Previous() - default: - if s, ok := p.completion.GetSelectedSuggestion(); ok { - w := p.buf.Document().GetWordBeforeCursorUntilSeparator(p.completion.wordSeparator) - if w != "" { - p.buf.DeleteBeforeCursor(len([]rune(w))) - } - p.buf.InsertText(s.Text, false, true) - } - p.completion.Reset() - } -} - -func (p *Prompt) handleKeyBinding(key Key) { + // Key bindings for i := range commonKeyBindings { kb := commonKeyBindings[i] if kb.Key == key { @@ -204,17 +191,7 @@ func (p *Prompt) handleKeyBinding(key Key) { kb.Fn(p.buf) } } -} - -func (p *Prompt) handleASCIICodeBinding(b []byte) bool { - checked := false - for _, kb := range p.ASCIICodeBindings { - if bytes.Compare(kb.ASCIICode, b) == 0 { - kb.Fn(p.buf) - checked = true - } - } - return checked + return } // Input just returns user input text. @@ -242,7 +219,6 @@ func (p *Prompt) Input() string { case b := <-bufCh: if shouldExit, e := p.feed(b); shouldExit { p.renderer.BreakLine(p.buf) - stopReadBufCh <- struct{}{} return "" } else if e != nil { // Stop goroutine to run readBuffer function @@ -261,16 +237,16 @@ func (p *Prompt) Input() string { func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) { log.Printf("[INFO] readBuffer start") for { + time.Sleep(10 * time.Millisecond) select { case <-stopCh: log.Print("[INFO] stop readBuffer") return default: - if b, err := p.in.Read(); err == nil && !(len(b) == 1 && b[0] == 0) { + if b, err := p.in.Read(); err == nil { bufCh <- b } } - time.Sleep(10 * time.Millisecond) } } diff --git a/vendor/github.com/c-bata/go-prompt/render.go b/vendor/github.com/c-bata/go-prompt/render.go index 2d774aafca..e37867876e 100644 --- a/vendor/github.com/c-bata/go-prompt/render.go +++ b/vendor/github.com/c-bata/go-prompt/render.go @@ -1,11 +1,5 @@ package prompt -import ( - "runtime" - - "github.com/mattn/go-runewidth" -) - // Render to render prompt information from state of Buffer. type Render struct { out ConsoleWriter @@ -88,6 +82,7 @@ func (r *Render) renderWindowTooSmall() { r.out.EraseScreen() r.out.SetColor(DarkRed, White, false) r.out.WriteStr("Your console window is too small...") + r.out.Flush() return } @@ -99,7 +94,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { prefix := r.getCurrentPrefix() formatted, width := formatSuggestions( suggestions, - int(r.col)-runewidth.StringWidth(prefix)-1, // -1 means a width of scrollbar + int(r.col)-len(prefix)-1, // -1 means a width of scrollbar ) // +1 means a width of scrollbar. width++ @@ -111,10 +106,10 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight] r.prepareArea(windowHeight) - cursor := runewidth.StringWidth(prefix) + runewidth.StringWidth(buf.Document().TextBeforeCursor()) + cursor := len(prefix) + len(buf.Document().TextBeforeCursor()) x, _ := r.toPos(cursor) if x+width >= int(r.col) { - cursor = r.backward(cursor, x+width-int(r.col)) + r.out.CursorBackward(x + width - int(r.col)) } contentHeight := len(completions.tmp) @@ -153,10 +148,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { r.out.SetColor(DefaultColor, r.scrollbarBGColor, false) } r.out.WriteStr(" ") - r.out.SetColor(DefaultColor, DefaultColor, false) - - r.lineWrap(cursor + width) - r.backward(cursor+width, width) + r.out.CursorBackward(width) } if x+width >= int(r.col) { @@ -170,64 +162,53 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { // Render renders to the console. func (r *Render) Render(buffer *Buffer, completion *CompletionManager) { - // In situations where a pseudo tty is allocated (e.g. within a docker container), - // window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions. - if r.col == 0 { - return - } - defer r.out.Flush() - r.move(r.previousCursor, 0) - line := buffer.Text() prefix := r.getCurrentPrefix() - cursor := runewidth.StringWidth(prefix) + runewidth.StringWidth(line) + cursor := len(prefix) + len(line) - // prepare area - _, y := r.toPos(cursor) + // In situations where a psuedo tty is allocated (e.g. within a docker container), + // window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions. + if r.col > 0 { + // Erasing + r.clear(r.previousCursor) - h := y + 1 + int(completion.max) - if h > int(r.row) || completionMargin > int(r.col) { - r.renderWindowTooSmall() - return + // prepare area + _, y := r.toPos(cursor) + + h := y + 1 + int(completion.max) + if h > int(r.row) || completionMargin > int(r.col) { + r.renderWindowTooSmall() + return + } } // Rendering - r.out.HideCursor() - defer r.out.ShowCursor() - r.renderPrefix() r.out.SetColor(r.inputTextColor, r.inputBGColor, false) r.out.WriteStr(line) r.out.SetColor(DefaultColor, DefaultColor, false) - r.lineWrap(cursor) - r.out.EraseDown() - - cursor = r.backward(cursor, runewidth.StringWidth(line)-buffer.DisplayCursorPosition()) + cursor = r.backward(cursor, len(line)-buffer.CursorPosition) r.renderCompletion(buffer, completion) if suggest, ok := completion.GetSelectedSuggestion(); ok { - cursor = r.backward(cursor, runewidth.StringWidth(buffer.Document().GetWordBeforeCursorUntilSeparator(completion.wordSeparator))) + cursor = r.backward(cursor, len(buffer.Document().GetWordBeforeCursor())) r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false) r.out.WriteStr(suggest.Text) r.out.SetColor(DefaultColor, DefaultColor, false) - cursor += runewidth.StringWidth(suggest.Text) - rest := buffer.Document().TextAfterCursor() - r.out.WriteStr(rest) - cursor += runewidth.StringWidth(rest) - r.lineWrap(cursor) - - cursor = r.backward(cursor, runewidth.StringWidth(rest)) + cursor += len(suggest.Text) } + r.out.Flush() + r.previousCursor = cursor } // BreakLine to break line. func (r *Render) BreakLine(buffer *Buffer) { // Erasing and Render - cursor := runewidth.StringWidth(buffer.Document().TextBeforeCursor()) + runewidth.StringWidth(r.getCurrentPrefix()) + cursor := len(buffer.Document().TextBeforeCursor()) + len(r.getCurrentPrefix()) r.clear(cursor) r.renderPrefix() r.out.SetColor(r.inputTextColor, r.inputBGColor, false) @@ -238,40 +219,37 @@ func (r *Render) BreakLine(buffer *Buffer) { r.previousCursor = 0 } -// clear erases the screen from a beginning of input -// even if there is line break which means input length exceeds a window's width. func (r *Render) clear(cursor int) { - r.move(cursor, 0) + r.backward(cursor, cursor) r.out.EraseDown() } -// backward moves cursor to backward from a current cursor position -// regardless there is a line break. func (r *Render) backward(from, n int) int { return r.move(from, from-n) } -// move moves cursor to specified position from the beginning of input -// even if there is a line break. func (r *Render) move(from, to int) int { - fromX, fromY := r.toPos(from) + _, fromY := r.toPos(from) toX, toY := r.toPos(to) r.out.CursorUp(fromY - toY) - r.out.CursorBackward(fromX - toX) + r.out.WriteRaw([]byte{'\r'}) + r.out.CursorForward(toX) return to } // toPos returns the relative position from the beginning of the string. +// the coordinate system with the beginning of the string as (0,0) and the width as r.col. +// the cursor points to the next character, but it points to that character only at the right end (x == r.col - 1). +// x will not return 0 except for the first row. func (r *Render) toPos(cursor int) (x, y int) { col := int(r.col) - return cursor % col, cursor / col -} -func (r *Render) lineWrap(cursor int) { - if runtime.GOOS != "windows" && cursor > 0 && cursor%int(r.col) == 0 { - r.out.WriteRaw([]byte{'\n'}) + if cursor > 0 && cursor%col == 0 { + return col - 1, cursor/col - 1 } + + return cursor % col, cursor / col } func clamp(high, low, x float64) float64 { diff --git a/vendor/github.com/c-bata/go-prompt/shortcut.go b/vendor/github.com/c-bata/go-prompt/shortcut.go deleted file mode 100644 index 20fe71d085..0000000000 --- a/vendor/github.com/c-bata/go-prompt/shortcut.go +++ /dev/null @@ -1,43 +0,0 @@ -package prompt - -func dummyExecutor(in string) { return } - -// Input get the input data from the user and return it. -func Input(prefix string, completer Completer, opts ...Option) string { - pt := New(dummyExecutor, completer) - pt.renderer.prefixTextColor = DefaultColor - pt.renderer.prefix = prefix - - for _, opt := range opts { - if err := opt(pt); err != nil { - panic(err) - } - } - return pt.Input() -} - -// Choose to the shortcut of input function to select from string array. -// Deprecated: Maybe anyone want to use this. -func Choose(prefix string, choices []string, opts ...Option) string { - completer := newChoiceCompleter(choices, FilterHasPrefix) - pt := New(dummyExecutor, completer) - pt.renderer.prefixTextColor = DefaultColor - pt.renderer.prefix = prefix - - for _, opt := range opts { - if err := opt(pt); err != nil { - panic(err) - } - } - return pt.Input() -} - -func newChoiceCompleter(choices []string, filter Filter) Completer { - s := make([]Suggest, len(choices)) - for i := range choices { - s[i] = Suggest{Text: choices[i]} - } - return func(x Document) []Suggest { - return filter(s, x.GetWordBeforeCursor(), true) - } -} diff --git a/vendor/github.com/c-bata/go-prompt/windows_input.go b/vendor/github.com/c-bata/go-prompt/windows_input.go new file mode 100644 index 0000000000..13b332a775 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/windows_input.go @@ -0,0 +1,214 @@ +// +build windows + +package prompt + +import ( + "bytes" + "unicode/utf8" + + "github.com/mattn/go-tty" +) + +const maxReadBytes = 1024 + +// WindowsParser is a ConsoleParser implementation for Win32 console. +type WindowsParser struct { + tty *tty.TTY +} + +// Setup should be called before starting input +func (p *WindowsParser) Setup() error { + t, err := tty.Open() + if err != nil { + return err + } + p.tty = t + return nil +} + +// TearDown should be called after stopping input +func (p *WindowsParser) TearDown() error { + return p.tty.Close() +} + +// GetKey returns Key correspond to input byte codes. +func (p *WindowsParser) GetKey(b []byte) Key { + for _, k := range asciiSequences { + if bytes.Compare(k.ASCIICode, b) == 0 { + return k.Key + } + } + return NotDefined +} + +// Read returns byte array. +func (p *WindowsParser) Read() ([]byte, error) { + buf := make([]byte, maxReadBytes) + r, err := p.tty.ReadRune() + if err != nil { + return []byte{}, err + } + n := utf8.EncodeRune(buf[:], r) + for p.tty.Buffered() && n < maxReadBytes { + r, err := p.tty.ReadRune() + if err != nil { + break + } + n += utf8.EncodeRune(buf[n:], r) + } + return buf[:n], nil +} + +// GetWinSize returns WinSize object to represent width and height of terminal. +func (p *WindowsParser) GetWinSize() *WinSize { + w, h, err := p.tty.Size() + if err != nil { + panic(err) + } + return &WinSize{ + Row: uint16(h), + Col: uint16(w), + } +} + +var asciiSequences []*ASCIICode = []*ASCIICode{ + {Key: Escape, ASCIICode: []byte{0x1b}}, + + {Key: ControlSpace, ASCIICode: []byte{0x00}}, + {Key: ControlA, ASCIICode: []byte{0x1}}, + {Key: ControlB, ASCIICode: []byte{0x2}}, + {Key: ControlC, ASCIICode: []byte{0x3}}, + {Key: ControlD, ASCIICode: []byte{0x4}}, + {Key: ControlE, ASCIICode: []byte{0x5}}, + {Key: ControlF, ASCIICode: []byte{0x6}}, + {Key: ControlG, ASCIICode: []byte{0x7}}, + {Key: ControlH, ASCIICode: []byte{0x8}}, + //{Key: ControlI, ASCIICode: []byte{0x9}}, + //{Key: ControlJ, ASCIICode: []byte{0xa}}, + {Key: ControlK, ASCIICode: []byte{0xb}}, + {Key: ControlL, ASCIICode: []byte{0xc}}, + {Key: ControlM, ASCIICode: []byte{0xd}}, + {Key: ControlN, ASCIICode: []byte{0xe}}, + {Key: ControlO, ASCIICode: []byte{0xf}}, + {Key: ControlP, ASCIICode: []byte{0x10}}, + {Key: ControlQ, ASCIICode: []byte{0x11}}, + {Key: ControlR, ASCIICode: []byte{0x12}}, + {Key: ControlS, ASCIICode: []byte{0x13}}, + {Key: ControlT, ASCIICode: []byte{0x14}}, + {Key: ControlU, ASCIICode: []byte{0x15}}, + {Key: ControlV, ASCIICode: []byte{0x16}}, + {Key: ControlW, ASCIICode: []byte{0x17}}, + {Key: ControlX, ASCIICode: []byte{0x18}}, + {Key: ControlY, ASCIICode: []byte{0x19}}, + {Key: ControlZ, ASCIICode: []byte{0x1a}}, + + {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, + {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, + {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, + {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, + {Key: Backspace, ASCIICode: []byte{0x7f}}, + + {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x4f, 0x48}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x70}}, + {Key: End, ASCIICode: []byte{0x1b, 0x4f, 0x70}}, + + {Key: Enter, ASCIICode: []byte{0xa}}, + {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x02, 0x7e}}, + {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x05, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x04, 0x7e}}, + {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x7e}}, + {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x06, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x07, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x09, 0x7e}}, + {Key: Tab, ASCIICode: []byte{0x9}}, + {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, + {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x02, 0x7e}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, + {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, + {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, + {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console + + {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode + + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, + {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, + {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, + {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, + {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, + {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, + {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, + {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, + {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + + // Xterm + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x50}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x51}}, + // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}}, // Conflicts with CPR response + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x02, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x02, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x02, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x02, 0x7e}}, + {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x02, 0x7e}}, + {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x02, 0x7e}}, + {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x02, 0x7e}}, + {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x02, 0x7e}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5a}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5b}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5c}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5d}}, + + {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2a}}, + {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2b}}, + {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2c}}, + {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2d}}, + + // Tmux sends following keystrokes when control+arrow is pressed, but for + // Emacs ansi-term sends the same sequences for normal arrow keys. Consider + // it a normal arrow press, because that's more important. + {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x44}}, + + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt + + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +} + +// NewStandardInputParser returns ConsoleParser object to read from stdin. +func NewStandardInputParser() *WindowsParser { + return &WindowsParser{} +} diff --git a/vendor/github.com/c-bata/go-prompt/windows_output.go b/vendor/github.com/c-bata/go-prompt/windows_output.go new file mode 100644 index 0000000000..2281ab1563 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/windows_output.go @@ -0,0 +1,333 @@ +// +build windows + +package prompt + +import ( + "io" + "strconv" + + "github.com/mattn/go-colorable" +) + +// WindowsWriter is a ConsoleWriter implementation for Win32 console. +// Output is converted from VT100 escape sequences by mattn/go-colorable. +type WindowsWriter struct { + out io.Writer + buffer []byte +} + +// WriteRaw to write raw byte array +func (w *WindowsWriter) WriteRaw(data []byte) { + w.buffer = append(w.buffer, data...) + // Flush because sometimes the render is broken when a large amount data in buffer. + w.Flush() + return +} + +// Write to write byte array without control sequences +func (w *WindowsWriter) Write(data []byte) { + w.WriteRaw(byteFilter(data, writeFilter)) + return +} + +// WriteRawStr to write raw string +func (w *WindowsWriter) WriteRawStr(data string) { + w.WriteRaw([]byte(data)) + return +} + +// WriteStr to write string without control sequences +func (w *WindowsWriter) WriteStr(data string) { + w.Write([]byte(data)) + return +} + +// Flush to flush buffer +func (w *WindowsWriter) Flush() error { + _, err := w.out.Write(w.buffer) + if err != nil { + return err + } + w.buffer = []byte{} + return nil +} + +/* Erase */ + +// EraseScreen erases the screen with the background colour and moves the cursor to home. +func (w *WindowsWriter) EraseScreen() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a}) + return +} + +// EraseUp erases the screen from the current line up to the top of the screen. +func (w *WindowsWriter) EraseUp() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a}) + return +} + +// EraseDown erases the screen from the current line down to the bottom of the screen. +func (w *WindowsWriter) EraseDown() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4a}) + return +} + +// EraseStartOfLine erases from the current cursor position to the start of the current line. +func (w *WindowsWriter) EraseStartOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b}) + return +} + +// EraseEndOfLine erases from the current cursor position to the end of the current line. +func (w *WindowsWriter) EraseEndOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4b}) + return +} + +// EraseLine erases the entire current line. +func (w *WindowsWriter) EraseLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b}) + return +} + +/* Cursor */ + +// ShowCursor stops blinking cursor and show. +func (w *WindowsWriter) ShowCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68}) +} + +// HideCursor hides cursor. +func (w *WindowsWriter) HideCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c}) + return +} + +// CursorGoTo sets the cursor position where subsequent text will begin. +func (w *WindowsWriter) CursorGoTo(row, col int) { + if row == 0 && col == 0 { + // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. + w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48}) + return + } + r := strconv.Itoa(row) + c := strconv.Itoa(col) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(r)) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw([]byte(c)) + w.WriteRaw([]byte{0x48}) + return +} + +// CursorUp moves the cursor up by 'n' rows; the default count is 1. +func (w *WindowsWriter) CursorUp(n int) { + if n < 0 { + w.CursorDown(n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x41}) + return +} + +// CursorDown moves the cursor down by 'n' rows; the default count is 1. +func (w *WindowsWriter) CursorDown(n int) { + if n < 0 { + w.CursorUp(n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x42}) + return +} + +// CursorForward moves the cursor forward by 'n' columns; the default count is 1. +func (w *WindowsWriter) CursorForward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorBackward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x43}) + return +} + +// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. +func (w *WindowsWriter) CursorBackward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorForward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x44}) + return +} + +// AskForCPR asks for a cursor position report (CPR). +func (w *WindowsWriter) AskForCPR() { + // CPR: Cursor Position Request. + w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e}) + w.Flush() + return +} + +// SaveCursor saves current cursor position. +func (w *WindowsWriter) SaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x73}) + return +} + +// UnSaveCursor restores cursor position after a Save Cursor. +func (w *WindowsWriter) UnSaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x75}) + return +} + +/* Scrolling */ + +// ScrollDown scrolls display down one line. +func (w *WindowsWriter) ScrollDown() { + w.WriteRaw([]byte{0x1b, 0x44}) + return +} + +// ScrollUp scroll display up one line. +func (w *WindowsWriter) ScrollUp() { + w.WriteRaw([]byte{0x1b, 0x4d}) + return +} + +/* Title */ + +// SetTitle sets a title of terminal window. +func (w *WindowsWriter) SetTitle(title string) { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b}) + w.WriteRaw(byteFilter([]byte(title), setTextFilter)) + w.WriteRaw([]byte{0x07}) + return +} + +// ClearTitle clears a title of terminal window. +func (w *WindowsWriter) ClearTitle() { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07}) + return +} + +/* Font */ + +// SetColor sets text and background colors. and specify whether text is bold. +func (w *WindowsWriter) SetColor(fg, bg Color, bold bool) { + f, ok := foregroundANSIColors[fg] + if !ok { + f, _ = foregroundANSIColors[DefaultColor] + } + b, ok := backgroundANSIColors[bg] + if !ok { + b, _ = backgroundANSIColors[DefaultColor] + } + w.out.Write([]byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d}) + w.WriteRaw([]byte{0x1b, 0x5b}) + if !bold { + w.WriteRaw([]byte{0x30, 0x3b}) + } + w.WriteRaw(f) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw(b) + if bold { + w.WriteRaw([]byte{0x3b, 0x31}) + } + w.WriteRaw([]byte{0x6d}) + return +} + +var foregroundANSIColors = map[Color][]byte{ + DefaultColor: {0x33, 0x39}, // 39 + + // Low intensity. + Black: {0x33, 0x30}, // 30 + DarkRed: {0x33, 0x31}, // 31 + DarkGreen: {0x33, 0x32}, // 32 + Brown: {0x33, 0x33}, // 33 + DarkBlue: {0x33, 0x34}, // 34 + Purple: {0x33, 0x35}, // 35 + Cyan: {0x33, 0x36}, //36 + LightGray: {0x33, 0x37}, //37 + + // High intensity. + DarkGray: {0x39, 0x30}, // 90 + Red: {0x39, 0x31}, // 91 + Green: {0x39, 0x32}, // 92 + Yellow: {0x39, 0x33}, // 93 + Blue: {0x39, 0x34}, // 94 + Fuchsia: {0x39, 0x35}, // 95 + Turquoise: {0x39, 0x36}, // 96 + White: {0x39, 0x37}, // 97 +} + +var backgroundANSIColors = map[Color][]byte{ + DefaultColor: {0x34, 0x39}, // 49 + + // Low intensity. + Black: {0x34, 0x30}, // 40 + DarkRed: {0x34, 0x31}, // 41 + DarkGreen: {0x34, 0x32}, // 42 + Brown: {0x34, 0x33}, // 43 + DarkBlue: {0x34, 0x34}, // 44 + Purple: {0x34, 0x35}, // 45 + Cyan: {0x34, 0x36}, // 46 + LightGray: {0x34, 0x37}, // 47 + + // High intensity + DarkGray: {0x31, 0x30, 0x30}, // 100 + Red: {0x31, 0x30, 0x31}, // 101 + Green: {0x31, 0x30, 0x32}, // 102 + Yellow: {0x31, 0x30, 0x33}, // 103 + Blue: {0x31, 0x30, 0x34}, // 104 + Fuchsia: {0x31, 0x30, 0x35}, // 105 + Turquoise: {0x31, 0x30, 0x36}, // 106 + White: {0x31, 0x30, 0x37}, // 107 +} + +func writeFilter(buf byte) bool { + return buf != 0x1b && buf != 0x3f +} + +func setTextFilter(buf byte) bool { + return buf != 0x1b && buf != 0x07 +} + +func byteFilter(buf []byte, fn ...func(b byte) bool) []byte { + if len(fn) == 0 { + return buf + } + ret := make([]byte, 0, len(buf)) + f := fn[0] + for i, n := range buf { + if f(n) { + ret = append(ret, buf[i]) + } + } + return byteFilter(ret, fn[1:]...) +} + +var _ ConsoleWriter = &WindowsWriter{} + +// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. +func NewStandardOutputWriter() *WindowsWriter { + return &WindowsWriter{ + out: colorable.NewColorableStdout(), + } +} From 6da9ea4a77c1a62daa9e8cc6e88a1e311b10e232 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Sat, 29 Sep 2018 12:31:13 +0800 Subject: [PATCH 10/13] fix: climc prompt for macos --- Gopkg.lock | 6 +- Gopkg.toml | 2 +- .../github.com/c-bata/go-prompt/CHANGELOG.md | 23 +- .../c-bata/go-prompt/DEVELOPER_GUIDE.md | 155 ++++++++ vendor/github.com/c-bata/go-prompt/Gopkg.lock | 14 +- vendor/github.com/c-bata/go-prompt/Gopkg.toml | 4 - vendor/github.com/c-bata/go-prompt/Makefile | 5 - vendor/github.com/c-bata/go-prompt/README.md | 62 ++-- vendor/github.com/c-bata/go-prompt/bisect.go | 27 ++ vendor/github.com/c-bata/go-prompt/buffer.go | 60 ++-- .../github.com/c-bata/go-prompt/completion.go | 47 +-- .../{output.go => console_interface.go} | 69 ++-- .../github.com/c-bata/go-prompt/document.go | 309 ++-------------- vendor/github.com/c-bata/go-prompt/emacs.go | 1 - vendor/github.com/c-bata/go-prompt/input.go | 186 ++-------- .../c-bata/go-prompt/input_posix.go | 128 ------- .../c-bata/go-prompt/input_windows.go | 94 ----- vendor/github.com/c-bata/go-prompt/key.go | 3 - .../github.com/c-bata/go-prompt/key_bind.go | 39 +- .../c-bata/go-prompt/key_bind_func.go | 48 --- vendor/github.com/c-bata/go-prompt/option.go | 34 +- .../c-bata/go-prompt/output_posix.go | 35 -- .../c-bata/go-prompt/output_vt100.go | 333 ----------------- .../c-bata/go-prompt/output_windows.go | 36 -- .../c-bata/go-prompt/posix_input.go | 265 ++++++++++++++ .../c-bata/go-prompt/posix_output.go | 335 ++++++++++++++++++ vendor/github.com/c-bata/go-prompt/prompt.go | 98 ++--- vendor/github.com/c-bata/go-prompt/render.go | 94 ++--- .../github.com/c-bata/go-prompt/shortcut.go | 43 --- .../c-bata/go-prompt/windows_input.go | 214 +++++++++++ .../c-bata/go-prompt/windows_output.go | 333 +++++++++++++++++ 31 files changed, 1591 insertions(+), 1511 deletions(-) create mode 100644 vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md create mode 100644 vendor/github.com/c-bata/go-prompt/bisect.go rename vendor/github.com/c-bata/go-prompt/{output.go => console_interface.go} (58%) delete mode 100644 vendor/github.com/c-bata/go-prompt/input_posix.go delete mode 100644 vendor/github.com/c-bata/go-prompt/input_windows.go delete mode 100644 vendor/github.com/c-bata/go-prompt/key_bind_func.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_posix.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_vt100.go delete mode 100644 vendor/github.com/c-bata/go-prompt/output_windows.go create mode 100644 vendor/github.com/c-bata/go-prompt/posix_input.go create mode 100644 vendor/github.com/c-bata/go-prompt/posix_output.go delete mode 100644 vendor/github.com/c-bata/go-prompt/shortcut.go create mode 100644 vendor/github.com/c-bata/go-prompt/windows_input.go create mode 100644 vendor/github.com/c-bata/go-prompt/windows_output.go diff --git a/Gopkg.lock b/Gopkg.lock index 4776167d56..420644b68a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -87,12 +87,12 @@ version = "v0.5.0" [[projects]] - digest = "1:a2486f94bda33b301614ff5cfce102b7eed4c923fa650c2b83dba1fcf2117466" + digest = "1:1659cb76cbd08a29826688d006e7a3d9279d6ca8a12155acb7b20164958987d3" name = "github.com/c-bata/go-prompt" packages = ["."] pruneopts = "UT" - revision = "c52492ff1b386e5c0ba5271b5eaad165fab09eca" - version = "v0.2.2" + revision = "e99fbc797b795e0a7a94affc8d44f6a0350d85f0" + version = "v0.2.1" [[projects]] digest = "1:40098afbdd06a76dee4b6bcb85a22fed81ac9d2ebaf775c91e558c80f57aaff1" diff --git a/Gopkg.toml b/Gopkg.toml index dcb3fa9554..2cf2019028 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,7 +27,7 @@ [[constraint]] name = "github.com/c-bata/go-prompt" - version = "0.2.2" + version = "=0.2.1" [[constraint]] branch = "master" diff --git a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md index a1fcb273be..ac4b02dc1f 100644 --- a/vendor/github.com/c-bata/go-prompt/CHANGELOG.md +++ b/vendor/github.com/c-bata/go-prompt/CHANGELOG.md @@ -1,31 +1,10 @@ # Change Log -## v0.3.0 (2018/??/??) - -next release. - -## v0.2.2 (2018/06/28) - -### What's new? - -* Support CJK(Chinese, Japanese and Korean) and Cyrillic characters. -* Add OptionCompletionWordSeparator(x string) to customize insertion points for completions. - * To support this, text query functions by arbitrary word separator are added in Document (please see [here](https://github.com/c-bata/go-prompt/pull/79) for more details). -* Add FilePathCompleter to complete file path on your system. -* Add option to customize ascii code key bindings. -* Add GetWordAfterCursor method in Document. - -### Removed or Deprecated - -* SetColor method in ConsoleWriter is deprecated. Please use SetDisplayAttributes instead. -* prompt.Choose shortcut function is deprecated. - ## v0.2.1 (2018/02/14) ### What's New? -* ~~It seems that windows support is almost perfect.~~ - * A critical bug is found :( When you change a terminal window size, the layout will be broken because current implementation cannot catch signal for updating window size on Windows. +* It seems that windows support is almost perfect. ### Fixed diff --git a/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md b/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md new file mode 100644 index 0000000000..44f2c55bd2 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/DEVELOPER_GUIDE.md @@ -0,0 +1,155 @@ +# Developer Guide + +## Getting Started + +The most simple example is below. + +```go +package main + +import ( + "fmt" + + "github.com/c-bata/go-prompt" +) + +// executor executes command and print the output. +func executor(in string) { + fmt.Println("Your input: " + in) +} + +// completer returns the completion items from user input. +func completer(d prompt.Document) []prompt.Suggest { + s := []prompt.Suggest{ + {Text: "users", Description: "user table"}, + {Text: "sites", Description: "sites table"}, + {Text: "articles", Description: "articles table"}, + {Text: "comments", Description: "comments table"}, + } + return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true) +} + +func main() { + p := prompt.New( + executor, + completer, + prompt.OptionPrefix(">>> "), + prompt.OptionTitle("sql-prompt"), + ) + p.Run() +} +``` + +If you want to create CLI using go-prompt, I recommend you to look at the [source code of kube-prompt](https://github.com/c-bata/kube-prompt). +It is the most practical example. + + +## Options + +go-prompt has many color options. +It is difficult to describe by text. So please see below figure: + +![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png) + +* **OptionPrefixTextColor(prompt.Color)** : default `prompt.Blue` +* **OptionPrefixBackgroundColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionInputTextColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionInputBGColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionPreviewSuggestionTextColor(prompt.Color)** : default `prompt.Green` +* **OptionPreviewSuggestionBGColor(prompt.Color)** : default `prompt.DefaultColor` +* **OptionSuggestionTextColor(prompt.Color)** : default `prompt.White` +* **OptionSuggestionBGColor(prompt.Color)** : default `prompt.Cyan` +* **OptionSelectedSuggestionTextColor(prompt.Color)** : `default prompt.Black` +* **OptionSelectedSuggestionBGColor(prompt.Color)** : `default prompt.DefaultColor` +* **OptionDescriptionTextColor(prompt.Color)** : default `prompt.Black` +* **OptionDescriptionBGColor(prompt.Color)** : default `prompt.Turquoise` +* **OptionSelectedDescriptionTextColor(prompt.Color)** : default `prompt.White` +* **OptionSelectedDescriptionBGColor(prompt.Color)** : default `prompt.Cyan` +* **OptionScrollbarThumbColor** : `prompt.DarkGray` +* **OptionScrollbarBGColor** : `prompt.Cyan` + +**Other Options** + +#### `OptionTitle(string)` : default `""` +Option to set a title that wll be displayed on the header bar of terminal. + +#### `OptionHistory([]string)` : default `[]string{}` +Option to set history. + +#### `OptionPrefix(string)` : default `"> "` +Option to set prefix string. + +#### `OptionLivePrefix(func() (prefix string, useLivePrefix bool))` : default `nil` +Option to set a callback function for updating prefix string dynamically. + +#### `OptionMaxSuggestions(x uint16)` : default `6` +The max number of displayed suggestions. + +#### `OptionParser(prompt.ConsoleParser)` : default `VT100Parser` +To set a custom ConsoleParser object. +The argument should implement ConsoleParser interface. + +#### `OptionWriter(prompt.ConsoleWriter)` : default `VT100Writer` +To set a custom ConsoleWriter object. +The argument should implement ConsoleWriter interface. + +#### `SwitchKeyBindMode(prompt.KeyBindMode)` : default `prompt.EmacsKeyBindMode` +To set a key bind mode. + +#### `OptionAddKeyBind(...KeyBind)` : default `[]KeyBind{}` +To set a custom key bind. + +## Architecture of go-prompt + +*Caution: This section is WIP.* + +This is a short description of go-prompt implementation. +go-prompt consists of three parts. + +1. Input parser +2. Emulate user input with Buffer object. +3. Render buffer object. + +### Input Parser + +![input-parser animation](https://github.com/c-bata/assets/raw/master/go-prompt/input-parser.gif) + +Input Parser only supports vt100-compatible console now. + +* Set as the raw mode. +* Read a standard input. +* Parse to byte array + +### Emulate user input + +go-prompt contains Buffer class. +It represents input state by handling a key input by user. + +`Buffer` object has text and cursor position. + +**TODO prepare the sample of buffer** + +```go +package main + +import "github.com/c-bata/go-prompt" + +func main() { + b := prompt.NewBuffer() + ... wip +} +``` + +### Renderer + +`Renderer` object renders a buffer object. + +**TODO prepare the sample of brender** + +```go +package main +``` + +the output is below: + +**TODO prepare a screen shot** diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.lock b/vendor/github.com/c-bata/go-prompt/Gopkg.lock index 1b6866bfcb..0228094527 100644 --- a/vendor/github.com/c-bata/go-prompt/Gopkg.lock +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.lock @@ -13,33 +13,27 @@ revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" -[[projects]] - branch = "master" - name = "github.com/mattn/go-runewidth" - packages = ["."] - revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" - [[projects]] branch = "master" name = "github.com/mattn/go-tty" packages = ["."] - revision = "931426f7535ac39720c8909d70ece5a41a2502a6" + revision = "c1750293025292316a611ae8f632d9791515e51c" [[projects]] branch = "master" name = "github.com/pkg/term" packages = ["termios"] - revision = "cda20d4ac917ad418d86e151eff439648b06185b" + revision = "b1f72af2d63057363398bec5873d16a98b453312" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1" + revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d6a0ea9e49092cfd8cb3d6077c97a938de7c39195b83828dae2a0befdd207ffd" + inputs-digest = "6c442f55617e93df75aa1ee2fd2b36cfab23003fded4723be56c6b6fd0545c56" solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/c-bata/go-prompt/Gopkg.toml b/vendor/github.com/c-bata/go-prompt/Gopkg.toml index 903f53b251..fe31c575ad 100644 --- a/vendor/github.com/c-bata/go-prompt/Gopkg.toml +++ b/vendor/github.com/c-bata/go-prompt/Gopkg.toml @@ -32,7 +32,3 @@ [[constraint]] branch = "master" name = "github.com/pkg/term" - -[[constraint]] - branch = "master" - name = "github.com/mattn/go-runewidth" diff --git a/vendor/github.com/c-bata/go-prompt/Makefile b/vendor/github.com/c-bata/go-prompt/Makefile index 3cb6edfe63..caf263cabc 100644 --- a/vendor/github.com/c-bata/go-prompt/Makefile +++ b/vendor/github.com/c-bata/go-prompt/Makefile @@ -21,11 +21,6 @@ lint: ## Run golint and go vet. test: ## Run the tests. @go test . -.PHONY: coverage -cover: ## Run the tests. - @go test -coverprofile=coverage.o - @go tool cover -func=coverage.o - .PHONY: race-test race-test: ## Checking the race condition. @go test -race . diff --git a/vendor/github.com/c-bata/go-prompt/README.md b/vendor/github.com/c-bata/go-prompt/README.md index a949b8b9d8..e66ae425e0 100644 --- a/vendor/github.com/c-bata/go-prompt/README.md +++ b/vendor/github.com/c-bata/go-prompt/README.md @@ -1,10 +1,7 @@ # go-prompt -[![Go Report Card](https://goreportcard.com/badge/github.com/c-bata/go-prompt)](https://goreportcard.com/report/github.com/c-bata/go-prompt) -![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square) - -A library for building powerful interactive prompts inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit), -making it easier to build cross-platform command line tools using Go. +Library for building a powerful interactive prompt, inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit). +Easy building a multi-platform binary of the command line tools because written in Golang. ```go package main @@ -30,15 +27,15 @@ func main() { } ``` + #### Projects using go-prompt * [c-bata/kube-prompt : An interactive kubernetes client featuring auto-complete written in Go.](https://github.com/c-bata/kube-prompt) * [rancher/cli : The Rancher Command Line Interface (CLI)is a unified tool to manage your Rancher server](https://github.com/rancher/cli) -* [kubicorn/kubicorn : Simple, cloud native infrastructure for Kubernetes.](https://github.com/kubicorn/kubicorn) +* [kris-nova/kubicorn : Simple. Cloud Native. Kubernetes. Infrastructure.](https://github.com/kris-nova/kubicorn) * [cch123/asm-cli : Interactive shell of assembly language(X86/X64) based on unicorn and rasm2](https://github.com/cch123/asm-cli) * [ktr0731/evans : more expressive universal gRPC client](https://github.com/ktr0731/evans) -* [CrushedPixel/moshpit: A Command-line tool for datamoshing.](https://github.com/CrushedPixel/moshpit) -* (If you create a CLI utility using go-prompt and want your own project to be listed here, please submit a GitHub issue.) +* (If you create a CLI using go-prompt and want your own project to be listed here, Please submit a Github Issue.) ## Features @@ -50,52 +47,54 @@ func main() { ### Flexible options -go-prompt provides many options. Please check [option section of GoDoc](https://godoc.org/github.com/c-bata/go-prompt#Option) for more details. +go-prompt provides many options. All options are listed in [Developer Guide](./DEVELOPER_GUIDE.md). [![options](https://github.com/c-bata/assets/raw/master/go-prompt/prompt-options.png)](#flexible-options) ### Keyboard Shortcuts -Emacs-like keyboard shortcuts are available by default (these also are the default shortcuts in Bash shell). +Emacs-like keyboard shortcut is available by default (it's also default shortcuts in Bash shell). You can customize and expand these shortcuts. [![keyboard shortcuts](https://github.com/c-bata/assets/raw/master/go-prompt/keyboard-shortcuts.gif)](#keyboard-shortcuts) -Key Binding | Description ----------------------|--------------------------------------------------------- -Ctrl + A | Go to the beginning of the line (Home) -Ctrl + E | Go to the end of the line (End) -Ctrl + P | Previous command (Up arrow) -Ctrl + N | Next command (Down arrow) -Ctrl + F | Forward one character -Ctrl + B | Backward one character -Ctrl + D | Delete character under the cursor -Ctrl + H | Delete character before the cursor (Backspace) -Ctrl + W | Cut the word before the cursor to the clipboard -Ctrl + K | Cut the line after the cursor to the clipboard -Ctrl + U | Cut the line before the cursor to the clipboard -Ctrl + L | Clear the screen +KeyBinding | Description +--------------------|--------------------------------------------------------- +Ctrl + A | Go to the beginning of the line (Home) +Ctrl + E | Go to the End of the line (End) +Ctrl + P | Previous command (Up arrow) +Ctrl + N | Next command (Down arrow) +Ctrl + F | Forward one character +Ctrl + B | Backward one character +Ctrl + D | Delete character under the cursor +Ctrl + H | Delete character before the cursor (Backspace) +Ctrl + W | Cut the Word before the cursor to the clipboard. +Ctrl + K | Cut the Line after the cursor to the clipboard. +Ctrl + U | Cut/delete the Line before the cursor to the clipboard. +Ctrl + L | Clear the screen ### History -You can use Up arrow and Down arrow to walk through the history of commands executed. +You can use up-arrow and down-arrow to walk through the history of commands executed. [![History](https://github.com/c-bata/assets/raw/master/go-prompt/history.gif)](#history) + ### Multiple platform support -We have confirmed go-prompt works fine in the following terminals: +We confirmed following terminals * iTerm2 (macOS) * Terminal.app (macOS) * Command Prompt (Windows) -* gnome-terminal (Ubuntu) +* GNU Terminal (Ubuntu) + ## Links +* [Developer Guide](./DEVELOPER_GUIDE.md). * [Change Log](./CHANGELOG.md) -* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt) -* [gocover.io](https://gocover.io/github.com/c-bata/go-prompt) +* [GoDoc](http://godoc.org/github.com/c-bata/go-prompt). ## Author @@ -105,6 +104,7 @@ Masashi Shibata * Github: [@c-bata](https://github.com/c-bata/) * Facebook: [Masashi Shibata](https://www.facebook.com/masashi.cbata) -## License +## LICENSE + +This software is licensed under the MIT License (See [LICENSE](./LICENSE) ). -This software is licensed under the MIT license, see [LICENSE](./LICENSE) for more information. diff --git a/vendor/github.com/c-bata/go-prompt/bisect.go b/vendor/github.com/c-bata/go-prompt/bisect.go new file mode 100644 index 0000000000..2bcef3a5be --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/bisect.go @@ -0,0 +1,27 @@ +package prompt + +import "sort" + +// BisectLeft to Locate the insertion point for v in a to maintain sorted order. +func BisectLeft(a []int, v int) int { + return bisectLeftRange(a, v, 0, len(a)) +} + +func bisectLeftRange(a []int, v int, lo, hi int) int { + s := a[lo:hi] + return sort.Search(len(s), func(i int) bool { + return s[i] >= v + }) +} + +// BisectRight to Locate the insertion point for v in a to maintain sorted order. +func BisectRight(a []int, v int) int { + return bisectRightRange(a, v, 0, len(a)) +} + +func bisectRightRange(a []int, v int, lo, hi int) int { + s := a[lo:hi] + return sort.Search(len(s), func(i int) bool { + return s[i] > v + }) +} diff --git a/vendor/github.com/c-bata/go-prompt/buffer.go b/vendor/github.com/c-bata/go-prompt/buffer.go index 6c29b461e3..929d2fcecb 100644 --- a/vendor/github.com/c-bata/go-prompt/buffer.go +++ b/vendor/github.com/c-bata/go-prompt/buffer.go @@ -9,7 +9,7 @@ import ( type Buffer struct { workingLines []string // The working lines. Similar to history workingIndex int - cursorPosition int + CursorPosition int cacheDocument *Document preferredColumn int // Remember the original column for the next up/down movement. } @@ -23,25 +23,19 @@ func (b *Buffer) Text() string { func (b *Buffer) Document() (d *Document) { if b.cacheDocument == nil || b.cacheDocument.Text != b.Text() || - b.cacheDocument.cursorPosition != b.cursorPosition { + b.cacheDocument.CursorPosition != b.CursorPosition { b.cacheDocument = &Document{ Text: b.Text(), - cursorPosition: b.cursorPosition, + CursorPosition: b.CursorPosition, } } return b.cacheDocument } -// DisplayCursorPosition returns the cursor position on rendered text on terminal emulators. -// So if Document is "日本(cursor)語", DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. -func (b *Buffer) DisplayCursorPosition() int { - return b.Document().DisplayCursorPosition() -} - // InsertText insert string from current line. func (b *Buffer) InsertText(v string, overwrite bool, moveCursor bool) { or := []rune(b.Text()) - oc := b.cursorPosition + oc := b.CursorPosition if overwrite { overwritten := string(or[oc : oc+len(v)]) @@ -55,15 +49,15 @@ func (b *Buffer) InsertText(v string, overwrite bool, moveCursor bool) { } if moveCursor { - b.cursorPosition += len([]rune(v)) + b.CursorPosition += len([]rune(v)) } } -// SetText method to set text and update cursorPosition. +// SetText method to set text and update CursorPosition. // (When doing this, make sure that the cursor_position is valid for this text. // text/cursor_position should be consistent at any time, otherwise set a Document instead.) func (b *Buffer) setText(v string) { - if b.cursorPosition > len([]rune(v)) { + if b.CursorPosition > len([]rune(v)) { log.Print("[ERROR] The length of input value should be shorter than the position of cursor.") } o := b.workingLines[b.workingIndex] @@ -78,11 +72,11 @@ func (b *Buffer) setText(v string) { // Set cursor position. Return whether it changed. func (b *Buffer) setCursorPosition(p int) { - o := b.cursorPosition + o := b.CursorPosition if p > 0 { - b.cursorPosition = p + b.CursorPosition = p } else { - b.cursorPosition = 0 + b.CursorPosition = 0 } if p != o { // Cursor position is changed. @@ -92,21 +86,21 @@ func (b *Buffer) setCursorPosition(p int) { func (b *Buffer) setDocument(d *Document) { b.cacheDocument = d - b.setCursorPosition(d.cursorPosition) // Call before setText because setText check the relation between cursorPosition and line length. + b.setCursorPosition(d.CursorPosition) // Call before setText because setText check the relation between cursorPosition and line length. b.setText(d.Text) } // CursorLeft move to left on the current line. func (b *Buffer) CursorLeft(count int) { l := b.Document().GetCursorLeftPosition(count) - b.cursorPosition += l + b.CursorPosition += l return } // CursorRight move to right on the current line. func (b *Buffer) CursorRight(count int) { l := b.Document().GetCursorRightPosition(count) - b.cursorPosition += l + b.CursorPosition += l return } @@ -117,7 +111,7 @@ func (b *Buffer) CursorUp(count int) { if b.preferredColumn == -1 { // -1 means nil orig = b.Document().CursorPositionCol() } - b.cursorPosition += b.Document().GetCursorUpPosition(count, orig) + b.CursorPosition += b.Document().GetCursorUpPosition(count, orig) // Remember the original column for the next up/down movement. b.preferredColumn = orig @@ -130,7 +124,7 @@ func (b *Buffer) CursorDown(count int) { if b.preferredColumn == -1 { // -1 means nil orig = b.Document().CursorPositionCol() } - b.cursorPosition += b.Document().GetCursorDownPosition(count, orig) + b.CursorPosition += b.Document().GetCursorDownPosition(count, orig) // Remember the original column for the next up/down movement. b.preferredColumn = orig @@ -143,15 +137,15 @@ func (b *Buffer) DeleteBeforeCursor(count int) (deleted string) { } r := []rune(b.Text()) - if b.cursorPosition > 0 { - start := b.cursorPosition - count + if b.CursorPosition > 0 { + start := b.CursorPosition - count if start < 0 { start = 0 } - deleted = string(r[start:b.cursorPosition]) + deleted = string(r[start:b.CursorPosition]) b.setDocument(&Document{ - Text: string(r[:start]) + string(r[b.cursorPosition:]), - cursorPosition: b.cursorPosition - len([]rune(deleted)), + Text: string(r[:start]) + string(r[b.CursorPosition:]), + CursorPosition: b.CursorPosition - len([]rune(deleted)), }) } return @@ -169,9 +163,9 @@ func (b *Buffer) NewLine(copyMargin bool) { // Delete specified number of characters and Return the deleted text. func (b *Buffer) Delete(count int) (deleted string) { r := []rune(b.Text()) - if b.cursorPosition < len(r) { + if b.CursorPosition < len(r) { deleted = b.Document().TextAfterCursor()[:count] - b.setText(string(r[:b.cursorPosition]) + string(r[b.cursorPosition+len(deleted):])) + b.setText(string(r[:b.CursorPosition]) + string(r[b.CursorPosition+len(deleted):])) } return } @@ -179,7 +173,7 @@ func (b *Buffer) Delete(count int) (deleted string) { // JoinNextLine joins the next line to the current one by deleting the line ending after the current line. func (b *Buffer) JoinNextLine(separator string) { if !b.Document().OnLastLine() { - b.cursorPosition += b.Document().GetEndOfLinePosition() + b.CursorPosition += b.Document().GetEndOfLinePosition() b.Delete(1) // Remove spaces b.setText(b.Document().TextBeforeCursor() + separator + strings.TrimLeft(b.Document().TextAfterCursor(), " ")) @@ -188,10 +182,10 @@ func (b *Buffer) JoinNextLine(separator string) { // SwapCharactersBeforeCursor swaps the last two characters before the cursor. func (b *Buffer) SwapCharactersBeforeCursor() { - if b.cursorPosition >= 2 { - x := b.Text()[b.cursorPosition-2 : b.cursorPosition-1] - y := b.Text()[b.cursorPosition-1 : b.cursorPosition] - b.setText(b.Text()[:b.cursorPosition-2] + y + x + b.Text()[b.cursorPosition:]) + if b.CursorPosition >= 2 { + x := b.Text()[b.CursorPosition-2 : b.CursorPosition-1] + y := b.Text()[b.CursorPosition-1 : b.CursorPosition] + b.setText(b.Text()[:b.CursorPosition-2] + y + x + b.Text()[b.CursorPosition:]) } } diff --git a/vendor/github.com/c-bata/go-prompt/completion.go b/vendor/github.com/c-bata/go-prompt/completion.go index 5f698034bf..0c0f54bf81 100644 --- a/vendor/github.com/c-bata/go-prompt/completion.go +++ b/vendor/github.com/c-bata/go-prompt/completion.go @@ -3,21 +3,16 @@ package prompt import ( "log" "strings" - - "github.com/mattn/go-runewidth" ) const ( - shortenSuffix = "..." - leftPrefix = " " - leftSuffix = " " - rightPrefix = " " - rightSuffix = " " -) - -var ( - leftMargin = runewidth.StringWidth(leftPrefix + leftSuffix) - rightMargin = runewidth.StringWidth(rightPrefix + rightSuffix) + shortenSuffix = "..." + leftPrefix = " " + leftSuffix = " " + rightPrefix = " " + rightSuffix = " " + leftMargin = len(leftPrefix + leftSuffix) + rightMargin = len(rightPrefix + rightSuffix) completionMargin = leftMargin + rightMargin ) @@ -35,7 +30,6 @@ type CompletionManager struct { completer Completer verticalScroll int - wordSeparator string } // GetSelectedSuggestion returns the selected item. @@ -108,26 +102,17 @@ func (c *CompletionManager) update() { } } -func deleteBreakLineCharacters(s string) string { - s = strings.Replace(s, "\n", "", -1) - s = strings.Replace(s, "\r", "", -1) - return s -} - func formatTexts(o []string, max int, prefix, suffix string) (new []string, width int) { l := len(o) n := make([]string, l) - lenPrefix := runewidth.StringWidth(prefix) - lenSuffix := runewidth.StringWidth(suffix) - lenShorten := runewidth.StringWidth(shortenSuffix) + lenPrefix := len([]rune(prefix)) + lenSuffix := len([]rune(suffix)) + lenShorten := len(shortenSuffix) min := lenPrefix + lenSuffix + lenShorten for i := 0; i < l; i++ { - o[i] = deleteBreakLineCharacters(o[i]) - - w := runewidth.StringWidth(o[i]) - if width < w { - width = w + if width < len([]rune(o[i])) { + width = len([]rune(o[i])) } } @@ -143,15 +128,13 @@ func formatTexts(o []string, max int, prefix, suffix string) (new []string, widt } for i := 0; i < l; i++ { - x := runewidth.StringWidth(o[i]) + r := []rune(o[i]) + x := len(r) if x <= width { spaces := strings.Repeat(" ", width-x) n[i] = prefix + o[i] + spaces + suffix } else if x > width { - x := runewidth.Truncate(o[i], width, shortenSuffix) - // When calling runewidth.Truncate("您好xxx您好xxx", 11, "...") returns "您好xxx..." - // But the length of this result is 10. So we need fill right using runewidth.FillRight. - n[i] = prefix + runewidth.FillRight(x, width) + suffix + n[i] = prefix + string(r[:width-lenShorten]) + shortenSuffix + suffix } } return n, lenPrefix + width + lenSuffix diff --git a/vendor/github.com/c-bata/go-prompt/output.go b/vendor/github.com/c-bata/go-prompt/console_interface.go similarity index 58% rename from vendor/github.com/c-bata/go-prompt/output.go rename to vendor/github.com/c-bata/go-prompt/console_interface.go index 5afb81c090..1215359a77 100644 --- a/vendor/github.com/c-bata/go-prompt/output.go +++ b/vendor/github.com/c-bata/go-prompt/console_interface.go @@ -1,90 +1,63 @@ package prompt -// DisplayAttribute represents display attributes like Blinking, Bold, Italic and so on. -type DisplayAttribute int - -const ( - // DisplayReset reset all display attributes. - DisplayReset DisplayAttribute = iota - // DisplayBold set bold or increases intensity. - DisplayBold - // DisplayLowIntensity decreases intensity. Not widely supported. - DisplayLowIntensity - // DisplayItalic set italic. Not widely supported. - DisplayItalic - // DisplayUnderline set underline - DisplayUnderline - // DisplayBlink set blink (less than 150 per minute). - DisplayBlink - // DisplayRapidBlink set blink (more than 150 per minute). Not widely supported. - DisplayRapidBlink - // DisplayReverse swap foreground and background colors. - DisplayReverse - // DisplayInvisible set invisible. Not widely supported. - DisplayInvisible - // DisplayCrossedOut set characters legible, but marked for deletion. Not widely supported. - DisplayCrossedOut - // DisplayDefaultFont set primary(default) font - DisplayDefaultFont -) +// WinSize represents the width and height of terminal. +type WinSize struct { + Row uint16 + Col uint16 +} // Color represents color on terminal. type Color int const ( - // DefaultColor represents a default color. DefaultColor Color = iota // Low intensity - - // Black represents a black. Black - // DarkRed represents a dark red. DarkRed - // DarkGreen represents a dark green. DarkGreen - // Brown represents a brown. Brown - // DarkBlue represents a dark blue. DarkBlue - // Purple represents a purple. Purple - // Cyan represents a cyan. Cyan - // LightGray represents a light gray. LightGray // High intensity - - // DarkGray represents a dark gray. DarkGray - // Red represents a red. Red - // Green represents a green. Green - // Yellow represents a yellow. Yellow - // Blue represents a blue. Blue - // Fuchsia represents a fuchsia. Fuchsia - // Turquoise represents a turquoise. Turquoise - // White represents a white. White ) +// ConsoleParser is an interface to abstract input layer. +type ConsoleParser interface { + // Setup should be called before starting input + Setup() error + // TearDown should be called after stopping input + TearDown() error + // GetKey returns Key correspond to input byte codes. + GetKey(b []byte) Key + // GetWinSize returns WinSize object to represent width and height of terminal. + GetWinSize() *WinSize + // Read returns byte array. + Read() ([]byte, error) +} + // ConsoleWriter is an interface to abstract output layer. type ConsoleWriter interface { /* Write */ // WriteRaw to write raw byte array. WriteRaw(data []byte) - // Write to write safety byte array by removing control sequences. + // Write to write byte array without control sequences. Write(data []byte) // WriteStr to write raw string. WriteRawStr(data string) - // WriteStr to write safety string by removing control sequences. + // WriteStr to write string without control sequences. WriteStr(data string) // Flush to flush buffer. Flush() error diff --git a/vendor/github.com/c-bata/go-prompt/document.go b/vendor/github.com/c-bata/go-prompt/document.go index 06b436a21d..94a410e229 100644 --- a/vendor/github.com/c-bata/go-prompt/document.go +++ b/vendor/github.com/c-bata/go-prompt/document.go @@ -1,41 +1,24 @@ package prompt import ( - "sort" "strings" "unicode/utf8" - - "github.com/mattn/go-runewidth" ) // Document has text displayed in terminal and cursor position. type Document struct { - Text string - // This represents a index in a rune array of Document.Text. - // So if Document is "日本(cursor)語", cursorPosition is 2. - // But DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. - cursorPosition int + Text string + CursorPosition int } // NewDocument return the new empty document. func NewDocument() *Document { return &Document{ Text: "", - cursorPosition: 0, + CursorPosition: 0, } } -// DisplayCursorPosition returns the cursor position on rendered text on terminal emulators. -// So if Document is "日本(cursor)語", DisplayedCursorPosition returns 4 because '日' and '本' are double width characters. -func (d *Document) DisplayCursorPosition() int { - var position int - runes := []rune(d.Text)[:d.cursorPosition] - for i := range runes { - position += runewidth.RuneWidth(runes[i]) - } - return position -} - // GetCharRelativeToCursor return character relative to cursor position, or empty string func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { s := d.Text @@ -44,7 +27,7 @@ func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { for len(s) > 0 { cnt++ r, size := utf8.DecodeRuneInString(s) - if cnt == d.cursorPosition+offset { + if cnt == d.CursorPosition+offset { return r } s = s[size:] @@ -55,13 +38,13 @@ func (d *Document) GetCharRelativeToCursor(offset int) (r rune) { // TextBeforeCursor returns the text before the cursor. func (d *Document) TextBeforeCursor() string { r := []rune(d.Text) - return string(r[:d.cursorPosition]) + return string(r[:d.CursorPosition]) } // TextAfterCursor returns the text after the cursor. func (d *Document) TextAfterCursor() string { r := []rune(d.Text) - return string(r[d.cursorPosition:]) + return string(r[d.CursorPosition:]) } // GetWordBeforeCursor returns the word before the cursor. @@ -71,13 +54,6 @@ func (d *Document) GetWordBeforeCursor() string { return x[d.FindStartOfPreviousWord():] } -// GetWordAfterCursor returns the word after the cursor. -// If we have whitespace after the cursor this returns an empty string. -func (d *Document) GetWordAfterCursor() string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWord()] -} - // GetWordBeforeCursorWithSpace returns the word before the cursor. // Unlike GetWordBeforeCursor, it returns string containing space func (d *Document) GetWordBeforeCursorWithSpace() string { @@ -85,46 +61,16 @@ func (d *Document) GetWordBeforeCursorWithSpace() string { return x[d.FindStartOfPreviousWordWithSpace():] } -// GetWordAfterCursorWithSpace returns the word after the cursor. -// Unlike GetWordAfterCursor, it returns string containing space -func (d *Document) GetWordAfterCursorWithSpace() string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordWithSpace()] -} - -// GetWordBeforeCursorUntilSeparator returns the text before the cursor until next separator. -func (d *Document) GetWordBeforeCursorUntilSeparator(sep string) string { - x := d.TextBeforeCursor() - return x[d.FindStartOfPreviousWordUntilSeparator(sep):] -} - -// GetWordAfterCursorUntilSeparator returns the text after the cursor until next separator. -func (d *Document) GetWordAfterCursorUntilSeparator(sep string) string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordUntilSeparator(sep)] -} - -// GetWordBeforeCursorUntilSeparatorIgnoreNextToCursor returns the word before the cursor. -// Unlike GetWordBeforeCursor, it returns string containing space -func (d *Document) GetWordBeforeCursorUntilSeparatorIgnoreNextToCursor(sep string) string { - x := d.TextBeforeCursor() - return x[d.FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor(sep):] -} - -// GetWordAfterCursorUntilSeparatorIgnoreNextToCursor returns the word after the cursor. -// Unlike GetWordAfterCursor, it returns string containing space -func (d *Document) GetWordAfterCursorUntilSeparatorIgnoreNextToCursor(sep string) string { - x := d.TextAfterCursor() - return x[:d.FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor(sep)] -} - // FindStartOfPreviousWord returns an index relative to the cursor position -// pointing to the start of the previous word. Return 0 if nothing was found. +// pointing to the start of the previous word. Return `None` if nothing was found. func (d *Document) FindStartOfPreviousWord() int { + // Reverse the text before the cursor, in order to do an efficient backwards search. x := d.TextBeforeCursor() - i := strings.LastIndexByte(x, ' ') - if i != -1 { - return i + 1 + l := len(x) + for i := l; i > 0; i-- { + if x[i-1:i] == " " { + return i + } } return 0 } @@ -132,119 +78,21 @@ func (d *Document) FindStartOfPreviousWord() int { // FindStartOfPreviousWordWithSpace is almost the same as FindStartOfPreviousWord. // The only difference is to ignore contiguous spaces. func (d *Document) FindStartOfPreviousWordWithSpace() int { + // Reverse the text before the cursor, in order to do an efficient backwards search. x := d.TextBeforeCursor() - end := lastIndexByteNot(x, ' ') - if end == -1 { - return 0 - } - - start := strings.LastIndexByte(x[:end], ' ') - if start == -1 { - return 0 - } - return start + 1 -} - -// FindStartOfPreviousWordUntilSeparator is almost the same as FindStartOfPreviousWord. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindStartOfPreviousWordUntilSeparator(sep string) int { - if sep == "" { - return d.FindStartOfPreviousWord() - } - - x := d.TextBeforeCursor() - i := strings.LastIndexAny(x, sep) - if i != -1 { - return i + 1 + l := len(x) + appear := false + for i := l; i > 0; i-- { + if x[i-1:i] != " " { + appear = true + } + if x[i-1:i] == " " && appear { + return i + } } return 0 } -// FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor is almost the same as FindStartOfPreviousWordWithSpace. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor(sep string) int { - if sep == "" { - return d.FindStartOfPreviousWordWithSpace() - } - - x := d.TextBeforeCursor() - end := lastIndexAnyNot(x, sep) - if end == -1 { - return 0 - } - start := strings.LastIndexAny(x[:end], sep) - if start == -1 { - return 0 - } - return start + 1 -} - -// FindEndOfCurrentWord returns an index relative to the cursor position. -// pointing to the end of the current word. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWord() int { - x := d.TextAfterCursor() - i := strings.IndexByte(x, ' ') - if i != -1 { - return i - } - return len(x) -} - -// FindEndOfCurrentWordWithSpace is almost the same as FindEndOfCurrentWord. -// The only difference is to ignore contiguous spaces. -func (d *Document) FindEndOfCurrentWordWithSpace() int { - x := d.TextAfterCursor() - - start := indexByteNot(x, ' ') - if start == -1 { - return len(x) - } - - end := strings.IndexByte(x[start:], ' ') - if end == -1 { - return len(x) - } - - return start + end -} - -// FindEndOfCurrentWordUntilSeparator is almost the same as FindEndOfCurrentWord. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWordUntilSeparator(sep string) int { - if sep == "" { - return d.FindEndOfCurrentWord() - } - - x := d.TextAfterCursor() - i := strings.IndexAny(x, sep) - if i != -1 { - return i - } - return len(x) -} - -// FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor is almost the same as FindEndOfCurrentWordWithSpace. -// But this can specify Separator. Return 0 if nothing was found. -func (d *Document) FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor(sep string) int { - if sep == "" { - return d.FindEndOfCurrentWordWithSpace() - } - - x := d.TextAfterCursor() - - start := indexAnyNot(x, sep) - if start == -1 { - return len(x) - } - - end := strings.IndexAny(x[start:], sep) - if end == -1 { - return len(x) - } - - return start + end -} - // CurrentLineBeforeCursor returns the text from the start of the line until the cursor. func (d *Document) CurrentLineBeforeCursor() string { s := strings.Split(d.TextBeforeCursor(), "\n") @@ -292,14 +140,14 @@ func (d *Document) lineStartIndexes() []int { // the first character on that line. func (d *Document) findLineStartIndex(index int) (pos int, lineStartIndex int) { indexes := d.lineStartIndexes() - pos = bisectRight(indexes, index) - 1 + pos = BisectRight(indexes, index) - 1 lineStartIndex = indexes[pos] return } // CursorPositionRow returns the current row. (0-based.) func (d *Document) CursorPositionRow() (row int) { - row, _ = d.findLineStartIndex(d.cursorPosition) + row, _ = d.findLineStartIndex(d.CursorPosition) return } @@ -307,8 +155,8 @@ func (d *Document) CursorPositionRow() (row int) { func (d *Document) CursorPositionCol() (col int) { // Don't use self.text_before_cursor to calculate this. Creating substrings // and splitting is too expensive for getting the cursor position. - _, index := d.findLineStartIndex(d.cursorPosition) - col = d.cursorPosition - index + _, index := d.findLineStartIndex(d.CursorPosition) + col = d.CursorPosition - index return } @@ -348,7 +196,7 @@ func (d *Document) GetCursorUpPosition(count int, preferredColumn int) int { if row < 0 { row = 0 } - return d.TranslateRowColToIndex(row, col) - d.cursorPosition + return d.TranslateRowColToIndex(row, col) - d.CursorPosition } // GetCursorDownPosition return the relative cursor position (character index) where we would be if the @@ -361,7 +209,7 @@ func (d *Document) GetCursorDownPosition(count int, preferredColumn int) int { col = preferredColumn } row := d.CursorPositionRow() + count - return d.TranslateRowColToIndex(row, col) - d.cursorPosition + return d.TranslateRowColToIndex(row, col) - d.CursorPosition } // Lines returns the array of all the lines. @@ -432,102 +280,3 @@ func (d *Document) leadingWhitespaceInCurrentLine() (margin string) { margin = d.CurrentLine()[:len(d.CurrentLine())-len(trimmed)] return } - -// bisectRight to Locate the insertion point for v in a to maintain sorted order. -func bisectRight(a []int, v int) int { - return bisectRightRange(a, v, 0, len(a)) -} - -func bisectRightRange(a []int, v int, lo, hi int) int { - s := a[lo:hi] - return sort.Search(len(s), func(i int) bool { - return s[i] > v - }) -} - -func indexByteNot(s string, c byte) int { - n := len(s) - for i := 0; i < n; i++ { - if s[i] != c { - return i - } - } - return -1 -} - -func lastIndexByteNot(s string, c byte) int { - for i := len(s) - 1; i >= 0; i-- { - if s[i] != c { - return i - } - } - return -1 -} - -type asciiSet [8]uint32 - -func (as *asciiSet) notContains(c byte) bool { - return (as[c>>5] & (1 << uint(c&31))) == 0 -} - -func makeASCIISet(chars string) (as asciiSet, ok bool) { - for i := 0; i < len(chars); i++ { - c := chars[i] - if c >= utf8.RuneSelf { - return as, false - } - as[c>>5] |= 1 << uint(c&31) - } - return as, true -} - -func indexAnyNot(s, chars string) int { - if len(chars) > 0 { - if len(s) > 8 { - if as, isASCII := makeASCIISet(chars); isASCII { - for i := 0; i < len(s); i++ { - if as.notContains(s[i]) { - return i - } - } - return -1 - } - } - for i := 0; i < len(s); { - // I don't know why strings.IndexAny doesn't add rune count here. - r, size := utf8.DecodeRuneInString(s[i:]) - i += size - for _, c := range chars { - if r != c { - return i - } - } - } - } - return -1 -} - -func lastIndexAnyNot(s, chars string) int { - if len(chars) > 0 { - if len(s) > 8 { - if as, isASCII := makeASCIISet(chars); isASCII { - for i := len(s) - 1; i >= 0; i-- { - if as.notContains(s[i]) { - return i - } - } - return -1 - } - } - for i := len(s); i > 0; { - r, size := utf8.DecodeLastRuneInString(s[:i]) - i -= size - for _, c := range chars { - if r != c { - return i - } - } - } - } - return -1 -} diff --git a/vendor/github.com/c-bata/go-prompt/emacs.go b/vendor/github.com/c-bata/go-prompt/emacs.go index 9dc71edcab..f38d3f7302 100644 --- a/vendor/github.com/c-bata/go-prompt/emacs.go +++ b/vendor/github.com/c-bata/go-prompt/emacs.go @@ -113,7 +113,6 @@ var emacsKeyBindings = []KeyBind{ out := NewStandardOutputWriter() out.EraseScreen() out.CursorGoTo(0, 0) - out.Flush() }, }, } diff --git a/vendor/github.com/c-bata/go-prompt/input.go b/vendor/github.com/c-bata/go-prompt/input.go index 4c90b0f309..70837103cc 100644 --- a/vendor/github.com/c-bata/go-prompt/input.go +++ b/vendor/github.com/c-bata/go-prompt/input.go @@ -1,158 +1,42 @@ package prompt -// WinSize represents the width and height of terminal. -type WinSize struct { - Row uint16 - Col uint16 +func dummyExecutor(in string) { return } + +// Input get the input data from the user and return it. +func Input(prefix string, completer Completer, opts ...Option) string { + pt := New(dummyExecutor, completer) + pt.renderer.prefixTextColor = DefaultColor + pt.renderer.prefix = prefix + + for _, opt := range opts { + if err := opt(pt); err != nil { + panic(err) + } + } + return pt.Input() } -// ConsoleParser is an interface to abstract input layer. -type ConsoleParser interface { - // Setup should be called before starting input - Setup() error - // TearDown should be called after stopping input - TearDown() error - // GetKey returns Key correspond to input byte codes. - GetKey(b []byte) Key - // GetWinSize returns WinSize object to represent width and height of terminal. - GetWinSize() *WinSize - // Read returns byte array. - Read() ([]byte, error) +// Choose to the shortcut of input function to select from string array. +func Choose(prefix string, choices []string, opts ...Option) string { + completer := newChoiceCompleter(choices, FilterHasPrefix) + pt := New(dummyExecutor, completer) + pt.renderer.prefixTextColor = DefaultColor + pt.renderer.prefix = prefix + + for _, opt := range opts { + if err := opt(pt); err != nil { + panic(err) + } + } + return pt.Input() } -var asciiSequences = []*ASCIICode{ - {Key: Escape, ASCIICode: []byte{0x1b}}, - - {Key: ControlSpace, ASCIICode: []byte{0x00}}, - {Key: ControlA, ASCIICode: []byte{0x1}}, - {Key: ControlB, ASCIICode: []byte{0x2}}, - {Key: ControlC, ASCIICode: []byte{0x3}}, - {Key: ControlD, ASCIICode: []byte{0x4}}, - {Key: ControlE, ASCIICode: []byte{0x5}}, - {Key: ControlF, ASCIICode: []byte{0x6}}, - {Key: ControlG, ASCIICode: []byte{0x7}}, - {Key: ControlH, ASCIICode: []byte{0x8}}, - //{Key: ControlI, ASCIICode: []byte{0x9}}, - //{Key: ControlJ, ASCIICode: []byte{0xa}}, - {Key: ControlK, ASCIICode: []byte{0xb}}, - {Key: ControlL, ASCIICode: []byte{0xc}}, - {Key: ControlM, ASCIICode: []byte{0xd}}, - {Key: ControlN, ASCIICode: []byte{0xe}}, - {Key: ControlO, ASCIICode: []byte{0xf}}, - {Key: ControlP, ASCIICode: []byte{0x10}}, - {Key: ControlQ, ASCIICode: []byte{0x11}}, - {Key: ControlR, ASCIICode: []byte{0x12}}, - {Key: ControlS, ASCIICode: []byte{0x13}}, - {Key: ControlT, ASCIICode: []byte{0x14}}, - {Key: ControlU, ASCIICode: []byte{0x15}}, - {Key: ControlV, ASCIICode: []byte{0x16}}, - {Key: ControlW, ASCIICode: []byte{0x17}}, - {Key: ControlX, ASCIICode: []byte{0x18}}, - {Key: ControlY, ASCIICode: []byte{0x19}}, - {Key: ControlZ, ASCIICode: []byte{0x1a}}, - - {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, - {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, - {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, - {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, - {Key: Backspace, ASCIICode: []byte{0x7f}}, - - {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, - {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, - {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, - {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x30, 0x48}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, - {Key: End, ASCIICode: []byte{0x1b, 0x30, 0x46}}, - - {Key: Enter, ASCIICode: []byte{0xa}}, - {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, - {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e}}, - {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, - {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x7e}}, - {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x36, 0x7e}}, - {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x37, 0x7e}}, - {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x38, 0x7e}}, - {Key: Tab, ASCIICode: []byte{0x9}}, - {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, - {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, - - {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, - {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, - {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, - {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, - - {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console - {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console - {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console - {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console - {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console - - {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode - {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode - {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode - {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode - - {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, - {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, - {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, - {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, - {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, - {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, - {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, - {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, - {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, - {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, - {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, - {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, - {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, - {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, - {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, - {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, - - // Xterm - {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x50}}, - {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51}}, - // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, // Conflicts with CPR response - {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, - {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x32, 0x7e}}, - {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x32, 0x7e}}, - {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x32, 0x7e}}, - {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x32, 0x7e}}, - {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x32, 0x7e}}, - {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x32, 0x7e}}, - {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x32, 0x7e}}, - {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x32, 0x7e}}, - - {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41}}, - {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42}}, - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43}}, - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44}}, - - {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41}}, - {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x42}}, - {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43}}, - {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x44}}, - - // Tmux sends following keystrokes when control+arrow is pressed, but for - // Emacs ansi-term sends the same sequences for normal arrow keys. Consider - // it a normal arrow press, because that's more important. - {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, - {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, - {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, - {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, - - {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x41}}, - {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x42}}, - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x43}}, - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x44}}, - - {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt - {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt - - {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm - {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +func newChoiceCompleter(choices []string, filter Filter) Completer { + s := make([]Suggest, len(choices)) + for i := range choices { + s[i] = Suggest{Text: choices[i]} + } + return func(x Document) []Suggest { + return filter(s, x.GetWordBeforeCursor(), true) + } } diff --git a/vendor/github.com/c-bata/go-prompt/input_posix.go b/vendor/github.com/c-bata/go-prompt/input_posix.go deleted file mode 100644 index 46ecb511a1..0000000000 --- a/vendor/github.com/c-bata/go-prompt/input_posix.go +++ /dev/null @@ -1,128 +0,0 @@ -// +build !windows - -package prompt - -import ( - "bytes" - "log" - "syscall" - "unsafe" - - "github.com/pkg/term/termios" -) - -const maxReadBytes = 1024 - -// PosixParser is a ConsoleParser implementation for POSIX environment. -type PosixParser struct { - fd int - origTermios syscall.Termios -} - -// Setup should be called before starting input -func (t *PosixParser) Setup() error { - // Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh. - if err := syscall.SetNonblock(t.fd, true); err != nil { - log.Println("[ERROR] Cannot set non blocking mode.") - return err - } - if err := t.setRawMode(); err != nil { - log.Println("[ERROR] Cannot set raw mode.") - return err - } - return nil -} - -// TearDown should be called after stopping input -func (t *PosixParser) TearDown() error { - if err := syscall.SetNonblock(t.fd, false); err != nil { - log.Println("[ERROR] Cannot set blocking mode.") - return err - } - if err := t.resetRawMode(); err != nil { - log.Println("[ERROR] Cannot reset from raw mode.") - return err - } - return nil -} - -// Read returns byte array. -func (t *PosixParser) Read() ([]byte, error) { - buf := make([]byte, maxReadBytes) - n, err := syscall.Read(syscall.Stdin, buf) - if err != nil { - return []byte{}, err - } - return buf[:n], nil -} - -func (t *PosixParser) setRawMode() error { - x := t.origTermios.Lflag - if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag { - // fd is already raw mode - return nil - } - var n syscall.Termios - if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil { - return err - } - n = t.origTermios - // "&^=" used like: https://play.golang.org/p/8eJw3JxS4O - n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG - n.Cc[syscall.VMIN] = 1 - n.Cc[syscall.VTIME] = 0 - termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n) - return nil -} - -func (t *PosixParser) resetRawMode() error { - if t.origTermios.Lflag == 0 { - return nil - } - return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios) -} - -// GetKey returns Key correspond to input byte codes. -func (t *PosixParser) GetKey(b []byte) Key { - for _, k := range asciiSequences { - if bytes.Equal(k.ASCIICode, b) { - return k.Key - } - } - return NotDefined -} - -// winsize is winsize struct got from the ioctl(2) system call. -type ioctlWinsize struct { - Row uint16 - Col uint16 - X uint16 // pixel value - Y uint16 // pixel value -} - -// GetWinSize returns WinSize object to represent width and height of terminal. -func (t *PosixParser) GetWinSize() *WinSize { - ws := &ioctlWinsize{} - retCode, _, errno := syscall.Syscall( - syscall.SYS_IOCTL, - uintptr(t.fd), - uintptr(syscall.TIOCGWINSZ), - uintptr(unsafe.Pointer(ws))) - - if int(retCode) == -1 { - panic(errno) - } - return &WinSize{ - Row: ws.Row, - Col: ws.Col, - } -} - -var _ ConsoleParser = &PosixParser{} - -// NewStandardInputParser returns ConsoleParser object to read from stdin. -func NewStandardInputParser() *PosixParser { - return &PosixParser{ - fd: syscall.Stdin, - } -} diff --git a/vendor/github.com/c-bata/go-prompt/input_windows.go b/vendor/github.com/c-bata/go-prompt/input_windows.go deleted file mode 100644 index f52b538977..0000000000 --- a/vendor/github.com/c-bata/go-prompt/input_windows.go +++ /dev/null @@ -1,94 +0,0 @@ -// +build windows - -package prompt - -import ( - "bytes" - "errors" - "syscall" - "unicode/utf8" - "unsafe" - - "github.com/mattn/go-tty" -) - -const maxReadBytes = 1024 - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents") - -// WindowsParser is a ConsoleParser implementation for Win32 console. -type WindowsParser struct { - tty *tty.TTY -} - -// Setup should be called before starting input -func (p *WindowsParser) Setup() error { - t, err := tty.Open() - if err != nil { - return err - } - p.tty = t - return nil -} - -// TearDown should be called after stopping input -func (p *WindowsParser) TearDown() error { - return p.tty.Close() -} - -// GetKey returns Key correspond to input byte codes. -func (p *WindowsParser) GetKey(b []byte) Key { - for _, k := range asciiSequences { - if bytes.Compare(k.ASCIICode, b) == 0 { - return k.Key - } - } - return NotDefined -} - -// Read returns byte array. -func (p *WindowsParser) Read() ([]byte, error) { - var ev uint32 - r0, _, err := procGetNumberOfConsoleInputEvents.Call(p.tty.Input().Fd(), uintptr(unsafe.Pointer(&ev))) - if r0 == 0 { - return nil, err - } - if ev == 0 { - return nil, errors.New("EAGAIN") - } - - r, err := p.tty.ReadRune() - if err != nil { - return nil, err - } - - buf := make([]byte, maxReadBytes) - n := utf8.EncodeRune(buf[:], r) - for p.tty.Buffered() && n < maxReadBytes { - r, err := p.tty.ReadRune() - if err != nil { - break - } - n += utf8.EncodeRune(buf[n:], r) - } - return buf[:n], nil -} - -// GetWinSize returns WinSize object to represent width and height of terminal. -func (p *WindowsParser) GetWinSize() *WinSize { - w, h, err := p.tty.Size() - if err != nil { - panic(err) - } - return &WinSize{ - Row: uint16(h), - Col: uint16(w), - } -} - -// NewStandardInputParser returns ConsoleParser object to read from stdin. -func NewStandardInputParser() *WindowsParser { - return &WindowsParser{} -} diff --git a/vendor/github.com/c-bata/go-prompt/key.go b/vendor/github.com/c-bata/go-prompt/key.go index 068b70e9f7..9f79185f50 100644 --- a/vendor/github.com/c-bata/go-prompt/key.go +++ b/vendor/github.com/c-bata/go-prompt/key.go @@ -1,6 +1,3 @@ -// Code generated "This is a fake comment to avoid golint errors"; DO NOT EDIT. -// FIXME: This is a little bit stupid, but there are many public constants which is no value for writing godoc comment. - package prompt // Key is the type express the key inserted from user. diff --git a/vendor/github.com/c-bata/go-prompt/key_bind.go b/vendor/github.com/c-bata/go-prompt/key_bind.go index 42669e7f94..0cfcab44aa 100644 --- a/vendor/github.com/c-bata/go-prompt/key_bind.go +++ b/vendor/github.com/c-bata/go-prompt/key_bind.go @@ -1,59 +1,62 @@ package prompt -// KeyBindFunc receives buffer and processed it. type KeyBindFunc func(*Buffer) -// KeyBind represents which key should do what operation. type KeyBind struct { Key Key Fn KeyBindFunc } -// ASCIICodeBind represents which []byte should do what operation -type ASCIICodeBind struct { - ASCIICode []byte - Fn KeyBindFunc -} - -// KeyBindMode to switch a key binding flexibly. type KeyBindMode string const ( - // CommonKeyBind is a mode without any keyboard shortcut CommonKeyBind KeyBindMode = "common" - // EmacsKeyBind is a mode to use emacs-like keyboard shortcut - EmacsKeyBind KeyBindMode = "emacs" + EmacsKeyBind KeyBindMode = "emacs" ) var commonKeyBindings = []KeyBind{ // Go to the End of the line { Key: End, - Fn: GoLineEnd, + Fn: func(buf *Buffer) { + x := []rune(buf.Document().TextAfterCursor()) + buf.CursorRight(len(x)) + }, }, // Go to the beginning of the line { Key: Home, - Fn: GoLineBeginning, + Fn: func(buf *Buffer) { + x := []rune(buf.Document().TextBeforeCursor()) + buf.CursorLeft(len(x)) + }, }, // Delete character under the cursor { Key: Delete, - Fn: DeleteChar, + Fn: func(buf *Buffer) { + buf.Delete(1) + }, }, // Backspace { Key: Backspace, - Fn: DeleteBeforeChar, + Fn: func(buf *Buffer) { + buf.DeleteBeforeCursor(1) + }, }, // Right allow: Forward one character { Key: Right, - Fn: GoRightChar, + Fn: func(buf *Buffer) { + buf.CursorRight(1) + }, }, // Left allow: Backward one character { Key: Left, - Fn: GoLeftChar, + Fn: func(buf *Buffer) { + buf.CursorLeft(1) + }, }, } diff --git a/vendor/github.com/c-bata/go-prompt/key_bind_func.go b/vendor/github.com/c-bata/go-prompt/key_bind_func.go deleted file mode 100644 index 7b2ecdf631..0000000000 --- a/vendor/github.com/c-bata/go-prompt/key_bind_func.go +++ /dev/null @@ -1,48 +0,0 @@ -package prompt - -// GoLineEnd Go to the End of the line -func GoLineEnd(buf *Buffer) { - x := []rune(buf.Document().TextAfterCursor()) - buf.CursorRight(len(x)) -} - -// GoLineBeginning Go to the beginning of the line -func GoLineBeginning(buf *Buffer) { - x := []rune(buf.Document().TextBeforeCursor()) - buf.CursorLeft(len(x)) -} - -// DeleteChar Delete character under the cursor -func DeleteChar(buf *Buffer) { - buf.Delete(1) -} - -// DeleteWord Delete word before the cursor -func DeleteWord(buf *Buffer) { - buf.DeleteBeforeCursor(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace()) -} - -// DeleteBeforeChar Go to Backspace -func DeleteBeforeChar(buf *Buffer) { - buf.DeleteBeforeCursor(1) -} - -// GoRightChar Forward one character -func GoRightChar(buf *Buffer) { - buf.CursorRight(1) -} - -// GoLeftChar Backward one character -func GoLeftChar(buf *Buffer) { - buf.CursorLeft(1) -} - -// GoRightWord Forward one word -func GoRightWord(buf *Buffer) { - buf.CursorRight(buf.Document().FindEndOfCurrentWordWithSpace()) -} - -// GoLeftWord Backward one word -func GoLeftWord(buf *Buffer) { - buf.CursorLeft(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace()) -} diff --git a/vendor/github.com/c-bata/go-prompt/option.go b/vendor/github.com/c-bata/go-prompt/option.go index 9a7f386da3..72c6de3103 100644 --- a/vendor/github.com/c-bata/go-prompt/option.go +++ b/vendor/github.com/c-bata/go-prompt/option.go @@ -12,7 +12,7 @@ func OptionParser(x ConsoleParser) Option { } } -// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interface. +// OptionWriter to set a custom ConsoleWriter object. An argument should implement ConsoleWriter interace. func OptionWriter(x ConsoleWriter) Option { return func(p *Prompt) error { p.renderer.out = x @@ -36,14 +36,6 @@ func OptionPrefix(x string) Option { } } -// OptionCompletionWordSeparator to set word separators. Enable only ' ' if empty. -func OptionCompletionWordSeparator(x string) Option { - return func(p *Prompt) error { - p.completion.wordSeparator = x - return nil - } -} - // OptionLivePrefix to change the prefix dynamically by callback function func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option { return func(p *Prompt) error { @@ -52,7 +44,6 @@ func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option { } } -// OptionPrefixTextColor change a text color of prefix string func OptionPrefixTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.prefixTextColor = x @@ -60,7 +51,6 @@ func OptionPrefixTextColor(x Color) Option { } } -// OptionPrefixBackgroundColor to change a background color of prefix string func OptionPrefixBackgroundColor(x Color) Option { return func(p *Prompt) error { p.renderer.prefixBGColor = x @@ -68,7 +58,6 @@ func OptionPrefixBackgroundColor(x Color) Option { } } -// OptionInputTextColor to change a color of text which is input by user func OptionInputTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.inputTextColor = x @@ -76,7 +65,6 @@ func OptionInputTextColor(x Color) Option { } } -// OptionInputBGColor to change a color of background which is input by user func OptionInputBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.inputBGColor = x @@ -84,7 +72,6 @@ func OptionInputBGColor(x Color) Option { } } -// OptionPreviewSuggestionTextColor to change a text color which is completed func OptionPreviewSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.previewSuggestionTextColor = x @@ -92,7 +79,6 @@ func OptionPreviewSuggestionTextColor(x Color) Option { } } -// OptionPreviewSuggestionBGColor to change a background color which is completed func OptionPreviewSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.previewSuggestionBGColor = x @@ -100,7 +86,6 @@ func OptionPreviewSuggestionBGColor(x Color) Option { } } -// OptionSuggestionTextColor to change a text color in drop down suggestions. func OptionSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.suggestionTextColor = x @@ -108,7 +93,6 @@ func OptionSuggestionTextColor(x Color) Option { } } -// OptionSuggestionBGColor change a background color in drop down suggestions. func OptionSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.suggestionBGColor = x @@ -116,7 +100,6 @@ func OptionSuggestionBGColor(x Color) Option { } } -// OptionSelectedSuggestionTextColor to change a text color for completed text which is selected inside suggestions drop down box. func OptionSelectedSuggestionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedSuggestionTextColor = x @@ -124,7 +107,6 @@ func OptionSelectedSuggestionTextColor(x Color) Option { } } -// OptionSelectedSuggestionBGColor to change a background color for completed text which is selected inside suggestions drop down box. func OptionSelectedSuggestionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedSuggestionBGColor = x @@ -132,7 +114,6 @@ func OptionSelectedSuggestionBGColor(x Color) Option { } } -// OptionDescriptionTextColor to change a background color of description text in drop down suggestions. func OptionDescriptionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.descriptionTextColor = x @@ -140,7 +121,6 @@ func OptionDescriptionTextColor(x Color) Option { } } -// OptionDescriptionBGColor to change a background color of description text in drop down suggestions. func OptionDescriptionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.descriptionBGColor = x @@ -148,7 +128,6 @@ func OptionDescriptionBGColor(x Color) Option { } } -// OptionSelectedDescriptionTextColor to change a text color of description which is selected inside suggestions drop down box. func OptionSelectedDescriptionTextColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedDescriptionTextColor = x @@ -156,7 +135,6 @@ func OptionSelectedDescriptionTextColor(x Color) Option { } } -// OptionSelectedDescriptionBGColor to change a background color of description which is selected inside suggestions drop down box. func OptionSelectedDescriptionBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.selectedDescriptionBGColor = x @@ -164,7 +142,6 @@ func OptionSelectedDescriptionBGColor(x Color) Option { } } -// OptionScrollbarThumbColor to change a thumb color on scrollbar. func OptionScrollbarThumbColor(x Color) Option { return func(p *Prompt) error { p.renderer.scrollbarThumbColor = x @@ -172,7 +149,6 @@ func OptionScrollbarThumbColor(x Color) Option { } } -// OptionScrollbarBGColor to change a background color of scrollbar. func OptionScrollbarBGColor(x Color) Option { return func(p *Prompt) error { p.renderer.scrollbarBGColor = x @@ -217,14 +193,6 @@ func OptionAddKeyBind(b ...KeyBind) Option { } } -// OptionAddASCIICodeBind to set a custom key bind. -func OptionAddASCIICodeBind(b ...ASCIICodeBind) Option { - return func(p *Prompt) error { - p.ASCIICodeBindings = append(p.ASCIICodeBindings, b...) - return nil - } -} - // New returns a Prompt with powerful auto-completion. func New(executor Executor, completer Completer, opts ...Option) *Prompt { pt := &Prompt{ diff --git a/vendor/github.com/c-bata/go-prompt/output_posix.go b/vendor/github.com/c-bata/go-prompt/output_posix.go deleted file mode 100644 index 4c2ccf63b1..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_posix.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build !windows - -package prompt - -import ( - "syscall" -) - -// PosixWriter is a ConsoleWriter implementation for POSIX environment. -// To control terminal emulator, this outputs VT100 escape sequences. -type PosixWriter struct { - VT100Writer - fd int -} - -// Flush to flush buffer -func (w *PosixWriter) Flush() error { - _, err := syscall.Write(w.fd, w.buffer) - if err != nil { - return err - } - w.buffer = []byte{} - return nil -} - -var _ ConsoleWriter = &PosixWriter{} - -// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. -// This generates VT100 escape sequences because almost terminal emulators -// in POSIX OS built on top of a VT100 specification. -func NewStandardOutputWriter() *PosixWriter { - return &PosixWriter{ - fd: syscall.Stdout, - } -} diff --git a/vendor/github.com/c-bata/go-prompt/output_vt100.go b/vendor/github.com/c-bata/go-prompt/output_vt100.go deleted file mode 100644 index 3b3031d70b..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_vt100.go +++ /dev/null @@ -1,333 +0,0 @@ -package prompt - -import ( - "bytes" - "strconv" -) - -// VT100Writer generates VT100 escape sequences. -type VT100Writer struct { - buffer []byte -} - -// WriteRaw to write raw byte array -func (w *VT100Writer) WriteRaw(data []byte) { - w.buffer = append(w.buffer, data...) - return -} - -// Write to write safety byte array by removing control sequences. -func (w *VT100Writer) Write(data []byte) { - w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1)) - return -} - -// WriteRawStr to write raw string -func (w *VT100Writer) WriteRawStr(data string) { - w.WriteRaw([]byte(data)) - return -} - -// WriteStr to write safety string by removing control sequences. -func (w *VT100Writer) WriteStr(data string) { - w.Write([]byte(data)) - return -} - -/* Erase */ - -// EraseScreen erases the screen with the background colour and moves the cursor to home. -func (w *VT100Writer) EraseScreen() { - w.WriteRaw([]byte{0x1b, '[', '2', 'J'}) - return -} - -// EraseUp erases the screen from the current line up to the top of the screen. -func (w *VT100Writer) EraseUp() { - w.WriteRaw([]byte{0x1b, '[', '1', 'J'}) - return -} - -// EraseDown erases the screen from the current line down to the bottom of the screen. -func (w *VT100Writer) EraseDown() { - w.WriteRaw([]byte{0x1b, '[', 'J'}) - return -} - -// EraseStartOfLine erases from the current cursor position to the start of the current line. -func (w *VT100Writer) EraseStartOfLine() { - w.WriteRaw([]byte{0x1b, '[', '1', 'K'}) - return -} - -// EraseEndOfLine erases from the current cursor position to the end of the current line. -func (w *VT100Writer) EraseEndOfLine() { - w.WriteRaw([]byte{0x1b, '[', 'K'}) - return -} - -// EraseLine erases the entire current line. -func (w *VT100Writer) EraseLine() { - w.WriteRaw([]byte{0x1b, '[', '2', 'K'}) - return -} - -/* Cursor */ - -// ShowCursor stops blinking cursor and show. -func (w *VT100Writer) ShowCursor() { - w.WriteRaw([]byte{0x1b, '[', '?', '1', '2', 'l', 0x1b, '[', '?', '2', '5', 'h'}) -} - -// HideCursor hides cursor. -func (w *VT100Writer) HideCursor() { - w.WriteRaw([]byte{0x1b, '[', '?', '2', '5', 'l'}) - return -} - -// CursorGoTo sets the cursor position where subsequent text will begin. -func (w *VT100Writer) CursorGoTo(row, col int) { - if row == 0 && col == 0 { - // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. - w.WriteRaw([]byte{0x1b, '[', 'H'}) - return - } - r := strconv.Itoa(row) - c := strconv.Itoa(col) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(r)) - w.WriteRaw([]byte{';'}) - w.WriteRaw([]byte(c)) - w.WriteRaw([]byte{'H'}) - return -} - -// CursorUp moves the cursor up by 'n' rows; the default count is 1. -func (w *VT100Writer) CursorUp(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorDown(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'A'}) - return -} - -// CursorDown moves the cursor down by 'n' rows; the default count is 1. -func (w *VT100Writer) CursorDown(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorUp(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'B'}) - return -} - -// CursorForward moves the cursor forward by 'n' columns; the default count is 1. -func (w *VT100Writer) CursorForward(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorBackward(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'C'}) - return -} - -// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. -func (w *VT100Writer) CursorBackward(n int) { - if n == 0 { - return - } else if n < 0 { - w.CursorForward(-n) - return - } - s := strconv.Itoa(n) - w.WriteRaw([]byte{0x1b, '['}) - w.WriteRaw([]byte(s)) - w.WriteRaw([]byte{'D'}) - return -} - -// AskForCPR asks for a cursor position report (CPR). -func (w *VT100Writer) AskForCPR() { - // CPR: Cursor Position Request. - w.WriteRaw([]byte{0x1b, '[', '6', 'n'}) - return -} - -// SaveCursor saves current cursor position. -func (w *VT100Writer) SaveCursor() { - w.WriteRaw([]byte{0x1b, '[', 's'}) - return -} - -// UnSaveCursor restores cursor position after a Save Cursor. -func (w *VT100Writer) UnSaveCursor() { - w.WriteRaw([]byte{0x1b, '[', 'u'}) - return -} - -/* Scrolling */ - -// ScrollDown scrolls display down one line. -func (w *VT100Writer) ScrollDown() { - w.WriteRaw([]byte{0x1b, 'D'}) - return -} - -// ScrollUp scroll display up one line. -func (w *VT100Writer) ScrollUp() { - w.WriteRaw([]byte{0x1b, 'M'}) - return -} - -/* Title */ - -// SetTitle sets a title of terminal window. -func (w *VT100Writer) SetTitle(title string) { - titleBytes := []byte(title) - patterns := []struct { - from []byte - to []byte - }{ - { - from: []byte{0x13}, - to: []byte{}, - }, - { - from: []byte{0x07}, - to: []byte{}, - }, - } - for i := range patterns { - titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1) - } - - w.WriteRaw([]byte{0x1b, ']', '2', ';'}) - w.WriteRaw(titleBytes) - w.WriteRaw([]byte{0x07}) - return -} - -// ClearTitle clears a title of terminal window. -func (w *VT100Writer) ClearTitle() { - w.WriteRaw([]byte{0x1b, ']', '2', ';', 0x07}) - return -} - -/* Font */ - -// SetColor sets text and background colors. and specify whether text is bold. -func (w *VT100Writer) SetColor(fg, bg Color, bold bool) { - if bold { - w.SetDisplayAttributes(fg, bg, DisplayBold) - } else { - w.SetDisplayAttributes(fg, bg, DisplayDefaultFont) - } - return -} - -// SetDisplayAttributes to set VT100 display attributes. -func (w *VT100Writer) SetDisplayAttributes(fg, bg Color, attrs ...DisplayAttribute) { - w.WriteRaw([]byte{0x1b, '['}) // control sequence introducer - defer w.WriteRaw([]byte{'m'}) // final character - - var separator byte = ';' - for i := range attrs { - p, ok := displayAttributeParameters[attrs[i]] - if !ok { - continue - } - w.WriteRaw(p) - w.WriteRaw([]byte{separator}) - } - - f, ok := foregroundANSIColors[fg] - if !ok { - f = foregroundANSIColors[DefaultColor] - } - w.WriteRaw(f) - w.WriteRaw([]byte{separator}) - b, ok := backgroundANSIColors[bg] - if !ok { - b = backgroundANSIColors[DefaultColor] - } - w.WriteRaw(b) - return -} - -var displayAttributeParameters = map[DisplayAttribute][]byte{ - DisplayReset: {'0'}, - DisplayBold: {'1'}, - DisplayLowIntensity: {'2'}, - DisplayItalic: {'3'}, - DisplayUnderline: {'4'}, - DisplayBlink: {'5'}, - DisplayRapidBlink: {'6'}, - DisplayReverse: {'7'}, - DisplayInvisible: {'8'}, - DisplayCrossedOut: {'9'}, - DisplayDefaultFont: {'1', '0'}, -} - -var foregroundANSIColors = map[Color][]byte{ - DefaultColor: {'3', '9'}, - - // Low intensity. - Black: {'3', '0'}, - DarkRed: {'3', '1'}, - DarkGreen: {'3', '2'}, - Brown: {'3', '3'}, - DarkBlue: {'3', '4'}, - Purple: {'3', '5'}, - Cyan: {'3', '6'}, - LightGray: {'3', '7'}, - - // High intensity. - DarkGray: {'9', '0'}, - Red: {'9', '1'}, - Green: {'9', '2'}, - Yellow: {'9', '3'}, - Blue: {'9', '4'}, - Fuchsia: {'9', '5'}, - Turquoise: {'9', '6'}, - White: {'9', '7'}, -} - -var backgroundANSIColors = map[Color][]byte{ - DefaultColor: {'4', '9'}, - - // Low intensity. - Black: {'4', '0'}, - DarkRed: {'4', '1'}, - DarkGreen: {'4', '2'}, - Brown: {'4', '3'}, - DarkBlue: {'4', '4'}, - Purple: {'4', '5'}, - Cyan: {'4', '6'}, - LightGray: {'4', '7'}, - - // High intensity - DarkGray: {'1', '0', '0'}, - Red: {'1', '0', '1'}, - Green: {'1', '0', '2'}, - Yellow: {'1', '0', '3'}, - Blue: {'1', '0', '4'}, - Fuchsia: {'1', '0', '5'}, - Turquoise: {'1', '0', '6'}, - White: {'1', '0', '7'}, -} diff --git a/vendor/github.com/c-bata/go-prompt/output_windows.go b/vendor/github.com/c-bata/go-prompt/output_windows.go deleted file mode 100644 index 7418af3491..0000000000 --- a/vendor/github.com/c-bata/go-prompt/output_windows.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build windows - -package prompt - -import ( - "io" - - "github.com/mattn/go-colorable" -) - -// WindowsWriter is a ConsoleWriter implementation for Win32 console. -// Output is converted from VT100 escape sequences by mattn/go-colorable. -type WindowsWriter struct { - VT100Writer - out io.Writer -} - -// Flush to flush buffer -func (w *WindowsWriter) Flush() error { - _, err := w.out.Write(w.buffer) - if err != nil { - return err - } - w.buffer = []byte{} - return nil -} - -var _ ConsoleWriter = &WindowsWriter{} - -// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. -// This generates win32 control sequences. -func NewStandardOutputWriter() *WindowsWriter { - return &WindowsWriter{ - out: colorable.NewColorableStdout(), - } -} diff --git a/vendor/github.com/c-bata/go-prompt/posix_input.go b/vendor/github.com/c-bata/go-prompt/posix_input.go new file mode 100644 index 0000000000..ead496d97e --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/posix_input.go @@ -0,0 +1,265 @@ +// +build !windows + +package prompt + +import ( + "bytes" + "log" + "syscall" + "unsafe" + + "github.com/pkg/term/termios" +) + +const maxReadBytes = 1024 + +// PosixParser is a ConsoleParser implementation for POSIX environment. +type PosixParser struct { + fd int + origTermios syscall.Termios +} + +// Setup should be called before starting input +func (t *PosixParser) Setup() error { + // Set NonBlocking mode because if syscall.Read block this goroutine, it cannot receive data from stopCh. + if err := syscall.SetNonblock(t.fd, true); err != nil { + log.Println("[ERROR] Cannot set non blocking mode.") + return err + } + if err := t.setRawMode(); err != nil { + log.Println("[ERROR] Cannot set raw mode.") + return err + } + return nil +} + +// TearDown should be called after stopping input +func (t *PosixParser) TearDown() error { + if err := syscall.SetNonblock(t.fd, false); err != nil { + log.Println("[ERROR] Cannot set blocking mode.") + return err + } + if err := t.resetRawMode(); err != nil { + log.Println("[ERROR] Cannot reset from raw mode.") + return err + } + return nil +} + +// Read returns byte array. +func (t *PosixParser) Read() ([]byte, error) { + buf := make([]byte, maxReadBytes) + n, err := syscall.Read(syscall.Stdin, buf) + if err != nil { + return []byte{}, err + } + return buf[:n], nil +} + +func (t *PosixParser) setRawMode() error { + x := t.origTermios.Lflag + if x &^= syscall.ICANON; x != 0 && x == t.origTermios.Lflag { + // fd is already raw mode + return nil + } + var n syscall.Termios + if err := termios.Tcgetattr(uintptr(t.fd), &t.origTermios); err != nil { + return err + } + n = t.origTermios + // "&^=" used like: https://play.golang.org/p/8eJw3JxS4O + n.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG + n.Cc[syscall.VMIN] = 1 + n.Cc[syscall.VTIME] = 0 + termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &n) + return nil +} + +func (t *PosixParser) resetRawMode() error { + if t.origTermios.Lflag == 0 { + return nil + } + return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.origTermios) +} + +// GetKey returns Key correspond to input byte codes. +func (t *PosixParser) GetKey(b []byte) Key { + for _, k := range asciiSequences { + if bytes.Equal(k.ASCIICode, b) { + return k.Key + } + } + return NotDefined +} + +// winsize is winsize struct got from the ioctl(2) system call. +type ioctlWinsize struct { + Row uint16 + Col uint16 + X uint16 // pixel value + Y uint16 // pixel value +} + +// GetWinSize returns WinSize object to represent width and height of terminal. +func (t *PosixParser) GetWinSize() *WinSize { + ws := &ioctlWinsize{} + retCode, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + uintptr(t.fd), + uintptr(syscall.TIOCGWINSZ), + uintptr(unsafe.Pointer(ws))) + + if int(retCode) == -1 { + panic(errno) + } + return &WinSize{ + Row: ws.Row, + Col: ws.Col, + } +} + +var asciiSequences = []*ASCIICode{ + {Key: Escape, ASCIICode: []byte{0x1b}}, + + {Key: ControlSpace, ASCIICode: []byte{0x00}}, + {Key: ControlA, ASCIICode: []byte{0x1}}, + {Key: ControlB, ASCIICode: []byte{0x2}}, + {Key: ControlC, ASCIICode: []byte{0x3}}, + {Key: ControlD, ASCIICode: []byte{0x4}}, + {Key: ControlE, ASCIICode: []byte{0x5}}, + {Key: ControlF, ASCIICode: []byte{0x6}}, + {Key: ControlG, ASCIICode: []byte{0x7}}, + {Key: ControlH, ASCIICode: []byte{0x8}}, + //{Key: ControlI, ASCIICode: []byte{0x9}}, + //{Key: ControlJ, ASCIICode: []byte{0xa}}, + {Key: ControlK, ASCIICode: []byte{0xb}}, + {Key: ControlL, ASCIICode: []byte{0xc}}, + {Key: ControlM, ASCIICode: []byte{0xd}}, + {Key: ControlN, ASCIICode: []byte{0xe}}, + {Key: ControlO, ASCIICode: []byte{0xf}}, + {Key: ControlP, ASCIICode: []byte{0x10}}, + {Key: ControlQ, ASCIICode: []byte{0x11}}, + {Key: ControlR, ASCIICode: []byte{0x12}}, + {Key: ControlS, ASCIICode: []byte{0x13}}, + {Key: ControlT, ASCIICode: []byte{0x14}}, + {Key: ControlU, ASCIICode: []byte{0x15}}, + {Key: ControlV, ASCIICode: []byte{0x16}}, + {Key: ControlW, ASCIICode: []byte{0x17}}, + {Key: ControlX, ASCIICode: []byte{0x18}}, + {Key: ControlY, ASCIICode: []byte{0x19}}, + {Key: ControlZ, ASCIICode: []byte{0x1a}}, + + {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, + {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, + {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, + {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, + {Key: Backspace, ASCIICode: []byte{0x7f}}, + + {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x30, 0x48}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, + {Key: End, ASCIICode: []byte{0x1b, 0x30, 0x46}}, + + {Key: Enter, ASCIICode: []byte{0xa}}, + {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e}}, + {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x7e}}, + {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x36, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x37, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x38, 0x7e}}, + {Key: Tab, ASCIICode: []byte{0x9}}, + {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, + {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, + {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, + {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, + {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console + + {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode + + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, + {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, + {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, + {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, + {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, + {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, + {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, + {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, + {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + + // Xterm + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x50}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51}}, + // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, // Conflicts with CPR response + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x32, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x32, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x32, 0x7e}}, + {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x32, 0x7e}}, + {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x32, 0x7e}}, + {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x32, 0x7e}}, + {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x32, 0x7e}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44}}, + + {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41}}, + {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x42}}, + {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43}}, + {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x44}}, + + // Tmux sends following keystrokes when control+arrow is pressed, but for + // Emacs ansi-term sends the same sequences for normal arrow keys. Consider + // it a normal arrow press, because that's more important. + {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x35, 0x44}}, + + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt + + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +} + +var _ ConsoleParser = &PosixParser{} + +// NewStandardInputParser returns ConsoleParser object to read from stdin. +func NewStandardInputParser() *PosixParser { + return &PosixParser{ + fd: syscall.Stdin, + } +} diff --git a/vendor/github.com/c-bata/go-prompt/posix_output.go b/vendor/github.com/c-bata/go-prompt/posix_output.go new file mode 100644 index 0000000000..52c69d9465 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/posix_output.go @@ -0,0 +1,335 @@ +// +build !windows + +package prompt + +import ( + "strconv" + "syscall" +) + +// PosixWriter is a ConsoleWriter implementation for POSIX environment. +// To control terminal emulator, this outputs VT100 escape sequences. +type PosixWriter struct { + fd int + buffer []byte +} + +// WriteRaw to write raw byte array +func (w *PosixWriter) WriteRaw(data []byte) { + w.buffer = append(w.buffer, data...) + // Flush because sometimes the render is broken when a large amount data in buffer. + w.Flush() + return +} + +// Write to write byte array without control sequences +func (w *PosixWriter) Write(data []byte) { + w.WriteRaw(byteFilter(data, writeFilter)) + return +} + +// WriteRawStr to write raw string +func (w *PosixWriter) WriteRawStr(data string) { + w.WriteRaw([]byte(data)) + return +} + +// WriteStr to write string without control sequences +func (w *PosixWriter) WriteStr(data string) { + w.Write([]byte(data)) + return +} + +// Flush to flush buffer +func (w *PosixWriter) Flush() error { + _, err := syscall.Write(w.fd, w.buffer) + if err != nil { + return err + } + w.buffer = []byte{} + return nil +} + +/* Erase */ + +// EraseScreen erases the screen with the background colour and moves the cursor to home. +func (w *PosixWriter) EraseScreen() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a}) + return +} + +// EraseUp erases the screen from the current line up to the top of the screen. +func (w *PosixWriter) EraseUp() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a}) + return +} + +// EraseDown erases the screen from the current line down to the bottom of the screen. +func (w *PosixWriter) EraseDown() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4a}) + return +} + +// EraseStartOfLine erases from the current cursor position to the start of the current line. +func (w *PosixWriter) EraseStartOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b}) + return +} + +// EraseEndOfLine erases from the current cursor position to the end of the current line. +func (w *PosixWriter) EraseEndOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4b}) + return +} + +// EraseLine erases the entire current line. +func (w *PosixWriter) EraseLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b}) + return +} + +/* Cursor */ + +// ShowCursor stops blinking cursor and show. +func (w *PosixWriter) ShowCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68}) +} + +// HideCursor hides cursor. +func (w *PosixWriter) HideCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c}) + return +} + +// CursorGoTo sets the cursor position where subsequent text will begin. +func (w *PosixWriter) CursorGoTo(row, col int) { + if row == 0 && col == 0 { + // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. + w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48}) + return + } + r := strconv.Itoa(row) + c := strconv.Itoa(col) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(r)) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw([]byte(c)) + w.WriteRaw([]byte{0x48}) + return +} + +// CursorUp moves the cursor up by 'n' rows; the default count is 1. +func (w *PosixWriter) CursorUp(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorDown(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x41}) + return +} + +// CursorDown moves the cursor down by 'n' rows; the default count is 1. +func (w *PosixWriter) CursorDown(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorUp(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x42}) + return +} + +// CursorForward moves the cursor forward by 'n' columns; the default count is 1. +func (w *PosixWriter) CursorForward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorBackward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x43}) + return +} + +// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. +func (w *PosixWriter) CursorBackward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorForward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x44}) + return +} + +// AskForCPR asks for a cursor position report (CPR). +func (w *PosixWriter) AskForCPR() { + // CPR: Cursor Position Request. + w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e}) + w.Flush() + return +} + +// SaveCursor saves current cursor position. +func (w *PosixWriter) SaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x73}) + return +} + +// UnSaveCursor restores cursor position after a Save Cursor. +func (w *PosixWriter) UnSaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x75}) + return +} + +/* Scrolling */ + +// ScrollDown scrolls display down one line. +func (w *PosixWriter) ScrollDown() { + w.WriteRaw([]byte{0x1b, 0x44}) + return +} + +// ScrollUp scroll display up one line. +func (w *PosixWriter) ScrollUp() { + w.WriteRaw([]byte{0x1b, 0x4d}) + return +} + +/* Title */ + +// SetTitle sets a title of terminal window. +func (w *PosixWriter) SetTitle(title string) { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b}) + w.WriteRaw(byteFilter([]byte(title), setTextFilter)) + w.WriteRaw([]byte{0x07}) + return +} + +// ClearTitle clears a title of terminal window. +func (w *PosixWriter) ClearTitle() { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07}) + return +} + +/* Font */ + +// SetColor sets text and background colors. and specify whether text is bold. +func (w *PosixWriter) SetColor(fg, bg Color, bold bool) { + f, ok := foregroundANSIColors[fg] + if !ok { + f = foregroundANSIColors[DefaultColor] + } + b, ok := backgroundANSIColors[bg] + if !ok { + b = backgroundANSIColors[DefaultColor] + } + syscall.Write(syscall.Stdout, []byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d}) + w.WriteRaw([]byte{0x1b, 0x5b}) + if !bold { + w.WriteRaw([]byte{0x30, 0x3b}) + } + w.WriteRaw(f) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw(b) + if bold { + w.WriteRaw([]byte{0x3b, 0x31}) + } + w.WriteRaw([]byte{0x6d}) + return +} + +var foregroundANSIColors = map[Color][]byte{ + DefaultColor: {0x33, 0x39}, // 39 + + // Low intensity. + Black: {0x33, 0x30}, // 30 + DarkRed: {0x33, 0x31}, // 31 + DarkGreen: {0x33, 0x32}, // 32 + Brown: {0x33, 0x33}, // 33 + DarkBlue: {0x33, 0x34}, // 34 + Purple: {0x33, 0x35}, // 35 + Cyan: {0x33, 0x36}, //36 + LightGray: {0x33, 0x37}, //37 + + // High intensity. + DarkGray: {0x39, 0x30}, // 90 + Red: {0x39, 0x31}, // 91 + Green: {0x39, 0x32}, // 92 + Yellow: {0x39, 0x33}, // 93 + Blue: {0x39, 0x34}, // 94 + Fuchsia: {0x39, 0x35}, // 95 + Turquoise: {0x39, 0x36}, // 96 + White: {0x39, 0x37}, // 97 +} + +var backgroundANSIColors = map[Color][]byte{ + DefaultColor: {0x34, 0x39}, // 49 + + // Low intensity. + Black: {0x34, 0x30}, // 40 + DarkRed: {0x34, 0x31}, // 41 + DarkGreen: {0x34, 0x32}, // 42 + Brown: {0x34, 0x33}, // 43 + DarkBlue: {0x34, 0x34}, // 44 + Purple: {0x34, 0x35}, // 45 + Cyan: {0x34, 0x36}, // 46 + LightGray: {0x34, 0x37}, // 47 + + // High intensity + DarkGray: {0x31, 0x30, 0x30}, // 100 + Red: {0x31, 0x30, 0x31}, // 101 + Green: {0x31, 0x30, 0x32}, // 102 + Yellow: {0x31, 0x30, 0x33}, // 103 + Blue: {0x31, 0x30, 0x34}, // 104 + Fuchsia: {0x31, 0x30, 0x35}, // 105 + Turquoise: {0x31, 0x30, 0x36}, // 106 + White: {0x31, 0x30, 0x37}, // 107 +} + +func writeFilter(buf byte) bool { + return buf != 0x1b && buf != 0x3f +} + +func setTextFilter(buf byte) bool { + return buf != 0x1b && buf != 0x07 +} + +func byteFilter(buf []byte, fn ...func(b byte) bool) []byte { + if len(fn) == 0 { + return buf + } + ret := make([]byte, 0, len(buf)) + f := fn[0] + for i, n := range buf { + if f(n) { + ret = append(ret, buf[i]) + } + } + return byteFilter(ret, fn[1:]...) +} + +var _ ConsoleWriter = &PosixWriter{} + +// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. +func NewStandardOutputWriter() *PosixWriter { + return &PosixWriter{ + fd: syscall.Stdout, + } +} diff --git a/vendor/github.com/c-bata/go-prompt/prompt.go b/vendor/github.com/c-bata/go-prompt/prompt.go index 6f72944c34..74ce03b616 100644 --- a/vendor/github.com/c-bata/go-prompt/prompt.go +++ b/vendor/github.com/c-bata/go-prompt/prompt.go @@ -1,7 +1,6 @@ package prompt import ( - "bytes" "io/ioutil" "log" "os" @@ -20,15 +19,14 @@ type Completer func(Document) []Suggest // Prompt is core struct of go-prompt. type Prompt struct { - in ConsoleParser - buf *Buffer - renderer *Render - executor Executor - history *History - completion *CompletionManager - keyBindings []KeyBind - ASCIICodeBindings []ASCIICodeBind - keyBindMode KeyBindMode + in ConsoleParser + buf *Buffer + renderer *Render + executor Executor + history *History + completion *CompletionManager + keyBindings []KeyBind + keyBindMode KeyBindMode } // Exec is the struct contains user input context. @@ -67,8 +65,6 @@ func (p *Prompt) Run() { case b := <-bufCh: if shouldExit, e := p.feed(b); shouldExit { p.renderer.BreakLine(p.buf) - stopReadBufCh <- struct{}{} - stopHandleSignalCh <- struct{}{} return } else if e != nil { // Stop goroutine to run readBuffer function @@ -109,7 +105,31 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) { // completion completing := p.completion.Completing() - p.handleCompletionKeyBinding(key, completing) + switch key { + case Down: + if completing { + p.completion.Next() + } + case Tab, ControlI: + p.completion.Next() + case Up: + if completing { + p.completion.Previous() + } + case BackTab: + p.completion.Previous() + case ControlSpace: + return + default: + if s, ok := p.completion.GetSelectedSuggestion(); ok { + w := p.buf.Document().GetWordBeforeCursor() + if w != "" { + p.buf.DeleteBeforeCursor(len([]rune(w))) + } + p.buf.InsertText(s.Text, false, true) + } + p.completion.Reset() + } switch key { case Enter, ControlJ, ControlM: @@ -144,43 +164,10 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) { return } case NotDefined: - if p.handleASCIICodeBinding(b) { - return - } p.buf.InsertText(string(b), false, true) } - p.handleKeyBinding(key) - return -} - -func (p *Prompt) handleCompletionKeyBinding(key Key, completing bool) { - switch key { - case Down: - if completing { - p.completion.Next() - } - case Tab, ControlI: - p.completion.Next() - case Up: - if completing { - p.completion.Previous() - } - case BackTab: - p.completion.Previous() - default: - if s, ok := p.completion.GetSelectedSuggestion(); ok { - w := p.buf.Document().GetWordBeforeCursorUntilSeparator(p.completion.wordSeparator) - if w != "" { - p.buf.DeleteBeforeCursor(len([]rune(w))) - } - p.buf.InsertText(s.Text, false, true) - } - p.completion.Reset() - } -} - -func (p *Prompt) handleKeyBinding(key Key) { + // Key bindings for i := range commonKeyBindings { kb := commonKeyBindings[i] if kb.Key == key { @@ -204,17 +191,7 @@ func (p *Prompt) handleKeyBinding(key Key) { kb.Fn(p.buf) } } -} - -func (p *Prompt) handleASCIICodeBinding(b []byte) bool { - checked := false - for _, kb := range p.ASCIICodeBindings { - if bytes.Compare(kb.ASCIICode, b) == 0 { - kb.Fn(p.buf) - checked = true - } - } - return checked + return } // Input just returns user input text. @@ -242,7 +219,6 @@ func (p *Prompt) Input() string { case b := <-bufCh: if shouldExit, e := p.feed(b); shouldExit { p.renderer.BreakLine(p.buf) - stopReadBufCh <- struct{}{} return "" } else if e != nil { // Stop goroutine to run readBuffer function @@ -261,16 +237,16 @@ func (p *Prompt) Input() string { func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) { log.Printf("[INFO] readBuffer start") for { + time.Sleep(10 * time.Millisecond) select { case <-stopCh: log.Print("[INFO] stop readBuffer") return default: - if b, err := p.in.Read(); err == nil && !(len(b) == 1 && b[0] == 0) { + if b, err := p.in.Read(); err == nil { bufCh <- b } } - time.Sleep(10 * time.Millisecond) } } diff --git a/vendor/github.com/c-bata/go-prompt/render.go b/vendor/github.com/c-bata/go-prompt/render.go index 2d774aafca..e37867876e 100644 --- a/vendor/github.com/c-bata/go-prompt/render.go +++ b/vendor/github.com/c-bata/go-prompt/render.go @@ -1,11 +1,5 @@ package prompt -import ( - "runtime" - - "github.com/mattn/go-runewidth" -) - // Render to render prompt information from state of Buffer. type Render struct { out ConsoleWriter @@ -88,6 +82,7 @@ func (r *Render) renderWindowTooSmall() { r.out.EraseScreen() r.out.SetColor(DarkRed, White, false) r.out.WriteStr("Your console window is too small...") + r.out.Flush() return } @@ -99,7 +94,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { prefix := r.getCurrentPrefix() formatted, width := formatSuggestions( suggestions, - int(r.col)-runewidth.StringWidth(prefix)-1, // -1 means a width of scrollbar + int(r.col)-len(prefix)-1, // -1 means a width of scrollbar ) // +1 means a width of scrollbar. width++ @@ -111,10 +106,10 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight] r.prepareArea(windowHeight) - cursor := runewidth.StringWidth(prefix) + runewidth.StringWidth(buf.Document().TextBeforeCursor()) + cursor := len(prefix) + len(buf.Document().TextBeforeCursor()) x, _ := r.toPos(cursor) if x+width >= int(r.col) { - cursor = r.backward(cursor, x+width-int(r.col)) + r.out.CursorBackward(x + width - int(r.col)) } contentHeight := len(completions.tmp) @@ -153,10 +148,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { r.out.SetColor(DefaultColor, r.scrollbarBGColor, false) } r.out.WriteStr(" ") - r.out.SetColor(DefaultColor, DefaultColor, false) - - r.lineWrap(cursor + width) - r.backward(cursor+width, width) + r.out.CursorBackward(width) } if x+width >= int(r.col) { @@ -170,64 +162,53 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) { // Render renders to the console. func (r *Render) Render(buffer *Buffer, completion *CompletionManager) { - // In situations where a pseudo tty is allocated (e.g. within a docker container), - // window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions. - if r.col == 0 { - return - } - defer r.out.Flush() - r.move(r.previousCursor, 0) - line := buffer.Text() prefix := r.getCurrentPrefix() - cursor := runewidth.StringWidth(prefix) + runewidth.StringWidth(line) + cursor := len(prefix) + len(line) - // prepare area - _, y := r.toPos(cursor) + // In situations where a psuedo tty is allocated (e.g. within a docker container), + // window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions. + if r.col > 0 { + // Erasing + r.clear(r.previousCursor) - h := y + 1 + int(completion.max) - if h > int(r.row) || completionMargin > int(r.col) { - r.renderWindowTooSmall() - return + // prepare area + _, y := r.toPos(cursor) + + h := y + 1 + int(completion.max) + if h > int(r.row) || completionMargin > int(r.col) { + r.renderWindowTooSmall() + return + } } // Rendering - r.out.HideCursor() - defer r.out.ShowCursor() - r.renderPrefix() r.out.SetColor(r.inputTextColor, r.inputBGColor, false) r.out.WriteStr(line) r.out.SetColor(DefaultColor, DefaultColor, false) - r.lineWrap(cursor) - r.out.EraseDown() - - cursor = r.backward(cursor, runewidth.StringWidth(line)-buffer.DisplayCursorPosition()) + cursor = r.backward(cursor, len(line)-buffer.CursorPosition) r.renderCompletion(buffer, completion) if suggest, ok := completion.GetSelectedSuggestion(); ok { - cursor = r.backward(cursor, runewidth.StringWidth(buffer.Document().GetWordBeforeCursorUntilSeparator(completion.wordSeparator))) + cursor = r.backward(cursor, len(buffer.Document().GetWordBeforeCursor())) r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false) r.out.WriteStr(suggest.Text) r.out.SetColor(DefaultColor, DefaultColor, false) - cursor += runewidth.StringWidth(suggest.Text) - rest := buffer.Document().TextAfterCursor() - r.out.WriteStr(rest) - cursor += runewidth.StringWidth(rest) - r.lineWrap(cursor) - - cursor = r.backward(cursor, runewidth.StringWidth(rest)) + cursor += len(suggest.Text) } + r.out.Flush() + r.previousCursor = cursor } // BreakLine to break line. func (r *Render) BreakLine(buffer *Buffer) { // Erasing and Render - cursor := runewidth.StringWidth(buffer.Document().TextBeforeCursor()) + runewidth.StringWidth(r.getCurrentPrefix()) + cursor := len(buffer.Document().TextBeforeCursor()) + len(r.getCurrentPrefix()) r.clear(cursor) r.renderPrefix() r.out.SetColor(r.inputTextColor, r.inputBGColor, false) @@ -238,40 +219,37 @@ func (r *Render) BreakLine(buffer *Buffer) { r.previousCursor = 0 } -// clear erases the screen from a beginning of input -// even if there is line break which means input length exceeds a window's width. func (r *Render) clear(cursor int) { - r.move(cursor, 0) + r.backward(cursor, cursor) r.out.EraseDown() } -// backward moves cursor to backward from a current cursor position -// regardless there is a line break. func (r *Render) backward(from, n int) int { return r.move(from, from-n) } -// move moves cursor to specified position from the beginning of input -// even if there is a line break. func (r *Render) move(from, to int) int { - fromX, fromY := r.toPos(from) + _, fromY := r.toPos(from) toX, toY := r.toPos(to) r.out.CursorUp(fromY - toY) - r.out.CursorBackward(fromX - toX) + r.out.WriteRaw([]byte{'\r'}) + r.out.CursorForward(toX) return to } // toPos returns the relative position from the beginning of the string. +// the coordinate system with the beginning of the string as (0,0) and the width as r.col. +// the cursor points to the next character, but it points to that character only at the right end (x == r.col - 1). +// x will not return 0 except for the first row. func (r *Render) toPos(cursor int) (x, y int) { col := int(r.col) - return cursor % col, cursor / col -} -func (r *Render) lineWrap(cursor int) { - if runtime.GOOS != "windows" && cursor > 0 && cursor%int(r.col) == 0 { - r.out.WriteRaw([]byte{'\n'}) + if cursor > 0 && cursor%col == 0 { + return col - 1, cursor/col - 1 } + + return cursor % col, cursor / col } func clamp(high, low, x float64) float64 { diff --git a/vendor/github.com/c-bata/go-prompt/shortcut.go b/vendor/github.com/c-bata/go-prompt/shortcut.go deleted file mode 100644 index 20fe71d085..0000000000 --- a/vendor/github.com/c-bata/go-prompt/shortcut.go +++ /dev/null @@ -1,43 +0,0 @@ -package prompt - -func dummyExecutor(in string) { return } - -// Input get the input data from the user and return it. -func Input(prefix string, completer Completer, opts ...Option) string { - pt := New(dummyExecutor, completer) - pt.renderer.prefixTextColor = DefaultColor - pt.renderer.prefix = prefix - - for _, opt := range opts { - if err := opt(pt); err != nil { - panic(err) - } - } - return pt.Input() -} - -// Choose to the shortcut of input function to select from string array. -// Deprecated: Maybe anyone want to use this. -func Choose(prefix string, choices []string, opts ...Option) string { - completer := newChoiceCompleter(choices, FilterHasPrefix) - pt := New(dummyExecutor, completer) - pt.renderer.prefixTextColor = DefaultColor - pt.renderer.prefix = prefix - - for _, opt := range opts { - if err := opt(pt); err != nil { - panic(err) - } - } - return pt.Input() -} - -func newChoiceCompleter(choices []string, filter Filter) Completer { - s := make([]Suggest, len(choices)) - for i := range choices { - s[i] = Suggest{Text: choices[i]} - } - return func(x Document) []Suggest { - return filter(s, x.GetWordBeforeCursor(), true) - } -} diff --git a/vendor/github.com/c-bata/go-prompt/windows_input.go b/vendor/github.com/c-bata/go-prompt/windows_input.go new file mode 100644 index 0000000000..13b332a775 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/windows_input.go @@ -0,0 +1,214 @@ +// +build windows + +package prompt + +import ( + "bytes" + "unicode/utf8" + + "github.com/mattn/go-tty" +) + +const maxReadBytes = 1024 + +// WindowsParser is a ConsoleParser implementation for Win32 console. +type WindowsParser struct { + tty *tty.TTY +} + +// Setup should be called before starting input +func (p *WindowsParser) Setup() error { + t, err := tty.Open() + if err != nil { + return err + } + p.tty = t + return nil +} + +// TearDown should be called after stopping input +func (p *WindowsParser) TearDown() error { + return p.tty.Close() +} + +// GetKey returns Key correspond to input byte codes. +func (p *WindowsParser) GetKey(b []byte) Key { + for _, k := range asciiSequences { + if bytes.Compare(k.ASCIICode, b) == 0 { + return k.Key + } + } + return NotDefined +} + +// Read returns byte array. +func (p *WindowsParser) Read() ([]byte, error) { + buf := make([]byte, maxReadBytes) + r, err := p.tty.ReadRune() + if err != nil { + return []byte{}, err + } + n := utf8.EncodeRune(buf[:], r) + for p.tty.Buffered() && n < maxReadBytes { + r, err := p.tty.ReadRune() + if err != nil { + break + } + n += utf8.EncodeRune(buf[n:], r) + } + return buf[:n], nil +} + +// GetWinSize returns WinSize object to represent width and height of terminal. +func (p *WindowsParser) GetWinSize() *WinSize { + w, h, err := p.tty.Size() + if err != nil { + panic(err) + } + return &WinSize{ + Row: uint16(h), + Col: uint16(w), + } +} + +var asciiSequences []*ASCIICode = []*ASCIICode{ + {Key: Escape, ASCIICode: []byte{0x1b}}, + + {Key: ControlSpace, ASCIICode: []byte{0x00}}, + {Key: ControlA, ASCIICode: []byte{0x1}}, + {Key: ControlB, ASCIICode: []byte{0x2}}, + {Key: ControlC, ASCIICode: []byte{0x3}}, + {Key: ControlD, ASCIICode: []byte{0x4}}, + {Key: ControlE, ASCIICode: []byte{0x5}}, + {Key: ControlF, ASCIICode: []byte{0x6}}, + {Key: ControlG, ASCIICode: []byte{0x7}}, + {Key: ControlH, ASCIICode: []byte{0x8}}, + //{Key: ControlI, ASCIICode: []byte{0x9}}, + //{Key: ControlJ, ASCIICode: []byte{0xa}}, + {Key: ControlK, ASCIICode: []byte{0xb}}, + {Key: ControlL, ASCIICode: []byte{0xc}}, + {Key: ControlM, ASCIICode: []byte{0xd}}, + {Key: ControlN, ASCIICode: []byte{0xe}}, + {Key: ControlO, ASCIICode: []byte{0xf}}, + {Key: ControlP, ASCIICode: []byte{0x10}}, + {Key: ControlQ, ASCIICode: []byte{0x11}}, + {Key: ControlR, ASCIICode: []byte{0x12}}, + {Key: ControlS, ASCIICode: []byte{0x13}}, + {Key: ControlT, ASCIICode: []byte{0x14}}, + {Key: ControlU, ASCIICode: []byte{0x15}}, + {Key: ControlV, ASCIICode: []byte{0x16}}, + {Key: ControlW, ASCIICode: []byte{0x17}}, + {Key: ControlX, ASCIICode: []byte{0x18}}, + {Key: ControlY, ASCIICode: []byte{0x19}}, + {Key: ControlZ, ASCIICode: []byte{0x1a}}, + + {Key: ControlBackslash, ASCIICode: []byte{0x1c}}, + {Key: ControlSquareClose, ASCIICode: []byte{0x1d}}, + {Key: ControlCircumflex, ASCIICode: []byte{0x1e}}, + {Key: ControlUnderscore, ASCIICode: []byte{0x1f}}, + {Key: Backspace, ASCIICode: []byte{0x7f}}, + + {Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x4f, 0x48}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x70}}, + {Key: End, ASCIICode: []byte{0x1b, 0x4f, 0x70}}, + + {Key: Enter, ASCIICode: []byte{0xa}}, + {Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x02, 0x7e}}, + {Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x05, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x04, 0x7e}}, + {Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x7e}}, + {Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x06, 0x7e}}, + {Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x07, 0x7e}}, + {Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x09, 0x7e}}, + {Key: Tab, ASCIICode: []byte{0x9}}, + {Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}}, + {Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x02, 0x7e}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}}, + {Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}}, + {Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}}, + {Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}}, + + {Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console + + {Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode + {Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode + {Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode + {Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode + + {Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}}, + {Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}}, + {Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}}, + {Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}}, + {Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}}, + {Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}}, + {Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}}, + {Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}}, + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}}, + {Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}}, + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}}, + + // Xterm + {Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x50}}, + {Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x51}}, + // &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}}, // Conflicts with CPR response + {Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}}, + {Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x02, 0x7e}}, + {Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x02, 0x7e}}, + {Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x02, 0x7e}}, + {Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x02, 0x7e}}, + {Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x02, 0x7e}}, + {Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x02, 0x7e}}, + {Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x02, 0x7e}}, + {Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x02, 0x7e}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5a}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5b}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5c}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5d}}, + + {Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2a}}, + {Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2b}}, + {Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2c}}, + {Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2d}}, + + // Tmux sends following keystrokes when control+arrow is pressed, but for + // Emacs ansi-term sends the same sequences for normal arrow keys. Consider + // it a normal arrow press, because that's more important. + {Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}}, + {Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}}, + {Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}}, + {Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}}, + + {Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x41}}, + {Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x42}}, + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x43}}, + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x44}}, + + {Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt + {Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt + + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm + {Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console +} + +// NewStandardInputParser returns ConsoleParser object to read from stdin. +func NewStandardInputParser() *WindowsParser { + return &WindowsParser{} +} diff --git a/vendor/github.com/c-bata/go-prompt/windows_output.go b/vendor/github.com/c-bata/go-prompt/windows_output.go new file mode 100644 index 0000000000..2281ab1563 --- /dev/null +++ b/vendor/github.com/c-bata/go-prompt/windows_output.go @@ -0,0 +1,333 @@ +// +build windows + +package prompt + +import ( + "io" + "strconv" + + "github.com/mattn/go-colorable" +) + +// WindowsWriter is a ConsoleWriter implementation for Win32 console. +// Output is converted from VT100 escape sequences by mattn/go-colorable. +type WindowsWriter struct { + out io.Writer + buffer []byte +} + +// WriteRaw to write raw byte array +func (w *WindowsWriter) WriteRaw(data []byte) { + w.buffer = append(w.buffer, data...) + // Flush because sometimes the render is broken when a large amount data in buffer. + w.Flush() + return +} + +// Write to write byte array without control sequences +func (w *WindowsWriter) Write(data []byte) { + w.WriteRaw(byteFilter(data, writeFilter)) + return +} + +// WriteRawStr to write raw string +func (w *WindowsWriter) WriteRawStr(data string) { + w.WriteRaw([]byte(data)) + return +} + +// WriteStr to write string without control sequences +func (w *WindowsWriter) WriteStr(data string) { + w.Write([]byte(data)) + return +} + +// Flush to flush buffer +func (w *WindowsWriter) Flush() error { + _, err := w.out.Write(w.buffer) + if err != nil { + return err + } + w.buffer = []byte{} + return nil +} + +/* Erase */ + +// EraseScreen erases the screen with the background colour and moves the cursor to home. +func (w *WindowsWriter) EraseScreen() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a}) + return +} + +// EraseUp erases the screen from the current line up to the top of the screen. +func (w *WindowsWriter) EraseUp() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a}) + return +} + +// EraseDown erases the screen from the current line down to the bottom of the screen. +func (w *WindowsWriter) EraseDown() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4a}) + return +} + +// EraseStartOfLine erases from the current cursor position to the start of the current line. +func (w *WindowsWriter) EraseStartOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b}) + return +} + +// EraseEndOfLine erases from the current cursor position to the end of the current line. +func (w *WindowsWriter) EraseEndOfLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x4b}) + return +} + +// EraseLine erases the entire current line. +func (w *WindowsWriter) EraseLine() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b}) + return +} + +/* Cursor */ + +// ShowCursor stops blinking cursor and show. +func (w *WindowsWriter) ShowCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68}) +} + +// HideCursor hides cursor. +func (w *WindowsWriter) HideCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c}) + return +} + +// CursorGoTo sets the cursor position where subsequent text will begin. +func (w *WindowsWriter) CursorGoTo(row, col int) { + if row == 0 && col == 0 { + // If no row/column parameters are provided (ie. [H), the cursor will move to the home position. + w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48}) + return + } + r := strconv.Itoa(row) + c := strconv.Itoa(col) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(r)) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw([]byte(c)) + w.WriteRaw([]byte{0x48}) + return +} + +// CursorUp moves the cursor up by 'n' rows; the default count is 1. +func (w *WindowsWriter) CursorUp(n int) { + if n < 0 { + w.CursorDown(n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x41}) + return +} + +// CursorDown moves the cursor down by 'n' rows; the default count is 1. +func (w *WindowsWriter) CursorDown(n int) { + if n < 0 { + w.CursorUp(n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x42}) + return +} + +// CursorForward moves the cursor forward by 'n' columns; the default count is 1. +func (w *WindowsWriter) CursorForward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorBackward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x43}) + return +} + +// CursorBackward moves the cursor backward by 'n' columns; the default count is 1. +func (w *WindowsWriter) CursorBackward(n int) { + if n == 0 { + return + } else if n < 0 { + w.CursorForward(-n) + return + } + s := strconv.Itoa(n) + w.WriteRaw([]byte{0x1b, 0x5b}) + w.WriteRaw([]byte(s)) + w.WriteRaw([]byte{0x44}) + return +} + +// AskForCPR asks for a cursor position report (CPR). +func (w *WindowsWriter) AskForCPR() { + // CPR: Cursor Position Request. + w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e}) + w.Flush() + return +} + +// SaveCursor saves current cursor position. +func (w *WindowsWriter) SaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x73}) + return +} + +// UnSaveCursor restores cursor position after a Save Cursor. +func (w *WindowsWriter) UnSaveCursor() { + w.WriteRaw([]byte{0x1b, 0x5b, 0x75}) + return +} + +/* Scrolling */ + +// ScrollDown scrolls display down one line. +func (w *WindowsWriter) ScrollDown() { + w.WriteRaw([]byte{0x1b, 0x44}) + return +} + +// ScrollUp scroll display up one line. +func (w *WindowsWriter) ScrollUp() { + w.WriteRaw([]byte{0x1b, 0x4d}) + return +} + +/* Title */ + +// SetTitle sets a title of terminal window. +func (w *WindowsWriter) SetTitle(title string) { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b}) + w.WriteRaw(byteFilter([]byte(title), setTextFilter)) + w.WriteRaw([]byte{0x07}) + return +} + +// ClearTitle clears a title of terminal window. +func (w *WindowsWriter) ClearTitle() { + w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07}) + return +} + +/* Font */ + +// SetColor sets text and background colors. and specify whether text is bold. +func (w *WindowsWriter) SetColor(fg, bg Color, bold bool) { + f, ok := foregroundANSIColors[fg] + if !ok { + f, _ = foregroundANSIColors[DefaultColor] + } + b, ok := backgroundANSIColors[bg] + if !ok { + b, _ = backgroundANSIColors[DefaultColor] + } + w.out.Write([]byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d}) + w.WriteRaw([]byte{0x1b, 0x5b}) + if !bold { + w.WriteRaw([]byte{0x30, 0x3b}) + } + w.WriteRaw(f) + w.WriteRaw([]byte{0x3b}) + w.WriteRaw(b) + if bold { + w.WriteRaw([]byte{0x3b, 0x31}) + } + w.WriteRaw([]byte{0x6d}) + return +} + +var foregroundANSIColors = map[Color][]byte{ + DefaultColor: {0x33, 0x39}, // 39 + + // Low intensity. + Black: {0x33, 0x30}, // 30 + DarkRed: {0x33, 0x31}, // 31 + DarkGreen: {0x33, 0x32}, // 32 + Brown: {0x33, 0x33}, // 33 + DarkBlue: {0x33, 0x34}, // 34 + Purple: {0x33, 0x35}, // 35 + Cyan: {0x33, 0x36}, //36 + LightGray: {0x33, 0x37}, //37 + + // High intensity. + DarkGray: {0x39, 0x30}, // 90 + Red: {0x39, 0x31}, // 91 + Green: {0x39, 0x32}, // 92 + Yellow: {0x39, 0x33}, // 93 + Blue: {0x39, 0x34}, // 94 + Fuchsia: {0x39, 0x35}, // 95 + Turquoise: {0x39, 0x36}, // 96 + White: {0x39, 0x37}, // 97 +} + +var backgroundANSIColors = map[Color][]byte{ + DefaultColor: {0x34, 0x39}, // 49 + + // Low intensity. + Black: {0x34, 0x30}, // 40 + DarkRed: {0x34, 0x31}, // 41 + DarkGreen: {0x34, 0x32}, // 42 + Brown: {0x34, 0x33}, // 43 + DarkBlue: {0x34, 0x34}, // 44 + Purple: {0x34, 0x35}, // 45 + Cyan: {0x34, 0x36}, // 46 + LightGray: {0x34, 0x37}, // 47 + + // High intensity + DarkGray: {0x31, 0x30, 0x30}, // 100 + Red: {0x31, 0x30, 0x31}, // 101 + Green: {0x31, 0x30, 0x32}, // 102 + Yellow: {0x31, 0x30, 0x33}, // 103 + Blue: {0x31, 0x30, 0x34}, // 104 + Fuchsia: {0x31, 0x30, 0x35}, // 105 + Turquoise: {0x31, 0x30, 0x36}, // 106 + White: {0x31, 0x30, 0x37}, // 107 +} + +func writeFilter(buf byte) bool { + return buf != 0x1b && buf != 0x3f +} + +func setTextFilter(buf byte) bool { + return buf != 0x1b && buf != 0x07 +} + +func byteFilter(buf []byte, fn ...func(b byte) bool) []byte { + if len(fn) == 0 { + return buf + } + ret := make([]byte, 0, len(buf)) + f := fn[0] + for i, n := range buf { + if f(n) { + ret = append(ret, buf[i]) + } + } + return byteFilter(ret, fn[1:]...) +} + +var _ ConsoleWriter = &WindowsWriter{} + +// NewStandardOutputWriter returns ConsoleWriter object to write to stdout. +func NewStandardOutputWriter() *WindowsWriter { + return &WindowsWriter{ + out: colorable.NewColorableStdout(), + } +} From 28d59c5ebd6258a80b98e41b58038d098220d31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Fri, 21 Sep 2018 15:19:11 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/climc/shell/webconsole.go | 4 +- pkg/mcclient/modules/mod_webconsole.go | 4 +- pkg/mcclient/options/webconsole.go | 4 +- pkg/webconsole/command/command.go | 10 +++ pkg/webconsole/command/ssh_command.go | 110 +++++++++++-------------- pkg/webconsole/handlers.go | 18 +--- pkg/webconsole/options/options.go | 4 +- pkg/webconsole/server/tty_server.go | 66 +++++++++++++-- pkg/webconsole/service/service.go | 2 +- pkg/webconsole/session/pty_session.go | 49 +++++++---- 10 files changed, 162 insertions(+), 109 deletions(-) diff --git a/cmd/climc/shell/webconsole.go b/cmd/climc/shell/webconsole.go index 7658e8e158..e8d4a26d9f 100644 --- a/cmd/climc/shell/webconsole.go +++ b/cmd/climc/shell/webconsole.go @@ -66,8 +66,8 @@ func init() { return nil }) - R(&o.WebConsoleServerOptions{}, "webconsole-server", "Connect server webconsole", func(s *mcclient.ClientSession, args *o.WebConsoleServerOptions) error { - ret, err := modules.WebConsole.DoServerConnect(s, args.ID) + R(&o.WebConsoleSshOptions{}, "webconsole-ssh", "Connect ssh webconsole", func(s *mcclient.ClientSession, args *o.WebConsoleSshOptions) error { + ret, err := modules.WebConsole.DoSshConnect(s, args.IP) if err != nil { return err } diff --git a/pkg/mcclient/modules/mod_webconsole.go b/pkg/mcclient/modules/mod_webconsole.go index ad0509a21b..963240028d 100644 --- a/pkg/mcclient/modules/mod_webconsole.go +++ b/pkg/mcclient/modules/mod_webconsole.go @@ -70,6 +70,6 @@ func (m WebConsoleManager) DoBaremetalConnect(s *mcclient.ClientSession, id stri return m.DoConnect(s, "baremetal", id, "", nil) } -func (m WebConsoleManager) DoServerConnect(s *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { - return m.DoConnect(s, "server", id, "", nil) +func (m WebConsoleManager) DoSshConnect(s *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) { + return m.DoConnect(s, "ssh", id, "", nil) } diff --git a/pkg/mcclient/options/webconsole.go b/pkg/mcclient/options/webconsole.go index d01e521985..ba9a36db14 100644 --- a/pkg/mcclient/options/webconsole.go +++ b/pkg/mcclient/options/webconsole.go @@ -37,7 +37,7 @@ func (opt *WebConsoleBaremetalOptions) Params() (*jsonutils.JSONDict, error) { return StructToParams(opt) } -type WebConsoleServerOptions struct { +type WebConsoleSshOptions struct { WebConsoleOptions - ID string `help:"Server id or name"` + IP string `help:"IP to connect"` } diff --git a/pkg/webconsole/command/command.go b/pkg/webconsole/command/command.go index 35ed12f53d..faccd18e88 100644 --- a/pkg/webconsole/command/command.go +++ b/pkg/webconsole/command/command.go @@ -15,6 +15,8 @@ type ICommand interface { GetProtocol() string GetCommand() *exec.Cmd Cleanup() error + GetData(string) (isShow bool, ouput string, command string) + ShowInfo() string } type BaseCommand struct { @@ -40,6 +42,14 @@ func (c BaseCommand) GetCommand() *exec.Cmd { return exec.Command(c.name, c.args...) } +func (c BaseCommand) GetData(comand string) (isShow bool, ouput string, command string) { + return true, "", "" +} + +func (c BaseCommand) ShowInfo() string { + return "" +} + func (c BaseCommand) Cleanup() error { log.Infof("BaseCommand Cleanup do nothing") return nil diff --git a/pkg/webconsole/command/ssh_command.go b/pkg/webconsole/command/ssh_command.go index 1d6423e454..8bd6cce9c1 100644 --- a/pkg/webconsole/command/ssh_command.go +++ b/pkg/webconsole/command/ssh_command.go @@ -4,81 +4,69 @@ import ( "fmt" "net" "os/exec" - "strings" + "time" + "yunion.io/x/log" o "yunion.io/x/onecloud/pkg/webconsole/options" - "yunion.io/x/pkg/utils" ) -type Metadata struct { - LoginAccount string `json:"login_account"` - LoginKey string `json:"login_key"` -} - -type SSHInfo struct { - ID string `json:"id"` - Metadata Metadata `json:"metadata"` - Eip string `json:"eip"` - IPs string `json:"ips"` - Keypaire string `json:"keypair"` - OsType string `json:"os_type"` -} - type SSHtoolSol struct { *BaseCommand - Info *SSHInfo + IP string + Username string + reTry int + showInfo string } -func NewSSHtoolSolCommand(info *SSHInfo) (*SSHtoolSol, error) { - if info.IPs == "" { - return nil, fmt.Errorf("Empty server ip address") - } - if info.Metadata.LoginAccount == "" { - return nil, fmt.Errorf("Empty username") - } - if len(info.Keypaire) != 0 { - return nil, fmt.Errorf("Not support private_key login") - } - if info.OsType != "Linux" { - return nil, fmt.Errorf("Not support login for %s", info.OsType) - } - args := "" - if info.Eip != "" { - args = fmt.Sprintf("%s@%s", info.Metadata.LoginAccount, info.Eip) +func NewSSHtoolSolCommand(ip string) (*SSHtoolSol, error) { + if conn, err := net.DialTimeout("tcp", ip+":22", time.Second*2); err != nil { + return nil, fmt.Errorf("IPAddress %s not accessable", ip) } else { - for _, ip := range strings.Split(info.IPs, ",") { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:22", ip)) - if err == nil { - args = fmt.Sprintf("%s@%s", info.Metadata.LoginAccount, ip) - break - } - defer conn.Close() - } + conn.Close() + return &SSHtoolSol{ + BaseCommand: nil, + IP: ip, + Username: "", + reTry: 0, + showInfo: fmt.Sprintf("%s login:", ip), + }, nil } - if len(args) == 0 { - return nil, fmt.Errorf("failed find usable connection ip address") - } - cmd := NewBaseCommand(o.Options.SSHtoolPath) - if info.Metadata.LoginKey != "" { - cmd := NewBaseCommand(o.Options.SSHtoolPath) - if passwd, err := utils.DescryptAESBase64(info.ID, info.Metadata.LoginKey); err != nil { - return nil, err - } else { - cmd.AppendArgs("-p", passwd) - } - } - cmd.AppendArgs(args) - tool := &SSHtoolSol{ - BaseCommand: cmd, - Info: info, - } - return tool, nil } func (c *SSHtoolSol) GetCommand() *exec.Cmd { - return c.BaseCommand.GetCommand() + return nil } -func (c SSHtoolSol) GetProtocol() string { +func (c *SSHtoolSol) Cleanup() error { + log.Infof("SSHtoolSol Cleanup do nothing") + return nil +} + +func (c *SSHtoolSol) GetProtocol() string { return PROTOCOL_TTY } + +func (c *SSHtoolSol) GetData(data string) (isShow bool, ouput string, command string) { + if len(c.Username) == 0 { + if len(data) == 0 { + //用户名不能为空 + return true, c.showInfo, "" + } + c.Username = data + return false, "Password:", "" + } else { + return true, "", fmt.Sprintf("%s -p %s %s %s@%s", o.Options.SshpassToolPath, data, o.Options.SshToolPath, c.Username, c.IP) + } +} + +func (c *SSHtoolSol) ShowInfo() string { + c.Username = "" + c.reTry++ + if c.reTry == 3 { + c.reTry = 0 + //清屏 + time.Sleep(1 * time.Second) + return "\033c " + c.showInfo + } + return c.showInfo +} diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index b470820f57..39f5627924 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -31,7 +31,7 @@ func InitHandlers(app *appsrv.Application) { app.AddHandler("POST", ApiPathPrefix+"k8s//shell", auth.Authenticate(handleK8sShell)) app.AddHandler("POST", ApiPathPrefix+"k8s//log", auth.Authenticate(handleK8sLog)) app.AddHandler("POST", ApiPathPrefix+"baremetal/", auth.Authenticate(handleBaremetalShell)) - app.AddHandler("POST", ApiPathPrefix+"server/", auth.Authenticate(handleServerShell)) + app.AddHandler("POST", ApiPathPrefix+"ssh/", auth.Authenticate(handleSshShell)) } func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) { @@ -157,25 +157,13 @@ func handleK8sLog(ctx context.Context, w http.ResponseWriter, r *http.Request) { handleK8sCommand(ctx, w, r, command.NewPodLogCommand) } -func handleServerShell(ctx context.Context, w http.ResponseWriter, r *http.Request) { +func handleSshShell(ctx context.Context, w http.ResponseWriter, r *http.Request) { env, err := fetchCloudEnv(ctx, w, r) if err != nil { httperrors.GeneralServerError(w, err) return } - serverId := env.Params[""] - ret, err := modules.Servers.Get(env.ClientSessin, serverId, jsonutils.Marshal(map[string]bool{"with_meta": true, "admin": true})) - if err != nil { - httperrors.GeneralServerError(w, err) - return - } - info := command.SSHInfo{} - err = ret.Unmarshal(&info) - if err != nil { - httperrors.GeneralServerError(w, err) - return - } - cmd, err := command.NewSSHtoolSolCommand(&info) + cmd, err := command.NewSSHtoolSolCommand(env.Params[""]) if err != nil { httperrors.GeneralServerError(w, err) return diff --git a/pkg/webconsole/options/options.go b/pkg/webconsole/options/options.go index b504dfa7af..d881ea9386 100644 --- a/pkg/webconsole/options/options.go +++ b/pkg/webconsole/options/options.go @@ -14,6 +14,6 @@ type WebConsoleOptions struct { ApiServer string `help:"API server url to handle websocket connection, usually with public access" default:"http://webconsole.yunion.io"` KubectlPath string `help:"kubectl binary path used to connect k8s cluster" default:"/usr/bin/kubectl"` IpmitoolPath string `help:"ipmitool binary path used to connect baremetal sol" default:"/usr/bin/ipmitool"` - SSHtoolPath string `help:"sshtool binary path used to connect server sol" default:"/usr/bin/ssh"` - SSHPasstoolPath string `help:"sshpass tool binary path used to connect server sol" default:"/usr/local/bin/sshpass"` + SshToolPath string `help:"sshtool binary path used to connect server sol" default:"/usr/bin/ssh"` + SshpassToolPath string `help:"sshpass tool binary path used to connect server sol" default:"/usr/local/bin/sshpass"` } diff --git a/pkg/webconsole/server/tty_server.go b/pkg/webconsole/server/tty_server.go index 717f7d1fa4..4cf4f0757d 100644 --- a/pkg/webconsole/server/tty_server.go +++ b/pkg/webconsole/server/tty_server.go @@ -1,6 +1,11 @@ package server import ( + "os/exec" + "strconv" + "strings" + "time" + socketio "github.com/googollee/go-socket.io" "github.com/kr/pty" @@ -56,19 +61,67 @@ func initSocketHandler(so socketio.Socket, p *session.Pty) { go func() { buf := make([]byte, 1024) for { - n, err := p.Pty.Read(buf) - if err != nil { - log.Errorf("Failed to read from pty master: %v", err) - cleanUp(so, p) + if p.IsOk { + if p.Cmd == nil || p.Cmd.Process == nil { + p.IsOk = false + } else if p.Pty == nil { + p.IsOk = false + } else if n, err := p.Pty.Read(buf); err != nil { + p.IsOk = false + } else { + so.Emit(OUTPUT_EVENT, string(buf[0:n])) + } + if !p.IsOk { + p.Stop() + if info := p.Session.ShowInfo(); len(info) > 0 { + so.Emit(OUTPUT_EVENT, info) + } + } + } else if p.Exit { return + } else { + //避免goroutine死循环导致主进程卡死 + time.Sleep(time.Microsecond * 50) } - so.Emit(OUTPUT_EVENT, string(buf[0:n])) } }() // handle write so.On(INPUT_EVENT, func(data string) { - p.Pty.Write([]byte(data)) + if !p.IsOk { + if data == "\r" { + p.Show, p.Output, p.Command = p.Session.GetData(p.Buffer) + so.Emit(OUTPUT_EVENT, "\r\n") + if len(p.Output) > 0 { + so.Emit(OUTPUT_EVENT, p.Output) + } + if len(p.Command) > 0 { + log.Infof("exec: %s", p.Command) + args := strings.Split(p.Command, " ") + cmd := exec.Command(args[0], args[1:]...) + if _pty, err := pty.Start(cmd); err != nil { + so.Emit(OUTPUT_EVENT, err.Error()+"\r\n") + log.Errorf("exec error: %v", err) + } else { + p.Pty, p.Cmd, p.IsOk = _pty, cmd, true + } + } + p.Buffer, data = "", "" + } else if data == "\u007f" { + //退格处理 + if len(p.Buffer) > 0 { + p.Buffer = p.Buffer[:len(p.Buffer)-1] + data = "\b \b" + } + } else if strconv.IsPrint([]rune(data)[0]) { + p.Buffer += data + } + if p.Show && len(data) > 0 { + so.Emit(OUTPUT_EVENT, data) + } + } else { + p.Pty.Write([]byte(data)) + } }) // handle resize @@ -106,4 +159,5 @@ func initSocketHandler(so socketio.Socket, p *session.Pty) { func cleanUp(so socketio.Socket, p *session.Pty) { so.Disconnect() p.Stop() + p.Exit = true } diff --git a/pkg/webconsole/service/service.go b/pkg/webconsole/service/service.go index 3836f618bb..3cf4f09adb 100644 --- a/pkg/webconsole/service/service.go +++ b/pkg/webconsole/service/service.go @@ -34,7 +34,7 @@ func StartService() { log.Fatalf("invalid --api-server %s", o.Options.ApiServer) } - for _, binPath := range []string{o.Options.KubectlPath, o.Options.IpmitoolPath} { + for _, binPath := range []string{o.Options.KubectlPath, o.Options.IpmitoolPath, o.Options.SshToolPath, o.Options.SshpassToolPath} { ensureBinExists(binPath) } diff --git a/pkg/webconsole/session/pty_session.go b/pkg/webconsole/session/pty_session.go index c28d0e6542..7cb2ea8182 100644 --- a/pkg/webconsole/session/pty_session.go +++ b/pkg/webconsole/session/pty_session.go @@ -17,6 +17,12 @@ type Pty struct { Pty *os.File sizeCh chan os.Signal size *pty.Winsize + Show bool + IsOk bool + Buffer string + Output string + Command string + Exit bool } func NewPty(session *SSession) (p *Pty, err error) { @@ -24,11 +30,17 @@ func NewPty(session *SSession) (p *Pty, err error) { p = &Pty{ Session: session, Cmd: cmd, + Show: true, + IsOk: true, + Exit: false, + Pty: nil, } log.Debugf("[session %s] Start command: %#v", session.Id, cmd) - p.Pty, err = pty.Start(p.Cmd) - if err != nil { - return + if cmd != nil { + p.Pty, err = pty.Start(p.Cmd) + if err != nil { + return + } } p.sizeCh = make(chan os.Signal, 1) p.size = &pty.Winsize{} @@ -40,10 +52,12 @@ func NewPty(session *SSession) (p *Pty, err error) { func (p *Pty) startResizeMonitor() { go func() { for range p.sizeCh { - if err := pty.Setsize(p.Pty, p.size); err != nil { - log.Errorf("Resize pty error: %v", err) - } else { - log.Debugf("Resize pty to %#v, cmd: %#v", p.size, p.Cmd) + if p.Pty != nil { + if err := pty.Setsize(p.Pty, p.size); err != nil { + log.Errorf("Resize pty error: %v", err) + } else { + log.Debugf("Resize pty to %#v, cmd: %#v", p.size, p.Cmd) + } } } }() @@ -55,18 +69,17 @@ func (p *Pty) Resize(size *pty.Winsize) { } func (p *Pty) Stop() { - var err error - err = p.Pty.Close() - if err != nil { - log.Errorf("Close PTY error: %v", err) + if p.Pty != nil { + if err := p.Pty.Close(); err != nil { + log.Errorf("Close PTY error: %v", err) + } } - err = p.Cmd.Process.Signal(os.Kill) - if err != nil { - log.Errorf("Kill command process error: %v", err) - } - err = p.Cmd.Wait() - if err != nil { - log.Errorf("Wait command error: %v", err) + if p.Cmd != nil && p.Cmd.Process != nil { + if err := p.Cmd.Process.Signal(os.Kill); err != nil { + log.Errorf("Kill command process error: %v", err) + } else if err := p.Cmd.Wait(); err != nil { + log.Errorf("Wait command error: %v", err) + } } p.Session.Close() } From 8fc8d892fae001d5e5e988113a40d8bf4e1b84ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Sat, 29 Sep 2018 18:09:24 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E7=BB=84=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/guests.go | 4 ++-- pkg/compute/models/secgroups.go | 18 ++++++++++++------ pkg/mcclient/modules/mod_secgroups.go | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 454e438dfe..9c952822f5 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1151,7 +1151,7 @@ func (self *SGuest) getAdminSecgroupName() string { func (self *SGuest) getSecurityRules() string { secgrp := self.getSecgroup() if secgrp != nil { - return secgrp.getSecurityRuleString() + return secgrp.getSecurityRuleString("") } else { return options.Options.DefaultSecurityRules } @@ -1160,7 +1160,7 @@ func (self *SGuest) getSecurityRules() string { func (self *SGuest) getAdminSecurityRules() string { secgrp := self.getAdminSecgroup() if secgrp != nil { - return secgrp.getSecurityRuleString() + return secgrp.getSecurityRuleString("") } else { return options.Options.DefaultAdminSecurityRules } diff --git a/pkg/compute/models/secgroups.go b/pkg/compute/models/secgroups.go index 1fc2ca24bd..15407fe8e6 100644 --- a/pkg/compute/models/secgroups.go +++ b/pkg/compute/models/secgroups.go @@ -9,6 +9,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" ) @@ -56,7 +57,7 @@ func (self *SSecurityGroup) GetGuests() []SGuest { func (self *SSecurityGroup) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query) extra.Add(jsonutils.NewInt(int64(len(self.GetGuests()))), "guest_cnt") - extra.Add(jsonutils.NewString(self.getSecurityRuleString()), "rules") + extra.Add(jsonutils.NewString(self.getSecurityRuleString("")), "rules") return extra } @@ -65,6 +66,8 @@ func (self *SSecurityGroup) GetCustomizeColumns(ctx context.Context, userCred mc extra.Add(jsonutils.NewInt(int64(len(self.GetGuests()))), "guest_cnt") extra.Add(jsonutils.NewTimeString(self.CreatedAt), "created_at") extra.Add(jsonutils.NewString(self.Description), "description") + extra.Add(jsonutils.NewString(self.getSecurityRuleString("in")), "in_rules") + extra.Add(jsonutils.NewString(self.getSecurityRuleString("out")), "out_rules") return extra } @@ -89,9 +92,12 @@ func (manager *SSecurityGroupManager) FetchSecgroupById(secId string) *SSecurity return nil } -func (self *SSecurityGroup) getSecurityRules() (rules []SSecurityGroupRule) { +func (self *SSecurityGroup) getSecurityRules(direction string) (rules []SSecurityGroupRule) { secgrouprules := SecurityGroupRuleManager.Query().SubQuery() - sql := secgrouprules.Query().Filter(sqlchemy.Equals(secgrouprules.Field("secgroup_id"), self.Id)) + sql := secgrouprules.Query().Filter(sqlchemy.Equals(secgrouprules.Field("secgroup_id"), self.Id)).Desc("priority") + if len(direction) > 0 && utils.IsInStringArray(direction, []string{"in", "out"}) { + sql = sql.Equals("direction", direction) + } if err := db.FetchModelObjects(SecurityGroupRuleManager, sql, &rules); err != nil { log.Errorf("GetGuests fail %s", err) return nil @@ -99,8 +105,8 @@ func (self *SSecurityGroup) getSecurityRules() (rules []SSecurityGroupRule) { return } -func (self *SSecurityGroup) getSecurityRuleString() string { - secgrouprules := self.getSecurityRules() +func (self *SSecurityGroup) getSecurityRuleString(direction string) string { + secgrouprules := self.getSecurityRules(direction) var rules []string for _, rule := range secgrouprules { rules = append(rules, rule.GetRule()) @@ -138,7 +144,7 @@ func (self *SSecurityGroup) PerformClone(ctx context.Context, userCred mcclient. return nil, err //db.OpsLog.LogCloneEvent(self, secgroup, userCred, nil) } - secgrouprules := self.getSecurityRules() + secgrouprules := self.getSecurityRules("") for _, rule := range secgrouprules { secgrouprule := &SSecurityGroupRule{} secgrouprule.SetModelManager(SecurityGroupRuleManager) diff --git a/pkg/mcclient/modules/mod_secgroups.go b/pkg/mcclient/modules/mod_secgroups.go index 78dbaacae0..7208a5bcb4 100644 --- a/pkg/mcclient/modules/mod_secgroups.go +++ b/pkg/mcclient/modules/mod_secgroups.go @@ -8,7 +8,7 @@ func init() { SecGroups = NewComputeManager("secgroup", "secgroups", []string{"ID", "Name", "Rules", "Is_public", "Created_at", - "Guest_cnt", "Description"}, + "Guest_cnt", "Description", "in_rules", "out_rules"}, []string{}) registerCompute(&SecGroups) From 3eea07f2791c9251da1bb504bb3d07de5216b718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Thu, 20 Sep 2018 10:57:31 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E9=81=BF=E5=85=8D/tmp=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=AE=B9=E9=87=8F=E4=B8=8D=E5=A4=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/cloudcommon/options.go | 1 + pkg/cloudprovider/resources.go | 2 +- pkg/compute/hostdrivers/aliyun.go | 12 ++++++++--- pkg/util/aliyun/aliyun.go | 33 ++++++++++++++++--------------- pkg/util/aliyun/storagecache.go | 19 +++++++++++------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/pkg/cloudcommon/options.go b/pkg/cloudcommon/options.go index a6e4fa7336..5d97bf2f0f 100644 --- a/pkg/cloudcommon/options.go +++ b/pkg/cloudcommon/options.go @@ -26,6 +26,7 @@ type Options struct { AdminProject string `help:"Admin project" default:"system" alias:"admin-tenant-name"` CorsHosts []string `help:"List of hostname that allow CORS"` AuthTokenCacheSize uint32 `help:"Auth token Cache Size" default:"2048"` + TempPath string `help:"Path for store temp file, at least 40G space" default:"/opt/yunion/tmp"` ApplicationID string `help:"Application ID"` RequestWorkerCount int `default:"4" help:"Request worker thread count, default is 4"` diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index f4d95664b9..25b17d1387 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -79,7 +79,7 @@ type ICloudStoragecache interface { CreateIImage(snapshotId, imageName, imageDesc string) (ICloudImage, error) - DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string) (jsonutils.JSONObject, error) + DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) } diff --git a/pkg/compute/hostdrivers/aliyun.go b/pkg/compute/hostdrivers/aliyun.go index 15842e7a9b..f5e00d3775 100644 --- a/pkg/compute/hostdrivers/aliyun.go +++ b/pkg/compute/hostdrivers/aliyun.go @@ -3,14 +3,16 @@ package hostdrivers import ( "context" "fmt" + "os" "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" - "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" ) type SAliyunHostDriver struct { @@ -36,7 +38,6 @@ func (self *SAliyunHostDriver) CheckAndSetCacheImage(ctx context.Context, host * osType, _ := params.GetString("os_type") osDist, _ := params.GetString("os_distribution") - isForce := jsonutils.QueryBoolean(params, "is_force", false) userCred := task.GetUserCred() taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { @@ -93,7 +94,12 @@ func (self *SAliyunHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, return nil, err } else { scimg.SetExternalId(iImage.GetId()) - if result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId()); err != nil { + if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) { + if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil { + return nil, err + } + } + if result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath); err != nil { scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) return nil, err } else { diff --git a/pkg/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index c8a334a57c..b41cefe251 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -4,11 +4,12 @@ import ( "github.com/aliyun/alibaba-cloud-sdk-go/sdk" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "time" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" - "time" ) const ( @@ -205,11 +206,11 @@ func (self *SAliyunClient) GetIStoragecacheById(id string) (cloudprovider.ICloud } type SAccountBalance struct { - AvailableAmount float64 + AvailableAmount float64 AvailableCashAmount float64 - CreditAmount float64 - MybankCreditAmount float64 - Currency string + CreditAmount float64 + MybankCreditAmount float64 + Currency string } type SCashCoupon struct { @@ -226,15 +227,15 @@ type SCashCoupon struct { } type SPrepaidCard struct { - PrepaidCardId string - PrepaidCardNo string - GrantedTime time.Time - EffectiveTime time.Time - ExpiryTime time.Time - NominalValue float64 - Balance float64 - ApplicableProducts string - ApplicableScenarios string + PrepaidCardId string + PrepaidCardNo string + GrantedTime time.Time + EffectiveTime time.Time + ExpiryTime time.Time + NominalValue float64 + Balance float64 + ApplicableProducts string + ApplicableScenarios string } func (self *SAliyunClient) QueryAccountBalance() (*SAccountBalance, error) { @@ -272,7 +273,7 @@ func (self *SAliyunClient) QueryCashCoupons() ([]SCashCoupon, error) { func (self *SAliyunClient) QueryPrepaidCards() ([]SPrepaidCard, error) { params := make(map[string]string) params["EffectiveOrNot"] = "True" - body, err := self.businessRequest("QueryPrepaidCards", params) + body, err := self.businessRequest("QueryPrepaidCards", params) if err != nil { log.Errorf("QueryPrepaidCards fail %s", err) return nil, err @@ -284,4 +285,4 @@ func (self *SAliyunClient) QueryPrepaidCards() ([]SPrepaidCard, error) { return nil, err } return cards, nil -} \ No newline at end of file +} diff --git a/pkg/util/aliyun/storagecache.go b/pkg/util/aliyun/storagecache.go index 5f8bd8e41c..1bf66aa958 100644 --- a/pkg/util/aliyun/storagecache.go +++ b/pkg/util/aliyun/storagecache.go @@ -2,9 +2,11 @@ package aliyun import ( "fmt" + "io/ioutil" "os" "strings" "time" + "github.com/aliyun/aliyun-oss-go-sdk/oss" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -252,8 +254,8 @@ func (self *SRegion) createIImage(snapshoutId, imageName, imageDesc string) (str } } -func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string) (jsonutils.JSONObject, error) { - return self.downloadImage(userCred, imageId, extId) +func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { + return self.downloadImage(userCred, imageId, extId, path) } // 定义进度条监听器。 @@ -279,8 +281,12 @@ func (listener *OssProgressListener) ProgressChanged(event *oss.ProgressEvent) { } } -func (self *SStoragecache) downloadImage(userCred mcclient.TokenCredential, imageId string, extId string) (jsonutils.JSONObject, error) { - tmpImageFile := fmt.Sprintf("/tmp/%s", extId) +func (self *SStoragecache) downloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { + tmpImageFile, err := ioutil.TempFile(path, extId) + if err != nil { + return nil, err + } + defer os.Remove(tmpImageFile.Name()) bucketName := strings.ToLower(fmt.Sprintf("imgcache-%s", self.region.GetId())) if bucket, err := self.region.checkBucket(bucketName); err != nil { return nil, err @@ -294,17 +300,16 @@ func (self *SStoragecache) downloadImage(userCred mcclient.TokenCredential, imag return nil, err } else if len(imageList.Objects) != 1 { return nil, httperrors.NewResourceNotFoundError("exported image not find") - } else if err := bucket.DownloadFile(imageList.Objects[0].Key, tmpImageFile, 12*1024*1024, oss.Routines(3), oss.Progress(&OssProgressListener{})); err != nil { + } else if err := bucket.DownloadFile(imageList.Objects[0].Key, tmpImageFile.Name(), 12*1024*1024, oss.Routines(3), oss.Progress(&OssProgressListener{})); err != nil { return nil, err } else { s := auth.GetAdminSession(options.Options.Region, "") params := jsonutils.Marshal(map[string]string{"image_id": imageId, "disk-format": "raw"}) - if file, err := os.Open(tmpImageFile); err != nil { + if file, err := os.Open(tmpImageFile.Name()); err != nil { return nil, err } else if result, err := modules.Images.Upload(s, params, file, imageList.Objects[0].Size); err != nil { return nil, err } else { - os.Remove(tmpImageFile) return result, nil } }