2019-01-16 19:47:15 +02:00
|
|
|
// This "script" generates a file called Keybindings_{{.LANG}}.md
|
|
|
|
// in current working directory.
|
|
|
|
//
|
|
|
|
// The content of this generated file is a keybindings cheatsheet.
|
|
|
|
//
|
|
|
|
// To generate cheatsheet in english run:
|
2022-01-04 01:46:14 +02:00
|
|
|
// go run scripts/generate_cheatsheet.go
|
2019-01-16 19:47:15 +02:00
|
|
|
|
2022-01-04 01:46:14 +02:00
|
|
|
package cheatsheet
|
2019-01-16 19:47:15 +02:00
|
|
|
|
|
|
|
import (
|
2019-01-16 19:54:54 +02:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
2023-05-21 03:13:02 +02:00
|
|
|
"strings"
|
2019-02-16 03:07:27 +02:00
|
|
|
|
2022-03-19 07:34:46 +02:00
|
|
|
"github.com/jesseduffield/generics/maps"
|
2022-10-15 18:47:55 +02:00
|
|
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
2019-02-16 03:07:27 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/app"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
2022-03-27 08:15:17 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
2022-01-16 05:46:53 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
2022-01-04 00:20:38 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
2022-03-20 04:59:33 +02:00
|
|
|
"github.com/samber/lo"
|
2023-07-24 05:06:42 +02:00
|
|
|
"golang.org/x/exp/slices"
|
2019-01-16 19:47:15 +02:00
|
|
|
)
|
|
|
|
|
2019-03-02 09:45:18 +02:00
|
|
|
type bindingSection struct {
|
|
|
|
title string
|
2022-01-16 05:46:53 +02:00
|
|
|
bindings []*types.Binding
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
type header struct {
|
|
|
|
// priority decides the order of the headers in the cheatsheet (lower means higher)
|
|
|
|
priority int
|
|
|
|
title string
|
|
|
|
}
|
|
|
|
|
|
|
|
type headerWithBindings struct {
|
|
|
|
header header
|
|
|
|
bindings []*types.Binding
|
|
|
|
}
|
|
|
|
|
2022-01-04 01:46:14 +02:00
|
|
|
func CommandToRun() string {
|
|
|
|
return "go run scripts/cheatsheet/main.go generate"
|
|
|
|
}
|
|
|
|
|
2022-10-15 18:47:55 +02:00
|
|
|
func GetKeybindingsDir() string {
|
|
|
|
return utils.GetLazyRootDirectory() + "/docs/keybindings"
|
2022-01-04 01:46:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func generateAtDir(cheatsheetDir string) {
|
2022-01-04 00:20:38 +02:00
|
|
|
translationSetsByLang := i18n.GetTranslationSets()
|
|
|
|
mConfig := config.NewDummyAppConfig()
|
2019-03-02 09:45:18 +02:00
|
|
|
|
2022-01-04 00:20:38 +02:00
|
|
|
for lang := range translationSetsByLang {
|
2022-03-16 10:28:10 +02:00
|
|
|
mConfig.GetUserConfig().Gui.Language = lang
|
2022-05-07 07:23:08 +02:00
|
|
|
common, err := app.NewCommon(mConfig)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
mApp, _ := app.NewApp(mConfig, common)
|
2022-01-04 01:46:14 +02:00
|
|
|
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
|
|
|
file, err := os.Create(path)
|
2019-03-22 11:13:48 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-03-02 09:45:18 +02:00
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
bindings := mApp.Gui.GetCheatsheetKeybindings()
|
|
|
|
bindingSections := getBindingSections(bindings, mApp.Tr)
|
2022-01-04 00:20:38 +02:00
|
|
|
content := formatSections(mApp.Tr, bindingSections)
|
2022-01-19 12:36:42 +02:00
|
|
|
content = fmt.Sprintf("_This file is auto-generated. To update, make the changes in the "+
|
|
|
|
"pkg/i18n directory and then run `%s` from the project root._\n\n%s", CommandToRun(), content)
|
2019-03-16 09:40:48 +02:00
|
|
|
writeString(file, content)
|
|
|
|
}
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-01-04 01:46:14 +02:00
|
|
|
func Generate() {
|
2022-10-15 18:47:55 +02:00
|
|
|
generateAtDir(GetKeybindingsDir())
|
2022-01-04 01:46:14 +02:00
|
|
|
}
|
|
|
|
|
2019-01-16 19:47:15 +02:00
|
|
|
func writeString(file *os.File, str string) {
|
2019-01-16 19:54:54 +02:00
|
|
|
_, err := file.WriteString(str)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-01-16 19:47:15 +02:00
|
|
|
}
|
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
2020-10-04 02:00:48 +02:00
|
|
|
contextTitleMap := map[string]string{
|
2023-01-21 13:38:14 +02:00
|
|
|
"global": tr.GlobalTitle,
|
|
|
|
"navigation": tr.NavigationTitle,
|
|
|
|
"branches": tr.BranchesTitle,
|
|
|
|
"localBranches": tr.LocalBranchesTitle,
|
|
|
|
"files": tr.FilesTitle,
|
|
|
|
"status": tr.StatusTitle,
|
|
|
|
"submodules": tr.SubmodulesTitle,
|
|
|
|
"subCommits": tr.SubCommitsTitle,
|
|
|
|
"remoteBranches": tr.RemoteBranchesTitle,
|
|
|
|
"remotes": tr.RemotesTitle,
|
|
|
|
"reflogCommits": tr.ReflogCommitsTitle,
|
|
|
|
"tags": tr.TagsTitle,
|
|
|
|
"commitFiles": tr.CommitFilesTitle,
|
2023-07-22 06:05:42 +02:00
|
|
|
"commitMessage": tr.CommitSummaryTitle,
|
2023-01-21 13:38:14 +02:00
|
|
|
"commitDescription": tr.CommitDescriptionTitle,
|
|
|
|
"commits": tr.CommitsTitle,
|
|
|
|
"confirmation": tr.ConfirmationTitle,
|
|
|
|
"information": tr.InformationTitle,
|
|
|
|
"main": tr.NormalTitle,
|
|
|
|
"patchBuilding": tr.PatchBuildingTitle,
|
|
|
|
"mergeConflicts": tr.MergingTitle,
|
|
|
|
"staging": tr.StagingTitle,
|
|
|
|
"menu": tr.MenuTitle,
|
|
|
|
"search": tr.SearchTitle,
|
|
|
|
"secondary": tr.SecondaryTitle,
|
|
|
|
"stash": tr.StashTitle,
|
|
|
|
"suggestions": tr.SuggestionsCheatsheetTitle,
|
|
|
|
"extras": tr.ExtrasTitle,
|
2022-09-01 20:25:41 +02:00
|
|
|
"worktrees": tr.WorktreesTitle,
|
2020-10-04 02:00:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
title, ok := contextTitleMap[str]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("title not found for %s", str))
|
|
|
|
}
|
|
|
|
|
|
|
|
return title
|
2019-01-16 19:47:15 +02:00
|
|
|
}
|
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
|
2022-06-13 03:01:26 +02:00
|
|
|
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
|
2023-07-24 05:06:42 +02:00
|
|
|
bindingsToDisplay := lo.Filter(bindings, func(binding *types.Binding, _ int) bool {
|
2022-06-13 03:01:26 +02:00
|
|
|
if lo.Contains(excludedViews, binding.ViewName) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-12-30 02:34:01 +02:00
|
|
|
return (binding.Description != "" || binding.Alternative != "") && binding.Key != nil
|
2022-03-20 04:59:33 +02:00
|
|
|
})
|
2019-01-16 19:54:54 +02:00
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header {
|
|
|
|
return getHeader(binding, tr)
|
2022-03-20 04:59:33 +02:00
|
|
|
})
|
2020-02-29 00:17:19 +02:00
|
|
|
|
2022-03-26 05:44:30 +02:00
|
|
|
bindingGroups := maps.MapToSlice(
|
|
|
|
bindingsByHeader,
|
|
|
|
func(header header, hBindings []*types.Binding) headerWithBindings {
|
|
|
|
uniqBindings := lo.UniqBy(hBindings, func(binding *types.Binding) string {
|
2022-08-06 10:50:52 +02:00
|
|
|
return binding.Description + keybindings.LabelFromKey(binding.Key)
|
2022-03-26 05:44:30 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
return headerWithBindings{
|
|
|
|
header: header,
|
|
|
|
bindings: uniqBindings,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2020-02-29 00:17:19 +02:00
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
slices.SortFunc(bindingGroups, func(a, b headerWithBindings) bool {
|
|
|
|
if a.header.priority != b.header.priority {
|
|
|
|
return a.header.priority > b.header.priority
|
2020-02-29 00:17:19 +02:00
|
|
|
}
|
2022-03-20 04:59:33 +02:00
|
|
|
return a.header.title < b.header.title
|
|
|
|
})
|
2019-11-10 07:33:31 +02:00
|
|
|
|
2023-07-24 05:06:42 +02:00
|
|
|
return lo.Map(bindingGroups, func(hb headerWithBindings, _ int) *bindingSection {
|
2022-03-20 04:59:33 +02:00
|
|
|
return &bindingSection{
|
|
|
|
title: hb.header.title,
|
|
|
|
bindings: hb.bindings,
|
2019-03-02 06:11:53 +02:00
|
|
|
}
|
2022-03-20 04:59:33 +02:00
|
|
|
})
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
|
2022-03-20 04:59:33 +02:00
|
|
|
if binding.Tag == "navigation" {
|
2022-06-13 03:01:26 +02:00
|
|
|
return header{priority: 2, title: localisedTitle(tr, "navigation")}
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-03-20 04:59:33 +02:00
|
|
|
if binding.ViewName == "" {
|
2022-06-13 03:01:26 +02:00
|
|
|
return header{priority: 3, title: localisedTitle(tr, "global")}
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-06-13 03:01:26 +02:00
|
|
|
return header{priority: 1, title: localisedTitle(tr, binding.ViewName)}
|
2019-03-02 09:45:18 +02:00
|
|
|
}
|
|
|
|
|
2022-01-04 00:20:38 +02:00
|
|
|
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
|
|
|
content := fmt.Sprintf("# Lazygit %s\n", tr.Keybindings)
|
2019-03-02 09:45:18 +02:00
|
|
|
|
2023-05-21 03:13:02 +02:00
|
|
|
content += fmt.Sprintf("\n%s\n", italicize(tr.KeybindingsLegend))
|
|
|
|
|
2019-03-02 09:45:18 +02:00
|
|
|
for _, section := range bindingSections {
|
|
|
|
content += formatTitle(section.title)
|
|
|
|
content += "<pre>\n"
|
|
|
|
for _, binding := range section.bindings {
|
|
|
|
content += formatBinding(binding)
|
|
|
|
}
|
|
|
|
content += "</pre>\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
return content
|
2019-01-16 19:47:15 +02:00
|
|
|
}
|
2022-03-20 04:59:33 +02:00
|
|
|
|
|
|
|
func formatTitle(title string) string {
|
|
|
|
return fmt.Sprintf("\n## %s\n\n", title)
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatBinding(binding *types.Binding) string {
|
2023-05-21 03:13:02 +02:00
|
|
|
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
|
2022-03-20 04:59:33 +02:00
|
|
|
if binding.Alternative != "" {
|
2023-05-21 03:13:02 +02:00
|
|
|
result += fmt.Sprintf(" (%s)", binding.Alternative)
|
2022-03-20 04:59:33 +02:00
|
|
|
}
|
2023-05-21 03:13:02 +02:00
|
|
|
result += "\n"
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func escapeAngleBrackets(str string) string {
|
|
|
|
result := strings.ReplaceAll(str, ">", ">")
|
|
|
|
result = strings.ReplaceAll(result, "<", "<")
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func italicize(str string) string {
|
|
|
|
return fmt.Sprintf("_%s_", str)
|
2022-03-20 04:59:33 +02:00
|
|
|
}
|