ruco 0.1.12 → 0.1.13

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
@@ -86,7 +86,6 @@ TODO
86
86
  - do not fall back to 0:0 after undoing the first change
87
87
  - check writable status every x seconds (e.g. in background) -> faster while typing
88
88
  - search help e.g. 'Nothing found' '#4 of 6 hits' 'no more hits, start from beginning ?'
89
- - highlight current work when reopening search (so typing replaces it)
90
89
  - align soft-tabs
91
90
  - highlight tabs (e.g. strange character or reverse/underline/color)
92
91
  - big warning when editing a not-writable file
@@ -96,7 +95,6 @@ TODO
96
95
  - raise when binding to a unsupported key
97
96
  - search history via up/down arrow
98
97
  - search options regex + case-sensitive
99
- - add auto-confirm to 'replace?' dialog -> type s == skip, no enter needed
100
98
  - 1.8: unicode support <-> already finished but unusable due to Curses (see encoding branch)
101
99
  - add double quotes/braces when typing one + skip over quote/brace when its already typed at current position
102
100
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.12
1
+ 0.1.13
data/bin/ruco CHANGED
@@ -40,75 +40,6 @@ BANNER
40
40
  options
41
41
  end
42
42
 
43
- def write(line,row,text)
44
- Curses.setpos(line,row)
45
- Curses.addstr(text);
46
- end
47
-
48
- def init_screen
49
- Curses.noecho # do not show typed chars
50
- Curses.nonl # turn off newline translation
51
- Curses.stdscr.keypad(true) # enable arrow keys
52
- Curses.raw # give us all other keys
53
- Curses.stdscr.nodelay = 1 # do not block -> we can use timeouts
54
- Curses.init_screen
55
-
56
- begin
57
- yield
58
- ensure
59
- Curses.clear # needed to clear the menu/status bar on windows
60
- Curses.close_screen
61
- end
62
- end
63
-
64
- def display(lines, style_mask)
65
- columns = Curses.stdscr.maxx
66
- @screen ||= [] # current screen is used as cache
67
- style_mask = style_mask.flatten
68
-
69
- lines.each_with_index do |content, line|
70
- styles = style_mask[line]
71
-
72
- # expand line with whitespace to overwrite previous content
73
- missing = columns - content.size
74
- raise content if missing < 0
75
- content += " " * missing
76
-
77
- # display tabs as single-space -> nothing breaks
78
- content.gsub!("\t",' ')
79
-
80
- # cache !?
81
- next if @screen[line] == [content, styles]
82
- @screen[line] = [content, styles]
83
-
84
- # position at start of line and draw
85
- Curses.setpos(line,0)
86
- Ruco::StyleMap.styled(content, styles).each do |style, part|
87
- Curses.attrset Ruco::StyleMap.curses_style(style)
88
- Curses.addstr part
89
- end
90
-
91
- if @options[:debug_cache]
92
- write(line, 0, (rand(899)+100).to_s)
93
- end
94
- end
95
- end
96
-
97
- def show_app(app)
98
- lines = app.view.naive_split("\n")
99
- style_map = app.style_map
100
-
101
- # TODO move this logic into application
102
- display(lines, style_map)
103
- Curses.setpos(app.cursor.line, app.cursor.column)
104
- end
105
-
106
- def debug_key(key)
107
- @key_line ||= -1
108
- @key_line = (@key_line + 1) % Curses.stdscr.maxy
109
- write(@key_line, 0, "#{key.inspect}---")
110
- end
111
-
112
43
  def log(stuff)
113
44
  File.open('ruco.log','ab'){|f| f.puts stuff }
114
45
  end
@@ -117,28 +48,30 @@ end
117
48
 
118
49
  require 'ruco'
119
50
 
120
- app = Ruco::Application.new(ARGV[0],
121
- :convert_tabs => @options[:convert_tabs],
122
- :undo_stack_size => @options[:undo_stack_size],
123
- :lines => Curses.stdscr.maxy, :columns => Curses.stdscr.maxx
124
- )
51
+ # draw app and redraw after each keystroke (or paste)
52
+ Ruco::Screen.open(@options) do |screen|
53
+ app = Ruco::Application.new(ARGV[0],
54
+ :convert_tabs => @options[:convert_tabs],
55
+ :undo_stack_size => @options[:undo_stack_size],
56
+ :lines => screen.lines, :columns => screen.columns
57
+ )
125
58
 
