mirror of
				https://github.com/kardolus/chatgpt-cli.git
				synced 2024-09-08 23:15:00 +03:00 
			
		
		
		
	Add readline for dynamic interactive prompts
This commit is contained in:
		| @@ -1,7 +1,6 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| @@ -9,6 +8,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/chzyer/readline" | ||||
| 	"github.com/kardolus/chatgpt-cli/client" | ||||
| 	"github.com/kardolus/chatgpt-cli/config" | ||||
| 	"github.com/kardolus/chatgpt-cli/configmanager" | ||||
| @@ -196,30 +196,39 @@ func run(cmd *cobra.Command, args []string) error { | ||||
| 	} | ||||
|  | ||||
| 	if interactiveMode { | ||||
| 		fmt.Println("Entering interactive mode. Type 'exit' and press Enter or press Ctrl+C to quit.") | ||||
| 		fmt.Printf("Entering interactive mode. Type 'exit' and press Enter or press Ctrl+C to quit.\n\n") | ||||
|  | ||||
| 		// Initialize readline with an empty prompt first, as we'll set it dynamically. | ||||
| 		rl, err := readline.New("") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer rl.Close() | ||||
|  | ||||
| 		prompt := func(qNum int) string { | ||||
| 			return fmt.Sprintf("[%s] Q%d: ", time.Now().Format("2006-01-02 15:04:05"), qNum) | ||||
| 		} | ||||
|  | ||||
| 		scanner := bufio.NewScanner(os.Stdin) | ||||
| 		qNum := 1 | ||||
| 		for { | ||||
| 			fmt.Printf("\n[%s] Q%d: ", time.Now().Format("2006-01-02 15:04:05"), qNum) | ||||
| 			scanned := scanner.Scan() | ||||
| 			if !scanned { | ||||
| 				if err := scanner.Err(); err != nil { | ||||
| 					fmt.Println(err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 				// Exit the loop if no more input (e.g., Ctrl+D) | ||||
| 			// Set and update the readline prompt dynamically | ||||
| 			rl.SetPrompt(prompt(qNum)) | ||||
|  | ||||
| 			line, err := rl.Readline() | ||||
| 			if err == readline.ErrInterrupt || err == io.EOF { | ||||
| 				fmt.Println("Bye!") | ||||
| 				break | ||||
| 			} | ||||
| 			line := scanner.Text() | ||||
|  | ||||
| 			if line == "exit" { | ||||
| 				fmt.Println("Bye!") | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if err := client.Stream(line); err != nil { | ||||
| 				fmt.Println("Error:", err) | ||||
| 			} else { | ||||
| 				// Handle the streamed response here, which currently does nothing | ||||
| 				fmt.Println() | ||||
| 				qNum++ | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,6 +3,7 @@ module github.com/kardolus/chatgpt-cli | ||||
| go 1.20 | ||||
|  | ||||
| require ( | ||||
| 	github.com/chzyer/readline v1.5.1 | ||||
| 	github.com/golang/mock v1.6.0 | ||||
| 	github.com/onsi/gomega v1.32.0 | ||||
| 	github.com/sclevine/spec v1.4.0 | ||||
|   | ||||
							
								
								
									
										7
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,3 +1,9 @@ | ||||
| github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= | ||||
| github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= | ||||
| github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= | ||||
| github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= | ||||
| github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= | ||||
| github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| @@ -80,6 +86,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= | ||||
| golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/chzyer/readline/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/chzyer/readline/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| .vscode/* | ||||
							
								
								
									
										8
									
								
								vendor/github.com/chzyer/readline/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/chzyer/readline/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| language: go | ||||
| go: | ||||
|   - 1.x | ||||
| script: | ||||
|   - GOOS=windows go install github.com/chzyer/readline/example/... | ||||
|   - GOOS=linux go install github.com/chzyer/readline/example/... | ||||
|   - GOOS=darwin go install github.com/chzyer/readline/example/... | ||||
|   - go test -race -v | ||||
							
								
								
									
										58
									
								
								vendor/github.com/chzyer/readline/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/chzyer/readline/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| # ChangeLog | ||||
|  | ||||
| ### 1.4 - 2016-07-25 | ||||
|  | ||||
| * [#60][60] Support dynamic autocompletion | ||||
| * Fix ANSI parser on Windows | ||||
| * Fix wrong column width in complete mode on Windows | ||||
| * Remove dependent package "golang.org/x/crypto/ssh/terminal" | ||||
|  | ||||
| ### 1.3 - 2016-05-09 | ||||
|  | ||||
| * [#38][38] add SetChildren for prefix completer interface | ||||
| * [#42][42] improve multiple lines compatibility | ||||
| * [#43][43] remove sub-package(runes) for gopkg compatibility | ||||
| * [#46][46] Auto complete with space prefixed line | ||||
| * [#48][48]	support suspend process (ctrl+Z) | ||||
| * [#49][49] fix bug that check equals with previous command | ||||
| * [#53][53] Fix bug which causes integer divide by zero panicking when input buffer is empty | ||||
|  | ||||
| ### 1.2 - 2016-03-05 | ||||
|  | ||||
| * Add a demo for checking password strength [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go), , written by [@sahib](https://github.com/sahib) | ||||
| * [#23][23], support stdin remapping | ||||
| * [#27][27], add a `UniqueEditLine` to `Config`, which will erase the editing line after user submited it, usually use in IM. | ||||
| * Add a demo for multiline [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) which can submit one SQL by multiple lines. | ||||
| * Supports performs even stdin/stdout is not a tty. | ||||
| * Add a new simple apis for single instance, check by [here](https://github.com/chzyer/readline/blob/master/std.go). It need to save history manually if using this api. | ||||
| * [#28][28], fixes the history is not working as expected. | ||||
| * [#33][33], vim mode now support `c`, `d`, `x (delete character)`, `r (replace character)` | ||||
|  | ||||
| ### 1.1 - 2015-11-20 | ||||
|  | ||||
| * [#12][12] Add support for key `<Delete>`/`<Home>`/`<End>` | ||||
| * Only enter raw mode as needed (calling `Readline()`), program will receive signal(e.g. Ctrl+C) if not interact with `readline`. | ||||
| * Bugs fixed for `PrefixCompleter` | ||||
| * Press `Ctrl+D` in empty line will cause `io.EOF` in error, Press `Ctrl+C` in anytime will cause `ErrInterrupt` instead of `io.EOF`, this will privodes a shell-like user experience. | ||||
| * Customable Interrupt/EOF prompt in `Config` | ||||
| * [#17][17] Change atomic package to use 32bit function to let it runnable on arm 32bit devices | ||||
| * Provides a new password user experience(`readline.ReadPasswordEx()`). | ||||
|  | ||||
| ### 1.0 - 2015-10-14 | ||||
|  | ||||
| * Initial public release. | ||||
|  | ||||
| [12]: https://github.com/chzyer/readline/pull/12 | ||||
| [17]: https://github.com/chzyer/readline/pull/17 | ||||
| [23]: https://github.com/chzyer/readline/pull/23 | ||||
| [27]: https://github.com/chzyer/readline/pull/27 | ||||
| [28]: https://github.com/chzyer/readline/pull/28 | ||||
| [33]: https://github.com/chzyer/readline/pull/33 | ||||
| [38]: https://github.com/chzyer/readline/pull/38 | ||||
| [42]: https://github.com/chzyer/readline/pull/42 | ||||
| [43]: https://github.com/chzyer/readline/pull/43 | ||||
| [46]: https://github.com/chzyer/readline/pull/46 | ||||
| [48]: https://github.com/chzyer/readline/pull/48 | ||||
| [49]: https://github.com/chzyer/readline/pull/49 | ||||
| [53]: https://github.com/chzyer/readline/pull/53 | ||||
| [60]: https://github.com/chzyer/readline/pull/60 | ||||
							
								
								
									
										22
									
								
								vendor/github.com/chzyer/readline/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/chzyer/readline/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2015 Chzyer | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
|  | ||||
							
								
								
									
										114
									
								
								vendor/github.com/chzyer/readline/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/chzyer/readline/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| [](https://travis-ci.org/chzyer/readline) | ||||
| [](LICENSE.md) | ||||
| [](https://github.com/chzyer/readline/releases) | ||||
| [](https://godoc.org/github.com/chzyer/readline) | ||||
| [](#backers) | ||||
| [](#sponsors) | ||||
|  | ||||
| <p align="center"> | ||||
| <img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo.png" /> | ||||
| <a href="https://asciinema.org/a/32oseof9mkilg7t7d4780qt4m" target="_blank"><img src="https://asciinema.org/a/32oseof9mkilg7t7d4780qt4m.png" width="654"/></a> | ||||
| <img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo_f.png" /> | ||||
| </p> | ||||
|  | ||||
| A powerful readline library in `Linux` `macOS` `Windows` `Solaris` `AIX` | ||||
|  | ||||
| ## Guide | ||||
|  | ||||
| * [Demo](example/readline-demo/readline-demo.go) | ||||
| * [Shortcut](doc/shortcut.md) | ||||
|  | ||||
| ## Repos using readline | ||||
|  | ||||
| [](https://github.com/cockroachdb/cockroach) | ||||
| [](https://github.com/robertkrimen/otto) | ||||
| [](https://github.com/remind101/empire) | ||||
| [](https://github.com/mehrdadrad/mylg) | ||||
| [](https://github.com/knq/usql) | ||||
| [](https://github.com/youtube/doorman) | ||||
| [](https://github.com/bom-d-van/harp) | ||||
| [](https://github.com/abiosoft/ishell) | ||||
| [](https://github.com/Netflix/hal-9001) | ||||
| [](https://github.com/docker/go-p9p) | ||||
|  | ||||
|  | ||||
| ## Feedback | ||||
|  | ||||
| If you have any questions, please submit a github issue and any pull requests is welcomed :) | ||||
|  | ||||
| * [https://twitter.com/chzyer](https://twitter.com/chzyer) | ||||
| * [http://weibo.com/2145262190](http://weibo.com/2145262190) | ||||
|  | ||||
|  | ||||
| ## Backers | ||||
|  | ||||
| Love Readline? Help me keep it alive by donating funds to cover project expenses!<br /> | ||||
| [[Become a backer](https://opencollective.com/readline#backer)] | ||||
|  | ||||
| <a href="https://opencollective.com/readline/backer/0/website" target="_blank"><img src="https://opencollective.com/readline/backer/0/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/1/website" target="_blank"><img src="https://opencollective.com/readline/backer/1/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/2/website" target="_blank"><img src="https://opencollective.com/readline/backer/2/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/3/website" target="_blank"><img src="https://opencollective.com/readline/backer/3/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/4/website" target="_blank"><img src="https://opencollective.com/readline/backer/4/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/5/website" target="_blank"><img src="https://opencollective.com/readline/backer/5/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/6/website" target="_blank"><img src="https://opencollective.com/readline/backer/6/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/7/website" target="_blank"><img src="https://opencollective.com/readline/backer/7/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/8/website" target="_blank"><img src="https://opencollective.com/readline/backer/8/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/9/website" target="_blank"><img src="https://opencollective.com/readline/backer/9/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/10/website" target="_blank"><img src="https://opencollective.com/readline/backer/10/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/11/website" target="_blank"><img src="https://opencollective.com/readline/backer/11/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/12/website" target="_blank"><img src="https://opencollective.com/readline/backer/12/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/13/website" target="_blank"><img src="https://opencollective.com/readline/backer/13/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/14/website" target="_blank"><img src="https://opencollective.com/readline/backer/14/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/15/website" target="_blank"><img src="https://opencollective.com/readline/backer/15/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/16/website" target="_blank"><img src="https://opencollective.com/readline/backer/16/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/17/website" target="_blank"><img src="https://opencollective.com/readline/backer/17/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/18/website" target="_blank"><img src="https://opencollective.com/readline/backer/18/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/19/website" target="_blank"><img src="https://opencollective.com/readline/backer/19/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/20/website" target="_blank"><img src="https://opencollective.com/readline/backer/20/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/21/website" target="_blank"><img src="https://opencollective.com/readline/backer/21/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/22/website" target="_blank"><img src="https://opencollective.com/readline/backer/22/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/23/website" target="_blank"><img src="https://opencollective.com/readline/backer/23/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/24/website" target="_blank"><img src="https://opencollective.com/readline/backer/24/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/25/website" target="_blank"><img src="https://opencollective.com/readline/backer/25/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/26/website" target="_blank"><img src="https://opencollective.com/readline/backer/26/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/27/website" target="_blank"><img src="https://opencollective.com/readline/backer/27/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/28/website" target="_blank"><img src="https://opencollective.com/readline/backer/28/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/backer/29/website" target="_blank"><img src="https://opencollective.com/readline/backer/29/avatar.svg"></a> | ||||
|  | ||||
|  | ||||
| ## Sponsors | ||||
|  | ||||
| Become a sponsor and get your logo here on our Github page. [[Become a sponsor](https://opencollective.com/readline#sponsor)] | ||||
|  | ||||
| <a href="https://opencollective.com/readline/sponsor/0/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/0/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/1/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/1/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/2/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/2/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/3/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/3/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/4/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/4/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/5/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/5/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/6/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/6/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/7/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/7/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/8/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/8/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/9/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/9/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/10/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/10/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/11/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/11/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/12/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/12/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/13/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/13/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/14/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/14/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/15/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/15/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/16/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/16/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/17/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/17/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/18/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/18/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/19/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/19/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/20/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/20/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/21/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/21/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/22/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/22/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/23/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/23/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/24/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/24/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/25/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/25/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/26/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/26/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/27/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/27/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/28/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/28/avatar.svg"></a> | ||||
| <a href="https://opencollective.com/readline/sponsor/29/website" target="_blank"><img src="https://opencollective.com/readline/sponsor/29/avatar.svg"></a> | ||||
|  | ||||
							
								
								
									
										249
									
								
								vendor/github.com/chzyer/readline/ansi_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								vendor/github.com/chzyer/readline/ansi_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| // +build windows | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unicode/utf8" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	_                = uint16(0) | ||||
| 	COLOR_FBLUE      = 0x0001 | ||||
| 	COLOR_FGREEN     = 0x0002 | ||||
| 	COLOR_FRED       = 0x0004 | ||||
| 	COLOR_FINTENSITY = 0x0008 | ||||
|  | ||||
| 	COLOR_BBLUE      = 0x0010 | ||||
| 	COLOR_BGREEN     = 0x0020 | ||||
| 	COLOR_BRED       = 0x0040 | ||||
| 	COLOR_BINTENSITY = 0x0080 | ||||
|  | ||||
| 	COMMON_LVB_UNDERSCORE = 0x8000 | ||||
| 	COMMON_LVB_BOLD       = 0x0007 | ||||
| ) | ||||
|  | ||||
| var ColorTableFg = []word{ | ||||
| 	0,                                       // 30: Black | ||||
| 	COLOR_FRED,                              // 31: Red | ||||
| 	COLOR_FGREEN,                            // 32: Green | ||||
| 	COLOR_FRED | COLOR_FGREEN,               // 33: Yellow | ||||
| 	COLOR_FBLUE,                             // 34: Blue | ||||
| 	COLOR_FRED | COLOR_FBLUE,                // 35: Magenta | ||||
| 	COLOR_FGREEN | COLOR_FBLUE,              // 36: Cyan | ||||
| 	COLOR_FRED | COLOR_FBLUE | COLOR_FGREEN, // 37: White | ||||
| } | ||||
|  | ||||
| var ColorTableBg = []word{ | ||||
| 	0,                                       // 40: Black | ||||
| 	COLOR_BRED,                              // 41: Red | ||||
| 	COLOR_BGREEN,                            // 42: Green | ||||
| 	COLOR_BRED | COLOR_BGREEN,               // 43: Yellow | ||||
| 	COLOR_BBLUE,                             // 44: Blue | ||||
| 	COLOR_BRED | COLOR_BBLUE,                // 45: Magenta | ||||
| 	COLOR_BGREEN | COLOR_BBLUE,              // 46: Cyan | ||||
| 	COLOR_BRED | COLOR_BBLUE | COLOR_BGREEN, // 47: White | ||||
| } | ||||
|  | ||||
| type ANSIWriter struct { | ||||
| 	target io.Writer | ||||
| 	wg     sync.WaitGroup | ||||
| 	ctx    *ANSIWriterCtx | ||||
| 	sync.Mutex | ||||
| } | ||||
|  | ||||
| func NewANSIWriter(w io.Writer) *ANSIWriter { | ||||
| 	a := &ANSIWriter{ | ||||
| 		target: w, | ||||
| 		ctx:    NewANSIWriterCtx(w), | ||||
| 	} | ||||
| 	return a | ||||
| } | ||||
|  | ||||
| func (a *ANSIWriter) Close() error { | ||||
| 	a.wg.Wait() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type ANSIWriterCtx struct { | ||||
| 	isEsc     bool | ||||
| 	isEscSeq  bool | ||||
| 	arg       []string | ||||
| 	target    *bufio.Writer | ||||
| 	wantFlush bool | ||||
| } | ||||
|  | ||||
| func NewANSIWriterCtx(target io.Writer) *ANSIWriterCtx { | ||||
| 	return &ANSIWriterCtx{ | ||||
| 		target: bufio.NewWriter(target), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *ANSIWriterCtx) Flush() { | ||||
| 	a.target.Flush() | ||||
| } | ||||
|  | ||||
| func (a *ANSIWriterCtx) process(r rune) bool { | ||||
| 	if a.wantFlush { | ||||
| 		if r == 0 || r == CharEsc { | ||||
| 			a.wantFlush = false | ||||
| 			a.target.Flush() | ||||
| 		} | ||||
| 	} | ||||
| 	if a.isEscSeq { | ||||
| 		a.isEscSeq = a.ioloopEscSeq(a.target, r, &a.arg) | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	switch r { | ||||
| 	case CharEsc: | ||||
| 		a.isEsc = true | ||||
| 	case '[': | ||||
| 		if a.isEsc { | ||||
| 			a.arg = nil | ||||
| 			a.isEscSeq = true | ||||
| 			a.isEsc = false | ||||
| 			break | ||||
| 		} | ||||
| 		fallthrough | ||||
| 	default: | ||||
| 		a.target.WriteRune(r) | ||||
| 		a.wantFlush = true | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool { | ||||
| 	arg := *argptr | ||||
| 	var err error | ||||
|  | ||||
| 	if r >= 'A' && r <= 'D' { | ||||
| 		count := short(GetInt(arg, 1)) | ||||
| 		info, err := GetConsoleScreenBufferInfo() | ||||
| 		if err != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		switch r { | ||||
| 		case 'A': // up | ||||
| 			info.dwCursorPosition.y -= count | ||||
| 		case 'B': // down | ||||
| 			info.dwCursorPosition.y += count | ||||
| 		case 'C': // right | ||||
| 			info.dwCursorPosition.x += count | ||||
| 		case 'D': // left | ||||
| 			info.dwCursorPosition.x -= count | ||||
| 		} | ||||
| 		SetConsoleCursorPosition(&info.dwCursorPosition) | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	switch r { | ||||
| 	case 'J': | ||||
| 		killLines() | ||||
| 	case 'K': | ||||
| 		eraseLine() | ||||
| 	case 'm': | ||||
| 		color := word(0) | ||||
| 		for _, item := range arg { | ||||
| 			var c int | ||||
| 			c, err = strconv.Atoi(item) | ||||
| 			if err != nil { | ||||
| 				w.WriteString("[" + strings.Join(arg, ";") + "m") | ||||
| 				break | ||||
| 			} | ||||
| 			if c >= 30 && c < 40 { | ||||
| 				color ^= COLOR_FINTENSITY | ||||
| 				color |= ColorTableFg[c-30] | ||||
| 			} else if c >= 40 && c < 50 { | ||||
| 				color ^= COLOR_BINTENSITY | ||||
| 				color |= ColorTableBg[c-40] | ||||
| 			} else if c == 4 { | ||||
| 				color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7] | ||||
| 			} else if c == 1 { | ||||
| 				color |= COMMON_LVB_BOLD | COLOR_FINTENSITY | ||||
| 			} else { // unknown code treat as reset | ||||
| 				color = ColorTableFg[7] | ||||
| 			} | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		kernel.SetConsoleTextAttribute(stdout, uintptr(color)) | ||||
| 	case '\007': // set title | ||||
| 	case ';': | ||||
| 		if len(arg) == 0 || arg[len(arg)-1] != "" { | ||||
| 			arg = append(arg, "") | ||||
| 			*argptr = arg | ||||
| 		} | ||||
| 		return true | ||||
| 	default: | ||||
| 		if len(arg) == 0 { | ||||
| 			arg = append(arg, "") | ||||
| 		} | ||||
| 		arg[len(arg)-1] += string(r) | ||||
| 		*argptr = arg | ||||
| 		return true | ||||
| 	} | ||||
| 	*argptr = nil | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (a *ANSIWriter) Write(b []byte) (int, error) { | ||||
| 	a.Lock() | ||||
| 	defer a.Unlock() | ||||
|  | ||||
| 	off := 0 | ||||
| 	for len(b) > off { | ||||
| 		r, size := utf8.DecodeRune(b[off:]) | ||||
| 		if size == 0 { | ||||
| 			return off, io.ErrShortWrite | ||||
| 		} | ||||
| 		off += size | ||||
| 		a.ctx.process(r) | ||||
| 	} | ||||
| 	a.ctx.Flush() | ||||
| 	return off, nil | ||||
| } | ||||
|  | ||||
| func killLines() error { | ||||
| 	sbi, err := GetConsoleScreenBufferInfo() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x | ||||
| 	size += sbi.dwCursorPosition.x | ||||
|  | ||||
| 	var written int | ||||
| 	kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]), | ||||
| 		uintptr(size), | ||||
| 		sbi.dwCursorPosition.ptr(), | ||||
| 		uintptr(unsafe.Pointer(&written)), | ||||
| 	) | ||||
| 	return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '), | ||||
| 		uintptr(size), | ||||
| 		sbi.dwCursorPosition.ptr(), | ||||
| 		uintptr(unsafe.Pointer(&written)), | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func eraseLine() error { | ||||
| 	sbi, err := GetConsoleScreenBufferInfo() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	size := sbi.dwSize.x | ||||
| 	sbi.dwCursorPosition.x = 0 | ||||
| 	var written int | ||||
| 	return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '), | ||||
| 		uintptr(size), | ||||
| 		sbi.dwCursorPosition.ptr(), | ||||
| 		uintptr(unsafe.Pointer(&written)), | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										285
									
								
								vendor/github.com/chzyer/readline/complete.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								vendor/github.com/chzyer/readline/complete.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type AutoCompleter interface { | ||||
| 	// Readline will pass the whole line and current offset to it | ||||
| 	// Completer need to pass all the candidates, and how long they shared the same characters in line | ||||
| 	// Example: | ||||
| 	//   [go, git, git-shell, grep] | ||||
| 	//   Do("g", 1) => ["o", "it", "it-shell", "rep"], 1 | ||||
| 	//   Do("gi", 2) => ["t", "t-shell"], 2 | ||||
| 	//   Do("git", 3) => ["", "-shell"], 3 | ||||
| 	Do(line []rune, pos int) (newLine [][]rune, length int) | ||||
| } | ||||
|  | ||||
| type TabCompleter struct{} | ||||
|  | ||||
| func (t *TabCompleter) Do([]rune, int) ([][]rune, int) { | ||||
| 	return [][]rune{[]rune("\t")}, 0 | ||||
| } | ||||
|  | ||||
| type opCompleter struct { | ||||
| 	w     io.Writer | ||||
| 	op    *Operation | ||||
| 	width int | ||||
|  | ||||
| 	inCompleteMode  bool | ||||
| 	inSelectMode    bool | ||||
| 	candidate       [][]rune | ||||
| 	candidateSource []rune | ||||
| 	candidateOff    int | ||||
| 	candidateChoise int | ||||
| 	candidateColNum int | ||||
| } | ||||
|  | ||||
| func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter { | ||||
| 	return &opCompleter{ | ||||
| 		w:     w, | ||||
| 		op:    op, | ||||
| 		width: width, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) doSelect() { | ||||
| 	if len(o.candidate) == 1 { | ||||
| 		o.op.buf.WriteRunes(o.candidate[0]) | ||||
| 		o.ExitCompleteMode(false) | ||||
| 		return | ||||
| 	} | ||||
| 	o.nextCandidate(1) | ||||
| 	o.CompleteRefresh() | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) nextCandidate(i int) { | ||||
| 	o.candidateChoise += i | ||||
| 	o.candidateChoise = o.candidateChoise % len(o.candidate) | ||||
| 	if o.candidateChoise < 0 { | ||||
| 		o.candidateChoise = len(o.candidate) + o.candidateChoise | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) OnComplete() bool { | ||||
| 	if o.width == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	if o.IsInCompleteSelectMode() { | ||||
| 		o.doSelect() | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	buf := o.op.buf | ||||
| 	rs := buf.Runes() | ||||
|  | ||||
| 	if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) { | ||||
| 		o.EnterCompleteSelectMode() | ||||
| 		o.doSelect() | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	o.ExitCompleteSelectMode() | ||||
| 	o.candidateSource = rs | ||||
| 	newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx) | ||||
| 	if len(newLines) == 0 { | ||||
| 		o.ExitCompleteMode(false) | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	// only Aggregate candidates in non-complete mode | ||||
| 	if !o.IsInCompleteMode() { | ||||
| 		if len(newLines) == 1 { | ||||
| 			buf.WriteRunes(newLines[0]) | ||||
| 			o.ExitCompleteMode(false) | ||||
| 			return true | ||||
| 		} | ||||
|  | ||||
| 		same, size := runes.Aggregate(newLines) | ||||
| 		if size > 0 { | ||||
| 			buf.WriteRunes(same) | ||||
| 			o.ExitCompleteMode(false) | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	o.EnterCompleteMode(offset, newLines) | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) IsInCompleteSelectMode() bool { | ||||
| 	return o.inSelectMode | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) IsInCompleteMode() bool { | ||||
| 	return o.inCompleteMode | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) HandleCompleteSelect(r rune) bool { | ||||
| 	next := true | ||||
| 	switch r { | ||||
| 	case CharEnter, CharCtrlJ: | ||||
| 		next = false | ||||
| 		o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise]) | ||||
| 		o.ExitCompleteMode(false) | ||||
| 	case CharLineStart: | ||||
| 		num := o.candidateChoise % o.candidateColNum | ||||
| 		o.nextCandidate(-num) | ||||
| 	case CharLineEnd: | ||||
| 		num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1 | ||||
| 		o.candidateChoise += num | ||||
| 		if o.candidateChoise >= len(o.candidate) { | ||||
| 			o.candidateChoise = len(o.candidate) - 1 | ||||
| 		} | ||||
| 	case CharBackspace: | ||||
| 		o.ExitCompleteSelectMode() | ||||
| 		next = false | ||||
| 	case CharTab, CharForward: | ||||
| 		o.doSelect() | ||||
| 	case CharBell, CharInterrupt: | ||||
| 		o.ExitCompleteMode(true) | ||||
| 		next = false | ||||
| 	case CharNext: | ||||
| 		tmpChoise := o.candidateChoise + o.candidateColNum | ||||
| 		if tmpChoise >= o.getMatrixSize() { | ||||
| 			tmpChoise -= o.getMatrixSize() | ||||
| 		} else if tmpChoise >= len(o.candidate) { | ||||
| 			tmpChoise += o.candidateColNum | ||||
| 			tmpChoise -= o.getMatrixSize() | ||||
| 		} | ||||
| 		o.candidateChoise = tmpChoise | ||||
| 	case CharBackward: | ||||
| 		o.nextCandidate(-1) | ||||
| 	case CharPrev: | ||||
| 		tmpChoise := o.candidateChoise - o.candidateColNum | ||||
| 		if tmpChoise < 0 { | ||||
| 			tmpChoise += o.getMatrixSize() | ||||
| 			if tmpChoise >= len(o.candidate) { | ||||
| 				tmpChoise -= o.candidateColNum | ||||
| 			} | ||||
| 		} | ||||
| 		o.candidateChoise = tmpChoise | ||||
| 	default: | ||||
| 		next = false | ||||
| 		o.ExitCompleteSelectMode() | ||||
| 	} | ||||
| 	if next { | ||||
| 		o.CompleteRefresh() | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) getMatrixSize() int { | ||||
| 	line := len(o.candidate) / o.candidateColNum | ||||
| 	if len(o.candidate)%o.candidateColNum != 0 { | ||||
| 		line++ | ||||
| 	} | ||||
| 	return line * o.candidateColNum | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) OnWidthChange(newWidth int) { | ||||
| 	o.width = newWidth | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) CompleteRefresh() { | ||||
| 	if !o.inCompleteMode { | ||||
| 		return | ||||
| 	} | ||||
| 	lineCnt := o.op.buf.CursorLineCount() | ||||
| 	colWidth := 0 | ||||
| 	for _, c := range o.candidate { | ||||
| 		w := runes.WidthAll(c) | ||||
| 		if w > colWidth { | ||||
| 			colWidth = w | ||||
| 		} | ||||
| 	} | ||||
| 	colWidth += o.candidateOff + 1 | ||||
| 	same := o.op.buf.RuneSlice(-o.candidateOff) | ||||
|  | ||||
| 	// -1 to avoid reach the end of line | ||||
| 	width := o.width - 1 | ||||
| 	colNum := width / colWidth | ||||
| 	if colNum != 0 { | ||||
| 		colWidth += (width - (colWidth * colNum)) / colNum | ||||
| 	} | ||||
|  | ||||
| 	o.candidateColNum = colNum | ||||
| 	buf := bufio.NewWriter(o.w) | ||||
| 	buf.Write(bytes.Repeat([]byte("\n"), lineCnt)) | ||||
|  | ||||
| 	colIdx := 0 | ||||
| 	lines := 1 | ||||
| 	buf.WriteString("\033[J") | ||||
| 	for idx, c := range o.candidate { | ||||
| 		inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode() | ||||
| 		if inSelect { | ||||
| 			buf.WriteString("\033[30;47m") | ||||
| 		} | ||||
| 		buf.WriteString(string(same)) | ||||
| 		buf.WriteString(string(c)) | ||||
| 		buf.Write(bytes.Repeat([]byte(" "), colWidth-runes.WidthAll(c)-runes.WidthAll(same))) | ||||
|  | ||||
| 		if inSelect { | ||||
| 			buf.WriteString("\033[0m") | ||||
| 		} | ||||
|  | ||||
| 		colIdx++ | ||||
| 		if colIdx == colNum { | ||||
| 			buf.WriteString("\n") | ||||
| 			lines++ | ||||
| 			colIdx = 0 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// move back | ||||
| 	fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines) | ||||
| 	fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen()) | ||||
| 	buf.Flush() | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) aggCandidate(candidate [][]rune) int { | ||||
| 	offset := 0 | ||||
| 	for i := 0; i < len(candidate[0]); i++ { | ||||
| 		for j := 0; j < len(candidate)-1; j++ { | ||||
| 			if i > len(candidate[j]) { | ||||
| 				goto aggregate | ||||
| 			} | ||||
| 			if candidate[j][i] != candidate[j+1][i] { | ||||
| 				goto aggregate | ||||
| 			} | ||||
| 		} | ||||
| 		offset = i | ||||
| 	} | ||||
| aggregate: | ||||
| 	return offset | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) EnterCompleteSelectMode() { | ||||
| 	o.inSelectMode = true | ||||
| 	o.candidateChoise = -1 | ||||
| 	o.CompleteRefresh() | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) { | ||||
| 	o.inCompleteMode = true | ||||
| 	o.candidate = candidate | ||||
| 	o.candidateOff = offset | ||||
| 	o.CompleteRefresh() | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) ExitCompleteSelectMode() { | ||||
| 	o.inSelectMode = false | ||||
| 	o.candidate = nil | ||||
| 	o.candidateChoise = -1 | ||||
| 	o.candidateOff = -1 | ||||
| 	o.candidateSource = nil | ||||
| } | ||||
|  | ||||
| func (o *opCompleter) ExitCompleteMode(revent bool) { | ||||
| 	o.inCompleteMode = false | ||||
| 	o.ExitCompleteSelectMode() | ||||
| } | ||||
							
								
								
									
										165
									
								
								vendor/github.com/chzyer/readline/complete_helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								vendor/github.com/chzyer/readline/complete_helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Caller type for dynamic completion | ||||
| type DynamicCompleteFunc func(string) []string | ||||
|  | ||||
| type PrefixCompleterInterface interface { | ||||
| 	Print(prefix string, level int, buf *bytes.Buffer) | ||||
| 	Do(line []rune, pos int) (newLine [][]rune, length int) | ||||
| 	GetName() []rune | ||||
| 	GetChildren() []PrefixCompleterInterface | ||||
| 	SetChildren(children []PrefixCompleterInterface) | ||||
| } | ||||
|  | ||||
| type DynamicPrefixCompleterInterface interface { | ||||
| 	PrefixCompleterInterface | ||||
| 	IsDynamic() bool | ||||
| 	GetDynamicNames(line []rune) [][]rune | ||||
| } | ||||
|  | ||||
| type PrefixCompleter struct { | ||||
| 	Name     []rune | ||||
| 	Dynamic  bool | ||||
| 	Callback DynamicCompleteFunc | ||||
| 	Children []PrefixCompleterInterface | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) Tree(prefix string) string { | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	p.Print(prefix, 0, buf) | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) { | ||||
| 	if strings.TrimSpace(string(p.GetName())) != "" { | ||||
| 		buf.WriteString(prefix) | ||||
| 		if level > 0 { | ||||
| 			buf.WriteString("├") | ||||
| 			buf.WriteString(strings.Repeat("─", (level*4)-2)) | ||||
| 			buf.WriteString(" ") | ||||
| 		} | ||||
| 		buf.WriteString(string(p.GetName()) + "\n") | ||||
| 		level++ | ||||
| 	} | ||||
| 	for _, ch := range p.GetChildren() { | ||||
| 		ch.Print(prefix, level, buf) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) { | ||||
| 	Print(p, prefix, level, buf) | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) IsDynamic() bool { | ||||
| 	return p.Dynamic | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) GetName() []rune { | ||||
| 	return p.Name | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune { | ||||
| 	var names = [][]rune{} | ||||
| 	for _, name := range p.Callback(string(line)) { | ||||
| 		names = append(names, []rune(name+" ")) | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface { | ||||
| 	return p.Children | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) { | ||||
| 	p.Children = children | ||||
| } | ||||
|  | ||||
| func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter { | ||||
| 	return PcItem("", pc...) | ||||
| } | ||||
|  | ||||
| func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter { | ||||
| 	name += " " | ||||
| 	return &PrefixCompleter{ | ||||
| 		Name:     []rune(name), | ||||
| 		Dynamic:  false, | ||||
| 		Children: pc, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter { | ||||
| 	return &PrefixCompleter{ | ||||
| 		Callback: callback, | ||||
| 		Dynamic:  true, | ||||
| 		Children: pc, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) { | ||||
| 	return doInternal(p, line, pos, line) | ||||
| } | ||||
|  | ||||
| func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) { | ||||
| 	return doInternal(p, line, pos, line) | ||||
| } | ||||
|  | ||||
| func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) { | ||||
| 	line = runes.TrimSpaceLeft(line[:pos]) | ||||
| 	goNext := false | ||||
| 	var lineCompleter PrefixCompleterInterface | ||||
| 	for _, child := range p.GetChildren() { | ||||
| 		childNames := make([][]rune, 1) | ||||
|  | ||||
| 		childDynamic, ok := child.(DynamicPrefixCompleterInterface) | ||||
| 		if ok && childDynamic.IsDynamic() { | ||||
| 			childNames = childDynamic.GetDynamicNames(origLine) | ||||
| 		} else { | ||||
| 			childNames[0] = child.GetName() | ||||
| 		} | ||||
|  | ||||
| 		for _, childName := range childNames { | ||||
| 			if len(line) >= len(childName) { | ||||
| 				if runes.HasPrefix(line, childName) { | ||||
| 					if len(line) == len(childName) { | ||||
| 						newLine = append(newLine, []rune{' '}) | ||||
| 					} else { | ||||
| 						newLine = append(newLine, childName) | ||||
| 					} | ||||
| 					offset = len(childName) | ||||
| 					lineCompleter = child | ||||
| 					goNext = true | ||||
| 				} | ||||
| 			} else { | ||||
| 				if runes.HasPrefix(childName, line) { | ||||
| 					newLine = append(newLine, childName[len(line):]) | ||||
| 					offset = len(line) | ||||
| 					lineCompleter = child | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(newLine) != 1 { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	tmpLine := make([]rune, 0, len(line)) | ||||
| 	for i := offset; i < len(line); i++ { | ||||
| 		if line[i] == ' ' { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		tmpLine = append(tmpLine, line[i:]...) | ||||
| 		return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine) | ||||
| 	} | ||||
|  | ||||
| 	if goNext { | ||||
| 		return doInternal(lineCompleter, nil, 0, origLine) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										82
									
								
								vendor/github.com/chzyer/readline/complete_segment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								vendor/github.com/chzyer/readline/complete_segment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| package readline | ||||
|  | ||||
| type SegmentCompleter interface { | ||||
| 	// a | ||||
| 	// |- a1 | ||||
| 	// |--- a11 | ||||
| 	// |- a2 | ||||
| 	// b | ||||
| 	// input: | ||||
| 	//   DoTree([], 0) [a, b] | ||||
| 	//   DoTree([a], 1) [a] | ||||
| 	//   DoTree([a, ], 0) [a1, a2] | ||||
| 	//   DoTree([a, a], 1) [a1, a2] | ||||
| 	//   DoTree([a, a1], 2) [a1] | ||||
| 	//   DoTree([a, a1, ], 0) [a11] | ||||
| 	//   DoTree([a, a1, a], 1) [a11] | ||||
| 	DoSegment([][]rune, int) [][]rune | ||||
| } | ||||
|  | ||||
| type dumpSegmentCompleter struct { | ||||
| 	f func([][]rune, int) [][]rune | ||||
| } | ||||
|  | ||||
| func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]rune { | ||||
| 	return d.f(segment, n) | ||||
| } | ||||
|  | ||||
| func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter { | ||||
| 	return &SegmentComplete{&dumpSegmentCompleter{f}} | ||||
| } | ||||
|  | ||||
| func SegmentAutoComplete(completer SegmentCompleter) *SegmentComplete { | ||||
| 	return &SegmentComplete{ | ||||
| 		SegmentCompleter: completer, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type SegmentComplete struct { | ||||
| 	SegmentCompleter | ||||
| } | ||||
|  | ||||
| func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, int) { | ||||
| 	ret := make([][]rune, 0, len(cands)) | ||||
| 	lastSegment := segments[len(segments)-1] | ||||
| 	for _, cand := range cands { | ||||
| 		if !runes.HasPrefix(cand, lastSegment) { | ||||
| 			continue | ||||
| 		} | ||||
| 		ret = append(ret, cand[len(lastSegment):]) | ||||
| 	} | ||||
| 	return ret, idx | ||||
| } | ||||
|  | ||||
| func SplitSegment(line []rune, pos int) ([][]rune, int) { | ||||
| 	segs := [][]rune{} | ||||
| 	lastIdx := -1 | ||||
| 	line = line[:pos] | ||||
| 	pos = 0 | ||||
| 	for idx, l := range line { | ||||
| 		if l == ' ' { | ||||
| 			pos = 0 | ||||
| 			segs = append(segs, line[lastIdx+1:idx]) | ||||
| 			lastIdx = idx | ||||
| 		} else { | ||||
| 			pos++ | ||||
| 		} | ||||
| 	} | ||||
| 	segs = append(segs, line[lastIdx+1:]) | ||||
| 	return segs, pos | ||||
| } | ||||
|  | ||||
| func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, offset int) { | ||||
|  | ||||
| 	segment, idx := SplitSegment(line, pos) | ||||
|  | ||||
| 	cands := c.DoSegment(segment, idx) | ||||
| 	newLine, offset = RetSegment(segment, cands, idx) | ||||
| 	for idx := range newLine { | ||||
| 		newLine[idx] = append(newLine[idx], ' ') | ||||
| 	} | ||||
| 	return newLine, offset | ||||
| } | ||||
							
								
								
									
										330
									
								
								vendor/github.com/chzyer/readline/history.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								vendor/github.com/chzyer/readline/history.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,330 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"container/list" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| type hisItem struct { | ||||
| 	Source  []rune | ||||
| 	Version int64 | ||||
| 	Tmp     []rune | ||||
| } | ||||
|  | ||||
| func (h *hisItem) Clean() { | ||||
| 	h.Source = nil | ||||
| 	h.Tmp = nil | ||||
| } | ||||
|  | ||||
| type opHistory struct { | ||||
| 	cfg        *Config | ||||
| 	history    *list.List | ||||
| 	historyVer int64 | ||||
| 	current    *list.Element | ||||
| 	fd         *os.File | ||||
| 	fdLock     sync.Mutex | ||||
| 	enable     bool | ||||
| } | ||||
|  | ||||
| func newOpHistory(cfg *Config) (o *opHistory) { | ||||
| 	o = &opHistory{ | ||||
| 		cfg:     cfg, | ||||
| 		history: list.New(), | ||||
| 		enable:  true, | ||||
| 	} | ||||
| 	return o | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Reset() { | ||||
| 	o.history = list.New() | ||||
| 	o.current = nil | ||||
| } | ||||
|  | ||||
| func (o *opHistory) IsHistoryClosed() bool { | ||||
| 	o.fdLock.Lock() | ||||
| 	defer o.fdLock.Unlock() | ||||
| 	return o.fd.Fd() == ^(uintptr(0)) | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Init() { | ||||
| 	if o.IsHistoryClosed() { | ||||
| 		o.initHistory() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opHistory) initHistory() { | ||||
| 	if o.cfg.HistoryFile != "" { | ||||
| 		o.historyUpdatePath(o.cfg.HistoryFile) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // only called by newOpHistory | ||||
| func (o *opHistory) historyUpdatePath(path string) { | ||||
| 	o.fdLock.Lock() | ||||
| 	defer o.fdLock.Unlock() | ||||
| 	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	o.fd = f | ||||
| 	r := bufio.NewReader(o.fd) | ||||
| 	total := 0 | ||||
| 	for ; ; total++ { | ||||
| 		line, err := r.ReadString('\n') | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		// ignore the empty line | ||||
| 		line = strings.TrimSpace(line) | ||||
| 		if len(line) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		o.Push([]rune(line)) | ||||
| 		o.Compact() | ||||
| 	} | ||||
| 	if total > o.cfg.HistoryLimit { | ||||
| 		o.rewriteLocked() | ||||
| 	} | ||||
| 	o.historyVer++ | ||||
| 	o.Push(nil) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Compact() { | ||||
| 	for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 { | ||||
| 		o.history.Remove(o.history.Front()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Rewrite() { | ||||
| 	o.fdLock.Lock() | ||||
| 	defer o.fdLock.Unlock() | ||||
| 	o.rewriteLocked() | ||||
| } | ||||
|  | ||||
| func (o *opHistory) rewriteLocked() { | ||||
| 	if o.cfg.HistoryFile == "" { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	tmpFile := o.cfg.HistoryFile + ".tmp" | ||||
| 	fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	buf := bufio.NewWriter(fd) | ||||
| 	for elem := o.history.Front(); elem != nil; elem = elem.Next() { | ||||
| 		buf.WriteString(string(elem.Value.(*hisItem).Source) + "\n") | ||||
| 	} | ||||
| 	buf.Flush() | ||||
|  | ||||
| 	// replace history file | ||||
| 	if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil { | ||||
| 		fd.Close() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if o.fd != nil { | ||||
| 		o.fd.Close() | ||||
| 	} | ||||
| 	// fd is write only, just satisfy what we need. | ||||
| 	o.fd = fd | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Close() { | ||||
| 	o.fdLock.Lock() | ||||
| 	defer o.fdLock.Unlock() | ||||
| 	if o.fd != nil { | ||||
| 		o.fd.Close() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) { | ||||
| 	for elem := o.current; elem != nil; elem = elem.Prev() { | ||||
| 		item := o.showItem(elem.Value) | ||||
| 		if isNewSearch { | ||||
| 			start += len(rs) | ||||
| 		} | ||||
| 		if elem == o.current { | ||||
| 			if len(item) >= start { | ||||
| 				item = item[:start] | ||||
| 			} | ||||
| 		} | ||||
| 		idx := runes.IndexAllBckEx(item, rs, o.cfg.HistorySearchFold) | ||||
| 		if idx < 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		return idx, elem | ||||
| 	} | ||||
| 	return -1, nil | ||||
| } | ||||
|  | ||||
| func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) { | ||||
| 	for elem := o.current; elem != nil; elem = elem.Next() { | ||||
| 		item := o.showItem(elem.Value) | ||||
| 		if isNewSearch { | ||||
| 			start -= len(rs) | ||||
| 			if start < 0 { | ||||
| 				start = 0 | ||||
| 			} | ||||
| 		} | ||||
| 		if elem == o.current { | ||||
| 			if len(item)-1 >= start { | ||||
| 				item = item[start:] | ||||
| 			} else { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		idx := runes.IndexAllEx(item, rs, o.cfg.HistorySearchFold) | ||||
| 		if idx < 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		if elem == o.current { | ||||
| 			idx += start | ||||
| 		} | ||||
| 		return idx, elem | ||||
| 	} | ||||
| 	return -1, nil | ||||
| } | ||||
|  | ||||
| func (o *opHistory) showItem(obj interface{}) []rune { | ||||
| 	item := obj.(*hisItem) | ||||
| 	if item.Version == o.historyVer { | ||||
| 		return item.Tmp | ||||
| 	} | ||||
| 	return item.Source | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Prev() []rune { | ||||
| 	if o.current == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	current := o.current.Prev() | ||||
| 	if current == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	o.current = current | ||||
| 	return runes.Copy(o.showItem(current.Value)) | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Next() ([]rune, bool) { | ||||
| 	if o.current == nil { | ||||
| 		return nil, false | ||||
| 	} | ||||
| 	current := o.current.Next() | ||||
| 	if current == nil { | ||||
| 		return nil, false | ||||
| 	} | ||||
|  | ||||
| 	o.current = current | ||||
| 	return runes.Copy(o.showItem(current.Value)), true | ||||
| } | ||||
|  | ||||
| // Disable the current history | ||||
| func (o *opHistory) Disable() { | ||||
| 	o.enable = false | ||||
| } | ||||
|  | ||||
| // Enable the current history | ||||
| func (o *opHistory) Enable() { | ||||
| 	o.enable = true | ||||
| } | ||||
|  | ||||
| func (o *opHistory) debug() { | ||||
| 	Debug("-------") | ||||
| 	for item := o.history.Front(); item != nil; item = item.Next() { | ||||
| 		Debug(fmt.Sprintf("%+v", item.Value)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // save history | ||||
| func (o *opHistory) New(current []rune) (err error) { | ||||
|  | ||||
| 	// history deactivated | ||||
| 	if !o.enable { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	current = runes.Copy(current) | ||||
|  | ||||
| 	// if just use last command without modify | ||||
| 	// just clean lastest history | ||||
| 	if back := o.history.Back(); back != nil { | ||||
| 		prev := back.Prev() | ||||
| 		if prev != nil { | ||||
| 			if runes.Equal(current, prev.Value.(*hisItem).Source) { | ||||
| 				o.current = o.history.Back() | ||||
| 				o.current.Value.(*hisItem).Clean() | ||||
| 				o.historyVer++ | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(current) == 0 { | ||||
| 		o.current = o.history.Back() | ||||
| 		if o.current != nil { | ||||
| 			o.current.Value.(*hisItem).Clean() | ||||
| 			o.historyVer++ | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if o.current != o.history.Back() { | ||||
| 		// move history item to current command | ||||
| 		currentItem := o.current.Value.(*hisItem) | ||||
| 		// set current to last item | ||||
| 		o.current = o.history.Back() | ||||
|  | ||||
| 		current = runes.Copy(currentItem.Tmp) | ||||
| 	} | ||||
|  | ||||
| 	// err only can be a IO error, just report | ||||
| 	err = o.Update(current, true) | ||||
|  | ||||
| 	// push a new one to commit current command | ||||
| 	o.historyVer++ | ||||
| 	o.Push(nil) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Revert() { | ||||
| 	o.historyVer++ | ||||
| 	o.current = o.history.Back() | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Update(s []rune, commit bool) (err error) { | ||||
| 	o.fdLock.Lock() | ||||
| 	defer o.fdLock.Unlock() | ||||
| 	s = runes.Copy(s) | ||||
| 	if o.current == nil { | ||||
| 		o.Push(s) | ||||
| 		o.Compact() | ||||
| 		return | ||||
| 	} | ||||
| 	r := o.current.Value.(*hisItem) | ||||
| 	r.Version = o.historyVer | ||||
| 	if commit { | ||||
| 		r.Source = s | ||||
| 		if o.fd != nil { | ||||
| 			// just report the error | ||||
| 			_, err = o.fd.Write([]byte(string(r.Source) + "\n")) | ||||
| 		} | ||||
| 	} else { | ||||
| 		r.Tmp = append(r.Tmp[:0], s...) | ||||
| 	} | ||||
| 	o.current.Value = r | ||||
| 	o.Compact() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (o *opHistory) Push(s []rune) { | ||||
| 	s = runes.Copy(s) | ||||
| 	elem := o.history.PushBack(&hisItem{Source: s}) | ||||
| 	o.current = elem | ||||
| } | ||||
							
								
								
									
										537
									
								
								vendor/github.com/chzyer/readline/operation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										537
									
								
								vendor/github.com/chzyer/readline/operation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,537 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrInterrupt = errors.New("Interrupt") | ||||
| ) | ||||
|  | ||||
| type InterruptError struct { | ||||
| 	Line []rune | ||||
| } | ||||
|  | ||||
| func (*InterruptError) Error() string { | ||||
| 	return "Interrupted" | ||||
| } | ||||
|  | ||||
| type Operation struct { | ||||
| 	m       sync.Mutex | ||||
| 	cfg     *Config | ||||
| 	t       *Terminal | ||||
| 	buf     *RuneBuffer | ||||
| 	outchan chan []rune | ||||
| 	errchan chan error | ||||
| 	w       io.Writer | ||||
|  | ||||
| 	history *opHistory | ||||
| 	*opSearch | ||||
| 	*opCompleter | ||||
| 	*opPassword | ||||
| 	*opVim | ||||
| } | ||||
|  | ||||
| func (o *Operation) SetBuffer(what string) { | ||||
| 	o.buf.Set([]rune(what)) | ||||
| } | ||||
|  | ||||
| type wrapWriter struct { | ||||
| 	r      *Operation | ||||
| 	t      *Terminal | ||||
| 	target io.Writer | ||||
| } | ||||
|  | ||||
| func (w *wrapWriter) Write(b []byte) (int, error) { | ||||
| 	if !w.t.IsReading() { | ||||
| 		return w.target.Write(b) | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		n   int | ||||
| 		err error | ||||
| 	) | ||||
| 	w.r.buf.Refresh(func() { | ||||
| 		n, err = w.target.Write(b) | ||||
| 	}) | ||||
|  | ||||
| 	if w.r.IsSearchMode() { | ||||
| 		w.r.SearchRefresh(-1) | ||||
| 	} | ||||
| 	if w.r.IsInCompleteMode() { | ||||
| 		w.r.CompleteRefresh() | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func NewOperation(t *Terminal, cfg *Config) *Operation { | ||||
| 	width := cfg.FuncGetWidth() | ||||
| 	op := &Operation{ | ||||
| 		t:       t, | ||||
| 		buf:     NewRuneBuffer(t, cfg.Prompt, cfg, width), | ||||
| 		outchan: make(chan []rune), | ||||
| 		errchan: make(chan error, 1), | ||||
| 	} | ||||
| 	op.w = op.buf.w | ||||
| 	op.SetConfig(cfg) | ||||
| 	op.opVim = newVimMode(op) | ||||
| 	op.opCompleter = newOpCompleter(op.buf.w, op, width) | ||||
| 	op.opPassword = newOpPassword(op) | ||||
| 	op.cfg.FuncOnWidthChanged(func() { | ||||
| 		newWidth := cfg.FuncGetWidth() | ||||
| 		op.opCompleter.OnWidthChange(newWidth) | ||||
| 		op.opSearch.OnWidthChange(newWidth) | ||||
| 		op.buf.OnWidthChange(newWidth) | ||||
| 	}) | ||||
| 	go op.ioloop() | ||||
| 	return op | ||||
| } | ||||
|  | ||||
| func (o *Operation) SetPrompt(s string) { | ||||
| 	o.buf.SetPrompt(s) | ||||
| } | ||||
|  | ||||
| func (o *Operation) SetMaskRune(r rune) { | ||||
| 	o.buf.SetMask(r) | ||||
| } | ||||
|  | ||||
| func (o *Operation) GetConfig() *Config { | ||||
| 	o.m.Lock() | ||||
| 	cfg := *o.cfg | ||||
| 	o.m.Unlock() | ||||
| 	return &cfg | ||||
| } | ||||
|  | ||||
| func (o *Operation) ioloop() { | ||||
| 	for { | ||||
| 		keepInSearchMode := false | ||||
| 		keepInCompleteMode := false | ||||
| 		r := o.t.ReadRune() | ||||
|  | ||||
| 		if o.GetConfig().FuncFilterInputRune != nil { | ||||
| 			var process bool | ||||
| 			r, process = o.GetConfig().FuncFilterInputRune(r) | ||||
| 			if !process { | ||||
| 				o.t.KickRead() | ||||
| 				o.buf.Refresh(nil) // to refresh the line | ||||
| 				continue           // ignore this rune | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if r == 0 { // io.EOF | ||||
| 			if o.buf.Len() == 0 { | ||||
| 				o.buf.Clean() | ||||
| 				select { | ||||
| 				case o.errchan <- io.EOF: | ||||
| 				} | ||||
| 				break | ||||
| 			} else { | ||||
| 				// if stdin got io.EOF and there is something left in buffer, | ||||
| 				// let's flush them by sending CharEnter. | ||||
| 				// And we will got io.EOF int next loop. | ||||
| 				r = CharEnter | ||||
| 			} | ||||
| 		} | ||||
| 		isUpdateHistory := true | ||||
|  | ||||
| 		if o.IsInCompleteSelectMode() { | ||||
| 			keepInCompleteMode = o.HandleCompleteSelect(r) | ||||
| 			if keepInCompleteMode { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			o.buf.Refresh(nil) | ||||
| 			switch r { | ||||
| 			case CharEnter, CharCtrlJ: | ||||
| 				o.history.Update(o.buf.Runes(), false) | ||||
| 				fallthrough | ||||
| 			case CharInterrupt: | ||||
| 				o.t.KickRead() | ||||
| 				fallthrough | ||||
| 			case CharBell: | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if o.IsEnableVimMode() { | ||||
| 			r = o.HandleVim(r, o.t.ReadRune) | ||||
| 			if r == 0 { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		switch r { | ||||
| 		case CharBell: | ||||
| 			if o.IsSearchMode() { | ||||
| 				o.ExitSearchMode(true) | ||||
| 				o.buf.Refresh(nil) | ||||
| 			} | ||||
| 			if o.IsInCompleteMode() { | ||||
| 				o.ExitCompleteMode(true) | ||||
| 				o.buf.Refresh(nil) | ||||
| 			} | ||||
| 		case CharTab: | ||||
| 			if o.GetConfig().AutoComplete == nil { | ||||
| 				o.t.Bell() | ||||
| 				break | ||||
| 			} | ||||
| 			if o.OnComplete() { | ||||
| 				keepInCompleteMode = true | ||||
| 			} else { | ||||
| 				o.t.Bell() | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 		case CharBckSearch: | ||||
| 			if !o.SearchMode(S_DIR_BCK) { | ||||
| 				o.t.Bell() | ||||
| 				break | ||||
| 			} | ||||
| 			keepInSearchMode = true | ||||
| 		case CharCtrlU: | ||||
| 			o.buf.KillFront() | ||||
| 		case CharFwdSearch: | ||||
| 			if !o.SearchMode(S_DIR_FWD) { | ||||
| 				o.t.Bell() | ||||
| 				break | ||||
| 			} | ||||
| 			keepInSearchMode = true | ||||
| 		case CharKill: | ||||
| 			o.buf.Kill() | ||||
| 			keepInCompleteMode = true | ||||
| 		case MetaForward: | ||||
| 			o.buf.MoveToNextWord() | ||||
| 		case CharTranspose: | ||||
| 			o.buf.Transpose() | ||||
| 		case MetaBackward: | ||||
| 			o.buf.MoveToPrevWord() | ||||
| 		case MetaDelete: | ||||
| 			o.buf.DeleteWord() | ||||
| 		case CharLineStart: | ||||
| 			o.buf.MoveToLineStart() | ||||
| 		case CharLineEnd: | ||||
| 			o.buf.MoveToLineEnd() | ||||
| 		case CharBackspace, CharCtrlH: | ||||
| 			if o.IsSearchMode() { | ||||
| 				o.SearchBackspace() | ||||
| 				keepInSearchMode = true | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if o.buf.Len() == 0 { | ||||
| 				o.t.Bell() | ||||
| 				break | ||||
| 			} | ||||
| 			o.buf.Backspace() | ||||
| 			if o.IsInCompleteMode() { | ||||
| 				o.OnComplete() | ||||
| 			} | ||||
| 		case CharCtrlZ: | ||||
| 			o.buf.Clean() | ||||
| 			o.t.SleepToResume() | ||||
| 			o.Refresh() | ||||
| 		case CharCtrlL: | ||||
| 			ClearScreen(o.w) | ||||
| 			o.Refresh() | ||||
| 		case MetaBackspace, CharCtrlW: | ||||
| 			o.buf.BackEscapeWord() | ||||
| 		case CharCtrlY: | ||||
| 			o.buf.Yank() | ||||
| 		case CharEnter, CharCtrlJ: | ||||
| 			if o.IsSearchMode() { | ||||
| 				o.ExitSearchMode(false) | ||||
| 			} | ||||
| 			o.buf.MoveToLineEnd() | ||||
| 			var data []rune | ||||
| 			if !o.GetConfig().UniqueEditLine { | ||||
| 				o.buf.WriteRune('\n') | ||||
| 				data = o.buf.Reset() | ||||
| 				data = data[:len(data)-1] // trim \n | ||||
| 			} else { | ||||
| 				o.buf.Clean() | ||||
| 				data = o.buf.Reset() | ||||
| 			} | ||||
| 			o.outchan <- data | ||||
| 			if !o.GetConfig().DisableAutoSaveHistory { | ||||
| 				// ignore IO error | ||||
| 				_ = o.history.New(data) | ||||
| 			} else { | ||||
| 				isUpdateHistory = false | ||||
| 			} | ||||
| 		case CharBackward: | ||||
| 			o.buf.MoveBackward() | ||||
| 		case CharForward: | ||||
| 			o.buf.MoveForward() | ||||
| 		case CharPrev: | ||||
| 			buf := o.history.Prev() | ||||
| 			if buf != nil { | ||||
| 				o.buf.Set(buf) | ||||
| 			} else { | ||||
| 				o.t.Bell() | ||||
| 			} | ||||
| 		case CharNext: | ||||
| 			buf, ok := o.history.Next() | ||||
| 			if ok { | ||||
| 				o.buf.Set(buf) | ||||
| 			} else { | ||||
| 				o.t.Bell() | ||||
| 			} | ||||
| 		case CharDelete: | ||||
| 			if o.buf.Len() > 0 || !o.IsNormalMode() { | ||||
| 				o.t.KickRead() | ||||
| 				if !o.buf.Delete() { | ||||
| 					o.t.Bell() | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			// treat as EOF | ||||
| 			if !o.GetConfig().UniqueEditLine { | ||||
| 				o.buf.WriteString(o.GetConfig().EOFPrompt + "\n") | ||||
| 			} | ||||
| 			o.buf.Reset() | ||||
| 			isUpdateHistory = false | ||||
| 			o.history.Revert() | ||||
| 			o.errchan <- io.EOF | ||||
| 			if o.GetConfig().UniqueEditLine { | ||||
| 				o.buf.Clean() | ||||
| 			} | ||||
| 		case CharInterrupt: | ||||
| 			if o.IsSearchMode() { | ||||
| 				o.t.KickRead() | ||||
| 				o.ExitSearchMode(true) | ||||
| 				break | ||||
| 			} | ||||
| 			if o.IsInCompleteMode() { | ||||
| 				o.t.KickRead() | ||||
| 				o.ExitCompleteMode(true) | ||||
| 				o.buf.Refresh(nil) | ||||
| 				break | ||||
| 			} | ||||
| 			o.buf.MoveToLineEnd() | ||||
| 			o.buf.Refresh(nil) | ||||
| 			hint := o.GetConfig().InterruptPrompt + "\n" | ||||
| 			if !o.GetConfig().UniqueEditLine { | ||||
| 				o.buf.WriteString(hint) | ||||
| 			} | ||||
| 			remain := o.buf.Reset() | ||||
| 			if !o.GetConfig().UniqueEditLine { | ||||
| 				remain = remain[:len(remain)-len([]rune(hint))] | ||||
| 			} | ||||
| 			isUpdateHistory = false | ||||
| 			o.history.Revert() | ||||
| 			o.errchan <- &InterruptError{remain} | ||||
| 		default: | ||||
| 			if o.IsSearchMode() { | ||||
| 				o.SearchChar(r) | ||||
| 				keepInSearchMode = true | ||||
| 				break | ||||
| 			} | ||||
| 			o.buf.WriteRune(r) | ||||
| 			if o.IsInCompleteMode() { | ||||
| 				o.OnComplete() | ||||
| 				keepInCompleteMode = true | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		listener := o.GetConfig().Listener | ||||
| 		if listener != nil { | ||||
| 			newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r) | ||||
| 			if ok { | ||||
| 				o.buf.SetWithIdx(newPos, newLine) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		o.m.Lock() | ||||
| 		if !keepInSearchMode && o.IsSearchMode() { | ||||
| 			o.ExitSearchMode(false) | ||||
| 			o.buf.Refresh(nil) | ||||
| 		} else if o.IsInCompleteMode() { | ||||
| 			if !keepInCompleteMode { | ||||
| 				o.ExitCompleteMode(false) | ||||
| 				o.Refresh() | ||||
| 			} else { | ||||
| 				o.buf.Refresh(nil) | ||||
| 				o.CompleteRefresh() | ||||
| 			} | ||||
| 		} | ||||
| 		if isUpdateHistory && !o.IsSearchMode() { | ||||
| 			// it will cause null history | ||||
| 			o.history.Update(o.buf.Runes(), false) | ||||
| 		} | ||||
| 		o.m.Unlock() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *Operation) Stderr() io.Writer { | ||||
| 	return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t} | ||||
| } | ||||
|  | ||||
| func (o *Operation) Stdout() io.Writer { | ||||
| 	return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t} | ||||
| } | ||||
|  | ||||
| func (o *Operation) String() (string, error) { | ||||
| 	r, err := o.Runes() | ||||
| 	return string(r), err | ||||
| } | ||||
|  | ||||
| func (o *Operation) Runes() ([]rune, error) { | ||||
| 	o.t.EnterRawMode() | ||||
| 	defer o.t.ExitRawMode() | ||||
|  | ||||
| 	listener := o.GetConfig().Listener | ||||
| 	if listener != nil { | ||||
| 		listener.OnChange(nil, 0, 0) | ||||
| 	} | ||||
|  | ||||
| 	o.buf.Refresh(nil) // print prompt | ||||
| 	o.t.KickRead() | ||||
| 	select { | ||||
| 	case r := <-o.outchan: | ||||
| 		return r, nil | ||||
| 	case err := <-o.errchan: | ||||
| 		if e, ok := err.(*InterruptError); ok { | ||||
| 			return e.Line, ErrInterrupt | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) { | ||||
| 	cfg := o.GenPasswordConfig() | ||||
| 	cfg.Prompt = prompt | ||||
| 	cfg.Listener = l | ||||
| 	return o.PasswordWithConfig(cfg) | ||||
| } | ||||
|  | ||||
| func (o *Operation) GenPasswordConfig() *Config { | ||||
| 	return o.opPassword.PasswordConfig() | ||||
| } | ||||
|  | ||||
| func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) { | ||||
| 	if err := o.opPassword.EnterPasswordMode(cfg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer o.opPassword.ExitPasswordMode() | ||||
| 	return o.Slice() | ||||
| } | ||||
|  | ||||
| func (o *Operation) Password(prompt string) ([]byte, error) { | ||||
| 	return o.PasswordEx(prompt, nil) | ||||
| } | ||||
|  | ||||
| func (o *Operation) SetTitle(t string) { | ||||
| 	o.w.Write([]byte("\033[2;" + t + "\007")) | ||||
| } | ||||
|  | ||||
| func (o *Operation) Slice() ([]byte, error) { | ||||
| 	r, err := o.Runes() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return []byte(string(r)), nil | ||||
| } | ||||
|  | ||||
| func (o *Operation) Close() { | ||||
| 	select { | ||||
| 	case o.errchan <- io.EOF: | ||||
| 	default: | ||||
| 	} | ||||
| 	o.history.Close() | ||||
| } | ||||
|  | ||||
| func (o *Operation) SetHistoryPath(path string) { | ||||
| 	if o.history != nil { | ||||
| 		o.history.Close() | ||||
| 	} | ||||
| 	o.cfg.HistoryFile = path | ||||
| 	o.history = newOpHistory(o.cfg) | ||||
| } | ||||
|  | ||||
| func (o *Operation) IsNormalMode() bool { | ||||
| 	return !o.IsInCompleteMode() && !o.IsSearchMode() | ||||
| } | ||||
|  | ||||
| func (op *Operation) SetConfig(cfg *Config) (*Config, error) { | ||||
| 	op.m.Lock() | ||||
| 	defer op.m.Unlock() | ||||
| 	if op.cfg == cfg { | ||||
| 		return op.cfg, nil | ||||
| 	} | ||||
| 	if err := cfg.Init(); err != nil { | ||||
| 		return op.cfg, err | ||||
| 	} | ||||
| 	old := op.cfg | ||||
| 	op.cfg = cfg | ||||
| 	op.SetPrompt(cfg.Prompt) | ||||
| 	op.SetMaskRune(cfg.MaskRune) | ||||
| 	op.buf.SetConfig(cfg) | ||||
| 	width := op.cfg.FuncGetWidth() | ||||
|  | ||||
| 	if cfg.opHistory == nil { | ||||
| 		op.SetHistoryPath(cfg.HistoryFile) | ||||
| 		cfg.opHistory = op.history | ||||
| 		cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history, cfg, width) | ||||
| 	} | ||||
| 	op.history = cfg.opHistory | ||||
|  | ||||
| 	// SetHistoryPath will close opHistory which already exists | ||||
| 	// so if we use it next time, we need to reopen it by `InitHistory()` | ||||
| 	op.history.Init() | ||||
|  | ||||
| 	if op.cfg.AutoComplete != nil { | ||||
| 		op.opCompleter = newOpCompleter(op.buf.w, op, width) | ||||
| 	} | ||||
|  | ||||
| 	op.opSearch = cfg.opSearch | ||||
| 	return old, nil | ||||
| } | ||||
|  | ||||
| func (o *Operation) ResetHistory() { | ||||
| 	o.history.Reset() | ||||
| } | ||||
|  | ||||
| // if err is not nil, it just mean it fail to write to file | ||||
| // other things goes fine. | ||||
| func (o *Operation) SaveHistory(content string) error { | ||||
| 	return o.history.New([]rune(content)) | ||||
| } | ||||
|  | ||||
| func (o *Operation) Refresh() { | ||||
| 	if o.t.IsReading() { | ||||
| 		o.buf.Refresh(nil) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *Operation) Clean() { | ||||
| 	o.buf.Clean() | ||||
| } | ||||
|  | ||||
| func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener { | ||||
| 	return &DumpListener{f: f} | ||||
| } | ||||
|  | ||||
| type DumpListener struct { | ||||
| 	f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) | ||||
| } | ||||
|  | ||||
| func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { | ||||
| 	return d.f(line, pos, key) | ||||
| } | ||||
|  | ||||
| type Listener interface { | ||||
| 	OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) | ||||
| } | ||||
|  | ||||
| type Painter interface { | ||||
| 	Paint(line []rune, pos int) []rune | ||||
| } | ||||
|  | ||||
| type defaultPainter struct{} | ||||
|  | ||||
| func (p *defaultPainter) Paint(line []rune, _ int) []rune { | ||||
| 	return line | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/chzyer/readline/password.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/chzyer/readline/password.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| package readline | ||||
|  | ||||
| type opPassword struct { | ||||
| 	o         *Operation | ||||
| 	backupCfg *Config | ||||
| } | ||||
|  | ||||
| func newOpPassword(o *Operation) *opPassword { | ||||
| 	return &opPassword{o: o} | ||||
| } | ||||
|  | ||||
| func (o *opPassword) ExitPasswordMode() { | ||||
| 	o.o.SetConfig(o.backupCfg) | ||||
| 	o.backupCfg = nil | ||||
| } | ||||
|  | ||||
| func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) { | ||||
| 	o.backupCfg, err = o.o.SetConfig(cfg) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (o *opPassword) PasswordConfig() *Config { | ||||
| 	return &Config{ | ||||
| 		EnableMask:      true, | ||||
| 		InterruptPrompt: "\n", | ||||
| 		EOFPrompt:       "\n", | ||||
| 		HistoryLimit:    -1, | ||||
| 		Painter:         &defaultPainter{}, | ||||
|  | ||||
| 		Stdout: o.o.cfg.Stdout, | ||||
| 		Stderr: o.o.cfg.Stderr, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										125
									
								
								vendor/github.com/chzyer/readline/rawreader_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/github.com/chzyer/readline/rawreader_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| // +build windows | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import "unsafe" | ||||
|  | ||||
| const ( | ||||
| 	VK_CANCEL   = 0x03 | ||||
| 	VK_BACK     = 0x08 | ||||
| 	VK_TAB      = 0x09 | ||||
| 	VK_RETURN   = 0x0D | ||||
| 	VK_SHIFT    = 0x10 | ||||
| 	VK_CONTROL  = 0x11 | ||||
| 	VK_MENU     = 0x12 | ||||
| 	VK_ESCAPE   = 0x1B | ||||
| 	VK_LEFT     = 0x25 | ||||
| 	VK_UP       = 0x26 | ||||
| 	VK_RIGHT    = 0x27 | ||||
| 	VK_DOWN     = 0x28 | ||||
| 	VK_DELETE   = 0x2E | ||||
| 	VK_LSHIFT   = 0xA0 | ||||
| 	VK_RSHIFT   = 0xA1 | ||||
| 	VK_LCONTROL = 0xA2 | ||||
| 	VK_RCONTROL = 0xA3 | ||||
| ) | ||||
|  | ||||
| // RawReader translate input record to ANSI escape sequence. | ||||
| // To provides same behavior as unix terminal. | ||||
| type RawReader struct { | ||||
| 	ctrlKey bool | ||||
| 	altKey  bool | ||||
| } | ||||
|  | ||||
| func NewRawReader() *RawReader { | ||||
| 	r := new(RawReader) | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // only process one action in one read | ||||
| func (r *RawReader) Read(buf []byte) (int, error) { | ||||
| 	ir := new(_INPUT_RECORD) | ||||
| 	var read int | ||||
| 	var err error | ||||
| next: | ||||
| 	err = kernel.ReadConsoleInputW(stdin, | ||||
| 		uintptr(unsafe.Pointer(ir)), | ||||
| 		1, | ||||
| 		uintptr(unsafe.Pointer(&read)), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	if ir.EventType != EVENT_KEY { | ||||
| 		goto next | ||||
| 	} | ||||
| 	ker := (*_KEY_EVENT_RECORD)(unsafe.Pointer(&ir.Event[0])) | ||||
| 	if ker.bKeyDown == 0 { // keyup | ||||
| 		if r.ctrlKey || r.altKey { | ||||
| 			switch ker.wVirtualKeyCode { | ||||
| 			case VK_RCONTROL, VK_LCONTROL: | ||||
| 				r.ctrlKey = false | ||||
| 			case VK_MENU: //alt | ||||
| 				r.altKey = false | ||||
| 			} | ||||
| 		} | ||||
| 		goto next | ||||
| 	} | ||||
|  | ||||
| 	if ker.unicodeChar == 0 { | ||||
| 		var target rune | ||||
| 		switch ker.wVirtualKeyCode { | ||||
| 		case VK_RCONTROL, VK_LCONTROL: | ||||
| 			r.ctrlKey = true | ||||
| 		case VK_MENU: //alt | ||||
| 			r.altKey = true | ||||
| 		case VK_LEFT: | ||||
| 			target = CharBackward | ||||
| 		case VK_RIGHT: | ||||
| 			target = CharForward | ||||
| 		case VK_UP: | ||||
| 			target = CharPrev | ||||
| 		case VK_DOWN: | ||||
| 			target = CharNext | ||||
| 		} | ||||
| 		if target != 0 { | ||||
| 			return r.write(buf, target) | ||||
| 		} | ||||
| 		goto next | ||||
| 	} | ||||
| 	char := rune(ker.unicodeChar) | ||||
| 	if r.ctrlKey { | ||||
| 		switch char { | ||||
| 		case 'A': | ||||
| 			char = CharLineStart | ||||
| 		case 'E': | ||||
| 			char = CharLineEnd | ||||
| 		case 'R': | ||||
| 			char = CharBckSearch | ||||
| 		case 'S': | ||||
| 			char = CharFwdSearch | ||||
| 		} | ||||
| 	} else if r.altKey { | ||||
| 		switch char { | ||||
| 		case VK_BACK: | ||||
| 			char = CharBackspace | ||||
| 		} | ||||
| 		return r.writeEsc(buf, char) | ||||
| 	} | ||||
| 	return r.write(buf, char) | ||||
| } | ||||
|  | ||||
| func (r *RawReader) writeEsc(b []byte, char rune) (int, error) { | ||||
| 	b[0] = '\033' | ||||
| 	n := copy(b[1:], []byte(string(char))) | ||||
| 	return n + 1, nil | ||||
| } | ||||
|  | ||||
| func (r *RawReader) write(b []byte, char rune) (int, error) { | ||||
| 	n := copy(b, []byte(string(char))) | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| func (r *RawReader) Close() error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										338
									
								
								vendor/github.com/chzyer/readline/readline.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								vendor/github.com/chzyer/readline/readline.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| // Readline is a pure go implementation for GNU-Readline kind library. | ||||
| // | ||||
| // example: | ||||
| // 	rl, err := readline.New("> ") | ||||
| // 	if err != nil { | ||||
| // 		panic(err) | ||||
| // 	} | ||||
| // 	defer rl.Close() | ||||
| // | ||||
| // 	for { | ||||
| // 		line, err := rl.Readline() | ||||
| // 		if err != nil { // io.EOF | ||||
| // 			break | ||||
| // 		} | ||||
| // 		println(line) | ||||
| // 	} | ||||
| // | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type Instance struct { | ||||
| 	Config    *Config | ||||
| 	Terminal  *Terminal | ||||
| 	Operation *Operation | ||||
| } | ||||
|  | ||||
| type Config struct { | ||||
| 	// prompt supports ANSI escape sequence, so we can color some characters even in windows | ||||
| 	Prompt string | ||||
|  | ||||
| 	// readline will persist historys to file where HistoryFile specified | ||||
| 	HistoryFile string | ||||
| 	// specify the max length of historys, it's 500 by default, set it to -1 to disable history | ||||
| 	HistoryLimit           int | ||||
| 	DisableAutoSaveHistory bool | ||||
| 	// enable case-insensitive history searching | ||||
| 	HistorySearchFold bool | ||||
|  | ||||
| 	// AutoCompleter will called once user press TAB | ||||
| 	AutoComplete AutoCompleter | ||||
|  | ||||
| 	// Any key press will pass to Listener | ||||
| 	// NOTE: Listener will be triggered by (nil, 0, 0) immediately | ||||
| 	Listener Listener | ||||
|  | ||||
| 	Painter Painter | ||||
|  | ||||
| 	// If VimMode is true, readline will in vim.insert mode by default | ||||
| 	VimMode bool | ||||
|  | ||||
| 	InterruptPrompt string | ||||
| 	EOFPrompt       string | ||||
|  | ||||
| 	FuncGetWidth func() int | ||||
|  | ||||
| 	Stdin       io.ReadCloser | ||||
| 	StdinWriter io.Writer | ||||
| 	Stdout      io.Writer | ||||
| 	Stderr      io.Writer | ||||
|  | ||||
| 	EnableMask bool | ||||
| 	MaskRune   rune | ||||
|  | ||||
| 	// erase the editing line after user submited it | ||||
| 	// it use in IM usually. | ||||
| 	UniqueEditLine bool | ||||
|  | ||||
| 	// filter input runes (may be used to disable CtrlZ or for translating some keys to different actions) | ||||
| 	// -> output = new (translated) rune and true/false if continue with processing this one | ||||
| 	FuncFilterInputRune func(rune) (rune, bool) | ||||
|  | ||||
| 	// force use interactive even stdout is not a tty | ||||
| 	FuncIsTerminal      func() bool | ||||
| 	FuncMakeRaw         func() error | ||||
| 	FuncExitRaw         func() error | ||||
| 	FuncOnWidthChanged  func(func()) | ||||
| 	ForceUseInteractive bool | ||||
|  | ||||
| 	// private fields | ||||
| 	inited    bool | ||||
| 	opHistory *opHistory | ||||
| 	opSearch  *opSearch | ||||
| } | ||||
|  | ||||
| func (c *Config) useInteractive() bool { | ||||
| 	if c.ForceUseInteractive { | ||||
| 		return true | ||||
| 	} | ||||
| 	return c.FuncIsTerminal() | ||||
| } | ||||
|  | ||||
| func (c *Config) Init() error { | ||||
| 	if c.inited { | ||||
| 		return nil | ||||
| 	} | ||||
| 	c.inited = true | ||||
| 	if c.Stdin == nil { | ||||
| 		c.Stdin = NewCancelableStdin(Stdin) | ||||
| 	} | ||||
|  | ||||
| 	c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin) | ||||
|  | ||||
| 	if c.Stdout == nil { | ||||
| 		c.Stdout = Stdout | ||||
| 	} | ||||
| 	if c.Stderr == nil { | ||||
| 		c.Stderr = Stderr | ||||
| 	} | ||||
| 	if c.HistoryLimit == 0 { | ||||
| 		c.HistoryLimit = 500 | ||||
| 	} | ||||
|  | ||||
| 	if c.InterruptPrompt == "" { | ||||
| 		c.InterruptPrompt = "^C" | ||||
| 	} else if c.InterruptPrompt == "\n" { | ||||
| 		c.InterruptPrompt = "" | ||||
| 	} | ||||
| 	if c.EOFPrompt == "" { | ||||
| 		c.EOFPrompt = "^D" | ||||
| 	} else if c.EOFPrompt == "\n" { | ||||
| 		c.EOFPrompt = "" | ||||
| 	} | ||||
|  | ||||
| 	if c.AutoComplete == nil { | ||||
| 		c.AutoComplete = &TabCompleter{} | ||||
| 	} | ||||
| 	if c.FuncGetWidth == nil { | ||||
| 		c.FuncGetWidth = GetScreenWidth | ||||
| 	} | ||||
| 	if c.FuncIsTerminal == nil { | ||||
| 		c.FuncIsTerminal = DefaultIsTerminal | ||||
| 	} | ||||
| 	rm := new(RawMode) | ||||
| 	if c.FuncMakeRaw == nil { | ||||
| 		c.FuncMakeRaw = rm.Enter | ||||
| 	} | ||||
| 	if c.FuncExitRaw == nil { | ||||
| 		c.FuncExitRaw = rm.Exit | ||||
| 	} | ||||
| 	if c.FuncOnWidthChanged == nil { | ||||
| 		c.FuncOnWidthChanged = DefaultOnWidthChanged | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (c Config) Clone() *Config { | ||||
| 	c.opHistory = nil | ||||
| 	c.opSearch = nil | ||||
| 	return &c | ||||
| } | ||||
|  | ||||
| func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) { | ||||
| 	c.Listener = FuncListener(f) | ||||
| } | ||||
|  | ||||
| func (c *Config) SetPainter(p Painter) { | ||||
| 	c.Painter = p | ||||
| } | ||||
|  | ||||
| func NewEx(cfg *Config) (*Instance, error) { | ||||
| 	t, err := NewTerminal(cfg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	rl := t.Readline() | ||||
| 	if cfg.Painter == nil { | ||||
| 		cfg.Painter = &defaultPainter{} | ||||
| 	} | ||||
| 	return &Instance{ | ||||
| 		Config:    cfg, | ||||
| 		Terminal:  t, | ||||
| 		Operation: rl, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func New(prompt string) (*Instance, error) { | ||||
| 	return NewEx(&Config{Prompt: prompt}) | ||||
| } | ||||
|  | ||||
| func (i *Instance) ResetHistory() { | ||||
| 	i.Operation.ResetHistory() | ||||
| } | ||||
|  | ||||
| func (i *Instance) SetPrompt(s string) { | ||||
| 	i.Operation.SetPrompt(s) | ||||
| } | ||||
|  | ||||
| func (i *Instance) SetMaskRune(r rune) { | ||||
| 	i.Operation.SetMaskRune(r) | ||||
| } | ||||
|  | ||||
| // change history persistence in runtime | ||||
| func (i *Instance) SetHistoryPath(p string) { | ||||
| 	i.Operation.SetHistoryPath(p) | ||||
| } | ||||
|  | ||||
| // readline will refresh automatic when write through Stdout() | ||||
| func (i *Instance) Stdout() io.Writer { | ||||
| 	return i.Operation.Stdout() | ||||
| } | ||||
|  | ||||
| // readline will refresh automatic when write through Stdout() | ||||
| func (i *Instance) Stderr() io.Writer { | ||||
| 	return i.Operation.Stderr() | ||||
| } | ||||
|  | ||||
| // switch VimMode in runtime | ||||
| func (i *Instance) SetVimMode(on bool) { | ||||
| 	i.Operation.SetVimMode(on) | ||||
| } | ||||
|  | ||||
| func (i *Instance) IsVimMode() bool { | ||||
| 	return i.Operation.IsEnableVimMode() | ||||
| } | ||||
|  | ||||
| func (i *Instance) GenPasswordConfig() *Config { | ||||
| 	return i.Operation.GenPasswordConfig() | ||||
| } | ||||
|  | ||||
| // we can generate a config by `i.GenPasswordConfig()` | ||||
| func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) { | ||||
| 	return i.Operation.PasswordWithConfig(cfg) | ||||
| } | ||||
|  | ||||
| func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) { | ||||
| 	return i.Operation.PasswordEx(prompt, l) | ||||
| } | ||||
|  | ||||
| func (i *Instance) ReadPassword(prompt string) ([]byte, error) { | ||||
| 	return i.Operation.Password(prompt) | ||||
| } | ||||
|  | ||||
| type Result struct { | ||||
| 	Line  string | ||||
| 	Error error | ||||
| } | ||||
|  | ||||
| func (l *Result) CanContinue() bool { | ||||
| 	return len(l.Line) != 0 && l.Error == ErrInterrupt | ||||
| } | ||||
|  | ||||
| func (l *Result) CanBreak() bool { | ||||
| 	return !l.CanContinue() && l.Error != nil | ||||
| } | ||||
|  | ||||
| func (i *Instance) Line() *Result { | ||||
| 	ret, err := i.Readline() | ||||
| 	return &Result{ret, err} | ||||
| } | ||||
|  | ||||
| // err is one of (nil, io.EOF, readline.ErrInterrupt) | ||||
| func (i *Instance) Readline() (string, error) { | ||||
| 	return i.Operation.String() | ||||
| } | ||||
|  | ||||
| func (i *Instance) ReadlineWithDefault(what string) (string, error) { | ||||
| 	i.Operation.SetBuffer(what) | ||||
| 	return i.Operation.String() | ||||
| } | ||||
|  | ||||
| func (i *Instance) SaveHistory(content string) error { | ||||
| 	return i.Operation.SaveHistory(content) | ||||
| } | ||||
|  | ||||
| // same as readline | ||||
| func (i *Instance) ReadSlice() ([]byte, error) { | ||||
| 	return i.Operation.Slice() | ||||
| } | ||||
|  | ||||
| // we must make sure that call Close() before process exit. | ||||
| // if there has a pending reading operation, that reading will be interrupted. | ||||
| // so you can capture the signal and call Instance.Close(), it's thread-safe. | ||||
| func (i *Instance) Close() error { | ||||
| 	i.Config.Stdin.Close() | ||||
| 	i.Operation.Close() | ||||
| 	if err := i.Terminal.Close(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // call CaptureExitSignal when you want readline exit gracefully. | ||||
| func (i *Instance) CaptureExitSignal() { | ||||
| 	CaptureExitSignal(func() { | ||||
| 		i.Close() | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (i *Instance) Clean() { | ||||
| 	i.Operation.Clean() | ||||
| } | ||||
|  | ||||
| func (i *Instance) Write(b []byte) (int, error) { | ||||
| 	return i.Stdout().Write(b) | ||||
| } | ||||
|  | ||||
| // WriteStdin prefill the next Stdin fetch | ||||
| // Next time you call ReadLine() this value will be writen before the user input | ||||
| // ie : | ||||
| //  i := readline.New() | ||||
| //  i.WriteStdin([]byte("test")) | ||||
| //  _, _= i.Readline() | ||||
| // | ||||
| // gives | ||||
| // | ||||
| // > test[cursor] | ||||
| func (i *Instance) WriteStdin(val []byte) (int, error) { | ||||
| 	return i.Terminal.WriteStdin(val) | ||||
| } | ||||
|  | ||||
| func (i *Instance) SetConfig(cfg *Config) *Config { | ||||
| 	if i.Config == cfg { | ||||
| 		return cfg | ||||
| 	} | ||||
| 	old := i.Config | ||||
| 	i.Config = cfg | ||||
| 	i.Operation.SetConfig(cfg) | ||||
| 	i.Terminal.SetConfig(cfg) | ||||
| 	return old | ||||
| } | ||||
|  | ||||
| func (i *Instance) Refresh() { | ||||
| 	i.Operation.Refresh() | ||||
| } | ||||
|  | ||||
| // HistoryDisable the save of the commands into the history | ||||
| func (i *Instance) HistoryDisable() { | ||||
| 	i.Operation.history.Disable() | ||||
| } | ||||
|  | ||||
| // HistoryEnable the save of the commands into the history (default on) | ||||
| func (i *Instance) HistoryEnable() { | ||||
| 	i.Operation.history.Enable() | ||||
| } | ||||
							
								
								
									
										475
									
								
								vendor/github.com/chzyer/readline/remote.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								vendor/github.com/chzyer/readline/remote.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,475 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
|  | ||||
| type MsgType int16 | ||||
|  | ||||
| const ( | ||||
| 	T_DATA = MsgType(iota) | ||||
| 	T_WIDTH | ||||
| 	T_WIDTH_REPORT | ||||
| 	T_ISTTY_REPORT | ||||
| 	T_RAW | ||||
| 	T_ERAW // exit raw | ||||
| 	T_EOF | ||||
| ) | ||||
|  | ||||
| type RemoteSvr struct { | ||||
| 	eof           int32 | ||||
| 	closed        int32 | ||||
| 	width         int32 | ||||
| 	reciveChan    chan struct{} | ||||
| 	writeChan     chan *writeCtx | ||||
| 	conn          net.Conn | ||||
| 	isTerminal    bool | ||||
| 	funcWidthChan func() | ||||
| 	stopChan      chan struct{} | ||||
|  | ||||
| 	dataBufM sync.Mutex | ||||
| 	dataBuf  bytes.Buffer | ||||
| } | ||||
|  | ||||
| type writeReply struct { | ||||
| 	n   int | ||||
| 	err error | ||||
| } | ||||
|  | ||||
| type writeCtx struct { | ||||
| 	msg   *Message | ||||
| 	reply chan *writeReply | ||||
| } | ||||
|  | ||||
| func newWriteCtx(msg *Message) *writeCtx { | ||||
| 	return &writeCtx{ | ||||
| 		msg:   msg, | ||||
| 		reply: make(chan *writeReply), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewRemoteSvr(conn net.Conn) (*RemoteSvr, error) { | ||||
| 	rs := &RemoteSvr{ | ||||
| 		width:      -1, | ||||
| 		conn:       conn, | ||||
| 		writeChan:  make(chan *writeCtx), | ||||
| 		reciveChan: make(chan struct{}), | ||||
| 		stopChan:   make(chan struct{}), | ||||
| 	} | ||||
| 	buf := bufio.NewReader(rs.conn) | ||||
|  | ||||
| 	if err := rs.init(buf); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	go rs.readLoop(buf) | ||||
| 	go rs.writeLoop() | ||||
| 	return rs, nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) init(buf *bufio.Reader) error { | ||||
| 	m, err := ReadMessage(buf) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// receive isTerminal | ||||
| 	if m.Type != T_ISTTY_REPORT { | ||||
| 		return fmt.Errorf("unexpected init message") | ||||
| 	} | ||||
| 	r.GotIsTerminal(m.Data) | ||||
|  | ||||
| 	// receive width | ||||
| 	m, err = ReadMessage(buf) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if m.Type != T_WIDTH_REPORT { | ||||
| 		return fmt.Errorf("unexpected init message") | ||||
| 	} | ||||
| 	r.GotReportWidth(m.Data) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) HandleConfig(cfg *Config) { | ||||
| 	cfg.Stderr = r | ||||
| 	cfg.Stdout = r | ||||
| 	cfg.Stdin = r | ||||
| 	cfg.FuncExitRaw = r.ExitRawMode | ||||
| 	cfg.FuncIsTerminal = r.IsTerminal | ||||
| 	cfg.FuncMakeRaw = r.EnterRawMode | ||||
| 	cfg.FuncExitRaw = r.ExitRawMode | ||||
| 	cfg.FuncGetWidth = r.GetWidth | ||||
| 	cfg.FuncOnWidthChanged = func(f func()) { | ||||
| 		r.funcWidthChan = f | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) IsTerminal() bool { | ||||
| 	return r.isTerminal | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) checkEOF() error { | ||||
| 	if atomic.LoadInt32(&r.eof) == 1 { | ||||
| 		return io.EOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) Read(b []byte) (int, error) { | ||||
| 	r.dataBufM.Lock() | ||||
| 	n, err := r.dataBuf.Read(b) | ||||
| 	r.dataBufM.Unlock() | ||||
| 	if n == 0 { | ||||
| 		if err := r.checkEOF(); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if n == 0 && err == io.EOF { | ||||
| 		<-r.reciveChan | ||||
| 		r.dataBufM.Lock() | ||||
| 		n, err = r.dataBuf.Read(b) | ||||
| 		r.dataBufM.Unlock() | ||||
| 	} | ||||
| 	if n == 0 { | ||||
| 		if err := r.checkEOF(); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) writeMsg(m *Message) error { | ||||
| 	ctx := newWriteCtx(m) | ||||
| 	r.writeChan <- ctx | ||||
| 	reply := <-ctx.reply | ||||
| 	return reply.err | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) Write(b []byte) (int, error) { | ||||
| 	ctx := newWriteCtx(NewMessage(T_DATA, b)) | ||||
| 	r.writeChan <- ctx | ||||
| 	reply := <-ctx.reply | ||||
| 	return reply.n, reply.err | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) EnterRawMode() error { | ||||
| 	return r.writeMsg(NewMessage(T_RAW, nil)) | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) ExitRawMode() error { | ||||
| 	return r.writeMsg(NewMessage(T_ERAW, nil)) | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) writeLoop() { | ||||
| 	defer r.Close() | ||||
|  | ||||
| loop: | ||||
| 	for { | ||||
| 		select { | ||||
| 		case ctx, ok := <-r.writeChan: | ||||
| 			if !ok { | ||||
| 				break | ||||
| 			} | ||||
| 			n, err := ctx.msg.WriteTo(r.conn) | ||||
| 			ctx.reply <- &writeReply{n, err} | ||||
| 		case <-r.stopChan: | ||||
| 			break loop | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) Close() error { | ||||
| 	if atomic.CompareAndSwapInt32(&r.closed, 0, 1) { | ||||
| 		close(r.stopChan) | ||||
| 		r.conn.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) readLoop(buf *bufio.Reader) { | ||||
| 	defer r.Close() | ||||
| 	for { | ||||
| 		m, err := ReadMessage(buf) | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		switch m.Type { | ||||
| 		case T_EOF: | ||||
| 			atomic.StoreInt32(&r.eof, 1) | ||||
| 			select { | ||||
| 			case r.reciveChan <- struct{}{}: | ||||
| 			default: | ||||
| 			} | ||||
| 		case T_DATA: | ||||
| 			r.dataBufM.Lock() | ||||
| 			r.dataBuf.Write(m.Data) | ||||
| 			r.dataBufM.Unlock() | ||||
| 			select { | ||||
| 			case r.reciveChan <- struct{}{}: | ||||
| 			default: | ||||
| 			} | ||||
| 		case T_WIDTH_REPORT: | ||||
| 			r.GotReportWidth(m.Data) | ||||
| 		case T_ISTTY_REPORT: | ||||
| 			r.GotIsTerminal(m.Data) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) GotIsTerminal(data []byte) { | ||||
| 	if binary.BigEndian.Uint16(data) == 0 { | ||||
| 		r.isTerminal = false | ||||
| 	} else { | ||||
| 		r.isTerminal = true | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) GotReportWidth(data []byte) { | ||||
| 	atomic.StoreInt32(&r.width, int32(binary.BigEndian.Uint16(data))) | ||||
| 	if r.funcWidthChan != nil { | ||||
| 		r.funcWidthChan() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteSvr) GetWidth() int { | ||||
| 	return int(atomic.LoadInt32(&r.width)) | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| type Message struct { | ||||
| 	Type MsgType | ||||
| 	Data []byte | ||||
| } | ||||
|  | ||||
| func ReadMessage(r io.Reader) (*Message, error) { | ||||
| 	m := new(Message) | ||||
| 	var length int32 | ||||
| 	if err := binary.Read(r, binary.BigEndian, &length); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := binary.Read(r, binary.BigEndian, &m.Type); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	m.Data = make([]byte, int(length)-2) | ||||
| 	if _, err := io.ReadFull(r, m.Data); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func NewMessage(t MsgType, data []byte) *Message { | ||||
| 	return &Message{t, data} | ||||
| } | ||||
|  | ||||
| func (m *Message) WriteTo(w io.Writer) (int, error) { | ||||
| 	buf := bytes.NewBuffer(make([]byte, 0, len(m.Data)+2+4)) | ||||
| 	binary.Write(buf, binary.BigEndian, int32(len(m.Data)+2)) | ||||
| 	binary.Write(buf, binary.BigEndian, m.Type) | ||||
| 	buf.Write(m.Data) | ||||
| 	n, err := buf.WriteTo(w) | ||||
| 	return int(n), err | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| type RemoteCli struct { | ||||
| 	conn        net.Conn | ||||
| 	raw         RawMode | ||||
| 	receiveChan chan struct{} | ||||
| 	inited      int32 | ||||
| 	isTerminal  *bool | ||||
|  | ||||
| 	data  bytes.Buffer | ||||
| 	dataM sync.Mutex | ||||
| } | ||||
|  | ||||
| func NewRemoteCli(conn net.Conn) (*RemoteCli, error) { | ||||
| 	r := &RemoteCli{ | ||||
| 		conn:        conn, | ||||
| 		receiveChan: make(chan struct{}), | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) MarkIsTerminal(is bool) { | ||||
| 	r.isTerminal = &is | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) init() error { | ||||
| 	if !atomic.CompareAndSwapInt32(&r.inited, 0, 1) { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if err := r.reportIsTerminal(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := r.reportWidth(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// register sig for width changed | ||||
| 	DefaultOnWidthChanged(func() { | ||||
| 		r.reportWidth() | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) writeMsg(m *Message) error { | ||||
| 	r.dataM.Lock() | ||||
| 	_, err := m.WriteTo(r.conn) | ||||
| 	r.dataM.Unlock() | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) Write(b []byte) (int, error) { | ||||
| 	m := NewMessage(T_DATA, b) | ||||
| 	r.dataM.Lock() | ||||
| 	_, err := m.WriteTo(r.conn) | ||||
| 	r.dataM.Unlock() | ||||
| 	return len(b), err | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) reportWidth() error { | ||||
| 	screenWidth := GetScreenWidth() | ||||
| 	data := make([]byte, 2) | ||||
| 	binary.BigEndian.PutUint16(data, uint16(screenWidth)) | ||||
| 	msg := NewMessage(T_WIDTH_REPORT, data) | ||||
|  | ||||
| 	if err := r.writeMsg(msg); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) reportIsTerminal() error { | ||||
| 	var isTerminal bool | ||||
| 	if r.isTerminal != nil { | ||||
| 		isTerminal = *r.isTerminal | ||||
| 	} else { | ||||
| 		isTerminal = DefaultIsTerminal() | ||||
| 	} | ||||
| 	data := make([]byte, 2) | ||||
| 	if isTerminal { | ||||
| 		binary.BigEndian.PutUint16(data, 1) | ||||
| 	} else { | ||||
| 		binary.BigEndian.PutUint16(data, 0) | ||||
| 	} | ||||
| 	msg := NewMessage(T_ISTTY_REPORT, data) | ||||
| 	if err := r.writeMsg(msg); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) readLoop() { | ||||
| 	buf := bufio.NewReader(r.conn) | ||||
| 	for { | ||||
| 		msg, err := ReadMessage(buf) | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		switch msg.Type { | ||||
| 		case T_ERAW: | ||||
| 			r.raw.Exit() | ||||
| 		case T_RAW: | ||||
| 			r.raw.Enter() | ||||
| 		case T_DATA: | ||||
| 			os.Stdout.Write(msg.Data) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) ServeBy(source io.Reader) error { | ||||
| 	if err := r.init(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		defer r.Close() | ||||
| 		for { | ||||
| 			n, _ := io.Copy(r, source) | ||||
| 			if n == 0 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 	defer r.raw.Exit() | ||||
| 	r.readLoop() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) Close() { | ||||
| 	r.writeMsg(NewMessage(T_EOF, nil)) | ||||
| } | ||||
|  | ||||
| func (r *RemoteCli) Serve() error { | ||||
| 	return r.ServeBy(os.Stdin) | ||||
| } | ||||
|  | ||||
| func ListenRemote(n, addr string, cfg *Config, h func(*Instance), onListen ...func(net.Listener) error) error { | ||||
| 	ln, err := net.Listen(n, addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(onListen) > 0 { | ||||
| 		if err := onListen[0](ln); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	for { | ||||
| 		conn, err := ln.Accept() | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		go func() { | ||||
| 			defer conn.Close() | ||||
| 			rl, err := HandleConn(*cfg, conn) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 			h(rl) | ||||
| 		}() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func HandleConn(cfg Config, conn net.Conn) (*Instance, error) { | ||||
| 	r, err := NewRemoteSvr(conn) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	r.HandleConfig(&cfg) | ||||
|  | ||||
| 	rl, err := NewEx(&cfg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return rl, nil | ||||
| } | ||||
|  | ||||
| func DialRemote(n, addr string) error { | ||||
| 	conn, err := net.Dial(n, addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	cli, err := NewRemoteCli(conn) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return cli.Serve() | ||||
| } | ||||
							
								
								
									
										629
									
								
								vendor/github.com/chzyer/readline/runebuf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										629
									
								
								vendor/github.com/chzyer/readline/runebuf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,629 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| type runeBufferBck struct { | ||||
| 	buf []rune | ||||
| 	idx int | ||||
| } | ||||
|  | ||||
| type RuneBuffer struct { | ||||
| 	buf    []rune | ||||
| 	idx    int | ||||
| 	prompt []rune | ||||
| 	w      io.Writer | ||||
|  | ||||
| 	hadClean    bool | ||||
| 	interactive bool | ||||
| 	cfg         *Config | ||||
|  | ||||
| 	width int | ||||
|  | ||||
| 	bck *runeBufferBck | ||||
|  | ||||
| 	offset string | ||||
|  | ||||
| 	lastKill []rune | ||||
|  | ||||
| 	sync.Mutex | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) pushKill(text []rune) { | ||||
| 	r.lastKill = append([]rune{}, text...) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) OnWidthChange(newWidth int) { | ||||
| 	r.Lock() | ||||
| 	r.width = newWidth | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Backup() { | ||||
| 	r.Lock() | ||||
| 	r.bck = &runeBufferBck{r.buf, r.idx} | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Restore() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.bck == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		r.buf = r.bck.buf | ||||
| 		r.idx = r.bck.idx | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *RuneBuffer { | ||||
| 	rb := &RuneBuffer{ | ||||
| 		w:           w, | ||||
| 		interactive: cfg.useInteractive(), | ||||
| 		cfg:         cfg, | ||||
| 		width:       width, | ||||
| 	} | ||||
| 	rb.SetPrompt(prompt) | ||||
| 	return rb | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetConfig(cfg *Config) { | ||||
| 	r.Lock() | ||||
| 	r.cfg = cfg | ||||
| 	r.interactive = cfg.useInteractive() | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetMask(m rune) { | ||||
| 	r.Lock() | ||||
| 	r.cfg.MaskRune = m | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) CurrentWidth(x int) int { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 	return runes.WidthAll(r.buf[:x]) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) PromptLen() int { | ||||
| 	r.Lock() | ||||
| 	width := r.promptLen() | ||||
| 	r.Unlock() | ||||
| 	return width | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) promptLen() int { | ||||
| 	return runes.WidthAll(runes.ColorFilter(r.prompt)) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) RuneSlice(i int) []rune { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
|  | ||||
| 	if i > 0 { | ||||
| 		rs := make([]rune, i) | ||||
| 		copy(rs, r.buf[r.idx:r.idx+i]) | ||||
| 		return rs | ||||
| 	} | ||||
| 	rs := make([]rune, -i) | ||||
| 	copy(rs, r.buf[r.idx+i:r.idx]) | ||||
| 	return rs | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Runes() []rune { | ||||
| 	r.Lock() | ||||
| 	newr := make([]rune, len(r.buf)) | ||||
| 	copy(newr, r.buf) | ||||
| 	r.Unlock() | ||||
| 	return newr | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Pos() int { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 	return r.idx | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Len() int { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 	return len(r.buf) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveToLineStart() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
| 		r.idx = 0 | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveBackward() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
| 		r.idx-- | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) WriteString(s string) { | ||||
| 	r.WriteRunes([]rune(s)) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) WriteRune(s rune) { | ||||
| 	r.WriteRunes([]rune{s}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) WriteRunes(s []rune) { | ||||
| 	r.Refresh(func() { | ||||
| 		tail := append(s, r.buf[r.idx:]...) | ||||
| 		r.buf = append(r.buf[:r.idx], tail...) | ||||
| 		r.idx += len(s) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveForward() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == len(r.buf) { | ||||
| 			return | ||||
| 		} | ||||
| 		r.idx++ | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) IsCursorInEnd() bool { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 	return r.idx == len(r.buf) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Replace(ch rune) { | ||||
| 	r.Refresh(func() { | ||||
| 		r.buf[r.idx] = ch | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Erase() { | ||||
| 	r.Refresh(func() { | ||||
| 		r.idx = 0 | ||||
| 		r.pushKill(r.buf[:]) | ||||
| 		r.buf = r.buf[:0] | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Delete() (success bool) { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == len(r.buf) { | ||||
| 			return | ||||
| 		} | ||||
| 		r.pushKill(r.buf[r.idx : r.idx+1]) | ||||
| 		r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...) | ||||
| 		success = true | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) DeleteWord() { | ||||
| 	if r.idx == len(r.buf) { | ||||
| 		return | ||||
| 	} | ||||
| 	init := r.idx | ||||
| 	for init < len(r.buf) && IsWordBreak(r.buf[init]) { | ||||
| 		init++ | ||||
| 	} | ||||
| 	for i := init + 1; i < len(r.buf); i++ { | ||||
| 		if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { | ||||
| 			r.pushKill(r.buf[r.idx : i-1]) | ||||
| 			r.Refresh(func() { | ||||
| 				r.buf = append(r.buf[:r.idx], r.buf[i-1:]...) | ||||
| 			}) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	r.Kill() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveToPrevWord() (success bool) { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		for i := r.idx - 1; i > 0; i-- { | ||||
| 			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { | ||||
| 				r.idx = i | ||||
| 				success = true | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 		r.idx = 0 | ||||
| 		success = true | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) KillFront() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		length := len(r.buf) - r.idx | ||||
| 		r.pushKill(r.buf[:r.idx]) | ||||
| 		copy(r.buf[:length], r.buf[r.idx:]) | ||||
| 		r.idx = 0 | ||||
| 		r.buf = r.buf[:length] | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Kill() { | ||||
| 	r.Refresh(func() { | ||||
| 		r.pushKill(r.buf[r.idx:]) | ||||
| 		r.buf = r.buf[:r.idx] | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Transpose() { | ||||
| 	r.Refresh(func() { | ||||
| 		if len(r.buf) == 1 { | ||||
| 			r.idx++ | ||||
| 		} | ||||
|  | ||||
| 		if len(r.buf) < 2 { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if r.idx == 0 { | ||||
| 			r.idx = 1 | ||||
| 		} else if r.idx >= len(r.buf) { | ||||
| 			r.idx = len(r.buf) - 1 | ||||
| 		} | ||||
| 		r.buf[r.idx], r.buf[r.idx-1] = r.buf[r.idx-1], r.buf[r.idx] | ||||
| 		r.idx++ | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveToNextWord() { | ||||
| 	r.Refresh(func() { | ||||
| 		for i := r.idx + 1; i < len(r.buf); i++ { | ||||
| 			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { | ||||
| 				r.idx = i | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		r.idx = len(r.buf) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveToEndWord() { | ||||
| 	r.Refresh(func() { | ||||
| 		// already at the end, so do nothing | ||||
| 		if r.idx == len(r.buf) { | ||||
| 			return | ||||
| 		} | ||||
| 		// if we are at the end of a word already, go to next | ||||
| 		if !IsWordBreak(r.buf[r.idx]) && IsWordBreak(r.buf[r.idx+1]) { | ||||
| 			r.idx++ | ||||
| 		} | ||||
|  | ||||
| 		// keep going until at the end of a word | ||||
| 		for i := r.idx + 1; i < len(r.buf); i++ { | ||||
| 			if IsWordBreak(r.buf[i]) && !IsWordBreak(r.buf[i-1]) { | ||||
| 				r.idx = i - 1 | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 		r.idx = len(r.buf) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) BackEscapeWord() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
| 		for i := r.idx - 1; i > 0; i-- { | ||||
| 			if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { | ||||
| 				r.pushKill(r.buf[i:r.idx]) | ||||
| 				r.buf = append(r.buf[:i], r.buf[r.idx:]...) | ||||
| 				r.idx = i | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		r.buf = r.buf[:0] | ||||
| 		r.idx = 0 | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Yank() { | ||||
| 	if len(r.lastKill) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	r.Refresh(func() { | ||||
| 		buf := make([]rune, 0, len(r.buf)+len(r.lastKill)) | ||||
| 		buf = append(buf, r.buf[:r.idx]...) | ||||
| 		buf = append(buf, r.lastKill...) | ||||
| 		buf = append(buf, r.buf[r.idx:]...) | ||||
| 		r.buf = buf | ||||
| 		r.idx += len(r.lastKill) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Backspace() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == 0 { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		r.idx-- | ||||
| 		r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveToLineEnd() { | ||||
| 	r.Refresh(func() { | ||||
| 		if r.idx == len(r.buf) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		r.idx = len(r.buf) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) LineCount(width int) int { | ||||
| 	if width == -1 { | ||||
| 		width = r.width | ||||
| 	} | ||||
| 	return LineCount(width, | ||||
| 		runes.WidthAll(r.buf)+r.PromptLen()) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) { | ||||
| 	r.Refresh(func() { | ||||
| 		if reverse { | ||||
| 			for i := r.idx - 1; i >= 0; i-- { | ||||
| 				if r.buf[i] == ch { | ||||
| 					r.idx = i | ||||
| 					if prevChar { | ||||
| 						r.idx++ | ||||
| 					} | ||||
| 					success = true | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		for i := r.idx + 1; i < len(r.buf); i++ { | ||||
| 			if r.buf[i] == ch { | ||||
| 				r.idx = i | ||||
| 				if prevChar { | ||||
| 					r.idx-- | ||||
| 				} | ||||
| 				success = true | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) isInLineEdge() bool { | ||||
| 	if isWindows { | ||||
| 		return false | ||||
| 	} | ||||
| 	sp := r.getSplitByLine(r.buf) | ||||
| 	return len(sp[len(sp)-1]) == 0 | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) getSplitByLine(rs []rune) []string { | ||||
| 	return SplitByLine(r.promptLen(), r.width, rs) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) IdxLine(width int) int { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 	return r.idxLine(width) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) idxLine(width int) int { | ||||
| 	if width == 0 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	sp := r.getSplitByLine(r.buf[:r.idx]) | ||||
| 	return len(sp) - 1 | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) CursorLineCount() int { | ||||
| 	return r.LineCount(r.width) - r.IdxLine(r.width) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Refresh(f func()) { | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
|  | ||||
| 	if !r.interactive { | ||||
| 		if f != nil { | ||||
| 			f() | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	r.clean() | ||||
| 	if f != nil { | ||||
| 		f() | ||||
| 	} | ||||
| 	r.print() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetOffset(offset string) { | ||||
| 	r.Lock() | ||||
| 	r.offset = offset | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) print() { | ||||
| 	r.w.Write(r.output()) | ||||
| 	r.hadClean = false | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) output() []byte { | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	buf.WriteString(string(r.prompt)) | ||||
| 	if r.cfg.EnableMask && len(r.buf) > 0 { | ||||
| 		buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1))) | ||||
| 		if r.buf[len(r.buf)-1] == '\n' { | ||||
| 			buf.Write([]byte{'\n'}) | ||||
| 		} else { | ||||
| 			buf.Write([]byte(string(r.cfg.MaskRune))) | ||||
| 		} | ||||
| 		if len(r.buf) > r.idx { | ||||
| 			buf.Write(r.getBackspaceSequence()) | ||||
| 		} | ||||
|  | ||||
| 	} else { | ||||
| 		for _, e := range r.cfg.Painter.Paint(r.buf, r.idx) { | ||||
| 			if e == '\t' { | ||||
| 				buf.WriteString(strings.Repeat(" ", TabWidth)) | ||||
| 			} else { | ||||
| 				buf.WriteRune(e) | ||||
| 			} | ||||
| 		} | ||||
| 		if r.isInLineEdge() { | ||||
| 			buf.Write([]byte(" \b")) | ||||
| 		} | ||||
| 	} | ||||
| 	// cursor position | ||||
| 	if len(r.buf) > r.idx { | ||||
| 		buf.Write(r.getBackspaceSequence()) | ||||
| 	} | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) getBackspaceSequence() []byte { | ||||
| 	var sep = map[int]bool{} | ||||
|  | ||||
| 	var i int | ||||
| 	for { | ||||
| 		if i >= runes.WidthAll(r.buf) { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		if i == 0 { | ||||
| 			i -= r.promptLen() | ||||
| 		} | ||||
| 		i += r.width | ||||
|  | ||||
| 		sep[i] = true | ||||
| 	} | ||||
| 	var buf []byte | ||||
| 	for i := len(r.buf); i > r.idx; i-- { | ||||
| 		// move input to the left of one | ||||
| 		buf = append(buf, '\b') | ||||
| 		if sep[i] { | ||||
| 			// up one line, go to the start of the line and move cursor right to the end (r.width) | ||||
| 			buf = append(buf, "\033[A\r"+"\033["+strconv.Itoa(r.width)+"C"...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return buf | ||||
|  | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Reset() []rune { | ||||
| 	ret := runes.Copy(r.buf) | ||||
| 	r.buf = r.buf[:0] | ||||
| 	r.idx = 0 | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) calWidth(m int) int { | ||||
| 	if m > 0 { | ||||
| 		return runes.WidthAll(r.buf[r.idx : r.idx+m]) | ||||
| 	} | ||||
| 	return runes.WidthAll(r.buf[r.idx+m : r.idx]) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetStyle(start, end int, style string) { | ||||
| 	if end < start { | ||||
| 		panic("end < start") | ||||
| 	} | ||||
|  | ||||
| 	// goto start | ||||
| 	move := start - r.idx | ||||
| 	if move > 0 { | ||||
| 		r.w.Write([]byte(string(r.buf[r.idx : r.idx+move]))) | ||||
| 	} else { | ||||
| 		r.w.Write(bytes.Repeat([]byte("\b"), r.calWidth(move))) | ||||
| 	} | ||||
| 	r.w.Write([]byte("\033[" + style + "m")) | ||||
| 	r.w.Write([]byte(string(r.buf[start:end]))) | ||||
| 	r.w.Write([]byte("\033[0m")) | ||||
| 	// TODO: move back | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) { | ||||
| 	r.Refresh(func() { | ||||
| 		r.buf = buf | ||||
| 		r.idx = idx | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Set(buf []rune) { | ||||
| 	r.SetWithIdx(len(buf), buf) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) SetPrompt(prompt string) { | ||||
| 	r.Lock() | ||||
| 	r.prompt = []rune(prompt) | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) { | ||||
| 	buf := bufio.NewWriter(w) | ||||
|  | ||||
| 	if r.width == 0 { | ||||
| 		buf.WriteString(strings.Repeat("\r\b", len(r.buf)+r.promptLen())) | ||||
| 		buf.Write([]byte("\033[J")) | ||||
| 	} else { | ||||
| 		buf.Write([]byte("\033[J")) // just like ^k :) | ||||
| 		if idxLine == 0 { | ||||
| 			buf.WriteString("\033[2K") | ||||
| 			buf.WriteString("\r") | ||||
| 		} else { | ||||
| 			for i := 0; i < idxLine; i++ { | ||||
| 				io.WriteString(buf, "\033[2K\r\033[A") | ||||
| 			} | ||||
| 			io.WriteString(buf, "\033[2K\r") | ||||
| 		} | ||||
| 	} | ||||
| 	buf.Flush() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) Clean() { | ||||
| 	r.Lock() | ||||
| 	r.clean() | ||||
| 	r.Unlock() | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) clean() { | ||||
| 	r.cleanWithIdxLine(r.idxLine(r.width)) | ||||
| } | ||||
|  | ||||
| func (r *RuneBuffer) cleanWithIdxLine(idxLine int) { | ||||
| 	if r.hadClean || !r.interactive { | ||||
| 		return | ||||
| 	} | ||||
| 	r.hadClean = true | ||||
| 	r.cleanOutput(r.w, idxLine) | ||||
| } | ||||
							
								
								
									
										223
									
								
								vendor/github.com/chzyer/readline/runes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								vendor/github.com/chzyer/readline/runes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| var runes = Runes{} | ||||
| var TabWidth = 4 | ||||
|  | ||||
| type Runes struct{} | ||||
|  | ||||
| func (Runes) EqualRune(a, b rune, fold bool) bool { | ||||
| 	if a == b { | ||||
| 		return true | ||||
| 	} | ||||
| 	if !fold { | ||||
| 		return false | ||||
| 	} | ||||
| 	if a > b { | ||||
| 		a, b = b, a | ||||
| 	} | ||||
| 	if b < utf8.RuneSelf && 'A' <= a && a <= 'Z' { | ||||
| 		if b == a+'a'-'A' { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (r Runes) EqualRuneFold(a, b rune) bool { | ||||
| 	return r.EqualRune(a, b, true) | ||||
| } | ||||
|  | ||||
| func (r Runes) EqualFold(a, b []rune) bool { | ||||
| 	if len(a) != len(b) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i := 0; i < len(a); i++ { | ||||
| 		if r.EqualRuneFold(a[i], b[i]) { | ||||
| 			continue | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (Runes) Equal(a, b []rune) bool { | ||||
| 	if len(a) != len(b) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i := 0; i < len(a); i++ { | ||||
| 		if a[i] != b[i] { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (rs Runes) IndexAllBckEx(r, sub []rune, fold bool) int { | ||||
| 	for i := len(r) - len(sub); i >= 0; i-- { | ||||
| 		found := true | ||||
| 		for j := 0; j < len(sub); j++ { | ||||
| 			if !rs.EqualRune(r[i+j], sub[j], fold) { | ||||
| 				found = false | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if found { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| // Search in runes from end to front | ||||
| func (rs Runes) IndexAllBck(r, sub []rune) int { | ||||
| 	return rs.IndexAllBckEx(r, sub, false) | ||||
| } | ||||
|  | ||||
| // Search in runes from front to end | ||||
| func (rs Runes) IndexAll(r, sub []rune) int { | ||||
| 	return rs.IndexAllEx(r, sub, false) | ||||
| } | ||||
|  | ||||
| func (rs Runes) IndexAllEx(r, sub []rune, fold bool) int { | ||||
| 	for i := 0; i < len(r); i++ { | ||||
| 		found := true | ||||
| 		if len(r[i:]) < len(sub) { | ||||
| 			return -1 | ||||
| 		} | ||||
| 		for j := 0; j < len(sub); j++ { | ||||
| 			if !rs.EqualRune(r[i+j], sub[j], fold) { | ||||
| 				found = false | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if found { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| func (Runes) Index(r rune, rs []rune) int { | ||||
| 	for i := 0; i < len(rs); i++ { | ||||
| 		if rs[i] == r { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| func (Runes) ColorFilter(r []rune) []rune { | ||||
| 	newr := make([]rune, 0, len(r)) | ||||
| 	for pos := 0; pos < len(r); pos++ { | ||||
| 		if r[pos] == '\033' && r[pos+1] == '[' { | ||||
| 			idx := runes.Index('m', r[pos+2:]) | ||||
| 			if idx == -1 { | ||||
| 				continue | ||||
| 			} | ||||
| 			pos += idx + 2 | ||||
| 			continue | ||||
| 		} | ||||
| 		newr = append(newr, r[pos]) | ||||
| 	} | ||||
| 	return newr | ||||
| } | ||||
|  | ||||
| var zeroWidth = []*unicode.RangeTable{ | ||||
| 	unicode.Mn, | ||||
| 	unicode.Me, | ||||
| 	unicode.Cc, | ||||
| 	unicode.Cf, | ||||
| } | ||||
|  | ||||
| var doubleWidth = []*unicode.RangeTable{ | ||||
| 	unicode.Han, | ||||
| 	unicode.Hangul, | ||||
| 	unicode.Hiragana, | ||||
| 	unicode.Katakana, | ||||
| } | ||||
|  | ||||
| func (Runes) Width(r rune) int { | ||||
| 	if r == '\t' { | ||||
| 		return TabWidth | ||||
| 	} | ||||
| 	if unicode.IsOneOf(zeroWidth, r) { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	if unicode.IsOneOf(doubleWidth, r) { | ||||
| 		return 2 | ||||
| 	} | ||||
| 	return 1 | ||||
| } | ||||
|  | ||||
| func (Runes) WidthAll(r []rune) (length int) { | ||||
| 	for i := 0; i < len(r); i++ { | ||||
| 		length += runes.Width(r[i]) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (Runes) Backspace(r []rune) []byte { | ||||
| 	return bytes.Repeat([]byte{'\b'}, runes.WidthAll(r)) | ||||
| } | ||||
|  | ||||
| func (Runes) Copy(r []rune) []rune { | ||||
| 	n := make([]rune, len(r)) | ||||
| 	copy(n, r) | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| func (Runes) HasPrefixFold(r, prefix []rune) bool { | ||||
| 	if len(r) < len(prefix) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return runes.EqualFold(r[:len(prefix)], prefix) | ||||
| } | ||||
|  | ||||
| func (Runes) HasPrefix(r, prefix []rune) bool { | ||||
| 	if len(r) < len(prefix) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return runes.Equal(r[:len(prefix)], prefix) | ||||
| } | ||||
|  | ||||
| func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) { | ||||
| 	for i := 0; i < len(candicate[0]); i++ { | ||||
| 		for j := 0; j < len(candicate)-1; j++ { | ||||
| 			if i >= len(candicate[j]) || i >= len(candicate[j+1]) { | ||||
| 				goto aggregate | ||||
| 			} | ||||
| 			if candicate[j][i] != candicate[j+1][i] { | ||||
| 				goto aggregate | ||||
| 			} | ||||
| 		} | ||||
| 		size = i + 1 | ||||
| 	} | ||||
| aggregate: | ||||
| 	if size > 0 { | ||||
| 		same = runes.Copy(candicate[0][:size]) | ||||
| 		for i := 0; i < len(candicate); i++ { | ||||
| 			n := runes.Copy(candicate[i]) | ||||
| 			copy(n, n[size:]) | ||||
| 			candicate[i] = n[:len(n)-size] | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (Runes) TrimSpaceLeft(in []rune) []rune { | ||||
| 	firstIndex := len(in) | ||||
| 	for i, r := range in { | ||||
| 		if unicode.IsSpace(r) == false { | ||||
| 			firstIndex = i | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return in[firstIndex:] | ||||
| } | ||||
							
								
								
									
										164
									
								
								vendor/github.com/chzyer/readline/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/chzyer/readline/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"container/list" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	S_STATE_FOUND = iota | ||||
| 	S_STATE_FAILING | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	S_DIR_BCK = iota | ||||
| 	S_DIR_FWD | ||||
| ) | ||||
|  | ||||
| type opSearch struct { | ||||
| 	inMode    bool | ||||
| 	state     int | ||||
| 	dir       int | ||||
| 	source    *list.Element | ||||
| 	w         io.Writer | ||||
| 	buf       *RuneBuffer | ||||
| 	data      []rune | ||||
| 	history   *opHistory | ||||
| 	cfg       *Config | ||||
| 	markStart int | ||||
| 	markEnd   int | ||||
| 	width     int | ||||
| } | ||||
|  | ||||
| func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch { | ||||
| 	return &opSearch{ | ||||
| 		w:       w, | ||||
| 		buf:     buf, | ||||
| 		cfg:     cfg, | ||||
| 		history: history, | ||||
| 		width:   width, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opSearch) OnWidthChange(newWidth int) { | ||||
| 	o.width = newWidth | ||||
| } | ||||
|  | ||||
| func (o *opSearch) IsSearchMode() bool { | ||||
| 	return o.inMode | ||||
| } | ||||
|  | ||||
| func (o *opSearch) SearchBackspace() { | ||||
| 	if len(o.data) > 0 { | ||||
| 		o.data = o.data[:len(o.data)-1] | ||||
| 		o.search(true) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) { | ||||
| 	if o.dir == S_DIR_BCK { | ||||
| 		return o.history.FindBck(isNewSearch, o.data, o.buf.idx) | ||||
| 	} | ||||
| 	return o.history.FindFwd(isNewSearch, o.data, o.buf.idx) | ||||
| } | ||||
|  | ||||
| func (o *opSearch) search(isChange bool) bool { | ||||
| 	if len(o.data) == 0 { | ||||
| 		o.state = S_STATE_FOUND | ||||
| 		o.SearchRefresh(-1) | ||||
| 		return true | ||||
| 	} | ||||
| 	idx, elem := o.findHistoryBy(isChange) | ||||
| 	if elem == nil { | ||||
| 		o.SearchRefresh(-2) | ||||
| 		return false | ||||
| 	} | ||||
| 	o.history.current = elem | ||||
|  | ||||
| 	item := o.history.showItem(o.history.current.Value) | ||||
| 	start, end := 0, 0 | ||||
| 	if o.dir == S_DIR_BCK { | ||||
| 		start, end = idx, idx+len(o.data) | ||||
| 	} else { | ||||
| 		start, end = idx, idx+len(o.data) | ||||
| 		idx += len(o.data) | ||||
| 	} | ||||
| 	o.buf.SetWithIdx(idx, item) | ||||
| 	o.markStart, o.markEnd = start, end | ||||
| 	o.SearchRefresh(idx) | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o *opSearch) SearchChar(r rune) { | ||||
| 	o.data = append(o.data, r) | ||||
| 	o.search(true) | ||||
| } | ||||
|  | ||||
| func (o *opSearch) SearchMode(dir int) bool { | ||||
| 	if o.width == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	alreadyInMode := o.inMode | ||||
| 	o.inMode = true | ||||
| 	o.dir = dir | ||||
| 	o.source = o.history.current | ||||
| 	if alreadyInMode { | ||||
| 		o.search(false) | ||||
| 	} else { | ||||
| 		o.SearchRefresh(-1) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o *opSearch) ExitSearchMode(revert bool) { | ||||
| 	if revert { | ||||
| 		o.history.current = o.source | ||||
| 		o.buf.Set(o.history.showItem(o.history.current.Value)) | ||||
| 	} | ||||
| 	o.markStart, o.markEnd = 0, 0 | ||||
| 	o.state = S_STATE_FOUND | ||||
| 	o.inMode = false | ||||
| 	o.source = nil | ||||
| 	o.data = nil | ||||
| } | ||||
|  | ||||
| func (o *opSearch) SearchRefresh(x int) { | ||||
| 	if x == -2 { | ||||
| 		o.state = S_STATE_FAILING | ||||
| 	} else if x >= 0 { | ||||
| 		o.state = S_STATE_FOUND | ||||
| 	} | ||||
| 	if x < 0 { | ||||
| 		x = o.buf.idx | ||||
| 	} | ||||
| 	x = o.buf.CurrentWidth(x) | ||||
| 	x += o.buf.PromptLen() | ||||
| 	x = x % o.width | ||||
|  | ||||
| 	if o.markStart > 0 { | ||||
| 		o.buf.SetStyle(o.markStart, o.markEnd, "4") | ||||
| 	} | ||||
|  | ||||
| 	lineCnt := o.buf.CursorLineCount() | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	buf.Write(bytes.Repeat([]byte("\n"), lineCnt)) | ||||
| 	buf.WriteString("\033[J") | ||||
| 	if o.state == S_STATE_FAILING { | ||||
| 		buf.WriteString("failing ") | ||||
| 	} | ||||
| 	if o.dir == S_DIR_BCK { | ||||
| 		buf.WriteString("bck") | ||||
| 	} else if o.dir == S_DIR_FWD { | ||||
| 		buf.WriteString("fwd") | ||||
| 	} | ||||
| 	buf.WriteString("-i-search: ") | ||||
| 	buf.WriteString(string(o.data))         // keyword | ||||
| 	buf.WriteString("\033[4m \033[0m")      // _ | ||||
| 	fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev | ||||
| 	if x > 0 { | ||||
| 		fmt.Fprintf(buf, "\033[%dC", x) // move forward | ||||
| 	} | ||||
| 	o.w.Write(buf.Bytes()) | ||||
| } | ||||
							
								
								
									
										197
									
								
								vendor/github.com/chzyer/readline/std.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/github.com/chzyer/readline/std.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	Stdin  io.ReadCloser  = os.Stdin | ||||
| 	Stdout io.WriteCloser = os.Stdout | ||||
| 	Stderr io.WriteCloser = os.Stderr | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	std     *Instance | ||||
| 	stdOnce sync.Once | ||||
| ) | ||||
|  | ||||
| // global instance will not submit history automatic | ||||
| func getInstance() *Instance { | ||||
| 	stdOnce.Do(func() { | ||||
| 		std, _ = NewEx(&Config{ | ||||
| 			DisableAutoSaveHistory: true, | ||||
| 		}) | ||||
| 	}) | ||||
| 	return std | ||||
| } | ||||
|  | ||||
| // let readline load history from filepath | ||||
| // and try to persist history into disk | ||||
| // set fp to "" to prevent readline persisting history to disk | ||||
| // so the `AddHistory` will return nil error forever. | ||||
| func SetHistoryPath(fp string) { | ||||
| 	ins := getInstance() | ||||
| 	cfg := ins.Config.Clone() | ||||
| 	cfg.HistoryFile = fp | ||||
| 	ins.SetConfig(cfg) | ||||
| } | ||||
|  | ||||
| // set auto completer to global instance | ||||
| func SetAutoComplete(completer AutoCompleter) { | ||||
| 	ins := getInstance() | ||||
| 	cfg := ins.Config.Clone() | ||||
| 	cfg.AutoComplete = completer | ||||
| 	ins.SetConfig(cfg) | ||||
| } | ||||
|  | ||||
| // add history to global instance manually | ||||
| // raise error only if `SetHistoryPath` is set with a non-empty path | ||||
| func AddHistory(content string) error { | ||||
| 	ins := getInstance() | ||||
| 	return ins.SaveHistory(content) | ||||
| } | ||||
|  | ||||
| func Password(prompt string) ([]byte, error) { | ||||
| 	ins := getInstance() | ||||
| 	return ins.ReadPassword(prompt) | ||||
| } | ||||
|  | ||||
| // readline with global configs | ||||
| func Line(prompt string) (string, error) { | ||||
| 	ins := getInstance() | ||||
| 	ins.SetPrompt(prompt) | ||||
| 	return ins.Readline() | ||||
| } | ||||
|  | ||||
| type CancelableStdin struct { | ||||
| 	r      io.Reader | ||||
| 	mutex  sync.Mutex | ||||
| 	stop   chan struct{} | ||||
| 	closed int32 | ||||
| 	notify chan struct{} | ||||
| 	data   []byte | ||||
| 	read   int | ||||
| 	err    error | ||||
| } | ||||
|  | ||||
| func NewCancelableStdin(r io.Reader) *CancelableStdin { | ||||
| 	c := &CancelableStdin{ | ||||
| 		r:      r, | ||||
| 		notify: make(chan struct{}), | ||||
| 		stop:   make(chan struct{}), | ||||
| 	} | ||||
| 	go c.ioloop() | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| func (c *CancelableStdin) ioloop() { | ||||
| loop: | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-c.notify: | ||||
| 			c.read, c.err = c.r.Read(c.data) | ||||
| 			select { | ||||
| 			case c.notify <- struct{}{}: | ||||
| 			case <-c.stop: | ||||
| 				break loop | ||||
| 			} | ||||
| 		case <-c.stop: | ||||
| 			break loop | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *CancelableStdin) Read(b []byte) (n int, err error) { | ||||
| 	c.mutex.Lock() | ||||
| 	defer c.mutex.Unlock() | ||||
| 	if atomic.LoadInt32(&c.closed) == 1 { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
|  | ||||
| 	c.data = b | ||||
| 	select { | ||||
| 	case c.notify <- struct{}{}: | ||||
| 	case <-c.stop: | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
| 	select { | ||||
| 	case <-c.notify: | ||||
| 		return c.read, c.err | ||||
| 	case <-c.stop: | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *CancelableStdin) Close() error { | ||||
| 	if atomic.CompareAndSwapInt32(&c.closed, 0, 1) { | ||||
| 		close(c.stop) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // FillableStdin is a stdin reader which can prepend some data before | ||||
| // reading into the real stdin | ||||
| type FillableStdin struct { | ||||
| 	sync.Mutex | ||||
| 	stdin       io.Reader | ||||
| 	stdinBuffer io.ReadCloser | ||||
| 	buf         []byte | ||||
| 	bufErr      error | ||||
| } | ||||
|  | ||||
| // NewFillableStdin gives you FillableStdin | ||||
| func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) { | ||||
| 	r, w := io.Pipe() | ||||
| 	s := &FillableStdin{ | ||||
| 		stdinBuffer: r, | ||||
| 		stdin:       stdin, | ||||
| 	} | ||||
| 	s.ioloop() | ||||
| 	return s, w | ||||
| } | ||||
|  | ||||
| func (s *FillableStdin) ioloop() { | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			bufR := make([]byte, 100) | ||||
| 			var n int | ||||
| 			n, s.bufErr = s.stdinBuffer.Read(bufR) | ||||
| 			if s.bufErr != nil { | ||||
| 				if s.bufErr == io.ErrClosedPipe { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			s.Lock() | ||||
| 			s.buf = append(s.buf, bufR[:n]...) | ||||
| 			s.Unlock() | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| // Read will read from the local buffer and if no data, read from stdin | ||||
| func (s *FillableStdin) Read(p []byte) (n int, err error) { | ||||
| 	s.Lock() | ||||
| 	i := len(s.buf) | ||||
| 	if len(p) < i { | ||||
| 		i = len(p) | ||||
| 	} | ||||
| 	if i > 0 { | ||||
| 		n := copy(p, s.buf) | ||||
| 		s.buf = s.buf[:0] | ||||
| 		cerr := s.bufErr | ||||
| 		s.bufErr = nil | ||||
| 		s.Unlock() | ||||
| 		return n, cerr | ||||
| 	} | ||||
| 	s.Unlock() | ||||
| 	n, err = s.stdin.Read(p) | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func (s *FillableStdin) Close() error { | ||||
| 	s.stdinBuffer.Close() | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/chzyer/readline/std_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/chzyer/readline/std_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| // +build windows | ||||
|  | ||||
| package readline | ||||
|  | ||||
| func init() { | ||||
| 	Stdin = NewRawReader() | ||||
| 	Stdout = NewANSIWriter(Stdout) | ||||
| 	Stderr = NewANSIWriter(Stderr) | ||||
| } | ||||
							
								
								
									
										123
									
								
								vendor/github.com/chzyer/readline/term.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/chzyer/readline/term.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris | ||||
|  | ||||
| // Package terminal provides support functions for dealing with terminals, as | ||||
| // commonly found on UNIX systems. | ||||
| // | ||||
| // Putting a terminal into raw mode is the most common requirement: | ||||
| // | ||||
| // 	oldState, err := terminal.MakeRaw(0) | ||||
| // 	if err != nil { | ||||
| // 	        panic(err) | ||||
| // 	} | ||||
| // 	defer terminal.Restore(0, oldState) | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"syscall" | ||||
| ) | ||||
|  | ||||
| // State contains the state of a terminal. | ||||
| type State struct { | ||||
| 	termios Termios | ||||
| } | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal(fd int) bool { | ||||
| 	_, err := getTermios(fd) | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| // MakeRaw put the terminal connected to the given file descriptor into raw | ||||
| // mode and returns the previous state of the terminal so that it can be | ||||
| // restored. | ||||
| func MakeRaw(fd int) (*State, error) { | ||||
| 	var oldState State | ||||
|  | ||||
| 	if termios, err := getTermios(fd); err != nil { | ||||
| 		return nil, err | ||||
| 	} else { | ||||
| 		oldState.termios = *termios | ||||
| 	} | ||||
|  | ||||
| 	newState := oldState.termios | ||||
| 	// This attempts to replicate the behaviour documented for cfmakeraw in | ||||
| 	// the termios(3) manpage. | ||||
| 	newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON | ||||
| 	// newState.Oflag &^= syscall.OPOST | ||||
| 	newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN | ||||
| 	newState.Cflag &^= syscall.CSIZE | syscall.PARENB | ||||
| 	newState.Cflag |= syscall.CS8 | ||||
|  | ||||
| 	newState.Cc[syscall.VMIN] = 1 | ||||
| 	newState.Cc[syscall.VTIME] = 0 | ||||
|  | ||||
| 	return &oldState, setTermios(fd, &newState) | ||||
| } | ||||
|  | ||||
| // GetState returns the current state of a terminal which may be useful to | ||||
| // restore the terminal after a signal. | ||||
| func GetState(fd int) (*State, error) { | ||||
| 	termios, err := getTermios(fd) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &State{termios: *termios}, nil | ||||
| } | ||||
|  | ||||
| // Restore restores the terminal connected to the given file descriptor to a | ||||
| // previous state. | ||||
| func restoreTerm(fd int, state *State) error { | ||||
| 	return setTermios(fd, &state.termios) | ||||
| } | ||||
|  | ||||
| // ReadPassword reads a line of input from a terminal without local echo.  This | ||||
| // is commonly used for inputting passwords and other sensitive data. The slice | ||||
| // returned does not include the \n. | ||||
| func ReadPassword(fd int) ([]byte, error) { | ||||
| 	oldState, err := getTermios(fd) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	newState := oldState | ||||
| 	newState.Lflag &^= syscall.ECHO | ||||
| 	newState.Lflag |= syscall.ICANON | syscall.ISIG | ||||
| 	newState.Iflag |= syscall.ICRNL | ||||
| 	if err := setTermios(fd, newState); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		setTermios(fd, oldState) | ||||
| 	}() | ||||
|  | ||||
| 	var buf [16]byte | ||||
| 	var ret []byte | ||||
| 	for { | ||||
| 		n, err := syscall.Read(fd, buf[:]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if n == 0 { | ||||
| 			if len(ret) == 0 { | ||||
| 				return nil, io.EOF | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		if buf[n-1] == '\n' { | ||||
| 			n-- | ||||
| 		} | ||||
| 		ret = append(ret, buf[:n]...) | ||||
| 		if n < len(buf) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret, nil | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/chzyer/readline/term_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/chzyer/readline/term_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build darwin dragonfly freebsd netbsd openbsd | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| func getTermios(fd int) (*Termios, error) { | ||||
| 	termios := new(Termios) | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCGETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0) | ||||
| 	if err != 0 { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return termios, nil | ||||
| } | ||||
|  | ||||
| func setTermios(fd int, termios *Termios) error { | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCSETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0) | ||||
| 	if err != 0 { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/chzyer/readline/term_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/chzyer/readline/term_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // These constants are declared here, rather than importing | ||||
| // them from the syscall package as some syscall packages, even | ||||
| // on linux, for example gccgo, do not declare them. | ||||
| const ioctlReadTermios = 0x5401  // syscall.TCGETS | ||||
| const ioctlWriteTermios = 0x5402 // syscall.TCSETS | ||||
|  | ||||
| func getTermios(fd int) (*Termios, error) { | ||||
| 	termios := new(Termios) | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0) | ||||
| 	if err != 0 { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return termios, nil | ||||
| } | ||||
|  | ||||
| func setTermios(fd int, termios *Termios) error { | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0) | ||||
| 	if err != 0 { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										32
									
								
								vendor/github.com/chzyer/readline/term_nosyscall6.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/chzyer/readline/term_nosyscall6.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build aix os400 solaris | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| func GetSize(fd int) (int, int, error) { | ||||
| 	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) | ||||
| 	if err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
| 	return int(ws.Col), int(ws.Row), nil | ||||
| } | ||||
|  | ||||
| type Termios unix.Termios | ||||
|  | ||||
| func getTermios(fd int) (*Termios, error) { | ||||
| 	termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return (*Termios)(termios), nil | ||||
| } | ||||
|  | ||||
| func setTermios(fd int, termios *Termios) error { | ||||
| 	return unix.IoctlSetTermios(fd, unix.TCSETSF, (*unix.Termios)(termios)) | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/github.com/chzyer/readline/term_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/chzyer/readline/term_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build darwin dragonfly freebsd linux,!appengine netbsd openbsd | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| type Termios syscall.Termios | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| func GetSize(fd int) (int, int, error) { | ||||
| 	var dimensions [4]uint16 | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0) | ||||
| 	if err != 0 { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
| 	return int(dimensions[1]), int(dimensions[0]), nil | ||||
| } | ||||
							
								
								
									
										171
									
								
								vendor/github.com/chzyer/readline/term_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								vendor/github.com/chzyer/readline/term_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build windows | ||||
|  | ||||
| // Package terminal provides support functions for dealing with terminals, as | ||||
| // commonly found on UNIX systems. | ||||
| // | ||||
| // Putting a terminal into raw mode is the most common requirement: | ||||
| // | ||||
| // 	oldState, err := terminal.MakeRaw(0) | ||||
| // 	if err != nil { | ||||
| // 	        panic(err) | ||||
| // 	} | ||||
| // 	defer terminal.Restore(0, oldState) | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	enableLineInput       = 2 | ||||
| 	enableEchoInput       = 4 | ||||
| 	enableProcessedInput  = 1 | ||||
| 	enableWindowInput     = 8 | ||||
| 	enableMouseInput      = 16 | ||||
| 	enableInsertMode      = 32 | ||||
| 	enableQuickEditMode   = 64 | ||||
| 	enableExtendedFlags   = 128 | ||||
| 	enableAutoPosition    = 256 | ||||
| 	enableProcessedOutput = 1 | ||||
| 	enableWrapAtEolOutput = 2 | ||||
| ) | ||||
|  | ||||
| var kernel32 = syscall.NewLazyDLL("kernel32.dll") | ||||
|  | ||||
| var ( | ||||
| 	procGetConsoleMode             = kernel32.NewProc("GetConsoleMode") | ||||
| 	procSetConsoleMode             = kernel32.NewProc("SetConsoleMode") | ||||
| 	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	coord struct { | ||||
| 		x short | ||||
| 		y short | ||||
| 	} | ||||
| 	smallRect struct { | ||||
| 		left   short | ||||
| 		top    short | ||||
| 		right  short | ||||
| 		bottom short | ||||
| 	} | ||||
| 	consoleScreenBufferInfo struct { | ||||
| 		size              coord | ||||
| 		cursorPosition    coord | ||||
| 		attributes        word | ||||
| 		window            smallRect | ||||
| 		maximumWindowSize coord | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| type State struct { | ||||
| 	mode uint32 | ||||
| } | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal(fd int) bool { | ||||
| 	var st uint32 | ||||
| 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	return r != 0 && e == 0 | ||||
| } | ||||
|  | ||||
| // MakeRaw put the terminal connected to the given file descriptor into raw | ||||
| // mode and returns the previous state of the terminal so that it can be | ||||
| // restored. | ||||
| func MakeRaw(fd int) (*State, error) { | ||||
| 	var st uint32 | ||||
| 	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	if e != 0 { | ||||
| 		return nil, error(e) | ||||
| 	} | ||||
| 	raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) | ||||
| 	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0) | ||||
| 	if e != 0 { | ||||
| 		return nil, error(e) | ||||
| 	} | ||||
| 	return &State{st}, nil | ||||
| } | ||||
|  | ||||
| // GetState returns the current state of a terminal which may be useful to | ||||
| // restore the terminal after a signal. | ||||
| func GetState(fd int) (*State, error) { | ||||
| 	var st uint32 | ||||
| 	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	if e != 0 { | ||||
| 		return nil, error(e) | ||||
| 	} | ||||
| 	return &State{st}, nil | ||||
| } | ||||
|  | ||||
| // Restore restores the terminal connected to the given file descriptor to a | ||||
| // previous state. | ||||
| func restoreTerm(fd int, state *State) error { | ||||
| 	_, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // GetSize returns the dimensions of the given terminal. | ||||
| func GetSize(fd int) (width, height int, err error) { | ||||
| 	var info consoleScreenBufferInfo | ||||
| 	_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) | ||||
| 	if e != 0 { | ||||
| 		return 0, 0, error(e) | ||||
| 	} | ||||
| 	return int(info.size.x), int(info.size.y), nil | ||||
| } | ||||
|  | ||||
| // ReadPassword reads a line of input from a terminal without local echo.  This | ||||
| // is commonly used for inputting passwords and other sensitive data. The slice | ||||
| // returned does not include the \n. | ||||
| func ReadPassword(fd int) ([]byte, error) { | ||||
| 	var st uint32 | ||||
| 	_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	if e != 0 { | ||||
| 		return nil, error(e) | ||||
| 	} | ||||
| 	old := st | ||||
|  | ||||
| 	st &^= (enableEchoInput) | ||||
| 	st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) | ||||
| 	_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) | ||||
| 	if e != 0 { | ||||
| 		return nil, error(e) | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) | ||||
| 	}() | ||||
|  | ||||
| 	var buf [16]byte | ||||
| 	var ret []byte | ||||
| 	for { | ||||
| 		n, err := syscall.Read(syscall.Handle(fd), buf[:]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if n == 0 { | ||||
| 			if len(ret) == 0 { | ||||
| 				return nil, io.EOF | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		if buf[n-1] == '\n' { | ||||
| 			n-- | ||||
| 		} | ||||
| 		if n > 0 && buf[n-1] == '\r' { | ||||
| 			n-- | ||||
| 		} | ||||
| 		ret = append(ret, buf[:n]...) | ||||
| 		if n < len(buf) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret, nil | ||||
| } | ||||
							
								
								
									
										254
									
								
								vendor/github.com/chzyer/readline/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								vendor/github.com/chzyer/readline/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,254 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
|  | ||||
| type Terminal struct { | ||||
| 	m         sync.Mutex | ||||
| 	cfg       *Config | ||||
| 	outchan   chan rune | ||||
| 	closed    int32 | ||||
| 	stopChan  chan struct{} | ||||
| 	kickChan  chan struct{} | ||||
| 	wg        sync.WaitGroup | ||||
| 	isReading int32 | ||||
| 	sleeping  int32 | ||||
|  | ||||
| 	sizeChan chan string | ||||
| } | ||||
|  | ||||
| func NewTerminal(cfg *Config) (*Terminal, error) { | ||||
| 	if err := cfg.Init(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	t := &Terminal{ | ||||
| 		cfg:      cfg, | ||||
| 		kickChan: make(chan struct{}, 1), | ||||
| 		outchan:  make(chan rune), | ||||
| 		stopChan: make(chan struct{}, 1), | ||||
| 		sizeChan: make(chan string, 1), | ||||
| 	} | ||||
|  | ||||
| 	go t.ioloop() | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| // SleepToResume will sleep myself, and return only if I'm resumed. | ||||
| func (t *Terminal) SleepToResume() { | ||||
| 	if !atomic.CompareAndSwapInt32(&t.sleeping, 0, 1) { | ||||
| 		return | ||||
| 	} | ||||
| 	defer atomic.StoreInt32(&t.sleeping, 0) | ||||
|  | ||||
| 	t.ExitRawMode() | ||||
| 	ch := WaitForResume() | ||||
| 	SuspendMe() | ||||
| 	<-ch | ||||
| 	t.EnterRawMode() | ||||
| } | ||||
|  | ||||
| func (t *Terminal) EnterRawMode() (err error) { | ||||
| 	return t.cfg.FuncMakeRaw() | ||||
| } | ||||
|  | ||||
| func (t *Terminal) ExitRawMode() (err error) { | ||||
| 	return t.cfg.FuncExitRaw() | ||||
| } | ||||
|  | ||||
| func (t *Terminal) Write(b []byte) (int, error) { | ||||
| 	return t.cfg.Stdout.Write(b) | ||||
| } | ||||
|  | ||||
| // WriteStdin prefill the next Stdin fetch | ||||
| // Next time you call ReadLine() this value will be writen before the user input | ||||
| func (t *Terminal) WriteStdin(b []byte) (int, error) { | ||||
| 	return t.cfg.StdinWriter.Write(b) | ||||
| } | ||||
|  | ||||
| type termSize struct { | ||||
| 	left int | ||||
| 	top  int | ||||
| } | ||||
|  | ||||
| func (t *Terminal) GetOffset(f func(offset string)) { | ||||
| 	go func() { | ||||
| 		f(<-t.sizeChan) | ||||
| 	}() | ||||
| 	t.Write([]byte("\033[6n")) | ||||
| } | ||||
|  | ||||
| func (t *Terminal) Print(s string) { | ||||
| 	fmt.Fprintf(t.cfg.Stdout, "%s", s) | ||||
| } | ||||
|  | ||||
| func (t *Terminal) PrintRune(r rune) { | ||||
| 	fmt.Fprintf(t.cfg.Stdout, "%c", r) | ||||
| } | ||||
|  | ||||
| func (t *Terminal) Readline() *Operation { | ||||
| 	return NewOperation(t, t.cfg) | ||||
| } | ||||
|  | ||||
| // return rune(0) if meet EOF | ||||
| func (t *Terminal) ReadRune() rune { | ||||
| 	ch, ok := <-t.outchan | ||||
| 	if !ok { | ||||
| 		return rune(0) | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| func (t *Terminal) IsReading() bool { | ||||
| 	return atomic.LoadInt32(&t.isReading) == 1 | ||||
| } | ||||
|  | ||||
| func (t *Terminal) KickRead() { | ||||
| 	select { | ||||
| 	case t.kickChan <- struct{}{}: | ||||
| 	default: | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *Terminal) ioloop() { | ||||
| 	t.wg.Add(1) | ||||
| 	defer func() { | ||||
| 		t.wg.Done() | ||||
| 		close(t.outchan) | ||||
| 	}() | ||||
|  | ||||
| 	var ( | ||||
| 		isEscape       bool | ||||
| 		isEscapeEx     bool | ||||
| 		isEscapeSS3    bool | ||||
| 		expectNextChar bool | ||||
| 	) | ||||
|  | ||||
| 	buf := bufio.NewReader(t.getStdin()) | ||||
| 	for { | ||||
| 		if !expectNextChar { | ||||
| 			atomic.StoreInt32(&t.isReading, 0) | ||||
| 			select { | ||||
| 			case <-t.kickChan: | ||||
| 				atomic.StoreInt32(&t.isReading, 1) | ||||
| 			case <-t.stopChan: | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 		expectNextChar = false | ||||
| 		r, _, err := buf.ReadRune() | ||||
| 		if err != nil { | ||||
| 			if strings.Contains(err.Error(), "interrupted system call") { | ||||
| 				expectNextChar = true | ||||
| 				continue | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		if isEscape { | ||||
| 			isEscape = false | ||||
| 			if r == CharEscapeEx { | ||||
| 				// ^][ | ||||
| 				expectNextChar = true | ||||
| 				isEscapeEx = true | ||||
| 				continue | ||||
| 			} else if r == CharO { | ||||
| 				// ^]O | ||||
| 				expectNextChar = true | ||||
| 				isEscapeSS3 = true | ||||
| 				continue | ||||
| 			} | ||||
| 			r = escapeKey(r, buf) | ||||
| 		} else if isEscapeEx { | ||||
| 			isEscapeEx = false | ||||
| 			if key := readEscKey(r, buf); key != nil { | ||||
| 				r = escapeExKey(key) | ||||
| 				// offset | ||||
| 				if key.typ == 'R' { | ||||
| 					if _, _, ok := key.Get2(); ok { | ||||
| 						select { | ||||
| 						case t.sizeChan <- key.attr: | ||||
| 						default: | ||||
| 						} | ||||
| 					} | ||||
| 					expectNextChar = true | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			if r == 0 { | ||||
| 				expectNextChar = true | ||||
| 				continue | ||||
| 			} | ||||
| 		} else if isEscapeSS3 { | ||||
| 			isEscapeSS3 = false | ||||
| 			if key := readEscKey(r, buf); key != nil { | ||||
| 				r = escapeSS3Key(key) | ||||
| 			} | ||||
| 			if r == 0 { | ||||
| 				expectNextChar = true | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		expectNextChar = true | ||||
| 		switch r { | ||||
| 		case CharEsc: | ||||
| 			if t.cfg.VimMode { | ||||
| 				t.outchan <- r | ||||
| 				break | ||||
| 			} | ||||
| 			isEscape = true | ||||
| 		case CharInterrupt, CharEnter, CharCtrlJ, CharDelete: | ||||
| 			expectNextChar = false | ||||
| 			fallthrough | ||||
| 		default: | ||||
| 			t.outchan <- r | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func (t *Terminal) Bell() { | ||||
| 	fmt.Fprintf(t, "%c", CharBell) | ||||
| } | ||||
|  | ||||
| func (t *Terminal) Close() error { | ||||
| 	if atomic.SwapInt32(&t.closed, 1) != 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if closer, ok := t.cfg.Stdin.(io.Closer); ok { | ||||
| 		closer.Close() | ||||
| 	} | ||||
| 	close(t.stopChan) | ||||
| 	t.wg.Wait() | ||||
| 	return t.ExitRawMode() | ||||
| } | ||||
|  | ||||
| func (t *Terminal) GetConfig() *Config { | ||||
| 	t.m.Lock() | ||||
| 	cfg := *t.cfg | ||||
| 	t.m.Unlock() | ||||
| 	return &cfg | ||||
| } | ||||
|  | ||||
| func (t *Terminal) getStdin() io.Reader { | ||||
| 	t.m.Lock() | ||||
| 	r := t.cfg.Stdin | ||||
| 	t.m.Unlock() | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| func (t *Terminal) SetConfig(c *Config) error { | ||||
| 	if err := c.Init(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	t.m.Lock() | ||||
| 	t.cfg = c | ||||
| 	t.m.Unlock() | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										311
									
								
								vendor/github.com/chzyer/readline/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								vendor/github.com/chzyer/readline/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"container/list" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 	"unicode" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	isWindows = false | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	CharLineStart = 1 | ||||
| 	CharBackward  = 2 | ||||
| 	CharInterrupt = 3 | ||||
| 	CharDelete    = 4 | ||||
| 	CharLineEnd   = 5 | ||||
| 	CharForward   = 6 | ||||
| 	CharBell      = 7 | ||||
| 	CharCtrlH     = 8 | ||||
| 	CharTab       = 9 | ||||
| 	CharCtrlJ     = 10 | ||||
| 	CharKill      = 11 | ||||
| 	CharCtrlL     = 12 | ||||
| 	CharEnter     = 13 | ||||
| 	CharNext      = 14 | ||||
| 	CharPrev      = 16 | ||||
| 	CharBckSearch = 18 | ||||
| 	CharFwdSearch = 19 | ||||
| 	CharTranspose = 20 | ||||
| 	CharCtrlU     = 21 | ||||
| 	CharCtrlW     = 23 | ||||
| 	CharCtrlY     = 25 | ||||
| 	CharCtrlZ     = 26 | ||||
| 	CharEsc       = 27 | ||||
| 	CharO         = 79 | ||||
| 	CharEscapeEx  = 91 | ||||
| 	CharBackspace = 127 | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	MetaBackward rune = -iota - 1 | ||||
| 	MetaForward | ||||
| 	MetaDelete | ||||
| 	MetaBackspace | ||||
| 	MetaTranspose | ||||
| ) | ||||
|  | ||||
| // WaitForResume need to call before current process got suspend. | ||||
| // It will run a ticker until a long duration is occurs, | ||||
| // which means this process is resumed. | ||||
| func WaitForResume() chan struct{} { | ||||
| 	ch := make(chan struct{}) | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		ticker := time.NewTicker(10 * time.Millisecond) | ||||
| 		t := time.Now() | ||||
| 		wg.Done() | ||||
| 		for { | ||||
| 			now := <-ticker.C | ||||
| 			if now.Sub(t) > 100*time.Millisecond { | ||||
| 				break | ||||
| 			} | ||||
| 			t = now | ||||
| 		} | ||||
| 		ticker.Stop() | ||||
| 		ch <- struct{}{} | ||||
| 	}() | ||||
| 	wg.Wait() | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| func Restore(fd int, state *State) error { | ||||
| 	err := restoreTerm(fd, state) | ||||
| 	if err != nil { | ||||
| 		// errno 0 means everything is ok :) | ||||
| 		if err.Error() == "errno 0" { | ||||
| 			return nil | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func IsPrintable(key rune) bool { | ||||
| 	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff | ||||
| 	return key >= 32 && !isInSurrogateArea | ||||
| } | ||||
|  | ||||
| // translate Esc[X | ||||
| func escapeExKey(key *escapeKeyPair) rune { | ||||
| 	var r rune | ||||
| 	switch key.typ { | ||||
| 	case 'D': | ||||
| 		r = CharBackward | ||||
| 	case 'C': | ||||
| 		r = CharForward | ||||
| 	case 'A': | ||||
| 		r = CharPrev | ||||
| 	case 'B': | ||||
| 		r = CharNext | ||||
| 	case 'H': | ||||
| 		r = CharLineStart | ||||
| 	case 'F': | ||||
| 		r = CharLineEnd | ||||
| 	case '~': | ||||
| 		if key.attr == "3" { | ||||
| 			r = CharDelete | ||||
| 		} | ||||
| 	default: | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // translate EscOX SS3 codes for up/down/etc. | ||||
| func escapeSS3Key(key *escapeKeyPair) rune { | ||||
| 	var r rune | ||||
| 	switch key.typ { | ||||
| 	case 'D': | ||||
| 		r = CharBackward | ||||
| 	case 'C': | ||||
| 		r = CharForward | ||||
| 	case 'A': | ||||
| 		r = CharPrev | ||||
| 	case 'B': | ||||
| 		r = CharNext | ||||
| 	case 'H': | ||||
| 		r = CharLineStart | ||||
| 	case 'F': | ||||
| 		r = CharLineEnd | ||||
| 	default: | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| type escapeKeyPair struct { | ||||
| 	attr string | ||||
| 	typ  rune | ||||
| } | ||||
|  | ||||
| func (e *escapeKeyPair) Get2() (int, int, bool) { | ||||
| 	sp := strings.Split(e.attr, ";") | ||||
| 	if len(sp) < 2 { | ||||
| 		return -1, -1, false | ||||
| 	} | ||||
| 	s1, err := strconv.Atoi(sp[0]) | ||||
| 	if err != nil { | ||||
| 		return -1, -1, false | ||||
| 	} | ||||
| 	s2, err := strconv.Atoi(sp[1]) | ||||
| 	if err != nil { | ||||
| 		return -1, -1, false | ||||
| 	} | ||||
| 	return s1, s2, true | ||||
| } | ||||
|  | ||||
| func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair { | ||||
| 	p := escapeKeyPair{} | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	for { | ||||
| 		if r == ';' { | ||||
| 		} else if unicode.IsNumber(r) { | ||||
| 		} else { | ||||
| 			p.typ = r | ||||
| 			break | ||||
| 		} | ||||
| 		buf.WriteRune(r) | ||||
| 		r, _, _ = reader.ReadRune() | ||||
| 	} | ||||
| 	p.attr = buf.String() | ||||
| 	return &p | ||||
| } | ||||
|  | ||||
| // translate EscX to Meta+X | ||||
| func escapeKey(r rune, reader *bufio.Reader) rune { | ||||
| 	switch r { | ||||
| 	case 'b': | ||||
| 		r = MetaBackward | ||||
| 	case 'f': | ||||
| 		r = MetaForward | ||||
| 	case 'd': | ||||
| 		r = MetaDelete | ||||
| 	case CharTranspose: | ||||
| 		r = MetaTranspose | ||||
| 	case CharBackspace: | ||||
| 		r = MetaBackspace | ||||
| 	case 'O': | ||||
| 		d, _, _ := reader.ReadRune() | ||||
| 		switch d { | ||||
| 		case 'H': | ||||
| 			r = CharLineStart | ||||
| 		case 'F': | ||||
| 			r = CharLineEnd | ||||
| 		default: | ||||
| 			reader.UnreadRune() | ||||
| 		} | ||||
| 	case CharEsc: | ||||
|  | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| func SplitByLine(start, screenWidth int, rs []rune) []string { | ||||
| 	var ret []string | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	currentWidth := start | ||||
| 	for _, r := range rs { | ||||
| 		w := runes.Width(r) | ||||
| 		currentWidth += w | ||||
| 		buf.WriteRune(r) | ||||
| 		if currentWidth >= screenWidth { | ||||
| 			ret = append(ret, buf.String()) | ||||
| 			buf.Reset() | ||||
| 			currentWidth = 0 | ||||
| 		} | ||||
| 	} | ||||
| 	ret = append(ret, buf.String()) | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| // calculate how many lines for N character | ||||
| func LineCount(screenWidth, w int) int { | ||||
| 	r := w / screenWidth | ||||
| 	if w%screenWidth != 0 { | ||||
| 		r++ | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| func IsWordBreak(i rune) bool { | ||||
| 	switch { | ||||
| 	case i >= 'a' && i <= 'z': | ||||
| 	case i >= 'A' && i <= 'Z': | ||||
| 	case i >= '0' && i <= '9': | ||||
| 	default: | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func GetInt(s []string, def int) int { | ||||
| 	if len(s) == 0 { | ||||
| 		return def | ||||
| 	} | ||||
| 	c, err := strconv.Atoi(s[0]) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| type RawMode struct { | ||||
| 	state *State | ||||
| } | ||||
|  | ||||
| func (r *RawMode) Enter() (err error) { | ||||
| 	r.state, err = MakeRaw(GetStdin()) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (r *RawMode) Exit() error { | ||||
| 	if r.state == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return Restore(GetStdin(), r.state) | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| func sleep(n int) { | ||||
| 	Debug(n) | ||||
| 	time.Sleep(2000 * time.Millisecond) | ||||
| } | ||||
|  | ||||
| // print a linked list to Debug() | ||||
| func debugList(l *list.List) { | ||||
| 	idx := 0 | ||||
| 	for e := l.Front(); e != nil; e = e.Next() { | ||||
| 		Debug(idx, fmt.Sprintf("%+v", e.Value)) | ||||
| 		idx++ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // append log info to another file | ||||
| func Debug(o ...interface{}) { | ||||
| 	f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) | ||||
| 	fmt.Fprintln(f, o...) | ||||
| 	f.Close() | ||||
| } | ||||
|  | ||||
| func CaptureExitSignal(f func()) { | ||||
| 	cSignal := make(chan os.Signal, 1) | ||||
| 	signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) | ||||
| 	go func() { | ||||
| 		for range cSignal { | ||||
| 			f() | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
							
								
								
									
										83
									
								
								vendor/github.com/chzyer/readline/utils_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/chzyer/readline/utils_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| // +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| ) | ||||
|  | ||||
| type winsize struct { | ||||
| 	Row    uint16 | ||||
| 	Col    uint16 | ||||
| 	Xpixel uint16 | ||||
| 	Ypixel uint16 | ||||
| } | ||||
|  | ||||
| // SuspendMe use to send suspend signal to myself, when we in the raw mode. | ||||
| // For OSX it need to send to parent's pid | ||||
| // For Linux it need to send to myself | ||||
| func SuspendMe() { | ||||
| 	p, _ := os.FindProcess(os.Getppid()) | ||||
| 	p.Signal(syscall.SIGTSTP) | ||||
| 	p, _ = os.FindProcess(os.Getpid()) | ||||
| 	p.Signal(syscall.SIGTSTP) | ||||
| } | ||||
|  | ||||
| // get width of the terminal | ||||
| func getWidth(stdoutFd int) int { | ||||
| 	cols, _, err := GetSize(stdoutFd) | ||||
| 	if err != nil { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return cols | ||||
| } | ||||
|  | ||||
| func GetScreenWidth() int { | ||||
| 	w := getWidth(syscall.Stdout) | ||||
| 	if w < 0 { | ||||
| 		w = getWidth(syscall.Stderr) | ||||
| 	} | ||||
| 	return w | ||||
| } | ||||
|  | ||||
| // ClearScreen clears the console screen | ||||
| func ClearScreen(w io.Writer) (int, error) { | ||||
| 	return w.Write([]byte("\033[H")) | ||||
| } | ||||
|  | ||||
| func DefaultIsTerminal() bool { | ||||
| 	return IsTerminal(syscall.Stdin) && (IsTerminal(syscall.Stdout) || IsTerminal(syscall.Stderr)) | ||||
| } | ||||
|  | ||||
| func GetStdin() int { | ||||
| 	return syscall.Stdin | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| var ( | ||||
| 	widthChange         sync.Once | ||||
| 	widthChangeCallback func() | ||||
| ) | ||||
|  | ||||
| func DefaultOnWidthChanged(f func()) { | ||||
| 	widthChangeCallback = f | ||||
| 	widthChange.Do(func() { | ||||
| 		ch := make(chan os.Signal, 1) | ||||
| 		signal.Notify(ch, syscall.SIGWINCH) | ||||
|  | ||||
| 		go func() { | ||||
| 			for { | ||||
| 				_, ok := <-ch | ||||
| 				if !ok { | ||||
| 					break | ||||
| 				} | ||||
| 				widthChangeCallback() | ||||
| 			} | ||||
| 		}() | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										41
									
								
								vendor/github.com/chzyer/readline/utils_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/chzyer/readline/utils_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| // +build windows | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"syscall" | ||||
| ) | ||||
|  | ||||
| func SuspendMe() { | ||||
| } | ||||
|  | ||||
| func GetStdin() int { | ||||
| 	return int(syscall.Stdin) | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	isWindows = true | ||||
| } | ||||
|  | ||||
| // get width of the terminal | ||||
| func GetScreenWidth() int { | ||||
| 	info, _ := GetConsoleScreenBufferInfo() | ||||
| 	if info == nil { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return int(info.dwSize.x) | ||||
| } | ||||
|  | ||||
| // ClearScreen clears the console screen | ||||
| func ClearScreen(_ io.Writer) error { | ||||
| 	return SetConsoleCursorPosition(&_COORD{0, 0}) | ||||
| } | ||||
|  | ||||
| func DefaultIsTerminal() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func DefaultOnWidthChanged(func()) { | ||||
|  | ||||
| } | ||||
							
								
								
									
										176
									
								
								vendor/github.com/chzyer/readline/vim.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								vendor/github.com/chzyer/readline/vim.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| package readline | ||||
|  | ||||
| const ( | ||||
| 	VIM_NORMAL = iota | ||||
| 	VIM_INSERT | ||||
| 	VIM_VISUAL | ||||
| ) | ||||
|  | ||||
| type opVim struct { | ||||
| 	cfg     *Config | ||||
| 	op      *Operation | ||||
| 	vimMode int | ||||
| } | ||||
|  | ||||
| func newVimMode(op *Operation) *opVim { | ||||
| 	ov := &opVim{ | ||||
| 		cfg: op.cfg, | ||||
| 		op:  op, | ||||
| 	} | ||||
| 	ov.SetVimMode(ov.cfg.VimMode) | ||||
| 	return ov | ||||
| } | ||||
|  | ||||
| func (o *opVim) SetVimMode(on bool) { | ||||
| 	if o.cfg.VimMode && !on { // turn off | ||||
| 		o.ExitVimMode() | ||||
| 	} | ||||
| 	o.cfg.VimMode = on | ||||
| 	o.vimMode = VIM_INSERT | ||||
| } | ||||
|  | ||||
| func (o *opVim) ExitVimMode() { | ||||
| 	o.vimMode = VIM_INSERT | ||||
| } | ||||
|  | ||||
| func (o *opVim) IsEnableVimMode() bool { | ||||
| 	return o.cfg.VimMode | ||||
| } | ||||
|  | ||||
| func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) (t rune, handled bool) { | ||||
| 	rb := o.op.buf | ||||
| 	handled = true | ||||
| 	switch r { | ||||
| 	case 'h': | ||||
| 		t = CharBackward | ||||
| 	case 'j': | ||||
| 		t = CharNext | ||||
| 	case 'k': | ||||
| 		t = CharPrev | ||||
| 	case 'l': | ||||
| 		t = CharForward | ||||
| 	case '0', '^': | ||||
| 		rb.MoveToLineStart() | ||||
| 	case '$': | ||||
| 		rb.MoveToLineEnd() | ||||
| 	case 'x': | ||||
| 		rb.Delete() | ||||
| 		if rb.IsCursorInEnd() { | ||||
| 			rb.MoveBackward() | ||||
| 		} | ||||
| 	case 'r': | ||||
| 		rb.Replace(readNext()) | ||||
| 	case 'd': | ||||
| 		next := readNext() | ||||
| 		switch next { | ||||
| 		case 'd': | ||||
| 			rb.Erase() | ||||
| 		case 'w': | ||||
| 			rb.DeleteWord() | ||||
| 		case 'h': | ||||
| 			rb.Backspace() | ||||
| 		case 'l': | ||||
| 			rb.Delete() | ||||
| 		} | ||||
| 	case 'p': | ||||
| 		rb.Yank() | ||||
| 	case 'b', 'B': | ||||
| 		rb.MoveToPrevWord() | ||||
| 	case 'w', 'W': | ||||
| 		rb.MoveToNextWord() | ||||
| 	case 'e', 'E': | ||||
| 		rb.MoveToEndWord() | ||||
| 	case 'f', 'F', 't', 'T': | ||||
| 		next := readNext() | ||||
| 		prevChar := r == 't' || r == 'T' | ||||
| 		reverse := r == 'F' || r == 'T' | ||||
| 		switch next { | ||||
| 		case CharEsc: | ||||
| 		default: | ||||
| 			rb.MoveTo(next, prevChar, reverse) | ||||
| 		} | ||||
| 	default: | ||||
| 		return r, false | ||||
| 	} | ||||
| 	return t, true | ||||
| } | ||||
|  | ||||
| func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) { | ||||
| 	rb := o.op.buf | ||||
| 	handled = true | ||||
| 	switch r { | ||||
| 	case 'i': | ||||
| 	case 'I': | ||||
| 		rb.MoveToLineStart() | ||||
| 	case 'a': | ||||
| 		rb.MoveForward() | ||||
| 	case 'A': | ||||
| 		rb.MoveToLineEnd() | ||||
| 	case 's': | ||||
| 		rb.Delete() | ||||
| 	case 'S': | ||||
| 		rb.Erase() | ||||
| 	case 'c': | ||||
| 		next := readNext() | ||||
| 		switch next { | ||||
| 		case 'c': | ||||
| 			rb.Erase() | ||||
| 		case 'w': | ||||
| 			rb.DeleteWord() | ||||
| 		case 'h': | ||||
| 			rb.Backspace() | ||||
| 		case 'l': | ||||
| 			rb.Delete() | ||||
| 		} | ||||
| 	default: | ||||
| 		return r, false | ||||
| 	} | ||||
|  | ||||
| 	o.EnterVimInsertMode() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune) { | ||||
| 	switch r { | ||||
| 	case CharEnter, CharInterrupt: | ||||
| 		o.ExitVimMode() | ||||
| 		return r | ||||
| 	} | ||||
|  | ||||
| 	if r, handled := o.handleVimNormalMovement(r, readNext); handled { | ||||
| 		return r | ||||
| 	} | ||||
|  | ||||
| 	if r, handled := o.handleVimNormalEnterInsert(r, readNext); handled { | ||||
| 		return r | ||||
| 	} | ||||
|  | ||||
| 	// invalid operation | ||||
| 	o.op.t.Bell() | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (o *opVim) EnterVimInsertMode() { | ||||
| 	o.vimMode = VIM_INSERT | ||||
| } | ||||
|  | ||||
| func (o *opVim) ExitVimInsertMode() { | ||||
| 	o.vimMode = VIM_NORMAL | ||||
| } | ||||
|  | ||||
| func (o *opVim) HandleVim(r rune, readNext func() rune) rune { | ||||
| 	if o.vimMode == VIM_NORMAL { | ||||
| 		return o.HandleVimNormal(r, readNext) | ||||
| 	} | ||||
| 	if r == CharEsc { | ||||
| 		o.ExitVimInsertMode() | ||||
| 		return 0 | ||||
| 	} | ||||
|  | ||||
| 	switch o.vimMode { | ||||
| 	case VIM_INSERT: | ||||
| 		return r | ||||
| 	case VIM_VISUAL: | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
							
								
								
									
										152
									
								
								vendor/github.com/chzyer/readline/windows_api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/chzyer/readline/windows_api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // +build windows | ||||
|  | ||||
| package readline | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	kernel = NewKernel() | ||||
| 	stdout = uintptr(syscall.Stdout) | ||||
| 	stdin  = uintptr(syscall.Stdin) | ||||
| ) | ||||
|  | ||||
| type Kernel struct { | ||||
| 	SetConsoleCursorPosition, | ||||
| 	SetConsoleTextAttribute, | ||||
| 	FillConsoleOutputCharacterW, | ||||
| 	FillConsoleOutputAttribute, | ||||
| 	ReadConsoleInputW, | ||||
| 	GetConsoleScreenBufferInfo, | ||||
| 	GetConsoleCursorInfo, | ||||
| 	GetStdHandle CallFunc | ||||
| } | ||||
|  | ||||
| type short int16 | ||||
| type word uint16 | ||||
| type dword uint32 | ||||
| type wchar uint16 | ||||
|  | ||||
| type _COORD struct { | ||||
| 	x short | ||||
| 	y short | ||||
| } | ||||
|  | ||||
| func (c *_COORD) ptr() uintptr { | ||||
| 	return uintptr(*(*int32)(unsafe.Pointer(c))) | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	EVENT_KEY                = 0x0001 | ||||
| 	EVENT_MOUSE              = 0x0002 | ||||
| 	EVENT_WINDOW_BUFFER_SIZE = 0x0004 | ||||
| 	EVENT_MENU               = 0x0008 | ||||
| 	EVENT_FOCUS              = 0x0010 | ||||
| ) | ||||
|  | ||||
| type _KEY_EVENT_RECORD struct { | ||||
| 	bKeyDown          int32 | ||||
| 	wRepeatCount      word | ||||
| 	wVirtualKeyCode   word | ||||
| 	wVirtualScanCode  word | ||||
| 	unicodeChar       wchar | ||||
| 	dwControlKeyState dword | ||||
| } | ||||
|  | ||||
| // KEY_EVENT_RECORD          KeyEvent; | ||||
| // MOUSE_EVENT_RECORD        MouseEvent; | ||||
| // WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; | ||||
| // MENU_EVENT_RECORD         MenuEvent; | ||||
| // FOCUS_EVENT_RECORD        FocusEvent; | ||||
| type _INPUT_RECORD struct { | ||||
| 	EventType word | ||||
| 	Padding   uint16 | ||||
| 	Event     [16]byte | ||||
| } | ||||
|  | ||||
| type _CONSOLE_SCREEN_BUFFER_INFO struct { | ||||
| 	dwSize              _COORD | ||||
| 	dwCursorPosition    _COORD | ||||
| 	wAttributes         word | ||||
| 	srWindow            _SMALL_RECT | ||||
| 	dwMaximumWindowSize _COORD | ||||
| } | ||||
|  | ||||
| type _SMALL_RECT struct { | ||||
| 	left   short | ||||
| 	top    short | ||||
| 	right  short | ||||
| 	bottom short | ||||
| } | ||||
|  | ||||
| type _CONSOLE_CURSOR_INFO struct { | ||||
| 	dwSize   dword | ||||
| 	bVisible bool | ||||
| } | ||||
|  | ||||
| type CallFunc func(u ...uintptr) error | ||||
|  | ||||
| func NewKernel() *Kernel { | ||||
| 	k := &Kernel{} | ||||
| 	kernel32 := syscall.NewLazyDLL("kernel32.dll") | ||||
| 	v := reflect.ValueOf(k).Elem() | ||||
| 	t := v.Type() | ||||
| 	for i := 0; i < t.NumField(); i++ { | ||||
| 		name := t.Field(i).Name | ||||
| 		f := kernel32.NewProc(name) | ||||
| 		v.Field(i).Set(reflect.ValueOf(k.Wrap(f))) | ||||
| 	} | ||||
| 	return k | ||||
| } | ||||
|  | ||||
| func (k *Kernel) Wrap(p *syscall.LazyProc) CallFunc { | ||||
| 	return func(args ...uintptr) error { | ||||
| 		var r0 uintptr | ||||
| 		var e1 syscall.Errno | ||||
| 		size := uintptr(len(args)) | ||||
| 		if len(args) <= 3 { | ||||
| 			buf := make([]uintptr, 3) | ||||
| 			copy(buf, args) | ||||
| 			r0, _, e1 = syscall.Syscall(p.Addr(), size, | ||||
| 				buf[0], buf[1], buf[2]) | ||||
| 		} else { | ||||
| 			buf := make([]uintptr, 6) | ||||
| 			copy(buf, args) | ||||
| 			r0, _, e1 = syscall.Syscall6(p.Addr(), size, | ||||
| 				buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], | ||||
| 			) | ||||
| 		} | ||||
|  | ||||
| 		if int(r0) == 0 { | ||||
| 			if e1 != 0 { | ||||
| 				return error(e1) | ||||
| 			} else { | ||||
| 				return syscall.EINVAL | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) { | ||||
| 	t := new(_CONSOLE_SCREEN_BUFFER_INFO) | ||||
| 	err := kernel.GetConsoleScreenBufferInfo( | ||||
| 		stdout, | ||||
| 		uintptr(unsafe.Pointer(t)), | ||||
| 	) | ||||
| 	return t, err | ||||
| } | ||||
|  | ||||
| func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) { | ||||
| 	t := new(_CONSOLE_CURSOR_INFO) | ||||
| 	err := kernel.GetConsoleCursorInfo(stdout, uintptr(unsafe.Pointer(t))) | ||||
| 	return t, err | ||||
| } | ||||
|  | ||||
| func SetConsoleCursorPosition(c *_COORD) error { | ||||
| 	return kernel.SetConsoleCursorPosition(stdout, c.ptr()) | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,6 @@ | ||||
| # github.com/chzyer/readline v1.5.1 | ||||
| ## explicit; go 1.15 | ||||
| github.com/chzyer/readline | ||||
| # github.com/fsnotify/fsnotify v1.7.0 | ||||
| ## explicit; go 1.17 | ||||
| github.com/fsnotify/fsnotify | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 kardolus
					kardolus