rum 0.0.1-universal-darwin-10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/CHANGELOG +3 -0
  2. data/README +30 -0
  3. data/Rakefile +134 -0
  4. data/bin/rum-client +124 -0
  5. data/doc/basic.rb +10 -0
  6. data/doc/doc.html +602 -0
  7. data/doc/example.rb +59 -0
  8. data/doc/reference.rb +415 -0
  9. data/doc/resources/bg.png +0 -0
  10. data/doc/resources/bottom.png +0 -0
  11. data/doc/resources/build.rb +235 -0
  12. data/doc/resources/doc.haml +167 -0
  13. data/doc/resources/emacs-auto-completion.png +0 -0
  14. data/doc/resources/flash.png +0 -0
  15. data/doc/resources/highlight.css +94 -0
  16. data/doc/resources/intro.rb +17 -0
  17. data/doc/resources/left.png +0 -0
  18. data/doc/resources/logo.png +0 -0
  19. data/doc/resources/screen.css +420 -0
  20. data/doc/resources/screenshot.png +0 -0
  21. data/doc/resources/top.png +0 -0
  22. data/ext/mac/keyboard_hook/English.lproj/InfoPlist.strings +0 -0
  23. data/ext/mac/keyboard_hook/Event.h +17 -0
  24. data/ext/mac/keyboard_hook/Event.m +18 -0
  25. data/ext/mac/keyboard_hook/EventTap.h +11 -0
  26. data/ext/mac/keyboard_hook/EventTap.m +77 -0
  27. data/ext/mac/keyboard_hook/Info.plist +26 -0
  28. data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/TemplateIcon.icns +0 -0
  29. data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +323 -0
  30. data/ext/mac/keyboard_hook/KeyboardHook_Prefix.pch +7 -0
  31. data/ext/mac/keyboard_hook/version.plist +16 -0
  32. data/ext/windows/keyboard_hook/extconf.rb +2 -0
  33. data/ext/windows/keyboard_hook/keyboard_hook.c +126 -0
  34. data/ext/windows/system/autohotkey_stuff.c +255 -0
  35. data/ext/windows/system/autohotkey_stuff.h +2 -0
  36. data/ext/windows/system/clipboard_watcher.c +58 -0
  37. data/ext/windows/system/clipboard_watcher.h +2 -0
  38. data/ext/windows/system/extconf.rb +3 -0
  39. data/ext/windows/system/input_box.c +239 -0
  40. data/ext/windows/system/input_box.h +4 -0
  41. data/ext/windows/system/system.c +273 -0
  42. data/lib/rum.rb +4 -0
  43. data/lib/rum/apps.rb +4 -0
  44. data/lib/rum/barrel.rb +157 -0
  45. data/lib/rum/barrel/emacs.rb +44 -0
  46. data/lib/rum/barrel/emacs_client.rb +74 -0
  47. data/lib/rum/core.rb +125 -0
  48. data/lib/rum/dsl.rb +109 -0
  49. data/lib/rum/gui.rb +93 -0
  50. data/lib/rum/help.rb +128 -0
  51. data/lib/rum/hotkey_core.rb +479 -0
  52. data/lib/rum/mac.rb +18 -0
  53. data/lib/rum/mac/app.rb +4 -0
  54. data/lib/rum/mac/apps.rb +19 -0
  55. data/lib/rum/mac/gui.rb +26 -0
  56. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Info.plist +28 -0
  57. data/lib/rum/mac/gui/CocoaDialog.app/Contents/MacOS/CocoaDialog +0 -0
  58. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Info.plist +28 -0
  59. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/InfoPlist.strings +0 -0
  60. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/classes.nib +51 -0
  61. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/info.nib +16 -0
  62. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/keyedobjects.nib +0 -0
  63. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/classes.nib +7 -0
  64. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib +21 -0
  65. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib.orig +21 -0
  66. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib +0 -0
  67. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib.orig +0 -0
  68. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/classes.nib +27 -0
  69. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/info.nib +16 -0
  70. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/keyedobjects.nib +0 -0
  71. data/lib/rum/mac/gui/Growl.framework/Growl +0 -0
  72. data/lib/rum/mac/gui/Growl.framework/Versions/A/Growl +0 -0
  73. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/Growl.h +6 -0
  74. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge-Carbon.h +780 -0
  75. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +575 -0
  76. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlDefines.h +348 -0
  77. data/lib/rum/mac/gui/Growl.framework/Versions/A/Resources/Info.plist +24 -0
  78. data/lib/rum/mac/gui/growl.rb +54 -0
  79. data/lib/rum/mac/irb/completion.rb +207 -0
  80. data/lib/rum/mac/keyboard_hook.rb +73 -0
  81. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/KeyboardHook +0 -0
  82. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/KeyboardHook +0 -0
  83. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/English.lproj/InfoPlist.strings +0 -0
  84. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/Info.plist +22 -0
  85. data/lib/rum/mac/layouts.rb +146 -0
  86. data/lib/rum/mac/system.rb +45 -0
  87. data/lib/rum/remote.rb +48 -0
  88. data/lib/rum/server.rb +92 -0
  89. data/lib/rum/windows.rb +23 -0
  90. data/lib/rum/windows/app.rb +72 -0
  91. data/lib/rum/windows/apps.rb +25 -0
  92. data/lib/rum/windows/gui.rb +116 -0
  93. data/lib/rum/windows/keyboard.rb +80 -0
  94. data/lib/rum/windows/keyboard_hook.rb +20 -0
  95. data/lib/rum/windows/layouts.rb +232 -0
  96. data/lib/rum/windows/system.rb +310 -0
  97. data/lib/rum/windows/system_foreign_functions.rb +129 -0
  98. data/rum.gemspec +14 -0
  99. metadata +166 -0
