rmate 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +20 -0
- data/bin/rmate +195 -0
- metadata +71 -0
data/README.mdown
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# rmate
|
2
|
+
|
3
|
+
If you wish to activate TextMate from an ssh session you can do so by copying the `rmate` (ruby) script to the server you are logged into. The script will connect back to TextMate running on your Mac so you should setup an ssh tunnel (as your Mac is likely behind NAT):
|
4
|
+
|
5
|
+
ssh -R 52698:127.0.0.1:52698 «server»
|
6
|
+
|
7
|
+
# Usage
|
8
|
+
|
9
|
+
Call `rmate --help` for options. Default options can be set in `/etc/rmate.rc` or `~/.rmate.rc`, e.g.:
|
10
|
+
|
11
|
+
host: auto
|
12
|
+
port: 52698
|
13
|
+
|
14
|
+
You can also set the `RMATE_HOST` and `RMATE_PORT` environment variables.
|
15
|
+
|
16
|
+
For more info see this [blog post about rmate](http://blog.macromates.com/2011/mate-and-rmate/ "TextMate Blog » mate and rmate").
|
17
|
+
|
18
|
+
# Ports
|
19
|
+
|
20
|
+
- [Bash](https://github.com/aurora/rmate) by Harald Lapp
|
data/bin/rmate
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$VERBOSE = true # -w
|
5
|
+
$KCODE = "U" if RUBY_VERSION < "1.9" # -KU
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'socket'
|
9
|
+
require 'tempfile'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
VERSION_STRING = 'rmate version 1.5 (2012-09-19)'
|
13
|
+
|
14
|
+
class Settings
|
15
|
+
attr_accessor :host, :port, :wait, :force, :verbose, :lines, :names, :types
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@host, @port = 'localhost', 52698
|
19
|
+
|
20
|
+
@wait = false
|
21
|
+
@force = false
|
22
|
+
@verbose = false
|
23
|
+
@lines = []
|
24
|
+
@names = []
|
25
|
+
@types = []
|
26
|
+
|
27
|
+
read_disk_settings
|
28
|
+
|
29
|
+
@host = ENV['RMATE_HOST'].to_s if ENV.has_key? 'RMATE_HOST'
|
30
|
+
@port = ENV['RMATE_PORT'].to_i if ENV.has_key? 'RMATE_PORT'
|
31
|
+
|
32
|
+
parse_cli_options
|
33
|
+
|
34
|
+
@host = parse_ssh_connection if @host == 'auto'
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_disk_settings
|
38
|
+
[ "/etc/rmate.rc", "~/.rmate.rc"].each do |current_file|
|
39
|
+
file = File.expand_path current_file
|
40
|
+
if File.exist? file
|
41
|
+
params = YAML::load(File.open(file))
|
42
|
+
@host = params["host"] unless params["host"].nil?
|
43
|
+
@port = params["port"] unless params["port"].nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_cli_options
|
49
|
+
OptionParser.new do |o|
|
50
|
+
o.on( '--host=name', "Connect to host.", "Use 'auto' to detect the host from SSH.", "Defaults to #{@host}.") { |v| @host = v }
|
51
|
+
o.on('-p', '--port=#', Integer, "Port number to use for connection.", "Defaults to #{@port}.") { |v| @port = v }
|
52
|
+
o.on('-w', '--[no-]wait', 'Wait for file to be closed by TextMate.') { |v| @wait = v }
|
53
|
+
o.on('-l', '--line [NUMBER]', 'Place caret on line [NUMBER] after loading file.') { |v| @lines <<= v }
|
54
|
+
o.on('-m', '--name [NAME]', 'The display name shown in TextMate.') { |v| @names <<= v }
|
55
|
+
o.on('-t', '--type [TYPE]', 'Treat file as having [TYPE].') { |v| @types <<= v }
|
56
|
+
o.on('-f', '--force', 'Open even if the file is not writable.') { |v| @force = v }
|
57
|
+
o.on('-v', '--verbose', 'Verbose logging messages.') { |v| @verbose = v }
|
58
|
+
o.on_tail('-h', '--help', 'Show this message.') { puts o; exit }
|
59
|
+
o.on_tail( '--version', 'Show version.') { puts VERSION_STRING; exit }
|
60
|
+
o.parse!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_ssh_connection
|
65
|
+
ENV['SSH_CONNECTION'].nil? ? 'localhost' : ENV['SSH_CONNECTION'].split(' ').first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Command
|
70
|
+
def initialize(name)
|
71
|
+
@command = name
|
72
|
+
@variables = {}
|
73
|
+
@data = nil
|
74
|
+
@size = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def []=(name, value)
|
78
|
+
@variables[name] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_file(path)
|
82
|
+
@size = File.size(path)
|
83
|
+
@data = File.open(path, "rb") { |io| io.read(@size) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def send(socket)
|
87
|
+
socket.puts @command
|
88
|
+
@variables.each_pair do |name, value|
|
89
|
+
value = 'yes' if value === true
|
90
|
+
socket.puts "#{name}: #{value}"
|
91
|
+
end
|
92
|
+
if @data
|
93
|
+
socket.puts "data: #{@size}"
|
94
|
+
socket.puts @data
|
95
|
+
end
|
96
|
+
socket.puts
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_save(socket, variables, data)
|
101
|
+
path = variables["token"]
|
102
|
+
$stderr.puts "Saving #{path}" if $settings.verbose
|
103
|
+
begin
|
104
|
+
backup_path = "#{path}~"
|
105
|
+
backup_path = "#{backup_path}~" while File.exists? backup_path
|
106
|
+
File.link(path, backup_path) if File.exist? path
|
107
|
+
Tempfile.open("rmate", File.dirname(path)) do |temp|
|
108
|
+
temp.close(false)
|
109
|
+
if File.exists?(path)
|
110
|
+
if stat = File.stat(path)
|
111
|
+
File.chown(stat.uid, stat.gid, temp.path)
|
112
|
+
File.chmod(stat.mode, temp.path)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
open(temp.path, 'wb') {|file| file << data }
|
116
|
+
File.rename(temp.path, path)
|
117
|
+
end
|
118
|
+
File.unlink(backup_path) if File.exist? backup_path
|
119
|
+
rescue
|
120
|
+
# TODO We probably want some way to notify the server app that the save failed
|
121
|
+
$stderr.puts "Save failed! #{$!}" if $settings.verbose
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def handle_close(socket, variables, data)
|
126
|
+
path = variables["token"]
|
127
|
+
$stderr.puts "Closed #{path}" if $settings.verbose
|
128
|
+
end
|
129
|
+
|
130
|
+
def handle_cmd(socket)
|
131
|
+
cmd = socket.readline.chomp
|
132
|
+
|
133
|
+
variables = {}
|
134
|
+
data = ""
|
135
|
+
|
136
|
+
while line = socket.readline.chomp
|
137
|
+
break if line.empty?
|
138
|
+
name, value = line.split(': ', 2)
|
139
|
+
variables[name] = value
|
140
|
+
data << socket.read(value.to_i) if name == "data"
|
141
|
+
end
|
142
|
+
variables.delete("data")
|
143
|
+
|
144
|
+
case cmd
|
145
|
+
when "save" then handle_save(socket, variables, data)
|
146
|
+
when "close" then handle_close(socket, variables, data)
|
147
|
+
else abort "Received unknown command “#{cmd}”, exiting."
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def connect_and_handle_cmds(host, port, cmds)
|
152
|
+
socket = TCPSocket.new(host, port)
|
153
|
+
server_info = socket.readline.chomp
|
154
|
+
$stderr.puts "Connect: ‘#{server_info}’" if $settings.verbose
|
155
|
+
|
156
|
+
cmds.each { |cmd| cmd.send(socket) }
|
157
|
+
|
158
|
+
socket.puts "."
|
159
|
+
handle_cmd(socket) while !socket.eof?
|
160
|
+
socket.close
|
161
|
+
$stderr.puts "Done" if $settings.verbose
|
162
|
+
end
|
163
|
+
|
164
|
+
## MAIN
|
165
|
+
|
166
|
+
$settings = Settings.new
|
167
|
+
|
168
|
+
## Parse arguments.
|
169
|
+
cmds = []
|
170
|
+
ARGV.each_index do |idx|
|
171
|
+
path = ARGV[idx]
|
172
|
+
abort "File #{path} is not writable! Use -f/--force to open anyway." unless $settings.force or File.writable? path or not File.exists? path
|
173
|
+
$stderr.puts "File #{path} is not writable. Opening anyway." if not File.writable? path and File.exists? path and $settings.verbose
|
174
|
+
cmd = Command.new("open")
|
175
|
+
cmd['display-name'] = "#{Socket.gethostname}:#{path}"
|
176
|
+
cmd['display-name'] = $settings.names[idx] if $settings.names.length > idx
|
177
|
+
cmd['real-path'] = File.expand_path(path)
|
178
|
+
cmd['data-on-save'] = true
|
179
|
+
cmd['re-activate'] = true
|
180
|
+
cmd['token'] = path
|
181
|
+
cmd['selection'] = $settings.lines[idx] if $settings.lines.length > idx
|
182
|
+
cmd['file-type'] = $settings.types[idx] if $settings.types.length > idx
|
183
|
+
cmd.read_file(path) if File.exist? path
|
184
|
+
cmd['data'] = "0" unless File.exist? path
|
185
|
+
cmds << cmd
|
186
|
+
end
|
187
|
+
|
188
|
+
unless $settings.wait
|
189
|
+
pid = fork do
|
190
|
+
connect_and_handle_cmds($settings.host, $settings.port, cmds)
|
191
|
+
end
|
192
|
+
Process.detach(pid)
|
193
|
+
else
|
194
|
+
connect_and_handle_cmds($settings.host, $settings.port, cmds)
|
195
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rmate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 5
|
8
|
+
- 1
|
9
|
+
version: 1.5.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alex Ross
|
13
|
+
- Allan Odgaard
|
14
|
+
- "Ciar\xC3\xA1n Walsh"
|
15
|
+
- James Edward Gray II
|
16
|
+
- John St. John
|
17
|
+
- Nicolas Ledez
|
18
|
+
- OZAWA Sakuro
|
19
|
+
- Timothy Andrew
|
20
|
+
- Toby Butzon
|
21
|
+
autorequire:
|
22
|
+
bindir: bin
|
23
|
+
cert_chain: []
|
24
|
+
|
25
|
+
date: 2013-03-16 00:00:00 +01:00
|
26
|
+
default_executable:
|
27
|
+
dependencies: []
|
28
|
+
|
29
|
+
description: This program can be used to edit text files in TextMate 2 running on your local Mac.
|
30
|
+
email: rmate@textmate.org
|
31
|
+
executables: []
|
32
|
+
|
33
|
+
extensions: []
|
34
|
+
|
35
|
+
extra_rdoc_files: []
|
36
|
+
|
37
|
+
files:
|
38
|
+
- bin/rmate
|
39
|
+
- README.mdown
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: https://github.com/textmate/rmate
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.6
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Proxy edits to a Mac running TextMate 2
|
70
|
+
test_files: []
|
71
|
+
|