From e5a2279505357c1ac75ee1350101870faadf8ed3 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Fri, 1 May 2020 23:02:22 +0200 Subject: [PATCH 01/23] add simple status line --- cmd/cli/highlight.go | 6 ++++++ cmd/cli/main.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/cmd/cli/highlight.go b/cmd/cli/highlight.go index eabe539..6a8c39d 100644 --- a/cmd/cli/highlight.go +++ b/cmd/cli/highlight.go @@ -27,6 +27,12 @@ func cleanHighlight(str string) string { return str } +func highlightStatus(str string) string { + invert := "\033[7;1m" + end := "\033[0m" + return invert + cleanHighlight(str) + end +} + func highlightSelected(str string) string { // template "\033[3%d;%dm" // invertGreen := "\033[32;7;1m" diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 7fde661..70a1da9 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -440,8 +440,15 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } } + // status line + topBoxSize := 3 // size of the query box up top + realLineLength := maxX - 2 + printedLineLength := maxX - 4 + selectedCommand := m.s.data[m.s.highlightedItem].cmdLine + var selectedLineCount int = len(selectedCommand)/(printedLineLength) + 1 + for i, itm := range m.s.data { - if i == maxY { + if i == maxY-topBoxSize-selectedLineCount { if debug { log.Println(maxY) } @@ -449,7 +456,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } displayStr, _ := itm.produceLine(longestFlagsLen) if m.s.highlightedItem == i { - // use actual min requried length instead of 420 constant + // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*2) if debug { log.Println("### HightlightedItem string :", displayStr) @@ -469,6 +476,30 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { // v.SetHighlight(m.s.highlightedItem, true) // } } + // status line + var idxSt, idxEnd int + var nextLine bool + tab := " " + tabSize := len(tab) + for idxSt < len(selectedCommand) { + idxEnd = idxSt + printedLineLength + if nextLine { + idxEnd -= tabSize + } + + if idxEnd > len(selectedCommand) { + idxEnd = len(selectedCommand) + } + str := selectedCommand[idxSt:idxEnd] + + indent := " " + if nextLine { + indent += tab + } + v.WriteString(highlightStatus(rightCutPadString(indent+str, realLineLength)) + "\n") + idxSt += printedLineLength + nextLine = true + } if debug { log.Println("len(data) =", len(m.s.data)) log.Println("highlightedItem =", m.s.highlightedItem) From 52fc74c58821ea98630aea6d39d3045b9449277f Mon Sep 17 00:00:00 2001 From: Simon Let Date: Fri, 1 May 2020 23:15:39 +0200 Subject: [PATCH 02/23] do not select entries outside of the view --- cmd/cli/main.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 70a1da9..a843683 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -156,11 +156,12 @@ func runReshCli() (string, int) { } type state struct { - lock sync.Mutex - cliRecords []records.CliRecord - data []item - rawData []rawItem - highlightedItem int + lock sync.Mutex + cliRecords []records.CliRecord + data []item + rawData []rawItem + highlightedItem int + displayedItemsCount int rawMode bool @@ -316,10 +317,9 @@ func (m manager) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) } func (m manager) Next(g *gocui.Gui, v *gocui.View) error { - _, y := g.Size() m.s.lock.Lock() defer m.s.lock.Unlock() - if m.s.highlightedItem < y { + if m.s.highlightedItem < m.s.displayedItemsCount-1 { m.s.highlightedItem++ } return nil @@ -447,6 +447,8 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { selectedCommand := m.s.data[m.s.highlightedItem].cmdLine var selectedLineCount int = len(selectedCommand)/(printedLineLength) + 1 + m.s.displayedItemsCount = maxY - topBoxSize - selectedLineCount + for i, itm := range m.s.data { if i == maxY-topBoxSize-selectedLineCount { if debug { @@ -509,6 +511,8 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { func (m manager) rawMode(g *gocui.Gui, v *gocui.View) error { maxX, maxY := g.Size() + topBoxSize := 3 + m.s.displayedItemsCount = maxY - topBoxSize for i, itm := range m.s.rawData { if i == maxY { From 617cc31fa3aa82c4ac2422f5ad5ff86c0788ee14 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 2 May 2020 00:14:39 +0200 Subject: [PATCH 03/23] show datetime in cli - experiment --- cmd/cli/highlight.go | 7 +++++++ cmd/cli/item.go | 18 ++++++++++++++---- cmd/cli/main.go | 2 +- pkg/records/records.go | 17 +++++++++-------- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/cmd/cli/highlight.go b/cmd/cli/highlight.go index 6a8c39d..b748a0a 100644 --- a/cmd/cli/highlight.go +++ b/cmd/cli/highlight.go @@ -41,6 +41,13 @@ func highlightSelected(str string) string { return invert + cleanHighlight(str) + end } +func highlightDate(str string) string { + // template "\033[3%d;%dm" + yellowNormal := "\033[33m" + end := "\033[0m" + return yellowNormal + cleanHighlight(str) + end +} + func highlightHost(str string) string { // template "\033[3%d;%dm" redNormal := "\033[31m" diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 349da75..fd3cb7b 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -6,13 +6,14 @@ import ( "log" "strconv" "strings" + "time" "github.com/curusarn/resh/pkg/records" ) type item struct { - // dateWithColor string - // date string + dateWithColor string + date string // [host:]pwd locationWithColor string @@ -36,8 +37,11 @@ func (i item) less(i2 item) bool { return i.score > i2.score } -func (i item) produceLine(flagLength int) (string, int) { +func (i item) produceLine(flagLength int, showDate bool) (string, int) { line := "" + if showDate { + line += i.dateWithColor + } line += i.locationWithColor line += i.flagsWithColor flags := i.flags @@ -167,7 +171,11 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool // DISPLAY // DISPLAY > date - // TODO + secs := int64(record.RealtimeBeforeLocal) + nsecs := int64((record.RealtimeBeforeLocal - float64(secs)) * 1e9) + tm := time.Unix(secs, nsecs) + date := tm.Format("2006-01-02_15:04 ") + dateWithColor := highlightDate(date) // DISPLAY > location location := "" @@ -213,6 +221,8 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") it := item{ + date: date, + dateWithColor: dateWithColor, location: location, locationWithColor: locationWithColor, flags: flags, diff --git a/cmd/cli/main.go b/cmd/cli/main.go index a843683..147f90b 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -456,7 +456,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } break } - displayStr, _ := itm.produceLine(longestFlagsLen) + displayStr, _ := itm.produceLine(longestFlagsLen, true) if m.s.highlightedItem == i { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*2) diff --git a/pkg/records/records.go b/pkg/records/records.go index b8d9d67..04dd0ca 100644 --- a/pkg/records/records.go +++ b/pkg/records/records.go @@ -158,7 +158,7 @@ type CliRecord struct { GitOriginRemote string `json:"gitOriginRemote"` ExitCode int `json:"exitCode"` - // RealtimeBefore float64 `json:"realtimeBefore"` + RealtimeBeforeLocal float64 `json:"realtimeBeforeLocal"` // RealtimeAfter float64 `json:"realtimeAfter"` // RealtimeDuration float64 `json:"realtimeDuration"` } @@ -166,13 +166,14 @@ type CliRecord struct { // NewCliRecord from EnrichedRecord func NewCliRecord(r EnrichedRecord) CliRecord { return CliRecord{ - SessionID: r.SessionID, - CmdLine: r.CmdLine, - Host: r.Host, - Pwd: r.Pwd, - Home: r.Home, - GitOriginRemote: r.GitOriginRemote, - ExitCode: r.ExitCode, + SessionID: r.SessionID, + CmdLine: r.CmdLine, + Host: r.Host, + Pwd: r.Pwd, + Home: r.Home, + GitOriginRemote: r.GitOriginRemote, + ExitCode: r.ExitCode, + RealtimeBeforeLocal: r.RealtimeBeforeLocal, } } From 7c68d1da13130d2ac465ed17dce76c10a9bfc8ed Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 2 May 2020 01:47:31 +0200 Subject: [PATCH 04/23] show relative time, major speed up --- cmd/cli/item.go | 234 +++++++++++++++++++++++++++++------------ cmd/cli/main.go | 16 ++- pkg/records/records.go | 18 ++-- 3 files changed, 188 insertions(+), 80 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index fd3cb7b..3fd69fe 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -12,6 +12,29 @@ import ( ) type item struct { + realtimeBefore float64 + + // [host:]pwd + differentHost bool + host string + home string + samePwd bool + pwd string + + // [G] [E#] + sameGitRepo bool + exitCode int + + cmdLineWithColor string + cmdLine string + + score float64 + + key string + // cmdLineRaw string +} + +type itemColumns struct { dateWithColor string date string @@ -26,7 +49,7 @@ type item struct { cmdLineWithColor string cmdLine string - score float64 + // score float64 key string // cmdLineRaw string @@ -37,16 +60,134 @@ func (i item) less(i2 item) bool { return i.score > i2.score } -func (i item) produceLine(flagLength int, showDate bool) (string, int) { +func formatDatetime(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 1 { + return strconv.Itoa(yrs) + " years ago" + } + if yrs == 1 { + return "1 year ago" + } + months := int(hrs / (30 * 24)) + if months > 1 { + return strconv.Itoa(months) + " months ago" + } + if months == 1 { + return "1 month ago" + } + days := int(hrs / 24) + if days > 1 { + return strconv.Itoa(days) + " days ago" + } + if days == 1 { + return "1 day ago" + } + hrsInt := int(hrs) + if hrsInt > 1 { + return strconv.Itoa(hrsInt) + " hours ago" + } + if hrsInt == 1 { + return "1 hour ago" + } + mins := int(hrs * 60) + if mins > 1 { + return strconv.Itoa(mins) + " mins ago" + } + if mins == 1 { + return "1 min ago" + } + secs := int(hrs * 60 * 60) + if secs > 1 { + return strconv.Itoa(secs) + " secs ago" + } + if secs == 1 { + return "1 sec ago" + } + return "now" +} + +func (i item) drawItemColumns() itemColumns { + + // DISPLAY + // DISPLAY > date + secs := int64(i.realtimeBefore) + nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) + tm := time.Unix(secs, nsecs) + //date := tm.Format("2006-01-02 15:04 ") + // dateLength := len("12 months ago ") // longest date + date := formatDatetime(tm) + " " + // for len(date) < dateLength { + // date = " " + date + // } + dateWithColor := highlightDate(date) + + // DISPLAY > location + location := "" + locationWithColor := "" + if i.differentHost { + location += i.host + ":" + locationWithColor += highlightHost(i.host) + ":" + } + const locationLenght = 30 + // const locationLenght = 20 // small screenshots + pwdLength := locationLenght - len(location) + pwdTilde := strings.Replace(i.pwd, i.home, "~", 1) + location += leftCutPadString(pwdTilde, pwdLength) + if i.samePwd { + locationWithColor += highlightPwd(leftCutPadString(pwdTilde, pwdLength)) + } else { + locationWithColor += leftCutPadString(pwdTilde, pwdLength) + } + + // DISPLAY > flags + flags := "" + flagsWithColor := "" + if debug { + hitsStr := fmt.Sprintf("%.1f", i.score) + flags += " S" + hitsStr + } + if i.sameGitRepo { + flags += " G" + flagsWithColor += " " + highlightGit("G") + } + if i.exitCode != 0 { + flags += " E" + strconv.Itoa(i.exitCode) + flagsWithColor += " " + highlightWarn("E"+strconv.Itoa(i.exitCode)) + } + // NOTE: you can debug arbitrary metadata like this + // flags += " <" + record.GitOriginRemote + ">" + // flagsWithColor += " <" + record.GitOriginRemote + ">" + return itemColumns{ + date: date, + dateWithColor: dateWithColor, + location: location, + locationWithColor: locationWithColor, + flags: flags, + flagsWithColor: flagsWithColor, + cmdLine: i.cmdLine, + cmdLineWithColor: i.cmdLineWithColor, + // score: i.score, + key: i.key, + } +} + +func (ic itemColumns) produceLine(dateLength int, flagLength int, showDate bool) (string, int) { line := "" if showDate { - line += i.dateWithColor + date := ic.date + for len(date) < dateLength { + line += " " + date += " " + } + line += ic.dateWithColor } - line += i.locationWithColor - line += i.flagsWithColor - flags := i.flags - if flagLength < len(i.flags) { - log.Printf("produceLine can't specify line w/ flags shorter than the actual size. - len(flags) %v, requested %v\n", len(i.flags), flagLength) + line += ic.locationWithColor + line += ic.flagsWithColor + flags := ic.flags + if flagLength < len(ic.flags) { + log.Printf("produceLine can't specify line w/ flags shorter than the actual size. - len(flags) %v, requested %v\n", len(ic.flags), flagLength) } for len(flags) < flagLength { line += " " @@ -58,9 +199,9 @@ func (i item) produceLine(flagLength int, showDate bool) (string, int) { // because there is likely a long flag like E130 in the view spacer = " " } - line += spacer + i.cmdLineWithColor + line += spacer + ic.cmdLineWithColor - length := len(i.location) + flagLength + len(spacer) + len(i.cmdLine) + length := len(ic.location) + flagLength + len(spacer) + len(ic.cmdLine) return line, length } @@ -153,9 +294,9 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool differentHost = true score -= differentHostScorePenalty } - errorExitStatus := false + // errorExitStatus := false if record.ExitCode != 0 { - errorExitStatus = true + // errorExitStatus = true score -= nonZeroExitCodeScorePenalty } if score <= 0 && !anyHit { @@ -169,51 +310,6 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool record.GitOriginRemote + unlikelySeparator + record.Host // + strconv.Itoa(record.ExitCode) + unlikelySeparator - // DISPLAY - // DISPLAY > date - secs := int64(record.RealtimeBeforeLocal) - nsecs := int64((record.RealtimeBeforeLocal - float64(secs)) * 1e9) - tm := time.Unix(secs, nsecs) - date := tm.Format("2006-01-02_15:04 ") - dateWithColor := highlightDate(date) - - // DISPLAY > location - location := "" - locationWithColor := "" - if differentHost { - location += record.Host + ":" - locationWithColor += highlightHost(record.Host) + ":" - } - const locationLenght = 30 - // const locationLenght = 20 // small screenshots - pwdLength := locationLenght - len(location) - pwdTilde := strings.Replace(record.Pwd, record.Home, "~", 1) - location += leftCutPadString(pwdTilde, pwdLength) - if samePwd { - locationWithColor += highlightPwd(leftCutPadString(pwdTilde, pwdLength)) - } else { - locationWithColor += leftCutPadString(pwdTilde, pwdLength) - } - - // DISPLAY > flags - flags := "" - flagsWithColor := "" - if debug { - hitsStr := fmt.Sprintf("%.1f", score) - flags += " S" + hitsStr - } - if sameGitRepo { - flags += " G" - flagsWithColor += " " + highlightGit("G") - } - if errorExitStatus { - flags += " E" + strconv.Itoa(record.ExitCode) - flagsWithColor += " " + highlightWarn("E"+strconv.Itoa(record.ExitCode)) - } - // NOTE: you can debug arbitrary metadata like this - // flags += " <" + record.GitOriginRemote + ">" - // flagsWithColor += " <" + record.GitOriginRemote + ">" - // DISPLAY > cmdline // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" @@ -221,16 +317,20 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") it := item{ - date: date, - dateWithColor: dateWithColor, - location: location, - locationWithColor: locationWithColor, - flags: flags, - flagsWithColor: flagsWithColor, - cmdLine: cmdLine, - cmdLineWithColor: cmdLineWithColor, - score: score, - key: key, + realtimeBefore: record.RealtimeBefore, + + differentHost: differentHost, + host: record.Host, + home: record.Home, + samePwd: samePwd, + pwd: record.Pwd, + + sameGitRepo: sameGitRepo, + exitCode: record.ExitCode, + cmdLine: cmdLine, + cmdLineWithColor: cmdLineWithColor, + score: score, + key: key, } return it, nil } diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 147f90b..ca37a95 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -430,13 +430,21 @@ func SendCliMsg(m msg.CliMsg, port string) msg.CliResponse { func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { maxX, maxY := g.Size() + var data []itemColumns + + longestDateLen := 0 // at least 0 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) + ic := itm.drawItemColumns() + data = append(data, ic) + if len(ic.date) > longestDateLen { + longestDateLen = len(ic.date) + } + if len(ic.flags) > longestFlagsLen { + longestFlagsLen = len(ic.flags) } } @@ -449,14 +457,14 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { m.s.displayedItemsCount = maxY - topBoxSize - selectedLineCount - for i, itm := range m.s.data { + for i, itm := range data { if i == maxY-topBoxSize-selectedLineCount { if debug { log.Println(maxY) } break } - displayStr, _ := itm.produceLine(longestFlagsLen, true) + displayStr, _ := itm.produceLine(longestDateLen, longestFlagsLen, true) if m.s.highlightedItem == i { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*2) diff --git a/pkg/records/records.go b/pkg/records/records.go index 04dd0ca..9283429 100644 --- a/pkg/records/records.go +++ b/pkg/records/records.go @@ -158,7 +158,7 @@ type CliRecord struct { GitOriginRemote string `json:"gitOriginRemote"` ExitCode int `json:"exitCode"` - RealtimeBeforeLocal float64 `json:"realtimeBeforeLocal"` + RealtimeBefore float64 `json:"realtimeBefore"` // RealtimeAfter float64 `json:"realtimeAfter"` // RealtimeDuration float64 `json:"realtimeDuration"` } @@ -166,14 +166,14 @@ type CliRecord struct { // NewCliRecord from EnrichedRecord func NewCliRecord(r EnrichedRecord) CliRecord { return CliRecord{ - SessionID: r.SessionID, - CmdLine: r.CmdLine, - Host: r.Host, - Pwd: r.Pwd, - Home: r.Home, - GitOriginRemote: r.GitOriginRemote, - ExitCode: r.ExitCode, - RealtimeBeforeLocal: r.RealtimeBeforeLocal, + SessionID: r.SessionID, + CmdLine: r.CmdLine, + Host: r.Host, + Pwd: r.Pwd, + Home: r.Home, + GitOriginRemote: r.GitOriginRemote, + ExitCode: r.ExitCode, + RealtimeBefore: r.RealtimeBefore, } } From f3d3180f4fc187419456f1a4746075205648bf86 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 2 May 2020 17:34:57 +0200 Subject: [PATCH 05/23] minor improvement --- cmd/cli/item.go | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 3fd69fe..dc791ef 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -64,47 +64,47 @@ func formatDatetime(tm time.Time) string { tmSince := time.Since(tm) hrs := tmSince.Hours() yrs := int(hrs / (365 * 24)) - if yrs > 1 { + if yrs > 0 { + if yrs == 1 { + return "1 year ago" + } return strconv.Itoa(yrs) + " years ago" } - if yrs == 1 { - return "1 year ago" - } months := int(hrs / (30 * 24)) - if months > 1 { + if months > 0 { + if months == 1 { + return "1 month ago" + } return strconv.Itoa(months) + " months ago" } - if months == 1 { - return "1 month ago" - } days := int(hrs / 24) - if days > 1 { + if days > 0 { + if days == 1 { + return "1 day ago" + } return strconv.Itoa(days) + " days ago" } - if days == 1 { - return "1 day ago" - } hrsInt := int(hrs) - if hrsInt > 1 { + if hrsInt > 0 { + if hrsInt == 1 { + return "1 hour ago" + } return strconv.Itoa(hrsInt) + " hours ago" } - if hrsInt == 1 { - return "1 hour ago" - } mins := int(hrs * 60) - if mins > 1 { + if mins > 0 { + if mins == 1 { + return "1 min ago" + } return strconv.Itoa(mins) + " mins ago" } - if mins == 1 { - return "1 min ago" - } secs := int(hrs * 60 * 60) - if secs > 1 { + if secs > 0 { + if secs == 1 { + return "1 sec ago" + } return strconv.Itoa(secs) + " secs ago" } - if secs == 1 { - return "1 sec ago" - } return "now" } From 8349400806b4fe41f5e139b252f54c949a3c068f Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 17:04:00 +0200 Subject: [PATCH 06/23] status fixes, ctrl+pn works as arrow keys --- cmd/cli/main.go | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index ca37a95..4e9f9ce 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -124,9 +124,15 @@ func runReshCli() (string, int) { if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, layout.Next); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlN, gocui.ModNone, layout.Next); err != nil { + log.Panicln(err) + } if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, layout.Prev); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlP, gocui.ModNone, layout.Prev); err != nil { + log.Panicln(err) + } if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } @@ -448,6 +454,9 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } } + if m.s.highlightedItem >= len(m.s.data) { + m.s.highlightedItem = len(m.s.data) - 1 + } // status line topBoxSize := 3 // size of the query box up top realLineLength := maxX - 2 @@ -457,15 +466,16 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { m.s.displayedItemsCount = maxY - topBoxSize - selectedLineCount - for i, itm := range data { - if i == maxY-topBoxSize-selectedLineCount { - if debug { - log.Println(maxY) - } + var index int + for index < len(data) { + itm := data[index] + if index == maxY-topBoxSize-selectedLineCount { + // page is full break } + displayStr, _ := itm.produceLine(longestDateLen, longestFlagsLen, true) - if m.s.highlightedItem == i { + if m.s.highlightedItem == index { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*2) if debug { @@ -482,9 +492,12 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } } v.WriteString(displayStr + "\n") - // if m.s.highlightedItem == i { - // v.SetHighlight(m.s.highlightedItem, true) - // } + index++ + } + // push the status line to the bottom of the page + for index < maxY-topBoxSize-selectedLineCount { + v.WriteString("\n") + index++ } // status line var idxSt, idxEnd int From 653974826cf0fadf771c396209afdf4c0b122b9c Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 17:10:14 +0200 Subject: [PATCH 07/23] ctrl+g aborts resh-cli --- cmd/cli/main.go | 86 +++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 4e9f9ce..99ede3d 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -136,9 +136,6 @@ func runReshCli() (string, int) { if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyCtrlG, gocui.ModNone, quit); err != nil { - log.Panicln(err) - } if err := g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, quit); err != nil { log.Panicln(err) } @@ -148,6 +145,9 @@ func runReshCli() (string, int) { if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, layout.SelectPaste); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlG, gocui.ModNone, layout.AbortPaste); err != nil { + log.Panicln(err) + } if err := g.SetKeybinding("", gocui.KeyCtrlR, gocui.ModNone, layout.SwitchModes); err != nil { log.Panicln(err) } @@ -209,6 +209,17 @@ func (m manager) SelectPaste(g *gocui.Gui, v *gocui.View) error { return nil } +func (m manager) AbortPaste(g *gocui.Gui, v *gocui.View) error { + m.s.lock.Lock() + defer m.s.lock.Unlock() + if m.s.highlightedItem < len(m.s.data) { + m.s.output = v.Buffer() + m.s.exitCode = 0 // success + return gocui.ErrQuit + } + return nil +} + type dedupRecord struct { dataIndex int score float32 @@ -398,41 +409,6 @@ func (m manager) Layout(g *gocui.Gui) error { func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } - -// SendCliMsg to daemon -func SendCliMsg(m msg.CliMsg, port string) msg.CliResponse { - recJSON, err := json.Marshal(m) - if err != nil { - log.Fatal("send err 1", err) - } - - req, err := http.NewRequest("POST", "http://localhost:"+port+"/dump", - bytes.NewBuffer(recJSON)) - if err != nil { - log.Fatal("send err 2", err) - } - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - log.Fatal("resh-daemon is not running :(") - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal("read response error") - } - // log.Println(string(body)) - response := msg.CliResponse{} - err = json.Unmarshal(body, &response) - if err != nil { - log.Fatal("unmarshal resp error: ", err) - } - return response -} - func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { maxX, maxY := g.Size() @@ -570,3 +546,37 @@ func (m manager) rawMode(g *gocui.Gui, v *gocui.View) error { } return nil } + +// SendCliMsg to daemon +func SendCliMsg(m msg.CliMsg, port string) msg.CliResponse { + recJSON, err := json.Marshal(m) + if err != nil { + log.Fatal("send err 1", err) + } + + req, err := http.NewRequest("POST", "http://localhost:"+port+"/dump", + bytes.NewBuffer(recJSON)) + if err != nil { + log.Fatal("send err 2", err) + } + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Fatal("resh-daemon is not running :(") + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal("read response error") + } + // log.Println(string(body)) + response := msg.CliResponse{} + err = json.Unmarshal(body, &response) + if err != nil { + log.Fatal("unmarshal resp error: ", err) + } + return response +} From 9265e09938d5cc948cfa77ba3f83060b57da0dd8 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 17:44:18 +0200 Subject: [PATCH 08/23] add help under status line --- cmd/cli/main.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 99ede3d..cb317b1 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -434,18 +434,19 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { m.s.highlightedItem = len(m.s.data) - 1 } // status line - topBoxSize := 3 // size of the query box up top + topBoxHeight := 3 // size of the query box up top realLineLength := maxX - 2 printedLineLength := maxX - 4 selectedCommand := m.s.data[m.s.highlightedItem].cmdLine - var selectedLineCount int = len(selectedCommand)/(printedLineLength) + 1 + var statusLineHeight int = len(selectedCommand)/(printedLineLength) + 1 + statusLineHeight++ // help line - m.s.displayedItemsCount = maxY - topBoxSize - selectedLineCount + m.s.displayedItemsCount = maxY - topBoxHeight - statusLineHeight var index int for index < len(data) { itm := data[index] - if index == maxY-topBoxSize-selectedLineCount { + if index == maxY-topBoxHeight-statusLineHeight { // page is full break } @@ -471,7 +472,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { index++ } // push the status line to the bottom of the page - for index < maxY-topBoxSize-selectedLineCount { + for index < maxY-topBoxHeight-statusLineHeight { v.WriteString("\n") index++ } @@ -499,6 +500,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { idxSt += printedLineLength nextLine = true } + v.WriteString("HELP: type to search, UP/DOWN to select, RIGHT to edit, ENTER to execute, CTRL+G to abort, CTRL+C/D to quit") if debug { log.Println("len(data) =", len(m.s.data)) log.Println("highlightedItem =", m.s.highlightedItem) From 312f92168f84e7723d2c780792bb6dac16605994 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 18:06:10 +0200 Subject: [PATCH 09/23] minor changes --- cmd/cli/main.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index cb317b1..4529e22 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -133,21 +133,23 @@ func runReshCli() (string, int) { if err := g.SetKeybinding("", gocui.KeyCtrlP, gocui.ModNone, layout.Prev); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + + if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, layout.SelectPaste); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, quit); err != nil { + if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, layout.SelectExecute); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, layout.SelectExecute); err != nil { + if err := g.SetKeybinding("", gocui.KeyCtrlG, gocui.ModNone, layout.AbortPaste); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, layout.SelectPaste); err != nil { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("", gocui.KeyCtrlG, gocui.ModNone, layout.AbortPaste); err != nil { + if err := g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, quit); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlR, gocui.ModNone, layout.SwitchModes); err != nil { log.Panicln(err) } @@ -439,14 +441,18 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { printedLineLength := maxX - 4 selectedCommand := m.s.data[m.s.highlightedItem].cmdLine var statusLineHeight int = len(selectedCommand)/(printedLineLength) + 1 - statusLineHeight++ // help line - m.s.displayedItemsCount = maxY - topBoxHeight - statusLineHeight + helpLineHeight := 1 + const helpLine = "HELP: type to search, UP/DOWN to select, RIGHT to edit, ENTER to execute, CTRL+G to abort, CTRL+C/D to quit; " + + "TIP: when resh-cli is launched command line is used as initial search query" + + mainViewHeight := maxY - topBoxHeight - statusLineHeight - helpLineHeight + m.s.displayedItemsCount = mainViewHeight var index int for index < len(data) { itm := data[index] - if index == maxY-topBoxHeight-statusLineHeight { + if index == mainViewHeight { // page is full break } @@ -472,7 +478,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { index++ } // push the status line to the bottom of the page - for index < maxY-topBoxHeight-statusLineHeight { + for index < mainViewHeight { v.WriteString("\n") index++ } @@ -500,7 +506,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { idxSt += printedLineLength nextLine = true } - v.WriteString("HELP: type to search, UP/DOWN to select, RIGHT to edit, ENTER to execute, CTRL+G to abort, CTRL+C/D to quit") + v.WriteString(helpLine) if debug { log.Println("len(data) =", len(m.s.data)) log.Println("highlightedItem =", m.s.highlightedItem) From 9f85c6552ace7833a47497e16a71dbc7e7beed0a Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 23:14:10 +0200 Subject: [PATCH 10/23] dynamic location size --- cmd/cli/highlight.go | 14 ++++++++++ cmd/cli/item.go | 62 +++++++++++++++++++++++++------------------- cmd/cli/main.go | 45 +++++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 30 deletions(-) diff --git a/cmd/cli/highlight.go b/cmd/cli/highlight.go index b748a0a..a121dc9 100644 --- a/cmd/cli/highlight.go +++ b/cmd/cli/highlight.go @@ -27,6 +27,13 @@ func cleanHighlight(str string) string { return str } +func highlightHeader(str string) string { + underline := "\033[4m" + end := "\033[0m" + // no clean highlight + return underline + str + end +} + func highlightStatus(str string) string { invert := "\033[7;1m" end := "\033[0m" @@ -84,6 +91,13 @@ func highlightGit(str string) string { return greenBold + cleanHighlight(str) + end } +func doHighlightHeader(str string, minLength int) string { + if len(str) < minLength { + str = str + strings.Repeat(" ", minLength-len(str)) + } + return highlightHeader(str) +} + func doHighlightString(str string, minLength int) string { if len(str) < minLength { str = str + strings.Repeat(" ", minLength-len(str)) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index dc791ef..e9a6ed8 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -11,6 +11,8 @@ import ( "github.com/curusarn/resh/pkg/records" ) +const itemLocationLenght = 30 + type item struct { realtimeBefore float64 @@ -39,8 +41,12 @@ type itemColumns struct { date string // [host:]pwd - locationWithColor string - location string + hostWithColor string + host string + pwdTilde string + samePwd bool + //locationWithColor string + //location string // [G] [E#] flagsWithColor string @@ -124,22 +130,15 @@ func (i item) drawItemColumns() itemColumns { dateWithColor := highlightDate(date) // DISPLAY > location - location := "" - locationWithColor := "" + // DISPLAY > location > host + host := "" + hostWithColor := "" if i.differentHost { - location += i.host + ":" - locationWithColor += highlightHost(i.host) + ":" + host += i.host + ":" + hostWithColor += highlightHost(i.host) + ":" } - const locationLenght = 30 - // const locationLenght = 20 // small screenshots - pwdLength := locationLenght - len(location) + // DISPLAY > location > directory pwdTilde := strings.Replace(i.pwd, i.home, "~", 1) - location += leftCutPadString(pwdTilde, pwdLength) - if i.samePwd { - locationWithColor += highlightPwd(leftCutPadString(pwdTilde, pwdLength)) - } else { - locationWithColor += leftCutPadString(pwdTilde, pwdLength) - } // DISPLAY > flags flags := "" @@ -160,20 +159,22 @@ func (i item) drawItemColumns() itemColumns { // flags += " <" + record.GitOriginRemote + ">" // flagsWithColor += " <" + record.GitOriginRemote + ">" return itemColumns{ - date: date, - dateWithColor: dateWithColor, - location: location, - locationWithColor: locationWithColor, - flags: flags, - flagsWithColor: flagsWithColor, - cmdLine: i.cmdLine, - cmdLineWithColor: i.cmdLineWithColor, + date: date, + dateWithColor: dateWithColor, + host: host, + hostWithColor: hostWithColor, + pwdTilde: pwdTilde, + samePwd: i.samePwd, + flags: flags, + flagsWithColor: flagsWithColor, + cmdLine: i.cmdLine, + cmdLineWithColor: i.cmdLineWithColor, // score: i.score, key: i.key, } } -func (ic itemColumns) produceLine(dateLength int, flagLength int, showDate bool) (string, int) { +func (ic itemColumns) produceLine(dateLength int, locationLength int, flagLength int, showDate bool) (string, int) { line := "" if showDate { date := ic.date @@ -181,9 +182,18 @@ func (ic itemColumns) produceLine(dateLength int, flagLength int, showDate bool) line += " " date += " " } + // TODO: use strings.Repeat line += ic.dateWithColor } - line += ic.locationWithColor + // LOCATION + locationWithColor := ic.hostWithColor + pwdLength := locationLength - len(ic.host) + if ic.samePwd { + locationWithColor += highlightPwd(leftCutPadString(ic.pwdTilde, pwdLength)) + } else { + locationWithColor += leftCutPadString(ic.pwdTilde, pwdLength) + } + line += locationWithColor line += ic.flagsWithColor flags := ic.flags if flagLength < len(ic.flags) { @@ -201,7 +211,7 @@ func (ic itemColumns) produceLine(dateLength int, flagLength int, showDate bool) } line += spacer + ic.cmdLineWithColor - length := len(ic.location) + flagLength + len(spacer) + len(ic.cmdLine) + length := dateLength + locationLength + flagLength + len(spacer) + len(ic.cmdLine) return line, length } diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 4529e22..bd401b2 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -411,13 +411,38 @@ func (m manager) Layout(g *gocui.Gui) error { func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } + +func getHeader() itemColumns { + date := "TIME " + host := "HOST:" + dir := "DIRECTORY" + flags := " FLAGS" + cmdLine := "COMMAND-LINE" + return itemColumns{ + date: date, + dateWithColor: date, + host: host, + hostWithColor: host, + pwdTilde: dir, + samePwd: false, + flags: flags, + flagsWithColor: flags, + cmdLine: cmdLine, + cmdLineWithColor: cmdLine, + // score: i.score, + key: "_HEADERS_", + } +} + func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { maxX, maxY := g.Size() - var data []itemColumns + data := []itemColumns{} - longestDateLen := 0 // at least 0 - longestFlagsLen := 2 // at least 2 + header := getHeader() + longestDateLen := len(header.date) + longestLocationLen := len(header.host) + len(header.pwdTilde) + longestFlagsLen := 2 for i, itm := range m.s.data { if i == maxY { break @@ -427,10 +452,16 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { if len(ic.date) > longestDateLen { longestDateLen = len(ic.date) } + if len(ic.host)+len(ic.pwdTilde) > longestLocationLen { + longestLocationLen = len(ic.host) + len(ic.pwdTilde) + } if len(ic.flags) > longestFlagsLen { longestFlagsLen = len(ic.flags) } } + if longestLocationLen > maxX/5 { + longestLocationLen = maxX / 5 + } if m.s.highlightedItem >= len(m.s.data) { m.s.highlightedItem = len(m.s.data) - 1 @@ -449,6 +480,12 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { mainViewHeight := maxY - topBoxHeight - statusLineHeight - helpLineHeight m.s.displayedItemsCount = mainViewHeight + // header + // header := getHeader() + dispStr, _ := header.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true) + dispStr = doHighlightHeader(dispStr, maxX*2) + v.WriteString(dispStr + "\n") + var index int for index < len(data) { itm := data[index] @@ -457,7 +494,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { break } - displayStr, _ := itm.produceLine(longestDateLen, longestFlagsLen, true) + displayStr, _ := itm.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true) if m.s.highlightedItem == index { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*2) From b1651cea07fe0ff90aaf47ca0c60e96466425b6c Mon Sep 17 00:00:00 2001 From: Simon Let Date: Mon, 4 May 2020 23:15:17 +0200 Subject: [PATCH 11/23] fix help and tips --- cmd/cli/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index bd401b2..06afbf0 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -468,6 +468,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { } // status line topBoxHeight := 3 // size of the query box up top + topBoxHeight++ // headers realLineLength := maxX - 2 printedLineLength := maxX - 4 selectedCommand := m.s.data[m.s.highlightedItem].cmdLine From 8b2e45c748ba9ef189d312f16bceb07cacbca5c1 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Tue, 5 May 2020 00:27:31 +0200 Subject: [PATCH 12/23] make location column responsive, add compact rendering mode --- cmd/cli/item.go | 64 +++-------------- cmd/cli/main.go | 23 ++++-- cmd/cli/time.go | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 62 deletions(-) create mode 100644 cmd/cli/time.go diff --git a/cmd/cli/item.go b/cmd/cli/item.go index e9a6ed8..e781c55 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -65,68 +65,20 @@ func (i item) less(i2 item) bool { // reversed order return i.score > i2.score } - -func formatDatetime(tm time.Time) string { - tmSince := time.Since(tm) - hrs := tmSince.Hours() - yrs := int(hrs / (365 * 24)) - if yrs > 0 { - if yrs == 1 { - return "1 year ago" - } - return strconv.Itoa(yrs) + " years ago" - } - months := int(hrs / (30 * 24)) - if months > 0 { - if months == 1 { - return "1 month ago" - } - return strconv.Itoa(months) + " months ago" - } - days := int(hrs / 24) - if days > 0 { - if days == 1 { - return "1 day ago" - } - return strconv.Itoa(days) + " days ago" - } - hrsInt := int(hrs) - if hrsInt > 0 { - if hrsInt == 1 { - return "1 hour ago" - } - return strconv.Itoa(hrsInt) + " hours ago" - } - mins := int(hrs * 60) - if mins > 0 { - if mins == 1 { - return "1 min ago" - } - return strconv.Itoa(mins) + " mins ago" - } - secs := int(hrs * 60 * 60) - if secs > 0 { - if secs == 1 { - return "1 sec ago" - } - return strconv.Itoa(secs) + " secs ago" - } - return "now" -} - -func (i item) drawItemColumns() itemColumns { +func (i item) drawItemColumns(compactRendering bool) itemColumns { // DISPLAY // DISPLAY > date secs := int64(i.realtimeBefore) nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) tm := time.Unix(secs, nsecs) - //date := tm.Format("2006-01-02 15:04 ") - // dateLength := len("12 months ago ") // longest date - date := formatDatetime(tm) + " " - // for len(date) < dateLength { - // date = " " + date - // } + + var date string + if compactRendering { + date = formatTimeRelativeShort(tm) + " " + } else { + date = formatTimeRelativeLong(tm) + " " + } dateWithColor := highlightDate(date) // DISPLAY > location diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 06afbf0..a5dc44a 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -412,10 +412,13 @@ func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } -func getHeader() itemColumns { +func getHeader(compactRendering bool) itemColumns { date := "TIME " host := "HOST:" dir := "DIRECTORY" + if compactRendering { + dir = "DIR" + } flags := " FLAGS" cmdLine := "COMMAND-LINE" return itemColumns{ @@ -434,12 +437,19 @@ func getHeader() itemColumns { } } +const smallTerminalTresholdWidth = 110 + func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { maxX, maxY := g.Size() + compactRenderingMode := false + if maxX < smallTerminalTresholdWidth { + compactRenderingMode = true + } + data := []itemColumns{} - header := getHeader() + header := getHeader(compactRenderingMode) longestDateLen := len(header.date) longestLocationLen := len(header.host) + len(header.pwdTilde) longestFlagsLen := 2 @@ -447,7 +457,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { if i == maxY { break } - ic := itm.drawItemColumns() + ic := itm.drawItemColumns(compactRenderingMode) data = append(data, ic) if len(ic.date) > longestDateLen { longestDateLen = len(ic.date) @@ -459,8 +469,9 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { longestFlagsLen = len(ic.flags) } } - if longestLocationLen > maxX/5 { - longestLocationLen = maxX / 5 + maxLocationLen := maxX/7 + 8 + if longestLocationLen > maxLocationLen { + longestLocationLen = maxLocationLen } if m.s.highlightedItem >= len(m.s.data) { @@ -498,7 +509,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { displayStr, _ := itm.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true) if m.s.highlightedItem == index { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght - displayStr = doHighlightString(displayStr, maxX*2) + displayStr = doHighlightString(displayStr, maxX*3) if debug { log.Println("### HightlightedItem string :", displayStr) } diff --git a/cmd/cli/time.go b/cmd/cli/time.go new file mode 100644 index 0000000..77ca367 --- /dev/null +++ b/cmd/cli/time.go @@ -0,0 +1,181 @@ +package main + +import ( + "strconv" + "time" +) + +func formatTimeRelativeLongest(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 0 { + if yrs == 1 { + return "1 year ago" + } + return strconv.Itoa(yrs) + " years ago" + } + months := int(hrs / (30 * 24)) + if months > 0 { + if months == 1 { + return "1 month ago" + } + return strconv.Itoa(months) + " months ago" + } + days := int(hrs / 24) + if days > 0 { + if days == 1 { + return "1 day ago" + } + return strconv.Itoa(days) + " days ago" + } + hrsInt := int(hrs) + if hrsInt > 0 { + if hrsInt == 1 { + return "1 hour ago" + } + return strconv.Itoa(hrsInt) + " hours ago" + } + mins := int(hrs*60) % 60 + if mins > 0 { + if mins == 1 { + return "1 min ago" + } + return strconv.Itoa(mins) + " mins ago" + } + secs := int(hrs*60*60) % 60 + if secs > 0 { + if secs == 1 { + return "1 sec ago" + } + return strconv.Itoa(secs) + " secs ago" + } + return "now" +} + +func formatTimeRelativeLong(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 0 { + if yrs == 1 { + return "1 year" + } + return strconv.Itoa(yrs) + " years" + } + months := int(hrs / (30 * 24)) + if months > 0 { + if months == 1 { + return "1 month" + } + return strconv.Itoa(months) + " months" + } + days := int(hrs / 24) + if days > 0 { + if days == 1 { + return "1 day" + } + return strconv.Itoa(days) + " days" + } + hrsInt := int(hrs) + if hrsInt > 0 { + if hrsInt == 1 { + return "1 hour" + } + return strconv.Itoa(hrsInt) + " hours" + } + mins := int(hrs*60) % 60 + if mins > 0 { + if mins == 1 { + return "1 min" + } + return strconv.Itoa(mins) + " mins" + } + secs := int(hrs*60*60) % 60 + if secs > 0 { + if secs == 1 { + return "1 sec" + } + return strconv.Itoa(secs) + " secs" + } + return "now" +} + +func formatTimeMixedLongest(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 0 { + if yrs == 1 { + return "1 year ago" + } + return strconv.Itoa(yrs) + " years ago" + } + months := int(hrs / (30 * 24)) + if months > 0 { + if months == 1 { + return "1 month ago" + } + return strconv.Itoa(months) + " months ago" + } + days := int(hrs / 24) + if days > 0 { + if days == 1 { + return "1 day ago" + } + return strconv.Itoa(days) + " days ago" + } + hrsInt := int(hrs) + mins := int(hrs*60) % 60 + return strconv.Itoa(hrsInt) + ":" + strconv.Itoa(mins) +} + +func formatTimeRelativeShort(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 0 { + return strconv.Itoa(yrs) + " Y" + } + months := int(hrs / (30 * 24)) + if months > 0 { + return strconv.Itoa(months) + " M" + } + days := int(hrs / 24) + if days > 0 { + return strconv.Itoa(days) + " D" + } + hrsInt := int(hrs) + if hrsInt > 0 { + return strconv.Itoa(hrsInt) + " h" + } + mins := int(hrs*60) % 60 + if mins > 0 { + return strconv.Itoa(mins) + " m" + } + secs := int(hrs*60*60) % 60 + if secs > 0 { + return strconv.Itoa(secs) + " s" + } + return "now" +} + +func formatTimeMixedShort(tm time.Time) string { + tmSince := time.Since(tm) + hrs := tmSince.Hours() + yrs := int(hrs / (365 * 24)) + if yrs > 0 { + return strconv.Itoa(yrs) + " Y" + } + months := int(hrs / (30 * 24)) + if months > 0 { + return strconv.Itoa(months) + " M" + } + days := int(hrs / 24) + if days > 0 { + return strconv.Itoa(days) + " D" + } + hrsInt := int(hrs) + mins := int(hrs*60) % 60 + return strconv.Itoa(hrsInt) + ":" + strconv.Itoa(mins) +} From 7905252762ff38f9f0b011c0ce0000fcb259db95 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Tue, 5 May 2020 23:02:00 +0200 Subject: [PATCH 13/23] minor visual improvements --- cmd/cli/item.go | 4 ++-- cmd/cli/main.go | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index e781c55..58c4304 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -126,7 +126,7 @@ func (i item) drawItemColumns(compactRendering bool) itemColumns { } } -func (ic itemColumns) produceLine(dateLength int, locationLength int, flagLength int, showDate bool) (string, int) { +func (ic itemColumns) produceLine(dateLength int, locationLength int, flagLength int, header bool, showDate bool) (string, int) { line := "" if showDate { date := ic.date @@ -156,7 +156,7 @@ func (ic itemColumns) produceLine(dateLength int, locationLength int, flagLength flags += " " } spacer := " " - if flagLength > 5 { + if flagLength > 5 || header { // use shorter spacer // because there is likely a long flag like E130 in the view spacer = " " diff --git a/cmd/cli/main.go b/cmd/cli/main.go index a5dc44a..93b3ee4 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -453,12 +453,17 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { longestDateLen := len(header.date) longestLocationLen := len(header.host) + len(header.pwdTilde) longestFlagsLen := 2 + maxPossibleMainViewHeight := maxY - 3 - 1 - 1 - 1 // - top box - header - status - help for i, itm := range m.s.data { if i == maxY { break } ic := itm.drawItemColumns(compactRenderingMode) data = append(data, ic) + if i > maxPossibleMainViewHeight { + // do not stretch columns because of results that will end up outside of the page + continue + } if len(ic.date) > longestDateLen { longestDateLen = len(ic.date) } @@ -494,7 +499,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { // header // header := getHeader() - dispStr, _ := header.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true) + dispStr, _ := header.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true, true) dispStr = doHighlightHeader(dispStr, maxX*2) v.WriteString(dispStr + "\n") @@ -506,7 +511,7 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { break } - displayStr, _ := itm.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, true) + displayStr, _ := itm.produceLine(longestDateLen, longestLocationLen, longestFlagsLen, false, true) if m.s.highlightedItem == index { // maxX * 2 because there are escape sequences that make it hard to tell the real string lenght displayStr = doHighlightString(displayStr, maxX*3) From 2e0737fab80d95ed89244e6fdc8f71e2c5ae0fa1 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Wed, 6 May 2020 14:36:13 +0200 Subject: [PATCH 14/23] add record id --- cmd/collect/main.go | 2 ++ cmd/postcollect/main.go | 3 +++ pkg/records/records.go | 4 ++++ scripts/hooks.sh | 3 +++ 4 files changed, 12 insertions(+) diff --git a/cmd/collect/main.go b/cmd/collect/main.go index 8eba780..d2d2815 100644 --- a/cmd/collect/main.go +++ b/cmd/collect/main.go @@ -53,6 +53,7 @@ func main() { shell := flag.String("shell", "", "actual shell") uname := flag.String("uname", "", "uname") sessionID := flag.String("sessionId", "", "resh generated session id") + recordID := flag.String("recordId", "", "resh generated record id") // recall metadata recallActions := flag.String("recall-actions", "", "recall actions that took place before executing the command") @@ -195,6 +196,7 @@ func main() { Shell: *shell, Uname: *uname, SessionID: *sessionID, + RecordID: *recordID, // posix Home: *home, diff --git a/cmd/postcollect/main.go b/cmd/postcollect/main.go index d987bc5..b5cb6b0 100644 --- a/cmd/postcollect/main.go +++ b/cmd/postcollect/main.go @@ -44,6 +44,8 @@ func main() { cmdLine := flag.String("cmdLine", "", "command line") exitCode := flag.Int("exitCode", -1, "exit code") sessionID := flag.String("sessionId", "", "resh generated session id") + recordID := flag.String("recordId", "", "resh generated record id") + shlvl := flag.Int("shlvl", -1, "$SHLVL") shell := flag.String("shell", "", "actual shell") @@ -118,6 +120,7 @@ func main() { CmdLine: *cmdLine, ExitCode: *exitCode, SessionID: *sessionID, + RecordID: *recordID, Shlvl: *shlvl, Shell: *shell, diff --git a/pkg/records/records.go b/pkg/records/records.go index 9283429..8696ef3 100644 --- a/pkg/records/records.go +++ b/pkg/records/records.go @@ -23,6 +23,7 @@ type BaseRecord struct { Shell string `json:"shell"` Uname string `json:"uname"` SessionID string `json:"sessionId"` + RecordID string `json:"recordId"` // posix Home string `json:"home"` @@ -232,6 +233,9 @@ func (r *Record) Merge(r2 Record) error { if r.CmdLine != r2.CmdLine { return errors.New("Records to merge are not parts of the same records - r1:" + r.CmdLine + " r2:" + r2.CmdLine) } + if r.RecordID != r2.RecordID { + return errors.New("Records to merge do not have the same ID - r1:" + r.RecordID + " r2:" + r2.RecordID) + } // r.RealtimeBefore != r2.RealtimeBefore - can't be used because of bash-preexec runs when it's not supposed to r.ExitCode = r2.ExitCode r.PwdAfter = r2.PwdAfter diff --git a/scripts/hooks.sh b/scripts/hooks.sh index b0fd1ce..6a1d6bf 100644 --- a/scripts/hooks.sh +++ b/scripts/hooks.sh @@ -9,6 +9,7 @@ __resh_reset_variables() { __RESH_HIST_RECALL_ACTIONS="" __RESH_HIST_NO_PREFIX_MODE=0 __RESH_HIST_RECALL_STRATEGY="" + __RESH_RECORD_ID=$(__resh_get_uuid) } __resh_preexec() { @@ -81,6 +82,7 @@ __resh_collect() { -shell "$__RESH_SHELL" \ -uname "$__RESH_UNAME" \ -sessionId "$__RESH_SESSION_ID" \ + -recordId "$__RESH_RECORD_ID" \ -cols "$__RESH_COLS" \ -home "$__RESH_HOME" \ -lang "$__RESH_LANG" \ @@ -157,6 +159,7 @@ __resh_precmd() { -realtimeBefore "$__RESH_RT_BEFORE" \ -exitCode "$__RESH_EXIT_CODE" \ -sessionId "$__RESH_SESSION_ID" \ + -recordId "$__RESH_RECORD_ID" \ -shell "$__RESH_SHELL" \ -shlvl "$__RESH_SHLVL" \ -pwdAfter "$__RESH_PWD_AFTER" \ From d16d59759b2f39e4b5134e32c664a3b6f9cb04b3 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Wed, 6 May 2020 16:03:15 +0200 Subject: [PATCH 15/23] hack in support to use standard history in resh cli only use standard history when resh history is too short --- cmd/cli/item.go | 57 ++++++++++++++++++++++++++++++---------- pkg/histcli/histcli.go | 7 +++++ pkg/histfile/histfile.go | 10 +++++-- pkg/records/records.go | 10 +++++++ 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 58c4304..b30c513 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -14,6 +14,8 @@ import ( const itemLocationLenght = 30 type item struct { + isRaw bool + realtimeBefore float64 // [host:]pwd @@ -65,7 +67,23 @@ func (i item) less(i2 item) bool { // reversed order return i.score > i2.score } + func (i item) drawItemColumns(compactRendering bool) itemColumns { + if i.isRaw { + notAvailable := "n/a" + return itemColumns{ + date: notAvailable + " ", + dateWithColor: notAvailable + " ", + // dateWithColor: highlightDate(notAvailable) + " ", + host: "", + hostWithColor: "", + pwdTilde: notAvailable, + cmdLine: i.cmdLine, + cmdLineWithColor: i.cmdLineWithColor, + // score: i.score, + key: i.key, + } + } // DISPLAY // DISPLAY > date @@ -80,7 +98,6 @@ func (i item) drawItemColumns(compactRendering bool) itemColumns { date = formatTimeRelativeLong(tm) + " " } dateWithColor := highlightDate(date) - // DISPLAY > location // DISPLAY > location > host host := "" @@ -232,6 +249,31 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool // NO continue } } + // DISPLAY > cmdline + + // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" + cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";") + cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") + + // KEY for deduplication + + key := record.CmdLine + // NOTE: since we import standard history we need a compatible key without metadata + /* + unlikelySeparator := "|||||" + key := record.CmdLine + unlikelySeparator + record.Pwd + unlikelySeparator + + record.GitOriginRemote + unlikelySeparator + record.Host + */ + if record.IsRaw { + return item{ + isRaw: true, + + cmdLine: cmdLine, + cmdLineWithColor: cmdLineWithColor, + score: score, + key: key, + }, nil + } // actual pwd matches // N terms can only produce: // -> N matches against the command @@ -265,19 +307,6 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool return item{}, errors.New("no match for given record and query") } - // KEY for deduplication - - unlikelySeparator := "|||||" - key := record.CmdLine + unlikelySeparator + record.Pwd + unlikelySeparator + - record.GitOriginRemote + unlikelySeparator + record.Host - // + strconv.Itoa(record.ExitCode) + unlikelySeparator - - // DISPLAY > cmdline - - // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" - cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";") - cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") - it := item{ realtimeBefore: record.RealtimeBefore, diff --git a/pkg/histcli/histcli.go b/pkg/histcli/histcli.go index 2abb799..f9b6611 100644 --- a/pkg/histcli/histcli.go +++ b/pkg/histcli/histcli.go @@ -22,3 +22,10 @@ func (h *Histcli) AddRecord(record records.Record) { h.List = append(h.List, cli) } + +// AddCmdLine to the histcli +func (h *Histcli) AddCmdLine(cmdline string) { + cli := records.NewCliRecordFromCmdLine(cmdline) + + h.List = append(h.List, cli) +} diff --git a/pkg/histfile/histfile.go b/pkg/histfile/histfile.go index a72da7d..b1d8019 100644 --- a/pkg/histfile/histfile.go +++ b/pkg/histfile/histfile.go @@ -50,7 +50,13 @@ func New(input chan records.Record, sessionsToDrop chan string, } // load records from resh history, reverse, enrich and save -func (h *Histfile) loadFullRecords(recs []records.Record) { +func (h *Histfile) loadCliRecords(recs []records.Record) { + for _, cmdline := range h.bashCmdLines.List { + h.cliRecords.AddCmdLine(cmdline) + } + for _, cmdline := range h.zshCmdLines.List { + h.cliRecords.AddCmdLine(cmdline) + } for i := len(recs) - 1; i >= 0; i-- { rec := recs[i] h.cliRecords.AddRecord(rec) @@ -82,7 +88,7 @@ func (h *Histfile) loadHistory(bashHistoryPath, zshHistoryPath string, maxInitHi } log.Println("histfile: Loading resh history from file ...") history := records.LoadFromFile(h.historyPath, math.MaxInt32) - go h.loadFullRecords(history) + go h.loadCliRecords(history) // NOTE: keeping this weird interface for now because we might use it in the future // when we only load bash or zsh history reshCmdLines := loadCmdLines(history) diff --git a/pkg/records/records.go b/pkg/records/records.go index 8696ef3..c4e91db 100644 --- a/pkg/records/records.go +++ b/pkg/records/records.go @@ -150,6 +150,7 @@ type SlimRecord struct { // CliRecord used for sending records to RESH-CLI type CliRecord struct { + IsRaw bool `json:"isRaw"` SessionID string `json:"sessionId"` CmdLine string `json:"cmdLine"` @@ -164,9 +165,18 @@ type CliRecord struct { // RealtimeDuration float64 `json:"realtimeDuration"` } +// NewCliRecordFromCmdLine from EnrichedRecord +func NewCliRecordFromCmdLine(cmdLine string) CliRecord { + return CliRecord{ + IsRaw: true, + CmdLine: cmdLine, + } +} + // NewCliRecord from EnrichedRecord func NewCliRecord(r EnrichedRecord) CliRecord { return CliRecord{ + IsRaw: false, SessionID: r.SessionID, CmdLine: r.CmdLine, Host: r.Host, From 1e891198b73c284a16d4f51ec59702afa1e33532 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Fri, 8 May 2020 21:59:48 +0200 Subject: [PATCH 16/23] make query a bit more important in score --- cmd/cli/item.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index b30c513..1d1fb24 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -115,6 +115,7 @@ func (i item) drawItemColumns(compactRendering bool) itemColumns { if debug { hitsStr := fmt.Sprintf("%.1f", i.score) flags += " S" + hitsStr + flagsWithColor += " S" + hitsStr } if i.sameGitRepo { flags += " G" @@ -218,12 +219,12 @@ func properMatch(str, term, padChar string) bool { // newItemFromRecordForQuery creates new item from record based on given query // returns error if the query doesn't match the record func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool) (item, error) { - const hitScore = 1.0 + const hitScore = 1.2 const hitScoreConsecutive = 0.1 const properMatchScore = 0.3 const actualPwdScore = 0.9 - const nonZeroExitCodeScorePenalty = 0.5 - const sameGitRepoScore = 0.7 + const nonZeroExitCodeScorePenalty = 0.4 + const sameGitRepoScore = 0.6 // const sameGitRepoScoreExtra = 0.0 const differentHostScorePenalty = 0.2 From 45cded18f217427ce6da37dace178ad8d23ad1b3 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Fri, 8 May 2020 22:00:35 +0200 Subject: [PATCH 17/23] explicitly include time in score --- cmd/cli/item.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 1d1fb24..50f2bcb 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -228,6 +228,7 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool // const sameGitRepoScoreExtra = 0.0 const differentHostScorePenalty = 0.2 + const timeScoreCoef = 1e-13 // nonZeroExitCodeScorePenalty + differentHostScorePenalty score := 0.0 @@ -307,6 +308,7 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool if score <= 0 && !anyHit { return item{}, errors.New("no match for given record and query") } + score += record.RealtimeBefore * timeScoreCoef it := item{ realtimeBefore: record.RealtimeBefore, From 643805e245835c747a9d71813f4d09ffa00d48b4 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 9 May 2020 21:10:18 +0200 Subject: [PATCH 18/23] add time and pwd to the status line --- cmd/cli/item.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ cmd/cli/main.go | 29 ++++------------------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 50f2bcb..55c2b32 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -68,6 +68,52 @@ func (i item) less(i2 item) bool { return i.score > i2.score } +func splitStatusLineToLines(statusLine string, printedLineLength, realLineLength int) []string { + var statusLineSlice []string + // status line + var idxSt, idxEnd int + var nextLine bool + tab := " " + tabSize := len(tab) + for idxSt < len(statusLine) { + idxEnd = idxSt + printedLineLength + if nextLine { + idxEnd -= tabSize + } + + if idxEnd > len(statusLine) { + idxEnd = len(statusLine) + } + str := statusLine[idxSt:idxEnd] + + indent := " " + if nextLine { + indent += tab + } + statusLineSlice = append(statusLineSlice, highlightStatus(rightCutPadString(indent+str, realLineLength))+"\n") + idxSt += printedLineLength + nextLine = true + } + return statusLineSlice +} + +func (i item) drawStatusLine(compactRendering bool, printedLineLength, realLineLength int) []string { + if i.isRaw { + return splitStatusLineToLines(" "+i.cmdLine, printedLineLength, realLineLength) + } + secs := int64(i.realtimeBefore) + nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) + tm := time.Unix(secs, nsecs) + const timeFormat = "2006-01-02 15:04:05" + timeString := tm.Format(timeFormat) + + pwdTilde := strings.Replace(i.pwd, i.home, "~", 1) + + separator := " " + stLine := timeString + separator + pwdTilde + separator + i.cmdLine + return splitStatusLineToLines(stLine, printedLineLength, realLineLength) +} + func (i item) drawItemColumns(compactRendering bool) itemColumns { if i.isRaw { notAvailable := "n/a" diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 93b3ee4..c5c337e 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -487,8 +487,8 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { topBoxHeight++ // headers realLineLength := maxX - 2 printedLineLength := maxX - 4 - selectedCommand := m.s.data[m.s.highlightedItem].cmdLine - var statusLineHeight int = len(selectedCommand)/(printedLineLength) + 1 + statusLine := m.s.data[m.s.highlightedItem].drawStatusLine(compactRenderingMode, printedLineLength, realLineLength) + var statusLineHeight int = len(statusLine) + 1 // help line helpLineHeight := 1 const helpLine = "HELP: type to search, UP/DOWN to select, RIGHT to edit, ENTER to execute, CTRL+G to abort, CTRL+C/D to quit; " + @@ -536,29 +536,8 @@ func (m manager) normalMode(g *gocui.Gui, v *gocui.View) error { v.WriteString("\n") index++ } - // status line - var idxSt, idxEnd int - var nextLine bool - tab := " " - tabSize := len(tab) - for idxSt < len(selectedCommand) { - idxEnd = idxSt + printedLineLength - if nextLine { - idxEnd -= tabSize - } - - if idxEnd > len(selectedCommand) { - idxEnd = len(selectedCommand) - } - str := selectedCommand[idxSt:idxEnd] - - indent := " " - if nextLine { - indent += tab - } - v.WriteString(highlightStatus(rightCutPadString(indent+str, realLineLength)) + "\n") - idxSt += printedLineLength - nextLine = true + for _, line := range statusLine { + v.WriteString(line) } v.WriteString(helpLine) if debug { From a34b4b2d88857c54e4b8af85063a00f27ac3e393 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 9 May 2020 21:15:08 +0200 Subject: [PATCH 19/23] add host to the status line --- cmd/cli/item.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index 55c2b32..e8d6e55 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -110,7 +110,7 @@ func (i item) drawStatusLine(compactRendering bool, printedLineLength, realLineL pwdTilde := strings.Replace(i.pwd, i.home, "~", 1) separator := " " - stLine := timeString + separator + pwdTilde + separator + i.cmdLine + stLine := timeString + separator + i.host + ":" + pwdTilde + separator + i.cmdLine return splitStatusLineToLines(stLine, printedLineLength, realLineLength) } From 92c15c6a0b1812173a69fffb96de37ca26c94c09 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sat, 9 May 2020 22:13:13 +0200 Subject: [PATCH 20/23] minor fix --- cmd/cli/item.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index e8d6e55..e8f8ed7 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -99,7 +99,7 @@ func splitStatusLineToLines(statusLine string, printedLineLength, realLineLength func (i item) drawStatusLine(compactRendering bool, printedLineLength, realLineLength int) []string { if i.isRaw { - return splitStatusLineToLines(" "+i.cmdLine, printedLineLength, realLineLength) + return splitStatusLineToLines(i.cmdLine, printedLineLength, realLineLength) } secs := int64(i.realtimeBefore) nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) From 1910eb93d53dcba4cc6d25bebf19e63e5dd36e9f Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sun, 10 May 2020 15:18:33 +0200 Subject: [PATCH 21/23] hotfix highlighting issues when you highlight incrementally you keep adding escape codes to the string these escape codes contain chracters that next round of highlighting can wrap in a new set of escape codes this breaks the escape codes and produces mess on the view hotfix: sort the terms so that the short ones are highlighted first --- cmd/cli/query.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/cli/query.go b/cmd/cli/query.go index df72873..7fd8d4f 100644 --- a/cmd/cli/query.go +++ b/cmd/cli/query.go @@ -2,6 +2,7 @@ package main import ( "log" + "sort" "strings" ) @@ -54,6 +55,7 @@ func newQueryFromString(queryInput string, host string, pwd string, gitOriginRem log.Println("QUERY filtered terms =" + logStr) log.Println("QUERY pwd =" + pwd) } + sort.SliceStable(terms, func(i, j int) bool { return len(terms[i]) < len(terms[j]) }) return query{ terms: terms, host: host, From cfaec01c5e34f79a0d94eed7031eb0d4a99f79a8 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sun, 10 May 2020 15:18:50 +0200 Subject: [PATCH 22/23] adjust scoring weights --- cmd/cli/item.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/cmd/cli/item.go b/cmd/cli/item.go index e8f8ed7..b72a919 100644 --- a/cmd/cli/item.go +++ b/cmd/cli/item.go @@ -265,14 +265,24 @@ func properMatch(str, term, padChar string) bool { // newItemFromRecordForQuery creates new item from record based on given query // returns error if the query doesn't match the record func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool) (item, error) { - const hitScore = 1.2 - const hitScoreConsecutive = 0.1 - const properMatchScore = 0.3 - const actualPwdScore = 0.9 - const nonZeroExitCodeScorePenalty = 0.4 - const sameGitRepoScore = 0.6 - // const sameGitRepoScoreExtra = 0.0 - const differentHostScorePenalty = 0.2 + // Use numbers that won't add up to same score for any number of query words + const hitScore = 1.307 + const properMatchScore = 0.603 + const hitScoreConsecutive = 0.002 + + // Host penalty + var actualPwdScore = 0.9 + var sameGitRepoScore = 0.8 + var nonZeroExitCodeScorePenalty = 0.4 + var differentHostScorePenalty = 0.2 + + reduceHostPenalty := false + if reduceHostPenalty { + actualPwdScore = 0.9 + sameGitRepoScore = 0.7 + nonZeroExitCodeScorePenalty = 0.4 + differentHostScorePenalty = 0.1 + } const timeScoreCoef = 1e-13 // nonZeroExitCodeScorePenalty + differentHostScorePenalty @@ -286,7 +296,8 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool anyHit = true if termHit == false { score += hitScore - } else { + } else if len(term) > 1 { + // only count consecutive matches for queries longer than 1 score += hitScoreConsecutive } termHit = true @@ -294,7 +305,6 @@ func newItemFromRecordForQuery(record records.CliRecord, query query, debug bool score += properMatchScore } cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) - // NO continue } } // DISPLAY > cmdline From ef7b08dd2ea184427b2fd703cbbf5a514a72c71b Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sun, 10 May 2020 15:42:31 +0200 Subject: [PATCH 23/23] turn on resh-cli by default --- conf/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/config.toml b/conf/config.toml index 2e0adc2..3cd028b 100644 --- a/conf/config.toml +++ b/conf/config.toml @@ -4,4 +4,4 @@ sesshistInitHistorySize = 1000 debug = false bindArrowKeysBash = false bindArrowKeysZsh = true -bindControlR = false +bindControlR = true