@@ -0,0 +1,4 @@
1
+ require 'rum/core'
2
+ require 'rum/dsl'
3
+
4
+ puts "Rum loaded."
@@ -0,0 +1,4 @@
1
+ case Platform
2
+ when :mac then require 'rum/mac/apps'
3
+ when :windows then require 'rum/windows/apps'
4
+ end
@@ -0,0 +1,157 @@
1
+ # encoding: utf-8
2
+
3
+ module Rum
4
+ class Path
5
+ def self.contents dir
6
+ paths = []
7
+ dir = File.join(dir, '')
8
+ dir_dots = /(?:^|\/)\.\.?$/ # /.. or /.
9
+ Dir.glob(dir + '**/*', File::FNM_DOTMATCH).each do |path|
10
+ sub_path = path[(dir.length)..-1].encode(Encoding::UTF_8, \
11
+ Encoding::ISO_8859_1)
12
+ paths << sub_path unless sub_path =~ dir_dots
13
+ end
14
+ paths
15
+ end
16
+
17
+ def self.select dir
18
+ path = Gui.choose(nil, contents(dir))
19
+ (File.join(dir, path)) if path
20
+ end
21
+
22
+ def self.run path
23
+ start path
24
+ end
25
+
26
+ def self.select_and_run dir
27
+ path = select(dir)
28
+ run path if path
29
+ end
30
+
31
+ def self.sanitize path
32
+ path.gsub(/[\/\\|?*><":]/, '-')
33
+ end
34
+
35
+ def self.normalize path
36
+ path.gsub('\\', '/')
37
+ end
38
+ end
39
+
40
+ module AppDir
41
+ class << self
42
+ attr_accessor :base_dir
43
+
44
+ def get(exe)
45
+ File.join(@base_dir, exe, '')
46
+ end
47
+
48
+ def get_or_create(exe)
49
+ dir = get(exe)
50
+ if File.exists? dir
51
+ dir
52
+ else
53
+ prompt = "App-Dir für #{exe.capitalize} anlegen?"
54
+ if "Erzeugen" == Gui.choose(prompt, ["Erzeugen", "Nicht erzeugen"])
55
+ Dir.mkdir dir
56
+ dir
57
+ end
58
+ end
59
+ end
60
+
61
+ def current
62
+ get_or_create(active_window.exe_name)
63
+ end
64
+
65
+ def visit
66
+ dir = current
67
+ Dopus.go dir if dir
68
+ end
69
+
70
+ def select
71
+ dir = current
72
+ Path.select_and_run dir if dir
73
+ end
74
+ end
75
+ end
76
+
77
+ module Commands
78
+ class Command
79
+ def initialize(name, proc)
80
+ @name = name
81
+ @proc = proc
82
+ end
83
+
84
+ def to_s
85
+ @name
86
+ end
87
+
88
+ def run
89
+ @proc.call
90
+ end
91
+ end
92
+
93
+ class << self
94
+ attr_accessor :default_tag
95
+ attr_accessor :commands
96
+ Commands.commands = {}
97
+
98
+ def command(name, *args, &block)
99
+ args = args.first
100
+ tag = args[:tag] if args
101
+ tags = []
102
+ tags << tag if tag
103
+ tags << default_tag
104
+ tags.uniq!
105
+
106
+ if args and (hotkey = args[:hotkey])
107
+ apps = tags.select { |tag| tag.is_a? App }
108
+ apps.each { |app| hotkey.do(app, &block) }
109
+ end
110
+
111
+ cmd = Command.new(name, block)
112
+ tags.each do |tag|
113
+ commands_for_tag = (commands[tag] ||= {})
114
+ commands_for_tag[name] = cmd
115
+ end
116
+ end
117
+
118
+ def [] (tag=nil)
119
+ if (cmds = @commands[tag])
120
+ cmds.values
121
+ else
122
+ []
123
+ end
124
+ end
125
+
126
+ def select(tag=nil)
127
+ cmd = Gui.choose(nil, self[tag])
128
+ cmd.run if cmd
129
+ end
130
+
131
+ def for_active_window
132
+ cmds = []
133
+ exe = active_window.exe_name
134
+ app = App.for_exe(exe)
135
+ cmds.concat self[app] if app
136
+ if (dir = AppDir.get(exe))
137
+ cmds.concat Path.contents(dir)
138
+ end
139
+ if (chosen = Gui.choose(nil, cmds))
140
+ case chosen
141
+ when String
142
+ Path.run(dir + chosen)
143
+ else
144
+ chosen.run
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def Commands(tag=nil, &block)
152
+ Commands.default_tag = tag
153
+ Commands.instance_eval(&block)
154
+ ensure
155
+ Commands.default_tag = nil
156
+ end
157
+ end
@@ -0,0 +1,44 @@
1
+ require 'rum/barrel/emacs_client'
2
+
3
+ class << Emacs
4
+ attr_accessor :client, :eval_in_user_buffer
5
+
6
+ Emacs.client = EmacsClient.new
7
+
8
+ def eval(elisp)
9
+ # MacRuby hack
10
+ # @client.eval fails
11
+ elisp = "(with-current-buffer (window-buffer) #{elisp})" if @eval_in_user_buffer
12
+ Emacs.client.eval(elisp)
13
+ end
14
+
15
+ def funcall(*args)
16
+ eval("(#{args.join(' ')})")
17
+ end
18
+
19
+ Quoting = [["\n", '\n'],
20
+ ['"', '\\"']]
21
+
22
+ def quote(str)
23
+ str.gsub!('\\', '\\\\\\\\')
24
+ Quoting.each { |from, to| str.gsub!(from, to) }
25
+ '"' << str << '"'
26
+ end
27
+
28
+ def unquote(str)
29
+ Quoting.reverse.each { |from, to| str.gsub!(to, from) }
30
+ str.gsub('\\\\', '\\').chomp[1..-2]
31
+ end
32
+
33
+ def find_file(path, line)
34
+ line = if line.is_a? Fixnum
35
+ "(goto-line #{line})"
36
+ end
37
+ eval("(progn (find-file \"#{path}\")#{line})")
38
+ end
39
+
40
+ def open_file(path, line=nil)
41
+ Emacs.activate
42
+ Emacs.find_file(path, line)
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ require 'socket'
2
+
3
+ # Ruby implementation of
4
+ # emacs_source/lib-src/emacsclient.c
5
+
6
+ class EmacsClient
7
+ def eval(elisp)
8
+ socket = connect
9
+ socket.puts "-eval #{quote(elisp)}"
10
+ result = unquote(socket.read)
11
+ socket.close
12
+ format(result)
13
+ end
14
+
15
+ def format(str)
16
+ str[/.*? (.*)/, 1]
17
+ end
18
+
19
+ def quote str
20
+ r = str.gsub(/&|^-/, '&\&').gsub("\n", '&n').gsub(' ', '&_')
21
+ end
22
+
23
+ def unquote str
24
+ str.gsub(/&(.)/) do
25
+ case $1
26
+ when 'n'; "\n"
27
+ when '_'; ' '
28
+ else $1
29
+ end
30
+ end
31
+ end
32
+
33
+ if RUBY_PLATFORM =~ /mswin|mingw/
34
+ attr_accessor :ip, :port, :auth_string
35
+
36
+ def initialize
37
+ @server_file = File.join(ENV['HOME'], '.emacs.d', 'server', 'server')
38
+ read_config
39
+ end
40
+
41
+ def read_config
42
+ @server_active = File.exists? @server_file
43
+ return unless @server_active
44
+ lines = File.readlines(@server_file)
45
+ @ip, @port = lines.first.match(/(.*?):(\d+)/).captures
46
+ @auth_string = lines.last
47
+ end
48
+
49
+ def create_socket
50
+ return unless @server_active
51
+ begin
52
+ socket = TCPSocket.open(@ip, @port)
53
+ socket.write "-auth #@auth_string "
54
+ socket
55
+ rescue SystemCallError
56
+ end
57
+ end
58
+
59
+ def connect
60
+ create_socket or (read_config and create_socket) \
61
+ or raise "Can't connect to Emacs Server."
62
+ end
63
+ else # Unix
64
+ def initialize
65
+ @socket_path = File.join(ENV['TMPDIR'], "emacs#{Process::Sys.geteuid}", 'server')
66
+ end
67
+
68
+ def connect
69
+ UNIXSocket.open(@socket_path)
70
+ rescue SystemCallError => error
71
+ raise "Can't connect to Emacs Server\n" << error.message
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,125 @@
1
+ require 'rum/hotkey_core'
2
+ require 'rum/help'
3
+ require 'rum/gui'
4
+ require 'rum/barrel'
5
+ require 'thread'
6
+
7
+ case RUBY_DESCRIPTION
8
+ when /mswin|mingw/ then require 'rum/windows'
9
+ when /MacRuby/ then require 'rum/mac'
10
+ else raise "Platform not yet supported: #{RUBY_PLATFORM}"
11
+ end
12
+
13
+ Encoding.default_external = Encoding::UTF_8
14
+
15
+ module Rum
16
+ autoload :Server, 'rum/server'
17
+
18
+ class << self
19
+ attr_writer :layout
20
+ attr_reader :hotkey_set, :hotkey_processor, \
21
+ :work_queue, :worker_thread
22
+
23
+ def layout
24
+ @layout ||= Layouts.default_layout
25
+ end
26
+
27
+ def setup
28
+ return if setup_completed?
29
+ @hotkey_set = HotkeySet.new(layout)
30
+ @hotkey_processor = HotkeyProcessor.new(@hotkey_set)
31
+ end
32
+
33
+ def setup_completed?
34
+ !!@hotkey_set
35
+ end
36
+
37
+ def start
38
+ setup
39
+ Thread.abort_on_exception = true
40
+ @work_queue = Action.work_queue = Queue.new
41
+ @worker_thread = start_worker_thread(@work_queue)
42
+
43
+ KeyboardHook.start &@hotkey_processor.method(:process_event)
44
+ end
45
+
46
+ def stop
47
+ KeyboardHook.stop
48
+ end
49
+
50
+ def start_worker_thread(queue)
51
+ Thread.new do
52
+ while action = queue.deq
53
+ begin
54
+ action.call
55
+ rescue => exception
56
+ display_exception exception
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def display_exception(exception)
63
+ error_message = ["#{exception.class}:", exception,
64
+ '', *exception.backtrace].join("\n")
65
+
66
+ file, line = parse_stack_frame(exception.backtrace.first)
67
+ if file
68
+ file = File.expand_path(file)
69
+ callback = lambda do
70
+ Gui.message("Click here to jump to the last error:\n\n#{file}:#{line}") do
71
+ Gui.message error_message, :sticky
72
+ Gui.open_file file, line
73
+ end
74
+ end
75
+ end
76
+
77
+ Gui.message error_message, :sticky, &callback
78
+ end
79
+
80
+ def parse_stack_frame(frame)
81
+ if match = frame.match(/^(.+?):(\d+)/)
82
+ file, line = match.captures
83
+ # Different file names in IRB stackframes:
84
+ # Ruby 1.9.1: (eval)
85
+ # Ruby 1.9.2: <main>
86
+ # MacRuby 0.10: /working_directory/(eval)
87
+ if file !~ /\(eval\)$/ and file != '<main>'
88
+ [file, line.to_i]
89
+ end
90
+ end
91
+ end
92
+
93
+ def switch_worker_thread
94
+ return unless Thread.current == @worker_thread
95
+ old = @work_queue
96
+ new = @work_queue = Action.work_queue = Queue.new
97
+ new.enq(old.deq) until old.length == 0
98
+ @worker_thread = start_worker_thread(new)
99
+ old.enq nil # Signal the worker thread to stop
100
+ end
101
+ end
102
+
103
+ WorkingDir = Dir.pwd
104
+
105
+ def restart
106
+ Dir.chdir WorkingDir
107
+ if Thread.current == Rum::Server.thread
108
+ Thread.new do
109
+ sleep 0.01 # Allow server to respond. Slightly hacky.
110
+ restart_platform_specific
111
+ end
112
+ true
113
+ else
114
+ restart_platform_specific
115
+ end
116
+ end
117
+
118
+ def show
119
+ System.terminal_window.show
120
+ end
121
+
122
+ def hide
123
+ System.terminal_window.hide
124
+ end
125
+ end
@@ -0,0 +1,109 @@
1
+ include Rum
2
+ include System
3
+ include Keyboard
4
+
5
+ module Rum
6
+ class Action
7
+ def register
8
+ Rum.hotkey_set.register(self)
9
+ end
10
+
11
+ def unregister
12
+ Rum.hotkey_set.unregister(self)
13
+ end
14
+ end
15
+
16
+ class FileLocation
17
+ def initialize(file, line)
18
+ @file = file
19
+ @line = line
20
+ end
21
+
22
+ def self.from_stack_frame(frame)
23
+ file, line = Rum.parse_stack_frame(frame)
24
+ if file
25
+ file = File.expand_path(file) if File.dirname(file) == '.'
26
+ new(file, line)
27
+ end
28
+ end
29
+
30
+ def show
31
+ Gui.open_file(@file, @line)
32
+ true
33
+ end
34
+ end
35
+ end
36
+
37
+ module GuiMixin
38
+ # Delegating to Gui instead of directly including Gui avoids dealing
39
+ # with module hierarchy corner cases that appear when other modules
40
+ # are later dynamically included into Gui via Gui.use.
41
+ [:message, :alert, :read, :choose, :open_file, :browse, :goto].each do |method_name|
42
+ define_method(method_name) do |*args, &block|
43
+ Gui.send(method_name, *args, &block)
44
+ end
45
+ private method_name
46
+ end
47
+ end
48
+ include GuiMixin
49
+
50
+ def wait(timeout=5, interval=0.01)
51
+ start = Time.new
52
+ loop do
53
+ return true if yield
54
+ sleep interval
55
+ return false if Time.new - start > timeout
56
+ end
57
+ end
58
+
59
+ class String
60
+ def do(*options, &action)
61
+ repeated = true
62
+ options.reject! do |option|
63
+ case option
64
+ when :no_repeat
65
+ repeated = false
66
+ when String
67
+ action = lambda { Keyboard.type option }
68
+ end
69
+ end
70
+ if (condition = options.first) and condition.respond_to? :to_matcher
71
+ matcher = condition.to_matcher
72
+ condition = lambda { matcher.active? }
73
+ end
74
+ location = FileLocation.from_stack_frame(caller.first)
75
+ Rum.hotkey_set.add_hotkey(self, action, condition, repeated, location)
76
+ end
77
+
78
+ def unregister
79
+ Rum.hotkey_set.remove_hotkey(self)
80
+ end
81
+
82
+ def translate condition=nil, to
83
+ if condition and condition.respond_to? :to_matcher
84
+ matcher = condition.to_matcher
85
+ condition = lambda { matcher.active? }
86
+ end
87
+ Rum.hotkey_set.add_translation(self, to, condition,
88
+ FileLocation.from_stack_frame(caller.first))
89
+ end
90
+ end
91
+
92
+ class String
93
+ # Prepare a special version of #do that is only active when the user
94
+ # calls #do for the first time.
95
+ # After running Rum.setup it calls and restores the default version of #do.
96
+ alias :old_do :do
97
+ def do(*args, &block)
98
+ Rum.setup
99
+ String.class_eval do
100
+ alias :do :old_do
101
+ undef_method :old_do
102
+ end
103
+ action = self.do(*args, &block)
104
+ # The original location has been invalidated by the
105
+ # extra call to #do. Replace it.
106
+ action.location = FileLocation.from_stack_frame(caller.first)
107
+ action
108
+ end
109
+ end