First pass at filter support.

Press / to input a filter string. The proc list will be filtered to
procs that contain the filter as a substring of either the command or
the string representation of the PID.
This commit is contained in:
Brian Mattern 2019-05-31 19:58:29 -07:00
parent 1b5985860c
commit 866281cc42
4 changed files with 136 additions and 22 deletions

View File

@ -71,7 +71,7 @@ snap connect gotop-cjbassi:system-observe
### Keybinds
- Quit: `q` or `<C-c>`
- Process navigation
- Process navigation:
- `k` and `<Up>`: up
- `j` and `<Down`: down
- `<C-u>`: half page up
@ -83,10 +83,15 @@ snap connect gotop-cjbassi:system-observe
- Process actions:
- `<Tab>`: toggle process grouping
- `dd`: kill selected process or group of processes
- Process sorting
- Process sorting:
- `c`: CPU
- `m`: Mem
- `p`: PID
- Process filtering:
- /: start editing filter
- (while editing):
- <Enter> accept filter
- <C-c>: clear filter
- CPU and Mem graph scaling:
- `h`: scale in
- `l`: scale out

62
main.go
View File

@ -12,6 +12,7 @@ import (
"strconv"
"syscall"
"time"
"unicode/utf8"
docopt "github.com/docopt/docopt.go"
ui "github.com/gizak/termui/v3"
@ -296,12 +297,8 @@ func eventLoop() {
}
}
case e := <-uiEvents:
switch e.ID {
case "q", "<C-c>":
return
case "?":
helpVisible = !helpVisible
case "<Resize>":
// Handle resize event always.
if e.ID == "<Resize>" {
payload := e.Payload.(ui.Resize)
termWidth, termHeight := payload.Width, payload.Height
if statusbar {
@ -312,23 +309,55 @@ func eventLoop() {
}
help.Resize(payload.Width, payload.Height)
ui.Clear()
if helpVisible {
ui.Render(help)
} else {
ui.Render(grid)
if statusbar {
ui.Render(bar)
}
}
}
if helpVisible {
if proc.EditingFilter() {
if utf8.RuneCountInString(e.ID) == 1 {
proc.SetFilter(proc.Filter() + e.ID)
ui.Render(proc)
}
switch e.ID {
case "<C-c>":
proc.SetFilter("")
proc.SetEditingFilter(false)
ui.Render(proc)
case "<Enter>":
proc.SetEditingFilter(false)
ui.Render(proc)
case "<Backspace>":
if filter := proc.Filter(); filter != "" {
proc.SetFilter(filter[:len(filter)-1])
}
ui.Render(proc)
}
} else if helpVisible {
switch e.ID {
case "q", "<C-c>":
return
case "?":
ui.Clear()
ui.Render(help)
helpVisible = false
ui.Render(grid)
case "<Escape>":
helpVisible = false
ui.Render(grid)
case "<Resize>":
ui.Render(help)
}
} else {
switch e.ID {
case "q", "<C-c>":
return
case "?":
ui.Render(grid)
helpVisible = true
ui.Clear()
ui.Render(help)
case "h":
graphHorizontalScale += graphHorizontalScaleDelta
cpu.HorizontalScale = graphHorizontalScale
@ -341,11 +370,6 @@ func eventLoop() {
mem.HorizontalScale = graphHorizontalScale
ui.Render(cpu, mem)
}
case "<Resize>":
ui.Render(grid)
if statusbar {
ui.Render(bar)
}
case "<MouseLeft>":
payload := e.Payload.(ui.Mouse)
proc.HandleClick(payload.X, payload.Y)
@ -389,6 +413,10 @@ func eventLoop() {
case "m", "c", "p":
proc.ChangeProcSortMethod(w.ProcSortMethod(e.ID))
ui.Render(proc)
case "/":
proc.SetFilter("")
proc.SetEditingFilter(true)
ui.Render(proc)
}
if previousKey == e.ID {

View File

@ -10,7 +10,7 @@ import (
const KEYBINDS = `
Quit: q or <C-c>
Process navigation
Process navigation:
- k and <Up>: up
- j and <Down>: down
- <C-u>: half page up
@ -24,11 +24,17 @@ Process actions:
- <Tab>: toggle process grouping
- dd: kill selected process or group of processes
Process sorting
Process sorting:
- c: CPU
- m: Mem
- p: PID
Process filtering:
- /: start editing filter
- (while editing):
- <Enter>: accept filter
- <C-c> clear filter
CPU and Mem graph scaling:
- h: scale in
- l: scale out
@ -46,7 +52,7 @@ func NewHelpMenu() *HelpMenu {
func (self *HelpMenu) Resize(termWidth, termHeight int) {
textWidth := 53
textHeight := 22
textHeight := strings.Count(KEYBINDS, "\n") + 1
x := (termWidth - textWidth) / 2
y := (termHeight - textHeight) / 2

View File

@ -2,21 +2,26 @@ package widgets
import (
"fmt"
"image"
"log"
"os/exec"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
psCPU "github.com/shirou/gopsutil/cpu"
ui "github.com/cjbassi/gotop/src/termui"
"github.com/cjbassi/gotop/src/utils"
tui "github.com/gizak/termui/v3"
)
const (
UP_ARROW = "▲"
DOWN_ARROW = "▼"
ELLIPSIS = "…"
)
type ProcSortMethod string
@ -40,6 +45,8 @@ type ProcWidget struct {
cpuCount int
updateInterval time.Duration
sortMethod ProcSortMethod
filter string
editingFilter bool
groupedProcs []Proc
ungroupedProcs []Proc
showGroupedProcs bool
@ -56,6 +63,8 @@ func NewProcWidget() *ProcWidget {
cpuCount: cpuCount,
sortMethod: ProcSortCpu,
showGroupedProcs: true,
filter: "",
editingFilter: false,
}
self.Title = " Processes "
self.ShowCursor = true
@ -86,6 +95,71 @@ func NewProcWidget() *ProcWidget {
return self
}
func (self *ProcWidget) Filter() string {
return self.filter
}
func (self *ProcWidget) SetFilter(filter string) {
self.filter = filter
}
func (self *ProcWidget) EditingFilter() bool {
return self.editingFilter
}
func (self *ProcWidget) SetEditingFilter(editing bool) {
self.editingFilter = editing
if !editing {
self.update()
}
}
func (self *ProcWidget) Draw(buf *tui.Buffer) {
self.Table.Draw(buf)
if self.filter != "" || self.editingFilter {
self.drawFilter(buf)
}
}
func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
style := self.TitleStyle
label := "Filter: "
if self.editingFilter {
label = "[ Filter: "
style = tui.NewStyle(style.Fg, style.Bg, tui.ModifierBold)
}
p := image.Pt(self.Min.X+2, self.Max.Y-1)
buf.SetString(label, style, p)
p.X += utf8.RuneCountInString(label)
maxLen := self.Max.X - p.X - 4
filter := self.filter
if l := utf8.RuneCountInString(filter); l > maxLen {
filter = ELLIPSIS + filter[l-maxLen+1:]
}
buf.SetString(filter, self.TitleStyle, p)
p.X += utf8.RuneCountInString(filter)
if self.editingFilter {
remaining := self.Max.X - 2 - p.X
buf.SetString(fmt.Sprintf("%*s", remaining, "]"), style, p)
}
}
func (self *ProcWidget) filterProcs(procs []Proc) []Proc {
if self.filter == "" {
return procs
}
var filtered []Proc
for _, proc := range procs {
if strings.Contains(proc.FullCommand, self.filter) || strings.Contains(fmt.Sprintf("%d", proc.Pid), self.filter) {
filtered = append(filtered, proc)
}
}
return filtered
}
func (self *ProcWidget) update() {
procs, err := getProcs()
if err != nil {
@ -98,6 +172,7 @@ func (self *ProcWidget) update() {
procs[i].Cpu /= float64(self.cpuCount)
}
procs = self.filterProcs(procs)
self.ungroupedProcs = procs
self.groupedProcs = groupProcs(procs)