ruco 0.0.20 → 0.0.21

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
@@ -61,7 +61,7 @@ TIPS
61
61
  TODO
62
62
  =====
63
63
  - session storage (stay at same line/column when reopening)
64
- - past detection ? (ctrl+shift+insert / Cmd+v) -> no indentation
64
+ - paste detection ? (ctrl+shift+insert / Cmd+v) -> no indentation
65
65
  - selecting -> delete / overwrite / copy / cut
66
66
  - smart staying at end of line/column when changing line
67
67
  - warnings / messages
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.20
1
+ 0.0.21
data/bin/ruco CHANGED
@@ -95,16 +95,24 @@ end
95
95
  @options = parse_options
96
96
 
97
97
  require 'ruco'
98
- require 'ruco/keyboard' # needs curses <-> not loaded for specs
99
98
 
100
99
  app = Ruco::Application.new(ARGV[0], :lines => Curses.stdscr.maxy, :columns => Curses.stdscr.maxx)
101
100
 
102
101
  init_screen do
103
102
  show_app app
104
103
 
105
- Keyboard.listen do |key|
104
+ Keyboard.input do
105
+ Curses.getch
106
+ end
107
+
108
+ Keyboard.output do |key|
106
109
  debug_key(key) if @options[:debug_keys]
107
- result = app.key key
110
+ if key == :resize
111
+ app.resize(Curses.stdscr.maxy, Curses.stdscr.maxx)
112
+ @screen.clear # clear cache
113
+ else
114
+ result = app.key key
115
+ end
108
116
  break if result == :quit
109
117
  show_app app
110
118
  end
data/lib/ruco.rb CHANGED
@@ -3,6 +3,7 @@ require 'ruco/core_ext/string'
3
3
  require 'ruco/core_ext/array'
4
4
  require 'ruco/core_ext/hash'
5
5
 
6
+ require 'ruco/keyboard'
6
7
  require 'ruco/cursor'
7
8
 
8
9
  require 'ruco/editor'
@@ -77,6 +77,13 @@ module Ruco
77
77
  instance_exec(&block)
78
78
  end
79
79
 
80
+ def resize(lines, columns)
81
+ @options[:lines] = lines
82
+ @options[:columns] = columns
83
+ create_components
84
+ @editor.resize(editor_lines, columns)
85
+ end
86
+
80
87
  private
81
88
 
82
89
  attr_reader :editor, :status, :command
@@ -134,14 +141,16 @@ module Ruco
134
141
 
135
142
  def create_components
136
143
  @status_lines = 1
137
- command_lines = 1
138
- editor_lines = @options[:lines] - @status_lines - command_lines
139
-
140
- @editor = Ruco::Editor.new(@file, :lines => editor_lines, :columns => @options[:columns])
144
+ @editor ||= Ruco::Editor.new(@file, :lines => editor_lines, :columns => @options[:columns])
141
145
  @status = Ruco::StatusBar.new(@editor, :columns => @options[:columns])
142
146
  @command = Ruco::CommandBar.new(:columns => @options[:columns])
143
147
  command.cursor_line = editor_lines
144
148
  @focused = @editor
145
149
  end
150
+
151
+ def editor_lines
152
+ command_lines = 1
153
+ editor_lines = @options[:lines] - @status_lines - command_lines
154
+ end
146
155
  end
147
156
  end
data/lib/ruco/editor.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  module Ruco
2
2
  class Editor
3
3
  attr_reader :file
4
- delegate :view, :move, :cursor, :delete_line, :to => :text_area
4
+ delegate :view, :move, :cursor, :resize, :delete_line, :to => :text_area
5
5
 
6
6
  def initialize(file, options)
7
7
  @file = file
8
- @options = options
9
8
  content = (File.exist?(@file) ? File.read(@file) : '')
10
- @text_area = TextArea.new(content, @options)
9
+ @text_area = TextArea.new(content, options)
11
10
  @modified = false
12
11
  end
13
12
 
data/lib/ruco/keyboard.rb CHANGED
@@ -1,71 +1,127 @@
1
+ require 'curses'
2
+
1
3
  class Keyboard
