rewrite CLI line rendering to column based, show host

pull/123/head
Simon Let 6 years ago
parent b31aa890e4
commit e981f743bf
  1. 335
      cmd/cli/main.go
  2. 3
      scripts/reshctl.sh
  3. 3
      scripts/widgets.sh

@ -67,17 +67,26 @@ func runReshCli() (string, int) {
} }
sessionID := flag.String("sessionID", "", "resh generated session id") sessionID := flag.String("sessionID", "", "resh generated session id")
host := flag.String("host", "", "host")
pwd := flag.String("pwd", "", "present working directory") pwd := flag.String("pwd", "", "present working directory")
gitOriginRemote := flag.String("gitOriginRemote", "DEFAULT", "git origin remote")
query := flag.String("query", "", "search query") query := flag.String("query", "", "search query")
flag.Parse() flag.Parse()
if *sessionID == "" { if *sessionID == "" {
fmt.Println("Error: you need to specify sessionId") log.Println("Error: you need to specify sessionId")
}
if *host == "" {
log.Println("Error: you need to specify HOST")
} }
if *pwd == "" { if *pwd == "" {
fmt.Println("Error: you need to specify PWD") log.Println("Error: you need to specify PWD")
}
if *gitOriginRemote == "DEFAULT" {
log.Println("Error: you need to specify gitOriginRemote")
} }
log.Printf("gitRemoteOrigin: %s\n", *gitOriginRemote)
g, err := gocui.NewGui(gocui.OutputNormal, false) g, err := gocui.NewGui(gocui.OutputNormal, false)
if err != nil { if err != nil {
log.Panicln(err) log.Panicln(err)
@ -102,10 +111,12 @@ func runReshCli() (string, int) {
} }
layout := manager{ layout := manager{
sessionID: *sessionID, sessionID: *sessionID,
pwd: *pwd, host: *host,
config: config, pwd: *pwd,
s: &st, gitOriginRemote: *gitOriginRemote,
config: config,
s: &st,
} }
g.SetManager(layout) g.SetManager(layout)
@ -169,13 +180,17 @@ func cleanHighlight(str string) string {
invert := "\033[32;7;1m" invert := "\033[32;7;1m"
end := "\033[0m" end := "\033[0m"
blueBold := "\033[34;1m" replace := []string{invert, end}
redBold := "\033[31;1m" for i := 30; i < 48; i++ {
repace := []string{invert, end, blueBold, redBold} base := prefix + strconv.Itoa(i)
normal := base + "m"
bold := base + ";1m"
replace = append(replace, normal, bold)
}
if strings.Contains(str, prefix) == false { if strings.Contains(str, prefix) == false {
return str return str
} }
for _, escSeq := range repace { for _, escSeq := range replace {
str = strings.ReplaceAll(str, escSeq, "") str = strings.ReplaceAll(str, escSeq, "")
} }
return str return str
@ -188,7 +203,14 @@ func highlightSelected(str string) string {
return invert + cleanHighlight(str) + end return invert + cleanHighlight(str) + end
} }
func highlightMatchAlternative(str string) string { func highlightHost(str string) string {
// template "\033[3%d;%dm"
redNormal := "\033[31m"
end := "\033[0m"
return redNormal + cleanHighlight(str) + end
}
func highlightPwd(str string) string {
// template "\033[3%d;%dm" // template "\033[3%d;%dm"
blueBold := "\033[34;1m" blueBold := "\033[34;1m"
end := "\033[0m" end := "\033[0m"
@ -197,17 +219,24 @@ func highlightMatchAlternative(str string) string {
func highlightMatch(str string) string { func highlightMatch(str string) string {
// template "\033[3%d;%dm" // template "\033[3%d;%dm"
magentaBold := "\033[35;1m"
end := "\033[0m"
return magentaBold + cleanHighlight(str) + end
}
func highlightWarn(str string) string {
// template "\033[3%d;%dm"
// orangeBold := "\033[33;1m"
redBold := "\033[31;1m" redBold := "\033[31;1m"
end := "\033[0m" end := "\033[0m"
return redBold + cleanHighlight(str) + end return redBold + cleanHighlight(str) + end
} }
func highlightExitStatus(str string) string { func highlightGit(str string) string {
// template "\033[3%d;%dm" // template "\033[3%d;%dm"
// orangeBold := "\033[33;1m" greenBold := "\033[32;1m"
magentaBold := "\033[35;1m"
end := "\033[0m" end := "\033[0m"
return magentaBold + cleanHighlight(str) + end return greenBold + cleanHighlight(str) + end
} }
func toString(record records.EnrichedRecord, lineLength int) string { func toString(record records.EnrichedRecord, lineLength int) string {
@ -217,8 +246,10 @@ func toString(record records.EnrichedRecord, lineLength int) string {
} }
type query struct { type query struct {
terms []string terms []string
pwd string host string
pwd string
gitOriginRemote string
// pwdTilde string // pwdTilde string
} }
@ -242,7 +273,7 @@ func filterTerms(terms []string) []string {
return newTerms return newTerms
} }
func newQueryFromString(queryInput string, pwd string) query { func newQueryFromString(queryInput string, host string, pwd string, gitOriginRemote string) query {
if debug { if debug {
log.Println("QUERY input = <" + queryInput + ">") log.Println("QUERY input = <" + queryInput + ">")
} }
@ -263,18 +294,33 @@ func newQueryFromString(queryInput string, pwd string) query {
log.Println("QUERY filtered terms =" + logStr) log.Println("QUERY filtered terms =" + logStr)
log.Println("QUERY pwd =" + pwd) log.Println("QUERY pwd =" + pwd)
} }
return query{terms: terms, pwd: pwd} return query{
terms: terms,
host: host,
pwd: pwd,
gitOriginRemote: gitOriginRemote,
}
} }
type item struct { type item struct {
// record records.EnrichedRecord // dateWithColor string
display string // date string
displayNoColor string
cmdLine string // [host:]pwd
pwd string locationWithColor string
pwdTilde string location string
hits float64
exitStatus int // [G] [E#]
flagsWithColor string
flags string
cmdLineWithColor string
cmdLine string
hits float64
key string
// cmdLineRaw string
} }
func (i item) less(i2 item) bool { func (i item) less(i2 item) bool {
@ -282,37 +328,28 @@ func (i item) less(i2 item) bool {
return i.hits > i2.hits return i.hits > i2.hits
} }
// used for deduplication func (i item) produceLine(flagLength int) (string, int) {
func (i item) key() string { line := ""
unlikelySeparator := "|||||" line += i.locationWithColor
return i.cmdLine + unlikelySeparator + i.pwd line += i.flagsWithColor
} flags := i.flags
if flagLength < len(i.flags) {
func (i item) toString(length int) string { log.Printf("produceLine can't specify line w/ flags shorter than the actual size. - len(flags) %v, requested %v\n", len(i.flags), flagLength)
// dots := "…"
if i.exitStatus == 0 {
return i.display
} }
exitStStr := "EXIT-STATUS: " + strconv.Itoa(i.exitStatus) for len(flags) < flagLength {
// x := length - len(exitStStr) line += " "
exitStStr = highlightExitStatus(exitStStr) flags += " "
}
// visually align spacer := " "
spaces := " " // 20 spaces if flagLength > 5 {
block := len(spaces) // use shorter spacer
str := i.display + spaces // because there is likely a long flag like E130 in the view
x := len(str) / block * block spacer = " "
str = str[:x] }
return str + exitStStr line += spacer + i.cmdLineWithColor
//if len(i.display) < x { length := len(i.location) + flagLength + len(spacer) + len(i.cmdLine)
// str := i.display return line, length
// // for len(str) < x {
// // str += " "
// // }
// return str + " " + exitStStr
//}
//return i.display[:x] + dots + " " + exitStStr
} }
// func (i item) equals(i2 item) bool { // func (i item) equals(i2 item) bool {
@ -335,22 +372,20 @@ func newItemFromRecordForQuery(record records.EnrichedRecord, query query, debug
const hitScoreConsecutive = 0.1 const hitScoreConsecutive = 0.1
const properMatchScore = 0.3 const properMatchScore = 0.3
const actualPwdScore = 0.9 const actualPwdScore = 0.9
const actualPwdScoreExtra = 0.2 // this + hitScore > actualPwdScore const nonZeroExitCodeScorePenalty = 0.5
const nonZeroExitCodeScorePenalty = 0.8 // this < min(hitScore, actualPwdScore) const sameGitRepoScore = 0.7
// const sameGitRepoScoreExtra = 0.0
const differentHostScorePenalty = 0.2
// nonZeroExitCodeScorePenalty + differentHostScorePenalty
hits := 0.0 hits := 0.0
if record.ExitCode != 0 { anyHit := false
hits -= nonZeroExitCodeScorePenalty
}
cmd := record.CmdLine cmd := record.CmdLine
pwdTilde := strings.Replace(record.Pwd, record.Home, "~", 1)
pwdDisp := leftCutPadString(pwdTilde, 25)
pwdRawDisp := leftCutPadString(record.Pwd, 25)
var useRawPwd bool
var dirHit bool
for _, term := range query.terms { for _, term := range query.terms {
termHit := false termHit := false
if strings.Contains(record.CmdLine, term) { if strings.Contains(record.CmdLine, term) {
anyHit = true
if termHit == false { if termHit == false {
hits += hitScore hits += hitScore
} else { } else {
@ -363,87 +398,99 @@ func newItemFromRecordForQuery(record records.EnrichedRecord, query query, debug
cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) cmd = strings.ReplaceAll(cmd, term, highlightMatch(term))
// NO continue // NO continue
} }
if strings.Contains(pwdTilde, term) {
if termHit == false {
hits += hitScore
} else {
hits += hitScoreConsecutive
}
termHit = true
if properMatch(pwdTilde, term, "/") {
hits += properMatchScore
}
pwdDisp = strings.ReplaceAll(pwdDisp, term, highlightMatch(term))
pwdRawDisp = strings.ReplaceAll(pwdRawDisp, term, highlightMatch(term))
dirHit = true
} else if strings.Contains(record.Pwd, term) {
if termHit == false {
hits += hitScore
} else {
hits += hitScoreConsecutive
}
termHit = true
if properMatch(pwdTilde, term, "/") {
hits += properMatchScore
}
pwdRawDisp = strings.ReplaceAll(pwdRawDisp, term, highlightMatch(term))
dirHit = true
useRawPwd = true
}
// if strings.Contains(record.GitOriginRemote, term) {
// hits++
// }
} }
// actual pwd matches // actual pwd matches
// only use if there was no directory match on any of the terms
// N terms can only produce: // N terms can only produce:
// -> N matches against the command // -> N matches against the command
// -> N matches against the directory
// -> 1 extra match for the actual directory match // -> 1 extra match for the actual directory match
sameGitRepo := false
if query.gitOriginRemote != "" && query.gitOriginRemote == record.GitOriginRemote {
sameGitRepo = true
}
samePwd := false
if record.Pwd == query.pwd { if record.Pwd == query.pwd {
if dirHit { anyHit = true
hits += actualPwdScoreExtra samePwd = true
} else { hits += actualPwdScore
hits += actualPwdScore } else if sameGitRepo {
} anyHit = true
pwdDisp = highlightMatchAlternative(pwdDisp) hits += sameGitRepoScore
// pwdRawDisp = highlightMatchAlternative(pwdRawDisp)
useRawPwd = false
} }
if hits <= 0 {
differentHost := false
if record.Host != query.host {
differentHost = true
hits -= differentHostScorePenalty
}
errorExitStatus := false
if record.ExitCode != 0 {
errorExitStatus = true
hits -= nonZeroExitCodeScorePenalty
}
if hits <= 0 && !anyHit {
return item{}, errors.New("no match for given record and query") return item{}, errors.New("no match for given record and query")
} }
display := ""
// pwd := leftCutPadString("<"+pwdTilde+">", 20) // KEY for deduplication
if useRawPwd {
display += pwdRawDisp unlikelySeparator := "|||||"
key := record.CmdLine + unlikelySeparator + record.Pwd +
unlikelySeparator + strconv.Itoa(record.ExitCode) + unlikelySeparator +
record.GitOriginRemote + unlikelySeparator + record.Host
// DISPLAY
// DISPLAY > date
// TODO
// DISPLAY > location
location := ""
locationWithColor := ""
if differentHost {
location += record.Host + ":"
locationWithColor += highlightHost(record.Host) + ":"
}
const locationLenght = 30
pwdLength := locationLenght - len(location)
pwdTilde := strings.Replace(record.Pwd, record.Home, "~", 1)
location += leftCutPadString(pwdTilde, pwdLength)
if samePwd {
locationWithColor += highlightPwd(leftCutPadString(pwdTilde, pwdLength))
} else { } else {
display += pwdDisp locationWithColor += leftCutPadString(pwdTilde, pwdLength)
} }
// DISPLAY > flags
flags := ""
flagsWithColor := ""
if debug { if debug {
hitsStr := fmt.Sprintf("%.1f", hits) hitsStr := fmt.Sprintf("%.1f", hits)
hitsDisp := " " + hitsStr + " " flags += " S" + hitsStr
display += hitsDisp }
} else { if sameGitRepo {
display += " " flags += " G"
flagsWithColor += " " + highlightGit("G")
}
if errorExitStatus {
flags += " E" + strconv.Itoa(record.ExitCode)
flagsWithColor += " " + highlightWarn("E"+strconv.Itoa(record.ExitCode))
} }
// DISPLAY > cmdline
// cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">"
cmd = strings.ReplaceAll(cmd, "\n", ";") cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";")
display += cmd cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";")
// itDummy := item{
// cmdLine: record.CmdLine,
// pwd: record.Pwd,
// }
// + " #K:<" + itDummy.key() + ">"
it := item{ it := item{
display: display, location: location,
displayNoColor: display, locationWithColor: locationWithColor,
cmdLine: record.CmdLine, flags: flags,
pwd: record.Pwd, flagsWithColor: flagsWithColor,
pwdTilde: pwdTilde, cmdLine: cmdLine,
hits: hits, cmdLineWithColor: cmdLineWithColor,
exitStatus: record.ExitCode, hits: hits,
key: key,
} }
return it, nil return it, nil
} }
@ -468,9 +515,11 @@ type state struct {
} }
type manager struct { type manager struct {
sessionID string sessionID string
pwd string host string
config cfg.Config pwd string
gitOriginRemote string
config cfg.Config
s *state s *state
} }
@ -503,7 +552,7 @@ func (m manager) UpdateData(input string) {
log.Println("len(fullRecords) =", len(m.s.fullRecords)) log.Println("len(fullRecords) =", len(m.s.fullRecords))
log.Println("len(data) =", len(m.s.data)) log.Println("len(data) =", len(m.s.data))
} }
query := newQueryFromString(input, m.pwd) query := newQueryFromString(input, m.host, m.pwd, m.gitOriginRemote)
var data []item var data []item
itemSet := make(map[string]bool) itemSet := make(map[string]bool)
m.s.lock.Lock() m.s.lock.Lock()
@ -515,11 +564,11 @@ func (m manager) UpdateData(input string) {
// log.Println(" * continue (no match)", rec.Pwd) // log.Println(" * continue (no match)", rec.Pwd)
continue continue
} }
if itemSet[itm.key()] { if itemSet[itm.key] {
// log.Println(" * continue (already present)", itm.key(), itm.pwd) // log.Println(" * continue (already present)", itm.key(), itm.pwd)
continue continue
} }
itemSet[itm.key()] = true itemSet[itm.key] = true
data = append(data, itm) data = append(data, itm)
// log.Println("DATA =", itm.display) // log.Println("DATA =", itm.display)
} }
@ -600,6 +649,16 @@ func (m manager) Layout(g *gocui.Gui) error {
v.Clear() v.Clear()
v.Rewind() v.Rewind()
longestFlagsLen := 2 // at least 2
for i, itm := range m.s.data {
if i == maxY {
break
}
if len(itm.flags) > longestFlagsLen {
longestFlagsLen = len(itm.flags)
}
}
for i, itm := range m.s.data { for i, itm := range m.s.data {
if i == maxY { if i == maxY {
if debug { if debug {
@ -607,10 +666,10 @@ func (m manager) Layout(g *gocui.Gui) error {
} }
break break
} }
displayStr := itm.toString(maxX - 2) displayStr, _ := itm.produceLine(longestFlagsLen)
if m.s.highlightedItem == i { if m.s.highlightedItem == i {
// use actual min requried length instead of 420 constant // use actual min requried length instead of 420 constant
displayStr = doHighlightString(displayStr, 420) displayStr = doHighlightString(displayStr, maxX*2)
if debug { if debug {
log.Println("### HightlightedItem string :", displayStr) log.Println("### HightlightedItem string :", displayStr)
} }

@ -108,7 +108,8 @@ __resh_unbind_all() {
# wrapper for resh-cli for calling resh directly # wrapper for resh-cli for calling resh directly
resh() { resh() {
local buffer local buffer
buffer=$(resh-cli --sessionID "$__RESH_SESSION_ID" --pwd "$PWD") local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)"
buffer=$(resh-cli --sessionID "$__RESH_SESSION_ID" --host "$HOST" --pwd "$PWD" --gitOriginRemote "$git_remote")
status_code=$? status_code=$?
if [ $status_code = 111 ]; then if [ $status_code = 111 ]; then
# execute # execute

@ -100,7 +100,8 @@ __resh_widget_control_R() {
__RESH_HIST_RECALL_ACTIONS="$__RESH_HIST_RECALL_ACTIONS|||control_R:$BUFFER" __RESH_HIST_RECALL_ACTIONS="$__RESH_HIST_RECALL_ACTIONS|||control_R:$BUFFER"
local status_code local status_code
BUFFER=$(resh-cli --sessionID "$__RESH_SESSION_ID" --pwd "$PWD" --query "$BUFFER") local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)"
BUFFER=$(resh-cli --sessionID "$__RESH_SESSION_ID" --host "$HOST" --pwd "$PWD" --gitOriginRemote "$git_remote" --query "$BUFFER")
status_code=$? status_code=$?
if [ $status_code = 111 ]; then if [ $status_code = 111 ]; then
# execute # execute

Loading…
Cancel
Save