ruco 0.0.2 → 0.0.4

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/Readme.md CHANGED
@@ -5,9 +5,12 @@ Alpha, lets see if this works...
5
5
  Finished:
6
6
 
7
7
  - viewing / scrolling / editing / saving / creating
8
+ - Home/End
9
+ - basic Tab support (tab == 2 space)
8
10
  - change-indicator
9
11
  - writeable indicator
10
- - backspace
12
+ - backspace / delete
13
+ - find
11
14
 
12
15
  Install
13
16
  =======
@@ -19,8 +22,9 @@ Usage
19
22
 
20
23
  TODO
21
24
  =====
25
+ - cursor move + delete/backspace inside find field
26
+ - smart staying at end of line/column when changing line
22
27
  - indentation + paste support
23
- - delete key !?
24
28
  - warnings / messages
25
29
  - syntax highlighting
26
30
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.4
data/bin/ruco CHANGED
@@ -59,27 +59,56 @@ end
59
59
  parse_options
60
60
 
61
61
  require 'ruco'
62
- editor_offset = 1
63
- editor = Ruco::Editor.new(ARGV[0], :lines => Curses.stdscr.maxy - editor_offset, :columns => Curses.stdscr.maxx)
62
+ status_lines = 1
63
+ command_lines = 1
64
+ editor_lines = Curses.stdscr.maxy - status_lines - command_lines
65
+
66
+ editor = Ruco::Editor.new(ARGV[0], :lines => editor_lines, :columns => Curses.stdscr.maxx)
64
67
  status = Ruco::StatusBar.new(editor, :columns => Curses.stdscr.maxx)
68
+ command = Ruco::CommandBar.new(:columns => Curses.stdscr.maxx)
69
+ command.cursor_line = editor_lines
70
+
71
+ focused = editor
65
72
 
66
73
  init_screen do
67
74
  loop do
68
75
  display status, 0, Curses::A_REVERSE
69
- display editor, editor_offset, Curses::A_NORMAL
76
+ display editor, status_lines, Curses::A_NORMAL
77
+ display command, status_lines + editor_lines, Curses::A_REVERSE
70
78
 
71
- Curses.setpos(editor.cursor_line + editor_offset, editor.cursor_column)
79
+ Curses.setpos(focused.cursor_line + status_lines, focused.cursor_column)
72
80
 
73
81
  key = Curses.getch
74
82
 
75
83
  case key
76
- when Curses::Key::UP then editor.move(-1,0)
77
- when Curses::Key::DOWN then editor.move(1,0)
78
- when Curses::Key::RIGHT then editor.move(0,1)
79
- when Curses::Key::LEFT then editor.move(0,-1)
80
- when 32..126 then editor.insert(key.chr) # printable
81
- when 10 then editor.insert("\n") # enter
82
- when 263 then editor.delete(-1) # backspace
84
+
85
+ # move
86
+ when Curses::Key::UP then focused.move(-1,0)
87
+ when Curses::Key::DOWN then focused.move(1,0)
88
+ when Curses::Key::RIGHT then focused.move(0,1)
89
+ when Curses::Key::LEFT then focused.move(0,-1)
90
+ when Curses::KEY_END then focused.move_to_eol
91
+ when Curses::KEY_HOME then focused.move_to_bol
92
+
93
+ # modify
94
+ when 9 then focused.insert(key.chr) # tab
95
+ when 32..126 then focused.insert(key.chr) # printable
96
+ when 10 then # enter
97
+ result = focused.insert("\n")
98
+ if result.is_a?(Ruco::Command)
99
+ result.send_to(editor)
100
+ focused = editor
101
+ end
102
+ when 263 then focused.delete(-1) # backspace
103
+ when Curses::KEY_DC then focused.delete(1) # delete
104
+
105
+ # misc
106
+ when ?\C-f then
107
+ focused = command
108
+ command.find
109
+ when 27 then
110
+ focused.reset
111
+ focused = editor # escape from focused
83
112
  when ?\C-s then editor.save
84
113
  when ?\C-w, ?\C-q then break # quit
85
114
  end
