EMACS: How to write a syntax-coloring mode for an Algol-like language that uses keyword stropping - emacs

I'm trying to set up syntax colouring for Imp, an Algol-like language that uses keyword stropping, i.e. a keyword is the '%' character followed by alphabetics up to any non-alpha, such as %begin. I found that modify-syntax-entry was sufficient to colour Imp comments, but I had to use Hi-lock to colour keywords. What I'ld like help with is: how can I load the regexps for hi-lock from my ~/.emacs file when opening any file with a .imp extension, rather than having to explicitly save the rules in every individual .imp file? Here's what I have so far:
(defconst my-imp-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?' "\"" table)
(modify-syntax-entry ?\" "\"" table)
(modify-syntax-entry ?! "<" table)
(modify-syntax-entry ?\n ">" table)
(modify-syntax-entry ?{ "<" table)
(modify-syntax-entry ?} ">" table)
(modify-syntax-entry ?\( "()" table)
(modify-syntax-entry ?\) ")(" table)
table))
(define-derived-mode my-imp-mode prog-mode "Simple Imp Mode"
:syntax-table my-imp-mode-syntax-table
(font-lock-fontify-buffer))
(add-to-list 'auto-mode-alist '("\\.i\\'" . my-imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp\\'" . my-imp-mode))
(global-hi-lock-mode 1)
(setq hi-lock-file-patterns-policy (lambda (pattern) t))
(defface imp-keyword
'((t (:weight bold :foreground "cyan")))
"Face for IMP keywords"
:group 'hi-lock-faces)
(defface imp-constant
'((t (:foreground "green")))
"Face for IMP numeric constants"
:group 'hi-lock-faces)
;; I would like to load these patterns on opening a .imp file:
;; (("\\%[A-Za-z]*" (0 (quote imp-keyword) prepend)))
;; (("\\<[\\-+]*[0-9]*\\.?[0-9]+\\(\\|#[\\-+]?[0-9]+\\)?\\>" (0 (quote imp-constant) prepend)))
Here is an example of an Imp file:
! Hi-lock: (("\\%[A-Za-z]*" (0 (quote imp-keyword) prepend)))
! Hi-lock: (("\\<[\\-]?[0-9]*\\.?[0-9]+\\(\\#[\\-+]?[0-9]+\\)?\\>" (0 (quote imp-constant) prepend)))
%begin
! ackerman function - this is a whole-line comment
%integer x,y,j,k
%integerfn acker {short for ackerman - this is a bracketed comment by the way} (%integer m,n)
%if m = 0 %then %result = n+1
%if n = 0 %then %result = acker(m-1,1)
%result = acker(m-1, acker(m, n-1))
%end
prompt("Ackerman, First param (1..4)?"); read(x)
prompt(" Second param (1..7)?"); read(y)
write(acker(x,y), 4); newline
%endofprogram
You can see what the expected highlighting looks like by applying the elisp from above. (The details of the colouring are just a draft until I work out how to do it. I can tweak the colours etc later)

The Hi-lock module was not required - it is possible to write syntax colouring for a new language using the standard syntax colouring mechanism, as follows:
(defconst imp-mode-syntax-table
(let ((table (make-syntax-table)))
;; turn off ' and " from being string delimiters
(modify-syntax-entry ?' "-" table)
(modify-syntax-entry ?\" "-" table)
table))
;; Highlight comments, %stropped keywords, and numbers:
(setq imp-highlights
'(
;; many forms of comments including after labels
(";[ ]*!" ".*" nil nil (0 font-lock-comment-face))
("^[ ]*!" ".*;" nil nil (0 font-lock-comment-face))
("^[ ]*!" ".*" nil nil (0 font-lock-comment-face))
("%c\\([ ]+[%]+\\|[%]*\\)*o\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*e\\([ ]+[%]+\\|[%]*\\)*n\\([ ]+[%]+\\|[%]*\\)*t" ".*;" nil nil
(0 font-lock-comment-face))
("%c\\([ ]+[%]+\\|[%]*\\)*o\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*e\\([ ]+[%]+\\|[%]*\\)*n\\([ ]+[%]+\\|[%]*\\)*t" "[^$]*" nil nil
(0 font-lock-comment-face))
(":[ ]*!" "[^$]*" nil nil (0 font-lock-comment-face))
("{[^}]*}" . 'font-lock-comment-face )
("{.*$" . 'font-lock-comment-face )
;; char constants, old-style strings, and old-style based constants
("[MBOXER]?'[^']*'" . 'font-lock-constant-face)
("\"[^\"]*\"" . 'font-lock-string-face )
;; Based IMP constants
("\\<[0-9][ ]*[0-9 ]*[ ]*_[ ]*[0-9A-Fa-f][0-9A-Fa-f ]*\\>" . 'font-lock-constant-face)
;; Decimal IMP constants
("\\<[0-9][0-9 ]*\\.[ ]*[0-9][0-9 ]*#[ ]*[-+]?[ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
("\\<[0-9][0-9 ]*\\.[ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
;; Integer IMP constants
("\\<[0-9][ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
("\\<[0-9][ ]*\\>" . 'font-lock-constant-face)
;; stropped keywords
("\\%[A-Za-z][A-Za-z]*" . 'font-lock-keyword-face )
))
(define-derived-mode imp-mode prog-mode "Simple Imp Mode"
:syntax-table imp-mode-syntax-table
(setq font-lock-defaults '(imp-highlights))
(font-lock-fontify-buffer)
)
;; Apply automatically to Imp source files:
(add-to-list 'auto-mode-alist '("\\.i\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp77\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp80\\'" . imp-mode))
;; enter <esc>xfun<cr> to disable imp-mode if unwanted during an edit.
Based on the info from http://xahlee.info/emacs/emacs/elisp_syntax_coloring.html

Related

Removing enclosed text in Emacs

I would like to delete enclosed text between special characters like: ["{'<( etc .. this way I can remove text like "this is a very ... long text" with a simple keyboard shortcut. I was looking for some already existing mode that performs something similar but I didn't found any so I created some lisp code which performs good in most of situations, however it's not working correctly in all cases. For example if I have the following text entry and I put the cursor in the position"^" then I would liek to remove all the text enclosed by " but it doesn't work:
"aaaaa ] > [more text] aaaaa"
------------ ^
My lisp code is the following:
;; returns the enclosing character for the character "c"
(defun get-enc-char (c) (cond
((string= c "(") ")")
((string= c "[") "]")
((string= c "{") "}")
((string= c ">") "<")
((string= c "<") ">")
((string= c "'") "'")
((string= c "\"") "\"")
(t nil)
)
)
(defun delete-enclosed-text ()
"Delete texts between any pair of delimiters."
(interactive)
(save-excursion
(let (p1 p2 mychar)
; look for one of those characters and store the cursor position
(skip-chars-backward "^([\'\"><{") (setq p1 (point))
; store the char at this point, look for its enclosed char and advance
; the cursor newly (this done to avoid the cases when the char and
; its enclosed-char are the same like " or ' chars.
(backward-char 1) (setq mychar (thing-at-point 'char)) (forward-char 1)
; look forward for the enclosed char
(skip-chars-forward (concatenate 'string "^" (get-enc-char mychar))) (setq p2 (point))
; only delete the region if we found the enclosed character
(if (looking-at "[\]\}\"\'\)<>]") (kill-region p1 p2)))))
Following is an example:
Here a solution based on your code
;; returns the enclosing character for the character "c"
(defun get-enc-char (c) (cond
((string= c "(") ")")
((string= c "[") "]")
((string= c "{") "}")
((string= c ">") "<")
((string= c "<") ">")
((string= c "'") "'")
((string= c "\"") "\"")
(t nil)
))
(defvar empty-enclose 0)
(defun delete-enclosed-text ()
"Delete texts between any pair of delimiters."
(interactive)
(setq empty-enclose 0)
(save-excursion
(let (p1 p2 orig)
(setq orig (point))
(setq p1 (point))
(setq p2 (point))
(setq find 0)
(setq mychar (thing-at-point 'char))
(if (-contains? '("(" "[" "{" "<" "'" "\"") mychar)
(progn
(setq left_encloser (thing-at-point 'char))
(backward-char -1)
(if (string-equal (thing-at-point 'char) (get-enc-char left_encloser))
(progn
(backward-char -1)
(setq p2 (point))
(setq find 1)
(setq empty-enclose 1)))))
(while (eq find 0)
(skip-chars-backward "^({[<>\"'")
(setq p1 (point))
(backward-char 1)
(setq left_encloser (thing-at-point 'char))
(goto-char orig)
(while (and (not (eobp)) (eq find 0))
(backward-char -1)
(skip-chars-forward "^)}]<>\"'")
(setq right_encloser (thing-at-point 'char))
(if (string-equal right_encloser (get-enc-char left_encloser))
(progn
(setq p2 (point))
(setq find 1))))
(goto-char p1)
(backward-char 1))
(delete-region p1 p2)))
(if (eq empty-enclose 0)
(backward-char 1)))
I rapid-sketched something, it doesn't match exactly what you're asking for but I think it could fullfill the same requirements in an even more comfortable way, give it a try and let me know! This interactive function is called with no arguments after selecting a region and asks you for an enclosing mark: this can be any char or string that is directly recognized by replace-regex (direct use of *,.,[ etc wouldn't be the case, but you still can use other chars like {},% etc or even HTML-like markups like <idx>).
The function will delete all text within the selected region, from the very first apparition of the mark to the very last (even if there is an odd number of them), marks are also deleted.
(defun remove-enclosed-in-selection (beginning end)
"select a region, call this function and type any valid regex
markup. All characters from its first to its last appearance will
be removed (including the symbol itself. Example: try with § and %:
aaaa§bbbbcc%c§cc§ddddeeee§ffffgggghhhhiiii§jjjj§kkkkllll§mmmm%nnnn"
(interactive "r")
(let ((x (read-string "type enclosing mark: ")))
(narrow-to-region beginning end)
(replace-regexp (concat x ".*" x) "")
(widen)))
Then you can globally bind it to any keyboard shortcut you want as usual:
(global-set-key (kbd "C-. <C-return>") 'remove-enclosed-in-selection)
or locally to any custom hook you may have:
(defun custom-whatever-hook ()
(local-set-key (kbd "C-. <C-return>")) 'remove-enclosed-in-selection)
(add-hook 'whatever-hook 'custom-whatever-hook)
so, summarizing:
select region
M-x remove-enclosed-in-selection or your custom keystroke
press RET, type valid marker, press RET
the enclosed contents should be removed
The narrow-widen approach seems quick&dirty to me, but I couldn't find another way in the short term. So it may still need a couple of fixes, let me know if it works as expected. Plus, I'm not that an emacs hacker... yet! :P
cheers

Emacs major-mode for Mathematica based on cc-mode

****Solution to Issue 1 by Stephan - see Answer below****
I mark \ as an escape character in the syntax table, but then override that designation for the Mathematica syntax elements like \[Infinity]. Here is my syntax-propertize-function:
(defconst math-syntax-propertize-function
(syntax-propertize-rules
("\\\\\\[\\([A-Z][A-Za-z]*\\)]" (0 "_"))))
I referenced it from the (defun math-node() function like so:
(set (make-local-variable 'syntax-propertize-function)
math-syntax-propertize-function)
In my first attempt, I didn't use the make-local-variable function and I was surprised when my elisp buffer highlighting went awry.
****End Solution to Issue 1****
I am implementing a major-mode in Emacs derived from cc-mode for editing Mathematica files. The goal is syntax highlighting and indentation. I will leave interfacing with the Mathematica kernel for later.
I have the basic functionality working, but there are a couple of sticking points that are giving me trouble.
****Issue 1** - The \ character is used as an escape character and to prefix multi-character, bracketed keywords. **
Like many languages, Mathematica uses the \ character to escape " and other \ characters is strings.
Mathematic has what are called in Mathematica speak Syntax Characters like \[Times], \[Element], \[Infinity], etc. that represent mathematica operators and constants.
And, Mathematica makes heavy use of [ and ] instead of ( and ) for function definitions and calls, etc.
So, if I mark \ as an escape character in the syntax-table, then my brackets become mis-matched anywhere I use a Syntax Character. E.g.,
If[x < \[Pi], True, False]
Of course, cc-mode is intent on ignoring the [ right after the \. Given the functional nature of Mathematica, the mode is almost useless if it cannot match brackets. Think lisp without paren matching.
If I don't put \ in the syntax-table as an escape character, then how do I handle escape sequences in comments and strings?
It would be great if I could put Times, Element, Infinity, etc in a keyword list and have everything work correctly.
****Issue 2** - The syntax of Mathematica is different enough from C,C++,Java,ObjC, etc. that cc-mode's builtin syntactical analysis doesn't always produce the desired result.**
Consider the following code block:
FooBar[expression1,
expression2,
expression3];
This formats beautifully because the expressions are recognized as an argument list.
However, if a list is passed as an argument,
FooBar[{expression1,
expression2,
expression3}];
the result is not pretty because the expressions are considered continuations of a single statement within the { and }. Unfortunately, the simple hack of setting c-continuation-offset to 0 breaks actual continuations like,
addMe[x_Real, y_Real] :=
Plus[x, y];
which you want to be indented.
The issue is that in Mathematica { and } delineate lists and not code blocks.
Here is the current elisp file I am using:
(require 'cc-mode)
;; There are required at compile time to get the sources for the
;; language constants.
(eval-when-compile
(require 'cc-langs)
(require 'cc-fonts))
;; Add math mode the the language constant system. This needs to be
;; done at compile time because that is when the language constants
;; are evaluated.
(eval-and-compile
(c-add-language 'math-mode 'c-mode))
;; Function names
(c-lang-defconst c-cpp-matchers
math (append
(c-lang-const c-cpp-matchers c)
;; Abc[
'(("\\<\\([A-Z][A-Za-z0-9]*\\)\\>\\[" 1 font-lock-type-face))
;; abc[
'(("\\<\\([A-Za-z][A-Za-z0-9]*\\)\\>\\[" 1 font-lock-function-name-face))
;; Abc
'(("\\<\\([A-Z][A-Za-z0-9]*\\)\\>" 1 font-lock-keyword-face))
;; abc_
'(("\\<\\([a-z][A-Za-z0-9]*[_]\\)\\>" 1 font-lock-variable-name-face))
))
;; font-lock-comment-face
;; font-lock-doc-face
;; font-lock-string-face
;; font-lock-keyword-fact
;; font-lock-function-name-face
;; font-lock-constant-face
;; font-lock-type-face
;; font-lock-builtin-face
;; font-lock-reference-face
;; font-lock-warning-face
;; There is no line comment character.
(c-lang-defconst c-line-comment-starter
math nil)
;; The block comment starter is (*.
(c-lang-defconst c-block-comment-starter
math "(*")
;; The block comment ender is *).
(c-lang-defconst c-block-comment-ender
math "*)")
;; The assignment operators.
(c-lang-defconst c-assignment-operators
math '("=" ":=" "+=" "-=" "*=" "/=" "->" ":>"))
;; The operators.
(c-lang-defconst c-operators
math `(
;; Unary.
(prefix "+" "-" "!")
;; Multiplicative.
(left-assoc "*" "/")
;; Additive.
(left-assoc "+" "-")
;; Relational.
(left-assoc "<" ">" "<=" ">=")
;; Equality.
(left-assoc "==" "=!=")
;; Assignment.
(right-assoc ,#(c-lang-const c-assignment-operators))
;; Sequence.
(left-assoc ",")))
;; Syntax modifications necessary to recognize keywords with
;; punctuation characters.
;; (c-lang-defconst c-identifier-syntax-modifications
;; math (append '((?\\ . "w"))
;; (c-lang-const c-identifier-syntax-modifications)))
;; Constants.
(c-lang-defconst c-constant-kwds
math '( "False" "True" )) ;; "\\[Infinity]" "\\[Times]" "\\[Divide]" "\\[Sqrt]" "\\[Element]"\
))
(defcustom math-font-lock-extra-types nil
"Extra types to recognize in math mode.")
(defconst math-font-lock-keywords-1 (c-lang-const c-matchers-1 math)
"Minimal highlighting for math mode.")
(defconst math-font-lock-keywords-2 (c-lang-const c-matchers-2 math)
"Fast normal highlighting for math mode.")
(defconst math-font-lock-keywords-3 (c-lang-const c-matchers-3 math)
"Accurate normal highlighting for math mode.")
(defvar math-font-lock-keywords math-font-lock-keywords-3
"Default expressions to highlight in math mode.")
(defvar math-mode-syntax-table nil
"Syntax table used in math mode.")
(message "Setting math-mode-syntax-table to nil to force re-initialization")
(setq math-mode-syntax-table nil)
;; If a syntax table has not yet been set, allocate a new syntax table
;; and setup the entries.
(unless math-mode-syntax-table
(setq math-mode-syntax-table
(funcall (c-lang-const c-make-mode-syntax-table math)))
(message "Modifying the math-mode-syntax-table")
;; character (
;; ( - open paren class
;; ) - matching paren character
;; 1 - 1st character of comment delimitter (**)
;; n - nested comments allowed
(modify-syntax-entry ?\( "()1n" math-mode-syntax-table)
;; character )
;; ) - close parent class
;; ( - matching paren character
;; 4 - 4th character of comment delimitter (**)
;; n - nested comments allowed
(modify-syntax-entry ?\) ")(4n" math-mode-syntax-table)
;; character *
;; . - punctuation class
;; 2 - 2nd character of comment delimitter (**)
;; 3 - 3rd character of comment delimitter (**)
(modify-syntax-entry ?\* ". 23n" math-mode-syntax-table)
;; character [
;; ( - open paren class
;; ] - matching paren character
(modify-syntax-entry ?\[ "(]" math-mode-syntax-table)
;; character ]
;; ) - close paren class
;; [ - mathcing paren character
(modify-syntax-entry ?\] ")[" math-mode-syntax-table)
;; character {
;; ( - open paren class
;; } - matching paren character
(modify-syntax-entry ?\{ "(}" math-mode-syntax-table)
;; character }
;; ) - close paren class
;; { - matching paren character
(modify-syntax-entry ?\} "){" math-mode-syntax-table)
;; The following characters are punctuation (i.e. they cannot appear
;; in identifiers).
;;
;; / ' % & + - ^ < > = |
(modify-syntax-entry ?\/ "." math-mode-syntax-table)
(modify-syntax-entry ?\' "." math-mode-syntax-table)
(modify-syntax-entry ?% "." math-mode-syntax-table)
(modify-syntax-entry ?& "." math-mode-syntax-table)
(modify-syntax-entry ?+ "." math-mode-syntax-table)
(modify-syntax-entry ?- "." math-mode-syntax-table)
(modify-syntax-entry ?^ "." math-mode-syntax-table)
(modify-syntax-entry ?< "." math-mode-syntax-table)
(modify-syntax-entry ?= "." math-mode-syntax-table)
(modify-syntax-entry ?> "." math-mode-syntax-table)
(modify-syntax-entry ?| "." math-mode-syntax-table)
;; character $
;; _ - word class (since $ is allowed in identifier names)
(modify-syntax-entry ?\$ "_" math-mode-syntax-table)
;; character \
;; . - punctuation class (for now treat \ as punctuation
;; until we can fix the \[word] issue).
(modify-syntax-entry ?\\ "." math-mode-syntax-table)
) ;; end of math-mode-syntax-table adjustments
;;
;;
(defvar math-mode-abbrev-table nil
"Abbrevation table used in math mode buffers.")
(defvar math-mode-map (let ((map (c-make-inherited-keymap)))
map)
"Keymap used in math mode buffers.")
;; math-mode
;;
(defun math-mode ()
"Major mode for editing Mathematica code."
(interactive)
(kill-all-local-variables)
(c-initialize-cc-mode t)
(set-syntax-table math-mode-syntax-table)
(setq major-mode 'math-mode
mode-name "Math"
local-abbrev-table math-mode-abbrev-table
abbrev-mode t)
(use-local-map math-mode-map)
(c-init-language-vars math-mode)
(c-common-init 'math-mode)
(run-hooks 'c-mode-common-hook)
(run-hooks 'math-mode-hook)
(c-update-modeline))
(provide 'math-mode)
And a screenshot of some .
While cc-mode is designed to be adaptable to various languages, I'm not sure it will serve you well for Mathematica, because the syntax is too far from the one well-supported by cc-mode. I would suggest to try SMIE (an indentation engine that appeared in Emacs-23.4 and that was originally built for SML but is currently used for a variety of languages). Just like cc-mode, SMIE is not ideal for all languages either, but I wouldn't be surprised if it works better than cc-mode in your case.
For the backslash issue, your best bet is to use syntax-propertize-function to change the escaping-nature of specific backslashes (either set \ as escaping in the syntax-table and then mark the \ of \[foo] as non-escaping, or leave the \ as non-escaping in the syntax-table and then mark those \ of \" and \\ as escaping).

define your own tag in org-mode

There are Tags as in #+AUTHOR or #+LATEX in org-mode - are they called tags? I'd like to define my own tag which calls a function to preprocess the data and then outputs it - if the export target is LaTeX.
My solution was defining an own language, qtree, for SRC blocks.
#+BEGIN_SRC qtree
[.CP [.TP [.NP [] [.N' [.N Syntax] []]] [.VP [] [.V' [.V sucks] []]]]]
#+END_SRC
And process it accordingly. I even added a qtree-mode with paredit.
And a landscape parameter if the trees grow big. https://github.com/Tass/emacs-starter-kit/blob/master/vendor/assorted/org-babel-qtree.el
(require 'org)
(defun org-babel-execute:qtree (body params)
"Reformat a block of lisp-edited tree to one tikz-qtree likes."
(let (( tree
(concat "\\begin{tikzpicture}
\\tikzset{every tree node/.style={align=center, anchor=north}}
\\Tree "
(replace-regexp-in-string
" \\_<\\w+\\_>" (lambda (x) (concat "\\\\\\\\" (substring x 1)))
(replace-regexp-in-string
(regexp-quote "]") " ]" ; qtree needs a space
; before every closing
; bracket.
(replace-regexp-in-string
(regexp-quote "[]") "[.{}]" body)) ; empty leaf
; nodes, see
; http://tex.stackexchange.com/questions/75915
) ; For
; http://tex.stackexchange.com/questions/75217
"\n\\end{tikzpicture}"
)))
(if (assoc :landscape params)
(concat "\\begin{landscape}\n" tree "\n\\end{landscape}")
tree)))
(setq org-babel-default-header-args:qtree '((:results . "latex") (:exports . "results")))
(add-to-list 'org-src-lang-modes '("qtree" . qtree))
(define-generic-mode
'qtree-mode ;; name of the mode to create
'("%") ;; comments start with '%'
'() ;; no keywords
'(("[." . 'font-lock-operator) ;; some operators
("]" . 'font-lock-operator))
'() ;; files for which to activate this mode
'(paredit-mode) ;; other functions to call
"A mode for qtree edits" ;; doc string for this mode
)
They seem to be called keywords for in-buffer settings no more. Whatever they're called, they don't seem to be user-definable.
What you want to do is extremely related to a common way of handling whereas to export with xelatex or pdflatex as described on Worg.
The relevant part would be :
;; Originally taken from Bruno Tavernier: http://thread.gmane.org/gmane.emacs.orgmode/31150/focus=31432
(defun my-auto-tex-cmd ()
(if (string-match "YOUR_TAG: value1" (buffer-string))
(do something))
(if (string-match "YOUR_TAG: value2" (buffer-string))
(do something else))
(add-hook 'org-export-latex-after-initial-vars-hook 'my-auto-tex-cmd)

Emacs' align fails in plsql-mode

I've been using a PL/SQL mode available on EmacsWiki and I have been a rather happy with it.
But when I try to align or align-current I just get an error:
align: Wrong type argument: sequencep, plsql-align-rules-list
Example code that I'm trying to align:
declare
foo number;
x number;
y number;
begin
foo := 5;
x := 123;
y:=123;
end;
The expected outcome is:
declare
foo number;
x number;
y number;
begin
foo := 5;
x := 123;
y := 123;
end;
Here is the relevant (I hope) part of the plsql-mode's code:
;;;_ + Align
;; Should I make so that anything that is highlighted will line up?
;; Should we make a block anything inside ()?
(eval-and-compile
(defcustom plsql-align-rules-list '() ""
:group 'plsql
:type 'align-rules-list-type)
;; Should I make so that anything that is highlighted will line up?
;; Should we make a block anything inside ()?
(when (condition-case nil
(require 'align)
(error nil))
;; these are way too slow to use with indent before aligning
(unless (and plsql-align-rules-list plsql-debug)
(setq plsql-align-rules-list
'(
(plsql-assignment
(regexp . "\\(\\s-*\\):=\\(\\s-*\\)")
(group . (1 2))
(modes . '(plsql-mode))
(repeat t)
(tab-stop . nil))
(plsql-arrorw
(regexp . "\\(\\s-*\\)=>\\(\\s-*\\)")
(group . (1 2))
(modes . '(plsql-mode))
(repeat t)
(tab-stop . nil))
(plsql-equals ;; exclude the previous two cases
(regexp . "\\(\\s-*[^:]\\)=\\([^>]\\s-*\\)")
(group . (1 2))
(repeat t)
(tab-stop . nil)
(modes . '(plsql-mode)))
(plsql-operator ;; watch out for comments
(regexp . "\\(\\s-*\\)[-+/]{1}\\(\\s-*\\)")
(group . (1 2))
(repeat t)
(tab-stop . nil)
(modes . '(plsql-mode)))
(plsql-keywords
(regexp . "\\(\\s-+\\)\\(in\\|default\\|number\\|varchar2\\|blob\\|raw\\)\\b")
(group 1)
(repeat t)
(case-fold t)
(tab-stop . nil)
(modes . '(plsql-mode)))
)
))
(put 'plsql-align-rules-list 'risky-local-variable t)
(add-to-list 'align-c++-modes 'plsql-mode) ;; eg expression functions ...
(add-to-list 'align-sq-string-modes 'plsql-mode)
(add-to-list 'align-open-comment-modes 'plsql-mode)
;; Should we re-bind new-line-and-indent to align the current
;; region? That sounds expensive.
))
And later in defun plsql-mode ():
(set (make-local-variable 'align-mode-rules-list) 'plsql-align-rules-list)
How the plsql-mode should be modified to get align working ? My best work-around so far is to use align-regexp (inspired by this answer):
(defun my-align ()
""
(interactive)
(align-regexp
(region-beginning) (region-end)
"\\(\\s-*\\):=" 1 1 nil))
This is fine except it doesn't modify the right-hand side.
The author doesn't use the mode anymore. I'm using emacs 23.2.1.
As far as I can understand it, the function plsql-mode should contain:
(set (make-local-variable 'indent-line-function) 'plsql-indent)
(set (make-local-variable 'indent-region-function) 'plsql-indent-region)
(set (make-local-variable 'align-mode-rules-list) plsql-align-rules-list)
Since the first two buffer local variables are indeed references to functions, their argument should be a quoted symbol.
Since the third one should use the value contained in the variable plsql-align-rules-list, this variable should NOT be quoted.
HTH
)jack(

Highlighting correctly in an emacs major mode

I am developing an emacs major mode for a language (aka mydsl). However, using the techniques on xahlee's site doesn't seem to be working for some reason (possibly older emacs dialect..)
The key issues I am fighting with are (1) highlighting comments is not working and (2), the use of regexp-opt lines is not working.
I've reviewed the GNU manual and looked over cc-mode and elisp mode... those are significantly more complicated than I need.
;;;Standard # to newline comment
;;;Eventually should also have %% to %% multiline block comments
(defun mydsl-comment-dwim (arg)
"comment or uncomment"
(interactive "*P")
(require 'newcomment)
(let
((deactivate-mark nil)
(comment-start "#")
(comment-end "")
comment-dwim arg)))
(defvar mydsl-events
'("reservedword1"
"reservedword2"))
(defvar mydsl-keywords
'("other-keyword" "another-keyword"))
;;Highlight various elements
(setq mydsl-hilite
'(
; stuff between "
("\"\\.\\*\\?" . font-lock-string-face)
; : , ; { } => # $ = are all special elements
(":\\|,\\|;\\|{\\|}\\|=>\\|#\\|$\\|=" . font-lock-keyword-face)
( ,(regexp-opt mydsl-keywords 'words) . font-lock-builtin-face)
( ,(regexp-opt mydsl-events 'words) . font-lock-constant-face)
))
(defvar mydsl-tab-width nil "Width of a tab for MYDSL mode")
(define-derived-mode mydsl-mode fundamental-mode
"MYDSL mode is a major mode for editing MYDSL files"
;Recommended by manual
(kill-all-local-variables)
(setq mode-name "MYDSL script")
(setq font-lock-defaults '((mydsl-hilite)))
(if (null mydsl-tab-width)
(setq tab-width mydsl-tab-width)
(setq tab-width default-tab-width)
)
;Comment definitions
(define-key mydsl-mode-map [remap comment-dwim] 'mydsl-comment-dwim)
(modify-syntax-entry ?# "< b" mydsl-mode-syntax-table)
(modify-syntax-entry ?\n "> b" mydsl-mode-syntax-table)
;;A gnu-correct program will have some sort of hook call here.
)
(provide 'mydsl-mode)
You have a couple of syntactic problems in your code, but you got it nearly correct. Here's my edited version which appears to do the right thing for a buffer in mydsl-mode:
; No changes to the simple vars
(defvar mydsl-events
'("reservedword1"
"reservedword2"))
(defvar mydsl-keywords
'("other-keyword" "another-keyword"))
;; I'd probably put in a default that you want, as opposed to nil
(defvar mydsl-tab-width nil "Width of a tab for MYDSL mode")
;; Two small edits.
;; First is to put an extra set of parens () around the list
;; which is the format that font-lock-defaults wants
;; Second, you used ' (quote) at the outermost level where you wanted ` (backquote)
;; you were very close
(defvar mydsl-font-lock-defaults
`((
;; stuff between "
("\"\\.\\*\\?" . font-lock-string-face)
;; ; : , ; { } => # $ = are all special elements
(":\\|,\\|;\\|{\\|}\\|=>\\|#\\|$\\|=" . font-lock-keyword-face)
( ,(regexp-opt mydsl-keywords 'words) . font-lock-builtin-face)
( ,(regexp-opt mydsl-events 'words) . font-lock-constant-face)
)))
(define-derived-mode mydsl-mode fundamental-mode "MYDSL script"
"MYDSL mode is a major mode for editing MYDSL files"
;; fundamental-mode kills all local variables, no need to do it again
(setq mode-name "MYDSL script")
;; you again used quote when you had '((mydsl-hilite))
;; I just updated the variable to have the proper nesting (as noted above)
;; and use the value directly here
(setq font-lock-defaults mydsl-font-lock-defaults)
;; when there's an override, use it
;; otherwise it gets the default value
(when mydsl-tab-width
(setq tab-width mydsl-tab-width))
;; for comments
;; overriding these vars gets you what (I think) you want
;; they're made buffer local when you set them
(setq comment-start "#")
(setq comment-end "")
(modify-syntax-entry ?# "< b" mydsl-mode-syntax-table)
(modify-syntax-entry ?\n "> b" mydsl-mode-syntax-table)
;;A gnu-correct program will have some sort of hook call here.
)
(provide 'mydsl-mode)