Skip to content

Commit

Permalink
Add a copy-to-clipboard menu to the file view (with diff copy optio…
Browse files Browse the repository at this point in the history
…ns) (#3104)
  • Loading branch information
stefanhaller authored Dec 7, 2023
2 parents 2162e5f + 6907816 commit a8a4211
Show file tree
Hide file tree
Showing 17 changed files with 363 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: Amend last commit
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: ステージ/アンステージ
<kbd>&lt;c-b&gt;</kbd>: ファイルをフィルタ (ステージ/アンステージ)
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>A</kbd>: 最新のコミットにamend
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Staged 전환
<kbd>&lt;c-b&gt;</kbd>: 파일을 필터하기 (Staged/unstaged)
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: 마지맛 커밋 수정
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_nl.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: Wijzig laatste commit
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: Pokaż opcje porzucania zmian
<kbd>&lt;space&gt;</kbd>: Przełącz stan poczekalni
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>A</kbd>: Zmień ostatni commit
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ _Связки клавиш_
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
<kbd>&lt;space&gt;</kbd>: Переключить индекс
<kbd>&lt;c-b&gt;</kbd>: Фильтровать файлы (проиндексированные/непроиндексированные)
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: Сохранить изменения
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
<kbd>A</kbd>: Правка последнего коммита
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: 查看'放弃更改'选项
<kbd>&lt;space&gt;</kbd>: 切换暂存状态
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更改而无需预先提交钩子
<kbd>A</kbd>: 修补最后一次提交
Expand Down
1 change: 1 addition & 0 deletions docs/keybindings/Keybindings_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
<kbd>d</kbd>: 檢視“捨棄更改”的選項
<kbd>&lt;space&gt;</kbd>: 切換預存
<kbd>&lt;c-b&gt;</kbd>: 篩選檔案 (預存/未預存)
<kbd>y</kbd>: Copy to clipboard
<kbd>c</kbd>: 提交變更
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
<kbd>A</kbd>: 修正上次提交
Expand Down
23 changes: 23 additions & 0 deletions pkg/commands/git_commands/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,26 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
)
}

func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
return NewGitCmd("diff").
Arg("--no-ext-diff", "--no-color").
Arg(diffArgs...)
}

func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
return self.cmd.New(
self.internalDiffCmdObj().
ArgIf(staged, "--staged").
Arg(path).
ToArgv(),
).RunWithOutput()
}

func (self *DiffCommands) GetAllDiff(staged bool) (string, error) {
return self.cmd.New(
self.internalDiffCmdObj().
ArgIf(staged, "--staged").
ToArgv(),
).RunWithOutput()
}
2 changes: 2 additions & 0 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ type KeybindingFilesConfig struct {
ToggleTreeView string `yaml:"toggleTreeView"`
OpenMergeTool string `yaml:"openMergeTool"`
OpenStatusFilter string `yaml:"openStatusFilter"`
CopyFileInfoToClipboard string `yaml:"copyFileInfoToClipboard"`
}

type KeybindingBranchesConfig struct {
Expand Down Expand Up @@ -763,6 +764,7 @@ func GetDefaultConfig() *UserConfig {
OpenMergeTool: "M",
OpenStatusFilter: "<c-b>",
ConfirmDiscard: "x",
CopyFileInfoToClipboard: "y",
},
Branches: KeybindingBranchesConfig{
CopyPullRequestURL: "<c-y>",
Expand Down
103 changes: 103 additions & 0 deletions pkg/gui/controllers/files_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
Handler: self.handleStatusFilterPressed,
Description: self.c.Tr.FileFilter,
},
{
Key: opts.GetKey(opts.Config.Files.CopyFileInfoToClipboard),
Handler: self.openCopyMenu,
Description: self.c.Tr.CopyToClipboardMenu,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.CommitChanges),
Handler: self.c.Helpers().WorkingTree.HandleCommitPress,
Expand Down Expand Up @@ -748,6 +754,103 @@ func (self *FilesController) createStashMenu() error {
})
}

