From 4c382dc9441e831bc2af9de26fc68eae89b2bf5c Mon Sep 17 00:00:00 2001 From: Iain Bethune Date: Thu, 23 Jan 2014 13:36:54 +0000 Subject: [PATCH] Add emacs mode for editing CP2K input files (and locate it alongside the vim tools) (Lianheng Tong) svn-origin-rev: 13495 --- tools/input_editing/emacs/README | 69 +++++ tools/input_editing/emacs/cp2k-mode.el | 243 ++++++++++++++++++ .../input_editing/vim}/vim.xsl | 0 .../input_editing/vim}/xml2vim | 0 4 files changed, 312 insertions(+) create mode 100644 tools/input_editing/emacs/README create mode 100644 tools/input_editing/emacs/cp2k-mode.el rename {web/manual => tools/input_editing/vim}/vim.xsl (100%) rename {web/manual => tools/input_editing/vim}/xml2vim (100%) diff --git a/tools/input_editing/emacs/README b/tools/input_editing/emacs/README new file mode 100644 index 0000000000..ac683481da --- /dev/null +++ b/tools/input_editing/emacs/README @@ -0,0 +1,69 @@ +# -*- mode: org -*- + +#+title: cp2k-mode.el README +#+author: Lianheng Tong +#+date: Wednesday, 2014/01/22 + +cp2k-mode.el is provides a major mode in emacs for editing CP2K input +files. It has been tested on emacs 2.1, 2.3 and 2.4. + +* Functionalities + - Recognises and font-locks: + - the full CP2K input preprocessor syntax + - the sections and subsections + - the keywords + - the comment lines + - Indents lines according to the CP2K input syntax using key + - New interactive functions: + - cp2k-indent-line: indents the line according to CP2K + input syntax. + - cp2k-beginning-of-block: goes to the beginning of the subsection, + marks the current cursor position. + - cp2k-end-of-block: goes to the ending of the subsection, + marks the current cursor position. + - Key Bindings: + - cp2k-indent-line + - C-j newline-and-indent + - C-c ; comment-region + - C-M-a cp2k-beginning-of-block + - C-M-e cp2k-end-of-block + +* Installation + You need to put cp2k-mode.el in one of your local emacs lisp + directories, which is in the search path of your emacs installation. + +** Adding to emacs's search path + + If you have never installed any packages manually before, and do not + know the search path of your emacs installation, then in your home + directory create directory: + + ~/.emacs.d/lisp/ + + This is the usual place where the local/user defined emacs lisp + files are installed. Move cp2k-mode.el to ~/.emacs.d/lisp/ + + Then, add the following in your .emacs file (which should be in your + home directory, and if it does not exist, create one): + + (add-to-list 'load-path "~/.emacs.d/lisp/") + + This tells emacs to add ~/.emacs.d/lisp/ to its search path for *.el + files. + +** Tell emacs to load cp2k-mode.el at startup + + Once cp2k-mode.el is in the search path, we need to tell emacs to + load it at start up, this is done by adding + + (require 'cp2k-mode nil 'noerror) + + to your .emacs file. + +** Tell emacs to recognise *.inp automatically as a cp2k input file + + Add + + (add-to-list 'auto-mode-alist '("\\.inp\\'" . cp2k-mode)) + + to your .emacs file. diff --git a/tools/input_editing/emacs/cp2k-mode.el b/tools/input_editing/emacs/cp2k-mode.el new file mode 100644 index 0000000000..724792c314 --- /dev/null +++ b/tools/input_editing/emacs/cp2k-mode.el @@ -0,0 +1,243 @@ +;;;;; Emacs major mode for cp2k input, written by Lianheng Tong +;;;;; Copyright (c) Lianheng Tong +;;;;; Last modify date: Thursday, 2013/12/05 + +;;;; Syntax highlighting of keywords +(defconst cp2k-font-lock-keywords + (list + ;; blocks + '("^[ \t]*\\(&\\(END[ \t]*\\)?\\(\\sw+\\)\\)" + (1 font-lock-function-name-face)) + ;; keywords + '("^[ \t]*\\<\\([^&@$]\\sw*\\)\\>" + (1 font-lock-keyword-face)) + ;; preprocessor type 1 + '("^[ \t]*\\(@\\(\\(I\\(F\\|NCLUDE\\)\\)\\|ENDIF\\)\\)" + (1 font-lock-preprocessor-face)) + ;; preprocessor type 2 + '("^[ \t]*\\(@SET\\)[ \t]+\\(\\sw+\\)" + (1 font-lock-preprocessor-face) (2 font-lock-variable-name-face nil t)) + ;; variables + '("\\${?\\(\\sw+\\)}?" + (1 font-lock-variable-name-face))) + "Highlighting keywords for `cp2k-mode'.") + +;;;; Syntax table +(defconst cp2k-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?# "<" st) ; # is beg. comment + (modify-syntax-entry ?\n ">" st) ; \n is end. comment + (modify-syntax-entry ?_ "w" st) ; underscore is part of names + (modify-syntax-entry ?\' "\"" st) ; string quote + (modify-syntax-entry ?\" "\"" st) ; string quote + (modify-syntax-entry ?\r "-" st) ; return is whitespace + (modify-syntax-entry ?+ "." st) ; + is puntuation + (modify-syntax-entry ?- "." st) ; - is puntuation + (modify-syntax-entry ?* "." st) ; * is puntuation + (modify-syntax-entry ?/ "." st) ; / is puntuation + (modify-syntax-entry ?= "." st) ; = is puntuation + (modify-syntax-entry ?\\ "\\" st) ; \ is escape char + st) + "Syntax table for `cp2k-mode'.") + +;;;; Define keymap for the major mode +(defvar cp2k-mode-map + (let ((map (make-sparse-keymap))) + ;; Define mode specific key-bindings here + (define-key map (kbd "C-j") 'newline-and-indent) + (define-key map (kbd "C-c ;") 'comment-region) + (define-key map (kbd "TAB") 'cp2k-indent-line) + (define-key map (kbd "C-M-a") 'cp2k-beginning-of-block) + (define-key map (kbd "C-M-e") 'cp2k-end-of-block) + map) + "Keymap for `cp2k-mode'.") + +;;;; Syntax indentation +(defvar cp2k-indent + 2 + "standard indentation for in `cp2k-mode'") + +(defconst cp2k-emptyline + "^\\s-*$" + "regexp matching an empty line in `cp2k-mode'") + +(defconst cp2k-opening + ;; note that this definition also matches cp2k-closing, so for + ;; positive identification of cp2k-opening, one must use AND + ;; construct with (not lookingat cp2k-closing). One way to avoid + ;; this is to construct search for line that does NOT contain END, + ;; however, practical implementation of that leads to emacs + ;; occasionally throwing the exception "regex stack overflow"... + ;; The emacs regex search is pretty crudely implemented, and it is + ;; far more robust to do a positive search, than negative + ;; searches. So I opted for this more crude method here. + "^[ \t]*\\(\\(&\\sw+\\)\\|\\(@IF\\>\\)\\)" + "regexp matching lines starting (or closing --- due to + limitations in regex implementation) a cp2k block in + `cp2k-mode'") + +(defconst cp2k-closing + "^[ \t]*\\(\\(&END[ \t]*.*$\\)\\|\\(@ENDIF\\>\\)\\).*$" + "regexp matching lines closing a cp2k block in `cp2k-mode'") + +(defun cp2k-beginning-of-block (&optional not-set-mark) + "move the cursor to the beginning of the block in `cp2k-mode'" + (interactive) + (if (not not-set-mark) + (push-mark)) + (beginning-of-line) + (let ((find_opening t) + (in_nested nil) + (initial_opening nil)) + ;; if looking at opening now, search for the parent opening + (if (and (looking-at cp2k-opening) + (not (looking-at cp2k-closing)) + (not (bobp))) + (setq initial_opening t)) + (while (and find_opening (not (bobp))) + (if (and (looking-at cp2k-opening) + (not (looking-at cp2k-closing)) + (not in_nested) + (not initial_opening)) + (setq find_opening nil) + (progn + (when (and (looking-at cp2k-opening) + (not (looking-at cp2k-closing))) + (setq in_nested nil) + (setq initial_opening nil)) + (forward-line -1) + ;; if the line is a closing statement then + ;; skip to the line of the opening statement + (if (looking-at cp2k-closing) + (progn + (setq in_nested t) + (cp2k-beginning-of-block t)))))))) + +(defun cp2k-end-of-block (&optional not-set-mark) + "move the cursor to the ending of the block in `cp2k-mode'" + (interactive) + (if (not not-set-mark) + (push-mark)) + (beginning-of-line) + (let ((find_closing t) + (in_nested nil) + (initial_closing nil)) + ;; if looking at closing now, search for the parent closing + (if (and (looking-at cp2k-closing) + (not (eobp))) + (setq initial_closing t)) + (while (and find_closing (not (eobp))) + (if (and (looking-at cp2k-closing) + (not in_nested) + (not initial_closing)) + (setq find_closing nil) + (progn + (when (looking-at cp2k-closing) + (setq in_nested nil) + (setq initial_closing nil)) + (forward-line 1) + ;; if the line is a opening statement then + ;; skip to the line of the closing statement + (if (and (looking-at cp2k-opening) + (not (looking-at cp2k-closing))) + (progn + (setq in_nested t) + (cp2k-end-of-block t)))))))) + +(defun cp2k-forward-one-line () + "move the cursor foward one line, ignore empty or comment lines, in `cp2k-mode'" + (beginning-of-line) + (forward-line 1) + (while (and (or (looking-at cp2k-emptyline) + (looking-at "^[ \t]*#")) + (not (eobp))) + (forward-line 1))) + +(defun cp2k-backward-one-line () + "move the cursor foward one line, ignore empty or comment lines, in `cp2k-mode'" + (beginning-of-line) + (forward-line -1) + (while (and (or (looking-at cp2k-emptyline) + (looking-at "^[ \t]*#")) + (not (bobp))) + (forward-line -1))) + +(defun cp2k-left-of-point-is-empty () + "check if the left of the point is only white space in `cp2k-mode'" + (let (not-empty) + (save-excursion + (setq not-empty + (re-search-backward "[^ \t]" (line-beginning-position) t 1))) + (not not-empty))) + +(defun cp2k-indent-line () + "indent current line in `cp2k-mode'" + (interactive) + (let ((indent 0) + (position (point-marker))) + ;; record initial indentation position + (back-to-indentation) + (indent-line-to + (max + 0 + (catch 'indentation + (save-excursion + (beginning-of-line) + (if (bobp) (throw 'indentation 0)) + ;; get the block indentation + (save-excursion + (cp2k-beginning-of-block t) + (setq indent (current-indentation))) + ;; look at the current line + (if (looking-at cp2k-closing) + (throw 'indentation indent)) + ;; move up one non-empty line + (cp2k-backward-one-line) + (setq indent (current-indentation)) + (if (and (looking-at cp2k-opening) + (not (looking-at cp2k-closing))) + (setq indent (+ indent cp2k-indent))) + (throw 'indentation indent))))) + ;; move cursor to indentation point if in the beginning white + ;; space otherwise leave unchanged + (goto-char position) + (if (cp2k-left-of-point-is-empty) + (back-to-indentation)) + (set-marker position nil))) + +;;;; Define mode hook +(defvar cp2k-mode-hook nil) + +;;;; Entry function +(define-derived-mode cp2k-mode fundamental-mode "cp2k" + "Major mode for editing cp2k input. Copyright (c) Lianheng Tong 2013/12/06" + :syntax-table cp2k-mode-syntax-table + ;; setq-local is undefined in emacs versions prior 24.3, it is + ;; equivalent to make-local-variable, and followed by setq, and this + ;; is what has been used here. This works for older versions of + ;; emacs. + (make-local-variable 'comment-start) + (setq comment-start "# ") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "#+\\s-*") + ;; see emacs manual on font-lock-defaults, nil here means also + ;; font-lock comments and strings, and t means the value of + ;; font-lock-keywords-case-fold-search is set to non-nil, allowing + ;; case insensitive search + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(cp2k-font-lock-keywords nil t)) + ;; whether the indentation key-word search case-sensitive or not (in + ;; my implementation, using looking-at function) is effected only by + ;; the variable case-fold-search, and is uneffected by + ;; font-lock-keywords-case-fold-search. So turn case-fold-search on + ;; just in case + (make-local-variable 'case-fold-search) + (setq case-fold-search t) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'cp2k-indent-line)) + +;;;; Add to autoload alist +(add-to-list 'auto-mode-alist '("\\.cp2kin\\'" . cp2k-mode)) + +;;;; Last line +(provide 'cp2k-mode) diff --git a/web/manual/vim.xsl b/tools/input_editing/vim/vim.xsl similarity index 100% rename from web/manual/vim.xsl rename to tools/input_editing/vim/vim.xsl diff --git a/web/manual/xml2vim b/tools/input_editing/vim/xml2vim similarity index 100% rename from web/manual/xml2vim rename to tools/input_editing/vim/xml2vim