Skip to content

Commit a613d1d

Browse files
committed
New automerge framework.
Allow the creation and definition of functions to automatically merge updates without manual intervention. Proof of concept with the mirrorlist: the file can be automatically merged.
1 parent 4065110 commit a613d1d

File tree

4 files changed

+177
-9
lines changed

4 files changed

+177
-9
lines changed

pacfiles-automerge.el

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
;;; pacfiles-automerge.el --- Auto-Merging related functions -*- lexical-binding: t; -*-
2+
;;; Commentary:
3+
;; Functions to manage the Auto-Merging functions of pacfiles
4+
;;
5+
;;; Code:
6+
7+
(require 'pacfiles-utils)
8+
(require 'pacfiles-win)
9+
10+
(declare-function pacfiles-revert-buffer-no-confirm "pacfiles-mode" ())
11+
12+
(defvar pacfiles--automerge-alist
13+
'((:regexp "/etc/pacman\\.d/mirrorlist\\.pacnew$"
14+
:mergefn #'pacfiles--automerge-mirrorlist)))
15+
16+
;;;###autoload
17+
(defun pacfiles--automerge-available (update-file)
18+
"Return the function that knows how to merge UPDATE-FILE if it exists.
19+
20+
UPDATE-FILE searches for matches in `pacfiles--automerge-alist'. If a match is
21+
found, return a function, otherwise return nil."
22+
(let ((case-fold-search nil) ; do not ignore case with `string-match-p'
23+
(automerge-alist pacfiles--automerge-alist)
24+
(merge-available nil))
25+
(while (and automerge-alist (not merge-available))
26+
(when (string-match-p (plist-get (car automerge-alist) :regexp) update-file)
27+
(setq merge-available (plist-get (car automerge-alist) :mergefn)))
28+
(setq automerge-alist (cdr automerge-alist)))
29+
merge-available))
30+
31+
;; -----------------------------------------------------------------------
32+
;; --- Functions that auto-merge the files in `pacfiles--automerge-alist'.
33+
34+
;;;###autoload
35+
(defun pacfiles--automerge-mirrorlist (base-file update-file)
36+
"Automatically merge the mirrorlist in BASE-FILE into UPDATE-FILE."
37+
(let ((base-buffer (find-file-noselect base-file))
38+
(update-buffer (find-file-noselect update-file)))
39+
;; Check if reflector was used to generate mirrorlist.
40+
(if (with-current-buffer base-buffer
41+
(goto-char (point-min))
42+
(search-forward "generated by Reflector" nil t))
43+
(message "Automerge not possible. Use reflector to recreate the mirrorlist.")
44+
;; Do the merging of the mirrorlist.
45+
(let ((servers (pacfiles--find-mirrorlist-servers base-buffer)))
46+
(if (not servers)
47+
;; No servers selected, copy the update as is
48+
(progn
49+
(with-current-buffer (pacfiles--create-mirrorlist-merge-buffer
50+
update-file update-buffer)
51+
(save-buffer))
52+
(message "There are no mirrorlist servers to merge. Update copied over."))
53+
;; Servers found, use them in a new file
54+
(pacfiles--merge-mirrorlist-servers servers update-file update-buffer))
55+
(pacfiles-revert-buffer-no-confirm)))))
56+
57+
;;;###autoload
58+
(defun pacfiles--find-mirrorlist-servers (base-buffer)
59+
"Find the activated servers (i.e., those uncommented) in BASE-BUFFER."
60+
(let ((servers '()))
61+
(with-current-buffer base-buffer
62+
(save-excursion
63+
(goto-char (point-min))
64+
(while (re-search-forward "^\\([^#\n].*\\)\n" nil t)
65+
(push (match-string 1) servers))))
66+
(reverse servers)))
67+
68+
;;;###autoload
69+
(defun pacfiles--create-mirrorlist-merge-buffer (mirrorlist-update-file update-buffer)
70+
"Ready up the merge buffer for the mirrorlist.
71+
72+
The MIRRORLIST-UPDATE-FILE is the path of the original mirrorlist. UPDATE-BUFFER
73+
is the buffer with the updated mirrorlist. Return the buffer of the merged file."
74+
(let* ((merge-file
75+
(pacfiles--set-remote-path-maybe
76+
(pacfiles--calculate-merge-file mirrorlist-update-file
77+
pacfiles-merge-file-tmp-location)))
78+
(merge-buffer (find-file-noselect merge-file t)))
79+
(with-current-buffer merge-buffer
80+
(erase-buffer)
81+
(insert-buffer-substring update-buffer)
82+
merge-buffer)))
83+
84+
;;;###autoload
85+
(defun pacfiles--merge-mirrorlist-servers (servers mirrorlist-update-file update-buffer)
86+
"Automatically merge the mirrorlist update in MIRRORLIST-UPDATE-FILE.
87+
88+
Merge the uncommented lines in SERVERS that exist in UPDATE-BUFFER."
89+
(let ((servers-status-alist '()))
90+
;; Search for the status of the old servers in the update mirrorlist.
91+
(with-current-buffer update-buffer
92+
(save-excursion
93+
(while servers
94+
(let ((server (pop servers)))
95+
(goto-char (point-min))
96+
(push `(,server . ,(re-search-forward server nil t)) servers-status-alist)))))
97+
(setq servers-status-alist (reverse servers-status-alist))
98+
;; Merge the servers into a merge file.
99+
(with-current-buffer (pacfiles--create-mirrorlist-merge-buffer
100+
mirrorlist-update-file update-buffer)
101+
(erase-buffer)
102+
(insert-buffer-substring update-buffer)
103+
(goto-char (point-min))
104+
;; Insert the existing servers after the first blank line.
105+
(if (not (seq-some (lambda (s) (cdr s)) servers-status-alist))
106+
(message "No valid servers were found")
107+
(re-search-forward "^[[:blank:]]*$")
108+
(open-line 2)
109+
(forward-line)
110+
(insert "## pacfiles: Automerged servers\n")
111+
(insert
112+
(string-join
113+
(mapcar #'car (seq-filter (lambda (s) (cdr s)) servers-status-alist))
114+
"\n")))
115+
;; Report about non-existing servers after the first blank line.
116+
(when (seq-some (lambda (s) (not (cdr s))) servers-status-alist)
117+
(re-search-forward "^[[:blank:]]*$")
118+
(open-line 2)
119+
(forward-line)
120+
(insert "## pacfiles: Servers no longer available\n")
121+
(insert
122+
(string-join
123+
(mapcar (lambda (s) (concat "#" (car s)))
124+
(seq-remove (lambda (s) (cdr s)) servers-status-alist))
125+
"\n")))
126+
(save-buffer))))
127+
128+
(provide 'pacfiles-automerge)
129+
;;; pacfiles-automerge.el ends here

pacfiles-buttons.el

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
;;; Code:
77

88
(require 'tramp)
9+
(require 'pacfiles-automerge)
910

1011
(defgroup pacfiles-button-faces nil
1112
"Faces of the buttons used in pacfiles-mode."
@@ -16,6 +17,16 @@
1617
"Face for the Apply All button."
1718
:group 'pacfiles-button-faces)
1819

20+
(defface pacfiles--automerge
21+
'((t (:inherit 'button :foreground "#50fa7b" :height 1.1)))
22+
"Face for the AutoMerge button."
23+
:group 'pacfiles-button-faces)
24+
25+
(defface pacfiles--discard-all
26+
'((t (:inherit 'button :height 1.3)))
27+
"Face for the Apply All button."
28+
:group 'pacfiles-button-faces)
29+
1930
(defface pacfiles--discard-all
2031
'((t (:inherit 'button :height 1.3)))
2132
"Face for the Apply All button."
@@ -52,6 +63,10 @@
5263
'face 'pacfiles--delete
5364
'follow-link t)
5465

