374 lines
13 KiB
EmacsLisp
374 lines
13 KiB
EmacsLisp
;;; ess-smart-underscore.el --- Ess Smart Underscore
|
|
;;
|
|
;; Filename: ess-smart-underscore.el
|
|
;; Description: ess-smart-underscore
|
|
;; Author: Matthew L. Fidler
|
|
;; Maintainer: Matthew Fidler
|
|
;; Created: Thu Jul 14 11:04:42 2011 (-0500)
|
|
;; Version: 0.79
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500)
|
|
;; By: Matthew L. Fidler
|
|
;; Update #: 137
|
|
;; URL: http://github.com/mlf176f2/ess-smart-underscore.el
|
|
;; Package-Requires: ((ess "0"))
|
|
;; Keywords: ESS, underscore
|
|
;; Compatibility:
|
|
;;
|
|
;; Features that might be required by this library:
|
|
;;
|
|
;; `custom', `easymenu', `ess', `ess-compat', `ess-custom',
|
|
;; `font-lock', `syntax', `widget'.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Commentary:
|
|
;;
|
|
;; * Installation
|
|
;;
|
|
;; To use without using a package manager:
|
|
;;
|
|
;; - Put the library in a directory in the emacs load path, like ~/.emacs.d
|
|
;; - Add (require 'ess-smart-underscore) in your ~/.emacs file
|
|
;;
|
|
;; This is in emacswiki, so this package can also be installed using el-get.
|
|
;;
|
|
;; After installing el-get, Type M-x el-get-install ess-smart-underscore.
|
|
;; * Ess-Smart Underscore Package Information
|
|
;; Smart "_" key: insert `ess-S-assign', unless:
|
|
;;
|
|
;; 1. in string/comment
|
|
;; 2. after a $ (like d$one_two) (toggle with `ess-S-underscore-after-$')
|
|
;; 3. when the underscore is part of a variable definition previously defined.
|
|
;; (toggle with `ess-S-underscore-after-defined')
|
|
;; 4. when the underscore is after a "=" or "<-" on the same line.
|
|
;; 5. inside a parenthetical statement () or [].
|
|
;; (toggle with `ess-S-underscore-when-inside-paren')
|
|
;; 6. At the beginning of a line.
|
|
;; 7. In a variable that contains underscores already (for example foo_a)
|
|
;; (toggle with `ess-S-underscore-when-variable-contains-underscores')
|
|
;; 8. The preceding character is not a tab/space (toggle with
|
|
;; `ess-S-underscore-when-last-character-is-a-space'. Not enabled
|
|
;; by default.)
|
|
;;
|
|
;; An exception to
|
|
;;
|
|
;;
|
|
;; a <- b |
|
|
;;
|
|
;;
|
|
;; Pressing an underscore here would produce
|
|
;;
|
|
;;
|
|
;;
|
|
;; a <- b <-
|
|
;;
|
|
;;
|
|
;; However when in the following situation
|
|
;;
|
|
;;
|
|
;; a <- b|
|
|
;;
|
|
;;
|
|
;; Pressing an underscore would produce
|
|
;;
|
|
;;
|
|
;; a <- b_
|
|
;;
|
|
;;
|
|
;; This behavior can be toggled by `ess-S-space-underscore-is-assignment'
|
|
;;
|
|
;; If the underscore key is pressed a second time, the assignment
|
|
;; operator is removed and replaced by the underscore. `ess-S-assign',
|
|
;; typically " <- ", can be customized. In ESS modes other than R/S,
|
|
;; an underscore is always inserted.
|
|
;;
|
|
;; In addition the ess-smart-underscore attempts to work with noweb-mode
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Change Log:
|
|
;; 22-May-2013 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Added more ggplot operators.
|
|
;; 21-May-2013 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Added math to ggplot's functions...
|
|
;; 21-May-2013 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Added hook to R-mode to make it behave a little better.
|
|
;; 15-May-2013 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Fixed ess-smart-underscore to work in an inferior R process.
|
|
;; 15-May-2013 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Added ggplot function prefixes.
|
|
;; 05-Nov-2012 Matthew L. Fidler
|
|
;; Last-Updated: Mon Apr 9 15:27:09 2012 (-0500) #137 (Matthew L. Fidler)
|
|
;; Better handling of noweb. I think it Came from Denis Haine and
|
|
;; Martin Maechler.
|
|
;; 22-Feb-2012 Matthew L. Fidler
|
|
;; Last-Updated: Wed Feb 22 20:27:04 2012 (-0600) #120 (Matthew L. Fidler)
|
|
;; Support unbalanced sexps.
|
|
;; 02-Feb-2012 Matthew L. Fidler
|
|
;; Last-Updated: Thu Feb 2 21:06:52 2012 (-0600) #117 (Matthew L. Fidler)
|
|
;; Took out auto-installing. Most package managers don't want you
|
|
;; to do this.
|
|
;; 03-Aug-2011 Matthew L. Fidler
|
|
;; Last-Updated: Wed Aug 3 15:05:15 2011 (-0500) #112 (Matthew L. Fidler)
|
|
;; Bug fix for parenthetical statement
|
|
;; 20-Jul-2011 Matthew L. Fidler
|
|
;; Last-Updated: Wed Jul 20 15:20:10 2011 (-0500) #101 (Matthew L. Fidler)
|
|
|
|
;; Changed to allow underscore instead of assign when inside a
|
|
;; parenthetical statement.
|
|
|
|
;; 15-Jul-2011 Matthew L. Fidler
|
|
;; Last-Updated: Fri Jul 15 11:34:52 2011 (-0500) #90 (Matthew L. Fidler)
|
|
;; Bug fix for d[d$CMT == 2,"DV"] _ to produce d[d$CMT == 2,"DV"] <-
|
|
;;
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; This program is free software; you can redistribute it and/or
|
|
;; modify it under the terms of the GNU General Public License as
|
|
;; published by the Free Software Foundation; either version 3, 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; see the file COPYING. If not, write to
|
|
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
;; Floor, Boston, MA 02110-1301, USA.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Code:
|
|
(require 'ess)
|
|
|
|
(defcustom ess-S-underscore-after-$ t
|
|
"Should underscore produce an underscore if it is an element of a list/data structure?
|
|
|
|
Used by \\[ess-smart-underscore]."
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-after-defined t
|
|
"Should underscore produce an underscore if it is after a variable has been defined?
|
|
|
|
Used by \\[ess-smart-underscore]."
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-after-<-or-= t
|
|
"Should underscore produce an underscore if it is after a \"<-\" or \"=\"?
|
|
|
|
Used by \\[ess-smart-underscore]."
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-space-underscore-is-assignment t
|
|
"Should underscore produce `ess-S-assign' when a space is right before the cursor.
|
|
|
|
Used by \\[ess-smart-underscore]."
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-when-inside-paren t
|
|
"Should an underscore be produced instead of `ess-S-assign' when inside a parenthetical expression?"
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-when-inside-unbalanced-parenthesis t
|
|
"Should an underscore be produced instead of `ess-S-assign' when inside an unbalanced parenthetical expression such as:
|
|
|
|
foo(bar_
|
|
|
|
This requires `ess-S-underscore-when-inside-paren' to be true.
|
|
"
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-when-preceeding-words
|
|
'(
|
|
"add"
|
|
"aes"
|
|
"annotation"
|
|
"calc"
|
|
"continuous"
|
|
"coord"
|
|
"coord"
|
|
"cut"
|
|
"discrete"
|
|
"element"
|
|
"expand"
|
|
"facet"
|
|
"geom"
|
|
"gg"
|
|
"group"
|
|
"guide"
|
|
"label"
|
|
"last"
|
|
"math"
|
|
"mean"
|
|
"mutate"
|
|
"position"
|
|
"scale"
|
|
"scale_color"
|
|
"scale_colour"
|
|
"scale_x"
|
|
"scale_y"
|
|
"stat"
|
|
"theme"
|
|
"trans"
|
|
"translate"
|
|
"translate_qplot"
|
|
"update"
|
|
"update_"
|
|
"update_geom"
|
|
)
|
|
"Things that should have underscores after them. "
|
|
:group 'ess-S
|
|
:type '(repeat
|
|
(string :tag "Word")))
|
|
|
|
|
|
(defcustom ess-S-underscore-when-variable-contains-underscores t
|
|
"Should an underscore be produced instead of `ess-S-assign' when variable already contains an underscore?"
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
(defcustom ess-S-underscore-when-last-character-is-a-space nil
|
|
"ESS produces an underscore only when the last character is not a space or a tab."
|
|
:group 'ess-S
|
|
:type 'boolean)
|
|
|
|
;;;###autoload
|
|
(defun ess-smarter-underscore ()
|
|
"Smart \"_\" key: insert `ess-S-assign', unless:
|
|
1. in string/comment
|
|
2. after a $ (like d$one_two) (toggle with `ess-S-underscore-after-$')
|
|
3. when the underscore is part of a variable definition previously defined.
|
|
(toggle with `ess-S-underscore-after-defined')
|
|
4. when the underscore is after a \"=\" or \"<-\" on the same line.
|
|
(toggle with `ess-S-underscore-after-<-or-=')
|
|
5. inside a parenthetical statement () or [].
|
|
(toggle with `ess-S-underscore-when-inside-paren')
|
|
6. At the beginning of a line.
|
|
7. In a variable that contains underscores already (for example foo_a)
|
|
(toggle with `ess-S-underscore-when-variable-contains-underscores')
|
|
8. The preceding character is not a tab/space
|
|
(toggle with `ess-S-underscore-when-last-character-is-a-space'. Not enabled by default.)
|
|
9. The preceding words/characters are in `ess-S-underscore-when-preceeding-words'
|
|
|
|
|
|
An exception to #4 is in the following situation:
|
|
|
|
a <- b |
|
|
|
|
pressing an underscore here would produce
|
|
|
|
a <- b <-
|
|
|
|
However when in the following situation
|
|
|
|
a <- b|
|
|
|
|
pressing an underscore would produce
|
|
|
|
a <- b_
|
|
|
|
This behavior can be toggled by `ess-S-space-underscore-is-assignment'
|
|
|
|
If the underscore key is pressed a second time, the assignment
|
|
operator is removed and replaced by the underscore. `ess-S-assign',
|
|
typically \" <- \", can be customized. In ESS modes other than R/S,
|
|
an underscore is always inserted. "
|
|
(interactive)
|
|
;;(insert (if (ess-inside-string-or-comment-p (point)) "_"
|
|
;;ess-S-assign))
|
|
;;(message "%s" (looking-back "_[^ \t\n]*?\\="))
|
|
(save-restriction
|
|
(ignore-errors
|
|
(when (and (eq major-mode 'inferior-ess-mode)
|
|
(> (point) (process-mark (get-buffer-process
|
|
(current-buffer)))))
|
|
(narrow-to-region (process-mark (get-process ess-local-process-name)) (point-max)))
|
|
(and ess-noweb-mode
|
|
(noweb-in-code-chunk)
|
|
(noweb-narrow-to-chunk)))
|
|
(if (or
|
|
(not (equal ess-language "S"))
|
|
(looking-back "^[ \t\n]*\\=")
|
|
(looking-back (regexp-opt ess-S-underscore-when-preceeding-words t))
|
|
(and ess-S-underscore-when-variable-contains-underscores
|
|
(looking-back "_[^ \t\n]*?\\="))
|
|
(and ess-S-underscore-when-last-character-is-a-space
|
|
(looking-back "[^ \t]\\="))
|
|
(ess-inside-string-or-comment-p (point))
|
|
;; Data
|
|
(and ess-S-underscore-after-$ (save-match-data (save-excursion (re-search-backward "\\([$]\\)[A-Za-z0-9.]+\\=" nil t))))
|
|
(and ess-S-underscore-after-<-or-=
|
|
(let ((ret (save-match-data (and (not (looking-back ess-S-assign))
|
|
(looking-back "\\(<-\\|\\<=\\>\\).*")))))
|
|
(if (and ret ess-S-space-underscore-is-assignment
|
|
(looking-back "[ \t]"))
|
|
(setq ret nil))
|
|
(symbol-value 'ret)))
|
|
;; Look for variable
|
|
(and ess-S-underscore-after-defined
|
|
(not (looking-back ess-S-assign)) ; Hack to fix bug
|
|
(save-match-data
|
|
(save-excursion
|
|
(let (word)
|
|
(when (looking-back "\\<[A-Za-z0-9.]+[ \t]*")
|
|
(setq word (match-string 0))
|
|
(setq ret
|
|
(or (re-search-backward (format "^[ \t]*%s_[A-Za-z0-9.]*[ \t]*\\(<-\\|=\\)" word) nil t)
|
|
(re-search-backward (format "->[ \t]*%s_[A-Za-z0-9.]*[ \t]*$" word) nil t)))
|
|
(symbol-value 'ret))))))
|
|
(and ess-S-underscore-when-inside-paren
|
|
(save-match-data
|
|
(save-excursion
|
|
(let ((pt (point))
|
|
ret)
|
|
(when (re-search-backward "\\((\\|\\[\\).*\\=" nil t)
|
|
(condition-case err
|
|
(progn
|
|
(forward-sexp)
|
|
(when (> (point) pt)
|
|
(setq ret t)))
|
|
(error
|
|
(when ess-S-underscore-when-inside-unbalanced-parenthesis
|
|
(setq ret 't)))))
|
|
(symbol-value 'ret))))))
|
|
(insert "_")
|
|
;; Else one keypress produces ess-S-assign; a second keypress will delete
|
|
;; ess-S-assign and instead insert _
|
|
;; Rather than trying to count a second _ keypress, just check whether
|
|
;; the current point is preceded by ess-S-assign.
|
|
(let ((assign-len (length ess-S-assign)))
|
|
(if (and
|
|
(>= (point) (+ assign-len (point-min))) ;check that we can move back
|
|
(looking-back ess-S-assign))
|
|
;; If we are currently looking at ess-S-assign, replace it with _
|
|
(progn
|
|
(replace-match "")
|
|
(insert "_"))
|
|
(delete-horizontal-space)
|
|
(insert ess-S-assign))))))
|
|
(define-key ess-mode-map (kbd "_") 'ess-smarter-underscore)
|
|
|
|
(defun ess-smart-underscore-add-inf ()
|
|
"Add to inferior mode."
|
|
(local-set-key (kbd "_") 'ess-smarter-underscore))
|
|
|
|
(add-hook 'ess-R-post-run-hook 'ess-smart-underscore-add-inf)
|
|
(add-hook 'R-mode-hook 'ess-smart-underscore-add-inf)
|
|
(provide 'ess-smart-underscore)
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ess-smart-underscore.el ends here
|