4
+ ENTER = 13
5
+ IS_18 = RUBY_VERSION =~ /^1\.8/
2
6
  SEQUENCE_TIMEOUT = 0.01
3
7
  NOTHING = 4294967295 # getch returns this as 'nothing' on 1.8 but nil on 1.9.2
4
8
  A_TO_Z = ('a'..'z').to_a
5
9
 
6
- def self.listen
7
- loop do
8
- key = Curses.getch || NOTHING
10
+ def self.input(&block)
11
+ @input = block
12
+ end
13
+
14
+ def self.output
15
+ @sequence = nil
16
+ @started = Time.now.to_f
9
17
 
10
- if @sequence
11
- if sequence_finished?
12
- yield @sequence.pack('c*').force_encoding('utf-8')
13
- @sequence = nil
18
+ loop do
19
+ key = fetch_user_input
20
+ if sequence_finished?
21
+ if needs_paste_fix?(@sequence)
22
+ yield bytes_to_string(@sequence)
14
23
  else
15
- @sequence << key unless key == NOTHING
24
+ bytes_to_key_codes(@sequence).each{|c| yield c }
16
25
  end
17
- next
26
+ @sequence = nil
18
27
  end
19
-
20
28
  next if key == NOTHING
29
+ start_or_append_sequence key
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def self.translate_key_to_code(key)
36
+ case key
37
+
38
+ # move
39
+ when Curses::Key::UP then :up
40
+ when Curses::Key::DOWN then :down
41
+ when Curses::Key::RIGHT then :right
42
+ when Curses::Key::LEFT then :left
43
+ when 554 then :"Ctrl+right"
44
+ when 555 then :"Ctrl+Shift+right"
45
+ when 539 then :"Ctrl+left"
46
+ when 540 then :"Ctrl+Shift+left"
47
+ when 560 then :"Ctrl+up"
48
+ when 519 then :"Ctrl+down"
49
+ when Curses::KEY_END then :end
50
+ when Curses::KEY_HOME then :home
51
+ when Curses::KEY_NPAGE then :page_down
52
+ when Curses::KEY_PPAGE then :page_up
53
+ when Curses::KEY_IC then :insert
54
+ when Curses::KEY_F0..Curses::KEY_F63 then :"F#{key - Curses::KEY_F0}"
55
+
56
+ # modify
57
+ when 9 then :tab
58
+ when ENTER then :enter # shadows Ctrl+m
59
+ when 263, 127 then :backspace # ubuntu / mac
60
+ when Curses::KEY_DC then :delete
61
+
62
+ # misc
63
+ when 0 then :"Ctrl+space"
64
+ when 1..26 then :"Ctrl+#{A_TO_Z[key-1]}"
65
+ when 27 then :escape
66
+ when Curses::KEY_RESIZE then :resize
67
+ else
68
+ key.chr
69
+ end
70
+ end
71
+
72
+ def self.fetch_user_input
73
+ key = @input.call || NOTHING
74
+ key = key.ord if key.is_a?(String) # ruby 1.9 fix
75
+ key
76
+ end
77
+
78
+ def self.start_or_append_sequence(key)
79
+ @started = Time.now.to_f
80
+ @sequence ||= []
81
+ @sequence << key
82
+ end
83
+
84
+ def self.bytes_to_string(bytes)
85
+ bytes.pack('c*').gsub("\r","\n").force_encoding('utf-8')
86
+ end
87
+
88
+ # split a text so fast-typers do not get bugs like ^B^C in output
89
+ def self.bytes_to_key_codes(bytes)
90
+ result = []
91
+ multi_byte = nil
21
92
 
