init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
package patch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/app"
|
||||
"git.sr.ht/~rjarry/aerc/commands"
|
||||
"git.sr.ht/~rjarry/aerc/commands/msg"
|
||||
"git.sr.ht/~rjarry/aerc/lib/log"
|
||||
"git.sr.ht/~rjarry/aerc/lib/pama"
|
||||
"git.sr.ht/~rjarry/aerc/lib/pama/models"
|
||||
)
|
||||
|
||||
type Apply struct {
|
||||
Cmd string `opt:"-c" desc:"Apply patches with provided command."`
|
||||
Worktree string `opt:"-w" desc:"Create linked worktree on this <commit-ish>."`
|
||||
Tag string `opt:"tag" required:"true" complete:"CompleteTag" desc:"Identify patches with tag."`
|
||||
}
|
||||
|
||||
func init() {
|
||||
register(Apply{})
|
||||
}
|
||||
|
||||
func (Apply) Description() string {
|
||||
return "Apply the selected message(s) to the current project."
|
||||
}
|
||||
|
||||
func (Apply) Context() commands.CommandContext {
|
||||
return commands.MESSAGE_LIST | commands.MESSAGE_VIEWER
|
||||
}
|
||||
|
||||
func (Apply) Aliases() []string {
|
||||
return []string{"apply"}
|
||||
}
|
||||
|
||||
func (*Apply) CompleteTag(arg string) []string {
|
||||
patches, err := pama.New().CurrentPatches()
|
||||
if err != nil {
|
||||
log.Errorf("failed to current patches for completion: %v", err)
|
||||
patches = nil
|
||||
}
|
||||
|
||||
acct := app.SelectedAccount()
|
||||
if acct == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uids, err := acct.MarkedMessages()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(uids) == 0 {
|
||||
msg, err := acct.SelectedMessage()
|
||||
if err == nil {
|
||||
uids = append(uids, msg.Uid)
|
||||
}
|
||||
}
|
||||
|
||||
store := acct.Store()
|
||||
if store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var subjects []string
|
||||
for _, uid := range uids {
|
||||
if msg, ok := store.Messages[uid]; !ok || msg == nil || msg.Envelope == nil {
|
||||
continue
|
||||
} else {
|
||||
subjects = append(subjects, msg.Envelope.Subject)
|
||||
}
|
||||
}
|
||||
return proposePatchName(patches, subjects)
|
||||
}
|
||||
|
||||
func (a Apply) Execute(args []string) error {
|
||||
patch := a.Tag
|
||||
worktree := a.Worktree
|
||||
applyCmd := a.Cmd
|
||||
|
||||
m := pama.New()
|
||||
p, err := m.CurrentProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Tracef("Current project: %v", p)
|
||||
|
||||
if worktree != "" {
|
||||
p, err = m.CreateWorktree(p, worktree, patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.SwitchProject(p.Name)
|
||||
if err != nil {
|
||||
log.Warnf("could not switch to worktree project: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if models.Commits(p.Commits).HasTag(patch) {
|
||||
return fmt.Errorf("Patch name '%s' already exists.", patch)
|
||||
}
|
||||
|
||||
if !m.Clean(p) {
|
||||
return fmt.Errorf("Aborting... There are unstaged changes in " +
|
||||
"your repository.")
|
||||
}
|
||||
|
||||
commit, err := m.Head(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Tracef("HEAD commit before: %s", commit)
|
||||
|
||||
if applyCmd != "" {
|
||||
rootFmt := "%r"
|
||||
if strings.Contains(applyCmd, rootFmt) {
|
||||
applyCmd = strings.ReplaceAll(applyCmd, rootFmt, p.Root)
|
||||
}
|
||||
log.Infof("use custom apply command: %s", applyCmd)
|
||||
} else {
|
||||
applyCmd, err = m.ApplyCmd(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
msgData := collectMessageData()
|
||||
|
||||
// apply patches with the pipe cmd
|
||||
pipe := msg.Pipe{
|
||||
Background: false,
|
||||
Full: true,
|
||||
Part: false,
|
||||
Command: applyCmd,
|
||||
}
|
||||
return pipe.Run(func() {
|
||||
p, err = m.ApplyUpdate(p, patch, commit, msgData)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to save patch data: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// collectMessageData returns a map where the key is the message id and the
|
||||
// value the subject of the marked messages
|
||||
func collectMessageData() map[string]string {
|
||||
acct := app.SelectedAccount()
|
||||
if acct == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uids, err := commands.MarkedOrSelected(acct)
|
||||
if err != nil {
|
||||
log.Errorf("error occurred: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
store := acct.Store()
|
||||
if store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
kv := make(map[string]string)
|
||||
for _, uid := range uids {
|
||||
msginfo, ok := store.Messages[uid]
|
||||
if !ok || msginfo == nil {
|
||||
continue
|
||||
}
|
||||
id, err := msginfo.MsgId()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if msginfo.Envelope == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
kv[id] = msginfo.Envelope.Subject
|
||||
}
|
||||
|
||||
return kv
|
||||
}
|
||||
|
||||
func proposePatchName(patches, subjects []string) []string {
|
||||
parse := func(s string) (string, string, bool) {
|
||||
var tag strings.Builder
|
||||
var version string
|
||||
var i, j int
|
||||
|
||||
i = strings.Index(s, "[")
|
||||
if i < 0 {
|
||||
goto noPatch
|
||||
}
|
||||
s = s[i+1:]
|
||||
|
||||
j = strings.Index(s, "]")
|
||||
if j < 0 {
|
||||
goto noPatch
|
||||
}
|
||||
for _, elem := range strings.Fields(s[:j]) {
|
||||
vers := strings.ToLower(elem)
|
||||
if !strings.HasPrefix(vers, "v") {
|
||||
continue
|
||||
}
|
||||
isVersion := true
|
||||
for _, r := range vers[1:] {
|
||||
if !unicode.IsDigit(r) {
|
||||
isVersion = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isVersion {
|
||||
version = vers
|
||||
break
|
||||
}
|
||||
}
|
||||
s = strings.TrimSpace(s[j+1:])
|
||||
|
||||
for _, r := range s {
|
||||
if unicode.IsSpace(r) || r == ':' {
|
||||
break
|
||||
}
|
||||
_, err := tag.WriteRune(r)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return tag.String(), version, true
|
||||
noPatch:
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
summary := make(map[string]struct{})
|
||||
|
||||
var results []string
|
||||
for _, s := range subjects {
|
||||
tag, version, isPatch := parse(s)
|
||||
if tag == "" || !isPatch {
|
||||
continue
|
||||
}
|
||||
if version == "" {
|
||||
version = "v1"
|
||||
}
|
||||
result := fmt.Sprintf("%s_%s", tag, version)
|
||||
result = strings.ReplaceAll(result, " ", "")
|
||||
|
||||
collision := false
|
||||
for _, name := range patches {
|
||||
if name == result {
|
||||
collision = true
|
||||
}
|
||||
}
|
||||
if collision {
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok := summary[result]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
results = append(results, result)
|
||||
summary[result] = struct{}{}
|
||||
}
|
||||
|
||||
sort.Strings(results)
|
||||
return results
|
||||
}
|
||||
Reference in New Issue
Block a user