xim 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []