Moving all of my Emacs-related files into their own directory at the root of this repository.
		
			
				
	
	
		
			304 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
;;; reason-indent.el --- Indentation functions for ReasonML -*-lexical-binding: t-*-
 | 
						||
 | 
						||
;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
 | 
						||
 | 
						||
;;; Commentary:
 | 
						||
 | 
						||
;; Indentation functions for Reason.
 | 
						||
 | 
						||
;;; Code:
 | 
						||
 | 
						||
(defconst reason-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*")
 | 
						||
 | 
						||
(defcustom reason-indent-offset 2
 | 
						||
  "Indent Reason code by this number of spaces."
 | 
						||
  :type 'integer
 | 
						||
  :group 'reason-mode
 | 
						||
  :safe #'integerp)
 | 
						||
 | 
						||
(defun reason-looking-back-str (str)
 | 
						||
  "Like `looking-back' but for fixed strings rather than regexps.
 | 
						||
Works around some regexp slowness.
 | 
						||
Argument STR string to search for."
 | 
						||
  (let ((len (length str)))
 | 
						||
    (and (> (point) len)
 | 
						||
         (equal str (buffer-substring-no-properties (- (point) len) (point))))))
 | 
						||
 | 
						||
(defun reason-paren-level ()
 | 
						||
  "Get the level of nesting inside parentheses."
 | 
						||
  (nth 0 (syntax-ppss)))
 | 
						||
 | 
						||
(defun reason-in-str-or-cmnt ()
 | 
						||
  "Return whether point is currently inside a string or a comment."
 | 
						||
  (nth 8 (syntax-ppss)))
 | 
						||
 | 
						||
(defun reason-rewind-past-str-cmnt ()
 | 
						||
  "Rewind past string or comment."
 | 
						||
  (goto-char (nth 8 (syntax-ppss))))
 | 
						||
 | 
						||
(defun reason-rewind-irrelevant ()
 | 
						||
  "Rewind past irrelevant characters (whitespace of inside comments)."
 | 
						||
  (interactive)
 | 
						||
  (let ((starting (point)))
 | 
						||
    (skip-chars-backward "[:space:]\n")
 | 
						||
    (if (reason-looking-back-str "*/") (backward-char))
 | 
						||
    (if (reason-in-str-or-cmnt)
 | 
						||
        (reason-rewind-past-str-cmnt))
 | 
						||
    (if (/= starting (point))
 | 
						||
        (reason-rewind-irrelevant))))
 | 
						||
 | 
						||
(defun reason-align-to-expr-after-brace ()
 | 
						||
  "Align the expression at point to the expression after the previous brace."
 | 
						||
  (save-excursion
 | 
						||
    (forward-char)
 | 
						||
    ;; We don't want to indent out to the open bracket if the
 | 
						||
    ;; open bracket ends the line
 | 
						||
    (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
 | 
						||
      (when (looking-at "[[:space:]]")
 | 
						||
        (forward-word 1)
 | 
						||
        (backward-word 1))
 | 
						||
      (current-column))))
 | 
						||
 | 
						||
(defun reason-align-to-prev-expr ()
 | 
						||
  "Align the expression at point to the previous expression."
 | 
						||
  (let ((alignment (save-excursion
 | 
						||
                     (forward-char)
 | 
						||
                     ;; We don't want to indent out to the open bracket if the
 | 
						||
                     ;; open bracket ends the line
 | 
						||
                     (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
 | 
						||
                       (if (looking-at "[[:space:]]")
 | 
						||
                           (progn
 | 
						||
                             (forward-word 1)
 | 
						||
                             (backward-word 1))
 | 
						||
                         (backward-char))
 | 
						||
                       (current-column)))))
 | 
						||
    (if (not alignment)
 | 
						||
        (save-excursion
 | 
						||
          (forward-char)
 | 
						||
          (forward-line)
 | 
						||
          (back-to-indentation)
 | 
						||
          (current-column))
 | 
						||
      alignment)))
 | 
						||
 | 
						||
;;; Start of a reason binding
 | 
						||
(defvar reason-binding
 | 
						||
  (regexp-opt '("let" "type" "module" "fun")))
 | 
						||
 | 
						||
(defun reason-beginning-of-defun (&optional arg)
 | 
						||
  "Move backward to the beginning of the current defun.
 | 
						||
 | 
						||
With ARG, move backward multiple defuns.  Negative ARG means
 | 
						||
move forward.
 | 
						||
 | 
						||
This is written mainly to be used as `beginning-of-defun-function'.
 | 
						||
Don't move to the beginning of the line.  `beginning-of-defun',
 | 
						||
which calls this, does that afterwards."
 | 
						||
  (interactive "p")
 | 
						||
  (re-search-backward (concat "^\\(" reason-binding "\\)\\_>")
 | 
						||
                      nil 'move (or arg 1)))
 | 
						||
 | 
						||
(defun reason-end-of-defun ()
 | 
						||
  "Move forward to the next end of defun.
 | 
						||
 | 
						||
With argument, do it that many times.
 | 
						||
Negative argument -N means move back to Nth preceding end of defun.
 | 
						||
 | 
						||
Assume that this is called after ‘beginning-of-defun’.  So point is
 | 
						||
at the beginning of the defun body.
 | 
						||
 | 
						||
This is written mainly to be used as `end-of-defun-function' for Reason."
 | 
						||
  (interactive)
 | 
						||
  ;; Find the opening brace
 | 
						||
  (if (re-search-forward "[{]" nil t)
 | 
						||
      (progn
 | 
						||
        (goto-char (match-beginning 0))
 | 
						||
        ;; Go to the closing brace
 | 
						||
        (condition-case nil
 | 
						||
            (forward-sexp)
 | 
						||
          (scan-error
 | 
						||
           ;; The parentheses are unbalanced; instead of being unable to fontify, just jump to the end of the buffer
 | 
						||
           (goto-char (point-max)))))
 | 
						||
    ;; There is no opening brace, so consider the whole buffer to be one "defun"
 | 
						||
    (goto-char (point-max))))
 | 
						||
 | 
						||
(defun reason-rewind-to-beginning-of-current-level-expr ()
 | 
						||
  "Rewind to the beginning of the expression on the current level of nesting."
 | 
						||
  (interactive)
 | 
						||
  (let ((current-level (reason-paren-level)))
 | 
						||
    (back-to-indentation)
 | 
						||
    (when (looking-at "=>")
 | 
						||
      (reason-rewind-irrelevant)
 | 
						||
      (back-to-indentation))
 | 
						||
    (while (> (reason-paren-level) current-level)
 | 
						||
      (backward-up-list)
 | 
						||
      (back-to-indentation))))
 | 
						||
 | 
						||
(defun reason-mode-indent-line ()
 | 
						||
  "Indent current line."
 | 
						||
  (interactive)
 | 
						||
  (let ((indent
 | 
						||
         (save-excursion
 | 
						||
           (back-to-indentation)
 | 
						||
           ;; Point is now at beginning of current line
 | 
						||
           (let* ((level (reason-paren-level))
 | 
						||
                  (baseline
 | 
						||
                   ;; Our "baseline" is one level out from the indentation of the expression
 | 
						||
                   ;; containing the innermost enclosing opening bracket. That
 | 
						||
                   ;; way if we are within a block that has a different
 | 
						||
                   ;; indentation than this mode would give it, we still indent
 | 
						||
                   ;; the inside of it correctly relative to the outside.
 | 
						||
                   (if (= 0 level)
 | 
						||
                       0
 | 
						||
                     (save-excursion
 | 
						||
                       (reason-rewind-irrelevant)
 | 
						||
                       (if (save-excursion
 | 
						||
                             (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
                             (looking-at "<"))
 | 
						||
                           (progn
 | 
						||
                             (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
                             (current-column))
 | 
						||
                           (progn
 | 
						||
                             (backward-up-list)
 | 
						||
                             (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
 | 
						||
                             (cond
 | 
						||
                              ((looking-at "switch")
 | 
						||
                               (current-column))
 | 
						||
 | 
						||
                              ((looking-at "|")
 | 
						||
                               (+ (current-column) (* reason-indent-offset 2)))
 | 
						||
 | 
						||
                              (t
 | 
						||
                               (let ((current-level (reason-paren-level)))
 | 
						||
                                 (save-excursion
 | 
						||
                                   (while (and (= current-level (reason-paren-level))
 | 
						||
                                               (not (looking-at reason-binding)))
 | 
						||
                                     (reason-rewind-irrelevant)
 | 
						||
                                     (reason-rewind-to-beginning-of-current-level-expr))
 | 
						||
                                   (+ (current-column) reason-indent-offset)))))))))))
 | 
						||
             (cond
 | 
						||
              ;; A function return type is indented to the corresponding function arguments
 | 
						||
              ((looking-at "=>")
 | 
						||
               (+ baseline reason-indent-offset))
 | 
						||
 | 
						||
              ((reason-in-str-or-cmnt)
 | 
						||
               (cond
 | 
						||
                ;; In the end of the block -- align with star
 | 
						||
                ((looking-at "*/") (+ baseline 1))
 | 
						||
                ;; Indent to the following shape:
 | 
						||
                ;; /* abcd
 | 
						||
                ;;  * asdf
 | 
						||
                ;;  */
 | 
						||
                ;;
 | 
						||
                ((looking-at "*") (+ baseline 1))
 | 
						||
                ;; Indent to the following shape:
 | 
						||
                ;; /* abcd
 | 
						||
                ;;    asdf
 | 
						||
                ;;  */
 | 
						||
                ;;
 | 
						||
                (t (+ baseline (+ reason-indent-offset 1)))))
 | 
						||
 | 
						||
              ((looking-at "</") (- baseline reason-indent-offset))
 | 
						||
 | 
						||
              ;; A closing brace is 1 level unindented
 | 
						||
              ((looking-at "}\\|)\\|\\]")
 | 
						||
               (save-excursion
 | 
						||
                 (reason-rewind-irrelevant)
 | 
						||
                 (let ((jsx? (reason-looking-back-str ">")))
 | 
						||
                   (backward-up-list)
 | 
						||
                   (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
                   (cond
 | 
						||
                    ((looking-at "switch") baseline)
 | 
						||
 | 
						||
                    (jsx? (current-column))
 | 
						||
 | 
						||
                    (t (- baseline reason-indent-offset))))))
 | 
						||
 | 
						||
              ;; Doc comments in /** style with leading * indent to line up the *s
 | 
						||
              ((and (nth 4 (syntax-ppss)) (looking-at "*"))
 | 
						||
               (+ 1 baseline))
 | 
						||
 | 
						||
              ;; If we're in any other token-tree / sexp, then:
 | 
						||
              (t
 | 
						||
               (or
 | 
						||
                ;; If we are inside a pair of braces, with something after the
 | 
						||
                ;; open brace on the same line and ending with a comma, treat
 | 
						||
                ;; it as fields and align them.
 | 
						||
                (when (> level 0)
 | 
						||
                  (save-excursion
 | 
						||
                    (reason-rewind-irrelevant)
 | 
						||
                    (backward-up-list)
 | 
						||
                    ;; Point is now at the beginning of the containing set of braces
 | 
						||
                    (reason-align-to-expr-after-brace)))
 | 
						||
 | 
						||
                (progn
 | 
						||
                  (back-to-indentation)
 | 
						||
                  (cond ((looking-at (regexp-opt '("and" "type")))
 | 
						||
                         baseline)
 | 
						||
                        ((save-excursion
 | 
						||
                           (reason-rewind-irrelevant)
 | 
						||
                           (= (point) 1))
 | 
						||
                         baseline)
 | 
						||
                        ((save-excursion
 | 
						||
                           (while (looking-at "|")
 | 
						||
                             (reason-rewind-irrelevant)
 | 
						||
                             (back-to-indentation))
 | 
						||
                           (looking-at (regexp-opt '("type"))))
 | 
						||
                         (+ baseline reason-indent-offset))
 | 
						||
                        ((looking-at "|\\|/[/*]")
 | 
						||
                         baseline)
 | 
						||
                        ((and (> level 0)
 | 
						||
                              (save-excursion
 | 
						||
                                (reason-rewind-irrelevant)
 | 
						||
                                (backward-up-list)
 | 
						||
                                (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
                                (looking-at "switch")))
 | 
						||
                         (+ baseline reason-indent-offset))
 | 
						||
                        ((save-excursion
 | 
						||
                           (reason-rewind-irrelevant)
 | 
						||
                           (looking-back "[{;,\\[(]" (- (point) 2)))
 | 
						||
                         baseline)
 | 
						||
                        ((and
 | 
						||
                          (save-excursion
 | 
						||
                            (reason-rewind-irrelevant)
 | 
						||
                            (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
                            (and (looking-at reason-binding)
 | 
						||
                                 (not (progn
 | 
						||
                                        (forward-sexp)
 | 
						||
                                        (forward-sexp)
 | 
						||
                                        (skip-chars-forward "[:space:]\n")
 | 
						||
                                        (looking-at "=")))))
 | 
						||
                          (not (save-excursion
 | 
						||
                                 (skip-chars-backward "[:space:]\n")
 | 
						||
                                 (reason-looking-back-str "=>"))))
 | 
						||
                         (save-excursion
 | 
						||
                           (reason-rewind-irrelevant)
 | 
						||
                           (backward-sexp)
 | 
						||
                           (reason-align-to-prev-expr)))
 | 
						||
                        ((save-excursion
 | 
						||
                           (reason-rewind-irrelevant)
 | 
						||
                           (looking-back "<\/.*?>" (- (point) 30)))
 | 
						||
                         baseline)
 | 
						||
                        (t
 | 
						||
                         (save-excursion
 | 
						||
                           (reason-rewind-irrelevant)
 | 
						||
                           (reason-rewind-to-beginning-of-current-level-expr)
 | 
						||
 | 
						||
                           (if (looking-at "|")
 | 
						||
                               baseline
 | 
						||
                             (+ baseline reason-indent-offset)))))
 | 
						||
                  ;; Point is now at the beginning of the current line
 | 
						||
                  ))))))))
 | 
						||
 | 
						||
    (when indent
 | 
						||
      ;; If we're at the beginning of the line (before or at the current
 | 
						||
      ;; indentation), jump with the indentation change.  Otherwise, save the
 | 
						||
      ;; excursion so that adding the indentations will leave us at the
 | 
						||
      ;; equivalent position within the line to where we were before.
 | 
						||
      (if (<= (current-column) (current-indentation))
 | 
						||
          (indent-line-to indent)
 | 
						||
        (save-excursion (indent-line-to indent))))))
 | 
						||
 | 
						||
(provide 'reason-indent)
 | 
						||
 | 
						||
;;; reason-indent.el ends here
 |