func (self *FilesController) openCopyMenu() error {
node := self.context().GetSelected()

copyNameItem := &types.MenuItem{
Label: self.c.Tr.CopyFileName,
OnPress: func() error {
if err := self.c.OS().CopyToClipboard(node.Name()); err != nil {
return self.c.Error(err)
}
self.c.Toast(self.c.Tr.FileNameCopiedToast)
return nil
},
Key: 'n',
}
copyPathItem := &types.MenuItem{
Label: self.c.Tr.CopyFilePath,
OnPress: func() error {
if err := self.c.OS().CopyToClipboard(node.Path); err != nil {
return self.c.Error(err)
}
self.c.Toast(self.c.Tr.FilePathCopiedToast)
return nil
},
Key: 'p',
}
copyFileDiffItem := &types.MenuItem{
Label: self.c.Tr.CopySelectedDiff,
Tooltip: self.c.Tr.CopyFileDiffTooltip,
OnPress: func() error {
path := self.context().GetSelectedPath()
hasStaged := self.hasPathStagedChanges(node)
diff, err := self.c.Git().Diff.GetPathDiff(path, hasStaged)
if err != nil {
return self.c.Error(err)
}
if err := self.c.OS().CopyToClipboard(diff); err != nil {
return self.c.Error(err)
}
self.c.Toast(self.c.Tr.FileDiffCopiedToast)
return nil
},
Key: 's',
}
copyAllDiff := &types.MenuItem{
Label: self.c.Tr.CopyAllFilesDiff,
Tooltip: self.c.Tr.CopyFileDiffTooltip,
OnPress: func() error {
hasStaged := self.c.Helpers().WorkingTree.AnyStagedFiles()
diff, err := self.c.Git().Diff.GetAllDiff(hasStaged)
if err != nil {
return self.c.Error(err)
}
if err := self.c.OS().CopyToClipboard(diff); err != nil {
return self.c.Error(err)
}
self.c.Toast(self.c.Tr.AllFilesDiffCopiedToast)
return nil
},
Key: 'a',
}

if node == nil {
copyNameItem.DisabledReason = self.c.Tr.NoContentToCopyError
copyPathItem.DisabledReason = self.c.Tr.NoContentToCopyError
copyFileDiffItem.DisabledReason = self.c.Tr.NoContentToCopyError
}
if node != nil && !node.GetHasStagedOrTrackedChanges() {
copyFileDiffItem.DisabledReason = self.c.Tr.NoContentToCopyError
}
if !self.anyStagedOrTrackedFile() {
copyAllDiff.DisabledReason = self.c.Tr.NoContentToCopyError
}

return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.CopyToClipboardMenu,
Items: []*types.MenuItem{
copyNameItem,
copyPathItem,
copyFileDiffItem,
copyAllDiff,
},
})
}

func (self *FilesController) anyStagedOrTrackedFile() bool {
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
return self.c.Helpers().WorkingTree.AnyTrackedFiles()
}
return true
}

func (self *FilesController) hasPathStagedChanges(node *filetree.FileNode) bool {
return node.SomeFile(func(t *models.File) bool {
return t.HasStagedChanges
})
}

func (self *FilesController) stash() error {
return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashAllChanges)
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/gui/filetree/file_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ func (self *FileNode) GetHasUnstagedChanges() bool {
return self.SomeFile(func(file *models.File) bool { return file.HasUnstagedChanges })
}

func (self *FileNode) GetHasStagedOrTrackedChanges() bool {
if !self.GetHasStagedChanges() {
return self.SomeFile(func(t *models.File) bool {
return t.Tracked
})
}
return true
}

func (self *FileNode) GetHasStagedChanges() bool {
return self.SomeFile(func(file *models.File) bool { return file.HasStagedChanges })
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/gui/filetree/node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package filetree

import (
"path"

"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo"
Expand Down Expand Up @@ -300,3 +302,7 @@ func (self *Node[T]) ID() string {
func (self *Node[T]) Description() string {
return self.GetPath()
}

func (self *Node[T]) Name() string {
return path.Base(self.Path)
}
22 changes: 22 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ type TranslationSet struct {
Pull string
Scroll string
FileFilter string
CopyToClipboardMenu string
CopyFileName string
CopyFilePath string
CopyFileDiffTooltip string
CopySelectedDiff string
CopyAllFilesDiff string
NoContentToCopyError string
FileNameCopiedToast string
FilePathCopiedToast string
FileDiffCopiedToast string
AllFilesDiffCopiedToast string
FilterStagedFiles string
FilterUnstagedFiles string
ResetFilter string
Expand Down Expand Up @@ -851,6 +862,17 @@ func EnglishTranslationSet() TranslationSet {
CantCheckoutBranchWhilePulling: "You cannot checkout another branch while pulling the current branch",
CantPullOrPushSameBranchTwice: "You cannot push or pull a branch while it is already being pushed or pulled",
FileFilter: "Filter files by status",
CopyToClipboardMenu: "Copy to clipboard",
CopyFileName: "File name",
CopyFilePath: "Path",
CopyFileDiffTooltip: "If there are staged items, this command considers only them. Otherwise, it considers all the unstaged ones.",
CopySelectedDiff: "Diff of selected file",
CopyAllFilesDiff: "Diff of all files",
NoContentToCopyError: "Nothing to copy",
FileNameCopiedToast: "File name copied to clipboard",
FilePathCopiedToast: "File path copied to clipboard",
FileDiffCopiedToast: "File diff copied to clipboard",
AllFilesDiffCopiedToast: "All files diff copied to clipboard",
FilterStagedFiles: "Show only staged files",
FilterUnstagedFiles: "Show only unstaged files",
ResetFilter: "Reset filter",
Expand Down
Loading

0 comments on commit a8a4211

Please sign in to comment.