Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/pacseek.1
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ Search;
.br
Install or remove a selected package

.TP
.B d
.br
Show optional dependencies for selected package

.TP
.BR Tab ", " Ctrl+Up / Down / Left / Right
Navigate between boxes
Expand Down
107 changes: 101 additions & 6 deletions internal/pacseek/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,114 @@ import (
"github.com/rivo/tview"
)

// we don't need whole InfoRecord struct just .Name and .OptDepends
// if there isn't .Name in optsList that mean toggle state is false
// var optsList []InfoRecord
type PkgOpts struct {
Name string
OptDependsName []string
OptDepends []Package
Row int
}

// if optsList is emtpy simply means no package has toggled dep state true
var optsList []PkgOpts

func (ps *UI) searchForOptDeps(pkg PkgOpts) PkgOpts {
for _, opt := range pkg.OptDependsName {
// search repositories
pkgOpts, _, err := searchRepos(ps.alpmHandle, opt, "StartsWith", "Name", 1)
if err != nil {
ps.app.QueueUpdateDraw(func() {
ps.displayMessage(err.Error(), true)
})
}

if len(pkgOpts) != 0 {
pkg.OptDepends = append(pkg.OptDepends, pkgOpts...)
} else if !ps.conf.DisableAur {
// if pkgname wasn't found, search in aur
aurOpts, err := searchAur(ps.conf.AurRpcUrl, opt, ps.conf.AurTimeout, "StartsWith", "Name", 1)
if err != nil {
ps.app.QueueUpdateDraw(func() {
ps.displayMessage(err.Error(), true)
})
}

// probably for aur installed mark
for i := 0; i < len(aurOpts); i++ {
aurOpts[i].IsInstalled = isPackageInstalled(ps.alpmHandle, aurOpts[i].Name)
}

pkg.OptDepends = append(pkg.OptDepends, aurOpts...)
}

// sort OptDepends by name
//sort.Slice(pkg.OptDepends, func(i, j int) bool {
// return pkg.OptDepends[i].Name < opts[j].Name
//})
if len(pkg.OptDepends) == 0 {
ps.displayMessage("Couldn't find any opt package for search-term: "+opt, false)
// should always find optional dependency,
// if not then have no idea what else should we do
}
}
return pkg
}

func (ps *UI) toggleOptDeps() {
if ps.selectedPackage != nil {
index := -1
for i := range optsList {
if optsList[i].Name == ps.selectedPackage.Name {
index = i
optsList = util.Delete(optsList, index, index+1)
break
}
}
if index == -1 {
row, _ := ps.tablePackages.GetSelection()
pkg := PkgOpts{
Name: ps.selectedPackage.Name,
OptDependsName: ps.selectedPackage.OptDepends,
OptDepends: nil,
Row: row,
}

pkg = ps.searchForOptDeps(pkg)

// show message if we couldn't find anything
if pkg.OptDepends == nil {
ps.displayMessage("No OptDepends found for package: "+pkg.Name, false)
} else {
optsList = append(optsList, pkg)
}

// it's better for them to be sorted by row since we draw by row
sort.Slice(optsList, func(i, j int) bool {
return optsList[i].Row < optsList[j].Row
})
}
}
ps.displayPackages(ps.lastSearchTerm, true)
}