126
- init_screen do
127
- show_app app
59
+ screen.draw *app.display_info
128
60
 
129
61
  Keyboard.input do
130
62
  Curses.getch
131
63
  end
132
64
 
133
65
  Keyboard.output do |key|
134
- debug_key(key) if @options[:debug_keys]
66
+ screen.debug_key(key) if @options[:debug_keys]
135
67
  if key == :resize
136
- app.resize(Curses.stdscr.maxy, Curses.stdscr.maxx)
137
- @screen.clear # clear cache
68
+ app.resize(lines, columns)
69
+ screen.clear_cache
138
70
  else
139
71
  result = app.key key
72
+ break if result == :quit
140
73
  end
141
- break if result == :quit
142
- show_app app
74
+
75
+ screen.draw *app.display_info
143
76
  end
144
77
  end
data/lib/ruco.rb CHANGED
@@ -13,6 +13,7 @@ require 'ruco/history'
13
13
  require 'ruco/option_accessor'
14
14
  require 'ruco/file_store'
15
15
  require 'ruco/window'
16
+ require 'ruco/screen'
16
17
  require 'ruco/style_map'
17
18
 
18
19
  require 'ruco/editor'
@@ -14,31 +14,30 @@ module Ruco
14
14
  @editor.move(:to, go_to_line.to_i-1,0) if go_to_line
15
15
  end
16
16
 
17
+ def display_info
18
+ [view, style_map, cursor]
19
+ end
20
+
17
21
  def view
18
- status.view + "\n" + editor.view + "\n" + command.view
22
+ [status.view, editor.view, command.view].join("\n")
19
23
  end
20
24
 
21
25
  def style_map
22
- reverse = StyleMap.single_line_reversed(@options[:columns])
23
- reverse + editor.style_map + @command.style_map
26
+ status.style_map + editor.style_map + command.style_map
24
27
  end
25
28
 
26
29
  def cursor
27
30
  Position.new(@focused.cursor.line + @status_lines, @focused.cursor.column)
28
31
  end
29
32
 
33
+ # user typed a key
30
34
  def key(key)
31
35
  # deactivate select_mode if its not re-enabled in this action
32
36
  @select_mode_was_on = @select_mode
33
37
  @select_mode = false
34
38
 
35
39
  if bound = @bindings[key]
36
- result = if bound.is_a?(Symbol)
37
- @actions[bound].call
38
- else
39
- bound.call
40
- end
41
- return result
40
+ return execute_action(bound)
42
41
  end
43
42
 
44
43
  case key
@@ -70,8 +69,7 @@ module Ruco
70
69
  @focused.insert("\t")
71
70
  end
72
71
  when :"Shift+tab" then @editor.unindent
73
- when :enter then
74
- @focused.insert("\n")
72
+ when :enter then @focused.insert("\n")
75
73
  when :backspace then @focused.delete(-1)
76
74
  when :delete then @focused.delete(1)
77
75
 
@@ -85,7 +83,7 @@ module Ruco
85
83
 
86
84
  def bind(key, action=nil, &block)
87
85
  raise "Ctrl+m cannot be bound" if key == :"Ctrl+m" # would shadow enter -> bad
88
- raise if action and block
86
+ raise "Cannot bind an action and a block" if action and block
89
87
  @bindings[key] = action || block
90
88
  end
91
89
 
@@ -185,7 +183,7 @@ module Ruco
185
183
  ask("Find: ", :cache => true) do |term|
186
184
  if editor.find(term)
187
185
  ask("Replace with: ", :cache => true) do |replace|
188
- loop_ask("Replace=Enter Skip=s All=a Cancel=Esc") do |ok|
186
+ loop_ask("Replace=Enter Skip=s All=a Cancel=Esc", :auto_enter => true) do |ok|
189
187
  case ok
190
188
  when '' # enter
191
189
  editor.insert(replace)
@@ -290,5 +288,13 @@ module Ruco
290
288
  @focused.send(:move, *args)
291
289
  end
292
290
  end
291
+
292
+ def execute_action(action)
293
+ if action.is_a?(Symbol)
294
+ @actions[action].call
295
+ else
296
+ action.call
297
+ end
298
+ end
293
299
  end
