;;; mumamo.el --- Multiple major modes in a buffer ;; ;; Author: Lennart Borgman (lennart O borgman A gmail O com) ;; Maintainer: ;; Created: Fri Mar 09 2007 (defconst mumamo:version "0.91") ;;Version: ;; Last-Updated: 2009-10-19 Mon ;; URL: http://OurComments.org/Emacs/Emacs.html ;; Keywords: ;; Compatibility: ;; ;; Features that might be required by this library: ;; ;; `appmenu', `apropos', `backquote', `button', `bytecomp', `cl', ;; `comint', `compile', `easymenu', `flyspell', `grep', `ido', ;; `ispell', `mail-prsvr', `mlinks', `mm-util', `nxml-enc', ;; `nxml-glyph', `nxml-mode', `nxml-ns', `nxml-outln', ;; `nxml-parse', `nxml-rap', `nxml-util', `ourcomments-util', ;; `recentf', `ring', `rng-dt', `rng-loc', `rng-match', ;; `rng-parse', `rng-pttrn', `rng-uri', `rng-util', `rng-valid', ;; `rx', `sgml-mode', `timer', `tool-bar', `tree-widget', ;; `url-expand', `url-methods', `url-parse', `url-util', ;; `url-vars', `wid-edit', `xmltok'. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Commentary: ;; ;; In some cases you may find that it is quite hard to write one major ;; mode that does everything for the type of file you want to handle. ;; That is the case for example for a PHP file where there comes ;; useful major modes with Emacs for the html parts, and where you can ;; get a major mode for PHP from other sources (see EmacsWiki for ;; Aaron Hawleys php-mode.el, or the very similar version that comes ;; with nXhtml). ;; ;; Using one major mode for the HTML part and another for the PHP part ;; sounds like a good solution. But this means you want to use (at ;; least) two major modes in the same buffer. ;; ;; This file implements just that, support for MUltiple MAjor MOdes ;; (mumamo) in a buffer. ;; ;; ;;;; Usage: ;; ;; The multiple major mode support is turned on by calling special ;; functions which are used nearly the same way as major modes. See ;; `mumamo-defined-multi-major-modes' for more information about those ;; functions. ;; ;; Each such function defines how to take care of a certain mix of ;; major functions in the buffer. We call them "multi major modes". ;; ;; You may call those functions directly (like you can with major mode ;; functions) or you may use them in for example `auto-mode-alist'. ;; ;; You can load mumamo in your .emacs with ;; ;; (require 'mumamo-fun) ;; ;; or you can generate an autoload file from mumamo-fun.el ;; ;; Note that no multi major mode functions are defined in this file. ;; Together with this file comes the file mumamo-fun.el that defines ;; some such functions. All those functions defined in that file are ;; marked for autoload. ;; ;; ;; ;; Thanks to Stefan Monnier for beeing a good and knowledgeable ;; speaking partner for some difficult parts while I was trying to ;; develop this. ;; ;; Thanks to RMS for giving me support and ideas about the programming ;; interface. That simplified the code and usage quite a lot. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; How to add support for a new mix of major modes ;; ;; This is done by creating a new function using ;; `define-mumamo-multi-major-mode'. See that function for more ;; information. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Information for major mode authors ;; ;; There are a few special requirements on major modes to make them ;; work with mumamo: ;; ;; - fontification-functions should be '(jit-lock-function). However ;; nxml-mode derivates can work too, see the code for more info. ;; ;; - narrowing should be respected during fontification and ;; indentation when font-lock-dont-widen is non-nil. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Information for minor mode authors ;; ;; Some minor modes are written to be specific for the file edited in ;; the buffer and some are written to be specific for a major ;; modes. Others are emulating another editor. Those are probably ;; global, but might still have buffer local values. ;; ;; Those minor modes that are not meant to be specific for a major ;; mode should probably survive changing major mode in the ;; buffer. That is mostly not the case in Emacs today. ;; ;; There are (at least) two type of values for those minor modes that ;; sometimes should survive changing major mode: buffer local ;; variables and functions added locally to hooks. ;; ;; * Some buffer local variables are really that - buffer local. Other ;; are really meant not for the buffer but for the major mode or ;; some minor mode that is local to the buffer. ;; ;; If the buffer local variable is meant for the buffer then it is ;; easy to make them survive changing major mode: just add ;; ;; (put 'VARIABLE 'permanent-local t) ;; ;; to those variables. That will work regardless of the way major ;; mode is changed. ;; ;; If one only wants the variables to survive the major mode change ;; that is done when moving between chunks with different major ;; modes then something different must be used. To make a variable ;; survive this, but not a major mode change for the whole buffer, ;; call any the function `mumamo-make-variable-buffer-permanent': ;; ;; (mumamo-make-variable-buffer-permanent 'VARIABLE) ;; ;; * For functions entered to local hooks use this ;; ;; (put 'FUNSYM 'permanent-local-hook t) ;; (add-hook 'HOOKSYM 'FUNSYM nil t) ;; ;; where HOOKSYM is the hook and FUNSYM is the function. ;; ;; * Some functions that are run in `change-major-mode' and dito ;; after- must be avoided when mumamo changes major mode. The ;; functions to avoid should be listed in ;; ;; `mumamo-change-major-mode-no-nos' ;; `mumamo-after-change-major-mode-no-nos' ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Comments on code etc: ;; ;; This is yet another way to try to get different major modes for ;; different chunks of a buffer to work. (I borrowed the term "chunk" ;; here from multi-mode.el.) I am aware of two main previous elisp ;; packages that tries to do this, multi-mode.el and mmm-mode.el. ;; (See http://www.emacswiki.org/cgi-bin/wiki/MultipleModes where ;; there are also some other packages mentioned.) The solutions in ;; those are a bit different from the approach here. ;; ;; The idea of doing it the way mumamo does it is of course based on a ;; hope that switching major mode when moving between chunks should be ;; quick. I found that it took from 0 - 62 000 ms, typically 0 - 16 ;; 000 ms on a 3ghz cpu. However unfortunately this is not the whole ;; truth. It could take longer time, depending on what is run in the ;; hooks: The major mode specific hook, `after-change-major-mode-hook' ;; and `change-major-mode-hook'. ;; ;; Because it currently may take long enough time switching major mode ;; when moving between chunks to disturb smooth moving around in the ;; buffer I have added a way to let the major mode switching be done ;; after moving when Emacs is idle. This is currently the default, but ;; see the custom variable `mumamo-set-major-mode-delay'. ;; ;; Since the intention is to set up the new major mode the same way as ;; it should have been done if this was a major mode for the whole ;; buffer these hooks must be run. However if this idea is developed ;; further some of the things done in these hooks (like switching on ;; minor modes) could perhaps be streamlined so that switching minor ;; modes off and then on again could be avoided. In fact there is ;; already tools for this in mumamo.el, see the section below named ;; "Information for minor mode authors". ;; ;; Another problem is that the major modes must use ;; `font-lock-fontify-region-function'. Currently the only major ;; modes I know that does not do this are `nxml-mode' and its ;; derivatives. ;; ;; The indentation is currently working rather ok, but with the price ;; that buffer modified is sometimes set even though there are no ;; actual changes. That seems a bit unnecessary and it could be ;; avoided if the indentation functions for the the various major ;; modes were rewritten so that you could get the indentation that ;; would be done instead of actually doing the indentation. (Or ;; mumamo could do this better, but I do not know how right now.) ;; ;; See also "Known bugs and problems etc" below. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Known bugs: ;; ;; - See the various FIX-ME for possible bugs. See also below. ;; ;; ;;;; Known problems and ideas: ;; ;; - There is no way in Emacs to tell a mode not to change ;; fontification when changing to or from that mode. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Change log: ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;; Code: (eval-when-compile (require 'cl)) (eval-when-compile (require 'cc-engine)) (eval-when-compile (require 'desktop)) (eval-when-compile (require 'flyspell)) (eval-when-compile (require 'rngalt nil t)) (eval-when-compile (require 'nxml-mode nil t)) (eval-when-compile (when (featurep 'nxml-mode) (require 'rng-valid nil t) ;;(require 'rngalt nil t) )) (eval-when-compile (require 'sgml-mode)) ;; For sgml-xml-mode ;; For `define-globalized-minor-mode-with-on-off': ;;(require 'ourcomments-util) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; rng-valid.el support (defvar rng-get-major-mode-chunk-function nil "Function to use to get major mode chunk. It should take one argument, the position where to get the major mode chunk. This is to be set by multiple major mode frame works, like mumamo. See also `rng-valid-nxml-major-mode-chunk-function' and `rng-end-major-mode-chunk-function'. Note that all three variables must be set.") (make-variable-buffer-local 'rng-get-major-mode-chunk-function) (put 'rng-get-major-mode-chunk-function 'permanent-local t) (defvar rng-valid-nxml-major-mode-chunk-function nil "Function to use to check if nxml can parse major mode chunk. It should take one argument, the chunk. For more info see also `rng-get-major-mode-chunk-function'.") (make-variable-buffer-local 'rng-valid-nxml-major-mode-chunk-function) (put 'rng-valid-nxml-major-mode-chunk-function 'permanent-local t) (defvar rng-end-major-mode-chunk-function nil "Function to use to get the end of a major mode chunk. It should take one argument, the chunk. For more info see also `rng-get-major-mode-chunk-function'.") (make-variable-buffer-local 'rng-end-major-mode-chunk-function) (put 'rng-end-major-mode-chunk-function 'permanent-local t) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Some variables (defvar mumamo-major-mode-indent-line-function nil) (make-variable-buffer-local 'mumamo-major-mode-indent-line-function) (defvar mumamo-buffer-locals-per-major nil) (make-variable-buffer-local 'mumamo-buffer-locals-per-major) (put 'mumamo-buffer-locals-per-major 'permanent-local t) (defvar mumamo-just-changed-major nil "Avoid refontification when switching major mode. Set to t by `mumamo-set-major'. Checked and reset to nil by `mumamo-jit-lock-function'.") (make-variable-buffer-local 'mumamo-just-changed-major) (defvar mumamo-multi-major-mode nil "The function that handles multiple major modes. If this is nil then multiple major modes in the buffer is not handled by mumamo. Set by functions defined by `define-mumamo-multi-major-mode'.") (make-variable-buffer-local 'mumamo-multi-major-mode) (put 'mumamo-multi-major-mode 'permanent-local t) (defvar mumamo-set-major-running nil "Internal use. Handling of mumamo turn off.") (defun mumamo-chunk-car (chunk prop) (car (overlay-get chunk prop))) (defun mumamo-chunk-cadr (chunk prop) (cadr (overlay-get chunk prop))) ;; (let ((l '(1 2))) (setcar (nthcdr 1 l) 10) l) ;; setters (defsubst mumamo-chunk-value-set-min (chunk-values min) "In CHUNK-VALUES set min value to MIN. CHUNK-VALUES should have the format return by `mumamo-create-chunk-values-at'." (setcar (nthcdr 0 chunk-values) min)) (defsubst mumamo-chunk-value-set-max (chunk-values max) "In CHUNK-VALUES set max value to MAX. See also `mumamo-chunk-value-set-min'." (setcar (nthcdr 1 chunk-values) max)) (defsubst mumamo-chunk-value-set-syntax-min (chunk-values min) "In CHUNK-VALUES set min syntax diff value to MIN. See also `mumamo-chunk-value-set-min'." (setcar (nthcdr 3 chunk-values) min)) (defsubst mumamo-chunk-value-set-syntax-max (chunk-values max) "In CHUNK-VALUES set max syntax diff value to MAX. See also `mumamo-chunk-value-set-min'." (setcar (nthcdr 3 chunk-values) max)) ;; getters (defsubst mumamo-chunk-value-min (chunk-values) "Get min value from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 0 chunk-values)) (defsubst mumamo-chunk-value-max (chunk-values) "Get max value from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 1 chunk-values)) (defsubst mumamo-chunk-value-major (chunk-values) "Get major value from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 2 chunk-values)) (defsubst mumamo-chunk-value-syntax-min (chunk-values) "Get min syntax diff value from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 3 chunk-values)) (defsubst mumamo-chunk-value-syntax-max (chunk-values) "Get max syntax diff value from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 4 chunk-values)) (defsubst mumamo-chunk-value-parseable-by (chunk-values) "Get parseable-by from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'. For parseable-by see `mumamo-find-possible-chunk'." (nth 5 chunk-values)) ;; (defsubst mumamo-chunk-prev-chunk (chunk-values) ;; "Get previous chunk from CHUNK-VALUES. ;; See also `mumamo-chunk-value-set-min'." ;; (nth 6 chunk-values)) (defsubst mumamo-chunk-value-fw-exc-fun (chunk-values) "Get function that find chunk end from CHUNK-VALUES. See also `mumamo-chunk-value-set-min'." (nth 6 chunk-values)) (defsubst mumamo-chunk-major-mode (chunk) "Get major mode specified in CHUNK." ;;(assert chunk) ;;(assert (overlay-buffer chunk)) (let ((mode-spec (if chunk (mumamo-chunk-car chunk 'mumamo-major-mode) (mumamo-main-major-mode)))) (mumamo-major-mode-from-modespec mode-spec))) (defsubst mumamo-chunk-syntax-min-max (chunk no-obscure) (when chunk (let* ((ovl-end (overlay-end chunk)) (ovl-start (overlay-start chunk)) (syntax-min (min ovl-end (+ ovl-start (or (overlay-get chunk 'mumamo-syntax-min-d) 0)))) ;;(dummy (msgtrc "chunk-syntax-min-max:syntax-min=%s, chunk=%S" syntax-min chunk)) (syntax-max (max ovl-start (- (overlay-end chunk) (or (overlay-get chunk 'mumamo-syntax-max-d) 0) (if (= (1+ (buffer-size)) (overlay-end chunk)) 0 ;; Note: We must subtract one here because ;; overlay-end is +1 from the last point in the ;; overlay. ;; ;; This cured the problem with ;; kubica-freezing-i.html that made Emacs loop ;; in `font-lock-extend-region-multiline'. But ;; was it really this one, I can't find any ;; 'font-lock-multiline property. So it should ;; be `font-lock-extend-region-whole-lines'. ;; ;; Should not the problem then be the value of font-lock-end? ;; ;; Fix-me: however this is not correct since it ;; leads to not fontifying the last character in ;; the chunk, see bug 531324. ;; ;; I think this is cured by now. I have let ;; bound `font-lock-extend-region-functions' ;; once more before the call to ;; `font-lock-fontify-region'. 0 ;;0 )))) (obscure (unless no-obscure (overlay-get chunk 'obscured))) (region-info (cadr obscure)) (obscure-min (car region-info)) (obscure-max (cdr region-info)) ;;(dummy (message "syn-mn-mx:obs=%s r-info=%s ob=%s/%s" obscure region-info obscure-min obscure-max )) (actual-min (max (or obscure-min ovl-start) (or syntax-min ovl-start))) (actual-max (min (or obscure-max ovl-end) (or syntax-max ovl-end))) (maj (mumamo-chunk-car chunk 'mumamo-major-mode)) ;;(dummy (message "syn-mn-mx:obs=%s r-info=%s ob=%s/%s ac=%s/%s" obscure region-info obscure-min obscure-max actual-min actual-max)) ) (cons actual-min actual-max)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Macros ;; Borrowed from font-lock.el (defmacro mumamo-save-buffer-state (varlist &rest body) "Bind variables according to VARLIST and eval BODY restoring buffer state. Do not record undo information during evaluation of BODY." (declare (indent 1) (debug let)) (let ((modified (make-symbol "modified"))) `(let* ,(append varlist `((,modified (buffer-modified-p)) (buffer-undo-list t) (inhibit-read-only t) (inhibit-point-motion-hooks t) (inhibit-modification-hooks t) deactivate-mark buffer-file-name buffer-file-truename)) (progn ,@body) (unless ,modified (restore-buffer-modified-p nil))))) ;; From jit-lock.el: (defmacro mumamo-jit-with-buffer-unmodified (&rest body) "Eval BODY, preserving the current buffer's modified state." (declare (debug t)) (let ((modified (make-symbol "modified"))) `(let ((,modified (buffer-modified-p))) (unwind-protect (progn ,@body) (unless ,modified (restore-buffer-modified-p nil)))))) (defmacro mumamo-with-buffer-prepared-for-jit-lock (&rest body) "Execute BODY in current buffer, overriding several variables. Preserves the `buffer-modified-p' state of the current buffer." (declare (debug t)) `(mumamo-jit-with-buffer-unmodified (let ((buffer-undo-list t) (inhibit-read-only t) (inhibit-point-motion-hooks t) (inhibit-modification-hooks t) deactivate-mark buffer-file-name buffer-file-truename) ,@body))) (defmacro mumamo-condition-case (var body-form &rest handlers) "Like `condition-case', but optional. If `mumamo-use-condition-case' is non-nil then do (condition-case VAR BODY-FORM HANDLERS). Otherwise just evaluate BODY-FORM." (declare (indent 2) (debug t)) `(if (not mumamo-use-condition-case) (let* ((debugger (or mumamo-debugger 'debug)) (debug-on-error (if debugger t debug-on-error))) ,body-form) (condition-case ,var ,body-form ,@handlers))) (defmacro mumamo-msgfntfy (format-string &rest args) "Give some messages during fontification. This macro should just do nothing during normal use. However if there are any problems you can uncomment one of the lines in this macro and recompile/reeval mumamo.el to get those messages. You have to search the code to see where you will get them. All uses are in this file. FORMAT-STRING and ARGS have the same meaning as for the function `message'." ;;(list 'apply (list 'quote 'msgtrc) format-string (append '(list) args)) ;;(list 'apply (list 'quote 'message) format-string (append '(list) args)) ;;(list 'progn 'apply (list 'quote 'message) format-string (append '(list) args) nil) ;; (condition-case err ;; (list 'apply (list 'quote 'message) format-string (append '(list) args)) ;; <-- ;; (error (message "err in msgfntfy %S" err))) ;;(message "%s %S" format-string args) ;;(list 'apply (list 'quote 'message) (list 'concat "%s: " format-string) ;; (list 'get-internal-run-time) (append '(list) args)) ) ;;(mumamo-msgfntfy "my-format=%s" (get-internal-run-time)) (defmacro mumamo-msgindent (format-string &rest args) "Give some messages during indentation. This macro should just do nothing during normal use. However if there are any problems you can uncomment one of the lines in this macro and recompile/reeval mumamo.el to get those messages. You have to search the code to see where you will get them. All uses are in this file. FORMAT-STRING and ARGS have the same meaning as for the function `message'." ;;(list 'apply (list 'quote 'msgtrc) format-string (append '(list) args)) ;;(list 'apply (list 'quote 'message) format-string (append '(list) args)) ;; <--- ;;(list 'apply (list 'quote 'message) (list 'concat "%s: " format-string) ;; (list 'get-internal-run-time) (append '(list) args)) ) (defmacro mumamo-with-major-mode-setup (major for-what &rest body) "Run code with some local variables set as in specified major mode. Set variables as needed for major mode MAJOR when doing FOR-WHAT and then run BODY using `with-syntax-table'. FOR-WHAT is used to choose another major mode than MAJOR in certain cases. It should be 'fontification or 'indentation. Note: We must let-bind the variables here instead of make them buffer local since they otherwise could be wrong at \(point) in top level \(ie user interaction level)." (declare (indent 2) (debug t)) `(let ((need-major-mode (mumamo-get-major-mode-substitute ,major ,for-what))) ;;(msgtrc "mumamo-with-major-mode-setup %s => %s, modified=%s" ,major need-major-mode (buffer-modified-p)) ;;(msgtrc "with-major-mode-setup <<<<<<<<<< body=%S\n>>>>>>>>>>" '(progn ,@body)) ;;(msgtrc "with-major-mode-setup:in buffer %s after-chunk=%s" (current-buffer) (when (boundp 'after-chunk) after-chunk)) (let ((major-mode need-major-mode) (evaled-set-mode (mumamo-get-major-mode-setup need-major-mode))) ;;(message ">>>>>> before %s" evaled-set-mode) ;;(message ">>>>>> before %s, body=%s" evaled-set-mode (list ,@body)) (funcall (symbol-value evaled-set-mode) (list 'progn ,@body)) ;;(mumamo-msgfntfy "<<<<<< after evaled-set-mode modified=%s" (buffer-modified-p)) ))) (defmacro mumamo-with-major-mode-fontification (major &rest body) "With fontification variables set as major mode MAJOR eval BODY. This is used during font locking and indentation. The variables affecting those are set as they are in major mode MAJOR. See the code in `mumamo-fetch-major-mode-setup' for exactly which local variables that are set." (declare (indent 1) (debug t)) `(mumamo-with-major-mode-setup ,major 'fontification ,@body)) ;; Fontification disappears in for example *grep* if ;; font-lock-mode-major-mode is 'permanent-local t. ;;(put 'font-lock-mode-major-mode 'permanent-local t) (defmacro mumamo-with-major-mode-indentation (major &rest body) "With indentation variables set as in another major mode do things. Same as `mumamo-with-major-mode-fontification' but for indentation. See that function for some notes about MAJOR and BODY." (declare (indent 1) (debug t)) `(mumamo-with-major-mode-setup ,major 'indentation ,@body)) ;; fix-me: tell no sub-chunks in sub-chunks ;;;###autoload (defmacro define-mumamo-multi-major-mode (fun-sym spec-doc chunks) "Define a function that turn on support for multiple major modes. Define a function FUN-SYM that set up to divide the current buffer into chunks with different major modes. The documentation string for FUN-SYM should contain the special documentation in the string SPEC-DOC, general documentation for functions of this type and information about chunks. The new function will use the definitions in CHUNKS \(which is called a \"chunk family\") to make the dividing of the buffer. The function FUN-SYM can be used to setup a buffer instead of a major mode function: - The function FUN-SYM can be called instead of calling a major mode function when you want to use multiple major modes in a buffer. - The defined function can be used instead of a major mode function in for example `auto-mode-alist'. - As the very last thing FUN-SYM will run the hook FUN-SYM-hook, just as major modes do. - There is also a general hook, `mumamo-turn-on-hook', which is run when turning on mumamo with any of these functions. This is run right before the hook specific to any of the functions above that turns on the multiple major mode support. - The multi major mode FUN-SYM has a keymap named FUN-SYM-map. This overrides the major modes' keymaps since it is handled as a minor mode keymap. - There is also a special mumamo keymap, `mumamo-map' that is active in every buffer with a multi major mode. This is also handled as a minor mode keymap and therefor overrides the major modes' keymaps. - However when this support for multiple major mode is on the buffer is divided into chunks, each with its own major mode. - The chunks are fontified according the major mode assigned to them for that. - Indenting is also done according to the major mode assigned to them for that. - The actual major mode used in the buffer is changed to the one in the chunk when moving point between these chunks. - When major mode is changed the hooks for the new major mode, `after-change-major-mode-hook' and `change-major-mode-hook' are run. - There will be an alias for FUN-SYM called mumamo-alias-FUN-SYM. This can be used to check whic multi major modes have been defined. ** A little bit more technical description: The dividing of a buffer into chunks is done during fontification by `mumamo-get-chunk-at'. The name of the function is saved in in the buffer local variable `mumamo-multi-major-mode' when the function is called. All functions defined by this macro is added to the list `mumamo-defined-multi-major-modes'. Basically Mumamo handles only major modes that uses jit-lock. However as a special effort also `nxml-mode' and derivatives thereof are handled. Since it seems impossible to me to restrict those major modes fontification to only a chunk without changing `nxml-mode' the fontification is instead done by `html-mode'/`sgml-mode' for chunks using `nxml-mode' and its derivates. CHUNKS is a list where each entry have the format \(CHUNK-DEF-NAME MAIN-MAJOR-MODE SUBMODE-CHUNK-FUNCTIONS) CHUNK-DEF-NAME is the key name by which the entry is recognized. MAIN-MAJOR-MODE is the major mode used when there is no chunks. If this is nil then `major-mode' before turning on this mode will be used. SUBMODE-CHUNK-FUNCTIONS is a list of the functions that does the chunk division of the buffer. They are tried in the order they appear here during the chunk division process. If you want to write new functions for chunk divisions then please see `mumamo-find-possible-chunk'. You can perhaps also use `mumamo-quick-static-chunk' which is more easy-to-use alternative. See also the file mumamo-fun.el where there are many routines for chunk division. When you write those new functions you may want to use some of the functions for testing chunks: `mumamo-test-create-chunk-at' `mumamo-test-create-chunks-at-all' `mumamo-test-easy-make' `mumamo-test-fontify-region' These are in the file mumamo-test.el." ;;(let ((c (if (symbolp chunks) (symbol-value chunks) chunks))) (message "c=%S" c)) (let* (;;(mumamo-describe-chunks (make-symbol "mumamo-describe-chunks")) (turn-on-fun (if (symbolp fun-sym) fun-sym (error "Parameter FUN-SYM must be a symbol"))) (turn-on-fun-alias (intern (concat "mumamo-alias-" (symbol-name fun-sym)))) ;; Backward compatibility nXhtml v 1.60 (turn-on-fun-old (when (string= (substring (symbol-name fun-sym) -5) "-mode") (intern (substring (symbol-name fun-sym) 0 -5)))) (turn-on-hook (intern (concat (symbol-name turn-on-fun) "-hook"))) (turn-on-map (intern (concat (symbol-name turn-on-fun) "-map"))) (turn-on-hook-doc (concat "Hook run at the very end of `" (symbol-name turn-on-fun) "'.")) (chunks2 (if (symbolp chunks) (symbol-value chunks) chunks)) (docstring (concat spec-doc " This function is called a multi major mode. It sets up for multiple major modes in the buffer in the following way: " ;; Fix-me: During byte compilation the next line is not ;; expanded as I thought because the functions in CHUNKS ;; are not defined. How do I fix this? Move out the ;; define-mumamo-multi-major-mode calls? (funcall 'mumamo-describe-chunks chunks2) " At the very end this multi major mode function runs first the hook `mumamo-turn-on-hook' and then `" (symbol-name turn-on-hook) "'. There is a keymap specific to this multi major mode, but it is not returned by `current-local-map' which returns the chunk's major mode's local keymap. The multi mode keymap is named `" (symbol-name turn-on-map) "'. The main use for a multi major mode is to use it instead of a normal major mode in `auto-mode-alist'. \(You can of course call this function directly yourself too.) The value of `mumamo-multi-major-mode' tells you which multi major mode if any has been turned on in a buffer. For more information about multi major modes please see `define-mumamo-multi-major-mode'. Note: When adding new font-lock keywords for major mode chunks you should use the function `mumamo-refresh-multi-font-lock' afterwards. " ))) `(progn ;;(add-to-list 'mumamo-defined-multi-major-modes (cons (car ',chunks2) ',turn-on-fun)) (mumamo-add-to-defined-multi-major-modes (cons (car ',chunks2) ',turn-on-fun)) (defvar ,turn-on-hook nil ,turn-on-hook-doc) (defvar ,turn-on-map (make-sparse-keymap) ,(concat "Keymap for multi major mode function `" (symbol-name turn-on-fun) "'")) (defvar ,turn-on-fun nil) (make-variable-buffer-local ',turn-on-fun) (put ',turn-on-fun 'permanent-local t) (put ',turn-on-fun 'mumamo-chunk-family (copy-tree ',chunks2)) (put ',turn-on-fun-alias 'mumamo-chunk-family (copy-tree ',chunks2)) (defun ,turn-on-fun nil ,docstring (interactive) (let ((old-major-mode (or mumamo-major-mode major-mode))) (kill-all-local-variables) (run-hooks 'change-major-mode-hook) (setq mumamo-multi-major-mode ',turn-on-fun) (setq ,turn-on-fun t) (mumamo-add-multi-keymap ',turn-on-fun ,turn-on-map) (setq mumamo-current-chunk-family (copy-tree ',chunks2)) (mumamo-turn-on-actions old-major-mode) (run-hooks ',turn-on-hook))) (defalias ',turn-on-fun-alias ',turn-on-fun) (when (intern-soft ',turn-on-fun-old) (defalias ',turn-on-fun-old ',turn-on-fun)) ))) ;;;###autoload (defun mumamo-add-to-defined-multi-major-modes (entry) (add-to-list 'mumamo-defined-multi-major-modes entry)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Debugging etc (defsubst mumamo-while (limit counter where) (let ((count (symbol-value counter))) (if (= count limit) (progn (msgtrc "Reached (while limit=%s, where=%s)" limit where) nil) (set counter (1+ count))))) ;; (defun dbg-smarty-err () ;; ;; (insert "}{") ;; ;; (insert "}{") ;; ;; (backward-char) ;; ;; (backward-char) ;; ;; (search-backward "}") ;; ;; This gives an error rather often, but not always: ;; (delete-char 3) ;; (search-backward "}") ;; ) ;; (defun dbg-smarty-err2 () ;; (forward-char 5) ;; (insert "}{") ;; ;; Start in nxhtml part and make sure the insertion is in smarty ;; ;; part. Gives reliably an error if moved backward so point stay in ;; ;; the new nxhtml-mode part, otherwise not. ;; ;; ;; ;; Eh, no. If chunk family is changed and reset there is no more an ;; ;; error. ;; ;; ;; ;; Seems to be some race condition, but I am unable to understand ;; ;; how. I believed that nxml always left in a reliable state. Is ;; ;; this a state problem in mumamo or nxml? I am unable to make it ;; ;; happen again now. ;; ;; ;; ;; I saw one very strange thing: The error message got inserted in ;; ;; the .phps buffer once. How could this happen? Is this an Emacs ;; ;; bug? Can't see how this could happen since it is the message ;; ;; function that outputs the message. A w32 race condition? Are ;; ;; people aware that the message queue runs in parallell? (I have ;; ;; tried to ask on the devel list, but got no answer at that time.) ;; (backward-char 2) ;; ) (defvar msgtrc-buffer "*Messages*" ;;"*trace-output*" "Buffer or name of buffer for trace messages. See `msgtrc'." ) (defun msgtrc (format-string &rest args) "Print message to `msgtrc-buffer'. Arguments FORMAT-STRING and ARGS are like for `message'." (if nil nil ;;(apply 'message format-string args) ;; bug#3350 prevents use of this: (let ((trc-buffer (get-buffer-create msgtrc-buffer)) ;; Cure 3350: Stop insert from deactivating the mark (deactivate-mark)) (with-current-buffer trc-buffer (goto-char (point-max)) (insert "MU:" (apply 'format format-string args) "\n") ;;(insert "constant string\n") (when buffer-file-name (write-region nil nil buffer-file-name)))))) (defvar mumamo-message-file-buffer nil) (defsubst mumamo-msgtrc-to-file () "Start writing message to file. Erase `msgtrc-buffer' first." (unless mumamo-message-file-buffer (setq mumamo-message-file-buffer (find-file-noselect "c:/emacs/bugs/temp-messages.txt")) (setq msgtrc-buffer mumamo-message-file-buffer) (with-current-buffer mumamo-message-file-buffer (erase-buffer)))) (defvar mumamo-display-error-lwarn nil "Set to t to call `lwarn' on fontification errors. If this is t then `*Warnings*' buffer will popup on fontification errors.") (defvar mumamo-display-error-stop nil "Set to t to stop fontification on errors.") (defun mumamo-message-with-face (msg face) "Put MSG with face FACE in *Messages* buffer." (let ((start (+ (with-current-buffer msgtrc-buffer (point-max)) 1)) ;; This is for the echo area: (msg-with-face (propertize (format "%s" msg) 'face face))) (msgtrc "%s" msg-with-face) ;; This is for the buffer: (with-current-buffer msgtrc-buffer (goto-char (point-max)) (backward-char) (put-text-property start (point) 'face face)))) ;;(run-with-idle-timer 1 nil 'mumamo-show-report-message) (defun mumamo-show-report-message () "Tell the user there is a long error message." (save-match-data ;; runs in timer (mumamo-message-with-face "MuMaMo error, please look in the *Messages* buffer" 'highlight))) ;; This code can't be used now because `debugger' is currently not ;; useable in timers. I keep it here since I hope someone will make it ;; possible in the future. ;; ;; (defmacro mumamo-get-backtrace-if-error (bodyform) ;; "Evaluate BODYFORM, return a list with error message and backtrace. ;; If there is an error in BODYFORM then return a list with the ;; error message and the backtrace as a string. Otherwise return ;; nil." ;; `(let* ((debugger ;; (lambda (&rest debugger-args) ;; (let ((debugger-ret (with-output-to-string (backtrace)))) ;; ;; I believe we must put the result in a buffer, ;; ;; otherwise `condition-case' might erase it: ;; (with-current-buffer (get-buffer-create "TEMP GET BACKTRACE") ;; (erase-buffer) ;; (insert debugger-ret))))) ;; (debug-on-error t) ;; (debug-on-signal t)) ;; (mumamo-condition-case err ;; (progn ;; ,bodyform ;; nil) ;; (error ;; (let* ((errmsg (error-message-string err)) ;; (dbg1-ret ;; (with-current-buffer ;; (get-buffer "TEMP GET BACKTRACE") (buffer-string))) ;; ;; Remove lines from this routine: ;; (debugger-lines (split-string dbg1-ret "\n")) ;; (dbg-ret (mapconcat 'identity (nthcdr 6 debugger-lines) "\n")) ;; ) ;; (list errmsg (concat errmsg "\n" dbg-ret))))))) ;;(mumamo-display-error 'test-lwarn-type "testing 1=%s, 2=%s" "one" 'two) (defun mumamo-display-error (lwarn-type format-string &rest args) "Display a message plus traceback in the *Messages* buffer. Use this for errors that happen during fontification or when running a timer. LWARN-TYPE is used as the type argument to `lwarn' if warnings are displayed. FORMAT-STRING and ARGS are used as the corresponding arguments to `message' and `lwarn'. All the output from this function in the *Messages* buffer is displayed with the highlight face. After the message printed by `message' is traceback from where this function was called. Note: There is no error generated, just a traceback that is put in *Messages* as above. Display an error message using `message' and colorize it using the `highlight' face to make it more prominent. Add a backtrace colored with the `highlight' face to the buffer *Messages*. Then display the error message once again after this so that the user can see it. If `mumamo-display-error-lwarn' is non-nil, indicate the error by calling `lwarn'. This will display the `*Warnings*' buffer and thus makes it much more easy to spot that there was an error. If `mumamo-display-error-stop' is non-nil raise an error that may stop fontification." ;; Warnings are sometimes disturbning, make it optional: (when mumamo-display-error-lwarn (apply 'lwarn lwarn-type :error format-string args)) (let ((format-string2 (concat "%s: " format-string)) (bt (with-output-to-string (backtrace)))) (mumamo-message-with-face (concat (apply 'format format-string2 lwarn-type args) "\n" (format "** In buffer %s\n" (current-buffer)) bt) 'highlight) ;; Output message once again so the user can see it: (apply 'message format-string2 lwarn-type args) ;; But ... there might be more messages so wait until things has ;; calmed down and then show a message telling that there was an ;; error and that there is more information in the *Messages* ;; buffer. (run-with-idle-timer 1 nil 'mumamo-show-report-message) ;; Stop fontifying: (when mumamo-display-error-stop ;;(font-lock-mode -1) (setq font-lock-mode nil) (when (timerp jit-lock-context-timer) (cancel-timer jit-lock-context-timer)) (when (timerp jit-lock-defer-timer) (cancel-timer jit-lock-defer-timer)) (apply 'error format-string2 lwarn-type args)))) (defun mumamo-debug-to-backtrace (&rest debugger-args) "This function should give a backtrace during fontification errors. The variable `debugger' should then be this function. See the function `debug' for an explanation of DEBUGGER-ARGS. Fix-me: Can't use this function yet since the display routines uses safe_eval and safe_call." (mumamo-display-error 'mumamo-debug-to-backtrace "%s" (nth 1 debugger-args))) ;; (defun my-test-err3 () ;; (interactive) ;; (let ((debugger 'mumamo-debug-to-backtrace) ;; (debug-on-error t)) ;; (my-err) ;; )) ;;(my-test-err3() ;;(set-default 'mumamo-use-condition-case nil) ;;(set-default 'mumamo-use-condition-case t) (defvar mumamo-use-condition-case t) (make-variable-buffer-local 'mumamo-use-condition-case) (put 'mumamo-use-condition-case 'permanent-local t) (defvar mumamo-debugger 'mumamo-debug-to-backtrace) (make-variable-buffer-local 'mumamo-debugger) (put 'mumamo-debugger 'permanent-local t) ;; (defun my-test-err4 () ;; (interactive) ;; (mumamo-condition-case err ;; (my-errx) ;; (arith-error (message "here")) ;; (error (message "%s, %s" err (error-message-string err))) ;; )) (defvar mumamo-warned-once nil) (make-variable-buffer-local 'mumamo-warned-once) (put 'mumamo-warned-once 'permanent-local t) ; (append '(0 1) '(a b)) (defun mumamo-warn-once (type message &rest args) "Warn only once with TYPE, MESSAGE and ARGS. If the same problem happens again then do not warn again." (let ((msgrec (append (list type message) args))) (unless (member msgrec mumamo-warned-once) (setq mumamo-warned-once (cons msgrec mumamo-warned-once)) ;;(apply 'lwarn type :warning message args) (apply 'message (format "%s: %s" type message) args) ))) (defun mumamo-add-help-tabs () "Add key bindings for moving between buttons. Add bindings similar to those in `help-mode' for moving between text buttons." (local-set-key [tab] 'forward-button) (local-set-key [(meta tab)] 'backward-button) (local-set-key [(shift tab)] 'backward-button) (local-set-key [backtab] 'backward-button)) (defun mumamo-insert-describe-button (symbol type) "Insert a text button that describes SYMBOL of type TYPE." (let ((func `(lambda (btn) (funcall ',type ',symbol)))) (mumamo-add-help-tabs) (insert-text-button (symbol-name symbol) :type 'help-function 'face 'link 'action func))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Custom group ;;;###autoload (defgroup mumamo nil "Customization group for multiple major modes in a buffer." :group 'editing :group 'languages :group 'sgml :group 'nxhtml ) ;;(setq mumamo-set-major-mode-delay -1) ;;(setq mumamo-set-major-mode-delay 5) (defcustom mumamo-set-major-mode-delay idle-update-delay "Delay this number of seconds before setting major mode. When point enters a region where the major mode should be different than the current major mode, wait until Emacs has been idle this number of seconds before switching major mode. If negative switch major mode immediately. Ideally the switching of major mode should occur immediately when entering a region. However this can make movements a bit unsmooth for some major modes on a slow computer. Therefore on a slow computer use a short delay. If you have a fast computer and want to use mode specific movement commands then set this variable to -1. I tried to measure the time for switching major mode in mumamo. For most major modes it took 0 ms, but for `nxml-mode' and its derivate it took 20 ms on a 3GHz CPU." :type 'number :group 'mumamo) (defgroup mumamo-display nil "Customization group for mumamo chunk display." :group 'mumamo) (defun mumamo-update-this-buffer-margin-use () (mumamo-update-buffer-margin-use (current-buffer))) (define-minor-mode mumamo-margin-info-mode "Display chunk info in margin when on. Display chunk depth and major mode where a chunk begin in left or right margin. \(The '-mode' part of the major mode is stripped.) See also `mumamo-margin-use'. Note: When `linum-mode' is on the right margin is always used now \(since `linum-mode' uses the left)." :group 'mumamo-display (mumamo-update-this-buffer-margin-use) (if mumamo-margin-info-mode (progn ;;(add-hook 'window-configuration-change-hook 'mumamo-update-this-buffer-margin-use nil t) (add-hook 'linum-mode-hook 'mumamo-update-this-buffer-margin-use nil t) ) ;;(remove-hook 'window-configuration-change-hook 'mumamo-update-this-buffer-margin-use t) (remove-hook 'linum-mode-hook 'mumamo-update-this-buffer-margin-use t) )) ;;(put 'mumamo-margin-info-mode 'permanent-local t) (defun mumamo-margin-info-mode-turn-off () (mumamo-margin-info-mode -1)) (put 'mumamo-margin-info-mode-turn-off 'permanent-local-hook t) (define-globalized-minor-mode mumamo-margin-info-global-mode mumamo-margin-info-mode (lambda () (when (and (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode) (mumamo-margin-info-mode 1))) :group 'mumamo-display) (defcustom mumamo-margin-use '(left-margin 13) "Display chunk info in left or right margin if non-nil." :type '(list (radio (const :tag "Display chunk info in left margin" left-margin) (const :tag "Display chunk info in right margin" right-margin)) (integer :tag "Margin width (when used)" :value 13)) :set (lambda (sym val) (set-default sym val) (when (fboundp 'mumamo-update-all-buffers-margin-use) (mumamo-update-all-buffers-margin-use))) :group 'mumamo-display) (defun mumamo-update-all-buffers-margin-use () (dolist (buf (buffer-list)) (mumamo-update-buffer-margin-use buf))) (define-minor-mode mumamo-no-chunk-coloring "Use no background colors to distinguish chunks. When this minor mode is on in a buffer no chunk coloring is done in that buffer. This is overrides `mumamo-chunk-coloring'. It is meant for situations when you temporarily need to remove the background colors." :lighter " ΓΈ" :group 'mumamo-display (font-lock-mode -1) (font-lock-mode 1)) (put 'mumamo-no-chunk-coloring 'permanent-local t) ;; (setq mumamo-chunk-coloring 4) (defcustom mumamo-chunk-coloring 0 "Color chunks with depth greater than or equal to this. When 0 all chunks will be colored. If 1 all sub mode chunks will be colored, etc." :type '(integer :tag "Color chunks with depth greater than this") :group 'mumamo-display) (defface mumamo-background-chunk-major '((((class color) (min-colors 88) (background dark)) ;;:background "blue3") :background "MidnightBlue") (((class color) (min-colors 88) (background light)) ;;:background "lightgoldenrod2") :background "cornsilk") (((class color) (min-colors 16) (background dark)) :background "blue4") (((class color) (min-colors 16) (background light)) :background "cornsilk") (((class color) (min-colors 8)) :background "blue") (((type tty) (class mono)) :inverse-video t) (t :background "gray")) "Background colors for chunks in sub modes. You should only specify :background here, otherwise it will interfere with syntax highlighting." :group 'mumamo-display) (defface mumamo-background-chunk-submode1 '((((class color) (min-colors 88) (background dark)) ;;:background "blue3") :background "DarkGreen" ;;:background "#081010" ) (((class color) (min-colors 88) (background light)) ;;:background "lightgoldenrod2") :background "Azure") (((class color) (min-colors 16) (background dark)) :background "blue3") (((class color) (min-colors 16) (background light)) :background "azure") (((class color) (min-colors 8)) :background "Blue") (((type tty) (class mono)) :inverse-video t) (t :background "gray")) "Background colors for chunks in major mode. You should only specify :background here, otherwise it will interfere with syntax highlighting." :group 'mumamo-display) (defface mumamo-background-chunk-submode2 '((((class color) (min-colors 88) (background dark)) ;;:background "blue3") :background "dark green") (((class color) (min-colors 88) (background light)) ;;:background "lightgoldenrod2") :background "#e6ff96") (((class color) (min-colors 16) (background dark)) :background "blue3") (((class color) (min-colors 16) (background light)) :background "azure") (((class color) (min-colors 8)) :background "blue") (((type tty) (class mono)) :inverse-video t) (t :background "gray")) "Background colors for chunks in major mode. You should only specify :background here, otherwise it will interfere with syntax highlighting." :group 'mumamo-display) (defface mumamo-background-chunk-submode3 '((((class color) (min-colors 88) (background dark)) ;;:background "blue3") :background "dark green") (((class color) (min-colors 88) (background light)) ;;:background "lightgoldenrod2") :background "#f7d1f4") ;;:background "green") (((class color) (min-colors 16) (background dark)) :background "blue3") (((class color) (min-colors 16) (background light)) :background "azure") (((class color) (min-colors 8)) :background "blue") (((type tty) (class mono)) :inverse-video t) (t :background "gray")) "Background colors for chunks in major mode. You should only specify :background here, otherwise it will interfere with syntax highlighting." :group 'mumamo-display) (defface mumamo-background-chunk-submode4 '((((class color) (min-colors 88) (background dark)) ;;:background "blue3") :background "dark green") (((class color) (min-colors 88) (background light)) ;;:background "lightgoldenrod2") :background "orange") (((class color) (min-colors 16) (background dark)) :background "blue3") (((class color) (min-colors 16) (background light)) :background "azure") (((class color) (min-colors 8)) :background "blue") (((type tty) (class mono)) :inverse-video t) (t :background "gray")) "Background colors for chunks in major mode. You should only specify :background here, otherwise it will interfere with syntax highlighting." :group 'mumamo-display) (defcustom mumamo-background-chunk-major 'mumamo-background-chunk-major "Background colors for chunks in major mode. Pointer to face with background color. If you do not want any special background color use the face named default." :type 'face :group 'mumamo-display) (defcustom mumamo-background-chunk-submode1 'mumamo-background-chunk-submode1 "Background colors for chunks in sub modes. Pointer to face with background color. If you do not want any special background color use the face named default." :type 'face :group 'mumamo-display) (defcustom mumamo-background-chunk-submode2 'mumamo-background-chunk-submode2 "Background colors for chunks in sub modes. Pointer to face with background color. If you do not want any special background color use the face named default." :type 'face :group 'mumamo-display) (defcustom mumamo-background-chunk-submode3 'mumamo-background-chunk-submode3 "Background colors for chunks in sub modes. Pointer to face with background color. If you do not want any special background color use the face named default." :type 'face :group 'mumamo-display) (defcustom mumamo-background-chunk-submode4 'mumamo-background-chunk-submode4 "Background colors for chunks in sub modes. Pointer to face with background color. If you do not want any special background color use the face named default." :type 'face :group 'mumamo-display) ;; Fix-me: use and enhance this (defcustom mumamo-background-colors '(mumamo-background-chunk-major mumamo-background-chunk-submode1 mumamo-background-chunk-submode2 mumamo-background-chunk-submode3 mumamo-background-chunk-submode4 ) "List of background colors in order of use. First color is for main major mode chunks, then for submode chunks, sub-submode chunks etc. Colors are reused in cyclic order. The default colors are choosen so that inner chunks has a more standing out color the further in you get. This is supposed to be helpful when you make mistakes and the chunk nesting is not what you intended. Note: Only the light background colors have been set by me. The dark background colors might currently be unuseful. Contributions and suggestions are welcome! The values in the list should be symbols. Each symbol should either be 1: a variable symbol pointing to a face (or beeing nil) 2: a face symbol 3: a function with one argument (subchunk depth) returning a face symbol" :type '(repeat symbol) :group 'mumamo-display) ;;(mumamo-background-color 0) ;;(mumamo-background-color 1) ;;(mumamo-background-color 2) (defun mumamo-background-color (sub-chunk-depth) (when (and (not mumamo-no-chunk-coloring) (or (not (integerp mumamo-chunk-coloring)) ;; Old values (>= sub-chunk-depth mumamo-chunk-coloring))) (let* ((idx (when mumamo-background-colors (mod sub-chunk-depth (length mumamo-background-colors)))) (sym (when idx (nth idx mumamo-background-colors))) fac) (when sym (when (boundp sym) (setq fac (symbol-value sym)) (unless (facep fac) (setq fac nil))) (unless fac (when (facep sym) (setq fac sym))) (unless fac (when (fboundp sym) (setq fac (funcall sym sub-chunk-depth)))) (when fac (unless (facep fac) (setq fac nil))) fac )))) (defface mumamo-border-face-in '((t (:inherit font-lock-preprocessor-face :bold t :italic t :underline t))) "Face for marking borders." :group 'mumamo-display) (defface mumamo-border-face-out '((t (:inherit font-lock-preprocessor-face :bold t :italic t :underline t))) "Face for marking borders." :group 'mumamo-display) (defgroup mumamo-indentation nil "Customization group for mumamo chunk indentation." :group 'mumamo) (defcustom mumamo-submode-indent-offset 2 "Indentation of submode relative outer major mode. If this is nil then indentation first non-empty line in a subchunk will \(normally) be 0. See however `mumamo-indent-line-function-1' for special handling of first line in subsequent subchunks. See also `mumamo-submode-indent-offset-0'." :type '(choice integer (const :tag "No special")) :group 'mumamo-indentation) (defcustom mumamo-submode-indent-offset-0 0 "Indentation of submode at column 0. This value overrides `mumamo-submode-indent-offset' when the outer major mode above has indentation 0." :type '(choice integer (const :tag "No special")) :group 'mumamo-indentation) (defcustom mumamo-indent-major-to-use '( ;;(nxhtml-mode html-mode) (html-mode nxhtml-mode) ) "Major mode to use for indentation. This is normally the major mode specified for the chunk. Here you can make exceptions." :type '(repeat (list (symbol :tag "Major mode symbol specified") (command :tag "Major mode to use"))) :group 'mumamo-indentation) ;;(mumamo-indent-get-major-to-use 'nxhtml-mode) ;;(mumamo-indent-get-major-to-use 'html-mode) (defun mumamo-indent-get-major-to-use (major depth) (or (and (= depth 0) (cadr (assq major mumamo-indent-major-to-use))) major)) (defcustom mumamo-indent-widen-per-major '( (php-mode (use-widen)) (nxhtml-mode (use-widen (html-mumamo-mode nxhtml-mumamo-mode))) (html-mode (use-widen (html-mumamo-mode nxhtml-mumamo-mode))) ) "Wether do widen buffer during indentation. If not then the buffer is narrowed to the current chunk when indenting a line in a chunk." :type '(repeat (list (symbol :tag "Major mode symbol") (set (const :tag "Widen buffer during indentation" use-widen) (repeat (command :tag "Widen if multi major is any of those")) ))) :group 'mumamo-indentation) ;;;###autoload (defgroup mumamo-hi-lock-faces nil "Faces for hi-lock that are visible in mumamo multiple modes. This is a workaround for the problem that text properties are always hidden behind overlay dito. This faces are not as visible as those that defines background colors. However they use underlining so they are at least somewhat visible." :group 'hi-lock :group 'mumamo-display :group 'faces) (defface hi-mumamo-yellow '((((min-colors 88) (background dark)) (:underline "yellow1")) (((background dark)) (:underline "yellow")) (((min-colors 88)) (:underline "yellow1")) (t (:underline "yellow"))) "Default face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-pink '((((background dark)) (:underline "pink")) (t (:underline "pink"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-green '((((min-colors 88) (background dark)) (:underline "green1")) (((background dark)) (:underline "green")) (((min-colors 88)) (:underline "green1")) (t (:underline "green"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-blue '((((background dark)) (:underline "light blue")) (t (:underline "light blue"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-black-b '((t (:weight bold :underline t))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-blue-b '((((min-colors 88)) (:weight bold :underline "blue1")) (t (:weight bold :underline "blue"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-green-b '((((min-colors 88)) (:weight bold :underline "green1")) (t (:weight bold :underline "green"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) (defface hi-mumamo-red-b '((((min-colors 88)) (:weight bold :underline "red1")) (t (:weight bold :underline "red"))) "Face for hi-lock mode." :group 'mumamo-hi-lock-faces) ;; (defcustom mumamo-check-chunk-major-same nil ;; "Check if main major mode is the same as normal mode." ;; :type 'boolean ;; :group 'mumamo) ;; (customize-option 'mumamo-major-modes) ;;(require 'django) (defgroup mumamo-modes nil "Customization group for mumamo chunk modes." :group 'mumamo) (defcustom mumamo-major-modes '( (asp-js-mode js-mode ;; Not autoloaded in the pretest javascript-mode espresso-mode ecmascript-mode) (asp-vb-mode visual-basic-mode) ;;(css-mode fundamental-mode) (javascript-mode js-mode ;; Not autoloaded in the pretest javascript-mode espresso-mode ;;js2-fl-mode ecmascript-mode) (java-mode jde-mode java-mode) (groovy-mode groovy-mode) ;; For Emacs 22 that do not have nxml by default ;; Fix me: fallback when autoload fails! (nxhtml-mode nxhtml-mode html-mode) ) "Alist for conversion of chunk major mode specifier to major mode. Each entry has the form \(MAJOR-SPEC MAJORMODE ...) where the symbol MAJOR-SPEC specifies the code type and should match the value returned from `mumamo-find-possible-chunk'. The MAJORMODE symbols are major modes that can be used for editing that code type. The first available MAJORMODE is the one that is used. The MAJOR-SPEC symbols are used by the chunk definitions in `define-mumamo-multi-major-mode'. The major modes are not specified directly in the chunk definitions. Instead a chunk definition contains a symbol that is looked up in this list to find the chunk's major mode. The reason for doing it this way is to make it possible to use new major modes with existing multi major modes. If for example someone writes a new CSS mode that could easily be used instead of the current one in `html-mumamo-mode'. Lookup in this list is done by `mumamo-major-mode-from-modespec'." :type '(alist :key-type (symbol :tag "Symbol for major mode spec in chunk") :value-type (repeat (choice (command :tag "Major mode") (symbol :tag "Major mode (not yet loaded)"))) ) :group 'mumamo-modes) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; JIT lock functions (defun mumamo-jit-lock-function (start) "This function is added to `fontification-functions' by mumamo. START is a parameter given to functions in that hook." (mumamo-msgfntfy "mumamo-jit-lock-function %s, ff=%s, just-changed=%s" start (when start (save-restriction (widen) (get-text-property start 'fontified))) mumamo-just-changed-major) ;;(msgtrc "jit-lock-function %s, ff=%s, just-changed=%s" start (get-text-property start 'fontified) mumamo-just-changed-major) ;;(msgtrc "mumamo-jit-lock-function enter: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) (if mumamo-just-changed-major (setq mumamo-just-changed-major nil)) (let ((ret (jit-lock-function start))) (mumamo-msgfntfy "mumamo-jit-lock-function EXIT %s, ff=%s, just-changed=%s" start (when start (save-restriction (widen) (get-text-property start 'fontified))) mumamo-just-changed-major) ;;(msgtrc "mumamo-jit-lock-function exit: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) ret)) (defun mumamo-jit-lock-register (fun &optional contextual) "Replacement for `jit-lock-register'. Avoids refontification, otherwise same. FUN and CONTEXTUAL has the some meaning as there." (add-hook 'jit-lock-functions fun nil t) (when (and contextual jit-lock-contextually) (set (make-local-variable 'jit-lock-contextually) t)) ;;(jit-lock-mode t) ;; ;; Replace this with the code below from jit-lock-mode t part: (setq jit-lock-mode t) ;; Mark the buffer for refontification. ;; This is what we want to avoid in mumamo: ;;(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 t t) ;;(add-hook 'after-change-functions 'mumamo-jit-lock-after-change t t) (add-hook 'after-change-functions 'mumamo-after-change t t) ;; Set up fontification to call jit: (let ((ff (reverse fontification-functions))) (mapc (lambda (f) ;;(unless (eq f 'jit-lock-function) (remove-hook 'fontification-functions f t)) ;;) ff)) (add-hook 'fontification-functions 'mumamo-jit-lock-function nil t) ) ;; Fix-me: integrate this with fontify-region! (defvar mumamo-find-chunks-timer nil) (make-variable-buffer-local 'mumamo-find-chunks-timer) (put 'mumamo-find-chunks-timer 'permanent-local t) (defvar mumamo-find-chunk-delay idle-update-delay) (make-variable-buffer-local 'mumamo-find-chunk-delay) (put 'mumamo-find-chunk-delay 'permanent-local t) (defun mumamo-stop-find-chunks-timer () "Stop timer that find chunks." (when (and mumamo-find-chunks-timer (timerp mumamo-find-chunks-timer)) (cancel-timer mumamo-find-chunks-timer)) (setq mumamo-find-chunks-timer nil)) (defun mumamo-start-find-chunks-timer () "Start timer that find chunks." (mumamo-stop-find-chunks-timer) ;; (setq mumamo-find-chunks-timer ;; (run-with-idle-timer mumamo-find-chunk-delay nil ;; 'mumamo-find-chunks-in-timer (current-buffer))) ) (defun mumamo-find-chunks-in-timer (buffer) "Run `mumamo-find-chunks' in buffer BUFFER in a timer." (mumamo-msgfntfy "mumamo-find-chunks-in-timer %s" buffer) ;;(message "mumamo-find-chunks-in-timer %s" buffer) (condition-case err (when (buffer-live-p buffer) (with-current-buffer buffer (mumamo-find-chunks nil "mumamo-find-chunks-in-timer"))) (error (message "mumamo-find-chunks error: %s" err)))) (defvar mumamo-last-chunk nil) (make-variable-buffer-local 'mumamo-last-chunk) (put 'mumamo-last-chunk 'permanent-local t) (defvar mumamo-last-change-pos nil) (make-variable-buffer-local 'mumamo-last-change-pos) (put 'mumamo-last-change-pos 'permanent-local t) ;; Fix-me: maybe this belongs to contextual fontification? Eh, ;; no. Unfortunately there is not way to make that handle more than ;; multiple lines. (defvar mumamo-find-chunk-is-active nil "Protect from recursive calls.") ;; Fix-me: temporary things for testing new chunk routines. (defvar mumamo-find-chunks-level 0) (setq mumamo-find-chunks-level 0) (defvar mumamo-old-tail nil) (make-variable-buffer-local 'mumamo-old-tail) (put 'mumamo-old-tail 'permanent-local t) (defun mumamo-update-obscure (chunk pos) "Update obscure cache." (let ((obscured (overlay-get chunk 'obscured)) region-info) (unless (and obscured (= (car obscured) pos)) (setq region-info (mumamo-get-region-from pos)) ;;(msgtrc "update-obscure:region-info=%s" region-info) ;; This should not be a chunk here (mumamo-put-obscure chunk pos region-info)))) (defun mumamo-put-obscure (chunk pos region-or-chunk) "Cache obscure info." (assert (overlayp chunk) t) (when pos (assert (or (markerp pos) (integerp pos)) t)) (let* ((region-info (if (overlayp region-or-chunk) (cons (overlay-start region-or-chunk) (overlay-end region-or-chunk)) region-or-chunk)) (obscured (when pos (list pos region-info)))) ;;(msgtrc "put-obscure:region-info=%s, obscured=%s" region-info obscured) (when region-info (assert (consp region-info) t)) (assert (not (overlayp region-info)) t) (overlay-put chunk 'obscured obscured) (setq obscured (overlay-get chunk 'obscured)) ;;(msgtrc " obscured=%s" obscured) )) (defun mumamo-get-region-from (point) "Return mumamo region values for POINT." ;; Note: `mumamo-get-region-from-1' is defined in mumamo-regions.el (when (fboundp 'mumamo-get-region-from-1) (mumamo-get-region-from-1 point))) (defun mumamo-clear-chunk-ppss-cache (chunk) (overlay-put chunk 'mumamo-ppss-cache nil) (overlay-put chunk 'mumamo-ppss-last nil) (overlay-put chunk 'mumamo-ppss-stats nil)) (defun mumamo-find-chunks (end tracer) "Find or create chunks from last known chunk. Ie, start from the end of `mumamo-last-chunk' if this is non-nil, otherwise 1. If END is nil then continue till end of buffer or until any input is available. In this case the return value is undefined. Otherwise END must be a position in the buffer. Return the mumamo chunk containing the position. If `mumamo-last-chunk' ends before END then create chunks upto END." (when mumamo-multi-major-mode (let ((chunk (mumamo-find-chunks-1 end tracer)) region-info) (when (and end chunk (featurep 'mumamo-regions)) (setq region-info (mumamo-get-region-from end)) ;;(msgtrc "find-chunks:region-info=%s" region-info) (if (overlayp region-info) (setq chunk region-info) ;;(overlay-put chunk 'obscured (list end region-info)))) (mumamo-put-obscure chunk end region-info))) ;;(msgtrc "find-chunks ret chunk=%s" chunk) chunk))) (defun mumamo-move-to-old-tail (first-check-from) "Divide the chunk list. Make it two parts. The first, before FIRST-CHECK-FROM is still correct but we want to check those after. Put thosie in `mumamo-old-tail'." (let ((while-n0 0)) (while (and (mumamo-while 500 'while-n0 "mumamo-last-chunk first-check-from") mumamo-last-chunk first-check-from (< first-check-from (overlay-end mumamo-last-chunk))) (overlay-put mumamo-last-chunk 'mumamo-next-chunk mumamo-old-tail) (setq mumamo-old-tail mumamo-last-chunk) (overlay-put mumamo-old-tail 'mumamo-is-new nil) (when nil ;; For debugging (overlay-put mumamo-old-tail 'face (list :background (format "red%d" (overlay-get mumamo-old-tail 'mumamo-depth))))) (setq mumamo-last-chunk (overlay-get mumamo-last-chunk 'mumamo-prev-chunk))))) (defun mumamo-delete-empty-chunks-at-end () ;; fix-me: later? Delete empty chunks at end, will be recreated if really needed (let ((while-n1 0)) (while (and (mumamo-while 500 'while-n1 "mumamo-last-chunk del empty chunks") mumamo-last-chunk ;;(= (point-max) (overlay-end mumamo-last-chunk)) (= (overlay-end mumamo-last-chunk) (overlay-start mumamo-last-chunk))) ;;(msgtrc "delete-overlay at end") (delete-overlay mumamo-last-chunk) (setq mumamo-last-chunk (overlay-get mumamo-last-chunk 'mumamo-prev-chunk)) (when mumamo-last-chunk (overlay-put mumamo-last-chunk 'mumamo-next-chunk nil))))) (defun mumamo-delete-chunks-upto (ok-pos) "Delete old chunks upto OK-POS." (or (not mumamo-old-tail) (overlay-buffer mumamo-old-tail) (setq mumamo-old-tail nil)) (let ((while-n2 0)) (while (and (mumamo-while 500 'while-n2 "mumamo-old-tail") (and mumamo-old-tail (< (overlay-start mumamo-old-tail) ok-pos))) (mumamo-mark-for-refontification (overlay-start mumamo-old-tail) (overlay-end mumamo-old-tail)) ;;(msgtrc "find-chunks:ok-pos=%s, not eq delete %s" ok-pos mumamo-old-tail) (delete-overlay mumamo-old-tail) (setq mumamo-old-tail (overlay-get mumamo-old-tail 'mumamo-next-chunk)) (or (not mumamo-old-tail) (overlay-buffer mumamo-old-tail) (setq mumamo-old-tail nil))))) (defun mumamo-reuse-old-tail-head () ;;(msgtrc "reusing %S" mumamo-old-tail) (setq mumamo-last-chunk mumamo-old-tail) (overlay-put mumamo-last-chunk 'mumamo-is-new t) (mumamo-clear-chunk-ppss-cache mumamo-last-chunk) (overlay-put mumamo-last-chunk 'face (mumamo-background-color (overlay-get mumamo-last-chunk 'mumamo-depth))) (setq mumamo-old-tail (overlay-get mumamo-old-tail 'mumamo-next-chunk))) (defun mumamo-old-tail-fits (this-new-values) (and mumamo-old-tail (overlay-buffer mumamo-old-tail) (mumamo-new-chunk-equal-chunk-values mumamo-old-tail this-new-values))) (defun mumamo-find-chunks-1 (end tracer) ;; min max) ;; Note: This code must probably be reentrant. The globals changed ;; here are `mumamo-last-chunk' and `mumamo-old-tail'. They must be ;; handled as a pair. (mumamo-msgfntfy "") (setq mumamo-find-chunks-level (1+ mumamo-find-chunks-level)) (unless (and (overlayp mumamo-last-chunk) (overlay-buffer mumamo-last-chunk)) (setq mumamo-last-chunk nil)) (save-restriction (widen) (let* ((mumamo-find-chunks-1-active t) (here (point)) ;; Any changes? (change-min (car mumamo-last-change-pos)) (change-max (cdr mumamo-last-change-pos)) (chunk-at-change-min (when change-min (mumamo-get-existing-new-chunk-at change-min nil))) (chunk-at-change-min-start (when chunk-at-change-min (overlay-start chunk-at-change-min))) ;; Check if change is near border (this-syntax-min-max (when chunk-at-change-min (mumamo-update-obscure chunk-at-change-min chunk-at-change-min-start) (mumamo-chunk-syntax-min-max chunk-at-change-min nil))) (this-syntax-min (car this-syntax-min-max)) (in-min-border (when this-syntax-min (>= this-syntax-min change-min))) (first-check-from (if chunk-at-change-min (if (or in-min-border ;; Fix-me: 20? (> 20 (- change-min chunk-at-change-min-start))) (max 1 (- chunk-at-change-min-start 1)) chunk-at-change-min-start) (when change-min (goto-char change-min) (skip-chars-backward "^\n") (unless (bobp) (backward-char)) (prog1 (point) (goto-char here)))))) (when (and chunk-at-change-min (= 0 (- (overlay-end chunk-at-change-min) (overlay-start chunk-at-change-min)))) (assert in-min-border)) ;; 0 len must be in border (setq mumamo-last-change-pos nil) (when chunk-at-change-min (mumamo-move-to-old-tail first-check-from) (mumamo-delete-empty-chunks-at-end)) ;; Now mumamo-last-chunk is the last in the top chain and ;; mumamo-old-tail the first in the bottom chain. (let* ( ;;(last-chunk-is-closed (when mumamo-last-chunk (overlay-get mumamo-last-chunk 'mumamo-is-closed))) (last-chunk-is-closed t) (ok-pos (or (and mumamo-last-chunk (- (overlay-end mumamo-last-chunk) ;;(or (and last-chunk-is-closed 1) (or (and (/= (overlay-end mumamo-last-chunk) (1+ (buffer-size))) 1) 0))) 0)) (end-param end) (end (or end (point-max))) this-new-values this-new-chunk prev-chunk first-change-pos interrupted (while-n3 0)) (when (>= ok-pos end) (setq this-new-chunk (mumamo-get-existing-new-chunk-at end nil)) (unless this-new-chunk (error "Could not find new chunk ok-pos-new=%s > end=%s (ovls at end=%s), level=%d, old-tail=%s, %S" ok-pos end (overlays-in end end) mumamo-find-chunks-level mumamo-old-tail tracer))) (unless this-new-chunk (save-match-data (unless mumamo-find-chunk-is-active ;;(setq mumamo-find-chunk-is-active t) (mumamo-stop-find-chunks-timer) (mumamo-save-buffer-state nil (progn ;; Loop forward until end or buffer end ... (while (and (mumamo-while 1500 'while-n3 "until end") (or (not end) (<= ok-pos end)) ;;(prog1 t (msgtrc "ok-pos=%s in while" ok-pos)) (< ok-pos (point-max)) (not (setq interrupted (and (not end) (input-pending-p))))) ;; Narrow to speed up. However the chunk divider may be ;; before ok-pos here. Assume that the marker is not ;; longer than 200 chars. fix-me. (narrow-to-region (max (- ok-pos 200) 1) (1+ (buffer-size))) ;; If this was after a change within one chunk then tell that: (let ((use-change-max (when (and change-max chunk-at-change-min (overlay-buffer chunk-at-change-min) (< change-max (overlay-end chunk-at-change-min)) (or (not mumamo-last-chunk) (> change-max (overlay-end mumamo-last-chunk)))) change-max)) (use-chunk-at-change-min (when (or (not mumamo-last-chunk) (not (overlay-buffer mumamo-last-chunk)) (not chunk-at-change-min) (not (overlay-buffer chunk-at-change-min)) (> (overlay-end chunk-at-change-min) (overlay-end mumamo-last-chunk))) chunk-at-change-min ))) (setq this-new-values (mumamo-find-next-chunk-values mumamo-last-chunk first-check-from use-change-max use-chunk-at-change-min))) (if (not this-new-values) (setq ok-pos (point-max)) (setq first-check-from nil) (setq ok-pos (or (mumamo-new-chunk-value-max this-new-values) ;;(overlay-end this-chunk) (point-max))) ;;(msgtrc "ok-pos=%s, point-max=%s max=%s" ok-pos (point-max) (mumamo-new-chunk-value-max this-new-values)) ;; With the new organization all chunks are created here. (if (mumamo-old-tail-fits this-new-values) (mumamo-reuse-old-tail-head) (mumamo-delete-chunks-upto ok-pos) ;; Create chunk and chunk links (setq mumamo-last-chunk (mumamo-new-create-chunk this-new-values)) ;;(setq last-chunk-is-closed (overlay-get mumamo-last-chunk 'mumamo-is-closed)) (unless first-change-pos (setq first-change-pos (mumamo-new-chunk-value-min this-new-values)))))) (setq this-new-chunk mumamo-last-chunk))) (widen) (when (or interrupted (and mumamo-last-chunk (overlayp mumamo-last-chunk) (overlay-buffer mumamo-last-chunk) (buffer-live-p (overlay-buffer mumamo-last-chunk)) (< (overlay-end mumamo-last-chunk) (point-max)))) (mumamo-start-find-chunks-timer) ) (when first-change-pos (setq jit-lock-context-unfontify-pos (if jit-lock-context-unfontify-pos (min jit-lock-context-unfontify-pos first-change-pos) first-change-pos)))) (goto-char here) (setq mumamo-find-chunk-is-active nil))) ;; fix-me: continue here (when chunk-at-change-min (mumamo-clear-chunk-ppss-cache chunk-at-change-min)) (setq mumamo-find-chunks-level (1- mumamo-find-chunks-level)) ;; Avoid empty overlays at the end of the buffer. Those can ;; come from for example deleting to the end of the buffer. (when this-new-chunk ;; Fix-me: can this happen now? (setq prev-chunk (overlay-get this-new-chunk 'mumamo-prev-chunk)) (when (and prev-chunk (overlay-buffer prev-chunk) (= (overlay-start this-new-chunk) (overlay-end this-new-chunk)) (= (overlay-start prev-chunk) (overlay-end prev-chunk))) (overlay-put prev-chunk 'mumamo-next-chunk nil) (overlay-put prev-chunk 'mumamo-prev-chunk nil) ;;(msgtrc "find-chunks:deleting this-new-chunk %s" this-new-chunk) (delete-overlay this-new-chunk) (setq this-new-chunk prev-chunk) ) (while (and mumamo-old-tail (overlay-buffer mumamo-old-tail) (= (overlay-start mumamo-old-tail) (overlay-end mumamo-old-tail))) (assert (not (eq mumamo-old-tail (overlay-get mumamo-old-tail 'mumamo-next-chunk))) t) (setq prev-chunk mumamo-old-tail) (setq mumamo-old-tail (overlay-get mumamo-old-tail 'mumamo-next-chunk)) ;;(msgtrc "mumamo-find-chunks-1:after mumamo-old-tail=%s" mumamo-old-tail) (delete-overlay prev-chunk) ) ) ;;(unless (overlay-get mumamo-last-chunk 'mumamo-is-closed) (unless t ;(= (overlay-end mumamo-last-chunk) (save-restriction (widen) (point-max))) ;; Check that there are no left-over old chunks (save-restriction (widen) (dolist (o (overlays-in (point-min) (point-max))) (when (and (overlay-get o 'mumamo-depth) (not (overlay-get o 'mumamo-is-new))) (error "mumamo-find-chunks: left over chunk: %s end=%s, last-chunk=%s" o end mumamo-last-chunk))))) (when end-param ;;(msgtrc "find-chunks:Exit.end-param=%s, this-new-chunk=%s, point-max=%s, last=%s" end-param this-new-chunk (point-max) mumamo-last-chunk) (let* ((ret this-new-chunk) (ret-beg (overlay-start ret)) (ret-end (overlay-end ret))) (unless (and (<= ret-beg end-param) (<= end-param ret-end)) (error "mumamo-find-chunks: Bad ret=%s, end=%s" ret end-param)) ;;(msgtrc "find-chunks=>%S" ret) ret)))))) (defun mumamo-find-chunk-after-change (min max) "Save change position after a buffer change. This should be run after a buffer change. For MIN see `after-change-functions'." ;; Fix-me: Maybe use a list of all min, max instead? (mumamo-start-find-chunks-timer) ;;(msgtrc "(mumamo-find-chunk-after-change %s %s)" min max) (setq min (copy-marker min nil)) (setq max (copy-marker max t)) (setq mumamo-last-change-pos (if mumamo-last-change-pos (let* ((old-min (car mumamo-last-change-pos)) (old-max (cdr mumamo-last-change-pos)) (new-min (min min old-min)) (new-max (max max old-max))) (cons new-min new-max)) (cons min max)))) (defun mumamo-after-change (min max old-len) "Everything that needs to be done in mumamo after a change. This is run in the `after-change-functions' hook. For MIN, MAX and OLD-LEN see that variable." ;;(msgtrc "mumamo-after-change BEGIN min/max/old-len=%s/%s/%s" min max old-len) ;;(msgtrc "mumamo-after-change BEGIN") (mumamo-find-chunk-after-change min max) (mumamo-jit-lock-after-change min max old-len) (mumamo-msgfntfy "mumamo-after-change EXIT") ;;(msgtrc "mumamo-after-change EXIT mumamo-last-change-pos=%s" mumamo-last-change-pos) ) (defun mumamo-jit-lock-after-change (min max old-len) ;; Fix-me: Should not this be on ;; jit-lock-after-change-externd-region-functions?? "Replacement for `jit-lock-after-change'. Does the nearly the same thing as that function, but takes care of that there might be different major modes at MIN and MAX. It also marks for refontification only in the current mumamo chunk. OLD-LEN is the pre-change length. Jit-lock after change functions is organized this way: `jit-lock-after-change' (doc: Mark the rest of the buffer as not fontified after a change) is added locally to the hook `after-change-functions'. This function runs `jit-lock-after-change-extend-region-functions'." (when (and jit-lock-mode (not memory-full)) (mumamo-msgfntfy "mumamo-jit-lock-after-change ENTER %s %s %s" min max old-len) ;; Why is this nil?: (mumamo-msgfntfy " mumamo-jit-lock-after-change: font-lock-extend-after-change-region-function=%s" font-lock-extend-after-change-region-function) (let* ((ovl-min (mumamo-get-existing-new-chunk-at min nil)) (ovl-max (when (or (not ovl-min) (< (overlay-end ovl-min) max)) (mumamo-get-existing-new-chunk-at max nil))) (major-min (when ovl-min (mumamo-chunk-major-mode ovl-min))) (major-max (when ovl-max (mumamo-chunk-major-mode ovl-max))) (r-min nil) (r-max nil) (new-min min) (new-max max)) (if (and major-min (eq major-min major-max)) (setq r-min (when major-min (mumamo-jit-lock-after-change-1 min max old-len major-min))) (setq r-min (when major-min (mumamo-jit-lock-after-change-1 min max old-len major-min))) (setq r-max (when major-max (mumamo-jit-lock-after-change-1 min max old-len major-max)))) (mumamo-msgfntfy "mumamo-jit-lock-after-change r-min,max=%s,%s major-min,max=%s,%s" r-min r-max major-min major-max) (when r-min (setq new-min (min new-min (car r-min))) (setq new-max (max new-max (cdr r-min)))) (when r-max (setq new-min (min new-min (car r-max))) (setq new-max (max new-max (cdr r-max)))) (setq new-min (max new-min (point-min))) (setq new-max (min new-max (point-max))) ;; Make sure we change at least one char (in case of deletions). (setq new-max (min (max new-max (1+ new-min)) (point-max))) (mumamo-msgfntfy "mumamo-jit-lock-after-change new-min,max=%s,%s" new-min new-max) (mumamo-mark-for-refontification new-min new-max) ;; Mark the change for deferred contextual refontification. ;;(setq jit-lock-context-unfontify-pos nil) (setq message-log-max t) (when jit-lock-context-unfontify-pos (setq jit-lock-context-unfontify-pos ;; Here we use `start' because nothing guarantees that the ;; text between start and end will be otherwise refontified: ;; usually it will be refontified by virtue of being ;; displayed, but if it's outside of any displayed area in the ;; buffer, only jit-lock-context-* will re-fontify it. (min jit-lock-context-unfontify-pos new-min)) ;;(with-current-buffer (get-buffer "*Messages*") (erase-buffer)) (mumamo-msgfntfy "mumamo-jit-lock-after-change EXIT unfontify-pos=%s" jit-lock-context-unfontify-pos) ;;(message "mumamo-jit-lock-after-change.unfontify-pos=%s" jit-lock-context-unfontify-pos) )))) ;;(min jit-lock-context-unfontify-pos jit-lock-start)))))) ;;(put 'mumamo-jit-lock-after-change 'permanent-local-hook t) (put 'mumamo-after-change 'permanent-local-hook t) (defun mumamo-jit-lock-after-change-1 (min max old-len major) "Extend the region the same way jit-lock does it. This function tries to extend the region between MIN and MAX the same way jit-lock does it after a change. OLD-LEN is the pre-change length. The extending of the region is done as if MAJOR was the major mode." (mumamo-with-major-mode-fontification major `(progn (let ((jit-lock-start ,min) (jit-lock-end ,max)) ;;(mumamo-msgfntfy "mumamo-mumamo-jit-lock-after-change-1 jlacer=%s" ,jit-lock-after-change-extend-region-functions) (mumamo-with-buffer-prepared-for-jit-lock ;;(font-lock-extend-jit-lock-region-after-change ,min ,max ,old-len) (run-hook-with-args 'jit-lock-after-change-extend-region-functions min max old-len) ;;(setq jit-lock-end (min (max jit-lock-end (1+ min)) (point-max))) ;;; ;; Just run the buffer local function: ;;; (dolist (extend-fun jit-lock-after-change-extend-region-functions) ;;; (when (fboundp extend-fun) ;;; (funcall extend-fun ,min ,max ,old-len))) ) (setq min jit-lock-start) (setq max jit-lock-end) ;;(syntax-ppss-flush-cache min) ))) (mumamo-msgfntfy "mumamo-mumamo-jit-lock-after-change-1 EXIT %s" (cons min max)) (cons min max)) (defun mumamo-mark-chunk () "Mark chunk and move point to beginning of chunk." (interactive) (let ((chunk (mumamo-find-chunks (point) "mumamo-mark-chunk"))) (unless chunk (error "There is no MuMaMo chunk here")) (goto-char (overlay-start chunk)) (push-mark (overlay-end chunk) t t))) (defun mumamo-narrow-to-chunk-inner () (interactive) (let* ((chunk (mumamo-find-chunks (point) "mumamo-narrow-to-chunk-innner")) (syntax-min-max (mumamo-chunk-syntax-min-max chunk t)) (syntax-min (car syntax-min-max)) (syntax-max (cdr syntax-min-max))) (narrow-to-region syntax-min syntax-max))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Font lock functions (defadvice hi-lock-set-pattern (around use-overlays activate) (if mumamo-multi-major-mode (let ((font-lock-fontified nil)) ad-do-it) ad-do-it)) ;;;###autoload (defun mumamo-mark-for-refontification (min max) "Mark region between MIN and MAX for refontification." ;;(msgtrc "mark-for-refontification A min,max=%s,%s point-min,max=%s,%s modified=%s" min max (point-min) (point-max) (buffer-modified-p) ) ;;(mumamo-backtrace "mark-for-refontification") (mumamo-msgfntfy "mumamo-mark-for-refontification A min,max=%s,%s point-min,max=%s,%s modified=%s" min max (point-min) (point-max) (buffer-modified-p) ) (assert (<= min max)) (when (< min max) (save-restriction (widen) (mumamo-msgfntfy "mumamo-mark-for-refontification B min,max=%s,%s point-min,max=%s,%s modified=%s" min max (point-min) (point-max) (buffer-modified-p) ) ;;(mumamo-with-buffer-prepared-for-jit-lock (mumamo-save-buffer-state nil (put-text-property min max 'fontified nil) )))) ;; Fix me: The functions in this list must be replaced by variables ;; pointing to anonymous functions for buffer local values of ;; fontification keywords to be supported. And that is of course ;; necessary for things like hi-lock etc. (Or..., perhaps some kind of ;; with-variable-values... as RMS suggested once... but that will not ;; help here...) ;; ;; Seems like font-lock-add-keywords must be advised... (defvar mumamo-internal-major-modes-alist nil "Alist with info for different major modes. Internal use only. This is automatically set up by `mumamo-get-major-mode-setup'.") (setq mumamo-internal-major-modes-alist nil) (put 'mumamo-internal-major-modes-alist 'permanent-local t) (defvar mumamo-ppss-last-chunk nil "Internal variable used to avoid unnecessary flushing.") (defvar mumamo-ppss-last-major nil "Internal variable used to avoid unnecessary flushing.") ;;(mumamo-get-major-mode-substitute 'nxhtml-mode 'fontification) ;;(mumamo-get-major-mode-substitute 'nxhtml-mode 'indentation) ;;(mumamo-get-major-mode-substitute 'css-mode 'fontification) ;;(mumamo-get-major-mode-substitute 'css-mode 'indentation) ;; (assq 'nxml-mode mumamo-major-mode-substitute) (defconst mumamo-major-mode-substitute '( (nxhtml-mode (html-mode nxhtml-mode)) ;;(nxhtml-mode (html-mode)) (nxhtml-genshi-mode (html-mode nxhtml-mode)) (nxhtml-mjt-mode (html-mode nxhtml-mode)) (nxml-mode (sgml-mode)) ) "Major modes substitute to use for fontification and indentation. The entries in this list has either of the formats \(MAJOR (FONT-MODE INDENT-MODE)) \(MAJOR (FONT-MODE)) where major is the major mode in a mumamo chunk and FONT-MODE is the major mode for fontification of that chunk and INDENT-MODE is dito for indentation. In the second form the same mode is used for indentation as for fontification.") ;;(mumamo-get-major-mode-substitute 'nxhtml-mode 'indentation) ;;(mumamo-get-major-mode-substitute 'nxhtml-mode 'fontification) (defun mumamo-get-major-mode-substitute (major for-what) "For major mode MAJOR return major mode to use for FOR-WHAT. FOR-WHAT can be either 'fontification or indentation. mumamo must handle fontification and indentation for `major-mode' by using other major mode if the functions for this in `major-mode' are not compatible with mumamo. This functions looks in the table `mumamo-major-mode-substitute' for get major mode to use." ;;(when (eq for-what 'indentation) (message "subst.major=%s" major)) (let ((m (assq major mumamo-major-mode-substitute)) ret-major) (if (not m) (setq ret-major major) (setq m (nth 1 m)) (setq ret-major (cond ((eq for-what 'fontification) (nth 0 m)) ((eq for-what 'indentation) (nth 1 m)) (t (mumamo-display-error 'mumamo-get-major-mode-substitute "Bad parameter, for-what=%s" for-what)))) (unless ret-major (setq ret-major major))) (unless (commandp ret-major) (setq ret-major 'mumamo-bad-mode)) ;;(when (eq for-what 'indentation) (message "ret.ind=%s, major=%s, m=%s" ret major m)) ret-major)) (defun mumamo-assert-fontified-t (start end) "Assert that the region START to END has 'fontified t." (let ((start-ok (get-text-property start 'fontified)) (first-not-ok (next-single-property-change (1+ start) 'fontified nil end))) (when (not start-ok) (message "==== mumamo-assert-fontified-t %s-%s start not ok" start end)) (when (not (= first-not-ok end)) (message "==== mumamo-assert-fontified-t %s-%s first not ok=%s" start end first-not-ok)))) ;; Keep this separate for easier debugging. (defun mumamo-do-fontify (start end verbose chunk-syntax-min chunk-syntax-max chunk-major) "Fontify region between START and END. If VERBOSE is non-nil then print status messages during fontification. CHUNK-SYNTAX-MIN, CHUNK-SYNTAX-MAX and CHUNK-MAJOR are the chunk's min point, max point and major mode. During fontification narrow the buffer to the chunk to make syntactic fontification work. If chunks starts or end with \" then the first respective last char then exclude those chars from from the narrowed part, since otherwise the syntactic fontification can't find out where strings start and stop. Note that this function is run under `mumamo-with-major-mode-fontification'. This function takes care of `font-lock-dont-widen' and `font-lock-extend-region-functions'. Normally `font-lock-default-fontify-region' does this, but that function is not called when mumamo is used! PS: `font-lock-fontify-syntactically-region' is the main function that does syntactic fontification." ;;(msgtrc "mumamo-do-fontify enter: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) ;;(msgtrc "mumamo-do-fontify <<<<<<< %s %s %s %s %s %s" start end verbose chunk-syntax-min chunk-syntax-max chunk-major) ;;(msgtrc "font-lock-keywords=%S" font-lock-keywords) ;;(mumamo-assert-fontified-t start end) (mumamo-condition-case err (let* ((font-lock-dont-widen t) (font-lock-extend-region-functions ;; nil font-lock-extend-region-functions ) ;; Extend like in `font-lock-default-fontify-region': (funs font-lock-extend-region-functions) (font-lock-beg (max chunk-syntax-min start)) (font-lock-end (min chunk-syntax-max end)) (while-n1 0)) ;;(while (and (> 500 (setq while-n1 (1+ while-n1))) (while (and (mumamo-while 500 'while-n1 "funs") funs) (setq funs (if (or (not (funcall (car funs))) (eq funs font-lock-extend-region-functions)) (cdr funs) ;; If there's been a change, we should go through ;; the list again since this new position may ;; warrant a different answer from one of the fun ;; we've already seen. font-lock-extend-region-functions))) ;; But we must restrict to the chunk here: (let ((new-start (max chunk-syntax-min font-lock-beg)) (new-end (min chunk-syntax-max font-lock-end))) ;;(msgtrc "do-fontify %s %s, chunk-syntax-min,max=%s,%s, new: %s %s" start end chunk-syntax-min chunk-syntax-max new-start new-end) ;; A new condition-case just to catch errors easier: (when (< new-start new-end) (mumamo-condition-case err (save-restriction ;;(when (and (>= 625 (point-min)) (<= 625 (point-max))) (msgtrc "multi at 625=%s" (get-text-property 625 'font-lock-multiline))) ;;(msgtrc "(narrow-to-region %s %s)" chunk-syntax-min chunk-syntax-max) (when (< chunk-syntax-min chunk-syntax-max) (narrow-to-region chunk-syntax-min chunk-syntax-max) ;; Now call font-lock-fontify-region again but now ;; with the chunk font lock parameters: (setq font-lock-syntactically-fontified (1- new-start)) (mumamo-msgfntfy "ENTER font-lock-fontify-region %s %s %s" new-start new-end verbose) ;;(msgtrc "mumamo-do-fontify: font-lock-keywords-only =%s in buffer %s, def=%s" font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) (let (font-lock-extend-region-functions) (font-lock-fontify-region new-start new-end verbose)) (mumamo-msgfntfy "END font-lock-fontify-region %s %s %s" new-start new-end verbose) ) ) (error (mumamo-display-error 'mumamo-do-fontify-2 "mumamo-do-fontify m=%s, s/e=%s/%s syn-min/max=%s/%s: %s" chunk-major start end chunk-syntax-min chunk-syntax-max (error-message-string err))))))) (error (mumamo-display-error 'mumamo-do-fontify "mumamo-do-fontify m=%s, s=%s, e=%s: %s" chunk-major start end (error-message-string err))) ) (mumamo-msgfntfy "mumamo-do-fontify exit >>>>>>> %s %s %s %s %s %s" start end verbose chunk-syntax-min chunk-syntax-max chunk-major) ;;(msgtrc "mumamo-do-fontify exit: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) ) (defun mumamo-do-unfontify (start end) "Unfontify region between START and END." (mumamo-condition-case err (font-lock-unfontify-region start end) (error (mumamo-display-error 'mumamo-do-unfontify "%s" (error-message-string err))))) (defun mumamo-fontify-region-with (start end verbose major chunk-syntax-min chunk-syntax-max) "Fontify from START to END. If VERBOSE is non-nil then print status messages during fontification. Do the fontification as in major mode MAJOR. Narrow to region CHUNK-SYNTAX-MIN and CHUNK-SYNTAX-MAX during fontification." ;; The text property 'fontified is always t here due to the way ;; jit-lock works! ;;(msgtrc "fontify-region-with %s %s %s %s, ff=%s" start end verbose major (get-text-property start 'fontified)) ;;(mumamo-assert-fontified-t start end) ;;(msgtrc "mumamo-fontify-region-with enter: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) (mumamo-condition-case err (progn ;;(msgtrc "mumamo-fontify-region-with: font-lock-keywords-only =%s in buffer %s, def=%s" font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) (mumamo-with-major-mode-fontification major `(mumamo-do-fontify ,start ,end ,verbose ,chunk-syntax-min ,chunk-syntax-max major)) ) (error (mumamo-display-error 'mumamo-fontify-region-with "%s" (error-message-string err)))) ;;(msgtrc "mumamo-fontify-region-with exit: font-lock-keywords-only def=%s" (default-value 'font-lock-keywords-only)) ) (defun mumamo-unfontify-region-with (start end major) "Unfontify from START to END as in major mode MAJOR." (mumamo-msgfntfy "mumamo-unfontify-region-with %s %s %s, ff=%s" start end major (when start (save-restriction (widen) (get-text-property start 'fontified)))) (mumamo-with-major-mode-fontification major `(mumamo-do-unfontify ,start ,end))) (defun mumamo-backtrace (label) (msgtrc "%s:backtrace in START buffer %s <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n%s" label (current-buffer) (with-output-to-string (backtrace))) (msgtrc "%s:backtrace in END buffer %s >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" label (current-buffer))) (defun mumamo-unfontify-buffer () "Unfontify buffer. This function is called when the minor mode function `font-lock-mode' is turned off. \(It is the value of `font-lock-unfontify-uffer-function')." (when (and mumamo-multi-major-mode (not (and (boundp 'mumamo-find-chunks-1-active) mumamo-find-chunks-1-active))) ;;(mumamo-backtrace "unfontify-buffer") ;;(msgtrc "mumamo-unfontify-buffer:\n%s" (with-output-to-string (backtrace))) (save-excursion (save-restriction (widen) (let ((ovls (overlays-in (point-min) (point-max))) (main-major (mumamo-main-major-mode))) (dolist (o ovls) (when (overlay-get o 'mumamo-is-new) (let ((major (mumamo-chunk-major-mode o))) (when major (unless (mumamo-fun-eq major main-major) (mumamo-unfontify-chunk o)) ;;(msgtrc "delete-overlay 1") (delete-overlay o) )))) (mumamo-unfontify-region-with (point-min) (point-max) (mumamo-main-major-mode))))))) (defun mumamo-fontify-buffer () "For `font-lock-fontify-buffer-function' call. Not sure when this normally is done. However some functions call this to ensure that the whole buffer is fontified." (mumamo-msgfntfy "===> mumamo-fontify-buffer-function called") ;;(font-lock-default-fontify-buffer) (unless mumamo-set-major-running ;; This function is normally not called, but when new patterns ;; have been added by hi-lock it will be called. In this case we ;; need to make buffer local fontification variables: (set (make-local-variable 'mumamo-internal-major-modes-alist) nil) (jit-lock-refontify))) (defun mumamo-unfontify-chunk (chunk) ; &optional start end) "Unfontify mumamo chunk CHUNK." (let* ((major (mumamo-chunk-major-mode chunk)) ;;(start (overlay-start chunk)) ;;(end (overlay-end chunk)) (syntax-min-max (mumamo-chunk-syntax-min-max chunk t)) (syntax-min (car syntax-min-max)) (syntax-max (cdr syntax-min-max)) (font-lock-dont-widen t)) (when (< syntax-min syntax-max) (save-restriction (narrow-to-region syntax-min syntax-max) (mumamo-unfontify-region-with syntax-min syntax-max major))))) (defun mumamo-fontify-region (start end &optional verbose) "Fontify between START and END. Take the major mode chunks into account while doing this. If VERBOSE do the verbously. The value of `font-lock-fontify-region-function' when mumamo is used is this function." (mumamo-msgfntfy "++++++ mumamo-fontify-regionX %s %s %s, skip=%s" start end verbose mumamo-just-changed-major) ;;(msgtrc "mumamo-fontify-region: font-lock-keywords-only =%s in buffer %s, def=%s" font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) ;;(mumamo-assert-fontified-t start end) ;; If someone else tries to fontify the buffer ... (if (and mumamo-just-changed-major ;; The above variable is reset in `post-command-hook' so ;; check if we are in a recursive search. (Note: There are ;; other situation when this can occur. It might be best to ;; remove this test later, or make it optional.) ;; ;; skip the test for now: nil (= 0 (recursion-depth))) (mumamo-display-error 'mumamo-fontify-region "Just changed major, should not happen") (mumamo-condition-case err (mumamo-fontify-region-1 start end verbose) (error (mumamo-display-error 'mumamo-fontify-region "%s" (error-message-string err)))))) (defconst mumamo-dbg-pretend-fontified nil "Set this to t to be able to debug more easily. This is for debugging `mumamo-fontify-region-1' more easily by just calling it. It will make that function believe that the text has a non-nil 'fontified property.") (defun mumamo-exc-mode (chunk) "Return sub major mode for CHUNK. If chunk is a main major mode chunk return nil, otherwise return the major mode for the chunk." (let ((major (mumamo-chunk-major-mode chunk))) (unless (mumamo-fun-eq major (mumamo-main-major-mode)) major))) ;;; Chunk in chunk needs push/pop relative prev chunk (defun mumamo-chunk-push (chunk prop val) (let* ((prev-chunk (overlay-get chunk 'mumamo-prev-chunk)) (prev-val (when prev-chunk (overlay-get prev-chunk prop)))) (overlay-put chunk prop (cons val prev-val)))) (defun mumamo-chunk-pop (chunk prop) (overlay-put chunk prop (cdr (overlay-get (overlay-get chunk 'mumamo-prev-chunk) prop)))) ;; (defvar mumamo-chunks-to-remove nil ;; "Internal. Chunk overlays marked for removal.") ;; (make-variable-buffer-local 'mumamo-chunks-to-remove) (defun mumamo-flush-chunk-syntax (chunk chunk-min chunk-max) "Flush syntax cache for chunk CHUNK. This includes removing text property 'syntax-table between CHUNK-MIN and CHUNK-MAX." ;; syntax-ppss-flush-cache (overlay-put chunk 'syntax-ppss-last nil) (overlay-put chunk 'syntax-ppss-cache nil) (overlay-put chunk 'syntax-ppss-stats nil) (mumamo-save-buffer-state nil (remove-list-of-text-properties chunk-min chunk-max '(syntax-table)))) ;; Fix-me: If I open nxhtml-changes.html and then go to the bottom of ;; the file at once syntax-ppss seems to be upset. It is however cured ;; by doing some change above the region that is badly fontified. (defun mumamo-fontify-region-1 (start end verbose) "Fontify region between START and END. If VERBOSE is non-nil then print status messages during fontification. This is called from `mumamo-fontify-region' which is the value of `font-lock-fontify-region-function' when mumamo is used. \(This means that it ties into the normal font lock framework in Emacs.) Note: The purpose of extracting this function from `mumamo-fontify-region' \(which is the only place where it is called) is to make debugging easier. Edebug will without this function just step over the `condition-case' in `mumamo-fontify-region'. The fontification is done in steps: - First a mumamo chunk is found or created at the start of the region with `mumamo-get-chunk-at'. - Then this chunk is fontified according to the major mode for that chunk. - If the chunk did not encompass the whole region then this procedure is repeated with the rest of the region. If some mumamo chunk in the region between START and END has been marked for removal \(for example by `mumamo-jit-lock-after-change') then they are removed by this function. For some main major modes \(see `define-mumamo-multi-major-mode') the main major modes is first used to fontify the whole region. This is because otherwise the fontification routines for that mode may have trouble finding the correct starting state in a chunk. Special care has been taken for chunks that are strings, ie surrounded by \"...\" since they are fontified a bit special in most major modes." ;; Fix-me: unfontifying should be done using the correct syntax table etc. ;; Fix-me: refontify when new chunk ;;(msgtrc "fontify-region-1: font-lock-keywords-only =%s in buffer %s, def=%s" font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) (save-match-data (let* ((old-point (point)) (here start) (main-major (mumamo-main-major-mode)) (fontified-t ;;(or mumamo-dbg-pretend-fontified ;; (get-text-property here 'fontified)) t) after-change-functions ;; Fix-me: tested adding this to avoid looping (first-new-ovl nil) (last-new-ovl nil) (chunk-at-start-1 (mumamo-find-chunks start "mumamo-fontify-region-1")) (while-n1 0) ) (when chunk-at-start-1 (unless (= start (1- (overlay-end chunk-at-start-1))) (setq chunk-at-start-1 nil))) ;;(while (and (> 500 (setq while-n1 (1+ while-n1))) (while (and (mumamo-while 9000 'while-n1 "fontified-t") fontified-t (< here end)) ;;(msgtrc "mumamo-fontify-region-1 heree 1, here=%s, end=%s" here end) ;;(mumamo-assert-fontified-t here end) ;;(mumamo-assert-fontified-t start end) ;; Check where new chunks should be, adjust old chunks as ;; necessary. Refontify inside end-start and outside of ;; start-end mark for refontification when major-mode has ;; changed or there was no old chunk. ;; ;; Fix-me: Join chunks! (let* ((chunk (mumamo-find-chunks here "mumamo-fontify-region-1 2")) (chunk-min (when chunk (overlay-start chunk))) (chunk-max (when chunk (overlay-end chunk))) (chunk-min-1 (when chunk (if (> chunk-min (point-min)) (1- chunk-min) (point-min)))) (chunk-max-1 (when chunk (if (< chunk-max (point-max)) (1+ chunk-max) (point-max)))) (chunk-min-face (when chunk (get-text-property chunk-min-1 'face))) (chunk-max-face (when chunk (get-text-property chunk-max-1 'face))) (chunk-major (when chunk (mumamo-chunk-major-mode chunk))) max ; (min chunk-max end)) ) (assert chunk) (setq chunk-min (when chunk (overlay-start chunk))) (setq chunk-max (when chunk (overlay-end chunk))) (setq chunk-min-1 (when chunk (if (> chunk-min (point-min)) (1- chunk-min) (point-min)))) ;chunk-min (setq chunk-max-1 (when chunk (if (< chunk-max (point-max)) (1+ chunk-max) (point-max)))) ;chunk-max (setq chunk-min-face (when chunk (get-text-property chunk-min-1 'face))) (setq chunk-max-face (when chunk (get-text-property chunk-max-1 'face))) (setq chunk-major (when chunk (mumamo-chunk-major-mode chunk))) (if (and first-new-ovl (overlay-buffer first-new-ovl)) (setq last-new-ovl chunk) (setq last-new-ovl chunk) (setq first-new-ovl chunk)) ;;(mumamo-assert-fontified-t chunk-min chunk-max) (setq max (min chunk-max end)) (assert chunk) (assert (overlay-buffer chunk)) (assert chunk-min) (assert chunk-max) (assert chunk-major) ;; Fix-me: The next assertion sometimes fails. Could it be ;; that this loop is continuing even after a change in the ;; buffer? How do I stop that? When?: ;;(assert (or (= here start) (= here chunk-min)) nil "h=%s, s=%s, cm=%s-%s, e=%s, chunk-major=%s" here start chunk-min chunk-max end chunk-major) ;;(assert (not (mumamo-fun-eq prev-major chunk-major))) ;;(when prev-chunk ;; (assert (= (overlay-end prev-chunk) (overlay-start chunk)))) ;; Fontify ;;(msgtrc "\nmumamo-fontify-region-1 before chunk=%s" chunk) (mumamo-update-obscure chunk here) (let* ((syntax-min-max (mumamo-chunk-syntax-min-max chunk nil)) (syntax-min (car syntax-min-max)) (syntax-max (cdr syntax-min-max)) (chunk-min (overlay-start chunk)) (chunk-max (overlay-end chunk)) (border-min-max (mumamo-chunk-syntax-min-max chunk t)) (border-min (car border-min-max)) (border-max (cdr border-min-max)) ) ;;(msgtrc "fontify-region-1:syntax-min-max=%S, chunk=%S" syntax-min-max chunk) ;;(msgtrc "chunk mumamo-border-face: %s" chunk) (mumamo-msgfntfy "mumamo-fontify-region-1, here=%s chunk-min=%s syn-mn/mx=%s/%s" here chunk-min syntax-min syntax-max) (when (<= here syntax-min) (mumamo-flush-chunk-syntax chunk syntax-min syntax-max)) (when (and (<= here syntax-min) (< chunk-min border-min)) ;;(msgtrc "face-in: %s-%s" chunk-min border-min) (put-text-property chunk-min border-min 'face 'mumamo-border-face-in) ) (when (and (<= chunk-max max) ;;(< (1+ border-max) chunk-max)) (< border-max chunk-max)) ;;(put-text-property (1+ border-max) chunk-max (put-text-property border-max chunk-max 'face 'mumamo-border-face-out)) (mumamo-fontify-region-with here max verbose chunk-major syntax-min syntax-max)) ;;(setq prev-major chunk-major) ;;(setq prev-chunk chunk) (setq here (if (= max here) (1+ max) max)) ;;(setq fontified-t (or mumamo-dbg-pretend-fontified (get-text-property (1- here) 'fontified))) ) ;;(msgtrc "ft here end=%s %s %s" fontified-t here end) ) (goto-char old-point) ;;(msgtrc "b first-new-ovl=%s last-new-ovl=%s" first-new-ovl last-new-ovl) (unless fontified-t ;; Fix-me: I am not sure what to do here. Probably just ;; refontify the rest between start and end. But does not ;; this lead to unnecessary refontification? ;;(msgtrc "not sure, here=%s, end=%s" here end) (unless (= here (point-max)) (mumamo-mark-for-refontification here end))) )) ;;(msgtrc "EXIT mumamo-fontify-region-1") ) (defvar mumamo-known-buffer-local-fontifications '( font-lock-mode-hook ;; css-color-mode hi-lock-mode hi-lock-file-patterns hi-lock-interactive-patterns wrap-to-fill-column-mode )) (defconst mumamo-irrelevant-buffer-local-vars '( ;; This list was fetched with ;; emacs-Q, fundamental-mode after-change-functions ;;auto-composition-function ;;auto-composition-mode ;;auto-composition-mode-major-mode buffer-auto-save-file-format buffer-auto-save-file-name buffer-backed-up buffer-display-count buffer-display-time buffer-file-format buffer-file-name buffer-file-truename buffer-invisibility-spec buffer-read-only buffer-saved-size buffer-undo-list change-major-mode-hook ;;char-property-alias-alist cursor-type default-directory delay-mode-hooks enable-multibyte-characters ;;font-lock-mode ;;font-lock-mode-major-mode ;;major-mode mark-active mark-ring mode-name point-before-scroll ;; Handled by font lock etc font-lock-defaults font-lock-fontified font-lock-keywords ;;font-lock-keywords-only font-lock-keywords-case-fold-search font-lock-mode ;;font-lock-mode-major-mode font-lock-set-defaults font-lock-syntax-table font-lock-beginning-of-syntax-function fontification-functions jit-lock-context-unfontify-pos jit-lock-mode ;; Mumamo font-lock-fontify-buffer-function jit-lock-contextually jit-lock-functions ;; More symbols from visual inspection before-change-functions delayed-mode-hooks isearch-mode line-move-ignore-invisible local-abbrev-table ;;syntax-ppss-last ;;syntax-ppss-cache ;; Cua cua--explicit-region-start ;; Viper viper--intercept-key-maps viper--key-maps viper-ALPHA-char-class viper-current-state viper-emacs-global-user-minor-mode viper-emacs-intercept-minor-mode viper-emacs-kbd-minor-mode viper-emacs-local-user-minor-mode viper-emacs-state-modifier-minor-mode viper-insert-basic-minor-mode viper-insert-diehard-minor-mode viper-insert-global-user-minor-mode viper-insert-intercept-minor-mode viper-insert-kbd-minor-mode viper-insert-local-user-minor-mode viper-insert-minibuffer-minor-mode viper-insert-point viper-insert-state-modifier-minor-mode viper-intermediate-command viper-last-posn-while-in-insert-state viper-minibuffer-current-face viper-mode-string viper-non-word-characters viper-replace-minor-mode viper-replace-overlay viper-undo-functions viper-undo-needs-adjustment viper-vi-basic-minor-mode viper-vi-diehard-minor-mode viper-vi-global-user-minor-mode viper-vi-intercept-minor-mode viper-vi-kbd-minor-mode viper-vi-local-user-minor-mode viper-vi-minibuffer-minor-mode viper-vi-state-modifier-minor-mode ;; hs minor mode hs-adjust-block-beginning hs-block-start-mdata-select hs-block-start-regexp hs-c-start-regexp hs-forward-sexp-func hs-minor-mode ;; Imenu imenu-case-fold-search imenu-generic-expression ;; Fix-me: add more here )) (defun mumamo-get-relevant-buffer-local-vars () "Get list of buffer local variables to save. Like `buffer-local-variables', but remove variables that are known to not be necessary to save for fontification, indentation or filling \(or that can even disturb things)." (let (var-vals) (dolist (vv (buffer-local-variables)) (unless (or (not (listp vv)) (memq (car vv) mumamo-irrelevant-buffer-local-vars) (let* ((sym (car vv)) (val (symbol-value sym))) (or (markerp val) (overlayp val)))) (let ((ent (list (car vv) (custom-quote (cdr vv))))) (setq var-vals (cons ent var-vals))))) ;; Sorting is for debugging/testing (setq var-vals (sort var-vals (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))) var-vals)) (defvar mumamo-major-modes-local-maps nil "An alist with major mode and local map. An entry in the list looks like \(MAJOR-MODE LOCAL-KEYMAP)") ;; (defun mumamo-font-lock-keyword-hook-symbol (major) ;; "Return hook symbol for adding font-lock keywords to MAJOR." ;; (intern (concat "mumamo-" (symbol-name major) "-font-lock-keyword-hook"))) ;; (defun mumamo-remove-font-lock-hook (major setup-fun) ;; "For mode MAJOR remove function SETUP-FUN. ;; See `mumamo-add-font-lock-hook' for more information." ;; (remove-hook (mumamo-font-lock-keyword-hook-symbol major) setup-fun)) (defun mumamo-refresh-multi-font-lock (major) "Refresh font lock information for mode MAJOR in chunks. If multi fontification functions for major mode MAJOR is already setup up they will be refreshed. If MAJOR is nil then all font lock information for major modes used in chunks will be refreshed. After calling font-lock-add-keywords or changing the fontification in other ways you must call this function for the changes to take effect. However already fontified buffers will not be refontified. You can use `normal-mode' to refontify them. Fix-me: Does not work yet." (setq mumamo-internal-major-modes-alist (if (not major) nil (assq-delete-all major mumamo-internal-major-modes-alist)))) ;; RMS had the following idea: ;; ;; Suppose we add a Lisp primitive to bind a set of variables under ;; the control of an alist. Would it be possible to eliminate these ;; helper functions and use that primitive instead? ;; ;;; But wouldn't it be better to test this version first? There is ;;; no hurry, this version works and someone might find that there ;;; is a better way to do this than with helper functions. ;; ;; OK with me, as long as this point doesn't get forgotten. (defun mumamo-fetch-major-mode-setup (major keywords mode-keywords add-keywords how) "Return a helper function to do fontification etc like in major mode MAJOR. Fetch the variables affecting font locking, indentation and filling by calling the major mode MAJOR in a temporary buffer. Make a function with one parameter BODY which is elisp code to eval. The function should let bind the variables above, sets the syntax table temporarily to the one used by the major mode \(using the mode symbol name to find it) and then evaluates body. Name this function mumamo-eval-in-MAJOR. Put the code for this function in the property `mumamo-defun' on this function symbol. ** Some notes about background etc. The function made here is used in `mumamo-with-major-mode-setup'. The code in the function parameter BODY is typically involved in fontification, indentation or filling. The main reasons for doing it this way is: - It is faster and than setting the major mode directly. - It does not affect buffer local variables." ;; (info "(elisp) Other Font Lock Variables") ;; (info "(elisp) Syntactic Font Lock) ;;(msgtrc "fetch-major 1: font-lock-keywords-only =%s" font-lock-keywords-only) (let ((func-sym (intern (concat "mumamo-eval-in-" (symbol-name major)))) (func-def-sym (intern (concat "mumamo-def-eval-in-" (symbol-name major)))) ;;(add-keywords-hook (mumamo-font-lock-keyword-hook-symbol major)) byte-compiled-fun (fetch-func-definition `(lambda (body))) ;;`(defun ,func-sym (body))) temp-buf-name temp-buf) ;; font-lock-mode can't be turned on in buffers whose names start ;; with a char with white space syntax. Temp buffer names are ;; such and it is not possible to change name of a temp buffer. (setq temp-buf-name (concat "mumamo-fetch-major-mode-setup-" (symbol-name major))) (setq temp-buf (get-buffer temp-buf-name)) (when temp-buf (kill-buffer temp-buf)) (setq temp-buf (get-buffer-create temp-buf-name)) ;;(msgtrc "fetch-major-mode-setup in buffer %s, after-chunk=%s, before with-current-buffer" (current-buffer) (when (boundp 'after-chunk) after-chunk)) (with-current-buffer temp-buf (mumamo-msgfntfy "mumamo-fetch-major-mode-setup %s" major) (let ((mumamo-fetching-major t) mumamo-multi-major-mode) ;;(msgtrc "fetch-major-mode-setup in buffer %s, before (funcall %s)" (current-buffer) major) (funcall major) ) (mumamo-msgfntfy ">>> mumamo-fetch-major-mode-setup A font-lock-mode=%s" font-lock-mode) (font-lock-mode 1) (mumamo-msgfntfy "<<< mumamo-fetch-major-mode-setup B font-lock-mode=%s" font-lock-mode) (mumamo-msgfntfy "mumamo-fetch-major-mode-setup: fetching jit-lock-after-change-extend-region-functions A=%s" jit-lock-after-change-extend-region-functions) ;; Note: font-lock-set-defaults must be called before adding ;; keywords. Otherwise Emacs loops. I have no idea why. Hm, ;; probably wrong, it is likely to be nxhtml-mumamo that is the ;; problem. Does not loop in html-mumamo. ;;(msgtrc "\n--------------------") (font-lock-set-defaults) ;; Fix-me: but hi-lock still does not work... what have I ;; forgotten??? font-lock-keywords looks ok... (when keywords (if add-keywords (progn ;;(msgtrc "fetch:font-lock-add-keywords %S %S %S" (if mode-keywords major nil) keywords how) (font-lock-add-keywords (if mode-keywords major nil) keywords how) ;;(font-lock-add-keywords major keywords how) ;;(msgtrc "fetch:font-lock-keywords=%S" font-lock-keywords) ) (font-lock-remove-keywords (if mode-keywords major nil) keywords) ;;(font-lock-remove-keywords major keywords) ) (unless mode-keywords (font-lock-mode -1) (font-lock-mode 1)) ;;(msgtrc "fetch-major-mode-setup:font-lock-keywords=%S" font-lock-keywords) ) ;;(run-hooks add-keywords-hook) (add-to-list 'mumamo-major-modes-local-maps (let ((local-map (current-local-map))) (cons major-mode (if local-map (copy-keymap local-map) 'no-local-map)))) (mumamo-msgfntfy "mumamo-fetch-major-mode-setup: fetching jit-lock-after-change-extend-region-functions B=%s" jit-lock-after-change-extend-region-functions) (let* ((syntax-sym (intern-soft (concat (symbol-name major) "-syntax-table"))) (fetch-func-definition-let ;; Be XML compliant: (list (list 'sgml-xml-mode ;;(when (mumamo-derived-from-mode ',major 'sgml-mode) t)) (when (mumamo-derived-from-mode major 'sgml-mode) t)) ;; We need to copy the variables that we need and ;; that are not automatically buffer local, but ;; could be it. Arguably it is a bug if they are not ;; buffer local though we have to adapt. ;; From cc-mode.el: (list 'indent-line-function (custom-quote indent-line-function)) (list 'indent-region-function (custom-quote indent-region-function)) (list 'normal-auto-fill-function (custom-quote normal-auto-fill-function)) (list 'comment-start (custom-quote comment-start)) (list 'comment-end (custom-quote comment-end)) (list 'comment-start-skip (custom-quote comment-start-skip)) (list 'comment-end-skip (custom-quote comment-end-skip)) (list 'comment-multi-line (custom-quote comment-multi-line)) (list 'comment-line-break-function (custom-quote comment-line-break-function)) (list 'paragraph-start (custom-quote paragraph-start)) (list 'paragraph-separate (custom-quote paragraph-separate)) (list 'paragraph-ignore-fill-prefix (custom-quote paragraph-ignore-fill-prefix)) (list 'adaptive-fill-mode (custom-quote adaptive-fill-mode)) (list 'adaptive-fill-regexp (custom-quote adaptive-fill-regexp)) ;;; Try doing the font lock things last, keywords really last (list 'font-lock-multiline (custom-quote font-lock-multiline)) (list 'font-lock-extend-after-change-region-function (custom-quote font-lock-extend-after-change-region-function)) (list 'font-lock-extend-region-functions (custom-quote font-lock-extend-region-functions)) (list 'font-lock-comment-start-skip (custom-quote font-lock-comment-start-skip)) (list 'font-lock-comment-end-skip (custom-quote font-lock-comment-end-skip)) (list 'font-lock-syntactic-keywords (custom-quote font-lock-syntactic-keywords)) (list 'font-lock-keywords (custom-quote font-lock-keywords)) ;;(list 'font-lock-keywords-alist (custom-quote font-lock-keywords-alist)) ;;(list 'font-lock-removed-keywords-alist (custom-quote font-lock-removed-keywords-alist)) ;; Fix-me: uncommenting this line (as it should be) ;; sets font-lock-keywords-only to t globally...: bug 3467 (list 'font-lock-keywords-only (custom-quote font-lock-keywords-only)) (list 'font-lock-keywords-case-fold-search (custom-quote font-lock-keywords-case-fold-search)) (list 'font-lock-set-defaults t) ; whether we have set up defaults. ;; Set from font-lock-defaults normally: (list 'font-lock-defaults (custom-quote (copy-tree font-lock-defaults))) ;; Syntactic Font Lock (list 'font-lock-syntax-table (custom-quote font-lock-syntax-table)) ;; See nXhtml bug 400415 (list 'font-lock-beginning-of-syntax-function (custom-quote font-lock-beginning-of-syntax-function)) (list 'font-lock-syntactic-face-function (custom-quote font-lock-syntactic-face-function)) ;; Other Font Lock Variables (list 'font-lock-mark-block-function (custom-quote font-lock-mark-block-function)) (list 'font-lock-extra-managed-props (custom-quote font-lock-extra-managed-props)) ;; This value is fetched from font-lock: (list 'font-lock-fontify-buffer-function (custom-quote font-lock-fontify-buffer-function)) (list 'font-lock-unfontify-buffer-function (custom-quote font-lock-unfontify-buffer-function)) (list 'font-lock-fontify-region-function (custom-quote font-lock-fontify-region-function)) (list 'font-lock-unfontify-region-function (custom-quote font-lock-unfontify-region-function)) ;; Jit Lock Variables (list 'jit-lock-after-change-extend-region-functions (custom-quote jit-lock-after-change-extend-region-functions)) ;;(list 'syntax-table (custom-quote (copy-syntax-table (syntax-table)))) ;;(list 'mumamo-original-syntax-begin-function (custom-quote syntax-begin-function)) (list 'syntax-begin-function (custom-quote syntax-begin-function)) (list 'fill-paragraph-function (custom-quote fill-paragraph-function)) (list 'fill-forward-paragraph-function (when (boundp 'fill-forward-paragraph-function) (custom-quote fill-forward-paragraph-function))) ;; newcomment (list 'comment-use-global-state (custom-quote (when (boundp 'comment-use-global-state) comment-use-global-state))) ;; parsing sexps (list 'multibyte-syntax-as-symbol (custom-quote multibyte-syntax-as-symbol)) (list 'parse-sexp-ignore-comments (custom-quote parse-sexp-ignore-comments)) (list 'parse-sexp-lookup-properties (custom-quote parse-sexp-lookup-properties)) ;; fix-me: does not the next line work? (list 'forward-sexp-function (custom-quote forward-sexp-function)) )) (relevant-buffer-locals (mumamo-get-relevant-buffer-local-vars)) ) ;;(append '(1 2) '(3 4) '((eval body))) (mumamo-msgfntfy "===========> before setq fetch-func-definition %s" func-sym) ;; Avoid doublets (dolist (fetched fetch-func-definition-let) (let ((fvar (car fetched))) (setq relevant-buffer-locals (assq-delete-all fvar relevant-buffer-locals)))) (setq fetch-func-definition (append fetch-func-definition `((let ,(append fetch-func-definition-let relevant-buffer-locals) (with-syntax-table ,(if syntax-sym syntax-sym '(standard-syntax-table));;'syntax-table ;; fix-me: Protect against font-lock-keywords-only to t globally...: bug 3467 ;;(msgtrc "%s enter 1: font-lock-keywords-only def=%s, body=%S" ',major (default-value 'font-lock-keywords-only) body) (let (;(font-lock-keywords-only font-lock-keywords-only) ret) ;;(msgtrc "%s enter 2: font-lock-keywords-only def=%s" ',major (default-value 'font-lock-keywords-only)) (setq ret (eval body)) ;;(msgtrc "%s exit 1: font-lock-keywords-only def=%s" ',major (default-value 'font-lock-keywords-only)) ret)) ;;(msgtrc "in %s 1: font-lock-keywords-only =%s in buffer %s, def=%s" ',func-sym font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) ) ;;(msgtrc "in %s 2: font-lock-keywords-only =%s in buffer %s, def=%s" ',func-sym font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) ;;(message "backtrace there:\n%s" (with-output-to-string (backtrace))) ))) (setq byte-compiled-fun (let ((major-syntax-table)) (byte-compile fetch-func-definition))) (assert (functionp byte-compiled-fun)) (unless keywords (eval `(defvar ,func-sym nil)) (eval `(defvar ,func-def-sym ,fetch-func-definition)) (set func-sym byte-compiled-fun) ;; Will be used as default (assert (functionp (symbol-value func-sym)) t) (funcall (symbol-value func-sym) nil) (put func-sym 'permanent-local t) (put func-def-sym 'permanent-local t)))) (kill-buffer temp-buf) ;; Use the new value in current buffer. (when keywords ;;(set (make-local-variable func-sym) (symbol-value func-sym)) ;;(msgtrc "fetch: major=%s func-def-sym=%s cb=%s fetch-func-definition=%s" major func-def-sym (current-buffer) fetch-func-definition) ;;(msgtrc "fetch: major=%s func-def-sym=%s cb=%s fetch-func-definition" major func-def-sym (current-buffer)) (set (make-local-variable func-sym) byte-compiled-fun) (set (make-local-variable func-def-sym) fetch-func-definition) (put func-sym 'permanent-local t) (put func-def-sym 'permanent-local t)) (assert (functionp (symbol-value func-sym)) t) ;; return a list def + fun (cons func-sym func-def-sym))) ;; Fix-me: maybe a hook in font-lock-add-keywords?? (defun mumamo-ad-font-lock-keywords-helper (major keywords how add-keywords) ;;(msgtrc "ad-font-lock-keywords-helper %s %s %s %s" major keywords how add-keywords) (if major (mumamo-fetch-major-mode-setup major keywords t t how) ;; Fix-me: Can't do that, need a list of all ;; mumamo-current-chunk-family chunk functions major ;; modes. But this is impossible since the major modes might ;; be determined dynamically. As a work around look in current ;; chunks. (let ((majors (list (mumamo-main-major-mode)))) (dolist (entry mumamo-internal-major-modes-alist) (let ((major (car entry)) (fun-var-sym (caadr entry))) (when (local-variable-p fun-var-sym) (setq majors (cons (car entry) majors))))) (dolist (major majors) (setq major (mumamo-get-major-mode-substitute major 'fontification)) ;;(msgtrc "(fetch-major-mode-setup %s %s %s %s %s)" major keywords nil t how) (mumamo-fetch-major-mode-setup major keywords nil add-keywords how)) ;;(font-lock-mode -1) (font-lock-mode 1) ))) ;; Fix-me: This has stopped working again 2009-11-04, but I do not know when it began... (defadvice font-lock-add-keywords (around mumamo-ad-font-lock-add-keywords activate compile) (if (or (boundp 'mumamo-fetching-major) (boundp 'mumamo-add-font-lock-called) (not mumamo-multi-major-mode)) ad-do-it (let (mumamo-multi-major-mode mumamo-add-font-lock-called (major (ad-get-arg 0)) (keywords (ad-get-arg 1)) (how (ad-get-arg 2))) (mumamo-ad-font-lock-keywords-helper major keywords how t)))) (defadvice font-lock-remove-keywords (around mumamo-ad-font-lock-remove-keywords activate compile) (if (or (boundp 'mumamo-fetching-major) (boundp 'mumamo-add-font-lock-called) (not mumamo-multi-major-mode)) ad-do-it (let (mumamo-multi-major-mode mumamo-add-font-lock-called (major (ad-get-arg 0)) (keywords (ad-get-arg 1))) (mumamo-ad-font-lock-keywords-helper major keywords nil nil)))) (defun mumamo-bad-mode () "MuMaMo replacement for a major mode that could not be loaded." (interactive) (kill-all-local-variables) (setq major-mode 'mumamo-bad-mode) (setq mode-name (propertize "Mumamo Bad Mode" 'face 'font-lock-warning-face))) ;;(mumamo-get-major-mode-setup 'css-mode) ;;(mumamo-get-major-mode-setup 'fundamental-mode) (defun mumamo-get-major-mode-setup (use-major) "Return function for evaluating code in major mode USE-MAJOR. Fix-me: This doc string is wrong, old: Get local variable values for major mode USE-MAJOR. These variables are used for indentation and fontification. The variables are returned in a list with the same format as `mumamo-fetch-major-mode-setup'. The list of local variable values which is returned by this function is cached in `mumamo-internal-major-modes-alist'. This avoids calling the major mode USE-MAJOR for each chunk during fontification and speeds up fontification significantly." ;; Fix-me: Problems here can cause mumamo to loop badly when this ;; function is called over and over again. To avoid this add a ;; temporary entry using mumamo-bad-mode while trying to fetch the ;; correct mode. ;;(assq 'mumamo-bad-mode mumamo-internal-major-modes-alist) (let ((use-major-entry (assq use-major mumamo-internal-major-modes-alist)) bad-mode-entry dummy-entry fun-var-sym fun-var-def-sym) (unless use-major-entry ;; Get mumamo-bad-mode entry and add a dummy entry based on ;; this to avoid looping. (setq bad-mode-entry (assq 'mumamo-bad-mode mumamo-internal-major-modes-alist)) (unless bad-mode-entry ;; Assume it is safe to get the mumamo-bad-mode entry ;-) (add-to-list 'mumamo-internal-major-modes-alist (list 'mumamo-bad-mode (mumamo-fetch-major-mode-setup 'mumamo-bad-mode nil nil nil nil))) (setq bad-mode-entry (assq 'mumamo-bad-mode mumamo-internal-major-modes-alist))) (setq dummy-entry (list use-major (cadr bad-mode-entry))) ;; Before fetching setup add the dummy entry and then ;; immediately remove it. (add-to-list 'mumamo-internal-major-modes-alist dummy-entry) (setq use-major-entry (list use-major (mumamo-fetch-major-mode-setup use-major nil nil nil nil))) (setq mumamo-internal-major-modes-alist (delete dummy-entry mumamo-internal-major-modes-alist)) (add-to-list 'mumamo-internal-major-modes-alist use-major-entry)) (setq fun-var-sym (caadr use-major-entry)) (setq fun-var-def-sym (cdadr use-major-entry)) (assert (functionp (symbol-value fun-var-sym)) t) (assert (eq 'lambda (car (symbol-value fun-var-def-sym))) t) ;; Always make a buffer local value for keywords. (unless (local-variable-p fun-var-sym) (set (make-local-variable fun-var-sym) (symbol-value fun-var-sym)) (set (make-local-variable fun-var-def-sym) (symbol-value fun-var-def-sym))) (caadr (or (assq use-major mumamo-internal-major-modes-alist) )))) ;; (assq use-major ;; (add-to-list 'mumamo-internal-major-modes-alist ;; (list use-major ;; (mumamo-fetch-major-mode-setup ;; use-major nil nil nil)))))))) (defun mumamo-remove-all-chunk-overlays () "Remove all CHUNK overlays from the current buffer." (save-restriction (widen) (mumamo-delete-new-chunks))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Creating and accessing chunks (defun mumamo-define-no-mode (mode-sym) "Fallback major mode when no major mode for MODE-SYM is found." (let ((mumamo-repl4 (intern (format "mumamo-4-%s" mode-sym))) (lighter (format "No %s" mode-sym)) (doc (format "MuMaMo replacement for %s which was not found." mode-sym))) (if (commandp mumamo-repl4) mumamo-repl4 (eval `(defun ,mumamo-repl4 () ,doc (interactive) (kill-all-local-variables) (setq major-mode ',mumamo-repl4) (setq mode-name (propertize ,lighter 'face 'font-lock-warning-face))))))) ;;(mumamo-define-no-mode 'my-ownB-mode) ;;(mumamo-major-mode-from-modespec 'javascript-mode) (defun mumamo-major-mode-from-modespec (major-spec) "Translate MAJOR-SPEC to a major mode. Translate MAJOR-SPEC used in chunk definitions of multi major modes to a major mode. See `mumamo-major-modes' for an explanation." (mumamo-major-mode-from-spec major-spec mumamo-major-modes)) (defun mumamo-major-mode-from-spec (major-spec table) (unless major-spec (mumamo-backtrace "mode-from-modespec, major-spec is nil")) (let ((modes (cdr (assq major-spec table))) (mode 'mumamo-bad-mode)) (setq mode (catch 'mode (dolist (m modes) (when (functionp m) (let ((def (symbol-function m))) (when (and (listp def) (eq 'autoload (car def))) (mumamo-condition-case err (load (nth 1 def)) (error (setq m nil))))) (when m (throw 'mode m)))) nil)) (unless mode (if (functionp major-spec) ;; As a last resort allow spec to be a major mode too: (setq mode major-spec) (if modes (mumamo-warn-once '(mumamo-major-mode-from-modespec) "Couldn't find an available major mode for specification %s,\n alternatives are:\n %s" major-spec modes) (mumamo-warn-once '(mumamo-major-mode-from-modespec) "Couldn't find an available major mode for spec %s" major-spec)) ;;(setq mode 'fundamental-mode) (setq mode (mumamo-define-no-mode major-spec)) )) (mumamo-msgfntfy " mumamo-major-mode-from-modespec %s => %s" major-spec mode) mode)) (defun mumamo-get-existing-new-chunk-at (pos &optional first) "Return last existing chunk at POS if any. However if FIRST get first existing chunk at POS instead." ;;(msgtrc "(mumamo-get-existing-new-chunk-at %s)" pos) (let ((chunk-ovl) (orig-pos pos)) (when (= pos (point-max)) (setq pos (1- pos))) (when (= pos 0) (setq pos 1)) (dolist (o (overlays-in pos (1+ pos))) (when (and (overlay-get o 'mumamo-is-new) ;; Because overlays-in need to have a range of length ;; > 0 we might have got overlays that is after our ;; orig-pos: (<= (overlay-start o) orig-pos)) ;; There can be two, choose the last or first depending on ;; FIRST. (if chunk-ovl ;; (when (or (> (overlay-end o) (overlay-start o)) ;; (overlay-get o 'mumamo-prev-chunk)) (when (if first (< (overlay-end o) (overlay-end chunk-ovl)) (> (overlay-end o) (overlay-end chunk-ovl)) ) (setq chunk-ovl o)) (setq chunk-ovl o)))) chunk-ovl)) (defun mumamo-get-chunk-save-buffer-state (pos) "Return chunk overlay at POS. Preserve state." (let (chunk) ;;(mumamo-save-buffer-state nil ;;(setq chunk (mumamo-get-chunk-at pos))) (setq chunk (mumamo-find-chunks pos "mumamo-get-chunk-save-buffer-state")) ;;) chunk)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Chunk and chunk family properties (defun mumamo-syntax-maybe-completable (pnt) "Return non-nil if at point PNT non-printable characters may occur. This just considers existing chunks." (let* ((chunk (mumamo-find-chunks pnt "mumamo-syntax-maybe-completable")) syn-min-max) (if (not chunk) t (mumamo-update-obscure chunk pnt) (setq syn-min-max (mumamo-chunk-syntax-min-max chunk nil)) ;;(and (> pnt (1+ (mumamo-chunk-syntax-min chunk))) (and (> pnt (1+ (car syn-min-max))) ;;(< pnt (1- (mumamo-chunk-syntax-max chunk))))))) (< pnt (1- (cdr syn-min-max))))))) (defvar mumamo-current-chunk-family nil "The currently used chunk family.") (make-variable-buffer-local 'mumamo-current-chunk-family) (put 'mumamo-current-chunk-family 'permanent-local t) ;; (defvar mumamo-main-major-mode nil) ;; (make-variable-buffer-local 'mumamo-main-major-mode) ;; (put 'mumamo-main-major-mode 'permanent-local t) (defun mumamo-main-major-mode () "Return major mode used when there are no chunks." (let ((mm (cadr mumamo-current-chunk-family))) (if mm mm (msgtrc "main-major-mode => nil, mumamo-current-chunk-family=%s" mumamo-current-chunk-family)))) ;;; (let ((main (cadr mumamo-current-chunk-family))) ;;; (if main ;;; main ;;; mumamo-main-major-mode))) ;; (defun mumamo-unset-chunk-family () ;; "Set chunk family to nil, ie undecided." ;; (interactive) ;; (setq mumamo-current-chunk-family nil)) ;; (defun mumamo-define-chunks (chunk-family) ;; "Set the CHUNK-FAMILY used to divide the buffer." ;; (setq mumamo-current-chunk-family chunk-family)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; General chunk search routines ;; search start forward ;;(defun mumamo-search-fw-exc-start-str (pos max marker) (defun mumamo-chunk-start-fw-str (pos max marker) "General chunk function helper. A chunk function helper like this can be used in `mumamo-find-possible-chunk' to find the borders of a chunk. There are several functions like this that comes with mumamo. Their names tell what they do. Lets look at the parts of the name of this function: mumamo-chunk: All this helper functions begins so -start-: Search for the start of a chunk -fw-: Search forward -str: Search for a string Instead of '-start-' there could be '-end-', ie end. Instead of '-fw-' there could be '-bw-', ie backward. Instead of '-str' there could be '-re', ie regular expression. There could also be a '-inc' at the end of the name. If the name ends with this then the markers should be included in the chunks, otherwise not. The argument POS means where to start the search. MAX means how far to search (when searching backwards the argument is called 'min' instead). MARKER is a string or regular expression (see the name) to search for." (assert (stringp marker)) (let ((pm (point-min)) (cb (current-buffer))) (message "cb=%s" cb) (goto-char (max pm (- pos (length marker))))) (search-forward marker max t)) (defun mumamo-chunk-start-fw-re (pos max marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MAX and MARKER." (assert (stringp marker)) (goto-char (- pos (length marker))) (re-search-forward marker max t)) (defun mumamo-chunk-start-fw-str-inc (pos max marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MAX and MARKER." (assert (stringp marker)) (goto-char pos) (let ((start (search-forward marker max t))) (when start (setq start (- start (length marker)))))) ;; search start backward ;; (defun mumamo-chunk-start-bw-str (pos min marker) ;; "General chunk function helper. ;; See `mumamo-chunk-start-fw-str' for more information and the ;; meaning of POS, MIN and MARKER." ;; ;;(assert (stringp marker)) ;; (let (start-in) ;; (goto-char pos) ;; (setq start-in (search-backward marker min t)) ;; (when start-in ;; ;; do not include the marker ;; (setq start-in (+ start-in (length marker)))) ;; start-in)) ;; (defun mumamo-chunk-start-bw-re (pos min marker) ;; "General chunk function helper. ;; See `mumamo-chunk-start-fw-str' for more information and the ;; meaning of POS, MIN and MARKER." ;; (assert (stringp marker)) ;; (let (start-in) ;; (goto-char pos) ;; (setq start-in (re-search-backward marker min t)) ;; (when start-in ;; ;; do not include the marker ;; (setq start-in (match-end 0))) ;; start-in)) ;; (defun mumamo-chunk-start-bw-str-inc (pos min marker) ;; "General chunk function helper. ;; See `mumamo-chunk-start-fw-str' for more information and the ;; meaning of POS, MIN and MARKER." ;; (assert (stringp marker)) ;; (goto-char (+ pos (length marker))) ;; (search-backward marker min t)) ;; search end forward (defun mumamo-chunk-end-fw-str (pos max marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MAX and MARKER." (assert (stringp marker)) ;;(goto-char (1+ pos)) ;; 1+ cause otherwise ?> is at point (goto-char pos) (let (end-in) (setq end-in (search-forward marker max t)) (when end-in ;; do not include the marker (setq end-in (- end-in (length marker)))) end-in)) (defun mumamo-chunk-end-fw-re (pos max marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MAX and MARKER." (assert (stringp marker)) (goto-char (1+ pos)) ;; 1+ cause otherwise ?> is at point (let (end-in) (setq end-in (re-search-forward marker max t)) (when end-in ;; do not include the marker (setq end-in (match-beginning 0))) end-in)) (defun mumamo-chunk-end-fw-str-inc (pos max marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MAX and MARKER." (assert (stringp marker)) ;;(goto-char (1+ pos)) ;; 1+ cause otherwise ?> is at point (goto-char (1+ (- pos (length marker)))) ;;(msgtrc "mumamo-chunk-end-fw-str-inc %s %s %s, point=%s point-max=%s" pos max marker (point) (point-max)) (search-forward marker max t)) ;; search end backward ;; (defun mumamo-chunk-end-bw-str (pos min marker) ;; "General chunk function helper. ;; See `mumamo-chunk-start-fw-str' for more information and the ;; meaning of POS, MIN and MARKER." ;; (assert (stringp marker)) ;; (goto-char (+ pos (length marker))) ;; (search-backward marker min t)) ;; (defun mumamo-chunk-end-bw-re (pos min marker) ;; "General chunk function helper. ;; See `mumamo-chunk-start-fw-str' for more information and the ;; meaning of POS, MIN and MARKER." ;; (assert (stringp marker)) ;; (goto-char (+ pos (length marker))) ;; (re-search-backward marker min t)) (defun mumamo-chunk-end-bw-str-inc (pos min marker) "General chunk function helper. See `mumamo-chunk-start-fw-str' for more information and the meaning of POS, MIN and MARKER." (assert (stringp marker)) (goto-char pos) (let ((end (search-backward marker min t))) (when end (setq end (+ end (length marker)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; General chunk routines ;; (defvar mumamo-known-chunk-start nil "Internal use only!.") (defconst mumamo-string-syntax-table (let ((tbl (copy-syntax-table))) (modify-syntax-entry ?\" "\"" tbl) (modify-syntax-entry ?\' "\"" tbl) tbl) "Just for \"..\" and '...'.") ;; "..." '...' "..'.." '.."..' (defun mumamo-guess-in-string (pos) "If POS is in a string then return string start position. Otherwise return nil." (when (and (>= pos (point-min))) (let ((here (point)) (inhibit-field-text-motion t) line-beg parsed str-char str-pos) (goto-char pos) (setq line-beg (line-beginning-position)) (setq parsed (with-syntax-table mumamo-string-syntax-table (parse-partial-sexp line-beg pos))) (setq str-char (nth 3 parsed)) (when str-char (skip-chars-backward (string ?^ str-char)) (setq str-pos (point))) (goto-char here) str-pos))) ;;; The main generic chunk routine ;; Fix-me: new routine that really search forward only. Rewrite ;; `mumamo-quick-static-chunk' first with this. (defun mumamo-possible-chunk-forward (pos max chunk-start-fun chunk-end-fun &optional borders-fun) "Search forward from POS to MAX for possible chunk. Return as a list with values \(START END CHUNK-MAJOR BORDERS PARSEABLE-BY CHUNK-END-FUN BORDERS-FUN) START and END are start and end of the possible chunk. CHUNK-MAJOR is the major mode specifier for this chunk. \(Note that this specifier is translated to a major mode through `mumamo-major-modes'.) START-BORDER and END-BORDER may be nil. Otherwise they should be the position where the border ends respectively start at the corresponding end of the chunk. BORDERS is the return value of the optional BORDERS-FUN which takes three parameters, START, END and EXCEPTION-MODE in the return values above. BORDERS may be nil and otherwise has this format: \(START-BORDER END-BORDER CHUNK-MAJOR CHUNK-END-FUN) PARSEABLE-BY is a list of major modes with parsers that can parse the chunk. CHUNK-START-FUN and CHUNK-END-FUN should be functions that searches forward from point for start and end of chunk. They both take two parameters, POS and MAX above. If no possible chunk is found both these functions should return nil, otherwise see below. CHUNK-START-FUN should return a list of the form below if a possible chunk is found: (START CHUNK-MAJOR PARSEABLE-BY) CHUNK-END-FUN should return the end of the chunk. " ;;(msgtrc "possible-chunk-forward %s %s" pos max) (let ((here (point)) start-rec start end chunk-major parseable-by borders ret ) (goto-char pos) ;; Fix-me: check valid. Should this perhaps be done in the ;; function calling this instead? ;;(mumamo-end-in-code syntax-min syntax-max curr-major) (setq start-rec (funcall chunk-start-fun (point) max)) (when start-rec (setq start (nth 0 start-rec)) (setq chunk-major (nth 1 start-rec)) (setq parseable-by (nth 2 start-rec)) (goto-char start) ;; Fix-me: check valid ;;(setq end (funcall chunk-end-fun (point) max)) (when borders-fun (let ((start-border (when start (unless (and (= 1 start) (not chunk-major)) start))) (end-border (when end (unless (and (= (point-max) end) (not chunk-major)) end)))) (setq borders (funcall borders-fun start-border end-border chunk-major)))) (setq ret (list start end chunk-major borders parseable-by chunk-end-fun borders-fun))) (goto-char here) ret)) ;; Fix-me: This routine has some difficulties. One of the more ;; problematic things is that chunk borders may depend on the ;; surrounding chunks syntax. Patterns that possibly could be chunk ;; borders might instead be parts of comments or strings in cases ;; where they should not be valid borders there. (defun mumamo-find-possible-chunk (pos min max bw-exc-start-fun ;; obsolete bw-exc-end-fun fw-exc-start-fun fw-exc-end-fun &optional find-borders-fun) (mumamo-find-possible-chunk-new pos ;;min max bw-exc-start-fun ;;bw-exc-end-fun fw-exc-start-fun fw-exc-end-fun find-borders-fun)) (defun mumamo-find-possible-chunk-new (pos ;;min max bw-exc-start-fun ;;bw-exc-end-fun fw-exc-start-fun fw-exc-end-fun &optional find-borders-fun) ;; This should return no end value! "Return list describing a possible chunk that starts after POS. No notice is taken about existing chunks and no chunks are created. The description returned is for the smallest possible chunk which is delimited by the function parameters. POS must be less than MAX. The function BW-EXC-START-FUN takes two parameters, POS and MIN. It should search backward from POS, bound by MIN, for exception start and return a cons or a list: \(FOUND-POS . EXCEPTION-MODE) \(FOUND-POS EXCEPTION-MODE PARSEABLE-BY) Here FOUND-POS is the start of the chunk. EXCEPTION-MODE is the major mode specifier for this chunk. \(Note that this specifier is translated to a major mode through `mumamo-major-modes'.) PARSEABLE-BY is a list of parsers that can handle the chunk beside the one that may be used by the chunks major mode. Currently only the XML parser in `nxml-mode' is recognized. In this list it should be the symbol `nxml-mode'. The functions FW-EXC-START-FUN and FW-EXC-END-FUN should search for exception start or end, forward resp backward. Those two takes two parameters, start position POS and max position MAX, and should return just the start respectively the end of the chunk. For all three functions the position returned should be nil if search fails. Return as a list with values \(START END EXCEPTION-MODE BORDERS PARSEABLE-BY FR-EXC-FUN FIND-BORDERS-FUN) **Fix-me: FIND-BORDERS-FUN must be split for chunks-in-chunks! The bounds START and END are where the exception starts or stop. Either of them may be nil, in which case this is equivalent to `point-min' respectively `point-max'. If EXCEPTION-MODE is non-nil that is the submode for this range. Otherwise the main major mode should be used for this chunk. BORDERS is the return value of the optional FIND-BORDERS-FUN which takes three parameters, START, END and EXCEPTION-MODE in the return values above. BORDERS may be nil and otherwise has this format: \(START-BORDER END-BORDER EXCEPTION-MODE FW-EXC-FUN) START-BORDER and END-BORDER may be nil. Otherwise they should be the position where the border ends respectively start at the corresponding end of the chunk. PARSEABLE-BY is a list of major modes with parsers that can parse the chunk. FW-EXC-FUN is the function that finds the end of the chunk. This is either FW-EXC-START-FUN or FW-EXC-END-FUN. ---- * Note: This routine is used by to create new members for chunk families. If you want to add a new chunk family you could most often do that by writing functions for this routine. Please see the many examples in mumamo-fun.el for how this can be done. See also `mumamo-quick-static-chunk'." ;;(msgtrc "====") ;;(msgtrc "find-poss-new %s %s %s %s %s %s" pos max bw-exc-start-fun fw-exc-start-fun fw-exc-end-fun find-borders-fun) ;;(mumamo-condition-case err (progn (assert (and (<= pos max)) nil "mumamo-chunk: pos=%s, max=%s, bt=%S" pos max (with-output-to-string (backtrace))) ;; "in" refers to "in exception" and "out" is then in main ;; major mode. (let (start-in-cons exc-mode fw-exc-mode fw-exc-fun parseable-by start-in start-out end-in end-out start end ;;end-of-exception wants-end-type found-valid-end (main-major (mumamo-main-major-mode)) borders border-beg border-end) ;;;; find start of range ;; ;; start normal ;; ;;(setq start-out (funcall bw-exc-end-fun pos min)) ;; Do not check end here! ;;(setq start-out (funcall fw-exc-end-fun pos max)) ;;(msgtrc "find-poss-new.start-out=%s" start-out) ;; start exception (setq start-in (funcall fw-exc-start-fun pos max)) ;;(msgtrc "find-poss-new.start-in=%s" start-in) (when (listp start-in) (setq fw-exc-mode (nth 1 start-in)) (setq start-in (car start-in))) ;; compare (when (and start-in start-out) (if (> start-in start-out) (setq start-in nil) (setq start-out nil))) (cond (start-in (setq start-in-cons (funcall bw-exc-start-fun start-in pos)) ;;(msgtrc "find-poss-new.start-in=%s start-in-cons=%s" start-in start-in-cons) (when start-in-cons (assert (= start-in (car start-in-cons))) (setq exc-mode (cdr start-in-cons))) (setq start start-in)) (start-out (setq start start-out)) ) (when (and exc-mode (listp exc-mode)) (setq parseable-by (cadr exc-mode)) (setq exc-mode (car exc-mode))) ;; borders (when find-borders-fun (let ((start-border (when start (unless (and (= 1 start) (not exc-mode)) start))) (end-border (when end (unless (and (= (point-max) end) (not exc-mode)) end)))) (setq borders (funcall find-borders-fun start-border end-border exc-mode)))) ;; check (setq border-beg (nth 0 borders)) (setq border-end (nth 1 borders)) ;;(when start (assert (<= start pos))) ;;(assert (or (not start) (= start pos))) (when border-beg (assert (<= start border-beg))) ;; Fix-me: This is just totally wrong in some pieces and a ;; desperate try after seeing the problems with wp-app.php ;; around line 1120. Maybe this can be used when cutting chunks ;; from top to bottom however. (when nil ;end (let ((here (point)) end-line-beg end-in-string start-in-string (start-border (or (nth 0 borders) start)) (end-border (or (nth 1 borders) end))) ;; Check if in string ;; Fix-me: add comments about why and examples + tests ;; Fix-me: must loop to find good borders .... (when end ;; Fix-me: more careful positions for guess (setq end-in-string (mumamo-guess-in-string ;;(+ end 2) (1+ end-border) )) (when end-in-string (when start (setq start-in-string (mumamo-guess-in-string ;;(- start 2) (1- start-border) ))) (if (not start-in-string) (setq end nil) (if exc-mode (if (and start-in-string end-in-string) ;; If both are in a string and on the same line then ;; guess this is actually borders, otherwise not. (unless (= start-in-string end-in-string) (setq start nil) (setq end nil)) (when start-in-string (setq start nil)) (when end-in-string (setq end nil))) ;; Fix-me: ??? (when start-in-string (setq start nil)) )) (unless (or start end) (setq exc-mode nil) (setq borders nil) (setq parseable-by nil)))))) (when (or start end exc-mode borders parseable-by) (setq fw-exc-fun (if exc-mode ;; Fix-me: this is currently correct, ;; but will change if exc mode in exc ;; mode is allowed. fw-exc-end-fun ;; Fix-me: these should be collected later ;;fw-exc-start-fun nil )) (mumamo-msgfntfy "--- mumamo-find-possible-chunk-new %s" (list start end exc-mode borders parseable-by fw-exc-fun)) ;;(message "--- mumamo-find-possible-chunk-new %s" (list start end exc-mode borders parseable-by fw-exc-fun)) (when fw-exc-mode (unless (eq fw-exc-mode exc-mode) ;;(message "fw-exc-mode=%s NEQ exc-mode=%s" fw-exc-mode exc-mode) )) ;;(msgtrc "find-poss-new returns %s" (list start end exc-mode borders parseable-by fw-exc-fun find-borders-fun)) (when fw-exc-fun (list start end exc-mode borders parseable-by fw-exc-fun find-borders-fun))))) ;;(error (mumamo-display-error 'mumamo-chunk "%s" (error-message-string err))) ;;) ) ;; (defun temp-overlays-here () ;; (interactive) ;; (let* ((here (point)) ;; (ovl-at (overlays-at here)) ;; (ovl-in (overlays-in here (1+ here))) ;; (ovl-in0 (overlays-in here here)) ;; ) ;; (with-output-to-temp-buffer (help-buffer) ;; (help-setup-xref (list #'temp-overlays-at) (interactive-p)) ;; (with-current-buffer (help-buffer) ;; (insert (format "overlays-at %s:\n%S\n\n" here ovl-at)) ;; (insert (format "overlays-in %s-%s:\n%S\n\n" here (1+ here) ovl-in)) ;; (insert (format "overlays-in %s-%s:\n%S\n\n" here here ovl-in0)) ;; )))) ;; (defun temp-cursor-pos () ;; (interactive) ;; (what-cursor-position t)) ;; ;;(global-set-key [f9] 'temp-cursor-pos) ;; (defun temp-test-new-create-chunk () ;; (interactive) ;; (mumamo-delete-new-chunks) ;; ;;(setq x1 nil) ;; (let (x1 ;; (first t)) ;; (while (or first x1) ;; (setq first nil) ;; (setq x1 (mumamo-new-create-chunk (mumamo-find-next-chunk-values x1 nil nil nil))))) ;; ) ;; (defun temp-create-last-chunk () ;; (interactive) ;; (mumamo-new-create-chunk (mumamo-find-next-chunk-values mumamo-last-chunk nil nil nil))) (defun mumamo-delete-new-chunks () (setq mumamo-last-chunk nil) (save-restriction (widen) (let ((ovls (overlays-in (point-min) (point-max)))) (dolist (ovl ovls) (when (overlay-get ovl 'mumamo-is-new) ;;(msgtrc "delete-overlay %s delete-new-chunks" ovl) (delete-overlay ovl)))))) (defun mumamo-new-create-chunk (new-chunk-values) "Create and return a chunk from NEW-CHUNK-VALUES. When doing this store the functions for creating the next chunk after this in the properties below of the now created chunk: - 'mumamo-next-major: is nil or the next chunk's major mode. - 'mumamo-next-end-fun: function that searches for end of AFTER-CHUNK - 'mumamo-next-border-fun: functions that finds borders" ;;((1 696 nxhtml-mode nil nil nil nil) (696 nil php-mode nil nil nil nil)) ;;(current (list curr-min curr-max curr-major curr-border-min curr-border-max curr-parseable curr-fw-exc-fun)) ;;(msgtrc "######new-create.chunk.new-chunk-values=%s" new-chunk-values) (when new-chunk-values (let* ((this-values (nth 0 new-chunk-values)) (next-values (nth 1 new-chunk-values)) (next-major (nth 0 next-values)) (next-end-fun (nth 1 next-values)) (next-border-fun (nth 2 next-values)) (next-depth-diff (nth 3 next-values)) (next-indent (nth 4 next-values)) (this-beg (nth 0 this-values)) (this-end (nth 1 this-values)) (this-maj (nth 2 this-values)) (this-bmin (nth 3 this-values)) (this-bmax (nth 4 this-values)) (this-pable (nth 5 this-values)) (this-after-chunk (nth 7 this-values)) ;;(this-is-closed (nth 8 this-values)) (this-insertion-type-beg (nth 8 this-values)) (this-insertion-type-end (nth 9 this-values)) ;;(this-is-closed (and this-end (< 1 this-end))) (this-after-chunk-depth (when this-after-chunk (overlay-get this-after-chunk 'mumamo-depth))) (depth-diff (if this-after-chunk (overlay-get this-after-chunk 'mumamo-next-depth-diff) 1)) (depth (if this-after-chunk-depth (+ this-after-chunk-depth depth-diff) 0)) ;;(fw-funs (nth 6 this-values)) ;;(borders-fun (nth 7 this-values)) ;;(this-is-closed (when (or this-end (mumamo-fun-eq this-maj (mumamo-main-major-mode))) t)) (use-this-end (if this-end this-end (1+ (buffer-size)))) ;(save-restriction (widen) (point-max)))) (this-chunk (when (and (<= this-beg use-this-end) ;; Avoid creating two empty overlays ;; at the this-end - but what if we are ;; not creating, just changing the ;; last overlay ... ;; ;; (not (and (= this-beg use-this-end) ;; (= use-this-end (1+ (buffer-size))) ;; this-after-chunk ;; (= 0 (- (overlay-end this-after-chunk) (overlay-start this-after-chunk))) ;; )) ) (when (= this-beg 1) (if (= use-this-end 1) (assert (mumamo-fun-eq (mumamo-main-major-mode) this-maj) t) (if this-after-chunk ;; not first (assert (not (mumamo-fun-eq (mumamo-main-major-mode) this-maj)) t) (assert (mumamo-fun-eq (mumamo-main-major-mode) this-maj) t)))) ;;(message "Create chunk %s - %s" this-beg use-this-end) ;;(make-overlay this-beg use-this-end nil nil (not this-is-closed)) (make-overlay this-beg use-this-end nil this-insertion-type-beg this-insertion-type-end) )) ;; Fix-me: move to mumamo-find-next-chunk-values (this-border-fun (when (and this-chunk this-after-chunk) ;;(overlay-get this-after-chunk 'mumamo-next-border-fun) (mumamo-chunk-car this-after-chunk 'mumamo-next-border-fun) )) (this-borders (when this-border-fun ;;(msgtrc "(funcall %s %s %s %s)" this-border-fun this-beg this-end this-maj) (funcall this-border-fun this-beg this-end this-maj))) ;; Fix-me, check: there is no first border when moving out. (this-borders-min (when (= 1 depth-diff) (nth 0 this-borders))) ;; Fix-me, check: there is no bottom border when we move ;; further "in" since borders are now always inside ;; sub-chunks (if I remember correctly...). ;;(this-borders-max (when (and this-is-closed (this-borders-max (when (and (not this-insertion-type-end) (/= 1 next-depth-diff)) (nth 1 this-borders))) ) ;;(msgtrc "created %s, major=%s" this-chunk this-maj) (when (> depth 4) (error "Chunk depth > 4")) (setq this-bmin nil) (setq this-bmax nil) (when this-borders-min (setq this-bmin (- this-borders-min this-beg))) (when this-borders-max (setq this-bmax (- this-end this-borders-max))) ;;(when this-after-chunk (message "this-after-chunk.this-end=%s, this-beg=%s, this-end=%s" (overlay-end this-after-chunk) this-beg this-end)) ;;(message "fw-funs=%s" fw-funs) (when this-chunk (overlay-put this-chunk 'mumamo-is-new t) (overlay-put this-chunk 'face (mumamo-background-color depth)) (overlay-put this-chunk 'mumamo-depth depth) ;; Values for next chunk (overlay-put this-chunk 'mumamo-next-depth-diff next-depth-diff) (assert (symbolp next-major) t) (overlay-put this-chunk 'mumamo-next-major next-major) ;; Values for this chunk ;;(overlay-put this-chunk 'mumamo-is-closed this-is-closed) (overlay-put this-chunk 'mumamo-insertion-type-end this-insertion-type-end) (overlay-put this-chunk 'mumamo-syntax-min-d this-bmin) (overlay-put this-chunk 'mumamo-syntax-max-d this-bmax) (overlay-put this-chunk 'mumamo-prev-chunk this-after-chunk) (overlay-put this-chunk 'mumamo-next-indent next-indent) (when this-after-chunk (overlay-put this-after-chunk 'mumamo-next-chunk this-chunk)) ;;(msgtrc "\n<<<<<<<<<<<<<<<<< next-depth-diff/depth-diff=%s/%s, this-maj=%s, this-after-chunk=%s" next-depth-diff depth-diff this-maj this-after-chunk) ;;(overlay-put this-chunk 'mumamo-next-end-fun next-end-fun) (cond ((= 1 next-depth-diff) (mumamo-chunk-push this-chunk 'mumamo-next-border-fun next-border-fun) (mumamo-chunk-push this-chunk 'mumamo-next-end-fun next-end-fun)) ((= -1 next-depth-diff) (mumamo-chunk-pop this-chunk 'mumamo-next-border-fun) (mumamo-chunk-pop this-chunk 'mumamo-next-end-fun)) ((= 0 next-depth-diff) nil) (t (error "next-depth-diff=%s" next-depth-diff))) ;;(msgtrc "mumamo-next-end-fun=%S" (overlay-get this-chunk 'mumamo-next-end-fun)) ;; Fix-me: replace 'mumamo-major-mode with multi major mode to make it more flexible. (cond ((= 1 depth-diff) (mumamo-chunk-push this-chunk 'mumamo-major-mode this-maj)) ((= -1 depth-diff) (mumamo-chunk-pop this-chunk 'mumamo-major-mode) ) (t (error "depth-diff=%s" depth-diff))) (overlay-put this-chunk 'mumamo-parseable-by this-pable) (overlay-put this-chunk 'created (current-time-string)) (mumamo-update-chunk-margin-display this-chunk) (setq mumamo-last-chunk this-chunk) ;; Use this chunk!!!! ;; Get syntax-begin-function for syntax-ppss: (let* ((syntax-begin-function (mumamo-with-major-mode-fontification this-maj ;; Do like in syntax.el: '(if syntax-begin-function (progn syntax-begin-function) (when (and (not syntax-begin-function) ;; fix-me: How to handle boundp here? (boundp 'font-lock-beginning-of-syntax-function) font-lock-beginning-of-syntax-function) font-lock-beginning-of-syntax-function))))) (mumamo-msgfntfy "Got syntax-begin-function, modified=%s" (buffer-modified-p)) (overlay-put this-chunk 'syntax-begin-function syntax-begin-function)) ) ;;(msgtrc "Created %s, this=%s, next=%s" this-chunk this-values next-values) this-chunk ) )) (defun mumamo-update-chunk-margin-display (chunk) "Set before-string of CHUNK as spec by `mumamo-margin-use'." ;; Fix-me: This is not displayed. Emacs bug? ;;(overlay-put this-chunk 'before-string `((margin left-margin) ,(format "%d %s" depth maj))) (if (not mumamo-margin-info-mode) (overlay-put chunk 'before-string nil) (let* ((depth (overlay-get chunk 'mumamo-depth)) (maj (mumamo-chunk-car chunk 'mumamo-major-mode)) (strn (propertize (format "%d" depth) 'face (list :inherit (or (mumamo-background-color depth) 'default) :foreground "#505050" :underline t :slant 'normal :weight 'normal ))) (maj-name (substring (symbol-name maj) 0 -5)) (strm (propertize maj-name 'face (list :foreground "#a0a0a0" :underline nil :background (frame-parameter nil 'background-color) :weight 'normal :slant 'normal))) str (margin (mumamo-margin-used))) (when (> (length strm) 5) (setq strm (substring strm 0 5))) (setq str (concat strn strm (propertize " " 'face 'default) )) (overlay-put chunk 'before-string (propertize " " 'display `((margin ,margin) ,str)))))) (defun mumamo-update-chunks-margin-display (buffer) "Apply `update-chunk-margin-display' to all chunks in BUFFER." (with-current-buffer buffer (save-restriction (widen) (let ((chunk (mumamo-find-chunks 1 "margin-disp")) (while-n0 0)) (while (and (mumamo-while 1500 'while-n0 "chunk") chunk) (mumamo-update-chunk-margin-display chunk) (setq chunk (overlay-get chunk 'mumamo-next-chunk))))))) (defvar mumamo-margin-used nil) (make-variable-buffer-local 'mumamo-margin-used) (put 'mumamo-margin-used 'permanent-local t) (defun mumamo-margin-used () (setq mumamo-margin-used (if (and (boundp 'linum-mode) linum-mode) 'right-margin (nth 0 mumamo-margin-use)))) ;; (defun mumamo-set-window-margins-used (win) ;; "Set window margin according to `mumamo-margin-use'." ;; ;; Fix-me: old-margin does not work, break it up ;; (let* ((old-margin-used mumamo-margin-used) ;; (margin-used (mumamo-margin-used)) ;; (width (nth 1 mumamo-margin-use)) ;; (both-widths (window-margins win)) ;; (old-left (eq old-margin-used 'left-margin)) ;; (left (eq margin 'left-margin))) ;; ;; Change only the margin we used! ;; (if (not mumamo-margin-info-mode) ;; (progn ;; (set-window-margins win ;; (if left nil (car both-widths)) ;; (if (not left) nil (cdr both-widths))) ;; ) ;; ;;(msgtrc "set-window-margins-used margin-info-mode=t") ;; (case margin-used ;; ('left-margin (set-window-margins win width (when old-left (cdr both-widths)))) ;; ('right-margin (set-window-margins win (car both-widths) width)))))) (defun mumamo-update-buffer-margin-use (buffer) ;;(msgtrc "update-buffer-margin-use %s" buffer) (when (fboundp 'mumamo-update-chunks-margin-display) (with-current-buffer buffer (when mumamo-multi-major-mode (let* ((old-margin-used mumamo-margin-used) (margin-used (mumamo-margin-used)) (old-is-left (eq old-margin-used 'left-margin)) (is-left (eq margin-used 'left-margin)) (width (nth 1 mumamo-margin-use)) (need-update nil)) (if (not mumamo-margin-info-mode) (when old-margin-used (setq need-update t) (setq old-margin-used nil) (if old-is-left (setq left-margin-width 0) (setq right-margin-width 0))) (unless (and (eq old-margin-used margin-used) (= width (if old-is-left left-margin-width right-margin-width))) (setq need-update t) (if is-left (setq left-margin-width width) (setq right-margin-width width)) (unless (eq old-margin-used margin-used) (if old-is-left (setq left-margin-width 0) (setq right-margin-width 0))))) (when need-update (mumamo-update-chunks-margin-display buffer) (dolist (win (get-buffer-window-list buffer)) (set-window-buffer win buffer))) ) ;; Note: window update must be before buffer update because it ;; uses old-margin from the call to function margin-used. ;; (dolist (win (get-buffer-window-list buffer)) ;; (mumamo-set-window-margins-used win)) ;; (mumamo-update-chunks-margin-display buffer) )))) (defun mumamo-new-chunk-value-min (values) (let ((this-values (nth 0 values))) (nth 0 this-values))) (defun mumamo-new-chunk-value-max (values) (let ((this-values (nth 0 values))) (nth 1 this-values))) (defun mumamo-new-chunk-equal-chunk-values (chunk values) ;;(msgtrc "eq? chunk=%S, values=%S" chunk values) (let* (;; Chunk (chunk-is-new (overlay-get chunk 'mumamo-is-new)) ;;(chunk-is-closed (overlay-get chunk 'mumamo-is-closed)) (chunk-insertion-type-end (overlay-get chunk 'mumamo-insertion-type-end)) (chunk-next-major (overlay-get chunk 'mumamo-next-major)) (chunk-next-end-fun (mumamo-chunk-car chunk 'mumamo-next-end-fun)) (chunk-next-border-fun (mumamo-chunk-car chunk 'mumamo-next-border-fun)) (chunk-next-chunk-diff (overlay-get chunk 'mumamo-next-depth-diff)) (chunk-beg (overlay-start chunk)) (chunk-end (overlay-end chunk)) (chunk-bmin (overlay-get chunk 'mumamo-syntax-min-d)) (chunk-bmax (overlay-get chunk 'mumamo-syntax-max-d)) (chunk-prev-chunk (overlay-get chunk 'mumamo-prev-chunk)) (chunk-major-mode (mumamo-chunk-car chunk 'mumamo-major-mode)) (chunk-pable (overlay-get chunk 'mumamo-parseable-by)) (chunk-depth-diff (if chunk-prev-chunk (overlay-get chunk-prev-chunk 'mumamo-next-depth-diff) 0)) ;; Values (this-values (nth 0 values)) (next-values (nth 1 values)) (values-next-major (nth 0 next-values)) (values-next-end-fun (nth 1 next-values)) (values-next-border-fun (nth 2 next-values)) (values-next-depth-diff (nth 3 next-values)) (values-beg (nth 0 this-values)) (values-end (nth 1 this-values)) (values-major-mode (nth 2 this-values)) (values-bmin (nth 3 this-values)) (values-bmax (nth 4 this-values)) (values-pable (nth 5 this-values)) (values-prev-chunk (nth 7 this-values)) (values-insertion-type-beg (nth 8 this-values)) (values-insertion-type-end (nth 9 this-values)) ;;(values-is-closed (when values-end t)) ) ;;(msgtrc "values=%S" values) (and t ;chunk-is-new (eq chunk-next-major values-next-major) ;; Can't check chunk-next-end-fun or chunk-next-border-fun ;; here since they are fetched from prev chunk: ;;(progn (message "eq-c-v: here b: %s /= %s" chunk-next-end-fun values-next-end-fun) t) ;;(eq chunk-next-end-fun values-next-end-fun) ;;(progn (message "eq-c-v: here c, %s /= %s" chunk-next-border-fun values-next-border-fun) t) ;;(eq chunk-next-border-fun values-next-border-fun) (= chunk-next-chunk-diff values-next-depth-diff) (= chunk-beg values-beg) ;;(progn (message "eq-c-v: here b") t) ;; (and (equal chunk-is-closed values-is-closed) ;; (or (not chunk-is-closed) (and (equal chunk-insertion-type-end values-insertion-type-end) (or ;;chunk-insertion-type-end (= chunk-end values-end))) ;;(progn (message "eq-c-v: here c, %s /= %s" chunk-major-mode values-major-mode) t) (or (= -1 chunk-depth-diff) (eq chunk-major-mode values-major-mode)) ;;(progn (message "eq-c-v: here d") t) (equal chunk-pable values-pable) ;;(progn (message "eq-c-v: here e") t) (eq chunk-prev-chunk values-prev-chunk) ;;(progn (message "eq-c-v: here f") t) ;;(eq chunk-is-closed values-is-closed) (eq chunk-insertion-type-end values-insertion-type-end) ;; fix-me: bmin bmax ;;(and chunk-bmin values-bmin (= chunk-bmin values-bmin)) ;;(and chunk-bmax values-bmax (= chunk-bmax values-bmax)) ) )) (defvar mumamo-sub-chunk-families nil "Chunk dividing routines for sub chunks. A major mode in a sub chunk can inherit chunk dividing routines from multi major modes. This is the way chunks in chunks is implemented. This variable is an association list with entries of the form \(CHUNK-MAJOR CHUNK-FAMILY) where CHUNK-MAJOR is the major mode in a chunk and CHUNK-FAMILY is a chunk family \(ie the third argument to `define-mumamo-multi-major-mode'. You can use the function `mumamo-inherit-sub-chunk-family' to add to this list.") (defvar mumamo-multi-local-sub-chunk-families nil "Multi major mode local chunk dividing rourines for sub chunks. Like `mumamo-sub-chunk-families' specific additions for multi major modes. The entries have the form \((CHUNK-MAJOR . MULTI-MAJOR) CHUNK-FAMILY) Use the function `mumamo-inherit-sub-chunk-family-locally' to add to this list.") ;;(mumamo-get-sub-chunk-funs 'html-mode) (defun mumamo-get-sub-chunk-funs (major) "Get chunk family sub chunk with major mode MAJOR." (let ((rec (or (assoc (cons major mumamo-multi-major-mode) mumamo-multi-local-sub-chunk-families) (assoc major mumamo-sub-chunk-families)))) (caddr (cadr rec)))) (defun mumamo-inherit-sub-chunk-family-locally (multi-major multi-using) "Add chunk dividing routines from MULTI-MAJOR locally. The dividing routines from multi major mode MULTI-MAJOR can then be used in sub chunks in buffers using multi major mode MULTI-USING." (let* ((chunk-family (get multi-major 'mumamo-chunk-family)) (major (nth 1 chunk-family))) (let ((major-mode major)) (when (derived-mode-p 'nxml-mode) (error "Major mode %s major can't be used in sub chunks" major))) (add-to-list 'mumamo-multi-local-sub-chunk-families (list (cons major multi-using) chunk-family)))) (defun mumamo-inherit-sub-chunk-family (multi-major) "Inherit chunk dividing routines from multi major modes. Add chunk family from multi major mode MULTI-MAJOR to `mumamo-sub-chunk-families'. Sub chunks with major mode the same as MULTI-MAJOR mode will use this chunk familyu to find subchunks." (let* ((chunk-family (get multi-major 'mumamo-chunk-family)) (major (nth 1 chunk-family))) (let ((major-mode major)) (when (derived-mode-p 'nxml-mode) (error "Major mode %s major can't be used in sub chunks" major))) (add-to-list 'mumamo-sub-chunk-families (list major chunk-family)))) (defun mumamo-find-next-chunk-values (after-chunk from after-change-max chunk-at-after-change) "Search forward for start of next chunk. Return a list with chunk values for next chunk after AFTER-CHUNK and some values for the chunk after it. For the first chunk AFTER-CHUNK is nil. Otherwise the values in stored in AFTER-CHUNK is used to find the new chunk, its border etc. See also `mumamo-new-create-chunk' for more information." ;;(msgtrc "(find-next-chunk-values %s %s %s %s)" after-chunk from after-change-max chunk-at-after-change) ;;(mumamo-backtrace "find-next") (when after-chunk (unless (eq (overlay-buffer after-chunk) (current-buffer)) (error "mumamo-find-next-chunk-values: after-chunk=%S, cb=%S" after-chunk (current-buffer)))) (let* ((here (point)) (max (point-max)) ;;(after-chunk-is-closed (when after-chunk-valid (overlay-get after-chunk 'mumamo-is-closed))) (after-chunk-insertion-type-end (when after-chunk (overlay-get after-chunk 'mumamo-insertion-type-end))) ;; Note that "curr-*" values are fetched from "mumamo-next-*" values in after-chunk (curr-min (if after-chunk (overlay-end after-chunk) 1)) (curr-end-fun (when after-chunk (mumamo-chunk-car after-chunk 'mumamo-next-end-fun))) (curr-border-fun (when curr-end-fun (mumamo-chunk-car after-chunk 'mumamo-next-border-fun))) (curr-syntax-min-max (when curr-border-fun (funcall curr-border-fun (overlay-end after-chunk) nil nil))) (curr-syntax-min (or (car curr-syntax-min-max) (when after-chunk (overlay-end after-chunk)) 1)) (search-from (or nil ;from curr-syntax-min)) ;;(dummy (msgtrc "search-from=%s" search-from)) (main-chunk-funs (let ((chunk-info (cdr mumamo-current-chunk-family))) (cadr chunk-info))) (curr-major (if after-chunk (or ;; 'mumamo-next-major is used when we are going into a sub chunk. (overlay-get after-chunk 'mumamo-next-major) ;; We are going out of a sub chunk. (mumamo-chunk-cadr after-chunk 'mumamo-major-mode)) (mumamo-main-major-mode))) ;;(dummy (msgtrc "curr-major=%s" curr-major)) (curr-chunk-funs (if (or (not after-chunk) (= 0 (+ (overlay-get after-chunk 'mumamo-depth) (overlay-get after-chunk 'mumamo-next-depth-diff)))) main-chunk-funs (mumamo-get-sub-chunk-funs curr-major))) curr-max next-max curr-max-found next-min curr-border-min curr-border-max curr-parseable next-fw-exc-fun next-indent next-major curr-end-fun-end next-border-fun ;; The insertion types for the new chunk (curr-insertion-type-beg (when after-chunk after-chunk-insertion-type-end)) curr-insertion-type-end next-depth-diff r-point ) (unless (and after-chunk-insertion-type-end (= (1+ (buffer-size)) ;; ie point-max (overlay-end after-chunk))) (when (>= max search-from) (when curr-end-fun ;; If after-change-max is non-nil here then this function has ;; been called after changes that are all in one chunk. We ;; need to check if the chunk right border have been changed, ;; but we do not have to look much longer than the max point ;; of the change. ;;(message "set after-change-max nil") (setq after-change-max nil) (let* ((use-max (if nil ;;after-change-max (+ after-change-max 100) max)) (chunk-end (and chunk-at-after-change (overlay-end chunk-at-after-change))) ;;(use-min (max (- search-from 2) (point-min))) (use-min curr-syntax-min) (possible-end-fun-end t) (end-search-pos use-min)) ;; The code below takes care of the case when to subsequent ;; chunks have the same ending delimiter. (Maybe a while ;; loop is bit overkill here.) (while (and possible-end-fun-end (not curr-end-fun-end) (< end-search-pos use-max)) (setq curr-end-fun-end (funcall curr-end-fun end-search-pos use-max)) (if (not curr-end-fun-end) (setq possible-end-fun-end nil) (cond ((and t ;after-chunk-is-closed (< curr-end-fun-end (overlay-end after-chunk))) (setq curr-end-fun-end nil) (setq end-search-pos (1+ end-search-pos))) ;; See if the end is in code ((let* ((syn2-min-max (when curr-border-fun (funcall curr-border-fun (overlay-end after-chunk) curr-end-fun-end nil))) (syn2-max (or (cadr syn2-min-max) curr-end-fun-end))) (not (mumamo-end-in-code use-min syn2-max curr-major))) (setq end-search-pos (1+ curr-end-fun-end)) (setq curr-end-fun-end nil) )))) (unless curr-end-fun-end ;; Use old end if valid (and after-change-max chunk-end (= -1 (overlay-get chunk-at-after-change 'mumamo-next-depth-diff)) (< after-change-max chunk-end) chunk-end)) ;; Fix-me: Check if old chunk is valid. It is not valid if ;; depth-diff = -1 and curr-end-fun-end is not the same as ;; before. ;; Fix-me: this test should also be made for other chunks ;; searches, but this catches most problems I think. ;; (or (not curr-end-fun-end) ;; ;; Fix-me: The bug in wiki-090804-js.html indicates that ;; ;; we should not subtract 1 here. The subchunk there ;; ;; ends with and this can't be in column 1 ;; ;; when the line before ends with a // style js comment ;; ;; unless we don't subtract 1. ;; ;; ;; ;; However wiki-strange-hili-080629.html does not work ;; ;; then because then the final " in style="..." is ;; ;; included in the scan done in mumamo-end-in-code. ;; ;; ;; ;; The solution is to check for the syntax borders here. ;; (let* ((syn2-min-max (when curr-border-fun ;; (funcall curr-border-fun ;; (overlay-end after-chunk) ;; curr-end-fun-end ;; nil))) ;; (syntax-max (or (cadr syn2-min-max) ;; curr-end-fun-end))) ;; ;;(mumamo-end-in-code syntax-min (- curr-end-fun-end 1) curr-major) ;; ;; ;; ;; fix-me: This should be really in the individual ;; ;; routines that finds possible chunks. Mabye this is ;; ;; possible to fix now when just looking forward for ;; ;; chunks? ;; (mumamo-end-in-code curr-syntax-min syntax-max curr-major) ;; ) ;; (setq curr-end-fun-end nil)) ;; Use old result if valid ;; (and nil ;(not curr-end-fun-end) ;; chunk-at-after-change ;; (= -1 (overlay-get chunk-at-after-change 'mumamo-next-depth-diff)) ;; (setq curr-end-fun-end (overlay-end chunk-at-after-change))) ;;(msgtrc "find-next-chunk-values:curr-end-fun-end after end-in-code=%s" curr-end-fun-end) )) ;;(msgtrc "find-next-chunk-values:here d, curr-min=%s, after-chunk=%s" curr-min after-chunk) (when (listp curr-chunk-funs) ;;(msgtrc "find-next-chunk-values:curr-chunk-funs=%s" curr-chunk-funs) (setq r-point (point)) (dolist (fn curr-chunk-funs) ;;(msgtrc "find-next-chunk-values:before (r (funcall fn search-from search-from max)), fn=%s search-from=%s, max=%s" fn search-from max) (assert (= r-point (point)) t) (let* ((r (funcall fn search-from search-from max)) (rmin (nth 0 r)) (rmax (nth 1 r)) (rmajor-sub (nth 2 r)) (rborder (nth 3 r)) (rparseable (nth 4 r)) (rfw-exc-fun (nth 5 r)) (rborder-fun (nth 6 r)) (rindent (nth 7 r)) (rborder-min (when rborder (nth 0 rborder))) (rborder-max (when rborder (nth 1 rborder))) ;;(rmin-found rmin) ) ;;(msgtrc "find-next-chunk-values:fn=%s, r=%s" fn r) (goto-char r-point) (when r (when rmax (message "mumamo warning: Bad r=%s, nth 1 should be nil" r)) (unless (or rmin rmax) (error "Bad r=%s, fn=%s" r fn)) (unless rfw-exc-fun (error "No fw-exc-fun returned from fn=%s, r=%s" fn r)) (unless rmajor-sub (error "No major mode for sub chunk, fn=%s, r=%s" fn r))) (when r (mumamo-msgfntfy " fn=%s, r=%s" fn r) (unless rmin (setq rmin (point-max))) ;;(unless rmax (setq rmax (point-min))) ;; Do not allow zero length chunks (unless rmax (setq rmax (point-max))) (unless (and (> rmin 1) rmax (= rmin rmax)) ;; comparision have to be done differently if we are in an ;; exception part or not. since we are doing this from top to ;; bottom the rules are: ;; ;; - exception parts always outrules non-exception part. when ;; in exception part the min start point should be used. ;; - when in non-exception part the max start point and the ;; min end point should be used. ;; ;; check if first run: ;; Fix-me: there is some bug here when borders are not ;; included and are not 0 width. (if (not next-min) (progn (setq next-min rmin) (setq curr-border-min rborder-min) (setq next-max rmax) (setq curr-border-max rborder-max) ;;(setq curr-max-found rmin-found) (setq curr-parseable rparseable) (setq next-fw-exc-fun rfw-exc-fun) (setq next-border-fun rborder-fun) (setq next-indent rindent) (setq next-major rmajor-sub)) (if rmajor-sub (if next-major (when (or (not next-min) (< rmin next-min)) (setq next-min rmin) (setq curr-border-min rborder-min) (when rmax (setq max rmax)) (setq curr-border-max rborder-max) ;;(when rmin-found (setq curr-max-found t)) (setq curr-parseable rparseable) (setq next-fw-exc-fun rfw-exc-fun) (setq next-border-fun rborder-fun) (setq next-indent rindent) (setq next-major rmajor-sub)) (setq next-min rmin) (setq curr-border-min rborder-min) (when rmax (setq max rmax)) (setq curr-border-max rborder-max) ;;(when rmin-found (setq curr-max-found t)) (setq curr-parseable rparseable) (setq next-fw-exc-fun rfw-exc-fun) (setq next-border-fun rborder-fun) (setq next-indent rindent) (setq next-major rmajor-sub)) (unless next-major (when (> next-min rmin) (setq next-min rmin) (setq curr-border-min rborder-min)) (when (and rmax max (> rmax max)) ;;(setq max-found rmin-found) ;;(when rmin-found (setq curr-max-found t)) (when rmax (setq max rmax)) (setq curr-border-max rborder-max)) )))) (mumamo-msgfntfy "next-min/max=%s/%s border=%s/%s search-from=%s" next-min max curr-border-min curr-border-max search-from) ;; check! (when (and next-min max) ;;(assert (>= next-min search-from) t) (assert (<= search-from max) t) (when curr-border-min (assert (<= next-min curr-border-min) t) (assert (<= curr-border-min max) t)) (when curr-border-max (assert (<= next-min curr-border-max) t) (assert (<= curr-border-max max) t)))) ))) (goto-char here) (setq curr-max-found (or curr-max-found curr-end-fun-end)) (when t ;curr-max-found (setq curr-max (if max max (point-max))) (setq curr-max (min (if next-min next-min curr-max) (if curr-end-fun-end curr-end-fun-end curr-max)))) ;;(setq curr-max nil) (setq next-depth-diff (cond ( (and curr-max curr-end-fun-end (= curr-max curr-end-fun-end)) -1) ( (= curr-max (1+ (buffer-size))) 0) ( t 1))) (when (= -1 next-depth-diff) ;; We will pop it from 'mumamo-major-mode (setq next-major nil)) (when curr-max (unless (>= curr-max curr-min) (error "curr-max is not >= curr-min"))) ;;(setq curr-is-closed (and curr-max (< 1 curr-max))) (when (and curr-max (= 1 curr-max)) (assert (mumamo-fun-eq curr-major (mumamo-main-major-mode)) t) ) (assert (symbolp next-major) t) ;; Fix-me: see for example rr-min8.php (when (or ;;(not after-chunk) (= curr-max (1+ (buffer-size))) (cond ((= next-depth-diff 1) next-border-fun) ((= next-depth-diff -1) next-border-fun) ((= next-depth-diff 0) t) (t (error "next-depth-diff=%s" next-depth-diff)))) (setq curr-insertion-type-end t)) (let ((current (list curr-min curr-max curr-major curr-border-min curr-border-max curr-parseable curr-chunk-funs after-chunk ;;curr-is-closed curr-insertion-type-beg curr-insertion-type-end )) (next (list next-major next-fw-exc-fun next-border-fun next-depth-diff next-indent))) ;;(msgtrc "find-next-chunk-values=> current=%s, next=%s" current next) (list current next)))))) ;; Fix-me: This should check if the new chunk should be ;; parsed or not ;; (defsubst mumamo-chunk-nxml-parseable (chunk) ;; (mumamo-fun-eq (mumamo-main-major-mode) ;; (mumamo-chunk-major-mode xml-chunk))) (defun mumamo-valid-nxml-point (pos) "Return non-nil if position POS is in an XML chunk." (memq 'nxml-mode (get-text-property pos 'mumamo-parseable-by))) (defun mumamo-valid-nxml-chunk (chunk) "Return t if chunk CHUNK should be valid XML." (when chunk (let ((major-mode (mumamo-chunk-major-mode chunk)) (region (overlay-get chunk 'mumamo-region)) (parseable-by (overlay-get chunk 'mumamo-parseable-by))) ;;(message "mumamo-valid-nxml-chunk: major-mode=%s, parseble-by=%s" major-mode parseable-by) (or region (derived-mode-p 'nxml-mode) (memq 'nxml-mode parseable-by))))) ;; A good test case for the use of this is the troublesome code in the ;; first line of xml-as-string.php in nxml/nxhtml/bug-tests. Currently ;; this test code is however splitted and it looks like the code below ;; can't handle the line above if the line looks like below. The ?> is ;; still thought to be a border. Does this mean that ' is not treated ;; as a string separator? ;; ;; '; ?> ;; ;; However there are the reverse cases also, in lines like ;; ;; href="url($url); ?>" ;; . Could this be solved by RMS suggestion with a ;; function/defmacro that binds variables to their global values? (mumamo-msgfntfy "point-min,max=%s,%s syntax-start,end=%s,%s, major=%s" (point-min) (point-max) syntax-start syntax-end major) ;;(msgtrc "end-in-code:here a after-chunk=%s" (when (boundp 'after-chunk) after-chunk)) (assert (and syntax-start syntax-end) t) (let ((doesnt-here (point)) doesnt-ret) (save-restriction (widen) ;;(msgtrc "end-in-code:here a2 after-chunk=%s" (when (boundp 'after-chunk) after-chunk)) (mumamo-with-major-mode-fontification major `(let (ppss) ;; fix-me: Use main major mode, and `syntax-ppss'. Change the ;; defadvice of this to make that possible. ;;(msgtrc "end-in-code:here b after-chunk=%s" (when (boundp 'after-chunk) after-chunk)) (setq ppss (parse-partial-sexp ,syntax-start (+ ,syntax-end 0))) ;;(msgtrc "end-in-code %s %s %s:ppss=%S" ,syntax-start ,syntax-end ',major ppss) ;;(msgtrc "end-in-code:here c after-chunk=%s" (when (boundp 'after-chunk) after-chunk)) ;; If inside a string or comment then the end marker is ;; invalid: ;;(msgtrc "mumamo-end-in-code:ppss=%s" ppss) (if (or (nth 3 ppss) (nth 4 ppss)) (progn ;;(msgtrc "invalid end, syntax-end =%s" syntax-end) (setq doesnt-ret nil) (if (nth 4 ppss) ;; in comment, check if single line comment (let ((here (point)) eol-pos) ;;(msgtrc "end-in-code, was in comment, ppss=%S" ppss) (goto-char ,syntax-end) (setq eol-pos (line-end-position)) (goto-char here) (setq ppss (parse-partial-sexp ,syntax-start (+ eol-pos 1))) ;;(msgtrc "end-in-code, in comment, new ppss %s %s=%S" ,syntax-start (+ eol-pos 1) ppss) (unless (nth 4 ppss) (setq doesnt-ret t))))) (setq doesnt-ret t) ;;(msgtrc "valid end, syntax-end =%s" syntax-end) )))) (goto-char doesnt-here) ;;(msgtrc "end-in-code:ret=%s" doesnt-ret) doesnt-ret)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Easy chunk defining (defun mumamo-quick-chunk-forward (pos min max begin-mark end-mark inc mode mark-is-border) ;;(msgtrc "quick-chunk-forward %s %s %s" pos min max) (let ((search-fw-exc-start `(lambda (pos max) (let ((exc-start (if ,inc (mumamo-chunk-start-fw-str-inc pos max ,begin-mark) (mumamo-chunk-start-fw-str pos max ,begin-mark)))) (when exc-start (list exc-start mode nil))))) (search-fw-exc-end `(lambda (pos max) ;;(msgtrc "search-fw-exc-end %s %s, inc=%s, end-mark=%s" pos max ,inc ,end-mark) (save-match-data (let ((ret (if ,inc (mumamo-chunk-end-fw-str-inc pos max ,end-mark) (mumamo-chunk-end-fw-str pos max ,end-mark)))) ;;(msgtrc "search-fw-exc-end ret=%s" ret) ret)))) (find-borders (when mark-is-border `(lambda (start end exc-mode) (let ((start-border) (end-border)) (if (and ,inc);; exc-mode) (progn (when start (setq start-border (+ start (length ,begin-mark)))) (when end (setq end-border (- end (length ,end-mark))))) (if (and (not ,inc) (not exc-mode)) (progn (when start (setq start-border (+ start (length ,end-mark)))) (when end (setq end-border (- end (length ,begin-mark))))))) (when (or start-border end-border) (mumamo-msgfntfy "quick.start-border/end=%s/%s, start/end=%s/%s exc-mode=%s" start-border end-border start end exc-mode) (list start-border end-border))))))) (mumamo-possible-chunk-forward pos max search-fw-exc-start search-fw-exc-end find-borders))) (defun mumamo-quick-static-chunk (pos min max begin-mark end-mark inc mode mark-is-border) (if t (mumamo-quick-chunk-forward pos min max begin-mark end-mark inc mode mark-is-border) ;; (let ((old (mumamo-quick-static-chunk-old pos min max begin-mark end-mark inc mode mark-is-border)) ;; (new (mumamo-quick-chunk-forward pos min max begin-mark end-mark inc mode mark-is-border))) ;; (unless (equal old new) (msgtrc "equal=%s\n\told=%S\n\tnew=%S" (equal old new) old new)) ;; (if nil old new)) )) ;; (defun mumamo-quick-static-chunk-old (pos ;; min max ;; begin-mark end-mark inc mode ;; mark-is-border) ;; "Quick way to make a chunk function with static dividers. ;; Here is an example of how to use it: ;; (defun mumamo-chunk-embperl-<- (pos min max) ;; \"Find [- ... -], return range and perl-mode.\" ;; (mumamo-quick-static-chunk pos min max \"[-\" \"-]\" nil 'perl-mode)) ;; As you can see POS, MIN and MAX comes from argument of the ;; function you define. ;; BEGIN-MARK should be a string that begins the chunk. ;; END-MARK should be a string that ends the chunk. ;; If INC is non-nil then the dividers are included in the chunk. ;; Otherwise they are instead made parts of the surrounding chunks. ;; MODE should be the major mode for the chunk. ;; If MARK-IS-BORDER is non-nil then the marks are just borders and ;; not supposed to have the same syntax as the inner part of the ;; Fix-me: This can only be useful if the marks are included in the ;; chunk, ie INC is non-nil. Should not these two arguments be ;; mixed then? ;; " ;; (mumamo-msgfntfy "quick.pos=%s min,max=%s,%s begin-mark/end=%s/%s mark-is-border=%s" pos min max begin-mark end-mark mark-is-border) ;; (let ((search-bw-exc-start ;; `(lambda (pos min) ;; (let ((exc-start ;; (if ,inc ;; (mumamo-chunk-start-bw-str-inc pos min begin-mark) ;; (mumamo-chunk-start-bw-str pos min begin-mark)))) ;; (when (and exc-start ;; (<= exc-start pos)) ;; (cons exc-start mode))))) ;; (search-bw-exc-end ;; `(lambda (pos min) ;; (if ,inc ;; (mumamo-chunk-end-bw-str-inc pos min ,end-mark) ;; (mumamo-chunk-end-bw-str pos min ,end-mark)))) ;; (search-fw-exc-start ;; `(lambda (pos max) ;; (if ,inc ;; (mumamo-chunk-start-fw-str-inc pos max ,begin-mark) ;; (mumamo-chunk-start-fw-str pos max ,begin-mark)))) ;; (search-fw-exc-end ;; `(lambda (pos max) ;; (save-match-data ;; (if ,inc ;; (mumamo-chunk-end-fw-str-inc pos max ,end-mark) ;; (mumamo-chunk-end-fw-str pos max ,end-mark))))) ;; (find-borders ;; (when mark-is-border ;; `(lambda (start end exc-mode) ;; (let ((start-border) ;; (end-border)) ;; (if (and ,inc exc-mode) ;; (progn ;; (when start ;; (setq start-border ;; (+ start (length ,begin-mark)))) ;; (when end ;; (setq end-border ;; (- end (length ,end-mark))))) ;; (if (and (not ,inc) (not exc-mode)) ;; (progn ;; (when start ;; (setq start-border ;; (+ start (length ,end-mark)))) ;; (when end ;; (setq end-border ;; (- end (length ,begin-mark))))))) ;; (when (or start-border end-border) ;; (mumamo-msgfntfy "quick.start-border/end=%s/%s, start/end=%s/%s exc-mode=%s" start-border end-border start end exc-mode) ;; (list start-border end-border))))))) ;; (mumamo-find-possible-chunk pos min max ;; search-bw-exc-start ;; search-bw-exc-end ;; search-fw-exc-start ;; search-fw-exc-end ;; find-borders))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Changing the major mode that the user sees (defvar mumamo-unread-command-events-timer nil) (make-variable-buffer-local 'mumamo-unread-command-events-timer) (defun mumamo-unread-command-events (command-keys new-major old-last-command) "Sync new keymaps after changing major mode in a timer. Also tell new major mode. COMMAND-KEYS is the keys entered after last command and the call to `mumamo-idle-set-major-mode' \(which is done in an idle timer). Those keys are added to `unread-command-events' so they can be used in the new keymaps. They should be in the format returned by \(listify-key-sequence (this-command-keys-vector)) NEW-MAJOR mode is the new major mode. OLD-LAST-COMMAND is the value of `last-command' after switching major mode. \(This is cleared by the function `top-level' so this function will not see it since it is run in a timer.)" (mumamo-condition-case err (progn ;; last-command seems to be cleared by top-level so set it ;; back here. (unless last-command (setq last-command old-last-command)) (when (< 0 (length command-keys)) ;;(setq last-command-char nil) ;; For `viper-command-argument' (setq unread-command-events (append command-keys nil))) (message "Switched to %s" new-major)) (error (let ((mumamo-display-error-lwarn t)) (mumamo-display-error 'mumamo-unread-command-events "err=%s" err))))) (defvar mumamo-idle-set-major-mode-timer nil) (make-variable-buffer-local 'mumamo-idle-set-major-mode-timer) (put 'mumamo-idle-set-major-mode-timer 'permanent-local t) (defun mumamotemp-pre-command () "Temporary command for debugging." (message "mumamotemp-pre 1: modified=%s %s" (buffer-modified-p) (current-buffer))) (defun mumamotemp-post-command () "Temporary command for debugging." (message "mumamotemp-post 1: modified=%s %s" (buffer-modified-p) (current-buffer))) (put 'mumamotemp-pre-command 'permanent-local-hook t) (put 'mumamotemp-post-command 'permanent-local-hook t) (defun mumamotemp-start () "Temporary command for debugging." (add-hook 'post-command-hook 'mumamotemp-post-command nil t) (add-hook 'pre-command-hook 'mumamotemp-pre-command nil t)) (defsubst mumamo-cancel-idle-set-major-mode () (when (timerp mumamo-idle-set-major-mode-timer) (cancel-timer mumamo-idle-set-major-mode-timer)) (setq mumamo-idle-set-major-mode-timer nil)) (defun mumamo-request-idle-set-major-mode () "Setup to change major mode from chunk when Emacs is idle." (mumamo-cancel-idle-set-major-mode) (setq mumamo-idle-set-major-mode-timer (run-with-idle-timer mumamo-set-major-mode-delay nil 'mumamo-idle-set-major-mode (current-buffer) (selected-window)))) (defvar mumamo-done-first-set-major nil) (make-variable-buffer-local 'mumamo-done-first-set-major) (put 'mumamo-done-first-set-major 'permanent-local t) ;; Fix-me: Add a property to the symbol instead (like in CUA). (defvar mumamo-safe-commands-in-wrong-major '(self-insert-command fill-paragraph ;; It changes major mode forward-char viper-forward-char backward-char viper-backward-char next-line viper-next-line previous-line viper-previous-line scroll-down cua-scroll-down scroll-up cua-scroll-up move-beginning-of-line move-end-of-line nonincremental-search-forward nonincremental-search-backward mumamo-backward-chunk mumamo-forward-chunk ;; Fix-me: add more ) ) (defun mumamo-fetch-local-map (major) "Fetch local keymap for major mode MAJOR. Do that by turning on the major mode in a new buffer. Add the keymap to `mumamo-major-modes-local-maps'. Return the fetched local map." (let (temp-buf-name temp-buf local-map) (setq temp-buf-name (concat "mumamo-fetch-major-mode-local-" (symbol-name major))) (setq temp-buf (get-buffer temp-buf-name)) (when temp-buf (kill-buffer temp-buf)) (setq temp-buf (get-buffer-create temp-buf-name)) (with-current-buffer temp-buf (let ((mumamo-fetching-major t)) (funcall major)) (setq local-map (current-local-map)) (when local-map (setq local-map (copy-keymap (current-local-map)))) (add-to-list 'mumamo-major-modes-local-maps (cons major-mode local-map))) (kill-buffer temp-buf) local-map)) (defvar mumamo-post-command-chunk nil) (make-variable-buffer-local 'mumamo-post-command-chunk) (defun mumamo-post-command-get-chunk (pos) "Get chunk at POS fast." (let ((have-regions (and (boundp 'mumamo-regions) mumamo-regions))) (when have-regions (setq mumamo-post-command-chunk nil)) (if (and mumamo-post-command-chunk (overlayp mumamo-post-command-chunk) ;;(progn (message "here a=%s" mumamo-post-command-chunk) t) (overlay-buffer mumamo-post-command-chunk) ;;(progn (message "here b=%s" mumamo-post-command-chunk) t) (< pos (overlay-end mumamo-post-command-chunk)) ;;(progn (message "here c=%s" mumamo-post-command-chunk) t) (>= pos (overlay-start mumamo-post-command-chunk)) ;;(progn (message "here d=%s" mumamo-post-command-chunk) t) (mumamo-chunk-major-mode mumamo-post-command-chunk) ;;(progn (msgtrc "here e=%s" mumamo-post-command-chunk) t) ) mumamo-post-command-chunk ;;(msgtrc "--------------- new post-command-chunk") (setq mumamo-post-command-chunk (or (unless have-regions (mumamo-get-existing-new-chunk-at (point) nil)) (mumamo-find-chunks (point) "post-command-get-chunk")))))) ;; (setq mumamo-set-major-mode-delay 10) (defun mumamo-set-major-post-command () "Change major mode if necessary after a command. If the major mode for chunk at `window-point' differ from current major mode then change major mode to that for the chunk. If however `mumamo-set-major-mode-delay' is greater than 0 just request a change of major mode when Emacs is idle that long. See the variable above for an explanation why a delay might be needed \(and is the default)." ;;(msgtrc "set-major-post-command here") (let* ((in-pre-hook (memq 'mumamo-set-major-pre-command pre-command-hook)) (ovl (unless in-pre-hook (mumamo-post-command-get-chunk (point)))) (major (when ovl (mumamo-chunk-major-mode ovl))) (set-it-now (not (or in-pre-hook (mumamo-fun-eq major major-mode))))) ;;(msgtrc "set-major-post-command ovl=%s, in-pre-hook=%s" ovl in-pre-hook) (if (not set-it-now) (unless (mumamo-fun-eq major major-mode) (when mumamo-idle-set-major-mode-timer (mumamo-request-idle-set-major-mode))) (if mumamo-done-first-set-major (if (<= 0 mumamo-set-major-mode-delay) ;; Window point has been moved to a new chunk with a new ;; major mode. Major mode will not be changed directly, ;; but in an idle timer or in pre-command-hook. To avoid ;; that the user get the wrong key bindings for the new ;; chunk fetch the local map directly and apply that. (let* ((map-rec (assoc major mumamo-major-modes-local-maps)) (map (cdr map-rec))) (unless map (setq map (mumamo-fetch-local-map major))) (unless (eq map 'no-local-map) (use-local-map map)) (add-hook 'pre-command-hook 'mumamo-set-major-pre-command nil t) (mumamo-request-idle-set-major-mode)) (mumamo-set-major major ovl) (message "Switched to %s" major-mode)) (mumamo-set-major major ovl))))) (defun mumamo-set-major-pre-command () "Change major mode if necessary before a command. When the key sequence that invoked the command is in current local map and major mode is not the major mode for the current mumamo chunk then set major mode to that for the chunk." (mumamo-condition-case err ;; First see if we can avoid changing major mode (if (memq this-command mumamo-safe-commands-in-wrong-major) (mumamo-request-idle-set-major-mode) ;;(message "pre point=%s" (point)) (let* ((ovl (mumamo-find-chunks (point) "mumamo-set-major-pre-command")) (major (mumamo-chunk-major-mode ovl))) ;;(message "pre point=%s" (point)) (if (not major) (lwarn '(mumamo-set-major-pre-command) :error "major=%s" major) (when (or (not (mumamo-fun-eq major-mode major)) (not (mumamo-set-major-check-keymap))) (setq major-mode nil) (mumamo-set-major major ovl) ;; Unread the last command key sequence (setq unread-command-events (append (listify-key-sequence (this-command-keys-vector)) unread-command-events)) ;; Some commands, like `viper-command-argument' need to ;; know the last command, so tell them. (setq this-command (lambda () (interactive) (setq this-command last-command))))))) (error (mumamo-display-error 'mumamo-set-major-pre-command "cb:%s, %s" (current-buffer) (error-message-string err))))) (defun mumamo-idle-set-major-mode (buffer window) "Set major mode from mumamo chunk when Emacs is idle. Do this only if current buffer is BUFFER and then do it in window WINDOW. See the variable `mumamo-set-major-mode-delay' for an explanation." (save-match-data ;; runs in idle timer (mumamo-msgfntfy "mumamo-idle-set-major-mode b=%s, window=%s" buffer window) (with-selected-window window ;; According to Stefan Monnier we need to set the buffer too. (with-current-buffer (window-buffer window) (when (eq buffer (current-buffer)) (mumamo-condition-case err ;;(let* ((ovl (mumamo-get-chunk-at (point))) ;;(message "idle point=%s" (point)) (let* ((ovl (mumamo-find-chunks (point) "mumamo-idle-set-major-mode")) (major (mumamo-chunk-major-mode ovl)) (modified (buffer-modified-p))) ;;(message "idle point=%s" (point)) (unless (mumamo-fun-eq major major-mode) ;;(message "mumamo-set-major at A") (mumamo-set-major major ovl) ;; Fix-me: This is a bug workaround. Possibly in Emacs. (when (and (buffer-modified-p) (not modified)) (set-buffer-modified-p nil)) ;; sync keymap (when (timerp mumamo-unread-command-events-timer) (cancel-timer mumamo-unread-command-events-timer)) (when unread-command-events ;; Save unread keys before calling `top-level' which ;; will clear them. (setq mumamo-unread-command-events-timer (run-with-idle-timer 0 nil 'mumamo-unread-command-events unread-command-events major last-command)) (top-level) ))) (error (mumamo-display-error 'mumamo-idle-set-major-mode "cb=%s, err=%s" (current-buffer) err)))))))) (defun mumamo-post-command-1 (&optional no-debug) "See `mumamo-post-command'. Turn on `debug-on-error' unless NO-DEBUG is nil." (unless no-debug (setq debug-on-error t)) (setq mumamo-find-chunks-level 0) (mumamo-msgfntfy "mumamo-post-command-1 ENTER: font-lock-mode=%s" font-lock-mode) (if font-lock-mode (mumamo-set-major-post-command) ;;(mumamo-on-font-lock-off) ) ;;(msgtrc "mumamo-post-command-1 EXIT: font-lock-keywords-only =%s" (default-value 'font-lock-keywords-only)) ) (defvar mumamo-bug-3467-w14 41) (defvar mumamo-bug-3467-w15 51) ;;(mumamo-check-has-bug3467 t) ;;(kill-local-variable 'mumamo-bug-3467-w14) (defun mumamo-check-has-bug3467 (verbose) (let ((has-bug nil)) (with-temp-buffer (let ((mumamo-bug-3467-w14 42) (mumamo-bug-3467-w15 52)) (when verbose (message "mumamo-bug-3467-w14 maybe let: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w14 (default-value 'mumamo-bug-3467-w14))) (when verbose (message "mumamo-bug-3467-w15 maybe let: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w15 (default-value 'mumamo-bug-3467-w15))) (set (make-local-variable 'mumamo-bug-3467-w14) 43) (set-default 'mumamo-bug-3467-w14 44) (set-default 'mumamo-bug-3467-w15 54) (when verbose (message "mumamo-bug-3467-w14 maybe let: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w14 (default-value 'mumamo-bug-3467-w14))) (when verbose (message "mumamo-bug-3467-w15 maybe let: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w15 (default-value 'mumamo-bug-3467-w15)))) (when verbose (message "mumamo-bug-3467-w14 top level: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w14 (default-value 'mumamo-bug-3467-w14))) (when (/= mumamo-bug-3467-w14 43) (setq has-bug t)) (when (/= (default-value 'mumamo-bug-3467-w14) 41) (setq has-bug t)) (when verbose (message "mumamo-bug-3467-w15 top level: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w15 (default-value 'mumamo-bug-3467-w15))) ) (when verbose (message "mumamo-bug-3467-w14 top level: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w14 (default-value 'mumamo-bug-3467-w14))) (when verbose (message "mumamo-bug-3467-w15 top level: in buffer %s=%S, global=%S" (current-buffer) mumamo-bug-3467-w15 (default-value 'mumamo-bug-3467-w15))) (or has-bug (local-variable-p 'mumamo-bug-3467-w14) (/= (default-value 'mumamo-bug-3467-w14) 41) ) )) (defvar mumamo-has-bug3467 (mumamo-check-has-bug3467 nil)) (defun mumamo-emacs-start-bug3467-timer-if-needed () "Work around for Emacs bug 3467. The only one I have found." (when mumamo-has-bug3467 (run-with-idle-timer 0 nil 'mumamo-emacs-bug3467-workaround))) (defun mumamo-emacs-bug3467-workaround () "Work around for Emacs bug 3467. The only one I have found." (set-default 'font-lock-keywords-only nil)) (defun mumamo-post-command () "Run this in `post-command-hook'. Change major mode if necessary." ;;(msgtrc "mumamo-post-command") (when mumamo-multi-major-mode (mumamo-condition-case err (mumamo-post-command-1 t) (error (mumamo-msgfntfy "mumamo-post-command %S" err) ;; Warnings are to disturbing when run in post-command-hook, ;; but this message is important so show it with an highlight. (message (propertize "%s\n- Please try M-: (mumamo-post-command-1) to see what happened." 'face 'highlight) (error-message-string err)))))) (defun mumamo-change-major-function () "Function added to `change-major-mode-hook'. Remove mumamo when changing to a new major mode if the change is not done because point was to a new chunk." (unless mumamo-set-major-running (mumamo-turn-off-actions))) (defun mumamo-derived-from-mode (major from-mode) "Return t if major mode MAJOR is derived from FROM-MODE." (let ((major-mode major)) (derived-mode-p from-mode))) ;; This is the new version of add-hook. For its origin see ;; http://lists.gnu.org/archive/html/emacs-devel/2007-12/msg00169.html ;; ;;(unless (> emacs-major-version 22) (defvar mumamo-test-add-hook nil "Internal use.") (unless (and t (let ((has-it nil)) ;;(add-hook 'mumamo-test-add-hook 'mumamo-jit-lock-after-change nil t) (add-hook 'mumamo-test-add-hook 'mumamo-after-change nil t) (setq has-it (eq 'permanent-local-hook (get 'mumamo-test-add-hook 'permanent-local))) has-it)) (defun add-hook (hook function &optional append local) "Add to the value of HOOK the function FUNCTION. FUNCTION is not added if already present. FUNCTION is added (if necessary) at the beginning of the hook list unless the optional argument APPEND is non-nil, in which case FUNCTION is added at the end. The optional fourth argument, LOCAL, if non-nil, says to modify the hook's buffer-local value rather than its default value. This makes the hook buffer-local if needed, and it makes t a member of the buffer-local value. That acts as a flag to run the hook functions in the default value as well as in the local value. HOOK should be a symbol, and FUNCTION may be any valid function. If HOOK is void, it is first set to nil. If HOOK's value is a single function, it is changed to a list of functions." (or (boundp hook) (set hook nil)) (or (default-boundp hook) (set-default hook nil)) (if local (unless (local-variable-if-set-p hook) (set (make-local-variable hook) (list t))) ;; Detect the case where make-local-variable was used on a hook ;; and do what we used to do. (unless (and (consp (symbol-value hook)) (memq t (symbol-value hook))) (setq local t))) (let ((hook-value (if local (symbol-value hook) (default-value hook)))) ;; If the hook value is a single function, turn it into a list. (when (or (not (listp hook-value)) (eq (car hook-value) 'lambda)) (setq hook-value (list hook-value))) ;; Do the actual addition if necessary (unless (member function hook-value) (setq hook-value (if append (append hook-value (list function)) (cons function hook-value)))) ;; Set the actual variable (if local (progn ;; If HOOK isn't a permanent local, ;; but FUNCTION wants to survive a change of modes, ;; mark HOOK as partially permanent. (and (symbolp function) (get function 'permanent-local-hook) (not (get hook 'permanent-local)) (put hook 'permanent-local 'permanent-local-hook)) (set hook hook-value)) (set-default hook hook-value)))) ) (defvar mumamo-survive-hooks '( ;; activate-mark-hook after-change-functions after-save-hook ;; before-save-functions auto-save-hook before-revert-hook ;; buffer-access-fontify-functions calendar-load-hook ;; command-line-functions compilation-finish-function ;; deactivate-mark-hook find-file-hook ;; find-file-not-found-functions first-change-hook ;; kbd-macro-termination-hook kill-buffer-hook ;; kill-buffer-query-functions menu-bar-update-hook ;; post-command-hook pre-abbrev-expand-hook pre-command-hook ;; write-contents-functions write-file-functions ;; write-region-annotate-functions ;; c-special-indent-hook )) ;; ;; Emulation modes ;; ;; These variables should have 'permanant-local t set in their ;; packages IMO, but now they do not have that. (eval-after-load 'viper-cmd (progn (put 'viper-after-change-functions 'permanent-local t) (put 'viper-before-change-functions 'permanent-local t) )) (eval-after-load 'viper (progn (put 'viper-post-command-hooks 'permanent-local t) (put 'viper-pre-command-hooks 'permanent-local t) ;;minor-mode-map-alist ;; viper-mode-string -- is already buffer local, globally void (put 'viper-mode-string 'permanent-local t) )) ;;viper-tut--part (eval-after-load 'viper-init (progn (put 'viper-d-com 'permanent-local t) (put 'viper-last-insertion 'permanent-local t) (put 'viper-command-ring 'permanent-local t) (put 'viper-vi-intercept-minor-mode 'permanent-local t) (put 'viper-vi-basic-minor-mode 'permanent-local t) (put 'viper-vi-local-user-minor-mode 'permanent-local t) (put 'viper-vi-global-user-minor-mode 'permanent-local t) (put 'viper-vi-state-modifier-minor-mode 'permanent-local t) (put 'viper-vi-diehard-minor-mode 'permanent-local t) (put 'viper-vi-kbd-minor-mode 'permanent-local t) (put 'viper-insert-intercept-minor-mode 'permanent-local t) (put 'viper-insert-basic-minor-mode 'permanent-local t) (put 'viper-insert-local-user-minor-mode 'permanent-local t) (put 'viper-insert-global-user-minor-mode 'permanent-local t) (put 'viper-insert-state-modifier-minor-mode 'permanent-local t) (put 'viper-insert-diehard-minor-mode 'permanent-local t) (put 'viper-insert-kbd-minor-mode 'permanent-local t) (put 'viper-replace-minor-mode 'permanent-local t) (put 'viper-emacs-intercept-minor-mode 'permanent-local t) (put 'viper-emacs-local-user-minor-mode 'permanent-local t) (put 'viper-emacs-global-user-minor-mode 'permanent-local t) (put 'viper-emacs-kbd-minor-mode 'permanent-local t) (put 'viper-emacs-state-modifier-minor-mode 'permanent-local t) (put 'viper-vi-minibuffer-minor-mode 'permanent-local t) (put 'viper-insert-minibuffer-minor-mode 'permanent-local t) (put 'viper-automatic-iso-accents 'permanent-local t) (put 'viper-special-input-method 'permanent-local t) (put 'viper-intermediate-command 'permanent-local t) ;; already local: viper-undo-needs-adjustment (put 'viper-began-as-replace 'permanent-local t) ;; already local: viper-replace-overlay ;; already local: viper-last-posn-in-replace-region ;; already local: viper-last-posn-while-in-insert-state ;; already local: viper-sitting-in-replace (put 'viper-replace-chars-to-delete 'permanent-local t) (put 'viper-replace-region-chars-deleted 'permanent-local t) (put 'viper-current-state 'permanent-local t) (put 'viper-cted 'permanent-local t) (put 'viper-current-indent 'permanent-local t) (put 'viper-preserve-indent 'permanent-local t) (put 'viper-auto-indent 'permanent-local t) (put 'viper-electric-mode 'permanent-local t) ;; already local: viper-insert-point ;; already local: viper-pre-command-point (put 'viper-com-point 'permanent-local t) (put 'viper-ex-style-motion 'permanent-local t) (put 'viper-ex-style-editing 'permanent-local t) (put 'viper-ESC-moves-cursor-back 'permanent-local t) (put 'viper-delete-backwards-in-replace 'permanent-local t) ;; already local: viper-related-files-and-buffers-ring (put 'viper-local-search-start-marker 'permanent-local t) (put 'viper-search-overlay 'permanent-local t) (put 'viper-last-jump 'permanent-local t) (put 'viper-last-jump-ignore 'permanent-local t) (put 'viper-minibuffer-current-face 'permanent-local t) ;; already local: viper-minibuffer-overlay (put 'viper-command-ring 'permanent-local t) (put 'viper-last-insertion 'permanent-local t) )) (eval-after-load 'viper-keym (progn ;; already local: viper-vi-local-user-map ;; already local: viper-insert-local-user-map ;; already local: viper-emacs-local-user-map (put 'viper--key-maps 'permanent-local t) (put 'viper--intercept-key-maps 'permanent-local t) ;; already local: viper-need-new-vi-local-map ;; already local: viper-need-new-insert-local-map ;; already local: viper-need-new-emacs-local-map )) (eval-after-load 'viper-mous (progn (put 'viper-mouse-click-search-noerror 'permanent-local t) (put 'viper-mouse-click-search-limit 'permanent-local t) )) (eval-after-load 'viper-util (progn (put 'viper-syntax-preference 'permanent-local t) (put 'viper-non-word-characters 'permanent-local t) (put 'viper-ALPHA-char-class 'permanent-local t) )) (eval-after-load 'cua-base (progn (put 'cua-inhibit-cua-keys 'permanent-local t) (put 'cua--explicit-region-start 'permanent-local t) (put 'cua--status-string 'permanent-local t) )) ;; This is for the defvar in ido.el: (eval-after-load 'ido (progn (put 'cua-inhibit-cua-keys 'permanent-local t) )) (eval-after-load 'cua-rect (progn (put 'cua--rectangle 'permanent-local t) (put 'cua--rectangle-overlays 'permanent-local t) )) (eval-after-load 'edt (progn (put 'edt-select-mode 'permanent-local t) )) (eval-after-load 'tpu-edt (progn (put 'tpu-newline-and-indent-p 'permanent-local t) (put 'tpu-newline-and-indent-string 'permanent-local t) (put 'tpu-saved-delete-func 'permanent-local t) (put 'tpu-buffer-local-map 'permanent-local t) (put 'tpu-mark-flag 'permanent-local t) )) (eval-after-load 'vi (progn (put 'vi-add-to-mode-line 'permanent-local t) ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-scroll-amount ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-shift-width ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-ins-point ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-ins-length ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-ins-repetition ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-ins-overwrt-p ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-ins-prefix-code ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-last-change-command ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-last-shell-command ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-last-find-char ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-mark-alist ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-insert-state ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-mode-old-local-map ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-mode-old-mode-name ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-mode-old-major-mode ;;Warning (mumamo-per-buffer-local-vars): Not a local variable: vi-mode-old-case-fold ;; )) (eval-after-load 'vi (progn (put 'vip-emacs-local-map 'permanent-local t) (put 'vip-insert-local-map 'permanent-local t) (put 'vip-insert-point 'permanent-local t) (put 'vip-com-point 'permanent-local t) (put 'vip-current-mode 'permanent-local t) (put 'vip-emacs-mode-line-buffer-identification 'permanent-local t) (put 'vip-current-major-mode 'permanent-local t) )) (eval-after-load 'hi-lock (progn (put 'hi-lock-mode 'permanent-local t) )) ;; ;; Minor modes that are not major mode specific ;; (put 'visual-line-mode 'permanent-local t) (eval-after-load 'flymake (progn ;; hook functions: (put 'flymake-after-change-function 'permanent-local-hook t) (put 'flymake-after-save-hook 'permanent-local-hook t) (put 'flymake-kill-buffer-hook 'permanent-local-hook t) ;; hooks: ;;; (put 'after-change-functions 'permanent-local 'permanent-local-hook) ;;; (put 'after-save-hook 'permanent-local 'permanent-local-hook) ;;; (put 'kill-buffer-hook 'permanent-local 'permanent-local-hook) ;; vars: (put 'flymake-mode 'permanent-local t) (put 'flymake-is-running 'permanent-local t) (put 'flymake-timer 'permanent-local t) (put 'flymake-last-change-time 'permanent-local t) (put 'flymake-check-start-time 'permanent-local t) (put 'flymake-check-was-interrupted 'permanent-local t) (put 'flymake-err-info 'permanent-local t) (put 'flymake-new-err-info 'permanent-local t) (put 'flymake-output-residual 'permanent-local t) (put 'flymake-mode-line 'permanent-local t) (put 'flymake-mode-line-e-w 'permanent-local t) (put 'flymake-mode-line-status 'permanent-local t) (put 'flymake-temp-source-file-name 'permanent-local t) (put 'flymake-master-file-name 'permanent-local t) (put 'flymake-temp-master-file-name 'permanent-local t) (put 'flymake-base-dir 'permanent-local t))) ;; (eval-after-load 'imenu ;; (progn ;; ;; Fix-me: imenu is only useful for main major mode. The menu ;; ;; disappears in sub chunks because it is tighed to ;; ;; local-map. Don't know what to do about that. I do not ;; ;; understand the reason for binding it to local-map, but I ;; ;; suspect the intent is to have different menu items for ;; ;; different modes. Could not that be achieved by deleting the ;; ;; menu and creating it again when changing major mode? (That must ;; ;; be implemented in imenu.el of course.) ;; ;; ;; ;; hook functions: ;; ;;; (put 'imenu-update-menubar 'permanent-local-hook t) ;; ;; hooks: ;; (put 'menu-bar-update-hook 'permanent-local 'permanent-local-hook) ;; ;; vars: ;; (put 'imenu-generic-expression 'permanent-local t) ;; (put 'imenu-create-index-function 'permanent-local t) ;; (put 'imenu-prev-index-position-function 'permanent-local t) ;; (put 'imenu-extract-index-name-function 'permanent-local t) ;; (put 'imenu-name-lookup-function 'permanent-local t) ;; (put 'imenu-default-goto-function 'permanent-local t) ;; (put 'imenu--index-alist 'permanent-local t) ;; (put 'imenu--last-menubar-index-alist 'permanent-local t) ;; (put 'imenu-syntax-alist 'permanent-local t) ;; (put 'imenu-case-fold-search 'permanent-local t) ;; (put 'imenu-menubar-modified-tick 'permanent-local t) ;; )) (eval-after-load 'longlines (progn ;; Fix-me: take care of longlines-mode-off (put 'longlines-mode 'permanent-local t) (put 'longlines-wrap-beg 'permanent-local t) (put 'longlines-wrap-end 'permanent-local t) (put 'longlines-wrap-point 'permanent-local t) (put 'longlines-showing 'permanent-local t) (put 'longlines-decoded 'permanent-local t) ;; (put 'longlines-after-change-function 'permanent-local-hook t) (put 'longlines-after-revert-hook 'permanent-local-hook t) (put 'longlines-before-revert-hook 'permanent-local-hook t) (put 'longlines-decode-buffer 'permanent-local-hook t) (put 'longlines-decode-region 'permanent-local-hook t) (put 'longlines-mode-off 'permanent-local-hook t) (put 'longlines-post-command-function 'permanent-local-hook t) (put 'longlines-window-change-function 'permanent-local-hook t) ;;(put 'mail-indent-citation 'permanent-local-hook t) )) ;; Fix-me: Rails, many problematic things: ;;; Fix-me: No idea about these, where are they used?? Add them to ;;; mumamo-per-buffer-local-vars?: ;; predictive-main-dict ;; predictive-prog-mode-main-dict ;; predictive-use-auto-learn-cache ;; predictive-dict-autosave-on-kill-buffer (eval-after-load 'inf-ruby (progn (put 'inferior-ruby-first-prompt-pattern 'permanent-local t) (put 'inferior-ruby-prompt-pattern 'permanent-local t) )) ;;; These are for the output buffer (no problems): ;; font-lock-keywords-only ;; font-lock-defaults -- always buffer local ;; scroll-margin ;; scroll-preserve-screen-position (eval-after-load 'rails-script (progn (put 'rails-script:run-after-stop-hook 'permanent-local t) (put 'rails-script:show-buffer-hook 'permanent-local t) (put 'rails-script:output-mode-ret-value 'permanent-local t) )) ;;; No problems I believe (it is in output buffer): ;; compilation-error-regexp-alist-alist ;; compilation-error-regexp-alist ;;; Fix-me: This is in the minor mode, what to do? Looks like it ;;; should have 'permanent-local t - in this case. I have added it to ;;; mumamo-per-buffer-local-vars for now. ;; tags-file-name (eval-after-load 'rails (progn (put 'rails-primary-switch-func 'permanent-local t) (put 'rails-secondary-switch-func 'permanent-local t) )) ;; (defun test-js-perm () ;; (put 'js--quick-match-re 'permanent-local t) ;; (put 'js--quick-match-re-func 'permanent-local t) ;; (put 'js--cache-end 'permanent-local t) ;; (put 'js--last-parse-pos 'permanent-local t) ;; (put 'js--state-at-last-parse-pos 'permanent-local t) ;; (put 'js--tmp-location 'permanent-local t)) ;; (test-js-perm) (defvar mumamo-per-buffer-local-vars '( buffer-file-name left-margin-width right-margin-width ;; Fix-me: This is to prevent font-lock-mode turning off/on, but ;; is it necessary? ;;font-lock-mode-major-mode tags-file-name nxhtml-menu-mode ;; Fix-me: adding rng timers here stops Emacs from looping after ;; indenting in ind-0-error.php, but I have no clue why. Hm. This ;; problem is gone, but I forgot why. rng-c-current-token ;;rng-cmpct.el:132:(make-variable-buffer-local 'rng-c-current-token) rng-c-escape-positions ;;rng-cmpct.el:341:(make-variable-buffer-local 'rng-c-escape-positions) rng-c-file-name ;;rng-cmpct.el:344:(make-variable-buffer-local 'rng-c-file-name) rng-current-schema-file-name ;;rng-loc.el:37:(make-variable-buffer-local 'rng-current-schema-file-name) rng-current-schema ;;rng-pttrn.el:71:(make-variable-buffer-local 'rng-current-schema) ;;rng-validate-timer is permanent-local t ;;rng-validate-timer ;;rng-valid.el:141:(make-variable-buffer-local 'rng-validate-timer) ;;rng-validate-quick-timer is permanent-local t ;;rng-validate-quick-timer ;;rng-valid.el:146:(make-variable-buffer-local 'rng-validate-quick-timer) rng-error-count ;;rng-valid.el:153:(make-variable-buffer-local 'rng-error-count) rng-message-overlay ;;rng-valid.el:158:(make-variable-buffer-local 'rng-message-overlay) rng-message-overlay-inhibit-point ;;rng-valid.el:165:(make-variable-buffer-local 'rng-message-overlay-inhibit-point) rng-message-overlay-current ;;rng-valid.el:169:(make-variable-buffer-local 'rng-message-overlay-current) rng-validate-up-to-date-end ;;rng-valid.el:188:(make-variable-buffer-local 'rng-validate-up-to-date-end) rng-conditional-up-to-date-start ;;rng-valid.el:199:(make-variable-buffer-local 'rng-conditional-up-to-date-start) rng-conditional-up-to-date-end ;;rng-valid.el:205:(make-variable-buffer-local 'rng-conditional-up-to-date-end) rng-validate-mode ;;rng-valid.el:212:(make-variable-buffer-local 'rng-validate-mode) rng-dtd ;;rng-valid.el:215:(make-variable-buffer-local 'rng-dtd) nxml-syntax-highlight-flag ;; For pre-Emacs nxml ;;nxml-ns-state - not buffer local currently nxml-prolog-regions ;;snxml-mode.el:362:(make-variable-buffer-local 'nxml-prolog-regions) nxml-last-fontify-end ;;dnxml-mode.el:367:(make-variable-buffer-local 'nxml-last-fontify-end) nxml-degraded ;;dnxml-mode.el:373:(make-variable-buffer-local 'nxml-degraded) nxml-char-ref-extra-display ;;ynxml-mode.el:397:(make-variable-buffer-local 'nxml-char-ref-extra-display) nxml-prolog-end ;;dnxml-rap.el:92:(make-variable-buffer-local 'nxml-prolog-end) nxml-scan-end ;;dnxml-rap.el:107:(make-variable-buffer-local 'nxml-scan-end) ;;buffer-invisibility-spec ;;header-line-format ;; Fix-me: These must be handled with 'permanent-local since they may be changed: line-move-visual ;;simple.el:4537: (kill-local-variable 'line-move-visual) word-wrap ;;simple.el:4538: (kill-local-variable 'word-wrap) truncate-lines ;;simple.el:4539: (kill-local-variable 'truncate-lines) truncate-partial-width-windows ;;simple.el:4540: (kill-local-variable 'truncate-partial-width-windows) fringe-indicator-alist ;;simple.el:4541: (kill-local-variable 'fringe-indicator-alist) visual-line--saved-state ;;simple.el:4544: (kill-local-variable 'visual-line--saved-state))) vis-mode-saved-buffer-invisibility-spec ;;simple.el:6237: (kill-local-variable 'vis-mode-saved-buffer-invisibility-spec)) ) "Per buffer local variables. See also `mumamo-per-main-major-local-vars'.") ;; Fix-me: use this, but how exactly? I think the var values must be ;; picked up at every change from main major mode. And restored after ;; changing to the new major mode - but maybe a bit differently if ;; this is the main major mode. (defvar mumamo-per-main-major-local-vars '( buffer-invisibility-spec header-line-format ) "Per main major local variables. Like `mumamo-per-buffer-local-vars', but this is fetched from the main major mode.") ;; (when nil ;; (make-variable-buffer-local 'mumamo-survive-minor-modes) ;; (put 'mumamo-survive-minor-modes 'permanent-local t) ;; (defvar mumamo-survive-minor-modes nil ;; "Hold local minor mode variables specific major modes. ;; Those values are saved when leaving a chunk with a certain ;; major mode and restored when entering a chunk with the same ;; major mode again. ;; The value of this variable is an associative list where the key ;; is a list with ;; \(MAJOR-MODE MINOR-MODE) ;; and the value is a stored value for the minor mode.") ;; ) (defun mumamo-make-variable-buffer-permanent (var) "Make buffer local value of VAR survive when moving point to a new chunk. When point is moved between chunks in a multi major mode the major mode will be changed. This will by default kill all local variables unless they have a non-nil `permanent-local' property \(see info node `(elisp)Creating Buffer-Local'). If you do not want to put a `permanent-local' property on a variable you can instead use this function to make variable VAR survive chunk switches in all mumamo multi major mode buffers." ;; If you want it to survive chunk switches only in the current ;; buffer then use `mumamo-make-local-permanent' instead." (pushnew var (default-value 'mumamo-per-buffer-local-vars))) ;; ;; Fix-me: use local value ;; ;; Fix-me: delelete local value when exiting mumamo ;; (defun mumamo-make-local-permanent (var) ;; "Make buffer local value of VAR survive when moving point to a new chunk. ;; This is for the current buffer only. ;; In most cases you almost certainly want to use ;; `mumamo-make-variable-buffer-permanent' instead." ;; (pushnew var mumamo-per-buffer-local-vars)) (defvar mumamo-per-buffer-local-vars-done-by-me nil "Variables set by mumamo already. Used to avoid unnecessary warnings if setting major mode fails.") ;; (mumamo-hook-p 'viper-pre-command-hooks) ;; (mumamo-hook-p 'viper-before-change-functions) ;; (mumamo-hook-p 'c-special-indent-hook) (defun mumamo-hook-p (sym) "Try to detect if SYM is a hook variable. Just check the name." (let ((name (symbol-name sym))) (or (string= "-hook" (substring name -5)) (string= "-hooks" (substring name -6)) (string= "-functions" (substring name -10))))) (defvar mumamo-major-mode nil) (make-variable-buffer-local 'mumamo-major-mode) (put 'mumamo-major-mode 'permanent-local t) (defvar mumamo-change-major-mode-no-nos '((font-lock-change-mode t) (longlines-mode-off t) global-font-lock-mode-cmhh (nxml-cleanup t) (turn-off-hideshow t)) "Avoid running these in `change-major-mode-hook'.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Remove things from hooks temporarily ;; Fix-me: This is a bit disorganized, could not decide which level I ;; wanted this on. (defvar mumamo-after-change-major-mode-no-nos '(;;nxhtml-global-minor-mode-enable-in-buffers global-font-lock-mode-enable-in-buffers) "Avoid running these in `after-change-major-mode-hook'.") (defvar mumamo-removed-from-hook nil) (defun mumamo-remove-from-hook (hook remove) "From hook HOOK remove functions in list REMOVE. Save HOOK and the list of functions removed to `mumamo-removed-from-hook'." (let (did-remove removed) (dolist (rem remove) ;;(message "rem.rem=%s" rem) (setq did-remove nil) (if (listp rem) (when (memq (car rem) (symbol-value hook)) (setq did-remove t) (remove-hook hook (car rem) t)) (when (memq rem (symbol-value hook)) (setq did-remove t) (remove-hook hook rem))) (when did-remove (setq removed (cons rem removed)))) (setq mumamo-removed-from-hook (cons (cons hook removed) mumamo-removed-from-hook)))) (defun mumamo-addback-to-hooks () "Add back what was removed by `mumamo-remove-from-hook'." ;;(message "mumamo-removed-from-hook=%s" mumamo-removed-from-hook) (dolist (rem-rec mumamo-removed-from-hook) (mumamo-addback-to-hook (car rem-rec) (cdr rem-rec)))) (defun mumamo-addback-to-hook (hook removed) "Add to hook HOOK the list of functions in REMOVED." ;;(message "addback: hook=%s, removed=%s" hook removed) (dolist (rem removed) ;;(message "add.rem=%s" rem) (if (listp rem) (add-hook hook (car rem) nil t) (add-hook hook rem)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Compare mumamo-irrelevant-buffer-local-vars (defvar mumamo-buffer-locals-dont-set '( adaptive-fill-mode adaptive-fill-first-line-regexp adaptive-fill-regexp add-log-current-defun-header-regexp auto-composition-function auto-composition-mode auto-composition-mode-major-mode auto-fill-chars beginning-of-defun-function buffer-auto-save-file-format buffer-auto-save-file-name buffer-backed-up buffer-display-count buffer-display-time buffer-file-coding-system buffer-file-format buffer-file-name buffer-file-truename buffer-invisibility-spec buffer-read-only buffer-saved-size buffer-undo-list c++-template-syntax-table c-<-op-cont-regexp c-<>-multichar-token-regexp c->-op-cont-regexp c-after-suffixed-type-decl-key c-after-suffixed-type-maybe-decl-key c-anchored-cpp-prefix c-assignment-op-regexp c-at-vsemi-p-fn c-backslash-column c-backslash-max-column ;;c-basic-offset c-before-font-lock-function c-block-comment-prefix c-block-comment-start-regexp c-block-prefix-charset c-block-stmt-1-key c-block-stmt-2-key c-brace-list-key c-cast-parens c-class-key c-cleanup-list c-colon-type-list-re c-comment-only-line-offset c-comment-prefix-regexp c-comment-start-regexp c-current-comment-prefix c-decl-block-key c-decl-hangon-key c-decl-prefix-or-start-re c-decl-prefix-re c-decl-start-re c-doc-comment-start-regexp c-doc-comment-style c-found-types c-get-state-before-change-function c-hanging-braces-alist c-hanging-colons-alist c-hanging-semi&comma-criteria c-identifier-key c-identifier-start c-identifier-syntax-modifications c-identifier-syntax-table ;;c-indent-comment-alist ;;c-indent-comments-syntactically-p ;;c-indentation-style c-keywords-obarray c-keywords-regexp c-known-type-key c-label-kwds-regexp c-label-minimum-indentation c-label-prefix-re c-line-comment-starter c-literal-start-regexp c-multiline-string-start-char c-nonlabel-token-key c-nonsymbol-chars c-nonsymbol-token-regexp c-not-decl-init-keywords ;;c-offsets-alist c-old-BOM c-old-EOM c-opt-<>-arglist-start c-opt-<>-arglist-start-in-paren c-opt-<>-sexp-key c-opt-asm-stmt-key c-opt-bitfield-key c-opt-block-decls-with-vars-key c-opt-block-stmt-key c-opt-cpp-macro-define-id c-opt-cpp-macro-define-start c-opt-cpp-prefix c-opt-cpp-start c-opt-extra-label-key c-opt-friend-key c-opt-identifier-concat-key c-opt-inexpr-brace-list-key c-opt-method-key c-opt-op-identifier-prefix c-opt-postfix-decl-spec-key c-opt-type-component-key c-opt-type-concat-key c-opt-type-modifier-key c-opt-type-suffix-key c-other-decl-block-key c-other-decl-block-key-in-symbols-alist c-overloadable-operators-regexp c-paragraph-separate c-paragraph-start c-paren-stmt-key c-prefix-spec-kwds-re c-primary-expr-regexp c-primitive-type-key c-recognize-<>-arglists c-recognize-colon-labels c-recognize-knr-p c-recognize-paren-inexpr-blocks c-recognize-paren-inits c-recognize-typeless-decls c-regular-keywords-regexp c-simple-stmt-key c-special-brace-lists c-special-indent-hook c-specifier-key c-stmt-delim-chars c-stmt-delim-chars-with-comma c-string-escaped-newlines c-symbol-key c-symbol-start c-syntactic-eol c-syntactic-ws-end c-syntactic-ws-start c-type-decl-end-used c-type-decl-prefix-key c-type-decl-suffix-key c-type-prefix-key c-vsemi-status-unknown-p-fn case-fold-search comment-end comment-end-skip comment-indent-function comment-line-break-function comment-multi-line comment-start comment-start-skip cursor-type default-directory defun-prompt-regexp delay-mode-hooks enable-multibyte-characters end-of-defun-function fill-paragraph-function font-lock-beginning-of-syntax-function font-lock-defaults font-lock-extend-after-change-region-function font-lock-extend-region-functions font-lock-fontified font-lock-fontify-buffer-function font-lock-fontify-region-function font-lock-keywords ;;font-lock-keywords-only font-lock-keywords-case-fold-search font-lock-mode font-lock-mode-hook font-lock-mode-major-mode font-lock-multiline font-lock-set-defaults font-lock-syntactic-keywords font-lock-syntactically-fontified font-lock-syntax-table font-lock-unfontify-buffer-function font-lock-unfontify-region-function fontification-functions forward-sexp-function indent-line-function indent-region-function imenu--index-alist imenu--last-menubar-index-alist imenu-create-index-function imenu-menubar-modified-tick isearch-mode jit-lock-after-change-extend-region-functions jit-lock-context-unfontify-pos jit-lock-contextually jit-lock-functions jit-lock-mode line-move-ignore-invisible local-abbrev-table major-mode mark-active ;;mark-ring mode-line-process mode-name normal-auto-fill-function ;;nxhtml-menu-mode-major-mode open-paren-in-column-0-is-defun-start outline-level outline-regexp paragraph-ignore-fill-prefix paragraph-separate paragraph-start parse-sexp-ignore-comments parse-sexp-lookup-properties php-mode-pear-hook point-before-scroll ;; More symbols from visual inspection ;;before-change-functions ;;delayed-mode-hooks ;;imenu-case-fold-search ;;imenu-generic-expression rngalt-completing-read-tag rngalt-completing-read-attribute-name rngalt-completing-read-attribute-value rngalt-complete-first-try rngalt-complete-last-try rngalt-complete-tag-hooks syntax-begin-function ) "Buffer local variables that is not saved/set per chunk. This is supposed to contain mostly buffer local variables specific to major modes and that are not meant to be customized by the user. ") (when (< emacs-major-version 23) (defadvice c-after-change (around mumamo-ad-c-after-change activate compile ) ;;(msgtrc "c-after-change: major-mode=%s c-nonsymbol-token-regexp=%s" major-mode c-nonsymbol-token-regexp) (when (or (not mumamo-multi-major-mode) (derived-mode-p 'c-mode)) ad-do-it)) ) (defun mumamo-save-per-major-local-vars (major) "Save some per major local variables for major mode MAJOR. This should be called before switching to a new chunks major mode." ;;(message "mumamo-save-per-major-local-vars %s %s" major (current-buffer)) (let ((locals (buffer-local-variables))) (setq locals (mapcar (lambda (local) (unless (or (memq (car local) mumamo-buffer-locals-dont-set) (memq (car local) mumamo-per-buffer-local-vars) (memq (car local) mumamo-per-main-major-local-vars) (get (car local) 'permanent-local)) local)) locals)) (setq locals (delq nil locals)) (setq locals (sort locals (lambda (sym-a sym-b) (string< (symbol-name (car sym-a)) (symbol-name (car sym-b)))))) (setq mumamo-buffer-locals-per-major (assq-delete-all major mumamo-buffer-locals-per-major)) (setq mumamo-buffer-locals-per-major (cons (cons major-mode locals) mumamo-buffer-locals-per-major)))) ;; (benchmark 1000 '(mumamo-save-per-major-local-vars major-mode)) ;; (benchmark 1000 '(mumamo-restore-per-major-local-vars major-mode)) (defvar mumamo-restore-per-major-local-vars-in-hook-major nil) (defun mumamo-restore-per-major-local-vars-in-hook () "Restore some per major mode local variables. Call `mumamo-restore-per-major-local-vars'. Use `mumamo-restore-per-major-local-vars-in-hook-major' as the major mode. This should be called in the major mode setup hook." (mumamo-restore-per-major-local-vars mumamo-restore-per-major-local-vars-in-hook-major) (setq mumamo-restore-per-major-local-vars-in-hook-major nil)) (put 'mumamo-restore-per-major-local-vars-in-hook 'permanent-local-hook t) (defun mumamo-restore-per-major-local-vars (major) "Restore some per major local variables for major mode MAJOR. This should be called after switching to a new chunks major mode." (let ((locals (cdr (assq major mumamo-buffer-locals-per-major))) var perm) (dolist (rec locals) (setq var (car rec)) (setq perm (get var 'permanent-local)) (unless (or perm (memq var mumamo-buffer-locals-dont-set)) (set (make-local-variable var) (cdr rec)))))) ;; (defun mumamo-testing-new () ;; (let ((locals (buffer-local-variables)) ;; var ;; perm ;; ) ;; (dolist (rec locals) ;; (setq var (car rec)) ;; (setq perm (get var 'permanent-local)) ;; (unless (or perm ;; (memq var mumamo-buffer-locals-dont-set)) ;; (setq var (cdr rec)))) ;; )) ;; ;;(benchmark 1000 '(mumamo-testing-new)) (defun mumamo-get-hook-value (hook remove) "Return hook HOOK value with entries in REMOVE removed. Remove also t. The value returned is a list of both local and default values." (let ((value (append (symbol-value hook) (default-value hook) nil))) (dolist (rem remove) (setq value (delq rem value))) (delq t value))) ;; FIX-ME: Clean up the different ways of surviving variables during ;; change of major mode. (defvar mumamo-set-major-keymap-checked nil) (make-variable-buffer-local 'mumamo-set-major-keymap-checked) (defvar mumamo-org-startup-done nil) (make-variable-buffer-local 'mumamo-org-startup-done) (put 'mumamo-org-startup-done 'permanent-local t) (defun mumamo-font-lock-fontify-chunk () "Like `font-lock-default-fontify-buffer' but for a chunk. Buffer must be narrowed to inner part of chunk when this function is called." (let ((verbose (if (numberp font-lock-verbose) (and (> font-lock-verbose 0) (> (- (point-max) (point-min)) font-lock-verbose)) font-lock-verbose)) font-lock-extend-region-functions ;; accept narrowing (font-lock-unfontify-region-function 'ignore)) ;;(setq verbose t) (with-temp-message (when verbose (format "Fontifying %s part %s-%s (%s)..." (buffer-name) (point-min) (point-max) font-lock-verbose)) (condition-case err (save-excursion (save-match-data (font-lock-fontify-region (point-min) (point-max) verbose) (font-lock-after-fontify-buffer) (setq font-lock-fontified t))) (msgtrc "font-lock-fontify-chunk: %s" (error-message-string err)) ;; We don't restore the old fontification, so it's best to unfontify. (quit (mumamo-font-lock-unfontify-chunk)))))) (defun mumamo-font-lock-unfontify-chunk () "Like `font-lock-default-unfontify-buffer' for . Buffer must be narrowed to chunk when this function is called." ;; Make sure we unfontify etc. in the whole buffer. (save-restriction ;;(widen) (font-lock-unfontify-region (point-min) (point-max)) (font-lock-after-unfontify-buffer) (setq font-lock-fontified nil))) (defun mumamo-set-major (major chunk) "Set major mode to MAJOR for mumamo." (mumamo-msgfntfy "mumamo-set-major %s, %s" major (current-buffer)) (mumamo-cancel-idle-set-major-mode) (remove-hook 'pre-command-hook 'mumamo-set-major-pre-command t) ;;(mumamo-backtrace "mumamo-set-major") (remove-hook 'text-mode-hook 'viper-mode) ;; Fix-me: maybe add it back... (let ((start-time (get-internal-run-time)) end-time used-time ;; Viper viper-vi-state-mode-list viper-emacs-state-mode-list viper-insert-state-mode-list ;; Org-Mode (org-inhibit-startup mumamo-org-startup-done) ;; Tell `mumamo-change-major-function': (mumamo-set-major-running major) ;; Fix-me: Take care of the new values added to these hooks! ;; That looks difficult. We may after this have changes to ;; both buffer local value and global value. The global ;; changes are in this variable, but the buffer local values ;; have been set once again. (change-major-mode-hook (mumamo-get-hook-value 'change-major-mode-hook mumamo-change-major-mode-no-nos)) (after-change-major-mode-hook (mumamo-get-hook-value 'after-change-major-mode-hook mumamo-after-change-major-mode-no-nos)) ;; Some major modes deactivates the mark, we do not want that: deactivate-mark ;; Font lock (font-lock-mode font-lock-mode) ;; We have to save and reset the cursor type, at least when ;; Viper is used (old-cursor-type cursor-type) ;; Protect last-command: fix-me: probably remove (last-command last-command) ;; Fix-me: remove this (old-rng-schema-file (when (boundp 'rng-current-schema-file-name) rng-current-schema-file-name)) ;; Local vars, per buffer and per major mode per-buffer-local-vars-state per-main-major-local-vars-state ) ;; We are not changing mode from font-lock's point of view, so do ;; not tell font-lock (let binding these hooks is probably not a ;; good choice since they may contain other stuff too): (setq mumamo-removed-from-hook nil) (mumamo-remove-from-hook 'change-major-mode-hook mumamo-change-major-mode-no-nos) ;;;;;;;;;;;;;;;; ;; Save per buffer local variables (dolist (sym (reverse mumamo-per-buffer-local-vars)) (when (boundp sym) (when (and (get sym 'permanent-local) (not (memq sym mumamo-per-buffer-local-vars-done-by-me)) (not (mumamo-hook-p sym))) (delq sym mumamo-per-buffer-local-vars) (lwarn 'mumamo-per-buffer-local-vars :warning "Already 'permanent-local t: %s" sym)))) (dolist (var mumamo-per-buffer-local-vars) (if (local-variable-p var) (push (cons var (symbol-value var)) per-buffer-local-vars-state))) ;;;;;;;;;;;;;;;; ;; Save per main major local variables (when (mumamo-fun-eq major-mode (mumamo-main-major-mode)) (dolist (var mumamo-per-main-major-local-vars) (if (local-variable-p var) (push (cons var (symbol-value var)) per-main-major-local-vars-state)))) ;; For all hooks that probably can have buffer local values, go ;; through the buffer local values and look for a permanent-local ;; property on each function. Remove those functions that does not ;; have it. Then make the buffer local value of the hook survive ;; by putting a permanent-local property on it. (unless (> emacs-major-version 22) (dolist (hk mumamo-survive-hooks) (put hk 'permanent-local t) (when (local-variable-p hk) (let ((hkv (copy-sequence (symbol-value hk)))) (dolist (v hkv) (unless (or (eq v t) (get v 'permanent-local-hook)) (remove-hook hk v t) )))))) (run-hooks 'mumamo-change-major-mode-hook) (setq mumamo-major-mode major) ;;;;;;;;;;;;;;;; ;; Save per major mode local variables before switching major (mumamo-save-per-major-local-vars major-mode) ;; Prepare to restore per major mode local variables after ;; switching back to major-mode, but do it in the greatest ;; ancestor's mode hook (see `run-mode-hooks'): (let (ancestor-hook-sym parent-hook-sym (parent major)) ;; We want the greatest ancestor's mode hook: (setq parent-hook-sym (intern-soft (concat (symbol-name parent) "-hook"))) (when parent-hook-sym (setq ancestor-hook-sym parent-hook-sym)) (while (get parent 'derived-mode-parent) (setq parent (get parent 'derived-mode-parent)) (setq parent-hook-sym (intern-soft (concat (symbol-name parent) "-hook"))) (when parent-hook-sym (setq ancestor-hook-sym parent-hook-sym))) (when ancestor-hook-sym ;; Put first in local hook to run it first: (setq mumamo-restore-per-major-local-vars-in-hook-major major) (add-hook ancestor-hook-sym 'mumamo-restore-per-major-local-vars-in-hook nil t)) ;;(msgtrc "set-major A: buffer-invisibility-spec=%S" buffer-invisibility-spec) ;;(msgtrc "set-major A: word-wrap=%S, cb=%s" word-wrap (current-buffer)) ;;(mumamo-backtrace "set-major") (let ((here (point))) (unwind-protect (save-restriction (let* ((minmax (mumamo-chunk-syntax-min-max chunk t)) (min (car minmax)) (max (cdr minmax)) (here (point)) ;; Fix-me: For some reason let binding did not help. Is this a bug or? ;; ;;(font-lock-fontify-buffer-function 'mumamo-font-lock-fontify-chunk) (old-bf (buffer-local-value 'font-lock-fontify-buffer-function (current-buffer))) (inhibit-redisplay t) ;; Fix-me: said to be for internal purposes only ) (narrow-to-region min max) (set (make-local-variable 'font-lock-fontify-buffer-function) 'mumamo-font-lock-fontify-chunk) ;;(message "funcall major=%s, %s" major font-lock-fontify-buffer-function) ;;(message "before funcall: function=%s" font-lock-fontify-buffer-function) (put 'font-lock-fontify-buffer-function 'permanent-local t) (funcall major) ;; <----------------------------------------------- (put 'font-lock-fontify-buffer-function 'permanent-local nil) (when old-bf (set (make-local-variable 'font-lock-fontify-buffer-function) old-bf)) )) (goto-char here))) ;;(msgtrc "set-major B: buffer-invisibility-spec=%S" buffer-invisibility-spec) ;;(msgtrc "set-major B: word-wrap=%S, cb=%s" word-wrap (current-buffer)) (setq font-lock-mode-major-mode major) ;; Tell font-lock it is ok (set (make-local-variable 'font-lock-function) 'mumamo-font-lock-function) (if (not ancestor-hook-sym) (mumamo-restore-per-major-local-vars major) (remove-hook ancestor-hook-sym 'mumamo-restore-per-major-local-vars-in-hook t))) ;;(msgtrc "set-major c: buffer-invisibility-spec=%S" buffer-invisibility-spec) (when (mumamo-fun-eq major 'org-mode) (setq mumamo-org-startup-done t)) (setq mumamo-major-mode-indent-line-function (cons major-mode indent-line-function)) (make-local-variable 'indent-line-function) (setq mode-name (concat (format-mode-line mode-name) (save-match-data (replace-regexp-in-string "-mumamo-mode$" "" (format "/%s" mumamo-multi-major-mode))))) (dolist (hk mumamo-survive-hooks) (put hk 'permanent-local nil)) ;; (when (and (featurep 'flymake) ;; flymake-mode) ;; (add-hook 'after-change-functions 'flymake-after-change-function nil t) ;; (add-hook 'after-save-hook 'flymake-after-save-hook nil t) ;; (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t)) ;;;;;;;;;;;;;;;; ;; Restore per buffer local variables ;; (dolist (sym mumamo-per-buffer-local-vars) ;; (when (boundp sym) ;; (put sym 'permanent-local nil))) ;;(msgtrc "per-buffer-local-vars-state=%S" per-buffer-local-vars-state) (dolist (saved per-buffer-local-vars-state) ;;(msgtrc "restore p buffer: %s, local=%s" (car saved) (local-variable-p (car saved))) (unless (local-variable-p (car saved)) (set (make-local-variable (car saved)) (cdr saved)))) ;;;;;;;;;;;;;;;; ;; Restore per main major local variables (unless (mumamo-fun-eq major-mode (mumamo-main-major-mode)) (dolist (saved per-main-major-local-vars-state) (set (make-local-variable (car saved)) (cdr saved)))) (mumamo-addback-to-hooks) (setq cursor-type old-cursor-type) (run-hooks 'mumamo-after-change-major-mode-hook) (when (derived-mode-p 'nxml-mode) (when (and old-rng-schema-file (not (string= old-rng-schema-file rng-current-schema-file-name))) (let ((rng-schema-change-hook nil)) ;(list 'rng-alidate-clear))) (condition-case err (progn (rng-set-schema-file-1 old-rng-schema-file) (rng-what-schema)) (nxml-file-parse-error (nxml-display-file-parse-error err))) (when rng-validate-mode ;; Fix-me: Change rng-validate variables so that this is ;; not necessary any more. (rng-validate-mode 0) (rng-validate-mode 1)) ))) ;; The nxml-parser should not die: (when (mumamo-derived-from-mode (mumamo-main-major-mode) 'nxml-mode) (add-hook 'after-change-functions 'rng-after-change-function nil t) (add-hook 'after-change-functions 'nxml-after-change nil t) ;; Added these for Emacs 22: (unless nxml-prolog-end (setq nxml-prolog-end 1)) (unless nxml-scan-end (setq nxml-scan-end (copy-marker 1)))) ;;; (when (and global-font-lock-mode ;;; font-lock-global-modes ;;; font-lock-mode) ;;; (when global-font-lock-mode ;;; (add-hook 'change-major-mode-hook 'global-font-lock-mode-cmhh)) ;;; (add-hook 'change-major-mode-hook 'font-lock-change-mode nil t) (mumamo-set-fontification-functions) ;; If user has used M-x flyspell-mode then we need to correct it: ;; Fix-me: This is inflexible. Need flyspell to cooperate. (when (featurep 'flyspell) (setq flyspell-generic-check-word-predicate 'mumamo-flyspell-verify)) (if mumamo-done-first-set-major (setq mumamo-just-changed-major t) (mumamo-msgfntfy "mumamo-set-major: ----- removing 'fontified") ;; Set up to fontify buffer (mumamo-save-buffer-state nil (remove-list-of-text-properties (point-min) (point-max) '(fontified))) (setq mumamo-done-first-set-major t)) ;; Timing, on a 3ghz cpu: ;; ;; used-time=(0 0 0), major-mode=css-mode ;; used-time=(0 0 0), major-mode=ecmascript-mode ;; used-time=(0 0 0), major-mode=html-mode ;; used-time=(0 0 203000), major-mode=nxhtml-mode ;; ;; After some changes 2007-04-25: ;; ;; used-time=(0 0 15000), major-mode=nxhtml-mode ;; ;; which is 15 ms. That seems acceptable though I am not sure ;; everything is correct when switching to nxhtml-mode yet. I ;; will have to wait for bug reports ;-) ;; ;; The delay is clearly noticeable and disturbing IMO unless you ;; change major mode in an idle timer. ;; ;;(setq end-time (get-internal-run-time)) ;;(setq used-time (time-subtract end-time start-time)) ) (setq mumamo-set-major-keymap-checked nil) ;; Fix-me: Seems like setting/checking the keymap in a timer is ;; problematc. This is an Emacs bug. ;;(run-with-idle-timer 1 nil 'mumamo-set-major-check-keymap) ;;(force-mode-line-update) (message "force-mode-line-update called") ) (defun mumamo-set-major-check-keymap () "Helper to work around an Emacs bug when setting local map in a timer." (or mumamo-set-major-keymap-checked (setq mumamo-set-major-keymap-checked (let ((map-sym (intern-soft (concat (symbol-name major-mode) "-map")))) (if (not map-sym) t ;; Don't know what to do (equal (current-local-map) (symbol-value map-sym))))))) (defvar mumamo-original-fill-paragraph-function nil) (make-variable-buffer-local 'mumamo-original-fill-paragraph-function) (defun mumamo-setup-local-fontification-vars () "Set up buffer local variables for mumamo style fontification." (make-local-variable 'font-lock-fontify-region-function) (setq font-lock-fontify-region-function 'mumamo-fontify-region) ;; Like font-lock-turn-on-thing-lock: (make-local-variable 'font-lock-fontify-buffer-function) (setq font-lock-fontify-buffer-function 'jit-lock-refontify) (setq font-lock-fontify-buffer-function 'mumamo-fontify-buffer) ;; Don't fontify eagerly (and don't abort if the buffer is large). (set (make-local-variable 'font-lock-fontified) t) (make-local-variable 'font-lock-unfontify-buffer-function) (setq font-lock-unfontify-buffer-function 'mumamo-unfontify-buffer) (set (make-local-variable 'indent-line-function) 'mumamo-indent-line-function) ;;(setq mumamo-original-fill-paragraph-function fill-paragraph-function) ;;(set (make-local-variable 'fill-paragraph-function) 'mumamo-fill-paragraph-function) ;;(set (make-local-variable 'fill-forward-paragraph-function 'forward-paragraph) (make-local-variable 'indent-region-function) (setq indent-region-function 'mumamo-indent-region-function) ;;(set (make-local-variable 'syntax-begin-function) 'mumamo-beginning-of-syntax) ;;(put 'font-lock-function 'permanent-local t) ;; FIX-ME: Not sure about this one, but it looks like it must be ;; set: (make-local-variable 'jit-lock-contextually) (setq jit-lock-contextually t) ) (defun mumamo-font-lock-function (mode) ;;(mumamo-backtrace "font-lock-function") (font-lock-default-function mode)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Turning on/off multi major modes (defun mumamo-set-fontification-functions () "Let mumamo take over fontification. This is run after changing major mode so that jit-lock will get the major mode specific values. \(There are currently no such values.)" ;; Give the jit machinery a starting point: (mumamo-jit-lock-register 'font-lock-fontify-region t) ;; Set the functions that font-lock should use: (mumamo-setup-local-fontification-vars) ;; Need some hook modifications to keep things together too: (add-hook 'change-major-mode-hook 'mumamo-change-major-function nil t) (add-hook 'post-command-hook 'mumamo-post-command nil t) (remove-hook 'change-major-mode-hook 'nxml-change-mode t) (remove-hook 'change-major-mode-hook 'nxhtml-change-mode t) ) (defun mumamo-initialize-state () "Initialize some mumamo state variables." (setq mumamo-done-first-set-major nil) (setq mumamo-just-changed-major nil)) (defun mumamo-turn-on-actions (old-major-mode) "Do what is necessary to turn on mumamo. Turn on minor mode function `font-lock-mode'. Set up for mumamo style fontification. Create a mumamo chunk at point. Run `mumamo-turn-on-hook'. OLD-MAJOR-MODE is used for the main major mode if the main major mode in the chunk family is nil." ;;(unless font-lock-mode (font-lock-mode 1)) (mumamo-msgfntfy "mumamo-turn-on-actions") (unless mumamo-current-chunk-family (error "Internal error: Chunk family is not set")) (if (not mumamo-current-chunk-family) (progn (lwarn '(mumamo) :warning "Could not turn on mumamo because chunk family was not set\n\tin buffer %s." (current-buffer)) (with-current-buffer "*Warnings*" (insert "\tFor more information see `") (mumamo-insert-describe-button 'define-mumamo-multi-major-mode 'describe-function) (insert "'.\n"))) ;; Load major mode: (setq mumamo-org-startup-done nil) (let ((main-major-mode (mumamo-major-mode-from-modespec (mumamo-main-major-mode)))) (unless main-major-mode (setcar (cdr mumamo-current-chunk-family) old-major-mode) (setq main-major-mode (mumamo-main-major-mode))) ;;(with-temp-buffer (funcall main-major-mode)) (setq mumamo-major-mode main-major-mode) (when (boundp 'nxml-syntax-highlight-flag) (when (mumamo-derived-from-mode main-major-mode 'nxml-mode) (set (make-local-variable 'nxml-syntax-highlight-flag) nil))) ;; Init fontification (mumamo-initialize-state) (mumamo-set-fontification-functions) (mumamo-save-buffer-state nil (remove-list-of-text-properties (point-min) (point-max) (list 'fontified))) ;; For validation header etc: (when (mumamo-derived-from-mode main-major-mode 'nxhtml-mode) (require 'rngalt nil t) (when (featurep 'rngalt) (setq rngalt-major-mode (mumamo-main-major-mode)) (rngalt-update-validation-header-overlay)) (when (featurep 'rng-valid) (setq rng-get-major-mode-chunk-function 'mumamo-find-chunks) (setq rng-valid-nxml-major-mode-chunk-function 'mumamo-valid-nxml-chunk) (setq rng-end-major-mode-chunk-function 'overlay-end)))) ;;(mumamo-set-major-post-command) ;;(add-hook 'change-major-mode-hook 'mumamo-change-major-function nil t) (when (boundp 'flyspell-generic-check-word-predicate) (setq flyspell-generic-check-word-predicate 'mumamo-flyspell-verify)) (run-hooks 'mumamo-turn-on-hook) ;;(mumamo-get-chunk-save-buffer-state (point)) (let ((buffer-windows (get-buffer-window-list (current-buffer)))) (if (not buffer-windows) (let* ((ovl (mumamo-find-chunks (point) "mumamo-turn-on-actions")) (major (when ovl (mumamo-chunk-major-mode ovl)))) (when major (mumamo-set-major major ovl))) (dolist (win (get-buffer-window-list (current-buffer) nil t)) (let ((wp (or (window-end win) (window-point win) (window-start win)))) (mumamo-get-chunk-save-buffer-state wp) (when (eq win (selected-window)) (let* ((ovl (mumamo-find-chunks wp "mumamo-turn-on-actions")) (major (when ovl (mumamo-chunk-major-mode ovl)))) (when major (mumamo-set-major major ovl)))))))) ;;(msgtrc "mumamo-turn-on-action exit: font-lock-keywords-only =%s in buffer %s, def=%s" font-lock-keywords-only (current-buffer) (default-value 'font-lock-keywords-only)) ;; This did not help for Emacs bug 3467: ;;(set-default 'font-lock-keywords-only nil) ;;(setq font-lock-keywords-only nil) ) (set (make-local-variable 'font-lock-function) 'mumamo-font-lock-function) (mumamo-emacs-start-bug3467-timer-if-needed) ) ;; (defun mumamo-on-font-lock-off () ;; "The reverse of `mumamo-turn-on-actions'." ;; (let ((mumamo-main-major-mode (mumamo-main-major-mode))) ;; (mumamo-turn-off-actions) ;; ;; Turning off `font-lock-mode' also turns off `mumamo-mode'. It is ;; ;; quite tricky to not turn on `font-lock-mode' again in case we got ;; ;; here because it was turned off. We must first remove the cmhh ;; ;; function and then also run the internal font lock turn off. ;; (let* ((flm font-lock-mode) ;; (flgm global-font-lock-mode) ;; (remove-cmhh (and (not flm) flgm))) ;; ;; If remove-cmhh is non-nil then we got here because ;; ;; `font-lock-mode' was beeing turned off in the buffer, but ;; ;; `global-font-lock-mode' is still on. ;; (when remove-cmhh ;; (remove-hook 'change-major-mode-hook 'global-font-lock-mode-cmhh)) ;; (if mumamo-main-major-mode ;; (funcall mumamo-main-major-mode) ;; (fundamental-mode)) ;; (unless flm ;; (setq font-lock-mode nil) ;; (font-lock-mode-internal nil)) ;; (when remove-cmhh ;; (add-hook 'change-major-mode-hook 'global-font-lock-mode-cmhh))))) (defun mumamo-turn-off-actions () "The reverse of `mumamo-turn-on-actions'." (mumamo-msgfntfy "mumamo-turn-off-actions") (when (fboundp 'nxhtml-validation-header-mode) (nxhtml-validation-header-mode -1)) (when (mumamo-derived-from-mode (nth 1 mumamo-current-chunk-family) 'nxml-mode) (when (fboundp 'nxml-change-mode) (nxml-change-mode))) (when (and (boundp 'rng-validate-mode) rng-validate-mode) (rng-validate-mode 0)) (when (featurep 'rng-valid) (setq rng-get-major-mode-chunk-function nil) (setq rng-valid-nxml-major-mode-chunk-function nil) (setq rng-end-major-mode-chunk-function nil) ) ;; Remove nxml for Emacs 22 (remove-hook 'after-change-functions 'rng-after-change-function t) (remove-hook 'after-change-functions 'nxml-after-change t) (when (boundp 'rngalt-major-mode) (setq rngalt-major-mode nil)) (remove-hook 'change-major-mode-hook 'mumamo-change-major-function t) ;;(mumamo-unfontify-chunks) ;;(remove-hook 'after-change-functions 'mumamo-jit-lock-after-change t) (remove-hook 'after-change-functions 'mumamo-after-change t) (remove-hook 'post-command-hook 'mumamo-post-command t) ;;(remove-hook 'c-special-indent-hook 'mumamo-c-special-indent t) (mumamo-margin-info-mode -1) (when (fboundp 'mumamo-clear-all-regions) (mumamo-clear-all-regions)) (save-restriction (widen) (mumamo-save-buffer-state nil (set-text-properties (point-min) (point-max) nil))) (setq mumamo-current-chunk-family nil) (setq mumamo-major-mode nil) (setq mumamo-multi-major-mode nil) ;; for minor-mode-map-alist (setq mumamo-multi-major-mode nil) (mumamo-remove-all-chunk-overlays) (when (fboundp 'rng-cancel-timers) (rng-cancel-timers)) ) (defvar mumamo-turn-on-hook nil "Normal hook run after turning on `mumamo-mode'.") (put 'mumamo-turn-on-hook 'permanent-local t) (defvar mumamo-change-major-mode-hook nil "Normal hook run before internal change of major mode.") (put 'mumamo-change-major-mode-hook 'permanent-local t) (defvar mumamo-after-change-major-mode-hook nil "Normal hook run after internal change of major mode.") (put 'mumamo-after-change-major-mode-hook 'permanent-local t) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Defining multi major modes (defvar mumamo-defined-multi-major-modes nil "List of functions defined for turning on mumamo. Those functions should be called instead of calling a major mode function when you want to use multiple major modes in a buffer. They may be added to for example `auto-mode-alist' to automatically have the major mode support turned on when opening a file. Each of these functions defines how to mix certain major modes in a buffer. All functions defined by `define-mumamo-multi-major-mode' are added to this list. See this function for a general description of how the functions work. If you want to quickly define a new mix of major modes you can use `mumamo-quick-static-chunk'.") ;;;###autoload (defun mumamo-list-defined-multi-major-modes (show-doc show-chunks match) "List currently defined multi major modes. If SHOW-DOC is non-nil show the doc strings added when defining them. \(This is not the full doc string. To show the full doc string you can click on the multi major mode in the list.) If SHOW-CHUNKS is non-nil show the names of the chunk dividing functions each multi major mode uses. If MATCH then show only multi major modes whos names matches." (interactive (list (y-or-n-p "Include short doc string? ") (y-or-n-p "Include chunk function names? ") (read-string "List only multi major mode matching regexp (emtpy for all): "))) (with-output-to-temp-buffer (help-buffer) (help-setup-xref (list #'mumamo-list-defined-multi-major-modes) (interactive-p)) (with-current-buffer (help-buffer) (insert "The currently defined multi major modes in your Emacs are:\n\n") (let ((mmms (reverse mumamo-defined-multi-major-modes)) (here (point))) (setq mmms (sort mmms (lambda (a b) (string< (symbol-name (cdr a)) (symbol-name (cdr b)))))) (when (string= match "") (setq match nil)) (while mmms (let* ((mmm (car mmms)) (sym (cdr mmm)) (desc (car mmm)) (auto (get sym 'autoload)) (auto-desc (when auto (nth 1 auto))) (family (get sym 'mumamo-chunk-family)) (chunks (nth 2 family))) (when (or (not match) (string-match-p match (symbol-name sym))) (insert " `" (symbol-name sym) "'" " (" desc ")\n" (if (and show-doc auto-desc) (concat " " auto-desc "\n") "") (if show-chunks (format " Chunks:%s\n" (let ((str "") (nn 0)) (mapc (lambda (c) (if (< nn 2) (setq str (concat str " ")) (setq nn 0) (setq str (concat str "\n "))) (setq nn (1+ nn)) (setq str (concat str (format "%-30s" (format "`%s'" c)))) ) chunks) str)) "") (if (or show-doc show-chunks) "\n\n" "") )) (setq mmms (cdr mmms)))) )))) (defun mumamo-describe-chunks (chunks) "Return text describing CHUNKS." (let* ((desc (concat "* Main major mode: `" (symbol-name (nth 1 chunks)) "'\n" "\n* Functions for dividing into submodes:\n"))) (dolist (divider (nth 2 chunks)) (setq desc (concat desc "\n`" (symbol-name divider) "'\n " (let ((doc (if (functionp divider) (documentation divider t) "(Function not compiled when building doc)"))) (if (not doc) "(Not documented)" (substring doc 0 (string-match "\n" doc))))))) (setq desc (concat desc "\n\n(Note that the functions for dividing into chunks returns\n" "a major mode specifier which may be translated into a major mode\n" "by `mumamo-main-major-mode'.)\n")) desc)) (defun mumamo-add-multi-keymap (toggle keymap) "Add TOGGLE and KEYMAP to `minor-mode-map-alist'. This is used to add a keymap to multi major modes since the local keymap is occupied by the major modes. It is also used to add the `mumamo-map' keymap to every buffer with a multi major mode." ;; Copied from add-minor-mode ;; Add the map to the minor-mode-map-alist. (when keymap (let ((existing (assq toggle minor-mode-map-alist)) (after t)) (if existing (setcdr existing keymap) (let ((tail minor-mode-map-alist) found) (while (and tail (not found)) (if (eq after (caar tail)) (setq found tail) (setq tail (cdr tail)))) (if found (let ((rest (cdr found))) (setcdr found nil) (nconc found (list (cons toggle keymap)) rest)) (setq minor-mode-map-alist (cons (cons toggle keymap) minor-mode-map-alist)))))))) (defvar mumamo-map (let ((map (make-sparse-keymap))) (define-key map [(control meta prior)] 'mumamo-backward-chunk) (define-key map [(control meta next)] 'mumamo-forward-chunk) ;; Use mumamo-indent-line-function: ;;(define-key map [tab] 'indent-for-tab-command) (define-key map [(meta ?q)] 'fill-paragraph) map) "Keymap that is active in all mumamo buffers. It has the some priority as minor mode maps.") ;;(make-variable-buffer-local 'mumamo-map) (put 'mumamo-map 'permanent-local t) (mumamo-add-multi-keymap 'mumamo-multi-major-mode mumamo-map) ;;;###autoload (defun mumamo-multi-major-modep (value) "Return t if VALUE is a multi major mode function." (and (fboundp value) (rassq value mumamo-defined-multi-major-modes))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Indenting, filling, moving etc ;; FIX-ME: Indentation in perl here doc indents the ending mark which ;; corrupts the perl here doc. (defun mumamo-indent-line-function () "Function to indent the current line. This is the buffer local value of `indent-line-function' when mumamo is used." (let ((here (point-marker)) fontification-functions rng-nxml-auto-validate-flag (before-text (<= (current-column) (current-indentation)))) (mumamo-indent-line-function-1 nil nil nil) ;; If the marker was in the indentation part strange things happen ;; if we try to go back to the marker, at least in php-mode parts. (if before-text (back-to-indentation) (goto-char here)))) (defun mumamo-indent-current-line-chunks (last-chunk-prev-line) "Return a list of chunks to consider when indenting current line. This list consists of four chunks at these positions: - Beginning of line - 1 - Beginning of line - End of line - End of line + 1" ;; Fix-me: must take markers into account too when a submode ;; includes the markers. (setq last-chunk-prev-line nil) ;;(msgtrc "indent-current-line-chunks: last-chunk-prev-line=%S" last-chunk-prev-line) (save-restriction (widen) (let* ((lb-pos (line-beginning-position)) (le-pos (line-end-position)) (pos0 (if (> lb-pos (point-min)) (1- lb-pos) (point-min))) (pos1 lb-pos) (pos2 le-pos) (pos3 (if (< le-pos (point-max)) (+ 1 le-pos) (point-max))) ;; Create all chunks on this line first, then grab them (ovl3 (mumamo-find-chunks pos3 "mumamo-indent-current-line-chunks")) (ovl2 (if (>= pos2 (overlay-start ovl3)) ovl3 (mumamo-get-existing-new-chunk-at pos2))) (ovl1 (if (>= pos1 (overlay-start ovl2)) ovl2 (mumamo-get-existing-new-chunk-at pos1))) (ovl0 (if (> pos0 (overlay-start ovl1)) ovl1 (mumamo-get-existing-new-chunk-at pos0 t)))) (list ovl0 ovl1 ovl2 ovl3)))) ;; Fix-me: need to back up past comments in for example Going out this line; First char inner or outer; line end outer; 3) Going out this line; first char inner; line end outer; From this we deduce the following way to compute if we are going in or out: - Odd above (going in): Compare prev line end's mumamo-depth with current line end's dito. Set flag for first line in chunk. - Even above (going out): Same test as for going in, but going out happens on current line. " ;;(msgtrc "indent-line-function-1 blp=%s" (line-beginning-position)) (setq prev-line-chunks nil) ;;(setq last-parent-major-indent nil) ;;(setq entering-submode-arg nil) (unless prev-line-chunks (save-excursion (goto-char (line-beginning-position 1)) (unless (= (point) 1) (skip-chars-backward "\n\t ") (goto-char (line-beginning-position 1)) (setq prev-line-chunks (mumamo-indent-current-line-chunks nil)) ;;(msgtrc "%d:prev-line-chunks=%S" (save-restriction (widen) (line-number-at-pos)) prev-line-chunks ) ))) (let* ((prev-line-chunk0 (nth 0 prev-line-chunks)) (prev-line-chunk2 (nth 2 prev-line-chunks)) (prev-line-chunk3 (nth 3 prev-line-chunks)) (prev-line-major0 (mumamo-chunk-major-mode (nth 0 prev-line-chunks))) (prev-line-major1 (mumamo-chunk-major-mode (nth 1 prev-line-chunks))) (prev-line-major2 (mumamo-chunk-major-mode (nth 2 prev-line-chunks))) (prev-line-major3 (mumamo-chunk-major-mode (nth 3 prev-line-chunks))) (prev-depth2 (if prev-line-chunk2 (overlay-get prev-line-chunk2 'mumamo-depth) 0)) (prev-depth3 (if prev-line-chunk3 (overlay-get prev-line-chunk3 'mumamo-depth) 0)) (this-line-chunks (mumamo-indent-current-line-chunks (nth 3 prev-line-chunks))) ;;(dummy (msgtrc "%d:this-line-chunks=%S" (save-restriction (widen) (line-number-at-pos)) this-line-chunks)) (this-line-chunk0 (nth 0 this-line-chunks)) (this-line-chunk2 (nth 2 this-line-chunks)) (this-line-chunk3 (nth 3 this-line-chunks)) (this-line-major0 (mumamo-chunk-major-mode (nth 0 this-line-chunks))) (this-line-major1 (mumamo-chunk-major-mode (nth 1 this-line-chunks))) (this-line-major2 (mumamo-chunk-major-mode (nth 2 this-line-chunks))) (this-line-major3 (mumamo-chunk-major-mode (nth 3 this-line-chunks))) (this-depth2 (overlay-get this-line-chunk2 'mumamo-depth)) (this-depth3 (overlay-get this-line-chunk3 'mumamo-depth)) ;;(dummy (msgtrc "a\t this=%S" this-line-chunks)) this-line-indent-major major-indent-line-function (main-major (mumamo-main-major-mode)) (old-indent (current-indentation)) (next-entering-submode (if (< prev-depth3 this-depth3) 'yes 'no)) (entering-submode ;; Fix-me (progn (unless nil ;entering-submode-arg (let* ((prev-prev-line-chunks (save-excursion (goto-char (line-beginning-position 0)) (unless (bobp) (skip-chars-backward "\n\t ") (goto-char (line-beginning-position 1)) (let ((chunks (mumamo-indent-current-line-chunks nil))) ;;(msgtrc "%d:prev-prev-line-chunks=%S" (save-restriction (widen) (line-number-at-pos)) chunks) chunks)))) (prev-prev-line-chunk2 (nth 2 prev-prev-line-chunks)) (prev-prev-line-chunk3 (nth 3 prev-prev-line-chunks)) (prev-prev-depth2 (when prev-prev-line-chunk2 (overlay-get prev-prev-line-chunk2 'mumamo-depth))) (prev-prev-depth3 (when prev-prev-line-chunk3 (overlay-get prev-prev-line-chunk3 'mumamo-depth)))) ;;(msgtrc "depths 2=%s/%s/%s 3=%s/%s/%s" prev-prev-depth2 prev-depth2 this-depth2 prev-prev-depth3 prev-depth3 this-depth3) (setq entering-submode-arg (if prev-prev-depth2 (if (and (eq prev-prev-line-chunk2 (overlay-get prev-line-chunk2 'mumamo-prev-chunk)) (< prev-prev-depth2 prev-depth2)) 'yes 'no) (if (> this-depth2 0) 'yes 'no) )) )) (eq 'yes entering-submode-arg) )) ;; fix-me ;; Fix-me (leaving-submode (> prev-depth2 this-depth2)) want-indent ;; The indentation we desire got-indent (here-on-line (point-marker)) this-pending-undo-list (while-n1 0) (while-n2 0) (while-n3 0) ;; Is there a possible indentor chunk on this line?: (this-line-indentor-chunk (when (> (overlay-start this-line-chunk2) (point-at-bol)) (overlay-get this-line-chunk2 'mumamo-prev-chunk))) ;;(dummy (msgtrc "this-line-indentor-chunk=%S" this-line-indentor-chunk)) ;; Check if this really is an indentor chunk: ;; Fix-me: 'mumamo-indentor is not put on the chunk yet since ;; it is done in mumamo-template-indent-get-chunk-shift ... - ;; and now it is calle too often ... (this-line-indentor-prev (when this-line-indentor-chunk (overlay-get this-line-indentor-chunk 'mumamo-prev-chunk))) (this-line-is-indentor (and this-line-indentor-prev (eq (overlay-get this-line-indentor-prev 'mumamo-next-indent) 'mumamo-template-indentor) (progn (goto-char (overlay-start this-line-indentor-chunk)) (back-to-indentation) (= (point) (overlay-start this-line-indentor-chunk))))) ;; Fix-me: rewrite and reorder. We do not need both shift-in and shift-out (this-template-shift (when this-line-is-indentor (mumamo-template-indent-get-chunk-shift this-line-indentor-chunk))) ;;(dummy (msgtrc "this-line-indentor=%s, %S" this-template-shift this-line-is-indentor)) ;; Fix-me: skip over blank lines backward here: (prev-template-indentor (when prev-line-chunk0 (unless (eq this-line-chunk0 prev-line-chunk0) (let* ((prev (overlay-get this-line-chunk0 'mumamo-prev-chunk)) (prev-prev (overlay-get prev 'mumamo-prev-chunk))) (when (and (eq prev-prev prev-line-chunk0) (eq (overlay-get prev-prev 'mumamo-next-indent) 'mumamo-template-indentor)) prev))))) (prev-template-shift-rec (when prev-template-indentor (mumamo-template-indent-get-chunk-shift prev-template-indentor) )) (template-shift (if (and (car this-template-shift) (/= 0 (car this-template-shift))) (car this-template-shift) (when prev-template-shift-rec (cdr prev-template-shift-rec)))) (template-indent-abs (when (and template-shift (/= 0 template-shift)) (+ template-shift (let ((here (point))) (if prev-template-indentor (goto-char (overlay-start prev-template-indentor)) (goto-char (overlay-start this-line-indentor-chunk)) (skip-chars-backward " \t\r\n\f")) (prog1 (current-indentation) (goto-char here)))))) ) (when (and leaving-submode entering-submode) (message "Do not know how to indent here (both leaving and entering sub chunks)") ) ;; Fix-me: indentation ;;(error "Leaving=%s, entering=%s this0,1,2,3=%s,%s,%s,%s" leaving-submode entering-submode this-line-major0 this-line-major1 this-line-major2 this-line-major3) (when (or leaving-submode entering-submode) (unless last-parent-major-indent (save-excursion ;;(while (and (> 500 (setq while-n1 (1+ while-n1))) (while (and (mumamo-while 500 'while-n1 "last-parent-major-indent") (not last-parent-major-indent)) (if (bobp) (setq last-parent-major-indent 0) (goto-char (line-beginning-position 0)) (when (mumamo-fun-eq main-major (mumamo-chunk-major-mode (car (mumamo-indent-current-line-chunks nil))) ) (skip-chars-forward " \t") (if (eolp) (setq last-parent-major-indent 0) (setq last-parent-major-indent (current-column))))))))) (mumamo-msgindent " leaving-submode=%s, entering-submode=%s" leaving-submode entering-submode) ;;(msgtrc " leaving-submode=%s, entering-submode=%s, template-indentor=%s" leaving-submode entering-submode template-indentor) ;; Fix-me: use this. ;; - clean up after chunk deletion ;; - next line after a template-indentor, what happens? ;;(setq template-indentor nil) ;; fix-me (cond ( template-indent-abs (setq want-indent (max 0 template-indent-abs))) ( leaving-submode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; First line after submode (mumamo-msgindent " leaving last-parent-major-indent=%s" last-parent-major-indent) (if (eq (overlay-get (overlay-get this-line-chunk0 'mumamo-prev-chunk) 'mumamo-next-indent) 'heredoc) (setq want-indent 0) (setq want-indent last-parent-major-indent))) ( entering-submode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; First line in submode ;;(setq this-line-indent-major this-line-major0) (setq this-line-indent-major (mumamo-indent-get-major-to-use this-line-major0 this-depth3)) ;;(when (and prev-line-major0 (not (mumamo-fun-eq this-line-major0 prev-line-major0))) (setq this-line-indent-major prev-line-major0)) (mumamo-msgindent " this-line-indent-major=%s, major-mode=%s this0=%s" this-line-indent-major major-mode this-line-major0) (mumamo-msgindent " mumamo-submode-indent-offset=%s" mumamo-submode-indent-offset) (unless (mumamo-fun-eq this-line-indent-major major-mode) (mumamo-set-major this-line-indent-major this-line-chunk0)) (setq want-indent (+ last-parent-major-indent (if (= 0 last-parent-major-indent) (if mumamo-submode-indent-offset-0 mumamo-submode-indent-offset-0 -1000) (if mumamo-submode-indent-offset mumamo-submode-indent-offset -1000)))) (unless (< 0 want-indent) (setq want-indent nil)) (when (and want-indent (mumamo-indent-use-widen major-mode)) ;; In this case only use want-indent if it is bigger than the ;; indentation calling indent-line-function would give. (condition-case nil (atomic-change-group (mumamo-call-indent-line (nth 0 this-line-chunks)) (when (> want-indent (current-indentation)) (signal 'mumamo-error-ind-0 nil)) (setq want-indent nil)) (mumamo-error-ind-0))) (unless want-indent (mumamo-call-indent-line (nth 0 this-line-chunks))) (mumamo-msgindent " enter sub.want-indent=%s, curr=%s, last-main=%s" want-indent (current-indentation) last-parent-major-indent) ;;(unless (> want-indent (current-indentation)) (setq want-indent nil)) ) ( t ;; We have to change major mode, because we know nothing ;; about the requirements of the indent-line-function: ;; Fix-me: This may be cured by RMS suggestion to ;; temporarily set all variables back to global values? (setq this-line-indent-major (mumamo-indent-get-major-to-use this-line-major0 this-depth3)) (mumamo-msgindent " this-line-indent-major=%s" this-line-indent-major) (unless (mumamo-fun-eq this-line-indent-major major-mode) (mumamo-set-major this-line-indent-major this-line-chunk0)) ;; Use the major mode at the beginning of since a sub chunk may ;; start at start of line. (if (mumamo-fun-eq this-line-major1 main-major) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; In main major mode ;; ;; Take care of the case when all the text is in a ;; sub chunk. In that case use the same indentation as if ;; the code all belongs to the surrounding major mode. (let ((here (point)) (use-widen (mumamo-indent-use-widen main-major))) ;; If we can't indent indent using the main major mode ;; because it is only blanks and we should not widen, ;; then use the indentation on the line where it starts. (mumamo-msgindent " In main major mode") (forward-line 0) (skip-chars-backward " \t\n\r\f") (forward-line 0) (if (or use-widen (>= (point) (overlay-start this-line-chunk0))) (progn (goto-char here) (mumamo-call-indent-line this-line-chunk0)) (setq want-indent (current-indentation)) (goto-char here)) (mumamo-msgindent " In main major mode B") (setq last-parent-major-indent (current-indentation))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; In sub major mode ;; ;; Get the indentation the major mode alone would use: ;;(setq got-indent (mumamo-get-major-mode-indent-column)) ;; Since this line has another major mode than the ;; previous line we instead want to indent relative to ;; that line in a way decided in mumamo: (mumamo-msgindent " In sub major mode") (let ((chunk (mumamo-get-chunk-save-buffer-state (point))) (font-lock-dont-widen t) ind-zero (here (point)) ind-on-first-sub-line) (save-restriction (mumamo-update-obscure chunk here) (let ((syn-min-max (mumamo-chunk-syntax-min-max chunk nil))) (narrow-to-region (car syn-min-max) (cdr syn-min-max))) (condition-case nil (atomic-change-group (mumamo-call-indent-line (nth 0 this-line-chunks)) (when (= 0 (current-indentation)) (setq ind-zero t) ;; It is maybe ok if indentation on first sub ;; line is 0 so check that: (goto-char (point-min)) (widen) (setq ind-on-first-sub-line (current-indentation)) (goto-char here) (signal 'mumamo-error-ind-0 nil))) (mumamo-error-ind-0)) ;; Unfortunately the indentation can sometimes get 0 ;; here even though it is clear it should not be 0. This ;; happens when there are only comments or empty lines ;; above. ;; ;; See c:/test/erik-lilja-index.php for an example. (when ind-zero ;(and t (= 0 (current-indentation))) (save-excursion (setq want-indent 0) (unless (= 0 ind-on-first-sub-line) ;;(while (and (> 500 (setq while-n2 (1+ while-n2))) (while (and (mumamo-while 500 'while-n2 "want-indent") (= 0 want-indent) (/= (point) (point-min))) (beginning-of-line 0) (setq want-indent (current-indentation))) ;; Now if want-indent is still 0 we need to look further above (when (= 0 want-indent) (widen) ;;(while (and (> 500 (setq while-n3 (1+ while-n3))) (while (and (mumamo-while 500 'while-n3 "want-indent 2") (= 0 want-indent) (/= (point) (point-min))) (beginning-of-line 0) (setq want-indent (current-indentation))) ;; If we got to the main major mode we need to add ;; the special submode offset: (let* ((ovl (mumamo-get-chunk-save-buffer-state (point))) (major (mumamo-chunk-major-mode ovl))) (when (mumamo-fun-eq major main-major) (setq want-indent (+ want-indent (if (= 0 want-indent) mumamo-submode-indent-offset-0 mumamo-submode-indent-offset))))))))) ))))) (when want-indent ;;(msgtrc "indent-line-to %s at line-beginning=%s" want-indent (line-beginning-position)) (indent-line-to want-indent)) ;; (when (and template-shift (/= 0 template-shift)) ;; (let ((ind (+ (current-indentation) template-shift))) ;; (indent-line-to ind))) ;; (when template-indent-abs ;; (indent-line-to template-indent-abs)) (goto-char here-on-line) ;;(msgtrc "exit: %s" (list this-line-chunks last-parent-major-indent)) (list this-line-chunks last-parent-major-indent next-entering-submode))) ;; Fix-me: use this for first line in a submode ;; Fix-me: check more carefully for widen since it may lead to bad results. (defun mumamo-indent-use-widen (major-mode) "Return non-nil if widen before indentation in MAJOR-MODE." (let* ((specials (cadr (assoc major-mode mumamo-indent-widen-per-major))) (use-widen (memq 'use-widen specials)) (use-widen-maybe (assq 'use-widen specials))) (or use-widen (memq mumamo-multi-major-mode (cadr use-widen-maybe))))) ;;(mumamo-indent-use-widen 'php-mode) ;;(mumamo-indent-use-widen 'nxhtml-mode) ;;(mumamo-indent-use-widen 'html-mode) ;; Fix-me: remove ;; (defun mumamo-indent-special-or-default (default-indent) ;; "Indent to DEFAULT-INDENT unless a special indent can be done." ;; (mumamo-with-major-mode-indentation major-mode ;; `(progn ;; (if (mumamo-indent-use-widen major-mode) ;; (save-restriction ;; (widen) ;; (mumamo-msgindent "=> special-or-default did widen, %s" major-mode) ;; (funcall indent-line-function)) ;; (indent-to-column default-indent))))) (defun mumamo-call-indent-line (chunk) "Call the relevant `indent-line-function'." ;;(msgtrc "call-indent-line %s, lbp=%s" chunk (line-beginning-position)) (if nil (mumamo-with-major-mode-indentation major-mode `(save-restriction (when (mumamo-indent-use-widen major-mode) (mumamo-msgindent "=> indent-line did widen") (widen)) (funcall indent-line-function))) (let ((maj (car mumamo-major-mode-indent-line-function)) (fun (cdr mumamo-major-mode-indent-line-function))) (assert (mumamo-fun-eq maj major-mode)) (save-restriction ;; (unless (mumamo-indent-use-widen major-mode) ;; (let ((syn-min-max (mumamo-chunk-syntax-min-max chunk nil))) ;; (narrow-to-region (car syn-min-max) (cdr syn-min-max)))) (let ((mumamo-stop-widen (not (mumamo-indent-use-widen major-mode)))) (if (not mumamo-stop-widen) (widen) (let ((syn-min-max (mumamo-chunk-syntax-min-max chunk nil))) (narrow-to-region (car syn-min-max) (cdr syn-min-max)))) ;;(msgtrc "call-indent-line fun=%s" fun) ;;(funcall fun) ;; Fix-me: Use mumamo-funcall-evaled to avoid (widen): (mumamo-funcall-evaled fun) ))))) (defvar mumamo-stop-widen nil) (when nil (let* ((fun 'describe-variable) (lib (symbol-file fun 'defun))) (find-function-search-for-symbol fun nil lib))) (defun mumamo-funcall-evaled (fun &rest args) "Make sure FUN is evaled, then call it. This make sure (currently) that defadvice for primitives are called. They are not called in byte compiled code. See URL `http://debbugs.gnu.org/cgi/bugreport.cgi?bug=5863' since this may change." (when mumamo-stop-widen (unless (get fun 'mumamo-evaled) (let* ((lib (symbol-file fun 'defun)) (where (find-function-search-for-symbol fun nil lib)) (buf (car where)) (pos (cdr where))) (with-current-buffer buf (let ((close (and (not (buffer-modified-p)) (= 1 (point))))) ;;(goto-char pos) (eval-defun nil) (msgtrc "mumamo-funcall-evaled %s" (current-buffer)) (eval-buffer) (when close (kill-buffer)))) (put fun 'mumamo-evaled t)))) (apply 'funcall fun args)) ;;(require 'advice) (defun mumamo-defadvice-widen () (defadvice widen (around mumamo-ad-widen activate compile ) (unless (and mumamo-multi-major-mode mumamo-stop-widen) ad-do-it))) (eval-after-load 'mumamo '(mumamo-defadvice-widen)) ;; (defadvice font-lock-fontify-buffer (around ;; mumam-ad-font-lock-fontify-buffer ;; activate ;; compile ;; ) ;; (if mumamo-multi-major-mode ;; (save-restriction ;; (let* ((chunk (mumamo-find-chunks (point) "font-lock-fontify-buffer advice")) ;; (syn-min-max (mumamo-chunk-syntax-min-max chunk nil)) ;; (syn-min (car syn-min-max)) ;; (syn-max (cdr syn-min-max)) ;; (mumamo-stop-widen t)) ;; (narrow-to-region syn-min syn-max) ;; (font-lock-fontify-region syn-min syn-max))) ;; ad-do-it)) (defun mumamo-indent-region-function (start end) "Indent the region between START and END." (save-excursion (setq end (copy-marker end)) (goto-char start) (let ((old-point -1) prev-line-chunks last-parent-major-indent entering-submode-arg ;; Turn off validation during indentation (old-rng-validate-mode (when (boundp 'rng-validate-mode) rng-validate-mode)) (rng-nxml-auto-validate-flag nil) (nxhtml-use-imenu nil) fontification-functions rng-nxml-auto-validate-flag (nxhtml-mode-hook (mumamo-get-hook-value 'nxhtml-mode-hook '(html-imenu-setup))) ;; (while-n1 0)) (when old-rng-validate-mode (rng-validate-mode -1)) ;;(while (and (> 3000 (setq while-n1 (1+ while-n1))) (while (and (mumamo-while 3000 'while-n1 "indent-region") (< (point) end) (/= old-point (point))) ;;(message "mumamo-indent-region-function, point=%s" (point)) (or (and (bolp) (eolp)) (let ((ret (mumamo-indent-line-function-1 prev-line-chunks last-parent-major-indent entering-submode-arg))) (setq prev-line-chunks (nth 0 ret)) (setq last-parent-major-indent (nth 1 ret)) (setq entering-submode-arg (nth 2 ret)))) (setq old-point (point)) (forward-line 1)) (when old-rng-validate-mode (rng-validate-mode 1))) (message "Ready indenting region"))) (defun mumamo-fill-forward-paragraph-function(&optional arg) "Function to move over paragraphs used by filling code. This is the buffer local value of `fill-forward-paragraph-function' when mumamo is used." ;; fix-me: Do this chunk by chunk ;; Fix-me: use this (but only in v 23) (let* ((ovl (mumamo-get-chunk-save-buffer-state (point))) (major (mumamo-chunk-major-mode ovl))) (mumamo-with-major-mode-fontification major fill-forward-paragraph-function))) (defun mumamo-fill-chunk (&optional justify) "Fill each of the paragraphs in the current chunk. Narrow to chunk region trimmed white space at the ends. Then call `fill-region'. The argument JUSTIFY is the same as in `fill-region' and a prefix behaves the same way as there." (interactive (progn (barf-if-buffer-read-only) (list (if current-prefix-arg 'full)))) (let* ((ovl (mumamo-get-chunk-save-buffer-state (point))) (major (mumamo-chunk-major-mode ovl))) ;; Fix-me: There must be some bug that makes it necessary to ;; always change mode when fill-paragraph-function is ;; c-fill-paragraph. ;;(unless (mumamo-fun-eq major major-mode) (mumamo-set-major major ovl)) (mumamo-set-major major ovl) (save-restriction (mumamo-update-obscure ovl (point)) (let* ((syn-min-max (mumamo-chunk-syntax-min-max ovl nil)) (syn-min (car syn-min-max)) (syn-max (cdr syn-min-max)) use-min (here (point-marker))) (goto-char syn-min) (skip-syntax-forward " ") ;; Move back over chars that have whitespace syntax but have the p flag. (backward-prefix-chars) (setq use-min (point)) (goto-char syn-max) (skip-syntax-backward " ") (fill-region use-min (point) justify))))) ;; (defvar mumamo-dont-widen) ;; (defadvice widen (around ;; mumamo-ad-widen ;; activate ;; disable ;; compile ;; ) ;; "Make `widen' do nothing. ;; This is for `mumamo-fill-paragraph-function' and is necessary ;; when `c-fill-paragraph' is the real function used." ;; (unless (and (boundp 'mumamo-dont-widen) ;; mumamo-dont-widen) ;; ad-do-it)) (defadvice flymake-display-warning (around mumamo-ad-flymake-display-warning activate compile) "Display flymake warnings in the usual Emacs way." (let ((msg (ad-get-arg 0))) ;; Fix-me: Can't get backtrace here. Report it. ;;(setq msg (format (concat msg "\n%S" (with-output-to-string (backtrace))))) (lwarn '(flymake) :error msg))) ;;(lwarn '(flymake) :error "the warning") (defun mumamo-forward-chunk () "Move forward to next chunk." (interactive) (let* ((chunk (mumamo-get-chunk-save-buffer-state (point))) (end-pos (overlay-end chunk))) (goto-char (min end-pos (point-max))))) (defun mumamo-backward-chunk () "Move backward to previous chunk." (interactive) (let* ((chunk (mumamo-get-chunk-save-buffer-state (point))) (start-pos (overlay-start chunk))) (goto-char (max (1- start-pos) (point-min))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Spell checking (defun mumamo-flyspell-verify () "Function used for `flyspell-generic-check-word-predicate'." (let* ((chunk (when mumamo-multi-major-mode (mumamo-find-chunks (point) "mumamo-lyspell-verify"))) (chunk-major (when chunk (mumamo-chunk-major-mode chunk))) (mode-predicate (when chunk-major (let ((predicate (get chunk-major 'flyspell-mode-predicate))) (if predicate predicate (if (mumamo-derived-from-mode chunk-major 'text-mode) nil 'flyspell-generic-progmode-verify))))) ) (if mode-predicate ;; Fix-me: (run-hooks 'flyspell-prog-mode-hook) (funcall mode-predicate) t))) ;; (featurep 'cc-engine) (eval-after-load 'cc-engine (progn ;; From Alan's mail 2009-12-03: C Mode: acceleration in brace ;; deserts. ;; Fix-me: Should they be here, or...? (put 'c-state-cache 'permanent-local t) (put 'c-state-cache-good-pos 'permanent-local t) (put 'c-state-nonlit-pos-cache 'permanent-local t) (put 'c-state-nonlit-pos-cache-limit 'permanent-local t) (put 'c-state-brace-pair-desert 'permanent-local t) (put 'c-state-point-min 'permanent-local t) (put 'c-state-point-min-lit-type 'permanent-local t) (put 'c-state-point-min-lit-start 'permanent-local t) (put 'c-state-min-scan-pos 'permanent-local t) (put 'c-state-old-cpp-beg 'permanent-local t) (put 'c-state-old-cpp-end 'permanent-local t) )) ;; Fix-me: Seems perhaps like c-state-point-min-lit-start is reset in ;; c-state-mark-point-min-literal because c-state-literal-at returns ;; nil. (Or is (car lit) nil?) (defvar mumamo-c-state-cache-init nil) (make-variable-buffer-local 'mumamo-c-state-cache-init) (put 'mumamo-c-state-cache-init 'permanent-local t) (defun mumamo-c-state-cache-init () (unless mumamo-c-state-cache-init ;;(msgtrc "c-state-cache-init running") (setq mumamo-c-state-cache-init t) (setq c-state-cache (or c-state-cache nil)) (put 'c-state-cache 'permanent-local t) (setq c-state-cache-good-pos (or c-state-cache-good-pos 1)) (put 'c-state-cache-good-pos 'permanent-local t) (setq c-state-nonlit-pos-cache (or c-state-nonlit-pos-cache nil)) (put 'c-state-nonlit-pos-cache 'permanent-local t) (setq c-state-nonlit-pos-cache-limit (or c-state-nonlit-pos-cache-limit 1)) (put 'c-state-nonlit-pos-cache-limit 'permanent-local t) (setq c-state-brace-pair-desert (or c-state-brace-pair-desert nil)) (put 'c-state-brace-pair-desert 'permanent-local t) (setq c-state-point-min (or c-state-point-min 1)) (put 'c-state-point-min 'permanent-local t) (setq c-state-point-min-lit-type (or c-state-point-min-lit-type nil)) (put 'c-state-point-min-lit-type 'permanent-local t) (setq c-state-point-min-lit-start (or c-state-point-min-lit-start nil)) (put 'c-state-point-min-lit-start 'permanent-local t) (setq c-state-min-scan-pos (or c-state-min-scan-pos 1)) (put 'c-state-min-scan-pos 'permanent-local t) (setq c-state-old-cpp-beg (or c-state-old-cpp-beg nil)) (put 'c-state-old-cpp-beg 'permanent-local t) (setq c-state-old-cpp-end (or c-state-old-cpp-end nil)) (put 'c-state-old-cpp-end 'permanent-local t) (c-state-mark-point-min-literal))) (defadvice c-state-cache-init (around mumamo-ad-c-state-cache-init activate compile ) (if (not mumamo-multi-major-mode) ad-do-it (mumamo-c-state-cache-init))) ;; Fix-me: Have to add per chunk local majors for this one. (defun mumamo-c-state-literal-at (here) ;; If position HERE is inside a literal, return (START . END), the ;; boundaries of the literal (which may be outside the accessible bit of the ;; buffer). Otherwise, return nil. ;; ;; This function is almost the same as `c-literal-limits'. It differs in ;; that it is a lower level function, and that it rigourously follows the ;; syntax from BOB, whereas `c-literal-limits' uses a "local" safe position. (let* ((is-here (point)) (s (syntax-ppss here)) (ret (when (or (nth 3 s) (nth 4 s)) ; in a string or comment (parse-partial-sexp (point) (point-max) nil ; TARGETDEPTH nil ; STOPBEFORE s ; OLDSTATE 'syntax-table) ; stop at end of literal (cons (nth 8 s) (point))))) (goto-char is-here) ret)) ;; (save-restriction ;; (widen) ;; (let* ((chunk (mumamo-find-chunks (point) "mumamo-c-state-literal-at")) ;; (syntax-min-max (mumamo-chunk-syntax-min-max chunk t))) ;; (narrow-to-region (car syntax-min-max) (cdr syntax-min-max))) ;; (save-excursion ;; (let ((c c-state-nonlit-pos-cache) ;; pos npos lit) ;; ;; Trim the cache to take account of buffer changes. ;; (while (and c (> (car c) c-state-nonlit-pos-cache-limit)) ;; (setq c (cdr c))) ;; (setq c-state-nonlit-pos-cache c) ;; (while (and c (> (car c) here)) ;; (setq c (cdr c))) ;; (setq pos (or (car c) (point-min))) ;; (while (<= (setq npos (+ pos c-state-nonlit-pos-interval)) ;; here) ;; (setq lit (c-state-pp-to-literal pos npos)) ;; (setq pos (or (cdr lit) npos)) ; end of literal containing npos. ;; (setq c-state-nonlit-pos-cache (cons pos c-state-nonlit-pos-cache))) ;; (if (> pos c-state-nonlit-pos-cache-limit) ;; (setq c-state-nonlit-pos-cache-limit pos)) ;; (if (< pos here) ;; (setq lit (c-state-pp-to-literal pos here))) ;; lit)))) (defadvice c-state-literal-at (around mumamo-ad-c-state-state-literal-at activate compile ) (if (not mumamo-multi-major-mode) ad-do-it (mumamo-c-state-literal-at (ad-get-arg 0)))) (defun mumamo-c-state-get-min-scan-pos () ;; Return the lowest valid scanning pos. This will be the end of the ;; literal enclosing point-min, or point-min itself. (save-restriction (save-excursion (widen) (mumamo-narrow-to-chunk-inner) (or (and c-state-min-scan-pos (>= c-state-min-scan-pos (point-min)) c-state-min-scan-pos) (if (not c-state-point-min-lit-start) (goto-char (point-min)) (goto-char c-state-point-min-lit-start) (if (eq c-state-point-min-lit-type 'string) (forward-sexp) (forward-comment 1))) (setq c-state-min-scan-pos (point)))))) (defadvice c-state-get-min-scan-pos (around mumamo-ad-c-state-get-min-scan-pos-at activate compile ) (if (not mumamo-multi-major-mode) ad-do-it (setq ad-return-value (mumamo-c-state-get-min-scan-pos)))) (eval-after-load 'rng-match ;;; (defun rng-match-init-buffer () ;;; (make-local-variable 'rng-compile-table) ;;; (make-local-variable 'rng-ipattern-table) ;;; (make-local-variable 'rng-last-ipattern-index)) (progn (put 'rng-compile-table 'permanent-local t) (put 'rng-ipattern-table 'permanent-local t) (put 'rng-last-ipattern-index 'permanent-local t) )) (eval-after-load 'flyspell (progn (put 'flyspell-mode 'permanent-local t) (put 'flyspell-generic-check-word-predicate 'permanent-local t) (put 'flyspell-casechars-cache 'permanent-local t) (put 'flyspell-ispell-casechars-cache 'permanent-local t) (put 'flyspell-not-casechars-cache 'permanent-local t) (put 'flyspell-ispell-not-casechars-cache 'permanent-local t) (put 'flyspell-auto-correct-pos 'permanent-local t) (put 'flyspell-auto-correct-region 'permanent-local t) (put 'flyspell-auto-correct-ring 'permanent-local t) (put 'flyspell-auto-correct-word 'permanent-local t) (put 'flyspell-consider-dash-as-word-delimiter-flag 'permanent-local t) (put 'flyspell-dash-dictionary 'permanent-local t) (put 'flyspell-dash-local-dictionary 'permanent-local t) (put 'flyspell-word-cache-start 'permanent-local t) (put 'flyspell-word-cache-end 'permanent-local t) (put 'flyspell-word-cache-word 'permanent-local t) (put 'flyspell-word-cache-result 'permanent-local t) (put 'flyspell-word-cache-start 'permanent-local t) (put 'flyspell-kill-ispell-hook 'permanent-local-hook t) (put 'flyspell-post-command-hook 'permanent-local-hook t) (put 'flyspell-pre-command-hook 'permanent-local-hook t) (put 'flyspell-after-change-function 'permanent-local-hook t) (put 'flyspell-hack-local-variables-hook 'permanent-local-hook t) (put 'flyspell-auto-correct-previous-hook 'permanent-local-hook t) (when mumamo-multi-major-mode (when (featurep 'flyspell) (setq flyspell-generic-check-word-predicate 'mumamo-flyspell-verify))) )) (defun flyspell-mumamo-mode () "Turn on function `flyspell-mode' for multi major modes." (interactive) (require 'flyspell) (setq flyspell-generic-check-word-predicate 'mumamo-flyspell-verify) (flyspell-mode 1) ;;(run-hooks 'flyspell-prog-mode-hook) ) (eval-after-load 'sgml-mode (progn (put 'sgml-tag-face-alist 'permanent-local t) (put 'sgml-display-text 'permanent-local t) (put 'sgml-tag-alist 'permanent-local t) (put 'sgml-face-tag-alist 'permanent-local t) (put 'sgml-tag-help 'permanent-local t) )) (eval-after-load 'hl-line (progn (put 'hl-line-overlay 'permanent-local t) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; New versions of syntax-ppss functions, temporary written as defadvice. (defadvice syntax-ppss-flush-cache (around mumamo-ad-syntax-ppss-flush-cache activate compile ) "Support for mumamo. See the defadvice for `syntax-ppss' for an explanation." (if (not mumamo-multi-major-mode) ad-do-it (let ((pos (ad-get-arg 0))) (let* ((chunk-at-pos (when (and (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode) (mumamo-find-chunks-1 pos "syntax-ppss-flush-cache")))) (if chunk-at-pos (let* ((syntax-ppss-last (overlay-get chunk-at-pos 'syntax-ppss-last)) (syntax-ppss-cache (overlay-get chunk-at-pos 'syntax-ppss-cache))) ;;(setq ad-return-value ad-do-it) ad-do-it (overlay-put chunk-at-pos 'syntax-ppss-last syntax-ppss-last) (overlay-put chunk-at-pos 'syntax-ppss-cache syntax-ppss-cache)) ;;(setq ad-return-value ad-do-it) ad-do-it ))))) (defvar mumamo-syntax-chunk-at-pos nil "Internal use.") (make-variable-buffer-local 'mumamo-syntax-chunk-at-pos) ;; Fix-me: Is this really needed? ;; See http://lists.gnu.org/archive/html/emacs-devel/2008-04/msg00374.html (defadvice syntax-ppss-stats (around mumamo-ad-syntax-ppss-stats activate compile ) "Support for mumamo. See the defadvice for `syntax-ppss' for an explanation." (if mumamo-syntax-chunk-at-pos (let* ((syntax-ppss-stats (overlay-get mumamo-syntax-chunk-at-pos 'syntax-ppss-stats))) ad-do-it (overlay-put mumamo-syntax-chunk-at-pos 'syntax-ppss-stats syntax-ppss-stats)) ad-do-it)) (defvar mumamo-syntax-ppss-major nil) ;; FIX-ME: There is a problem with " in xhtml files, especially after ;; syntax="...". Looks like it is the " entry in ;; `sgml-font-lock-syntactic-keywords' that is jumping in! Dumping ;; things in `font-lock-apply-syntactic-highlight' seems to show that. ;; ;; (I have put in some dump code in my patched version of ;; Emacs+EmacsW32 there for that. This is commented out by default ;; and it will only work for the file nxhtml-changes.html which is big ;; enough for the problem to occur. It happens at point 1109.) ;; ;; It is this piece of code where the problem arise: ;; ;; (if (prog1 ;; (zerop (car (syntax-ppss (match-beginning 0)))) ;; (goto-char (match-end 0))) ;; .) ;; ;; ;; It comes from `sgml-font-lock-syntactic-keywords' in sgml-mode.el ;; and is supposed to protect from " that is not inside a tag. ;; However in this case for the second " in syntax="..." `syntax-ppss' ;; returns 0 as the first element in its return value. That happen ;; even though `major-mode' is correctly `html-mode'. It leads to ;; that the property 'syntax with the value (1) is added to the " ;; after the css-mode chunk in syntax="...". The problem persists ;; even if the chunk has `fundamental-mode' instead of `css-mode'. ;; ;; Bypassing the cache for `syntax-pss' by calling ;; `parse-partial-sexp' directly instead of doing ad-do-it (see ;; by-pass-chache in the code below) solves the problem for now. It ;; does not feel like the right solution however. ;; ;; One way of temporary solving the problem is perhaps to modify ;; `mumamo-chunk-attr=' to make "" borders, but I am not sure that it ;; works and it is the wrong solution. (defadvice syntax-ppss (around mumamo-ad-syntax-ppss activate compile ) "Support for mumamo chunks. For each chunk store as properties of the chunk the parse state that is normally hold in `syntax-ppss-last' and `syntax-ppss-cache'. Compute the beginning parse state for a chunk this way: - If the chunk major mode is the same as the main major mode for the multi major mode then parse from the beginning of the file to the beginning of the chunk using the main major mode. While doing that jump over chunks that do not belong to the main major mode and cache the state at the end and beginning of the the main major mode chunks. FIX-ME: implement above. Solution?: (parse-partial-sexp syntax-min (1+ syntax-max) nil nil state-at-syntax-min) Put this at next chunk's beginning. - Otherwise set the state at the beginning of the chunk to nil. Do here also other necessary adjustments for this." (if (not mumamo-multi-major-mode) ad-do-it (let ((pos (ad-get-arg 0))) (unless pos (setq pos (point))) (let* ((chunk-at-pos (when (and (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode) (mumamo-find-chunks-1 pos "syntax-ppss"))) (dump2 (and (boundp 'dump-quote-hunt) dump-quote-hunt (boundp 'start) ;;(= 1109 start) ))) ;;(setq dump2 t) (setq mumamo-syntax-chunk-at-pos chunk-at-pos) (when dump2 (msgtrc "\npos=%s point-min=%s mumamo-syntax-ppss.chunk-at-pos=%s" pos (point-min) chunk-at-pos)) (if chunk-at-pos (let* ((chunk-syntax-min-max (mumamo-chunk-syntax-min-max chunk-at-pos t)) (chunk-syntax-min (car chunk-syntax-min-max)) (chunk-major (mumamo-chunk-major-mode chunk-at-pos)) (syntax-ppss-last (overlay-get chunk-at-pos 'syntax-ppss-last)) (syntax-ppss-cache (overlay-get chunk-at-pos 'syntax-ppss-cache)) (syntax-ppss-last-min (overlay-get chunk-at-pos 'syntax-ppss-last-min)) (syntax-ppss-cache-min (list syntax-ppss-last-min)) ;; This must be fetch the same way as in syntax-ppss: (syntax-begin-function (overlay-get chunk-at-pos 'syntax-begin-function)) (syntax-ppss-max-span (if chunk-syntax-min (/ (- pos chunk-syntax-min -2) 2) syntax-ppss-max-span)) (syntax-ppss-stats (let ((stats (overlay-get chunk-at-pos 'syntax-ppss-stats))) (if stats stats (default-value 'syntax-ppss-stats)))) (last-min-pos (or (car syntax-ppss-last-min) 1)) ) ;; If chunk has moved the cached values are invalid. (unless (= chunk-syntax-min last-min-pos) (setq syntax-ppss-last nil) (setq syntax-ppss-last-min nil) (setq syntax-ppss-cache nil) (setq syntax-ppss-cache-min nil) (setq syntax-ppss-stats (default-value 'syntax-ppss-stats))) (when dump2 (msgtrc " get syntax-ppss-last-min=%s len=%s chunk=%s" syntax-ppss-last-min (length syntax-ppss-last-min) chunk-at-pos) (msgtrc " prop syntax-ppss-last-min=%s" (overlay-properties chunk-at-pos)) (msgtrc " chunk-major=%s, %s, syntax-min=%s\n last-min=%s" chunk-major major-mode chunk-syntax-min syntax-ppss-last-min)) ;;(setq dump2 nil) (when syntax-ppss-last-min (unless (car syntax-ppss-last-min) ;;(msgtrc "fix-me: emacs bug workaround, setting car of syntax-ppss-last-min") ;;(setcar syntax-ppss-last-min (1- chunk-syntax-min)) ;;(msgtrc "fix-me: emacs bug workaround, need new syntax-ppss-last-min because car is nil") (setq syntax-ppss-last-min nil) )) (unless syntax-ppss-last-min (setq syntax-ppss-last nil) (save-restriction (widen) (let* ((min-pos chunk-syntax-min) (chunk-sub-major (mumamo-chunk-major-mode chunk-at-pos)) (main-major (mumamo-main-major-mode)) (is-main-mode-chunk (mumamo-fun-eq chunk-sub-major main-major))) (when dump2 (msgtrc " min-pos=%s, is-main-mode-chunk=%s" min-pos is-main-mode-chunk)) ;; Looks like assert can not be used here for some reason??? ;;(assert (and min-pos) t) (unless (and min-pos) (error "defadvice syntax-ppss: (and min-pos=%s)" min-pos)) (setq syntax-ppss-last-min (cons min-pos ;;(1- min-pos) (if nil ;is-main-mode-chunk ;; Fix-me: previous chunks as a ;; cache? The problem is updating ;; this. Perhaps it is possible to ;; prune how far back to go by ;; going to the first chunk ;; backwards where ;; (pars-partial-sexp min max) is ;; "nil"? (mumamo-with-major-mode-fontification main-major `(parse-partial-sexp 1 ,min-pos nil nil nil nil)) (parse-partial-sexp 1 1)))) (setq syntax-ppss-cache-min (list syntax-ppss-last-min)) (when dump2 (msgtrc " put syntax-ppss-last-min=%s len=%s chunk=%s" syntax-ppss-last-min (length syntax-ppss-last-min) chunk-at-pos)) (when dump2 (msgtrc " prop syntax-ppss-last-min=%s" (overlay-properties chunk-at-pos))) (overlay-put chunk-at-pos 'syntax-ppss-last-min syntax-ppss-last-min) (let ((test-syntax-ppss-last-min (overlay-get chunk-at-pos 'syntax-ppss-last-min))) (when dump2 (msgtrc " test syntax-ppss-last-min=%s len=%s" test-syntax-ppss-last-min (length test-syntax-ppss-last-min))) (when dump2 (msgtrc " propt syntax-ppss-last-min=%s" (overlay-properties chunk-at-pos))) )))) (when dump2 (msgtrc " here 0, syntax-ppss-last=%s" syntax-ppss-last)) (unless syntax-ppss-last (setq syntax-ppss-last syntax-ppss-last-min) (setq syntax-ppss-cache syntax-ppss-cache-min)) ;;(syntax-ppss pos) (when dump2 (msgtrc " at 1, syntax-ppss-last=%s" syntax-ppss-last)) (when dump2 (msgtrc " at 1, syntax-ppss-cache=%s" syntax-ppss-cache)) (let (ret-val (by-pass-cache t) (dump2 dump2)) (if (not by-pass-cache) (progn (when dump2 (let ((old-ppss (cdr syntax-ppss-last)) (old-pos (car syntax-ppss-last))) ;;(assert (and old-pos pos) t) (unless (and old-pos pos) (error "defadvice syntax-ppss: (and old-pos=%s pos=%s)" old-pos pos)) (msgtrc "parse-partial-sexp=>%s" (parse-partial-sexp old-pos pos nil nil old-ppss)))) (let (dump2) (setq ret-val ad-do-it))) (let ((old-ppss (cdr syntax-ppss-last)) (old-pos (car syntax-ppss-last))) (when dump2 (msgtrc "Xparse-partial-sexp %s %s nil nil %s" old-pos pos old-ppss) (let (dump2) (msgtrc "ad-do-it=>%s" ad-do-it))) (save-restriction (widen) ;;(assert (and old-pos pos) t) (unless (and old-pos pos) (error "defadvice syntax-ppss 2 (and old-pos=%s pos=%s)" old-pos pos)) (when dump2 (msgtrc "parse-partial-sexp %s %s nil nil %s" old-pos pos old-ppss)) (setq ret-val (parse-partial-sexp old-pos pos nil nil old-ppss))))) (when dump2 (msgtrc " ==>ret-val=%s" ret-val)) ;;(mumamo-backtrace "syntax-ppss") (setq ad-return-value ret-val)) (overlay-put chunk-at-pos 'syntax-ppss-last syntax-ppss-last) (overlay-put chunk-at-pos 'syntax-ppss-cache syntax-ppss-cache) (overlay-put chunk-at-pos 'syntax-ppss-stats syntax-ppss-stats) ) ad-do-it))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; rng-valid.el support ;; Fix-me: The solution in this defadvice is temporary. The defadvice ;; for rng-do-some-validation should be fixed instead. ;; (ad-disable-advice 'rng-mark-error 'around 'mumamo-ad-rng-mark-error) ;; (ad-ensable-advice 'rng-mark-error 'around 'mumamo-ad-rng-mark-error) (defadvice rng-mark-error (around mumamo-ad-rng-mark-error activate compile) "Adjust range for error to chunks." (if (not mumamo-multi-major-mode) ad-do-it (let* ((beg (ad-get-arg 1)) (end (ad-get-arg 2)) (xml-parts nil) (chunk (mumamo-find-chunks beg "rng-mark-error"))) (if (not chunk) ad-do-it (when (and (not (overlay-get chunk 'mumamo-region)) (mumamo-valid-nxml-chunk chunk)) ;; rng-error (let ((part-beg (max (overlay-start chunk) beg)) (part-end (min (overlay-end chunk) end))) (when (< part-beg part-end) (ad-set-arg 1 part-beg) (ad-set-arg 2 part-end) ad-do-it))))))) (defadvice rng-do-some-validation-1 (around mumamo-ad-rng-do-some-validation-1 activate compile) "Adjust validation to chunks." (if (not mumamo-multi-major-mode) ad-do-it (let (major-mode-chunk (point-max (1+ (buffer-size))) ;(save-restriction (widen) (point-max))) end-major-mode-chunk (limit (+ rng-validate-up-to-date-end rng-validate-chunk-size)) (remove-start rng-validate-up-to-date-end) (next-cache-point (+ (point) rng-state-cache-distance)) (continue t) (xmltok-dtd rng-dtd) have-remaining-chars xmltok-type xmltok-start xmltok-name-colon xmltok-name-end xmltok-replacement xmltok-attributes xmltok-namespace-attributes xmltok-dependent-regions xmltok-errors (while-n1 0) (while-n2 0) (old-point -1) ) ;;(msgtrc "> > > > > enter rng-do-some-validation-1, continue-p-function=%s" continue-p-function) (setq have-remaining-chars (< (point) point-max)) (when (and continue (= (point) 1)) (let ((regions (xmltok-forward-prolog))) (rng-clear-overlays 1 (point)) (while regions (when (eq (aref (car regions) 0) 'encoding-name) (rng-process-encoding-name (aref (car regions) 1) (aref (car regions) 2))) (setq regions (cdr regions)))) (unless (equal rng-dtd xmltok-dtd) (rng-clear-conditional-region)) (setq rng-dtd xmltok-dtd)) (setq while-n1 0) (while (and (mumamo-while 2000 'while-n1 "continue") (/= old-point (point)) continue) (setq old-point (point)) ;; If mumamo (or something similar) is used then jump over parts ;; that can not be parsed by nxml-mode. (when (and rng-get-major-mode-chunk-function rng-valid-nxml-major-mode-chunk-function rng-end-major-mode-chunk-function) (let ((here (point)) next-non-space-pos) (skip-chars-forward " \t\r\n") (setq next-non-space-pos (point)) (goto-char here) (unless (and end-major-mode-chunk ;; Remaining chars in this chunk? (< next-non-space-pos end-major-mode-chunk)) (setq end-major-mode-chunk nil) (setq major-mode-chunk (funcall rng-get-major-mode-chunk-function next-non-space-pos "rng-do-some-validation-1 A")) (setq while-n2 0) (while (and (mumamo-while 500 'while-n2 "major-mode-chunk") major-mode-chunk (not (funcall rng-valid-nxml-major-mode-chunk-function major-mode-chunk)) (< next-non-space-pos (point-max))) ;;(msgtrc "next-non-space-pos=%s, cb=%s" next-non-space-pos (current-buffer)) (let ((end-pos (funcall rng-end-major-mode-chunk-function major-mode-chunk))) ;; fix-me: The problem here is that ;; mumamo-find-chunks can return a 0-length chunk. ;;(goto-char (+ end-pos 0)) (goto-char (+ end-pos (if (= end-pos (point)) 1 0))) (setq major-mode-chunk (funcall rng-get-major-mode-chunk-function (point) "rng-do-some-validation-1 B")) ;;(message "---> here 3, point=%s, ep=%s, mm-chunk=%s" (point) end-pos major-mode-chunk) ) (setq next-non-space-pos (point)))) ;; Stop parsing if we do not have a chunk here yet. ;;(message "major-mode-chunk=%s" major-mode-chunk) ;;(message "rng-valid-nxml-major-mode-chunk-function=%s" rng-valid-nxml-major-mode-chunk-function) (setq continue (and major-mode-chunk (funcall rng-valid-nxml-major-mode-chunk-function major-mode-chunk))) ;;(unless continue (message "continue=nil, no major-mode-chunk")) (when continue ;;(message " continue=t") (setq end-major-mode-chunk (funcall rng-end-major-mode-chunk-function major-mode-chunk))))) (when continue ;; Narrow since rng-forward will continue into next chunk ;; even if limit is at chunk end. (if t (progn ;;(message "before rng-forward, point=%s" (point)) (setq have-remaining-chars (rng-forward end-major-mode-chunk)) ;;(message "after rng-forward, point=%s" (point)) ) ;; Fix-me: Validation does not work when narrowing because ;; some state variables values seems to be lost. Probably ;; looking at `rng-validate-prepare' will tell what to do. (save-restriction (when (and end-major-mode-chunk (< (point-min) end-major-mode-chunk)) (narrow-to-region (point-min) end-major-mode-chunk)) (setq have-remaining-chars (rng-forward end-major-mode-chunk))) (unless (> end-major-mode-chunk (point)) ;;(setq have-remaining-chars t) (goto-char end-major-mode-chunk)) ) ;;(message "end-major-mode-chunk=%s, rng-validate-up-to-date-end=%s" end-major-mode-chunk rng-validate-up-to-date-end) (setq have-remaining-chars (< (point) point-max)) ;;(unless have-remaining-chars (message "*** here have-remaining-chars=%s, p=%s/%s" have-remaining-chars (point) point-max)) (let ((pos (point))) (when end-major-mode-chunk ;; Fix-me: Seems like we need a new initialization (or why ;; do we otherwise hang without this?) (and (> limit end-major-mode-chunk) (setq limit end-major-mode-chunk))) (setq continue (and have-remaining-chars continue (or (< pos limit) (and continue-p-function (funcall continue-p-function) (setq limit (+ limit rng-validate-chunk-size)) t)))) ;;(unless continue (message "continue=nil, why?: %s<%s, %s" pos limit (when continue-p-function (funcall continue-p-function)))) (cond ((and rng-conditional-up-to-date-start ;; > because we are getting the state from (1- pos) (> pos rng-conditional-up-to-date-start) (< pos rng-conditional-up-to-date-end) (rng-state-matches-current (get-text-property (1- pos) 'rng-state))) (when (< remove-start (1- pos)) (rng-clear-cached-state remove-start (1- pos))) ;; sync up with cached validation state (setq continue nil) ;; do this before settting rng-validate-up-to-date-end ;; in case we get a quit (rng-mark-xmltok-errors) (rng-mark-xmltok-dependent-regions) (setq rng-validate-up-to-date-end (marker-position rng-conditional-up-to-date-end)) (rng-clear-conditional-region) (setq have-remaining-chars (< rng-validate-up-to-date-end point-max)) ;;(unless have-remaining-chars (message "have-remaining-chars=%s rng-validate-up-to-date-end=%s, point-max=%s" have-remaining-chars rng-validate-up-to-date-end point-max)) ) ((or (>= pos next-cache-point) (not continue)) (setq next-cache-point (+ pos rng-state-cache-distance)) (rng-clear-cached-state remove-start pos) (when have-remaining-chars ;;(message "rng-cach-state (1- %s)" pos) (rng-cache-state (1- pos))) (setq remove-start pos) (unless continue ;; if we have just blank chars skip to the end (when have-remaining-chars (skip-chars-forward " \t\r\n") (when (= (point) point-max) (rng-clear-overlays pos (point)) (rng-clear-cached-state pos (point)) (setq have-remaining-chars nil) ;;(message "have-remaining-chars => nil, cause (point) = point-max") (setq pos (point)))) (when (not have-remaining-chars) (rng-process-end-document)) (rng-mark-xmltok-errors) (rng-mark-xmltok-dependent-regions) (setq rng-validate-up-to-date-end pos) (when rng-conditional-up-to-date-end (cond ((<= rng-conditional-up-to-date-end pos) (rng-clear-conditional-region)) ((< rng-conditional-up-to-date-start pos) (set-marker rng-conditional-up-to-date-start pos)))))))))) ;;(message "--- exit rng-do-some-validation-1, have-remaining-chars=%s" have-remaining-chars) (setq have-remaining-chars (< (point) point-max)) (setq ad-return-value have-remaining-chars)))) (defadvice rng-after-change-function (around mumamo-ad-rng-after-change-function activate compile) (when rng-validate-up-to-date-end ad-do-it)) (defadvice rng-validate-while-idle (around mumamo-ad-rng-validate-while-idle activate compile) (if (not (buffer-live-p buffer)) (rng-kill-timers) ad-do-it)) (defadvice rng-validate-quick-while-idle (around mumamo-ad-rng-validate-quick-while-idle activate compile) (if (not (buffer-live-p buffer)) (rng-kill-timers) ad-do-it)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; xmltok.el ;; (ad-disable-advice 'xmltok-add-error 'around 'mumamo-ad-xmltok-add-error) ;; (ad-ensable-advice 'xmltok-add-error 'around 'mumamo-ad-xmltok-add-error) (defadvice xmltok-add-error (around mumamo-ad-xmltok-add-error activate compile ) "Prevent rng validation errors in non-xml chunks. This advice only prevents adding nxml/rng-valid errors in non-xml chunks. Doing more seems like a very big job - unless Emacs gets a narrow-to-multiple-regions function!" (if (not mumamo-multi-major-mode) ad-do-it ;;(error "xmltok-add-error: %S" (with-output-to-string (backtrace))) (when (let* ((start (or start xmltok-start)) (end (or end (point))) (chunk (mumamo-find-chunks (if start start end) "xmltok-add-error")) ) (or (not chunk) (and (not (overlay-get chunk 'mumamo-region)) (mumamo-valid-nxml-chunk chunk)))) (setq xmltok-errors (cons (xmltok-make-error message (or start xmltok-start) (or end (point))) xmltok-errors))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Maybe activate advices ;; Fix-me: This assumes there are no other advices on these functions. (if t (progn ;; (ad-activate 'syntax-ppss) ;; (ad-activate 'syntax-ppss-flush-cache) ;; (ad-activate 'syntax-ppss-stats) ;; (ad-activate 'rng-do-some-validation-1) ;; (ad-activate 'rng-mark-error) ;; (ad-activate 'xmltok-add-error) (ad-enable-advice 'syntax-ppss 'around 'mumamo-ad-syntax-ppss) (ad-enable-advice 'syntax-ppss-flush-cache 'around 'mumamo-ad-syntax-ppss-flush-cache) (ad-enable-advice 'syntax-ppss-stats 'around 'mumamo-ad-syntax-ppss-stats) (ad-enable-advice 'rng-do-some-validation-1 'around 'mumamo-ad-rng-do-some-validation-1) (ad-enable-advice 'rng-mark-error 'around 'mumamo-ad-rng-mark-error) (ad-enable-advice 'rng-after-change-function 'around 'mumamo-ad-rng-after-change-function) (ad-enable-advice 'rng-validate-while-idle 'around 'mumamo-ad-rng-validate-while-idle) (ad-enable-advice 'rng-validate-quick-while-idle 'around 'mumamo-ad-rng-validate-quick-while-idle) (ad-enable-advice 'xmltok-add-error 'around 'mumamo-ad-xmltok-add-error) ) ;; (ad-deactivate 'syntax-ppss) ;; (ad-deactivate 'syntax-ppss-flush-cache) ;; (ad-deactivate 'syntax-ppss-stats) ;; (ad-deactivate 'rng-do-some-validation-1) ;; (ad-deactivate 'rng-mark-error) ;; (ad-deactivate 'xmltok-add-error) (ad-disable-advice 'syntax-ppss 'around 'mumamo-ad-syntax-ppss) (ad-disable-advice 'syntax-ppss-flush-cache 'around 'mumamo-ad-syntax-ppss-flush-cache) (ad-disable-advice 'syntax-ppss-stats 'around 'mumamo-ad-syntax-ppss-stats) (ad-disable-advice 'rng-do-some-validation-1 'around 'mumamo-ad-rng-do-some-validation-1) (ad-disable-advice 'rng-mark-error 'around 'mumamo-ad-rng-mark-error) (ad-disable-advice 'rng-after-change-function 'around 'mumamo-ad-rng-after-change-function) (ad-disable-advice 'rng-validate-while-idle 'around 'mumamo-ad-rng-validate-while-idle) (ad-disable-advice 'rng-validate-quick-while-idle 'around 'mumamo-ad-rng-validate-quick-while-idle) (ad-disable-advice 'xmltok-add-error 'around 'mumamo-ad-xmltok-add-error) ) (font-lock-add-keywords 'emacs-lisp-mode '(("\\" . font-lock-keyword-face))) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Simple defadvice to move into Emacs later (defun mumamo-ad-desktop-buffer-info (buffer) (set-buffer buffer) (list ;; base name of the buffer; replaces the buffer name if managed by uniquify (and (fboundp 'uniquify-buffer-base-name) (uniquify-buffer-base-name)) ;; basic information (desktop-file-name (buffer-file-name) desktop-dirname) (buffer-name) (if mumamo-multi-major-mode mumamo-multi-major-mode major-mode) ;; minor modes (let (ret) (mapc #'(lambda (minor-mode) (and (boundp minor-mode) (symbol-value minor-mode) (let* ((special (assq minor-mode desktop-minor-mode-table)) (value (cond (special (cadr special)) ((functionp minor-mode) minor-mode)))) (when value (add-to-list 'ret value))))) (mapcar #'car minor-mode-alist)) ret) ;; point and mark, and read-only status (point) (list (mark t) mark-active) buffer-read-only ;; auxiliary information (when (functionp desktop-save-buffer) (funcall desktop-save-buffer desktop-dirname)) ;; local variables (let ((locals desktop-locals-to-save) (loclist (buffer-local-variables)) (ll)) (while locals (let ((here (assq (car locals) loclist))) (if here (setq ll (cons here ll)) (when (member (car locals) loclist) (setq ll (cons (car locals) ll))))) (setq locals (cdr locals))) ll))) (defadvice desktop-buffer-info (around mumamo-ad-desktop-buffer-info activate compile) (setq ad-return-value (mumamo-ad-desktop-buffer-info (ad-get-arg 0)))) (defun mumamo-ad-set-auto-mode-0 (mode &optional keep-mode-if-same) "Apply MODE and return it. If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of any aliases and compared to current major mode. If they are the same, do nothing and return nil." (unless (and keep-mode-if-same (eq (indirect-function mode) (if mumamo-multi-major-mode (indirect-function mumamo-multi-major-mode) (indirect-function major-mode)))) (when mode (funcall mode) mode))) (defadvice set-auto-mode-0 (around mumamo-ad-set-auto-mode-0 activate compile) (setq ad-return-value (mumamo-ad-set-auto-mode-0 (ad-get-arg 0) (ad-get-arg 1) ))) (defvar mumamo-sgml-get-context-last-close nil "Last close tag start. Only used for outermost level.") (defun mumamo-sgml-get-context (&optional until) "Determine the context of the current position. By default, parse until we find a start-tag as the first thing on a line. If UNTIL is `empty', return even if the context is empty (i.e. we just skipped over some element and got to a beginning of line). The context is a list of tag-info structures. The last one is the tag immediately enclosing the current position. Point is assumed to be outside of any tag. If we discover that it's not the case, the first tag returned is the one inside which we are." (let ((here (point)) (stack nil) (ignore nil) (context nil) tag-info last-close) ;; CONTEXT keeps track of the tag-stack ;; STACK keeps track of the end tags we've seen (and thus the start-tags ;; we'll have to ignore) when skipping over matching open..close pairs. ;; IGNORE is a list of tags that can be ignored because they have been ;; closed implicitly. ;; LAST-CLOSE is last close tag that can be useful for indentation ;; when on outermost level. (skip-chars-backward " \t\n") ; Make sure we're not at indentation. (while (and (not (eq until 'now)) (or stack (not (if until (eq until 'empty) context)) (not (sgml-at-indentation-p)) (and context (/= (point) (sgml-tag-start (car context))) (sgml-unclosed-tag-p (sgml-tag-name (car context))))) (setq tag-info (ignore-errors (sgml-parse-tag-backward)))) ;; This tag may enclose things we thought were tags. If so, ;; discard them. (while (and context (> (sgml-tag-end tag-info) (sgml-tag-end (car context)))) (setq context (cdr context))) (cond ((> (sgml-tag-end tag-info) here) ;; Oops!! Looks like we were not outside of any tag, after all. (push tag-info context) (setq until 'now)) ;; start-tag ((eq (sgml-tag-type tag-info) 'open) (when (and (null stack) last-close) (setq last-close 'no-use)) (cond ((null stack) (if (assoc-string (sgml-tag-name tag-info) ignore t) ;; There was an implicit end-tag. nil (push tag-info context) ;; We're changing context so the tags implicitly closed inside ;; the previous context aren't implicitly closed here any more. ;; [ Well, actually it depends, but we don't have the info about ;; when it doesn't and when it does. --Stef ] (setq ignore nil))) ((eq t (compare-strings (sgml-tag-name tag-info) nil nil (car stack) nil nil t)) (setq stack (cdr stack))) (t ;; The open and close tags don't match. (if (not sgml-xml-mode) (unless (sgml-unclosed-tag-p (sgml-tag-name tag-info)) (message "Unclosed tag <%s>" (sgml-tag-name tag-info)) (let ((tmp stack)) ;; We could just assume that the tag is simply not closed ;; but it's a bad assumption when tags *are* closed but ;; not properly nested. (while (and (cdr tmp) (not (eq t (compare-strings (sgml-tag-name tag-info) nil nil (cadr tmp) nil nil t)))) (setq tmp (cdr tmp))) (if (cdr tmp) (setcdr tmp (cddr tmp))))) (message "Unmatched tags <%s> and " (sgml-tag-name tag-info) (pop stack))))) (if (and (null stack) (sgml-unclosed-tag-p (sgml-tag-name tag-info))) ;; This is a top-level open of an implicitly closed tag, so any ;; occurrence of such an open tag at the same level can be ignored ;; because it's been implicitly closed. (push (sgml-tag-name tag-info) ignore))) ;; end-tag ((eq (sgml-tag-type tag-info) 'close) (if (sgml-empty-tag-p (sgml-tag-name tag-info)) (message "Spurious : empty tag" (sgml-tag-name tag-info)) ;; Keep track of last close if context will return nil (when (and (not last-close) (null stack) (> here (point-at-eol)) (let ((here (point))) (goto-char (sgml-tag-start tag-info)) (skip-chars-backward " \t") (prog1 (bolp) (goto-char here)))) (setq last-close tag-info)) (push (sgml-tag-name tag-info) stack))) )) ;; return context (setq mumamo-sgml-get-context-last-close (when (and last-close (not (eq last-close 'no-use))) (sgml-tag-start last-close))) context)) (defadvice sgml-get-context (around mumamo-ad-sgml-get-context activate compile) (setq ad-return-value (mumamo-sgml-get-context (ad-get-arg 0)))) (defun mumamo-sgml-calculate-indent (&optional lcon) "Calculate the column to which this line should be indented. LCON is the lexical context, if any." (unless lcon (setq lcon (sgml-lexical-context))) ;; Indent comment-start markers inside