ruco 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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