ruco 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.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ group :dev do # not development <-> would add unneeded development dependencies in gemspec
4
+ gem 'rake'
5
+ gem 'rspec', '~>2'
6
+ gem 'jeweler'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.5.2)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+ rspec (2.0.1)
12
+ rspec-core (~> 2.0.1)
13
+ rspec-expectations (~> 2.0.1)
14
+ rspec-mocks (~> 2.0.1)
15
+ rspec-core (2.0.1)
16
+ rspec-expectations (2.0.1)
17
+ diff-lcs (>= 1.1.2)
18
+ rspec-mocks (2.0.1)
19
+ rspec-core (~> 2.0.1)
20
+ rspec-expectations (~> 2.0.1)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ jeweler
27
+ rake
28
+ rspec (~> 2)
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ task :default => :spec
2
+ require "rspec/core/rake_task"
3
+ RSpec::Core::RakeTask.new(:spec) do |t|
4
+ t.rspec_opts = '--backtrace --color'
5
+ end
6
+
7
+ task :run do
8
+ file = 'spec/temp.txt'
9
+ File.open(file, 'w'){|f|f.write("12345\n1234\n#{'abcdefg'*20}\n123")}
10
+ exec "./bin/ruco #{file}"
11
+ end
12
+
13
+ task :try do
14
+ require 'curses'
15
+ Curses.setpos(0,0)
16
+ Curses.addstr("xxxxxxxx\nyyyyyyy");
17
+ Curses.getch
18
+ end
19
+
20
+ task :key do
21
+ require 'curses'
22
+
23
+ Curses.noecho
24
+ Curses.raw
25
+ Curses.stdscr.keypad(true)
26
+
27
+ count = 0
28
+ loop do
29
+ count = (count + 1) % 20
30
+ key = Curses.getch
31
+ break if key == ?\C-c
32
+ Curses.setpos(count,0)
33
+ Curses.addstr("#{key.inspect} ");
34
+ end
35
+ end
36
+
37
+ begin
38
+ require 'jeweler'
39
+ Jeweler::Tasks.new do |gem|
40
+ gem.name = 'ruco'
41
+ gem.summary = "Commandline editor written in ruby"
42
+ gem.email = "michael@grosser.it"
43
+ gem.homepage = "http://github.com/grosser/#{gem.name}"
44
+ gem.authors = ["Michael Grosser"]
45
+ end
46
+
47
+ Jeweler::GemcutterTasks.new
48
+ rescue LoadError
49
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
50
+ end
data/Readme.md ADDED
@@ -0,0 +1,28 @@
1
+ Commandline editor written in ruby
2
+
3
+ Alpha, do not use...
4
+
5
+ Finished:
6
+ - viewing / scrolling / editing / saving / creating
7
+
8
+ Install
9
+ =======
10
+ sudo gem install ruco
11
+
12
+ Usage
13
+ =====
14
+ ruco file.rb
15
+
16
+ TODO
17
+ =====
18
+ - delete key !?
19
+ - warnings / messages
20
+ - help
21
+ - syntax highlighting
22
+
23
+
24
+ Author
25
+ ======
26
+ [Michael Grosser](http://grosser.it)
27
+ grosser.michael@gmail.com
28
+ Hereby placed under public domain, do what you want, just do not hold me accountable...
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/ruco ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ require 'curses'
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'ruco'
6
+
7
+ def write(line,row,text)
8
+ Curses.setpos(line,row)
9
+ Curses.addstr(text);
10
+ end
11
+
12
+ def init_screen
13
+ Curses.noecho # do not show typed chars
14
+ Curses.init_screen
15
+ Curses.stdscr.keypad(true) # enable arrow keys
16
+ Curses.raw # give us all other keys
17
+
18
+ begin
19
+ yield
20
+ ensure
21
+ Curses.close_screen
22
+ end
23
+ end
24
+
25
+ editor = Ruco::Editor.new(ARGV[0], :lines => Curses.stdscr.maxy, :columns => Curses.stdscr.maxx)
26
+
27
+ init_screen do
28
+ loop do
29
+ lines = editor.view.split("\n")
30
+ Curses.stdscr.maxy.times do |i|
31
+ line = lines[i] || ''
32
+ clearer = Curses.stdscr.maxx - line.size
33
+ clearer = " " * clearer
34
+ write(i, 0, line + clearer)
35
+ end
36
+
37
+ Curses.setpos(editor.cursor_line, editor.cursor_column)
38
+
39
+ key = Curses.getch
40
+
41
+ case key
42
+ when Curses::Key::UP then editor.move(-1,0)
43
+ when Curses::Key::DOWN then editor.move(1,0)
44
+ when Curses::Key::RIGHT then editor.move(0,1)
45
+ when Curses::Key::LEFT then editor.move(0,-1)
46
+ when 32..126 then editor.insert(key.chr) # printable
47
+ when 10 then editor.insert("\n") # enter
48
+ when 263 then editor.delete(-1) # backspace
49
+ when ?\C-w, ?\C-q then break # quit
50
+ end
51
+ end
52
+ end
data/lib/ruco.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'ruco/editor'
2
+ require 'ruco/core_ext/string'
3
+ require 'ruco/core_ext/array'
4
+
5
+ module Ruco
6
+ VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
7
+ end
@@ -0,0 +1,10 @@
1
+ # http://snippets.dzone.com/posts/show/5119
2
+ class Array
3
+ def map_with_index!
4
+ each_with_index do |e, idx| self[idx] = yield(e, idx); end
5
+ end
6
+
7
+ def map_with_index(&block)
8
+ dup.map_with_index!(&block)
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class String
2
+ def naive_split(pattern)
3
+ string = self.dup
4
+ found = []
5
+
6
+ while position = string.index(pattern)
7
+ found << string.slice!(0, position)
8
+ string.slice!(0,[pattern.size,1].max)
9
+ end
10
+
11
+ found << string
12
+ found
13
+ end
14
+ end
@@ -0,0 +1,144 @@
1
+ module Ruco
2
+ class Editor
3
+ SCROLLING_OFFSET = 20
4
+
5
+ attr_reader :cursor_line, :cursor_column
6
+
7
+ def initialize(file, options)
8
+ @file = file
9
+ @options = options
10
+ @content = (File.exist?(@file) ? File.read(@file) : '')
11
+ @line = 0
12
+ @column = 0
13
+ @cursor_line = 0
14
+ @cursor_column = 0
15
+ @scrolled_lines = 0
16
+ @scrolled_columns = 0
17
+ @options[:line_scrolling_offset] ||= @options[:lines] / 2
18
+ @options[:column_scrolling_offset] ||= @options[:columns] / 2
19
+ end
20
+
21
+ def view
22
+ Array.new(@options[:lines]).map_with_index do |_,i|
23
+ (lines[i + @scrolled_lines] || "").slice(@scrolled_columns, @options[:columns])
24
+ end * "\n" + "\n"
25
+ end
26
+
27
+ def move(line, column)
28
+ @line = [[@line + line, 0].max, lines.size].min
29
+ @column = [[@column + column, 0].max, (lines[@line]||'').size].min
30
+
31
+ adjust_view
32
+ end
33
+
34
+ def insert(text)
35
+ insert_into_content cursor_index, text
36
+ move_according_to_insert(text)
37
+ end
38
+
39
+ def delete(count)
40
+ if count > 0
41
+ @content.slice!(cursor_index, count)
42
+ else
43
+ backspace(count.abs)
44
+ end
45
+ end
46
+
47
+ def backspace(count)
48
+ start_index = cursor_index - count
49
+ if start_index < 0
50
+ count += start_index
51
+ start_index = 0
52
+ end
53
+
54
+ @content.slice!(start_index, count)
55
+ set_cursor_to_index start_index
56
+ end
57
+
58
+ def save
59
+ File.open(@file,'w'){|f| f.write(@content) }
60
+ end
61
+
62
+ def cursor
63
+ [cursor_line, cursor_column]
64
+ end
65
+
66
+ private
67
+
68
+ def lines
69
+ @content.naive_split("\n")
70
+ end
71
+
72
+ def adjust_view
73
+ reposition_cursor
74
+ scroll_column_into_view
75
+ scroll_line_into_view
76
+ reposition_cursor
77
+ end
78
+
79
+ def scroll_column_into_view
80
+ offset = [@options[:column_scrolling_offset], @options[:columns]].min
81
+
82
+ if @cursor_column >= @options[:columns]
83
+ @scrolled_columns = @column - @options[:columns] + offset
84
+ end
85
+
86
+ if @cursor_column < 0
87
+ @scrolled_columns = @column - offset
88
+ end
89
+
90
+ @scrolled_columns = [[@scrolled_columns, 0].max, @column].min
91
+ end
92
+
93
+ def scroll_line_into_view
94
+ offset = [@options[:line_scrolling_offset], @options[:lines]].min
95
+
96
+ if @cursor_line >= @options[:lines]
97
+ @scrolled_lines = @line - @options[:lines] + offset
98
+ end
99
+
100
+ if @cursor_line < 0
101
+ @scrolled_lines = @line - offset
102
+ end
103
+
104
+ @scrolled_lines = [[@scrolled_lines, 0].max, @line].min
105
+ end
106
+
107
+ def reposition_cursor
108
+ @cursor_column = @column - @scrolled_columns
109
+ @cursor_line = @line - @scrolled_lines
110
+ end
111
+
112
+ def insert_into_content(index, text)
113
+ # expand with newlines when inserting after maximum position
114
+ if index > @content.size
115
+ @content << "\n" * (index - @content.size)
116
+ end
117
+ @content.insert(index, text)
118
+ end
119
+
120
+ def cursor_index
121
+ insertion_point = lines[0...@line].join("\n").size + @column
122
+ insertion_point += 1 if @line > 0 # account for missing newline
123
+ insertion_point
124
+ end
125
+
126
+ def set_cursor_to_index(index)
127
+ jump = @content.slice(0, index).to_s.naive_split("\n")
128
+ @line = jump.size - 1
129
+ @column = jump.last.size
130
+ reposition_cursor
131
+ end
132
+
133
+ def move_according_to_insert(text)
134
+ inserted_lines = text.naive_split("\n")
135
+ if inserted_lines.size > 1
136
+ # column position does not add up when hitting return
137
+ @column = inserted_lines.last.size
138
+ move(inserted_lines.size - 1, 0)
139
+ else
140
+ move(inserted_lines.size - 1, inserted_lines.last.size)
141
+ end
142
+ end
143
+ end
144
+ end
data/ruco.gemspec ADDED
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ruco}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Michael Grosser"]
12
+ s.date = %q{2011-01-08}
13
+ s.default_executable = %q{ruco}
14
+ s.email = %q{michael@grosser.it}
15
+ s.executables = ["ruco"]
16
+ s.files = [
17
+ "Gemfile",
18
+ "Gemfile.lock",
19
+ "Rakefile",
20
+ "Readme.md",
21
+ "VERSION",
22
+ "bin/ruco",
23
+ "lib/ruco.rb",
24
+ "lib/ruco/core_ext/array.rb",
25
+ "lib/ruco/core_ext/string.rb",
26
+ "lib/ruco/editor.rb",
27
+ "ruco.gemspec",
28
+ "spec/ruco/core_ext/string_spec.rb",
29
+ "spec/ruco_spec.rb",
30
+ "spec/spec_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/grosser/ruco}
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.7}
35
+ s.summary = %q{Commandline editor written in ruby}
36
+ s.test_files = [
37
+ "spec/ruco/core_ext/string_spec.rb",
38
+ "spec/ruco_spec.rb",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ else
48
+ end
49
+ else
50
+ end
51
+ end
52
+
@@ -0,0 +1,17 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe String do
4
+ describe :naive_split do
5
+ it "splits repeated pattern" do
6
+ "aaa".naive_split('a').should == ['','','','']
7
+ end
8
+
9
+ it "splits normal stuff" do
10
+ "abacad".naive_split('a').should == ['','b','c','d']
11
+ end
12
+
13
+ it "splits empty into 1" do
14
+ "".naive_split('a').should == ['']
15
+ end
16
+ end
17
+ end
data/spec/ruco_spec.rb ADDED
@@ -0,0 +1,240 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe Ruco do
4
+ def write(content)
5
+ File.open(@file,'w'){|f| f.write(content) }
6
+ end
7
+
8
+ let(:editor){ Ruco::Editor.new(@file, :lines => 3, :columns => 5, :line_scrolling_offset => 5, :column_scrolling_offset => 5) }
9
+
10
+ it "has a VERSION" do
11
+ Ruco::VERSION.should =~ /^\d+\.\d+\.\d+$/
12
+ end
13
+
14
+ before do
15
+ @file = 'spec/temp.txt'
16
+ end
17
+
18
+ describe 'moving' do
19
+ before do
20
+ write(" \n \n ")
21
+ end
22
+
23
+ it "starts at 0,0" do
24
+ editor.cursor.should == [0,0]
25
+ end
26
+
27
+ it "can move" do
28
+ editor.move(1,2)
29
+ editor.cursor.should == [1,2]
30
+ editor.move(1,1)
31
+ editor.cursor.should == [2,3]
32
+ end
33
+
34
+ it "can move in empty file" do
35
+ write("\n\n\n")
36
+ editor.move(2,0)
37
+ editor.cursor.should == [2,0]
38
+ end
39
+
40
+ it "cannot move left/top off screen" do
41
+ editor.move(-1,-1)
42
+ editor.cursor.should == [0,0]
43
+ end
44
+
45
+ it "cannot move right of characters" do
46
+ editor.move(2,6)
47
+ editor.cursor.should == [2,4]
48
+ end
49
+
50
+ it "gets reset to empty line when moving past lines" do
51
+ write(" ")
52
+ editor.move(6,3)
53
+ editor.cursor.should == [1,0]
54
+ end
55
+
56
+ describe 'column scrolling' do
57
+ it "can scroll columns" do
58
+ write("123456789\n123")
59
+ editor.move(0,4)
60
+ editor.view.should == "12345\n123\n\n"
61
+ editor.cursor_column.should == 4
62
+
63
+ editor.move(0,1)
64
+ editor.view.should == "6789\n\n\n"
65
+ editor.cursor_column.should == 0
66
+ end
67
+
68
+ it "cannot scroll past the screen" do
69
+ write('123456789')
70
+ editor.move(0,4)
71
+ 6.times{ editor.move(0,1) }
72
+ editor.view.should == "6789\n\n\n"
73
+ editor.cursor_column.should == 4
74
+ end
75
+
76
+ it "can scroll columns backwards" do
77
+ write('123456789')
78
+ editor.move(0,5)
79
+ editor.view.should == "6789\n\n\n"
80
+
81
+ editor.move(0,-1)
82
+ editor.view.should == "12345\n\n\n"
83
+ editor.cursor_column.should == 4
84
+ end
85
+ end
86
+
87
+ describe 'line scrolling' do
88
+ before do
89
+ write("1\n2\n3\n4\n5\n6\n7\n8\n9")
90
+ end
91
+
92
+ it "can scroll lines down (at maximum of screen size)" do
93
+ editor.move(2,0)
94
+ editor.view.should == "1\n2\n3\n"
95
+
96
+ editor.move(1,0)
97
+ editor.view.should == "4\n5\n6\n"
98
+ editor.cursor_line.should == 0
99
+ end
100
+
101
+ it "can scroll till end of file" do
102
+ editor.move(15,0)
103
+ editor.view.should == "\n\n\n"
104
+ editor.cursor_line.should == 0
105
+ end
106
+ end
107
+ end
108
+
109
+ describe 'viewing' do
110
+ before do
111
+ write('')
112
+ end
113
+
114
+ it "displays an empty screen" do
115
+ editor.view.should == "\n\n\n"
116
+ end
117
+
118
+ it "displays short file content" do
119
+ write('xxx')
120
+ editor.view.should == "xxx\n\n\n"
121
+ end
122
+
123
+ it "displays long file content" do
124
+ write('1234567')
125
+ editor.view.should == "12345\n\n\n"
126
+ end
127
+
128
+ it "displays multiline-file content" do
129
+ write("xxx\nyyy\nzzz\niii")
130
+ editor.view.should == "xxx\nyyy\nzzz\n"
131
+ end
132
+ end
133
+
134
+ describe 'inserting' do
135
+ before do
136
+ write('')
137
+ end
138
+
139
+ it "can insert new chars" do
140
+ write('123')
141
+ editor.move(0,1)
142
+ editor.insert('ab')
143
+ editor.view.should == "1ab23\n\n\n"
144
+ editor.cursor.should == [0,3]
145
+ end
146
+
147
+ it "can insert new newlines" do
148
+ editor.insert("ab\nc")
149
+ editor.view.should == "ab\nc\n\n"
150
+ editor.cursor.should == [1,1]
151
+ end
152
+
153
+ it "jumps to correct column when inserting newline" do
154
+ write("abc\ndefg")
155
+ editor.move(1,2)
156
+ editor.insert("1\n23")
157
+ editor.view.should == "abc\nde1\n23fg\n"
158
+ editor.cursor.should == [2,2]
159
+ end
160
+
161
+ it "jumps to correct column when inserting 1 newline" do
162
+ write("abc\ndefg")
163
+ editor.move(1,2)
164
+ editor.insert("\n")
165
+ editor.view.should == "abc\nde\nfg\n"
166
+ editor.cursor.should == [2,0]
167
+ end
168
+
169
+ it "can add newlines to the end" do
170
+ write('')
171
+ editor.insert("\n")
172
+ editor.insert("\n")
173
+ editor.cursor.should == [2,0]
174
+ end
175
+
176
+ it "can add newlines to the moveable end" do
177
+ write('')
178
+ editor.move(1,0)
179
+ editor.insert("\n")
180
+ editor.cursor.should == [2,0]
181
+ end
182
+ end
183
+
184
+ describe 'save' do
185
+ it 'stores the file' do
186
+ write('xxx')
187
+ editor.insert('a')
188
+ editor.save
189
+ File.read(@file).should == 'axxx'
190
+ end
191
+
192
+ it 'creates the file' do
193
+ `rm #{@file}`
194
+ editor.insert('a')
195
+ editor.save
196
+ File.read(@file).should == 'a'
197
+ end
198
+ end
199
+
200
+ describe 'delete' do
201
+ it 'removes a char' do
202
+ write('123')
203
+ editor.delete(1)
204
+ editor.view.should == "23\n\n\n"
205
+ editor.cursor.should == [0,0]
206
+ end
207
+
208
+ it 'removes a line' do
209
+ write("123\n45")
210
+ editor.move(0,3)
211
+ editor.delete(1)
212
+ editor.view.should == "12345\n\n\n"
213
+ editor.cursor.should == [0,3]
214
+ end
215
+
216
+ it "cannot backspace over 0,0" do
217
+ write("aa")
218
+ editor.move(0,1)
219
+ editor.delete(-3)
220
+ editor.view.should == "a\n\n\n"
221
+ editor.cursor.should == [0,0]
222
+ end
223
+
224
+ it 'backspaces a char' do
225
+ write('123')
226
+ editor.move(0,3)
227
+ editor.delete(-1)
228
+ editor.view.should == "12\n\n\n"
229
+ editor.cursor.should == [0,2]
230
+ end
231
+
232
+ it 'backspaces a newline' do
233
+ write("1\n234")
234
+ editor.move(1,0)
235
+ editor.delete(-1)
236
+ editor.view.should == "1234\n\n\n"
237
+ editor.cursor.should == [0,1]
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift 'lib'
2
+ require 'ruco'
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruco
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Michael Grosser
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-08 00:00:00 +01:00
19
+ default_executable: ruco
20
+ dependencies: []
21
+
22
+ description:
23
+ email: michael@grosser.it
24
+ executables:
25
+ - ruco
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Gemfile
32
+ - Gemfile.lock
33
+ - Rakefile
34
+ - Readme.md
35
+ - VERSION
36
+ - bin/ruco
37
+ - lib/ruco.rb
38
+ - lib/ruco/core_ext/array.rb
39
+ - lib/ruco/core_ext/string.rb
40
+ - lib/ruco/editor.rb
41
+ - ruco.gemspec
42
+ - spec/ruco/core_ext/string_spec.rb
43
+ - spec/ruco_spec.rb
44
+ - spec/spec_helper.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/grosser/ruco
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Commandline editor written in ruby
79
+ test_files:
80
+ - spec/ruco/core_ext/string_spec.rb
81
+ - spec/ruco_spec.rb
82
+ - spec/spec_helper.rb