mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-13 09:06:27 -04:00
Copy the selected diff line(s) from the focused main view
`ctrl+o` in the focused main view copies the selection to the clipboard,
mirroring the staging view's copy. Unlike stage / discard, copying is the same
for every diff panel — it just reads the text shown — so it's a direct
MainViewController command rather than a FocusedMainViewActions method. That
keeps it free of per-panel duplication and lets it work over panels that have
no actions, notably the reflog.
The +/- prefix handling is metadata-aware where it matters: with no pager the
main view shows the raw diff, so a homogeneous selection has its +/-/space
column stripped (dropDiffPrefix) to ease pasting into code. With a pager
configured the rendered lines carry no such column, so stripping a leading
character would eat real content — copy verbatim instead. (Refining the raw
case to classify header lines via the diff-line metadata, fixing the
'--- a/file' edge case noted in 159bbb0825, is left for later.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a0af1b1d9a
commit
f0a997705a
|
|
@ -226,6 +226,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Stage | Toggle selection staged / unstaged. |
|
||||
| `` d `` | Discard | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Copy selected text to clipboard | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Toggle range select | |
|
||||
| `` <left>, h `` | Go to previous hunk | |
|
||||
|
|
@ -339,6 +340,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Stage | Toggle selection staged / unstaged. |
|
||||
| `` d `` | Discard | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Copy selected text to clipboard | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Toggle range select | |
|
||||
| `` <left>, h `` | Go to previous hunk | |
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | サイドパネルに戻る | |
|
||||
| `` <space> `` | ステージ | 選択された部分のステージ / アンステージを切り替えます。 |
|
||||
| `` d `` | 破棄 | ステージされていない変更が選択されている場合、`git reset`を使用して変更を破棄します。ステージされた変更が選択されている場合、変更をアンステージします。 |
|
||||
| `` <ctrl+o> `` | 選択したテキストをクリップボードにコピー | |
|
||||
| `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 範囲選択を切り替え | |
|
||||
| `` <left>, h `` | 前のハンクに移動 | |
|
||||
|
|
@ -316,6 +317,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | サイドパネルに戻る | |
|
||||
| `` <space> `` | ステージ | 選択された部分のステージ / アンステージを切り替えます。 |
|
||||
| `` d `` | 破棄 | ステージされていない変更が選択されている場合、`git reset`を使用して変更を破棄します。ステージされた変更が選択されている場合、変更をアンステージします。 |
|
||||
| `` <ctrl+o> `` | 選択したテキストをクリップボードにコピー | |
|
||||
| `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 範囲選択を切り替え | |
|
||||
| `` <left>, h `` | 前のハンクに移動 | |
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Staged 전환 | 선택한 행을 staged / unstaged |
|
||||
| `` d `` | 변경을 삭제 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | 선택한 텍스트를 클립보드에 복사 | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 드래그 선택 전환 | |
|
||||
| `` <left>, h `` | 이전 hunk를 선택 | |
|
||||
|
|
@ -173,6 +174,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Staged 전환 | 선택한 행을 staged / unstaged |
|
||||
| `` d `` | 변경을 삭제 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | 선택한 텍스트를 클립보드에 복사 | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 드래그 선택 전환 | |
|
||||
| `` <left>, h `` | 이전 hunk를 선택 | |
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Toggle staged | Toggle lijnen staged / unstaged |
|
||||
| `` d `` | Verwijdert change (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Copy selected text to clipboard | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Toggle drag selecteer | |
|
||||
| `` <left>, h `` | Selecteer de vorige hunk | |
|
||||
|
|
@ -317,6 +318,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Toggle staged | Toggle lijnen staged / unstaged |
|
||||
| `` d `` | Verwijdert change (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Copy selected text to clipboard | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Toggle drag selecteer | |
|
||||
| `` <left>, h `` | Selecteer de vorige hunk | |
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Zatwierdź | Przełącz zaznaczenie zatwierdzone/niezatwierdzone. |
|
||||
| `` d `` | Odrzuć | Gdy zaznaczona jest niezatwierdzona zmiana, odrzuć ją używając `git reset`. Gdy zaznaczona jest zatwierdzona zmiana, cofnij zatwierdzenie. |
|
||||
| `` <ctrl+o> `` | Kopiuj zaznaczony tekst do schowka | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Przełącz zaznaczenie zakresu | |
|
||||
| `` <left>, h `` | Idź do poprzedniego fragmentu | |
|
||||
|
|
@ -212,6 +213,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Zatwierdź | Przełącz zaznaczenie zatwierdzone/niezatwierdzone. |
|
||||
| `` d `` | Odrzuć | Gdy zaznaczona jest niezatwierdzona zmiana, odrzuć ją używając `git reset`. Gdy zaznaczona jest zatwierdzona zmiana, cofnij zatwierdzenie. |
|
||||
| `` <ctrl+o> `` | Kopiuj zaznaczony tekst do schowka | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Przełącz zaznaczenie zakresu | |
|
||||
| `` <left>, h `` | Idź do poprzedniego fragmentu | |
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Etapa | Ativar/desativar seleção em staged/unstaged |
|
||||
| `` d `` | Descartar | Quando a mudança não desejada for selecionada, descarte a mudança usando `git reset`. Quando a mudança em fase é selecionada, despare a mudança. |
|
||||
| `` <ctrl+o> `` | Copiar texto selecionado para área de transferência | |
|
||||
| `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. |
|
||||
| `` v `` | Toggle range select | |
|
||||
| `` <left>, h `` | Ir para o local anterior | |
|
||||
|
|
@ -348,6 +349,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Etapa | Ativar/desativar seleção em staged/unstaged |
|
||||
| `` d `` | Descartar | Quando a mudança não desejada for selecionada, descarte a mudança usando `git reset`. Quando a mudança em fase é selecionada, despare a mudança. |
|
||||
| `` <ctrl+o> `` | Copiar texto selecionado para área de transferência | |
|
||||
| `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. |
|
||||
| `` v `` | Toggle range select | |
|
||||
| `` <left>, h `` | Ir para o local anterior | |
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Переключить индекс | Переключить строку в проиндексированные / непроиндексированные |
|
||||
| `` d `` | Отменить изменение (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Скопировать выделенный текст в буфер обмена | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Переключить выборку перетаскивания | |
|
||||
| `` <left>, h `` | Выбрать предыдущую часть | |
|
||||
|
|
@ -117,6 +118,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | Переключить индекс | Переключить строку в проиндексированные / непроиндексированные |
|
||||
| `` d `` | Отменить изменение (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | Скопировать выделенный текст в буфер обмена | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | Переключить выборку перетаскивания | |
|
||||
| `` <left>, h `` | Выбрать предыдущую часть | |
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | 退出回到侧边面板 | |
|
||||
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
|
||||
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时,使用git reset丢弃该变更。当选择已暂存的变更时,取消暂存该变更 |
|
||||
| `` <ctrl+o> `` | 复制选中文本到剪贴板 | |
|
||||
| `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 |
|
||||
| `` v `` | 切换拖动选择 | |
|
||||
| `` <left>, h `` | 选择上一个区块 | |
|
||||
|
|
@ -345,6 +346,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | 退出回到侧边面板 | |
|
||||
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
|
||||
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时,使用git reset丢弃该变更。当选择已暂存的变更时,取消暂存该变更 |
|
||||
| `` <ctrl+o> `` | 复制选中文本到剪贴板 | |
|
||||
| `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 |
|
||||
| `` v `` | 切换拖动选择 | |
|
||||
| `` <left>, h `` | 选择上一个区块 | |
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
|
||||
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | 複製所選文本至剪貼簿 | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 切換拖曳選擇 | |
|
||||
| `` <left>, h `` | 選擇上一段 | |
|
||||
|
|
@ -374,6 +375,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
|||
| `` <esc> `` | Exit back to side panel | |
|
||||
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
|
||||
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
|
||||
| `` <ctrl+o> `` | 複製所選文本至剪貼簿 | |
|
||||
| `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. |
|
||||
| `` v `` | 切換拖曳選擇 | |
|
||||
| `` <left>, h `` | 選擇上一段 | |
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gocui"
|
||||
|
|
@ -87,6 +88,12 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
|||
Tooltip: self.c.Tr.DiscardSelectionTooltip,
|
||||
DisplayOnScreen: selectionShown,
|
||||
},
|
||||
{
|
||||
Keys: opts.GetKeys(opts.Config.Universal.CopyToClipboard),
|
||||
Handler: self.copySelection,
|
||||
Description: self.c.Tr.CopySelectedTextToClipboard,
|
||||
DisplayOnScreen: selectionShown,
|
||||
},
|
||||
{
|
||||
Keys: opts.GetKeys(opts.Config.Universal.GoInto),
|
||||
Handler: self.enter,
|
||||
|
|
@ -362,6 +369,66 @@ func (self *MainViewController) discardSelectionDisabledReason() *types.Disabled
|
|||
return actions.DiscardSelectionDisabledReason()
|
||||
}
|
||||
|
||||
// copySelection copies the selected diff line(s) to the clipboard. Unlike the primary
|
||||
// action and discard, this is the same for every diff panel (it just reads the text the
|
||||
// focused main view shows), so it lives here rather than on FocusedMainViewActions — and
|
||||
// so it works over panels with no actions, like the reflog. A no-op when there's no
|
||||
// selection (non-diff content).
|
||||
//
|
||||
// With no pager the main view shows the raw diff, so the +/-/space column is stripped
|
||||
// from a homogeneous selection (dropDiffPrefix) to ease pasting into code, mirroring the
|
||||
// staging view's copy. With a pager configured we can't assume that column is present —
|
||||
// some pagers keep it and only colorize (e.g. ydiff), others drop or restructure it (e.g.
|
||||
// delta) — and we can't tell which, so we conservatively don't strip and copy verbatim.
|
||||
// (The cost is missing the convenience for column-preserving pagers.)
|
||||
func (self *MainViewController) copySelection() error {
|
||||
v := self.context.GetView()
|
||||
if !v.Highlight {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The selection is in (wrapped) view-line space; map it to the unwrapped buffer lines
|
||||
// it covers and copy those, so a wrapped line is copied whole and once — not indexed
|
||||
// into the buffer by a view-line number (which copies the wrong line, or panics when
|
||||
// the view line is past the buffer's length).
|
||||
firstView, lastView := v.SelectedLineRange()
|
||||
firstBuffer, ok := v.BufferLineForViewLine(firstView)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
lastBuffer, ok := v.BufferLineForViewLine(lastView)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
contents := v.DiffLineContents()
|
||||
lastBuffer = min(lastBuffer, len(contents)-1)
|
||||
if firstBuffer > lastBuffer {
|
||||
return nil
|
||||
}
|
||||
lines := make([]string, 0, lastBuffer-firstBuffer+1)
|
||||
for i := firstBuffer; i <= lastBuffer; i++ {
|
||||
lines = append(lines, contents[i].Text)
|
||||
}
|
||||
|
||||
// Trailing newline included so the last line is terminated too (dropDiffPrefix keeps
|
||||
// it; the pager-verbatim path needs it added here).
|
||||
selected := strings.Join(lines, "\n") + "\n"
|
||||
if !self.usingExternalDiff() {
|
||||
selected = dropDiffPrefix(selected)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopySelectedTextToClipboard)
|
||||
return self.c.OS().CopyToClipboard(selected)
|
||||
}
|
||||
|
||||
// usingExternalDiff reports whether the focused main view's diff is produced by an
|
||||
// external diff command (a pager), in which case the rendered lines may not carry the raw
|
||||
// +/-/space column (see copySelection).
|
||||
func (self *MainViewController) usingExternalDiff() bool {
|
||||
return self.c.State().GetDiffRendererConfigManager().GetDiffRendererType() == config.DiffRendererType_RawGit
|
||||
}
|
||||
|
||||
// revealSelectionAfterPrimaryAction re-establishes the focused-main-view selection after a
|
||||
// primary action (staging or a patch toggle) re-renders the diff. The selection's
|
||||
// change-line ordinal is read from the source pane (still showing the pre-action diff
|
||||
|
|
|
|||
41
pkg/integration/tests/staging/copy_from_main_view.go
Normal file
41
pkg/integration/tests/staging/copy_from_main_view.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package staging
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CopyFromMainView = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Copy a hunk from the focused main view to the clipboard, with the raw diff's +/- prefix stripped",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Gui.UseHunkModeInStagingView = true
|
||||
// Emulate the clipboard by writing to a file.
|
||||
config.GetUserConfig().OS.CopyToClipboardCmd = "printf '%s' {{text}} > clipboard"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file1", "one\ntwo\n")
|
||||
shell.Commit("one")
|
||||
|
||||
// Two consecutive added lines, so the selected hunk is a homogeneous addition.
|
||||
shell.UpdateFile("file1", "one\nADD1\nADD2\ntwo\n")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Universal.FocusMainView)
|
||||
|
||||
t.Views().Main().
|
||||
IsFocused().
|
||||
SelectedLines(
|
||||
Contains("+ADD1"),
|
||||
Contains("+ADD2"),
|
||||
).
|
||||
Press(keys.Universal.CopyToClipboard)
|
||||
|
||||
// With no pager the main view shows the raw diff, so a homogeneous addition is
|
||||
// copied with the '+' column stripped — ready to paste into code.
|
||||
t.FileSystem().FileContent("clipboard", Equals("ADD1\nADD2\n"))
|
||||
},
|
||||
})
|
||||
|
|
@ -412,6 +412,7 @@ var tests = []*components.IntegrationTest{
|
|||
shell_commands.History,
|
||||
shell_commands.OmitFromHistory,
|
||||
staging.AdvanceToNextHunkAfterStagingShiftsLineNumbers,
|
||||
staging.CopyFromMainView,
|
||||
staging.DiffChangeScreenMode,
|
||||
staging.DiffContextChange,
|
||||
staging.DiscardAllChanges,
|
||||
|
|
|
|||
Loading…
Reference in a new issue