I'm writing a simple mode for a Lisp-like language, and am having trouble setting up indentation. I've been following the emacswiki mode tutorial.
However, I can't figure out how to adapt their example indentation to my needs because they don't do any form of counting.
Basically, I just need to add 2 spaces to my indentation count every time I see a { or (, even if there are multiple on the same line, and subtract 2 spaces when I see closures of the above. I'm new to elisp; how can I adapt their example to count braces and brackets?
For convenience, here is the code they are using (for a non-bracket language):
(defun wpdl-indent-line ()
"Indent current line as WPDL code"
(interactive)
(beginning-of-line)
(if (bobp) ; Check for rule 1
(indent-line-to 0)
(let ((not-indented t) cur-indent)
(if (looking-at "^[ \t]*END_") ; Check for rule 2
(progn
(save-excursion
(forward-line -1)
(setq cur-indent (- (current-indentation) default-tab-width)))
(if (< cur-indent 0)
(setq cur-indent 0)))
(save-excursion
(while not-indented
(forward-line -1)
(if (looking-at "^[ \t]*END_") ; Check for rule 3
(progn
(setq cur-indent (current-indentation))
(setq not-indented nil))
; Check for rule 4
(if (looking-at "^[ \t]*\\(PARTICIPANT\\|MODEL\\|APPLICATION\\|WORKFLOW\\|ACTIVITY\\|DATA\\|TOOL_LIST\\|TRANSITION\\)")
(progn
(setq cur-indent (+ (current-indentation) default-tab-width))
(setq not-indented nil))
(if (bobp) ; Check for rule 5
(setq not-indented nil)))))))
(if cur-indent
(indent-line-to cur-indent)
(indent-line-to 0))))) ; If we didn't see an indentation hint, then allow no indentation
How can I just implement lisp-like indentation (but also with curly braces)?
If you want something simple for a Lisp-style language, I suggest you start with (syntax-ppss) which returns the "parsing state" at point. The first element of that state is the current paren-nesting depth. While I used the word "paren", this doesn't really count parens but counts those chars which the syntax-table defines as paren-like, so if you set your syntax-table such that { and } are declared as paren-like, then those will also be counted.
So you could start with something like
(defun foo-indent-function ()
(save-excursion
(beginning-of-line)
(indent-line-to (* 2 (car (syntax-ppss))))))
Do not define this as interactive, since the way to use it is by adding
(set (make-local-variable 'indent-line-function) #'foo-indent-function)
in your major-mode function.
But maybe a better option is to simply do:
(require 'smie)
...
(define-derived-mode foo-mode "Foo"
...
(smie-setup nil #'ignore)
...)
This will use an indentation step of 4 (configured in smie-indent-basic).
Related
After wandering off in frustration from before, I've decided to try Haskell in Emacs org-mode again. I'm using Haskell stack-ghci (8.6.3), Emacs 26.2, org-mode 9.2.3 set up with intero. This code block
#+begin_src haskell :results raw :session *haskell*
pyth2 :: Int -> [(Int, Int, Int)]
pyth2 n =
[ (x, y, z)
| x <- [1 .. n]
, y <- [x .. n]
, z <- [y .. n]
, x ^ 2 + y ^ 2 == z ^ 2
]
#+end_src
produces this RESULTS:
*Main| *Main| *Main| *Main| *Main|
<interactive>:59:16: error: Variable not in scope: n
<interactive>:60:16: error: Variable not in scope: n
<interactive>:61:16: error: Variable not in scope: n
However, this
#+begin_src haskell :results raw
tripleMe x = x + x + x
#+end_src
works fine. I've added the :set +m to both ghci.conf and the individual code block to no effect. This code works fine in a separate hs file run in a separate REPL. The pyth2 code in a separate file also can be called from the org-mode started REPL and run just fine as well. Not sure how to proceed. Can include Emacs init info if necessary.
Over on the org-mode mailing list I got an answer that basically is saying the same as you, D. Gillis. He had a similar work-around that actually is more org-mode-centric. Under a heading where your code blocks will be put this "drawer"
:PROPERTIES:
:header-args:haskell: :prologue ":{\n" :epilogue ":}\n"
:END:
and then (possibly in a local variable) run
#+begin_src haskell :results output
:set prompt-cont ""
#+end_src
For reasons unknown I've had to include the :results output otherwise a cryptic error of "expecting a string" happens.
On a few other notes, haskell babel doesn't respond/care about the :session option, i.e., when you run a code block, a REPL *haskell* starts and that will be the sole REPL. Also, a haskell-mode started REPL doesn't play well with an existing org-mode initiated REPL, i.e., if you start a REPL from haskell-mode, it kills the original org-mode *haskkell*REPL, and any new attempt to run org-mode code blocks can't see this new, non-*haskell*REPL. Then if you kill the haskell-mode REPL and try to run org-mode blocks, you get
executing Haskell code block...
inferior-haskell-start-process: List contains a loop: ("--no-build" "--no-load" "--ghci-options=-ferror-spans" "--no-build" "--no-load" . #2)
... you're hosed -- and nothing seems to shake it, not any restart/refresh, nor killing, reloading the file, i.e., a complete restart of Emacs is necessary. Anyone knowing a better solution, please tells usses.
This is a GHCi issue.
The same error occurs when your code is copied directly into GHCi, which also gives a parse error when it encounters the new line after the equal sign. This first error isn't showing up here because org-babel only shows the value of the last expression (in this case, the error caused by the list comprehension).
I'm not entirely familiar with how Haskell-mode sends the code to GHCi, but it looks like it involves loading in the buffer into GHCi as a file, which may be why you didn't have this problem working from the hs file.
There are a few options to fix this, none of which are completely ideal:
Move some portion of the list into the first line (e.g. the first line could be pyth2 n = [).
Wrap the entire function definition with :{ and :}.
Write an Elisp function to modify what is being sent to GHCi and then changes it back after it is evaluated.
The first two options require you to format your code in a form that the GHCi will accept. In your example case, the first option may not be too bad, but this won't always be so trivial for all multi-line declarations (e.g. pattern-matching function declarations). The downside to the second option is that it requires adding brackets to the code that shouldn't be there in real source code.
To fix the issue of extraneous brackets being added, I've written an Elisp command (my-org-babel-execute-haskell-blocks) that places these brackets around code blocks that it finds, evaluates the region, and then deletes the brackets. Note that this function requires that blocks be separated from all other code with at least one empty line.
Calling my-org-babel-execute-haskell-blocks on your example declares the function without any errors.
EDIT: The previous function I gave failed to work on pattern matching declarations. I've rewritten the function to fix this issue as well as to be comment aware. This new function should be significantly more useful. However, it's worth noting that I didn't handle multi-line comments in a sophisticated manner, so code blocks with multi-line comments may not be wrapped properly.
(defun my-org-babel-execute-haskell-blocks ()
"Wraps :{ and :} around all multi-line blocks and then evaluates the source block.
Multi-line blocks are those where all non-indented, non-comment lines are declarations using the same token."
(interactive)
(save-excursion
;; jump to top of source block
(my-org-jump-to-top-of-block)
(forward-line)
;; get valid blocks
(let ((valid-block-start-ends (seq-filter #'my-haskell-block-valid-p (my-get-babel-blocks))))
(mapcar #'my-insert-haskell-braces valid-block-start-ends)
(org-babel-execute-src-block)
(mapcar #'my-delete-inserted-haskell-braces (reverse valid-block-start-ends)))))
(defun my-get-blocks-until (until-string)
(let ((block-start nil)
(block-list nil))
(while (not (looking-at until-string))
(if (looking-at "[[:space:]]*\n")
(when (not (null block-start))
(setq block-list (cons (cons block-start (- (point) 1))
block-list)
block-start nil))
(when (null block-start)
(setq block-start (point))))
(forward-line))
(when (not (null block-start))
(setq block-list (cons (cons block-start (- (point) 1))
block-list)))))
(defun my-get-babel-blocks ()
(my-get-blocks-until "#\\+end_src"))
(defun my-org-jump-to-top-of-block ()
(forward-line)
(org-previous-block 1))
(defun my-empty-line-p ()
(beginning-of-line)
(= (char-after) 10))
(defun my-haskell-type-declaration-line-p ()
(beginning-of-line)
(and (not (looking-at "--"))
(looking-at "^.*::.*$")))
(defun my-insert-haskell-braces (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end)))
(goto-char block-end)
(insert "\n:}")
(goto-char block-start)
(insert ":{\n")))
(defun my-delete-inserted-haskell-braces (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end)))
(goto-char block-start)
(delete-char 3)
(goto-char block-end)
(delete-char 3)))
(defun my-get-first-haskell-token ()
"Gets all consecutive non-whitespace text until first whitespace"
(save-excursion
(beginning-of-line)
(let ((starting-point (point)))
(re-search-forward ".*?[[:blank:]\n]")
(goto-char (- (point) 1))
(buffer-substring-no-properties starting-point (point)))))
(defun my-haskell-declaration-line-p ()
(beginning-of-line)
(or (looking-at "^.*=.*$") ;; has equals sign
(looking-at "^.*\n[[:blank:]]*|")
(looking-at "^.*where[[:blank:]]*$")))
(defun my-haskell-block-valid-p (block-start-end)
(let ((block-start (car block-start-end))
(block-end (cdr block-start-end))
(line-count 0))
(save-excursion
(goto-char block-start)
(let ((token 'nil)
(is-valid t))
;; eat top comments
(while (or (looking-at "--")
(looking-at "{-"))
(forward-line))
(when (my-haskell-type-declaration-line-p)
(progn
(setq token (my-get-first-haskell-token)
line-count 1)
(forward-line)))
(while (<= (point) block-end)
(let ((current-token (my-get-first-haskell-token)))
(cond ((string= current-token "") ; line with indentation
(when (null token) (setq is-valid nil))
(setq line-count (+ 1 line-count)))
((or (string= (substring current-token 0 2) "--") ;; skip comments
(string= (substring current-token 0 2) "{-"))
'())
((and (my-haskell-declaration-line-p)
(or (null token) (string= token current-token)))
(setq token current-token
line-count (+ 1 line-count)))
(t (setq is-valid nil)
(goto-char (+ 1 block-end))))
(forward-line)))
(and is-valid (> line-count 1))))))
I have one org mode document that includes other org mode documents. The parent document should be exportable to pdf and each of the children should be aswell. Here is an example:
index.org
#+TITLE: Test Title
* Intro
This file must be exportable
* Heading 1
#+INCLUDE: doc1.org :minlevel 2 :only-contents t
doc1.org
#+TITLE: Inner title
This file must be exportable by itself aswell
* Heading 2
And here is some text
Exporting doc1.org produces the expected:
But exporting index.org yields (notice the title):
Is there a way to suppress the export options of included org documents?
The #+INCLUDE mechanism can include a portion of the file, so you can say
#+INCLUDE: doc1.org :minlevel 2 :only-contents t :lines "2-"
and have it skip the #+TITLE line in the included file. See http://orgmode.org/org.html#Include-files.
I made this ugly fix by overriding some org mode elisp. I put this in my .emacs and now things work as expected. Maybe I will post a patch to org mode when I get the time.
(defun fd--org-doc-begin ()
"Skip all the initial export options"
(save-excursion
(goto-char (point-min))
(while (and (or
(looking-at "[ \t]*#+")
(looking-at "[ \t]*$"))
(progn (next-line) (< (point) (point-max))))
(beginning-of-line))
(point)))
;;; This was overriden from ox.el
(defun org-export--prepare-file-contents
(file &optional lines ind minlevel id footnotes with-export-options)
"Prepare contents of FILE for inclusion and return it as a string.
When optional argument LINES is a string specifying a range of
lines, include only those lines.
Optional argument IND, when non-nil, is an integer specifying the
global indentation of returned contents. Since its purpose is to
allow an included file to stay in the same environment it was
created (e.g., a list item), it doesn't apply past the first
headline encountered.
Optional argument MINLEVEL, when non-nil, is an integer
specifying the level that any top-level headline in the included
file should have.
Optional argument ID is an integer that will be inserted before
each footnote definition and reference if FILE is an Org file.
This is useful to avoid conflicts when more than one Org file
with footnotes is included in a document.
Optional argument FOOTNOTES is a hash-table to store footnotes in
the included document.
Optional argument WITH-EXPORT-OPTIONS will stop this function
from ignoring export options at the beginning of the file."
(with-temp-buffer
(insert-file-contents file)
(when (not with-export-options)
(narrow-to-region (fd--org-doc-begin) (point-max)))
(when lines
(let* ((lines (split-string lines "-"))
(lbeg (string-to-number (car lines)))
(lend (string-to-number (cadr lines)))
(beg (if (zerop lbeg) (point-min)
(goto-char (point-min))
(forward-line (1- lbeg))
(point)))
(end (if (zerop lend) (point-max)
(goto-char (point-min))
(forward-line (1- lend))
(point))))
(narrow-to-region beg end)))
;; Remove blank lines at beginning and end of contents. The logic
;; behind that removal is that blank lines around include keyword
;; override blank lines in included file.
(goto-char (point-min))
(org-skip-whitespace)
(beginning-of-line)
(delete-region (point-min) (point))
(goto-char (point-max))
(skip-chars-backward " \r\t\n")
(forward-line)
(delete-region (point) (point-max))
;; If IND is set, preserve indentation of include keyword until
;; the first headline encountered.
(when (and ind (> ind 0))
(unless (eq major-mode 'org-mode)
(let ((org-inhibit-startup t)) (org-mode)))
(goto-char (point-min))
(let ((ind-str (make-string ind ?\s)))
(while (not (or (eobp) (looking-at org-outline-regexp-bol)))
;; Do not move footnote definitions out of column 0.
(unless (and (looking-at org-footnote-definition-re)
(eq (org-element-type (org-element-at-point))
'footnote-definition))
(insert ind-str))
(forward-line))))
;; When MINLEVEL is specified, compute minimal level for headlines
;; in the file (CUR-MIN), and remove stars to each headline so
;; that headlines with minimal level have a level of MINLEVEL.
(when minlevel
(unless (eq major-mode 'org-mode)
(let ((org-inhibit-startup t)) (org-mode)))
(org-with-limited-levels
(let ((levels (org-map-entries
(lambda () (org-reduced-level (org-current-level))))))
(when levels
(let ((offset (- minlevel (apply #'min levels))))
(unless (zerop offset)
(when org-odd-levels-only (setq offset (* offset 2)))
;; Only change stars, don't bother moving whole
;; sections.
(org-map-entries
(lambda ()
(if (< offset 0) (delete-char (abs offset))
(insert (make-string offset ?*)))))))))))
;; Append ID to all footnote references and definitions, so they
;; become file specific and cannot collide with footnotes in other
;; included files. Further, collect relevant footnote definitions
;; outside of LINES, in order to reintroduce them later.
(when id
(let ((marker-min (point-min-marker))
(marker-max (point-max-marker))
(get-new-label
(lambda (label)
;; Generate new label from LABEL by prefixing it with
;; "-ID-".
(format "-%d-%s" id label)))
(set-new-label
(lambda (f old new)
;; Replace OLD label with NEW in footnote F.
(save-excursion
(goto-char (+ (org-element-property :begin f) 4))
(looking-at (regexp-quote old))
(replace-match new))))
(seen-alist))
(goto-char (point-min))
(while (re-search-forward org-footnote-re nil t)
(let ((footnote (save-excursion
(backward-char)
(org-element-context))))
(when (memq (org-element-type footnote)
'(footnote-definition footnote-reference))
(let* ((label (org-element-property :label footnote)))
;; Update the footnote-reference at point and collect
;; the new label, which is only used for footnotes
;; outsides LINES.
(when label
(let ((seen (cdr (assoc label seen-alist))))
(if seen (funcall set-new-label footnote label seen)
(let ((new (funcall get-new-label label)))
(push (cons label new) seen-alist)
(org-with-wide-buffer
(let* ((def (org-footnote-get-definition label))
(beg (nth 1 def)))
(when (and def
(or (< beg marker-min)
(>= beg marker-max)))
;; Store since footnote-definition is
;; outside of LINES.
(puthash new
(org-element-normalize-string (nth 3 def))
footnotes))))
(funcall set-new-label footnote label new)))))))))
(set-marker marker-min nil)
(set-marker marker-max nil)))
(org-element-normalize-string (buffer-string))))
I don't like how plists are indented in Elisp.
;; current desired Python (for comparison)
;; '(a 1 '(a 1 {'a': 1,
;; b 2 b 2 'b': 2,
;; c 3) c 3) 'c': 3}
Tried on M-x emacs-version 24.3.1, ran emacs -Q, typed the plist and pressed C-x h C-M-\.
This indentation makes sense when it isn't a list:
(mapcar (lambda (x) (x + 1))
'(1 2 3 4))
How do I change formatting settings so that only plists (or, if that's impossible, all quoted lists) have the desired rectangular indentation, but indentation of everything else stays the same? I need this stored locally in an .el file, so that when I edit this file, it is indented as desired, but this behavior doesn't end up anywhere else.
Found it:
(setq lisp-indent-function 'common-lisp-indent-function)
Here's a sample file:
(setq x '(a 1
b 2
c 3))
;;; Local Variables:
;;; lisp-indent-function: common-lisp-indent-function
;;; End:
I'll just dump my whole indentation config here:
(setq lisp-indent-function 'common-lisp-indent-function)
(put 'cl-flet 'common-lisp-indent-function
(get 'flet 'common-lisp-indent-function))
(put 'cl-labels 'common-lisp-indent-function
(get 'labels 'common-lisp-indent-function))
(put 'if 'common-lisp-indent-function 2)
(put 'dotimes-protect 'common-lisp-indent-function
(get 'when 'common-lisp-indent-function))
You can fix this (in my opinion) bug by overriding lisp-indent-function. The original source of the hack was this Github Gist, which was referenced with some more explanation from this Emacs Stack Exchange answer.
However, I was very uncomfortable overriding a core function like this. For one, it's very opaque—how is a reader supposed to tell what is changed? And worse—what if the official definition of lisp-indent-function changed in the future? How would I know that I needed to update my hack?
As a response, I created the library el-patch, which is specifically designed to address this problem. After installing the package, you can override lisp-indent-function as follows:
(el-patch-defun lisp-indent-function (indent-point state)
"This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
if the arguments of a Lisp function call should be indented specially.
INDENT-POINT is the position at which the line being indented begins.
Point is located at the point to indent under (for default indentation);
STATE is the `parse-partial-sexp' state for that position.
If the current line is in a call to a Lisp function that has a non-nil
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
it specifies how to indent. The property value can be:
* `defun', meaning indent `defun'-style
(this is also the case if there is no property and the function
has a name that begins with \"def\", and three or more arguments);
* an integer N, meaning indent the first N arguments specially
(like ordinary function arguments), and then indent any further
arguments like a body;
* a function to call that returns the indentation (or nil).
`lisp-indent-function' calls this function with the same two arguments
that it itself received.
This function returns either the indentation to use, or nil if the
Lisp function does not specify a special indentation."
(el-patch-let (($cond (and (elt state 2)
(el-patch-wrap 1 1
(or (not (looking-at "\\sw\\|\\s_"))
(looking-at ":")))))
($then (progn
(if (not (> (save-excursion (forward-line 1) (point))
calculate-lisp-indent-last-sexp))
(progn (goto-char calculate-lisp-indent-last-sexp)
(beginning-of-line)
(parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)))
;; Indent under the list or under the first sexp on the same
;; line as calculate-lisp-indent-last-sexp. Note that first
;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(backward-prefix-chars)
(current-column)))
($else (let ((function (buffer-substring (point)
(progn (forward-sexp 1) (point))))
method)
(setq method (or (function-get (intern-soft function)
'lisp-indent-function)
(get (intern-soft function) 'lisp-indent-hook)))
(cond ((or (eq method 'defun)
(and (null method)
(> (length function) 3)
(string-match "\\`def" function)))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(method
(funcall method indent-point state))))))
(let ((normal-indent (current-column))
(el-patch-add
(orig-point (point))))
(goto-char (1+ (elt state 1)))
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
(el-patch-swap
(if $cond
;; car of form doesn't seem to be a symbol
$then
$else)
(cond
;; car of form doesn't seem to be a symbol, or is a keyword
($cond $then)
((and (save-excursion
(goto-char indent-point)
(skip-syntax-forward " ")
(not (looking-at ":")))
(save-excursion
(goto-char orig-point)
(looking-at ":")))
(save-excursion
(goto-char (+ 2 (elt state 1)))
(current-column)))
(t $else))))))
Here is another less heavyweight solution, based on emacsql-fix-vector-indentation. An advice around calculate-lisp-indent is sufficient.
This only works for plists that use keywords as keys, but that covers a majority of plists. To make this work on quoted lists instead, you could change the looking-at regexp to detect the ' or "`", but that will not cover, say, a nested list.
This can further be packaged up into a minor mode if there is a need to turn it off.
(defun my/inside-plist? ()
"Is point situated inside a plist?
We determine a plist to be a list that starts with a keyword."
(let ((start (point)))
(save-excursion
(beginning-of-defun)
(let ((sexp (nth 1 (parse-partial-sexp (point) start))))
(when sexp
(setf (point) sexp)
(looking-at (rx "(" (* (syntax whitespace)) ":")))))))
(define-advice calculate-lisp-indent (:around (func &rest args)
plist)
(if (save-excursion
(beginning-of-line)
(my/inside-plist?))
(let ((lisp-indent-offset 1))
(apply func args))
(apply func args)))
I am working with emacs24 with cc-mode, I want to know how to make my emacs more "clever". After I type a }, it will auto insert a new line and indent as excepted. I want to know how to switch the point to previous line.
For example, when i define a function, Now my emacs behavior is:
void f()
{
}
//point
"//point" is the position of cursor after } was input.
But i want is this:
void f()
{
//point
}
I hope the position of cursor can switch to previous line and indent automatically.
I know emacs can do this, but I don't know how to do it, who can help me?
I think you are after these.. C-M-u, C-M-d, C-M-f and C-M-b
Practice a bit... They are kind of global and they do behave contextually in almost all modes..
UPDATE:
ohh.. It seems you want to place the cursor automatically.. actually in more general Emacs will help you not to type } at all. I mean emacs can insert closing paran automatically.
There is inbuilt one
electric pair mode
third party
autopair.el
I don't trust anything electric, so I wrote this function.
(defconst insert-logical-brackets-logical-bracket-begin "{")
(defconst insert-logical-brackets-logical-bracket-end "}")
(defconst insert-logical-brackets-default-style 0)
(make-variable-buffer-local 'logical-bracket-begin)
(make-variable-buffer-local 'logical-bracket-end)
(make-variable-buffer-local 'insert-logical-brackets-default-style)
(defun insert-logical-brackets(&optional style)
"If STYLE = 0(default, according to `insert-logical-brackets-default-style' value), make a newline before opening bracket, if line is not empty. Make a newline after closing bracket, if there is something after this bracket. Make two newlines in the middle.
If STYLE = 1, don't make newlines before opening a bracket(one of c styles).
If STYLE = 2, don't make newlines before opening and after closing bracket.
If STYLE = 3, allways make all newlines.
If STYLE is not nil, don't make newlines between brackets(still makes before/after lines)."
(interactive "P")
(when (eq style nil)
(setq style insert-logical-brackets-default-style))
(funcall indent-line-function)
(unless (or (eq 1 style) (eq 2 style))
(when (or (/= (point) (save-excursion (back-to-indentation) (point))) (eq 3 style))
(newline)
(funcall indent-line-function)))
(unless (and (integerp style) (= 2 style))
(when (or (not (looking-at "\n")) (eq 3 style))
(newline)
(funcall indent-line-function)
(forward-line -1)
(goto-char (point-at-eol))))
(insert logical-bracket-begin)
(funcall indent-line-function)
(let ((return-point (point)))
(when (or (not style) (or (eq 0 style) (eq 1 style) (eq 2 style) (eq 3 style)))
(newline)
(funcall indent-line-function)
(setq return-point (point))
(newline))
(insert logical-bracket-end)
(funcall indent-line-function)
(goto-char return-point)))
Take a look at template systems like yasnippet: http://www.emacswiki.org/emacs/CategoryTemplates
auto-indent-mode maybe what you want!
ESS/Stata mode in emacs incorrectly indents lines that follow lines ending in operators. It seems to incorrectly interpret these lines as multiline commands.
For example:
gen foo = 1
/* generate another variable */
gen bar = 1
The line "gen bar = 1" should not be indented. It looks like EMACS interprets the trailing slash in the comment as an operator, and thinks this line of code spans two lines.
In fact, multiline commands in stata have 3 trailing slashes, and newlines without 3 trailing slashes indicate the end of a statement. e.g. the following indentation would be correct:
gen bar = 1
gen ///
foo = 1
Is there something I can put in my .emacs to correct this behavior? I don't want to give up automatic tabbing completely - it works very well for everything except comments that /* look like this */.
Thanks,
Pnj
You're right, ESS interprets the trailing / as an indication of line continuation. This is hard-coded into the function ess-continued-statement-p, so to modify the behaviour you have to rewrite the code. The following code (in your .emacs) works for your examples.
(eval-after-load 'ess-mode
'(defun ess-continued-statement-p ()
"this is modified code"
(let ((eol (point)))
(save-excursion
(cond ((memq (preceding-char) '(nil ?\, ?\; ?\} ?\{ ?\]))
nil)
;; ((bolp))
((= (preceding-char) ?\))
(forward-sexp -2)
(looking-at "if\\b[ \t]*(\\|function\\b[ \t]*(\\|for\\b[ \t]*(\\|while\\b[ \t]*("))
((progn (forward-sexp -1)
(and (looking-at "else\\b\\|repeat\\b")
(not (looking-at "else\\s_\\|repeat\\s_"))))
(skip-chars-backward " \t")
(or (bolp)
(= (preceding-char) ?\;)))
(t
(progn (goto-char eol)
(skip-chars-backward " \t")
(or (and (> (current-column) 1)
(save-excursion (backward-char 1)
;;;; Modified code starts here: ;;;;
(or (looking-at "[-:+*><=]")
(and (looking-at "/")
(save-excursion (backward-char 1)
(not (looking-at "*")))))))
;;;; End of modified code ;;;;
(and (> (current-column) 3)
(progn (backward-char 3)
(looking-at "%[^ \t]%")))))))))))