diff --git a/cmd/cli/highlight.go b/cmd/cli/highlight.go new file mode 100644 index 0000000..775d782 --- /dev/null +++ b/cmd/cli/highlight.go @@ -0,0 +1,79 @@ +package main + +import ( + "strconv" + "strings" +) + +func cleanHighlight(str string) string { + prefix := "\033[" + + invert := "\033[7;1m" + invertGreen := "\033[32;7;1m" + end := "\033[0m" + replace := []string{invert, invertGreen, end} + for i := 30; i < 48; i++ { + base := prefix + strconv.Itoa(i) + normal := base + "m" + bold := base + ";1m" + replace = append(replace, normal, bold) + } + if strings.Contains(str, prefix) == false { + return str + } + for _, escSeq := range replace { + str = strings.ReplaceAll(str, escSeq, "") + } + return str +} + +func highlightSelected(str string) string { + // template "\033[3%d;%dm" + // invertGreen := "\033[32;7;1m" + invert := "\033[7;1m" + end := "\033[0m" + return invert + cleanHighlight(str) + end +} + +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" + blueBold := "\033[34;1m" + end := "\033[0m" + return blueBold + cleanHighlight(str) + end +} + +func highlightMatch(str string) string { + // 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" + end := "\033[0m" + return redBold + cleanHighlight(str) + end +} + +func highlightGit(str string) string { + // template "\033[3%d;%dm" + greenBold := "\033[32;1m" + end := "\033[0m" + return greenBold + cleanHighlight(str) + end +} + +func doHighlightString(str string, minLength int) string { + if len(str) < minLength { + str = str + strings.Repeat(" ", minLength-len(str)) + } + return highlightSelected(str) +} diff --git a/cmd/cli/item.go b/cmd/cli/item.go new file mode 100644 index 0000000..c3e11bb --- /dev/null +++ b/cmd/cli/item.go @@ -0,0 +1,278 @@ +package main + +import ( + "errors" + "fmt" + "log" + "strconv" + "strings" + + "github.com/curusarn/resh/pkg/records" +) + +type item struct { + // dateWithColor string + // date string + + // [host:]pwd + locationWithColor string + location string + + // [G] [E#] + flagsWithColor string + flags string + + cmdLineWithColor string + cmdLine string + + hits float64 + + key string + // cmdLineRaw string +} + +func (i item) less(i2 item) bool { + // reversed order + return i.hits > i2.hits +} + +func (i item) produceLine(flagLength int) (string, int) { + line := "" + 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) + } + for len(flags) < flagLength { + line += " " + flags += " " + } + spacer := " " + if flagLength > 5 { + // use shorter spacer + // because there is likely a long flag like E130 in the view + spacer = " " + } + line += spacer + i.cmdLineWithColor + + length := len(i.location) + flagLength + len(spacer) + len(i.cmdLine) + return line, length +} + +func leftCutPadString(str string, newLen int) string { + dots := "…" + strLen := len(str) + if newLen > strLen { + return strings.Repeat(" ", newLen-strLen) + str + } else if newLen < strLen { + return dots + str[strLen-newLen+1:] + } + return str +} + +func rightCutPadString(str string, newLen int) string { + dots := "…" + strLen := len(str) + if newLen > strLen { + return str + strings.Repeat(" ", newLen-strLen) + } else if newLen < strLen { + return str[:newLen-1] + dots + } + return str +} + +// proper match for path is when whole directory is matched +// proper match for command is when term matches word delimeted by whitespace +func properMatch(str, term, padChar string) bool { + if strings.Contains(padChar+str+padChar, padChar+term+padChar) { + return true + } + return false +} + +// newItemFromRecordForQuery creates new item from record based on given query +// returns error if the query doesn't match the record +func newItemFromRecordForQuery(record records.EnrichedRecord, query query, debug bool) (item, error) { + const hitScore = 1.0 + const hitScoreConsecutive = 0.1 + const properMatchScore = 0.3 + const actualPwdScore = 0.9 + const nonZeroExitCodeScorePenalty = 0.5 + const sameGitRepoScore = 0.7 + // const sameGitRepoScoreExtra = 0.0 + const differentHostScorePenalty = 0.2 + + // nonZeroExitCodeScorePenalty + differentHostScorePenalty + + hits := 0.0 + anyHit := false + cmd := record.CmdLine + for _, term := range query.terms { + termHit := false + if strings.Contains(record.CmdLine, term) { + anyHit = true + if termHit == false { + hits += hitScore + } else { + hits += hitScoreConsecutive + } + termHit = true + if properMatch(cmd, term, " ") { + hits += properMatchScore + } + cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) + // NO continue + } + } + // actual pwd matches + // N terms can only produce: + // -> N matches against the command + // -> 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 { + anyHit = true + samePwd = true + hits += actualPwdScore + } else if sameGitRepo { + anyHit = true + hits += sameGitRepoScore + } + + 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") + } + + // KEY for deduplication + + 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 { + locationWithColor += leftCutPadString(pwdTilde, pwdLength) + } + + // DISPLAY > flags + flags := "" + flagsWithColor := "" + if debug { + hitsStr := fmt.Sprintf("%.1f", hits) + 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)) + } + + // DISPLAY > cmdline + + // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" + cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";") + cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") + + it := item{ + location: location, + locationWithColor: locationWithColor, + flags: flags, + flagsWithColor: flagsWithColor, + cmdLine: cmdLine, + cmdLineWithColor: cmdLineWithColor, + hits: hits, + key: key, + } + return it, nil +} + +type rawItem struct { + cmdLineWithColor string + cmdLine string + + hits float64 + + key string + // cmdLineRaw string +} + +// newRawItemFromRecordForQuery creates new item from record based on given query +// returns error if the query doesn't match the record +func newRawItemFromRecordForQuery(record records.EnrichedRecord, terms []string, debug bool) (rawItem, error) { + const hitScore = 1.0 + const hitScoreConsecutive = 0.1 + const properMatchScore = 0.3 + + hits := 0.0 + anyHit := false + cmd := record.CmdLine + for _, term := range terms { + termHit := false + if strings.Contains(record.CmdLine, term) { + anyHit = true + if termHit == false { + hits += hitScore + } else { + hits += hitScoreConsecutive + } + termHit = true + if properMatch(cmd, term, " ") { + hits += properMatchScore + } + cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) + // NO continue + } + } + _ = anyHit + // KEY for deduplication + key := record.CmdLine + + // DISPLAY > cmdline + + // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" + cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";") + cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") + + it := rawItem{ + cmdLine: cmdLine, + cmdLineWithColor: cmdLineWithColor, + hits: hits, + key: key, + } + return it, nil +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index b50e431..8d789ce 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -3,7 +3,6 @@ package main import ( "bytes" "encoding/json" - "errors" "flag" "fmt" "io/ioutil" @@ -143,8 +142,12 @@ func runReshCli() (string, int) { if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, layout.SelectPaste); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlR, gocui.ModNone, layout.SwitchModes); err != nil { + log.Panicln(err) + } layout.UpdateData(*query) + layout.UpdateRawData(*query) err = g.MainLoop() if err != nil && gocui.IsQuit(err) == false { log.Panicln(err) @@ -152,361 +155,15 @@ func runReshCli() (string, int) { return layout.s.output, layout.s.exitCode } -func leftCutPadString(str string, newLen int) string { - dots := "…" - strLen := len(str) - if newLen > strLen { - return strings.Repeat(" ", newLen-strLen) + str - } else if newLen < strLen { - return dots + str[strLen-newLen+1:] - } - return str -} - -func rightCutPadString(str string, newLen int) string { - dots := "…" - strLen := len(str) - if newLen > strLen { - return str + strings.Repeat(" ", newLen-strLen) - } else if newLen < strLen { - return str[:newLen-1] + dots - } - return str -} - -func cleanHighlight(str string) string { - prefix := "\033[" - - invert := "\033[32;7;1m" - end := "\033[0m" - replace := []string{invert, end} - for i := 30; i < 48; i++ { - base := prefix + strconv.Itoa(i) - normal := base + "m" - bold := base + ";1m" - replace = append(replace, normal, bold) - } - if strings.Contains(str, prefix) == false { - return str - } - for _, escSeq := range replace { - str = strings.ReplaceAll(str, escSeq, "") - } - return str -} - -func highlightSelected(str string) string { - // template "\033[3%d;%dm" - invert := "\033[32;7;1m" - end := "\033[0m" - return invert + cleanHighlight(str) + end -} - -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" - blueBold := "\033[34;1m" - end := "\033[0m" - return blueBold + cleanHighlight(str) + end -} - -func highlightMatch(str string) string { - // 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" - end := "\033[0m" - return redBold + cleanHighlight(str) + end -} - -func highlightGit(str string) string { - // template "\033[3%d;%dm" - greenBold := "\033[32;1m" - end := "\033[0m" - return greenBold + cleanHighlight(str) + end -} - -func toString(record records.EnrichedRecord, lineLength int) string { - dirColWidth := 24 // make this dynamic somehow - return leftCutPadString(strings.Replace(record.Pwd, record.Home, "~", 1), dirColWidth) + " " + - rightCutPadString(strings.ReplaceAll(record.CmdLine, "\n", "; "), lineLength-dirColWidth-3) + "\n" -} - -type query struct { - terms []string - host string - pwd string - gitOriginRemote string - // pwdTilde string -} - -func isValidTerm(term string) bool { - if len(term) == 0 { - return false - } - if strings.Contains(term, " ") { - return false - } - return true -} - -func filterTerms(terms []string) []string { - var newTerms []string - for _, term := range terms { - if isValidTerm(term) { - newTerms = append(newTerms, term) - } - } - return newTerms -} - -func newQueryFromString(queryInput string, host string, pwd string, gitOriginRemote string) query { - if debug { - log.Println("QUERY input = <" + queryInput + ">") - } - terms := strings.Fields(queryInput) - var logStr string - for _, term := range terms { - logStr += " <" + term + ">" - } - if debug { - log.Println("QUERY raw terms =" + logStr) - } - terms = filterTerms(terms) - logStr = "" - for _, term := range terms { - logStr += " <" + term + ">" - } - if debug { - log.Println("QUERY filtered terms =" + logStr) - log.Println("QUERY pwd =" + pwd) - } - return query{ - terms: terms, - host: host, - pwd: pwd, - gitOriginRemote: gitOriginRemote, - } -} - -type item struct { - // dateWithColor string - // date string - - // [host:]pwd - locationWithColor string - location string - - // [G] [E#] - flagsWithColor string - flags string - - cmdLineWithColor string - cmdLine string - - hits float64 - - key string - // cmdLineRaw string -} - -func (i item) less(i2 item) bool { - // reversed order - return i.hits > i2.hits -} - -func (i item) produceLine(flagLength int) (string, int) { - line := "" - 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) - } - for len(flags) < flagLength { - line += " " - flags += " " - } - spacer := " " - if flagLength > 5 { - // use shorter spacer - // because there is likely a long flag like E130 in the view - spacer = " " - } - line += spacer + i.cmdLineWithColor - - length := len(i.location) + flagLength + len(spacer) + len(i.cmdLine) - return line, length -} - -// func (i item) equals(i2 item) bool { -// return i.cmdLine == i2.cmdLine && i.pwd == i2.pwd -// } - -// proper match for path is when whole directory is matched -// proper match for command is when term matches word delimeted by whitespace -func properMatch(str, term, padChar string) bool { - if strings.Contains(padChar+str+padChar, padChar+term+padChar) { - return true - } - return false -} - -// newItemFromRecordForQuery creates new item from record based on given query -// returns error if the query doesn't match the record -func newItemFromRecordForQuery(record records.EnrichedRecord, query query, debug bool) (item, error) { - const hitScore = 1.0 - const hitScoreConsecutive = 0.1 - const properMatchScore = 0.3 - const actualPwdScore = 0.9 - const nonZeroExitCodeScorePenalty = 0.5 - const sameGitRepoScore = 0.7 - // const sameGitRepoScoreExtra = 0.0 - const differentHostScorePenalty = 0.2 - - // nonZeroExitCodeScorePenalty + differentHostScorePenalty - - hits := 0.0 - anyHit := false - cmd := record.CmdLine - for _, term := range query.terms { - termHit := false - if strings.Contains(record.CmdLine, term) { - anyHit = true - if termHit == false { - hits += hitScore - } else { - hits += hitScoreConsecutive - } - termHit = true - if properMatch(cmd, term, " ") { - hits += properMatchScore - } - cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) - // NO continue - } - } - // actual pwd matches - // N terms can only produce: - // -> N matches against the command - // -> 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 { - anyHit = true - samePwd = true - hits += actualPwdScore - } else if sameGitRepo { - anyHit = true - hits += sameGitRepoScore - } - - 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") - } - - // KEY for deduplication - - 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 { - locationWithColor += leftCutPadString(pwdTilde, pwdLength) - } - - // DISPLAY > flags - flags := "" - flagsWithColor := "" - if debug { - hitsStr := fmt.Sprintf("%.1f", hits) - 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)) - } - - // DISPLAY > cmdline - - // cmd := "<" + strings.ReplaceAll(record.CmdLine, "\n", ";") + ">" - cmdLine := strings.ReplaceAll(record.CmdLine, "\n", ";") - cmdLineWithColor := strings.ReplaceAll(cmd, "\n", ";") - - it := item{ - location: location, - locationWithColor: locationWithColor, - flags: flags, - flagsWithColor: flagsWithColor, - cmdLine: cmdLine, - cmdLineWithColor: cmdLineWithColor, - hits: hits, - key: key, - } - return it, nil -} - -func doHighlightString(str string, minLength int) string { - if len(str) < minLength { - str = str + strings.Repeat(" ", minLength-len(str)) - } - return highlightSelected(str) -} - type state struct { lock sync.Mutex fullRecords []records.EnrichedRecord data []item + rawData []rawItem highlightedItem int + rawMode bool + initialQuery string output string @@ -592,8 +249,58 @@ func (m manager) UpdateData(input string) { } } +func (m manager) UpdateRawData(input string) { + if debug { + log.Println("EDIT start") + log.Println("len(fullRecords) =", len(m.s.fullRecords)) + log.Println("len(data) =", len(m.s.data)) + } + query := getRawTermsFromString(input) + var data []rawItem + itemSet := make(map[string]bool) + m.s.lock.Lock() + defer m.s.lock.Unlock() + for _, rec := range m.s.fullRecords { + itm, err := newRawItemFromRecordForQuery(rec, query, m.config.Debug) + if err != nil { + // records didn't match the query + // log.Println(" * continue (no match)", rec.Pwd) + continue + } + if itemSet[itm.key] { + // log.Println(" * continue (already present)", itm.key(), itm.pwd) + continue + } + itemSet[itm.key] = true + data = append(data, itm) + // log.Println("DATA =", itm.display) + } + if debug { + log.Println("len(tmpdata) =", len(data)) + } + sort.SliceStable(data, func(p, q int) bool { + return data[p].hits > data[q].hits + }) + m.s.rawData = nil + for _, itm := range data { + if len(m.s.rawData) > 420 { + break + } + m.s.rawData = append(m.s.rawData, itm) + } + m.s.highlightedItem = 0 + if debug { + log.Println("len(fullRecords) =", len(m.s.fullRecords)) + log.Println("len(data) =", len(m.s.data)) + log.Println("EDIT end") + } +} func (m manager) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { gocui.DefaultEditor.Edit(v, key, ch, mod) + if m.s.rawMode { + m.UpdateRawData(v.Buffer()) + return + } m.UpdateData(v.Buffer()) } @@ -616,6 +323,19 @@ func (m manager) Prev(g *gocui.Gui, v *gocui.View) error { return nil } +func (m manager) SwitchModes(g *gocui.Gui, v *gocui.View) error { + m.s.lock.Lock() + m.s.rawMode = !m.s.rawMode + m.s.lock.Unlock() + + if m.s.rawMode { + m.UpdateRawData(v.Buffer()) + return nil + } + m.UpdateData(v.Buffer()) + return nil +} + func (m manager) Layout(g *gocui.Gui) error { var b byte maxX, maxY := g.Size() @@ -627,7 +347,11 @@ func (m manager) Layout(g *gocui.Gui) error { v.Editable = true v.Editor = m - v.Title = "resh cli" + if m.s.rawMode { + v.Title = " RESH CLI - NON-CONTEXTUAL \"RAW\" MODE " + } else { + v.Title = " RESH CLI - CONTEXTUAL MODE " + } g.SetCurrentView("input") @@ -648,6 +372,53 @@ func (m manager) Layout(g *gocui.Gui) error { v.Clear() v.Rewind() + if m.s.rawMode { + return m.rawMode(g, v) + } + return m.normalMode(g, v) +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} + +// SendDumpMsg to daemon +func SendDumpMsg(m msg.DumpMsg, port string) msg.DumpResponse { + 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.DumpResponse{} + 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() + longestFlagsLen := 2 // at least 2 for i, itm := range m.s.data { if i == maxY { @@ -694,40 +465,41 @@ func (m manager) Layout(g *gocui.Gui) error { return nil } -func quit(g *gocui.Gui, v *gocui.View) error { - return gocui.ErrQuit -} - -// SendDumpMsg to daemon -func SendDumpMsg(m msg.DumpMsg, port string) msg.DumpResponse { - 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 :(") - } +func (m manager) rawMode(g *gocui.Gui, v *gocui.View) error { + maxX, maxY := g.Size() - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal("read response error") + for i, itm := range m.s.rawData { + if i == maxY { + if debug { + log.Println(maxY) + } + break + } + displayStr := itm.cmdLineWithColor + if m.s.highlightedItem == i { + // use actual min requried length instead of 420 constant + displayStr = doHighlightString(displayStr, maxX*2) + if debug { + log.Println("### HightlightedItem string :", displayStr) + } + } else if debug { + log.Println(displayStr) + } + if strings.Contains(displayStr, "\n") { + log.Println("display string contained \\n") + displayStr = strings.ReplaceAll(displayStr, "\n", "#") + if debug { + log.Println("display string contained \\n") + } + } + v.WriteString(displayStr + "\n") + // if m.s.highlightedItem == i { + // v.SetHighlight(m.s.highlightedItem, true) + // } } - // log.Println(string(body)) - response := msg.DumpResponse{} - err = json.Unmarshal(body, &response) - if err != nil { - log.Fatal("unmarshal resp error: ", err) + if debug { + log.Println("len(data) =", len(m.s.data)) + log.Println("highlightedItem =", m.s.highlightedItem) } - return response + return nil } diff --git a/cmd/cli/query.go b/cmd/cli/query.go new file mode 100644 index 0000000..df72873 --- /dev/null +++ b/cmd/cli/query.go @@ -0,0 +1,83 @@ +package main + +import ( + "log" + "strings" +) + +type query struct { + terms []string + host string + pwd string + gitOriginRemote string + // pwdTilde string +} + +func isValidTerm(term string) bool { + if len(term) == 0 { + return false + } + if strings.Contains(term, " ") { + return false + } + return true +} + +func filterTerms(terms []string) []string { + var newTerms []string + for _, term := range terms { + if isValidTerm(term) { + newTerms = append(newTerms, term) + } + } + return newTerms +} + +func newQueryFromString(queryInput string, host string, pwd string, gitOriginRemote string) query { + if debug { + log.Println("QUERY input = <" + queryInput + ">") + } + terms := strings.Fields(queryInput) + var logStr string + for _, term := range terms { + logStr += " <" + term + ">" + } + if debug { + log.Println("QUERY raw terms =" + logStr) + } + terms = filterTerms(terms) + logStr = "" + for _, term := range terms { + logStr += " <" + term + ">" + } + if debug { + log.Println("QUERY filtered terms =" + logStr) + log.Println("QUERY pwd =" + pwd) + } + return query{ + terms: terms, + host: host, + pwd: pwd, + gitOriginRemote: gitOriginRemote, + } +} + +func getRawTermsFromString(queryInput string) []string { + if debug { + log.Println("QUERY input = <" + queryInput + ">") + } + terms := strings.Fields(queryInput) + var logStr string + for _, term := range terms { + logStr += " <" + term + ">" + } + if debug { + log.Println("QUERY raw terms =" + logStr) + } + terms = filterTerms(terms) + logStr = "" + for _, term := range terms { + logStr += " <" + term + ">" + } + return terms +}