summaryrefslogtreecommitdiff
path: root/misc/ruby-electric.el
diff options
context:
space:
mode:
Diffstat (limited to 'misc/ruby-electric.el')
-rw-r--r--misc/ruby-electric.el693
1 files changed, 538 insertions, 155 deletions
diff --git a/misc/ruby-electric.el b/misc/ruby-electric.el
index c361089938..61e84d2adb 100644
--- a/misc/ruby-electric.el
+++ b/misc/ruby-electric.el
@@ -1,97 +1,215 @@
-;; -*-Emacs-Lisp-*-
+;;; ruby-electric.el --- Minor mode for electrically editing ruby code
;;
-;; ruby-electric.el --- electric editing commands for ruby files
-;;
-;; Copyright (C) 2005 by Dee Zsombor <dee dot zsombor at gmail dot com>.
-;; Released under same license terms as Ruby.
-;;
-;; Due credit: this work was inspired by a code snippet posted by
-;; Frederick Ros at http://rubygarden.org/ruby?EmacsExtensions.
-;;
-;; Following improvements where added:
-;;
-;; - handling of strings of type 'here document'
-;; - more keywords, with special handling for 'do'
-;; - packaged into a minor mode
-;;
-;; Usage:
-;;
-;; 0) copy ruby-electric.el into directory where emacs can find it.
-;;
-;; 1) modify your startup file (.emacs or whatever) by adding
-;; following line:
-;;
-;; (require 'ruby-electric)
-;;
-;; note that you need to have font lock enabled beforehand.
-;;
-;; 2) toggle Ruby Electric Mode on/off with ruby-electric-mode.
-;;
-;; Changelog:
-;;
-;; 2005/Jan/14: inserts matching pair delimiters like {, [, (, ', ",
-;; ' and | .
+;; Authors: Dee Zsombor <dee dot zsombor at gmail dot com>
+;; Yukihiro Matsumoto
+;; Nobuyoshi Nakada
+;; Akinori MUSHA <knu@iDaemons.org>
+;; Jakub Kuźma <qoobaa@gmail.com>
+;; Maintainer: Akinori MUSHA <knu@iDaemons.org>
+;; Created: 6 Mar 2005
+;; URL: https://github.com/knu/ruby-electric.el
+;; Keywords: languages ruby
+;; License: The same license terms as Ruby
+;; Version: 2.3.1
+
+;;; Commentary:
;;
-;; 2005/Jan/14: added basic Custom support for configuring keywords
-;; with electric closing.
+;; `ruby-electric-mode' accelerates code writing in ruby by making
+;; some keys "electric" and automatically supplying with closing
+;; parentheses and "end" as appropriate.
;;
-;; 2005/Jan/18: more Custom support for configuring characters for
-;; which matching expansion should occur.
+;; This work was originally inspired by a code snippet posted by
+;; [Frederick Ros](https://github.com/sleeper).
;;
-;; 2005/Jan/18: no longer uses 'looking-back' or regexp character
-;; classes like [:space:] since they are not implemented on XEmacs.
+;; Add the following line to enable ruby-electric-mode under
+;; ruby-mode.
;;
-;; 2005/Feb/01: explicitly provide default argument of 1 to
-;; 'backward-word' as it requires it on Emacs 21.3
+;; (eval-after-load "ruby-mode"
+;; '(add-hook 'ruby-mode-hook 'ruby-electric-mode))
;;
-;; 2005/Mar/06: now stored inside ruby CVS; customize pages now have
-;; ruby as parent; cosmetic fixes.
+;; Type M-x customize-group ruby-electric for configuration.
+;;; Code:
(require 'ruby-mode)
+(eval-when-compile
+ (require 'cl))
+
(defgroup ruby-electric nil
"Minor mode providing electric editing commands for ruby files"
- :group 'ruby)
+ :group 'ruby)
-(defconst ruby-electric-expandable-do-re
- "do\\s-$")
+(defconst ruby-electric-expandable-bar-re
+ "\\s-\\(do\\|{\\)\\s-*|")
-(defconst ruby-electric-expandable-bar
- "\\s-\\(do\\|{\\)\\s-+|")
+(defconst ruby-electric-delimiters-alist
+ '((?\{ :name "Curly brace" :handler ruby-electric-curlies :closing ?\})
+ (?\[ :name "Square brace" :handler ruby-electric-matching-char :closing ?\])
+ (?\( :name "Round brace" :handler ruby-electric-matching-char :closing ?\))
+ (?\' :name "Quote" :handler ruby-electric-matching-char)
+ (?\" :name "Double quote" :handler ruby-electric-matching-char)
+ (?\` :name "Back quote" :handler ruby-electric-matching-char)
+ (?\| :name "Vertical bar" :handler ruby-electric-bar)
+ (?\# :name "Hash" :handler ruby-electric-hash)))
(defvar ruby-electric-matching-delimeter-alist
- '((?\[ . ?\])
- (?\( . ?\))
- (?\' . ?\')
- (?\` . ?\`)
- (?\" . ?\")))
-
-(defcustom ruby-electric-simple-keywords-re
- "\\(def\\|if\\|class\\|module\\|unless\\|case\\|while\\|do\\|until\\|for\\|begin\\)"
- "*Regular expresion matching keywords for which closing 'end'
-is to be inserted."
- :type 'regexp :group 'ruby-electric)
+ (apply 'nconc
+ (mapcar #'(lambda (x)
+ (let ((delim (car x))
+ (plist (cdr x)))
+ (if (eq (plist-get plist :handler) 'ruby-electric-matching-char)
+ (list (cons delim (or (plist-get plist :closing)
+ delim))))))
+ ruby-electric-delimiters-alist)))
+
+(defvar ruby-electric-expandable-keyword-re)
+
+(defmacro ruby-electric--try-insert-and-do (string &rest body)
+ (declare (indent 1))
+ `(let ((before (point))
+ (after (progn
+ (insert ,string)
+ (point))))
+ (unwind-protect
+ (progn ,@body)
+ (delete-region before after)
+ (goto-char before))))
+
+(defconst ruby-modifier-beg-symbol-re
+ (regexp-opt ruby-modifier-beg-keywords 'symbols))
+
+(defun ruby-electric--modifier-keyword-at-point-p ()
+ "Test if there is a modifier keyword at point."
+ (and (looking-at ruby-modifier-beg-symbol-re)
+ (let ((end (match-end 1)))
+ (not (looking-back "\\."))
+ (save-excursion
+ (let ((indent1 (ruby-electric--try-insert-and-do "\n"
+ (ruby-calculate-indent)))
+ (indent2 (save-excursion
+ (goto-char end)
+ (ruby-electric--try-insert-and-do " x\n"
+ (ruby-calculate-indent)))))
+ (= indent1 indent2))))))
+
+(defconst ruby-block-mid-symbol-re
+ (regexp-opt ruby-block-mid-keywords 'symbols))
+
+(defun ruby-electric--block-mid-keyword-at-point-p ()
+ "Test if there is a block mid keyword at point."
+ (and (looking-at ruby-block-mid-symbol-re)
+ (looking-back "^\\s-*")))
+
+(defconst ruby-block-beg-symbol-re
+ (regexp-opt ruby-block-beg-keywords 'symbols))
+
+(defun ruby-electric--block-beg-keyword-at-point-p ()
+ "Test if there is a block beginning keyword at point."
+ (and (looking-at ruby-block-beg-symbol-re)
+ (if (string= (match-string 1) "do")
+ (looking-back "\\s-")
+ (not (looking-back "\\.")))
+ ;; (not (ruby-electric--modifier-keyword-at-point-p)) ;; implicit assumption
+ ))
+
+(defcustom ruby-electric-keywords-alist
+ '(("begin" . end)
+ ("case" . end)
+ ("class" . end)
+ ("def" . end)
+ ("do" . end)
+ ("else" . reindent)
+ ("elsif" . reindent)
+ ("end" . reindent)
+ ("ensure" . reindent)
+ ("for" . end)
+ ("if" . end)
+ ("module" . end)
+ ("rescue" . reindent)
+ ("unless" . end)
+ ("until" . end)
+ ("when" . reindent)
+ ("while" . end))
+ "Alist of keywords and actions to define how to react to space
+or return right after each keyword. In each (KEYWORD . ACTION)
+cons, ACTION can be set to one of the following values:
+
+ `reindent' Reindent the line.
+
+ `end' Reindent the line and auto-close the keyword with
+ end if applicable.
+
+ `nil' Do nothing.
+"
+ :type '(repeat (cons (string :tag "Keyword")
+ (choice :tag "Action"
+ :menu-tag "Action"
+ (const :tag "Auto-close with end"
+ :value end)
+ (const :tag "Auto-reindent"
+ :value reindent)
+ (const :tag "None"
+ :value nil))))
+ :set (lambda (sym val)
+ (set sym val)
+ (let (keywords)
+ (dolist (x val)
+ (let ((keyword (car x))
+ (action (cdr x)))
+ (if action
+ (setq keywords (cons keyword keywords)))))
+ (setq ruby-electric-expandable-keyword-re
+ (concat (regexp-opt keywords 'symbols)
+ "$"))))
+ :group 'ruby-electric)
+
+(defvar ruby-electric-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map " " 'ruby-electric-space/return)
+ (define-key map [remap delete-backward-char] 'ruby-electric-delete-backward-char)
+ (define-key map [remap newline] 'ruby-electric-space/return)
+ (define-key map [remap newline-and-indent] 'ruby-electric-space/return)
+ (define-key map [remap electric-newline-and-maybe-indent] 'ruby-electric-space/return)
+ (define-key map [remap reindent-then-newline-and-indent] 'ruby-electric-space/return)
+ (dolist (x ruby-electric-delimiters-alist)
+ (let* ((delim (car x))
+ (plist (cdr x))
+ (name (plist-get plist :name))
+ (func (plist-get plist :handler))
+ (closing (plist-get plist :closing)))
+ (define-key map (char-to-string delim) func)
+ (if closing
+ (define-key map (char-to-string closing) 'ruby-electric-closing-char))))
+ map)
+ "Keymap used in ruby-electric-mode")
(defcustom ruby-electric-expand-delimiters-list '(all)
- "*List of contexts where matching delimiter should be
-inserted. The word 'all' will do all insertions."
- :type '(set :extra-offset 8
- (const :tag "Everything" all )
- (const :tag "Curly brace" ?\{ )
- (const :tag "Square brace" ?\[ )
- (const :tag "Round brace" ?\( )
- (const :tag "Quote" ?\' )
- (const :tag "Double quote" ?\" )
- (const :tag "Back quote" ?\` )
- (const :tag "Vertical bar" ?\| ))
- :group 'ruby-electric)
+ "*List of contexts where matching delimiter should be inserted.
+The word 'all' will do all insertions."
+ :type `(set :extra-offset 8
+ (const :tag "Everything" all)
+ ,@(apply 'list
+ (mapcar #'(lambda (x)
+ `(const :tag ,(plist-get (cdr x) :name)
+ ,(car x)))
+ ruby-electric-delimiters-alist)))
+ :group 'ruby-electric)
(defcustom ruby-electric-newline-before-closing-bracket nil
- "*Controls whether a newline should be inserted before the
-closing bracket or not."
+ "*Non-nil means a newline should be inserted before an
+automatically inserted closing bracket."
+ :type 'boolean :group 'ruby-electric)
+
+(defcustom ruby-electric-autoindent-on-closing-char nil
+ "*Non-nil means the current line should be automatically
+indented when a closing character is manually typed in."
:type 'boolean :group 'ruby-electric)
+(defvar ruby-electric-mode-hook nil
+ "Called after `ruby-electric-mode' is turned on.")
+
+;;;###autoload
(define-minor-mode ruby-electric-mode
"Toggle Ruby Electric minor mode.
With no argument, this command toggles the mode. Non-null prefix
@@ -101,100 +219,365 @@ mode.
When Ruby Electric mode is enabled, an indented 'end' is
heuristicaly inserted whenever typing a word like 'module',
'class', 'def', 'if', 'unless', 'case', 'until', 'for', 'begin',
-'do'. Simple, double and back quotes as well as braces are paired
-auto-magically. Expansion does not occur inside comments and
-strings. Note that you must have Font Lock enabled."
+'do' followed by a space. Single, double and back quotes as well
+as braces are paired auto-magically. Expansion does not occur
+inside comments and strings. Note that you must have Font Lock
+enabled."
;; initial value.
nil
;;indicator for the mode line.
" REl"
;;keymap
- ruby-mode-map
- (ruby-electric-setup-keymap))
-
-(defun ruby-electric-setup-keymap()
- (define-key ruby-mode-map " " 'ruby-electric-space)
- (define-key ruby-mode-map "{" 'ruby-electric-curlies)
- (define-key ruby-mode-map "(" 'ruby-electric-matching-char)
- (define-key ruby-mode-map "[" 'ruby-electric-matching-char)
- (define-key ruby-mode-map "\"" 'ruby-electric-matching-char)
- (define-key ruby-mode-map "\'" 'ruby-electric-matching-char)
- (define-key ruby-mode-map "|" 'ruby-electric-bar))
-
-(defun ruby-electric-space (arg)
- (interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (if (ruby-electric-space-can-be-expanded-p)
- (save-excursion
- (ruby-indent-line t)
- (newline)
- (ruby-insert-end))))
-
-(defun ruby-electric-code-at-point-p()
- (and ruby-electric-mode
- (let* ((properties (text-properties-at (point))))
- (and (null (memq 'font-lock-string-face properties))
- (null (memq 'font-lock-comment-face properties))))))
+ ruby-electric-mode-map
+ (if ruby-electric-mode
+ (run-hooks 'ruby-electric-mode-hook)))
+
+(defun ruby-electric-space/return-fallback ()
+ (if (or (eq this-original-command 'ruby-electric-space/return)
+ (null (ignore-errors
+ ;; ac-complete may fail if there is nothing left to complete
+ (call-interactively this-original-command)
+ (setq this-command this-original-command))))
+ ;; fall back to a globally bound command
+ (let ((command (global-key-binding (char-to-string last-command-event) t)))
+ (and command
+ (call-interactively (setq this-command command))))))
+
+(defun ruby-electric-space/return (arg)
+ (interactive "*P")
+ (and (boundp 'sp-last-operation)
+ (setq sp-delayed-pair nil))
+ (cond ((or arg
+ (region-active-p))
+ (or (= last-command-event ?\s)
+ (setq last-command-event ?\n))
+ (ruby-electric-replace-region-or-insert))
+ ((ruby-electric-space/return-can-be-expanded-p)
+ (let (action)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (let* ((keyword (match-string 1))
+ (allowed-actions
+ (cond ((ruby-electric--modifier-keyword-at-point-p)
+ '(reindent)) ;; no end necessary
+ ((ruby-electric--block-mid-keyword-at-point-p)
+ '(reindent)) ;; ditto
+ ((ruby-electric--block-beg-keyword-at-point-p)
+ '(end reindent)))))
+ (if allowed-actions
+ (setq action
+ (let ((action (cdr (assoc keyword ruby-electric-keywords-alist))))
+ (and (memq action allowed-actions)
+ action))))))
+ (cond ((eq action 'end)
+ (ruby-indent-line)
+ (save-excursion
+ (newline)
+ (ruby-electric-end)))
+ ((eq action 'reindent)
+ (ruby-indent-line)))
+ (ruby-electric-space/return-fallback)))
+ ((and (eq this-original-command 'newline-and-indent)
+ (ruby-electric-comment-at-point-p))
+ (call-interactively (setq this-command 'comment-indent-new-line)))
+ (t
+ (ruby-electric-space/return-fallback))))
-(defun ruby-electric-string-at-point-p()
+(defun ruby-electric--get-faces-at-point ()
+ (let* ((point (point))
+ (value (or
+ (get-text-property point 'read-face-name)
+ (get-text-property point 'face))))
+ (if (listp value) value (list value))))
+
+(defun ruby-electric--faces-include-p (pfaces &rest faces)
(and ruby-electric-mode
- (consp (memq 'font-lock-string-face (text-properties-at (point))))))
+ (loop for face in faces
+ thereis (memq face pfaces))))
+
+(defun ruby-electric--faces-at-point-include-p (&rest faces)
+ (apply 'ruby-electric--faces-include-p
+ (ruby-electric--get-faces-at-point)
+ faces))
+
+(defun ruby-electric-code-face-p (faces)
+ (not (ruby-electric--faces-include-p
+ faces
+ 'font-lock-string-face
+ 'font-lock-comment-face
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-heredoc-delimiter-face
+ 'enh-ruby-regexp-delimiter-face
+ 'enh-ruby-regexp-face)))
+
+(defun ruby-electric-code-at-point-p ()
+ (ruby-electric-code-face-p
+ (ruby-electric--get-faces-at-point)))
+
+(defun ruby-electric-string-face-p (faces)
+ (ruby-electric--faces-include-p
+ faces
+ 'font-lock-string-face
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-heredoc-delimiter-face
+ 'enh-ruby-regexp-delimiter-face
+ 'enh-ruby-regexp-face))
+
+(defun ruby-electric-string-at-point-p ()
+ (ruby-electric-string-face-p
+ (ruby-electric--get-faces-at-point)))
-(defun ruby-electric-is-last-command-char-expandable-punct-p()
+(defun ruby-electric-comment-at-point-p ()
+ (ruby-electric--faces-at-point-include-p
+ 'font-lock-comment-face))
+
+(defun ruby-electric-escaped-p()
+ (let ((f nil))
+ (save-excursion
+ (while (char-equal ?\\ (preceding-char))
+ (backward-char 1)
+ (setq f (not f))))
+ f))
+
+(defun ruby-electric-command-char-expandable-punct-p(char)
(or (memq 'all ruby-electric-expand-delimiters-list)
- (memq last-command-char ruby-electric-expand-delimiters-list)))
-
-(defun ruby-electric-space-can-be-expanded-p()
- (if (ruby-electric-code-at-point-p)
- (let* ((ruby-electric-keywords-re
- (concat ruby-electric-simple-keywords-re "\\s-$"))
- (ruby-electric-single-keyword-in-line-re
- (concat "\\s-*" ruby-electric-keywords-re)))
- (save-excursion
- (backward-word 1)
- (or (looking-at ruby-electric-expandable-do-re)
- (and (looking-at ruby-electric-keywords-re)
- (not (string= "do" (match-string 1)))
- (progn
- (beginning-of-line)
- (looking-at ruby-electric-single-keyword-in-line-re))))))))
-
-
-(defun ruby-electric-curlies(arg)
- (interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (if (ruby-electric-is-last-command-char-expandable-punct-p)
- (cond ((ruby-electric-code-at-point-p)
- (insert " ")
- (save-excursion
- (if ruby-electric-newline-before-closing-bracket
- (newline))
- (insert "}")))
- ((ruby-electric-string-at-point-p)
- (save-excursion
- (backward-char 1)
- (when (char-equal ?\# (preceding-char))
- (forward-char 1)
- (insert "}")))))))
-
-(defun ruby-electric-matching-char(arg)
- (interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (and (ruby-electric-is-last-command-char-expandable-punct-p)
- (ruby-electric-code-at-point-p)
- (save-excursion
- (insert (cdr (assoc last-command-char
- ruby-electric-matching-delimeter-alist))))))
+ (memq char ruby-electric-expand-delimiters-list)))
+
+(defun ruby-electric-space/return-can-be-expanded-p()
+ (and (ruby-electric-code-at-point-p)
+ (looking-back ruby-electric-expandable-keyword-re)))
+
+(defun ruby-electric-replace-region-or-insert ()
+ (and (region-active-p)
+ (bound-and-true-p delete-selection-mode)
+ (fboundp 'delete-selection-helper)
+ (delete-selection-helper (get 'self-insert-command 'delete-selection)))
+ (insert (make-string (prefix-numeric-value current-prefix-arg)
+ last-command-event))
+ (setq this-command 'self-insert-command))
+
+(defmacro ruby-electric-insert (arg &rest body)
+ `(cond ((and
+ (null ,arg)
+ (ruby-electric-command-char-expandable-punct-p last-command-event))
+ (let ((region-beginning
+ (cond ((region-active-p)
+ (prog1
+ (save-excursion
+ (goto-char (region-beginning))
+ (insert last-command-event)
+ (point))
+ (goto-char (region-end))))
+ (t
+ (insert last-command-event)
+ nil)))
+ (faces-at-point
+ (ruby-electric--get-faces-at-point)))
+ ,@body
+ (and region-beginning
+ ;; If no extra character is inserted, go back to the
+ ;; region beginning.
+ (eq this-command 'self-insert-command)
+ (goto-char region-beginning))))
+ ((ruby-electric-replace-region-or-insert))))
+
+(defun ruby-electric-curlies (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (cond
+ ((or (ruby-electric-code-at-point-p)
+ (ruby-electric--faces-include-p
+ faces-at-point
+ 'enh-ruby-string-delimiter-face
+ 'enh-ruby-regexp-delimiter-face))
+ (save-excursion
+ (insert "}")
+ (font-lock-fontify-region (line-beginning-position) (point)))
+ (cond
+ ((or (ruby-electric-string-at-point-p) ;; %w{}, %r{}, etc.
+ (looking-back "%[QqWwRrxIis]{"))
+ (if region-beginning
+ (forward-char 1)))
+ (ruby-electric-newline-before-closing-bracket
+ (cond (region-beginning
+ (save-excursion
+ (goto-char region-beginning)
+ (newline))
+ (newline)
+ (forward-char 1)
+ (indent-region region-beginning (line-end-position)))
+ (t
+ (insert " ")
+ (save-excursion
+ (newline)
+ (ruby-indent-line t)))))
+ (t
+ (if region-beginning
+ (save-excursion
+ (goto-char region-beginning)
+ (insert " "))
+ (insert " "))
+ (insert " ")
+ (backward-char 1)
+ (and region-beginning
+ (forward-char 1)))))
+ ((ruby-electric-string-at-point-p)
+ (let ((start-position (1- (or region-beginning (point)))))
+ (cond
+ ((char-equal ?\# (char-before start-position))
+ (unless (save-excursion
+ (goto-char (1- start-position))
+ (ruby-electric-escaped-p))
+ (insert "}")
+ (or region-beginning
+ (backward-char 1))))
+ ((or
+ (ruby-electric-command-char-expandable-punct-p ?\#)
+ (save-excursion
+ (goto-char start-position)
+ (ruby-electric-escaped-p)))
+ (if region-beginning
+ (goto-char region-beginning))
+ (setq this-command 'self-insert-command))
+ (t
+ (save-excursion
+ (goto-char start-position)
+ (insert "#"))
+ (insert "}")
+ (or region-beginning
+ (backward-char 1))))))
+ (t
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert)))))
+
+(defun ruby-electric-hash (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (if (ruby-electric-string-at-point-p)
+ (let ((start-position (1- (or region-beginning (point)))))
+ (cond
+ ((char-equal (following-char) ?')) ;; likely to be in ''
+ ((save-excursion
+ (goto-char start-position)
+ (ruby-electric-escaped-p)))
+ (region-beginning
+ (save-excursion
+ (goto-char (1+ start-position))
+ (insert "{"))
+ (insert "}"))
+ (t
+ (insert "{")
+ (save-excursion
+ (insert "}")))))
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert))))
+
+(defun ruby-electric-matching-char (arg)
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (let ((closing (cdr (assoc last-command-event
+ ruby-electric-matching-delimeter-alist))))
+ (cond
+ ;; quotes
+ ((char-equal closing last-command-event)
+ (cond ((not (ruby-electric-string-face-p faces-at-point))
+ (if region-beginning
+ ;; escape quotes of the same kind, backslash and hash
+ (let ((re (format "[%c\\%s]"
+ last-command-event
+ (if (char-equal last-command-event ?\")
+ "#" "")))
+ (bound (point)))
+ (save-excursion
+ (goto-char region-beginning)
+ (while (re-search-forward re bound t)
+ (let ((end (point)))
+ (replace-match "\\\\\\&")
+ (setq bound (+ bound (- (point) end))))))))
+ (insert closing)
+ (or region-beginning
+ (backward-char 1)))
+ (t
+ (and (eq last-command 'ruby-electric-matching-char)
+ (char-equal (following-char) closing) ;; repeated quotes
+ (delete-char 1))
+ (setq this-command 'self-insert-command))))
+ ((ruby-electric-code-at-point-p)
+ (insert closing)
+ (or region-beginning
+ (backward-char 1)))))))
+
+(defun ruby-electric-closing-char(arg)
+ (interactive "*P")
+ (cond
+ (arg
+ (ruby-electric-replace-region-or-insert))
+ ((and
+ (eq last-command 'ruby-electric-curlies)
+ (= last-command-event ?})
+ (not (char-equal (preceding-char) last-command-event))) ;; {}
+ (if (char-equal (following-char) ?\n) (delete-char 1))
+ (delete-horizontal-space)
+ (forward-char))
+ ((and
+ (= last-command-event (following-char))
+ (not (char-equal (preceding-char) last-command-event))
+ (memq last-command '(ruby-electric-matching-char
+ ruby-electric-closing-char))) ;; ()/[] and (())/[[]]
+ (forward-char))
+ (t
+ (ruby-electric-replace-region-or-insert)
+ (if ruby-electric-autoindent-on-closing-char
+ (ruby-indent-line)))))
(defun ruby-electric-bar(arg)
- (interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (and (ruby-electric-is-last-command-char-expandable-punct-p)
- (ruby-electric-code-at-point-p)
- (and (save-excursion (re-search-backward ruby-electric-expandable-bar nil t))
- (= (point) (match-end 0))) ;looking-back is missing on XEmacs
- (save-excursion
- (insert "|"))))
+ (interactive "*P")
+ (ruby-electric-insert
+ arg
+ (cond ((and (ruby-electric-code-at-point-p)
+ (looking-back ruby-electric-expandable-bar-re))
+ (save-excursion (insert "|")))
+ (t
+ (delete-char -1)
+ (ruby-electric-replace-region-or-insert)))))
+(defun ruby-electric-delete-backward-char(arg)
+ (interactive "*p")
+ (cond ((memq last-command '(ruby-electric-matching-char
+ ruby-electric-bar))
+ (delete-char 1))
+ ((eq last-command 'ruby-electric-curlies)
+ (cond ((eolp)
+ (cond ((char-equal (preceding-char) ?\s)
+ (setq this-command last-command))
+ ((char-equal (preceding-char) ?{)
+ (and (looking-at "[ \t\n]*}")
+ (delete-char (- (match-end 0) (match-beginning 0)))))))
+ ((char-equal (following-char) ?\s)
+ (setq this-command last-command)
+ (delete-char 1))
+ ((char-equal (following-char) ?})
+ (delete-char 1))))
+ ((eq last-command 'ruby-electric-hash)
+ (and (char-equal (preceding-char) ?{)
+ (delete-char 1))))
+ (delete-char (- arg)))
+
+(put 'ruby-electric-delete-backward-char 'delete-selection 'supersede)
+
+(defun ruby-electric-end ()
+ (interactive)
+ (if (eq (char-syntax (preceding-char)) ?w)
+ (insert " "))
+ (insert "end")
+ (save-excursion
+ (if (eq (char-syntax (following-char)) ?w)
+ (insert " "))
+ (ruby-indent-line t)))
(provide 'ruby-electric)
+
+;;; ruby-electric.el ends here