ruco 0.0.42 → 0.0.43

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
@@ -72,7 +72,6 @@ TODO
72
72
  - find next (Alt+n)
73
73
  - add selection colors to forms in command_bar
74
74
  - smart staying at column when changing line
75
- - warnings / messages
76
75
  - syntax highlighting
77
76
  - raise when binding to a unsupported key
78
77
  - search history via up/down arrow
@@ -84,6 +83,6 @@ TODO
84
83
 
85
84
  Author
86
85
  ======
87
- [Michael Grosser](http://grosser.it)
88
- grosser.michael@gmail.com
86
+ [Michael Grosser](http://grosser.it)<br/>
87
+ grosser.michael@gmail.com<br/>
89
88
  Hereby placed under public domain, do what you want, just do not hold me accountable...
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.42
1
+ 0.0.43
data/bin/ruco CHANGED
@@ -60,49 +60,44 @@ def init_screen
60
60
  end
61
61
  end
62
62
 
63
- def display(lines, offset, color_mask)
63
+ def display(lines, style_mask)
64
+ columns = Curses.stdscr.maxx
64
65
  @screen ||= [] # current screen is used as cache
66
+ style_mask = style_mask.flatten
65
67
 
66
68
  lines.each_with_index do |content, line|
67
- colors = color_mask[line] || []
69
+ styles = style_mask[line]
68
70
 
69
- # expand line with whitespace
70
- line += offset
71
- clearer = Curses.stdscr.maxx + 1 - content.size
72
- clearer = " " * clearer
73
- content += clearer
74
- content.gsub!("\t",' ') # display tabs as single-space -> nothing breaks
71
+ # expand line with whitespace to overwrite previous content
72
+ missing = columns - content.size
73
+ content += " " * missing
74
+
75
+ # display tabs as single-space -> nothing breaks
76
+ content.gsub!("\t",' ')
75
77
 
76
78
  # cache !?
77
- next if @screen[line] == [content, colors]
78
- @screen[line] = [content, colors]
79
+ next if @screen[line] == [content, styles]
80
+ @screen[line] = [content, styles]
79
81
 
80
- # position at start of line
81
- index = 0
82
+ # position at start of line and draw
82
83
  Curses.setpos(line,0)
83
- Curses.attrset(Curses::A_NORMAL)
84
-
85
- # write colored string
86
- colors.each do |start, color|
87
- Curses.addstr(content[index...start])
88
- Curses.attrset color
89
- index = start
84
+ Ruco::StyleMap.styled(content, styles).each do |style, part|
85
+ Curses.attrset Ruco::StyleMap.curses_style(style)
86
+ Curses.addstr part
90
87
  end
91
- Curses.addstr(content[index...-1])
92
88
 
93
- write(line, 0, (rand(899)+100).to_s) if @options[:debug_cache]
89
+ if @options[:debug_cache]
90
+ write(line, 0, (rand(899)+100).to_s)
91
+ end
94
92
  end
95
93
  end
96
94
 
97
95
  def show_app(app)
98
96
  lines = app.view.naive_split("\n")
99
- color_mask = app.color_mask
97
+ style_map = app.style_map
100
98
 
101
99
  # TODO move this logic into application
102
- display([lines.first], 0, [color_mask.first])
103
- display(lines[1..-2], 1, color_mask[1..-2])
104
- display([lines.last], Curses.stdscr.maxy - 1, [color_mask.last])
105
-
100
+ display(lines, style_map)
106
101
  Curses.setpos(app.cursor.line, app.cursor.column)
107
102
  end
108
103
 
@@ -14,9 +14,10 @@ module Ruco
14
14
  status.view + "\n" + editor.view + command.view
15
15
  end
16
16
 
17
- def color_mask
18
- reverse = [[[0,Curses::A_REVERSE]]]
19
- reverse + editor.color_mask + reverse
17
+ def style_map
18
+ reverse = StyleMap.new(1)
19
+ reverse.add(:reverse, 0, 0...@options[:columns])
20
+ reverse + editor.style_map + reverse
20
21
  end
21
22
 
22
23
  def cursor
@@ -30,4 +30,18 @@ class Array
30
30
  def >=(other)
31
31
  self.>(other) or self.==other
32
32
  end
33
+ end
34
+
35
+ # http://madeofcode.com/posts/74-ruby-core-extension-array-sum
36
+ class Array
37
+ def sum(method = nil, &block)
38
+ if block_given?
39
+ raise ArgumentError, "You cannot pass a block and a method!" if method
40
+ inject(0) { |sum, i| sum + yield(i) }
41
+ elsif method
42
+ inject(0) { |sum, i| sum + i.send(method) }
43
+ else
44
+ inject(0) { |sum, i| sum + i }
45
+ end
46
+ end
33
47
  end
@@ -32,4 +32,16 @@ class String
32
32
  bytes.first
33
33
  end
34
34
  end
35
+ end
36
+
37
+ # http://grosser.it/2010/12/31/ruby-string-indexes-indices-find-all-indexes-in-a-string
38
+ class String
39
+ def indexes(needle)
40
+ found = []
41
+ current_index = -1
42
+ while current_index = index(needle, current_index+1)
43
+ found << current_index
44
+ end
45
+ found
46
+ end
35
47
  end
data/lib/ruco/editor.rb CHANGED
@@ -3,7 +3,7 @@ module Ruco
3
3
  attr_reader :file
4
4
  attr_reader :text_area
5
5
  private :text_area
6
- delegate :view, :color_mask, :cursor,
6
+ delegate :view, :style_map, :cursor,
7
7
  :insert, :indent, :unindent, :delete, :delete_line,
8
8
  :redo, :undo,
9
9
  :selecting, :selection, :text_in_selection, :reset,
@@ -0,0 +1,90 @@
1
+ module Ruco
2
+ class StyleMap
3
+ attr_accessor :lines
4
+
5
+ def initialize(lines)
6
+ @lines = Array.new(lines)
7
+ end
8
+
9
+ def add(style, line, columns)
10
+ @lines[line] ||= []
11
+ @lines[line] << [style, columns]
12
+ end
13
+
14
+ def flatten
15
+ @lines.map do |styles|
16
+ next unless styles
17
+
18
+ # start and one after end of every column-range changes styles
19
+ points_of_change = styles.map{|s,c| [c.first, c.last+1] }.flatten
20
+
21
+ flat = []
22
+
23
+ styles.each do |style, columns|
24
+ points_of_change.each do |point|
25
+ next unless columns.include?(point)
26
+ flat[point] ||= []
27
+ flat[point].unshift style
28
+ end
29
+ end
30
+
31
+ max = styles.map{|s,c|c.last}.max
32
+ flat[max+1] = []
33
+ flat
34
+ end
35
+ end
36
+
37
+ def +(other)
38
+ lines = self.lines + other.lines
39
+ new = StyleMap.new(0)
40
+ new.lines = lines
41
+ new
42
+ end
43
+
44
+ def slice!(*args)
45
+ sliced = lines.slice!(*args)
46
+ new = StyleMap.new(0)
47
+ new.lines = sliced
48
+ new
49
+ end
50
+
51
+ def shift
52
+ slice!(0, 1)
53
+ end
54
+
55
+ def pop
56
+ slice!(-1, 1)
57
+ end
58
+
59
+ STYLES = {
60
+ :normal => 0,
61
+ :reverse => Curses::A_REVERSE
62
+ }
63
+
64
+ def self.styled(content, styles)
65
+ styles ||= []
66
+ content = content.dup
67
+
68
+ build = []
69
+ build << [[]]
70
+
71
+ buffered = ''
72
+ styles.each do |style|
73
+ if style
74
+ build[-1] << buffered
75
+ buffered = ''
76
+
77
+ # set new style
78
+ build << [style]
79
+ end
80
+ buffered << (content.slice!(0,1) || '')
81
+ end
82
+ build[-1] << buffered + content
83
+ build
84
+ end
85
+
86
+ def self.curses_style(styles)
87
+ styles.sum{|style| STYLES[style] or raise("Unknown style #{style}") }
88
+ end
89
+ end
90
+ end
@@ -21,9 +21,9 @@ module Ruco
21
21
  @window.cursor
22
22
  end
23
23
 
24
- def color_mask
24
+ def style_map
25
25
  adjust_window
26
- @window.color_mask(@selection)
26
+ @window.style_map(@selection)
27
27
  end
28
28
 
29
29
  def move(where, *args)
data/lib/ruco/window.rb CHANGED
@@ -49,22 +49,23 @@ module Ruco
49
49
  self.left = result if result
50
50
  end
51
51
 
52
- def color_mask(selection)
53
- mask = Array.new(lines)
52
+ def style_map(selection)
53
+ mask = StyleMap.new(lines)
54
54
  return mask unless selection
55
55
 
56
- mask.map_with_index do |_,line|
56
+ lines.times do |line|
57
57
  visible = visible_area(line)
58
58
  next unless selection.overlap?(visible)
59
59
 
60
60
  first = [selection.first, visible.first].max
61
+ first = first[1] - left
61
62
  last = [selection.last, visible.last].min
63
+ last = last[1] - left
62
64
 
63
- [
64
- [first[1]-left, Curses::A_REVERSE],
65
- [last[1]-left, Curses::A_NORMAL]
66
- ]
65
+ mask.add(:reverse, line, first...last)
67
66
  end
67
+
68
+ mask
68
69
  end
69
70
 
70
71
  def left=(x)
data/lib/ruco.rb CHANGED
@@ -12,6 +12,7 @@ require 'ruco/position'
12
12
  require 'ruco/history'
13
13
  require 'ruco/file_store'
14
14
  require 'ruco/window'
15
+ require 'ruco/style_map'
15
16
 
16
17
  require 'ruco/editor'
17
18
  require 'ruco/status_bar'
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.0.42"
8
+ s.version = "0.0.43"
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"]
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
37
37
  "lib/ruco/keyboard.rb",
38
38
  "lib/ruco/position.rb",
39
39
  "lib/ruco/status_bar.rb",
40
+ "lib/ruco/style_map.rb",
40
41
  "lib/ruco/text_area.rb",
41
42
  "lib/ruco/text_field.rb",
42
43
  "lib/ruco/version.rb",
@@ -52,6 +53,7 @@ Gem::Specification.new do |s|
52
53
  "spec/ruco/history_spec.rb",
53
54
  "spec/ruco/keyboard_spec.rb",
54
55
  "spec/ruco/status_bar_spec.rb",
56
+ "spec/ruco/style_map_spec.rb",
55
57
  "spec/ruco/text_area_spec.rb",
56
58
  "spec/ruco/window_spec.rb",
57
59
  "spec/ruco_spec.rb",
@@ -59,7 +61,7 @@ Gem::Specification.new do |s|
59
61
  ]
60
62
  s.homepage = %q{http://github.com/grosser/ruco}
61
63
  s.require_paths = ["lib"]
62
- s.rubygems_version = %q{1.3.7}
64
+ s.rubygems_version = %q{1.4.2}
63
65
  s.summary = %q{Commandline editor written in ruby}
64
66
  s.test_files = [
65
67
  "spec/ruco/application_spec.rb",
@@ -72,6 +74,7 @@ Gem::Specification.new do |s|
72
74
  "spec/ruco/history_spec.rb",
73
75
  "spec/ruco/keyboard_spec.rb",
74
76
  "spec/ruco/status_bar_spec.rb",
77
+ "spec/ruco/style_map_spec.rb",
75
78
  "spec/ruco/text_area_spec.rb",
76
79
  "spec/ruco/window_spec.rb",
77
80
  "spec/ruco_spec.rb",
@@ -79,7 +82,6 @@ Gem::Specification.new do |s|
79
82
  ]
80
83
 
81
84
  if s.respond_to? :specification_version then
82
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
83
85
  s.specification_version = 3
84
86
 
85
87
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -363,9 +363,9 @@ describe Ruco::Editor do
363
363
  end
364
364
  end
365
365
 
366
- describe :color_mask do
366
+ describe :style_map do
367
367
  it "is empty by default" do
368
- editor.color_mask.should == [nil,nil,nil]
368
+ editor.style_map.flatten.should == [nil,nil,nil]
369
369
  end
370
370
 
371
371
  it "shows one-line selection" do
@@ -373,10 +373,10 @@ describe Ruco::Editor do
373
373
  editor.selecting do
374
374
  move(:to, 0, 4)
375
375
  end
376
- editor.color_mask.should == [
377
- [[0,262144],[4,0]],
378
- nil,
376
+ editor.style_map.flatten.should == [
377
+ [[:reverse], nil, nil, nil, nil, []],
379
378
  nil,
379
+ nil
380
380
  ]
381
381
  end
382
382
 
@@ -386,10 +386,10 @@ describe Ruco::Editor do
386
386
  editor.selecting do
387
387
  move(:to, 1, 1)
388
388
  end
389
- editor.color_mask.should == [
390
- [[1,262144],[5,0]],
391
- [[0,262144],[1,0]],
392
- nil,
389
+ editor.style_map.flatten.should == [
390
+ [nil, [:reverse], nil, nil, nil, nil, []],
391
+ [[:reverse], nil, []],
392
+ nil
393
393
  ]
394
394
  end
395
395
 
@@ -399,10 +399,10 @@ describe Ruco::Editor do
399
399
  editor.selecting do
400
400
  move(:to, 0, 4)
401
401
  end
402
- editor.color_mask.should == [
403
- [[2,262144], [4,0]],
404
- nil,
402
+ editor.style_map.flatten.should == [
403
+ [nil, nil, [:reverse], nil, nil, []],
405
404
  nil,
405
+ nil
406
406
  ]
407
407
  end
408
408
 
@@ -412,10 +412,10 @@ describe Ruco::Editor do
412
412
  editor.selecting do
413
413
  move(:to, 1, 4)
414
414
  end
415
- editor.color_mask.should == [
416
- nil,
417
- [[2,262144], [4,0]],
415
+ editor.style_map.flatten.should == [
418
416
  nil,
417
+ [nil, nil, [:reverse], nil, nil, []],
418
+ nil
419
419
  ]
420
420
  end
421
421
 
@@ -429,10 +429,10 @@ describe Ruco::Editor do
429
429
  end
430
430
  editor.view.should == "789\n789\n789\n"
431
431
  editor.cursor.should == [2,2]
432
- editor.color_mask.should == [
433
- [[1,262144],[5,0]], # start to end of screen
434
- [[0,262144],[5,0]], # 0 to end of screen
435
- [[0,262144],[2,0]], # 0 to end of selection
432
+ editor.style_map.flatten.should == [
433
+ [nil, [:reverse], nil, nil, nil, nil, []], # start to end of screen
434
+ [[:reverse], nil, nil, nil, nil, nil, []], # 0 to end of screen
435
+ [[:reverse], nil, nil, []] # 0 to end of selection
436
436
  ]
437
437
  end
438
438
  end
@@ -0,0 +1,97 @@
1
+ require File.expand_path('spec/spec_helper')
2
+
3
+ describe Ruco::StyleMap do
4
+ let(:map){ Ruco::StyleMap.new(3) }
5
+
6
+ describe :flat do
7
+ it "is empty by default" do
8
+ map.flatten.should == [nil, nil, nil]
9
+ end
10
+
11
+ it "reproduces simple styles" do
12
+ map.add(:red, 1, 3..5)
13
+ # red from 3 to 5
14
+ map.flatten.should == [
15
+ nil,
16
+ [nil, nil, nil, [:red], nil, nil, []],
17
+ nil
18
+ ]
19
+ end
20
+
21
+ it "reproduces merged styles" do
22
+ map.add(:red, 1, 3..5)
23
+ map.add(:reverse, 1, 2..4)
24
+ # reverse at 2 -- reverse and red at 3,4 -- red at 5
25
+ map.flatten.should == [
26
+ nil,
27
+ [nil, nil, [:reverse], [:reverse, :red], nil, [:red], []],
28
+ nil
29
+ ]
30
+ end
31
+ end
32
+
33
+ describe 'array style operations' do
34
+ it "adds two maps" do
35
+ s1 = Ruco::StyleMap.new(1)
36
+ s1.add(:reverse, 0, 0..1)
37
+ s2 = Ruco::StyleMap.new(2)
38
+ s2.add(:reverse, 0, 2..3)
39
+ (s1 + s2).flatten.should == [
40
+ [[:reverse], nil, []],
41
+ [nil, nil, [:reverse], nil, []],
42
+ nil
43
+ ]
44
+ end
45
+
46
+ it "can shift" do
47
+ s = Ruco::StyleMap.new(2)
48
+ s.add(:reverse, 0, 0..1)
49
+ s.add(:reverse, 1, 1..2)
50
+ s.shift.flatten.should == [[[:reverse],nil,[]]]
51
+ s.flatten.should == [[nil, [:reverse],nil,[]]]
52
+ end
53
+
54
+ it "can pop" do
55
+ s = Ruco::StyleMap.new(2)
56
+ s.add(:reverse, 0, 0..1)
57
+ s.add(:reverse, 1, 1..2)
58
+ s.pop.flatten.should == [[nil, [:reverse],nil,[]]]
59
+ s.flatten.should == [[[:reverse],nil,[]]]
60
+ end
61
+ end
62
+
63
+ describe :styled do
64
+ it "can style an unstyled line" do
65
+ Ruco::StyleMap.styled("a", nil).should == [[[], "a"]]
66
+ end
67
+
68
+ it "can style an styled line" do
69
+ Ruco::StyleMap.styled("a", [[:reverse],nil]).should == [[[], ""], [[:reverse], "a"]]
70
+ end
71
+
72
+ it "keeps unstyled parts" do
73
+ Ruco::StyleMap.styled("abc", [[:reverse],[]]).should == [[[], ""], [[:reverse], "a"],[[],'bc']]
74
+ end
75
+ end
76
+
77
+ describe :curses_style do
78
+ it "is 'normal' for nothing" do
79
+ Ruco::StyleMap.curses_style([]).should == Curses::A_NORMAL
80
+ end
81
+
82
+ it "is red for red" do
83
+ pending
84
+ Ruco::StyleMap.curses_style([:red]).should == Curses::color_pair( Curses::COLOR_RED )
85
+ end
86
+
87
+ it "is reverse for reverse" do
88
+ Ruco::StyleMap.curses_style([:reverse]).should == Curses::A_REVERSE
89
+ end
90
+
91
+ it "raises on unknown style" do
92
+ lambda{
93
+ Ruco::StyleMap.curses_style([:foo])
94
+ }.should raise_error
95
+ end
96
+ end
97
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruco
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 73
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
8
  - 0
8
- - 42
9
- version: 0.0.42
9
+ - 43
10
+ version: 0.0.43
10
11
  platform: ruby
11
12
  authors:
12
13
  - Michael Grosser
@@ -18,20 +19,21 @@ date: 2011-02-06 00:00:00 +01:00
18
19
  default_executable: ruco
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
- name: clipboard
22
22
  requirement: &id001 !ruby/object:Gem::Requirement
23
23
  none: false
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
+ hash: 51
27
28
  segments:
28
29
  - 0
29
30
  - 9
30
31
  - 4
31
32
  version: 0.9.4
32
- type: :runtime
33
33
  prerelease: false
34
34
  version_requirements: *id001
35
+ type: :runtime
36
+ name: clipboard
35
37
  description:
36
38
  email: michael@grosser.it
37
39
  executables:
@@ -64,6 +66,7 @@ files:
64
66
  - lib/ruco/keyboard.rb
65
67
  - lib/ruco/position.rb
66
68
  - lib/ruco/status_bar.rb
69
+ - lib/ruco/style_map.rb
67
70
  - lib/ruco/text_area.rb
68
71
  - lib/ruco/text_field.rb
69
72
  - lib/ruco/version.rb
@@ -79,6 +82,7 @@ files:
79
82
  - spec/ruco/history_spec.rb
80
83
  - spec/ruco/keyboard_spec.rb
81
84
  - spec/ruco/status_bar_spec.rb
85
+ - spec/ruco/style_map_spec.rb
82
86
  - spec/ruco/text_area_spec.rb
83
87
  - spec/ruco/window_spec.rb
84
88
  - spec/ruco_spec.rb
@@ -97,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
101
  requirements:
98
102
  - - ">="
99
103
  - !ruby/object:Gem::Version
100
- hash: 916252531
104
+ hash: 3
101
105
  segments:
102
106
  - 0
103
107
  version: "0"
@@ -106,13 +110,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
110
  requirements:
107
111
  - - ">="
108
112
  - !ruby/object:Gem::Version
113
+ hash: 3
109
114
  segments:
110
115
  - 0
111
116
  version: "0"
112
117
  requirements: []
113
118
 
114
119
  rubyforge_project:
115
- rubygems_version: 1.3.7
120
+ rubygems_version: 1.4.2
116
121
  signing_key:
117
122
  specification_version: 3
118
123
  summary: Commandline editor written in ruby
@@ -127,6 +132,7 @@ test_files:
127
132
  - spec/ruco/history_spec.rb
128
133
  - spec/ruco/keyboard_spec.rb
129
134
  - spec/ruco/status_bar_spec.rb
135
+ - spec/ruco/style_map_spec.rb
130
136
  - spec/ruco/text_area_spec.rb
131
137
  - spec/ruco/window_spec.rb
132
138
  - spec/ruco_spec.rb