Compare commits

..

No commits in common. "master" and "ab1503d2e851ce96aba74329c825ca99aa6f7e55" have entirely different histories.

4 changed files with 1 additions and 762 deletions

View File

@ -1,388 +0,0 @@
;;; edit-indirect.el --- Edit regions in separate buffers -*- lexical-binding: t -*-
;; Author: Fanael Linithien <fanael4@gmail.com>
;; URL: https://github.com/Fanael/edit-indirect
;; Version: 0.1.5
;; Package-Requires: ((emacs "24.3"))
;; This file is NOT part of GNU Emacs.
;; SPDX-License-Identifier: BSD-2-clause
;;
;; Copyright (c) 2014-2017, Fanael Linithien
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;;
;; * Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; * Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
;; IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
;; TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
;; PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
;; OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
;; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
;; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
;; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;; Commentary:
;; Edit regions in separate buffers, like `org-edit-src-code' but for arbitrary
;; regions.
;;
;; See the docstring of `edit-indirect-region' for details.
;;; Code:
(defgroup edit-indirect nil
"Editing regions in separate buffers."
:group 'editing)
(defcustom edit-indirect-guess-mode-function #'edit-indirect-default-guess-mode
"The function used to guess the major mode of an edit-indirect buffer.
It's called with the edit-indirect buffer as the current buffer.
It's called with three arguments, the parent buffer, the beginning
and the end of the parent buffer region being editing.
Note that the buffer-local value from the parent buffer is used."
:type 'function
:group 'edit-indirect)
(defcustom edit-indirect-after-creation-hook nil
"Functions called after the edit-indirect buffer is created.
The functions are called with the edit-indirect buffer as the
current buffer.
Note that the buffer-local value from the parent buffer is used."
:type 'hook
:group 'edit-indirect)
(defcustom edit-indirect-before-commit-hook nil
"Functions called before the edit-indirect buffer is committed.
The functions are called with the edit-indirect buffer as the
current buffer.
Note that the buffer-local value from the edit-indirect buffer is
used."
:type 'hook
:group 'edit-indirect)
(defcustom edit-indirect-before-commit-functions nil
"Functions called before an edit-indirect buffer is committed.
The functions are called with the parent buffer as the current
buffer.
Each function is called with two arguments, the beginning and the
end of the region to be changed."
:type 'hook
:group 'edit-indirect)
(defcustom edit-indirect-after-commit-functions nil
"Functions called after an edit-indirect buffer has been committed.
The functions are called with the parent buffer as the current
buffer.
Each function is called with two arguments, the beginning and the
end of the changed region."
:type 'hook
:group 'edit-indirect)
(defgroup edit-indirect-faces nil
"Faces used in `edit-indirect'."
:group 'edit-indirect
:group 'faces
:prefix "edit-indirect")
(defface edit-indirect-edited-region
'((t :inherit secondary-selection))
"Face used to highlight an indirectly edited region."
:group 'edit-indirect-faces)
;; Emacs <= 24.3 has no `define-error'.
(let* ((user-error-conditions (get 'user-error 'error-conditions))
(define-user-error (lambda (name message)
(put name 'error-conditions
(cons name user-error-conditions))
(put name 'error-message message))))
(funcall define-user-error 'edit-indirect-overlapping
"Indirectly edited regions cannot overlap")
(funcall define-user-error 'edit-indirect-read-only
"Text is read-only, modify the edit-indirect buffer instead")
(funcall define-user-error 'edit-indirect-not-indirect
"This is not an edit-indirect buffer"))
(defvar edit-indirect--overlay)
(defvar edit-indirect--should-quit-window nil)
;;;###autoload
(defun edit-indirect-region (beg end &optional display-buffer)
"Edit the region BEG..END in a separate buffer.
The region is copied, without text properties, to a separate
buffer, called edit-indirect buffer, and
`edit-indirect-guess-mode-function' is called to set the major
mode.
When done, exit with `edit-indirect-commit', which will remove the
original region and replace it with the edited version; or with
`edit-indirect-abort', which will drop the modifications.
This differs from `clone-indirect-buffer' with narrowing in that
the text properties are not shared, so the parent buffer major mode
and the edit-indirect buffer major mode will not be able to tread
on each other's toes by setting up potentially conflicting text
properties, which happens surprisingly often when the font-lock
mode is used.
Edit-indirect buffers use the `edit-indirect-mode-map' keymap.
If there's already an edit-indirect buffer for BEG..END, use that.
If there's already an edit-indirect buffer active overlapping any
portion of BEG..END, an `edit-indirect-overlapping' error is
signaled.
When DISPLAY-BUFFER is non-nil or when called interactively,
display the edit-indirect buffer in some window and select it.
In any case, return the edit-indirect buffer."
(interactive
(if (or (use-region-p) (not transient-mark-mode))
(prog1 (list (region-beginning) (region-end) t)
(deactivate-mark))
(user-error "No region")))
(let ((buffer (edit-indirect--get-edit-indirect-buffer beg end)))
(when display-buffer
(with-current-buffer buffer
(setq-local edit-indirect--should-quit-window t))
(select-window (display-buffer buffer)))
buffer))
(defvar edit-indirect-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-x C-s") #'edit-indirect-save)
(define-key map (kbd "C-c '") #'edit-indirect-commit)
(define-key map (kbd "C-c C-c") #'edit-indirect-commit)
(define-key map (kbd "C-c C-k") #'edit-indirect-abort)
map)
"Keymap for edit-indirect buffers.
\\{edit-indirect-mode-map}")
(defun edit-indirect-commit ()
"Commit the modifications done in an edit-indirect buffer.
That is, replace the original region in the parent buffer with the
contents of the edit-indirect buffer.
The edit-indirect buffer is then killed.
Can be called only when the current buffer is an edit-indirect
buffer."
(interactive)
(edit-indirect--barf-if-not-indirect)
(edit-indirect--commit)
(edit-indirect--clean-up))
(defun edit-indirect-save ()
"Save the modifications done in an edit-indirect buffer.
That is, replace the original region in the parent buffer with the
contents of the edit-indirect buffer.
Can be called only when the current buffer is an edit-indirect
buffer."
(interactive)
(edit-indirect--barf-if-not-indirect)
(edit-indirect--commit))
(defun edit-indirect-abort ()
"Abort indirect editing in the current buffer and kill the buffer.
Can be called only when the current buffer is an edit-indirect
buffer."
(interactive)
(edit-indirect--barf-if-not-indirect)
(edit-indirect--abort))
(defun edit-indirect-buffer-indirect-p (&optional buffer)
"Non-nil iff the BUFFER is an edit-indirect buffer.
BUFFER defaults to the current buffer."
(save-current-buffer
(when buffer
(set-buffer buffer))
;; (not (null)) so we don't leak the overlay to the outside world.
(not (null edit-indirect--overlay))))
(defun edit-indirect-default-guess-mode (_parent-buffer _beg _end)
"Guess the major mode for an edit-indirect buffer.
It's done by calling `normal-mode'."
(normal-mode))
(defvar edit-indirect--overlay nil
"The overlay spanning the region of the parent buffer being edited.
It's also used as the variable determining if we're in an
edit-indirect buffer at all.")
(make-variable-buffer-local 'edit-indirect--overlay)
(put 'edit-indirect--overlay 'permanent-local t)
;; Normally this would use `define-minor-mode', but that makes the mode function
;; interactive, which we don't want, because it's just an implementation detail.
(defun edit-indirect--mode (overlay)
"Turn the `edit-indirect--mode' \"minor mode\" on.
OVERLAY is the value to set `edit-indirect--overlay' to."
(setq edit-indirect--overlay overlay)
(add-hook 'kill-buffer-hook #'edit-indirect--abort-on-kill-buffer nil t))
(with-no-warnings
(add-minor-mode
'edit-indirect--overlay " indirect" edit-indirect-mode-map nil #'ignore))
(defun edit-indirect--get-edit-indirect-buffer (beg end)
"Return an edit-indirect buffer for the region BEG..END.
If there's already an edit-indirect buffer active overlapping any
portion of BEG..END, an `edit-indirect-overlapping' error is
signaled."
(let ((old-overlay (edit-indirect--search-for-edit-indirect beg end)))
(cond
((null old-overlay)
(let ((overlay (edit-indirect--create-overlay beg end)))
(edit-indirect--create-indirect-buffer beg end overlay)))
((and (= beg (overlay-start old-overlay))
(= end (overlay-end old-overlay)))
(overlay-get old-overlay 'edit-indirect-buffer))
(t
(signal 'edit-indirect-overlapping '())))))
(defun edit-indirect--search-for-edit-indirect (beg end)
"Return an existing edit-indirect overlay for some region inside BEG..END.
If there's no indirectly edited region inside BEG..END, return
nil."
(catch 'done
(dolist (overlay (overlays-in beg end))
(when (overlay-get overlay 'edit-indirect-buffer)
(throw 'done overlay)))
nil))
(defmacro edit-indirect--buffer-local-value (buffer variable)
"Get the BUFFER local value of VARIABLE.
VARIABLE shall be a symbol."
(unless (symbolp variable)
(signal 'wrong-type-argument (list #'symbolp variable)))
;; `with-current-buffer' is used instead of `buffer-local-value' because
;; the latter doesn't give warnings about free variables when
;; byte-compiled.
`(with-current-buffer ,buffer ,variable))
(defun edit-indirect--create-indirect-buffer (beg end overlay)
"Create an edit-indirect buffer and return it.
BEG..END is the parent buffer region to insert.
OVERLAY is the overlay, see `edit-indirect--overlay'."
(let ((buffer (generate-new-buffer (format "*edit-indirect %s*" (buffer-name))))
(parent-buffer (current-buffer)))
(overlay-put overlay 'edit-indirect-buffer buffer)
(with-current-buffer buffer
(insert-buffer-substring-no-properties parent-buffer beg end)
(set-buffer-modified-p nil)
(edit-indirect--mode overlay)
;; Use the buffer-local values from the parent buffer. Don't retrieve the
;; values before actual uses in case these variables are changed by some
;; of the many possible hooks.
(funcall (edit-indirect--buffer-local-value
parent-buffer edit-indirect-guess-mode-function)
parent-buffer beg end)
(let ((edit-indirect-after-creation-hook
(edit-indirect--buffer-local-value
parent-buffer edit-indirect-after-creation-hook)))
(run-hooks 'edit-indirect-after-creation-hook)))
buffer))
(defun edit-indirect--create-overlay (beg end)
"Create the edit-indirect overlay and return it.
BEG and END specify the region the overlay should encompass."
(let ((overlay (make-overlay beg end)))
(overlay-put overlay 'face 'edit-indirect-edited-region)
(overlay-put overlay 'modification-hooks '(edit-indirect--barf-read-only))
(overlay-put overlay 'insert-in-front-hooks '(edit-indirect--barf-read-only))
overlay))
(defvar edit-indirect--inhibit-read-only nil
"Non-nil means disregard read-only status of indirectly-edited region.")
(defun edit-indirect--barf-read-only (_ov _after _beg _end &optional _len)
"Signal an error because the text is read-only.
No error is signaled if `inhibit-read-only' or
`edit-indirect--inhibit-read-only' is non-nil."
(unless (or inhibit-read-only edit-indirect--inhibit-read-only)
(signal 'edit-indirect-read-only '())))
(defun edit-indirect--commit ()
"Commit the modifications done in an edit-indirect buffer."
(run-hooks 'edit-indirect-before-commit-hook)
(let ((beg (overlay-start edit-indirect--overlay))
(end (overlay-end edit-indirect--overlay))
(buffer (current-buffer))
(edit-indirect--inhibit-read-only t))
(with-current-buffer (overlay-buffer edit-indirect--overlay)
(save-excursion
(let ((beg-marker (copy-marker beg))
(end-marker (copy-marker end)))
(edit-indirect--run-hook-with-positions
'edit-indirect-before-commit-functions beg-marker end-marker)
(save-match-data
(set-match-data (list beg-marker end-marker))
(replace-match (with-current-buffer buffer
(buffer-substring-no-properties 1 (1+ (buffer-size))))
t t))
(edit-indirect--run-hook-with-positions
'edit-indirect-after-commit-functions beg-marker (point))
(set-marker beg-marker nil)
(set-marker end-marker nil))))))
(defun edit-indirect--run-hook-with-positions (hook beg end)
"Run HOOK with the specified positions BEG and END.
HOOK should be a symbol, a hook variable.
The functions are passed integer positions.
If a function changes the buffer contents, the next function will be
called with updated positions."
(let ((beg-marker (unless (markerp beg) (copy-marker beg)))
(end-marker (unless (markerp end) (copy-marker end))))
(run-hook-wrapped hook
(lambda (f beg end)
(funcall f (marker-position beg) (marker-position end))
nil)
(or beg-marker beg) (or end-marker end))
(when beg-marker (set-marker beg-marker nil))
(when end-marker (set-marker end-marker nil))))
(defun edit-indirect--abort ()
"Abort indirect edit."
(edit-indirect--clean-up))
(defun edit-indirect--clean-up ()
"Clean up an edit-indirect buffer."
(delete-overlay edit-indirect--overlay)
;; Kill the overlay reference so that `edit-indirect--abort-on-kill-buffer'
;; won't try to call us again.
(setq edit-indirect--overlay nil)
;; If we created a window, get rid of it. Kill the buffer we created.
(if edit-indirect--should-quit-window
(quit-window t)
(kill-buffer)))
(defun edit-indirect--abort-on-kill-buffer ()
"Abort indirect edit.
Should be called only from `kill-buffer-hook'."
(when edit-indirect--overlay
(edit-indirect--abort)))
(defun edit-indirect--barf-if-not-indirect ()
"Signal an error if the current buffer is not an edit-indirect buffer.
The error signaled is `edit-indirect-not-indirect'."
(unless edit-indirect--overlay
(signal 'edit-indirect-not-indirect '())))
(provide 'edit-indirect)
;;; edit-indirect.el ends here

@ -1 +1 @@
Subproject commit 35f6826e435c3004dabf134d0f2ae2f31ea7b6a2
Subproject commit 64be4a31390a62d248da5c0509aaa0de09b8e4f4

View File

@ -1,242 +0,0 @@
;;; ssass-mode.el --- Edit Sass without a Turing Machine
;; Copyright 2017 Adam Niederer
;; Author: Adam Niederer <adam.niederer@gmail.com>
;; URL: http://github.com/AdamNiederer/ssass-mode
;; Version: 0.2.0
;; Keywords: languages sass
;; Package-Requires: ((emacs "24.3"))
;; This program 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 of the License, or
;; (at your option) any later version.
;;
;; This program 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This mode is a clone of sass-mode which works in mmm-mode and doesn't
;; indent things as eagerly. Syntax highlighting is provided with
;; `font-lock-mode'.
;;
;; Exported names start with "ssass-"; private names start with
;; "ssass--".
;;; Code:
(defgroup ssass nil
"Major mode for Sass files"
:prefix "ssass-"
:group 'languages
:link '(url-link :tag "Github" "https://github.com/AdamNiederer/ssass-mode")
:link '(emacs-commentary-link :tag "Commentary" "ssass-mode"))
(defconst ssass-id-regex
"#[a-z][A-Za-z0-9\-]+")
(defconst ssass-class-regex
"\\.[a-z][A-Za-z0-9\-]+")
(defconst ssass-pseudoselector-regex
"::?[A-Za-z0-9\-]+")
(defconst ssass-key-regex
"^\s+[a-z\-]+:")
(defconst ssass-directive-noindent-regex
"@\\(include\\|extend\\|import\\|warn\\|debug\\|error\\)"
"Matches all directives which do not require indentation.")
(defconst ssass-variable-regex
"\$[A-Za-z0-9\-]+")
(defconst ssass-variable-assignment-regex
(concat ssass-variable-regex ":"))
(defconst ssass-builtin-regex
"@[A-Za-z]+")
(defconst ssass-comment-regex
"^\s+/[/*].*") ; TODO: Make better or use syntax table
(defconst ssass-function-regex
"\\([A-Za-z\-]+?\\)\\((.*)\\)")
(defconst ssass-keywords
'("and" "or" "not" "in" "from" "to" "through"))
(defconst ssass-control-directives
'("@if" "@else" "@each" "@for"))
(defconst ssass-function-directives
'("@function" "@return"))
(defconst ssass-mixin-directives
'("@mixin" "@include"))
(defconst ssass-constants
'("true" "false" "null"))
(defconst ssass-bang-regex
"![a-z][A-Za-z0-9]+")
(defcustom ssass-tab-width 2
"Tab width for ssass-mode."
:group 'ssass
:type 'integer)
(defcustom ssass-indent-blanks t
"Whether to indent blank lines."
:group 'ssass
:type 'boolean)
(defcustom ssass-compiler "sassc"
"Sass compiler for `ssass-eval-region' and `ssass-eval-buffer'."
:group 'ssass
:type 'string)
(defcustom ssass-opt "--sass"
"Options for `ssass-compiler'.
Use --sass for sassc, and --indented-syntax for node-sass."
:group 'ssass
:type 'string)
(defcustom ssass-color-keys nil
"(TODO) Whether to color proprty names."
:group 'ssass
:type 'boolean)
(defconst ssass-font-lock-keywords
`((,ssass-id-regex . (0 font-lock-keyword-face))
(,ssass-class-regex . (0 font-lock-type-face))
(,ssass-key-regex . (0 font-lock-variable-name-face))
(,ssass-function-regex . (1 font-lock-function-name-face))
(,ssass-builtin-regex . (0 font-lock-builtin-face))
(,ssass-pseudoselector-regex . (0 font-lock-function-name-face))
(,ssass-variable-regex . (0 font-lock-variable-name-face))
(,ssass-bang-regex . (0 font-lock-warning-face))
(,(regexp-opt ssass-keywords 'words) . font-lock-keyword-face)
(,(regexp-opt ssass-control-directives 'words) . font-lock-keyword-face)
(,(regexp-opt ssass-function-directives 'words) . font-lock-keyword-face)
(,(regexp-opt ssass-constants 'words) . font-lock-constant-face))
"List of Font Lock keywords.")
(defvar ssass-mode-map
(let ((map (make-keymap)))
(define-key map (kbd "<backtab>") 'ssass-dedent)
(define-key map (kbd "C-c C-c") 'ssass-eval-buffer)
(define-key map (kbd "C-c C-r") 'ssass-eval-region)
map)
"Keymap for ssass-mode.")
(defun ssass--selector-p (line)
"Return whether LINE is a selector."
(not (or (string-empty-p line)
(string-match-p ssass-key-regex line)
(string-match-p ssass-variable-assignment-regex line)
(string-match-p ssass-directive-noindent-regex line)
(string-match-p ssass-comment-regex line))))
(defun ssass--goto-last-anchor-line ()
"Move point to the line of the last selector, or the beginning of the buffer."
(forward-line -1)
(while (not (or (equal (point-min) (point-at-bol))
(ssass--selector-p (buffer-substring (point-at-bol) (point-at-eol)))))
(forward-line -1)))
(defun ssass--last-anchor-line-indent-level ()
"Return the number of spaces indenting the line of the last selector."
(save-excursion
(ssass--goto-last-anchor-line)
(ssass--indent-level)))
(defun ssass--indent-level ()
"Return the number of spaces indenting the current line."
(- (save-excursion
(back-to-indentation)
(current-column))
(save-excursion
(beginning-of-line)
(current-column))))
(defun ssass--whitespace-p (line)
"Return whether the line at offset from point LINE consists solely of whitespace."
(save-excursion
(forward-line line)
(string-match-p "^[[:space:]]*$" (buffer-substring (point-at-bol) (point-at-eol)))))
(defun ssass--comma-before-p ()
"Return whether the previous line has a comma at its end."
(save-excursion
(forward-line -1)
(string-match-p ",\\s-*$" (buffer-substring (point-at-bol) (point-at-eol)))))
(defun ssass--no-anchor-line-p ()
"Return whether there is no proper selector or keyword above this line."
(save-excursion
(ssass--goto-last-anchor-line)
(not (ssass--selector-p (buffer-substring (point-at-bol) (point-at-eol))))))
(defun ssass-indent ()
"Indent the current line."
(interactive)
(indent-line-to
(cond
((and (not ssass-indent-blanks) (ssass--whitespace-p 0)) 0)
((ssass--whitespace-p -1) 0)
((ssass--no-anchor-line-p) 0)
((ssass--comma-before-p) (ssass--last-anchor-line-indent-level))
(t (+ ssass-tab-width (ssass--last-anchor-line-indent-level))))))
(defun ssass-dedent ()
"Remove one level of indentation from the current line."
(interactive)
(indent-line-to (max 0 (- (ssass--indent-level) ssass-tab-width))))
(defun ssass-eval-file (&optional filename)
"Run the given file through sass, and display the output in another window.
If FILENAME is nil, it will open the current buffer's file"
(interactive)
(when (buffer-live-p (get-buffer "*sass*"))
(kill-buffer "*sass*"))
(start-process "sass" "*sass*" ssass-compiler ssass-opt (or filename (buffer-file-name)))
(switch-to-buffer-other-window "*sass*")
(special-mode))
(defun ssass-eval-region (beg end)
"Run the region from BEG to END through sass, and display the output in another window."
(interactive "r")
(let ((tmp-file (make-temp-file "sass-eval" nil ".sass")))
(write-region beg end tmp-file nil nil nil nil)
(ssass-eval-file tmp-file)
(delete-file tmp-file)) )
(defun ssass-eval-buffer ()
"Run the current buffer through sass, and display the output in another window."
(interactive)
(ssass-eval-region (point-min) (point-max)))
;;;###autoload
(define-derived-mode ssass-mode prog-mode "Ssass"
"Major mode for Sass"
(setq-local electric-indent-mode nil)
(setq-local comment-start "//")
(setq-local comment-start-skip "\\(//+\\|/\\*+\\)\\s *")
(set (make-local-variable 'tab-width) ssass-tab-width)
(set (make-local-variable 'indent-line-function) 'ssass-indent)
(font-lock-add-keywords nil ssass-font-lock-keywords)
(modify-syntax-entry ?/ ". 124" ssass-mode-syntax-table)
(modify-syntax-entry ?* ". 23b" ssass-mode-syntax-table)
(modify-syntax-entry ?\n ">" ssass-mode-syntax-table))
(provide 'ssass-mode)
;;; ssass-mode.el ends here

View File

@ -1,131 +0,0 @@
;;; vue-html-mode.el --- Major mode for editing Vue.js templates
;; Copyright 2016, 2017 Adam Niederer
;; Author: Adam Niederer <adam.niederer@gmail.com>
;; URL: http://github.com/AdamNiederer/vue-html-mode
;; Version: 0.2.0
;; Keywords: languages vue template
;; Package-Requires: ()
;; This program 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 of the License, or
;; (at your option) any later version.
;;
;; This program 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 <http://www.gnu.org/licenses/>.
;;; Commentary
;; The main features of this mode are syntax highlighting (enabled with
;; `font-lock-mode' or `global-font-lock-mode'), and html-mode
;; integration
;;
;; Exported names start with "vue-html-"; private names start with
;; "vue-html--".
;;
;; TODO: Chained filters, possible code folding with overlays and colors
;;; Code:
(defgroup vue-html nil
"Major mode for vue template files"
:prefix "vue-html-"
:group 'languages
:link '(url-link :tag "Github" "https://github.com/AdamNiederer/vue-html-mode")
:link '(emacs-commentary-link :tag "Commentary" "vue-html-mode"))
(defconst vue-html-complex-interp-regex
"\\({{\\)\\([^{].*?\\)?\\(|\\) *\\(.*?\\)(.*) *\\(}}\\)")
(defconst vue-html-filter-interp-regex
"\\({{\\)\\([^{].*?\\)?\\(|\\) *\\([^\(\)]*?\\) *\\(}}\\)")
(defconst vue-html-simple-interp-regex
"\\({{\\)\\(?:[^{].*?\\)?\\(}}\\)")
(defconst vue-html-shorthand-regex
"\\s +\\([@:]\\)\\([A-z0-9-.]+\\)=.*?")
(defconst vue-html-directive-regex
"\\b\\(v-[A-Za-z0-9-.]+\\)\\(:[A-z.]\\)?")
(defconst vue-html-keyword-regex
"\\(v-\\(?:for\\|if\\|else-if\\|else\\|once\\)\\)[^-.A-Za-z0-9]")
(defcustom vue-html-tab-width 2
"Tab width for vue-html-mode."
:group 'vue-html
:type 'integer)
(defcustom vue-html-extra-indent 0
"The number of columns added to every line's indentation."
:group 'vue-html
:type 'integer)
(defcustom vue-html-color-interpolations nil
"Whether to color the body of variable interpolations the same as delimiters.
Does not affect the colors of filters and their arguments."
:group 'vue-html
:type 'boolean)
(defconst vue-html-color-interpolations-font-lock-keywords
`((,vue-html-simple-interp-regex . (0 font-lock-variable-name-face t))
(,vue-html-filter-interp-regex . (2 font-lock-variable-name-face t))
(,vue-html-complex-interp-regex . (2 font-lock-variable-name-face t)))
"List of Font Lock rules applied if `vue-html-color-interpolations' is non-nil.")
(defconst vue-html-font-lock-keywords
`((,vue-html-simple-interp-regex . (1 font-lock-variable-name-face t))
(,vue-html-simple-interp-regex . (2 font-lock-variable-name-face t))
(,vue-html-filter-interp-regex . (1 font-lock-variable-name-face t))
(,vue-html-filter-interp-regex . (3 font-lock-function-name-face t))
(,vue-html-filter-interp-regex . (4 font-lock-function-name-face t))
(,vue-html-filter-interp-regex . (5 font-lock-variable-name-face t))
(,vue-html-complex-interp-regex . (1 font-lock-variable-name-face t))
(,vue-html-complex-interp-regex . (3 font-lock-function-name-face t))
(,vue-html-complex-interp-regex . (4 font-lock-function-name-face t))
(,vue-html-complex-interp-regex . (5 font-lock-variable-name-face t))
(,vue-html-directive-regex . (1 font-lock-builtin-face t))
(,vue-html-shorthand-regex . (1 font-lock-builtin-face t))
(,vue-html-shorthand-regex . (2 font-lock-variable-name-face t))
(,vue-html-keyword-regex . (1 font-lock-keyword-face t)))
"List of Font Lock keywords which are applied regardless of settings.")
(defvar vue-html-mode-map
(let ((map (make-keymap)))
map)
"Keymap for vue-html-mode.")
(defun vue-html-last-line-p ()
"Return whether point is on the last line in the buffer."
(save-excursion (= (line-number-at-pos) (line-number-at-pos (point-max)))))
(defun vue-html-line-empty-p ()
"Return whether point is on an empty line."
(string-match-p "^\s*$" (buffer-substring (point-at-bol) (point-at-eol))))
(defun vue-html-indent ()
"Indent the line according to `vue-html-tab-width' and `vue-html-extra-indent'."
(cond
((= 1 (line-number-at-pos)) (indent-line-to vue-html-extra-indent))
((and (vue-html-last-line-p) (vue-html-line-empty-p)) (indent-line-to 0))
(t (sgml-indent-line))))
;;;###autoload
(define-derived-mode vue-html-mode html-mode "vue-html"
"Major mode for Vue.js templates."
(setq tab-width vue-html-tab-width)
(setq indent-line-function #'vue-html-indent)
(font-lock-add-keywords nil vue-html-font-lock-keywords)
(when vue-html-color-interpolations
(font-lock-add-keywords nil vue-html-color-interpolations-font-lock-keywords)))
(provide 'vue-html-mode)
;;; vue-html-mode.el ends here