Blame


1 b63a5e47 2021-05-08 rnkn ;;; side-hustle.el --- Hustle through Imenu in a side window -*- lexical-binding: t; -*-
2 e0ef727d 2021-05-02 rnkn
3 0ac8dfa0 2024-03-25 rnkn ;; Copyright (c) 2021-2024 Paul W. Rankin
4 e0ef727d 2021-05-02 rnkn
5 94450b58 2024-06-25 rnkn ;; Author: Paul W. Rankin <rnkn@rnkn.xyz>
6 e0ef727d 2021-05-02 rnkn ;; Keywords: convenience
7 252c924c 2024-03-25 rnkn ;; Version: 0.3.0
8 e0ef727d 2021-05-02 rnkn ;; Package-Requires: ((emacs "24.4") (seq "2.20"))
9 83dd0444 2021-05-24 rnkn ;; URL: https://github.com/rnkn/side-hustle
10 e0ef727d 2021-05-02 rnkn
11 e0ef727d 2021-05-02 rnkn ;; This program is free software; you can redistribute it and/or modify
12 e0ef727d 2021-05-02 rnkn ;; it under the terms of the GNU General Public License as published by
13 e0ef727d 2021-05-02 rnkn ;; the Free Software Foundation, either version 3 of the License, or
14 e0ef727d 2021-05-02 rnkn ;; (at your option) any later version.
15 e0ef727d 2021-05-02 rnkn
16 e0ef727d 2021-05-02 rnkn ;; This program is distributed in the hope that it will be useful,
17 e0ef727d 2021-05-02 rnkn ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 e0ef727d 2021-05-02 rnkn ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 e0ef727d 2021-05-02 rnkn ;; GNU General Public License for more details.
20 e0ef727d 2021-05-02 rnkn
21 e0ef727d 2021-05-02 rnkn ;; You should have received a copy of the GNU General Public License
22 e0ef727d 2021-05-02 rnkn ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
23 e0ef727d 2021-05-02 rnkn
24 e0ef727d 2021-05-02 rnkn ;;; Commentary:
25 e0ef727d 2021-05-02 rnkn
26 3b9d369b 2021-05-08 rnkn ;; Side Hustle
27 3b9d369b 2021-05-08 rnkn ;; ===========
28 e0ef727d 2021-05-02 rnkn
29 3b9d369b 2021-05-08 rnkn ;; Hustle through a buffer's Imenu in a side window in GNU Emacs.
30 3b9d369b 2021-05-08 rnkn
31 43cb301f 2021-06-27 rnkn ;; Side Hustle spawns a side window linked to the current buffer, which allows
32 43cb301f 2021-06-27 rnkn ;; working with multiple buffers simultaneously.
33 3b9d369b 2021-05-08 rnkn
34 3b9d369b 2021-05-08 rnkn
35 3b9d369b 2021-05-08 rnkn ;; Installation
36 3b9d369b 2021-05-08 rnkn ;; ------------
37 3b9d369b 2021-05-08 rnkn
38 43cb301f 2021-06-27 rnkn ;; The latest stable release of Side Hustle is available via [MELPA-stable][1].
39 43cb301f 2021-06-27 rnkn ;; First, add MELPA-stable to your package archives:
40 43cb301f 2021-06-27 rnkn
41 43cb301f 2021-06-27 rnkn ;; M-x customize-option RET package-archives RET
42 43cb301f 2021-06-27 rnkn
43 43cb301f 2021-06-27 rnkn ;; Insert an entry named melpa-stable with URL:
44 43cb301f 2021-06-27 rnkn ;; https://stable.melpa.org/packages/
45 43cb301f 2021-06-27 rnkn
46 43cb301f 2021-06-27 rnkn ;; You can then find the latest stable version of side-hustle in the list
47 43cb301f 2021-06-27 rnkn ;; returned by:
48 43cb301f 2021-06-27 rnkn
49 43cb301f 2021-06-27 rnkn ;; M-x list-packages RET
50 43cb301f 2021-06-27 rnkn
51 43cb301f 2021-06-27 rnkn ;; If you prefer the latest but perhaps unstable version, do the above using
52 43cb301f 2021-06-27 rnkn ;; [MELPA][2].
53 43cb301f 2021-06-27 rnkn
54 43cb301f 2021-06-27 rnkn ;; Then add a key binding to your init file:
55 3b9d369b 2021-05-08 rnkn
56 3b9d369b 2021-05-08 rnkn ;; (define-key (current-global-map) (kbd "M-s l") #'side-hustle-toggle)
57 3b9d369b 2021-05-08 rnkn
58 3b9d369b 2021-05-08 rnkn
59 3b9d369b 2021-05-08 rnkn ;; Bugs and Feature Requests
60 3b9d369b 2021-05-08 rnkn ;; -------------------------
61 3b9d369b 2021-05-08 rnkn
62 3b9d369b 2021-05-08 rnkn ;; Send me an email (address in the package header). For bugs, please
63 3b9d369b 2021-05-08 rnkn ;; ensure you can reproduce with:
64 3b9d369b 2021-05-08 rnkn
65 3b9d369b 2021-05-08 rnkn ;; $ emacs -Q -l side-hustle.el
66 3b9d369b 2021-05-08 rnkn
67 3b9d369b 2021-05-08 rnkn ;; Known issues are tracked with FIXME comments in the source.
68 3b9d369b 2021-05-08 rnkn
69 3b9d369b 2021-05-08 rnkn
70 3b9d369b 2021-05-08 rnkn ;; Alternatives
71 3b9d369b 2021-05-08 rnkn ;; ------------
72 3b9d369b 2021-05-08 rnkn
73 3b9d369b 2021-05-08 rnkn ;; Side Hustle takes inspiration primarily from
74 3b9d369b 2021-05-08 rnkn ;; [imenu-list](https://github.com/bmag/imenu-list).
75 3b9d369b 2021-05-08 rnkn
76 43cb301f 2021-06-27 rnkn
77 43cb301f 2021-06-27 rnkn ;; [1]: https://stable.melpa.org/#/side-hustle
78 43cb301f 2021-06-27 rnkn ;; [2]: https://melpa.org/#/side-hustle
79 43cb301f 2021-06-27 rnkn
80 e0ef727d 2021-05-02 rnkn ;;; Code:
81 e0ef727d 2021-05-02 rnkn
82 e0ef727d 2021-05-02 rnkn (require 'imenu)
83 e0ef727d 2021-05-02 rnkn
84 e0ef727d 2021-05-02 rnkn (defgroup side-hustle nil
85 e0ef727d 2021-05-02 rnkn "Navigate Imenu in a side window."
86 e0ef727d 2021-05-02 rnkn :group 'convenience
87 e0ef727d 2021-05-02 rnkn :group 'matching
88 e0ef727d 2021-05-02 rnkn :link '(info-link "(emacs) Imenu"))
89 e0ef727d 2021-05-02 rnkn
90 e0ef727d 2021-05-02 rnkn
91 03fee7f6 2021-05-29 rnkn ;;; Internal Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
92 e0ef727d 2021-05-02 rnkn
93 e0ef727d 2021-05-02 rnkn (defvar-local side-hustle--source-buffer nil)
94 81ac148f 2021-05-29 rnkn (defvar-local side-hustle--hidden nil)
95 e0ef727d 2021-05-02 rnkn
96 e0ef727d 2021-05-02 rnkn
97 03fee7f6 2021-05-29 rnkn ;;; User Options ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98 e0ef727d 2021-05-02 rnkn
99 e0ef727d 2021-05-02 rnkn (defcustom side-hustle-display-alist
100 e0ef727d 2021-05-02 rnkn '((side . left)
101 e0ef727d 2021-05-02 rnkn (slot . 0)
102 e0ef727d 2021-05-02 rnkn (window-width . 40))
103 e0ef727d 2021-05-02 rnkn "Alist used to display side-hustle buffer."
104 e0ef727d 2021-05-02 rnkn :type 'alist
105 e0ef727d 2021-05-02 rnkn :group 'side-hustle
106 e0ef727d 2021-05-02 rnkn :link '(info-link "(elisp) Buffer Display Action Alists"))
107 e0ef727d 2021-05-02 rnkn
108 e0ef727d 2021-05-02 rnkn (defcustom side-hustle-select-window t
109 e0ef727d 2021-05-02 rnkn "When non-nil, select the menu window after creating it."
110 e0ef727d 2021-05-02 rnkn :type 'boolean
111 e0ef727d 2021-05-02 rnkn :safe 'booleanp
112 e0ef727d 2021-05-02 rnkn :group 'side-hustle)
113 e0ef727d 2021-05-02 rnkn
114 ccd3996f 2021-05-07 rnkn (defcustom side-hustle-persistent-window nil
115 e0ef727d 2021-05-02 rnkn "When non-nil, make the side-window persistent.
116 ccd3996f 2021-05-07 rnkn This requires either calling `quit-window' or
117 ccd3996f 2021-05-07 rnkn `side-hustle-toggle' to quit the side-window."
118 ccd3996f 2021-05-07 rnkn :type 'boolean
119 ccd3996f 2021-05-07 rnkn :safe 'booleanp
120 ccd3996f 2021-05-07 rnkn :group 'side-hustle)
121 ccd3996f 2021-05-07 rnkn
122 ccd3996f 2021-05-07 rnkn (defcustom side-hustle-evaporate-window nil
123 3b9d369b 2021-05-08 rnkn "When non-nil, quit the side window when following link."
124 e0ef727d 2021-05-02 rnkn :type 'boolean
125 e0ef727d 2021-05-02 rnkn :safe 'booleanp
126 c114849a 2021-05-08 rnkn :group 'side-hustle)
127 c114849a 2021-05-08 rnkn
128 c114849a 2021-05-08 rnkn (defcustom side-hustle-item-char ?\*
129 c114849a 2021-05-08 rnkn "Character to use to itemize `imenu' items."
130 4b23f0ff 2021-05-08 rnkn :type '(choice (const nil) character)
131 e0ef727d 2021-05-02 rnkn :group 'side-hustle)
132 e0ef727d 2021-05-02 rnkn
133 4b23f0ff 2021-05-08 rnkn (defcustom side-hustle-indent-width 4
134 4b23f0ff 2021-05-08 rnkn "Indent width in columns for sublevels of `imenu' items."
135 4b23f0ff 2021-05-08 rnkn :type 'integer
136 4b23f0ff 2021-05-08 rnkn :safe 'integerp
137 e0ef727d 2021-05-02 rnkn :group 'side-hustle)
138 e0ef727d 2021-05-02 rnkn
139 e0ef727d 2021-05-02 rnkn
140 03fee7f6 2021-05-29 rnkn ;;; Faces ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141 e0ef727d 2021-05-02 rnkn
142 e0ef727d 2021-05-02 rnkn (defface side-hustle
143 d4dd6c5a 2021-05-08 rnkn '((t (:underline nil :inherit button)))
144 e0ef727d 2021-05-02 rnkn "Default face for side-window items."
145 e0ef727d 2021-05-02 rnkn :group 'side-hustle)
146 e0ef727d 2021-05-02 rnkn
147 b27d4a57 2021-06-22 rnkn ;; (defface side-hustle-highlight
148 b27d4a57 2021-06-22 rnkn ;; '((t (:extend t :inherit (secondary-selection))))
149 b27d4a57 2021-06-22 rnkn ;; "Default face for highlighted items."
150 b27d4a57 2021-06-22 rnkn ;; :group 'side-hustle)
151 e0ef727d 2021-05-02 rnkn
152 e0ef727d 2021-05-02 rnkn
153 03fee7f6 2021-05-29 rnkn ;;; Internal Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154 e0ef727d 2021-05-02 rnkn
155 e816a98b 2021-05-08 rnkn (defun side-hustle-button-ensure ()
156 57fe28f9 2021-05-08 rnkn "Ensure point is at button."
157 e816a98b 2021-05-08 rnkn (or (button-at (point))
158 57fe28f9 2021-05-08 rnkn (and (eolp) (forward-button -1 nil nil t))
159 e816a98b 2021-05-08 rnkn (forward-button 1 nil nil t)))
160 e816a98b 2021-05-08 rnkn
161 252c924c 2024-03-25 rnkn (defun side-hustle-imenu-item (button save-window)
162 93bf5cc3 2021-05-09 rnkn "Pop up buffer containing `imenu' item for BUTTON.
163 93bf5cc3 2021-05-09 rnkn Handle buffer according to SAVE-WINDOW or value of
164 99ba3bdd 2021-05-08 rnkn `side-hustle-evaporate-window'."
165 93bf5cc3 2021-05-09 rnkn (let ((buffer (current-buffer))
166 252c924c 2024-03-25 rnkn (imenu-item (button-get button 'hustle-item)))
167 252c924c 2024-03-25 rnkn (when imenu-item
168 252c924c 2024-03-25 rnkn (pop-to-buffer side-hustle--source-buffer)
169 252c924c 2024-03-25 rnkn (imenu imenu-item)
170 93bf5cc3 2021-05-09 rnkn (recenter-top-bottom)
171 93bf5cc3 2021-05-09 rnkn (cond (save-window
172 93bf5cc3 2021-05-09 rnkn (select-window (get-buffer-window buffer (selected-frame))))
173 93bf5cc3 2021-05-09 rnkn (side-hustle-evaporate-window
174 93bf5cc3 2021-05-09 rnkn (quit-window nil (get-buffer-window buffer (selected-frame))))))))
175 99ba3bdd 2021-05-08 rnkn
176 81ac148f 2021-05-29 rnkn (defun side-hustle-switch-hide-state (label)
177 81ac148f 2021-05-29 rnkn "Toggle inclusion of LABEL in `side-hustle--hidden'."
178 81ac148f 2021-05-29 rnkn (setq side-hustle--hidden
179 81ac148f 2021-05-29 rnkn (if (member label side-hustle--hidden)
180 81ac148f 2021-05-29 rnkn (remove label side-hustle--hidden)
181 81ac148f 2021-05-29 rnkn (cons label side-hustle--hidden))))
182 81ac148f 2021-05-29 rnkn
183 99ba3bdd 2021-05-08 rnkn (defun side-hustle-show-hide (start end)
184 99ba3bdd 2021-05-08 rnkn "Toggle invisibility of items between START and END."
185 99ba3bdd 2021-05-08 rnkn (with-silent-modifications
186 99ba3bdd 2021-05-08 rnkn (if (eq (get-text-property (button-end (button-at (point))) 'invisible)
187 99ba3bdd 2021-05-08 rnkn 'hustle-invisible)
188 99ba3bdd 2021-05-08 rnkn (put-text-property start end 'invisible nil)
189 99ba3bdd 2021-05-08 rnkn (put-text-property start end 'invisible 'hustle-invisible))))
190 99ba3bdd 2021-05-08 rnkn
191 81ac148f 2021-05-29 rnkn (defun side-hustle-button-action (button &optional hide save-window)
192 99ba3bdd 2021-05-08 rnkn "Call appropriate button action for BUTTON.
193 81ac148f 2021-05-29 rnkn When HIDE is non-nil, always hide child items. Pass SAVE-WINDOW
194 252c924c 2024-03-25 rnkn to `side-hustle-imenu-item'."
195 e816a98b 2021-05-08 rnkn (let ((level (button-get button 'hustle-level))
196 81ac148f 2021-05-29 rnkn (label (button-label button))
197 e816a98b 2021-05-08 rnkn (start (button-end button))
198 99ba3bdd 2021-05-08 rnkn (end (button-end button)))
199 e816a98b 2021-05-08 rnkn (save-excursion
200 e816a98b 2021-05-08 rnkn (while (and (forward-button 1 nil nil t)
201 e816a98b 2021-05-08 rnkn (< level (button-get (button-at (point)) 'hustle-level)))
202 e816a98b 2021-05-08 rnkn (setq end (button-end (button-at (point))))))
203 81ac148f 2021-05-29 rnkn (cond ((= start end)
204 252c924c 2024-03-25 rnkn (side-hustle-imenu-item button save-window))
205 81ac148f 2021-05-29 rnkn (hide
206 81ac148f 2021-05-29 rnkn (side-hustle-show-hide start end))
207 81ac148f 2021-05-29 rnkn (t
208 81ac148f 2021-05-29 rnkn (side-hustle-show-hide start end)
209 81ac148f 2021-05-29 rnkn (side-hustle-switch-hide-state label)))))
210 e816a98b 2021-05-08 rnkn
211 e0ef727d 2021-05-02 rnkn (defun side-hustle-insert (item level)
212 e0ef727d 2021-05-02 rnkn "Insert ITEM at indentation level LEVEL.
213 4b23f0ff 2021-05-08 rnkn And `imenu' marker as button property."
214 4b23f0ff 2021-05-08 rnkn (insert (make-string level ?\t))
215 4b23f0ff 2021-05-08 rnkn (when (characterp side-hustle-item-char)
216 4b23f0ff 2021-05-08 rnkn (insert side-hustle-item-char "\s"))
217 a88720b9 2021-05-07 rnkn (insert-text-button (car item)
218 252c924c 2024-03-25 rnkn 'hustle-item item
219 e816a98b 2021-05-08 rnkn 'hustle-level level
220 e816a98b 2021-05-08 rnkn 'face 'side-hustle
221 e816a98b 2021-05-08 rnkn 'action #'side-hustle-button-action
222 671d86ce 2021-05-08 rnkn 'help-echo "mouse-1, RET: go to this item; SPC: show this item"
223 a88720b9 2021-05-07 rnkn 'follow-link t)
224 e0ef727d 2021-05-02 rnkn (insert ?\n))
225 e0ef727d 2021-05-02 rnkn
226 e0ef727d 2021-05-02 rnkn (defun side-hustle-insert-items (imenu-items level)
227 e0ef727d 2021-05-02 rnkn "For each item in IMENU-ITEMS, insert appropriately.
228 e0ef727d 2021-05-02 rnkn Either call `side-hustle-insert' at LEVEL, or if item is an
229 e0ef727d 2021-05-02 rnkn alist, insert alist string and increment LEVEL before calling
230 e0ef727d 2021-05-02 rnkn recursively with `cdr'."
231 e0ef727d 2021-05-02 rnkn (mapc
232 e0ef727d 2021-05-02 rnkn (lambda (item)
233 e0ef727d 2021-05-02 rnkn (if (imenu--subalist-p item)
234 e0ef727d 2021-05-02 rnkn (progn
235 e0ef727d 2021-05-02 rnkn (side-hustle-insert item level)
236 e0ef727d 2021-05-02 rnkn (side-hustle-insert-items (cdr item) (1+ level)))
237 e0ef727d 2021-05-02 rnkn (side-hustle-insert item level)))
238 e0ef727d 2021-05-02 rnkn imenu-items))
239 e0ef727d 2021-05-02 rnkn
240 e0ef727d 2021-05-02 rnkn (defun side-hustle-refresh ()
241 671d86ce 2021-05-08 rnkn "Rebuild and insert `imenu' entries for source buffer."
242 e0ef727d 2021-05-02 rnkn (interactive)
243 e0ef727d 2021-05-02 rnkn (with-silent-modifications
244 e0ef727d 2021-05-02 rnkn (let ((x (point))
245 e0ef727d 2021-05-02 rnkn imenu-items)
246 e0ef727d 2021-05-02 rnkn (when (buffer-live-p side-hustle--source-buffer)
247 e0ef727d 2021-05-02 rnkn (setq imenu-items
248 e0ef727d 2021-05-02 rnkn (with-current-buffer side-hustle--source-buffer
249 e0ef727d 2021-05-02 rnkn (setq imenu--index-alist nil)
250 e0ef727d 2021-05-02 rnkn (imenu--make-index-alist t)
251 e0ef727d 2021-05-02 rnkn imenu--index-alist)))
252 e0ef727d 2021-05-02 rnkn (erase-buffer)
253 ca7a6d66 2021-05-07 rnkn (setq header-line-format (buffer-name side-hustle--source-buffer))
254 4b23f0ff 2021-05-08 rnkn (setq tab-width side-hustle-indent-width)
255 e0ef727d 2021-05-02 rnkn (when imenu-items (side-hustle-insert-items imenu-items 0))
256 81ac148f 2021-05-29 rnkn (goto-char (point-min))
257 81ac148f 2021-05-29 rnkn (let (button)
258 81ac148f 2021-05-29 rnkn (while (setq button (forward-button 1 nil nil t))
259 81ac148f 2021-05-29 rnkn (when (member (button-label button) side-hustle--hidden)
260 81ac148f 2021-05-29 rnkn (side-hustle-button-action button t))))
261 e0ef727d 2021-05-02 rnkn (goto-char x))))
262 e0ef727d 2021-05-02 rnkn
263 7b974ef3 2021-05-08 rnkn (defun side-hustle-find-existing (sourcebuf)
264 7b974ef3 2021-05-08 rnkn "Return existing `side-hustle' buffer for SOURCEBUF or nil if none."
265 e0ef727d 2021-05-02 rnkn (seq-find
266 e0ef727d 2021-05-02 rnkn (lambda (buf)
267 e0ef727d 2021-05-02 rnkn (with-current-buffer buf
268 7b974ef3 2021-05-08 rnkn (eq side-hustle--source-buffer sourcebuf)))
269 e0ef727d 2021-05-02 rnkn (buffer-list)))
270 e0ef727d 2021-05-02 rnkn
271 7b974ef3 2021-05-08 rnkn (defun side-hustle-get-buffer-create (sourcebuf)
272 7b974ef3 2021-05-08 rnkn "Get or create `side-hustle' buffer for SOURCEBUF."
273 7b974ef3 2021-05-08 rnkn (or (side-hustle-find-existing sourcebuf)
274 e0ef727d 2021-05-02 rnkn (let ((new-buf (get-buffer-create
275 7b974ef3 2021-05-08 rnkn (concat "Side-Hustle: " (buffer-name sourcebuf)))))
276 e0ef727d 2021-05-02 rnkn (with-current-buffer new-buf
277 e0ef727d 2021-05-02 rnkn (side-hustle-mode)
278 81ac148f 2021-05-29 rnkn (setq side-hustle--source-buffer sourcebuf)
279 81ac148f 2021-05-29 rnkn (setq side-hustle--hidden nil))
280 e0ef727d 2021-05-02 rnkn new-buf)))
281 e0ef727d 2021-05-02 rnkn
282 53e4b47f 2021-06-22 rnkn ;; (defun side-hustle-highlight-current ()
283 53e4b47f 2021-06-22 rnkn ;; "Highlight the current `imenu' item in `side-hustle'.
284 53e4b47f 2021-06-22 rnkn ;; Added to `window-configuration-change-hook'."
285 53e4b47f 2021-06-22 rnkn ;; (unless (or (minibuffer-window-active-p (selected-window))
286 53e4b47f 2021-06-22 rnkn ;; (eq major-mode 'side-hustle-mode))
287 53e4b47f 2021-06-22 rnkn ;; (let ((x (point))
288 53e4b47f 2021-06-22 rnkn ;; (buf (side-hustle-find-existing (current-buffer)))
289 53e4b47f 2021-06-22 rnkn ;; candidate diff)
290 53e4b47f 2021-06-22 rnkn ;; (when (and buf (window-live-p (get-buffer-window buf)))
291 53e4b47f 2021-06-22 rnkn ;; (with-current-buffer buf
292 53e4b47f 2021-06-22 rnkn ;; (with-silent-modifications
293 53e4b47f 2021-06-22 rnkn ;; (remove-text-properties (point-min) (point-max) '(face))
294 53e4b47f 2021-06-22 rnkn ;; (goto-char (point-min))
295 53e4b47f 2021-06-22 rnkn ;; (while (< (point) (point-max))
296 53e4b47f 2021-06-22 rnkn ;; (let ((marker (get-text-property (point) 'side-hustle-imenu-marker)))
297 53e4b47f 2021-06-22 rnkn ;; (when (and (markerp marker)
298 53e4b47f 2021-06-22 rnkn ;; (<= (marker-position marker) x)
299 53e4b47f 2021-06-22 rnkn ;; (or (null diff) (< (- x (marker-position marker)) diff)))
300 53e4b47f 2021-06-22 rnkn ;; (setq candidate (point)
301 53e4b47f 2021-06-22 rnkn ;; diff (if diff
302 53e4b47f 2021-06-22 rnkn ;; (min diff (- x (marker-position marker)))
303 53e4b47f 2021-06-22 rnkn ;; (- x (marker-position marker))))))
304 53e4b47f 2021-06-22 rnkn ;; (forward-line 1))
305 53e4b47f 2021-06-22 rnkn ;; (when candidate
306 53e4b47f 2021-06-22 rnkn ;; (goto-char candidate)
307 53e4b47f 2021-06-22 rnkn ;; (put-text-property (line-beginning-position 1)
308 53e4b47f 2021-06-22 rnkn ;; (line-beginning-position 2)
309 53e4b47f 2021-06-22 rnkn ;; 'face 'side-hustle-highlight))))))))
310 e0ef727d 2021-05-02 rnkn
311 e0ef727d 2021-05-02 rnkn
312 03fee7f6 2021-05-29 rnkn ;;; Commands ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
313 e0ef727d 2021-05-02 rnkn
314 99ba3bdd 2021-05-08 rnkn (defun side-hustle-goto-item ()
315 99ba3bdd 2021-05-08 rnkn "Go to the `imenu' item at point in other window."
316 99ba3bdd 2021-05-08 rnkn (interactive)
317 99ba3bdd 2021-05-08 rnkn (when (side-hustle-button-ensure)
318 99ba3bdd 2021-05-08 rnkn (side-hustle-button-action (button-at (point)))))
319 99ba3bdd 2021-05-08 rnkn
320 e0ef727d 2021-05-02 rnkn (defun side-hustle-show-item ()
321 e0ef727d 2021-05-02 rnkn "Display the `imenu' item at point in other window."
322 e0ef727d 2021-05-02 rnkn (interactive)
323 e816a98b 2021-05-08 rnkn (when (side-hustle-button-ensure)
324 81ac148f 2021-05-29 rnkn (side-hustle-button-action (button-at (point)) nil t)))
325 e0ef727d 2021-05-02 rnkn
326 e0ef727d 2021-05-02 rnkn ;;;###autoload
327 e0ef727d 2021-05-02 rnkn (defun side-hustle-toggle ()
328 e0ef727d 2021-05-02 rnkn "Pop up a side window containing `side-hustle'."
329 e0ef727d 2021-05-02 rnkn (interactive)
330 e0ef727d 2021-05-02 rnkn (if (eq major-mode 'side-hustle-mode)
331 e0ef727d 2021-05-02 rnkn (quit-window)
332 e0ef727d 2021-05-02 rnkn (let ((display-buffer-mark-dedicated t)
333 e0ef727d 2021-05-02 rnkn (buf (side-hustle-get-buffer-create (current-buffer))))
334 e0ef727d 2021-05-02 rnkn (if (get-buffer-window buf (selected-frame))
335 e0ef727d 2021-05-02 rnkn (delete-windows-on buf (selected-frame))
336 e0ef727d 2021-05-02 rnkn (display-buffer-in-side-window
337 e0ef727d 2021-05-02 rnkn buf (append side-hustle-display-alist
338 e0ef727d 2021-05-02 rnkn (when side-hustle-persistent-window
339 e0ef727d 2021-05-02 rnkn (list '(window-parameters
340 e0ef727d 2021-05-02 rnkn (no-delete-other-windows . t))))))
341 e0ef727d 2021-05-02 rnkn (with-current-buffer buf (side-hustle-refresh))
342 be4768f9 2021-05-07 rnkn ;; (side-hustle-highlight-current)
343 e0ef727d 2021-05-02 rnkn (when side-hustle-select-window
344 e0ef727d 2021-05-02 rnkn (select-window (get-buffer-window buf (selected-frame))))))))
345 e0ef727d 2021-05-02 rnkn
346 02e81425 2021-06-21 rnkn ;; (defalias 'toggle-side-hustle #'side-hustle-toggle)
347 e0ef727d 2021-05-02 rnkn
348 e0ef727d 2021-05-02 rnkn
349 03fee7f6 2021-05-29 rnkn ;;; Mode Definition ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
350 e0ef727d 2021-05-02 rnkn
351 e0ef727d 2021-05-02 rnkn (defvar side-hustle-mode-map
352 e0ef727d 2021-05-02 rnkn (let ((map (make-sparse-keymap)))
353 e0ef727d 2021-05-02 rnkn (define-key map (kbd "p") #'previous-line)
354 e0ef727d 2021-05-02 rnkn (define-key map (kbd "n") #'next-line)
355 e0ef727d 2021-05-02 rnkn (define-key map (kbd "q") #'quit-window)
356 e0ef727d 2021-05-02 rnkn (define-key map (kbd "g") #'side-hustle-refresh)
357 99ba3bdd 2021-05-08 rnkn (define-key map (kbd "RET") #'side-hustle-goto-item)
358 e0ef727d 2021-05-02 rnkn (define-key map (kbd "SPC") #'side-hustle-show-item)
359 a88720b9 2021-05-07 rnkn (define-key map (kbd "TAB") #'forward-button)
360 a88720b9 2021-05-07 rnkn (define-key map (kbd "S-TAB") #'backward-button)
361 a88720b9 2021-05-07 rnkn (define-key map (kbd "<backtab>") #'backward-button)
362 e0ef727d 2021-05-02 rnkn map))
363 e0ef727d 2021-05-02 rnkn
364 e0ef727d 2021-05-02 rnkn (define-derived-mode side-hustle-mode
365 e0ef727d 2021-05-02 rnkn special-mode "Side-Hustle"
366 671d86ce 2021-05-08 rnkn "Major mode to navigate `imenu' via a side window.
367 671d86ce 2021-05-08 rnkn
368 671d86ce 2021-05-08 rnkn You should not activate this mode directly, rather, call
369 671d86ce 2021-05-08 rnkn `side-hustle-toggle' \\[side-hustle-toggle] in a source buffer."
370 d4dd6c5a 2021-05-08 rnkn (add-to-invisibility-spec '(hustle-invisible . t)))
371 e0ef727d 2021-05-02 rnkn
372 e0ef727d 2021-05-02 rnkn
373 e0ef727d 2021-05-02 rnkn
374 e0ef727d 2021-05-02 rnkn (provide 'side-hustle)
375 e0ef727d 2021-05-02 rnkn ;;; side-hustle.el ends here
376 e0ef727d 2021-05-02 rnkn
377 e0ef727d 2021-05-02 rnkn ;; Local Variables:
378 e0ef727d 2021-05-02 rnkn ;; coding: utf-8-unix
379 e0ef727d 2021-05-02 rnkn ;; fill-column: 80
380 e0ef727d 2021-05-02 rnkn ;; require-final-newline: t
381 e0ef727d 2021-05-02 rnkn ;; sentence-end-double-space: nil
382 e0ef727d 2021-05-02 rnkn ;; indent-tabs-mode: nil
383 e0ef727d 2021-05-02 rnkn ;; End: