PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/monky.el

http://github.com/ananthakumaran/monky
Emacs Lisp | 3276 lines | 2749 code | 432 blank | 95 comment | 54 complexity | 863a30ac743630b265f81a214c3a9723 MD5 | raw file
Possible License(s): GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. ;;; monky.el --- Control Hg from Emacs. -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2011 Anantha Kumaran.
  3. ;; Author: Anantha kumaran <ananthakumaran@gmail.com>
  4. ;; URL: http://github.com/ananthakumaran/monky
  5. ;; Version: 0.2
  6. ;; Keywords: tools
  7. ;; Monky is free software: you can redistribute it and/or modify it
  8. ;; under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; Monky is distributed in the hope that it will be useful, but
  12. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;; General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;;; Code:
  19. (require 'cl)
  20. (require 'cl-lib)
  21. (require 'bindat)
  22. (require 'ediff)
  23. (require 'subr-x)
  24. (require 'view)
  25. (require 'tramp)
  26. (defgroup monky nil
  27. "Controlling Hg from Emacs."
  28. :prefix "monky-"
  29. :group 'tools)
  30. (defcustom monky-hg-executable "hg"
  31. "The name of the Hg executable."
  32. :group 'monky
  33. :type 'string)
  34. (defcustom monky-hg-standard-options '("--config" "diff.git=Off" "--config" "ui.merge=:merge")
  35. "Standard options when running Hg."
  36. :group 'monky
  37. :type '(repeat string))
  38. (defcustom monky-hg-process-environment '("TERM=dumb" "HGPLAIN=" "LANGUAGE=C")
  39. "Default environment variables for hg."
  40. :group 'monky
  41. :type '(repeat string))
  42. ;; TODO
  43. (defcustom monky-save-some-buffers t
  44. "Non-nil means that \\[monky-status] will save modified buffers before running.
  45. Setting this to t will ask which buffers to save, setting it to 'dontask will
  46. save all modified buffers without asking."
  47. :group 'monky
  48. :type '(choice (const :tag "Never" nil)
  49. (const :tag "Ask" t)
  50. (const :tag "Save without asking" dontask)))
  51. (defcustom monky-revert-item-confirm t
  52. "Require acknowledgment before reverting an item."
  53. :group 'monky
  54. :type 'boolean)
  55. (defcustom monky-log-edit-confirm-cancellation nil
  56. "Require acknowledgment before canceling the log edit buffer."
  57. :group 'monky
  58. :type 'boolean)
  59. (defcustom monky-process-popup-time -1
  60. "Popup the process buffer if a command takes longer than this many seconds."
  61. :group 'monky
  62. :type '(choice (const :tag "Never" -1)
  63. (const :tag "Immediately" 0)
  64. (integer :tag "After this many seconds")))
  65. (defcustom monky-log-cutoff-length 100
  66. "The maximum number of commits to show in the log buffer."
  67. :group 'monky
  68. :type 'integer)
  69. (defcustom monky-log-infinite-length 99999
  70. "Number of log used to show as maximum for `monky-log-cutoff-length'."
  71. :group 'monky
  72. :type 'integer)
  73. (defcustom monky-log-auto-more t
  74. "Insert more log entries automatically when moving past the last entry.
  75. Only considered when moving past the last entry with `monky-goto-next-section'."
  76. :group 'monky
  77. :type 'boolean)
  78. (defcustom monky-incoming-repository "default"
  79. "The repository from which changes are pulled from by default."
  80. :group 'monky
  81. :type 'string)
  82. (defcustom monky-outgoing-repository ""
  83. "The repository to which changes are pushed to by default."
  84. :group 'monky
  85. :type 'string)
  86. (defcustom monky-process-type nil
  87. "How monky spawns Mercurial processes.
  88. Monky can either spawn a new Mercurial process for each request or
  89. use Mercurial's command server feature to run several commands in a
  90. single process instances. While the former is more robust, the latter
  91. is usually faster if Monky runs several commands."
  92. :group 'monky
  93. :type '(choice (const :tag "Single processes" :value nil)
  94. (const :tag "Use command server" :value cmdserver)))
  95. (defcustom monky-pull-args ()
  96. "Extra args to pass to pull."
  97. :group 'monky
  98. :type '(repeat string))
  99. (defcustom monky-repository-paths nil
  100. "*Paths where to find repositories. For each repository an alias is defined, which can then be passed to `monky-open-repository` to open the repository.
  101. Lisp-type of this option: The value must be a list L whereas each
  102. element of L is a 2-element list: The first element is the full
  103. path of a directory \(string) and the second element is an
  104. arbitrary alias \(string) for this directory which is then
  105. displayed instead of the underlying directory."
  106. :group 'monky
  107. :initialize 'custom-initialize-default
  108. :set (function (lambda (symbol value)
  109. (set symbol value)
  110. (if (and (boundp 'ecb-minor-mode)
  111. ecb-minor-mode
  112. (functionp 'ecb-update-directories-buffer))
  113. (ecb-update-directories-buffer))))
  114. :type '(repeat (cons :tag "Path with alias"
  115. (string :tag "Alias")
  116. (directory :tag "Path"))))
  117. (defun monky-root-dir-descr (dir)
  118. "Return the name of dir if it matches a path in monky-repository-paths, otherwise return nil"
  119. (catch 'exit
  120. (dolist (root-dir monky-repository-paths)
  121. (let ((base-dir
  122. (concat
  123. (replace-regexp-in-string
  124. "/$" ""
  125. (replace-regexp-in-string
  126. "^\~" (getenv "HOME")
  127. (cdr root-dir)))
  128. "/")))
  129. (when (equal base-dir dir)
  130. (throw 'exit (cons (car root-dir)
  131. base-dir)))))))
  132. (defun monky-open-repository ()
  133. "Prompt for a repository path or alias, then display the status
  134. buffer. Aliases are set in monky-repository-paths."
  135. (interactive)
  136. (let* ((rootdir (condition-case nil
  137. (monky-get-root-dir)
  138. (error nil)))
  139. (default-repo (or (monky-root-dir-descr rootdir) rootdir))
  140. (msg (if default-repo
  141. (concat "repository (default " (car default-repo) "): ")
  142. "repository: "))
  143. (repo-name (completing-read msg (mapcar 'car monky-repository-paths)))
  144. (repo (or (assoc repo-name monky-repository-paths) default-repo)))
  145. (when repo (monky-status (cdr repo)))))
  146. (defgroup monky-faces nil
  147. "Customize the appearance of Monky"
  148. :prefix "monky-"
  149. :group 'faces
  150. :group 'monky)
  151. (defface monky-header
  152. '((t :weight bold))
  153. "Face for generic header lines.
  154. Many Monky faces inherit from this one by default."
  155. :group 'monky-faces)
  156. (defface monky-section-title
  157. '((((class color) (background light)) :foreground "DarkGoldenrod4" :inherit monky-header)
  158. (((class color) (background dark)) :foreground "LightGoldenrod2" :inherit monky-header))
  159. "Face for section titles."
  160. :group 'monky-faces)
  161. (defface monky-branch
  162. '((t :weight bold :inherit monky-header))
  163. "Face for the current branch."
  164. :group 'monky-faces)
  165. (defface monky-diff-title
  166. '((t :inherit (monky-header)))
  167. "Face for diff title lines."
  168. :group 'monky-faces)
  169. (defface monky-diff-hunk-header
  170. '((((class color) (background light))
  171. :background "grey80"
  172. :foreground "grey30")
  173. (((class color) (background dark))
  174. :background "grey25"
  175. :foreground "grey70"))
  176. "Face for diff hunk header lines."
  177. :group 'monky-faces)
  178. (defface monky-diff-add
  179. '((((class color) (background light))
  180. :background "#cceecc"
  181. :foreground "#22aa22")
  182. (((class color) (background dark))
  183. :background "#336633"
  184. :foreground "#cceecc"))
  185. "Face for lines in a diff that have been added."
  186. :group 'monky-faces)
  187. (defface monky-diff-none
  188. '((t))
  189. "Face for lines in a diff that are unchanged."
  190. :group 'monky-faces)
  191. (defface monky-diff-del
  192. '((((class color) (background light))
  193. :background "#eecccc"
  194. :foreground "#aa2222")
  195. (((class color) (background dark))
  196. :background "#663333"
  197. :foreground "#eecccc"))
  198. "Face for lines in a diff that have been deleted."
  199. :group 'monky-faces)
  200. (defface monky-commit-id
  201. '((((class color) (background light))
  202. :foreground "firebrick")
  203. (((class color) (background dark))
  204. :foreground "tomato"))
  205. "Face for commit IDs: SHA1 codes and commit numbers."
  206. :group 'monky-faces)
  207. (defface monky-log-sha1
  208. '((t :inherit monky-commit-id))
  209. "Face for the sha1 element of the log output."
  210. :group 'monky-faces)
  211. (defface monky-log-message
  212. '((t))
  213. "Face for the message element of the log output."
  214. :group 'monky-faces)
  215. (defface monky-log-author
  216. '((((class color) (background light))
  217. :foreground "navy")
  218. (((class color) (background dark))
  219. :foreground "cornflower blue"))
  220. "Face for author shown in log buffer."
  221. :group 'monky-faces)
  222. (defface monky-log-head-label-local
  223. '((((class color) (background light))
  224. :box t
  225. :background "Grey85"
  226. :foreground "LightSkyBlue4")
  227. (((class color) (background dark))
  228. :box t
  229. :background "Grey13"
  230. :foreground "LightSkyBlue1"))
  231. "Face for local branch head labels shown in log buffer."
  232. :group 'monky-faces)
  233. (defface monky-log-head-label-tags
  234. '((((class color) (background light))
  235. :box t
  236. :background "LemonChiffon1"
  237. :foreground "goldenrod4")
  238. (((class color) (background dark))
  239. :box t
  240. :background "LemonChiffon1"
  241. :foreground "goldenrod4"))
  242. "Face for tag labels shown in log buffer."
  243. :group 'monky-faces)
  244. (defface monky-queue-patch
  245. '((t :weight bold :inherit (monky-header highlight)))
  246. "Face for patch name"
  247. :group 'monky-faces)
  248. (defface monky-log-head-label-bookmarks
  249. '((((class color) (background light))
  250. :box t
  251. :background "IndianRed1"
  252. :foreground "IndianRed4")
  253. (((class color) (background dark))
  254. :box t
  255. :background "IndianRed1"
  256. :foreground "IndianRed4"))
  257. "Face for bookmark labels shown in log buffer."
  258. :group 'monky-faces)
  259. (defface monky-log-head-label-phase
  260. '((((class color) (background light))
  261. :box t
  262. :background "light green"
  263. :foreground "dark olive green")
  264. (((class color) (background dark))
  265. :box t
  266. :background "light green"
  267. :foreground "dark olive green"))
  268. "Face for phase label shown in log buffer."
  269. :group 'monky-faces)
  270. (defface monky-log-date
  271. '((t :weight bold :inherit monky-header))
  272. "Face for date in log."
  273. :group 'monky-faces)
  274. (defface monky-queue-active
  275. '((((class color) (background light))
  276. :box t
  277. :background "light green"
  278. :foreground "dark olive green")
  279. (((class color) (background dark))
  280. :box t
  281. :background "light green"
  282. :foreground "dark olive green"))
  283. "Face for active patch queue"
  284. :group 'monky-faces)
  285. (defface monky-queue-positive-guard
  286. '((((class color) (background light))
  287. :box t
  288. :background "light green"
  289. :foreground "dark olive green")
  290. (((class color) (background dark))
  291. :box t
  292. :background "light green"
  293. :foreground "dark olive green"))
  294. "Face for queue postive guards"
  295. :group 'monky-faces)
  296. (defface monky-queue-negative-guard
  297. '((((class color) (background light))
  298. :box t
  299. :background "IndianRed1"
  300. :foreground "IndianRed4")
  301. (((class color) (background dark))
  302. :box t
  303. :background "IndianRed1"
  304. :foreground "IndianRed4"))
  305. "Face for queue negative guards"
  306. :group 'monky-faces)
  307. (defvar monky-mode-hook nil
  308. "Hook run by `monky-mode'.")
  309. ;;; User facing configuration
  310. (put 'monky-mode 'mode-class 'special)
  311. ;;; Compatibilities
  312. (eval-when-compile
  313. (when (< emacs-major-version 23)
  314. (defvar line-move-visual nil)))
  315. ;;; Utilities
  316. (defmacro monky-with-process-environment (&rest body)
  317. (declare (indent 0)
  318. (debug (body)))
  319. `(let ((process-environment (append monky-hg-process-environment
  320. process-environment)))
  321. ,@body))
  322. (defmacro monky-with-refresh (&rest body)
  323. "Refresh monky buffers after evaluating BODY.
  324. It is safe to call the functions which uses this macro inside of
  325. this macro. As it is time consuming to refresh monky buffers,
  326. this macro enforces refresh to occur exactly once by pending
  327. refreshes inside of this macro. Nested calls of this
  328. macro (possibly via functions) does not refresh buffers multiple
  329. times. Instead, only the outside-most call of this macro
  330. refreshes buffers."
  331. (declare (indent 0)
  332. (debug (body)))
  333. `(monky-refresh-wrapper (lambda () ,@body)))
  334. (defun monky-completing-read (&rest args)
  335. (apply (if (null ido-mode)
  336. 'completing-read
  337. 'ido-completing-read)
  338. args))
  339. (defun monky-start-process (&rest args)
  340. (monky-with-process-environment
  341. (apply (if (functionp 'start-file-process)
  342. 'start-file-process
  343. 'start-process) args)))
  344. (defun monky-process-file-single (&rest args)
  345. (monky-with-process-environment
  346. (apply 'process-file args)))
  347. ;; Command server
  348. (defvar monky-process nil)
  349. (defvar monky-process-buffer-name "*monky-process*")
  350. (defvar monky-process-client-buffer nil)
  351. (defvar monky-cmd-process nil)
  352. (defvar monky-cmd-process-buffer-name "*monky-cmd-process*")
  353. (defvar monky-cmd-process-input-buffer nil)
  354. (defvar monky-cmd-process-input-point nil)
  355. (defvar monky-cmd-error-message nil)
  356. (defvar monky-cmd-hello-message nil
  357. "Variable to store parsed hello message.")
  358. ;; TODO: does this need to be permanent? If it's only used in monky buffers (not source file buffers), it shouldn't be.
  359. (defvar-local monky-root-dir nil)
  360. (put 'monky-root-dir 'permanent-local t)
  361. (defun monky-cmdserver-sentinel (proc _change)
  362. (unless (memq (process-status proc) '(run stop))
  363. (delete-process proc)))
  364. (defun monky-cmdserver-read-data (size)
  365. (with-current-buffer (process-buffer monky-cmd-process)
  366. (while (< (point-max) size)
  367. (accept-process-output monky-cmd-process 0.1 nil t))
  368. (let ((str (buffer-substring (point-min) (+ (point-min) size))))
  369. (delete-region (point-min) (+ (point-min) size))
  370. (goto-char (point-min))
  371. (vconcat str))))
  372. (defun monky-cmdserver-read ()
  373. "Read one channel and return cons (CHANNEL . RAW-DATA)."
  374. (let* ((data (bindat-unpack '((channel byte) (len u32))
  375. (monky-cmdserver-read-data 5)))
  376. (channel (bindat-get-field data 'channel))
  377. (len (bindat-get-field data 'len)))
  378. (cons channel (monky-cmdserver-read-data len))))
  379. (defun monky-cmdserver-unpack-int (data)
  380. (bindat-get-field (bindat-unpack '((field u32)) data) 'field))
  381. (defun monky-cmdserver-unpack-string (data)
  382. (bindat-get-field (bindat-unpack `((field str ,(length data))) data) 'field))
  383. (defun monky-cmdserver-write (data)
  384. (process-send-string monky-cmd-process
  385. (concat (bindat-pack '((len u32))
  386. `((len . ,(length data))))
  387. data)))
  388. (defun monky-cmdserver-start ()
  389. (unless monky-root-dir
  390. (let (monky-process monky-process-type)
  391. (setq monky-root-dir (monky-get-root-dir))))
  392. (let ((dir monky-root-dir)
  393. (buf (get-buffer-create monky-cmd-process-buffer-name))
  394. (default-directory monky-root-dir)
  395. (process-connection-type nil))
  396. (with-current-buffer buf
  397. (setq buffer-read-only nil)
  398. (setq buffer-file-coding-system 'no-conversion)
  399. (set-buffer-multibyte nil)
  400. (erase-buffer)
  401. (setq view-exit-action
  402. #'(lambda (buffer)
  403. (with-current-buffer buffer
  404. (bury-buffer))))
  405. (setq default-directory dir)
  406. (let ((monky-cmd-process (monky-start-process
  407. "monky-hg" buf "sh" "-c"
  408. (format "%s --config extensions.mq= serve --cmdserver pipe 2> /dev/null" monky-hg-executable))))
  409. (set-process-coding-system monky-cmd-process 'no-conversion 'no-conversion)
  410. (set-process-sentinel monky-cmd-process #'monky-cmdserver-sentinel)
  411. (setq monky-cmd-hello-message
  412. (monky-cmdserver-parse-hello (monky-cmdserver-read)))
  413. monky-cmd-process))))
  414. (defun monky-cmdserver-parse-hello (hello-message)
  415. "Parse hello message to get encoding information."
  416. (let ((channel (car hello-message))
  417. (text (cdr hello-message)))
  418. (if (eq channel ?o)
  419. (progn
  420. (mapcar
  421. (lambda (s)
  422. (string-match "^\\([a-z0-9]+\\) *: *\\(.*\\)$" s)
  423. (let ((field-name (match-string 1 s))
  424. (field-data (match-string 2 s)))
  425. (cons (intern field-name) field-data)))
  426. (split-string (monky-cmdserver-unpack-string text) "\n")))
  427. (error "unknown channel %c for hello message" channel))))
  428. (defun monky-cmdserver-get-encoding (&optional default)
  429. "Get encoding stored in `monky-cmd-hello-message'."
  430. (let ((e (assoc 'encoding monky-cmd-hello-message)))
  431. (if e
  432. (cond
  433. ((string-equal (downcase (cdr e)) "ascii")
  434. 'us-ascii)
  435. (t
  436. (intern (downcase (cdr e)))))
  437. default)))
  438. (defun monky-cmdserver-runcommand (&rest cmd-and-args)
  439. (setq monky-cmd-error-message nil)
  440. (with-current-buffer (process-buffer monky-cmd-process)
  441. (setq buffer-read-only nil)
  442. (erase-buffer))
  443. (process-send-string monky-cmd-process "runcommand\n")
  444. (monky-cmdserver-write (mapconcat #'identity cmd-and-args "\0"))
  445. (let* ((inhibit-read-only t)
  446. (start (point))
  447. (result
  448. (catch 'finished
  449. (while t
  450. (let* ((result (monky-cmdserver-read))
  451. (channel (car result))
  452. (text (cdr result)))
  453. (cond
  454. ((eq channel ?o)
  455. (insert (monky-cmdserver-unpack-string text)))
  456. ((eq channel ?r)
  457. (throw 'finished
  458. (monky-cmdserver-unpack-int text)))
  459. ((eq channel ?e)
  460. (setq monky-cmd-error-message
  461. (concat monky-cmd-error-message text)))
  462. ((memq channel '(?I ?L))
  463. (with-current-buffer monky-cmd-process-input-buffer
  464. (let* ((max (if (eq channel ?I)
  465. (point-max)
  466. (save-excursion
  467. (goto-char monky-cmd-process-input-point)
  468. (line-beginning-position 2))))
  469. (maxreq (monky-cmdserver-unpack-int text))
  470. (len (min (- max monky-cmd-process-input-point)
  471. maxreq))
  472. (end (+ monky-cmd-process-input-point len)))
  473. (monky-cmdserver-write
  474. (buffer-substring monky-cmd-process-input-point end))
  475. (setq monky-cmd-process-input-point end))))
  476. (t
  477. (setq monky-cmd-error-message
  478. (format "Unsupported channel: %c" channel)))))))))
  479. (decode-coding-region start (point)
  480. (monky-cmdserver-get-encoding 'utf-8))
  481. result))
  482. (defun monky-cmdserver-process-file (program infile buffer display &rest args)
  483. "Same as `process-file' but uses the currently active hg command-server."
  484. (if (or infile display)
  485. (apply #'monky-process-file-single program infile buffer display args)
  486. (let ((stdout (if (consp buffer) (car buffer) buffer))
  487. (stderr (and (consp buffer) (cadr buffer))))
  488. (if (eq stdout t) (setq stdout (current-buffer)))
  489. (if (eq stderr t) (setq stderr stdout))
  490. (let ((result
  491. (if stdout
  492. (with-current-buffer stdout
  493. (apply #'monky-cmdserver-runcommand args))
  494. (with-temp-buffer
  495. (apply #'monky-cmdserver-runcommand args)))))
  496. (cond
  497. ((bufferp stderr)
  498. (when monky-cmd-error-message
  499. (with-current-buffer stderr
  500. (insert monky-cmd-error-message))))
  501. ((stringp stderr)
  502. (with-temp-file stderr
  503. (when monky-cmd-error-message
  504. (insert monky-cmd-error-message)))))
  505. result))))
  506. (defun monky-process-file (&rest args)
  507. "Same as `process-file' in the current hg environment.
  508. This function either calls `monky-cmdserver-process-file' or
  509. `monky-process-file-single' depending on whether the hg
  510. command-server should be used."
  511. (apply (cond
  512. (monky-cmd-process #'monky-cmdserver-process-file)
  513. ;; ((eq monky-process-type 'cmdserver)
  514. ;; (error "No process started (forget `monky-with-process`?)"))
  515. (t #'monky-process-file-single))
  516. args))
  517. (defmacro monky-with-process (&rest body)
  518. (declare (indent 0)
  519. (debug (body)))
  520. `(let ((outer (not monky-cmd-process)))
  521. (when (and outer (eq monky-process-type 'cmdserver))
  522. (setq monky-cmd-process (monky-cmdserver-start)))
  523. (unwind-protect
  524. (progn ,@body)
  525. (when (and monky-cmd-process outer (eq monky-process-type 'cmdserver))
  526. (delete-process monky-cmd-process)
  527. (setq monky-cmd-process nil)))))
  528. (defvar monky-bug-report-url "http://github.com/ananthakumaran/monky/issues")
  529. (defun monky-bug-report (str)
  530. (message "Unknown error: %s\nPlease file a bug at %s"
  531. str monky-bug-report-url))
  532. (defun monky-string-starts-with-p (string prefix)
  533. (eq (compare-strings string nil (length prefix) prefix nil nil) t))
  534. (defun monky-trim-line (str)
  535. (if (string= str "")
  536. nil
  537. (if (equal (elt str (- (length str) 1)) ?\n)
  538. (substring str 0 (- (length str) 1))
  539. str)))
  540. (defun monky-delete-line (&optional end)
  541. "Delete the text in current line.
  542. If END is non-nil, deletes the text including the newline character"
  543. (let ((end-point (if end
  544. (1+ (point-at-eol))
  545. (point-at-eol))))
  546. (delete-region (point-at-bol) end-point)))
  547. (defun monky-split-lines (str)
  548. (if (string= str "")
  549. nil
  550. (let ((lines (nreverse (split-string str "\n"))))
  551. (if (string= (car lines) "")
  552. (setq lines (cdr lines)))
  553. (nreverse lines))))
  554. (defun monky-put-line-property (prop val)
  555. (put-text-property (line-beginning-position) (line-beginning-position 2)
  556. prop val))
  557. (defun monky-parse-args (command)
  558. (require 'pcomplete)
  559. (car (with-temp-buffer
  560. (insert command)
  561. (pcomplete-parse-buffer-arguments))))
  562. (defun monky-prefix-p (prefix list)
  563. "Return non-nil if PREFIX is a prefix of LIST.
  564. PREFIX and LIST should both be lists.
  565. If the car of PREFIX is the symbol '*, then return non-nil if the cdr of PREFIX
  566. is a sublist of LIST (as if '* matched zero or more arbitrary elements of LIST)"
  567. (or (null prefix)
  568. (if (eq (car prefix) '*)
  569. (or (monky-prefix-p (cdr prefix) list)
  570. (and (not (null list))
  571. (monky-prefix-p prefix (cdr list))))
  572. (and (not (null list))
  573. (equal (car prefix) (car list))
  574. (monky-prefix-p (cdr prefix) (cdr list))))))
  575. (defun monky-wash-sequence (func)
  576. "Run FUNC until end of buffer is reached.
  577. FUNC should leave point at the end of the modified region"
  578. (while (and (not (eobp))
  579. (funcall func))))
  580. (defun monky-goto-line (line)
  581. "Like `goto-line' but doesn't set the mark."
  582. (save-restriction
  583. (widen)
  584. (goto-char 1)
  585. (forward-line (1- line))))
  586. ;;; Key bindings
  587. (defvar monky-mode-map
  588. (let ((map (make-keymap)))
  589. (suppress-keymap map t)
  590. (define-key map (kbd "n") 'monky-goto-next-section)
  591. (define-key map (kbd "p") 'monky-goto-previous-section)
  592. (define-key map (kbd "RET") 'monky-visit-item)
  593. (define-key map (kbd "TAB") 'monky-toggle-section)
  594. (define-key map (kbd "SPC") 'monky-show-item-or-scroll-up)
  595. (define-key map (kbd "DEL") 'monky-show-item-or-scroll-down)
  596. (define-key map (kbd "g") 'monky-refresh)
  597. (define-key map (kbd "$") 'monky-display-process)
  598. (define-key map (kbd ":") 'monky-hg-command)
  599. (define-key map (kbd "l l") 'monky-log-current-branch)
  600. (define-key map (kbd "l a") 'monky-log-all)
  601. (define-key map (kbd "l r") 'monky-log-revset)
  602. (define-key map (kbd "b") 'monky-branches)
  603. (define-key map (kbd "Q") 'monky-queue)
  604. (define-key map (kbd "q") 'monky-quit-window)
  605. (define-key map (kbd "M-1") 'monky-section-show-level-1-all)
  606. (define-key map (kbd "M-2") 'monky-section-show-level-2-all)
  607. (define-key map (kbd "M-3") 'monky-section-show-level-3-all)
  608. (define-key map (kbd "M-4") 'monky-section-show-level-4-all)
  609. map))
  610. (defvar monky-status-mode-map
  611. (let ((map (make-keymap)))
  612. (define-key map (kbd "s") 'monky-stage-item)
  613. (define-key map (kbd "S") 'monky-stage-all)
  614. (define-key map (kbd "u") 'monky-unstage-item)
  615. (define-key map (kbd "U") 'monky-unstage-all)
  616. (define-key map (kbd "a") 'monky-commit-amend)
  617. (define-key map (kbd "c") 'monky-log-edit)
  618. (define-key map (kbd "e") 'monky-ediff-item)
  619. (define-key map (kbd "y") 'monky-bookmark-create)
  620. (define-key map (kbd "C") 'monky-checkout)
  621. (define-key map (kbd "M") 'monky-merge)
  622. (define-key map (kbd "B") 'monky-backout)
  623. (define-key map (kbd "P") 'monky-push)
  624. (define-key map (kbd "f") 'monky-pull)
  625. (define-key map (kbd "k") 'monky-discard-item)
  626. (define-key map (kbd "m") 'monky-resolve-item)
  627. (define-key map (kbd "x") 'monky-unresolve-item)
  628. (define-key map (kbd "X") 'monky-reset-tip)
  629. (define-key map (kbd "A") 'monky-addremove-all)
  630. (define-key map (kbd "L") 'monky-rollback)
  631. map))
  632. (defvar monky-log-mode-map
  633. (let ((map (make-keymap)))
  634. (define-key map (kbd "e") 'monky-log-show-more-entries)
  635. (define-key map (kbd "C") 'monky-checkout-item)
  636. (define-key map (kbd "M") 'monky-merge-item)
  637. (define-key map (kbd "B") 'monky-backout-item)
  638. (define-key map (kbd "i") 'monky-qimport-item)
  639. map))
  640. (defvar monky-blame-mode-map
  641. (let ((map (make-keymap)))
  642. map))
  643. (defvar monky-branches-mode-map
  644. (let ((map (make-keymap)))
  645. (define-key map (kbd "C") 'monky-checkout-item)
  646. (define-key map (kbd "M") 'monky-merge-item)
  647. map))
  648. (defvar monky-commit-mode-map
  649. (let ((map (make-keymap)))
  650. map))
  651. (defvar monky-queue-mode-map
  652. (let ((map (make-keymap)))
  653. (define-key map (kbd "u") 'monky-qpop-item)
  654. (define-key map (kbd "U") 'monky-qpop-all)
  655. (define-key map (kbd "s") 'monky-qpush-item)
  656. (define-key map (kbd "S") 'monky-qpush-all)
  657. (define-key map (kbd "r") 'monky-qrefresh)
  658. (define-key map (kbd "R") 'monky-qrename-item)
  659. (define-key map (kbd "k") 'monky-qremove-item)
  660. (define-key map (kbd "N") 'monky-qnew)
  661. (define-key map (kbd "f") 'monky-qfinish-item)
  662. (define-key map (kbd "F") 'monky-qfinish-applied)
  663. (define-key map (kbd "d") 'monky-qfold-item)
  664. (define-key map (kbd "G") 'monky-qguard-item)
  665. (define-key map (kbd "o") 'monky-qreorder)
  666. (define-key map (kbd "A") 'monky-addremove-all)
  667. map))
  668. (defvar monky-pre-log-edit-window-configuration nil)
  669. (defvar monky-log-edit-client-buffer nil)
  670. (defvar monky-log-edit-operation nil)
  671. (defvar monky-log-edit-info nil)
  672. (defvar monky-log-edit-mode-map
  673. (let ((map (make-sparse-keymap)))
  674. (define-key map (kbd "C-c C-c") 'monky-log-edit-commit)
  675. (define-key map (kbd "C-c C-k") 'monky-log-edit-cancel-log-message)
  676. (define-key map (kbd "C-x C-s")
  677. (lambda ()
  678. (interactive)
  679. (message "Not saved. Use C-c C-c to finalize this %s." monky-log-edit-operation)))
  680. map))
  681. ;;; Sections
  682. (defvar-local monky-top-section nil)
  683. (defvar monky-old-top-section nil)
  684. (defvar monky-section-hidden-default nil)
  685. ;; A buffer in monky-mode is organized into hierarchical sections.
  686. ;; These sections are used for navigation and for hiding parts of the
  687. ;; buffer.
  688. ;;
  689. ;; Most sections also represent the objects that Monky works with,
  690. ;; such as files, diffs, hunks, commits, etc. The 'type' of a section
  691. ;; identifies what kind of object it represents (if any), and the
  692. ;; parent and grand-parent, etc provide the context.
  693. (defstruct monky-section
  694. parent children beginning end type title hidden info)
  695. (defun monky-set-section-info (info &optional section)
  696. (setf (monky-section-info (or section monky-top-section)) info))
  697. (defun monky-new-section (title type)
  698. "Create a new section with title TITLE and type TYPE in current buffer.
  699. If not `monky-top-section' exist, the new section will be the new top-section
  700. otherwise, the new-section will be a child of the current top-section.
  701. If TYPE is nil, the section won't be highlighted."
  702. (let* ((s (make-monky-section :parent monky-top-section
  703. :title title
  704. :type type
  705. :hidden monky-section-hidden-default))
  706. (old (and monky-old-top-section
  707. (monky-find-section (monky-section-path s)
  708. monky-old-top-section))))
  709. (if monky-top-section
  710. (push s (monky-section-children monky-top-section))
  711. (setq monky-top-section s))
  712. (if old
  713. (setf (monky-section-hidden s) (monky-section-hidden old)))
  714. s))
  715. (defmacro monky-with-section (title type &rest body)
  716. "Create a new section of title TITLE and type TYPE and evaluate BODY there.
  717. Sections create into BODY will be child of the new section.
  718. BODY must leave point at the end of the created section.
  719. If TYPE is nil, the section won't be highlighted."
  720. (declare (indent 2)
  721. (debug (symbolp symbolp body)))
  722. (let ((s (make-symbol "*section*")))
  723. `(let* ((,s (monky-new-section ,title ,type))
  724. (monky-top-section ,s))
  725. (setf (monky-section-beginning ,s) (point))
  726. ,@body
  727. (setf (monky-section-end ,s) (point))
  728. (setf (monky-section-children ,s)
  729. (nreverse (monky-section-children ,s)))
  730. ,s)))
  731. (defmacro monky-create-buffer-sections (&rest body)
  732. "Empty current buffer of text and monky's section, and then evaluate BODY."
  733. (declare (indent 0)
  734. (debug (body)))
  735. `(let ((inhibit-read-only t))
  736. (erase-buffer)
  737. (let ((monky-old-top-section monky-top-section))
  738. (setq monky-top-section nil)
  739. ,@body
  740. (when (null monky-top-section)
  741. (monky-with-section 'top nil
  742. (insert "(empty)\n")))
  743. (monky-propertize-section monky-top-section)
  744. (monky-section-set-hidden monky-top-section
  745. (monky-section-hidden monky-top-section)))))
  746. (defun monky-propertize-section (section)
  747. "Add text-property needed for SECTION."
  748. (put-text-property (monky-section-beginning section)
  749. (monky-section-end section)
  750. 'monky-section section)
  751. (dolist (s (monky-section-children section))
  752. (monky-propertize-section s)))
  753. (defun monky-find-section (path top)
  754. "Find the section at the path PATH in subsection of section TOP."
  755. (if (null path)
  756. top
  757. (let ((secs (monky-section-children top)))
  758. (while (and secs (not (equal (car path)
  759. (monky-section-title (car secs)))))
  760. (setq secs (cdr secs)))
  761. (and (car secs)
  762. (monky-find-section (cdr path) (car secs))))))
  763. (defun monky-section-path (section)
  764. "Return the path of SECTION."
  765. (if (not (monky-section-parent section))
  766. '()
  767. (append (monky-section-path (monky-section-parent section))
  768. (list (monky-section-title section)))))
  769. (defun monky-insert-section (section-title-and-type buffer-title washer cmd &rest args)
  770. "Run CMD and put its result in a new section.
  771. SECTION-TITLE-AND-TYPE is either a string that is the title of the section
  772. or (TITLE . TYPE) where TITLE is the title of the section and TYPE is its type.
  773. If there is no type, or if type is nil, the section won't be highlighted.
  774. BUFFER-TITLE is the inserted title of the section
  775. WASHER is a function that will be run after CMD.
  776. The buffer will be narrowed to the inserted text.
  777. It should add sectioning as needed for monky interaction
  778. CMD is an external command that will be run with ARGS as arguments"
  779. (monky-with-process
  780. (let* ((body-beg nil)
  781. (section-title (if (consp section-title-and-type)
  782. (car section-title-and-type)
  783. section-title-and-type))
  784. (section-type (if (consp section-title-and-type)
  785. (cdr section-title-and-type)
  786. nil))
  787. (section (monky-with-section section-title section-type
  788. (if buffer-title
  789. (insert (propertize buffer-title 'face 'monky-section-title) "\n"))
  790. (setq body-beg (point))
  791. (apply 'monky-process-file cmd nil t nil args)
  792. (if (not (eq (char-before) ?\n))
  793. (insert "\n"))
  794. (if washer
  795. (save-restriction
  796. (narrow-to-region body-beg (point))
  797. (goto-char (point-min))
  798. (funcall washer)
  799. (goto-char (point-max)))))))
  800. (when (= body-beg (point))
  801. (monky-cancel-section section))
  802. section)))
  803. (defun monky-cancel-section (section)
  804. (delete-region (monky-section-beginning section)
  805. (monky-section-end section))
  806. (let ((parent (monky-section-parent section)))
  807. (if parent
  808. (setf (monky-section-children parent)
  809. (delq section (monky-section-children parent)))
  810. (setq monky-top-section nil))))
  811. (defun monky-current-section ()
  812. "Return the monky section at point."
  813. (monky-section-at (point)))
  814. (defun monky-section-at (pos)
  815. "Return the monky section at position POS."
  816. (or (get-text-property pos 'monky-section)
  817. monky-top-section))
  818. (defun monky-find-section-after (pos secs)
  819. "Find the first section that begins after POS in the list SECS."
  820. (while (and secs
  821. (not (> (monky-section-beginning (car secs)) pos)))
  822. (setq secs (cdr secs)))
  823. (car secs))
  824. (defun monky-find-section-before (pos secs)
  825. "Find the last section that begins before POS in the list SECS."
  826. (let ((prev nil))
  827. (while (and secs
  828. (not (> (monky-section-beginning (car secs)) pos)))
  829. (setq prev (car secs))
  830. (setq secs (cdr secs)))
  831. prev))
  832. (defun monky-next-section (section)
  833. "Return the section that is after SECTION."
  834. (let ((parent (monky-section-parent section)))
  835. (if parent
  836. (let ((next (cadr (memq section
  837. (monky-section-children parent)))))
  838. (or next
  839. (monky-next-section parent))))))
  840. (defvar-local monky-submode nil)
  841. (defvar-local monky-refresh-function nil)
  842. (defvar-local monky-refresh-args nil)
  843. (defun monky-goto-next-section ()
  844. "Go to the next monky section."
  845. (interactive)
  846. (let* ((section (monky-current-section))
  847. (next (or (and (not (monky-section-hidden section))
  848. (monky-section-children section)
  849. (monky-find-section-after (point)
  850. (monky-section-children
  851. section)))
  852. (monky-next-section section))))
  853. (cond
  854. ((and next (eq (monky-section-type next) 'longer))
  855. (when monky-log-auto-more
  856. (monky-log-show-more-entries)
  857. (monky-goto-next-section)))
  858. (next
  859. (goto-char (monky-section-beginning next))
  860. (if (memq monky-submode '(log blame))
  861. (monky-show-commit next)))
  862. (t (message "No next section")))))
  863. (defun monky-prev-section (section)
  864. "Return the section that is before SECTION."
  865. (let ((parent (monky-section-parent section)))
  866. (if parent
  867. (let ((prev (cadr (memq section
  868. (reverse (monky-section-children parent))))))
  869. (cond (prev
  870. (while (and (not (monky-section-hidden prev))
  871. (monky-section-children prev))
  872. (setq prev (car (reverse (monky-section-children prev)))))
  873. prev)
  874. (t
  875. parent))))))
  876. (defun monky-goto-previous-section ()
  877. "Goto the previous monky section."
  878. (interactive)
  879. (let ((section (monky-current-section)))
  880. (cond ((= (point) (monky-section-beginning section))
  881. (let ((prev (monky-prev-section (monky-current-section))))
  882. (if prev
  883. (progn
  884. (if (memq monky-submode '(log blame))
  885. (monky-show-commit prev))
  886. (goto-char (monky-section-beginning prev)))
  887. (message "No previous section"))))
  888. (t
  889. (let ((prev (monky-find-section-before (point)
  890. (monky-section-children
  891. section))))
  892. (if (memq monky-submode '(log blame))
  893. (monky-show-commit (or prev section)))
  894. (goto-char (monky-section-beginning (or prev section))))))))
  895. (defun monky-section-context-type (section)
  896. (if (null section)
  897. '()
  898. (let ((c (or (monky-section-type section)
  899. (if (symbolp (monky-section-title section))
  900. (monky-section-title section)))))
  901. (if c
  902. (cons c (monky-section-context-type
  903. (monky-section-parent section)))
  904. '()))))
  905. (defun monky-hg-section (section-title-and-type buffer-title washer &rest args)
  906. (apply #'monky-insert-section
  907. section-title-and-type
  908. buffer-title
  909. washer
  910. monky-hg-executable
  911. (append monky-hg-standard-options args)))
  912. (defun monky-section-set-hidden (section hidden)
  913. "Hide SECTION if HIDDEN is not nil, show it otherwise."
  914. (setf (monky-section-hidden section) hidden)
  915. (let ((inhibit-read-only t)
  916. (beg (save-excursion
  917. (goto-char (monky-section-beginning section))
  918. (forward-line)
  919. (point)))
  920. (end (monky-section-end section)))
  921. (if (< beg end)
  922. (put-text-property beg end 'invisible hidden)))
  923. (if (not hidden)
  924. (dolist (c (monky-section-children section))
  925. (monky-section-set-hidden c (monky-section-hidden c)))))
  926. (defun monky-toggle-section ()
  927. "Toggle hidden status of current section."
  928. (interactive)
  929. (let ((section (monky-current-section)))
  930. (when (monky-section-parent section)
  931. (goto-char (monky-section-beginning section))
  932. (monky-section-set-hidden section (not (monky-section-hidden section))))))
  933. (defun monky-section-show-level-1-all ()
  934. "Collapse all the sections in the monky status buffer."
  935. (interactive)
  936. (save-excursion
  937. (goto-char (point-min))
  938. (while (not (eobp))
  939. (let ((section (monky-current-section)))
  940. (monky-section-set-hidden section t))
  941. (forward-line 1))))
  942. (defun monky-section-show-level-2-all ()
  943. "Show all the files changes, but not their contents."
  944. (interactive)
  945. (save-excursion
  946. (goto-char (point-min))
  947. (while (not (eobp))
  948. (let ((section (monky-current-section)))
  949. (if (memq (monky-section-type section) (list 'hunk 'diff))
  950. (monky-section-set-hidden section t)
  951. (monky-section-set-hidden section nil)))
  952. (forward-line 1))))
  953. (defun monky-section-show-level-3-all ()
  954. "Expand all file contents and line numbers, but not the actual changes."
  955. (interactive)
  956. (save-excursion
  957. (goto-char (point-min))
  958. (while (not (eobp))
  959. (let ((section (monky-current-section)))
  960. (if (memq (monky-section-type section) (list 'hunk))
  961. (monky-section-set-hidden section t)
  962. (monky-section-set-hidden section nil)))
  963. (forward-line 1))))
  964. (defun monky-section-show-level-4-all ()
  965. "Expand all sections."
  966. (interactive)
  967. (save-excursion
  968. (goto-char (point-min))
  969. (while (not (eobp))
  970. (let ((section (monky-current-section)))
  971. (monky-section-set-hidden section nil))
  972. (forward-line 1))))
  973. ;;; Running commands
  974. (defun monky-set-mode-line-process (str)
  975. (let ((pr (if str (concat " " str) "")))
  976. (save-excursion
  977. (monky-for-all-buffers (lambda ()
  978. (setq mode-line-process pr))))))
  979. (defun monky-process-indicator-from-command (comps)
  980. (if (monky-prefix-p (cons monky-hg-executable monky-hg-standard-options)
  981. comps)
  982. (setq comps (nthcdr (+ (length monky-hg-standard-options) 1) comps)))
  983. (car comps))
  984. (defun monky-run* (cmd-and-args
  985. &optional logline noerase noerror nowait input)
  986. (if (and monky-process
  987. (get-buffer monky-process-buffer-name))
  988. (error "Hg is already running"))
  989. (let ((cmd (car cmd-and-args))
  990. (args (cdr cmd-and-args))
  991. (dir default-directory)
  992. (buf (get-buffer-create monky-process-buffer-name))
  993. (successp nil))
  994. (monky-set-mode-line-process
  995. (monky-process-indicator-from-command cmd-and-args))
  996. (setq monky-process-client-buffer (current-buffer))
  997. (with-current-buffer buf
  998. (view-mode 1)
  999. (set (make-local-variable 'view-no-disable-on-exit) t)
  1000. (setq view-exit-action
  1001. (lambda (buffer)
  1002. (with-current-buffer buffer
  1003. (bury-buffer))))
  1004. (setq buffer-read-only t)
  1005. (let ((inhibit-read-only t))
  1006. (setq default-directory dir)
  1007. (if noerase
  1008. (goto-char (point-max))
  1009. (erase-buffer))
  1010. (insert "$ " (or logline
  1011. (mapconcat #'identity cmd-and-args " "))
  1012. "\n")
  1013. (cond (nowait
  1014. (setq monky-process
  1015. (let ((process-connection-type nil))
  1016. (apply 'monky-start-process cmd buf cmd args)))
  1017. (set-process-sentinel monky-process 'monky-process-sentinel)
  1018. (set-process-filter monky-process 'monky-process-filter)
  1019. (when input
  1020. (with-current-buffer input
  1021. (process-send-region monky-process
  1022. (point-min) (point-max))
  1023. (process-send-eof monky-process)
  1024. (sit-for 0.1 t)))
  1025. (cond ((= monky-process-popup-time 0)
  1026. (pop-to-buffer (process-buffer monky-process)))
  1027. ((> monky-process-popup-time 0)
  1028. (run-with-timer
  1029. monky-process-popup-time nil
  1030. (function
  1031. (lambda (buf)
  1032. (with-current-buffer buf
  1033. (when monky-process
  1034. (display-buffer (process-buffer monky-process))
  1035. (goto-char (point-max))))))
  1036. (current-buffer))))
  1037. (setq successp t))
  1038. (monky-cmd-process
  1039. (let ((monky-cmd-process-input-buffer input)
  1040. (monky-cmd-process-input-point (and input
  1041. (with-current-buffer input
  1042. (point-min)))))
  1043. (setq successp
  1044. (equal (apply #'monky-cmdserver-runcommand (cdr cmd-and-args)) 0))
  1045. (monky-set-mode-line-process nil)
  1046. (monky-need-refresh monky-process-client-buffer)))
  1047. (input
  1048. (with-current-buffer input
  1049. (setq default-directory dir)
  1050. (setq monky-process
  1051. ;; Don't use a pty, because it would set icrnl
  1052. ;; which would modify the input (issue #20).
  1053. (let ((process-connection-type nil))
  1054. (apply 'monky-start-process cmd buf cmd args)))
  1055. (set-process-filter monky-process 'monky-process-filter)
  1056. (process-send-region monky-process
  1057. (point-min) (point-max))
  1058. (process-send-eof monky-process)
  1059. (while (equal (process-status monky-process) 'run)
  1060. (sit-for 0.1 t))
  1061. (setq successp
  1062. (equal (process-exit-status monky-process) 0))
  1063. (setq monky-process nil))
  1064. (monky-set-mode-line-process nil)
  1065. (monky-need-refresh monky-process-client-buffer))
  1066. (t
  1067. (setq successp
  1068. (equal (apply 'monky-process-file-single cmd nil buf nil args) 0))
  1069. (monky-set-mode-line-process nil)
  1070. (monky-need-refresh monky-process-client-buffer))))
  1071. (or successp
  1072. noerror
  1073. (error
  1074. (or monky-cmd-error-message
  1075. (monky-abort-message (get-buffer monky-process-buffer-name))
  1076. "Hg failed")))
  1077. successp)))
  1078. (defun monky-process-sentinel (process event)
  1079. (let ((msg (format "Hg %s." (substring event 0 -1)))
  1080. (successp (string-match "^finished" event)))
  1081. (with-current-buffer (process-buffer process)
  1082. (let ((inhibit-read-only t))
  1083. (goto-char (point-max))
  1084. (insert msg "\n")
  1085. (message msg)))
  1086. (when (not successp)
  1087. (let ((msg (monky-abort-message (process-buffer process))))
  1088. (when msg
  1089. (message msg))))
  1090. (setq monky-process nil)
  1091. (monky-set-mode-line-process nil)
  1092. (if (buffer-live-p monky-process-client-buffer)
  1093. (with-current-buffer monky-process-client-buffer
  1094. (monky-with-refresh
  1095. (monky-need-refresh monky-process-client-buffer))))))
  1096. (defun monky-abort-message (buffer)
  1097. (with-current-buffer buffer
  1098. (save-excursion
  1099. (goto-char (point-min))
  1100. (when (re-search-forward
  1101. (concat "^abort: \\(.*\\)" paragraph-separate) nil t)
  1102. (match-string 1)))))
  1103. ;; TODO password?
  1104. (defun monky-process-filter (proc string)
  1105. (save-current-buffer
  1106. (set-buffer (process-buffer proc))
  1107. (let ((inhibit-read-only t))
  1108. (goto-char (process-mark proc))
  1109. (insert string)
  1110. (set-marker (process-mark proc) (point)))))
  1111. (defun monky-run-hg (&rest args)
  1112. (monky-with-refresh
  1113. (monky-run* (append (cons monky-hg-executable
  1114. monky-hg-standard-options)
  1115. args))))
  1116. (defun monky-run-hg-sync (&rest args)
  1117. (message "Running %s %s"
  1118. monky-hg-executable
  1119. (mapconcat #'identity args " "))
  1120. (monky-run* (append (cons monky-hg-executable
  1121. monky-hg-standard-options)
  1122. args)))
  1123. (defun monky-run-hg-async (&rest args)
  1124. (message "Running %s %s"
  1125. monky-hg-executable
  1126. (mapconcat #'identity args " "))
  1127. (monky-run* (append (cons monky-hg-executable
  1128. monky-hg-standard-options)
  1129. args)
  1130. nil nil nil t))
  1131. (defun monky-run-async-with-input (input cmd &rest args)
  1132. (monky-run* (cons cmd args) nil nil nil t input))
  1133. (defun monky-display-process ()
  1134. "Display output from most recent hg command."
  1135. (interactive)
  1136. (unless (get-buffer monky-process-buffer-name)
  1137. (user-error "No Hg commands have run"))
  1138. (display-buffer monky-process-buffer-name))
  1139. (defun monky-hg-command (command)
  1140. "Perform arbitrary Hg COMMAND."
  1141. (interactive "sRun hg like this: ")
  1142. (let ((args (monky-parse-args command))
  1143. (monky-process-popup-time 0))
  1144. (monky-with-refresh
  1145. (monky-run* (append (cons monky-hg-executable
  1146. monky-hg-standard-options)
  1147. args)
  1148. nil nil nil t))))
  1149. ;;; Actions
  1150. (defmacro monky-section-case (opname &rest clauses)
  1151. "Make different action depending of current section.
  1152. HEAD is (SECTION INFO &optional OPNAME),
  1153. SECTION will be bind to the current section,
  1154. INFO will be bind to the info's of the current section,
  1155. OPNAME is a string that will be used to describe current action,
  1156. CLAUSES is a list of CLAUSE, each clause is (SECTION-TYPE &BODY)
  1157. where SECTION-TYPE describe section where BODY will be run.
  1158. This returns non-nil if some section matches. If the
  1159. corresponding body return a non-nil value, it is returned,
  1160. otherwise it return t.
  1161. If no section matches, this returns nil if no OPNAME was given
  1162. and throws an error otherwise."
  1163. (declare (indent 1)
  1164. (debug (form &rest (sexp body))))
  1165. (let ((section (make-symbol "*section*"))
  1166. (type (make-symbol "*type*"))
  1167. (context (make-symbol "*context*")))
  1168. `(let* ((,section (monky-current-section))
  1169. (,type (monky-section-type ,section))
  1170. (,context (monky-section-context-type ,section)))
  1171. (cond ,@(mapcar (lambda (clause)
  1172. (let ((prefix (car clause))
  1173. (body (cdr clause)))
  1174. `(,(if (eq prefix t)
  1175. `t
  1176. `(monky-prefix-p ',(reverse prefix) ,context))
  1177. (or (progn ,@body)
  1178. t))))
  1179. clauses)
  1180. ,@(when opname
  1181. `(((not ,type)
  1182. (user-error "Nothing to %s here" ,opname))
  1183. (t
  1184. (error "Can't %s as %s"
  1185. ,opname
  1186. ,type))))))))
  1187. (defmacro monky-section-action (opname &rest clauses)
  1188. "Refresh monky buffers after executing action defined in CLAUSES.
  1189. See `monky-section-case' for the definition of HEAD and CLAUSES and
  1190. `monky-with-refresh' for how the buffers are refreshed."
  1191. (declare (indent 1)
  1192. (debug (form &rest (sexp body))))
  1193. `(monky-with-refresh
  1194. (monky-section-case ,opname ,@clauses)))
  1195. (defun monky-visit-item (&optional other-window)
  1196. "Visit current item.
  1197. With a prefix argument, visit in other window."
  1198. (interactive (list current-prefix-arg))
  1199. (let ((ff (if other-window 'find-file-other-window 'find-file)))
  1200. (monky-section-action "visit"
  1201. ((file)
  1202. (funcall ff (monky-section-info (monky-current-section))))
  1203. ((diff)
  1204. (funcall ff (monky-diff-item-file (monky-current-section))))
  1205. ((hunk)
  1206. (let ((file (monky-diff-item-file (monky-hunk-item-diff (monky-current-section))))
  1207. (line (monky-hunk-item-target-line (monky-current-section))))
  1208. (funcall ff file)
  1209. (goto-char (point-min))
  1210. (forward-line (1- line))))
  1211. ((commit)
  1212. (monky-show-commit (monky-section-info (monky-current-section))))
  1213. ((longer)

Large files files are truncated, but you can click here to view the full file