diff options
Diffstat (limited to 'misc')
| -rw-r--r-- | misc/README | 7 | ||||
| -rw-r--r-- | misc/inf-ruby.el | 253 | ||||
| -rwxr-xr-x | misc/lldb_cruby.py | 177 | ||||
| -rw-r--r-- | misc/rb_optparse.bash | 20 | ||||
| -rwxr-xr-x | misc/rb_optparse.zsh | 38 | ||||
| -rw-r--r-- | misc/rdebug.el | 136 | ||||
| -rw-r--r-- | misc/rdoc-mode.el | 166 | ||||
| -rw-r--r-- | misc/ruby-additional.el | 181 | ||||
| -rw-r--r-- | misc/ruby-electric.el | 693 | ||||
| -rw-r--r-- | misc/ruby-mode.el | 1917 | ||||
| -rw-r--r-- | misc/ruby-style.el | 81 | ||||
| -rw-r--r-- | misc/rubydb3x.el | 42 | ||||
| -rw-r--r-- | misc/test_lldb_cruby.rb | 36 |
13 files changed, 2539 insertions, 1208 deletions
diff --git a/misc/README b/misc/README index c7e63d7799..08a9010f58 100644 --- a/misc/README +++ b/misc/README @@ -1,7 +1,12 @@ README this file inf-ruby.el program to run ruby under emacs +rb_optparse.bash bash completion script +rb_optparse.zsh zsh completion script +rdoc-mode.el RDoc mode for emacs ruby-mode.el ruby mode for emacs +ruby-style.el Ruby's C/C++ mode style for emacs rubydb2x.el ruby debugger support for emacs 19.2x or before rubydb3x.el ruby debugger support for emacs 19.3x or later ruby-electric.el emacs minor mode providing electric commands -rdebug.el ruby-debug (rdebug) support for emacs 19.3x or later + +Check out https://github.com/ruby-debug/ also. diff --git a/misc/inf-ruby.el b/misc/inf-ruby.el index 632c80ce5b..b3f4f10267 100644 --- a/misc/inf-ruby.el +++ b/misc/inf-ruby.el @@ -1,8 +1,7 @@ ;;; -*-Emacs-Lisp-*- ;;; -;;; $Id: inf-ruby.el,v 1.6.2.1 2004/07/27 07:51:28 matz Exp $ -;;; $Author: matz $ -;;; $Date: 2004/07/27 07:51:28 $ +;;; $Id$ +;;; $Author$ ;;; ;;; Inferior Ruby Mode - ruby process in a buffer. ;;; adapted from cmuscheme.el @@ -11,7 +10,7 @@ ;;; ;;; (0) check ruby-program-name variable that can run your environment. ;;; -;;; (1) modify .emacs to use ruby-mode +;;; (1) modify .emacs to use ruby-mode ;;; for example : ;;; ;;; (autoload 'ruby-mode "ruby-mode" @@ -19,8 +18,8 @@ ;;; (setq auto-mode-alist ;;; (append '(("\\.rb$" . ruby-mode)) auto-mode-alist)) ;;; (setq interpreter-mode-alist (append '(("ruby" . ruby-mode)) -;;; interpreter-mode-alist)) -;;; +;;; interpreter-mode-alist)) +;;; ;;; (2) set to load inf-ruby and set inf-ruby key definition in ruby-mode. ;;; ;;; (autoload 'run-ruby "inf-ruby" @@ -34,79 +33,79 @@ ;;; ;;; HISTORY ;;; senda - 8 Apr 1998: Created. -;;; $Log: inf-ruby.el,v $ -;;; Revision 1.7 2004/07/27 08:11:36 matz -;;; * eval.c (rb_eval): copy on write for argument local variable -;;; assignment. -;;; -;;; * eval.c (assign): ditto. -;;; -;;; * eval.c (rb_call0): update ruby_frame->argv with the default -;;; value used for the optional arguments. -;;; -;;; * object.c (Init_Object): "===" calls rb_obj_equal() directly. -;;; [ruby-list:39937] -;;; -;;; Revision 1.6 2002/09/07 14:35:46 nobu -;;; * misc/inf-ruby.el (inferior-ruby-error-regexp-alist): regexp -;;; alist for error message from ruby. -;;; -;;; * misc/inf-ruby.el (inferior-ruby-mode): fixed for Emacs. -;;; -;;; * misc/inf-ruby.el (ruby-send-region): compilation-parse-errors -;;; doesn't parse first line, so insert separators before each -;;; evaluations. -;;; -;;; Revision 1.5 2002/08/19 10:05:47 nobu -;;; * misc/inf-ruby.el (inf-ruby-keys): ruby-send-definition -;;; conflicted with ruby-insert-end. -;;; -;;; * misc/inf-ruby.el (inferior-ruby-mode): compilation-minor-mode. -;;; -;;; * misc/inf-ruby.el (ruby-send-region): send as here document to -;;; adjust source file/line. [ruby-talk:47113], [ruby-dev:17965] -;;; -;;; * misc/inf-ruby.el (ruby-send-terminator): added to make unique -;;; terminator. -;;; -;;; Revision 1.4 2002/01/29 07:16:09 matz -;;; * file.c (rb_stat_rdev_major): added. [new] -;;; -;;; * file.c (rb_stat_rdev_minor): added. [new] -;;; -;;; * file.c (rb_stat_inspect): print mode in octal. -;;; -;;; Revision 1.3 1999/12/01 09:24:18 matz -;;; 19991201 -;;; -;;; Revision 1.2 1999/08/13 05:45:18 matz -;;; 1.4.0 -;;; -;;; Revision 1.1.1.1.2.1 1999/07/15 07:59:59 matz -;;; 990715 -;;; -;;; Revision 1.1.1.1 1999/01/20 04:59:36 matz -;;; ruby 1.3 cycle -;;; -;;; Revision 1.1.2.1 1998/12/16 07:30:36 matz -;;; first public release of 1.1d (pre1.2) series -;;; -;;; Revision 1.4 1998/05/20 02:45:58 senda -;;; default program to irb -;;; -;;; Revision 1.3 1998/04/10 04:11:30 senda -;;; modification by Matsumoto san (1.1b9_09) -;;; remove-in-string defined -;;; global variable : -;;; inferior-ruby-first-prompt-pattern -;;; inferior-ruby-prompt-pattern -;;; defined -;;; -;;; Revision 1.2 1998/04/09 07:53:42 senda -;;; remove M-C-x in inferior-ruby-mode -;;; -;;; Revision 1.1 1998/04/09 07:28:36 senda -;;; Initial revision +;;; $Log$ +;;; Revision 1.7 2004/07/27 08:11:36 matz +;;; * eval.c (rb_eval): copy on write for argument local variable +;;; assignment. +;;; +;;; * eval.c (assign): ditto. +;;; +;;; * eval.c (rb_call0): update ruby_frame->argv with the default +;;; value used for the optional arguments. +;;; +;;; * object.c (Init_Object): "===" calls rb_obj_equal() directly. +;;; [ruby-list:39937] +;;; +;;; Revision 1.6 2002/09/07 14:35:46 nobu +;;; * misc/inf-ruby.el (inferior-ruby-error-regexp-alist): regexp +;;; alist for error message from ruby. +;;; +;;; * misc/inf-ruby.el (inferior-ruby-mode): fixed for Emacs. +;;; +;;; * misc/inf-ruby.el (ruby-send-region): compilation-parse-errors +;;; doesn't parse first line, so insert separators before each +;;; evaluations. +;;; +;;; Revision 1.5 2002/08/19 10:05:47 nobu +;;; * misc/inf-ruby.el (inf-ruby-keys): ruby-send-definition +;;; conflicted with ruby-insert-end. +;;; +;;; * misc/inf-ruby.el (inferior-ruby-mode): compilation-minor-mode. +;;; +;;; * misc/inf-ruby.el (ruby-send-region): send as here document to +;;; adjust source file/line. [ruby-talk:47113], [ruby-dev:17965] +;;; +;;; * misc/inf-ruby.el (ruby-send-terminator): added to make unique +;;; terminator. +;;; +;;; Revision 1.4 2002/01/29 07:16:09 matz +;;; * file.c (rb_stat_rdev_major): added. [new] +;;; +;;; * file.c (rb_stat_rdev_minor): added. [new] +;;; +;;; * file.c (rb_stat_inspect): print mode in octal. +;;; +;;; Revision 1.3 1999/12/01 09:24:18 matz +;;; 19991201 +;;; +;;; Revision 1.2 1999/08/13 05:45:18 matz +;;; 1.4.0 +;;; +;;; Revision 1.1.1.1.2.1 1999/07/15 07:59:59 matz +;;; 990715 +;;; +;;; Revision 1.1.1.1 1999/01/20 04:59:36 matz +;;; ruby 1.3 cycle +;;; +;;; Revision 1.1.2.1 1998/12/16 07:30:36 matz +;;; first public release of 1.1d (pre1.2) series +;;; +;;; Revision 1.4 1998/05/20 02:45:58 senda +;;; default program to irb +;;; +;;; Revision 1.3 1998/04/10 04:11:30 senda +;;; modification by Matsumoto san (1.1b9_09) +;;; remove-in-string defined +;;; global variable : +;;; inferior-ruby-first-prompt-pattern +;;; inferior-ruby-prompt-pattern +;;; defined +;;; +;;; Revision 1.2 1998/04/09 07:53:42 senda +;;; remove M-C-x in inferior-ruby-mode +;;; +;;; Revision 1.1 1998/04/09 07:28:36 senda +;;; Initial revision ;;; ;;; @@ -146,17 +145,18 @@ (defconst inferior-ruby-error-regexp-alist '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2) - ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2))) + ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2))) (cond ((not inferior-ruby-mode-map) (setq inferior-ruby-mode-map - (copy-keymap comint-mode-map)) + (copy-keymap comint-mode-map)) ; (define-key inferior-ruby-mode-map "\M-\C-x" ;gnu convention -; 'ruby-send-definition) +; 'ruby-send-definition) ; (define-key inferior-ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp) (define-key inferior-ruby-mode-map "\C-c\C-l" 'ruby-load-file) )) +;;;###autoload (defun inf-ruby-keys () "Set local key defs for inf-ruby in ruby-mode" (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition) @@ -197,7 +197,7 @@ For information on running multiple processes in multiple buffers, see documentation for variable ruby-buffer. Commands: -Return after the end of the process' output sends the text from the +Return after the end of the process' output sends the text from the end of process to point. Return before the end of the process' output copies the sexp ending at point to the end of the process' output, and sends it. @@ -221,9 +221,9 @@ to continue it." (use-local-map inferior-ruby-mode-map) (setq comint-input-filter (function ruby-input-filter)) (setq comint-get-old-input (function ruby-get-old-input)) - (compilation-shell-minor-mode t) (make-local-variable 'compilation-error-regexp-alist) (setq compilation-error-regexp-alist inferior-ruby-error-regexp-alist) + (compilation-shell-minor-mode t) (run-hooks 'inferior-ruby-mode-hook)) (defvar inferior-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'" @@ -240,8 +240,8 @@ Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.") (let ((rtn-str "") (start 0) match prev-start) (while (setq match (string-match regexp str start)) (setq prev-start start - start (match-end 0) - rtn-str (concat rtn-str (substring str prev-start match)))) + start (match-end 0) + rtn-str (concat rtn-str (substring str prev-start match)))) (concat rtn-str (substring str start)))) (defun ruby-get-old-input () @@ -250,22 +250,23 @@ Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.") (let ((end (point))) (re-search-backward inferior-ruby-first-prompt-pattern) (remove-in-string (buffer-substring (point) end) - inferior-ruby-prompt-pattern) + inferior-ruby-prompt-pattern) ))) (defun ruby-args-to-list (string) (let ((where (string-match "[ \t]" string))) (cond ((null where) (list string)) - ((not (= where 0)) - (cons (substring string 0 where) - (ruby-args-to-list (substring string (+ 1 where) - (length string))))) - (t (let ((pos (string-match "[^ \t]" string))) - (if (null pos) - nil - (ruby-args-to-list (substring string pos - (length string))))))))) - + ((not (= where 0)) + (cons (substring string 0 where) + (ruby-args-to-list (substring string (+ 1 where) + (length string))))) + (t (let ((pos (string-match "[^ \t]" string))) + (if (null pos) + nil + (ruby-args-to-list (substring string pos + (length string))))))))) + +;;;###autoload (defun run-ruby (cmd) "Run an inferior Ruby process, input and output via buffer *ruby*. If there is a process already running in `*ruby*', switch to that buffer. @@ -275,13 +276,13 @@ of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook' \(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive (list (if current-prefix-arg - (read-string "Run Ruby: " ruby-program-name) - ruby-program-name))) + (read-string "Run Ruby: " ruby-program-name) + ruby-program-name))) (if (not (comint-check-proc "*ruby*")) (let ((cmdlist (ruby-args-to-list cmd))) - (set-buffer (apply 'make-comint "ruby" (car cmdlist) - nil (cdr cmdlist))) - (inferior-ruby-mode))) + (set-buffer (apply 'make-comint "ruby" (car cmdlist) + nil (cdr cmdlist))) + (inferior-ruby-mode))) (setq ruby-program-name cmd) (setq ruby-buffer "*ruby*") (pop-to-buffer "*ruby*")) @@ -298,20 +299,20 @@ Must not contain ruby meta characters.") (let (term (file (buffer-file-name)) line) (save-excursion (save-restriction - (widen) - (goto-char start) - (setq line (+ start (forward-line (- start)) 1)) - (goto-char start) - (while (progn - (setq term (apply 'format ruby-send-terminator (random) (current-time))) - (re-search-forward (concat "^" (regexp-quote term) "$") end t))))) + (widen) + (goto-char start) + (setq line (+ start (forward-line (- start)) 1)) + (goto-char start) + (while (progn + (setq term (apply 'format ruby-send-terminator (random) (current-time))) + (re-search-forward (concat "^" (regexp-quote term) "$") end t))))) ;; compilation-parse-errors parses from second line. (save-excursion (let ((m (process-mark (ruby-proc)))) - (set-buffer (marker-buffer m)) - (goto-char m) - (insert ruby-eval-separator "\n") - (set-marker m (point)))) + (set-buffer (marker-buffer m)) + (goto-char m) + (insert ruby-eval-separator "\n") + (set-marker m (point)))) (comint-send-string (ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line)) (comint-send-region (ruby-proc) start end) (comint-send-string (ruby-proc) (concat "\n" term "\n")))) @@ -348,8 +349,8 @@ With argument, positions cursor at end of buffer." (pop-to-buffer ruby-buffer) (error "No current process buffer. See variable ruby-buffer.")) (cond (eob-p - (push-mark) - (goto-char (point-max))))) + (push-mark) + (goto-char (point-max))))) (defun ruby-send-region-and-go (start end) "Send the current region to the inferior Ruby process. @@ -359,14 +360,14 @@ Then switch to the process buffer." (switch-to-ruby t)) (defun ruby-send-definition-and-go () - "Send the current definition to the inferior Ruby. + "Send the current definition to the inferior Ruby. Then switch to the process buffer." (interactive) (ruby-send-definition) (switch-to-ruby t)) (defun ruby-send-block-and-go () - "Send the current block to the inferior Ruby. + "Send the current block to the inferior Ruby. Then switch to the process buffer." (interactive) (ruby-send-block) @@ -381,35 +382,35 @@ Used by these commands to determine defaults.") (defvar ruby-prev-l/c-dir/file nil "Caches the last (directory . file) pair. Caches the last pair used in the last ruby-load-file command. -Used for determining the default in the +Used for determining the default in the next one.") (defun ruby-load-file (file-name) "Load a Ruby file into the inferior Ruby process." (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file - ruby-source-modes t)) ; T because LOAD + ruby-source-modes t)) ; T because LOAD ; needs an exact name (comint-check-source file-name) ; Check to see if buffer needs saved. (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name) - (file-name-nondirectory file-name))) + (file-name-nondirectory file-name))) (comint-send-string (ruby-proc) (concat "(load \"" - file-name - "\"\)\n"))) + file-name + "\"\)\n"))) (defun ruby-proc () "Returns the current ruby process. See variable ruby-buffer." (let ((proc (get-buffer-process (if (eq major-mode 'inferior-ruby-mode) - (current-buffer) - ruby-buffer)))) + (current-buffer) + ruby-buffer)))) (or proc - (error "No current process. See variable ruby-buffer")))) + (error "No current process. See variable ruby-buffer")))) ;;; Do the user's customisation... (defvar inf-ruby-load-hook nil "This hook is run when inf-ruby is loaded in. This is a good place to put keybindings.") - + (run-hooks 'inf-ruby-load-hook) (provide 'inf-ruby) diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py new file mode 100755 index 0000000000..e954d1be00 --- /dev/null +++ b/misc/lldb_cruby.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +#coding: utf-8 +# +# Usage: run `command script import -r misc/lldb_cruby.py` on LLDB +# +# Test: misc/test_lldb_cruby.rb +# + +import lldb +import commands +import os +import shlex + +def lldb_init(debugger): + target = debugger.GetSelectedTarget() + global SIZEOF_VALUE + SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize() + + value_types = [] + g = globals() + for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'): + enum = enum.GetType() + members = enum.GetEnumMembers() + for i in xrange(0, members.GetSize()): + member = members.GetTypeEnumMemberAtIndex(i) + name = member.GetName() + value = member.GetValueAsUnsigned() + g[name] = value + + if name.startswith('RUBY_T_'): + value_types.append(name) + g['value_types'] = value_types + +def string2cstr(rstring): + """Returns the pointer to the C-string in the given String object""" + flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned + if flags & RUBY_T_MASK != RUBY_T_STRING: + raise TypeError("not a string") + if flags & RUBY_FL_USER1: + cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0) + clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0) + else: + cptr = int(rstring.GetValueForExpressionPath(".as.ary").value, 0) + clen = (flags & RSTRING_EMBED_LEN_MASK) >> RSTRING_EMBED_LEN_SHIFT + return cptr, clen + +def output_string(ctx, rstring): + cptr, clen = string2cstr(rstring) + expr = 'printf("%%.*s", (size_t)%d, (const char*)%d)' % (clen, cptr) + ctx.frame.EvaluateExpression(expr) + +def fixnum_p(x): + return x & RUBY_FIXNUM_FLAG != 0 + +def flonum_p(x): + return (x&RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG + +def lldb_rp(debugger, command, result, internal_dict): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if frame.IsValid(): + val = frame.EvaluateExpression(command) + else: + val = target.EvaluateExpression(command) + error = val.GetError() + if error.Fail(): + print >> result, error + return + num = val.GetValueAsSigned() + if num == RUBY_Qfalse: + print >> result, 'false' + elif num == RUBY_Qtrue: + print >> result, 'true' + elif num == RUBY_Qnil: + print >> result, 'nil' + elif num == RUBY_Qundef: + print >> result, 'undef' + elif fixnum_p(num): + print >> result, num >> 1 + elif flonum_p(num): + debugger.HandleCommand("print rb_float_value(%0#x)" % val.GetValueAsUnsigned()) + elif num & RUBY_IMMEDIATE_MASK: + print >> result, 'immediate(%x)' % num + else: + tRBasic = target.FindFirstType("struct RBasic").GetPointerType() + val = val.Cast(tRBasic) + flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned() + if (flags & RUBY_FL_PROMOTED) == RUBY_FL_PROMOTED: + print >> result, "[PROMOTED] " + flType = flags & RUBY_T_MASK + if flType == RUBY_T_NONE: + print >> result, 'T_NONE: %s' % val.Dereference() + elif flType == RUBY_T_NIL: + print >> result, 'T_NIL: %s' % val.Dereference() + elif flType == RUBY_T_OBJECT: + tRObject = target.FindFirstType("struct RObject").GetPointerType() + val = val.Cast(tRObject) + print >> result, 'T_OBJECT: %s' % val.Dereference() + elif flType == RUBY_T_STRING: + tRString = target.FindFirstType("struct RString").GetPointerType() + val = val.Cast(tRString) + if flags & RSTRING_NOEMBED: + print >> result, val.GetValueForExpressionPath("->as.heap") + else: + print >> result, val.GetValueForExpressionPath("->as.ary") + elif flType == RUBY_T_ARRAY: + tRArray = target.FindFirstType("struct RArray").GetPointerType() + val = val.Cast(tRArray) + if flags & RUBY_FL_USER1: + len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3)) + print >> result, "T_ARRAY: len=%d (embed)" % len + if len == 0: + print >> result, "{(empty)}" + else: + print >> result, val.GetValueForExpressionPath("->as.ary") + else: + len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned() + print >> result, "T_ARRAY: len=%d " % len + #print >> result, val.GetValueForExpressionPath("->as.heap") + if flags & RUBY_FL_USER2: + shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned() + print >> result, "(shared) shared=%016x " % shared + else: + capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned() + print >> result, "(ownership) capa=%d " % capa + if len == 0: + print >> result, "{(empty)}" + else: + debugger.HandleCommand("expression -Z %d -fx -- (const VALUE*)((struct RArray*)%d)->as.heap.ptr" % (len, val.GetValueAsUnsigned())) + debugger.HandleCommand("p (struct RArray *) %0#x" % val.GetValueAsUnsigned()) + +def count_objects(debugger, command, ctx, result, internal_dict): + objspace = ctx.frame.EvaluateExpression("ruby_current_vm->objspace") + num_pages = objspace.GetValueForExpressionPath(".heap_pages.allocated_pages").unsigned + + counts = {} + total = 0 + for t in range(0x00, RUBY_T_MASK+1): + counts[t] = 0 + + for i in range(0, num_pages): + print "\rcounting... %d/%d" % (i, num_pages), + page = objspace.GetValueForExpressionPath('.heap_pages.sorted[%d]' % i) + p = page.GetChildMemberWithName('start') + num_slots = page.GetChildMemberWithName('total_slots').unsigned + for j in range(0, num_slots): + obj = p.GetValueForExpressionPath('[%d]' % j) + flags = obj.GetValueForExpressionPath('.as.basic.flags').unsigned + obj_type = flags & RUBY_T_MASK + counts[obj_type] += 1 + total += num_slots + + print "\rTOTAL: %d, FREE: %d" % (total, counts[0x00]) + for sym in value_types: + print "%s: %d" % (sym, counts[globals()[sym]]) + +def stack_dump_raw(debugger, command, ctx, result, internal_dict): + ctx.frame.EvaluateExpression("rb_vmdebug_stack_dump_raw_current()") + +def dump_node(debugger, command, ctx, result, internal_dict): + args = shlex.split(command) + if not args: + return + node = args[0] + + dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node) + output_string(ctx, dump) + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp") + debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects") + debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR") + debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node") + lldb_init(debugger) + print "lldb scripts for ruby has been installed." diff --git a/misc/rb_optparse.bash b/misc/rb_optparse.bash new file mode 100644 index 0000000000..8a59ec2dda --- /dev/null +++ b/misc/rb_optparse.bash @@ -0,0 +1,20 @@ +#! /bin/bash +# Completion for bash: +# +# (1) install this file, +# +# (2) load the script, and +# . ~/.profile.d/rb_optparse.bash +# +# (3) define completions in your .bashrc, +# rb_optparse command_using_optparse_1 +# rb_optparse command_using_optparse_2 + +_rb_optparse() { + COMPREPLY=($("${COMP_WORDS[0]}" "--*-completion-bash=${COMP_WORDS[COMP_CWORD]}")) + return 0 +} + +rb_optparse () { + [ $# = 0 ] || complete -o default -F _rb_optparse "$@" +} diff --git a/misc/rb_optparse.zsh b/misc/rb_optparse.zsh new file mode 100755 index 0000000000..d53170c5f7 --- /dev/null +++ b/misc/rb_optparse.zsh @@ -0,0 +1,38 @@ +#!/bin/zsh +# Completion for zsh: +# (based on <http://d.hatena.ne.jp/rubikitch/20071002/zshcomplete>) +# +# (1) install this file. +# mkdir -p ~/.zsh.d +# cp rb_optparse.zsh ~/.zsh.d/rb_optparse.zsh +# +# (2) load the script, and add a directory to fpath before compinit. +# echo '. ~/.zsh.d/rb_optparse.zsh' >> ~/.zshrc +# echo 'fpath=(~/.zsh.d/Completion $fpath)' >> ~/.zshrc +# echo 'autoload -U compinit; compinit' >> ~/.zshrc +# +# (3) restart zsh. +# +# (4) geneate completion files once. +# generate-complete-function/ruby/optparse COMMAND1 +# generate-complete-function/ruby/optparse COMMAND2 +# + +generate-complete-function/ruby/optparse () +{ + local cmpl="_${1:t}" + mkdir -p "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}" + $1 "--*-completion-zsh=${1:t}" >! "${ZSH_COMPLETION_DIR-$HOME/.zsh.d/Completion}/$cmpl" + if [[ $(type -w "$cmpl") == "${cmpl}: function" ]]; then + unfunction "$cmpl" + autoload -U "$cmpl" + else + compinit "$cmpl" + fi +} + +compdef _command generate-complete-function/ruby/optparse + +for cmd in "$@"; do + generate-complete-function/ruby/optparse "$cmd" +done diff --git a/misc/rdebug.el b/misc/rdebug.el deleted file mode 100644 index 81ab0071a0..0000000000 --- a/misc/rdebug.el +++ /dev/null @@ -1,136 +0,0 @@ -;; This file adds support for ruby-debug (rdebug) in Emacs. -;; Copyright (C) 2007 Martin Nordholts <enselic@gmail.com> -;; -;; This file is based on 'rubydb3x.el' that comes with Ruby which is -;; Copyright (C) Yukihiro Matsumoto aka Matz -;; -;; Installation: -;; ------------- -;; -;; 1. Make sure you have ruby-debug on your system (test by running -;; the commmand 'rdebug -v' in a shell). -;; -;; 2. Copy this file into e.g. ~/.elisp and make sure this is in -;; your ~/.emacs: -;; -;; (add-to-list 'load-path "~/.elisp") -;; (load-library "rdebug") -;; -;; you can then start the debugger with M-x rdebug -;; -;; 3. Setup convenient keybindings etc. This is what I have: -;; -;; (global-set-key [f9] 'gud-step) -;; (global-set-key [f10] 'gud-next) -;; (global-set-key [f11] 'gud-cont) -;; -;; (global-set-key "\C-c\C-d" 'rdebug) -;; -;; 4. Debug like crazy! -;; -;; Bugs: -;; ----- -;; -;; Basic functionality works fine, though there might be a bug hiding somewhere. - -(require 'gud) -(provide 'rdebug) - -;; ====================================================================== -;; rdebug functions - -;;; History of argument lists passed to rdebug. -(defvar gud-rdebug-history nil) - -(if (fboundp 'gud-overload-functions) - (defun gud-rdebug-massage-args (file args) - (cons file args)) - (defun gud-rdebug-massage-args (file args) - args)) - -;; There's no guarantee that Emacs will hand the filter the entire -;; marker at once; it could be broken up across several strings. We -;; might even receive a big chunk with several markers in it. If we -;; receive a chunk of text which looks like it might contain the -;; beginning of a marker, we save it here between calls to the -;; filter. -(defvar gud-rdebug-marker-acc "") -(make-variable-buffer-local 'gud-rdebug-marker-acc) - -(defun gud-rdebug-marker-filter (string) - (setq gud-rdebug-marker-acc (concat gud-rdebug-marker-acc string)) - (let ((output "")) - - ;; Process all the complete markers in this chunk. - (while (string-match "\\([^:\n]*\\):\\([0-9]+\\):.*\n" - gud-rdebug-marker-acc) - (setq - - ;; Extract the frame position from the marker. - gud-last-frame - (cons (substring gud-rdebug-marker-acc (match-beginning 1) (match-end 1)) - (string-to-int (substring gud-rdebug-marker-acc - (match-beginning 2) - (match-end 2)))) - - - ;; Append any text before the marker to the output we're going - ;; to return - we don't include the marker in this text. - output (concat output - (substring gud-rdebug-marker-acc 0 (match-beginning 0))) - - ;; Set the accumulator to the remaining text. - gud-rdebug-marker-acc (substring gud-rdebug-marker-acc (match-end 0)))) - - (setq output (concat output gud-rdebug-marker-acc) - gud-rdebug-marker-acc "") - - output)) - -(defun gud-rdebug-find-file (f) - (save-excursion - (let ((buf (find-file-noselect f))) - (set-buffer buf) -;; (gud-make-debug-menu) - buf))) - -(defvar rdebug-command-name "rdebug" - "File name for executing rdebug.") - -;;;###autoload -(defun rdebug (command-line) - "Run rdebug on program FILE in buffer *gud-FILE*. -The directory containing FILE becomes the initial working directory -and source-file directory for your debugger." - (interactive - (list (read-from-minibuffer "Run rdebug (like this): " - (if (consp gud-rdebug-history) - (car gud-rdebug-history) - (concat rdebug-command-name " ")) - nil nil - '(gud-rdebug-history . 1)))) - - (if (not (fboundp 'gud-overload-functions)) - (gud-common-init command-line 'gud-rdebug-massage-args - 'gud-rdebug-marker-filter 'gud-rdebug-find-file) - (gud-overload-functions '((gud-massage-args . gud-rdebug-massage-args) - (gud-marker-filter . gud-rdebug-marker-filter) - (gud-find-file . gud-rdebug-find-file))) - (gud-common-init command-line rdebug-command-name)) - - (gud-def gud-break "break %d%f:%l" "\C-b" "Set breakpoint at current line in current file.") -; (gud-def gud-remove "delete %d%f:%l" "\C-d" "Remove breakpoint at current line in current file.") - (gud-def gud-step "step" "\C-s" "Step one source line with display.") - (gud-def gud-next "next" "\C-n" "Step one line (skip functions).") - (gud-def gud-cont "cont" "\C-r" "Continue with display.") - (gud-def gud-finish "finish" "\C-f" "Finish executing current function.") - (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).") - (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).") - (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.") - - (setq comint-prompt-regexp "^(rdb:-) ") - (if (boundp 'comint-last-output-start) - (set-marker comint-last-output-start (point))) - (set (make-local-variable 'paragraph-start) comint-prompt-regexp) - (run-hooks 'rdebug-mode-hook) - ) diff --git a/misc/rdoc-mode.el b/misc/rdoc-mode.el new file mode 100644 index 0000000000..c26c2ee564 --- /dev/null +++ b/misc/rdoc-mode.el @@ -0,0 +1,166 @@ +;; +;; rdoc-mode.el +;; Major mode for RDoc editing +;; + +;; Created: Fri Sep 18 09:04:49 JST 2009 + +;; License: Ruby's + +(require 'derived) + +;;;###autoload +(define-derived-mode rdoc-mode text-mode "RDoc" + "Major mode for RD editing. +\\{rdoc-mode-map}" + (make-local-variable 'paragraph-separate) + (setq paragraph-separate "^\\(=+\\|\\*+\\)[ \t\v\f]*\\|^\\s *$") + (make-local-variable 'paragraph-start) + (setq paragraph-start paragraph-separate) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '((rdoc-font-lock-keywords) t nil)) + (make-local-variable 'font-lock-keywords) + (setq font-lock-keywords rdoc-font-lock-keywords) + (make-local-variable 'outline-regexp) + (setq outline-regexp "^\\(=+\\)[ \t\v\f]*") + (outline-minor-mode t) + (setq show-trailing-whitespace t) + (rdoc-setup-keys) + (setq indent-tabs-mode nil) + (run-hooks 'rdoc-mode-hook) + ) + +(defun rdoc-fill-paragraph (&optional justify region) + "Fills paragraph, except for cited region" + (interactive (progn + (barf-if-buffer-read-only) + (list (if current-prefix-arg 'full)))) + (save-excursion + (beginning-of-line) + (save-restriction + (let ((pos (point)) beg end indent hanging) + (cond + ((looking-at "^ +\\(\\*\\s *\\)") + (setq indent (- (match-end 0) (match-beginning 0)) + hanging (- (match-end 1) (match-beginning 1)))) + ((looking-at "^ +") + (setq indent (- (match-end 0) (match-beginning 0))) + (when (and (re-search-backward "^[^ ]\\|^\\( *\\(\\* *\\)\\)" nil t) + (match-beginning 1) + (= indent (- (match-end 1) (match-beginning 1)))) + (setq hanging (- (match-end 2) (match-beginning 2))) + (setq beg (match-beginning 1)))) + ((setq beg t))) + (when beg + (when indent + (goto-char pos) + (while (progn (beginning-of-line 2) + (and (looking-at "^\\( +\\)\\S ") + (= indent (- (match-end 1) (match-beginning 1)))))) + (setq end (point)) + (when (and beg (not region)) + (setq region (list beg end)) + (narrow-to-region beg end) + )) + (goto-char pos) + (fill-paragraph justify region) + (when (and indent + (or (goto-char beg) t) + (or (beginning-of-line 2) t) + (looking-at "^\\( +\\)") + (= (- indent hanging) (- (match-end 0) (match-beginning 0)))) + (insert-char ?\s hanging) + (beginning-of-line) + (narrow-to-region (point) end) + (fill-paragraph justify (list (point) end)))))))) + +(defun rdoc-setup-keys () + (interactive) + (define-key rdoc-mode-map "\M-q" 'rdoc-fill-paragraph) + ) + +(defvar rdoc-heading1-face 'font-lock-keywordoc-face) +(defvar rdoc-heading2-face 'font-lock-type-face) +(defvar rdoc-heading3-face 'font-lock-variable-name-face) +(defvar rdoc-heading4-face 'font-lock-comment-face) +(defvar rdoc-bold-face 'font-lock-function-name-face) +(defvar rdoc-emphasis-face 'font-lock-function-name-face) +(defvar rdoc-code-face 'font-lock-keyword-face) +(defvar rdoc-description-face 'font-lock-constant-face) + +(defvar rdoc-font-lock-keywords + (list + (list "^=([^=\r\n].*)?$" + 0 rdoc-heading1-face) + (list "^==([^=\r\n].*)?$" + 0 rdoc-heading2-face) + (list "^===([^=\r\n].*)?$" + 0 rdoc-heading3-face) + (list "^====+.*$" + 0 rdoc-heading4-face) + (list "\\(^\\|[ \t\v\f]\\)\\(\\*\\(\\sw\\|[-_:]\\)+\\*\\)\\($\\|[ \t\v\f]\\)" + 2 rdoc-bold-face) ; *bold* + (list "\\(^\\|[ \t\v\f]\\)\\(_\\(\\sw\\|[-_:]\\)+_\\)\\($\\|[ \t\v\f]\\)" + 2 rdoc-emphasis-face) ; _emphasis_ + (list "\\(^\\|[ \t\v\f]\\)\\(\\+\\(\\sw\\|[-_:]\\)+\\+\\)\\($\\|[ \t\v\f]\\)" + 2 rdoc-code-face) ; +code+ + (list "<em>[^<>]*</em>" 0 rdoc-emphasis-face) + (list "<i>[^<>]*</i>" 0 rdoc-emphasis-face) + (list "<b>[^<>]*</b>" 0 rdoc-bold-face) + (list "<tt>[^<>]*</tt>" 0 rdoc-code-face) + (list "<code>[^<>]*</code>" 0 rdoc-code-face) + (list "^\\([-*]\\|[0-9]+\\.\\|[A-Za-z]\\.\\)\\s " + 1 rdoc-description-face) ; bullet | numbered | alphabetically numbered + (list "^\\[[^\]]*\\]\\|\\S .*::\\)\\([ \t\v\f]\\|$\\)" + 1 rdoc-description-face) ; labeled | node + ;(list "^[ \t\v\f]+\\(.*\\)" 1 rdoc-verbatim-face) + )) + +(defun rdoc-imenu-create-index () + (let ((root '(nil . nil)) + cur-alist + (cur-level 0) + (pattern (concat outline-regexp "\\(.*?\\)[ \t\v\f]*$")) + (empty-heading "-") + (self-heading ".") + pos level heading alist) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward pattern (point-max) t) + (setq heading (match-string-no-properties 2) + level (min 6 (length (match-string-no-properties 1))) + pos (match-beginning 1)) + (if (= (length heading) 0) + (setq heading empty-heading)) + (setq alist (list (cons heading pos))) + (cond + ((= cur-level level) ; new sibling + (setcdr cur-alist alist) + (setq cur-alist alist)) + ((< cur-level level) ; first child + (dotimes (i (- level cur-level 1)) + (setq alist (list (cons empty-heading alist)))) + (if cur-alist + (let* ((parent (car cur-alist)) + (self-pos (cdr parent))) + (setcdr parent (cons (cons self-heading self-pos) alist))) + (setcdr root alist)) ; primogenitor + (setq cur-alist alist + cur-level level)) + (t ; new sibling of an ancestor + (let ((sibling-alist (last (cdr root)))) + (dotimes (i (1- level)) + (setq sibling-alist (last (cdar sibling-alist)))) + (setcdr sibling-alist alist) + (setq cur-alist alist + cur-level level)))))) + (cdr root))) + +(defun rdoc-set-imenu-create-index-function () + (setq imenu-create-index-function 'rdoc-imenu-create-index)) + +(add-hook 'rdoc-mode-hook 'rdoc-set-imenu-create-index-function) + +(provide 'rdoc-mode) diff --git a/misc/ruby-additional.el b/misc/ruby-additional.el new file mode 100644 index 0000000000..432adfedb6 --- /dev/null +++ b/misc/ruby-additional.el @@ -0,0 +1,181 @@ +;;; ruby-additional.el --- ruby-mode extensions yet to be merged into Emacs + +;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada, Akinori MUSHA +;; URL: https://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/misc/ +;; Created: 3 Sep 2012 +;; Package-Requires: ((emacs "24.3") (ruby-mode "1.2")) +;; Keywords: ruby, languages + +;;; Commentary: +;; +;; This package contains ruby-mode extensions yet to be merged into +;; the latest released version of Emacs distribution. For older +;; versions of Emacs, use ruby-mode.el bundled with CRuby. + +;;; Code: + +(eval-when-compile + (require 'ruby-mode)) + +(eval-after-load 'ruby-mode + '(progn + (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end) + + (defun ruby-insert-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) + (end-of-line))) + + (defconst ruby-default-encoding-map + '((us-ascii . nil) ;; Do not put coding: us-ascii + (utf-8 . nil) ;; Do not put coding: utf-8 + (shift-jis . cp932) ;; Emacs charset name of Shift_JIS + (shift_jis . cp932) ;; MIME charset name of Shift_JIS + (japanese-cp932 . cp932)) ;; Emacs charset name of CP932 + ) + + (custom-set-default 'ruby-encoding-map ruby-default-encoding-map) + + (defcustom ruby-encoding-map ruby-default-encoding-map + "Alist to map encoding name from Emacs to Ruby. +Associating an encoding name with nil means it needs not be +explicitly declared in magic comment." + :type '(repeat (cons (symbol :tag "From") (symbol :tag "To"))) + :group 'ruby) + + (defun ruby-mode-set-encoding () + "Insert or update a magic comment header with the proper encoding. +`ruby-encoding-map' is looked up to convert an encoding name from +Emacs to Ruby." + (let* ((nonascii + (save-excursion + (widen) + (goto-char (point-min)) + (re-search-forward "[^\0-\177]" nil t))) + (coding-system + (or coding-system-for-write + buffer-file-coding-system)) + (coding-system + (and coding-system + (coding-system-change-eol-conversion coding-system nil))) + (coding-system + (and coding-system + (or + (coding-system-get coding-system :mime-charset) + (let ((coding-type (coding-system-get coding-system :coding-type))) + (cond ((eq coding-type 'undecided) + (if nonascii + (or (and (coding-system-get coding-system :prefer-utf-8) + 'utf-8) + (coding-system-get default-buffer-file-coding-system :coding-type) + 'ascii-8bit))) + ((memq coding-type '(utf-8 shift-jis)) + coding-type) + (t coding-system)))))) + (coding-system + (or coding-system + 'us-ascii)) + (coding-system + (let ((cons (assq coding-system ruby-encoding-map))) + (if cons (cdr cons) coding-system))) + (coding-system + (and coding-system + (symbol-name coding-system)))) + (if coding-system + (save-excursion + (widen) + (goto-char (point-min)) + (if (looking-at "^#!") (beginning-of-line 2)) + (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)") + (unless (string= (match-string 2) coding-system) + (goto-char (match-beginning 2)) + (delete-region (point) (match-end 2)) + (and (looking-at "-\*-") + (let ((n (skip-chars-backward " "))) + (cond ((= n 0) (insert " ") (backward-char)) + ((= n -1) (insert " ")) + ((forward-char))))) + (insert coding-system))) + ((looking-at "\\s *#.*coding\\s *[:=]")) + (t (when ruby-insert-encoding-magic-comment + (insert "# -*- coding: " coding-system " -*-\n")))))))) + + (define-key ruby-mode-map "\C-cU" 'ruby-encode-decode-unicode) + + (defun ruby-encode-unicode (beg end) + "Convert non-ascii string in the given region to \\u{} form." + (interactive "r") + (setq end (set-marker (make-marker) end)) + (goto-char beg) + (while (and (< (point) end) + (re-search-forward "\\([\C-@-\C-I\C-K\C-_\C-?]+\\)\\|[^\C-@-\C-?]+" end t)) + (let ((str (match-string-no-properties 0)) sep b e f) + (if (match-beginning 1) + (setq b "" e "" sep "" + f (lambda (c) + (cond ((= c ?\t) "\\t") + ((= c ?\r) "\\r") + ((= c ?\e) "\\e") + ((= c ?\f) "\\f") + ((= c ?\b) "\\b") + ((= c ?\v) "\\v") + ((= c ?\C-?) "\\c?") + ((concat "\\c" (char-to-string (logior c #x40))))))) + (setq b "\\u{" e "}" sep " " f (lambda (c) (format "%x" c)))) + (setq str (mapconcat f str sep)) + (delete-region (match-beginning 0) (match-end 0)) + (insert b str e)))) + + (defun ruby-decode-unicode (beg end) + "Convert escaped Unicode in the given region to raw string." + (interactive "r") + (setq end (set-marker (make-marker) end)) + (goto-char beg) + (while (and (< (point) end) + (re-search-forward "\\\\u\\([0-9a-fA-F]\\{4\\}\\)\\|\\\\u{\\([0-9a-fA-F \t]+\\)}" end t)) + (let ((b (match-beginning 0)) (e (match-end 0)) + (s (match-string-no-properties 1))) + (if s + (setq s (cons s nil)) + (goto-char (match-beginning 2)) + (while (looking-at "[ \t]*\\([0-9a-fA-F]+\\)") + (setq s (cons (match-string-no-properties 1) s)) + (goto-char (match-end 0)))) + (setq s (mapconcat (lambda (c) (format "%c" (string-to-int c 16))) + (nreverse s) "")) + (delete-region b e) + (insert s)) + )) + + (defun ruby-encode-decode-unicode (dec beg end) + "Convert Unicode <-> \\u{} in the given region." + (interactive "P\nr") + (if dec (ruby-decode-unicode beg end) (ruby-encode-unicode beg end))) + + (defun ruby-insert-heredoc-code-block (arg) + "Insert indented here document code block" + (interactive "P") + (let ((c (if arg "~" "-"))) + (insert "\"#{<<" c "\"{#\"}\\n#{<<" c "'};'}\"")) + (end-of-line) + (if (eobp) (insert "\n") (forward-char)) + (indent-region (point) + (progn (insert "{#\n" "};\n") (point))) + (beginning-of-line 0)) + (define-key ruby-mode-map "\C-cH" 'ruby-insert-heredoc-code-block) + )) + +;; monkey-patching ruby-mode.el in Emacs 24, as r49872. +(when (and (boundp 'ruby-syntax-before-regexp-re) + (not (string-match ruby-syntax-before-regexp-re "foo {|" 1))) + (replace-regexp-in-string "\\[\\[" "\\&{|" ruby-syntax-before-regexp-re)) + +(provide 'ruby-additional) + +;;; ruby-additional.el ends here 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 diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el index 3fe99017d9..b1abd18a9e 100644 --- a/misc/ruby-mode.el +++ b/misc/ruby-mode.el @@ -1,74 +1,147 @@ -;;; -;;; ruby-mode.el - -;;; -;;; $Author: matz $ -;;; $Date: 2007/01/24 14:46:10 $ -;;; created at: Fri Feb 4 14:49:13 JST 1994 -;;; +;;; ruby-mode.el --- Major mode for editing Ruby files -(defconst ruby-mode-revision "$Revision: 1.74.2.14.2.1 $") +;; Copyright (C) 1994, 1995, 1996 1997, 1998, 1999, 2000, 2001, +;; 2002,2003, 2004, 2005, 2006, 2007, 2008 +;; Free Software Foundation, Inc. + +;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada +;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode +;; Created: Fri Feb 4 14:49:13 JST 1994 +;; Keywords: languages ruby +;; Version: 0.9 + +;; This file is not part of GNU Emacs. However, a newer version of +;; ruby-mode is included in recent releases of GNU Emacs (version 23 +;; and up), but the new version is not guaranteed to be compatible +;; with older versions of Emacs or XEmacs. This file is the last +;; version that aims to keep this compatibility. + +;; You can also get the latest version from the Emacs Lisp Package +;; Archive: http://tromey.com/elpa + +;; This file 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 of the License, or +;; (at your option) any later version. + +;; It 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 it. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Provides font-locking, indentation support, and navigation for Ruby code. +;; +;; If you're installing manually, you should add this to your .emacs +;; file after putting it on your load path: +;; +;; (autoload 'ruby-mode "ruby-mode" "Major mode for ruby files" t) +;; (add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode)) +;; (add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode)) +;; + +;;; Code: + +(defconst ruby-mode-revision "$Revision$" + "Ruby mode revision string.") (defconst ruby-mode-version - (progn - (string-match "[0-9.]+" ruby-mode-revision) - (substring ruby-mode-revision (match-beginning 0) (match-end 0)))) + (and (string-match "[0-9.]+" ruby-mode-revision) + (substring ruby-mode-revision (match-beginning 0) (match-end 0))) + "Ruby mode version number.") + +(defconst ruby-keyword-end-re + (if (string-match "\\_>" "ruby") + "\\_>" + "\\>")) + +(defconst ruby-block-beg-keywords + '("class" "module" "def" "if" "unless" "case" "while" "until" "for" "begin" "do") + "Keywords at the beginning of blocks.") (defconst ruby-block-beg-re - "class\\|module\\|def\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin\\|do" - ) + (regexp-opt ruby-block-beg-keywords) + "Regexp to match the beginning of blocks.") (defconst ruby-non-block-do-re - "\\(while\\|until\\|for\\|rescue\\)\\>[^_]" - ) + (concat (regexp-opt '("while" "until" "for" "rescue") t) ruby-keyword-end-re) + "Regexp to match") (defconst ruby-indent-beg-re - "\\(\\s *\\(class\\|module\\|def\\)\\)\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin" - ) + (concat "\\(\\s *" (regexp-opt '("class" "module" "def") t) "\\)\\|" + (regexp-opt '("if" "unless" "case" "while" "until" "for" "begin"))) + "Regexp to match where the indentation gets deeper.") + +(defconst ruby-modifier-beg-keywords + '("if" "unless" "while" "until") + "Modifiers that are the same as the beginning of blocks.") (defconst ruby-modifier-beg-re - "if\\|unless\\|while\\|until" - ) + (regexp-opt ruby-modifier-beg-keywords) + "Regexp to match modifiers same as the beginning of blocks.") (defconst ruby-modifier-re - (concat ruby-modifier-beg-re "\\|rescue") - ) + (regexp-opt (cons "rescue" ruby-modifier-beg-keywords)) + "Regexp to match modifiers.") + +(defconst ruby-block-mid-keywords + '("then" "else" "elsif" "when" "rescue" "ensure") + "Keywords where the indentation gets shallower in middle of block statements.") (defconst ruby-block-mid-re - "then\\|else\\|elsif\\|when\\|rescue\\|ensure" - ) + (regexp-opt ruby-block-mid-keywords) + "Regexp to match where the indentation gets shallower in middle of block statements.") -(defconst ruby-block-op-re - "and\\|or\\|not" - ) +(defconst ruby-block-op-keywords + '("and" "or" "not") + "Block operators.") (defconst ruby-block-hanging-re - (concat ruby-modifier-beg-re "\\|" ruby-block-op-re) - ) + (regexp-opt (append ruby-modifier-beg-keywords ruby-block-op-keywords)) + "Regexp to match hanging block modifiers.") -(defconst ruby-block-end-re "\\<end\\>") +(defconst ruby-block-end-re "\\_<end\\_>") (defconst ruby-here-doc-beg-re - "<<\\(-\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)") + "\\(<\\)<\\([-~]\\)?\\(\\([a-zA-Z0-9_]+\\)\\|[\"]\\([^\"]+\\)[\"]\\|[']\\([^']+\\)[']\\)") + +(defconst ruby-here-doc-end-re + "^\\([ \t]+\\)?\\(.*\\)\\(.\\)$") (defun ruby-here-doc-end-match () (concat "^" - (if (match-string 1) "[ \t]*" nil) - (regexp-quote - (or (match-string 3) - (match-string 4) - (match-string 5))))) + (if (match-string 2) "[ \t]*" nil) + (regexp-quote + (or (match-string 4) + (match-string 5) + (match-string 6))))) + +(defun ruby-here-doc-beg-match () + (let ((contents (concat + (regexp-quote (concat (match-string 2) (match-string 3))) + (if (string= (match-string 3) "_") "\\B" "\\b")))) + (concat "<<" + (let ((match (match-string 1))) + (if (and match (> (length match) 0)) + (concat "\\(?:[-~]\\([\"']?\\)\\|\\([\"']\\)" (match-string 1) "\\)" + contents "\\(\\1\\|\\2\\)") + (concat "[-~]?\\([\"']\\|\\)" contents "\\1")))))) (defconst ruby-delimiter - (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\<\\(" - ruby-block-beg-re - "\\)\\>\\|" ruby-block-end-re - "\\|^=begin\\|" ruby-here-doc-beg-re) + (concat "[?$/%(){}#\"'`.:]\\|<<\\|\\[\\|\\]\\|\\_<\\(" + ruby-block-beg-re + "\\)\\_>\\|" ruby-block-end-re + "\\|^=begin\\|" ruby-here-doc-beg-re) ) (defconst ruby-negative (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|" - ruby-block-end-re "\\|}\\|\\]\\)") - ) + ruby-block-end-re "\\|}\\|\\]\\)") + "Regexp to match where the indentation gets shallower.") (defconst ruby-operator-chars "-,.+*/%&|^~=<>:") (defconst ruby-operator-re (concat "[" ruby-operator-chars "]")) @@ -99,7 +172,8 @@ (define-key ruby-mode-map "\t" 'ruby-indent-command) (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end) (define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent) - (define-key ruby-mode-map "\C-m" 'newline)) + (define-key ruby-mode-map "\C-c{" 'ruby-toggle-block) + (define-key ruby-mode-map "\C-c\C-u" 'uncomment-region)) (defvar ruby-mode-syntax-table nil "Syntax table in use in ruby-mode buffers.") @@ -116,6 +190,7 @@ (modify-syntax-entry ?$ "." ruby-mode-syntax-table) (modify-syntax-entry ?? "_" ruby-mode-syntax-table) (modify-syntax-entry ?_ "_" ruby-mode-syntax-table) + (modify-syntax-entry ?: "_" ruby-mode-syntax-table) (modify-syntax-entry ?< "." ruby-mode-syntax-table) (modify-syntax-entry ?> "." ruby-mode-syntax-table) (modify-syntax-entry ?& "." ruby-mode-syntax-table) @@ -138,19 +213,23 @@ (defcustom ruby-indent-tabs-mode nil "*Indentation can insert tabs in ruby mode if this is non-nil." :type 'boolean :group 'ruby) +(put 'ruby-indent-tabs-mode 'safe-local-variable 'booleanp) (defcustom ruby-indent-level 2 "*Indentation of ruby statements." :type 'integer :group 'ruby) +(put 'ruby-indent-level 'safe-local-variable 'integerp) (defcustom ruby-comment-column 32 "*Indentation column of comments." :type 'integer :group 'ruby) +(put 'ruby-comment-column 'safe-local-variable 'integerp) (defcustom ruby-deep-arglist t "*Deep indent lists in parenthesis when non-nil. Also ignores spaces after parenthesis when 'space." :group 'ruby) +(put 'ruby-deep-arglist 'safe-local-variable 'booleanp) (defcustom ruby-deep-indent-paren '(?\( ?\[ ?\] t) "*Deep indent lists in parenthesis when non-nil. t means continuous line. @@ -161,12 +240,30 @@ Also ignores spaces after parenthesis when 'space." "Default deep indent style." :options '(t nil space) :group 'ruby) +(defcustom ruby-encoding-map + '((us-ascii . nil) ;; Do not put coding: us-ascii + (utf-8 . nil) ;; Do not put coding: utf-8 + (shift-jis . cp932) ;; Emacs charset name of Shift_JIS + (shift_jis . cp932) ;; MIME charset name of Shift_JIS + (japanese-cp932 . cp932)) ;; Emacs charset name of CP932 + "Alist to map encoding name from Emacs to Ruby. +Associating an encoding name with nil means it needs not be +explicitly declared in magic comment." + :type '(repeat (cons (symbol :tag "From") (symbol :tag "To"))) + :group 'ruby) + +(defcustom ruby-use-encoding-map t + "*Use `ruby-encoding-map' to set encoding magic comment if this is non-nil." + :type 'boolean :group 'ruby) + +(defvar ruby-indent-point nil "internal variable") + (eval-when-compile (require 'cl)) (defun ruby-imenu-create-index-in-block (prefix beg end) (let ((index-alist '()) (case-fold-search nil) - name next pos decl sing) + name next pos decl sing) (goto-char beg) - (while (re-search-forward "^\\s *\\(\\(class\\>\\(\\s *<<\\)?\\|module\\>\\)\\s *\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\>\\s *\\([^\(\n ]+\\)\\)" end t) + (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^\(\n ]+\\)\\)" end t) (setq sing (match-beginning 3)) (setq decl (match-string 5)) (setq next (match-end 0)) @@ -174,29 +271,29 @@ Also ignores spaces after parenthesis when 'space." (setq pos (match-beginning 0)) (cond ((string= "alias" decl) - (if prefix (setq name (concat prefix name))) - (push (cons name pos) index-alist)) + (if prefix (setq name (concat prefix name))) + (push (cons name pos) index-alist)) ((string= "def" decl) - (if prefix - (setq name - (cond - ((string-match "^self\." name) - (concat (substring prefix 0 -1) (substring name 4))) - (t (concat prefix name))))) - (push (cons name pos) index-alist) - (ruby-accurate-end-of-block end)) + (if prefix + (setq name + (cond + ((string-match "^self\." name) + (concat (substring prefix 0 -1) (substring name 4))) + (t (concat prefix name))))) + (push (cons name pos) index-alist) + (ruby-accurate-end-of-block end)) (t - (if (string= "self" name) - (if prefix (setq name (substring prefix 0 -1))) - (if prefix (setq name (concat (substring prefix 0 -1) "::" name))) - (push (cons name pos) index-alist)) - (ruby-accurate-end-of-block end) - (setq beg (point)) - (setq index-alist - (nconc (ruby-imenu-create-index-in-block - (concat name (if sing "." "#")) - next beg) index-alist)) - (goto-char beg)))) + (if (string= "self" name) + (if prefix (setq name (substring prefix 0 -1))) + (if prefix (setq name (concat (substring prefix 0 -1) "::" name))) + (push (cons name pos) index-alist)) + (ruby-accurate-end-of-block end) + (setq beg (point)) + (setq index-alist + (nconc (ruby-imenu-create-index-in-block + (concat name (if sing "." "#")) + next beg) index-alist)) + (goto-char beg)))) index-alist)) (defun ruby-imenu-create-index () @@ -206,26 +303,29 @@ Also ignores spaces after parenthesis when 'space." (let (state) (or end (setq end (point-max))) (while (and (setq state (apply 'ruby-parse-partial end state)) - (>= (nth 2 state) 0) (< (point) end))))) + (>= (nth 2 state) 0) (< (point) end))))) (defun ruby-mode-variables () (set-syntax-table ruby-mode-syntax-table) + (setq show-trailing-whitespace t) (setq local-abbrev-table ruby-mode-abbrev-table) (make-local-variable 'indent-line-function) (setq indent-line-function 'ruby-indent-line) (make-local-variable 'require-final-newline) (setq require-final-newline t) - (make-variable-buffer-local 'comment-start) + (make-local-variable 'comment-start) (setq comment-start "# ") - (make-variable-buffer-local 'comment-end) + (make-local-variable 'comment-end) (setq comment-end "") - (make-variable-buffer-local 'comment-column) + (make-local-variable 'comment-column) (setq comment-column ruby-comment-column) - (make-variable-buffer-local 'comment-start-skip) + (make-local-variable 'comment-start-skip) (setq comment-start-skip "#+ *") (setq indent-tabs-mode ruby-indent-tabs-mode) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) + (make-local-variable 'parse-sexp-lookup-properties) + (setq parse-sexp-lookup-properties t) (make-local-variable 'paragraph-start) (setq paragraph-start (concat "$\\|" page-delimiter)) (make-local-variable 'paragraph-separate) @@ -233,29 +333,62 @@ Also ignores spaces after parenthesis when 'space." (make-local-variable 'paragraph-ignore-fill-prefix) (setq paragraph-ignore-fill-prefix t)) -;;;###autoload -(defun ruby-mode () - "Major mode for editing ruby scripts. -\\[ruby-indent-command] properly indents subexpressions of multi-line -class, module, def, if, while, for, do, and case statements, taking -nesting into account. - -The variable ruby-indent-level controls the amount of indentation. -\\{ruby-mode-map}" - (interactive) - (kill-all-local-variables) - (use-local-map ruby-mode-map) - (setq mode-name "Ruby") - (setq major-mode 'ruby-mode) - (ruby-mode-variables) - - (make-local-variable 'imenu-create-index-function) - (setq imenu-create-index-function 'ruby-imenu-create-index) - - (make-local-variable 'add-log-current-defun-function) - (setq add-log-current-defun-function 'ruby-add-log-current-method) - - (run-mode-hooks 'ruby-mode-hook)) +(defun ruby-mode-set-encoding () + "Insert or update a magic comment header with the proper encoding. +`ruby-encoding-map' is looked up to convert an encoding name from +Emacs to Ruby." + (let* ((nonascii + (save-excursion + (widen) + (goto-char (point-min)) + (re-search-forward "[^\0-\177]" nil t))) + (coding-system + (or coding-system-for-write + buffer-file-coding-system)) + (coding-system + (and coding-system + (coding-system-change-eol-conversion coding-system nil))) + (coding-system + (and coding-system + (or + (coding-system-get coding-system :mime-charset) + (let ((coding-type (coding-system-get coding-system :coding-type))) + (cond ((eq coding-type 'undecided) + (if nonascii + (or (and (coding-system-get coding-system :prefer-utf-8) + 'utf-8) + (coding-system-get default-buffer-file-coding-system :coding-type) + 'ascii-8bit))) + ((memq coding-type '(utf-8 shift-jis)) + coding-type) + (t coding-system)))))) + (coding-system + (or coding-system + 'us-ascii)) + (coding-system + (let ((cons (assq coding-system ruby-encoding-map))) + (if cons (cdr cons) coding-system))) + (coding-system + (and coding-system + (symbol-name coding-system)))) + (if coding-system + (save-excursion + (widen) + (goto-char (point-min)) + (if (looking-at "^#!") (beginning-of-line 2)) + (cond ((looking-at "\\s *#.*-\*-\\s *\\(en\\)?coding\\s *:\\s *\\([-a-z0-9_]*\\)\\s *\\(;\\|-\*-\\)") + (unless (string= (match-string 2) coding-system) + (goto-char (match-beginning 2)) + (delete-region (point) (match-end 2)) + (and (looking-at "-\*-") + (let ((n (skip-chars-backward " "))) + (cond ((= n 0) (insert " ") (backward-char)) + ((= n -1) (insert " ")) + ((forward-char))))) + (insert coding-system))) + ((looking-at "\\s *#.*coding\\s *[:=]")) + (t (when ruby-insert-encoding-magic-comment + (insert "# -*- coding: " coding-system " -*-\n")))))))) (defun ruby-current-indentation () (save-excursion @@ -274,89 +407,102 @@ The variable ruby-indent-level controls the amount of indentation. (defun ruby-indent-to (x) (if x (let (shift top beg) - (and (< x 0) (error "invalid nest")) - (setq shift (current-column)) - (beginning-of-line) - (setq beg (point)) - (back-to-indentation) - (setq top (current-column)) - (skip-chars-backward " \t") - (if (>= shift top) (setq shift (- shift top)) - (setq shift 0)) - (if (and (bolp) - (= x top)) - (move-to-column (+ x shift)) - (move-to-column top) - (delete-region beg (point)) - (beginning-of-line) - (indent-to x) - (move-to-column (+ x shift)))))) + (and (< x 0) (error "invalid nest")) + (setq shift (current-column)) + (beginning-of-line) + (setq beg (point)) + (back-to-indentation) + (setq top (current-column)) + (skip-chars-backward " \t") + (if (>= shift top) (setq shift (- shift top)) + (setq shift 0)) + (if (and (bolp) + (= x top)) + (move-to-column (+ x shift)) + (move-to-column top) + (delete-region beg (point)) + (beginning-of-line) + (indent-to x) + (move-to-column (+ x shift)))))) (defun ruby-special-char-p (&optional pnt) (setq pnt (or pnt (point))) (let ((c (char-before pnt)) (b (and (< (point-min) pnt) (char-before (1- pnt))))) (cond ((or (eq c ??) (eq c ?$))) - ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? )))) - ((eq c ?\\) (eq b ??))))) + ((and (eq c ?:) (or (not b) (eq (char-syntax b) ? )))) + ((eq c ?\\) (eq b ??))))) + +(defun ruby-singleton-class-p () + (save-excursion + (forward-word -1) + (and (or (bolp) (not (eq (char-before (point)) ?_))) + (looking-at "class\\s *<<")))) (defun ruby-expr-beg (&optional option) (save-excursion (store-match-data nil) (let ((space (skip-chars-backward " \t")) - (start (point))) + (start (point))) (cond ((bolp) t) ((progn - (forward-char -1) - (and (looking-at "\\?") - (or (eq (char-syntax (char-before (point))) ?w) - (ruby-special-char-p)))) - nil) - ((and (eq option 'heredoc) (< space 0)) t) + (forward-char -1) + (and (looking-at "\\?") + (or (eq (char-syntax (preceding-char)) ?w) + (ruby-special-char-p)))) + nil) + ((and (eq option 'heredoc) (< space 0)) + (not (progn (goto-char start) (ruby-singleton-class-p)))) ((or (looking-at ruby-operator-re) - (looking-at "[\\[({,;]") - (and (looking-at "[!?]") - (or (not (eq option 'modifier)) - (bolp) - (save-excursion (forward-char -1) (looking-at "\\Sw$")))) - (and (looking-at ruby-symbol-re) - (skip-chars-backward ruby-symbol-chars) - (cond - ((or (looking-at (concat "\\<\\(" ruby-block-beg-re - "|" ruby-block-op-re - "|" ruby-block-mid-re "\\)\\>"))) - (goto-char (match-end 0)) - (not (looking-at "\\s_"))) - ((eq option 'expr-qstr) - (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]")) - ((eq option 'expr-re) - (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]")) - (t nil))))))))) + (looking-at "[\\[({,;]") + (and (looking-at "[!?]") + (or (not (eq option 'modifier)) + (bolp) + (save-excursion (forward-char -1) (looking-at "\\Sw$")))) + (and (looking-at ruby-symbol-re) + (skip-chars-backward ruby-symbol-chars) + (cond + ((looking-at (regexp-opt + (append ruby-block-beg-keywords + ruby-block-op-keywords + ruby-block-mid-keywords) + 'words)) + (goto-char (match-end 0)) + (not (looking-at "\\s_\\|[!?:]"))) + ((eq option 'expr-qstr) + (looking-at "[a-zA-Z][a-zA-z0-9_]* +%[^ \t]")) + ((eq option 'expr-re) + (looking-at "[a-zA-Z][a-zA-z0-9_]* +/[^ \t]")) + (t nil))))))))) (defun ruby-forward-string (term &optional end no-error expand) (let ((n 1) (c (string-to-char term)) - (re (if expand - (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)") - (concat "[^\\]\\(\\\\\\\\\\)*[" term "]")))) + (re (if expand + (concat "[^\\]\\(\\\\\\\\\\)*\\([" term "]\\|\\(#{\\)\\)") + (concat "[^\\]\\(\\\\\\\\\\)*[" term "]")))) (while (and (re-search-forward re end no-error) - (if (match-beginning 3) - (ruby-forward-string "}{" end no-error nil) - (> (setq n (if (eq (char-before (point)) c) - (1- n) (1+ n))) 0))) + (if (match-beginning 3) + (ruby-forward-string "}{" end no-error nil) + (> (setq n (if (eq (char-before (point)) c) + (1- n) (1+ n))) 0))) (forward-char -1)) (cond ((zerop n)) - (no-error nil) - ((error "unterminated string"))))) - -(defun ruby-deep-indent-paren-p (c) - (cond ((listp ruby-deep-indent-paren) - (let ((deep (assoc c ruby-deep-indent-paren))) - (cond (deep - (or (cdr deep) ruby-deep-indent-paren-style)) - ((memq c ruby-deep-indent-paren) - ruby-deep-indent-paren-style)))) - ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style) - ((eq c ?\( ) ruby-deep-arglist))) + (no-error nil) + ((error "unterminated string"))))) + +(defun ruby-deep-indent-paren-p (c &optional pos) + (cond ((save-excursion + (if pos (goto-char pos)) + (ruby-expr-beg)) + nil) + ((listp ruby-deep-indent-paren) + (let ((deep (assoc c ruby-deep-indent-paren))) + (cond (deep + (or (cdr deep) ruby-deep-indent-paren-style)) + ((memq c ruby-deep-indent-paren) + ruby-deep-indent-paren-style)))) + ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style) + ((eq c ?\( ) ruby-deep-arglist))) (defun ruby-parse-partial (&optional end in-string nest depth pcol indent) (or depth (setq depth 0)) @@ -366,215 +512,225 @@ The variable ruby-indent-level controls the amount of indentation. (goto-char (match-beginning 0)) (cond ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw")) - (goto-char pnt)) - ((looking-at "[\"`]") ;skip string - (cond - ((and (not (eobp)) - (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t)) - nil) - (t - (setq in-string (point)) - (goto-char end)))) + (goto-char pnt)) + ((looking-at "[\"`]") ;skip string + (cond + ((and (not (eobp)) + (ruby-forward-string (buffer-substring (point) (1+ (point))) end t t)) + nil) + (t + (setq in-string (point)) + (goto-char end)))) ((looking-at "'") - (cond - ((and (not (eobp)) - (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t)) - nil) - (t - (setq in-string (point)) - (goto-char end)))) + (cond + ((and (not (eobp)) + (re-search-forward "[^\\]\\(\\\\\\\\\\)*'" end t)) + nil) + (t + (setq in-string (point)) + (goto-char end)))) + ((looking-at "/=") + (goto-char pnt)) ((looking-at "/") - (cond - ((and (not (eobp)) (ruby-expr-beg 'expr-re)) - (if (ruby-forward-string "/" end t t) - nil - (setq in-string (point)) - (goto-char end))) - (t - (goto-char pnt)))) + (cond + ((and (not (eobp)) (ruby-expr-beg 'expr-re)) + (if (ruby-forward-string "/" end t t) + nil + (setq in-string (point)) + (goto-char end))) + (t + (goto-char pnt)))) ((looking-at "%") - (cond - ((and (not (eobp)) - (ruby-expr-beg 'expr-qstr) - (not (looking-at "%=")) - (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)")) - (goto-char (match-beginning 1)) - (setq expand (not (memq (char-before) '(?q ?w)))) - (setq w (match-string 1)) - (cond - ((string= w "[") (setq re "][")) - ((string= w "{") (setq re "}{")) - ((string= w "(") (setq re ")(")) - ((string= w "<") (setq re "><")) - ((and expand (string= w "\\")) - (setq w (concat "\\" w)))) - (unless (cond (re (ruby-forward-string re end t expand)) - (expand (ruby-forward-string w end t t)) - (t (re-search-forward - (if (string= w "\\") - "\\\\[^\\]*\\\\" - (concat "[^\\]\\(\\\\\\\\\\)*" w)) - end t))) - (setq in-string (point)) - (goto-char end))) - (t - (goto-char pnt)))) - ((looking-at "\\?") ;skip ?char - (cond - ((and (ruby-expr-beg) - (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?.")) - (goto-char (match-end 0))) - (t - (goto-char pnt)))) - ((looking-at "\\$") ;skip $char - (goto-char pnt) - (forward-char 1)) - ((looking-at "#") ;skip comment - (forward-line 1) - (goto-char (point)) - ) + (cond + ((and (not (eobp)) + (ruby-expr-beg 'expr-qstr) + (not (looking-at "%=")) + (looking-at "%[QqrxWw]?\\([^a-zA-Z0-9 \t\n]\\)")) + (goto-char (match-beginning 1)) + (setq expand (not (memq (char-before) '(?q ?w)))) + (setq w (match-string 1)) + (cond + ((string= w "[") (setq re "][")) + ((string= w "{") (setq re "}{")) + ((string= w "(") (setq re ")(")) + ((string= w "<") (setq re "><")) + ((and expand (string= w "\\")) + (setq w (concat "\\" w)))) + (unless (cond (re (ruby-forward-string re end t expand)) + (expand (ruby-forward-string w end t t)) + (t (re-search-forward + (if (string= w "\\") + "\\\\[^\\]*\\\\" + (concat "[^\\]\\(\\\\\\\\\\)*" w)) + end t))) + (setq in-string (point)) + (goto-char end))) + (t + (goto-char pnt)))) + ((looking-at "\\?") ;skip ?char + (cond + ((and (ruby-expr-beg) + (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?.")) + (goto-char (match-end 0))) + (t + (goto-char pnt)))) + ((looking-at "\\$") ;skip $char + (goto-char pnt) + (forward-char 1)) + ((looking-at "#") ;skip comment + (forward-line 1) + (goto-char (point)) + ) ((looking-at "[\\[{(]") - (let ((deep (ruby-deep-indent-paren-p (char-after)))) - (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg))) - (progn - (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]") - (setq pnt (1- (match-end 0)))) - (setq nest (cons (cons (char-after (point)) pnt) nest)) - (setq pcol (cons (cons pnt depth) pcol)) - (setq depth 0)) - (setq nest (cons (cons (char-after (point)) pnt) nest)) - (setq depth (1+ depth)))) - (goto-char pnt) - ) + (let ((deep (ruby-deep-indent-paren-p (char-after)))) + (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg))) + (progn + (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]") + (setq pnt (1- (match-end 0)))) + (setq nest (cons (cons (char-after (point)) (point)) nest)) + (setq pcol (cons (cons pnt depth) pcol)) + (setq depth 0)) + (setq nest (cons (cons (char-after (point)) pnt) nest)) + (setq depth (1+ depth)))) + (goto-char pnt) + ) ((looking-at "[])}]") - (if (ruby-deep-indent-paren-p (matching-paren (char-after))) - (setq depth (cdr (car pcol)) pcol (cdr pcol)) - (setq depth (1- depth))) - (setq nest (cdr nest)) - (goto-char pnt)) + (if (ruby-deep-indent-paren-p (matching-paren (char-after)) + (if nest + (cdr (nth 0 nest)) + (save-excursion + (forward-char) + (ruby-backward-sexp) + (point)))) + (setq depth (cdr (car pcol)) pcol (cdr pcol)) + (setq depth (1- depth))) + (setq nest (cdr nest)) + (goto-char pnt)) ((looking-at ruby-block-end-re) - (if (or (and (not (bolp)) - (progn - (forward-char -1) - (setq w (char-after (point))) - (or (eq ?_ w) - (eq ?. w)))) - (progn - (goto-char pnt) - (setq w (char-after (point))) - (or (eq ?_ w) - (eq ?! w) - (eq ?? w)))) - nil - (setq nest (cdr nest)) - (setq depth (1- depth))) - (goto-char pnt)) + (if (or (and (not (bolp)) + (progn + (forward-char -1) + (setq w (char-after (point))) + (or (eq ?_ w) + (eq ?. w)))) + (progn + (goto-char pnt) + (setq w (char-after (point))) + (or (eq ?_ w) + (eq ?! w) + (eq ?? w)))) + nil + (setq nest (cdr nest)) + (setq depth (1- depth))) + (goto-char pnt)) ((looking-at "def\\s +[^(\n;]*") - (if (or (bolp) - (progn - (forward-char -1) - (not (eq ?_ (char-after (point)))))) - (progn - (setq nest (cons (cons nil pnt) nest)) - (setq depth (1+ depth)))) - (goto-char (match-end 0))) - ((looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>")) - (and - (save-match-data - (or (not (looking-at "do\\>[^_]")) - (save-excursion - (back-to-indentation) - (not (looking-at ruby-non-block-do-re))))) - (or (bolp) - (progn - (forward-char -1) - (setq w (char-after (point))) - (not (or (eq ?_ w) - (eq ?. w))))) - (goto-char pnt) - (setq w (char-after (point))) - (not (eq ?_ w)) - (not (eq ?! w)) - (not (eq ?? w)) - (skip-chars-forward " \t") - (goto-char (match-beginning 0)) - (or (not (looking-at ruby-modifier-re)) - (ruby-expr-beg 'modifier)) - (goto-char pnt) - (setq nest (cons (cons nil pnt) nest)) - (setq depth (1+ depth))) - (goto-char pnt)) - ((looking-at ":\\(['\"]\\)\\(\\\\.\\|[^\\\\]\\)*\\1") - (goto-char (match-end 0))) - ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\)") - (goto-char (match-end 0))) + (if (or (bolp) + (progn + (forward-char -1) + (not (eq ?_ (char-after (point)))))) + (progn + (setq nest (cons (cons nil pnt) nest)) + (setq depth (1+ depth)))) + (goto-char (match-end 0))) + ((looking-at (concat "\\_<\\(" ruby-block-beg-re "\\)\\_>")) + (and + (save-match-data + (or (not (looking-at (concat "do" ruby-keyword-end-re))) + (save-excursion + (back-to-indentation) + (not (looking-at ruby-non-block-do-re))))) + (or (bolp) + (progn + (forward-char -1) + (setq w (char-after (point))) + (not (or (eq ?_ w) + (eq ?. w))))) + (goto-char pnt) + (setq w (char-after (point))) + (not (eq ?_ w)) + (not (eq ?! w)) + (not (eq ?? w)) + (not (eq ?: w)) + (skip-chars-forward " \t") + (goto-char (match-beginning 0)) + (or (not (looking-at ruby-modifier-re)) + (ruby-expr-beg 'modifier)) + (goto-char pnt) + (setq nest (cons (cons nil pnt) nest)) + (setq depth (1+ depth))) + (goto-char pnt)) + ((looking-at ":\\(['\"]\\)") + (goto-char (match-beginning 1)) + (ruby-forward-string (buffer-substring (match-beginning 1) (match-end 1)) end)) + ((looking-at ":\\([-,.+*/%&|^~<>]=?\\|===?\\|<=>\\|![~=]?\\)") + (goto-char (match-end 0))) ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*[!?=]?\\)?") - (goto-char (match-end 0))) + (goto-char (match-end 0))) ((or (looking-at "\\.\\.\\.?") - (looking-at "\\.[0-9]+") - (looking-at "\\.[a-zA-Z_0-9]+") - (looking-at "\\.")) - (goto-char (match-end 0))) + (looking-at "\\.[0-9]+") + (looking-at "\\.[a-zA-Z_0-9]+") + (looking-at "\\.")) + (goto-char (match-end 0))) ((looking-at "^=begin") - (if (re-search-forward "^=end" end t) - (forward-line 1) - (setq in-string (match-end 0)) - (goto-char end))) + (if (re-search-forward "^=end" end t) + (forward-line 1) + (setq in-string (match-end 0)) + (goto-char end))) ((looking-at "<<") - (cond - ((and (ruby-expr-beg 'heredoc) - (looking-at "<<\\(-\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\sw+\\)")) - (setq re (regexp-quote (or (match-string 4) (match-string 2)))) - (if (match-beginning 1) (setq re (concat "\\s *" re))) - (let* ((id-end (goto-char (match-end 0))) - (line-end-position (save-excursion (end-of-line) (point))) - (state (list in-string nest depth pcol indent))) - ;; parse the rest of the line - (while (and (> line-end-position (point)) - (setq state (apply 'ruby-parse-partial - line-end-position state)))) - (setq in-string (car state) - nest (nth 1 state) - depth (nth 2 state) - pcol (nth 3 state) - indent (nth 4 state)) - ;; skip heredoc section - (if (re-search-forward (concat "^" re "$") end 'move) - (forward-line 1) - (setq in-string id-end) - (goto-char end)))) - (t - (goto-char pnt)))) + (cond + ((and (ruby-expr-beg 'heredoc) + (looking-at "<<\\([-~]\\)?\\(\\([\"'`]\\)\\([^\n]+?\\)\\3\\|\\(?:\\sw\\|\\s_\\)+\\)")) + (setq re (regexp-quote (or (match-string 4) (match-string 2)))) + (if (match-beginning 1) (setq re (concat "\\s *" re))) + (let* ((id-end (goto-char (match-end 0))) + (line-end-position (save-excursion (end-of-line) (point))) + (state (list in-string nest depth pcol indent))) + ;; parse the rest of the line + (while (and (> line-end-position (point)) + (setq state (apply 'ruby-parse-partial + line-end-position state)))) + (setq in-string (car state) + nest (nth 1 state) + depth (nth 2 state) + pcol (nth 3 state) + indent (nth 4 state)) + ;; skip heredoc section + (if (re-search-forward (concat "^" re "$") end 'move) + (forward-line 1) + (setq in-string id-end) + (goto-char end)))) + (t + (goto-char pnt)))) ((looking-at "^__END__$") - (goto-char pnt)) + (goto-char pnt)) ((looking-at ruby-here-doc-beg-re) - (if (re-search-forward (ruby-here-doc-end-match) - indent-point t) - (forward-line 1) - (setq in-string (match-end 0)) - (goto-char indent-point))) + (if (re-search-forward (ruby-here-doc-end-match) + ruby-indent-point t) + (forward-line 1) + (setq in-string (match-end 0)) + (goto-char ruby-indent-point))) (t - (error (format "bad string %s" - (buffer-substring (point) pnt) - )))))) + (error (format "bad string %s" + (buffer-substring (point) pnt) + )))))) (list in-string nest depth pcol)) (defun ruby-parse-region (start end) (let (state) (save-excursion (if start - (goto-char start) - (ruby-beginning-of-indent)) + (goto-char start) + (ruby-beginning-of-indent)) (save-restriction - (narrow-to-region (point) end) - (while (and (> end (point)) - (setq state (apply 'ruby-parse-partial end state)))))) - (list (nth 0 state) ; in-string - (car (nth 1 state)) ; nest - (nth 2 state) ; depth - (car (car (nth 3 state))) ; pcol - ;(car (nth 5 state)) ; indent - ))) + (narrow-to-region (point) end) + (while (and (> end (point)) + (setq state (apply 'ruby-parse-partial end state)))))) + (list (nth 0 state) ; in-string + (car (nth 1 state)) ; nest + (nth 2 state) ; depth + (car (car (nth 3 state))) ; pcol + ;(car (nth 5 state)) ; indent + ))) (defun ruby-indent-size (pos nest) (+ pos (* (or nest 1) ruby-indent-level))) @@ -582,162 +738,173 @@ The variable ruby-indent-level controls the amount of indentation. (defun ruby-calculate-indent (&optional parse-start) (save-excursion (beginning-of-line) - (let ((indent-point (point)) - (case-fold-search nil) - state bol eol begin op-end - (paren (progn (skip-syntax-forward " ") - (and (char-after) (matching-paren (char-after))))) - (indent 0)) + (let ((ruby-indent-point (point)) + (case-fold-search nil) + state bol eol begin op-end + (paren (progn (skip-syntax-forward " ") + (and (char-after) (matching-paren (char-after))))) + (indent 0)) (if parse-start - (goto-char parse-start) - (ruby-beginning-of-indent) - (setq parse-start (point))) + (goto-char parse-start) + (ruby-beginning-of-indent) + (setq parse-start (point))) (back-to-indentation) (setq indent (current-column)) - (setq state (ruby-parse-region parse-start indent-point)) + (setq state (ruby-parse-region parse-start ruby-indent-point)) (cond - ((nth 0 state) ; within string - (setq indent nil)) ; do nothing - ((car (nth 1 state)) ; in paren - (goto-char (setq begin (cdr (nth 1 state)))) - (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state))))) - (if deep - (cond ((and (eq deep t) (eq (car (nth 1 state)) paren)) - (skip-syntax-backward " ") - (setq indent (1- (current-column)))) - ((let ((s (ruby-parse-region (point) indent-point))) - (and (nth 2 s) (> (nth 2 s) 0) - (or (goto-char (cdr (nth 1 s))) t))) - (forward-word -1) - (setq indent (ruby-indent-size (current-column) (nth 2 state)))) - (t - (setq indent (current-column)) - (cond ((eq deep 'space)) - (paren (setq indent (1- indent))) - (t (setq indent (ruby-indent-size (1- indent) 1)))))) - (if (nth 3 state) (goto-char (nth 3 state)) - (goto-char parse-start) (back-to-indentation)) - (setq indent (ruby-indent-size (current-column) (nth 2 state)))) - (and (eq (car (nth 1 state)) paren) - (ruby-deep-indent-paren-p (matching-paren paren)) - (search-backward (char-to-string paren)) - (setq indent (current-column))))) + ((nth 0 state) ; within string + (setq indent nil)) ; do nothing + ((car (nth 1 state)) ; in paren + (goto-char (setq begin (cdr (nth 1 state)))) + (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state)) + (1- (cdr (nth 1 state)))))) + (if deep + (cond ((and (eq deep t) (eq (car (nth 1 state)) paren)) + (skip-syntax-backward " ") + (setq indent (1- (current-column)))) + ((eq deep 'space) + (goto-char (cdr (nth 1 state))) + (setq indent (1+ (current-column)))) + ((let ((s (ruby-parse-region (point) ruby-indent-point))) + (and (nth 2 s) (> (nth 2 s) 0) + (or (goto-char (cdr (nth 1 s))) t))) + (forward-word -1) + (setq indent (ruby-indent-size (current-column) (nth 2 state)))) + (t + (setq indent (current-column)) + (cond ((eq deep 'space)) + (paren (setq indent (1- indent))) + (t (setq indent (ruby-indent-size (1- indent) 1)))))) + (if (nth 3 state) (goto-char (nth 3 state)) + (goto-char parse-start) (back-to-indentation)) + (setq indent (ruby-indent-size (current-column) (nth 2 state)))) + (and (eq (car (nth 1 state)) paren) + (ruby-deep-indent-paren-p (matching-paren paren) + (1- (cdr (nth 1 state)))) + (search-backward (char-to-string paren)) + (setq indent (current-column))))) ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest - (if (null (cdr (nth 1 state))) - (error "invalid nest")) - (goto-char (cdr (nth 1 state))) - (forward-word -1) ; skip back a keyword - (setq begin (point)) - (cond - ((looking-at "do\\>[^_]") ; iter block is a special case - (if (nth 3 state) (goto-char (nth 3 state)) - (goto-char parse-start) (back-to-indentation)) - (setq indent (ruby-indent-size (current-column) (nth 2 state)))) - (t - (setq indent (+ (current-column) ruby-indent-level))))) - + (if (null (cdr (nth 1 state))) + (error "invalid nest")) + (goto-char (cdr (nth 1 state))) + (forward-word -1) ; skip back a keyword + (setq begin (point)) + (cond + ((looking-at "do\\>[^_]") ; iter block is a special case + (if (nth 3 state) (goto-char (nth 3 state)) + (goto-char parse-start) (back-to-indentation)) + (setq indent (ruby-indent-size (current-column) (nth 2 state)))) + (t + (setq indent (+ (current-column) ruby-indent-level))))) + ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest - (setq indent (ruby-indent-size (current-column) (nth 2 state))))) + (setq indent (ruby-indent-size (current-column) (nth 2 state))))) (when indent - (goto-char indent-point) - (end-of-line) - (setq eol (point)) - (beginning-of-line) - (cond - ((and (not (ruby-deep-indent-paren-p paren)) - (re-search-forward ruby-negative eol t)) - (and (not (eq ?_ (char-after (match-end 0)))) - (setq indent (- indent ruby-indent-level)))) - ((and - (save-excursion - (beginning-of-line) - (not (bobp))) - (or (ruby-deep-indent-paren-p t) - (null (car (nth 1 state))))) - ;; goto beginning of non-empty no-comment line - (let (end done) - (while (not done) - (skip-chars-backward " \t\n") - (setq end (point)) - (beginning-of-line) - (if (re-search-forward "^\\s *#" end t) - (beginning-of-line) - (setq done t)))) - (setq bol (point)) - (end-of-line) - ;; skip the comment at the end - (skip-chars-backward " \t") - (let (end (pos (point))) - (beginning-of-line) - (while (and (re-search-forward "#" pos t) - (setq end (1- (point))) - (or (ruby-special-char-p end) - (and (setq state (ruby-parse-region parse-start end)) - (nth 0 state)))) - (setq end nil)) - (goto-char (or end pos)) - (skip-chars-backward " \t") - (setq begin (if (nth 0 state) pos (cdr (nth 1 state)))) - (setq state (ruby-parse-region parse-start (point)))) - (or (bobp) (forward-char -1)) - (and - (or (and (looking-at ruby-symbol-re) - (skip-chars-backward ruby-symbol-chars) - (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")) - (not (eq (point) (nth 3 state))) - (save-excursion - (goto-char (match-end 0)) - (not (looking-at "[a-z_]")))) - (and (looking-at ruby-operator-re) - (not (ruby-special-char-p)) - ;; operator at the end of line - (let ((c (char-after (point)))) - (and -;; (or (null begin) -;; (save-excursion -;; (goto-char begin) -;; (skip-chars-forward " \t") -;; (not (or (eolp) (looking-at "#") -;; (and (eq (car (nth 1 state)) ?{) -;; (looking-at "|")))))) - (or (not (eq ?/ c)) - (null (nth 0 (ruby-parse-region (or begin parse-start) (point))))) - (or (not (eq ?| (char-after (point)))) - (save-excursion - (or (eolp) (forward-char -1)) - (cond - ((search-backward "|" nil t) - (skip-chars-backward " \t\n") - (and (not (eolp)) - (progn - (forward-char -1) - (not (looking-at "{"))) - (progn - (forward-word -1) - (not (looking-at "do\\>[^_]"))))) - (t t)))) - (not (eq ?, c)) - (setq op-end t))))) - (setq indent - (cond - ((and - (null op-end) - (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))) - (eq (ruby-deep-indent-paren-p t) 'space) - (not (bobp))) - (save-excursion - (widen) - (goto-char (or begin parse-start)) - (skip-syntax-forward " ") - (current-column))) - ((car (nth 1 state)) indent) - (t - (+ indent ruby-indent-level)))))))) - indent))) + (goto-char ruby-indent-point) + (end-of-line) + (setq eol (point)) + (beginning-of-line) + (cond + ((and (not (ruby-deep-indent-paren-p paren + (and (cdr (nth 1 state)) + (1- (cdr (nth 1 state)))))) + (re-search-forward ruby-negative eol t)) + (and (not (eq ?_ (char-after (match-end 0)))) + (setq indent (- indent ruby-indent-level)))) + ((and + (save-excursion + (beginning-of-line) + (not (bobp))) + (or (ruby-deep-indent-paren-p t) + (null (car (nth 1 state))))) + ;; goto beginning of non-empty no-comment line + (let (end done) + (while (not done) + (skip-chars-backward " \t\n") + (setq end (point)) + (beginning-of-line) + (if (re-search-forward "^\\s *#" end t) + (beginning-of-line) + (setq done t)))) + (setq bol (point)) + (end-of-line) + ;; skip the comment at the end + (skip-chars-backward " \t") + (let (end (pos (point))) + (beginning-of-line) + (while (and (re-search-forward "#" pos t) + (setq end (1- (point))) + (or (ruby-special-char-p end) + (and (setq state (ruby-parse-region parse-start end)) + (nth 0 state)))) + (setq end nil)) + (goto-char (or end pos)) + (skip-chars-backward " \t") + (setq begin (if (and end (nth 0 state)) pos (cdr (nth 1 state)))) + (setq state (ruby-parse-region parse-start (point)))) + (or (bobp) (forward-char -1)) + (and + (or (and (looking-at ruby-symbol-re) + (skip-chars-backward ruby-symbol-chars) + (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>")) + (not (eq (point) (nth 3 state))) + (save-excursion + (goto-char (match-end 0)) + (not (looking-at "[a-z_]")))) + (and (looking-at ruby-operator-re) + (not (ruby-special-char-p)) + ;; operator at the end of line + (let ((c (char-after (point)))) + (and +;; (or (null begin) +;; (save-excursion +;; (goto-char begin) +;; (skip-chars-forward " \t") +;; (not (or (eolp) (looking-at "#") +;; (and (eq (car (nth 1 state)) ?{) +;; (looking-at "|")))))) + (or (not (eq ?/ c)) + (null (nth 0 (ruby-parse-region (or begin parse-start) (point))))) + (or (not (eq ?| (char-after (point)))) + (save-excursion + (or (eolp) (forward-char -1)) + (cond + ((search-backward "|" nil t) + (skip-chars-backward " \t\n") + (and (not (eolp)) + (progn + (forward-char -1) + (not (looking-at "{"))) + (progn + (forward-word -1) + (not (looking-at "do\\>[^_]"))))) + (t t)))) + (not (eq ?, c)) + (setq op-end t))))) + (setq indent + (cond + ((and + (null op-end) + (not (looking-at (concat "\\<\\(" ruby-block-hanging-re "\\)\\>"))) + (eq (ruby-deep-indent-paren-p t) 'space) + (not (bobp))) + (widen) + (goto-char (or begin parse-start)) + (skip-syntax-forward " ") + (current-column)) + ((car (nth 1 state)) indent) + (t + (+ indent ruby-indent-level)))))))) + (goto-char ruby-indent-point) + (beginning-of-line) + (skip-syntax-forward " ") + (if (looking-at "\\.[^.]\\|&\\.") + (+ indent ruby-indent-level) + indent)))) (defun ruby-electric-brace (arg) (interactive "P") - (insert-char last-command-char 1) + (insert-char last-command-event 1) (ruby-indent-line t) (delete-char -1) (self-insert-command (prefix-numeric-value arg))) @@ -746,10 +913,10 @@ The variable ruby-indent-level controls the amount of indentation. (defmacro defun-region-command (func args &rest body) (let ((intr (car body))) (when (featurep 'xemacs) - (if (stringp intr) (setq intr (cadr body))) - (and (eq (car intr) 'interactive) - (setq intr (cdr intr)) - (setcar intr (concat "_" (car intr))))) + (if (stringp intr) (setq intr (cadr body))) + (and (eq (car intr) 'interactive) + (setq intr (cdr intr)) + (setcar intr (concat "_" (car intr))))) (cons 'defun (cons func (cons args body)))))) (defun-region-command ruby-beginning-of-defun (&optional arg) @@ -757,55 +924,65 @@ The variable ruby-indent-level controls the amount of indentation. With argument, do this that many times. Returns t unless search stops due to end of buffer." (interactive "p") - (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\b") - nil 'move (or arg 1)) + (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\_>") + nil 'move (or arg 1)) (progn (beginning-of-line) t))) (defun ruby-beginning-of-indent () - (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b") - nil 'move) + (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\_>") + nil 'move) (progn - (beginning-of-line) - t))) + (beginning-of-line) + t))) (defun-region-command ruby-end-of-defun (&optional arg) "Move forward to next end of defun. An end of a defun is found by moving forward from the beginning of one." (interactive "p") (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\($\\|\\b[^_]\\)") - nil 'move (or arg 1)) + nil 'move (or arg 1)) (progn (beginning-of-line) t)) (forward-line 1)) (defun ruby-move-to-block (n) - (let (start pos done down) + (let (start pos done down (orig (point))) (setq start (ruby-calculate-indent)) (setq down (looking-at (if (< n 0) ruby-block-end-re - (concat "\\<\\(" ruby-block-beg-re "\\)\\>")))) + (concat "\\<\\(" ruby-block-beg-re "\\)\\>")))) (while (and (not done) (not (if (< n 0) (bobp) (eobp)))) (forward-line n) (cond ((looking-at "^\\s *$")) ((looking-at "^\\s *#")) ((and (> n 0) (looking-at "^=begin\\>")) - (re-search-forward "^=end\\>")) + (re-search-forward "^=end\\>")) ((and (< n 0) (looking-at "^=end\\>")) - (re-search-backward "^=begin\\>")) + (re-search-backward "^=begin\\>")) (t - (setq pos (current-indentation)) - (cond - ((< start pos) - (setq down t)) - ((and down (= pos start)) - (setq done t)) - ((> start pos) - (setq done t))))) + (setq pos (current-indentation)) + (cond + ((< start pos) + (setq down t)) + ((and down (= pos start)) + (setq done t)) + ((> start pos) + (setq done t))))) (if done - (save-excursion - (back-to-indentation) - (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>")) - (setq done nil)))))) - (back-to-indentation)) + (save-excursion + (back-to-indentation) + (if (looking-at (concat "\\<\\(" ruby-block-mid-re "\\)\\>")) + (setq done nil))))) + (back-to-indentation) + (when (< n 0) + (let ((eol (point-at-eol)) state next) + (if (< orig eol) (setq eol orig)) + (setq orig (point)) + (while (and (setq next (apply 'ruby-parse-partial eol state)) + (< (point) eol)) + (setq state next)) + (when (cdaadr state) + (goto-char (cdaadr state))) + (backward-word))))) (defun-region-command ruby-beginning-of-block (&optional arg) "Move backward to next beginning-of-block" @@ -823,37 +1000,38 @@ An end of a defun is found by moving forward from the beginning of one." (ruby-backward-sexp (- cnt)) (let ((i (or cnt 1))) (condition-case nil - (while (> i 0) - (skip-syntax-forward " ") - (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ") - (goto-char (match-end 0))) - ((progn - (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*") - (looking-at "\\s(")) - (goto-char (scan-sexps (point) 1))) - ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>")) - (not (eq (char-before (point)) ?.)) - (not (eq (char-before (point)) ?:))) - (ruby-end-of-block) - (forward-word 1)) - ((looking-at "\\(\\$\\|@@?\\)?\\sw") - (while (progn - (while (progn (forward-word 1) (looking-at "_"))) - (cond ((looking-at "::") (forward-char 2) t) - ((> (skip-chars-forward ".") 0)) - ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)") - (forward-char 1) nil))))) - ((let (state expr) - (while - (progn - (setq expr (or expr (ruby-expr-beg) - (looking-at "%\\sw?\\Sw\\|[\"'`/]"))) - (nth 1 (setq state (apply 'ruby-parse-partial nil state)))) - (setq expr t) - (skip-chars-forward "<")) - (not expr)))) - (setq i (1- i))) - ((error) (forward-word 1))) + (while (> i 0) + (skip-syntax-forward " ") + (if (looking-at ",\\s *") (goto-char (match-end 0))) + (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ") + (goto-char (match-end 0))) + ((progn + (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*") + (looking-at "\\s(")) + (goto-char (scan-sexps (point) 1))) + ((and (looking-at (concat "\\<\\(" ruby-block-beg-re "\\)\\>")) + (not (eq (char-before (point)) ?.)) + (not (eq (char-before (point)) ?:))) + (ruby-end-of-block) + (forward-word 1)) + ((looking-at "\\(\\$\\|@@?\\)?\\sw") + (while (progn + (while (progn (forward-word 1) (looking-at "_"))) + (cond ((looking-at "::") (forward-char 2) t) + ((> (skip-chars-forward ".") 0)) + ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)") + (forward-char 1) nil))))) + ((let (state expr) + (while + (progn + (setq expr (or expr (ruby-expr-beg) + (looking-at "%\\sw?\\Sw\\|[\"'`/]"))) + (nth 1 (setq state (apply 'ruby-parse-partial nil state)))) + (setq expr t) + (skip-chars-forward "<")) + (not expr)))) + (setq i (1- i))) + ((error) (forward-word 1))) i))) (defun-region-command ruby-backward-sexp (&optional cnt) @@ -862,41 +1040,41 @@ An end of a defun is found by moving forward from the beginning of one." (ruby-forward-sexp (- cnt)) (let ((i (or cnt 1))) (condition-case nil - (while (> i 0) - (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*") - (forward-char -1) - (cond ((looking-at "\\s)") - (goto-char (scan-sexps (1+ (point)) -1)) - (case (char-before) - (?% (forward-char -1)) - ('(?q ?Q ?w ?W ?r ?x) - (if (eq (char-before (1- (point))) ?%) (forward-char -2)))) - nil) - ((looking-at "\\s\"\\|\\\\\\S_") - (let ((c (char-to-string (char-before (match-end 0))))) - (while (and (search-backward c) - (oddp (skip-chars-backward "\\"))))) - nil) - ((looking-at "\\s.\\|\\s\\") - (if (ruby-special-char-p) (forward-char -1))) - ((looking-at "\\s(") nil) - (t - (forward-char 1) - (while (progn (forward-word -1) - (case (char-before) - (?_ t) - (?. (forward-char -1) t) - ((?$ ?@) - (forward-char -1) - (and (eq (char-before) (char-after)) (forward-char -1))) - (?: - (forward-char -1) - (eq (char-before) :))))) - (if (looking-at ruby-block-end-re) - (ruby-beginning-of-block)) - nil)) - (setq i (1- i))) - ((error))) + (while (> i 0) + (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*") + (forward-char -1) + (cond ((looking-at "\\s)") + (goto-char (scan-sexps (1+ (point)) -1)) + (case (char-before) + (?% (forward-char -1)) + ('(?q ?Q ?w ?W ?r ?x) + (if (eq (char-before (1- (point))) ?%) (forward-char -2)))) + nil) + ((looking-at "\\s\"\\|\\\\\\S_") + (let ((c (char-to-string (char-before (match-end 0))))) + (while (and (search-backward c) + (oddp (skip-chars-backward "\\"))))) + nil) + ((looking-at "\\s.\\|\\s\\") + (if (ruby-special-char-p) (forward-char -1))) + ((looking-at "\\s(") nil) + (t + (forward-char 1) + (while (progn (forward-word -1) + (case (char-before) + (?_ t) + (?. (forward-char -1) t) + ((?$ ?@) + (forward-char -1) + (and (eq (char-before) (char-after)) (forward-char -1))) + (?: + (forward-char -1) + (eq (char-before) :))))) + (if (looking-at ruby-block-end-re) + (ruby-beginning-of-block)) + nil)) + (setq i (1- i))) + ((error))) i))) (defun ruby-reindent-then-newline-and-indent () @@ -917,7 +1095,7 @@ An end of a defun is found by moving forward from the beginning of one." (while (re-search-forward "^\\([ \t]*\\)#" end t) (replace-match "\\1" nil nil) (save-excursion - (ruby-indent-line))))) + (ruby-indent-line))))) (defun ruby-insert-end () (interactive) @@ -942,18 +1120,18 @@ balanced expression is found." (let ((here (point-marker)) start top column (nest t)) (set-marker-insertion-type here t) (unwind-protect - (progn - (beginning-of-line) - (setq start (point) top (current-indentation)) - (while (and (not (eobp)) - (progn - (setq column (ruby-calculate-indent start)) - (cond ((> column top) - (setq nest t)) - ((and (= column top) nest) - (setq nest nil) t)))) - (ruby-indent-to column) - (beginning-of-line 2))) + (progn + (beginning-of-line) + (setq start (point) top (current-indentation)) + (while (and (not (eobp)) + (progn + (setq column (ruby-calculate-indent start)) + (cond ((> column top) + (setq nest t)) + ((and (= column top) nest) + (setq nest nil) t)))) + (ruby-indent-to column) + (beginning-of-line 2))) (goto-char here) (set-marker here nil)))) @@ -961,191 +1139,360 @@ balanced expression is found." "Return current method string." (condition-case nil (save-excursion - (let ((mlist nil) (indent 0)) - ;; get current method (or class/module) - (if (re-search-backward - (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+" - "\\(" - ;; \\. for class method - "\\(" ruby-symbol-re "\\|\\." "\\)" - "+\\)") - nil t) - (progn - (setq mlist (list (match-string 2))) - (goto-char (match-beginning 1)) - (setq indent (current-column)) - (beginning-of-line))) - ;; nest class/module - (while (and (> indent 0) - (re-search-backward - (concat - "^[ \t]*\\(class\\|module\\)[ \t]+" - "\\([A-Z]" ruby-symbol-re "+\\)") - nil t)) - (goto-char (match-beginning 1)) - (if (< (current-column) indent) - (progn - (setq mlist (cons (match-string 2) mlist)) - (setq indent (current-column)) - (beginning-of-line)))) - ;; generate string - (if (consp mlist) - (mapconcat (function identity) mlist "::") - nil))))) - -(cond - ((featurep 'font-lock) + (let (mname mlist (indent 0)) + ;; get current method (or class/module) + (if (re-search-backward + (concat "^[ \t]*\\(def\\|class\\|module\\)[ \t]+" + "\\(" + ;; \\. and :: for class method + "\\([A-Za-z_]" ruby-symbol-re "*\\|\\.\\|::" "\\)" + "+\\)") + nil t) + (progn + (setq mname (match-string 2)) + (unless (string-equal "def" (match-string 1)) + (setq mlist (list mname) mname nil)) + (goto-char (match-beginning 1)) + (setq indent (current-column)) + (beginning-of-line))) + ;; nest class/module + (while (and (> indent 0) + (re-search-backward + (concat + "^[ \t]*\\(class\\|module\\)[ \t]+" + "\\([A-Z]" ruby-symbol-re "*\\)") + nil t)) + (goto-char (match-beginning 1)) + (if (< (current-column) indent) + (progn + (setq mlist (cons (match-string 2) mlist)) + (setq indent (current-column)) + (beginning-of-line)))) + (when mname + (let ((mn (split-string mname "\\.\\|::"))) + (if (cdr mn) + (progn + (cond + ((string-equal "" (car mn)) + (setq mn (cdr mn) mlist nil)) + ((string-equal "self" (car mn)) + (setq mn (cdr mn))) + ((let ((ml (nreverse mlist))) + (while ml + (if (string-equal (car ml) (car mn)) + (setq mlist (nreverse (cdr ml)) ml nil)) + (or (setq ml (cdr ml)) (nreverse mlist)))))) + (if mlist + (setcdr (last mlist) mn) + (setq mlist mn)) + (setq mn (last mn 2)) + (setq mname (concat "." (cadr mn))) + (setcdr mn nil)) + (setq mname (concat "#" mname))))) + ;; generate string + (if (consp mlist) + (setq mlist (mapconcat (function identity) mlist "::"))) + (if mname + (if mlist (concat mlist mname) mname) + mlist))))) + +(defun ruby-brace-to-do-end () + (when (looking-at "{") + (let ((orig (point)) (end (progn (ruby-forward-sexp) (point))) + oneline (end (make-marker))) + (setq oneline (and (eolp) (<= (point-at-bol) orig))) + (when (eq (char-before) ?\}) + (delete-char -1) + (cond + (oneline + (insert "\n") + (set-marker end (point))) + ((eq (char-syntax (preceding-char)) ?w) + (insert " "))) + (insert "end") + (if (eq (char-syntax (following-char)) ?w) + (insert " ")) + (goto-char orig) + (delete-char 1) + (if (eq (char-syntax (preceding-char)) ?w) + (insert " ")) + (insert "do") + (when (looking-at "\\sw\\||") + (insert " ") + (backward-char)) + (when oneline + (setq orig (point)) + (when (cond + ((looking-at "\\s *|") + (goto-char (match-end 0)) + (and (search-forward "|" (point-at-eol) 'move) + (not (eolp)))) + (t)) + (while (progn + (insert "\n") + (ruby-forward-sexp) + (looking-at "\\s *;\\s *")) + (delete-char (- (match-end 0) (match-beginning 0)))) + (goto-char orig) + (beginning-of-line 2) + (indent-region (point) end)) + (goto-char orig)) + t)))) + +(defun ruby-do-end-to-brace () + (when (and (or (bolp) + (not (memq (char-syntax (preceding-char)) '(?w ?_)))) + (looking-at "\\<do\\(\\s \\|$\\)")) + (let ((orig (point)) (end (progn (ruby-forward-sexp) (point))) + first last) + (backward-char 3) + (when (looking-at ruby-block-end-re) + (delete-char 3) + (insert "}") + (setq last (and (eolp) + (progn (backward-char 1) + (skip-syntax-backward " ") + (bolp)) + (1- (point-at-eol -1)))) + (goto-char orig) + (delete-char 2) + (insert "{") + (setq orig (point)) + (when (and last (<= last (point)) + (not (search-forward "#" (setq first (point-at-eol)) t))) + (goto-char (- end 4)) + (end-of-line 0) + (if (looking-at "\n\\s *") + (delete-char (- (match-end 0) (match-beginning 0))) t) + (goto-char first) + (if (looking-at "\n\\s *") + (delete-char (- (match-end 0) (match-beginning 0))) t)) + (goto-char orig) + (if (looking-at "\\s +|") + (delete-char (- (match-end 0) (match-beginning 0) 1))) + t)))) + +(defun ruby-toggle-block () + (interactive) + (or (ruby-brace-to-do-end) + (ruby-do-end-to-brace))) + +(eval-when-compile + (if (featurep 'font-lock) + (defmacro eval-when-font-lock-available (&rest args) (cons 'progn args)) + (defmacro eval-when-font-lock-available (&rest args)))) + +(eval-when-compile + (if (featurep 'hilit19) + (defmacro eval-when-hilit19-available (&rest args) (cons 'progn args)) + (defmacro eval-when-hilit19-available (&rest args)))) + +(eval-when-font-lock-available (or (boundp 'font-lock-variable-name-face) (setq font-lock-variable-name-face font-lock-type-face)) - (setq ruby-font-lock-syntactic-keywords - '( - ;; #{ }, #$hoge, #@foo are not comments - ("\\(#\\)[{$@]" 1 (1 . nil)) - ;; the last $', $", $` in the respective string is not variable - ;; the last ?', ?", ?` in the respective string is not ascii code - ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)" - (2 (7 . nil)) - (4 (7 . nil))) - ;; $' $" $` .... are variables - ;; ?' ?" ?` are ascii codes - ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil)) - ;; regexps - ("\\(^\\|[=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" - (4 (7 . ?/)) - (6 (7 . ?/))) - ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) - ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)))) - - (cond ((featurep 'xemacs) - (put 'ruby-mode 'font-lock-defaults - '((ruby-font-lock-keywords) - nil nil nil - beginning-of-line - (font-lock-syntactic-keywords - . ruby-font-lock-syntactic-keywords)))) - (t - (add-hook 'ruby-mode-hook - '(lambda () - (make-local-variable 'font-lock-defaults) - (make-local-variable 'font-lock-keywords) - (make-local-variable 'font-lock-syntax-table) - (make-local-variable 'font-lock-syntactic-keywords) - (setq font-lock-defaults '((ruby-font-lock-keywords) nil nil)) - (setq font-lock-keywords ruby-font-lock-keywords) - (setq font-lock-syntax-table ruby-font-lock-syntax-table) - (setq font-lock-syntactic-keywords ruby-font-lock-syntactic-keywords))))) + (defconst ruby-font-lock-syntactic-keywords + `( + ;; #{ }, #$hoge, #@foo are not comments + ("\\(#\\)[{$@]" 1 (1 . nil)) + ;; the last $', $", $` in the respective string is not variable + ;; the last ?', ?", ?` in the respective string is not ascii code + ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*?\\\\?[?$]\\(\\2\\)" + (2 (7 . nil)) + (4 (7 . nil))) + ;; $' $" $` .... are variables + ;; ?' ?" ?` are ascii codes + ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil)) + ;; regexps + ("\\(^\\|[[{|=(,~?:;<>]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)" + (4 (7 . ?/)) + (6 (7 . ?/))) + ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) + ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)) + (,(concat ruby-here-doc-beg-re ".*\\(\n\\)") + ,(+ 1 (regexp-opt-depth ruby-here-doc-beg-re)) + (ruby-here-doc-beg-syntax)) + (,ruby-here-doc-end-re 3 (ruby-here-doc-end-syntax)))) + + (unless (functionp 'syntax-ppss) + (defun syntax-ppss (&optional pos) + (parse-partial-sexp (point-min) (or pos (point))))) + + (defun ruby-in-ppss-context-p (context &optional ppss) + (let ((ppss (or ppss (syntax-ppss (point))))) + (if (cond + ((eq context 'anything) + (or (nth 3 ppss) + (nth 4 ppss))) + ((eq context 'string) + (nth 3 ppss)) + ((eq context 'heredoc) + (and (nth 3 ppss) + ;; If it's generic string, it's a heredoc and we don't care + ;; See `parse-partial-sexp' + (not (numberp (nth 3 ppss))))) + ((eq context 'non-heredoc) + (and (ruby-in-ppss-context-p 'anything) + (not (ruby-in-ppss-context-p 'heredoc)))) + ((eq context 'comment) + (nth 4 ppss)) + (t + (error (concat + "Internal error on `ruby-in-ppss-context-p': " + "context name `" (symbol-name context) "' is unknown")))) + t))) + + (defun ruby-in-here-doc-p () + (save-excursion + (let ((old-point (point)) (case-fold-search nil)) + (beginning-of-line) + (catch 'found-beg + (while (and (re-search-backward ruby-here-doc-beg-re nil t) + (not (ruby-singleton-class-p))) + (if (not (or (ruby-in-ppss-context-p 'anything) + (ruby-here-doc-find-end old-point))) + (throw 'found-beg t))))))) + + (defun ruby-here-doc-find-end (&optional limit) + "Expects the point to be on a line with one or more heredoc +openers. Returns the buffer position at which all heredocs on the +line are terminated, or nil if they aren't terminated before the +buffer position `limit' or the end of the buffer." + (save-excursion + (beginning-of-line) + (catch 'done + (let ((eol (save-excursion (end-of-line) (point))) + (case-fold-search nil) + ;; Fake match data such that (match-end 0) is at eol + (end-match-data (progn (looking-at ".*$") (match-data))) + beg-match-data end-re) + (while (re-search-forward ruby-here-doc-beg-re eol t) + (setq beg-match-data (match-data)) + (setq end-re (ruby-here-doc-end-match)) + + (set-match-data end-match-data) + (goto-char (match-end 0)) + (unless (re-search-forward end-re limit t) (throw 'done nil)) + (setq end-match-data (match-data)) + + (set-match-data beg-match-data) + (goto-char (match-end 0))) + (set-match-data end-match-data) + (goto-char (match-end 0)) + (point))))) + + (defun ruby-here-doc-beg-syntax () + (save-excursion + (goto-char (match-beginning 0)) + (unless (or (ruby-in-ppss-context-p 'non-heredoc) + (ruby-in-here-doc-p)) + (string-to-syntax "|")))) + + (defun ruby-here-doc-end-syntax () + (let ((pss (syntax-ppss)) (case-fold-search nil)) + (when (ruby-in-ppss-context-p 'heredoc pss) + (save-excursion + (goto-char (nth 8 pss)) ; Go to the beginning of heredoc. + (let ((eol (point))) + (beginning-of-line) + (if (and (re-search-forward (ruby-here-doc-beg-match) eol t) ; If there is a heredoc that matches this line... + (not (ruby-in-ppss-context-p 'anything)) ; And that's not inside a heredoc/string/comment... + (progn (goto-char (match-end 0)) ; And it's the last heredoc on its line... + (not (re-search-forward ruby-here-doc-beg-re eol t)))) + (string-to-syntax "|"))))))) + + (eval-when-compile + (put 'ruby-mode 'font-lock-defaults + '((ruby-font-lock-keywords) + nil nil nil + beginning-of-line + (font-lock-syntactic-keywords + . ruby-font-lock-syntactic-keywords)))) (defun ruby-font-lock-docs (limit) (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t) - (let (beg) - (beginning-of-line) - (setq beg (point)) - (forward-line 1) - (if (re-search-forward "^=end\\(\\s \\|$\\)" limit t) - (progn - (set-match-data (list beg (point))) - t))))) + (let (beg) + (beginning-of-line) + (setq beg (point)) + (forward-line 1) + (if (re-search-forward "^=end\\(\\s \\|$\\)" limit t) + (progn + (set-match-data (list beg (point))) + t))))) (defun ruby-font-lock-maybe-docs (limit) (let (beg) (save-excursion - (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t) - (string= (match-string 1) "begin")) - (progn - (beginning-of-line) - (setq beg (point))))) + (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t) + (string= (match-string 1) "begin")) + (progn + (beginning-of-line) + (setq beg (point))))) (if (and beg (and (re-search-forward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t) - (string= (match-string 1) "end"))) - (progn - (set-match-data (list beg (point))) - t) - nil))) + (string= (match-string 1) "end"))) + (progn + (set-match-data (list beg (point))) + t) + nil))) (defvar ruby-font-lock-syntax-table (let* ((tbl (copy-syntax-table ruby-mode-syntax-table))) (modify-syntax-entry ?_ "w" tbl) tbl)) - (defun ruby-font-lock-here-docs (limit) - (if (re-search-forward ruby-here-doc-beg-re limit t) - (let (beg) - (beginning-of-line) - (forward-line) - (setq beg (point)) - (if (re-search-forward (ruby-here-doc-end-match) nil t) - (progn - (set-match-data (list beg (point))) - t))))) - - (defun ruby-font-lock-maybe-here-docs (limit) - (let (beg) - (save-excursion - (if (re-search-backward ruby-here-doc-beg-re nil t) - (progn - (beginning-of-line) - (forward-line) - (setq beg (point))))) - (if (and beg - (let ((end-match (ruby-here-doc-end-match))) - (and (not (re-search-backward end-match beg t)) - (re-search-forward end-match nil t)))) - (progn - (set-match-data (list beg (point))) - t) - nil))) - - (defvar ruby-font-lock-keywords + (defconst ruby-font-lock-keywords (list ;; functions '("^\\s *def\\s +\\([^( \t\n]+\\)" 1 font-lock-function-name-face) ;; keywords (cons (concat - "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(defined\\?\\|\\(" - (mapconcat - 'identity - '("alias" - "and" - "begin" - "break" - "case" - "catch" - "class" - "def" - "do" - "elsif" - "else" - "fail" - "ensure" - "for" - "end" - "if" - "in" - "module" - "next" - "not" - "or" - "raise" - "redo" - "rescue" - "retry" - "return" - "then" - "throw" - "super" - "unless" - "undef" - "until" - "when" - "while" - "yield" - ) - "\\|") - "\\)\\>\\)") - 2) + "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(defined\\?\\|" + (regexp-opt + '("alias" + "and" + "begin" + "break" + "case" + "catch" + "class" + "def" + "do" + "elsif" + "else" + "fail" + "ensure" + "for" + "end" + "if" + "in" + "module" + "next" + "not" + "or" + "raise" + "redo" + "rescue" + "retry" + "return" + "then" + "throw" + "super" + "unless" + "undef" + "until" + "when" + "while" + "yield" + ) + t) + "\\)" + ruby-keyword-end-re) + 2) + ;; here-doc beginnings + (list ruby-here-doc-beg-re 0 'font-lock-string-face) ;; variables - '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>" + '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\_<\\(nil\\|self\\|true\\|false\\)\\>" 2 font-lock-variable-name-face) ;; variables '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W" @@ -1157,22 +1504,16 @@ balanced expression is found." 0 font-lock-comment-face t) '(ruby-font-lock-maybe-docs 0 font-lock-comment-face t) - ;; "here" document - '(ruby-font-lock-here-docs - 0 font-lock-string-face t) - '(ruby-font-lock-maybe-here-docs - 0 font-lock-string-face t) - `(,ruby-here-doc-beg-re - 0 font-lock-string-face t) ;; general delimited string '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" (2 font-lock-string-face)) ;; constants - '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)" + '("\\(^\\|[^_]\\)\\_<\\([A-Z]+\\(\\w\\|_\\)*\\)" 2 font-lock-type-face) ;; symbols - '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)" + '("\\(^\\|[^:]\\)\\(:\\([-+~]@?\\|[/%&|^`]\\|\\*\\*?\\|<\\(<\\|=>?\\)?\\|>[>=]?\\|===?\\|=~\\|![~=]?\\|\\[\\]=?\\|\\(\\w\\|_\\)+\\([!?=]\\|\\b_*\\)\\|#{[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\)\\)" 2 font-lock-reference-face) + '("\\(^\\s *\\|[\[\{\(,]\\s *\\|\\sw\\s +\\)\\(\\(\\sw\\|_\\)+\\):[^:]" 2 font-lock-reference-face) ;; expression expansion '("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)" 0 font-lock-variable-name-face t) @@ -1182,7 +1523,7 @@ balanced expression is found." ) "*Additional expressions to highlight in ruby mode.")) - ((featurep 'hilit19) +(eval-when-hilit19-available (hilit-set-mode-patterns 'ruby-mode '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string) @@ -1199,7 +1540,45 @@ balanced expression is found." ("\\$\\(.\\|\\sw+\\)" nil type) ("[$@].[a-zA-Z_0-9]*" nil struct) ("^__END__" nil label)))) - ) +;;;###autoload +(defun ruby-mode () + "Major mode for editing ruby scripts. +\\[ruby-indent-command] properly indents subexpressions of multi-line +class, module, def, if, while, for, do, and case statements, taking +nesting into account. + +The variable ruby-indent-level controls the amount of indentation. +\\{ruby-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map ruby-mode-map) + (setq mode-name "Ruby") + (setq major-mode 'ruby-mode) + (ruby-mode-variables) + + (make-local-variable 'imenu-create-index-function) + (setq imenu-create-index-function 'ruby-imenu-create-index) + + (make-local-variable 'add-log-current-defun-function) + (setq add-log-current-defun-function 'ruby-add-log-current-method) + + (add-hook + (cond ((boundp 'before-save-hook) + (make-local-variable 'before-save-hook) + 'before-save-hook) + ((boundp 'write-contents-functions) 'write-contents-functions) + ((boundp 'write-contents-hooks) 'write-contents-hooks)) + 'ruby-mode-set-encoding) + + (set (make-local-variable 'font-lock-defaults) '((ruby-font-lock-keywords) nil nil)) + (set (make-local-variable 'font-lock-keywords) ruby-font-lock-keywords) + (set (make-local-variable 'font-lock-syntax-table) ruby-font-lock-syntax-table) + (set (make-local-variable 'font-lock-syntactic-keywords) ruby-font-lock-syntactic-keywords) + + (if (fboundp 'run-mode-hooks) + (run-mode-hooks 'ruby-mode-hook) + (run-hooks 'ruby-mode-hook))) + (provide 'ruby-mode) diff --git a/misc/ruby-style.el b/misc/ruby-style.el new file mode 100644 index 0000000000..b8593b202a --- /dev/null +++ b/misc/ruby-style.el @@ -0,0 +1,81 @@ +;;; -*- emacs-lisp -*- +;;; +;;; ruby-style.el - +;;; +;;; C/C++ mode style for Ruby. +;;; +;;; $Author$ +;;; created at: Thu Apr 26 13:54:01 JST 2007 +;;; +;;; Put this file under a directory contained in ``load-path'', and +;;; then load it. +;;; To switch to the "ruby" style automatically if it looks like a +;;; source file of ruby, add ruby-style-c-mode to c-mode-hook: +;;; +;;; (require 'ruby-style) +;;; (add-hook 'c-mode-hook 'ruby-style-c-mode) +;;; (add-hook 'c++-mode-hook 'ruby-style-c-mode) +;;; +;;; Customize the c-default-style variable to set the default style +;;; for each CC major mode. + +(defconst ruby-style-revision "$Revision$" + "Ruby style revision string.") + +(defconst ruby-style-version + (and + (string-match "[0-9.]+" ruby-style-revision) + (substring ruby-style-revision (match-beginning 0) (match-end 0))) + "Ruby style version number.") + +(defun ruby-style-case-indent (x) + (save-excursion + (back-to-indentation) + (unless (progn (backward-up-list) (back-to-indentation) + (> (point) (cdr x))) + (goto-char (cdr x)) + (if (looking-at "\\<case\\|default\\>") '*)))) + +(defun ruby-style-label-indent (x) + (save-excursion + (back-to-indentation) + (unless (progn (backward-up-list) (back-to-indentation) + (>= (point) (cdr x))) + (goto-char (cdr x)) + (condition-case () + (progn + (backward-up-list) + (backward-sexp 2) + (if (looking-at "\\<switch\\>") '/)) + (error))))) + +(require 'cc-styles) +(c-add-style + "ruby" + '("bsd" + (c-basic-offset . 4) + (tab-width . 8) + (indent-tabs-mode . t) + (setq show-trailing-whitespace t) + (c-offsets-alist + (case-label . *) + (label . (ruby-style-label-indent *)) + (statement-case-intro . *) + (statement-case-open . *) + (statement-block-intro . (ruby-style-case-indent +)) + (access-label /) + ))) + +;;;###autoload +(defun ruby-style-c-mode () + (interactive) + (if (or (let ((name (buffer-file-name))) (and name (string-match "/ruby\\>" name))) + (save-excursion + (goto-char (point-min)) + (let ((head (progn (forward-line 100) (point))) + (case-fold-search nil)) + (goto-char (point-min)) + (re-search-forward "Copyright (C) .* Yukihiro Matsumoto" head t)))) + (c-set-style "ruby"))) + +(provide 'ruby-style) diff --git a/misc/rubydb3x.el b/misc/rubydb3x.el index 98ce1a1978..9d6bc57d5a 100644 --- a/misc/rubydb3x.el +++ b/misc/rubydb3x.el @@ -28,20 +28,20 @@ ;; Process all the complete markers in this chunk. (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n" - gud-rubydb-marker-acc) + gud-rubydb-marker-acc) (setq ;; Extract the frame position from the marker. gud-last-frame (cons (substring gud-rubydb-marker-acc (match-beginning 1) (match-end 1)) - (string-to-int (substring gud-rubydb-marker-acc - (match-beginning 2) - (match-end 2)))) + (string-to-int (substring gud-rubydb-marker-acc + (match-beginning 2) + (match-end 2)))) ;; Append any text before the marker to the output we're going ;; to return - we don't include the marker in this text. output (concat output - (substring gud-rubydb-marker-acc 0 (match-beginning 0))) + (substring gud-rubydb-marker-acc 0 (match-beginning 0))) ;; Set the accumulator to the remaining text. gud-rubydb-marker-acc (substring gud-rubydb-marker-acc (match-end 0)))) @@ -52,17 +52,17 @@ ;; know the full marker regexp above failed, it's pretty simple to ;; test for marker starts. (if (string-match "\032.*\\'" gud-rubydb-marker-acc) - (progn - ;; Everything before the potential marker start can be output. - (setq output (concat output (substring gud-rubydb-marker-acc - 0 (match-beginning 0)))) + (progn + ;; Everything before the potential marker start can be output. + (setq output (concat output (substring gud-rubydb-marker-acc + 0 (match-beginning 0)))) - ;; Everything after, we save, to combine with later input. - (setq gud-rubydb-marker-acc - (substring gud-rubydb-marker-acc (match-beginning 0)))) + ;; Everything after, we save, to combine with later input. + (setq gud-rubydb-marker-acc + (substring gud-rubydb-marker-acc (match-beginning 0)))) (setq output (concat output gud-rubydb-marker-acc) - gud-rubydb-marker-acc "")) + gud-rubydb-marker-acc "")) output)) @@ -83,18 +83,18 @@ The directory containing FILE becomes the initial working directory and source-file directory for your debugger." (interactive (list (read-from-minibuffer "Run rubydb (like this): " - (if (consp gud-rubydb-history) - (car gud-rubydb-history) - (concat rubydb-command-name " ")) - nil nil - '(gud-rubydb-history . 1)))) + (if (consp gud-rubydb-history) + (car gud-rubydb-history) + (concat rubydb-command-name " ")) + nil nil + '(gud-rubydb-history . 1)))) (if (not (fboundp 'gud-overload-functions)) (gud-common-init command-line 'gud-rubydb-massage-args - 'gud-rubydb-marker-filter 'gud-rubydb-find-file) + 'gud-rubydb-marker-filter 'gud-rubydb-find-file) (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args) - (gud-marker-filter . gud-rubydb-marker-filter) - (gud-find-file . gud-rubydb-find-file))) + (gud-marker-filter . gud-rubydb-marker-filter) + (gud-find-file . gud-rubydb-find-file))) (gud-common-init command-line rubydb-command-name)) (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.") diff --git a/misc/test_lldb_cruby.rb b/misc/test_lldb_cruby.rb new file mode 100644 index 0000000000..4d1cc499f5 --- /dev/null +++ b/misc/test_lldb_cruby.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +require 'open3' +require 'tempfile' +require 'test/unit' + +class TestLLDBInit < Test::Unit::TestCase + def assert_rp(expr, pattern, message=nil) + Tempfile.create('lldb') do |tf| + tf.puts <<eom +target create ./miniruby +command script import -r misc/lldb_cruby.py +b rb_p +run -e'p #{expr}' +rp obj +eom + tf.flush + o, s = Open3.capture2('lldb', '-b', '-s', tf.path) + assert_true s.success?, message + assert_match /^\(lldb\) rp obj\n#{pattern}/, o, message + end + end + + def test_rp_object + assert_rp 'Object.new', 'T_OBJECT' + end + + def test_rp_symbol + assert_rp ':abcde', /immediate\(\h+\)/ + end + + def test_rp_string + assert_rp '"abc"', /\(char \[\d+\]\) ary = "abc"/ + assert_rp "\"\u3042\"", /\(char \[\d+\]\) ary = "\u3042"/ + assert_rp '"' + "\u3042"*10 + '"', /\(RString::\(anonymous struct\)\) heap = \{/ + end +end |