294
300
  end
@@ -40,7 +40,7 @@ module Ruco
40
40
 
41
41
  def ask(question, options={}, &block)
42
42
  @form = cached_form_if(options[:cache], question) do
43
- Form.new(question, :columns => @options[:columns]) do |result|
43
+ Form.new(question, :columns => @options[:columns], :auto_enter => options[:auto_enter]) do |result|
44
44
  @form = nil
45
45
  block.call(result)
46
46
  end
@@ -1,12 +1,12 @@
1
1
  class String
2
- # fix strange string split behavior without cluttering the knowledge
3
- # all over the codebase
2
+ # http://grosser.it/2011/08/28/ruby-string-naive-split-because-split-is-to-clever/
4
3
  # " ".split(' ') == []
5
- # " ".split(/ /) == []
6
- # " ".split(' ',-1) == ['']
7
4
  # " ".naive_split(' ') == ['','','','']
5
+ # "".split(' ') == []
6
+ # "".naive_split(' ') == ['']
8
7
  def naive_split(pattern)
9
- result = split(/#{pattern}/, -1)
8
+ pattern = /#{pattern}/ unless pattern.is_a?(Regexp)
9
+ result = split(pattern, -1)
10
10
  result.empty? ? [''] : result
11
11
  end
12
12
 
data/lib/ruco/form.rb CHANGED
@@ -21,7 +21,7 @@ module Ruco
21
21
 
22
22
  def insert(text)
23
23
  @text_field.insert(text.gsub("\n",'')) unless text == "\n"
24
- @submit.call(@text_field.value) if text.include?("\n")
24
+ @submit.call(@text_field.value) if text.include?("\n") or @options[:auto_enter]
25
25
  end
26
26
 
27
27
  def cursor
@@ -0,0 +1,101 @@
1
+ module Ruco
2
+ class Screen
3
+ def initialize(options)
4
+ @options = options
5
+ @cache = []
6
+ end
7
+
8
+ def self.open(options, &block)
9
+ new(options).open(&block)
10
+ end
11
+
12
+ def open(&block)
13
+ Curses.noecho # do not show typed chars
14
+ Curses.nonl # turn off newline translation
15
+ Curses.stdscr.keypad(true) # enable arrow keys
16
+ Curses.raw # give us all other keys
17
+ Curses.stdscr.nodelay = 1 # do not block -> we can use timeouts
18
+ Curses.init_screen
19
+ yield self
20
+ ensure
21
+ Curses.clear # needed to clear the menu/status bar on windows
22
+ Curses.close_screen
23
+ end
24
+
25
+ def columns
26
+ Curses.stdscr.maxx
27
+ end
28
+
29
+ def lines
30
+ Curses.stdscr.maxy
31
+ end
32
+
33
+ def clear_cache
34
+ @cache.clear
35
+ end
36
+
37
+ def draw(view, style_map, cursor)
38
+ draw_view(view, style_map)
39
+ Curses.setpos(*cursor)
40
+ end
41
+
42
+ def debug_key(key)
43
+ @key_line ||= -1
44
+ @key_line = (@key_line + 1) % lines
45
+ write(@key_line, 0, "#{key.inspect}---")
46
+ end
47
+
48
+ private
49
+
50
+ def write(line,row,text)
51
+ Curses.setpos(line,row)
52
+ Curses.addstr(text);
53
+ end
54
+
55
+ def draw_view(view, style_mask)
56
+ lines = view.naive_split("\n")
57
+ style_mask = style_mask.flatten
58
+
59
+ lines.each_with_index do |line, line_number|
60
+ styles = style_mask[line_number]
61
+
62
+ # expand line with whitespace to overwrite previous content
63
+ missing = columns - line.size
64
+ raise line if missing < 0
65
+ line += " " * missing
66
+
67
+ # display tabs as single-space -> nothing breaks
68
+ line.gsub!("\t",' ')
69
+
70
+ if_line_changes line_number, [line, styles] do
71
+ # position at start of line and draw
72
+ Curses.setpos(line_number,0)
73
+ Ruco::StyleMap.styled(line, styles).each do |style, part|
74
+ Curses.attrset self.class.curses_style(style)
75
+ Curses.addstr part
76
+ end
77
+
78
+ if @options[:debug_cache]
79
+ write(line_number, 0, (rand(899)+100).to_s)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def if_line_changes(key, args)
86
+ return if @cache[key] == args # would not change the line -> nothing to do
87
+ @cache[key] = args # store current line
88
+ yield # render the line
89
+ end
90
+
91
+ STYLES = {
92
+ :normal => 0,
93
+ :reverse => Curses::A_REVERSE
94
+ }
95
+
96
+ def self.curses_style(style)
97
+ return 0 unless style
98
+ STYLES[style] or raise("Unknown style #{style.inspect}")
99
+ end
100
+ end
101
+ end
@@ -22,6 +22,10 @@ module Ruco
22
22
  "#{version}#{file}#{indicators}#{' ' * space_left}#{position}"[0, columns]
23
23
  end
24
24
 
25
+ def style_map
26
+ StyleMap.single_line_reversed(@options[:columns])
27
+ end
28
+
25
29
  def change_indicator
26
30
  @editor.modified? ? '*' : ' '
27
31
  end
@@ -100,16 +100,6 @@ module Ruco
100
100
  build
101
101
  end
102
102
 
103
- STYLES = {
104
- :normal => 0,
105
- :reverse => Curses::A_REVERSE
106
- }
107
-
108
- def self.curses_style(style)
109
- return 0 unless style
110
- STYLES[style] or raise("Unknown style #{style.inspect}")
111
- end
112
-
113
103
  def self.single_line_reversed(columns)
114
104
  map = StyleMap.new(1)
115
105
  map.add(:reverse, 0, 0...columns)
data/ruco.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruco}
8
- s.version = "0.1.12"
8
+ s.version = "0.1.13"
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"]
@@ -39,6 +39,7 @@ Gem::Specification.new do |s|
39
39
  "lib/ruco/keyboard.rb",
