Repository: jesseduffield/lazygit
File: pkg/gui/controllers/helpers/refs_helper.go (Go)

Code before:
			LabelColumns:   []string{self.c.Tr.Actions.CheckoutBranch},
			OnPress:        func() error { return nil },
			DisabledReason: &types.DisabledReason{Text: self.c.Tr.NoBranchesFoundAtCommitTooltip},
			Key:            '1',
		})
	}

	return self.c.Menu(types.CreateMenuOptions{
		Title: self.c.Tr.Actions.CheckoutBranchOrCommit,
		Items: menuItems,
	})
}

func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggestedBranchName string) error {
	message := utils.ResolvePlaceholderString(
		self.c.Tr.NewBranchNameBranchOff,
		map[string]string{
			"branchName": fromFormattedName,
		},
	)

	if suggestedBranchName == "" {
		var err error

		suggestedBranchName, err = utils.ResolveTemplate(self.c.UserConfig().Git.BranchPrefix, nil, template.FuncMap{
			"runCommand": func(command string) (string, error) {
				output, err := self.c.Git().Custom.RunWithOutput(command)
				if err != nil {
					return "", err
				}

				output = strings.TrimRight(output, "\r\n")

				if strings.ContainsAny(output, "\r\n\t ") {
					return "", fmt.Errorf("command output contains whitespace characters: %s", output)
				}

				return output, nil
			},
		})
		if err != nil {
			return err
		}
	}

	refresh := func() error {
		if self.c.Context().Current() != self.c.Contexts().Branches {
			self.c.Context().Push(self.c.Contexts().Branches)
		}

		self.c.Contexts().LocalCommits.SetSelection(0)

Review comment (suggestion):
I think it would be good to share this code between here and handler_creator.go. You'd think that `pkg/utils/template.go` would be a good place to put it, but that doesn't work because you'd have to pass in the `git_commands.CustomCommands`, which creates a circular dependency.

So the next best place to put it is in `pkg/commands/git_commands/custom.go`, maybe as `TemplateFunctionRunCommand`. This makes the "runCommand" entry of the two template.FuncMaps one-liners, which is nice.

The only problem is that the two ContainsAny calls are slightly different, see below.

EDIT: I had to try locally if it all works out the way I think, so I actually made the change already; so why not just push it. 😄  See b5da6c49c7f.

Code after:
	})
}

func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggestedBranchName string) error {
	message := utils.ResolvePlaceholderString(
		self.c.Tr.NewBranchNameBranchOff,
		map[string]string{
			"branchName": fromFormattedName,
		},
	)

	if suggestedBranchName == "" {
		var err error

		suggestedBranchName, err = utils.ResolveTemplate(self.c.UserConfig().Git.BranchPrefix, nil, template.FuncMap{
			"runCommand": self.c.Git().Custom.TemplateFunctionRunCommand,
		})
		if err != nil {
			return err
		}
		suggestedBranchName = strings.ReplaceAll(suggestedBranchName, "\t", " ")
	}

	refresh := func() error {
		if self.c.Context().Current() != self.c.Contexts().Branches {
			self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
		}

		self.c.Contexts().LocalCommits.SetSelection(0)
		self.c.Contexts().Branches.SetSelection(0)

		return self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI, KeepBranchSelectionIndex: true})
	}

	self.c.Prompt(types.PromptOpts{
		Title:          message,
		InitialContent: suggestedBranchName,
		HandleConfirm: func(response string) error {
			self.c.LogAction(self.c.Tr.Actions.CreateBranch)
			newBranchName := SanitizedBranchName(response)
			newBranchFunc := self.c.Git().Branch.New
			if newBranchName != suggestedBranchName {
				newBranchFunc = self.c.Git().Branch.NewWithoutTracking
			}
			if err := newBranchFunc(newBranchName, from); err != nil {
				if IsSwitchBranchUncommittedChangesError(err) {
					// offer to autostash changes
					self.c.Confirm(types.ConfirmOpts{
						Title:  self.c.Tr.AutoStashTitle,
						Prompt: self.c.Tr.AutoStashPrompt,
						HandleConfirm: func() error {