feat: e on the status pane edits the config, as in lazygit
Instead of a global E: e on the status pane opens the config, shown in its footer as "e edit config". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
7 files changed
+26
-16
No files matched your search
+3
-3
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
Optional, at `~/.config/lazysecrets/config.yaml` (or
|
Optional, at `~/.config/lazysecrets/config.yaml` (or
|
||||||
`$XDG_CONFIG_HOME/lazysecrets/config.yaml`, or `--config`). Flags override
|
`$XDG_CONFIG_HOME/lazysecrets/config.yaml`, or `--config`). Flags override
|
||||||
it. Unknown keys are an error, so typos don't go unnoticed. `E` in
|
it. Unknown keys are an error, so typos don't go unnoticed. `e` on the
|
||||||
lazysecrets opens it in your editor (making it if it isn't there yet);
|
status pane (`0`) opens it in your editor, as in lazygit (making it if
|
||||||
changes apply when lazysecrets starts again.
|
it isn't there yet); changes apply when lazysecrets starts again.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0
|
icons: true # Nerd Font icons (v3+); also --icons=false or LAZYSECRETS_ICONS=0
|
||||||
|
|||||||
+2
-2
@@ -59,7 +59,6 @@ match case exactly.
|
|||||||
| `x` | [custom commands](custom-commands.md) |
|
| `x` | [custom commands](custom-commands.md) |
|
||||||
| `L` | sign in again (shown when a login has expired) |
|
| `L` | sign in again (shown when a login has expired) |
|
||||||
| `U` | a newer lazysecrets: its changelog, and update in place (restart to use it); or look for one |
|
| `U` | a newer lazysecrets: its changelog, and update in place (restart to use it); or look for one |
|
||||||
| `E` | edit your [config](config.md), like `e` opens a file (made if it isn't there yet; restart to use it) |
|
|
||||||
|
|
||||||
A revealed value is shown as it reads best: JSON indented, and PEM
|
A revealed value is shown as it reads best: JSON indented, and PEM
|
||||||
certificates as their subject, issuer, dates, names and thumbprint, with
|
certificates as their subject, issuer, dates, names and thumbprint, with
|
||||||
@@ -123,7 +122,8 @@ Secrets. Going into it (`l`, or `4`) keeps it on that; there `j`/`k`,
|
|||||||
it's hidden.
|
it's hidden.
|
||||||
|
|
||||||
On top, the status pane (`0`) shows the version, a newer release and how
|
On top, the status pane (`0`) shows the version, a newer release and how
|
||||||
your accounts are doing. Focused, the right pane lists more under the
|
your accounts are doing; `e` there opens your [config](config.md) in your
|
||||||
|
editor, as in lazygit. Focused, the right pane lists more under the
|
||||||
logo (when there's room): the version and updates (`U`), each account
|
logo (when there's room): the version and updates (`U`), each account
|
||||||
(`L` on one that needs it signs in), what's expiring (`enter` goes to
|
(`L` on one that needs it signs in), what's expiring (`enter` goes to
|
||||||
it), this session's errors, newest first (what a pane showed, or the
|
it), this session's errors, newest first (what a pane showed, or the
|
||||||
|
|||||||
@@ -965,8 +965,6 @@ func (m *Model) handleKey(msg tea.KeyPressMsg) (cmd tea.Cmd, quit bool) {
|
|||||||
return m.refresh(), false
|
return m.refresh(), false
|
||||||
case key.Matches(msg, k.Update):
|
case key.Matches(msg, k.Update):
|
||||||
return m.update(), false
|
return m.update(), false
|
||||||
case key.Matches(msg, k.EditConfig):
|
|
||||||
return m.editConfig(), false
|
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"git.kebcloud.no/keb/lazysecrets/internal/update"
|
"git.kebcloud.no/keb/lazysecrets/internal/update"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Opening a repo's file (e): SOPS files with sops edit, which decrypts
|
// Opening a repo's file (e), or on the status pane the config: SOPS files with sops edit, which decrypts
|
||||||
// them for the editor and encrypts them again after. Other files, inside
|
// them for the editor and encrypts them again after. Other files, inside
|
||||||
// nvim: in that nvim, once lazysecrets has quit; else in $VISUAL or
|
// nvim: in that nvim, once lazysecrets has quit; else in $VISUAL or
|
||||||
// $EDITOR, coming back after; else in the system's default app. On a
|
// $EDITOR, coming back after; else in the system's default app. On a
|
||||||
@@ -47,6 +47,8 @@ func (m *Model) edit() tea.Cmd {
|
|||||||
var c provider.Container
|
var c provider.Container
|
||||||
var o *provider.Object
|
var o *provider.Object
|
||||||
switch m.focus {
|
switch m.focus {
|
||||||
|
case paneStatus: // like lazygit's status panel
|
||||||
|
return m.editConfig()
|
||||||
case paneSources:
|
case paneSources:
|
||||||
m.flash = warning("select a file to open it")
|
m.flash = warning("select a file to open it")
|
||||||
return nil
|
return nil
|
||||||
@@ -100,7 +102,7 @@ func (m *Model) edit() tea.Cmd {
|
|||||||
const configHeader = "# lazysecrets config: " + update.DefaultRepo + "/src/branch/main/docs/config.md\n"
|
const configHeader = "# lazysecrets config: " + update.DefaultRepo + "/src/branch/main/docs/config.md\n"
|
||||||
|
|
||||||
// editConfig opens the user's config file (the first of them), as e
|
// editConfig opens the user's config file (the first of them), as e
|
||||||
// opens a file, making it if it isn't there yet.
|
// opens a file, making it if it isn't there yet: e on the status pane.
|
||||||
func (m *Model) editConfig() tea.Cmd {
|
func (m *Model) editConfig() tea.Cmd {
|
||||||
if len(m.configFiles) == 0 {
|
if len(m.configFiles) == 0 {
|
||||||
m.flash = warning("no config file")
|
m.flash = warning("no config file")
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ func (c contextHelp) ShortHelp() []key.Binding {
|
|||||||
switch {
|
switch {
|
||||||
case c.focus == paneStatus:
|
case c.focus == paneStatus:
|
||||||
details := key.NewBinding(key.WithKeys(k.Enter.Keys()...), key.WithHelp(k.Enter.Help().Key, "details"))
|
details := key.NewBinding(key.WithKeys(k.Enter.Keys()...), key.WithHelp(k.Enter.Help().Key, "details"))
|
||||||
return append(b, details, k.Update, k.Help, k.Quit)
|
config := key.NewBinding(key.WithKeys(k.Edit.Keys()...), key.WithHelp(k.Edit.Help().Key, "edit config"))
|
||||||
|
return append(b, details, config, k.Update, k.Help, k.Quit)
|
||||||
case c.focus == paneDetail && c.account && c.of == paneStatus:
|
case c.focus == paneDetail && c.account && c.of == paneStatus:
|
||||||
open := key.NewBinding(key.WithKeys(k.Open.Keys()...), key.WithHelp(k.Open.Help().Key, "open link"))
|
open := key.NewBinding(key.WithKeys(k.Open.Keys()...), key.WithHelp(k.Open.Help().Key, "open link"))
|
||||||
return append(b, k.Up, k.Down, copyField, open, k.Edit, k.Filter, k.Help, k.Quit)
|
return append(b, k.Up, k.Down, copyField, open, k.Edit, k.Filter, k.Help, k.Quit)
|
||||||
|
|||||||
@@ -350,8 +350,9 @@ func TestEditConfig(t *testing.T) {
|
|||||||
d := newDriverOpts(t, Options{ConfigFiles: []string{path}, OpenURL: func(u string) error { opened = append(opened, u); return nil }})
|
d := newDriverOpts(t, Options{ConfigFiles: []string{path}, OpenURL: func(u string) error { opened = append(opened, u); return nil }})
|
||||||
d.m.getenv = func(string) string { return "" }
|
d.m.getenv = func(string) string { return "" }
|
||||||
|
|
||||||
// No editor: the system's default app, on a config made for it.
|
// e on the status pane. No editor: the system's default app, on a
|
||||||
d.keys("E")
|
// config made for it.
|
||||||
|
d.keys("0", "e")
|
||||||
if !slices.Equal(opened, []string{path}) {
|
if !slices.Equal(opened, []string{path}) {
|
||||||
t.Errorf("opened %q", opened)
|
t.Errorf("opened %q", opened)
|
||||||
}
|
}
|
||||||
@@ -382,3 +383,12 @@ func TestEditConfig(t *testing.T) {
|
|||||||
t.Errorf("flash %q", d.m.flash.text)
|
t.Errorf("flash %q", d.m.flash.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatusFooterEditsConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
d := newDriver(t)
|
||||||
|
d.keys("0")
|
||||||
|
if !strings.Contains(d.screen(), "e edit config") {
|
||||||
|
t.Errorf("no edit config in the footer:\n%s", d.screen())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ type Map struct {
|
|||||||
SplitLeft, SplitRight, SplitReset key.Binding
|
SplitLeft, SplitRight, SplitReset key.Binding
|
||||||
Taller, Shorter, ZoomIn, ZoomOut key.Binding
|
Taller, Shorter, ZoomIn, ZoomOut key.Binding
|
||||||
Login, Refresh, Help, Quit key.Binding
|
Login, Refresh, Help, Quit key.Binding
|
||||||
Update, EditConfig key.Binding
|
Update key.Binding
|
||||||
// filter
|
// filter
|
||||||
AcceptFilter, ClearFilter key.Binding
|
AcceptFilter, ClearFilter key.Binding
|
||||||
FilterUp, FilterDown key.Binding
|
FilterUp, FilterDown key.Binding
|
||||||
@@ -114,7 +114,7 @@ func (m *Map) Actions() []Action {
|
|||||||
a(Lists, "copyReference", &m.CopyRef, "copy reference", "secrets", "Copy the reference to use in config, e.g. @Microsoft.KeyVault(SecretUri=…) or {{secrets/scope/key}}."),
|
a(Lists, "copyReference", &m.CopyRef, "copy reference", "secrets", "Copy the reference to use in config, e.g. @Microsoft.KeyVault(SecretUri=…) or {{secrets/scope/key}}."),
|
||||||
a(Lists, "versions", &m.Versions, "versions", "secrets", "List the versions of the selected item (Azure). Reveal, copy and share then act on the selected version."),
|
a(Lists, "versions", &m.Versions, "versions", "secrets", "List the versions of the selected item (Azure). Reveal, copy and share then act on the selected version."),
|
||||||
a(Lists, "openInBrowser", &m.Open, "open in browser", "secrets", "Open the selected subscription, vault or item in the browser (Azure portal or Databricks workspace)."),
|
a(Lists, "openInBrowser", &m.Open, "open in browser", "secrets", "Open the selected subscription, vault or item in the browser (Azure portal or Databricks workspace)."),
|
||||||
a(Lists, "edit", &m.Edit, "open file", "secrets", "Open the selected SOPS or .env file, or the file of the selected secret at its line. SOPS files open with sops edit. Others open in the nvim lazysecrets runs in (quitting lazysecrets), else in $VISUAL or $EDITOR, else in the system's default app."),
|
a(Lists, "edit", &m.Edit, "open file", "secrets", "Open the selected SOPS or .env file, or the file of the selected secret at its line; on the status pane, your config (made if it isn't there yet). SOPS files open with sops edit. Others open in the nvim lazysecrets runs in (quitting lazysecrets), else in $VISUAL or $EDITOR, else in the system's default app."),
|
||||||
|
|
||||||
a(Lists, "share", &m.Share, "copy link", "share", "Copy a lazysecrets:// link to the selected subscription, workspace, vault, scope or secret, for sharing. From the versions view the link is pinned to that version."),
|
a(Lists, "share", &m.Share, "copy link", "share", "Copy a lazysecrets:// link to the selected subscription, workspace, vault, scope or secret, for sharing. From the versions view the link is pinned to that version."),
|
||||||
a(Lists, "shareMenu", &m.ShareMenu, "share", "share", "Choose what to share: the lazysecrets link, the plain Key Vault URL, the config reference, or the web page."),
|
a(Lists, "shareMenu", &m.ShareMenu, "share", "share", "Choose what to share: the lazysecrets link, the plain Key Vault URL, the config reference, or the web page."),
|
||||||
@@ -131,7 +131,6 @@ func (m *Map) Actions() []Action {
|
|||||||
a(Lists, "refresh", &m.Refresh, "refresh", "other", "Reload the focused pane, bypassing the cache."),
|
a(Lists, "refresh", &m.Refresh, "refresh", "other", "Reload the focused pane, bypassing the cache."),
|
||||||
a(Lists, "login", &m.Login, "sign in", "other", "Sign in again (az login or databricks auth login) when a login has expired."),
|
a(Lists, "login", &m.Login, "sign in", "other", "Sign in again (az login or databricks auth login) when a login has expired."),
|
||||||
a(Lists, "update", &m.Update, "update", "other", "Check for a newer lazysecrets: its changelog, and update to it."),
|
a(Lists, "update", &m.Update, "update", "other", "Check for a newer lazysecrets: its changelog, and update to it."),
|
||||||
a(Lists, "editConfig", &m.EditConfig, "edit config", "other", "Open your config file like e opens a file: in the nvim lazysecrets runs in, else in $VISUAL or $EDITOR, else in the system's default app. Made if it doesn't exist yet. Changes apply when lazysecrets starts again."),
|
|
||||||
a(Lists, "help", &m.Help, "help", "other", "Show this list. Also works in Go to while browsing, and in menus, showing their keys first."),
|
a(Lists, "help", &m.Help, "help", "other", "Show this list. Also works in Go to while browsing, and in menus, showing their keys first."),
|
||||||
a(Lists, "quit", &m.Quit, "quit", "other", "Quit lazysecrets. ctrl+c always quits too."),
|
a(Lists, "quit", &m.Quit, "quit", "other", "Quit lazysecrets. ctrl+c always quits too."),
|
||||||
|
|
||||||
@@ -195,7 +194,7 @@ var defaults = map[string][]string{
|
|||||||
"lists.dividerLeft": {"ctrl+left", "<"}, "lists.dividerRight": {"ctrl+right", ">"},
|
"lists.dividerLeft": {"ctrl+left", "<"}, "lists.dividerRight": {"ctrl+right", ">"},
|
||||||
"lists.taller": {"ctrl+down", "}"}, "lists.shorter": {"ctrl+up", "{"},
|
"lists.taller": {"ctrl+down", "}"}, "lists.shorter": {"ctrl+up", "{"},
|
||||||
"lists.zoomIn": {"+"}, "lists.zoomOut": {"_"}, "lists.resetLayout": {"="},
|
"lists.zoomIn": {"+"}, "lists.zoomOut": {"_"}, "lists.resetLayout": {"="},
|
||||||
"lists.refresh": {"r"}, "lists.login": {"L"}, "lists.update": {"U"}, "lists.editConfig": {"E"}, "lists.help": {"?"}, "lists.quit": {"q"},
|
"lists.refresh": {"r"}, "lists.login": {"L"}, "lists.update": {"U"}, "lists.help": {"?"}, "lists.quit": {"q"},
|
||||||
|
|
||||||
"filter.down": {"ctrl+j", "ctrl+n", "down"}, "filter.up": {"ctrl+k", "ctrl+p", "up"},
|
"filter.down": {"ctrl+j", "ctrl+n", "down"}, "filter.up": {"ctrl+k", "ctrl+p", "up"},
|
||||||
"filter.copyValue": {"ctrl+y"}, "filter.reveal": {"ctrl+r"}, "filter.star": {"ctrl+f"},
|
"filter.copyValue": {"ctrl+y"}, "filter.reveal": {"ctrl+r"}, "filter.star": {"ctrl+f"},
|
||||||
|
|||||||
Reference in new issue
Block a user