mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-05 04:36:48 +02:00
Add config option to filter out duplicate history entries as requested in #10
This commit is contained in:
parent
83ca625ad9
commit
6dea8a989e
11
README.md
11
README.md
@ -71,7 +71,6 @@ hishtory config-set displayed-columns CWD Command
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Custom Columns</summary>
|
<summary>Custom Columns</summary>
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ You can create custom column definitions that are populated from arbitrary comma
|
|||||||
hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true'
|
hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true'
|
||||||
hishtory config-add displayed-columns git_remote
|
hishtory config-add displayed-columns git_remote
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -89,6 +87,15 @@ hishtory config-add displayed-columns git_remote
|
|||||||
If you'd like to disable the control-R integration in your shell, you can do so by running `hishtory config-set enable-control-r false`.
|
If you'd like to disable the control-R integration in your shell, you can do so by running `hishtory config-set enable-control-r false`.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Filtering duplicate entries</summary>
|
||||||
|
By default, hishtory query will show all results even if this includes duplicate history entries. This helps you keep track of how many times you've run a command and in what contexts. If you'd rather disable this so that hiSHtory won't show duplicate entries, you can run:
|
||||||
|
|
||||||
|
```
|
||||||
|
hishtory config-set filter-duplicate-commands true
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Offline Install</summary>
|
<summary>Offline Install</summary>
|
||||||
If you don't need the ability to sync your shell history, you can install hiSHtory in offline mode.
|
If you don't need the ability to sync your shell history, you can install hiSHtory in offline mode.
|
||||||
|
@ -2069,6 +2069,38 @@ echo bar`)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveDuplicateRows(t *testing.T) {
|
||||||
|
// Setup
|
||||||
|
tester := bashTester{}
|
||||||
|
defer testutils.BackupAndRestore(t)()
|
||||||
|
installHishtory(t, tester, "")
|
||||||
|
|
||||||
|
// Record a few commands and check that they get recorded and all are displayed in a table
|
||||||
|
tester.RunInteractiveShell(t, `echo foo
|
||||||
|
echo foo
|
||||||
|
echo baz
|
||||||
|
echo baz
|
||||||
|
echo foo`)
|
||||||
|
out := tester.RunInteractiveShell(t, `hishtory export -pipefail`)
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-export")
|
||||||
|
tester.RunInteractiveShell(t, `hishtory config-set displayed-columns 'Exit Code' Command`)
|
||||||
|
out = tester.RunInteractiveShell(t, `hishtory query -pipefail`)
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-query")
|
||||||
|
out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"})
|
||||||
|
out = strings.TrimSpace(strings.Split(out, "hishtory tquery")[1])
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-tquery")
|
||||||
|
|
||||||
|
// And change the config to filter out duplicate rows
|
||||||
|
tester.RunInteractiveShell(t, `hishtory config-set filter-duplicate-commands true`)
|
||||||
|
out = tester.RunInteractiveShell(t, `hishtory export -pipefail`)
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-enabled-export")
|
||||||
|
out = tester.RunInteractiveShell(t, `hishtory query -pipefail`)
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-enabled-query")
|
||||||
|
out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"})
|
||||||
|
out = strings.TrimSpace(strings.Split(out, "hishtory tquery")[1])
|
||||||
|
compareGoldens(t, out, "testRemoveDuplicateRows-enabled-tquery")
|
||||||
|
}
|
||||||
|
|
||||||
type deviceSet struct {
|
type deviceSet struct {
|
||||||
deviceMap *map[device]deviceOp
|
deviceMap *map[device]deviceOp
|
||||||
currentDevice *device
|
currentDevice *device
|
||||||
|
@ -163,6 +163,8 @@ type ClientConfig struct {
|
|||||||
CustomColumns []CustomColumnDefinition `json:"custom_columns"`
|
CustomColumns []CustomColumnDefinition `json:"custom_columns"`
|
||||||
// Whether this is an offline instance of hishtory with no syncing
|
// Whether this is an offline instance of hishtory with no syncing
|
||||||
IsOffline bool `json:"is_offline"`
|
IsOffline bool `json:"is_offline"`
|
||||||
|
// Whether duplicate commands should be displayed
|
||||||
|
FilterDuplicateCommands bool `json:"filter_duplicate_commands"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomColumnDefinition struct {
|
type CustomColumnDefinition struct {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
echo foo
|
||||||
|
echo foo
|
||||||
|
echo baz
|
||||||
|
echo baz
|
||||||
|
echo foo
|
||||||
|
hishtory config-set displayed-columns 'Exit Code' Command
|
||||||
|
source /Users/david/.bashrc
|
||||||
|
hishtory config-set filter-duplicate-commands true
|
7
client/lib/goldens/testRemoveDuplicateRows-enabled-query
Normal file
7
client/lib/goldens/testRemoveDuplicateRows-enabled-query
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Exit Code Command
|
||||||
|
0 hishtory config-set filter-duplicate-commands true
|
||||||
|
0 source /Users/david/.bashrc
|
||||||
|
0 hishtory config-set displayed-columns 'Exit Code' Command
|
||||||
|
0 echo foo
|
||||||
|
0 echo baz
|
||||||
|
0 echo foo
|
30
client/lib/goldens/testRemoveDuplicateRows-enabled-tquery
Normal file
30
client/lib/goldens/testRemoveDuplicateRows-enabled-tquery
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-pipefail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Search Query: > -pipefail
|
||||||
|
|
||||||
|
┌───────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Exit Code Command │
|
||||||
|
│───────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ 0 source /Users/david/.bashrc │
|
||||||
|
│ 0 hishtory config-set filter-duplicate-commands true │
|
||||||
|
│ 0 source /Users/david/.bashrc │
|
||||||
|
│ 0 hishtory config-set displayed-columns 'Exit Code' Command │
|
||||||
|
│ 0 echo foo │
|
||||||
|
│ 0 echo baz │
|
||||||
|
│ 0 echo foo │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└───────────────────────────────────────────────────────────────────────────┘
|
5
client/lib/goldens/testRemoveDuplicateRows-export
Normal file
5
client/lib/goldens/testRemoveDuplicateRows-export
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
echo foo
|
||||||
|
echo foo
|
||||||
|
echo baz
|
||||||
|
echo baz
|
||||||
|
echo foo
|
7
client/lib/goldens/testRemoveDuplicateRows-query
Normal file
7
client/lib/goldens/testRemoveDuplicateRows-query
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Exit Code Command
|
||||||
|
0 hishtory config-set displayed-columns 'Exit Code' Command
|
||||||
|
0 echo foo
|
||||||
|
0 echo baz
|
||||||
|
0 echo baz
|
||||||
|
0 echo foo
|
||||||
|
0 echo foo
|
30
client/lib/goldens/testRemoveDuplicateRows-tquery
Normal file
30
client/lib/goldens/testRemoveDuplicateRows-tquery
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-pipefail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Search Query: > -pipefail
|
||||||
|
|
||||||
|
┌───────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Exit Code Command │
|
||||||
|
│───────────────────────────────────────────────────────────────────────────│
|
||||||
|
│ 0 source /Users/david/.bashrc │
|
||||||
|
│ 0 hishtory config-set displayed-columns 'Exit Code' Command │
|
||||||
|
│ 0 echo foo │
|
||||||
|
│ 0 echo baz │
|
||||||
|
│ 0 echo baz │
|
||||||
|
│ 0 echo foo │
|
||||||
|
│ 0 echo foo │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└───────────────────────────────────────────────────────────────────────────┘
|
@ -461,7 +461,7 @@ func stringArrayToAnyArray(arr []string) []any {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisplayResults(ctx *context.Context, results []*data.HistoryEntry) error {
|
func DisplayResults(ctx *context.Context, results []*data.HistoryEntry, numResults int) error {
|
||||||
config := hctx.GetConf(ctx)
|
config := hctx.GetConf(ctx)
|
||||||
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
|
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
|
||||||
|
|
||||||
@ -472,12 +472,22 @@ func DisplayResults(ctx *context.Context, results []*data.HistoryEntry) error {
|
|||||||
tbl := table.New(columns...)
|
tbl := table.New(columns...)
|
||||||
tbl.WithHeaderFormatter(headerFmt)
|
tbl.WithHeaderFormatter(headerFmt)
|
||||||
|
|
||||||
for _, result := range results {
|
lastCommand := ""
|
||||||
row, err := buildTableRow(ctx, config.DisplayedColumns, *result)
|
numRows := 0
|
||||||
|
for _, entry := range results {
|
||||||
|
if entry != nil && entry.Command == lastCommand && config.FilterDuplicateCommands {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
row, err := buildTableRow(ctx, config.DisplayedColumns, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tbl.AddRow(stringArrayToAnyArray(row)...)
|
tbl.AddRow(stringArrayToAnyArray(row)...)
|
||||||
|
numRows += 1
|
||||||
|
lastCommand = entry.Command
|
||||||
|
if numRows >= numResults {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbl.Print()
|
tbl.Print()
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const TABLE_HEIGHT = 20
|
const TABLE_HEIGHT = 20
|
||||||
const PADDED_NUM_ENTRIES = TABLE_HEIGHT * 3
|
const PADDED_NUM_ENTRIES = TABLE_HEIGHT * 5
|
||||||
|
|
||||||
var selectedRow string = ""
|
var selectedRow string = ""
|
||||||
|
|
||||||
@ -212,20 +212,26 @@ func (m model) View() string {
|
|||||||
|
|
||||||
func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
|
func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
|
||||||
db := hctx.GetDb(ctx)
|
db := hctx.GetDb(ctx)
|
||||||
|
config := hctx.GetConf(ctx)
|
||||||
data, err := Search(ctx, db, query, numEntries)
|
data, err := Search(ctx, db, query, numEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
var rows []table.Row
|
var rows []table.Row
|
||||||
|
lastCommand := ""
|
||||||
for i := 0; i < numEntries; i++ {
|
for i := 0; i < numEntries; i++ {
|
||||||
if i < len(data) {
|
if i < len(data) {
|
||||||
entry := data[i]
|
entry := data[i]
|
||||||
|
if entry.Command == lastCommand && config.FilterDuplicateCommands {
|
||||||
|
continue
|
||||||
|
}
|
||||||
entry.Command = strings.ReplaceAll(entry.Command, "\n", " ") // TODO: handle multi-line commands better here
|
entry.Command = strings.ReplaceAll(entry.Command, "\n", " ") // TODO: handle multi-line commands better here
|
||||||
row, err := buildTableRow(ctx, columnNames, *entry)
|
row, err := buildTableRow(ctx, columnNames, *entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err)
|
return nil, 0, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err)
|
||||||
}
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
|
lastCommand = entry.Command
|
||||||
} else {
|
} else {
|
||||||
rows = append(rows, table.Row{})
|
rows = append(rows, table.Row{})
|
||||||
}
|
}
|
||||||
|
14
hishtory.go
14
hishtory.go
@ -135,6 +135,8 @@ func main() {
|
|||||||
switch key {
|
switch key {
|
||||||
case "enable-control-r":
|
case "enable-control-r":
|
||||||
fmt.Printf("%v", config.ControlRSearchEnabled)
|
fmt.Printf("%v", config.ControlRSearchEnabled)
|
||||||
|
case "filter-duplicate-commands":
|
||||||
|
fmt.Printf("%v", config.FilterDuplicateCommands)
|
||||||
case "displayed-columns":
|
case "displayed-columns":
|
||||||
for _, col := range config.DisplayedColumns {
|
for _, col := range config.DisplayedColumns {
|
||||||
if strings.Contains(col, " ") {
|
if strings.Contains(col, " ") {
|
||||||
@ -162,6 +164,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
config.ControlRSearchEnabled = (val == "true")
|
config.ControlRSearchEnabled = (val == "true")
|
||||||
lib.CheckFatalError(hctx.SetConfig(config))
|
lib.CheckFatalError(hctx.SetConfig(config))
|
||||||
|
case "filter-duplicate-commands":
|
||||||
|
val := os.Args[3]
|
||||||
|
if val != "true" && val != "false" {
|
||||||
|
log.Fatalf("Unexpected config value %s, must be one of: true, false", val)
|
||||||
|
}
|
||||||
|
config.FilterDuplicateCommands = (val == "true")
|
||||||
|
lib.CheckFatalError(hctx.SetConfig(config))
|
||||||
case "displayed-columns":
|
case "displayed-columns":
|
||||||
vals := os.Args[3:]
|
vals := os.Args[3:]
|
||||||
config.DisplayedColumns = vals
|
config.DisplayedColumns = vals
|
||||||
@ -313,9 +322,10 @@ func query(ctx *context.Context, query string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lib.CheckFatalError(displayBannerIfSet(ctx))
|
lib.CheckFatalError(displayBannerIfSet(ctx))
|
||||||
data, err := lib.Search(ctx, db, query, 25)
|
numResults := 25
|
||||||
|
data, err := lib.Search(ctx, db, query, numResults*5)
|
||||||
lib.CheckFatalError(err)
|
lib.CheckFatalError(err)
|
||||||
lib.CheckFatalError(lib.DisplayResults(ctx, data))
|
lib.CheckFatalError(lib.DisplayResults(ctx, data, numResults))
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayBannerIfSet(ctx *context.Context) error {
|
func displayBannerIfSet(ctx *context.Context) error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user