40
40
  "lib/ruco/option_accessor.rb",
41
41
  "lib/ruco/position.rb",
42
+ "lib/ruco/screen.rb",
42
43
  "lib/ruco/status_bar.rb",
43
44
  "lib/ruco/style_map.rb",
44
45
  "lib/ruco/text_area.rb",
@@ -56,6 +57,7 @@ Gem::Specification.new do |s|
56
57
  "spec/ruco/history_spec.rb",
57
58
  "spec/ruco/keyboard_spec.rb",
58
59
  "spec/ruco/option_accessor_spec.rb",
60
+ "spec/ruco/screen_spec.rb",
59
61
  "spec/ruco/status_bar_spec.rb",
60
62
  "spec/ruco/style_map_spec.rb",
61
63
  "spec/ruco/text_area_spec.rb",
@@ -83,6 +85,7 @@ Gem::Specification.new do |s|
83
85
  "spec/ruco/history_spec.rb",
84
86
  "spec/ruco/keyboard_spec.rb",
85
87
  "spec/ruco/option_accessor_spec.rb",
88
+ "spec/ruco/screen_spec.rb",
86
89
  "spec/ruco/status_bar_spec.rb",
87
90
  "spec/ruco/style_map_spec.rb",
88
91
  "spec/ruco/text_area_spec.rb",
@@ -199,9 +199,9 @@ describe Ruco::Application do
199
199
  app.view.should include("Replace with:")
200
200
  type 'd', :enter
201
201
  app.view.should include("Replace=Enter")
202
- type 's', :enter # skip
202
+ type 's' # skip
203
203
  app.view.should include("Replace=Enter")
204
- type 's', :enter # skip
204
+ type 's' # skip
205
205
  app.view.should_not include("Replace=Enter")
206
206
  editor_part(app.view).should == "xabac\n\n"
207
207
  end
@@ -212,9 +212,9 @@ describe Ruco::Application do
212
212
  app.view.should include("Replace with:")
213
213
  type 'd', :enter
214
214
  app.view.should include("Replace=Enter")
215
- type 's', :enter # skip first
215
+ type 's' # skip first
216
216
  app.view.should include("Replace=Enter")
217
- type 'a', :enter # all
217
+ type 'a' # all
218
218
  app.view.should_not include("Replace=Enter")
219
219
  editor_part(app.view).should == "_a_d_d_d\n\n"
