ruco 0.0.20 → 0.0.21

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