ruco 0.1.12 → 0.1.13

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