unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: A Soare <alinsoar@voila.fr>
To: "Lennart Borgman (gmail)" <lennart.borgman@gmail.com>
Cc: "Emacs   Dev  \[emacs-devel\]" <emacs-devel@gnu.org>
Subject: Re: An automaton for indenting the lisp code in linear time
Date: Tue,  1 Jul 2008 04:24:59 +0200 (CEST)	[thread overview]
Message-ID: <24276243.2453471214879099434.JavaMail.www@wwinf4604> (raw)

[-- Attachment #1: Type: text/plain, Size: 8666 bytes --]

> 
> Maybe it would be good if you explained a bit more how to use the 
> example code. (The example code seems to have difficulties with lines 
> that have indentation 0.)
> 

For all: please report me problems , unconsidered cases, where the indent does not work. When I wrote it, I tested it on a limited number (little) of examples.

This version works just on the examples that I tested. Today I realised that it did not work on all the uses of comments.

I attach again here my last version. The automaton is written long time ago, and yesterday I wrote just the examples: consult the 2 examples to understand.

The examples are just suggestions to understand how it works:

I explain here for all to understand:

1. Put the text of the function you want to indent.

2. Put after the function that you want to indent this code (leave before the "(let" a space):


 (let (indent-lines
       state 
       (l (prog2 (beginning-of-line) (point))))
   (save-excursion (lisp-indent-automaton))
   indent-lines)


3. Evaluate this . You will get a list L whose (mapcar 'car L) is the correct indentation.

I believed that there are hackers of lisp here and you see imediately how it works :)


EXAMPLE:


(defun lisp-indent-automaton (&optional end state indent-what-argument start-col)
  "
   an automaton to indent the lisp code
   VERSION: NEW
  "
  (let (
        indent
        indent-next-line-to
        )
    (or end (beginning-of-line) (setq end (point)))
    (or indent-what-argument
        (setq indent-what-argument 0))
    (or state (beginning-of-defun))
    (and (null start-col) (setq start-col 0))
    (catch 'STOP
      (while (< (point) end)
        (cond ((and (elt state 8)
                    (not (elt state 4)))
               ;; inside a string
               (and (looking-at "\\s>")
                    (setq indent-lines (cons (lisp-indent-value) indent-lines)))
               (setq state (lisp-indent-next-state))
               )
              ((looking-at "\\s\"")
               ;; the start of a string
               (setq indent (append indent (list :s () (current-column)))
                     state (lisp-indent-next-state))
               )
              ((looking-at "\\s\(")
               ;; open a parenthesis
               (let ((col (if indent-lines
                              (+ (current-column) (- (caar indent-lines) (cdar indent-lines)))
                            (current-column))))
                 (setq indent (append indent (list :l () col))
                       state (lisp-indent-automaton end (lisp-indent-next-state)
                                                    (lisp-indent-what-argument)
                                                    col)))
               )
              ((looking-at "\\s\)")
               ;; close a parenthesis
               (throw 'STOP (lisp-indent-next-state))
               )
              ((looking-at "\\s ")
               ;; spaces are ignored
               (while (looking-at "[[:blank:]]")
                 (setq state (lisp-indent-next-state)))
               )
              ((looking-at "\\sw\\|\\s_")
               ;; a function or a parameter
               (let* ((col (if indent-lines
                              (+ (current-column) (- (caar indent-lines) (cdar indent-lines)))
                            (current-column)))
                      r
                      (w (catch 'WORD
                           (while t
                             (setq r (concat r (string (following-char))))
                             (setq state (lisp-indent-next-state))
                             (and (not (looking-at "\\w\\|\\s_"))
                                  (throw 'WORD r))))))
                 (setq indent (append indent (list :w w col))))
               )
              ((looking-at "\\s<")
               ;; start of a comment
               (while (not (looking-at "\\s>"))
                 (setq state (lisp-indent-next-state)))
               )
              ((looking-at "\\s\'")
               ;; quote
               (setq indent (append indent (list :q () (current-column))))
               (setq state (lisp-indent-next-state))
               )
              ((looking-at "\\s\\")
               ;; a special character
               (setq state (lisp-indent-next-state 2))
               )
              ((looking-at "\\s>")
               ;; end of line
               (setq state (lisp-indent-next-state))
               (while (looking-at "\\s ")
                 (setq state (lisp-indent-next-state)))
               (setq indent-lines (cons (cons (lisp-indent-value) (current-column)) indent-lines))
               )
              (t
               (error "oops! unknown class of syntax ! (indent failed at position %d)" (point))))))))





<ONE SPACE>(let (indent-lines
       state 
       (l (prog2 (beginning-of-line) (point))))
   (save-excursion (lisp-indent-automaton))
   indent-lines)

EVALUATE HERE WITH M-x M-e


((0 . 0) (0 . 0) (0 . 0) (2 . 3) (2 . 3) (6 . 7) (6 . 7) (0 . 1) (0 . 0) (0 . 0) (0 . 0) (0 . 0) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (34 . 34) (29 . 29) (29 . 29) (29 . 29) (27 . 27) (22 . 22) (22 . 22) (29 . 28) (31 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (52 . 52) (52 . 52) (23 . 23) (17 . 17) (28 . 28) (30 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (21 . 21) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (20 . 20) (15 . 15) (15 . 15) (20 . 20) (8 . 8) (6 . 6) (4 . 4) (4 . 4) (4 . 4) (8 . 8) (4 . 4) (4 . 4) (8 . 8) (8 . 8) (8 . 8) (2 . 2) nil nil nil (2 . 2))




(mapcar 'car 
'((0 . 0) (0 . 0) (0 . 0) (2 . 3) (2 . 3) (6 . 7) (6 . 7) (0 . 1) (0 . 0) (0 . 0) (0 . 0) (0 . 0) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (34 . 34) (29 . 29) (29 . 29) (29 . 29) (27 . 27) (22 . 22) (22 . 22) (29 . 28) (31 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (52 . 52) (52 . 52) (23 . 23) (17 . 17) (28 . 28) (30 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (21 . 21) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (20 . 20) (15 . 15) (15 . 15) (20 . 20) (8 . 8) (6 . 6) (4 . 4) (4 . 4) (4 . 4) (8 . 8) (4 . 4) (4 . 4) (8 . 8) (8 . 8) (8 . 8) (2 . 2) nil nil nil (2 . 2)))


(0 0 0 2 2 6 6 0 0 0 0 0 15 14 15 15 17 15 15 15 14 15 15 15 14 15 15 15 15 14 15 17 15 15 14 15 17 34 29 29 29 27 22 22 29 31 15 15 14 15 17 15 15 14 15 15 15 14 15 52 52 23 17 28 30 15 15 14 15 21 15 15 14 15 15 20 15 15 20 8 6 4 4 4 8 4 4 8 8 8 2 nil nil nil 2)

THIS IS THE CORRECT INDENTATION.


(mapcar 'cdr 
'((0 . 0) (0 . 0) (0 . 0) (2 . 3) (2 . 3) (6 . 7) (6 . 7) (0 . 1) (0 . 0) (0 . 0) (0 . 0) (0 . 0) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (34 . 34) (29 . 29) (29 . 29) (29 . 29) (27 . 27) (22 . 22) (22 . 22) (29 . 28) (31 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (17 . 17) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (52 . 52) (52 . 52) (23 . 23) (17 . 17) (28 . 28) (30 . 30) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (21 . 21) (15 . 15) (15 . 15) (14 . 14) (15 . 15) (15 . 15) (20 . 20) (15 . 15) (15 . 15) (20 . 20) (8 . 8) (6 . 6) (4 . 4) (4 . 4) (4 . 4) (8 . 8) (4 . 4) (4 . 4) (8 . 8) (8 . 8) (8 . 8) (2 . 2) nil nil nil (2 . 2)))


(0 0 0 3 3 7 7 1 0 0 0 0 15 14 15 15 17 15 15 15 14 15 15 15 14 15 15 15 15 14 15 17 15 15 14 15 17 34 29 29 29 27 22 22 28 30 15 15 14 15 17 15 15 14 15 15 15 14 15 52 52 23 17 28 30 15 15 14 15 21 15 15 14 15 15 20 15 15 20 8 6 4 4 4 8 4 4 8 8 8 2 nil nil nil 2)

THIS IS THE OLD INDENTATION (nil means that there are spaces)


Read the indent-defun to see how to use it.



Alin Soare.



____________________________________________________

Sur le mail Voila, vous pouvez personnaliser l’anti-spam ! http://mail.voila.fr

[-- Attachment #2: indent.el --]
[-- Type: application/octet-stream, Size: 8119 bytes --]


(defun lisp-indent-value ()
  "
  VERSION : NEW
  "
  (cond ((elt state 3)
         ;; Inside a string, don't change indentation.
         nil
         )
        ((null (cadr state))
         ;; outside an expression align to the left column
         0
         )
        ((null indent)
         ;; an empty parethesis
         (1+ start-col))
        ((and (integerp lisp-indent-offset)
              (integerp (cadr indent)))
         ;; indent by constant offset
         (+ (cadr indent) lisp-indent-offset)
         )
        ((looking-at "\\s<\\s<\\s<")
         ;; Comments that start with three semicolons or more, should
         ;; start at the left margin
         0
         )
        ((eq (car indent) :l)
         ;; indent beneath a list
         (caddr indent)
         )
         ((and (eq (car indent) :w)
              (stringp (cadr indent))
              (>= (length (cadr indent)) 3)
              (string-equal "def" (substring (cadr indent) 0 3)))
         ;; indent a def-form
         (+ lisp-body-indent start-col)
         )
        ((and (eq (car indent) :w)
              (wholenump (get (intern-soft (cadr indent)) 'lisp-indent-function)))
         ;; here there is a special form
         (setq oo (or (get (intern-soft (cadr indent)) 'lisp-indent-function)
                      (get (intern-soft (cadr indent)) 'lisp-indent-hook)))
         (if (> (length indent) (* 3 oo))
             (+ lisp-body-indent start-col)
           (+ (* 2 lisp-body-indent) start-col))
         )
        ((and (eq (car indent) :w)
              (get (intern-soft (cadr indent)) 'lisp-indent-hook)
              nil)
         ;; indent defined by another function
         ;; not defined yet
         (funcall (get (intern-soft (cadr indent)) 'lisp-indent-hook))
         )
        ((and (equal (following-char) ?:)
              nil)
         ;; indent of a constant symbol
         ;; not yed defined
         (caddr indent)
         )
        ((and (eq (car indent) :w)
              (> (length indent) 3))
         ;; indent beneath the `indent-what-argument' parameter
         (elt indent (+ 3 (1- (* 3 indent-what-argument))))
         )
        (t
         ;;  the first parameter of a function call
         (caddr indent))
         ))

(defun lisp-indent-what-argument nil
  (cond ((and (eq (car indent) :w)
              (stringp (cadr indent))
              (>= (length (cadr indent)) 3)
              (string-equal "def" (substring (cadr indent) 0 3))
              (= (length indent) 9))
         ;; if the second parameter of defun is a list, then this list
         ;; is not a function call
         0)
        ((or
          (and (>= (length indent) 6)
               (eq (elt indent (- (length indent ) 6)) :q))
          (and (eq (car indent) :w)
               (stringp (cadr indent))
               (string-equal "quote" (cadr indent))))
         ;; a quoted list is not a function call
         0
         )
        (t
         ;; for the rest, always indent beneath the first parameter of
         ;; the function...
         1)))

(defun lisp-indent-next-state (&optional n)
  (parse-partial-sexp (point) (+ (or n 1) (point)) () () state))

(defun lisp-indent-automaton (&optional end state indent-what-argument start-col)
  "
   an automaton to indent the lisp code
   VERSION: NEW
  "
  (let (
        indent
        indent-next-line-to
        )
    (or end (beginning-of-line) (setq end (point)))
    (or indent-what-argument
        (setq indent-what-argument 0))
    (or state (beginning-of-defun))
    (and (null start-col) (setq start-col 0))
    (catch 'STOP
      (while (< (point) end)
        (cond ((and (elt state 8)
                    (not (elt state 4)))
               ;; inside a string
               (and (looking-at "\\s>")
                    (setq indent-lines (cons (lisp-indent-value) indent-lines)))
               (setq state (lisp-indent-next-state))
               )
              ((looking-at "\\s\"")
               ;; the start of a string
               (setq indent (append indent (list :s () (current-column)))
                     state (lisp-indent-next-state))
               )
              ((looking-at "\\s\(")
               ;; open a parenthesis
               (let ((col (if indent-lines
                              (+ (current-column) (- (caar indent-lines) (cdar indent-lines)))
                            (current-column))))
                 (setq indent (append indent (list :l () col))
                       state (lisp-indent-automaton end (lisp-indent-next-state)
                                                    (lisp-indent-what-argument)
                                                    col)))
               )
              ((looking-at "\\s\)")
               ;; close a parenthesis
               (throw 'STOP (lisp-indent-next-state))
               )
              ((looking-at "\\s ")
               ;; spaces are ignored
               (while (looking-at "[[:blank:]]")
                 (setq state (lisp-indent-next-state)))
               )
              ((looking-at "\\sw\\|\\s_")
               ;; a function or a parameter
               (let* ((col (if indent-lines
                              (+ (current-column) (- (caar indent-lines) (cdar indent-lines)))
                            (current-column)))
                      r
                      (w (catch 'WORD
                           (while t
                             (setq r (concat r (string (following-char))))
                             (setq state (lisp-indent-next-state))
                             (and (not (looking-at "\\w\\|\\s_"))
                                  (throw 'WORD r))))))
                 (setq indent (append indent (list :w w col))))
               )
              ((looking-at "\\s<")
               ;; start of a comment
               (while (not (looking-at "\\s>"))
                 (setq state (lisp-indent-next-state)))
               )
              ((looking-at "\\s\'")
               ;; quote
               (setq indent (append indent (list :q () (current-column))))
               (setq state (lisp-indent-next-state))
               )
              ((looking-at "\\s\\")
               ;; a special character
               (setq state (lisp-indent-next-state 2))
               )
              ((looking-at "\\s>")
               ;; end of line
               (setq state (lisp-indent-next-state))
               (while (looking-at "\\s ")
                 (setq state (lisp-indent-next-state)))
               (setq indent-lines (cons (cons (lisp-indent-value) (current-column)) indent-lines))
               )
              (t
               (error "oops! unknown class of syntax ! (indent failed at position %d)" (point))))))))

(defun indent-defun-example nil
  "
  An example how to use the automatonin for indentig a whole region
  of lisp code in linear time (a function from the beginning to the point).
  "
  (let* (indent-lines
         state 
         l)
    (save-excursion
      (lisp-indent-automaton)
      (dolist (i indent-lines)
        (when (consp i)
          (setq l (prog2 (beginning-of-line) (point)))
          (skip-chars-forward "[[:blank:]]")
          (when (not (zerop (-  (- l (point)) (- (cdr i) (car i)))))
            (delete-region l (point))
            (indent-to (car i))))
        (previous-line)))))

(defun indent-line-example nil
  "
  An example how to indent a line of code using the automaton
  "
  (let* (indent-lines
         state 
         dif
         (pos (point-marker))
         (l (prog2 (beginning-of-line) (point))))
    (save-excursion (lisp-indent-automaton))
    (if (not (consp (car indent-lines)))
        nil
      (setq dif
            (catch 'DIF
              (dolist (i (cdr indent-lines))
                (and (consp i)
                     (throw 'DIF (- (cdr i) (car i)))))))
      (skip-chars-forward "[[:blank:]]")
      (when (not (zerop (- (+ (caar indent-lines) dif) (- (point) l))))
        (delete-region l (point))
        (indent-to (+ (caar indent-lines) dif))
        (goto-char (max pos (point)))))))




             reply	other threads:[~2008-07-01  2:24 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-07-01  2:24 A Soare [this message]
  -- strict thread matches above, loose matches on Subject: below --
2008-07-04 21:14 An automaton for indenting the lisp code in linear time A Soare
2008-07-01 15:28 A Soare
2008-07-01 13:59 A Soare
2008-07-01  0:49 A Soare
2008-07-01  1:06 ` Lennart Borgman (gmail)
2008-06-30  0:41 A Soare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=24276243.2453471214879099434.JavaMail.www@wwinf4604 \
    --to=alinsoar@voila.fr \
    --cc=emacs-devel@gnu.org \
    --cc=lennart.borgman@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).