about summary refs log tree commit diff
path: root/emacs.d/vendor/reason-indent.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs.d/vendor/reason-indent.el')
-rw-r--r--emacs.d/vendor/reason-indent.el304
1 files changed, 304 insertions, 0 deletions
diff --git a/emacs.d/vendor/reason-indent.el b/emacs.d/vendor/reason-indent.el
new file mode 100644
index 000000000000..8fd3c9425866
--- /dev/null
+++ b/emacs.d/vendor/reason-indent.el
@@ -0,0 +1,304 @@
+;;; 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