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