;;; elm-mode.el --- Major mode for Elm ;; Copyright (C) 2013, 2014 Joseph Collard ;; Copyright (C) 2015, 2016 Bogdan Popa ;; Author: Joseph Collard ;; Package-Requires: ((f "0.17") (s "1.7.0") (emacs "25.1") (dash "2.13.0") (reformatter "0.3")) ;; URL: https://github.com/jcollard/elm-mode ;; Package-Version: 0-snapshot ;; This file is not part of GNU Emacs. ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Provides a major mode for editing Elm source code, and working with ;; common core and third-party Elm tools including the compiler, repl, ;; elm-format and more. ;;; Code: (require 'elm-tags) (require 'elm-format) (require 'elm-imenu) (require 'elm-indent) (require 'elm-interactive) (require 'elm-font-lock) (defgroup elm nil "Support for the elm programming language." :link '(url-link :tag "Github" "https://github.com/jcollard/elm-mode") :group 'languages) (defun elm-beginning-of-defun (&optional arg) "Move backward to the beginning of an ELM \"defun\". With ARG, do it that many times. Negative arg -N means move forward to Nth following beginning of defun. Returns t unless search stops due to beginning or end of buffer. Find the roots of this function in the c-awk-mode." (interactive "p") (or arg (setq arg 1)) (save-match-data (let ((found t) ; Has the most recent regexp search found b-of-defun? (regexp "^[^- \t\n\r]")) (if (>= arg 0) ;; Go back one defun each time round the following loop. (For +ve arg) (while (and found (< 0 arg) (not (eq (point) (point-min)))) ;; Go back one "candidate" each time round the next loop until one ;; is genuinely a beginning-of-defun. (setq found (search-backward-regexp regexp (point-min) 'stop-at-limit)) (when found (while (and (forward-line -1) (looking-at regexp) (not (= (point-min) (point))))) (forward-line)) (setq arg (1- arg))) ;; The same for a -ve arg. (if (not (eq (point) (point-max))) (forward-char 1)) (while (and found (< arg 0) (not (eq (point) (point-max)))) ; The same for -ve arg. (setq found (search-forward-regexp regexp (point-min) 'stop-at-limit)) (setq arg (1+ arg)))) (eq arg 0)))) (defun elm-end-of-defun (&optional arg) "Move forward to the end of an ELM \"defun\". With ARG, do it that many times. Negative arg -N means move forward to Nth previous end of defun. Returns t unless search stops due to beginning or end of buffer. Find the roots of this function in the c-awk-mode." (interactive "p") (or arg (setq arg 1)) (save-match-data (let ((found t) ; Has the most recent regexp search found b-of-defun? (regexp "^\n\n")) (if (>= arg 0) ;; Go back one defun each time round the following loop. (For +ve arg) (while (and found (< 0 arg) (not (eq (point) (point-max)))) ;; Go back one "candidate" each time round the next loop until one ;; is genuinely a beginning-of-defun. (setq found (search-forward-regexp regexp (point-max) 'stop-at-limit)) (setq arg (1- arg))) ;; The same for a -ve arg. (if (not (eq (point) (point-min))) (forward-char 1)) (while (and found (< arg 0) (not (eq (point) (point-min)))) ; The same for -ve arg. (setq found (search-backward-regexp regexp (point-min) 'stop-at-limit)) (setq arg (1+ arg))) (if found (goto-char (match-beginning 0)))) (eq arg 0)))) (defun elm-mode-after-save-handler () "Perform various operations upon saving a buffer." (when elm-sort-imports-on-save (elm-sort-imports)) (when elm-tags-on-save (elm-mode-generate-tags)) (when (or elm-sort-imports-on-save elm-tags-on-save) (let ((before-save-hook '()) (after-save-hook '())) (basic-save-buffer)))) (defvar elm-mode-map (let ((map (make-keymap))) (define-key map (kbd "C-c C-f") 'elm-mode-format-buffer) (define-key map (kbd "C-c M-t") 'elm-mode-generate-tags) (define-key map (kbd "C-c C-l") 'elm-repl-load) (define-key map (kbd "C-c C-p") 'elm-repl-push) (define-key map (kbd "C-c C-e") 'elm-repl-push-decl) (define-key map (kbd "C-c C-z") 'run-elm-interactive) (define-key map (kbd "C-c C-a") 'elm-compile-add-annotations) (define-key map (kbd "C-c C-r") 'elm-compile-clean-imports) (define-key map (kbd "C-c C-c") 'elm-compile-buffer) (define-key map (kbd "C-c M-c") 'elm-compile-main) (define-key map (kbd "C-c M-k") 'elm-package-catalog) (define-key map (kbd "C-c C-n") 'elm-preview-buffer) (define-key map (kbd "C-c C-m") 'elm-preview-main) (define-key map (kbd "C-c C-d") 'elm-documentation-lookup) (define-key map (kbd "C-c C-i") 'elm-import) (define-key map (kbd "C-c C-s") 'elm-sort-imports) (define-key map (kbd "C-c C-t") 'elm-oracle-type-at-point) (define-key map (kbd "C-c M-d") 'elm-oracle-doc-at-point) (define-key map (kbd "C-c C-v") 'elm-test-project) map) "Keymap for Elm major mode.") ;;;###autoload (define-derived-mode elm-mode prog-mode "Elm" "Major mode for editing Elm source code." (setq-local indent-tabs-mode nil) ;; Elm is not generally suitable for electric indentation, since ;; there is no unambiguously correct indent level for any given ;; line. (when (boundp 'electric-indent-inhibit) (setq-local electric-indent-inhibit t)) (setq-local comment-start "--") (setq-local comment-end "") (setq-local imenu-create-index-function #'elm-imenu-create-index) (setq-local paragraph-start (concat " *{-\\| *-- |\\|" page-delimiter)) (setq-local paragraph-separate (concat " *$\\| *\\({-\\|-}\\) *$\\|" page-delimiter)) (setq-local beginning-of-defun-function #'elm-beginning-of-defun) (setq-local end-of-defun-function #'elm-end-of-defun) (add-function :before-until (local 'eldoc-documentation-function) #'elm-eldoc) (when elm-format-on-save (elm-format-on-save-mode)) (add-hook 'after-save-hook #'elm-mode-after-save-handler nil t) (turn-on-elm-font-lock)) ;; We enable intelligent indenting, but users can remove this from the ;; hook if they prefer. (add-hook 'elm-mode-hook 'elm-indent-mode) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.elm\\'" . elm-mode)) (provide 'elm-mode) ;;; elm-mode.el ends here