66+
(define-button-type 'pacfiles--button-automerge
67+
'face 'pacfiles--automerge
68+
'follow-link t)
69+
5570
(define-button-type 'pacfiles--button-generic
5671
'face 'button
5772
'follow-link t)
@@ -68,8 +83,9 @@
6883
(defun pacfiles--insert-merge-button (file-pair)
6984
"Insert a button to merge FILE-PAIR.
7085
71-
To determine the file-pair against which FILE will be merged, the extension of
72-
FILE is removed."
86+
FILE-PAIR corresponds to the path list (base-file merge-file). To determine the
87+
FILE-PAIR against which its `car' will be merged, the extension of its `car'
88+
is removed."
7389
(let* ((update-file (car file-pair))
7490
(base-file (file-name-sans-extension update-file)))
7591
(if (file-exists-p base-file)
@@ -102,6 +118,24 @@ FILE is removed."
102118
'type 'pacfiles--button-generic)
103119
(insert " "))))
104120

121+
(defun pacfiles--insert-automerge-button-maybe (file-pair)
122+
"Insert a button to auto-merge FILE-PAIR if possible.
123+
124+
Remove the extensin of the `car' of FILE-PAIR to choose the merge file."
125+
(let* ((update-file (car file-pair))
126+
(base-file (file-name-sans-extension update-file))
127+
(mergefn (pacfiles--automerge-available update-file)))
128+
(when mergefn
129+
;; Insert button that merges two files.
130+
(insert-text-button "[A]"
131+
'help-echo (format "Auto-merge '%s' into '%s'."
132+
(file-name-nondirectory update-file)
133+
(file-name-nondirectory base-file))
134+
'action `(lambda (_)
135+
(funcall ,mergefn ,base-file ,update-file))
136+
'type 'pacfiles--button-automerge)
137+
(insert " "))))
138+
105139
(defun pacfiles--insert-view-merge-button (file-pair)
106140
"Insert a button that displays the merge in FILE-PAIR."
107141
(let* ((file-update (car file-pair))
@@ -121,7 +155,7 @@ FILE is removed."
121155
(insert " ")))
122156

123157
(defun pacfiles--insert-diff-button (file-update)
124-
"Insert a button that displays a diff of the update FILE-UPDATE and its base file."
158+
"Insert a button that displays a diff of FILE-UPDATE and its base file."
125159
(let ((file-base (file-name-sans-extension file-update)))
126160
(if (file-exists-p file-base)
127161
(progn

pacfiles-mode.el

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
;;; pacfiles-mode.el --- pacnew and pacsave merging tool -*- lexical-binding: t; -*-
1+
;;; pacfiles-mode.el --- The pacnew and pacsave merging tool -*- lexical-binding: t; -*-
22
;;
3-
;; Copyright (C) 2018 Carlos G. Cordero
3+
;; Copyright (C) 2023 Carlos G. Cordero
44
;;
55
;; Author: Carlos G. Cordero <http://github/UndeadKernel>
66
;; Maintainer: Carlos G. Cordero <[email protected]>
77
;; Created: Oct 11, 2018
8-
;; Modified: Sep 15, 2020
9-
;; Version: 1.1
8+
;; Modified: May 03, 2023
9+
;; Version: 1.2
1010
;; Keywords: files pacman arch pacnew pacsave update linux
1111
;; URL: https://github.com/UndeadKernel/pacfiles-mode
12-
;; Package-Requires: ((emacs "26") (cl-lib "0.5"))
12+
;; Package-Requires: ((emacs "26.1"))
1313
;;
1414
;; This file is not part of GNU Emacs.
1515
;;
@@ -29,6 +29,7 @@
2929
(require 'pacfiles-buttons)
3030
(require 'pacfiles-utils)
3131
(require 'pacfiles-win)
32+
(require 'pacfiles-automerge)
3233

3334
(require 'cl-lib)
3435
(require 'ediff)
@@ -69,6 +70,7 @@
6970
(pacfiles-mode)
7071
(pacfiles-revert-buffer t t))))
7172

73+
;;;###autoload
7274
(defun pacfiles-quit ()
7375
"Quit ‘pacfiles-mode’ and restore the previous window and ediff configuration."
7476
(interactive)
@@ -78,6 +80,7 @@
7880
(pacfiles--pop-window-conf))
7981

8082
;; Main function that displays the contents of the PACFILES buffer.
83+
;;;###autoload
8184
(defun pacfiles-revert-buffer (&optional _ignore-auto noconfirm)
8285
"Populate the ‘pacfiles-mode’ buffer with .pacnew and .pacsave files.
8386
@@ -146,6 +149,7 @@ The FILE-TYPE specifies which type of update file we are processing."
146149
(insert (propertize "--- no pending files ---\n" 'font-lock-face 'font-lock-comment-face))
147150
(dolist (file-pair pending-alist)
148151
(pacfiles--insert-merge-button file-pair)
152+
(pacfiles--insert-automerge-button-maybe file-pair)
149153
(pacfiles--insert-diff-button (car file-pair))
150154
(pacfiles--insert-delete-button file-pair)
151155
(insert (car file-pair) " ")

pacfiles-utils.el

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
(require 'tramp)
1010

1111
(defun pacfiles--calculate-merge-file (file path)
12-
"Compute full path of a merge file tied to FILE located in PATH."
12+
"Compute the merge file name associated with FILE and place it under PATH."
1313
(concat path (substring (secure-hash 'md5 file) 0 10) ".pacmerge"))
1414

1515
(defun pacfiles--add-sudo-maybe (file-path permission)
@@ -42,6 +42,7 @@ PERMISSION is either \":read\" or \":write\""
4242
Use the same tramp method used by the user as the remote path."
4343
(if (file-remote-p default-directory)
4444
(with-parsed-tramp-file-name default-directory pf
45+
;; TODO: adapt the calling convention of this next function
4546
(tramp-make-tramp-file-name pf-method pf-user pf-domain pf-host pf-port file-path))
4647
file-path))
4748

0 commit comments

Comments
 (0)