File: matlab-sections.el

package info (click to toggle)
matlab-mode 6.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 900 kB
  • sloc: lisp: 10,932; sh: 5; makefile: 5
file content (408 lines) | stat: -rw-r--r-- 15,540 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
;;; matlab-sections.el --- Support for code sections in matlab mode -*- lexical-binding: t -*-

;; Copyright (C) 2024 Free Software Foundation, Inc.


;; Author: Nidish Narayanaa Balaji <nidbid@gmail.com>
;; Created: 2024-05-14
;; Renamed: 2024-10-22
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
;;
;;; Commentary:
;;
;; NOTE: What is referred to as "sections" herein were previously
;; referred to as code cells.
;;
;; This creates a minor mode called `matlab-sections-mode' that adds
;; utilities for working with code sections in matlab code.  The basic mechanic
;; is to redefine the page-delimiter (locally) to any line that starts
;; with "%%" as the first non-empty characters followed by some
;; comment strings.
;; Consequently, the line that is detected in the above manner is
;; highlighted by the face `matlab-sections-section-break-face'.  By default,
;; this is bold-faced and has an overline above it.
;;
;; The section that point is on is highlighted by the face
;; `matlab-sections-highlight-face'.  By default this is set to
;; "extra-bold".  The section-highlight can be toggled using
;; `matlab-sections-highlight-section' (defaults to "t").
;;
;; Another variable, `matlab-sections-sticky-flag' is defined, that
;; defines whether the current section is highlighted even when point
;; moves to another window (defaults to "t").
;;
;; Finally, the minor-mode provides the following interactive
;; navigation functions (default keybindings provided within []):
;; 1. `matlab-sections-forward-section' : Move point to the beginning of the
;;    section right below.  [C-s-<down>]
;; 2. `matlab-sections-backward-section' : Move point to the end of the section
;;    right above.  [C-s-<up>]
;; 3. `matlab-sections-beginning-of-section' : Move point to beginning of
;;    current section.  Return (point).  [C-s-<left>]
;; 4. `matlab-sections-end-of-section' : Move point to end of current section.
;;    Return (point).  [C-s-<right>]
;; 5. `matlab-sections-move-section-up' : Move the contents of the current section
;;    \"up\", so that it occurs before the previous.  [s-<up>]
;; 6. `matlab-sections-move-section-down' : Move the contents of the current
;;    section \"down\", so that it occurs after the next.  [s-<down>]
;; 7. `matlab-sections-run-till-point' : Run all the sections from beginning
;;    till previous section.  [s-<return>]
;; 8. `matlab-sections-mark-section' : Mark the current section.  [s-c]
;; (Note that some default keybindings may clash with existing
;; keybindings in the desktop environment)
;;
;; Other than this, there are some utility functions to help
;; development.
;;
;; Major parts of the code are modified from python-cell.el by Thomas
;; Hisch (currently at: https://github.com/twmr/python-cell.el).
;;
;;; Code:

(require 'subr-x)

;;
;; Customizable Variables and Faces
(defgroup matlab-sections nil
  "MATLAB-GUI-like sections in matlab-mode."
  :group 'matlab)

(defface matlab-sections-highlight-face
  '((t :weight extra-bold))
  "Default face for highlighting the current section in matlab-sections minor mode."
  :group 'matlab-sections)

(defface matlab-sections-section-break-face
  '((t :weight bold :overline t))
  "Default face for the section separation line in matlab-sections minor mode."
  :group 'matlab-sections)

(defcustom matlab-sections-highlight-section t
  "Non-nil tells matlab-sections mode to highlight the current section."
  :type 'boolean
  :group 'matlab-sections
  :safe 'booleanp)

(defcustom matlab-sections-section-break-regexp
  (rx line-start (* space)
      (group "%%" (* (not (any "\n"))) line-end))
  "Regexp used for detecting the section boundaries of code sections."
  :type 'string
  :group 'matlab-sections
  :safe 'stringp)

(defvar matlab-sections-mode)

(defvar matlab-sections-overlay nil
  "Overlay used by matlab-sections mode to highlight the current section.")
(make-variable-buffer-local 'matlab-sections-overlay)

(defcustom matlab-sections-highlight-face 'matlab-sections-highlight-face
  "Face with which to highlight the current section in matlab-sections mode."
  :type 'face
  :group 'matlab-sections
  :set (lambda (symbol value)
	 (set symbol value)
	 (dolist (buffer (buffer-list))
	   (with-current-buffer buffer
	     (when matlab-sections-overlay
	       (overlay-put matlab-sections-overlay 'face matlab-sections-highlight-face))))))

(defcustom matlab-sections-sticky-flag t
  "Non-nil means the matlab-sections mode highlight appears in all windows.
Otherwise matlab-sections mode will highlight only in the selected
window.  Setting this variable takes effect the next time you use
the command `matlab-sections-mode' to turn matlab-sections mode on."
  :type 'boolean
  :group 'matlab-sections)

