We use an internal scripting language (let’s call it pkc), which allows for embedding C++ code. The C++ code segments are delimited by {{{ and }}} markers.
I create an emacs mode for this language, using Generic Mode and mmm-mode.
Here is what I got (stripped down non-essential parts for posting here):
(require 'generic-x)
(setq pkc-imenu-generic-expression
'(("macros" "^[ \t]*macro[ \n\t]+\\([a-zA-Z0-9_]+\\)" 1)
("functions" "function[ \n\t]+\\([a-zA-Z0-9_]+\\)" 1)
))
(require 'cc-mode) ;; for c++-mode
(require 'mmm-auto)
(setq mmm-global-mode 'maybe)
(define-generic-mode
'pkc-mode ;; name of the mode to create
'("//" ("/*" . "*/")) ;; comments are same C++ comments
'( ... ) ;; some keywords
'("\\.pkc$") ;; files for which to activate this mode
;; other functions to call
'((lambda ()
(mmm-mode 1)
(setq mmm-submode-decoration-level 2)
(setq imenu-generic-expression pkc-imenu-generic-expression)
(which-function-mode 1)
(c-initialize-cc-mode t)
(c-init-language-vars-for 'c++-mode)
(c-common-init 'c++-mode)
(c-update-modeline)
(message "pkc-mode[mmm] is on")))
"A mode for pkc source files" ;; doc string for this mode
)
(mmm-add-classes
'((embedded-c++
:submode c++-mode
:face mmm-default-submode-face
:front "{{{"
:front-offset -1
:back "}}}"
:back-offset 1)))
(mmm-add-mode-ext-class 'pkc-mode nil 'embedded-c++)
When I load the source file with embedded C++ code segments, they are highlighted somewhat less than what would have been if C++ mode was the major mode (that’s not my problem, however). When the cursor in C++ code, the modeline changes to pkc/l[C++/l] (as expected).
The problem is, whenever I press TAB to indent a line, I get the error Wrong type argument: stringp, nil. When I turned on debugger, this is the stack trace I see:
Debugger entered--Lisp error: (wrong-type-argument stringp nil)
c-syntactic-skip-backward(nil nil t)
c-looking-at-decl-block(nil t)
c-guess-basic-syntax()
c-indent-line()
#[nil \302>\203)\212\303 \210\304\305x\210\303 \210\306 )i\306 X\203\"\307 !\202'\212\307 !))\20 \207" [indent-line-function column (indent-relative indent-relative-maybe) beginning-of-line "\n " nil current-indentation indent-line-to] 2 1908700 nil]()
c-indent-command(nil)
c-indent-line-or-region(nil nil)
call-interactively(c-indent-line-or-region nil nil)
Looking at the definition of c-looking-at-decl-block and particularly the call to `c-syntactic-skip-backward, I find:
(c-syntactic-skip-backward c-block-prefix-charset limit t)
Examining the first argument c-block-prefix-charset reveals that its value is nil. Doing the same from a plain C++ buffer shows a non-nil value. So, I suspect that C++-mode requires some initialization that’s not being properly done.
So, my questions are:
- What am I missing in the C++-mode initialization section in my implementation?
- Am I using the right approach (combining
generic-xandmmm-mode)? Is there a better approach?
I also see another error (File mode specification error) when I load the file, but I suspect the problem is the same or something similar.
Thanks for your time.
(Rewritten after the discussion in the comments, for any later visitors).
The setup in the question is basically fine, but to work with current
c++-modeyou need to use an updated version ofmmm-mode. It’s available at the GitHub project page and also at Melpa.The relevant patches (one, two, three) added new entries to the value of
mmm-save-local-variables, whichmmm-modeuses to decide which local variables to save or restore when leaving or entering a submode region.From what I understand, a future update to
cc-modecan add more such vars, so the list may need to be updated from time to time.Extra tip: to have better indentation in the subregions, you may want to wrap the submode’s
indent-line-functionwith some code that will narrow the buffer before calling it (example here). Depending on the indentation function (and whether it callswiden), it may or may not help.