How to indent square brackets identically as parentheses in Emacs? - emacs

I've defined a few read-macros in Common Lisp with square bracket delimiters, and I'd like to make Emacs indent those square brackets in the exactly same way as with parentheses.
For example, from this:
(mapcar [if (flag)
:t
:f]
my-list)
To this:
(mapcar [if (flag)
:t
:f]
my-list)
...which is what Emacs would do if the square brackets were parentheses.
How can I achieve that?

You need to tell Emacs that [ and ] are matching parentheses:
(modify-syntax-entry ?\[ "(]" lisp-mode-syntax-table)
(modify-syntax-entry ?\] ")[" lisp-mode-syntax-table)

Related

Writing major mode: how set different start string and end string character?

I'm writing a major mode where I can have multiline strings like this:
Text : >abcde
fgh
ijklmonp<
where '>' and '<' indicate the respective start and end of the string. The following syntax table entries only mark >...> and <...< strings, which is not what I want.
(modify-syntax-entry ?> "\"" st)
(modify-syntax-entry ?< "\"" st)
Currently the best solution is using generic string delimiters: ‘|’, but it still messes up my system as I have >...<...< situations sometimes. The best would be if I could use a multiline regexp like
^Text : >.*<$
How can I achieve this?
As thornjad explains, this is not supported directly by syntax-table, so you need to use syntax-propertize-function. E.g.
(defconst my-syntax-propertize
(syntax-propertize-rules
(">" (0 (unless (nth 8 (save-excursion (syntax-ppss (match-beginning 0)))
(string-to-syntax "|"))))
("<" (0 (when (eq t (nth 3 (save-excursion
(syntax-ppss (match-beginning 0))))
(string-to-syntax "|"))))))
then in your major mode function:
(setq-local syntax-propertize-function my-syntax-propertize)
The nth 8 test makes sure > is only marked as a string delimiter if it is not within another string or comment, and the nth 3 test makes sure that < is only marked as a string delimiter when it occurs with a string that was started by another generic string delimiter.
Unfortunately modify-syntax-entry isn't powerful enough to handle this sort of situation. Luckily we have other options! My orson-mode deals with a similar issue where strings are delimited by double-single quotes ('') instead of double quotes (").
To do this, a regexp looks for the entire string, quotes included, then uses Emacs's string-fence class to mark the quotes as fences.
(defconst orson--string-rx
"\\(''[^']*''\\)")
(defun orson-syntax-propertize-function (start end)
(save-excursion
(goto-char start)
(while (re-search-forward orson--string-rx end 'noerror)
(let ((a (match-beginning 1))
(b (match-end 1))
(string-fence (string-to-syntax "|")))
(put-text-property a (1+ a) 'syntax-table string-fence)
(put-text-property (1- b) b 'syntax-table string-fence))))

Is it possible to turn off qualification of symbols when using clojure syntax quote in a macro?

I am generating emacs elisp code from a clojure function. I originally started off using a defmacro, but I realized since I'm going cross-platform and have to manually eval the code into the elisp environment anyway, I can just as easily use a standard clojure function. But basically what I'm doing is very macro-ish.
I am doing this because my goal is to create a DSL from which I will generate code in elisp, clojure/java, clojurescript/javascript, and maybe even haskell.
My "macro" looks like the following:
(defn vt-fun-3 []
(let [hlq "vt"]
(let [
f0 'list
f1 '(quote (defun vt-inc (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))]
`(~f0 ~f1 ~f2)
)))
This generates a list of two function definitions -- the generated elisp defun and a unit test:
(list (quote (defun vt-inc (n) (+ n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
Then from an emacs scratch buffer, I utilize clomacs https://github.com/clojure-emacs/clomacs to import into the elisp environment:
(clomacs-defun vt-fun-3 casc-gen.core/vt-fun-3)
(progn
(eval (nth 0 (eval (read (vt-fun-3)))))
(eval (nth 1 (eval (read (vt-fun-3))))))
From here I can then run the function and the unit test:
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
Note: like all macros, the syntax quoting and escaping is very fragile. It took me a while to figure out the proper way to get it eval properly in elisp (the whole "(quote (list..)" prefix thing).
Anyway, as suggested by the presences of the "hlq" (high-level-qualifier) on the first "let", I want to prefix any generated symbols with this hlq instead of hard-coding it.
Unfortunately, when I use standard quotes and escapes on the "f1" for instance:
f1 '(quote (defun ~hlq -inc (n) (+ n 1)))
This generates:
(list (quote (defun (clojure.core/unquote hlq) -inc (n) (+ n 1)))
(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
In other words it substitutes 'clojure.core/unquote' for "~" which is not what I want.
The clojure syntax back-quote:
f1 `(quote (defun ~hlq -inc (n) (+ n 1)))
doesn't have this problem:
(list (quote (casc-gen.core/defun vt casc-gen.core/-inc (casc-gen.core/n) (clojure.core/+ casc-gen.core/n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
It properly escapes and inserts "vt" as I want (I still have to work out to concat to the stem of the name, but I'm not worried about that).
Problem solved, right? Unfortunately syntax quote fully qualifies all the symbols, which I don't want since the code will be running under elisp.
Is there a way to turn off the qualifying of symbols when using the syntax quote (back tick)?
It also seems to me that the syntax quote is more "capable" than the standard quote. Is this true? Or can you, by trickery, always make the standard quote behave the same as the syntax quote? If you cannot turn off qualification with syntax quote, how could I get this working with the standard quote? Would I gain anything by trying to do this as a defmacro instead?
The worst case scenario is I have to run a regex on the generated elisp and manually remove any qualifications.
There is no way to "turn off" the qualifying of symbols when using syntax quote. You can do this however:
(let [hlq 'vt] `(~'quote (~'defun ~hlq ~'-inc (~'n) (~'+ ~'n 1))))
Which is admittedly pretty tedious. The equivalent without syntax quote is:
(let [hlq 'vt] (list 'quote (list 'defun hlq '-inc '(n) '(+ n 1))))
There is no way to get your desired output when using standard quote prefixing the entire form however.
As to the issue of using defmacro instead, as far as I understand your intentions, I don't think you would gain anything by using a macro.
Based on the input from justncon, here is my final solution. I had to do a little extra formatting to get the string concat on the function name right, but everything was pretty much like he recommended:
(defn vt-gen-4 []
(let [hlq 'vt]
(let [
f1 `(~'quote (~'defun ~(symbol (str hlq "-inc")) (~'n) (~'+ ~'n 1)))
f2 `(~'quote (~'defun ~(symbol (str hlq "-inc-test")) () (~'should (~'= (~(symbol (str hlq "-inc")) 7) 8))))
]
`(~'list ~f1 ~f2))))
What I learned:
syntax quote is the way to go, you just have to know how to control unquoting at the elemental level.
~' (tilde quote) is my friend here. Within a syntax quote expression, if you specify ~' before either a function or var it will be passed through to the caller as specified.
Take the expression (+ 1 1)
Here is a synopsis of how this expression will expand within a syntax quote expression based on various levels of escaping:
(defn vt-foo []
(println "(+ 1 1) -> " `(+ 1 1)) --> (clojure.core/+ 1 1)
(println "~(+ 1 1) -> " `~(+ 1 1)) --> 2
(println "~'(+ 1 1) -> " `~'(+ 1 1)) --> (+ 1 1)
)
The last line was what I wanted. The first line was what I was getting.
If you escape a function then do not escape any parameters you want escaped. For instance, here we want
to call the "str" function at macro expand time and to expand the variable "hlq" to it's value 'vt:
;; this works
f1 `(quote (defun ~(str hlq "-inc") ~hlq (n) (+ n 1)))
;; doesn't work if you escape the hlq:
f1 `(quote (defun ~(str ~hlq "-inc") ~hlq (n) (+ n 1)))
I guess an escape spans to everything in the unit your escaping. Typically you escape atoms (like strings or symbols), but if it's a list then everything in the list is automatically escaped as well, so don't double escape.
4) FWIW, I ended writing a regex solution before I got the final answer. It's definitely not as nice:
(defn vt-gen-3 []
(let [hlq "vt"]
(let
[
f0 'list
f1 `(quote (defun ~(symbol (str hlq "-inc")) (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))
]
`(~f0 ~f1 ~f2)
))
)
;; this strips out any qualifiers like "casc-gen.core/"
(defn vt-gen-3-regex []
(clojure.string/replace (str (vt-gen-3)) #"([\( ])([a-zA-Z0-9-\.]+\/)" "$1" ))
Macro expansion is very delicate and requires lots of practice.

emacs major-mode font-lock between characters (parenthesis, quotes, etc)

I'm trying to setup an emacs major-mode which essentially just highlights text between lots of different characters, in different colors. I have square brackets working with:
(font-lock-add-keywords nil '(("\\[\\(.*\\)\\]"
1 font-lock-keyword-face prepend)))
but when I try replacing the [ and ] with other characters, it stops working. For example, round parentheses '()' does not work:
(font-lock-add-keywords nil '(("\\(\\(.*\\)\\)"
1 font-lock-function-name-face prepend)))
Trying single, double, or back-quotes, etc also don't work. I'm completely unfamiliar with lisp-syntax --- what am I doing wrong? Also: is there any way to include the characters bracketing the expression?
You're mixing regular expressions and regular strings.
Try these:
;; square brackets - escape the first one so you don't get a [..] regexp
(font-lock-add-keywords nil '(("\\(\\[.*]\\)"
1 font-lock-keyword-face prepend)))
;; parentheses - don't escape the parentheses you want to match!
(font-lock-add-keywords nil '(("\\((.*)\\)"
1 font-lock-keyword-face prepend)))
;; quotes - single escape so you don't break your string:
(font-lock-add-keywords nil '(("\\(\".*\"\\)"
1 font-lock-keyword-face prepend)))
;; other characters - not regexps, so don't escape them:
(font-lock-add-keywords nil '(("\\('.*'\\)"
1 font-lock-keyword-face prepend)))
(font-lock-add-keywords nil '(("\\(<.*>\\)"
1 font-lock-keyword-face prepend)))

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).

Generating a code from Emacs

How can I write the following code quickly in emacs?
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
...
...
\newcommand{\cY}{\mathcal Y}
\newcommand{\cZ}{\mathcal Z}
Is there a way faster than writing
A
B
C
D
.
.
.
Y
Z
and then doing macro on each line? (changing A to \newcommand{\cA}{\mathcal A})
I agree that a keyboard macro will get the result the quickest.
More fun is a programmatic approach:
(loop
for n from (string-to-char "A") to (string-to-char "Z")
for c = (char-to-string n)
do (insert (concat "\\newcommand{\\c" c "}{\\mathcal " c "}\n")))
In general, the first time you are confronted with this kind of problem, you'd use keyboard macros, as JB already said.
The second time, check out this very very interesting article by Steve Yegge: http://steve-yegge.blogspot.com/2006/06/shiny-and-new-emacs-22.html, which contains solutions for problems exactly like yours.
For your convenience, and my own illumination, I actually went ahead and tested it:
I would start with
A
...
A
26 times
and do a
M-x replace-regexp
A
\\newcommand{\\c\,(string (+ ?A \#))}{\\mathcal \,(string (+ ?A \#))}
A to Z is only 26 lines. You'd waste more time automating generation than just naturally using keyboard macros, imho.
It depends on your background. I'd just type M-! and:
perl -e"print map {q(\newcommand{\c).$_.q(}{\mathcal ).qq($_}\n)} A..Z
Do you know the rectangle selection ?
There's also string-rectangle:
place point (the cursor) at the beginning of the text, mark (C-SPC)
place point at the beginning of the last line
type M-x string-rectangle
type a string (\newcommand{\c)
This will insert that string before each line since the mark.
I don't have enough karma or whatever to comment, but I wanted to improve HD's style a bit.
Here is the original:
(loop
for n from (string-to-char "A") to (string-to-char "Z")
for c = (char-to-string n)
do (insert (concat "\\newcommand{\\c" c "}{\\mathcal " c "}\n")))
First off, Emacs Lisp has reader syntax for chars. Instead of (string-to-char "X"), you can just write ?X. Then, you can use the printf-style format instead of char-to-string and concat to produce the final result:
(loop for n from ?A to ?Z
do (insert (format "\\newcommand{\\c%s}{\\mathcal %s}\n" n n)))
Now it's concise enough to type without thinking into the M-: prompt.
I will also point out that TeX has macros too, if this is indeed TeX.
Edit: Another bit of style advice for Joe Casadonte; (incf foo) is much easier to type than (setq foo (+ foo 1)).
Or the interactive way if you want to do them one at a time
(defun somefun(inputval)
(interactive "sInputVal: ")
(insert ( format "\\newcommand{\\c%s}{\\mathcal %s}" inputval inputval) )
)
just eval and M-x somefun every time you want the text
Not exactly an answer.
For people who don't use emacs or vim, I'd like to add that you can open Excel and use its autofill feature and text concatenation function to generate such code.
I like HD's loop a little better than mine, but here's an alternate, more generalized approach:
(defun my-append (str)
"Substitute A-Z in STR and insert into current buffer.
Looks for '--HERE--' (without quotes) in string STR, substitutes the
letters A-Z in the string and then inserts the resulting string into
the current buffer."
(interactive "sInput String: ")
(let ((lcv 0)
letter newstr)
(while (< lcv 26)
(setq letter (+ ?A lcv))
(insert (replace-regexp-in-string "--HERE--" (char-to-string letter) str t t) "\n")
(setq lcv (+ lcv 1)))))
Shouldn't be too hard to combine the two.