initial commit
This commit is contained in:
commit
72f5fa21c7
62
ansi_seq.go
Normal file
62
ansi_seq.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doItRaw() {
|
||||||
|
rawMode := exec.Command("/bin/stty", "raw", "-echo")
|
||||||
|
rawMode.Stdin = os.Stdin
|
||||||
|
err := rawMode.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cook() {
|
||||||
|
cookedMode := exec.Command("/bin/stty", "-raw", "echo")
|
||||||
|
cookedMode.Stdin = os.Stdin
|
||||||
|
err := cookedMode.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearEOL() {
|
||||||
|
os.Stdout.Write([]byte{0x1b, 0x5b, 0x4b})
|
||||||
|
}
|
||||||
|
|
||||||
|
func clear() {
|
||||||
|
os.Stdout.Write([]byte{0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, 0x1b, 0x5b, 0x33, 0x4a})
|
||||||
|
}
|
||||||
|
|
||||||
|
func goUp() {
|
||||||
|
os.Stdout.Write([]byte{0x1b, 0x5b, 0x41})
|
||||||
|
}
|
||||||
|
|
||||||
|
func goLeft() {
|
||||||
|
os.Stdout.Write([]byte{0x08})
|
||||||
|
}
|
||||||
|
|
||||||
|
// func save() {
|
||||||
|
// cmd := exec.Command("tput", "sc")
|
||||||
|
// cmd.Stdin = os.Stdin
|
||||||
|
// cmd.Stdout = os.Stdout
|
||||||
|
// cmd.Stderr = os.Stderr
|
||||||
|
// err := cmd.Run()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func restore() {
|
||||||
|
// cmd := exec.Command("tput", "rc")
|
||||||
|
// cmd.Stdin = os.Stdin
|
||||||
|
// cmd.Stdout = os.Stdout
|
||||||
|
// cmd.Stderr = os.Stderr
|
||||||
|
// err := cmd.Run()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// }
|
46
colorstring.go
Normal file
46
colorstring.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColorString struct {
|
||||||
|
color string
|
||||||
|
raw string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) Append(str string, attrs ...color.Attribute) *ColorString {
|
||||||
|
s.color += color.New(attrs...).Sprint(str)
|
||||||
|
s.raw += str
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) AppendColorString(str ...*ColorString) *ColorString {
|
||||||
|
for _, o := range str {
|
||||||
|
s.color += o.color
|
||||||
|
s.raw += o.raw
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) Equals(other *ColorString) bool {
|
||||||
|
return s.color == other.color && s.raw == other.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) Len() int {
|
||||||
|
return utf8.RuneCountInString(s.raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) ColorLen() int {
|
||||||
|
return utf8.RuneCountInString(s.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) String() string {
|
||||||
|
return s.color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ColorString) Raw() string {
|
||||||
|
return s.raw
|
||||||
|
}
|
16
go.mod
Normal file
16
go.mod
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module git.milar.in/slash/shell
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.milar.in/milarin/adverr v1.1.0
|
||||||
|
github.com/fatih/color v1.12.0
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||||
|
)
|
20
go.sum
Normal file
20
go.sum
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
git.milar.in/milarin/adverr v1.1.0 h1:jD9WnOvs40lfMhvqQ7cllOaRJNBMWr1f07/s9jAadp0=
|
||||||
|
git.milar.in/milarin/adverr v1.1.0/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E=
|
||||||
|
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||||
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
46
handle.go
Normal file
46
handle.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.milar.in/milarin/adverr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handle(input string) {
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
|
if input == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if input == "up" {
|
||||||
|
goUp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(input, "cd ") {
|
||||||
|
os.Chdir(strings.Split(input, " ")[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cook()
|
||||||
|
defer doItRaw()
|
||||||
|
|
||||||
|
parts := strings.Split(input, " ")
|
||||||
|
cmd := exec.Command(parts[0], parts[1:]...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
fmt.Println(adverr.Wrap("Could not start process", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
fmt.Println(adverr.Wrap("Could not wait for process to complete", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
19
input.go
Normal file
19
input.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleInput() <-chan byte {
|
||||||
|
ch := make(chan byte)
|
||||||
|
|
||||||
|
go func(ch chan byte) {
|
||||||
|
data := make([]byte, 1)
|
||||||
|
for _, err := os.Stdin.Read(data); err == nil; _, err = os.Stdin.Read(data) {
|
||||||
|
ch <- data[0]
|
||||||
|
}
|
||||||
|
close(ch)
|
||||||
|
}(ch)
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
161
main.go
Normal file
161
main.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
inputBuffer = new(ColorString)
|
||||||
|
oldLineContent = new(ColorString)
|
||||||
|
oldWidth int
|
||||||
|
|
||||||
|
debugInfo string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// enable raw mode
|
||||||
|
doItRaw()
|
||||||
|
defer cook()
|
||||||
|
|
||||||
|
inputChannel := handleInput()
|
||||||
|
|
||||||
|
running := true
|
||||||
|
|
||||||
|
refreshInputBuffer(true)
|
||||||
|
|
||||||
|
for running {
|
||||||
|
select {
|
||||||
|
case input := <-inputChannel:
|
||||||
|
if unicode.IsControl(rune(input)) {
|
||||||
|
if input == 127 && inputBuffer.Len() > 0 { // Backspace
|
||||||
|
str := inputBuffer.Raw()
|
||||||
|
inputBuffer = new(ColorString)
|
||||||
|
inputBuffer.Append(str[:len(str)-1])
|
||||||
|
} else if input == 12 { // C-L
|
||||||
|
clear()
|
||||||
|
refreshInputBuffer(true)
|
||||||
|
} else if input == 13 { // Enter
|
||||||
|
cmd := inputBuffer.Raw()
|
||||||
|
if strings.TrimSpace(cmd) == "exit" {
|
||||||
|
running = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
inputBuffer = new(ColorString)
|
||||||
|
fmt.Print("\r\n")
|
||||||
|
handle(cmd)
|
||||||
|
refreshInputBuffer(true)
|
||||||
|
} else if input == 3 { // C-C
|
||||||
|
inputBuffer = new(ColorString)
|
||||||
|
} else if input == 17 { // C-Q
|
||||||
|
running = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//fmt.Print(input, "\r\n")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
inputBuffer.Append(string(input))
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshInputBuffer(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshInputBuffer(force bool) {
|
||||||
|
ps := prompt()
|
||||||
|
lineContent := new(ColorString).AppendColorString(ps, inputBuffer)
|
||||||
|
|
||||||
|
width, _, err := terminal.GetSize(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if oldWidth == 0 {
|
||||||
|
oldWidth = width
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := oldLineContent.Len() / width
|
||||||
|
debugInfo = strconv.Itoa(lines)
|
||||||
|
|
||||||
|
for i := 0; i <= lines; i++ {
|
||||||
|
fmt.Print("\r")
|
||||||
|
clearEOL()
|
||||||
|
|
||||||
|
if i < lines {
|
||||||
|
goUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(lineContent.String())
|
||||||
|
oldLineContent = lineContent
|
||||||
|
oldWidth = width
|
||||||
|
}
|
||||||
|
|
||||||
|
// func refreshInputBuffer(force bool) {
|
||||||
|
// width, _, err := terminal.GetSize(int(os.Stdin.Fd()))
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// clearEOL()
|
||||||
|
// debugInfo = fmt.Sprint(oldLineContent.Len(), width, oldLineContent.Len()/width)
|
||||||
|
// for i := 0; i < oldLineContent.Len()/width; i++ {
|
||||||
|
// goUp()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ps := prompt()
|
||||||
|
// lineContent := new(ColorString).AppendColorString(ps, inputBuffer)
|
||||||
|
// if !force && lineContent.Equals(oldLineContent) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// diff := lineContent.Len() - oldLineContent.Len()
|
||||||
|
// if diff < 0 {
|
||||||
|
// diff = -diff
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for i := 0; i < diff; i++ {
|
||||||
|
// goLeft()
|
||||||
|
// }
|
||||||
|
// clearEOL()
|
||||||
|
|
||||||
|
// fmt.Print("\r")
|
||||||
|
// fmt.Print(lineContent.String())
|
||||||
|
// oldLineContent = lineContent
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func getTerminalSize() (width, height int, err error) {
|
||||||
|
// width, height, err = terminal.GetSize(int(os.Stdout.Fd()))
|
||||||
|
// if err != nil {
|
||||||
|
// return 0, 0, adverr.Wrap("Could not retrieve terminal size", err)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func print(str string, attrs ...color.Attribute) string {
|
||||||
|
return color.New(attrs...).Sprint(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func host() string {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
return hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
func wd() string {
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
home := env("HOME")
|
||||||
|
return strings.Replace(wd, home, "~", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func env(key string) string {
|
||||||
|
value, _ := os.LookupEnv(key)
|
||||||
|
return value
|
||||||
|
}
|
87
prompt.go
Normal file
87
prompt.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prompts = []func() *ColorString{ps1, ps2, ps3, ps4, ps5}
|
||||||
|
|
||||||
|
func prompt() *ColorString {
|
||||||
|
// width, _, err := terminal.GetSize(int(os.Stdin.Fd()))
|
||||||
|
// if err != nil {
|
||||||
|
// return ">"
|
||||||
|
// }
|
||||||
|
|
||||||
|
return ps1()
|
||||||
|
|
||||||
|
// for _, pfunc := range prompts {
|
||||||
|
// prompt := pfunc()
|
||||||
|
// if prompt.Len()+inputBuffer.Len() < width {
|
||||||
|
// return prompt.String()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps1() *ColorString {
|
||||||
|
p := new(ColorString)
|
||||||
|
p.Append(time.Now().Format("15:04:05"), color.FgRed)
|
||||||
|
p.Append(" • ")
|
||||||
|
|
||||||
|
p.Append(debugInfo, color.FgMagenta)
|
||||||
|
if len(debugInfo) > 0 {
|
||||||
|
p.Append(" • ")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Append(env("USER"), color.FgGreen)
|
||||||
|
p.Append(" • ")
|
||||||
|
p.Append(host(), color.FgYellow)
|
||||||
|
p.Append(" • ")
|
||||||
|
p.Append(wd(), color.FgBlue)
|
||||||
|
p.Append(" ❯", color.FgRed)
|
||||||
|
p.Append("❯", color.FgYellow)
|
||||||
|
p.Append("❯ ", color.FgGreen)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps2() *ColorString {
|
||||||
|
p := new(ColorString)
|
||||||
|
p.Append(env("USER"), color.FgGreen)
|
||||||
|
p.Append(" • ")
|
||||||
|
p.Append(host(), color.FgYellow)
|
||||||
|
p.Append(" • ")
|
||||||
|
p.Append(wd(), color.FgBlue)
|
||||||
|
p.Append(" ❯", color.FgRed)
|
||||||
|
p.Append("❯", color.FgYellow)
|
||||||
|
p.Append("❯ ", color.FgGreen)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps3() *ColorString {
|
||||||
|
p := new(ColorString)
|
||||||
|
p.Append(env("USER"), color.FgGreen)
|
||||||
|
p.Append(" • ")
|
||||||
|
p.Append(wd(), color.FgBlue)
|
||||||
|
p.Append(" ❯", color.FgRed)
|
||||||
|
p.Append("❯", color.FgYellow)
|
||||||
|
p.Append("❯ ", color.FgGreen)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps4() *ColorString {
|
||||||
|
p := new(ColorString)
|
||||||
|
p.Append(wd(), color.FgBlue)
|
||||||
|
p.Append(" ❯", color.FgRed)
|
||||||
|
p.Append("❯", color.FgYellow)
|
||||||
|
p.Append("❯ ", color.FgGreen)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps5() *ColorString {
|
||||||
|
p := new(ColorString)
|
||||||
|
p.Append(" ❯", color.FgRed)
|
||||||
|
p.Append("❯", color.FgYellow)
|
||||||
|
p.Append("❯ ", color.FgGreen)
|
||||||
|
return p
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user