feat: Go to scopes are grouped like the Sources pane, an account at a time
The scope list follows the Sources pane: All, then each account (or Databricks, or Repos) with its sources under it, in place of All Azure, All Databricks and All repos. An account scope searches and loads only that account. ctrl+s on a group header narrows to its group. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
57aec8a381
commit
331e53fb49
7 files changed
+121
-71
No files matched your search
+11
-10
@@ -84,15 +84,16 @@ under every account. Both are kept in the [state file](config.md#state).
|
|||||||
starred) and `R` on Recent (what you used last). `enter` goes to the
|
starred) and `R` on Recent (what you used last). `enter` goes to the
|
||||||
selected item.
|
selected item.
|
||||||
|
|
||||||
Search has the scope on the left (All; all of Azure, Databricks or repos;
|
Search has the scope on the left and what's in it on the right. The
|
||||||
or one source; with `○ ◐ ●` for how much of it is loaded) and what's in
|
scopes are All, then grouped like the Sources pane: each account (or
|
||||||
it on the right. All repos searches every repo without asking Azure or
|
Databricks, or Repos) with its sources under it, with `○ ◐ ●` for how
|
||||||
Databricks for anything. It opens on All; `ctrl+s` again narrows it to
|
much of each is loaded. Repos searches every repo without asking Azure
|
||||||
the source you're in, and once more goes back to All. Typing a search
|
or Databricks for anything. Go to opens on All; `ctrl+s` again narrows
|
||||||
loads what it covers: everything on All, else the source picked;
|
it to the source or group selected in the Sources pane, and once more
|
||||||
results appear as lists arrive. That's once each time Go to opens, and
|
goes back to All. Typing a search loads what it covers: everything on
|
||||||
only what isn't loaded yet; `ctrl+a` loads again, lists that failed
|
All, else the account or source picked; results appear as lists
|
||||||
included.
|
arrive. That's once each time Go to opens, and only what isn't loaded
|
||||||
|
yet; `ctrl+a` loads again, lists that failed included.
|
||||||
|
|
||||||
Like a pane, Go to is either browsing or typing a filter:
|
Like a pane, Go to is either browsing or typing a filter:
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ Like a pane, Go to is either browsing or typing a filter:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `j`/`k` | `ctrl+j`/`ctrl+k` | move |
|
| `j`/`k` | `ctrl+j`/`ctrl+k` | move |
|
||||||
| `g`/`G` | | top / bottom |
|
| `g`/`G` | | top / bottom |
|
||||||
| `ctrl+s` | `ctrl+s` | only the source you're in / All |
|
| `ctrl+s` | `ctrl+s` | only the source or group you're in / All |
|
||||||
| `h`/`l`, `tab` | `ctrl+h`/`ctrl+l`, `tab` | scope list / results |
|
| `h`/`l`, `tab` | `ctrl+h`/`ctrl+l`, `tab` | scope list / results |
|
||||||
| `[` / `]` | | previous / next tab |
|
| `[` / `]` | | previous / next tab |
|
||||||
| `f` | `ctrl+f` | star |
|
| `f` | `ctrl+f` | star |
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (m Model) expiringFields() []panes.Field {
|
|||||||
}
|
}
|
||||||
out = append(out, f)
|
out = append(out, f)
|
||||||
}
|
}
|
||||||
cov := m.coverageOf(provider.KindAzure)
|
cov := m.coverageOf(ofKind(provider.KindAzure))
|
||||||
switch {
|
switch {
|
||||||
case cov.vaults == 0 && len(items) == 0:
|
case cov.vaults == 0 && len(items) == 0:
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -206,9 +206,9 @@ func TestSearchAllRepos(t *testing.T) {
|
|||||||
for d.m.search.src > 0 {
|
for d.m.search.src > 0 {
|
||||||
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
|
d.send(tea.KeyPressMsg{Code: tea.KeyUp})
|
||||||
}
|
}
|
||||||
for d.m.search.scope().id != kindScope+string(provider.KindRepo) {
|
for d.m.search.scope().id != accountScope+"Repos" {
|
||||||
if d.m.search.src == len(d.m.search.scopes)-1 {
|
if d.m.search.src == len(d.m.search.scopes)-1 {
|
||||||
t.Fatalf("no All repos scope: %v", d.m.search.scopes)
|
t.Fatalf("no Repos scope: %v", d.m.search.scopes)
|
||||||
}
|
}
|
||||||
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
|
d.send(tea.KeyPressMsg{Code: tea.KeyDown})
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ func TestSearchAllRepos(t *testing.T) {
|
|||||||
if azure() != azureBefore || d.m.crawl.running {
|
if azure() != azureBefore || d.m.crawl.running {
|
||||||
t.Errorf("searching all repos loaded Azure: %d → %d lists", azureBefore, azure())
|
t.Errorf("searching all repos loaded Azure: %d → %d lists", azureBefore, azure())
|
||||||
}
|
}
|
||||||
if !strings.Contains(d.screen(), "Results in all repos") {
|
if !strings.Contains(d.screen(), "Results in Repos") {
|
||||||
t.Errorf("title:\n%s", d.screen())
|
t.Errorf("title:\n%s", d.screen())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,13 +131,25 @@ func (c coverage) complete() bool {
|
|||||||
return c.sourcesListed == c.sources && c.vaultsLoaded == c.vaults
|
return c.sourcesListed == c.sources && c.vaultsLoaded == c.vaults
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) searchCoverage() coverage { return m.coverageOf("") }
|
func (m *Model) searchCoverage() coverage { return m.coverageOf(nil) }
|
||||||
|
|
||||||
// coverageOf is searchCoverage for one kind of provider ("" for all).
|
// providerFilter picks the providers a search or a load covers; nil is
|
||||||
func (m *Model) coverageOf(kind provider.Kind) coverage {
|
// all of them.
|
||||||
|
type providerFilter func(provider.Provider) bool
|
||||||
|
|
||||||
|
func ofKind(k provider.Kind) providerFilter {
|
||||||
|
return func(p provider.Provider) bool { return p.Kind() == k }
|
||||||
|
}
|
||||||
|
|
||||||
|
func ofProvider(id string) providerFilter {
|
||||||
|
return func(p provider.Provider) bool { return p.ID() == id }
|
||||||
|
}
|
||||||
|
|
||||||
|
// coverageOf is searchCoverage for the providers in.
|
||||||
|
func (m *Model) coverageOf(in providerFilter) coverage {
|
||||||
var c coverage
|
var c coverage
|
||||||
for _, p := range m.providers {
|
for _, p := range m.providers {
|
||||||
if kind != "" && p.Kind() != kind {
|
if in != nil && !in(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, src := range m.sourceResults[p.ID()].sources {
|
for _, src := range m.sourceResults[p.ID()].sources {
|
||||||
@@ -197,12 +209,12 @@ func plural(n int, word string) string {
|
|||||||
|
|
||||||
// startCrawl loads every unlisted subscription's vaults and every vault's
|
// startCrawl loads every unlisted subscription's vaults and every vault's
|
||||||
// secrets not loaded yet.
|
// secrets not loaded yet.
|
||||||
func (m *Model) startCrawl() tea.Cmd { return m.startCrawlOf("") }
|
func (m *Model) startCrawl() tea.Cmd { return m.startCrawlOf(nil) }
|
||||||
|
|
||||||
// startCrawlOf is startCrawl for one kind of provider ("" for all).
|
// startCrawlOf is startCrawl for the providers in.
|
||||||
func (m *Model) startCrawlOf(kind provider.Kind) tea.Cmd {
|
func (m *Model) startCrawlOf(in providerFilter) tea.Cmd {
|
||||||
for _, p := range m.providers {
|
for _, p := range m.providers {
|
||||||
if kind != "" && p.Kind() != kind {
|
if in != nil && !in(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, src := range m.sourceResults[p.ID()].sources {
|
for _, src := range m.sourceResults[p.ID()].sources {
|
||||||
|
|||||||
@@ -218,6 +218,31 @@ func TestSearchStartsWhereYouAre(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchNarrowsToAccount(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
d := newDriver(t)
|
||||||
|
d.keys("1", "g") // the first group header
|
||||||
|
header, _ := d.m.sources.Selected()
|
||||||
|
d.openSearch()
|
||||||
|
d.send(ctrl('s'))
|
||||||
|
v := d.m.search
|
||||||
|
if v.scope().id != accountScope+header.provider {
|
||||||
|
t.Fatalf("ctrl+s on %q narrowed to %q", header.label, v.scope().id)
|
||||||
|
}
|
||||||
|
if len(v.shown) == 0 || !strings.Contains(d.screen(), "Results in "+header.label) {
|
||||||
|
t.Errorf("no results in the account:\n%s", d.screen())
|
||||||
|
}
|
||||||
|
for _, o := range v.shown {
|
||||||
|
if providerOf(o.group) != header.provider {
|
||||||
|
t.Errorf("result from another account: %q", ansiStrip(o.label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The account's sources are under it, indented.
|
||||||
|
if next := v.scopes[v.src+1]; next.group == "" || providerOf(next.group) != header.provider || !strings.HasPrefix(ansiStrip(next.label), " ") {
|
||||||
|
t.Errorf("after the account: %q", ansiStrip(next.label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSearchHint(t *testing.T) {
|
func TestSearchHint(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
d := newDriver(t)
|
d := newDriver(t)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const (
|
|||||||
scopeFavs = "#favs"
|
scopeFavs = "#favs"
|
||||||
scopeRecent = "#recent"
|
scopeRecent = "#recent"
|
||||||
allSources = "#all"
|
allSources = "#all"
|
||||||
kindScope = "#kind:" // + a provider kind: all of Azure, Databricks or repos
|
accountScope = "#account:" // + a provider ID: an account's sources, or Databricks', or the repos
|
||||||
)
|
)
|
||||||
|
|
||||||
const goToTitle = "Go to"
|
const goToTitle = "Go to"
|
||||||
@@ -138,7 +138,11 @@ func (v *searchView) onSource() bool { return v.tab == tabSearch && v.scope().gr
|
|||||||
func (m *Model) preselect() {
|
func (m *Model) preselect() {
|
||||||
v := m.search
|
v := m.search
|
||||||
row := m.sources.SelectedID()
|
row := m.sources.SelectedID()
|
||||||
i := slices.IndexFunc(v.scopes, func(o option) bool { return o.group != "" && o.group == row })
|
account := ""
|
||||||
|
if id, ok := strings.CutPrefix(row, "group:"); ok { // a group header
|
||||||
|
account = accountScope + id
|
||||||
|
}
|
||||||
|
i := slices.IndexFunc(v.scopes, func(o option) bool { return o.id == account || o.group != "" && o.group == row })
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -172,25 +176,36 @@ func (m *Model) fillSearch(keep bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sources, items := m.searchEntries()
|
sources, items := m.searchEntries()
|
||||||
all := option{label: "All", id: allSources, mark: m.coverageMark("")}
|
all := option{label: "All", id: allSources, mark: m.coverageMark(nil)}
|
||||||
scopes := []option{all}
|
// Grouped like the Sources pane: each account (or Databricks, or the
|
||||||
// All of one kind, so a search can stay on repos (local, quick)
|
// repos) is a scope too, its sources under it, so a search can stay on
|
||||||
// without asking Azure for every vault; only with several kinds.
|
// one, like the repos (local, quick), without asking Azure for every
|
||||||
var kinds []provider.Kind
|
// vault. With only one, it's the same as All.
|
||||||
|
var groups []provider.Provider
|
||||||
for _, p := range m.providers {
|
for _, p := range m.providers {
|
||||||
if len(m.sourceResults[p.ID()].sources) > 0 && !slices.Contains(kinds, p.Kind()) {
|
if len(m.sourceResults[p.ID()].sources) > 0 {
|
||||||
kinds = append(kinds, p.Kind())
|
groups = append(groups, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(kinds) > 1 {
|
scopes := []option{all}
|
||||||
for _, k := range kinds {
|
if len(groups) < 2 {
|
||||||
|
scopes = append(scopes, sources...)
|
||||||
|
} else {
|
||||||
|
for _, p := range groups {
|
||||||
|
k := p.Kind()
|
||||||
scopes = append(scopes, option{
|
scopes = append(scopes, option{
|
||||||
label: m.st.Kind(k).Render(m.icons.Prefix(k) + kindScopeLabel(k)),
|
label: m.st.Kind(k).Render(m.icons.Prefix(k) + p.Label()),
|
||||||
id: kindScope + string(k), kind: k, mark: m.coverageMark(k),
|
id: accountScope + p.ID(), kind: k, mark: m.coverageMark(ofProvider(p.ID())),
|
||||||
})
|
})
|
||||||
|
for _, o := range sources {
|
||||||
|
if providerOf(o.group) == p.ID() {
|
||||||
|
o.label = " " + o.label
|
||||||
|
scopes = append(scopes, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.scopes, v.items = append(scopes, sources...), items
|
}
|
||||||
|
}
|
||||||
|
v.scopes, v.items = scopes, items
|
||||||
v.src = max(0, slices.IndexFunc(v.scopes, func(o option) bool { return o.id == atSrc }))
|
v.src = max(0, slices.IndexFunc(v.scopes, func(o option) bool { return o.id == atSrc }))
|
||||||
|
|
||||||
v.shown = v.match()
|
v.shown = v.match()
|
||||||
@@ -203,17 +218,9 @@ func (m *Model) fillSearch(keep bool) {
|
|||||||
v.res = max(0, min(v.res, len(v.shown)-1))
|
v.res = max(0, min(v.res, len(v.shown)-1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// kindScopeLabel names the scope of all of a kind's sources.
|
// coverageMark is ○ ◐ ● for how much of the providers in is loaded.
|
||||||
func kindScopeLabel(k provider.Kind) string {
|
func (m *Model) coverageMark(in providerFilter) string {
|
||||||
if k == provider.KindRepo {
|
switch c := m.coverageOf(in); {
|
||||||
return "All repos"
|
|
||||||
}
|
|
||||||
return "All " + string(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// coverageMark is ○ ◐ ● for how much of a kind ("" for all) is loaded.
|
|
||||||
func (m *Model) coverageMark(kind provider.Kind) string {
|
|
||||||
switch c := m.coverageOf(kind); {
|
|
||||||
case c.complete():
|
case c.complete():
|
||||||
return m.st.Success.Render("●")
|
return m.st.Success.Render("●")
|
||||||
case c.vaultsLoaded > 0:
|
case c.vaultsLoaded > 0:
|
||||||
@@ -222,15 +229,28 @@ func (m *Model) coverageMark(kind provider.Kind) string {
|
|||||||
return m.st.Muted.Render("○")
|
return m.st.Muted.Render("○")
|
||||||
}
|
}
|
||||||
|
|
||||||
// scopeKind is the kind a scope covers all of, if it's one of those.
|
// scopeAccount is the provider whose sources the scope covers, if it's
|
||||||
func (v *searchView) scopeKind() (provider.Kind, bool) {
|
// an account's.
|
||||||
k, ok := strings.CutPrefix(v.scope().id, kindScope)
|
func (v *searchView) scopeAccount() (string, bool) {
|
||||||
|
id, ok := strings.CutPrefix(v.scope().id, accountScope)
|
||||||
if !ok || v.tab != tabSearch {
|
if !ok || v.tab != tabSearch {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
return provider.Kind(k), true
|
return id, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scopeFilter is what loading everything in the scope covers: an
|
||||||
|
// account's, else all of them.
|
||||||
|
func (v *searchView) scopeFilter() providerFilter {
|
||||||
|
if id, ok := v.scopeAccount(); ok {
|
||||||
|
return ofProvider(id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopeName is the scope's label without colours or indent.
|
||||||
|
func scopeName(o option) string { return strings.TrimSpace(ansi.Strip(o.label)) }
|
||||||
|
|
||||||
// match lists the scope's entries matching the input, best first.
|
// match lists the scope's entries matching the input, best first.
|
||||||
func (v *searchView) match() []option {
|
func (v *searchView) match() []option {
|
||||||
var cand []option
|
var cand []option
|
||||||
@@ -240,9 +260,9 @@ func (v *searchView) match() []option {
|
|||||||
case scopeRecent:
|
case scopeRecent:
|
||||||
cand = v.recent
|
cand = v.recent
|
||||||
default:
|
default:
|
||||||
kind, ofKind := v.scopeKind()
|
account, ofAccount := v.scopeAccount()
|
||||||
for _, o := range v.items {
|
for _, o := range v.items {
|
||||||
if sc.id == allSources || ofKind && o.kind == kind || sc.group != "" && o.group == sc.group {
|
if sc.id == allSources || ofAccount && providerOf(o.group) == account || sc.group != "" && o.group == sc.group {
|
||||||
cand = append(cand, o)
|
cand = append(cand, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,11 +441,9 @@ func (m *Model) handleSearch(msg tea.KeyPressMsg) tea.Cmd {
|
|||||||
if v.onSource() {
|
if v.onSource() {
|
||||||
return v.scope().reload(m)
|
return v.scope().reload(m)
|
||||||
}
|
}
|
||||||
kind, _ := v.scopeKind()
|
return m.startCrawlOf(v.scopeFilter())
|
||||||
return m.startCrawlOf(kind)
|
|
||||||
case inSearch && key.Matches(msg, k.loadAll):
|
case inSearch && key.Matches(msg, k.loadAll):
|
||||||
kind, _ := v.scopeKind()
|
return m.startCrawlOf(v.scopeFilter())
|
||||||
return m.startCrawlOf(kind)
|
|
||||||
case key.Matches(msg, k.star):
|
case key.Matches(msg, k.star):
|
||||||
if o, ok := v.selected(); ok && o.star != "" {
|
if o, ok := v.selected(); ok && o.star != "" {
|
||||||
m.toggleStarKey(o.star, o.starAccount)
|
m.toggleStarKey(o.star, o.starAccount)
|
||||||
@@ -488,8 +506,8 @@ func (m *Model) searchLoads() tea.Cmd {
|
|||||||
v.loaded = map[string]bool{}
|
v.loaded = map[string]bool{}
|
||||||
}
|
}
|
||||||
v.loaded[sc.id] = true
|
v.loaded[sc.id] = true
|
||||||
if kind, ok := v.scopeKind(); ok {
|
if _, ok := v.scopeAccount(); ok {
|
||||||
return m.startCrawlOf(kind)
|
return m.startCrawlOf(v.scopeFilter())
|
||||||
}
|
}
|
||||||
if !v.onSource() {
|
if !v.onSource() {
|
||||||
return m.startCrawl()
|
return m.startCrawl()
|
||||||
@@ -557,7 +575,7 @@ func (m Model) narrowHint() string {
|
|||||||
if i < 0 || v.scope().id != allSources {
|
if i < 0 || v.scope().id != allSources {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return withKey(m.keys.Search, "%s only "+ansi.Strip(v.scopes[i].label))
|
return withKey(m.keys.Search, "%s only "+scopeName(v.scopes[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadHint says how much of the scope is loaded, and the key to load it.
|
// loadHint says how much of the scope is loaded, and the key to load it.
|
||||||
@@ -569,8 +587,8 @@ func (m Model) loadHint() string {
|
|||||||
if v.tab != tabSearch {
|
if v.tab != tabSearch {
|
||||||
return "" // from the store: nothing to load
|
return "" // from the store: nothing to load
|
||||||
}
|
}
|
||||||
if kind, ok := v.scopeKind(); ok || v.scope().id == allSources {
|
if _, ok := v.scopeAccount(); ok || v.scope().id == allSources {
|
||||||
c := m.coverageOf(kind)
|
c := m.coverageOf(v.scopeFilter())
|
||||||
if c.complete() {
|
if c.complete() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -702,14 +720,8 @@ func (m Model) searchViewBox() string {
|
|||||||
}
|
}
|
||||||
title := func(s string, active bool) string { return panes.TitleStyle(m.st, active && focused).Render(s) }
|
title := func(s string, active bool) string { return panes.TitleStyle(m.st, active && focused).Render(s) }
|
||||||
resultsTitle := map[string]string{scopeFavs: "Favourites", scopeRecent: "Recent", allSources: "Results in all sources"}[v.scope().id]
|
resultsTitle := map[string]string{scopeFavs: "Favourites", scopeRecent: "Recent", allSources: "Results in all sources"}[v.scope().id]
|
||||||
if v.onSource() {
|
if _, ok := v.scopeAccount(); ok || v.onSource() {
|
||||||
resultsTitle = "Results in " + ansi.Strip(v.scope().label)
|
resultsTitle = "Results in " + scopeName(v.scope())
|
||||||
}
|
|
||||||
if kind, ok := v.scopeKind(); ok {
|
|
||||||
resultsTitle = "Results in " + string(kind)
|
|
||||||
if kind == provider.KindRepo {
|
|
||||||
resultsTitle = "Results in all repos"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var lists string
|
var lists string
|
||||||
if v.tab == tabSearch {
|
if v.tab == tabSearch {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func (m *Map) Actions() []Action {
|
|||||||
a(Lists, "nextTab", &m.NextTab, "next tab", "lists", "Next tab of the focused pane."),
|
a(Lists, "nextTab", &m.NextTab, "next tab", "lists", "Next tab of the focused pane."),
|
||||||
a(Lists, "sourcesView", &m.SourcesView, "grouped/flat sources", "lists", "Switch the Sources pane between grouped by provider and a flat list."),
|
a(Lists, "sourcesView", &m.SourcesView, "grouped/flat sources", "lists", "Switch the Sources pane between grouped by provider and a flat list."),
|
||||||
a(Lists, "star", &m.Favourite, "star", "lists", "Star or unstar the selected subscription, vault or secret, for the account showing it. Starred items get a ★ and sort first."),
|
a(Lists, "star", &m.Favourite, "star", "lists", "Star or unstar the selected subscription, vault or secret, for the account showing it. Starred items get a ★ and sort first."),
|
||||||
a(Lists, "search", &m.Search, "search", "lists", "Go to anything: search every vault and secret loaded and jump to one. Again in Go to: only the source you're in, and back to all. On the left, the scope: All, all of Azure, Databricks or repos, or one source (○ ◐ ● for how much of it is loaded)."),
|
a(Lists, "search", &m.Search, "search", "lists", "Go to anything: search every vault and secret loaded and jump to one. Again in Go to: only the source or group you're in, and back to all. On the left, the scope: All, an account (or Databricks, or Repos), or one source (○ ◐ ● for how much of it is loaded)."),
|
||||||
a(Lists, "favourites", &m.Favourites, "favourites", "lists", "Go to, on Favourites: everything you've starred, to search and jump to."),
|
a(Lists, "favourites", &m.Favourites, "favourites", "lists", "Go to, on Favourites: everything you've starred, to search and jump to."),
|
||||||
a(Lists, "recent", &m.Recent, "recent", "lists", "Go to, on Recent: what you used last, newest first."),
|
a(Lists, "recent", &m.Recent, "recent", "lists", "Go to, on Recent: what you used last, newest first."),
|
||||||
a(Lists, "commands", &m.Commands, "commands", "lists", "List the custom commands for the focused pane (customCommands in the config) and run one."),
|
a(Lists, "commands", &m.Commands, "commands", "lists", "List the custom commands for the focused pane (customCommands in the config) and run one."),
|
||||||
|
|||||||
Reference in new issue
Block a user