;; COMMON INITIALIZATION, UTILITIES and INTERNALS which didn't fit anywhere else
|
|
|
|
(eval-when-compile
|
|
(require 'cl))
|
|
(require 'font-lock)
|
|
(require 'color)
|
|
(require 'eieio)
|
|
(require 'eieio-base)
|
|
(require 'eieio-custom)
|
|
(require 'format-spec)
|
|
|
|
(defcustom polymode-display-process-buffers t
|
|
"When non-nil, display weaving and exporting process buffers."
|
|
:group 'polymode
|
|
:type 'boolean)
|
|
|
|
;; esential vars
|
|
(defvar-local pm/polymode nil)
|
|
(defvar-local pm/chunkmode nil)
|
|
(defvar-local pm/type nil)
|
|
(defvar-local polymode-major-mode nil)
|
|
(defvar-local pm--fontify-region-original nil)
|
|
(defvar-local pm--indent-line-function-original nil)
|
|
(defvar-local pm--syntax-begin-function-original nil)
|
|
;; (defvar-local pm--killed-once nil)
|
|
(defvar-local polymode-mode nil
|
|
"This variable is t if current \"mode\" is a polymode.")
|
|
|
|
;; silence the compiler for now
|
|
(defvar pm--output-file nil)
|
|
(defvar pm--input-buffer nil)
|
|
(defvar pm--input-file nil)
|
|
(defvar pm/type)
|
|
(defvar pm/polymode)
|
|
(defvar pm/chunkmode)
|
|
(defvar *span*)
|
|
|
|
;; core api from polymode.el, which relies on polymode-methods.el.
|
|
;; fixme: some of these are not api, rename
|
|
(declare-function pm/base-buffer "polymode")
|
|
(declare-function pm/get-innermost-span "polymode")
|
|
(declare-function pm/map-over-spans "polymode")
|
|
(declare-function pm/narrow-to-span "polymode")
|
|
(declare-function pm/fontify-region "polymode")
|
|
(declare-function pm/syntax-begin-function "polymode")
|
|
|
|
;; methods api from polymode-methods.el
|
|
(declare-function pm-initialize "polymode-methods")
|
|
(declare-function pm-get-buffer "polymode-methods")
|
|
(declare-function pm-select-buffer "polymode-methods")
|
|
(declare-function pm-install-buffer "polymode-methods")
|
|
(declare-function pm-get-adjust-face "polymode-methods")
|
|
(declare-function pm-get-span "polymode-methods")
|
|
(declare-function pm-indent-line "polymode-methods")
|
|
|
|
;; buffer manipulation function in polymode-methods.el
|
|
;; polymode-common.el:315:1:Warning: the following functions are not known to be defined:
|
|
;; pm--create-indirect-buffer, pm--setup-buffer, pm--span-at-point, polymode-select-buffer
|
|
|
|
;; temporary debugging facilities
|
|
(defvar pm--dbg-mode-line t)
|
|
(defvar pm--dbg-fontlock t)
|
|
(defvar pm--dbg-hook t)
|
|
|
|
;; other locals
|
|
(defvar-local pm--process-buffer nil)
|
|
|
|
|
|
;;; UTILITIES
|
|
(defun pm--display-file (ofile)
|
|
(display-buffer (find-file-noselect ofile 'nowarn)))
|
|
|
|
(defun pm--get-indirect-buffer-of-mode (mode)
|
|
(loop for bf in (oref pm/polymode -buffers)
|
|
when (and (buffer-live-p bf)
|
|
(eq mode (buffer-local-value 'polymode-major-mode bf)))
|
|
return bf))
|
|
|
|
;; ;; This doesn't work in 24.2, pcase bug ((void-variable xcar))
|
|
;; ;; Other pcases in this file don't throw this error
|
|
;; (defun pm--set-chunkmode-buffer (obj type buff)
|
|
;; (with-slots (buffer head-mode head-buffer tail-mode tail-buffer) obj
|
|
;; (pcase (list type head-mode tail-mode)
|
|
;; (`(body body ,(or `nil `body))
|
|
;; (setq buffer buff
|
|
;; head-buffer buff
|
|
;; tail-buffer buff))
|
|
;; (`(body ,_ body)
|
|
;; (setq buffer buff
|
|
;; tail-buffer buff))
|
|
;; (`(body ,_ ,_ )
|
|
;; (setq buffer buff))
|
|
;; (`(head ,_ ,(or `nil `head))
|
|
;; (setq head-buffer buff
|
|
;; tail-buffer buff))
|
|
;; (`(head ,_ ,_)
|
|
;; (setq head-buffer buff))
|
|
;; (`(tail ,_ ,(or `nil `head))
|
|
;; (setq tail-buffer buff
|
|
;; head-buffer buff))
|
|
;; (`(tail ,_ ,_)
|
|
;; (setq tail-buffer buff))
|
|
;; (_ (error "type must be one of 'body 'head and 'tail")))))
|
|
|
|
;; a literal transcript of the pcase above
|
|
(defun pm--set-chunkmode-buffer (obj type buff)
|
|
(with-slots (-buffer head-mode -head-buffer tail-mode -tail-buffer) obj
|
|
(cond
|
|
((and (eq type 'body)
|
|
(eq head-mode 'body)
|
|
(or (null tail-mode)
|
|
(eq tail-mode 'body)))
|
|
(setq -buffer buff
|
|
-head-buffer buff
|
|
-tail-buffer buff))
|
|
((and (eq type 'body)
|
|
(eq tail-mode 'body))
|
|
(setq -buffer buff
|
|
-tail-buffer buff))
|
|
((eq type 'body)
|
|
(setq -buffer buff))
|
|
((and (eq type 'head)
|
|
(or (null tail-mode)
|
|
(eq tail-mode 'head)))
|
|
(setq -head-buffer buff
|
|
-tail-buffer buff))
|
|
((eq type 'head)
|
|
(setq -head-buffer buff))
|
|
((and (eq type 'tail)
|
|
(or (null tail-mode)
|
|
(eq tail-mode 'head)))
|
|
(setq -tail-buffer buff
|
|
-head-buffer buff))
|
|
((eq type 'tail)
|
|
(setq -tail-buffer buff))
|
|
(t (error "type must be one of 'body 'head and 'tail")))))
|
|
|
|
(defun pm--get-chunkmode-mode (obj type)
|
|
(with-slots (mode head-mode tail-mode) obj
|
|
(cond ((or (eq type 'body)
|
|
(and (eq type 'head)
|
|
(eq head-mode 'body))
|
|
(and (eq type 'tail)
|
|
(or (eq tail-mode 'body)
|
|
(and (null tail-mode)
|
|
(eq head-mode 'body)))))
|
|
(oref obj :mode))
|
|
((or (and (eq type 'head)
|
|
(eq head-mode 'host))
|
|
(and (eq type 'tail)
|
|
(or (eq tail-mode 'host)
|
|
(and (null tail-mode)
|
|
(eq head-mode 'host)))))
|
|
(oref (oref pm/polymode -hostmode) :mode))
|
|
((eq type 'head)
|
|
(oref obj :head-mode))
|
|
((eq type 'tail)
|
|
(oref obj :tail-mode))
|
|
(t (error "type must be one of 'head 'tail 'body")))))
|
|
|
|
(defun pm--create-chunkmode-buffer-maybe (chunkmode type)
|
|
;; assumes pm/polymode is set
|
|
(let ((mode (pm--get-chunkmode-mode chunkmode type)))
|
|
(or (pm--get-indirect-buffer-of-mode mode)
|
|
(let ((buff (pm--create-indirect-buffer mode)))
|
|
(with-current-buffer buff
|
|
(setq pm/chunkmode chunkmode)
|
|
(setq pm/type type)
|
|
(pm--setup-buffer)
|
|
(funcall (oref pm/polymode :minor-mode))
|
|
buff)))))
|
|
|
|
(defun pm--get-mode-symbol-from-name (str)
|
|
"Guess and return mode function.
|
|
Return major mode function constructed from STR by appending
|
|
'-mode' if needed. If the constructed symbol is not a function
|
|
return an error."
|
|
(let* ((str (if (symbolp str)
|
|
(symbol-name str)
|
|
str))
|
|
(mname (if (string-match-p "-mode$" str)
|
|
str
|
|
(concat str "-mode"))))
|
|
(pm--get-available-mode (intern mname))))
|
|
|
|
(defun pm--get-available-mode (mode)
|
|
"Check if MODE symbol is defined and is a valid function.
|
|
If so, return it, otherwise return 'fundamental-mode with a
|
|
warnign."
|
|
(cond ((fboundp mode) mode)
|
|
(t (message "Cannot find %s function, using 'fundamental-mode instead" mode)
|
|
'fundamental-mode)))
|
|
|
|
(defun pm--oref-with-parents (object slot)
|
|
"Merge slots SLOT from the OBJECT and all its parent instances."
|
|
(let (VALS)
|
|
(while object
|
|
(setq VALS (append (and (slot-boundp object slot) ; don't cascade
|
|
(eieio-oref object slot))
|
|
VALS)
|
|
object (and (slot-boundp object :parent-instance)
|
|
(oref object :parent-instance))))
|
|
VALS))
|
|
|
|
(defun pm--abrev-names (list abrev-regexp)
|
|
"Abreviate names in LIST by replacing abrev-regexp with empty
|
|
string."
|
|
(mapcar (lambda (nm)
|
|
(let ((str-nm (if (symbolp nm)
|
|
(symbol-name nm)
|
|
nm)))
|
|
(propertize (replace-regexp-in-string abrev-regexp "" str-nm)
|
|
:orig str-nm)))
|
|
list))
|
|
|
|
(defun pm--put-hist (key val)
|
|
(oset pm/polymode -hist
|
|
(plist-put (oref pm/polymode -hist) key val)))
|
|
|
|
(defun pm--get-hist (key)
|
|
(plist-get (oref pm/polymode -hist) key))
|
|
|
|
(defun pm--comment-region (beg end)
|
|
;; mark as syntactic comment
|
|
(when (> end 1)
|
|
(with-silent-modifications
|
|
(let ((beg (or beg (region-beginning)))
|
|
(end (or end (region-end))))
|
|
(let ((ch-beg (char-after beg))
|
|
(ch-end (char-before end)))
|
|
(add-text-properties beg (1+ beg)
|
|
(list 'syntax-table (cons 11 ch-beg)
|
|
'rear-nonsticky t
|
|
'polymode-comment 'start))
|
|
(add-text-properties (1- end) end
|
|
(list 'syntax-table (cons 12 ch-end)
|
|
'rear-nonsticky t
|
|
'polymode-comment 'end)))))))
|
|
|
|
(defun pm--uncomment-region (beg end)
|
|
;; remove all syntax-table properties. Should not cause any problem as it is
|
|
;; always used before font locking
|
|
(when (> end 1)
|
|
(with-silent-modifications
|
|
(let ((props '(syntax-table nil rear-nonsticky nil polymode-comment nil)))
|
|
(remove-text-properties beg end props)
|
|
;; (remove-text-properties beg (1+ beg) props)
|
|
;; (remove-text-properties end (1- end) props)
|
|
))))
|
|
|
|
(defun pm--run-command (command sentinel buff-name message)
|
|
"Run command interactively.
|
|
Run command in a buffer (in comint-shell-mode) so that it accepts
|
|
user interaction."
|
|
;; simplified version of TeX-run-TeX
|
|
(require 'comint)
|
|
(let* ((buffer (get-buffer-create buff-name))
|
|
(process nil)
|
|
(command-buff (current-buffer))
|
|
(ofile pm--output-file))
|
|
(with-current-buffer buffer
|
|
(read-only-mode -1)
|
|
(erase-buffer)
|
|
(insert message)
|
|
(comint-exec buffer buff-name shell-file-name nil
|
|
(list shell-command-switch command))
|
|
(comint-mode)
|
|
(setq process (get-buffer-process buffer))
|
|
(set-process-sentinel process sentinel)
|
|
(setq pm--process-buffer t)
|
|
;; for communication with sentinel
|
|
(set (make-local-variable 'pm--output-file) ofile)
|
|
(set (make-local-variable 'pm--input-buffer) command-buff)
|
|
(set-marker (process-mark process) (point-max)))
|
|
(when polymode-display-process-buffers
|
|
(display-buffer buffer `(nil . ((inhibit-same-window . ,pop-up-windows)))))
|
|
nil))
|
|
|
|
(defun pm--run-command-sentinel (process name message)
|
|
(let ((buff (process-buffer process)))
|
|
(with-current-buffer buff
|
|
;; fixme: remove this later
|
|
(sit-for .5)
|
|
(goto-char (point-min))
|
|
(let ((case-fold-search t))
|
|
(if (not (re-search-forward "error" nil 'no-error))
|
|
pm--output-file
|
|
(progn
|
|
(display-buffer (current-buffer))
|
|
(message "Done with %s" message))
|
|
(error "Bumps while %s (%s)" message name))))))
|
|
|
|
|
|
;;; COMPATIBILITY and FIXES
|
|
(defun pm--flyspel-dont-highlight-in-chunkmodes (beg end poss)
|
|
(or (get-text-property beg 'chunkmode)
|
|
(get-text-property beg 'chunkmode)))
|
|
|
|
(defvar object-name)
|
|
(defun pm--object-name (object)
|
|
(cond ((fboundp 'eieio--object-name)
|
|
(eieio--object-name object))
|
|
((fboundp 'eieio-object-name)
|
|
(eieio-object-name object))
|
|
(t (aref object object-name))))
|
|
|
|
(defun pm--activate-jit-lock-mode-maybe ()
|
|
;; ugly hack for emacs 24.4
|
|
(when (and (string< "24.4" emacs-version)
|
|
;; jit-lock is deactivated after this one is set.
|
|
jit-lock-functions)
|
|
|
|
(setq jit-lock-mode t)
|
|
;; Mark the buffer for refontification.
|
|
(jit-lock-refontify)
|
|
|
|
;; Install an idle timer for stealth fontification.
|
|
(when (and jit-lock-stealth-time (null jit-lock-stealth-timer))
|
|
(setq jit-lock-stealth-timer
|
|
(run-with-idle-timer jit-lock-stealth-time t
|
|
'jit-lock-stealth-fontify)))
|
|
|
|
;; Create, but do not activate, the idle timer for repeated
|
|
;; stealth fontification.
|
|
(when (and jit-lock-stealth-time (null jit-lock-stealth-repeat-timer))
|
|
(setq jit-lock-stealth-repeat-timer (timer-create))
|
|
(timer-set-function jit-lock-stealth-repeat-timer
|
|
'jit-lock-stealth-fontify '(t)))
|
|
|
|
;; Init deferred fontification timer.
|
|
(when (and jit-lock-defer-time (null jit-lock-defer-timer))
|
|
(setq jit-lock-defer-timer
|
|
(run-with-idle-timer jit-lock-defer-time t
|
|
'jit-lock-deferred-fontify)))
|
|
|
|
;; Initialize contextual fontification if requested.
|
|
(when (eq jit-lock-contextually t)
|
|
(unless jit-lock-context-timer
|
|
(setq jit-lock-context-timer
|
|
(run-with-idle-timer jit-lock-context-time t
|
|
'jit-lock-context-fontify)))
|
|
(setq jit-lock-context-unfontify-pos
|
|
(or jit-lock-context-unfontify-pos (point-max))))
|
|
|
|
;; Setup our hooks.
|
|
(add-hook 'after-change-functions 'jit-lock-after-change nil t)
|
|
(add-hook 'fontification-functions 'jit-lock-function)))
|
|
|
|
|
|
(provide 'polymode-common)
|
|
|