22
- key = key.ord if key.is_a?(String) # ruby 1.9 fix
23
-
24
- code = case key
25
-
26
- # move
27
- when Curses::Key::UP then :up
28
- when Curses::Key::DOWN then :down
29
- when Curses::Key::RIGHT then :right
30
- when Curses::Key::LEFT then :left
31
- when 554 then :"Ctrl+right"
32
- when 555 then :"Ctrl+Shift+right"
33
- when 539 then :"Ctrl+left"
34
- when 540 then :"Ctrl+Shift+left"
35
- when 560 then :"Ctrl+up"
36
- when 519 then :"Ctrl+down"
37
- when Curses::KEY_END then :end
38
- when Curses::KEY_HOME then :home
39
- when Curses::KEY_NPAGE then :page_down
40
- when Curses::KEY_PPAGE then :page_up
41
- when Curses::KEY_IC then :insert
42
- when Curses::KEY_F0..Curses::KEY_F63 then :"F#{key - Curses::KEY_F0}"
43
-
44
- # modify
45
- when 9 then :tab
46
- when 13 then :enter # shadows Ctrl+m
47
- when 263, 127 then :backspace # ubuntu / mac
48
- when Curses::KEY_DC then :delete
49
-
50
- # misc
51
- when 0 then :"Ctrl+space"
52
- when 1..26 then :"Ctrl+#{A_TO_Z[key-1]}"
53
- when 27 then :escape
54
- when 195..197 # start of unicode sequence
55
- @sequence = [key]
56
- @sequence_started = Time.now.to_f
57
- next
93
+ bytes.each do |byte|
94
+ if multi_byte_part?(byte)
95
+ multi_byte ||= []
96
+ multi_byte << byte
58
97
  else
59
- key > 255 ? key : key.chr # output printable chars
98
+ if multi_byte
99
+ # finish multi-byte char
100
+ result << bytes_to_string(multi_byte)
101
+ multi_byte = nil
102
+ end
103
+ result << translate_key_to_code(byte)
60
104
  end
105
+ end
61
106
 
62
- yield code
107
+ if multi_byte
108
+ result << bytes_to_string(multi_byte)
63
109
  end
110
+
111
+ result
64
112
  end
65
113
 
66
- private
114
+ # not ascii and not control-char
115
+ def self.multi_byte_part?(byte)
116
+ 127 < byte and byte < 256
117
+ end
67
118
 
68
119
  def self.sequence_finished?
69
- (Time.now.to_f - @sequence_started) > SEQUENCE_TIMEOUT
120
+ @sequence and (Time.now.to_f - @started) > SEQUENCE_TIMEOUT
121
+ end
122
+
123
+ # paste of multiple \n or \n in text would cause weird indentation
124
+ def self.needs_paste_fix?(sequence)
125
+ sequence.size > 1 and sequence.include?(ENTER)
70
126
  end
71
127
  end
@@ -95,6 +95,11 @@ module Ruco
95
95
  (lines * "\n").freeze
96
96
  end
97
97
 
98
+ def resize(lines, columns)
99
+ @options[:lines] = lines
100
+ @options[:columns] = columns
101
+ end
102
+
98
103
  protected
99
104
 
100
105
  def with_lines_as_string
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.20"
8
+ s.version = "0.0.21"
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-19}
12
+ s.date = %q{2011-01-21}
13
13
  s.default_executable = %q{ruco}
14
14
  s.email = %q{michael@grosser.it}
15
15
  s.executables = ["ruco"]
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "spec/ruco/core_ext/string_spec.rb",
41
41
  "spec/ruco/editor_spec.rb",
42
42
  "spec/ruco/form_spec.rb",
43
+ "spec/ruco/keyboard_spec.rb",
43
44
  "spec/ruco/status_bar_spec.rb",
44
45
  "spec/ruco/text_area_spec.rb",
45
46
  "spec/ruco_spec.rb",
@@ -55,6 +56,7 @@ Gem::Specification.new do |s|
55
56
  "spec/ruco/core_ext/string_spec.rb",
56
57
  "spec/ruco/editor_spec.rb",
57
58
  "spec/ruco/form_spec.rb",
59
+ "spec/ruco/keyboard_spec.rb",
58
60
  "spec/ruco/status_bar_spec.rb",
59
61
  "spec/ruco/text_area_spec.rb",
60
62
  "spec/ruco_spec.rb",
@@ -47,6 +47,12 @@ describe Ruco::Application do
47
47
  app.cursor.should == [2,0] # 0 offset + 1 for statusbar
48
48
  end
49
49
 
50
+ it "can resize" do
51
+ write("01234567\n1\n2\n3\n4\n5678910111213\n6\n7\n8")
52
+ app.resize(8, 7)
53
+ app.view.should == "#{status}0123456\n1\n2\n3\n4\n5678910\n#{command}"
54
+ end
55
+
50
56
  describe 'closing' do
51
57
  it "can quit" do
52
58
  result = app.key(:"Ctrl+w")
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path('spec/spec_helper')
3
+
4
+ describe Keyboard do
5
+ def output
6
+ keys = []
7
+ Timeout.timeout(0.3) do
8
+ Keyboard.output do |key|
9
+ keys << key
10
+ end
11
+ end
12
+ keys
13
+ rescue Timeout::Error
14
+ keys
15
+ end
16
+
17
+ def type(chars)
18
+ Keyboard.input do
19
+ char = chars.shift
20
+ if char == :sleep_long
21
+ sleep 0.1
22
+ nil
23
+ else
24
+ char
25
+ end
26
+ end
27
+ end
28
+
29
+ it "can listen to simple keys" do
30
+ type [32]
31
+ output.should == [' ']
32
+ end
33
+
34
+ it "can listen to multiple keys" do
35
+ type [32, :sleep_long, 97]
36
+ output.should == [' ','a']
37
+ end
38
+
39
+ it "can listen ctrl+x" do
40
+ type [26]
41
+ output.should == [:'Ctrl+z']
42
+ end
43
+
44
+ it "can listen to enter" do
45
+ type [13]
46
+ output.should == [:enter]
47
+ end
48
+
49
+ it "does not listen to nil / NOTHING" do
50
+ type [nil, Keyboard::NOTHING, 13]
51
+ output.should == [:enter]
52
+ end
53
+
54
+ it "can fetch uft8-chars" do
55
+ type [195, 164]
56
+ output.should == ['ä']
57
+ end
58
+
59
+ it "cannot fetch long sequences" do
60
+ type [195, :sleep_long, 164]
61
+ output.map{|s|s.bytes.to_a}.should == [[195], [164]]
62
+ end
63
+
64
+ it "fetches pastes between normal key strokes" do
65
+ type [32, :sleep_long, 32, 13, 32, :sleep_long, 32]
66
+ output.should == [' '," \n ",' ']
67
+ end
68
+
69
+ it "returns pastes that do not need indentation fix as normal chars" do
70
+ type [32, :sleep_long, 32, 32, 32, :sleep_long, 32]
71
+ output.should == [' ',' ',' ',' ',' ']
72
+ end
73
+
74
+ it "returns control chars separately" do
75
+ type [260, 127, 127, 261, 260, 195, 164, 261, 260, 195, 164]
76
+ output.should == [:left, :backspace, :backspace, :right, :left, "ä", :right, :left, "ä"]
77
+ end
78
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  $LOAD_PATH.unshift 'lib'
2
2
  require 'ruco'
3
+ require 'timeout'
3
4
 
4
5
  require 'tempfile'
5
6
  class Tempfile
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 20
9
- version: 0.0.20
8
+ - 21
9
+ version: 0.0.21
10
10
  platform: ruby
11
11
  authors:
12
12
  - Michael Grosser
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-19 00:00:00 +01:00
17
+ date: 2011-01-21 00:00:00 +01:00
18
18
  default_executable: ruco
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -67,6 +67,7 @@ files:
67
67
  - spec/ruco/core_ext/string_spec.rb
68
68
  - spec/ruco/editor_spec.rb
69
69
  - spec/ruco/form_spec.rb
70
+ - spec/ruco/keyboard_spec.rb
70
71
  - spec/ruco/status_bar_spec.rb
71
72
  - spec/ruco/text_area_spec.rb
72
73
  - spec/ruco_spec.rb
@@ -85,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
86
  requirements:
86
87
  - - ">="
87
88
  - !ruby/object:Gem::Version
88
- hash: 588820097
89
+ hash: -755587743
89
90
  segments:
90
91
  - 0
91
92
  version: "0"
@@ -110,6 +111,7 @@ test_files:
110
111
  - spec/ruco/core_ext/string_spec.rb
111
112
  - spec/ruco/editor_spec.rb
112
113
  - spec/ruco/form_spec.rb
114
+ - spec/ruco/keyboard_spec.rb
113
115
  - spec/ruco/status_bar_spec.rb
114
116
  - spec/ruco/text_area_spec.rb
115
117
  - spec/ruco_spec.rb