1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-04 10:34:55 +02:00
lazygit/pkg/cheatsheet/generate.go

225 lines
6.3 KiB
Go
Raw Normal View History

// 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
2022-01-04 01:46:14 +02:00
package cheatsheet
import (
2019-01-16 19:54:54 +02:00
"fmt"
"log"
"os"
"strings"
2022-03-19 07:34:46 +02:00
"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"
2022-03-20 04:59:33 +02:00
"github.com/samber/lo"
"golang.org/x/exp/slices"
)
type bindingSection struct {
title string
bindings []*types.Binding
}
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"
}
func GetKeybindingsDir() string {
return utils.GetLazyRootDirectory() + "/docs/keybindings"
2022-01-04 01:46:14 +02:00
}
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)
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)
}
2022-03-20 04:59:33 +02:00
bindings := mApp.Gui.GetCheatsheetKeybindings()
bindingSections := getBindingSections(bindings, mApp.Tr)
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)
writeString(file, content)
}
}
2022-01-04 01:46:14 +02:00
func Generate() {
generateAtDir(GetKeybindingsDir())
2022-01-04 01:46:14 +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)
}
}
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{
"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,
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
}
2022-03-20 04:59:33 +02:00
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
}
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
bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header {
return getHeader(binding, tr)
2022-03-20 04:59:33 +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 {
return binding.Description + keybindings.LabelFromKey(binding.Key)
2022-03-26 05:44:30 +02:00
})
return headerWithBindings{
header: header,
bindings: uniqBindings,
}
},
)
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
}
2022-03-20 04:59:33 +02:00
return a.header.title < b.header.title
})
2019-11-10 07:33:31 +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
})
}
func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
2022-03-20 04:59:33 +02:00
if binding.Tag == "navigation" {
return header{priority: 2, title: localisedTitle(tr, "navigation")}
}
2022-03-20 04:59:33 +02:00
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
}
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 {
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 != "" {
result += fmt.Sprintf(" (%s)", binding.Alternative)
2022-03-20 04:59:33 +02:00
}
result += "\n"
return result
}
func escapeAngleBrackets(str string) string {
result := strings.ReplaceAll(str, ">", "&gt;")
result = strings.ReplaceAll(result, "<", "&lt;")
return result
}
func italicize(str string) string {
return fmt.Sprintf("_%s_", str)
2022-03-20 04:59:33 +02:00
}