mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-12 11:15:00 +02:00
e33fe37a99
We've been sometimes using lo and sometimes using my slices package, and we need to pick one for consistency. Lo is more extensive and better maintained so we're going with that. My slices package was a superset of go's own slices package so in some places I've just used the official one (the methods were just wrappers anyway). I've also moved the remaining methods into the utils package.
225 lines
6.3 KiB
Go
225 lines
6.3 KiB
Go
// 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:
|
|
// go run scripts/generate_cheatsheet.go
|
|
|
|
package cheatsheet
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/jesseduffield/generics/maps"
|
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
|
"github.com/jesseduffield/lazygit/pkg/app"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
|
"github.com/samber/lo"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
type bindingSection struct {
|
|
title string
|
|
bindings []*types.Binding
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func CommandToRun() string {
|
|
return "go run scripts/cheatsheet/main.go generate"
|
|
}
|
|
|
|
func GetKeybindingsDir() string {
|
|
return utils.GetLazyRootDirectory() + "/docs/keybindings"
|
|
}
|
|
|
|
func generateAtDir(cheatsheetDir string) {
|
|
translationSetsByLang := i18n.GetTranslationSets()
|
|
mConfig := config.NewDummyAppConfig()
|
|
|
|
for lang := range translationSetsByLang {
|
|
mConfig.GetUserConfig().Gui.Language = lang
|
|
common, err := app.NewCommon(mConfig)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
mApp, _ := app.NewApp(mConfig, common)
|
|
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
|
file, err := os.Create(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
bindings := mApp.Gui.GetCheatsheetKeybindings()
|
|
bindingSections := getBindingSections(bindings, mApp.Tr)
|
|
content := formatSections(mApp.Tr, bindingSections)
|
|
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)
|
|
writeString(file, content)
|
|
}
|
|
}
|
|
|
|
func Generate() {
|
|
generateAtDir(GetKeybindingsDir())
|
|
}
|
|
|
|
func writeString(file *os.File, str string) {
|
|
_, err := file.WriteString(str)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
|
contextTitleMap := map[string]string{
|
|
"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,
|
|
"commitMessage": tr.CommitSummaryTitle,
|
|
"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,
|
|
"worktrees": tr.WorktreesTitle,
|
|
}
|
|
|
|
title, ok := contextTitleMap[str]
|
|
if !ok {
|
|
panic(fmt.Sprintf("title not found for %s", str))
|
|
}
|
|
|
|
return title
|
|
}
|
|
|
|
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
|
|
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
|
|
bindingsToDisplay := lo.Filter(bindings, func(binding *types.Binding, _ int) bool {
|
|
if lo.Contains(excludedViews, binding.ViewName) {
|
|
return false
|
|
}
|
|
|
|
return (binding.Description != "" || binding.Alternative != "") && binding.Key != nil
|
|
})
|
|
|
|
bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header {
|
|
return getHeader(binding, tr)
|
|
})
|
|
|
|
bindingGroups := maps.MapToSlice(
|
|
bindingsByHeader,
|
|
func(header header, hBindings []*types.Binding) headerWithBindings {
|
|
uniqBindings := lo.UniqBy(hBindings, func(binding *types.Binding) string {
|
|
return binding.Description + keybindings.LabelFromKey(binding.Key)
|
|
})
|
|
|
|
return headerWithBindings{
|
|
header: header,
|
|
bindings: uniqBindings,
|
|
}
|
|
},
|
|
)
|
|
|
|
slices.SortFunc(bindingGroups, func(a, b headerWithBindings) bool {
|
|
if a.header.priority != b.header.priority {
|
|
return a.header.priority > b.header.priority
|
|
}
|
|
return a.header.title < b.header.title
|
|
})
|
|
|
|
return lo.Map(bindingGroups, func(hb headerWithBindings, _ int) *bindingSection {
|
|
return &bindingSection{
|
|
title: hb.header.title,
|
|
bindings: hb.bindings,
|
|
}
|
|
})
|
|
}
|
|
|
|
func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
|
|
if binding.Tag == "navigation" {
|
|
return header{priority: 2, title: localisedTitle(tr, "navigation")}
|
|
}
|
|
|
|
if binding.ViewName == "" {
|
|
return header{priority: 3, title: localisedTitle(tr, "global")}
|
|
}
|
|
|
|
return header{priority: 1, title: localisedTitle(tr, binding.ViewName)}
|
|
}
|
|
|
|
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
|
content := fmt.Sprintf("# Lazygit %s\n", tr.Keybindings)
|
|
|
|
content += fmt.Sprintf("\n%s\n", italicize(tr.KeybindingsLegend))
|
|
|
|
for _, section := range bindingSections {
|
|
content += formatTitle(section.title)
|
|
content += "<pre>\n"
|
|
for _, binding := range section.bindings {
|
|
content += formatBinding(binding)
|
|
}
|
|
content += "</pre>\n"
|
|
}
|
|
|
|
return content
|
|
}
|
|
|
|
func formatTitle(title string) string {
|
|
return fmt.Sprintf("\n## %s\n\n", title)
|
|
}
|
|
|
|
func formatBinding(binding *types.Binding) string {
|
|
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
|
|
if binding.Alternative != "" {
|
|
result += fmt.Sprintf(" (%s)", binding.Alternative)
|
|
}
|
|
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)
|
|
}
|