Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
11 KiB

  1. ;;; pacfiles-buttons.el --- the buttons of pacfiles-mode --- -*- lexical-binding: t; -*-
  2. ;;; Commentary:
  3. ;; Definitions that deal with buttons and their fonts.
  4. ;;
  5. ;;; Code:
  6. (defgroup pacfiles-button-faces nil
  7. "Faces for the buttons used in pacfiles-mode."
  8. :group 'pacfiles)
  9. (defface pacfiles--apply-all
  10. '((t (:inherit 'button :height 1.3)))
  11. "Face for the Apply All button."
  12. :group 'pacfiles-button-faces)
  13. (defface pacfiles--discard-all
  14. '((t (:inherit 'button :height 1.3)))
  15. "Face for the Apply All button."
  16. :group 'pacfiles-button-faces)
  17. (defface pacfiles--discard
  18. '((t (:inherit 'warning :weight bold :underline t)))
  19. "Face for the Apply All button."
  20. :group 'pacfiles-button-faces)
  21. (defface pacfiles--delete
  22. '((t (:inherit 'error :weight bold :underline t)))
  23. "Face for the Apply All button."
  24. :group 'pacfiles-button-faces)
  25. (define-button-type 'pacfiles--button-apply-all
  26. 'face 'pacfiles--apply-all
  27. 'follow-link t)
  28. (define-button-type 'pacfiles--button-discard-all
  29. 'face 'pacfiles--discard-all
  30. 'follow-link t)
  31. (define-button-type 'pacfiles--button-apply
  32. 'face 'button
  33. 'follow-link t)
  34. (define-button-type 'pacfiles--button-discard
  35. 'face 'pacfiles--discard
  36. 'follow-link t)
  37. (define-button-type 'pacfiles--button-delete
  38. 'face 'pacfiles--delete
  39. 'follow-link t)
  40. (define-button-type 'pacfiles--button-generic
  41. 'face 'button
  42. 'follow-link t)
  43. (defvar pacfiles-activate-no-confirm nil
  44. "Do not ask for user input when applying or discarding a merged file.")
  45. (defvar pacfiles--inhibit-button-revert nil
  46. "Clicking a button does not revert the pacfiles list buffer.")
  47. (defun pacfiles--insert-merge-button (file-pair)
  48. "Insert a button to merge FILE-PAIR.
  49. To determine the file-pair against which FILE will be merged, the extension of
  50. FILE is removed."
  51. (let* ((update-file (car file-pair))
  52. (base-file (file-name-sans-extension update-file)))
  53. (if (file-exists-p base-file)
  54. (progn
  55. ;; Insert button that merges two files.
  56. (insert-text-button "[merge]"
  57. 'help-echo (format "Start merging '%s' and '%s'."
  58. (file-name-nondirectory update-file)
  59. (file-name-nondirectory base-file))
  60. 'action `(lambda (_)
  61. (ediff-merge-files ,update-file ,base-file nil
  62. ;; location of the merged file-pair
  63. ,(cdr file-pair)))
  64. 'type 'pacfiles--button-generic)
  65. (insert " "))
  66. ;; The base file doesn't exist.
  67. ;; Insert button that just copies the update to the merge file.
  68. (insert-text-button "[merge]"
  69. 'help-echo (format "Merge '%s'."
  70. (file-name-nondirectory update-file))
  71. 'action `(lambda (_)
  72. (when (y-or-n-p
  73. (format "Base file '%s' not found. Use '%s' as is? "
  74. ,base-file ,update-file))
  75. (copy-file ,update-file ,(cdr file-pair))
  76. (when (not pacfiles--inhibit-button-revert) (revert-buffer t t))))
  77. 'type 'pacfiles--button-generic)
  78. (insert " "))))
  79. (defun pacfiles--insert-view-merge-button (file-pair)
  80. "Insert a button that displays the merge in FILE-PAIR."
  81. (let* ((file-update (car file-pair))
  82. (file-base (file-name-sans-extension file-update))
  83. (file-merge (cdr file-pair)))
  84. (insert-text-button "[view]"
  85. 'help-echo (format "View the merge of '%s' with '%s'."
  86. (file-name-nondirectory file-update)
  87. (file-name-nondirectory file-base))
  88. 'action `(lambda (_)
  89. (let ((window (split-window-right)))
  90. (select-window window)
  91. (set-window-buffer window
  92. (pacfiles--create-view-buffer
  93. (file-name-nondirectory ,file-base) ,file-merge))))
  94. 'type 'pacfiles--button-generic)
  95. (insert " ")))
  96. (defun pacfiles--insert-diff-button (file-update)
  97. "Insert a button that displays a diff of the update FILE-UPDATE and its base file."
  98. (let ((file-base (file-name-sans-extension file-update)))
  99. (if (file-exists-p file-base)
  100. (progn
  101. (insert-text-button "[diff]"
  102. 'help-echo (format "Diff '%s' with '%s'."
  103. (file-name-nondirectory file-update)
  104. (file-name-nondirectory file-base))
  105. 'action `(lambda (_) (ediff-files ,file-update ,file-base))
  106. 'type 'pacfiles--button-generic)
  107. (insert " "))
  108. ;; Replace the diff button with spaces
  109. (insert " "))))
  110. (defun pacfiles--insert-apply-button (file-pair)
  111. "Insert a button that copies the `cdr' of FILE-PAIR to its `car'."
  112. (let* ((merge-file (cdr file-pair))
  113. (update-file (car file-pair))
  114. (destination-file (file-name-sans-extension update-file)))
  115. (insert-text-button "[apply]"
  116. 'help-echo (format "Apply the merge of '%s' and '%s' to the file system."
  117. (file-name-nondirectory update-file)
  118. (file-name-sans-extension (file-name-nondirectory update-file)))
  119. 'action `(lambda (_)
  120. (when (or pacfiles-activate-no-confirm
  121. (y-or-n-p (format "Apply the merge and overwrite '%s'? "
  122. ,destination-file)))
  123. ;; Copy and keep the destination file's permissions and user/group
  124. (let* ((dst-file (pacfiles--add-sudo-maybe ,destination-file :write))
  125. (dst-attrs (file-attributes dst-file 'integer))
  126. (dst-uid (file-attribute-user-id dst-attrs))
  127. (dst-gid (file-attribute-group-id dst-attrs))
  128. (dst-mode (file-modes dst-file)))
  129. (copy-file ,merge-file dst-file t)
  130. (set-file-modes dst-file dst-mode)
  131. (tramp-set-file-uid-gid dst-file dst-uid dst-gid))
  132. ;; Delete the merge and update files
  133. (delete-file (pacfiles--add-sudo-maybe ,merge-file :write))
  134. (delete-file (pacfiles--add-sudo-maybe ,update-file :write))
  135. (when (not pacfiles--inhibit-button-revert) (revert-buffer t t))
  136. (message "Merge applied!")))
  137. 'type 'pacfiles--button-apply)
  138. (insert " ")))
  139. (defun pacfiles--insert-discard-button (file-pair)
  140. "Insert button that deletes the `cdr' of FILE-PAIR from the file system."
  141. (let ((merge-file (cdr file-pair))
  142. (update-file (car file-pair)))
  143. (insert-text-button "[discard]"
  144. 'help-echo (format "Delete the merge of '%s' from the file system."
  145. (file-name-sans-extension (file-name-nondirectory update-file)))
  146. 'action `(lambda (_)
  147. (let ((del-file (pacfiles--add-sudo-maybe ,merge-file :write)))
  148. (when (or pacfiles-activate-no-confirm
  149. (y-or-n-p (format "Discard the merge between '%s' and '%s'? "
  150. ,update-file
  151. ,(file-name-sans-extension update-file))))
  152. (delete-file del-file)
  153. (message "Merge discarded!")))
  154. (when (not pacfiles--inhibit-button-revert) (revert-buffer t t)))
  155. 'type 'pacfiles--button-discard)
  156. (insert " ")))
  157. (defun pacfiles--insert-delete-button (file-pair)
  158. "Insert a button that deletes the file in the `car' of FILE-PAIR."
  159. (let ((update-file (car file-pair)))
  160. (insert-text-button "[delete]"
  161. 'help-echo (format "Delete '%s' from the file system."
  162. (file-name-nondirectory update-file))
  163. 'action `(lambda (_)
  164. (when (y-or-n-p (format "Delete '%s' permanently? "
  165. ,update-file))
  166. (delete-file (pacfiles--add-sudo-maybe ,update-file :write))
  167. (message "File deleted!"))
  168. (when (not pacfiles--inhibit-button-revert) (revert-buffer t t)))
  169. 'type 'pacfiles--button-delete)
  170. (insert " ")))
  171. (defun pacfiles--insert-footer-buttons ()
  172. "Insert the `apply all' and `discard all' buttons."
  173. (insert-text-button "[Apply All]"
  174. 'help-echo "Write all merged files into the system."
  175. 'action (lambda (_)
  176. (pacfiles--activate-all-buttons 'pacfiles--button-apply "apply"))
  177. 'type 'pacfiles--button-apply-all)
  178. (insert " ")
  179. (insert-text-button "[Discard All]"
  180. 'help-echo "Discard all merged files."
  181. 'action (lambda (_)
  182. (pacfiles--activate-all-buttons 'pacfiles--button-discard "discard"))
  183. 'type 'pacfiles--button-discard-all))
  184. (defun pacfiles--activate-all-buttons (activate-type action-name)
  185. "Find all buttons with button type ACTIVATE-TYPE and activate them.
  186. Use ACTION-NAME to display an appropriate warning message."
  187. (when (y-or-n-p (concat (capitalize action-name) " all merged files? "))
  188. (save-excursion
  189. (goto-char (point-min))
  190. ;; Catch errors that `forward-button' might throw.
  191. (condition-case nil
  192. (let* ((pacfiles-activate-no-confirm t) ; do not ask the user
  193. (pacfiles--inhibit-button-revert t)
  194. (button (forward-button 1 nil nil))
  195. (type (button-type button)))
  196. ;; Iterate until we find the first footer button.
  197. (while (not (eq type 'pacfiles--button-apply-all))
  198. (when (eq type activate-type)
  199. (button-activate button))
  200. (setq button (forward-button 1 nil nil)
  201. type (button-type button))))))
  202. (message "Done!")
  203. (revert-buffer t t)))
  204. (provide 'pacfiles-buttons)
  205. ;;; pacfiles-buttons.el ends here