220
220
  end
@@ -225,7 +225,7 @@ describe Ruco::Application do
225
225
  app.view.should include("Replace with:")
226
226
  type "d", :enter
227
227
  app.view.should include("Replace=Enter")
228
- type 'x', :enter
228
+ type 'x' # stupid user
229
229
  app.view.should_not include("Replace=Enter")
230
230
  editor_part(app.view).should == "xabac\n\n"
231
231
  end
@@ -23,6 +23,12 @@ describe Ruco::Form do
23
23
  form.insert("d\n")
24
24
  @result.should == "abcd"
25
25
  end
26
+
27
+ it "returns result on normal insert when auto_enter is given" do
28
+ form.instance_eval{ @options[:auto_enter] = true }
29
+ form.insert('a')
30
+ @result.should == 'a'
31
+ end
26
32
  end
27
33
 
28
34
  describe :move do
@@ -74,4 +80,4 @@ describe Ruco::Form do
74
80
  form.view.should == "Test "
75
81
  end
76
82
  end
77
- end
83
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe Ruco::Screen do
4
+ describe :curses_style do
5
+ it "is 'normal' for nothing" do
6
+ Ruco::Screen.curses_style(:normal).should == Curses::A_NORMAL
7
+ end
8
+
9
+ it "is red for red" do
10
+ pending
11
+ Ruco::Screen.curses_style(:red).should == Curses::color_pair( Curses::COLOR_RED )
12
+ end
13
+
14
+ it "is reverse for reverse" do
15
+ Ruco::Screen.curses_style(:reverse).should == Curses::A_REVERSE
16
+ end
17
+
18
+ it "raises on unknown style" do
19
+ lambda{
20
+ Ruco::Screen.curses_style(:foo)
21
+ }.should raise_error
22
+ end
23
+ end
24
+ end
@@ -110,25 +110,4 @@ describe Ruco::StyleMap do
110
110
  Ruco::StyleMap.styled("abc", [:reverse, :normal]).should == [[:normal, ""], [:reverse, "a"],[:normal,'bc']]
111
111
  end
112
112
  end
113
-
114
- describe :curses_style do
115
- it "is 'normal' for nothing" do
116
- Ruco::StyleMap.curses_style(:normal).should == Curses::A_NORMAL
117
- end
118
-
119
- it "is red for red" do
120
- pending
121
- Ruco::StyleMap.curses_style(:red).should == Curses::color_pair( Curses::COLOR_RED )
122
- end
123
-
124
- it "is reverse for reverse" do
125
- Ruco::StyleMap.curses_style(:reverse).should == Curses::A_REVERSE
126
- end
127
-
128
- it "raises on unknown style" do
129
- lambda{
130
- Ruco::StyleMap.curses_style(:foo)
131
- }.should raise_error
132
- end
133
- end
134
113
  end
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: 3
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 12
10
- version: 0.1.12
9
+ - 13
10
+ version: 0.1.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -68,6 +68,7 @@ files:
68
68
  - lib/ruco/keyboard.rb
69
69
  - lib/ruco/option_accessor.rb
70
70
  - lib/ruco/position.rb
71
+ - lib/ruco/screen.rb
71
72
  - lib/ruco/status_bar.rb
72
73
  - lib/ruco/style_map.rb
73
74
  - lib/ruco/text_area.rb
@@ -85,6 +86,7 @@ files:
85
86
  - spec/ruco/history_spec.rb
86
87
  - spec/ruco/keyboard_spec.rb
87
88
  - spec/ruco/option_accessor_spec.rb
89
+ - spec/ruco/screen_spec.rb
88
90
  - spec/ruco/status_bar_spec.rb
89
91
  - spec/ruco/style_map_spec.rb
90
92
  - spec/ruco/text_area_spec.rb
@@ -140,6 +142,7 @@ test_files:
140
142
  - spec/ruco/history_spec.rb
141
143
  - spec/ruco/keyboard_spec.rb
142
144
  - spec/ruco/option_accessor_spec.rb
145
+ - spec/ruco/screen_spec.rb
143
146
  - spec/ruco/status_bar_spec.rb
144
147
  - spec/ruco/style_map_spec.rb
145
148
  - spec/ruco/text_area_spec.rb