1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51: (require 'ruby-mode)
52:
53: (defgroup ruby-electric nil
54: "Minor mode providing electric editing commands for ruby files"
55: :group 'ruby)
56:
57: (defconst ruby-electric-expandable-do-re
58: "do\\s-$")
59:
60: (defconst ruby-electric-expandable-bar
61: "\\s-\\(do\\|{\\)\\s-+|")
62:
63: (defvar ruby-electric-matching-delimeter-alist
64: '((?\[ . ?\])
65: (?\( . ?\))
66: (?\' . ?\')
67: (?\` . ?\`)
68: (?\" . ?\")))
69:
70: (defcustom ruby-electric-simple-keywords-re
71: "\\(def\\|if\\|class\\|module\\|unless\\|case\\|while\\|do\\|until\\|for\\|begin\\)"
72: "*Regular expresion matching keywords for which closing 'end'
73: is to be inserted."
74: :type 'regexp :group 'ruby-electric)
75:
76: (defcustom ruby-electric-expand-delimiters-list '(all)
77: "*List of contexts where matching delimiter should be
78: inserted. The word 'all' will do all insertions."
79: :type '(set :extra-offset 8
80: (const :tag "Everything" all )
81: (const :tag "Curly brace" ?\{ )
82: (const :tag "Square brace" ?\[ )
83: (const :tag "Round brace" ?\( )
84: (const :tag "Quote" ?\' )
85: (const :tag "Double quote" ?\" )
86: (const :tag "Back quote" ?\` )
87: (const :tag "Vertical bar" ?\| ))
88: :group 'ruby-electric)
89:
90: (defcustom ruby-electric-newline-before-closing-bracket nil
91: "*Controls whether a newline should be inserted before the
92: closing bracket or not."
93: :type 'boolean :group 'ruby-electric)
94:
95: (define-minor-mode ruby-electric-mode
96: "Toggle Ruby Electric minor mode.
97: With no argument, this command toggles the mode. Non-null prefix
98: argument turns on the mode. Null prefix argument turns off the
99: mode.
100:
101: When Ruby Electric mode is enabled, an indented 'end' is
102: heuristicaly inserted whenever typing a word like 'module',
103: 'class', 'def', 'if', 'unless', 'case', 'until', 'for', 'begin',
104: 'do'. Simple, double and back quotes as well as braces are paired
105: auto-magically. Expansion does not occur inside comments and
106: strings. Note that you must have Font Lock enabled."
107:
108: nil
109:
110: " REl"
111:
112: ruby-mode-map
113: (ruby-electric-setup-keymap))
114:
115: (defun ruby-electric-setup-keymap()
116: (define-key ruby-mode-map " " 'ruby-electric-space)
117: (define-key ruby-mode-map "{" 'ruby-electric-curlies)
118: (define-key ruby-mode-map "(" 'ruby-electric-matching-char)
119: (define-key ruby-mode-map "[" 'ruby-electric-matching-char)
120: (define-key ruby-mode-map "\"" 'ruby-electric-matching-char)
121: (define-key ruby-mode-map "\'" 'ruby-electric-matching-char)
122: (define-key ruby-mode-map "|" 'ruby-electric-bar))
123:
124: (defun ruby-electric-space (arg)
125: (interactive "P")
126: (self-insert-command (prefix-numeric-value arg))
127: (if (ruby-electric-space-can-be-expanded-p)
128: (save-excursion
129: (ruby-indent-line t)
130: (newline)
131: (ruby-insert-end))))
132:
133: (defun ruby-electric-code-at-point-p()
134: (and ruby-electric-mode
135: (let* ((properties (text-properties-at (point))))
136: (and (null (memq 'font-lock-string-face properties))
137: (null (memq 'font-lock-comment-face properties))))))
138:
139: (defun ruby-electric-string-at-point-p()
140: (and ruby-electric-mode
141: (consp (memq 'font-lock-string-face (text-properties-at (point))))))
142:
143: (defun ruby-electric-is-last-command-char-expandable-punct-p()
144: (or (memq 'all ruby-electric-expand-delimiters-list)
145: (memq last-command-char ruby-electric-expand-delimiters-list)))
146:
147: (defun ruby-electric-space-can-be-expanded-p()
148: (if (ruby-electric-code-at-point-p)
149: (let* ((ruby-electric-keywords-re
150: (concat ruby-electric-simple-keywords-re "\\s-$"))
151: (ruby-electric-single-keyword-in-line-re
152: (concat "\\s-*" ruby-electric-keywords-re)))
153: (save-excursion
154: (backward-word 1)
155: (or (looking-at ruby-electric-expandable-do-re)
156: (and (looking-at ruby-electric-keywords-re)
157: (not (string= "do" (match-string 1)))
158: (progn
159: (beginning-of-line)
160: (looking-at ruby-electric-single-keyword-in-line-re))))))))
161:
162:
163: (defun ruby-electric-curlies(arg)
164: (interactive "P")
165: (self-insert-command (prefix-numeric-value arg))
166: (if (ruby-electric-is-last-command-char-expandable-punct-p)
167: (cond ((ruby-electric-code-at-point-p)
168: (insert " ")
169: (save-excursion
170: (if ruby-electric-newline-before-closing-bracket
171: (newline))
172: (insert "}")))
173: ((ruby-electric-string-at-point-p)
174: (save-excursion
175: (backward-char 1)
176: (when (char-equal ?\# (preceding-char))
177: (forward-char 1)
178: (insert "}")))))))
179:
180: (defun ruby-electric-matching-char(arg)
181: (interactive "P")
182: (self-insert-command (prefix-numeric-value arg))
183: (and (ruby-electric-is-last-command-char-expandable-punct-p)
184: (ruby-electric-code-at-point-p)
185: (save-excursion
186: (insert (cdr (assoc last-command-char
187: ruby-electric-matching-delimeter-alist))))))
188:
189: (defun ruby-electric-bar(arg)
190: (interactive "P")
191: (self-insert-command (prefix-numeric-value arg))
192: (and (ruby-electric-is-last-command-char-expandable-punct-p)
193: (ruby-electric-code-at-point-p)
194: (and (save-excursion (re-search-backward ruby-electric-expandable-bar nil t))
195: (= (point) (match-end 0)))
196: (save-excursion
197: (insert "|"))))
198:
199:
200: (provide 'ruby-electric)