;;; charmm-mode.el --- Major mode for editing Charmm scripts ;; $Id: charmm-mode.el 645 2008-10-03 21:05:31Z juan $ ;; Copyright (c) 2008, Juan Roberto Perilla, Oliver Beckstein and Michael Lerner ;; All rights reserved. ;; Copyright (C) 2008 Juan R. Perilla, Oliver Beckstein and Michael Lerner ;; Comments, suggestions: ;; ;; Juan R. Perilla ;; Oliver Beckstein ;; Michael G. Lerner (m g lerner at gmail d0t com) ;; ;; ;; TODO: ;; 1. Indentation X thanks to Michael G. Lerner ;; 2. Autofill ;; 3. Menu (?) ;; 4. find out everything that should cause indentation (like vibran or if/then or label loopthing/goto loopthing) ;; (defconst charmm-version "$Revision: 645 $" "`charmm-mode' version number.") ;; ;; 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. ;; * The names of its contributors may not be used to endorse or promote ;; products derived from this software without specific prior ;; written permission. ;; ;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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. ;; This software is provided as-is, without express or implied ;; warranty. Permission to use, copy, modify, distribute or sell this ;; software, without fee, for any purpose and by any individual or ;; organization, is hereby granted, provided that the above copyright ;; notice and this paragraph appear in all copies ;; ------------------------------------------------------------ ;; INSTALL ;; ------------------------------------------------------------ ;; (1) Either copy this file into your site-lisp preferred directory, ;; then add the following lines to you .emacs file: ;; ;;;; (autoload 'charmm-mode "charmm-mode" "Charmm input files" t) ;;;; (setq auto-mode-alist (cons '("\\.inp\\'\\|\\.str\\'\\|\\.charmm\\'" . charmm-mode) auto-mode-alist)) ;; ;; (2) or put it somewhere else, eg save it as '~/.emacs.d/charmm-mode.el'. Then use in .emacs: ;; ;;;; (autoload 'charmm-mode "~/.emacs.d/charmm-mode.el" "Charmm input files" t) ;;;; (setq auto-mode-alist (cons '("\\.inp\\'\\|\\.str\\'\\|\\.charmm\\'" . charmm-mode) auto-mode-alist)) ;; ;; ;; ------------------------------------------------------------ ;; - Indentation: ;; This does some very basic indentation. Follow these rules: ;; 1. If blocks that are contained entirely on one line must ;; not use the word "then". e.g.: ;; ;; if something .gt. somethingelse goto somelabel ;; ;; 2. If blocks that span multiple lines must contain the word ;; "then" on the first line. e.g.: ;; ;; if ?NUMNODE .gt. 1 then ! These things cannot be run in parallel ;; echo "This cannot be run in parallel!" ;; stop ;; endif ;; ;; 3. Labels that control loops must begin with the word "loop". e.g.: ;; ;; label looptraj ;; various loopy things ;; if something .lt. somethingelse goto looptraj ;; ;; 4. You can indent a block by beginning it with a label that ;; starts with the word "start" and ending it with a label ;; that starts with the word "end". e.g.: ;; ;; label startsetup ;; do lots of setup calculations ;; that only need to be run once ;; even though you may want to run ;; this script several times ;; label startminimization ;; generate a minimized structure ;; and save it for later, etc. ;; label endminimization ;; label endsetup ;; ------------------------------------------------------------ (require 'speedbar) (require 'font-lock) (require 'custom) (require 'etags) (require 'generic-x) (eval-when-compile (require 'regexp-opt)) ;; Local variables (defgroup charmm nil "Major mode for editing Charmm scripts." :prefix "charmm-" :group 'languages) (defcustom charmm-speedbar-config t "*When set to true automatically configures Speedbar to observe Charmm files.\ Ignores charmm-file patterns option; fixed to expression \"\\.\\(inp\\)\"" :type 'boolean :group 'charmm) (defcustom charmm-mode-speedbar-open nil "Normally charmm-mode starts with the speedbar closed.\ Turning this on will open it whenever charmm-mode is loaded." :type 'boolean :group 'charmm) ;;;###autoload (defcustom charmm-file-patterns (list "\\.inp\\'\\|\\.str\\'|\\.charmm\\'") "*List of file patterns for which to automatically invoke charmm-mode." :type '(repeat (regexp :tag "Pattern")) :group 'charmm) (defcustom charmm-mode-user-hook nil "List of functions to be executed on entry to charmm-mode" :type 'hook :group 'charmm) (defvar charmm-completion-table nil "Obarray of tag names defined in current tags table and functions know to CHARMM.") (defun charmm-continuation-line () "Returns non-nil if current line continus a previous one" (eq ?- (char-before (line-end-position 0)))) ;; Mode-syntax-table (defvar charmm-mode-syntax-table nil "Syntax table used in `charmm-mode' buffers.") (when (not charmm-mode-syntax-table) (setq charmm-mode-syntax-table (make-syntax-table)) (modify-syntax-entry ?\( "()" charmm-mode-syntax-table) (modify-syntax-entry ?\) ")(" charmm-mode-syntax-table) (modify-syntax-entry ?\[ "(]" charmm-mode-syntax-table) (modify-syntax-entry ?\] ")[" charmm-mode-syntax-table) (modify-syntax-entry ?\{ "(}" charmm-mode-syntax-table) (modify-syntax-entry ?\} "){" charmm-mode-syntax-table) ;; Add operator symbols misassigned in the std table (modify-syntax-entry ?\$ "." charmm-mode-syntax-table) (modify-syntax-entry ?\% "." charmm-mode-syntax-table) (modify-syntax-entry ?\& "." charmm-mode-syntax-table) (modify-syntax-entry ?\* "." charmm-mode-syntax-table) (modify-syntax-entry ?\+ "." charmm-mode-syntax-table) (modify-syntax-entry ?\- "." charmm-mode-syntax-table) (modify-syntax-entry ?\/ "." charmm-mode-syntax-table) (modify-syntax-entry ?\< "." charmm-mode-syntax-table) (modify-syntax-entry ?\= "." charmm-mode-syntax-table) (modify-syntax-entry ?\> "." charmm-mode-syntax-table) (modify-syntax-entry ?\| "." charmm-mode-syntax-table) (modify-syntax-entry ?\_ "w" charmm-mode-syntax-table) ;; Both single quote and double quote are string delimiters (modify-syntax-entry ?\' "\"" charmm-mode-syntax-table) (modify-syntax-entry ?\" "\"" charmm-mode-syntax-table) ;; backquote is open and close paren (modify-syntax-entry ?\` "$" charmm-mode-syntax-table) ;; comment delimiters (modify-syntax-entry ?\# "<" charmm-mode-syntax-table) (modify-syntax-entry ?\n ">" charmm-mode-syntax-table) ) ;; Handle Speedbar (if charmm-mode-speedbar-open (speedbar 1)) (if (and charmm-speedbar-config (symbolp 'speedbar)) (speedbar-add-supported-extension "\\.\\(inp\\)")) ;; ;; We treat commands such as label, bomlev etc as keywords ;; (defconst charmm-keywords (eval-when-compile (regexp-opt '(;; core keywords "by" "label" "bomlev" "bomblev" "wrnlev" "iolev" "prnlev" "unit" "setup" "name" "read" "write" "sele" "end" "byres" "rms" "orient" "omsc" "bynu" ))) "Charmm keywords.") (defconst charmm-trouble-keywords (eval-when-compile (regexp-opt '(;; important keywords "card" "pdb" "unfo" "form" "coor" "all" ))) "More charmm keywords (red).") ;; ;; Everything that is parsed should be here, from ;; ;; charmm_main.src WRD.EQ. ;; miscom.src WRD.EQ. ;; ;; vibran WRD ;; correl WRD and WD ;; graphx WRD ;; ;; There are duplicate entries below(not within the same source dir), they wont hurt performance only make this file larger. TODO: eliminate duplicates :p ;; ;; Extracted from source code using: ;; ;; ;; grep -ih "\.EQ\." * | grep WRD |sed 's/[^'\'']*\(['\'']\)\([^'\'']*\).*/"\2"/' | awk '! a[$0]++' | awk '{print|"sort"}' | awk 'ORS=" "' ;; ;; Keywords and others are hand-removed. ;; (defconst charmm-commands (eval-when-compile (regexp-opt '( "ADD " "ADDL" "AE" "AFM" "ANAL" "ATLI" "AUTO" "BACT" "BANN" "BARI" "BLOC" "BOMB" "BOUN" "BUCK" "BUIL" "CADP" "CALC" "CHEQ" "CLOS" "CONS" "COOR" "CORR" "CORS" "CRAY" "CRYS" "CSA " "CVEL" "DATE" "DEAD" "DECR" "DEFA" "DEFI" "DELE" "DIHE" "DIMS" "DIVI" "DMCO" "DONO" "DPCO" "DRAW" "DRUD" "DYNA" "ECHO" "ECHU" "EDIF" "EDIT" "EEF1" "ELSE" "EMAP" "ENDI" "ENER" "ENSE" "ENVI" "EPMF" "EQ " "ESTA" "ETEN" "ETRA" "EXPA" "EXPO" "FACT" "FAST" "FITC" "FITP" "FLUC" "FORM" "FOUR" "FQBA" "GALG" "GAME" "GBIM" "GBMV" "GBOR" "GBSW" "GE " "GENE" "GET " "GETE" "GETH" "GNN " "GOTO" "GRAP" "GRID" "GT" "HBON" "HBTR" "HBUI" "HQBM" "IC" "IF" "IMAG" "IMPA" "INCR" "INQU" "INTE" "IOFO" "IOLE" "IRAN" "JOIN" "LABE" "LDTI" "LE " "LET " "LINK" "LONE" "LONG" "LOWE" "LT " "LUPO" "MAST" "MBON" "MC " "MCMA" "MERG" "MINI" "MKCL" "MKPR" "MLTC" "MLTE" "MMFP" "MMQM" "MNDO" "MOLV" "MONI" "MOVE" "MSCA" "MTS " "MULL" "MULT" "NBAC" "NBON" "NBSO" "NE " "NMR " "NOBO" "NOE " "NOSE" "OFF" "OLAP" "ON" "OPEN" "OUTU" "PAR" "PARA" "PARV" "PATC" "PATH" "PBEQ" "PERT" "PFBA" "PHMD" "PHTE" "PINT" "PIPF" "PNMI" "POLA" "POLY" "POTE" "PREF" "PRES" "PRIN" "PRLE" "PROT" "PULL" "QCHE" "QUAN" "QUB " "QUIC" "RAND" "RCON" "RDFS" "READ" "RECE" "RELL" "RENA" "REPD" "REPL" "RESD" "RESE" "RETU" "REWF" "REWI" "REWT" "RGYR" "RISM" "RMSD" "RPAT" "RUSH" "RXMD" "RXNC" "SASA" "SBOU" "SCAL" "SCCD" "SCPI" "SELE" "SERV" "SET " "SHAK" "SHAP" "SHEL" "SHOR" "SHOW" "SKIP" "SPCO" "SQUA" "STRE" "SYST" "TAMD" "TEST" "THEN" "TIME" "TITL" "TMDI" "TORS" "TPCO" "TRAJ" "TRAN" "TREK" "TRIM" "TSAL" "TSM " "UMBR" "UNBU" "UPDA" "UPPE" "USE " "USER" "VECT" "VIBR" "VPAR" "WAIT" "WHAM" "WRIT" "ZMAT" "ZMOD" ;; Correl "AROU" "CROS" "DEFI" "EXVC" "IMAG" "INFO" "MULTI" "PROT" "REMO" "RLP" "SELE" "SETA" "SETB" "SHBO" "SHTH" "SITE" "SOLV" "WATE" "XREF" "YREF" "ZREF" "ALPH" "ANGL" "ATOM" "BETA" "BOND" "CELL" "CLUS" "CONT" "COPY" "CORF" "CROS" "DDIP" "DENS" "DIPO" "DIST" "DOTP" "DUPL" "EDIT" "ENER" "ENTE" "GAMM" "GYRA" "HBON" "HELI" "HIST" "IMPR" "INER" "INTE" "IPOW" "MANT" "MODE" "MOVI" "MRTI" "MULT" "PRDI" "PROB" "PRVE" "PUCK" "RATI" "READ" "RMS " "SATM" "SATX" "SDDP" "SDIP" "SHAP" "SHOW" "SPEC" "STAT" "SURF" "TEMP" "TIME" "TORS" "TRAJ" "USER" "VACF" "VECM" "W" "WHIS" "WREO" "WRIT" "XYZ" "ZERO" ;; GraphVx "ASS" "AUT" "AXE" "BLAC" "BLUE" "BOX" "BROW" "CEN" "CHAR" "COL" "COLO" "COOR" "CYAN" "DEBU" "DEF" "DIS" "DKBL" "DRA" "END" "ERA" "EXE" "FON" "FUL" "GRAY" "GREE" "HBS" "HEL" "IC " "INT" "LARG" "LBL" "LIN" "MAGE" "MAK" "MAX" "MEDI" "NONE" "OFF" "ORAN" "PLU" "POI" "POV" "PSC" "PURP" "RAD" "RED " "RES" "ROT" "SCA" "SMAL" "SNA" "STE" "TEX" "TRA " "TRAJ" "TURQ" "VSMA" "WHIT" "XLIM" "YELL" "YLIM" "ZCL" "ZCU" "ZLIM" ;; Vibran "ADDB" "ANGL" "BASI" "BHES" "BLKS" "BOND" "COMP" "COOR" "COPY" "CORR" "DELE" "DIAG" "DIFF" "DIHE" "DIMB" "EDIT" "EXPL" "FILL" "FLUC" "FORC" "GNM" "IC" "INCL" "INIT" "MASS" "MBH" "MOVE" "MULT" "NOMA" "NORM" "ORTH" "PAFL" "PART" "PED " "POST" "PRIN" "PROJ" "QUAS" "RAYL" "RBQU" "READ" "REDU" "REMA" "REMO" "ROTA" "SECO" "SET " "SHAK" "SPHE" "THER" "TR" "TRAJ" "TRAN" "TX" "TY" "TZ" "USER" "VSA" "WRIT" ))) "Charmm commands.") (defconst charmm-identifier (eval-when-compile '"[a-zA-Z\_\x7f-\xff][a-zA-Z0-9\_\x7f-\xff]*") "Characters in a Charmm identifier.") ;; ;; Atoms and other "types ;; (defconst charmm-types (eval-when-compile (regexp-opt '("CA" "C" "O" "OT" "N" "OW" "H" "targ" "dims" "comp" ))) "Charmm \"types\".") ;; ;; The so-called Energy Variables ;; ;; Extracted from source code using ;; ;; grep -ir "SETMSI" |sed 's/[^'\'']*\(['\'']\)\([^'\'']*\).*/"\2"/' | awk '! a[$0]++' | awk '{print|"sort"}' | awk 'ORS=" "' ;; grep -ir "SETMSR" |sed 's/[^'\'']*\(['\'']\)\([^'\'']*\).*/"\2"/' | awk '! a[$0]++' | awk '{print|"sort"}' | awk 'ORS=" "' ;; ;; (defconst charmm-MSI (eval-when-compile (regexp-opt '( ;;MSI "BOMLEV" "CMAPSET" "EMLX" "EMLY" "EMLZ" "EMMX" "EMMY" "EMMZ" "EMNC" "EMNSR" "EMNST" "ENPI" "FASTER" "FLUSH" "FNBL" "HIREP" "INBA" "IOLEV" "IOSTAT" "IRAN" "LFAST" "LIREP" "MAXA" "MAXATC" "MAXB" "MAXCB" "MAXCH" "MAXCI" "MAXCN" "MAXCP" "MAXCSP" "MAXCT" "MAXDA1" "MAXDA2" "MAXIMP" "MAXNB" "MAXP" "MAXPAD" "MAXRES" "MAXSEG" "MAXT" "MINCONVRG" "MINDA1" "MINDA2" "MINECALLS" "MINSTEPS" "MYNODE" "MYREP" "NACC" "NACTEQV" "NALPHA" "NATC" "NATI" "NATO" "NATOM" "NATVDW" "NBDRUDE" "NBETA" "NBON" "NBOND" "NCB" "NCH" "NCI" "NCN" "NCONFIG" "NCONTACT" "NCOOP" "NCP" "NCPU" "NCQ" "NCRT" "NCSB" "NCSP" "NCT" "NCTP" "NDEGF" "NDON" "NDRUDE" "NENSEM" "NFILE" "NGRP" "NIC" "NIMGOO" "NIMGUU" "NIMGVU" "NIMGVV" "NIMP" "NIMPHI" "NMVI" "NNB" "NNBA" "NNBG" "NNBI" "NNNB" "NNOO" "NNUU" "NNVU" "NOCC" "NOK" "NPAIR" "NPHI" "NREP" "NRES" "NRXA" "NRXG" "NSEG" "NSEL" "NSTEP" "NTHE" "NTHETA" "NTRA" "NTREP" "NUMLP" "NUMNODE" "NUMPHMD" "NVAC" "OUTU" "PARGROUP" "PRNLEV" "QTSM" "SADI" "SADO" "SELATOM" "SELIRES" "SELISEG" "SKIP" "START" "STEP" "SYSSTAT" "TIMER" "TOTK" "VIOLNU" "WHOIAM" "WRNLEV" "XTLXDIM" "ndegf" "nfile" "nstep" "skip" "start" ;;MSR "AENE" "ALPHA" "AMP" "AREA" "AVER" "AVHBLF" "AVKE" "AVNOHB" "AVPH" "BETA" "CCELEC" "CFNORM" "CGTOT" "CHARGE" "CNVFRQ" "CPUTIME" "CTOT" "DCOEFF" "DELTA" "DFLC" "DIST" "DRSH" "DSCORE" "ELATIME" "EMCONS" "EMCORE" "EMCT" "EMCX" "EMCY" "EMCZ" "EMDX" "EMDY" "EMDZ" "EMELE" "EMENG" "EMENGM" "EMMM" "EMMN" "EMSCORE" "EMSOLV" "EMTX" "EMTY" "EMTZ" "EMXX" "EMXY" "EMXZ" "EMYX" "EMYY" "EMYZ" "EMZX" "EMZY" "EMZZ" "ENPB" "ENPR" "ENSMYT" "ENTROPY" "ESEL" "ETOT" "FCTO" "FLUC" "FREEVOL" "FTOT" "GSBC" "GSBE" "GSBP" "HTOT" "KBLZ" "KEFL" "LDSCORE" "MASS" "MASST" "MAXD" "MIND" "MINGRMS" "MYSEED" "NBFACT" "NHBOND" "NHYDAA" "NHYDAR" "NHYDRR" "OMSCORE" "P0" "P1" "P2" "P2R3" "P2RA" "P3" "P4" "P5" "P6" "P7" "PHASE" "PHI " "PI " "PQRES" "R3R" "R3S" "RAXI" "RDIP" "RGEO" "RGYR" "RHAL" "RMAX" "RMS " "RSHF" "RSHO" "RVMI" "SADE" "SAVE" "SFIT" "SHIFT" "SLDEL" "SLTOT" "SMAX" "SMIN" "SPEEDL" "SROT" "SSBPLRC" "SSBPLRCS" "SSBPLRCSD" "SSUM" "STOT" "STRA" "SVAR" "SVIB" "SWEI" "THET" "TIDEL" "TIME" "TIMFAC" "TITOT" "TOLE" "TPDEL" "TPTOT" "TRAC" "VENE" "VIOL" "VOLUME" "WAVE" "WHAMFE" "WMAX" "WMIN" "XAVE" "XAXI" "XCEN" "XCM " "XCM2" "XCM3" "XCM4" "XDIP" "XMAX" "XMIN" "XMOV" "XTLA" "XTLALPHA" "XTLB" "XTLBETA" "XTLC" "XTLGAMMA" "XVAL" "XVCM" "XVMI" "YAVE" "YAXI" "YCEN" "YCM " "YCM2" "YCM3" "YCM4" "YDIP" "YMAX" "YMIN" "YMOV" "YVAL" "YVCM" "YVMI" "ZAVE" "ZAXI" "ZCEN" "ZCM " "ZCM2" "ZCM3" "ZCM4" "ZDIP" "ZMAX" "ZMIN" "ZMOV" "ZTOT" "ZVAL" "ZVCM" "ZVMI" ))) "CHARMM MSI/MSR variables.") (defconst charmm-end-keywords (eval-when-compile (regexp-opt '(;; keywords ending a file "stop" "return" "end" ))) "Charmm keywords ending a file (bold-red).") ;; Set up font locking (defconst charmm-font-lock-keywords-1 (list ;; Titles as comments/doc ;;'("^*.*" . font-lock-doc-face) '("^[ \t]*[*].*" . font-lock-doc-face) ;; Fontify variables and globals `(,(concat "\\?\\(" charmm-MSI "\\)\\W") (1 font-lock-builtin-face nil nil)) ; ?OUTU & co '("\\(\\@\\|@{\\|@\\?\\|@\\?{\\)\\(\\(?:\\sw\\|\\s_\\)+\\)" (2 font-lock-variable-name-face)) ; @variable, @?var ;; Set, incr,if and Calc highlight '("\\(^\\<\\|^\\s-*\\)\\(set\\|calc\\|incr\\|if\\|then\\|else\\|endif\\)\\s-+&?\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*" (2 font-lock-keyword-face) (3 font-lock-variable-name-face nil t)) ;; Label highlight '("\\(^\\<\\|^\\s-*\\|\\s-*\\)\\(label\\|goto\\)\\s-+&?\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*" (2 font-lock-keyword-face) (3 font-lock-function-name-face nil t)) ;; Fontify commands - Oli's style - full word highlighting (cons (concat "\\(^\\|^\\s-*\\)\\(" charmm-commands "\\)\\w*") 'font-lock-keyword-face) ;; Fontify commands - Juan' Style - just 4 letters ;;(cons ;; (concat "\\(^\\|^\\s-*\\)\\(" charmm-commands "\\)") ;; 'font-lock-keyword-face) ;;Fontify keywords (cons (concat "\\<\\(" charmm-keywords "\\)\\w*") 'font-lock-constant-face) (cons (concat "\\<\\(" charmm-trouble-keywords "\\)\\w*") 'font-lock-type-face) ;; Fontify types (cons (concat "\\<\\(" charmm-types "\\)\\>") 'font-lock-type-face) ;; STOP and return (cons (concat "\\(^\\<\\|^\\s-*\\)\\(" charmm-end-keywords "\\)\\>") font-lock-warning-face) ;; Logical operators '("\\s-\\(\\.AND\\.\\|\\.OR\\.\\|\\.LE\\.\\|\\.EQ\\.\\|\\.LT\\.\\|\\.GT\\.\\|\\.not\\.\\|\\.GE\\.\\)\\s-" . font-lock-keyword-face) ;; Default everything else '("\\<[0-9]+" . default) ; number (also matches word) ) "Subdued level highlighting for CHARMM mode.") (defconst charmm-font-lock-syntactic-keywords (if (or (not (boundp 'xemacsp)) (equal xemacsp nil)) ;; Mark shell-style comments. font-lock handles this in a ;; separate pass from normal syntactic scanning (somehow), so we ;; get a chance to mark these in addition to C and C++ style ;; comments. This only works in GNU Emacs, not XEmacs 21 which ;; seems to ignore this same code if we try to use it. (list ;; Mark _all_ ! chars as being comment-start. That will be ;; ignored when inside a quoted string. '("\\(\!\\)" (1 (11 . nil))) ;; Mark all newlines ending a line with ! as being comment-end. ;; This causes a problem, premature end-of-comment, when '!' ;; appears inside a multiline C-style comment. Oh well. '("\!.*\\([\n]\\)" (1 (12 . nil))) ;; Mark titles as comments ;;'("^\\(*\\)" ;; (1 (13 . nil))) ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Indentation ; ; (defun charmm-indent-line () "Indent current line for CHARMM input files." ;; copied in large part from http://two-wugs.net/emacs/mode-tutorial.html ;; 1. if we are at the beginning of the buffer, indent to column 0 ;; 2. if we are at an END line, dedent relative to previous line ;; 3. if we first see an end line before our current line, indent our line to the same indentation as the end line ;; 4. if we first see a start line ("if", "vibran", and "correl" at this point), increase indentation relevant to that line ;; 4.5. some very simple parsing of labels and gotos. see comments below. ;; 5. if we're on a continuation line, indent two more spaces, but don't change cur-indent ;; 6. else, don't indent. (interactive) (save-excursion (beginning-of-line) (if (bobp) ;; First line is always non-indented (bobp tells us we're on the first line) (indent-line-to 0) (let ((not-indented t) (cur-indent 0)) ;; so i'm not about to do real parsing of things involving gotos ;; however, if you give all of your looping labels names starting with ;; the word "loop" this will work. ;; if you want blocks to be indented inside of labels, call the initial label startXXX and the final one endXXX (setq start-pattern "^[ \t]*\\(if.*then\\|vibran\\|correl\\|label[ \t]+loop\\|label[ \t]+start\\)") (setq end-pattern "^[ \t]*\\(end\\|quit\\|.*[ \t]+goto[ \t]+loop\\|label[ \t]+end\\)") ;; If the line we are looking at is the end of a block, then decrease the indentation (if (looking-at end-pattern) (progn (save-excursion (forward-line -1) (setq cur-indent (- (current-indentation) default-tab-width))) (if (< cur-indent 0) ;; We can't indent past the left margin (setq cur-indent 0))) (save-excursion (while not-indented ;; Iterate backwards until we find an indentation hint (forward-line -1) (if (looking-at end-pattern) ;; This hint indicates that we need to indent at the level of the END token (progn (setq cur-indent (current-indentation)) (setq not-indented nil)) (if (or (looking-at start-pattern) ) ;; This hint indicates that we need to indent an extra level (progn (setq cur-indent (+ (current-indentation) default-tab-width)) ;; Do the actual indenting (setq not-indented nil)) (if (bobp) (setq not-indented nil))))))) ;;(message "It will return %s and cur-indent is %s" (charmm-continuation-line) cur-indent) (if (charmm-continuation-line) (setq cur-indent (+ 2 cur-indent))) (if cur-indent ;;(message "The current indentation level is %d" cur-indent) (indent-line-to cur-indent) ;; If we didn't see an indentation hint, then allow no indentation (indent-line-to 0)))))) ;;;###autoload (defun charmm-mode () "Major mode for editing Charmm code. \n\n\\{charmm-mode-map}" (interactive) ;; set up local variables (kill-all-local-variables) (make-local-variable 'font-lock-defaults) (make-local-variable 'paragraph-separate) (make-local-variable 'paragraph-start) (make-local-variable 'require-final-newline) (make-local-variable 'comment-start) (make-local-variable 'comment-end) (make-local-variable 'comment-start-skip) (make-local-variable 'comment-column) (make-local-variable 'comment-indent-function) (make-local-variable 'indent-region-function) (make-local-variable 'indent-line-function) (make-local-variable 'add-log-current-defun-function) (make-local-variable 'fill-paragraph-function) (setq indent-line-function 'charmm-indent-line) (set-syntax-table charmm-mode-syntax-table) (setq major-mode 'charmm-mode mode-name "Charmm" paragraph-separate "^[ \t]*$" paragraph-start "^[ \t]*$" require-final-newline t comment-start "! " comment-end "" comment-start-skip "//* " comment-column 40 ;; comment-indent-function 'charmm-comment-indent-function ;; indent-region-function 'charmm-indent-region ;; indent-line-function 'charmm-indent-line font-lock-defaults '((charmm-font-lock-keywords-1) nil ; KEYWORDS-ONLY T ; CASE-FOLD nil ; SYNTAX-ALIST nil ; SYNTAX-BEGIN (font-lock-syntactic-keywords . charmm-font-lock-syntactic-keywords)) ) (setq font-lock-maximum-decoration t case-fold-search nil ; CHARMM vars are case-insensitive ) (run-hooks 'charmm-mode-user-hook)) ;; Make charmm-mode default mode for Charmm scripts ;;;###autoload (let ((charmm-file-patterns-temp charmm-file-patterns)) (while charmm-file-patterns-temp (add-to-list 'auto-mode-alist (cons (car charmm-file-patterns-temp) 'charmm-mode)) (setq charmm-file-patterns-temp (cdr charmm-file-patterns-temp)))) (provide 'charmm-mode) ;;; charmm-mode.el ends here