;; Function to obtain range of current section

(defun matlab-sections-range-function ()
  "Function to call to return highlight range.
The function of no args should return a cons section; its car value
is the beginning position of highlight and its cdr value is the
end position of highlight in the buffer.
It should return nil if there's no region to be highlighted."
  (save-match-data
    (let ((r-start (save-excursion
		     (progn (end-of-line)
			    (if (re-search-backward matlab-sections-section-break-regexp nil t)
				(progn (goto-char (match-beginning 0))
				       (point))
			      (point-min)))))
	  (r-end (save-excursion
		   (progn (end-of-line)
			  (if (re-search-forward matlab-sections-section-break-regexp nil t)
			      (progn (goto-char (match-beginning 0))
				     (point))
			    (point-max))))))
      (progn
	;; (message "cp is %s start is %s; end is %s" (point) r-start r-end)
	(if (and (eq r-start (point-min)) (eq r-end (point-max)))
	    nil
	  `(,r-start . ,r-end))))))

;; Navigation

(defun matlab-sections-move-section-up (&optional arg)
  "Move the contents of the current section up.
Optionally a prefix argument ARG can be provided for repeating it a
bunch of times."
  (interactive "p")

  (dotimes (_ (or arg 1))
    (let ((rngc (matlab-sections-range-function))
	  (rngp (save-excursion (matlab-sections-backward-section)
				(matlab-sections-range-function))))
      (goto-char (car rngp))
      (kill-region (car rngc) (cdr rngc))
      (yank)
      (matlab-sections-backward-section)))
  )

(defun matlab-sections-move-section-down (&optional arg)
  "Move the contents of the current section down.
Optionally a prefix argument ARG can be provided for repeating it a
  bunch of times."
  (interactive "p")

  (dotimes (_ (or arg 1))
    (let ((rngc (matlab-sections-range-function))
	  (rngn (save-excursion (matlab-sections-forward-section)
				(matlab-sections-range-function))))
      (goto-char (cdr rngn))
      (kill-region (car rngc) (cdr rngc))
      (yank)
      (forward-char -1)
      (matlab-sections-beginning-of-section)))
  )

(defun matlab-sections-forward-section  (&optional arg aggressive)
  "Move point forward by a section.
Optionally provide prefix argument ARG to move by that many sections.
Optionally provide argument AGGRESSIVE to specify whether to move
  aggressively to next section or just move to end of current section if
  next section is not visible."
  (interactive "p")

  (dotimes (_ (or arg 1))
    (let ((endp (save-excursion (matlab-sections-end-of-section))))
      (if (and (not (eq (point) endp))
	       (not (pos-visible-in-window-p endp))
	       (not aggressive))
	  (goto-char endp)
	(goto-char endp)
    (if (re-search-forward matlab-sections-section-break-regexp nil t)
	(progn (end-of-line)
	       (forward-char 1))
      (goto-char (point-max)))
    )))
  )

(defun matlab-sections-backward-section  (&optional arg aggressive)
  "Move point backwards by a section.
Optionally provide prefix argument ARG to move by that many sections.
Optionally provide argument AGGRESSIVE to specify whether to move
  aggressively to previous section or just move to beginning of current
  section if previous section is not visible."
  (interactive "p")

  (dotimes (_ (or arg 1))
    (let ((begp (save-excursion (matlab-sections-beginning-of-section))))
      (if (and (not (eq (point) begp))
	       (not (pos-visible-in-window-p begp))
	       (not aggressive))
	  (goto-char begp)
	(goto-char begp)
	(forward-char -1)
	(beginning-of-line)
	(and (save-excursion (re-search-backward matlab-sections-section-break-regexp
						 nil t))
	     (= (match-beginning 0) (save-excursion
				      (forward-char -1) (beginning-of-line) (point)))
	     (goto-char (match-beginning 0)))

	(if (> (point) (point-min))
	    (forward-char -1))
	(if (re-search-backward matlab-sections-section-break-regexp nil t)
	    (progn (goto-char (match-end 0))
		   (end-of-line)
		   (forward-char 1))
	  (goto-char (point-min)))
	)))
  )

(defun matlab-sections-beginning-of-section ()
  "Move point to beginning of section."
  (interactive)

  (end-of-line)
  (if (re-search-backward matlab-sections-section-break-regexp nil t)
      (progn (goto-char (match-end 0))
	     (end-of-line)
	     (forward-char 1))
    (goto-char (point-min)))
  (point)
  )

(defun matlab-sections-end-of-section ()
  "Move point to end of section."
  (interactive)

  (end-of-line)
  (if (re-search-forward matlab-sections-section-break-regexp nil t)
      (progn (goto-char (match-beginning 0))
	     (forward-char -1))
    (goto-char (point-max)))
  (point)
  )

(defun matlab-sections-mark-section ()
  "Mark the contents of the current section.  Replace `mark-page'."
  (interactive)
  (let ((rng (matlab-sections-range-function)))
    (set-mark (car rng))
    (goto-char (cdr rng))
    )
  )

;; Execution

(declare-function matlab-shell-run-region "matlab-shell.el")

(defun matlab-sections-shell-run-section ()
  "Run the section point is in, in matlab-shell."
  (interactive)
  (let ((rng (matlab-sections-range-function)))
    (matlab-shell-run-region (car rng) (cdr rng)))
  )

(defun matlab-sections-run-till-point ()
  "Run all sections until point, not including the section point is in."
  (interactive)
  (let ((pt (point)))
    (save-excursion
      (save-restriction
	(widen)
	(goto-char (point-min))
	(while (>= pt (point))
	  (save-window-excursion (matlab-sections-shell-run-section))
	  (matlab-sections-forward-section)
	  (matlab-sections-end-of-section))))))

;;; Section Highlighting

(defun matlab-sections-highlight ()
  "Activate the matlab-sections overlay on the current line."
  (if matlab-sections-mode  ; Might be changed outside the mode function.
      (progn
	(unless matlab-sections-overlay
	  (setq matlab-sections-overlay (make-overlay 1 1)) ; to be moved
	  (overlay-put matlab-sections-overlay 'face matlab-sections-highlight-face))
	(overlay-put matlab-sections-overlay
		     'window (unless matlab-sections-sticky-flag (selected-window)))
	(matlab-sections-move-overlay matlab-sections-overlay))
    (matlab-sections-unhighlight)))

(defun matlab-sections-unhighlight ()
  "Deactivate the matlab-sections overlay on the current line."
  (when matlab-sections-overlay
    (delete-overlay matlab-sections-overlay)))

(defun matlab-sections-move-overlay (overlay)
  "Move the matlab-sections overlay given as OVERLAY."
  (if-let ((start-end (matlab-sections-range-function)))
      (move-overlay overlay (car start-end) (cdr start-end))
    (move-overlay overlay 1 1)))

(defun matlab-sections-setup-section-highlight ()
  "Setup section highlighting."
  ;; In case `kill-all-local-variables' is called.
  (add-hook 'change-major-mode-hook #'matlab-sections-unhighlight nil t)
  (if matlab-sections-sticky-flag
      (remove-hook 'pre-command-hook #'matlab-sections-unhighlight t)
    (add-hook 'pre-command-hook #'matlab-sections-unhighlight nil t))
  (matlab-sections-highlight)
  (add-hook 'post-command-hook #'matlab-sections-highlight nil t))

;;; Keymap
(defvar matlab-sections-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-s-<down>") #'matlab-sections-forward-section)
    (define-key map (kbd "C-s-<up>") #'matlab-sections-backward-section)
    (define-key map (kbd "C-s-<left>") #'matlab-sections-beginning-of-section)
    (define-key map (kbd "C-s-<right>") #'matlab-sections-end-of-section)
    (define-key map (kbd "s-<up>") #'matlab-sections-move-section-up)
    (define-key map (kbd "s-<down>") #'matlab-sections-move-section-down)
    (define-key map (kbd "s-<return>") #'matlab-sections-run-till-point)
    (define-key map (kbd "s-c") #'matlab-sections-mark-section)
    map)
  "Key map for matlab-sections minor mode.")

(defalias 'matlab-sections-what-section #'what-page)
(defalias 'matlab-sections-narrow-to-section #'narrow-to-page)

;;; Minor mode:

;;;###autoload
(define-minor-mode matlab-sections-mode
  "Highlight MATLAB-like sections and navigate between them.
The minor-mode provides the following interactive navigation
functions.  The default keybindings are provided in square brackets for
each:
1. `matlab-sections-forward-section' : Move point to the beginning of the
   section right below.  \\[matlab-sections-forward-section]
2. `matlab-sections-backward-section' : Move point to the end of the section
   right above.  \\[matlab-sections-backward-section]
3. `matlab-sections-beginning-of-section' : Move point to beginning of
   current section.  Return (point).  \\[matlab-sections-beginning-of-section]
4. `matlab-sections-end-of-section' : Move point to end of current section.
   Return (point).  \\[matlab-sections-end-of-section]
5. `matlab-sections-move-section-up' : Move the contents of the current section
   \"up\", so that it occurs before the previous.  \\[matlab-sections-move-section-up]
6. `matlab-sections-move-section-down' : Move the contents of the current
   section \"down\", so that it occurs after the next.  \\[matlab-sections-move-section-down]
7. `matlab-sections-run-till-point' : Run all the sections from beginning
   till previous section.  \\[matlab-sections-run-till-point]
8. `matlab-sections-mark-section' : Mark the current section.  \\[matlab-sections-mark-section]"
  :init-value nil
  :keymap matlab-sections-mode-map

  ;; (let ((arg `((,matlab-sections-section-break-regexp 1 'matlab-sections-section-break-face prepend))))
  (make-local-variable 'page-delimiter)
  (setq page-delimiter matlab-sections-section-break-regexp)
  ;; (font-lock-add-keywords nil arg)
  (when matlab-sections-highlight-section
    (matlab-sections-setup-section-highlight))
  ;; (font-lock-flush))
  )

;;;###autoload
(defun matlab-sections-mode-enable ()
  "Enable matlab-sections-mode."
  (matlab-sections-mode 1))

;;;###autoload
(defun matlab-sections-mode-disable ()
  "Disable matlab-sections-mode."
  (matlab-sections-mode 0))

(provide 'matlab-sections)
;;; matlab-sections.el ends here

;; LocalWords:  Nidish Narayanaa Balaji nidbid gmail cellbreak Hisch subr defface defcustom booleanp
;; LocalWords:  stringp dolist defun cdr progn cp dotimes rngc rngp rngn endp begp setq Keymap
;; LocalWords:  keymap kbd defalias