1 ;;; mozadd.el --- Additional functionality for MozRepl
3 ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
4 ;; Created: 2009-07-22 Wed
5 (defconst mozadd:version "0.2") ;; Version:
6 ;; Last-Updated: 2009-08-04 Tue
11 ;; Features that might be required by this library:
13 ;; `cc-cmds', `cc-defs', `cc-engine', `cc-vars', `comint', `json',
14 ;; `moz', `regexp-opt', `ring'.
16 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 ;; Live tracking of editing changes, see
21 ;; `mozadd-mirror-mode'
22 ;; `mozadd-refresh-edited-on-save-mode'
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;; This program is free software; you can redistribute it and/or
32 ;; modify it under the terms of the GNU General Public License as
33 ;; published by the Free Software Foundation; either version 3, or
34 ;; (at your option) any later version.
36 ;; This program is distributed in the hope that it will be useful,
37 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
38 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 ;; General Public License for more details.
41 ;; You should have received a copy of the GNU General Public License
42 ;; along with this program; see the file COPYING. If not, write to
43 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
44 ;; Floor, Boston, MA 02110-1301, USA.
46 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
53 (defun mozadd-warning (format-string &rest args)
54 (let ((str (apply 'format format-string args)))
55 (message "%s" (propertize str 'face 'secondary-selection))))
57 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
58 ;;; Refresh Firefox after save etc
60 ;; Partly after an idea on EmacsWiki
62 (defvar mozadd-edited-buffer nil)
63 (setq mozadd-edited-buffer nil)
66 (define-minor-mode mozadd-refresh-edited-on-save-mode
67 "Refresh mozadd edited file in Firefox when saving file.
68 The mozadd edited file is the file in the last buffer visited in
71 You can use this for example when you edit CSS files.
73 The mozadd edited file must be shown in Firefox and visible."
75 (if mozadd-refresh-edited-on-save-mode
76 (add-hook 'after-save-hook 'mozadd-queue-reload-mozilla-edited-file nil t)
77 (remove-hook 'after-save-hook 'mozadd-queue-reload-mozilla-edited-file t)))
78 (put 'mozadd-refresh-edited-on-save-mode 'permanent-local t)
81 (define-globalized-minor-mode global-mozadd-refresh-edited-on-save-mode
82 mozadd-refresh-edited-on-save-mode
84 (when (or (derived-mode-p 'css-mode)
85 (mozadd-html-buffer-file-p))
86 (mozadd-refresh-edited-on-save-mode 1))))
88 (defun mozadd-queue-reload-mozilla-edited-file ()
90 (when (buffer-live-p mozadd-edited-buffer)
91 (if (buffer-modified-p mozadd-edited-buffer)
92 (mozadd-warning "Mozadd: Edited buffer %s is not saved, can't reload browser."
93 (buffer-name mozadd-edited-buffer))
94 (mozadd-add-queue-get-mirror-location)
95 (mozadd-add-task-1 'mozadd-send-refresh-edited-to-mozilla))))
97 (defun mozadd-send-refresh-edited-to-mozilla ()
98 "Update the remote mozrepl instance"
99 (with-current-buffer mozadd-edited-buffer
100 (if (not (mozadd-edited-file-is-shown))
101 (mozadd-warning "Mozadd: Edited buffer %s is not shown, can't reload browser."
102 (buffer-name mozadd-edited-buffer))
103 (comint-send-string (inferior-moz-process)
104 "setTimeout(BrowserReload(), \"1000\");")))
108 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
109 ;;; Mirror html buffer in Firefox
111 ;; Partly after an idea on
112 ;; http://people.internetconnection.net/2009/02/interactive-html-development-in-emacs/
114 ;; Fun, it kind of works, but is perhaps totally useless .... - slow
115 ;; and maybe scrolling... - but the file I am testing with have 3000
118 ;; Fix-me: How do you get the currently shown page in Firefox?
120 (defun mozadd-perhaps-start ()
121 "Start if MozRepl if not running. Return message if not ok."
122 (unless (buffer-live-p inferior-moz-buffer)
125 (inferior-moz-start-process)
127 (error (error-message-string err)))))
129 (defvar mozadd-mirror-location nil)
130 (make-variable-buffer-local 'mozadd-mirror-location)
131 (put 'mozadd-mirror-location 'permanent-local t)
133 (defvar mozadd-initial-mirror-location nil)
134 (make-variable-buffer-local 'mozadd-initial-mirror-location)
135 (put 'mozadd-initial-mirror-location 'permanent-local t)
137 ;;(mozadd-get-comint-string-part "\"hi\" there")
138 (defun mozadd-get-comint-string-part (comint-output)
140 (if (string-match "^\".*?\"" comint-output)
141 (match-string 0 comint-output)
144 (defun mozadd-get-initial-mirror-location (comint-output)
145 ;;(message "mozadd-get-initial-mirror-location %S" comint-output)
146 (with-current-buffer mozadd-edited-buffer
147 (setq mozadd-initial-mirror-location (mozadd-get-comint-string-part comint-output)))
151 (defun mozadd-get-mirror-location (comint-output)
152 ;;(message "mozadd-get-mirror-location %S" comint-output)
153 (with-current-buffer mozadd-edited-buffer
154 (setq mozadd-mirror-location (mozadd-get-comint-string-part comint-output)))
158 (defun mozadd-add-queue-get-mirror-location ()
159 (mozadd-add-task "content.location.href" 'mozadd-get-mirror-location))
161 (defun mozadd-skip-output-until-prompt (comint-output)
162 ;;(message "mozadd-skip-output-until-prompt %S" comint-output)
163 (if (not (string-match-p "\\(\\w+\\)> $" comint-output))
165 ;;(message "done recieve %s" (current-time-string))
171 (defun mozadd-queue-send-buffer-content-to-mozilla (buffer)
172 (mozadd-add-queue-get-mirror-location)
173 (setq mozadd-edited-buffer buffer)
174 (mozadd-add-task-1 'mozadd-send-buffer-content-to-mozilla))
176 (defun mozadd-edited-file-is-shown ()
177 (with-current-buffer mozadd-edited-buffer
178 (string= mozadd-mirror-location mozadd-initial-mirror-location)))
180 (defvar mozadd-xml-path-outline-style "2px solid red")
181 (defun mozadd-send-buffer-content-to-mozilla ()
182 "Update the remote mozrepl instance"
183 (with-current-buffer mozadd-edited-buffer
184 (if (mozadd-edited-file-is-shown)
185 (mozadd-requeue-me-as-task
186 (concat "content.document.body.innerHTML="
190 (let ((where-points nil)
194 ;; If nxml-where-mode is on add corresponding outline style.
195 (when (and (boundp 'nxml-where-mode) nxml-where-mode)
197 (when (overlay-get ovl 'nxml-where)
198 (when (/= ?/ (1+ (char-after (overlay-start ovl))))
199 (push (1- (overlay-end ovl)) where-points))))
200 (overlays-in (point-min) (point-max)))
201 (setq where-points (sort where-points '<)))
202 (dolist (p2 where-points)
203 (setq str (concat str
204 (buffer-substring-no-properties p1
206 (setq str (concat str
208 mozadd-xml-path-outline-style
212 (setq str (concat str
213 (buffer-substring-no-properties p1
218 'mozadd-skip-output-until-prompt)
219 (mozadd-skip-current-task))
220 ;; Timer to avoid looping
221 (run-with-idle-timer 0 nil 'mozadd-maybe-exec-next)
224 (defvar mozadd-current-task nil)
225 (setq mozadd-current-task nil)
227 (defvar mozadd-task-queue nil)
228 (setq mozadd-task-queue nil)
229 ;;(mozadd-add-task "content.location.href" 'mozadd-get-initial-mirror-location)
230 ;;(mozadd-add-task "hi" 1)
231 ;;(mozadd-add-task "hm" 2)
233 (defun mozadd-clear-exec-queue ()
234 (setq mozadd-current-task nil)
235 (setq mozadd-task-queue nil)
236 (when (buffer-live-p inferior-moz-buffer)
237 (with-current-buffer inferior-moz-buffer
238 (dolist (fun (buffer-local-value 'comint-preoutput-filter-functions (current-buffer)))
239 (remove-hook 'comint-preoutput-filter-functions fun t)))))
241 (defun mozadd-add-task (input task)
242 (mozadd-add-task-1 (list input task)))
244 (defun mozadd-add-task-1 (task)
245 (setq mozadd-task-queue (cons task mozadd-task-queue))
246 (setq mozadd-task-queue (reverse mozadd-task-queue))
247 ;;(message "add-task: mozadd-task-queue=%S, current=%s" mozadd-task-queue mozadd-current-task)
248 (mozadd-maybe-exec-next))
250 (defun mozadd-maybe-exec-next ()
251 ;;(message "mozadd-maybe-exec-next, current=%s" mozadd-current-task)
252 (unless mozadd-current-task
255 (defun mozadd-exec-next ()
256 (when mozadd-current-task
257 (let* ((old-task mozadd-current-task) ;;(pop mozadd-task-queue))
258 (old-filter (when (listp old-task) (nth 1 old-task))))
259 (when (and old-filter (buffer-live-p inferior-moz-buffer))
260 (with-current-buffer inferior-moz-buffer
261 (remove-hook 'comint-preoutput-filter-functions old-filter t)))))
262 (setq mozadd-current-task nil)
263 (when mozadd-task-queue
264 (let* ((this (pop mozadd-task-queue))
265 (input (when (listp this) (nth 0 this)))
266 (task (when (listp this) (nth 1 this)))
268 (setq mozadd-current-task this)
269 ;;(message "EXEC: %s" this)
270 (if (not (listp this))
272 (when (buffer-live-p inferior-moz-buffer)
273 (with-current-buffer inferior-moz-buffer
274 (add-hook 'comint-preoutput-filter-functions task nil t)))
275 (comint-send-string (inferior-moz-process) input)))))
277 (defun mozadd-skip-current-task ()
278 ;;(message "mozadd-skip-current-task")
279 ;;(pop mozadd-task-queue)
280 (setq mozadd-current-task nil))
282 (defun mozadd-requeue-me-as-task (input task)
283 (mozadd-skip-current-task)
284 ;;(message "mozadd-requeue-me-as-task %S %S" input task)
285 (setq mozadd-task-queue (cons (list input task) mozadd-task-queue)))
287 (defcustom mozadd-browseable-file-extensions
288 '("html" "htm" "xhtml")
289 "File extensions possibly viewable in a web browser."
290 :type '(repeat (string :tag "File extension (without leading dot)"))
293 (defun mozadd-html-buffer-file-p ()
294 "Return non-nil if buffer file is viewable in a web browser."
295 (when (buffer-file-name)
296 (member (file-name-extension (buffer-file-name))
297 mozadd-browseable-file-extensions)))
300 (define-minor-mode mozadd-mirror-mode
301 "Mirror content of current file buffer immediately in Firefox.
302 When you turn on this mode the file will be opened in Firefox.
303 Every change you make in the buffer will trigger a redraw in
304 Firefox - regardless of if you save the file or not.
306 For the mirroring to work the edited file must be shown in
309 If `nxml-where-mode' is on the marks will also be shown in
310 Firefox as CSS outline style. You can customize the style
311 through the option `mozadd-xml-path-outline-style'.
313 See also `mozadd-refresh-edited-on-save-mode'."
315 :lighter " MozMirror"
317 (if mozadd-mirror-mode
319 (unless (mozadd-html-buffer-file-p)
320 (mozadd-warning "You can only mirror html file buffers")
322 (when (buffer-modified-p)
323 (mozadd-warning "Please save buffer first")
325 (let ((msg (mozadd-perhaps-start)))
329 (mozadd-clear-exec-queue)
330 (setq mozadd-edited-buffer (current-buffer))
331 (mozadd-add-task (concat "content.location.href = "
332 "\"file:///" (buffer-file-name) "\";")
333 'mozadd-get-initial-mirror-location)
334 (add-hook 'after-change-functions 'mozadd-update-mozilla t t)
335 (add-hook 'nxhtml-where-hook 'mozadd-update-mozilla t t)
336 (add-hook 'post-command-hook 'mozadd-edited-buffer-post-command)
338 (setq mozadd-mirror-mode nil))
339 (setq mozadd-edited-buffer nil)
340 (remove-hook 'post-command-hook 'mozadd-edited-buffer-post-command)
341 (remove-hook 'nxhtml-where-hook 'mozadd-update-mozilla t)
342 (remove-hook 'after-change-functions 'mozadd-update-mozilla t)))
343 (put 'mozadd-mirror-mode 'permanent-local t)
346 (define-globalized-minor-mode global-mozadd-mirror-mode mozadd-mirror-mode
348 (when (mozadd-html-buffer-file-p)
349 (mozadd-mirror-mode 1))))
351 (defun mozadd-edited-buffer-post-command ()
352 "Check if we are in a new edited buffer."
353 (when mozadd-mirror-mode
354 (setq mozadd-edited-buffer (current-buffer))))
357 (defvar mozadd-buffer-content-to-mozilla-timer nil)
359 (defun mozadd-update-mozilla (&rest ignored)
360 (when (timerp mozadd-buffer-content-to-mozilla-timer)
361 (cancel-timer mozadd-buffer-content-to-mozilla-timer))
362 (setq mozadd-buffer-content-to-mozilla-timer
363 (run-with-idle-timer 1 nil 'mozadd-queue-send-buffer-content-to-mozilla (current-buffer))))
364 (put 'mozadd-update-mozilla 'permanent-local-hook t)
368 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
369 ;;; mozadd.el ends here