@@ -0,0 +1,19 @@
1
+ module Ruco
2
+ # Used to pass around commands
3
+ class Command
4
+ attr_reader :method, :args
5
+
6
+ def initialize(method, *args)
7
+ @method = method
8
+ @args = args
9
+ end
10
+
11
+ def send_to(object)
12
+ object.send(@method, *@args)
13
+ end
14
+
15
+ def ==(other)
16
+ other.method == method and other.args == args
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ module Ruco
2
+ class CommandBar
3
+ include Focusable
4
+
5
+ attr_accessor :cursor_line
6
+
7
+ SHORTCUTS = [
8
+ '^W Exit',
9
+ '^S Save',
10
+ '^F Find',
11
+ '^D Delete line'
12
+ ]
13
+
14
+ SEARCH_PREFIX = "Find: "
15
+
16
+ def initialize(options)
17
+ @options = options
18
+ reset
19
+ end
20
+
21
+ def view
22
+ if @find_mode
23
+ SEARCH_PREFIX + @find_term
24
+ else
25
+ available_shortcuts
26
+ end
27
+ end
28
+
29
+ def find
30
+ @find_mode = true
31
+ end
32
+
33
+ def insert(text)
34
+ @find_term += text
35
+ if @find_term.include?("\n")
36
+ @find_term.gsub!("\n",'')
37
+ Command.new(:find, @find_term)
38
+ end
39
+ end
40
+
41
+ def reset
42
+ @find_mode = false
43
+ @find_term = ''
44
+ end
45
+
46
+ def cursor_column
47
+ view.size
48
+ end
49
+
50
+ private
51
+
52
+ def available_shortcuts
53
+ used_columns = 0
54
+ spacer = ' '
55
+ shortcuts_that_fit = SHORTCUTS.select do |shortcut|
56
+ used_columns += shortcut.size
57
+ it_fits = (used_columns <= @options[:columns])
58
+ used_columns += spacer.size
59
+ it_fits
60
+ end
61
+ shortcuts_that_fit * spacer
62
+ end
63
+ end
64
+ end
@@ -11,4 +11,13 @@ class String
11
11
  found << string
12
12
  found
13
13
  end
14
+
15
+ def nth_index(text, n)
16
+ offset = -1
17
+ (n+1).times do
18
+ offset += 1
19
+ offset = index(text, offset) or return
20
+ end
21
+ offset
22
+ end
14
23
  end
data/lib/ruco/editor.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module Ruco
2
2
  class Editor
3
+ include Focusable
4
+
3
5
  SCROLLING_OFFSET = 20
4
6
 
5
7
  attr_reader :cursor_line, :cursor_column, :file
@@ -7,7 +9,7 @@ module Ruco
7
9
  def initialize(file, options)
8
10
  @file = file
9
11
  @options = options
10
- @content = (File.exist?(@file) ? File.read(@file) : '')
12
+ @content = tabs_to_spaces((File.exist?(@file) ? File.read(@file) : ''))
11
13
  @line = 0
12
14
  @column = 0
13
15
  @cursor_line = 0
@@ -28,11 +30,50 @@ module Ruco
28
30
  def move(line, column)
29
31
  @line = [[@line + line, 0].max, lines.size].min
30
32
  @column = [[@column + column, 0].max, (lines[@line]||'').size].min
33
+ adjust_view
34
+ end
31
35
 
36
+ def move_to(line, column)
37
+ @line = line
38
+ @column = column
32
39
  adjust_view
33
40
  end
34
41
 
42
+ def move_to_line(line)
43
+ move_to(line, @column)
44
+ end
45
+
46
+ def move_to_column(column)
47
+ move_to(@line, column)
48
+ end
49
+
50
+ def move_to_eol
51
+ after_last_word = current_line.index(/\s*$/)
52
+ after_last_whitespace = current_line.size
53
+
54
+ if @column == after_last_whitespace or @column < after_last_word
55
+ move_to_column after_last_word
56
+ else
57
+ move_to_column after_last_whitespace
58
+ end
59
+ end
60
+
61
+ def move_to_bol
62
+ before_first_word = current_line.index(/[^\s]/) || 0
63
+ if @column == 0 or @column > before_first_word
64
+ move_to_column before_first_word
65
+ else
66
+ move_to_column 0
67
+ end
68
+ end
69
+
70
+ def find(text)
71
+ index = @content.index(text, cursor_index+1) || cursor_index
72
+ move_to *cursor_for_index(index)
73
+ end
74
+
35
75
  def insert(text)
76
+ text = tabs_to_spaces(text)
36
77
  insert_into_content cursor_index, text
37
78
  move_according_to_insert(text)
38
79
  @modified = true
@@ -55,7 +96,7 @@ module Ruco
55
96
  end
56
97
 
57
98
  @content.slice!(start_index, count)
58
- set_cursor_to_index start_index
99
+ move_to *cursor_for_index(start_index)
59
100
  @modified = true
60
101
  end
61
102
 
@@ -126,17 +167,19 @@ module Ruco
126
167
  @content.insert(index, text)
127
168
  end
128
169
 
170
+ def current_line
171
+ lines[@line] || ''
172
+ end
173
+
129
174
  def cursor_index
130
- insertion_point = lines[0...@line].join("\n").size + @column
131
- insertion_point += 1 if @line > 0 # account for missing newline
132
- insertion_point
175
+ index = lines[0...@line].join("\n").size + @column
176
+ index += 1 if @line > 0 # account for missing newline
177
+ index
133
178
  end
134
179
 
135
- def set_cursor_to_index(index)
180
+ def cursor_for_index(index)
136
181
  jump = @content.slice(0, index).to_s.naive_split("\n")
137
- @line = jump.size - 1
138
- @column = jump.last.size
139
- reposition_cursor
182
+ [jump.size - 1, jump.last.size]
140
183
  end
141
184
 
142
185
  def move_according_to_insert(text)
@@ -149,5 +192,9 @@ module Ruco
149
192
  move(inserted_lines.size - 1, inserted_lines.last.size)
150
193
  end
151
194
  end
195
+
196
+ def tabs_to_spaces(text)
197
+ text.gsub("\t",' ' * Ruco::TAB_SIZE)
198
+ end
152
199
  end
153
200
  end
@@ -0,0 +1,11 @@
1
+ module Ruco
2
+ # Stub so things do not explode
3
+ module Focusable
4
+ def reset;end
5
+ def move(line, column);end
6
+ def move_to_eol;end
7
+ def move_to_bol;end
8
+ def insert(char);end
9
+ def delete(count);end
10
+ end
11
+ end
data/lib/ruco.rb CHANGED
@@ -1,8 +1,13 @@
1
+ require 'ruco/focusable'
2
+ require 'ruco/command'
3
+
1
4
  require 'ruco/editor'
2
5
  require 'ruco/status_bar'
6
+ require 'ruco/command_bar'
3
7
  require 'ruco/core_ext/string'
4
8
  require 'ruco/core_ext/array'
5
9
 
6
10
  module Ruco
7
11
  VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
12
+ TAB_SIZE = 2
8
13
  end
data/ruco.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruco}
8
- s.version = "0.0.2"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2011-01-08}
12
+ s.date = %q{2011-01-09}
13
13
  s.default_executable = %q{ruco}
14
14
  s.email = %q{michael@grosser.it}
15
15
  s.executables = ["ruco"]
@@ -21,11 +21,16 @@ Gem::Specification.new do |s|
21
21
  "VERSION",
22
22
  "bin/ruco",
23
23
  "lib/ruco.rb",
24
+ "lib/ruco/command.rb",
25
+ "lib/ruco/command_bar.rb",
24
26
  "lib/ruco/core_ext/array.rb",
25
27
  "lib/ruco/core_ext/string.rb",
26
28
  "lib/ruco/editor.rb",
29
+ "lib/ruco/focusable.rb",
27
30
  "lib/ruco/status_bar.rb",
28
31
  "ruco.gemspec",
32
+ "spec/ruco/command_bar_spec.rb",
33
+ "spec/ruco/command_spec.rb",
29
34
  "spec/ruco/core_ext/string_spec.rb",
30
35
  "spec/ruco/editor_spec.rb",
31
36
  "spec/ruco/status_bar_spec.rb",
@@ -37,6 +42,8 @@ Gem::Specification.new do |s|
37
42
  s.rubygems_version = %q{1.3.7}
38
43
  s.summary = %q{Commandline editor written in ruby}
