xim 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.md +4 -0
  2. data/bin/xim +5 -0
  3. data/lib/xim.rb +2 -0
  4. data/lib/xim/app.rb +9 -0
  5. data/lib/xim/editor.rb +350 -0
  6. metadata +54 -0
@@ -0,0 +1,4 @@
1
+ Xim Text Editor
2
+ ---------------
3
+
4
+ This is my clone of Vim ported to Ruby.
data/bin/xim ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'xim'
4
+
5
+ Xim::App.new(:file => ARGV[0])
@@ -0,0 +1,2 @@
1
+ require 'xim/app'
2
+ require 'xim/editor'
@@ -0,0 +1,9 @@
1
+ module Xim
2
+ class App
3
+ def initialize(opts = {})
4
+ @opts = opts
5
+
6
+ editor = Xim::Editor.new(:file => @opts[:file])
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,350 @@
1
+ require 'curses'
2
+
3
+ module Xim
4
+ class Editor
5
+ include Curses
6
+
7
+ attr_accessor :mode
8
+
9
+ def initialize(opts = {})
10
+ @opts = opts
11
+
12
+ @mode = :normal
13
+
14
+ file = @opts[:file]
15
+
16
+ init_screens
17
+
18
+ if file
19
+ load_file(:file => file)
20
+ else
21
+ new_file
22
+ end
23
+
24
+ main_loop
25
+ end
26
+
27
+ def init_screens
28
+ Curses.init_screen
29
+ Curses.start_color
30
+
31
+ Curses.init_pair(COLOR_RED,COLOR_WHITE,COLOR_RED)
32
+
33
+ @screen = Curses::Window.new(0,0,0,0)
34
+ @screen.keypad(true)
35
+
36
+ @main_scr = @screen.subwin(@screen.maxy-2,0,0,0)
37
+ @main_scr.keypad(true)
38
+ @main_scr.idlok(true)
39
+ @main_scr.scrollok(true)
40
+ @main_scr.setscrreg(0, @screen.maxy)
41
+ @main_scr.refresh
42
+
43
+ @status_scr = @screen.subwin(1,0,@screen.maxy-2,0)
44
+ @status_scr.keypad(true)
45
+ @status_scr.refresh
46
+ @status_scr.attrset(Curses::A_REVERSE)
47
+
48
+ @command_scr = @screen.subwin(1,0,@screen.maxy-1,0)
49
+ @command_scr.keypad(true)
50
+ @command_scr.refresh
51
+
52
+ Curses.noecho
53
+ Curses.cbreak
54
+ Curses.raw
55
+
56
+ @screen.setpos(0, 0)
57
+ @screen.refresh
58
+ end
59
+
60
+ def load_file(opts = {})
61
+ @file_name = opts[:file]
62
+
63
+ @file_x = 0
64
+ @file_y = 0
65
+
66
+ f = File.open(@file_name)
67
+ @file_contents = f.readlines()
68
+
69
+ @file_contents.each do |line|
70
+ @main_scr.addstr(line)
71
+ end
72
+
73
+ @main_scr.setpos(0,0)
74
+ @main_scr.refresh
75
+ update_status_line
76
+ end
77
+
78
+ def new_file(opts = {})
79
+ @file_x = 0
80
+ @file_y = 0
81
+ @file_contents = ["\n"]
82
+ @file_name = nil
83
+ update_status_line
84
+ end
85
+
86
+ def quit
87
+ Curses.close_screen
88
+ puts "closing ..."
89
+ exit(0)
90
+ end
91
+
92
+ def main_loop
93
+ loop do
94
+ react_to_key
95
+ end
96
+ rescue Interrupt => e
97
+ quit
98
+ end
99
+
100
+ def react_to_key
101
+ case @mode
102
+ when :normal then react_to_key_normal
103
+ when :command then react_to_key_command
104
+ when :insert then react_to_key_insert
105
+ end
106
+ end
107
+
108
+ def react_to_key_normal
109
+ case @screen.getch
110
+ when Curses::Key::UP then cursor_up
111
+ when Curses::Key::DOWN then cursor_down
112
+ when Curses::Key::RIGHT then cursor_right
113
+ when Curses::Key::LEFT then cursor_left
114
+ when ?x then cursor_del
115
+ when ?X then cursor_del_left
116
+ when ?u then scroll_up
117
+ when ?d then scroll_down
118
+ when ?: then command_mode
119
+ when ?i then insert_mode
120
+ end
121
+ end
122
+
123
+ def react_to_key_command
124
+ ch = @command_scr.getch
125
+ case ch.ord
126
+ when 10 then command_submit
127
+ when 32..126 then command_entry(ch)
128
+ when 127 then command_delete
129
+ when 27 then normal_mode
130
+ end
131
+ end
132
+
133
+ def react_to_key_insert
134
+ ch = @screen.getch
135
+ case ch.ord
136
+ when 10, 32..126 then insert_entry(ch)
137
+ when 27 then normal_mode
138
+ end
139
+ end
140
+
141
+ def cursor_up
142
+ if @file_y > 0
143
+ if (@main_scr.cury == 0)
144
+ scroll_up
145
+ else
146
+ @main_scr.setpos(@main_scr.cury - 1, @main_scr.curx)
147
+ end
148
+ @file_y -= 1
149
+
150
+ if @file_x >= (@file_contents[@file_y].length - 2)
151
+ @max_x = @file_contents[@file_y].length - 1
152
+ @main_scr.setpos(@main_scr.cury, @max_x)
153
+ @file_x = @max_x
154
+ end
155
+
156
+ update_status_line
157
+ end
158
+ end
159
+
160
+ def cursor_down
161
+ if @file_y < (@file_contents.length - 1)
162
+ if @main_scr.cury == (@main_scr.maxy - 1)
163
+ scroll_down
164
+ else
165
+ @main_scr.setpos(@main_scr.cury + 1, @main_scr.curx)
166
+ end
167
+ @file_y += 1
168
+
169
+ if @file_x >= (@file_contents[@file_y].length - 2)
170
+ @max_x = @file_contents[@file_y].length - 1
171
+ @main_scr.setpos(@main_scr.cury, @max_x)
172
+ @file_x = @max_x
173
+ end
174
+
175
+ update_status_line
176
+ end
177
+ end
178
+
179
+ def cursor_right
180
+ if @file_x < (@file_contents[@file_y].length - 2)
181
+ @main_scr.setpos(@main_scr.cury, @main_scr.curx + 1)
182
+ @file_x += 1
183
+ update_status_line
184
+ end
185
+ end
186
+
187
+ def cursor_left
188
+ if @file_x > 0
189
+ @main_scr.setpos(@main_scr.cury, @main_scr.curx - 1)
190
+ @file_x -= 1
191
+ update_status_line
192
+ end
193
+ end
194
+
195
+ def cursor_del
196
+ if @file_x < (@file_contents[@file_y].length - 2)
197
+ @main_scr.delch
198
+ @file_contents[@file_y].slice!(@file_x)
199
+ elsif @file_x == (@file_contents[@file_y].length - 2)
200
+ @main_scr.delch
201
+ cursor_left
202
+ @file_contents[@file_y].slice!(@file_x)
203
+ end
204
+ @main_scr.refresh
205
+ end
206
+
207
+ def cursor_del_left
208
+ if @file_x < (@file_contents[@file_y].length - 2)
209
+ unless @main_scr.curx == 0
210
+ cursor_left
211
+ @main_scr.delch
212
+ @file_contents[@file_y].slice!(@file_x)
213
+ end
214
+ end
215
+ @main_scr.refresh
216
+ end
217
+
218
+ def scroll_up
219
+ @main_scr.scrl(-1)
220
+ end
221
+
222
+ def scroll_down
223
+ @main_scr.scrl(1)
224
+ end
225
+
226
+ def update_status_line
227
+ x = @main_scr.curx
228
+ y = @main_scr.cury
229
+
230
+ @status_scr.setpos(0, 0)
231
+ @status_scr.deleteln
232
+ @status_scr.addstr(status_line)
233
+ @status_scr.refresh
234
+
235
+ @main_scr.setpos(y,x)
236
+ @main_scr.refresh
237
+ end
238
+
239
+ def status_line
240
+ "#{@file_name ? @file_name : '[No Name]'} [#{@file_x},#{@file_y}]"
241
+ end
242
+
243
+ def command_mode
244
+ @main_x = @main_scr.curx
245
+ @main_y = @main_scr.cury
246
+
247
+ @command_string = ""
248
+ @command_x = 0
249
+ @command_y = 0
250
+
251
+ @command_scr.setpos(0, 0)
252
+ @command_scr.deleteln
253
+ @command_scr.addstr(":")
254
+ @command_scr.refresh
255
+
256
+ @mode = :command
257
+ end
258
+
259
+ def normal_mode
260
+ @main_scr.refresh
261
+
262
+ @mode = :normal
263
+ end
264
+
265
+ def insert_mode
266
+ @mode = :insert
267
+ end
268
+
269
+ def command_entry(ch)
270
+ @command_scr.addch(ch)
271
+ @command_string = @command_string.insert(@command_x, ch)
272
+ @command_x += 1
273
+ @command_scr.refresh
274
+ end
275
+
276
+ def command_delete
277
+ if @command_x == 0
278
+ command_area_clear
279
+ normal_mode
280
+ else
281
+ @command_scr.setpos(0, @command_scr.curx - 1)
282
+ @command_x -= 1
283
+ @command_scr.delch
284
+ @command_string.slice!(@command_x)
285
+ @command_scr.refresh
286
+ end
287
+ end
288
+
289
+ def command_submit
290
+ command_area_clear
291
+ command_submit_process(@command_string)
292
+ normal_mode
293
+ end
294
+
295
+ def command_submit_process(command)
296
+ case command
297
+ when 'w'
298
+ command_write
299
+ when 'q'
300
+ quit
301
+ end
302
+ end
303
+
304
+ def command_write
305
+ if @file_name
306
+ tmpf = temp_file(@file_name)
307
+ f = File.open(tmpf, 'w')
308
+ @file_contents.each do |line|
309
+ f.write(line)
310
+ end
311
+ f.close
312
+ File.rename(tmpf, @file_name)
313
+ command_status_update("\"#{@file_name}\" written")
314
+ else
315
+ command_status_error("E32: No file name")
316
+ end
317
+ end
318
+
319
+ def temp_file(orig_file)
320
+ orig_file + '.tmp.' + Random.new.rand(1000000).to_s
321
+ end
322
+
323
+ def command_status_update(status)
324
+ command_area_clear
325
+ @command_scr.addstr(status)
326
+ @command_scr.refresh
327
+ end
328
+
329
+ def command_status_error(error)
330
+ command_area_clear
331
+ @command_scr.attron(color_pair(COLOR_RED)|A_BOLD)
332
+ @command_scr.addstr(error)
333
+ @command_scr.attrset(A_NORMAL)
334
+ @command_scr.refresh
335
+ end
336
+
337
+ def command_area_clear
338
+ @command_scr.attrset(A_NORMAL)
339
+ @command_scr.clear
340
+ @command_scr.refresh
341
+ end
342
+
343
+ def insert_entry(ch)
344
+ @main_scr.insch(ch)
345
+ @file_contents[@file_y] = @file_contents[@file_y].insert(@file_x, ch)
346
+ cursor_right
347
+ @main_scr.refresh
348
+ end
349
+ end
350
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xim
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ken Barber
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-06 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: vim-clone text editor
15
+ email: ken@bob.sh
16
+ executables:
17
+ - xim
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/xim
22
+ - lib/xim/app.rb
23
+ - lib/xim/editor.rb
24
+ - lib/xim.rb
25
+ - README.md
26
+ homepage: https://github.com/kbarber/xim
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ segments:
40
+ - 0
41
+ hash: 4076229734520165341
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.24
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: vim-clone text editor
54
+ test_files: []