/
malabar-groovy.el
executable file
·585 lines (512 loc) · 24.9 KB
/
malabar-groovy.el
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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
;;; malabar-groovy.el --- A better Java mode for Emacs
;;
;; Copyright (c) 2009, 2010 Espen Wiborg <espenhw@grumblesmurf.org>
;;
;; 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 2 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, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
;; 02110-1301 USA.
;;
(require 'comint)
(require 'ansi-color)
(require 'cl)
(require 'malabar-util)
(require 'fringe-helper)
(defgroup malabar-groovy nil
"Customization of malabar-mode's inferior Groovy."
:group 'malabar-mode)
(defvar malabar-groovy-comint-name "Malabar Groovy")
(defvar malabar-groovy-compile-server-comint-name "Malabar Compile Server")
(defvar malabar-groovy-eval-server-comint-name "Malabar Eval Server")
(defvar malabar-groovy-compilation-buffer-name "*Malabar Compilation*")
(get-buffer-create malabar-groovy-compilation-buffer-name)
(defvar malabar-groovy-time-out-waiting-for-groovysh 10)
(defvar malabar-groovy-buffer-name
(concat "*" malabar-groovy-comint-name "*"))
(defvar malabar-groovy-compile-server-buffer-name
(concat "*" malabar-groovy-compile-server-comint-name "*"))
(defvar malabar-groovy-eval-server-buffer-name
(concat "*" malabar-groovy-eval-server-comint-name "*"))
(defcustom malabar-groovy-java-command "java"
"The command to invoke Java. Include the full path if
necessary."
:group 'malabar-groovy
:type 'string)
(defcustom malabar-groovy-server-class "org.grumblesmurf.malabar.GroovyServer"
"The class name of the Malabar Groovy server. Don't touch
unless you know what you're doing."
:group 'malabar-groovy
:type 'string)
(defcustom malabar-groovy-lib-dir
(file-name-as-directory (expand-file-name "lib"
(file-name-directory load-file-name)))
"The location of all Malabar's JARs."
:group 'malabar-groovy
:type 'directory)
(defcustom malabar-groovy-extra-classpath '("~/src/malabar/target/classes")
"Extra classpath elements to pass to groovysh (mainly useful
for hacking on Malabar itself)."
:group 'malabar-groovy
:type '(repeat (choice directory file)))
(defcustom malabar-groovy-mode-hook '()
"Hook that gets called when entering malabar-groovy-mode."
:group 'malabar-groovy
:type 'hook)
(defcustom malabar-groovy-prompt-regexp "^groovy:[^>]*> "
"Regexp to recognize the groovysh prompt."
:group 'malabar-groovy
:type 'regexp)
(defcustom malabar-groovy-initial-statements
'("import org.grumblesmurf.malabar.*"
"import java.lang.reflect.*")
"Statements to execute immediately after starting groovysh."
:group 'malabar-groovy
:type '(repeat string))
(defcustom malabar-groovy-compile-server-port 0
"The port on which the Groovy compile server should listen.
By default this variable has a value of 0, which means the system will
pick an available port, whose number will be stored back in this
variable once the compile server has started."
:group 'malabar-groovy
:type 'integer)
(defcustom malabar-groovy-eval-server-port 0
"The port on which the Groovy eval server should listen.
By default this variable has a value of 0, which means the system will
pick an available port, whose number will be stored back in this
variable once the eval server has started."
:group 'malabar-groovy
:type 'integer)
(defcustom malabar-groovy-java-options nil
"Extra options to pass to Java."
:group 'malabar-groovy
:type '(repeat string))
(defvar malabar-groovy--last-compilation-command nil)
(defun malabar-groovy-mode ()
"A major mode for the Groovy console."
(interactive)
(delay-mode-hooks (comint-mode))
;;(ansi-color-for-comint-mode-on)
;; Set prompt regexp
(setq comint-prompt-regexp malabar-groovy-prompt-regexp)
(setq comint-process-echoes t)
(setq major-mode 'malabar-groovy-mode)
(setq mode-name malabar-groovy-comint-name)
(setq mode-line-process '(":%s"))
;; Set keymap?
;; set comint-input-filter
(run-mode-hooks 'malabar-groovy-mode-hook))
(defun malabar-groovy--check-groovysh-time-out (start-time time-out-msg)
(when (> (- (float-time) start-time) malabar-groovy-time-out-waiting-for-groovysh)
(error time-out-msg)))
(defun malabar-groovy--match-buffer (buffer regexp count initial-points-alist time-out-msg)
(let ((start-time (float-time)))
(while (not (with-current-buffer buffer
(save-excursion
(goto-char (point-max))
(re-search-backward regexp
(cdr (assoc buffer initial-points-alist)) t))))
(accept-process-output (get-buffer-process buffer) malabar-groovy-time-out-waiting-for-groovysh)
(malabar-groovy--check-groovysh-time-out start-time time-out-msg)))
(with-current-buffer buffer (match-string-no-properties count)))
(defun malabar-groovy--wait-for-prompt (buffer initial-points-alist)
(malabar-groovy--match-buffer buffer malabar-groovy-prompt-regexp 0 initial-points-alist
"Error starting groovy: time-out waiting for prompt"))
(defun malabar-groovy--get-server-ports-from-buffer (buffer initial-points-alist)
(let ((time-out-msg "Error starting groovy: time-out waiting for port"))
(setq malabar-groovy-compile-server-port
(string-to-number
(malabar-groovy--match-buffer buffer "compile-server: port=\\([0-9]*\\)" 1 initial-points-alist time-out-msg)))
(setq malabar-groovy-eval-server-port
(string-to-number
(malabar-groovy--match-buffer buffer "eval-server: port=\\([0-9]*\\)" 1 initial-points-alist time-out-msg)))))
(defun malabar-groovy-stop ()
"Stop the inferior Groovy."
(interactive)
(let ((start-time (float-time)) (groovy-process (get-buffer-process malabar-groovy-buffer-name)))
(malabar-groovy-eval-in-process groovy-process "exit")
(while (malabar-groovy-live-p)
(sit-for 0.1)
(malabar-groovy--check-groovysh-time-out start-time "Time-out waiting for Groovy to stop.")))
(message nil))
(defun malabar-groovy-make-class-path ( &optional classpath-pom-dir)
"Return the classpath to be used by malabar groovy. It loads the function
from malabar-mode-config-classpath-file if it exists. If not, it attempts to generate
it with an external call to maven."
(when (not (file-exists-p malabar-mode-config-classpath-file))
(save-current-buffer
(set-buffer (generate-new-buffer "*malabar-groovy-classpath-gen*"))
(setq default-directory (or classpath-pom-dir malabar-install-directory))
(call-process "mvn" nil t nil "-X" "-U" "-f" "pom/classpath.pom" "dependency:build-classpath")))
(concat
(mapconcat malabar-util-path-filter
(mapcar #'expand-file-name malabar-groovy-extra-classpath)
malabar-util-path-separator)
malabar-util-path-separator
(with-temp-buffer
(insert-file-contents (malabar-util-expand-file-name malabar-mode-config-classpath-file))
(buffer-string))))
(defun malabar-groovy-start (&optional silent)
"Start Groovy and wait for it to come up. If SILENT is NIL,
pop to the Groovy console buffer."
(interactive)
(unless (malabar-groovy-live-p)
(let ((reporter (make-progress-reporter "Starting Groovy... " 0 7)))
(let ((initial-points-alist (mapcar (lambda (b)
(with-current-buffer (get-buffer-create b)
(cons b (point))))
(list malabar-groovy-buffer-name
malabar-groovy-compile-server-buffer-name
malabar-groovy-eval-server-buffer-name))))
(progress-reporter-force-update reporter 1 "Starting Groovy...starting process ")
(with-current-buffer (get-buffer malabar-groovy-buffer-name)
(unless silent
(display-buffer malabar-groovy-buffer-name))
(let ((class-path (malabar-groovy-make-class-path)))
;;(message "%s" class-path)
(apply #'make-comint
malabar-groovy-comint-name
malabar-groovy-java-command
nil
"-cp"
class-path
(append malabar-groovy-java-options
(list malabar-groovy-server-class
"-c" (number-to-string malabar-groovy-compile-server-port)
"-e" (number-to-string malabar-groovy-eval-server-port))))
(malabar-groovy-mode)))
(progress-reporter-force-update reporter 2 "Starting Groovy...requesting ports ")
(malabar-groovy--get-server-ports-from-buffer malabar-groovy-buffer-name initial-points-alist)
(progress-reporter-force-update reporter 3 "Starting Groovy...waiting for main prompt ")
(malabar-groovy--wait-for-prompt malabar-groovy-buffer-name initial-points-alist)
(progress-reporter-force-update reporter 4 "Starting Groovy...connecting to servers ")
(make-comint malabar-groovy-compile-server-comint-name
(cons "localhost"
malabar-groovy-compile-server-port))
(make-comint malabar-groovy-eval-server-comint-name
(cons "localhost"
malabar-groovy-eval-server-port))
(progress-reporter-force-update reporter 5 "Starting Groovy...waiting for server prompts ")
(malabar-groovy--wait-for-prompt malabar-groovy-compile-server-buffer-name
initial-points-alist)
(malabar-groovy--wait-for-prompt malabar-groovy-eval-server-buffer-name
initial-points-alist)
(progress-reporter-force-update reporter 6 "Starting Groovy...evaluating initial statements ")
(dolist (process (list (get-buffer-process malabar-groovy-compile-server-buffer-name)
(get-buffer-process malabar-groovy-eval-server-buffer-name)
(get-buffer-process malabar-groovy-buffer-name)))
(dolist (stmt malabar-groovy-initial-statements)
(malabar-groovy-eval-in-process process stmt)))
(with-current-buffer malabar-groovy-compile-server-buffer-name
(malabar-groovy--init-compile-server-buffer))
(with-current-buffer malabar-groovy-eval-server-buffer-name
(malabar-groovy--init-eval-buffer))
(progress-reporter-force-update reporter 7 "Starting Groovy...")
(progress-reporter-done reporter))))
(unless silent
(pop-to-buffer malabar-groovy-buffer-name)))
(defun malabar-groovy-restart ()
"Stops Groovy (if running) and (re)starts Groovy and waits
for it to come up."
(interactive)
(when (malabar-groovy-live-p) (malabar-groovy-stop))
(malabar-groovy-start))
(defun malabar-groovy-eval-in-process (process string)
(let ((string (string-with-newline string))
(current-message (current-message)))
(unless current-message
(message "Invoking Groovy, please wait..."))
(comint-send-string process string)
(if current-message
(message "%s" current-message)
(message nil))))
(defun malabar-groovy-live-p ()
(comint-check-proc malabar-groovy-buffer-name))
(defvar malabar-groovy--eval-output (cons "" ""))
(defvar malabar-groovy--eval-buffer (get-buffer-create " *Malabar Groovy eval*"))
(defvar malabar-groovy--eval-callback nil)
(defun malabar-groovy--init-compile-server-buffer ()
(malabar-groovy-mode)
(add-hook 'comint-redirect-hook
'malabar-groovy--compile-handle-exit
nil t))
(defun malabar-groovy--init-eval-buffer ()
(malabar-groovy-mode)
(when (assq 'comint-output-filter-functions (buffer-local-variables))
;; HACK: There is no reliable way to remove a closure from this
;; list; just clear it, but only if it is already buffer-local
(setq comint-output-filter-functions nil))
(add-hook 'comint-output-filter-functions
(malabar-groovy--watch-for-prompt 'malabar-groovy--eval-callback
(current-buffer))
nil t))
(defun malabar-groovy--watch-for-prompt (hook buffer)
(lexical-let ((hook hook)
(buffer buffer))
(lambda (string)
(when (string-match malabar-groovy-prompt-regexp string)
(run-hook-with-args hook buffer)
(set hook nil)))))
(defun malabar-groovy--eval-get-output (buffer)
(setq malabar-groovy--eval-output
(with-current-buffer buffer
(cons (buffer-substring-no-properties
(save-excursion
(goto-char (point-max))
(re-search-backward malabar-groovy-prompt-regexp nil nil 2))
(point-max))
""))))
(defconst malabar-groovy--eval-log-output-marker-re
(concat "^" (regexp-opt (mapcar (lambda (level)
(concat "[" (symbol-name level) "]"))
'(DEBUG INFO WARN WARNING ERROR FATAL)))))
(defun malabar-groovy--eval-fix-output (cell)
(let* ((string (car cell))
(output (mapconcat
'identity
(cdr ;; Lose first...
(butlast ;; ...and last lines
(remove-if (lambda (s)
(string-match-p malabar-groovy--eval-log-output-marker-re
s))
(split-string (car cell) "\n"))))
"\n"))
(start-of-return (string-match "\n?===> " output)))
(cons (substring output 0 start-of-return)
(when start-of-return
(substring output (match-end 0) (1- (length output)))))))
(defun malabar-groovy-eval (string)
"Pass STRING to groovysh for evaluation."
(unless (malabar-groovy-live-p)
(malabar-groovy-start t))
(when (malabar-groovy-live-p)
(let ((groovy-process (get-buffer-process malabar-groovy-eval-server-buffer-name)))
(setq malabar-groovy--eval-callback 'malabar-groovy--eval-get-output)
(malabar-groovy-eval-in-process groovy-process string)
(while (not (string-match-p (regexp-quote string)
(car malabar-groovy--eval-output)))
(setq malabar-groovy--eval-callback 'malabar-groovy--eval-get-output)
(accept-process-output groovy-process))
(malabar-groovy--eval-fix-output malabar-groovy--eval-output))))
(defun malabar-groovy-eval-and-lispeval (string)
"Pass STRING to groovysh for evaluation, and read the output for Lisp use."
(car (read-from-string (car (malabar-groovy-eval string)))))
(defun malabar-eval-on-project (command &optional buffer)
"Execute COMMAND on the project associated with BUFFER. If BUFFER is nil, use `current-buffer`"
(interactive "sCommand:")
(let* ((buf (if buffer (get-buffer buffer) (current-buffer)))
(mbuf (malabar-project buf))
(cmd (format "Utils.printAsLisp(%s.%s)" mbuf command))
(rtnval (malabar-groovy-eval cmd))
)
rtnval))
(defun malabar-groovy-run-main (&optional class &rest args)
"Run the main method of the given class. `class` is the fully qualified class name and `args` is the strings to pass to main.
TODO: args is basically ignored
TODO: capture stdout and stderr"
(interactive)
(let ((class (or class (malabar-get-fully-qualified-class-name (current-buffer))))
(args (or args "new String[0]")))
(malabar-eval-on-project (format "runMain('%s', %s)" class args))))
(defcustom malabar-groovy-compilation-font-lock-keywords
'((malabar-groovy-highlight-compilation-message
(1 '(face nil invisible t) nil t) ;
(2 '(face nil invisible t) nil t) ; hide the class
(3 '(face nil invisible t) nil t) ;
(4 (compilation-face '(2 . 3)))
(5 compilation-line-face nil t)
(6 compilation-column-face nil t)
(7 '(face nil invisible t) nil t) ; hide the position info
(0 (compilation-error-properties 4 5 nil 6 nil '(2 . 3) nil)
append)))
"Font lock keywords for Malabar compilation."
:group 'malabar-mode
:type '(alist))
(defun malabar-groovy-setup-compilation-buffer (&optional for-files)
(with-current-buffer (get-buffer-create malabar-groovy-compilation-buffer-name)
(setq buffer-read-only nil)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(buffer-enable-undo (current-buffer))
(malabar-groovy-reset-compiler-notes for-files)
(setq mode-name "Compilation")
(malabar-compilation-minor-mode t)
(when for-files
;; Compiling a single file (or set of files), do magic
;; We do error message parsing slightly differently
(font-lock-remove-keywords nil (compilation-mode-font-lock-keywords))
(font-lock-add-keywords nil
(append
malabar-groovy-compilation-font-lock-keywords
compilation-mode-font-lock-keywords)
'set))
(setq buffer-read-only nil)))
(defvar malabar-compilation-minor-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "g" 'malabar-groovy-rerun-last-compilation)
(define-key map "n" 'next-error)
(define-key map "p" 'previous-error)
map))
(define-minor-mode malabar-compilation-minor-mode
nil nil nil malabar-compilation-minor-mode-map
(compilation-minor-mode malabar-compilation-minor-mode))
(defun malabar-groovy-rerun-last-compilation ()
(interactive)
(malabar-groovy-setup-compilation-buffer)
(malabar-groovy-eval-as-compilation malabar-groovy--last-compilation-command))
(defvar malabar-groovy--compiler-notes nil)
(defun malabar-groovy-reset-compiler-notes (&optional files)
"Remove all compiler notes from the given files, or all files if nil."
(interactive)
(let ((files-to-process (or files
(delete-duplicates
(mapcar (lambda (n)
(getf n :file))
malabar-groovy--compiler-notes)))))
(mapc (lambda (f)
(when-let (buf (get-file-buffer f))
(remove-overlays
(with-current-buffer buf
(mapc 'fringe-helper-remove malabar-groovy--fringe-overlays)
(remove-overlays (point-min) (point-max)
'malabar-compiler-note t)))))
files-to-process)
(setq malabar-groovy--compiler-notes
(remove-if (lambda (f)
(member f files-to-process))
malabar-groovy--compiler-notes
:key (lambda (n)
(getf n :file))))))
(defun malabar-groovy-highlight-compilation-message (limit)
;; CLASS::FILE::LINE::COLUMN::START::END::POS::Message
;; CLASS is either ERROR, MANDATORY_WARNING, WARNING, NOTE or OTHER
(when (re-search-forward "^\\(ERROR::\\)?\\(\\(?:MANDATORY_\\)?WARNING::\\)?\\(NOTE::\\|OTHER::\\)?\\(.*?\\)::\\(.*?\\)::\\(.*?\\)\\(::.*?::.*?::.*?\\)::" limit 'move)
(when malabar-compilation-project-file
(let ((file-start (match-beginning 4)))
(put-text-property file-start
(+ file-start
(length (file-name-directory
malabar-compilation-project-file)))
'invisible t)))
;; Hide some colons
(mapc (lambda (n)
(let ((end-n (match-end n)))
(when end-n
(put-text-property end-n (1+ end-n) 'invisible t))))
'(4 5 6 7))
(pushnew (list :class (cond ((match-beginning 1)
'error)
((match-beginning 2)
'warning)
((match-beginning 3)
'info))
:file (match-string-no-properties 4)
:message
(buffer-substring-no-properties (match-end 0)
(save-excursion
(end-of-line)
(point)))
:position-info
(when (match-string-no-properties 7)
(let ((positions (match-string-no-properties 7)))
(message "#%s" positions)
(mapcar #'1+
(car
(read-from-string
(concat "("
(replace-regexp-in-string "::" " " positions)
")")))))))
malabar-groovy--compiler-notes
:test #'equal)))
;; CLASS::FILE::LINE::COLUMN::START::END::POS::Message
(add-to-list 'compilation-error-regexp-alist '("ERROR::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)" 1 2))
(add-to-list 'compilation-error-regexp-alist '("WARNING::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)::\\(.+?\\)" 1 2 nil 1))
(defvar malabar-groovy--compilation-backlog nil)
(defun malabar-groovy-eval-as-compilation (string &optional silent)
"Passes STRING to groovysh for evaluation in the compile server."
(unless (malabar-groovy-live-p)
(malabar-groovy-start t))
(when (malabar-groovy-live-p)
(let* ((groovy-process (get-buffer-process malabar-groovy-compile-server-buffer-name))
(thunk (lexical-let ((string string)
(groovy-process groovy-process)
(silent silent))
(lambda ()
(setq compilation-in-progress
(cons groovy-process compilation-in-progress))
(setq malabar-groovy--last-compilation-command string)
(comint-redirect-send-command-to-process
string malabar-groovy-compilation-buffer-name
groovy-process nil silent)))))
(if (memq groovy-process compilation-in-progress)
(add-to-list 'malabar-groovy--compilation-backlog thunk t)
(funcall thunk)))))
(defun malabar-groovy--process-backlog (buffer message)
(message "%s" message)
(when (and (equal (buffer-name buffer) malabar-groovy-compilation-buffer-name)
malabar-groovy--compilation-backlog)
(if (equal message "finished\n")
(funcall (pop malabar-groovy--compilation-backlog))
(message "Compilation failed, clearing backlog")
(setq malabar-groovy--compilation-backlog nil))))
(add-hook 'compilation-finish-functions 'malabar-groovy--process-backlog)
(defun malabar-groovy--compile-handle-exit ()
(with-current-buffer malabar-groovy-compilation-buffer-name
(let ((result (progn (goto-char (point-max))
(re-search-backward "===> \\(.*\\)$")
(match-string-no-properties 1))))
(replace-match "" t t)
(setq compilation-in-progress
(delq (get-buffer-process malabar-groovy-compile-server-buffer-name)
compilation-in-progress))
(apply #'compilation-handle-exit 'exit
(if (equal (string-trim result) "true")
(list 0 "finished\n")
(list 1 "exited abnormally"))))
(mapcar #'malabar-groovy--add-compiler-annotation
malabar-groovy--compiler-notes)))
(defface malabar-error-face '((t (:underline "red")))
"Face used in code buffer for error annotations.")
(defface malabar-warning-face '((t (:underline "orange")))
"Face used in code buffer for warning annotations.")
(defface malabar-info-face '((t (:underline "blue")))
"Face used in code buffer for info annotations.")
(defvar malabar-groovy--fringe-overlays nil)
(make-variable-buffer-local 'malabar-groovy--fringe-overlays)
(defun malabar-groovy--add-compiler-annotation (compiler-note)
(let ((file (getf compiler-note :file)))
(when (file-exists-p file)
(save-excursion
(with-current-buffer (find-file-noselect file)
(let* ((modified (buffer-modified-p))
(buffer-undo-list t)
(position-info (getf compiler-note :position-info))
(beg (first position-info))
(end (second position-info))
(class (getf compiler-note :class)))
(let ((overlay (make-overlay beg end nil nil t)))
(overlay-put overlay 'malabar-compiler-note t)
(overlay-put overlay 'face (case class
(error 'malabar-error-face)
(warning 'malabar-warning-face)
(info 'malabar-info-face)))
(overlay-put overlay 'help-echo (getf compiler-note :message)))
(when (display-graphic-p)
(push (fringe-helper-insert-region
beg end (fringe-lib-load (case class
(error fringe-lib-exclamation-mark)
(warning fringe-lib-question-mark)
(info fringe-lib-wave)))
'left-fringe (when (eq class 'error) 'font-lock-warning-face))
malabar-groovy--fringe-overlays))
(set-buffer-modified-p modified)))))))
(provide 'malabar-groovy)