Initial commit.
This commit is contained in:
413
one-file-mode/bind-key.el
Normal file
413
one-file-mode/bind-key.el
Normal file
@@ -0,0 +1,413 @@
|
||||
;;; bind-key.el --- A simple way to manage personal keybindings
|
||||
|
||||
;; Copyright (c) 2012-2015 john wiegley
|
||||
|
||||
;; Author: John Wiegley <jwiegley@gmail.com>
|
||||
;; Maintainer: John Wiegley <jwiegley@gmail.com>
|
||||
;; Created: 16 Jun 2012
|
||||
;; Version: 1.0
|
||||
;; Keywords: keys keybinding config dotemacs
|
||||
;; URL: https://github.com/jwiegley/use-package
|
||||
|
||||
;; 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 2, 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 gnu emacs; see the file copying. if not, write to the
|
||||
;; free software foundation, inc., 59 temple place - suite 330,
|
||||
;; boston, ma 02111-1307, usa.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; If you have lots of keybindings set in your .emacs file, it can be hard to
|
||||
;; know which ones you haven't set yet, and which may now be overriding some
|
||||
;; new default in a new emacs version. This module aims to solve that
|
||||
;; problem.
|
||||
;;
|
||||
;; Bind keys as follows in your .emacs:
|
||||
;;
|
||||
;; (require 'bind-key)
|
||||
;;
|
||||
;; (bind-key "C-c x" 'my-ctrl-c-x-command)
|
||||
;;
|
||||
;; If you want the keybinding to override all minor modes that may also bind
|
||||
;; the same key, use the `bind-key*' form:
|
||||
;;
|
||||
;; (bind-key* "<C-return>" 'other-window)
|
||||
;;
|
||||
;; If you want to rebind a key only in a particular keymap, use:
|
||||
;;
|
||||
;; (bind-key "C-c x" 'my-ctrl-c-x-command some-other-mode-map)
|
||||
;;
|
||||
;; To unbind a key within a keymap (for example, to stop your favorite major
|
||||
;; mode from changing a binding that you don't want to override everywhere),
|
||||
;; use `unbind-key':
|
||||
;;
|
||||
;; (unbind-key "C-c x" some-other-mode-map)
|
||||
;;
|
||||
;; To bind multiple keys at once, or set up a prefix map, a `bind-keys' macro
|
||||
;; is provided. It accepts keyword arguments, please see its documentation
|
||||
;; for a detailed description.
|
||||
;;
|
||||
;; To add keys into a specific map, use :map argument
|
||||
;;
|
||||
;; (bind-keys :map dired-mode-map
|
||||
;; ("o" . dired-omit-mode)
|
||||
;; ("a" . some-custom-dired-function))
|
||||
;;
|
||||
;; To set up a prefix map, use `:prefix-map' and `:prefix' arguments (both are
|
||||
;; required)
|
||||
;;
|
||||
;; (bind-keys :prefix-map my-customize-prefix-map
|
||||
;; :prefix "C-c c"
|
||||
;; ("f" . customize-face)
|
||||
;; ("v" . customize-variable))
|
||||
;;
|
||||
;; You can combine all the keywords together. Additionally,
|
||||
;; `:prefix-docstring' can be specified to set documentation of created
|
||||
;; `:prefix-map' variable.
|
||||
;;
|
||||
;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings
|
||||
;; will not be overridden by other modes), you may use `bind-keys*' macro:
|
||||
;;
|
||||
;; (bind-keys*
|
||||
;; ("C-o" . other-window)
|
||||
;; ("C-M-n" . forward-page)
|
||||
;; ("C-M-p" . backward-page))
|
||||
;;
|
||||
;; After Emacs loads, you can see a summary of all your personal keybindings
|
||||
;; currently in effect with this command:
|
||||
;;
|
||||
;; M-x describe-personal-keybindings
|
||||
;;
|
||||
;; This display will tell you if you've overriden a default keybinding, and
|
||||
;; what the default was. Also, it will tell you if the key was rebound after
|
||||
;; your binding it with `bind-key', and what it was rebound it to.
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'easy-mmode)
|
||||
|
||||
(defgroup bind-key nil
|
||||
"A simple way to manage personal keybindings"
|
||||
:group 'emacs)
|
||||
|
||||
(defcustom bind-key-column-widths '(18 . 40)
|
||||
"Width of columns in `describe-personal-keybindings'."
|
||||
:type '(cons integer integer)
|
||||
:group 'bind-key)
|
||||
|
||||
(defcustom bind-key-segregation-regexp
|
||||
"\\`\\(\\(C-[chx] \\|M-[gso] \\)\\([CM]-\\)?\\|.+-\\)"
|
||||
"Regular expression used to divide key sets in the output from
|
||||
\\[describe-personal-keybindings]."
|
||||
:type 'regexp
|
||||
:group 'bind-key)
|
||||
|
||||
(defcustom bind-key-describe-special-forms nil
|
||||
"If non-nil, extract docstrings from lambdas, closures and keymaps if possible."
|
||||
:type 'boolean
|
||||
:group 'bind-key)
|
||||
|
||||
;; Create override-global-mode to force key remappings
|
||||
|
||||
(defvar override-global-map (make-keymap)
|
||||
"override-global-mode keymap")
|
||||
|
||||
(define-minor-mode override-global-mode
|
||||
"A minor mode so that keymap settings override other modes."
|
||||
t "")
|
||||
|
||||
;; the keymaps in `emulation-mode-map-alists' take precedence over
|
||||
;; `minor-mode-map-alist'
|
||||
(add-to-list 'emulation-mode-map-alists
|
||||
`((override-global-mode . ,override-global-map)))
|
||||
|
||||
(defvar personal-keybindings nil
|
||||
"List of bindings performed by `bind-key'.
|
||||
|
||||
Elements have the form ((KEY . [MAP]) CMD ORIGINAL-CMD)")
|
||||
|
||||
;;;###autoload
|
||||
(defmacro bind-key (key-name command &optional keymap predicate)
|
||||
"Bind KEY-NAME to COMMAND in KEYMAP (`global-map' if not passed).
|
||||
|
||||
KEY-NAME may be a vector, in which case it is passed straight to
|
||||
`define-key'. Or it may be a string to be interpreted as
|
||||
spelled-out keystrokes, e.g., \"C-c C-z\". See documentation of
|
||||
`edmacro-mode' for details.
|
||||
|
||||
If PREDICATE is non-nil, it is a form evaluated to determine when
|
||||
a key should be bound. It must return non-nil in such cases.
|
||||
Emacs can evaluate this form at any time that it does redisplay
|
||||
or operates on menu data structures, so you should write it so it
|
||||
can safely be called at any time."
|
||||
(let ((namevar (make-symbol "name"))
|
||||
(keyvar (make-symbol "key"))
|
||||
(kdescvar (make-symbol "kdesc"))
|
||||
(bindingvar (make-symbol "binding")))
|
||||
`(let* ((,namevar ,key-name)
|
||||
(,keyvar (if (vectorp ,namevar) ,namevar
|
||||
(read-kbd-macro ,namevar)))
|
||||
(,kdescvar (cons (if (stringp ,namevar) ,namevar
|
||||
(key-description ,namevar))
|
||||
(quote ,keymap)))
|
||||
(,bindingvar (lookup-key (or ,keymap global-map) ,keyvar)))
|
||||
(add-to-list 'personal-keybindings
|
||||
(list ,kdescvar ,command
|
||||
(unless (numberp ,bindingvar) ,bindingvar)))
|
||||
,(if predicate
|
||||
`(define-key (or ,keymap global-map) ,keyvar
|
||||
'(menu-item "" nil :filter (lambda (&optional _)
|
||||
(when ,predicate
|
||||
,command))))
|
||||
`(define-key (or ,keymap global-map) ,keyvar ,command)))))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro unbind-key (key-name &optional keymap)
|
||||
"Unbind the given KEY-NAME, within the KEYMAP (if specified).
|
||||
See `bind-key' for more details."
|
||||
`(progn
|
||||
(bind-key ,key-name nil ,keymap)
|
||||
(setq personal-keybindings
|
||||
(cl-delete-if #'(lambda (k)
|
||||
,(if keymap
|
||||
`(and (consp (car k))
|
||||
(string= (caar k) ,key-name)
|
||||
(eq (cdar k) ',keymap))
|
||||
`(and (stringp (car k))
|
||||
(string= (car k) ,key-name))))
|
||||
personal-keybindings))))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro bind-key* (key-name command &optional predicate)
|
||||
"Similar to `bind-key', but overrides any mode-specific bindings."
|
||||
`(bind-key ,key-name ,command override-global-map ,predicate))
|
||||
|
||||
(defun bind-keys-form (args)
|
||||
"Bind multiple keys at once.
|
||||
|
||||
Accepts keyword arguments:
|
||||
:map MAP - a keymap into which the keybindings should be
|
||||
added
|
||||
:prefix KEY - prefix key for these bindings
|
||||
:prefix-map MAP - name of the prefix map that should be created
|
||||
for these bindings
|
||||
:prefix-docstring STR - docstring for the prefix-map variable
|
||||
:menu-name NAME - optional menu string for prefix map
|
||||
:filter FORM - optional form to determine when bindings apply
|
||||
|
||||
The rest of the arguments are conses of keybinding string and a
|
||||
function symbol (unquoted)."
|
||||
;; jww (2016-02-26): This is a hack; this whole function needs to be
|
||||
;; rewritten to normalize arguments the way that use-package.el does.
|
||||
(if (and (eq (car args) :package)
|
||||
(not (eq (car (cdr (cdr args))) :map)))
|
||||
(setq args (cons :map (cons 'global-map args))))
|
||||
(let* ((map (plist-get args :map))
|
||||
(doc (plist-get args :prefix-docstring))
|
||||
(prefix-map (plist-get args :prefix-map))
|
||||
(prefix (plist-get args :prefix))
|
||||
(filter (plist-get args :filter))
|
||||
(menu-name (plist-get args :menu-name))
|
||||
(pkg (plist-get args :package))
|
||||
(key-bindings (progn
|
||||
(while (keywordp (car args))
|
||||
(pop args)
|
||||
(pop args))
|
||||
args)))
|
||||
(when (or (and prefix-map (not prefix))
|
||||
(and prefix (not prefix-map)))
|
||||
(error "Both :prefix-map and :prefix must be supplied"))
|
||||
(when (and menu-name (not prefix))
|
||||
(error "If :menu-name is supplied, :prefix must be too"))
|
||||
(let ((args key-bindings)
|
||||
saw-map first next)
|
||||
(while args
|
||||
(if (keywordp (car args))
|
||||
(progn
|
||||
(setq next args)
|
||||
(setq args nil))
|
||||
(if first
|
||||
(nconc first (list (car args)))
|
||||
(setq first (list (car args))))
|
||||
(setq args (cdr args))))
|
||||
(cl-flet
|
||||
((wrap (map bindings)
|
||||
(if (and map pkg (not (eq map 'global-map)))
|
||||
(if (boundp map)
|
||||
bindings
|
||||
`((eval-after-load
|
||||
,(if (symbolp pkg) `',pkg pkg)
|
||||
'(progn ,@bindings))))
|
||||
bindings)))
|
||||
(append
|
||||
(when prefix-map
|
||||
`((defvar ,prefix-map)
|
||||
,@(when doc `((put ',prefix-map 'variable-documentation ,doc)))
|
||||
,@(if menu-name
|
||||
`((define-prefix-command ',prefix-map nil ,menu-name))
|
||||
`((define-prefix-command ',prefix-map)))
|
||||
,@(if (and map (not (eq map 'global-map)))
|
||||
(wrap map `((bind-key ,prefix ',prefix-map ,map ,filter)))
|
||||
`((bind-key ,prefix ',prefix-map nil ,filter)))))
|
||||
(wrap map
|
||||
(cl-mapcan
|
||||
(lambda (form)
|
||||
(if prefix-map
|
||||
`((bind-key ,(car form) ',(cdr form) ,prefix-map ,filter))
|
||||
(if (and map (not (eq map 'global-map)))
|
||||
`((bind-key ,(car form) ',(cdr form) ,map ,filter))
|
||||
`((bind-key ,(car form) ',(cdr form) nil ,filter)))))
|
||||
first))
|
||||
(when next
|
||||
(bind-keys-form
|
||||
(if pkg
|
||||
(cons :package (cons pkg next))
|
||||
next))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro bind-keys (&rest args)
|
||||
"Bind multiple keys at once.
|
||||
|
||||
Accepts keyword arguments:
|
||||
:map MAP - a keymap into which the keybindings should be
|
||||
added
|
||||
:prefix KEY - prefix key for these bindings
|
||||
:prefix-map MAP - name of the prefix map that should be created
|
||||
for these bindings
|
||||
:prefix-docstring STR - docstring for the prefix-map variable
|
||||
:menu-name NAME - optional menu string for prefix map
|
||||
:filter FORM - optional form to determine when bindings apply
|
||||
|
||||
The rest of the arguments are conses of keybinding string and a
|
||||
function symbol (unquoted)."
|
||||
(macroexp-progn (bind-keys-form args)))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro bind-keys* (&rest args)
|
||||
(macroexp-progn
|
||||
(bind-keys-form `(:map override-global-map ,@args))))
|
||||
|
||||
(defun get-binding-description (elem)
|
||||
(cond
|
||||
((listp elem)
|
||||
(cond
|
||||
((eq 'lambda (car elem))
|
||||
(if (and bind-key-describe-special-forms
|
||||
(stringp (nth 2 elem)))
|
||||
(nth 2 elem)
|
||||
"#<lambda>"))
|
||||
((eq 'closure (car elem))
|
||||
(if (and bind-key-describe-special-forms
|
||||
(stringp (nth 3 elem)))
|
||||
(nth 3 elem)
|
||||
"#<closure>"))
|
||||
((eq 'keymap (car elem))
|
||||
"#<keymap>")
|
||||
(t
|
||||
elem)))
|
||||
;; must be a symbol, non-symbol keymap case covered above
|
||||
((and bind-key-describe-special-forms (keymapp elem))
|
||||
(let ((doc (get elem 'variable-documentation)))
|
||||
(if (stringp doc) doc elem)))
|
||||
((symbolp elem)
|
||||
elem)
|
||||
(t
|
||||
"#<byte-compiled lambda>")))
|
||||
|
||||
(defun compare-keybindings (l r)
|
||||
(let* ((regex bind-key-segregation-regexp)
|
||||
(lgroup (and (string-match regex (caar l))
|
||||
(match-string 0 (caar l))))
|
||||
(rgroup (and (string-match regex (caar r))
|
||||
(match-string 0 (caar r))))
|
||||
(lkeymap (cdar l))
|
||||
(rkeymap (cdar r)))
|
||||
(cond
|
||||
((and (null lkeymap) rkeymap)
|
||||
(cons t t))
|
||||
((and lkeymap (null rkeymap))
|
||||
(cons nil t))
|
||||
((and lkeymap rkeymap
|
||||
(not (string= (symbol-name lkeymap) (symbol-name rkeymap))))
|
||||
(cons (string< (symbol-name lkeymap) (symbol-name rkeymap)) t))
|
||||
((and (null lgroup) rgroup)
|
||||
(cons t t))
|
||||
((and lgroup (null rgroup))
|
||||
(cons nil t))
|
||||
((and lgroup rgroup)
|
||||
(if (string= lgroup rgroup)
|
||||
(cons (string< (caar l) (caar r)) nil)
|
||||
(cons (string< lgroup rgroup) t)))
|
||||
(t
|
||||
(cons (string< (caar l) (caar r)) nil)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun describe-personal-keybindings ()
|
||||
"Display all the personal keybindings defined by `bind-key'."
|
||||
(interactive)
|
||||
(with-output-to-temp-buffer "*Personal Keybindings*"
|
||||
(princ (format (concat "Key name%s Command%s Comments\n%s %s "
|
||||
"---------------------\n")
|
||||
(make-string (- (car bind-key-column-widths) 9) ? )
|
||||
(make-string (- (cdr bind-key-column-widths) 8) ? )
|
||||
(make-string (1- (car bind-key-column-widths)) ?-)
|
||||
(make-string (1- (cdr bind-key-column-widths)) ?-)))
|
||||
(let (last-binding)
|
||||
(dolist (binding
|
||||
(setq personal-keybindings
|
||||
(sort personal-keybindings
|
||||
(lambda (l r)
|
||||
(car (compare-keybindings l r))))))
|
||||
|
||||
(if (not (eq (cdar last-binding) (cdar binding)))
|
||||
(princ (format "\n\n%s\n%s\n\n"
|
||||
(cdar binding)
|
||||
(make-string (+ 21 (car bind-key-column-widths)
|
||||
(cdr bind-key-column-widths)) ?-)))
|
||||
(if (and last-binding
|
||||
(cdr (compare-keybindings last-binding binding)))
|
||||
(princ "\n")))
|
||||
|
||||
(let* ((key-name (caar binding))
|
||||
(at-present (lookup-key (or (symbol-value (cdar binding))
|
||||
(current-global-map))
|
||||
(read-kbd-macro key-name)))
|
||||
(command (nth 1 binding))
|
||||
(was-command (nth 2 binding))
|
||||
(command-desc (get-binding-description command))
|
||||
(was-command-desc (and was-command
|
||||
(get-binding-description was-command)))
|
||||
(at-present-desc (get-binding-description at-present))
|
||||
)
|
||||
(let ((line
|
||||
(format
|
||||
(format "%%-%ds%%-%ds%%s\n" (car bind-key-column-widths)
|
||||
(cdr bind-key-column-widths))
|
||||
key-name (format "`%s\'" command-desc)
|
||||
(if (string= command-desc at-present-desc)
|
||||
(if (or (null was-command)
|
||||
(string= command-desc was-command-desc))
|
||||
""
|
||||
(format "was `%s\'" was-command-desc))
|
||||
(format "[now: `%s\']" at-present)))))
|
||||
(princ (if (string-match "[ \t]+\n" line)
|
||||
(replace-match "\n" t t line)
|
||||
line))))
|
||||
|
||||
(setq last-binding binding)))))
|
||||
|
||||
(provide 'bind-key)
|
||||
|
||||
;; Local Variables:
|
||||
;; indent-tabs-mode: nil
|
||||
;; End:
|
||||
|
||||
;;; bind-key.el ends here
|
56
one-file-mode/c++1x-minor-mode.el
Normal file
56
one-file-mode/c++1x-minor-mode.el
Normal file
@@ -0,0 +1,56 @@
|
||||
(require 'font-lock)
|
||||
;;###autoload
|
||||
(define-minor-mode c++1x-minor-mode
|
||||
"Extra highlighting for c++-mode that includes c++11 and c++14 keywords and features"
|
||||
:lighter "c++1x"
|
||||
|
||||
(defun --copy-face (new-face face)
|
||||
"Define NEW-FACE from existing FACE."
|
||||
(copy-face face new-face)
|
||||
(eval `(defvar ,new-face nil))
|
||||
(set new-face new-face))
|
||||
|
||||
(--copy-face 'font-lock-label-face ; labels, case, public, private, proteced, namespace-tags
|
||||
'font-lock-keyword-face)
|
||||
(--copy-face 'font-lock-doc-markup-face ; comment markups such as Javadoc-tags
|
||||
'font-lock-doc-face)
|
||||
(--copy-face 'font-lock-doc-string-face ; comment markups
|
||||
'font-lock-comment-face)
|
||||
|
||||
(global-font-lock-mode t)
|
||||
(setq font-lock-maximum-decoration t)
|
||||
|
||||
|
||||
(font-lock-add-keywords
|
||||
nil '(;; complete some fundamental keywords
|
||||
("\\<\\(void\\|unsigned\\|signed\\|char\\|short\\|bool\\|int\\|long\\|float\\|double\\)\\>" . font-lock-keyword-face)
|
||||
;; add the new C++11 keywords
|
||||
("\\<\\(alignof\\|alignas\\|constexpr\\|noexcept\\|\\|static_assert\\|thread_local\\|override\\|final\\)\\>" . font-lock-keyword-face)
|
||||
("\\<\\(decltype\\)\\>" . font-lock-builtin-face)
|
||||
("\\<\\(nullptr\\)\\>" . font-lock-constant-face)
|
||||
("\\<\\(char[0-9]+_t\\)\\>" . font-lock-keyword-face)
|
||||
;; PREPROCESSOR_CONSTANT
|
||||
("\\<[A-Z]+[A-Z_]+\\>" . font-lock-constant-face)
|
||||
;; hexadecimal numbers
|
||||
("\\<0[xX][0-9A-Fa-f]+\\>" . font-lock-constant-face)
|
||||
;; integer/float/scientific numbers
|
||||
("\\<[\\-+]*[0-9]*\\.?[0-9]+\\([ulUL]+\\|[eE][\\-+]?[0-9]+\\)?\\>" . font-lock-constant-face)
|
||||
;; c++11 string literals
|
||||
;; L"wide string"
|
||||
;; L"wide string with UNICODE codepoint: \u2018"
|
||||
;; u8"UTF-8 string", u"UTF-16 string", U"UTF-32 string"
|
||||
("\\<\\([LuU8]+\\)\".*?\"" 1 font-lock-keyword-face)
|
||||
;; R"(user-defined literal)"
|
||||
;; R"( a "quot'd" string )"
|
||||
;; R"delimiter(The String Data" )delimiter"
|
||||
;; R"delimiter((a-z))delimiter" is equivalent to "(a-z)"
|
||||
("\\(\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\)" 1 font-lock-keyword-face t) ; start delimiter
|
||||
( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\(.*?\\))[^\\s-\\\\()]\\{0,16\\}\"" 1 font-lock-string-face t) ; actual string
|
||||
( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(.*?\\()[^\\s-\\\\()]\\{0,16\\}\"\\)" 1 font-lock-keyword-face t) ; end delimiter
|
||||
|
||||
)))
|
||||
|
||||
;;###autoload
|
||||
(add-hook 'c++-mode-hook 'c++1x-minor-mode)
|
||||
|
||||
(provide 'c++1x-minor-mode)
|
928
one-file-mode/doc-mode.el
Normal file
928
one-file-mode/doc-mode.el
Normal file
@@ -0,0 +1,928 @@
|
||||
;;; doc-mode.el --- convenient editing of in-code documentation
|
||||
;;
|
||||
;; Copyright (C) 2007, 2009 Nikolaj Schumacher
|
||||
;; Author: Nikolaj Schumacher <bugs * nschum de>
|
||||
;; Version: 0.2
|
||||
;; Keywords: convenience tools
|
||||
;; URL: http://nschum.de/src/emacs/doc-mode/
|
||||
;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x
|
||||
;;
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
;;
|
||||
;; 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 2
|
||||
;; 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 requires the Semantic package to be installed and running:
|
||||
;; http://cedet.sourceforge.net/
|
||||
;;
|
||||
;; doc-mode allows easy creation and editing of JavaDoc or Doxygen comment
|
||||
;; blocks in your code. It also greatly improves readability of code by folding
|
||||
;; the blocks, so they don't take up precious screen lines.
|
||||
;;
|
||||
;; Add the following to your .emacs file:
|
||||
;; (require 'doc-mode)
|
||||
;; (add-hook 'c-mode-common-hook 'doc-mode)
|
||||
;;
|
||||
;; The command `doc-mode-fix-tag-doc' or "C-cdd" adds or replaces the
|
||||
;; documentation for the function, variable, or class at point.
|
||||
;; `doc-mode-remove-tag-doc' or "C-cdr" removes it.
|
||||
;;
|
||||
;; You can fold the comments by using `doc-mode-toggle-tag-doc-folding' or
|
||||
;; `doc-mode-fold-all'.
|
||||
;;
|
||||
;;; Change Log:
|
||||
;;
|
||||
;; 2009-03-22 (0.2)
|
||||
;; Added `doc-mode-keywords-from-tag-func' as customizable option.
|
||||
;; Improved parameter list change recognition.
|
||||
;; `doc-mode-jump-to-template' now enables jumping to the latest comment.
|
||||
;; `doc-mode-first-template' now jumps to the first template in this buffer.
|
||||
;;
|
||||
;; 2007-09-09 (0.1.1)
|
||||
;; Fixed return value detection.
|
||||
;; Actual keyword highlighting.
|
||||
;;
|
||||
;; 2007-09-07 (0.1)
|
||||
;; Initial release.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'semantic)
|
||||
(require 'cc-mode)
|
||||
(require 'newcomment) ;comment-fill-column
|
||||
|
||||
(dolist (err `("^No tag found$" "^Semantic can't parse buffer$"
|
||||
"^No template found$" "^doc-mode not enabled$"))
|
||||
(add-to-list 'debug-ignored-errors err))
|
||||
|
||||
;; semantic-after-auto-parse-hooks
|
||||
|
||||
(defgroup doc-mode nil
|
||||
"Minor mode for editing in-code documentation."
|
||||
:group 'convenience
|
||||
:group 'tools)
|
||||
|
||||
(defcustom doc-mode-auto-check-p t
|
||||
"*Should the buffer documentation be checked after a Semantic reparse."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-jump-to-template t
|
||||
"*Should the point be moved inside the template after inserting a doc."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-template-start "/**"
|
||||
"*The string to insert at the beginning of a comment."
|
||||
:group 'doc-mode
|
||||
:type 'string)
|
||||
|
||||
(defcustom doc-mode-template-end " */"
|
||||
"*The string to insert at the end of a comment."
|
||||
:group 'doc-mode
|
||||
:type 'string)
|
||||
|
||||
(defcustom doc-mode-template-continue " * "
|
||||
"*The string to insert at the beginning of each line in a comment."
|
||||
:group 'doc-mode
|
||||
:type 'string)
|
||||
|
||||
(defcustom doc-mode-template-single-line-start "/** "
|
||||
"*The string to insert at the beginning of a single-line comment.
|
||||
For using single-line comments, see `doc-mode-allow-single-line-comments'"
|
||||
:group 'doc-mode
|
||||
:type 'string)
|
||||
|
||||
(defcustom doc-mode-template-single-line-end " */"
|
||||
"*The string to insert at the end of a single-line comment.
|
||||
For using single-line comments, see `doc-mode-allow-single-line-comments'"
|
||||
:group 'doc-mode
|
||||
:type 'string)
|
||||
|
||||
(defcustom doc-mode-template-keyword-char "@"
|
||||
"*The character used to begin keywords."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "@" "@")
|
||||
(const :tag "\\" "\\")
|
||||
(string :tag "Other")))
|
||||
|
||||
(defcustom doc-mode-template-empty-line-after-summary nil
|
||||
"*Whether to put an empty line after the first one in the comment."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-template-empty-line-before-keywords nil
|
||||
"*Whether to put an empty line before the keyword list in a comment."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-template-keywords
|
||||
'("deprecated" "param" "return" "author" "exception" "throws" "version"
|
||||
"since" "see" "sa" "todo")
|
||||
"*Keywords that should be listed in this order.
|
||||
All other keywords will be considered regular text."
|
||||
:group 'doc-mode
|
||||
:type '(repeat string))
|
||||
|
||||
(defcustom doc-mode-allow-single-line-comments t
|
||||
"*Whether to allow a more space-saving format for very short comments.
|
||||
When this is enabled, `doc-mode-template-single-line-start' and
|
||||
`doc-mode-template-single-line-end' will be used to format single-line
|
||||
comments instead of `doc-mode-template-start', `doc-mode-template-end' and
|
||||
`doc-mode-template-continue'."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-fold-single-line-comments nil
|
||||
"*Whether to bother folding comments that are already a single line."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-align-keyword-arguments t
|
||||
"*Whether to align the arguments to a keyword continued in the next line.
|
||||
This may also be a number, describing how far to indent the argument list."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(integer :tag "Indent" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom doc-mode-fill-column nil
|
||||
"*The column at which to break text when formatting it.
|
||||
If this is nil, `comment-fill-column' is used."
|
||||
:group 'doc-mode
|
||||
:type '(choice (const :tag "Default" nil)
|
||||
(integer :tag "Fill Column")))
|
||||
|
||||
(defcustom doc-mode-keywords-from-tag-func 'doc-mode-keywords-from-tag
|
||||
"*Function used to generate keywords for a tag.
|
||||
This must be a function that takes two arguments. The first argument is the
|
||||
Semantic tag for which to generate keywords, the second is a list of existing
|
||||
keywords taken from the current doc comment. It should return the new list of
|
||||
keywords. Each element in a keyword list can be either a string or a list with
|
||||
a keyword, optional argument and optional description. Additional entries with
|
||||
undetermined content should be created with `doc-mode-new-keyword'."
|
||||
:group 'doc-mode
|
||||
:type 'function)
|
||||
|
||||
;;; keywords ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defconst doc-mode-font-lock-keywords
|
||||
(eval-when-compile
|
||||
`((,(concat "[\\@]"
|
||||
(regexp-opt
|
||||
'("addindex" "addtogroup" "anchor" "arg" "author" "brief" "callgraph"
|
||||
"callergraph" "category" "code" "cond" "copydoc" "date" "defgroup"
|
||||
"deprecated" "details" "dir" "dontinclude" "dot" "dotfile" "e"
|
||||
"else" "elseif" "em" "endcode" "endcond" "enddot" "endhtmlonly"
|
||||
"endif" "endlatexonly" "endlink" "endmanonly" "endmsc" "endverbatim"
|
||||
"endxmlonly" "example" "f$" "f[" "f]" "file" "fn" "hideinitializer"
|
||||
"htmlinclude" "htmlonly" "if" "ifnot" "image" "include"
|
||||
"includelineno" "ingroup" "internal" "invariant" "latexonly" "li"
|
||||
"line" "link" "mainpage" "manonly" "msc" "name" "nosubgrouping"
|
||||
"note" "overload" "package" "page" "par" "paragraph" "post" "pre"
|
||||
"private" "privatesection" "property" "protected" "protectedsection"
|
||||
"public" "publicsection" "ref" "remarks" "return" "retval" "sa"
|
||||
"section" "see" "serial" "serialData" "serialField"
|
||||
"showinitializer" "since" "skip" "skipline" "subpage" "subsection"
|
||||
"subsubsection" "test" "typedef" "until" "defvar" "verbatim"
|
||||
"verbinclude" "version" "weakgroup" "xmlonly" "xrefitem" "$" "@"
|
||||
"\\" "&" "~" "<" ">" "#" "%") t)
|
||||
"\\>")
|
||||
(0 font-lock-keyword-face prepend))
|
||||
;; don't highlight \n, it's too common in code
|
||||
("@n" (0 font-lock-keyword-face prepend))
|
||||
(,(concat "\\([@\\]"
|
||||
(regexp-opt '("class" "struct" "union" "exception" "enum" "throw"
|
||||
"throws") t)
|
||||
"\\)\\>\\(?:[ \t]+\\(\\sw+\\)\\)?")
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(3 font-lock-type-face prepend))
|
||||
(,(concat "\\([@\\]"
|
||||
(regexp-opt '("param" "param[in]" "param[out]" "param[in+out]" "a"
|
||||
"namespace" "relates" "relatesalso" "def") t)
|
||||
"\\)\\>\\(?:[ \t]+\\(\\sw+\\)\\)?")
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(3 font-lock-variable-name-face prepend))
|
||||
(,(concat "\\([@\\]retval\\)\\>\\(?:[ \t]+\\(\\sw+\\)\\)?")
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(2 font-lock-function-name-face prepend))
|
||||
(,(concat "[@\\]" (regexp-opt '("attention" "warning" "todo" "bug") t)
|
||||
"\\>")
|
||||
(0 font-lock-warning-face prepend))
|
||||
(,(concat "{@"
|
||||
(regexp-opt '("docRoot" "inheritDoc" "link" "linkplain" "value") t)
|
||||
"}")
|
||||
(0 font-lock-keyword-face prepend))
|
||||
("\\([@\\]b\\)[ \t\n]+\\([^ \t\n]+\\)"
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(2 'bold prepend))
|
||||
("\\([@\\]em?\\)[ \t\n]+\\([^ \t\n]+\\)"
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(2 'italic prepend))
|
||||
("\\([@\\][cp]\\)[ \t\n]+\\([^ \t\n]+\\)"
|
||||
(1 font-lock-keyword-face prepend)
|
||||
(2 'underline prepend)))))
|
||||
|
||||
;;; templates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar doc-mode-templates nil)
|
||||
(make-variable-buffer-local 'doc-mode-templates)
|
||||
|
||||
(defun doc-mode-add-template (beg end)
|
||||
(let ((overlay (make-overlay beg (point))))
|
||||
(overlay-put overlay 'intangible t)
|
||||
(overlay-put overlay 'face 'highlight)
|
||||
(overlay-put overlay 'insert-in-front-hooks '(doc-mode-replace-overlay))
|
||||
(overlay-put overlay 'modification-hooks '(doc-mode-delete-overlay))
|
||||
(push overlay doc-mode-templates)))
|
||||
|
||||
(defvar doc-mode-temp nil)
|
||||
|
||||
(defun doc-mode-delete-overlay (ov after-p beg end &optional r)
|
||||
(unless after-p
|
||||
(mapc 'doc-mode-unfold-by-overlay
|
||||
(overlays-in (1- (overlay-start ov)) (1+ (overlay-end ov))))
|
||||
(delete-overlay ov)
|
||||
(setq doc-mode-templates (delq ov doc-mode-templates))))
|
||||
|
||||
(defun doc-mode-replace-overlay (ov after-p beg end &optional r)
|
||||
(unless after-p
|
||||
(let ((inhibit-modification-hooks nil))
|
||||
(delete-region (overlay-start ov) (overlay-end ov)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-next-template (&optional pos limit)
|
||||
"Jump to the next unfinished documentation template in this buffer."
|
||||
(interactive)
|
||||
(unless pos (setq pos (point)))
|
||||
(unless limit (setq limit (point-max)))
|
||||
(let ((min-start limit)
|
||||
start)
|
||||
(dolist (ov doc-mode-templates)
|
||||
(setq start (overlay-start ov))
|
||||
(and (> start pos)
|
||||
(< start min-start)
|
||||
(setq min-start start)))
|
||||
(when (= min-start limit)
|
||||
(error "End of buffer"))
|
||||
(push-mark)
|
||||
(goto-char min-start)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-previous-template (&optional pos limit)
|
||||
"Jump to the previous unfinished documentation template in this buffer."
|
||||
(interactive)
|
||||
(unless pos (setq pos (point)))
|
||||
(unless limit (setq limit (point-min)))
|
||||
(let ((max-start limit)
|
||||
start)
|
||||
(dolist (ov doc-mode-templates)
|
||||
(setq start (overlay-start ov))
|
||||
(and (< start pos)
|
||||
(> start max-start)
|
||||
(setq max-start start)))
|
||||
(when (= max-start limit)
|
||||
(error "Beginning of buffer"))
|
||||
(push-mark)
|
||||
(goto-char max-start)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-first-template ()
|
||||
"Jump to the first unfinished documentation template in this buffer."
|
||||
(interactive)
|
||||
(condition-case err
|
||||
(doc-mode-next-template (point-min))
|
||||
(error (error "No template found"))))
|
||||
|
||||
;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar doc-mode-lighter " doc")
|
||||
|
||||
(defvar doc-mode-prefix-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "d" 'doc-mode-fix-tag-doc)
|
||||
(define-key map "c" 'doc-mode-check-tag-doc)
|
||||
(define-key map "t" 'doc-mode-toggle-tag-doc-folding)
|
||||
(define-key map "f" 'doc-mode-fold-tag-doc)
|
||||
(define-key map "u" 'doc-mode-unfold-tag-doc)
|
||||
(define-key map "r" 'doc-mode-remove-tag-doc)
|
||||
(define-key map "i" 'doc-mode-add-tag-doc)
|
||||
(define-key map "e" 'doc-mode-next-faulty-doc)
|
||||
(define-key map "n" 'doc-mode-next-template)
|
||||
(define-key map "\C-c" 'doc-mode-check-buffer)
|
||||
(define-key map "\C-f" 'doc-mode-fold-all)
|
||||
(define-key map "\C-u" 'doc-mode-unfold-all)
|
||||
map))
|
||||
|
||||
(defvar doc-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "\C-c\C-d" doc-mode-prefix-map)
|
||||
map)
|
||||
"Keymap used for `doc-mode'.")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode doc-mode
|
||||
"Minor mode for editing in-code documentation."
|
||||
nil doc-mode-lighter doc-mode-map
|
||||
(if doc-mode
|
||||
(progn
|
||||
(font-lock-add-keywords nil doc-mode-font-lock-keywords)
|
||||
(when doc-mode-auto-check-p
|
||||
(add-hook 'semantic-after-auto-parse-hooks 'doc-mode-check-buffer
|
||||
nil t)
|
||||
(add-hook 'semantic-after-idle-scheduler-reparse-hooks
|
||||
'doc-mode-check-buffer nil t)))
|
||||
(dolist (ov doc-mode-templates)
|
||||
(delete-overlay ov))
|
||||
(kill-local-variable 'doc-mode-templates)
|
||||
(doc-mode-unfold-all)
|
||||
(font-lock-remove-keywords nil doc-mode-font-lock-keywords)
|
||||
(remove-hook 'semantic-after-auto-parse-hooks 'doc-mode-check-buffer t)
|
||||
(remove-hook 'semantic-after-idle-scheduler-reparse-hooks
|
||||
'doc-mode-check-buffer t))
|
||||
|
||||
(when font-lock-mode
|
||||
(font-lock-fontify-buffer)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun doc-mode-current-tag ()
|
||||
(when (semantic-parse-tree-unparseable-p)
|
||||
(error "Semantic can't parse buffer"))
|
||||
(when (or (semantic-parse-tree-needs-rebuild-p)
|
||||
(semantic-parse-tree-needs-update-p))
|
||||
(condition-case nil
|
||||
(semantic-fetch-tags)
|
||||
(error (error "Semantic can't parse buffer"))))
|
||||
(save-excursion
|
||||
(or (semantic-current-tag-of-class 'function)
|
||||
(semantic-current-tag-of-class 'variable)
|
||||
(progn (beginning-of-line) (skip-chars-forward " \t\n") nil)
|
||||
(semantic-current-tag-of-class 'function)
|
||||
(semantic-current-tag-of-class 'variable)
|
||||
(if (not (looking-at "/\\*\\*"))
|
||||
(semantic-current-tag-of-class 'type)
|
||||
(progn (search-forward "*/" nil t)
|
||||
(skip-chars-forward " \t\n")
|
||||
nil))
|
||||
(semantic-current-tag-of-class 'function)
|
||||
(semantic-current-tag-of-class 'variable)
|
||||
(semantic-current-tag-of-class 'type))))
|
||||
|
||||
(defun doc-mode-current-tag-or-bust ()
|
||||
(or (doc-mode-current-tag) (error "No tag found")))
|
||||
|
||||
;;; insertion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun doc-mode-line-indent (keyword)
|
||||
"Determine left side offset when indenting LINE."
|
||||
(if (numberp doc-mode-align-keyword-arguments)
|
||||
doc-mode-align-keyword-arguments
|
||||
(+ 1 (length (car keyword))
|
||||
(if (equal (car keyword) "param")
|
||||
(1+ (length (cdr keyword)))
|
||||
0))))
|
||||
|
||||
(defun doc-mode-insert (text)
|
||||
"Insert TEXT if a string, or a template if 'prompt."
|
||||
(if (stringp text)
|
||||
(insert text)
|
||||
(let ((beg (point)))
|
||||
(insert (cadr text))
|
||||
(when doc-mode
|
||||
(doc-mode-add-template beg (point))))))
|
||||
|
||||
(defun doc-mode-insert-markup (markup &optional argument description)
|
||||
(insert doc-mode-template-keyword-char markup)
|
||||
(when argument
|
||||
(insert " ")
|
||||
(doc-mode-insert argument))
|
||||
(when description
|
||||
(insert " ")
|
||||
(doc-mode-insert description)))
|
||||
|
||||
(defun doc-mode-insert-line (line indent)
|
||||
(indent-to-column indent)
|
||||
(let ((beg (point)))
|
||||
(insert doc-mode-template-continue)
|
||||
(if (and (consp line) (not (eq (car line) 'prompt)))
|
||||
(apply 'doc-mode-insert-markup line)
|
||||
(doc-mode-insert line))
|
||||
(delete-char (- (skip-chars-backward " \t")))
|
||||
(when (> (point) (+ beg 2))
|
||||
(save-excursion (fill-region beg (point) 'left t)))
|
||||
(insert "\n")))
|
||||
|
||||
(defun doc-mode-insert-keyword (keyword indent)
|
||||
(indent-to-column indent)
|
||||
(let ((fill-column (or doc-mode-fill-column comment-fill-column fill-column))
|
||||
(fill-prefix (when doc-mode-align-keyword-arguments
|
||||
(concat (buffer-substring (point-at-bol) (point))
|
||||
doc-mode-template-continue
|
||||
(make-string (doc-mode-line-indent keyword)
|
||||
? )))))
|
||||
(doc-mode-insert-line keyword indent)))
|
||||
|
||||
(defun doc-mode-insert-doc (keywords &optional pos)
|
||||
"Insert a documentation at POS.
|
||||
LINES is a list of keywords."
|
||||
(save-excursion
|
||||
(if pos
|
||||
(goto-char pos)
|
||||
(setq pos (point)))
|
||||
(let ((indent (current-column)))
|
||||
|
||||
(if (and (not (cdr keywords)) doc-mode-allow-single-line-comments)
|
||||
(progn (insert doc-mode-template-single-line-start)
|
||||
(doc-mode-insert (car keywords))
|
||||
(insert doc-mode-template-single-line-end "\n"))
|
||||
(insert doc-mode-template-start "\n")
|
||||
|
||||
;; first line
|
||||
(when (or (stringp (car keywords))
|
||||
(eq 'prompt (caar keywords)))
|
||||
(doc-mode-insert-line (pop keywords) indent))
|
||||
|
||||
(when (and doc-mode-template-empty-line-after-summary
|
||||
(or (null doc-mode-template-empty-line-before-keywords)
|
||||
(stringp (cadr keywords))))
|
||||
(doc-mode-insert-line "" indent))
|
||||
|
||||
;; paragraphs
|
||||
(if (cdr keywords)
|
||||
(while (stringp (car keywords))
|
||||
(doc-mode-insert-line (pop keywords) indent)
|
||||
(when (stringp (car keywords))
|
||||
(doc-mode-insert-line "" indent)))
|
||||
(while (stringp (car keywords))
|
||||
(doc-mode-insert-line (pop keywords) indent)))
|
||||
|
||||
(when doc-mode-template-empty-line-before-keywords
|
||||
(doc-mode-insert-line "" indent))
|
||||
|
||||
;; keywords
|
||||
(while keywords
|
||||
(doc-mode-insert-keyword (pop keywords) indent))
|
||||
(indent-to-column indent)
|
||||
(insert doc-mode-template-end "\n"))
|
||||
|
||||
;; re-indent original line
|
||||
(if (< (current-column) indent)
|
||||
(indent-to-column indent)
|
||||
(move-to-column indent t))))
|
||||
|
||||
(and doc-mode-jump-to-template doc-mode-templates
|
||||
(ignore-errors (doc-mode-next-template pos (point)))))
|
||||
|
||||
(defun doc-mode-remove-doc (point)
|
||||
"Remove the documentation before POINT."
|
||||
(let* ((bounds (doc-mode-find-doc-bounds point))
|
||||
(beg (plist-get bounds :beg))
|
||||
(end (plist-get bounds :end)))
|
||||
(when bounds
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(incf beg (skip-chars-backward " \t"))
|
||||
(goto-char end)
|
||||
(incf end (skip-chars-forward " \t"))
|
||||
(when (eolp) (incf end))
|
||||
(delete-region beg end)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-remove-tag-doc (tag)
|
||||
"Remove the documentation for TAG.
|
||||
If called interactively, use the tag given by `doc-mode-current-tag'."
|
||||
(interactive (list (doc-mode-current-tag-or-bust)))
|
||||
(doc-mode-remove-doc (semantic-tag-start tag)))
|
||||
|
||||
;;; registering ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun doc-mode-find-doc-bounds (pos)
|
||||
"Find the documentation right before POS.
|
||||
If there is anything but whitespace between the documentation and POS, nil is
|
||||
returned. Otherwise a cons of the doc's beginning and end is given."
|
||||
(let (end)
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(when (re-search-backward "[ \t]*\n[ \t]*\\=" nil t)
|
||||
(setq end (point))
|
||||
(cond
|
||||
;; /// Doxygen comment */
|
||||
((looking-back "[ \t]*//[/!]\\(.*\\)$")
|
||||
(forward-line -1)
|
||||
(while (looking-at "[ \t]*//[/!]\\(.*\\)$")
|
||||
(forward-line -1))
|
||||
(forward-line 1)
|
||||
(skip-chars-forward " \t")
|
||||
`(:beg ,(point) :end ,end :column ,(current-indentation)))
|
||||
;; /** JavaDoc comment */
|
||||
((looking-back "\\*/")
|
||||
(goto-char (match-beginning 0))
|
||||
;; search for /*, not allowing any */ in between
|
||||
(when (and (re-search-backward "\\(/\\*\\)\\|\\*/" nil t)
|
||||
(match-beginning 1)
|
||||
(memq (char-after (1+ (match-beginning 1))) '(?! ?*)))
|
||||
`(:beg ,(point) :end ,end :column ,(current-column)))))))))
|
||||
|
||||
;;; formating ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun doc-mode-new-keyword (keyword &optional argument)
|
||||
(if (equal keyword "param")
|
||||
(list keyword argument '(prompt "<doc>"))
|
||||
(list keyword '(prompt "<doc>"))))
|
||||
|
||||
(defun doc-mode-has-return-value-p (tag)
|
||||
"Test if TAG has a return value to format."
|
||||
(and (eq (semantic-tag-class tag) 'function)
|
||||
(not (equal (semantic-tag-type tag) "void"))
|
||||
(not (semantic-tag-get-attribute tag :constructor-flag))
|
||||
(or (not (equal (semantic-tag-type tag) "int"))
|
||||
;; semantic bug, constructors sometimes appear to have int type
|
||||
(save-excursion (goto-char (semantic-tag-start tag))
|
||||
(and (re-search-forward "\\(\\<int\\>\\)\\|{\\|;"
|
||||
(semantic-tag-end tag) t)
|
||||
(match-beginning 1))))))
|
||||
|
||||
;;; extracting ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun doc-mode-extract-summary (beg end)
|
||||
(let ((bounds (doc-mode-find-summary beg end)))
|
||||
(buffer-substring-no-properties (car bounds) (cdr bounds))))
|
||||
|
||||
(defun doc-mode-find-summary (beg end)
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(if (or (re-search-forward "^[@\\]brief \\([^\t ][^\n]*\n\\)" end t)
|
||||
(re-search-forward "\\<\\(.*\\)\\(\\*+/\\|\n\\)" end t))
|
||||
(cons (match-beginning 1) (match-end 1))
|
||||
(cons beg beg))))
|
||||
|
||||
(defconst doc-mode-begin-regexp
|
||||
(eval-when-compile (concat "[ \t\n]*"
|
||||
"\\("
|
||||
"/\\*\\(\\*+\\|!\\)"
|
||||
"\\|"
|
||||
"//[!/]"
|
||||
"\\)[ \t]*")))
|
||||
|
||||
(defun doc-mode-clean-doc (beg end)
|
||||
"Remove the comment delimiters between BEG and END."
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(when (looking-at doc-mode-begin-regexp)
|
||||
(setq beg (match-end 0)))
|
||||
(goto-char end)
|
||||
(when (looking-back "[ \t\n\r]*\\*+/" nil t)
|
||||
(setq end (match-beginning 0)))
|
||||
(let ((lines (split-string (buffer-substring-no-properties beg end)
|
||||
"[ \t]*\n[ \t]*\\(\\*/?\\|//[!/]\\)?[ \t]*")))
|
||||
(while (equal (car lines) "")
|
||||
(pop lines))
|
||||
(mapconcat 'identity lines "\n"))))
|
||||
|
||||
(defun doc-mode-extract-keywords (beg end)
|
||||
"Extract documentation keywords between BEG and END.
|
||||
Returns a alist of keywords, where each element is the list (keyword
|
||||
argument value) or (keyword argument)."
|
||||
(let* ((paragraphs (doc-mode-clean-doc beg end))
|
||||
(doc "")
|
||||
(pos 0)
|
||||
match results)
|
||||
|
||||
(when (string-match
|
||||
"[ \t\n]*\\(\\(.\\|\n\\)*?\\)\\([@\\]\\<\\(.\\|\n\\)*\\'\\)"
|
||||
paragraphs)
|
||||
(setq doc (match-string-no-properties 3 paragraphs)
|
||||
paragraphs (match-string-no-properties 1 paragraphs)))
|
||||
|
||||
;; first line summary
|
||||
(when (string-match "\\`[ \t\n]*\\(.+\\.\\)\\([ \n]+\\|\\'\\)" paragraphs)
|
||||
(push (match-string 1 paragraphs) results)
|
||||
(setq pos (match-end 0)))
|
||||
|
||||
;; other paragraphs
|
||||
(dolist (paragraph (split-string (substring paragraphs pos)
|
||||
"[ \t]*\n\\(\n+[ \t]*\\|$\\)" t))
|
||||
(push (replace-regexp-in-string "[\n\r]" " " paragraph) results))
|
||||
|
||||
;; keywords
|
||||
(dolist (keyword (cdr (split-string doc "[@\\]\\<")))
|
||||
(setq match (split-string keyword))
|
||||
(push (if (equal (car match) "param")
|
||||
(list (car match) (cadr match)
|
||||
(mapconcat 'identity (cddr match) " "))
|
||||
(list (car match) (mapconcat 'identity (cdr match) " ")))
|
||||
results))
|
||||
(nreverse results)))
|
||||
|
||||
(defun doc-mode-extract-keywords-for-tag (tag)
|
||||
(let ((bounds (doc-mode-find-doc-bounds (semantic-tag-start tag))))
|
||||
(when bounds (doc-mode-extract-keywords (plist-get bounds :beg)
|
||||
(plist-get bounds :end)))))
|
||||
|
||||
(defun doc-mode-find-keyword (keyword keywords)
|
||||
(when keywords
|
||||
(if (and (consp (car keywords)) (string= (car (car keywords)) keyword))
|
||||
(cons (car keywords) (doc-mode-find-keyword keyword (cdr keywords)))
|
||||
(doc-mode-find-keyword keyword (cdr keywords)))))
|
||||
|
||||
(defun doc-mode-filter-keyword (keyword keywords)
|
||||
(when keywords
|
||||
(if (and (consp (car keywords)) (string= (car (car keywords)) keyword))
|
||||
(doc-mode-filter-keyword keyword (cdr keywords))
|
||||
(cons (car keywords) (doc-mode-filter-keyword keyword (cdr keywords))))))
|
||||
|
||||
(defun doc-mode-find-eligible-tags ()
|
||||
(when buffer-file-name
|
||||
(unless (or (semantic-parse-tree-unparseable-p)
|
||||
(semantic-parse-tree-needs-rebuild-p)
|
||||
(semantic-parse-tree-needs-update-p))
|
||||
(ignore-errors
|
||||
(let (tags)
|
||||
(semantic-brute-find-tag-by-function
|
||||
(lambda (tag)
|
||||
(when (semantic-tag-start tag)
|
||||
(case (semantic-tag-class tag)
|
||||
((function variable) (push tag tags))
|
||||
(type (setq tags
|
||||
(nconc (semantic-tag-type-members tag)
|
||||
tags))))))
|
||||
(semanticdb-file-stream buffer-file-name))
|
||||
tags)))))
|
||||
|
||||
;;; checking ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defsubst doc-mode-position (element list)
|
||||
"Return the first position of ELEMENT in LIST.
|
||||
Returns (length LIST) if no occurrence was found."
|
||||
(let ((pos 0))
|
||||
(while (and list (not (equal element (pop list))))
|
||||
(incf pos))
|
||||
pos))
|
||||
|
||||
(defun doc-mode-keyword< (a b tag)
|
||||
(if (equal (car a) "param")
|
||||
(let* ((args (mapcar 'semantic-tag-name
|
||||
(semantic-tag-get-attribute tag :arguments)))
|
||||
(a-param (cadr a))
|
||||
(b-param (cadr b))
|
||||
(a-pos (doc-mode-position a-param args))
|
||||
(b-pos (doc-mode-position b-param args)))
|
||||
(if (= a-pos b-pos)
|
||||
(string< a-param b-param)
|
||||
(< a-pos b-pos)))
|
||||
(string< (cadr a) (cadr b))))
|
||||
|
||||
(defun doc-mode-sort-keywords (keywords tag)
|
||||
(let ((lists (make-vector (1+ (length doc-mode-template-keywords)) nil))
|
||||
description)
|
||||
(dolist (k keywords)
|
||||
(if (or (stringp k) (and (eq (car k) 'prompt)))
|
||||
(push k description)
|
||||
(push k (elt lists (doc-mode-position (car k)
|
||||
doc-mode-template-keywords)))))
|
||||
(let ((i (length lists)) result)
|
||||
(while (> i 0)
|
||||
(setq result (nconc (sort (elt lists (decf i))
|
||||
(lambda (a b) (doc-mode-keyword< a b tag)))
|
||||
result)))
|
||||
(nconc (nreverse description) result))))
|
||||
|
||||
(defun doc-mode-update-parameters (old new)
|
||||
"Cleanse and sort NEW parameters according to OLD parameter list."
|
||||
(let (params car-new)
|
||||
(while (setq car-new (pop new))
|
||||
(push (or (dolist (p old) ;; search for match in old
|
||||
(when (equal (cadr p) car-new)
|
||||
(setq old (delete p old))
|
||||
(return p)))
|
||||
;; this parameter wasn't there before
|
||||
(if (or (null old) (member (cadr (car old)) new))
|
||||
;; insertion, new
|
||||
(doc-mode-new-keyword "param" car-new)
|
||||
;; the old parameter at this pos isn't there anymore, rename
|
||||
(list* "param" car-new (cddr (pop old)))))
|
||||
params))
|
||||
(nreverse params)))
|
||||
|
||||
(defun doc-mode-keywords-from-tag (tag keywords)
|
||||
"Create keywords for a Semantic TAG, taking descriptions from old KEYWORDS"
|
||||
(let ((old-params (doc-mode-find-keyword "param" keywords))
|
||||
(new-params (mapcar 'semantic-tag-name
|
||||
(semantic-tag-get-attribute tag :arguments))))
|
||||
;; fix return value
|
||||
(if (doc-mode-has-return-value-p tag)
|
||||
;; add
|
||||
(unless (doc-mode-find-keyword "return" keywords)
|
||||
(push (doc-mode-new-keyword "return") keywords))
|
||||
;; remove
|
||||
(setq keywords (doc-mode-filter-keyword "return" keywords)))
|
||||
(unless (stringp (car keywords))
|
||||
(push `(prompt ,(format "Description for %s." (semantic-tag-name tag)))
|
||||
keywords))
|
||||
(doc-mode-sort-keywords (nconc (doc-mode-update-parameters old-params
|
||||
new-params)
|
||||
(doc-mode-filter-keyword "param" keywords))
|
||||
tag)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-fix-tag-doc (tag)
|
||||
(interactive (list (doc-mode-current-tag-or-bust)))
|
||||
(let ((keywords (funcall doc-mode-keywords-from-tag-func
|
||||
tag (doc-mode-extract-keywords-for-tag tag))))
|
||||
(doc-mode-remove-tag-doc tag)
|
||||
(doc-mode-insert-doc keywords (semantic-tag-start tag))
|
||||
;; update lighter
|
||||
(doc-mode-check-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'doc-mode-add-tag-doc 'doc-mode-fix-tag-doc)
|
||||
|
||||
(defun doc-mode-format-message (type parameters)
|
||||
(when parameters
|
||||
(concat (case type
|
||||
('missing "Missing")
|
||||
('invalid "Invalid"))
|
||||
" parameter" (when (cdr parameters) "s") ": "
|
||||
(mapconcat 'identity parameters ", "))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-check-tag-doc (tag &optional print-message-p)
|
||||
(interactive (list (doc-mode-current-tag-or-bust) t))
|
||||
(let* ((actual (doc-mode-extract-keywords-for-tag tag))
|
||||
(expected (mapcar 'semantic-tag-name
|
||||
(semantic-tag-get-attribute tag :arguments))))
|
||||
(if actual
|
||||
(let ((no-doc-p (not (stringp (car actual))))
|
||||
;; we only report parameters
|
||||
(actual (mapcar 'cadr (doc-mode-find-keyword "param"
|
||||
actual)))
|
||||
invalid)
|
||||
(dolist (keyword actual)
|
||||
(if (member keyword expected)
|
||||
(setq expected (delete keyword expected))
|
||||
(push keyword invalid)))
|
||||
(when print-message-p
|
||||
(message "%s" (concat (and no-doc-p "Missing documentation")
|
||||
(and no-doc-p expected "\n")
|
||||
(doc-mode-format-message 'missing expected)
|
||||
(and (or no-doc-p expected) invalid "\n")
|
||||
(doc-mode-format-message 'invalid invalid))))
|
||||
(or no-doc-p expected invalid))
|
||||
(when print-message-p
|
||||
(message "Missing comment"))
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-check-buffer ()
|
||||
(interactive)
|
||||
(kill-local-variable 'doc-mode-lighter)
|
||||
(dolist (tag (doc-mode-find-eligible-tags))
|
||||
(when (doc-mode-check-tag-doc tag)
|
||||
(set (make-local-variable 'doc-mode-lighter) " doc!")
|
||||
(return t))))
|
||||
|
||||
(defun doc-mode-first-faulty-tag-doc ()
|
||||
(dolist (tag (sort (doc-mode-find-eligible-tags)
|
||||
(lambda (a b) (< (semantic-tag-start a)
|
||||
(semantic-tag-start b)))))
|
||||
(when (doc-mode-check-tag-doc tag)
|
||||
(return tag))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-next-faulty-doc ()
|
||||
"Jump to the next faulty documentation and print error."
|
||||
(interactive)
|
||||
(let ((tag (or (doc-mode-first-faulty-tag-doc)
|
||||
(error "End of buffer"))))
|
||||
(push-mark)
|
||||
(goto-char (semantic-tag-start tag))
|
||||
;; check again with message
|
||||
(doc-mode-check-tag-doc tag t)))
|
||||
|
||||
;;; folding ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar doc-mode-folds nil)
|
||||
(make-variable-buffer-local 'doc-mode-folds)
|
||||
|
||||
(defun doc-mode-fold-doc (point)
|
||||
(let ((bounds (doc-mode-find-doc-bounds point)))
|
||||
(when bounds
|
||||
(let* ((beg (plist-get bounds :beg))
|
||||
(end (plist-get bounds :end))
|
||||
(summary-bounds (doc-mode-find-summary beg end))
|
||||
(before-overlay (make-overlay beg (car summary-bounds)))
|
||||
(after-overlay (make-overlay (cdr summary-bounds) end))
|
||||
(siblings (list before-overlay after-overlay)))
|
||||
(when (or doc-mode-fold-single-line-comments
|
||||
(> (count-lines beg end) 1))
|
||||
(dolist (ov siblings)
|
||||
(overlay-put ov 'invisible t)
|
||||
(overlay-put ov 'isearch-open-invisible-temporary
|
||||
'doc-mode-unfold-by-overlay-temporary)
|
||||
(overlay-put ov 'isearch-open-invisible 'doc-mode-unfold-by-overlay)
|
||||
(overlay-put ov 'doc-mode-fold siblings))
|
||||
(setq doc-mode-folds (nconc doc-mode-folds siblings)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-fold-tag-doc (tag)
|
||||
"Fold the documentation for TAG.
|
||||
If called interactively, use the tag given by `doc-mode-current-tag'."
|
||||
(interactive (list (doc-mode-current-tag-or-bust)))
|
||||
(unless doc-mode
|
||||
(error "doc-mode not enabled"))
|
||||
(doc-mode-fold-doc (semantic-tag-start tag)))
|
||||
|
||||
(defun doc-mode-unfold-by-overlay (overlay &rest foo)
|
||||
"Unfold OVERLAY and its siblings permanently"
|
||||
(dolist (ov (overlay-get overlay 'doc-mode-fold))
|
||||
;; remove overlay
|
||||
(setq doc-mode-folds (delq ov doc-mode-folds))
|
||||
(delete-overlay ov)
|
||||
;; don't let isearch do anything with it
|
||||
(setq isearch-opened-overlays (delq ov isearch-opened-overlays))))
|
||||
|
||||
(defun doc-mode-unfold-by-overlay-temporary (overlay invisible)
|
||||
"Unfold OVERLAY and its siblings temporarily."
|
||||
(dolist (ov (overlay-get overlay 'doc-mode-fold))
|
||||
(overlay-put ov 'invisible invisible)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-unfold-doc (point)
|
||||
"Unfold the comment before POINT."
|
||||
(interactive "d")
|
||||
(unless doc-mode
|
||||
(error "doc-mode not enabled"))
|
||||
(let ((bounds (doc-mode-find-doc-bounds point)))
|
||||
(when bounds
|
||||
(let* ((beg (plist-get bounds :beg))
|
||||
(end (plist-get bounds :end))
|
||||
(overlays (overlays-in beg end))
|
||||
anything-done)
|
||||
(dolist (ov overlays)
|
||||
(when (overlay-get ov 'doc-mode-fold)
|
||||
(setq anything-done t)
|
||||
(delete-overlay ov)
|
||||
(setq doc-mode-folds (delq ov doc-mode-folds))))
|
||||
;; return non-nil, if anything unfolded
|
||||
;; this is used to toggle
|
||||
anything-done))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-unfold-tag-doc (tag)
|
||||
"Unfold the documentation for TAG.
|
||||
If called interactively, use the tag given by `doc-mode-current-tag'."
|
||||
(interactive (list (doc-mode-current-tag-or-bust)))
|
||||
(unless doc-mode
|
||||
(error "doc-mode not enabled"))
|
||||
(doc-mode-unfold-doc (semantic-tag-start tag)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-fold-all (&optional arg)
|
||||
(interactive "P")
|
||||
(unless doc-mode
|
||||
(error "doc-mode not enabled"))
|
||||
(if arg
|
||||
(doc-mode-unfold-all)
|
||||
(dolist (tag (doc-mode-find-eligible-tags))
|
||||
(doc-mode-fold-tag-doc tag))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-unfold-all ()
|
||||
(interactive)
|
||||
(dolist (ov doc-mode-folds)
|
||||
(delete-overlay ov))
|
||||
(kill-local-variable 'doc-mode-folds))
|
||||
|
||||
;;; toggle
|
||||
|
||||
;;;###autoload
|
||||
(defun doc-mode-toggle-tag-doc-folding (tag)
|
||||
"Toggle folding of TAG's documentation.
|
||||
If called interactively, use the tag given by `doc-mode-current-tag'."
|
||||
(interactive (list (doc-mode-current-tag-or-bust)))
|
||||
(or (doc-mode-unfold-tag-doc tag)
|
||||
(doc-mode-fold-tag-doc tag)))
|
||||
|
||||
(provide 'doc-mode)
|
||||
|
||||
;;; doc-mode.el ends here
|
168
one-file-mode/dockerfile-mode.el
Normal file
168
one-file-mode/dockerfile-mode.el
Normal file
@@ -0,0 +1,168 @@
|
||||
;;; dockerfile-mode.el --- Major mode for editing Docker's Dockerfiles
|
||||
|
||||
;; Copyright (c) 2013 Spotify AB
|
||||
;;
|
||||
;; Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
;; use this file except in compliance with the License. You may obtain a copy of
|
||||
;; the License at
|
||||
;;
|
||||
;; http://www.apache.org/licenses/LICENSE-2.0
|
||||
;;
|
||||
;; Unless required by applicable law or agreed to in writing, software
|
||||
;; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
;; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
;; License for the specific language governing permissions and limitations under
|
||||
;; the License.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'sh-script)
|
||||
(require 'rx)
|
||||
|
||||
(declare-function cygwin-convert-file-name-to-windows "cygw32.c" (file &optional absolute-p))
|
||||
|
||||
(defvar docker-image-name nil)
|
||||
|
||||
(defgroup dockerfile nil
|
||||
"dockerfile code editing commands for Emacs."
|
||||
:link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
|
||||
:prefix "dockerfile-"
|
||||
:group 'languages)
|
||||
|
||||
(defcustom dockerfile-mode-hook nil
|
||||
"*Hook called by `dockerfile-mode'."
|
||||
:type 'hook
|
||||
:group 'dockerfile)
|
||||
|
||||
(defcustom dockerfile-use-sudo nil
|
||||
"Runs docker builder command with sudo.")
|
||||
|
||||
(defcustom dockerfile-build-args nil
|
||||
"List of --build-arg to pass to docker build.
|
||||
|
||||
Each element of the list will be passed as a separate
|
||||
--build-arg to the docker build command."
|
||||
:type '(repeat string)
|
||||
:group 'dockerfile)
|
||||
|
||||
(defvar dockerfile-font-lock-keywords
|
||||
`(,(cons (rx (or line-start "onbuild ")
|
||||
(group (or "from" "maintainer" "run" "cmd" "expose" "env" "arg"
|
||||
"add" "copy" "entrypoint" "volume" "user" "workdir" "onbuild"
|
||||
"label" "stopsignal"))
|
||||
word-boundary)
|
||||
font-lock-keyword-face)
|
||||
,@(sh-font-lock-keywords)
|
||||
,@(sh-font-lock-keywords-2)
|
||||
,@(sh-font-lock-keywords-1))
|
||||
"Default font-lock-keywords for `dockerfile mode'.")
|
||||
|
||||
(defvar dockerfile-mode-map
|
||||
(let ((map (make-sparse-keymap))
|
||||
(menu-map (make-sparse-keymap)))
|
||||
(define-key map "\C-c\C-b" 'dockerfile-build-buffer)
|
||||
(define-key map "\C-c\M-b" 'dockerfile-build-no-cache-buffer)
|
||||
(define-key map "\C-c\C-z" 'dockerfile-test-function)
|
||||
(define-key map "\C-c\C-c" 'comment-region)
|
||||
(define-key map [menu-bar dockerfile-mode] (cons "Dockerfile" menu-map))
|
||||
(define-key menu-map [dfc]
|
||||
'(menu-item "Comment Region" comment-region
|
||||
:help "Comment Region"))
|
||||
(define-key menu-map [dfb]
|
||||
'(menu-item "Build" dockerfile-build-buffer
|
||||
:help "Send the Dockerfile to docker build"))
|
||||
(define-key menu-map [dfb]
|
||||
'(menu-item "Build without cache" dockerfile-build-no-cache-buffer
|
||||
:help "Send the Dockerfile to docker build without cache"))
|
||||
map))
|
||||
|
||||
(defvar dockerfile-mode-syntax-table
|
||||
(let ((table (make-syntax-table)))
|
||||
(modify-syntax-entry ?# "<" table)
|
||||
(modify-syntax-entry ?\n ">" table)
|
||||
(modify-syntax-entry ?' "\"" table)
|
||||
table)
|
||||
"Syntax table for `dockerfile-mode'.")
|
||||
|
||||
(define-abbrev-table 'dockerfile-mode-abbrev-table nil
|
||||
"Abbrev table used while in `dockerfile-mode'.")
|
||||
|
||||
(unless dockerfile-mode-abbrev-table
|
||||
(define-abbrev-table 'dockerfile-mode-abbrev-table ()))
|
||||
|
||||
(defun dockerfile-build-arg-string ()
|
||||
"Create a --build-arg string for each element in `dockerfile-build-args'."
|
||||
(mapconcat (lambda (arg) (concat "--build-arg " "\"" arg "\""))
|
||||
dockerfile-build-args " "))
|
||||
|
||||
(defun standard-filename (file)
|
||||
"Convert the file name to OS standard.
|
||||
If in Cygwin environment, uses Cygwin specific function to convert the
|
||||
file name. Otherwise, uses Emacs' standard conversion function."
|
||||
(format "%s" (if (fboundp 'cygwin-convert-file-name-to-windows)
|
||||
(s-replace "\\" "\\\\" (cygwin-convert-file-name-to-windows file))
|
||||
(convert-standard-filename file))))
|
||||
|
||||
;;;###autoload
|
||||
(defun dockerfile-build-buffer (image-name)
|
||||
"Build an image based upon the buffer"
|
||||
(interactive
|
||||
(if (null docker-image-name)
|
||||
(list (read-string "image-name: " nil nil))
|
||||
(list docker-image-name)))
|
||||
(save-buffer)
|
||||
(if (stringp image-name)
|
||||
(async-shell-command
|
||||
(format "%sdocker build -t %s %s -f \"%s\" \"%s\""
|
||||
(if dockerfile-use-sudo "sudo " "")
|
||||
image-name
|
||||
(dockerfile-build-arg-string)
|
||||
(standard-filename (buffer-file-name))
|
||||
(standard-filename (file-name-directory (buffer-file-name))))
|
||||
"*docker-build-output*")
|
||||
(print "docker-image-name must be a string, consider surrounding it with double quotes")))
|
||||
|
||||
;;;###autoload
|
||||
(defun dockerfile-build-no-cache-buffer (image-name)
|
||||
"Build an image based upon the buffer without cache"
|
||||
(interactive
|
||||
(if (null docker-image-name)
|
||||
(list (read-string "image-name: " nil nil))
|
||||
(list docker-image-name)))
|
||||
(save-buffer)
|
||||
(if (stringp image-name)
|
||||
(async-shell-command
|
||||
(format "%s docker build --no-cache -t %s %s -f \"%s\" \"%s\""
|
||||
(if dockerfile-use-sudo "sudo" "")
|
||||
image-name
|
||||
(dockerfile-build-arg-string)
|
||||
(standard-filename (buffer-file-name))
|
||||
(standard-filename (file-name-directory (buffer-file-name))))
|
||||
"*docker-build-output*")
|
||||
(print "docker-image-name must be a string, consider surrounding it with double quotes")))
|
||||
|
||||
;; Handle emacs < 24, which does not have prog-mode
|
||||
(defalias 'dockerfile-parent-mode
|
||||
(if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode dockerfile-mode dockerfile-parent-mode "Dockerfile"
|
||||
"A major mode to edit Dockerfiles.
|
||||
\\{dockerfile-mode-map}
|
||||
"
|
||||
(set-syntax-table dockerfile-mode-syntax-table)
|
||||
(set (make-local-variable 'require-final-newline) mode-require-final-newline)
|
||||
(set (make-local-variable 'comment-start) "#")
|
||||
(set (make-local-variable 'comment-end) "")
|
||||
(set (make-local-variable 'comment-start-skip) "#+ *")
|
||||
(set (make-local-variable 'parse-sexp-ignore-comments) t)
|
||||
(set (make-local-variable 'font-lock-defaults)
|
||||
'(dockerfile-font-lock-keywords nil t))
|
||||
(setq local-abbrev-table dockerfile-mode-abbrev-table))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("Dockerfile.*\\'" . dockerfile-mode))
|
||||
|
||||
(provide 'dockerfile-mode)
|
||||
|
||||
;;; dockerfile-mode.el ends here
|
267
one-file-mode/go-template-mode.el
Normal file
267
one-file-mode/go-template-mode.el
Normal file
@@ -0,0 +1,267 @@
|
||||
;;; go-template-mode.el --- Major mode for Go template language
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; 1) Copy this file somewhere in your Emacs `load-path'. To see what
|
||||
;; your `load-path' is, run inside emacs: C-h v load-path<RET>
|
||||
;;
|
||||
;; 2) Add the following to your .emacs file:
|
||||
;;
|
||||
;; (require 'go-template-mode)
|
||||
|
||||
;;; Known Bugs:
|
||||
|
||||
;; 1) Highlights all strings in the source file, including HTML attributes,
|
||||
;; and does not properly highlight template actions inside these strings.
|
||||
|
||||
(defvar go-template-mode-syntax-table
|
||||
(let ((st (make-syntax-table)))
|
||||
;; Add _ to :word: character class
|
||||
(modify-syntax-entry ?_ "w" st)
|
||||
|
||||
;; Operators (punctuation)
|
||||
(modify-syntax-entry ?: "." st)
|
||||
(modify-syntax-entry ?= "." st)
|
||||
(modify-syntax-entry ?| "." st)
|
||||
|
||||
;; Strings and comments are font-locked separately.
|
||||
(modify-syntax-entry ?\" "." st)
|
||||
(modify-syntax-entry ?\' "." st)
|
||||
(modify-syntax-entry ?` "." st)
|
||||
(modify-syntax-entry ?\\ "." st)
|
||||
|
||||
st)
|
||||
"Syntax table for Go template mode.")
|
||||
|
||||
(defvar go-template-mode-keywords
|
||||
'("define" "else" "end" "if" "range" "template" "with")
|
||||
"All keywords in the Go template language. Used for font locking.")
|
||||
|
||||
(defvar go-template-mode-builtins
|
||||
'("and" "html" "index" "js" "len" "not" "or" "print" "printf" "println" "urlquery")
|
||||
"All builtin functions in the Go template language. Used for font locking.")
|
||||
|
||||
|
||||
(defconst go-template-mode-pair-tag
|
||||
(regexp-opt
|
||||
'("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
|
||||
"big" "blockquote" "body" "button" "caption" "center" "cite"
|
||||
"code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
|
||||
"dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
|
||||
"header" "nav" "footer" "section"
|
||||
"h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
|
||||
"kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
|
||||
"noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
|
||||
"s" "samp" "script" "select" "small" "span" "strike"
|
||||
"strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
|
||||
"tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
|
||||
t))
|
||||
(defconst go-template-mode-standalone-tag
|
||||
(regexp-opt
|
||||
'("base" "br" "hr" "img" "input" "meta" "param")
|
||||
t))
|
||||
|
||||
(defconst go-template-mode-font-lock-keywords
|
||||
`((go-template-mode-font-lock-cs-comment 0 font-lock-comment-face t)
|
||||
(go-template-mode-font-lock-cs-string 0 font-lock-string-face t)
|
||||
(,(regexp-opt '("{{" "}}")) (0 font-lock-preprocessor-face))
|
||||
("$[a-zA-Z0-9]*" (0 font-lock-variable-name-face))
|
||||
(,(regexp-opt go-template-mode-keywords 'words) . font-lock-keyword-face)
|
||||
(,(regexp-opt go-template-mode-builtins 'words) . font-lock-builtin-face)
|
||||
(,(concat "</?" go-template-mode-pair-tag ">?") (0 font-lock-function-name-face))
|
||||
(,(concat "<" go-template-mode-standalone-tag ">?") (0 font-lock-function-name-face))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Parser
|
||||
;;
|
||||
|
||||
(defvar go-template-mode-mark-cs-end 1
|
||||
"The point at which the comment/string cache ends. The buffer
|
||||
will be marked from the beginning up to this point (that is, up
|
||||
to and including character (1- go-template-mode-mark-cs-end)).")
|
||||
(make-variable-buffer-local 'go-template-mode-mark-cs-end)
|
||||
|
||||
(defvar go-template-mode-mark-nesting-end 1
|
||||
"The point at which the nesting cache ends. The buffer will be
|
||||
marked from the beginning up to this point.")
|
||||
(make-variable-buffer-local 'go-template-mode-mark-nesting-end)
|
||||
|
||||
(defun go-template-mode-mark-clear-cache (b e)
|
||||
"A before-change-function that clears the comment/string and
|
||||
nesting caches from the modified point on."
|
||||
|
||||
(save-restriction
|
||||
(widen)
|
||||
(when (<= b go-template-mode-mark-cs-end)
|
||||
;; Remove the property adjacent to the change position.
|
||||
;; It may contain positions pointing beyond the new end mark.
|
||||
(let ((b (let ((cs (get-text-property (max 1 (1- b)) 'go-template-mode-cs)))
|
||||
(if cs (car cs) b))))
|
||||
(remove-text-properties
|
||||
b (min go-template-mode-mark-cs-end (point-max)) '(go-template-mode-cs nil))
|
||||
(setq go-template-mode-mark-cs-end b)))
|
||||
(when (< b go-template-mode-mark-nesting-end)
|
||||
(remove-text-properties b (min go-template-mode-mark-nesting-end (point-max)) '(go-template-mode-nesting nil))
|
||||
(setq go-template-mode-mark-nesting-end b))))
|
||||
|
||||
(defmacro go-template-mode-parser (&rest body)
|
||||
"Evaluate BODY in an environment set up for parsers that use
|
||||
text properties to mark text. This inhibits changes to the undo
|
||||
list or the buffer's modification status and inhibits calls to
|
||||
the modification hooks. It also saves the excursion and
|
||||
restriction and widens the buffer, since most parsers are
|
||||
context-sensitive."
|
||||
|
||||
(let ((modified-var (make-symbol "modified")))
|
||||
`(let ((buffer-undo-list t)
|
||||
(,modified-var (buffer-modified-p))
|
||||
(inhibit-modification-hooks t)
|
||||
(inhibit-read-only t))
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(unwind-protect
|
||||
(progn ,@body)
|
||||
(set-buffer-modified-p ,modified-var)))))))
|
||||
|
||||
(defun go-template-mode-cs (&optional pos)
|
||||
"Return the comment/string state at point POS. If point is
|
||||
inside a comment or string (including the delimiters), this
|
||||
returns a pair (START . END) indicating the extents of the
|
||||
comment or string."
|
||||
|
||||
(unless pos
|
||||
(setq pos (point)))
|
||||
(when (> pos go-template-mode-mark-cs-end)
|
||||
(go-template-mode-mark-cs pos))
|
||||
(get-text-property pos 'go-template-mode-cs))
|
||||
|
||||
(defun go-template-mode-mark-cs (end)
|
||||
"Mark comments and strings up to point END. Don't call this
|
||||
directly; use `go-template-mode-cs'."
|
||||
(setq end (min end (point-max)))
|
||||
(go-template-mode-parser
|
||||
(save-match-data
|
||||
(let ((pos
|
||||
;; Back up to the last known state.
|
||||
(let ((last-cs
|
||||
(and (> go-template-mode-mark-cs-end 1)
|
||||
(get-text-property (1- go-template-mode-mark-cs-end)
|
||||
'go-template-mode-cs))))
|
||||
(if last-cs
|
||||
(car last-cs)
|
||||
(max 1 (1- go-template-mode-mark-cs-end))))))
|
||||
(while (< pos end)
|
||||
(goto-char pos)
|
||||
(let ((cs-end ; end of the text property
|
||||
(cond
|
||||
((looking-at "{{/\\*")
|
||||
(goto-char (+ pos 4))
|
||||
(if (search-forward "*/}}" (1+ end) t)
|
||||
(point)
|
||||
end))
|
||||
((looking-at "\"")
|
||||
(goto-char (1+ pos))
|
||||
(if (looking-at "[^\"\n\\\\]*\\(\\\\.[^\"\n\\\\]*\\)*\"")
|
||||
(match-end 0)
|
||||
(end-of-line)
|
||||
(point)))
|
||||
((looking-at "'")
|
||||
(goto-char (1+ pos))
|
||||
(if (looking-at "[^'\n\\\\]*\\(\\\\.[^'\n\\\\]*\\)*'")
|
||||
(match-end 0)
|
||||
(end-of-line)
|
||||
(point)))
|
||||
((looking-at "`")
|
||||
(goto-char (1+ pos))
|
||||
(while (if (search-forward "`" end t)
|
||||
(if (eq (char-after) ?`)
|
||||
(goto-char (1+ (point))))
|
||||
(goto-char end)
|
||||
nil))
|
||||
(point)))))
|
||||
(cond
|
||||
(cs-end
|
||||
(put-text-property pos cs-end 'go-template-mode-cs (cons pos cs-end))
|
||||
(setq pos cs-end))
|
||||
((re-search-forward "[\"'`]\\|{{/\\*" end t)
|
||||
(setq pos (match-beginning 0)))
|
||||
(t
|
||||
(setq pos end)))))
|
||||
(setq go-template-mode-mark-cs-end pos)))))
|
||||
|
||||
|
||||
|
||||
(defun go-template-mode-font-lock-cs (limit comment)
|
||||
"Helper function for highlighting comment/strings. If COMMENT is t,
|
||||
set match data to the next comment after point, and advance point
|
||||
after it. If COMMENT is nil, use the next string. Returns nil
|
||||
if no further tokens of the type exist."
|
||||
;; Ensures that `next-single-property-change' below will work properly.
|
||||
(go-template-mode-cs limit)
|
||||
(let (cs next (result 'scan))
|
||||
(while (eq result 'scan)
|
||||
(if (or (>= (point) limit) (eobp))
|
||||
(setq result nil)
|
||||
(setq cs (go-template-mode-cs))
|
||||
(if cs
|
||||
(if (eq (= (char-after (car cs)) ?/) comment)
|
||||
;; If inside the expected comment/string, highlight it.
|
||||
(progn
|
||||
;; If the match includes a "\n", we have a
|
||||
;; multi-line construct. Mark it as such.
|
||||
(goto-char (car cs))
|
||||
(when (search-forward "\n" (cdr cs) t)
|
||||
(put-text-property
|
||||
(car cs) (cdr cs) 'font-lock-multline t))
|
||||
(set-match-data (list (car cs) (cdr cs) (current-buffer)))
|
||||
(goto-char (cdr cs))
|
||||
(setq result t))
|
||||
;; Wrong type. Look for next comment/string after this one.
|
||||
(goto-char (cdr cs)))
|
||||
;; Not inside comment/string. Search for next comment/string.
|
||||
(setq next (next-single-property-change
|
||||
(point) 'go-template-mode-cs nil limit))
|
||||
(if (and next (< next limit))
|
||||
(goto-char next)
|
||||
(setq result nil)))))
|
||||
result))
|
||||
|
||||
(defun go-template-mode-font-lock-cs-string (limit)
|
||||
"Font-lock iterator for strings."
|
||||
(go-template-mode-font-lock-cs limit nil))
|
||||
|
||||
(defun go-template-mode-font-lock-cs-comment (limit)
|
||||
"Font-lock iterator for comments."
|
||||
(go-template-mode-font-lock-cs limit t))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode go-template-mode fundamental-mode "Go-Template"
|
||||
"Major mode for editing Go template text.
|
||||
|
||||
This provides basic syntax highlighting for keyword, built-ins, functions,
|
||||
and some types. It does not provide indentation."
|
||||
|
||||
;; Font lock
|
||||
(set (make-local-variable 'font-lock-defaults)
|
||||
'(go-template-mode-font-lock-keywords nil nil nil nil))
|
||||
|
||||
;; Remove stale text properties
|
||||
(save-restriction
|
||||
(widen)
|
||||
(remove-text-properties 1 (point-max)
|
||||
'(go-template-mode-cs nil go-template-mode-nesting nil)))
|
||||
|
||||
;; Reset the syntax mark caches
|
||||
(setq go-template-mode-mark-cs-end 1
|
||||
go-template-mode-mark-nesting-end 1)
|
||||
(add-hook 'before-change-functions #'go-template-mode-mark-clear-cache nil t)
|
||||
|
||||
;; Use tabs (Go style)
|
||||
(setq indent-tabs-mode t))
|
||||
|
||||
(add-to-list 'auto-mode-alist '("\\.gotmpl$" . go-template-mode))
|
||||
|
||||
(provide 'go-template-mode)
|
||||
|
||||
;;; go-template-mode.el ends here
|
1169
one-file-mode/use-package.el
Normal file
1169
one-file-mode/use-package.el
Normal file
File diff suppressed because it is too large
Load Diff
11830
one-file-mode/web-mode.el
Normal file
11830
one-file-mode/web-mode.el
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user