39
44
  s.test_files = [
45
+ "spec/ruco/command_bar_spec.rb",
46
+ "spec/ruco/command_spec.rb",
40
47
  "spec/ruco/core_ext/string_spec.rb",
41
48
  "spec/ruco/editor_spec.rb",
42
49
  "spec/ruco/status_bar_spec.rb",
@@ -0,0 +1,57 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe Ruco::CommandBar do
4
+ let(:bar){ Ruco::CommandBar.new(:columns => 30) }
5
+
6
+ it "shows shortcuts by default" do
7
+ bar.view.should == "^W Exit ^S Save ^F Find"
8
+ end
9
+
10
+ it "shows less shortcuts when space is low" do
11
+ bar = Ruco::CommandBar.new(:columns => 29)
12
+ bar.view.should == "^W Exit ^S Save ^F Find"
13
+ bar = Ruco::CommandBar.new(:columns => 28)
14
+ bar.view.should == "^W Exit ^S Save"
15
+ end
16
+
17
+ describe :find do
18
+ it "sets command bar into search mode" do
19
+ bar.find
20
+ bar.view.should == "Find: "
21
+ bar.cursor_column.should == 6
22
+ end
23
+
24
+ it "can enter stuff" do
25
+ bar.find
26
+ bar.insert('abc')
27
+ bar.view.should == "Find: abc"
28
+ bar.cursor_column.should == 9
29
+ end
30
+
31
+ it "can reset the search" do
32
+ bar.find
33
+ bar.insert('abc')
34
+ bar.insert("\n")
35
+ bar.reset
36
+
37
+ bar.view.should include("^W Exit ") # default view
38
+ bar.find
39
+ bar.view.should == "Find: " # term removed
40
+ end
41
+
42
+ it "can execute a search" do
43
+ bar.find
44
+ bar.insert('abc')
45
+ result = bar.insert("d\n")
46
+ result.should == Ruco::Command.new(:find, 'abcd')
47
+ end
48
+
49
+ it "finds with offset when same search is entered again" do
50
+ bar.find
51
+ bar.insert('abcd')
52
+ bar.insert("\n")
53
+ result = bar.insert("\n")
54
+ result.should == Ruco::Command.new(:find, 'abcd')
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe Ruco::Command do
4
+ it "can invoke a recorded command" do
5
+ Ruco::Command.new(:slice, 0,1).send_to("ab1").should == 'a'
6
+ end
7
+
8
+ it "can invoke a recorded command without arguments" do
9
+ Ruco::Command.new(:strip).send_to(" a ").should == 'a'
10
+ end
11
+ end
@@ -14,4 +14,19 @@ describe String do
14
14
  "".naive_split('a').should == ['']
15
15
  end
16
16
  end
17
+
18
+ describe :nth_index do
19
+ it "finds the first by default" do
20
+ "a a a".nth_index('a',0).should == 0
21
+ end
22
+
23
+ it "finds the n-th index" do
24
+ "a a a".nth_index('a',2).should == 4
25
+ end
26
+
27
+ it "is nil when not found" do
28
+ "b b b".nth_index('a',0).should == nil
29
+ "b b b".nth_index('a',1).should == nil
30
+ end
31
+ end
17
32
  end
@@ -11,7 +11,12 @@ describe Ruco::Editor do
11
11
  @file = 'spec/temp.txt'
12
12
  end
13
13
 
14
- describe 'moving' do
14
+ it "reads tab as spaces" do
15
+ write("\t\ta")
16
+ editor.view.should == " a\n\n\n"
17
+ end
18
+
19
+ describe :move do
15
20
  before do
16
21
  write(" \n \n ")
17
22
  end
@@ -102,7 +107,92 @@ describe Ruco::Editor do
102
107
  end
103
108
  end
104
109
 
105
- describe 'viewing' do
110
+ describe :move_to_eol do
111
+ before do
112
+ write("\n aa \n ")
113
+ end
114
+
115
+ it 'stays at start when line is empty' do
116
+ editor.move_to_eol
117
+ editor.cursor.should == [0,0]
118
+ end
119
+
120
+ it 'moves after last word if cursor was before it' do
121
+ editor.move(1,1)
122
+ editor.move_to_eol
123
+ editor.cursor.should == [1,3]
124
+ end
125
+
126
+ it 'moves after last whitespace if cursor was after last word' do
127
+ editor.move(1,3)
128
+ editor.move_to_eol
129
+ editor.cursor.should == [1,4]
130
+ end
131
+
132
+ it 'moves after last work if cursor was after last whitespace' do
133
+ editor.move(1,4)
134
+ editor.move_to_eol
135
+ editor.cursor.should == [1,3]
136
+ end
137
+ end
138
+
139
+ describe :move_to_bol do
140
+ before do
141
+ write("\n aa \n ")
142
+ end
143
+
144
+ it 'stays at start when line is empty' do
145
+ editor.move_to_bol
146
+ editor.cursor.should == [0,0]
147
+ end
148
+
149
+ it 'moves before first work if at start of line' do
150
+ editor.move(1,0)
151
+ editor.move_to_bol
152
+ editor.cursor.should == [1,2]
153
+ end
154
+
155
+ it 'moves to start of line if before first word' do
156
+ editor.move(1,1)
157
+ editor.move_to_bol
158
+ editor.cursor.should == [1,0]
159
+
160
+ editor.move(0,2)
161
+ editor.move_to_bol
162
+ editor.cursor.should == [1,0]
163
+ end
164
+
165
+ it 'moves before first word if inside line' do
166
+ editor.move(1,5)
167
+ editor.move_to_bol
168
+ editor.cursor.should == [1,2]
169
+ end
170
+ end
171
+
172
+ describe :find do
173
+ before do
174
+ write("\n ab\n ab")
175
+ end
176
+
177
+ it "moves to first occurrence" do
178
+ editor.find('ab')
179
+ editor.cursor.should == [1,1]
180
+ end
181
+
182
+ it "moves to next occurrence" do
183
+ editor.move(1,1)
184
+ editor.find('ab')
185
+ editor.cursor.should == [2,1]
186
+ end
187
+
188
+ it "stays in place when nothing was found" do
189
+ editor.move(2,1)
190
+ editor.find('ab')
191
+ editor.cursor.should == [2,1]
192
+ end
193
+ end
194
+
195
+ describe :view do
106
196
  before do
107
197
  write('')
108
198
  end
@@ -127,7 +217,7 @@ describe Ruco::Editor do
127
217
  end
128
218
  end
129
219
 
130
- describe 'inserting' do
220
+ describe :insert do
131
221
  before do
132
222
  write('')
133
223
  end
@@ -175,9 +265,15 @@ describe Ruco::Editor do
175
265
  editor.insert("\n")
176
266
  editor.cursor.should == [2,0]
177
267
  end
268
+
269
+ it "inserts tab as spaces" do
270
+ editor.insert("\t")
271
+ editor.view.should == " \n\n\n"
272
+ editor.cursor.should == [0,2]
273
+ end
178
274
  end
179
275
 
180
- describe 'save' do
276
+ describe :save do
181
277
  it 'stores the file' do
182
278
  write('xxx')
183
279
  editor.insert('a')
@@ -193,7 +289,7 @@ describe Ruco::Editor do
193
289
  end
194
290
  end
195
291
 
196
- describe 'delete' do
292
+ describe :delete do
197
293
  it 'removes a char' do
198
294
  write('123')
199
295
  editor.delete(1)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruco
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-08 00:00:00 +01:00
18
+ date: 2011-01-09 00:00:00 +01:00
19
19
  default_executable: ruco
20
20
  dependencies: []
21
21
 
@@ -35,11 +35,16 @@ files:
35
35
  - VERSION
36
36
  - bin/ruco
37
37
  - lib/ruco.rb
38
+ - lib/ruco/command.rb
39
+ - lib/ruco/command_bar.rb
38
40
  - lib/ruco/core_ext/array.rb
39
41
  - lib/ruco/core_ext/string.rb
40
42
  - lib/ruco/editor.rb
43
+ - lib/ruco/focusable.rb
41
44
  - lib/ruco/status_bar.rb
42
45
  - ruco.gemspec
46
+ - spec/ruco/command_bar_spec.rb
47
+ - spec/ruco/command_spec.rb
43
48
  - spec/ruco/core_ext/string_spec.rb
44
49
  - spec/ruco/editor_spec.rb
45
50
  - spec/ruco/status_bar_spec.rb
@@ -80,6 +85,8 @@ signing_key:
80
85
  specification_version: 3
81
86
  summary: Commandline editor written in ruby
82
87
  test_files:
88
+ - spec/ruco/command_bar_spec.rb
89
+ - spec/ruco/command_spec.rb
83
90
  - spec/ruco/core_ext/string_spec.rb
84
91
  - spec/ruco/editor_spec.rb
85
92
  - spec/ruco/status_bar_spec.rb