// gets packages from repos/AUR and displays them
func (ps *UI) displayPackages(text string) {
func (ps *UI) displayPackages(text string, isToggleDraw bool) {
var packages []Package

showFunc := func() {
ps.shownPackages = packages
best := bestMatch(text, packages) + 1
ps.drawPackageListContent(packages, ps.conf.PackageColumnWidth)
ps.drawPackageListContent(packages, optsList, ps.conf.PackageColumnWidth)
if ps.flexRight.GetItem(0) == ps.formSettings {
ps.flexRight.Clear()
ps.flexRight.AddItem(ps.tableDetails, 0, 1, false)
}
ps.tablePackages.Select(best, 0) // select the best match

if !isToggleDraw {
best := bestMatch(text, packages) + 1
ps.tablePackages.Select(best, 0) // select the best match
}
}

// check cache first
Expand Down Expand Up @@ -424,7 +519,7 @@ func (ps *UI) displayInstalled(displayUpdatesAfter bool) {
if installedCached, found := ps.cacheSearch.Get("#installed#"); found {
packages := installedCached.([]Package)
ps.shownPackages = packages
ps.drawPackageListContent(packages, ps.conf.PackageColumnWidth)
ps.drawPackageListContent(packages, nil, ps.conf.PackageColumnWidth)
ps.tablePackages.Select(1, 0)
return
}
Expand Down Expand Up @@ -470,7 +565,7 @@ func (ps *UI) displayInstalled(displayUpdatesAfter bool) {
}
ps.shownPackages = packages
ps.app.QueueUpdateDraw(func() {
ps.drawPackageListContent(packages, ps.conf.PackageColumnWidth)
ps.drawPackageListContent(packages, nil, ps.conf.PackageColumnWidth)
if displayUpdatesAfter {
ps.displayUpgradable()
} else {
Expand Down
61 changes: 54 additions & 7 deletions internal/pacseek/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (ps *UI) drawSettingsFields(disableAur, disableCache, separateAurCommands,
ps.formSettings.AddInputField("Package column width: ", strconv.Itoa(ps.conf.PackageColumnWidth), 6, nil, func(text string) {
ps.settingsChanged = true
width, _ := strconv.Atoi(text)
ps.drawPackageListContent(ps.shownPackages, width)
ps.drawPackageListContent(ps.shownPackages, nil, width)
})
ps.formSettings.AddCheckbox("Separate Deps with Newline: ", ps.conf.SepDepsWithNewLine, func(checked bool) {
ps.settingsChanged = true
Expand Down Expand Up @@ -502,41 +502,88 @@ func (ps *UI) drawUpgradeableLine(up InfoRecord, lNum int, ignored bool) {
}
}

// draw optional dependencies on screen
func (ps *UI) drawOptsListContent(opts []Package, pkgwidth int, row int) {
// rows
for i, pkg := range opts {
color := ps.conf.Colors().PackagelistSourceRepository

if pkg.Source == "AUR" {
color = ps.conf.Colors().PackagelistSourceAUR
}

ps.tablePackages.SetCell(i+row+1, 0, &tview.TableCell{
Text: pkg.Name,
Color: tcell.ColorGray,
BackgroundColor: ps.conf.Colors().DefaultBackground,
MaxWidth: pkgwidth,
}).
SetCell(i+row+1, 1, &tview.TableCell{
Text: pkg.Source,
Color: color,
BackgroundColor: ps.conf.Colors().DefaultBackground,
}).
SetCell(i+row+1, 2, &tview.TableCell{
Color: ps.conf.Colors().DefaultBackground,
Text: ps.getInstalledStateText(pkg.IsInstalled),
Expansion: 1000,
Reference: pkg.IsInstalled,
Transparent: true,
})
}
}

// draw packages on screen
func (ps *UI) drawPackageListContent(packages []Package, pkgwidth int) {
func (ps *UI) drawPackageListContent(packages []Package, optsList []PkgOpts, pkgwidth int) {
ps.tablePackages.Clear()

// header
ps.drawPackageListHeader(pkgwidth)

var rowShift int
var optsIndex = -1
// rows
for i, pkg := range packages {
optsIndex = -1
// for this purpose is required that list is sorted
for i, pkgOpt := range optsList {
if pkgOpt.Name == pkg.Name {
optsIndex = i
}
}

color := ps.conf.Colors().PackagelistSourceRepository

if pkg.Source == "AUR" {
color = ps.conf.Colors().PackagelistSourceAUR
}

ps.tablePackages.SetCell(i+1, 0, &tview.TableCell{
ps.tablePackages.SetCell(i+rowShift+1, 0, &tview.TableCell{
Text: pkg.Name,
Color: tcell.ColorWhite,
BackgroundColor: ps.conf.Colors().DefaultBackground,
MaxWidth: pkgwidth,
}).
SetCell(i+1, 1, &tview.TableCell{
SetCell(i+rowShift+1, 1, &tview.TableCell{
Text: pkg.Source,
Color: color,
BackgroundColor: ps.conf.Colors().DefaultBackground,
}).
SetCell(i+1, 2, &tview.TableCell{
SetCell(i+rowShift+1, 2, &tview.TableCell{
Color: ps.conf.Colors().DefaultBackground,
Text: ps.getInstalledStateText(pkg.IsInstalled),
Expansion: 1000,
Reference: pkg.IsInstalled,
Transparent: true,
})
if optsIndex != -1 {
ps.drawOptsListContent(optsList[optsIndex].OptDepends, pkgwidth, optsList[optsIndex].Row)
rowShift += len(optsList[optsIndex].OptDepends)
}
}
ps.tablePackages.ScrollToBeginning()

// not good since we can toggle 'd' and everytime it would reset cursor
//ps.tablePackages.ScrollToBeginning()
}

// draw pkgbuild on screen
Expand Down Expand Up @@ -655,7 +702,7 @@ func (ps *UI) sortAndRedrawPackageList(runeKey rune) {
}
}
ps.sortAscending = !ps.sortAscending
ps.drawPackageListContent(ps.shownPackages, ps.conf.PackageColumnWidth)
ps.drawPackageListContent(ps.shownPackages, nil, ps.conf.PackageColumnWidth)
ps.tablePackages.Select(1, 0)
}

Expand Down
7 changes: 6 additions & 1 deletion internal/pacseek/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ func (ps *UI) setupKeyBindings() {
ps.displayMessage("Minimum number of characters is 2", true)
return
}
ps.displayPackages(ps.lastSearchTerm)
ps.displayPackages(ps.lastSearchTerm, false)
} else if key == tcell.KeyTAB {
ps.app.SetFocus(ps.tablePackages)
}
Expand Down Expand Up @@ -441,6 +441,11 @@ func (ps *UI) setupKeyBindings() {
ps.installSelectedPackage()
return nil
}
// show optional dependencies
if event.Rune() == 'd' {
ps.toggleOptDeps()
return nil
}
// Down / j / k -> noop: WTF? Prevent lock-up with empty list ;) :(
// upstream issue?
if (event.Key() == tcell.KeyDown || event.Rune() == 'k' || event.Rune() == 'j') &&
Expand Down
2 changes: 1 addition & 1 deletion internal/pacseek/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func New(conf *config.Settings, flags args.Flags) (*UI, error) {
func (ps *UI) Start() error {
if ps.flags.SearchTerm != "" {
ps.inputSearch.SetText(ps.flags.SearchTerm)
ps.displayPackages(ps.flags.SearchTerm)
ps.displayPackages(ps.flags.SearchTerm, false)
} else {
if ps.flags.ShowInstalled {
ps.displayInstalled(ps.flags.ShowUpdates)
Expand Down
14 changes: 14 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,17 @@ func UniqueStrings(strSlices ...[]string) []string {

return result
}

// blatant copy of slices.Delete go 1.22 stdlib
func Delete[S ~[]E, E any](s S, i, j int) S {
_ = s[i:j:len(s)] // bounds check

if i == j {
return s
}

oldlen := len(s)
s = append(s[:i], s[j:]...)
clear(s[len(s):oldlen]) // zero/nil out the obsolete elements, for GC
return s
}