(require 'polymode-common) ;;; INITIALIZATION (defgeneric pm-initialize (config) "Initialize current buffer with CONFIG. First initialize the -hostmode and -innermodes slots of polymode object ...") (defmethod pm-initialize ((config pm-polymode)) ;; fixme: reinstalation leads to infloop of pm--fontify-region-original and others ... ;; On startup with local auto vars emacs reinstals the mode twice .. waf? ;; Temporary fix: don't install twice (unless pm/polymode (let* ((chunkmode (clone (symbol-value (oref config :hostmode)))) (_ (oset chunkmode -buffer (current-buffer))) ;; set if nil, to allow unspecified host chunkmodes to be used in minor modes (host-mode (or (oref chunkmode :mode) (oset chunkmode :mode major-mode)))) (unless (or (eq major-mode host-mode) (eq polymode-major-mode host-mode)) (let ((polymode-mode t)) ;;major-modes might check it (funcall host-mode))) ;; fixme: maybe: inconsistencies? ;; 1) not calling pm-install-buffer on host-buffer ;; But, we are not creating/installing a new buffer here .. so it is a ;; different thing .. and is probably ok ;; 2) not calling config's :minor-mode (polymode function). ;; But polymode function calls pm-initialize... so I guess it is ok (oset config -hostmode chunkmode) (setq pm/polymode config) (setq pm/chunkmode chunkmode) (setq pm/type 'host) (add-hook 'flyspell-incorrect-hook 'pm--flyspel-dont-highlight-in-chunkmodes nil t) (prog1 (pm--setup-buffer) ; general setup for host and innermode buffers (let ((PI pm/polymode) IFs) ;; aggregate and run hooks; parents first (while PI (setq IFs (append (and (slot-boundp PI :init-functions) ; don't cascade (oref PI :init-functions)) IFs) PI (and (slot-boundp PI :parent-instance) (oref PI :parent-instance)))) (run-hooks 'IFs)))))) (defmethod pm-initialize ((config pm-polymode-one)) (call-next-method) (eval `(oset config -innermodes (list (clone ,(oref config :innermode)))))) (defmethod pm-initialize ((config pm-polymode-multi)) (call-next-method) (oset config -innermodes (mapcar (lambda (sub-name) (clone (symbol-value sub-name))) (oref config :innermodes)))) ;;; BUFFERS (defgeneric pm-get-buffer (chunkmode &optional span-type) "Get the indirect buffer associated with SUBMODE and SPAN-TYPE. Should return nil if buffer has not yet been installed. Also see `pm-get-span'.") (defmethod pm-get-buffer ((chunkmode pm-chunkmode) &optional type) (oref chunkmode -buffer)) (defmethod pm-get-buffer ((chunkmode pm-hbtchunkmode) &optional type) (cond ((eq 'body type) (oref chunkmode -buffer)) ((eq 'head type) (oref chunkmode -head-buffer)) ((eq 'tail type) (oref chunkmode -tail-buffer)) (t (error "Don't know how to select buffer of type '%s' for chunkmode '%s' of class '%s'" type (pm--object-name chunkmode) (class-of chunkmode))))) (defgeneric pm-select-buffer (chunkmode span) "Ask SUBMODE to select (make current) its indirect buffer corresponding to the type of the SPAN returned by `pm-get-span'.") (defmethod pm-select-buffer ((chunkmode pm-chunkmode) span) "Select the buffer associated with SUBMODE. Install a new indirect buffer if it is not already installed. For this method to work correctly, SUBMODE's class should define `pm-install-buffer' and `pm-get-buffer' methods." (let* ((type (car span)) (buff (pm-get-buffer chunkmode type))) (unless (buffer-live-p buff) (pm-install-buffer chunkmode type) (setq buff (pm-get-buffer chunkmode type))) (pm--select-buffer buff))) (defmethod pm-select-buffer ((chunkmode pm-hbtchunkmode) span) (call-next-method) (pm--transfer-vars-from-base)) (defmethod pm-select-buffer ((config pm-polymode-multi-auto) &optional span) ;; :fixme: pm-get-span on multi configs returns config as last object of ;; span. That's freaking confusing. (if (null (car span)) (pm-select-buffer (oref config -hostmode) span) (let ((type (car span)) (proto (symbol-value (oref config :auto-innermode))) chunkmode) (save-excursion (goto-char (cadr span)) (unless (eq type 'head) (re-search-backward (oref proto :head-reg) nil 'noerr)) (let* ((str (or (and (oref proto :retriever-regexp) (re-search-forward (oref proto :retriever-regexp)) (match-string-no-properties (oref proto :retriever-num))) (and (oref proto :retriever-function) (funcall (oref proto :retriever-function))) (error "retriever subexpression didn't match"))) (name (concat "auto-innermode:" str))) (setq chunkmode (or (loop for obj in (oref config -auto-innermodes) when (equal name (object-name-string obj)) return obj) (let ((new-obj (clone proto name :mode (pm--get-mode-symbol-from-name str)))) (object-add-to-list config '-auto-innermodes new-obj) new-obj))))) (pm-select-buffer chunkmode span)))) (defgeneric pm-install-buffer (chunkmode &optional type) "Ask SUBMODE to install an indirect buffer corresponding to span TYPE. Should return newly installed/retrieved buffer.") (defmethod pm-install-buffer ((chunkmode pm-chunkmode) &optional type) "Independently on the TYPE call `pm/create-indirect-buffer' create and install a new buffer in slot -buffer of SUBMODE." (oset chunkmode -buffer (pm--create-chunkmode-buffer-maybe chunkmode type))) (defmethod pm-install-buffer ((chunkmode pm-hbtchunkmode) type) "Depending of the TYPE install an indirect buffer into slot -buffer of SUBMODE. Create this buffer if does not exist." (pm--set-chunkmode-buffer chunkmode type (pm--create-chunkmode-buffer-maybe chunkmode type))) (defun pm--get-adjusted-background (prop) ;; if > lighten on dark backgroun. Oposite on light. (color-lighten-name (face-background 'default) (if (eq (frame-parameter nil 'background-mode) 'light) (- prop) ;; darken prop))) (defun pm--adjust-chunk-face (beg end face) ;; propertize 'face of the region by adding chunk specific configuration (interactive "r") (when face (with-current-buffer (current-buffer) (let ((face (or (and (numberp face) (list (cons 'background-color (pm--get-adjusted-background face)))) face)) (pchange nil)) ;; (while (not (eq pchange end)) ;; (setq pchange (next-single-property-change beg 'face nil end)) ;; (put-text-property beg pchange 'face ;; `(,face ,@(get-text-property beg 'face))) ;; (setq beg pchange)) (font-lock-prepend-text-property beg end 'face face))))) (defun pm--adjust-visual-line-mode (vlm) (when (not (eq visual-line-mode vlm)) (if (null vlm) (visual-line-mode -1) (visual-line-mode 1)))) ;; move only in post-command hook, after buffer selection (defvar pm--can-move-overlays nil) (defun pm--move-overlays-to (new-buff) (when pm--can-move-overlays (mapc (lambda (o) (move-overlay o (overlay-start o) (overlay-end o) new-buff)) (overlays-in 1 (1+ (buffer-size)))))) (defun pm--transfer-vars-from-base () (let ((bb (pm/base-buffer))) (dolist (var '(buffer-file-name)) (set var (buffer-local-value var bb))))) (defun pm--select-buffer (buffer) (when (and (not (eq buffer (current-buffer))) (buffer-live-p buffer)) (let ((point (point)) (window-start (window-start)) (visible (pos-visible-in-window-p)) (oldbuf (current-buffer)) (vlm visual-line-mode) (ractive (region-active-p)) (mkt (mark t)) (bis buffer-invisibility-spec)) (pm--move-overlays-to buffer) (switch-to-buffer buffer) (setq buffer-invisibility-spec bis) (pm--adjust-visual-line-mode vlm) (bury-buffer oldbuf) ;; fixme: wha tis the right way to do this ... activate-mark-hook? (if (not ractive) (deactivate-mark) (set-mark mkt) (activate-mark)) (goto-char point) ;; Avoid the display jumping around. (when visible (set-window-start (get-buffer-window buffer t) window-start))))) (defun pm--setup-buffer (&optional buffer) ;; General buffer setup, should work for indirect and base buffers ;; alike. Assume pm/polymode and pm/chunkmode is already in place. Return ;; the buffer. (let ((buff (or buffer (current-buffer)))) (with-current-buffer buff ;; Don't let parse-partial-sexp get fooled by syntax outside ;; the chunk being fontified. ;; font-lock, forward-sexp etc should see syntactic comments ;; (set (make-local-variable 'parse-sexp-lookup-properties) t) (set (make-local-variable 'font-lock-dont-widen) t) (when pm--dbg-fontlock (setq pm--fontify-region-original font-lock-fontify-region-function) (set (make-local-variable 'font-lock-fontify-region-function) #'pm/fontify-region) (setq pm--syntax-begin-function-original syntax-begin-function) (set (make-local-variable 'syntax-begin-function) #'pm/syntax-begin-function)) (set (make-local-variable 'polymode-mode) t) ;; Indentation should first narrow to the chunk. Modes ;; should normally just bind `indent-line-function' to ;; handle indentation. (when (and indent-line-function ; not that it should ever be nil... (oref pm/chunkmode :protect-indent-line)) (setq pm--indent-line-function-original indent-line-function) (set (make-local-variable 'indent-line-function) 'pm-indent-line-dispatcher)) (add-hook 'kill-buffer-hook 'pm--kill-indirect-buffer t t) (when pm--dbg-hook (add-hook 'post-command-hook 'polymode-select-buffer nil t)) (object-add-to-list pm/polymode '-buffers (current-buffer))) buff)) (defvar-local pm--killed-once nil) (defun pm--kill-indirect-buffer () ;; find-alternate-file breaks (https://github.com/vspinu/polymode/issues/79) (let ((base (buffer-base-buffer))) (when (and base (buffer-live-p base)) ;; 'base' is non-nil in indirect buffers only (set-buffer-modified-p nil) (unless (buffer-local-value 'pm--killed-once base) (with-current-buffer base (setq pm--killed-once t)) (kill-buffer base))))) (defvar pm--ib-prefix "") (defun pm--create-indirect-buffer (mode) "Create indirect buffer with major MODE and initialize appropriately. This is a low lever function which must be called, one way or another from `pm/install' method. Among other things store `pm/polymode' from the base buffer (must always exist!) in the newly created buffer. Return newlly created buffer." (unless (buffer-local-value 'pm/polymode (pm/base-buffer)) (error "`pm/polymode' not found in the base buffer %s" (pm/base-buffer))) (setq mode (pm--get-available-mode mode)) (with-current-buffer (pm/base-buffer) (let* ((config (buffer-local-value 'pm/polymode (current-buffer))) (new-name (generate-new-buffer-name (format "%s%s[%s]" pm--ib-prefix (buffer-name) (replace-regexp-in-string "-mode" "" (symbol-name mode))))) (new-buffer (make-indirect-buffer (current-buffer) new-name)) ;; (hook pm/indirect-buffer-hook) (file (buffer-file-name)) (base-name (buffer-name)) (jit-lock-mode nil) (coding buffer-file-coding-system)) (with-current-buffer new-buffer (let ((polymode-mode t)) ;;major-modes might check it (funcall mode)) ;; hopefully temporary hack: (pm--activate-jit-lock-mode-maybe) (setq polymode-major-mode mode) ;; Avoid the uniqified name for the indirect buffer in the mode line. (when pm--dbg-mode-line (setq mode-line-buffer-identification (propertized-buffer-identification base-name))) (setq pm/polymode config) (setq buffer-file-coding-system coding) (setq buffer-file-name file) (vc-find-file-hook)) new-buffer))) ;;; SPAN MANIPULATION (defgeneric pm-get-span (chunkmode &optional pos) "Ask the CHUNKMODE for the span at point. Return a list of three elements (TYPE BEG END OBJECT) where TYPE is a symbol representing the type of the span surrounding POS (head, tail, body). BEG and END are the coordinates of the span. OBJECT is a sutable object which is 'responsable' for this span. This is an object that could be dispached upon with `pm-select-buffer', .. (fixme: complete this list). Should return nil if there is no SUBMODE specific span around POS.") (defmethod pm-get-span (chunkmode &optional pos) "Simply return nil. Base mode usually do not compute the span." nil) (defmethod pm-get-span ((config pm-polymode) &optional pos) "Apply pm-get-span on every element of chunkmodes slot of config object. Return a cons (chunkmode . span), for which START is closest to POS (and before it); i.e. the innermost span. POS defaults to point." (save-restriction (widen) ;; fixme: host should be last, to take advantage of the chunkmodes computation (let* ((smodes (cons (oref config -hostmode) (oref config -innermodes))) (start (point-min)) (end (point-max)) (pos (or pos (point))) (span (list nil start end nil)) val) (dolist (sm smodes) (setq val (pm-get-span sm pos)) (when (and val (or (> (nth 1 val) start) (< (nth 2 val) end))) (if (or (car val) (null span)) (setq span val start (nth 1 val) end (nth 2 val)) ;; nil car means outer chunkmode (usually host). And it can be an ;; intersection of spans returned by 2 different neighbour inner ;; chunkmodes. See rapport mode for an example (setq start (max (nth 1 val) (nth 1 span)) end (min (nth 2 val) (nth 2 span))) (setcar (cdr span) start) (setcar (cddr span) end)))) (unless (and (<= start end) (<= pos end) (>= pos start)) (error "Bad polymode selection: span:%s pos:%s" (list start end) pos)) (when (null (car span)) ; chunkmodes can compute the host span by returning nil (setcar (last span) (oref config -hostmode))) span))) ;; No need for this one so far. Basic method iterates through -innermodes ;; anyhow. ;; (defmethod pm-get-span ((config pm-polymode-multi) &optional pos)) (defmethod pm-get-span ((config pm-polymode-multi-auto) &optional pos) (let ((span-other (call-next-method)) (proto (symbol-value (oref config :auto-innermode)))) (if (oref proto :head-reg) (let ((span (pm--span-at-point (oref proto :head-reg) (oref proto :tail-reg) pos))) (if (and span-other (or (> (nth 1 span-other) (nth 1 span)) (< (nth 2 span-other) (nth 2 span)))) ;; treat intersections with the host mode (if (car span-other) span-other ;not host ;; here, car span should better be nil; no explicit check (setcar (cdr span-other) (max (nth 1 span-other) (nth 1 span))) (setcar (cddr span-other) (min (nth 2 span-other) (nth 2 span))) span-other) (append span (list config)))) ;fixme: this returns config as last object span-other))) (defmethod pm-get-span ((chunkmode pm-hbtchunkmode) &optional pos) "Return a list of the form (TYPE POS-START POS-END SELF). TYPE can be 'body, 'head or 'tail. SELF is just a chunkmode object in this case." (with-slots (head-reg tail-reg head-mode tail-mode) chunkmode (let* ((span (pm--span-at-point head-reg tail-reg pos)) (type (car span))) (when (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))))) (setcar span nil)) (append span (list chunkmode))))) (defmacro pm-create-indented-block-matchers (name regex) "Defines 2 functions, each return a list of the start and end points of the HEAD and TAIL portions of an indented block of interest, via some regex. You can then use these functions in the defcustom pm-inner modes. e.g. (pm-create-indented-block-matchers 'slim-coffee' \"^[^ ]*\\(.*:? *coffee: *\\)$\") creates the functions pm-slim-coffee-head-matcher pm-slim-coffee-tail-matcher In the example below, The head matcher will match against 'coffee:', returning the positions of the start and end of 'coffee:' The tail matcher will return a list (n, n) of the final characters is the block. |<----- Uses this indentation to define the left edge of the 'block' | |<--->| This region is higlighted by the :head-mode in the block-matchers | | | |<----- the head matcher uses this column as the end of the head | | ----:-----:-------------- example file ----------------------------------------- 1| : : 2| coffee: 3| myCoffeeCode() 4| moreCode -> 5| do things 6| : 7| This is no longer in the block 8| : ----------------:--------------------------------------------------------------- --->|<----- this region of 0 width is highlighted by the :tail-mode the 'block' ends after this column on line 5 All the stuff after the -end- of the head and before the start of the tail is sent to the new mode for syntax highlighting." (let* ((head-name (intern (format "pm-%s-head-matcher" name))) (tail-name (intern (format "pm-%s-tail-matcher" name)))) `(progn (defun ,head-name (ahead) (when (re-search-forward ,regex nil t ahead) (cons (match-beginning 1) (match-end 1)))) (defun ,tail-name (ahead) (save-excursion ;; (cons (point-max) (point-max))))))) (goto-char (car (,head-name 1))) (let* ((block-col (current-indentation)) (posn (catch 'break (while (not (eobp)) (forward-line 1) (when (and (<= (current-indentation) block-col) (not (progn (beginning-of-line) (looking-at "^[[:space:]]*$")))) (throw 'break (point-at-bol)))) (throw 'break (point-max))))) (cons posn posn))))))) (defun pm--default-matcher (reg ahead) (if (< ahead 0) (if (re-search-backward reg nil t) (cons (match-beginning 0) (match-end 0))) (if (re-search-forward reg nil t) (cons (match-beginning 0) (match-end 0))))) ;; fixme: there should be a simpler way... check the code and document (defun pm--span-at-point-fun-fun (hd-matcher tl-matcher) (save-excursion (let ((pos (point)) (posh (funcall hd-matcher -1))) (if (null posh) ;; special first chunk (let ((posh1 (progn (goto-char (point-min)) (funcall hd-matcher 1)))) (if (and posh1 (<= (car posh1) pos) (< pos (cdr posh1))) (list 'head (car posh1) (cdr posh1)) (list nil (point-min) (or (car posh1) (point-max))))) (let ((post (progn (goto-char (car posh)) (or (funcall tl-matcher 1) (cons (point-max) (point-max)))))) (if (and (<= (cdr posh) pos) (< pos (car post))) (list 'body (cdr posh) (car post)) (if (and (<= (car post) pos) (< pos (cdr post))) (list 'tail (car post) (cdr post)) (if (< pos (cdr post)) ;; might be in the head (progn (goto-char (car post)) (let ((posh1 (funcall hd-matcher -1))) (if (and (<= (car posh1) pos) (< pos (cdr posh1))) (list 'head (car posh1) (cdr posh1)) (list nil (cdr posh) (car posh1))))) ;; posh is point min, fixme: not true anymore? (goto-char (cdr post)) (let ((posh1 (or (funcall hd-matcher 1) (cons (point-max) (point-max))))) (if (and posh (<= (car posh1) pos ) (< pos (cdr posh1))) (list 'head (car posh1) (cdr posh1)) (list nil (cdr post) (car posh1)))))))))))) (defun pm--span-at-point-reg-reg (head-matcher tail-matcher) ;; Guaranteed to produce non-0 length spans. If no span has been found ;; (head-matcher didn't match) return (nil (point-min) (point-max)). ;; xxx1 relate to the first ascending search ;; xxx2 relate to the second descending search (save-excursion (let* ((pos (point)) (head1-beg (and (re-search-backward head-matcher nil t) (match-beginning 0))) (head1-end (and head1-beg (match-end 0)))) (if head1-end ;; we know that (>= pos head1-end) ;; ----------------------- ;; host](head)[body](tail)[host](head) (let* ((tail1-beg (and (goto-char head1-end) (re-search-forward tail-matcher nil t) (match-beginning 0))) (tail1-end (and tail1-beg (match-end 0))) (tail1-beg (or tail1-beg (point-max))) (tail1-end (or tail1-end (point-max)))) (if (or (< pos tail1-end) (= tail1-end (point-max))) (if (<= pos tail1-beg) ;; ------ ;; host](head)[body](tail)[host](head)) (list 'body head1-end tail1-beg) ;; ----- ;; host](head](body](tail)[host](head) (list 'tail tail1-beg tail1-end)) ;; ------------ ;; host](head](body](tail)[host](head) (let* ((head2-beg (or (and (re-search-forward head-matcher nil t) (match-beginning 0)) (point-max)))) (if (<= pos head2-beg) ;; ------ ;; host](head](body](tail)[host](head) (list nil tail1-end head2-beg) ;; ------ ;; host](head](body](tail)[host](head) (list 'head head2-beg (match-end 0)))))) ;; ----------- ;; host](head)[body](tail)[host (let ((head2-beg (and (goto-char (point-min)) (re-search-forward head-matcher nil t) (match-beginning 0)))) (if (null head2-beg) ;; no span found (list nil (point-min) (point-max)) (if (<= pos head2-beg) ;; ----- ;; host](head)[body](tail)[host (list nil (point-min) head2-beg) ;; ------ ;; host](head)[body](tail)[host (list 'head head2-beg (match-end 0))))))))) (defun pm--span-at-point (head-matcher tail-matcher &optional pos) "Basic span detector with head/tail. Either of HEAD-MATCHER and TAIL-MATCHER can be a regexp or a function. When a function the matcher must accept one argument that can take either values 1 (forwards search) or -1 (backward search). This function must return either nil (no match) or a (cons BEG END) representing the span of the head or tail respectively. See `pm--default-matcher' for an example. Return (type span-start span-end) where type is one of the follwoing symbols: nil - pos is between point-min and head-reg, or between tail-reg and point-max body - pos is between head-reg and tail-reg (exclusively) head - head span tail - tail span" ;; ! start of the span is part of the span ! (save-restriction (widen) (goto-char (or pos (point))) (cond ((and (stringp head-matcher) (stringp tail-matcher)) (pm--span-at-point-reg-reg head-matcher tail-matcher)) ((and (stringp head-matcher) (functionp tail-matcher)) (pm--span-at-point-fun-fun (lambda (ahead) (pm--default-matcher head-matcher ahead)) tail-matcher)) ((and (functionp head-matcher) (stringp tail-matcher)) (pm--span-at-point-fun-fun head-matcher (lambda (ahead) (pm--default-matcher tail-matcher ahead)))) ((and (functionp head-matcher) (functionp tail-matcher)) (pm--span-at-point-fun-fun head-matcher tail-matcher)) (t (error "head and tail matchers should be either regexp strings or functions"))))) ;;; INDENT (defun pm-indent-line-dispatcher () "Dispatch methods indent methods on current span." (let ((span (pm/get-innermost-span))) (pm-indent-line (car (last span)) span))) (defgeneric pm-indent-line (&optional chunkmode span) "Indent current line. Protect and call original indentation function associated with the chunkmode.") (defun pm--indent-line (span) ;; istr is auto-indent string (unwind-protect (save-restriction (pm--comment-region 1 (nth 1 span)) (pm/narrow-to-span span) (funcall pm--indent-line-function-original)) (pm--uncomment-region 1 (nth 1 span)))) (defmethod pm-indent-line ((chunkmode pm-chunkmode) &optional span) (pm--indent-line span)) (defmethod pm-indent-line ((chunkmode pm-hbtchunkmode) &optional span) "Indent line in inner chunkmodes. When point is at the beginning of head or tail, use parent chunk to indent." ;; sloppy work: ;; Assumes multiline chunks and single-line head/tail. ;; Assumes current buffer is the correct buffer. (let ((pos (point)) shift delta) (cond ((or (eq 'head (car span)) (eq 'tail (car span))) ;; use parent's indentation function in head and tail (back-to-indentation) (setq delta (- pos (point))) (backward-char) (let ((parent-span (pm/get-innermost-span))) (pm-select-buffer (car (last parent-span)) parent-span) (forward-char) (pm--indent-line parent-span) (when (eq 'tail (car span)) (setq shift (pm--get-head-shift parent-span)) (indent-to (+ shift (- (point) (point-at-bol)))))) (if (> delta 0) (goto-char (+ (point) delta)))) (t (setq shift (pm--get-head-shift span)) (pm--indent-line span) (when (= (current-column) 0) (setq shift (+ shift (oref chunkmode :indent-offset)))) (setq delta (- (point) (point-at-bol))) (beginning-of-line) (indent-to shift) (goto-char (+ (point) delta)))))) ;; fixme: This one is nowhere used? (defmethod pm-indent-line ((chunkmode pm-polymode-multi-auto) &optional span) (pm-select-buffer chunkmode span) (pm-indent-line pm/chunkmode span)) (defun pm--get-head-shift (span) (save-excursion (goto-char (cadr span)) (back-to-indentation) (- (point) (point-at-bol)))) ;;; FACES (defgeneric pm-get-adjust-face (chunkmode &optional type)) (defmethod pm-get-adjust-face ((chunkmode pm-chunkmode) &optional type) (oref chunkmode :adjust-face)) (defmethod pm-get-adjust-face ((chunkmode pm-hbtchunkmode) &optional type) (setq type (or type pm/type)) (cond ((eq type 'head) (oref chunkmode :head-adjust-face)) ((eq type 'tail) (if (eq 'head (oref pm/chunkmode :tail-adjust-face)) (oref pm/chunkmode :head-adjust-face) (oref pm/chunkmode :tail-adjust-face))) (t (oref pm/chunkmode :adjust-face)))) (provide 'polymode-methods)