Skip to content

Instantly share code, notes, and snippets.

@imakado
Created July 13, 2009 05:39
Show Gist options
  • Save imakado/145957 to your computer and use it in GitHub Desktop.
Save imakado/145957 to your computer and use it in GitHub Desktop.
;; for typester
(defun cperl-calculate-indent (&optional parse-data) ; was parse-start
"Return appropriate indentation for current line as Perl code.
In usual case returns an integer: the column to indent to.
Returns nil if line starts inside a string, t if in a comment.
Will not correct the indentation for labels, but will correct it for braces
and closing parentheses and brackets."
;; This code is still a broken architecture: in some cases we need to
;; compensate for some modifications which `cperl-indent-line' will add later
(save-excursion
(let ((i (cperl-sniff-for-indent parse-data)) what p)
(cond
;;((or (null i) (eq i t) (numberp i))
;; i)
((vectorp i)
(setq what (assoc (elt i 0) cperl-indent-rules-alist))
(cond
(what (cadr what)) ; Load from table
;;
;; Indenters for regular expressions with //x and qw()
;;
((eq 'REx-part2 (elt i 0)) ;; [self start] start of /REP in s//REP/x
(goto-char (elt i 1))
(condition-case nil ; Use indentation of the 1st part
(forward-sexp -1))
(current-column))
((eq 'indentable (elt i 0)) ; Indenter for REGEXP qw() etc
(cond ;;; [indentable terminator start-pos is-block]
((eq 'terminator (elt i 1)) ; Lone terminator of "indentable string"
(goto-char (elt i 2)) ; After opening parens
(1- (current-column)))
((eq 'first-line (elt i 1)); [indentable first-line start-pos]
(goto-char (elt i 2))
(+ (or cperl-regexp-indent-step cperl-indent-level)
-1
(current-column)))
((eq 'cont-line (elt i 1)); [indentable cont-line pos prev-pos first-char start-pos]
;; Indent as the level after closing parens
(goto-char (elt i 2)) ; indent line
(skip-chars-forward " \t)") ; Skip closing parens
(setq p (point))
(goto-char (elt i 3)) ; previous line
(skip-chars-forward " \t)") ; Skip closing parens
;; Number of parens in between:
(setq p (nth 0 (parse-partial-sexp (point) p))
what (elt i 4)) ; First char on current line
(goto-char (elt i 3)) ; previous line
(+ (* p (or cperl-regexp-indent-step cperl-indent-level))
(cond ((eq what ?\) )
(- cperl-close-paren-offset)) ; compensate
((eq what ?\| )
(- (or cperl-regexp-indent-step cperl-indent-level)))
(t 0))
(if (eq (following-char) ?\| )
(or cperl-regexp-indent-step cperl-indent-level)
0)
(current-column)))
(t
(error "Unrecognized value of indent: %s" i))))
;;
;; Indenter for stuff at toplevel
;;
((eq 'toplevel (elt i 0)) ;; [toplevel start char-after state immed-after-block]
(+ (save-excursion ; To beg-of-defun, or end of last sexp
(goto-char (elt i 1)) ; start = Good place to start parsing
(- (current-indentation) ;
(if (elt i 4) cperl-indent-level 0))) ; immed-after-block
(if (eq (elt i 2) ?{) cperl-continued-brace-offset 0) ; char-after
;; Look at previous line that's at column 0
;; to determine whether we are in top-level decls
;; or function's arg decls. Set basic-indent accordingly.
;; Now add a little if this is a continuation line.
(if (elt i 3) ; state (XXX What is the semantic???)
0
cperl-continued-statement-offset)))
;;
;; Indenter for stuff in "parentheses" (or brackets, braces-as-hash)
;;
((eq 'in-parens (elt i 0))
;; in-parens char-after old-indent-point is-brace containing-sexp
;; group is an expression, not a block:
;; indent to just after the surrounding open parens,
;; skip blanks if we do not close the expression.
(+ (progn
(goto-char (elt i 2)) ; old-indent-point
(current-column))
(if (and (elt i 3) ; is-brace
(eq (elt i 1) ?\})) ; char-after
;; Correct indentation of trailing ?\}
(+ cperl-indent-level cperl-close-paren-offset)
0)))
;;
;; Indenter for continuation lines
;;
((eq 'continuation (elt i 0))
;; [continuation statement-start char-after is-block is-brace]
(goto-char (elt i 1)) ; statement-start
(+ (if (memq (elt i 2) (append "}])" nil)) ; char-after
0 ; Closing parenth
cperl-continued-statement-offset)
(if (or (elt i 3) ; is-block
(not (elt i 4)) ; is-brace
(not (eq (elt i 2) ?\}))) ; char-after
0
;; Now it is a hash reference
(+ cperl-indent-level cperl-close-paren-offset))
;; Labels do not take :: ...
(if (looking-at "\\(\\w\\|_\\)+[ \t]*:")
(if (> (current-indentation) cperl-min-label-indent)
(- (current-indentation) cperl-label-offset)
;; Do not move `parse-data', this should
;; be quick anyway (this comment comes
;; from different location):
(cperl-calculate-indent))
(current-column))
(if (eq (elt i 2) ?\{) ; char-after
cperl-continued-brace-offset 0)))
;;
;; Indenter for lines in a block which are not leading lines
;;
((eq 'have-prev-sibling (elt i 0))
;; [have-prev-sibling sibling-beg colon-line-end block-start]
(goto-char (elt i 1)) ; sibling-beg
(if (> (elt i 2) (point)) ; colon-line-end; have label before point
(if (> (current-indentation)
cperl-min-label-indent)
(- (current-indentation) cperl-label-offset)
;; Do not believe: `max' was involved in calculation of indent
(+ cperl-indent-level
(save-excursion
(goto-char (elt i 3)) ; block-start
(current-indentation))))
(current-column)))
;;
;; Indenter for the first line in a block
;;
((eq 'code-start-in-block (elt i 0))
;;[code-start-in-block before-brace char-after
;; is-a-HASH-ref brace-is-first-thing-on-a-line
;; group-starts-before-start-of-sub start-of-control-group]
(goto-char (elt i 1))
;; For open brace in column zero, don't let statement
;; start there too. If cperl-indent-level=0,
;; use cperl-brace-offset + cperl-continued-statement-offset instead.
(+ (if (and (bolp) (zerop cperl-indent-level))
(+ cperl-brace-offset cperl-continued-statement-offset)
cperl-indent-level)
(if (and (elt i 3) ; is-a-HASH-ref
(eq (elt i 2) ?\})) ; char-after: End of a hash reference
(+ cperl-indent-level cperl-close-paren-offset)
0)
;; Unless openbrace is the first nonwhite thing on the line,
;; add the cperl-brace-imaginary-offset.
(if (elt i 4) 0 ; brace-is-first-thing-on-a-line
cperl-brace-imaginary-offset)
(progn
(goto-char (elt i 6)) ; start-of-control-group
(if (elt i 5) ; group-starts-before-start-of-sub
;; handle |fn(sub {..
;; | `!!'
(if (save-excursion
(ignore-errors
(skip-chars-backward " \t")
(or (string= (char-to-string (preceding-char)) "(")
(string= (char-to-string (preceding-char)) ","))))
(save-excursion (back-to-indentation)
(current-column))
(current-column))
;; Get initial indentation of the line we are on.
;; If line starts with label, calculate label indentation
(if (save-excursion
(beginning-of-line)
(looking-at "[ \t]*[a-zA-Z_][a-zA-Z_0-9]*:[^:]"))
(if (> (current-indentation) cperl-min-label-indent)
(- (current-indentation) cperl-label-offset)
;; Do not move `parse-data', this should
;; be quick anyway:
(cperl-calculate-indent))
(current-indentation))))))
(t
(error "Unrecognized value of indent: %s" i))))
(t
(error "Got strange value of indent: %s" i))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment