ruco 0.1.14 → 0.2.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +11 -1
- data/Rakefile +27 -10
- data/Readme.md +14 -0
- data/VERSION +1 -1
- data/bin/ruco +15 -6
- data/lib/ruco.rb +25 -0
- data/lib/ruco/application.rb +1 -1
- data/lib/ruco/array_processor.rb +53 -0
- data/lib/ruco/core_ext/object.rb +26 -0
- data/lib/ruco/core_ext/range.rb +16 -1
- data/lib/ruco/editor.rb +1 -0
- data/lib/ruco/editor/colors.rb +97 -0
- data/lib/ruco/editor_area.rb +1 -0
- data/lib/ruco/file_store.rb +23 -5
- data/lib/ruco/screen.rb +61 -7
- data/lib/ruco/style_map.rb +6 -7
- data/lib/ruco/syntax_parser.rb +33 -0
- data/lib/ruco/tm_theme.rb +45 -0
- data/lib/ruco/window.rb +19 -14
- data/playground/benchmark_syntax_parser.rb +23 -0
- data/ruco.gemspec +21 -22
- data/spec/fixtures/railscasts.tmTheme +369 -0
- data/spec/fixtures/slow.js +4 -0
- data/spec/fixtures/test.tmTheme +186 -0
- data/spec/ruco/application_spec.rb +2 -2
- data/spec/ruco/array_processor_spec.rb +46 -0
- data/spec/ruco/command_bar_spec.rb +1 -1
- data/spec/ruco/core_ext/range_spec.rb +37 -0
- data/spec/ruco/editor_spec.rb +137 -19
- data/spec/ruco/file_store_spec.rb +45 -0
- data/spec/ruco/screen_spec.rb +39 -2
- data/spec/ruco/style_map_spec.rb +12 -1
- data/spec/ruco/syntax_parser_spec.rb +74 -0
- data/spec/ruco/tm_theme_spec.rb +14 -0
- data/spec/spec_helper.rb +6 -2
- metadata +57 -30
data/Gemfile
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
3
|
gem 'clipboard', '>=0.9.8'
|
4
|
+
gem 'ultraviolet1x'
|
5
|
+
gem 'language_sniffer'
|
4
6
|
|
5
7
|
group :dev do # not development <-> would add unneeded development dependencies in gemspec
|
8
|
+
gem 'oniguruma', :platform => :ruby_18
|
6
9
|
gem 'ffi', :platform => [:mingw]
|
7
10
|
gem 'rake'
|
8
11
|
gem 'rspec', '~>2'
|
data/Gemfile.lock
CHANGED
@@ -8,7 +8,10 @@ GEM
|
|
8
8
|
bundler (~> 1.0.0)
|
9
9
|
git (>= 1.2.5)
|
10
10
|
rake
|
11
|
-
|
11
|
+
language_sniffer (1.0.0)
|
12
|
+
oniguruma (1.1.0)
|
13
|
+
plist (3.1.0)
|
14
|
+
rake (0.9.2)
|
12
15
|
rspec (2.6.0)
|
13
16
|
rspec-core (~> 2.6.0)
|
14
17
|
rspec-expectations (~> 2.6.0)
|
@@ -17,6 +20,10 @@ GEM
|
|
17
20
|
rspec-expectations (2.6.0)
|
18
21
|
diff-lcs (~> 1.1.2)
|
19
22
|
rspec-mocks (2.6.0)
|
23
|
+
textpow1x (0.11.0)
|
24
|
+
plist (>= 3.0.1)
|
25
|
+
ultraviolet1x (0.12.1)
|
26
|
+
textpow1x (>= 0.11.0)
|
20
27
|
|
21
28
|
PLATFORMS
|
22
29
|
ruby
|
@@ -25,5 +32,8 @@ DEPENDENCIES
|
|
25
32
|
clipboard (>= 0.9.8)
|
26
33
|
ffi
|
27
34
|
jeweler
|
35
|
+
language_sniffer
|
36
|
+
oniguruma
|
28
37
|
rake
|
29
38
|
rspec (~> 2)
|
39
|
+
ultraviolet1x
|
data/Rakefile
CHANGED
@@ -21,19 +21,23 @@ end
|
|
21
21
|
|
22
22
|
task :try_color do
|
23
23
|
require 'curses'
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
# fix colors for xterm...
|
25
|
+
#if Curses::has_colors?
|
26
|
+
ENV['TERM'] += '-256color' if ENV['TERM'] == 'xterm'
|
27
|
+
Curses::start_color
|
28
|
+
# initialize every color we want to use
|
29
|
+
# id, foreground, background
|
30
|
+
#Curses.use_default_colors if defined? Curses.use_default_colors # 1.9 only, maybe helps to get real white...
|
31
|
+
Curses::init_pair( 32, 253, 39 )
|
32
|
+
#Curses::init_pair( 32, -1, -1 )
|
33
|
+
#Curses::init_pair( Curses::COLOR_RED, Curses::COLOR_RED, Curses::COLOR_BLACK )
|
34
|
+
#Curses::init_pair( Curses::COLOR_GREEN, Curses::COLOR_GREEN, Curses::COLOR_BLACK )
|
35
|
+
#end
|
32
36
|
|
33
37
|
Curses.setpos(0,0)
|
34
|
-
Curses.attrset(Curses
|
38
|
+
Curses.attrset(Curses::color_pair( 32 )) # fetch color pair with the id xxx
|
35
39
|
Curses.addstr("xxxxxxxx\nyyyyyyy");
|
36
|
-
Curses.attrset(Curses
|
40
|
+
Curses.attrset(Curses::color_pair( 32 ))
|
37
41
|
Curses.addstr("xxxxxxxx\nyyyyyyy");
|
38
42
|
Curses.getch
|
39
43
|
end
|
@@ -61,6 +65,19 @@ task :key do
|
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
68
|
+
task :parse_syntax do
|
69
|
+
require 'ruco/array_processor'
|
70
|
+
require 'ultra_pow_list'
|
71
|
+
UltraPowList.make_loadable
|
72
|
+
require 'textpow'
|
73
|
+
require 'uv'
|
74
|
+
puts ruby = File.join(Uv.path.first,'uv', 'syntax','ruby.syntax')
|
75
|
+
syntax = Textpow::SyntaxNode.load(ruby)
|
76
|
+
processor = Ruco::ArrayProcessor.new
|
77
|
+
result = syntax.parse( "class Foo\n def xxx;end\nend", processor )
|
78
|
+
puts result.inspect
|
79
|
+
end
|
80
|
+
|
64
81
|
begin
|
65
82
|
require 'jeweler'
|
66
83
|
Jeweler::Tasks.new do |gem|
|
data/Readme.md
CHANGED
@@ -3,6 +3,7 @@ Simple, extendable, test-driven commandline editor written in ruby, for Linux/Ma
|
|
3
3
|
Features:
|
4
4
|
|
5
5
|
- **Intuitive interface**
|
6
|
+
- TextMate Syntax and Theme support
|
6
7
|
- selecting via Shift + arrow-keys (only Linux or iTerm) or 'select mode' Ctrl+b + arrow-keys
|
7
8
|
- move line up/down (Alt+Ctrl+up/down)
|
8
9
|
- Tab -> indent / Shift+Tab -> unindent
|
@@ -21,6 +22,9 @@ Features:
|
|
21
22
|
- (optional) blank line before eof on save
|
22
23
|
- (optional) line numbers
|
23
24
|
|
25
|
+
![ruco with railscasts theme](http://dl.dropbox.com/u/2670385/Web/ruco-with-railscasts-theme.png)<br/>
|
26
|
+
(to get colors on ruby 1.8, install oniguruma [OsX](http://blog.pastie.org/2010/01/oniguruma-on-snow-leopard.html) / [Ubuntu](http://blog.loftninjas.org/2008/09/25/installing-the-oniguruma-gem-on-debianubuntu/))
|
27
|
+
|
24
28
|
Install
|
25
29
|
=======
|
26
30
|
sudo gem install ruco
|
@@ -40,6 +44,10 @@ Customize
|
|
40
44
|
options.editor_remove_trailing_whitespace_on_save = true # default false
|
41
45
|
options.editor_blank_line_before_eof_on_save = true # default false
|
42
46
|
options.editor_line_numbers = true # default false
|
47
|
+
|
48
|
+
# Use any Textmate theme e.g. from http://wiki.macromates.com/Themes/UserSubmittedThemes
|
49
|
+
# use a url that points directly to the theme, e.g. github 'raw' urls
|
50
|
+
options.color_theme = "https://raw.github.com/deplorableword/textmate-solarized/master/Solarized%20%28dark%29.tmTheme"
|
43
51
|
...
|
44
52
|
|
45
53
|
# bind a key
|
@@ -83,6 +91,12 @@ TIPS
|
|
83
91
|
|
84
92
|
TODO
|
85
93
|
=====
|
94
|
+
- only do syntax parsing for changed lines + selected lines <-> will not be redrawn anyhow
|
95
|
+
- try to use complete file coloring as removed in 26d6da4
|
96
|
+
- javascript syntax parsing is slow and often causes syntax-timeouts
|
97
|
+
- some languages are still not mapped correctly to their syntax file
|
98
|
+
[languages](https://github.com/grosser/language_sniffer/blob/master/lib/language_sniffer/languages.yml) <->
|
99
|
+
[syntaxes](https://github.com/grosser/ultraviolet/tree/master/syntax)
|
86
100
|
- do not fall back to 0:0 after undoing the first change
|
87
101
|
- check writable status every x seconds (e.g. in background) -> faster while typing
|
88
102
|
- search help e.g. 'Nothing found' '#4 of 6 hits' 'no more hits, start from beginning ?'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0.beta
|
data/bin/ruco
CHANGED
@@ -21,6 +21,7 @@ Options:
|
|
21
21
|
BANNER
|
22
22
|
opts.on("-c", "--convert-tabs","Convert tabs to spaces") { options[:convert_tabs] = true }
|
23
23
|
opts.on("-u", "--undo-stack-size SIZE","Maximum size of the undo stack. 0 allows for a complete undo stack.") {|size| options[:undo_stack_size] = size.to_i }
|
24
|
+
opts.on("-n", "--no-colors","No colors -- helps performance / broken terminals") { options[:no_colors] = true }
|
24
25
|
opts.on("--debug-cache","Show caching in action") { options[:debug_cache] = true }
|
25
26
|
opts.on("--debug-keys", "Show pressed keys") { options[:debug_keys] = true }
|
26
27
|
opts.on("-v", "--version","Show Version"){
|
@@ -44,15 +45,23 @@ def log(stuff)
|
|
44
45
|
File.open('ruco.log','ab'){|f| f.puts stuff }
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
+
options = parse_options
|
49
|
+
|
50
|
+
# do not use colors if told so or the terminal does not support colors
|
51
|
+
$ruco_colors = (
|
52
|
+
not options[:no_colors] and
|
53
|
+
RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ and # windows
|
54
|
+
ENV['TERM'] != 'rxvt' and
|
55
|
+
ENV['TERM'] !~ /-color$/
|
56
|
+
)
|
48
57
|
|
49
58
|
require 'ruco'
|
50
59
|
|
51
60
|
# draw app and redraw after each keystroke (or paste)
|
52
|
-
Ruco::Screen.open(
|
61
|
+
Ruco::Screen.open(options) do |screen|
|
53
62
|
app = Ruco::Application.new(ARGV[0],
|
54
|
-
:convert_tabs =>
|
55
|
-
:undo_stack_size =>
|
63
|
+
:convert_tabs => options[:convert_tabs],
|
64
|
+
:undo_stack_size => options[:undo_stack_size],
|
56
65
|
:lines => screen.lines, :columns => screen.columns
|
57
66
|
)
|
58
67
|
|
@@ -63,9 +72,9 @@ Ruco::Screen.open(@options) do |screen|
|
|
63
72
|
end
|
64
73
|
|
65
74
|
Keyboard.output do |key|
|
66
|
-
screen.debug_key(key) if
|
75
|
+
screen.debug_key(key) if options[:debug_keys]
|
67
76
|
if key == :resize
|
68
|
-
app.resize(lines, columns)
|
77
|
+
app.resize(screen.lines, screen.columns)
|
69
78
|
screen.clear_cache
|
70
79
|
else
|
71
80
|
result = app.key key
|
data/lib/ruco.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'language_sniffer'
|
2
|
+
|
1
3
|
require 'ruco/version'
|
2
4
|
|
3
5
|
require 'ruco/core_ext/object'
|
@@ -15,6 +17,7 @@ require 'ruco/file_store'
|
|
15
17
|
require 'ruco/window'
|
16
18
|
require 'ruco/screen'
|
17
19
|
require 'ruco/style_map'
|
20
|
+
require 'ruco/syntax_parser'
|
18
21
|
|
19
22
|
require 'ruco/editor'
|
20
23
|
require 'ruco/editor/line_numbers'
|
@@ -23,6 +26,28 @@ require 'ruco/status_bar'
|
|
23
26
|
require 'ruco/command_bar'
|
24
27
|
require 'ruco/application'
|
25
28
|
|
29
|
+
if $ruco_colors
|
30
|
+
begin
|
31
|
+
# this can fail on ruby 1.8 <-> oniguruma is complicated to install
|
32
|
+
require 'oniguruma' if RUBY_VERSION < '1.9.0'
|
33
|
+
|
34
|
+
# there are some other gems out there like spox-textpow etc, so be picky
|
35
|
+
gem 'plist'
|
36
|
+
require 'plist'
|
37
|
+
gem 'textpow1x'
|
38
|
+
require 'textpow'
|
39
|
+
gem 'ultraviolet1x'
|
40
|
+
require 'uv'
|
41
|
+
|
42
|
+
# we do not need there if any other color li failed
|
43
|
+
require 'ruco/array_processor'
|
44
|
+
require 'ruco/tm_theme'
|
45
|
+
require 'ruco/editor/colors'
|
46
|
+
rescue LoadError
|
47
|
+
warn "Could not load color libs -- #{$!}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
26
51
|
require 'ruco/form'
|
27
52
|
require 'ruco/text_area'
|
28
53
|
require 'ruco/editor_area'
|
data/lib/ruco/application.rb
CHANGED
@@ -247,7 +247,7 @@ module Ruco
|
|
247
247
|
@status_lines = 1
|
248
248
|
|
249
249
|
editor_options = @options.slice(
|
250
|
-
:columns, :convert_tabs, :convert_newlines, :undo_stack_size
|
250
|
+
:columns, :convert_tabs, :convert_newlines, :undo_stack_size, :color_theme
|
251
251
|
).merge(
|
252
252
|
:window => @options.nested(:window),
|
253
253
|
:history => @options.nested(:history),
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Ruco
|
2
|
+
class ArrayProcessor
|
3
|
+
attr_accessor :lines
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@line_number = -1
|
7
|
+
@lines = []
|
8
|
+
@open_elements = []
|
9
|
+
@still_open_elements = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def open_tag(name, position)
|
13
|
+
#puts "Open #{name} #{@line_number}:#{position}"
|
14
|
+
@open_elements << [name, position]
|
15
|
+
end
|
16
|
+
|
17
|
+
def close_tag(name, position)
|
18
|
+
#puts "Close #{name} #{@line_number}:#{position}"
|
19
|
+
open_element = @open_elements.pop || @still_open_elements.pop
|
20
|
+
@lines[@line_number] << [name, open_element.last...position]
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_line(line)
|
24
|
+
#puts "Line #{line}"
|
25
|
+
# close elements only opened in last line
|
26
|
+
@open_elements.each do |name, position|
|
27
|
+
@lines[@line_number] << [name, position...@line.size]
|
28
|
+
end
|
29
|
+
|
30
|
+
# surround last line in still open elements from previouse lines
|
31
|
+
@still_open_elements.each do |name,_|
|
32
|
+
@lines[@line_number] << [name, 0...@line.size]
|
33
|
+
end
|
34
|
+
|
35
|
+
# mark open as 'still open'
|
36
|
+
# and let them start on column 0 -> if closed in this line its 0...position
|
37
|
+
@still_open_elements += @open_elements.map{|name,_| [name,0]}
|
38
|
+
@open_elements = []
|
39
|
+
|
40
|
+
# proceed with next line
|
41
|
+
@line = line
|
42
|
+
@line_number += 1
|
43
|
+
@lines[@line_number] = []
|
44
|
+
end
|
45
|
+
|
46
|
+
def start_parsing(name);end
|
47
|
+
def end_parsing(name);end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
@lines
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/ruco/core_ext/object.rb
CHANGED
@@ -50,3 +50,29 @@ class Object
|
|
50
50
|
Marshal.load(Marshal.dump(self))
|
51
51
|
end unless defined? deep_copy
|
52
52
|
end
|
53
|
+
|
54
|
+
class Object
|
55
|
+
# copy from active_support
|
56
|
+
def silence_warnings
|
57
|
+
with_warnings(nil) { yield }
|
58
|
+
end unless defined? silence_warnings
|
59
|
+
|
60
|
+
# copy from active_support
|
61
|
+
def with_warnings(flag)
|
62
|
+
old_verbose, $VERBOSE = $VERBOSE, flag
|
63
|
+
yield
|
64
|
+
ensure
|
65
|
+
$VERBOSE = old_verbose
|
66
|
+
end unless defined? with_warnings
|
67
|
+
end
|
68
|
+
|
69
|
+
# http://grosser.it/2010/07/23/open-uri-without-ssl-https-verification/
|
70
|
+
module OpenURI
|
71
|
+
def self.without_ssl_verification
|
72
|
+
old = ::OpenSSL::SSL::VERIFY_PEER
|
73
|
+
silence_warnings{ ::OpenSSL::SSL.const_set :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE }
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
silence_warnings{ ::OpenSSL::SSL.const_set :VERIFY_PEER, old }
|
77
|
+
end
|
78
|
+
end
|
data/lib/ruco/core_ext/range.rb
CHANGED
@@ -3,4 +3,19 @@ class Range
|
|
3
3
|
def overlap?(other)
|
4
4
|
(first <= other.last) and (other.first <= last)
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
|
+
# (1..2).last_element == 2
|
8
|
+
# (1...3).last_element == 2
|
9
|
+
def last_element
|
10
|
+
exclude_end? ? last.pred : last
|
11
|
+
end unless defined? last_element
|
12
|
+
|
13
|
+
# (1..2).move(2) == (3..4)
|
14
|
+
def move(n)
|
15
|
+
if exclude_end?
|
16
|
+
(first + n)...(last + n)
|
17
|
+
else
|
18
|
+
(first + n)..(last + n)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ruco/editor.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module Ruco
|
4
|
+
class Editor
|
5
|
+
module Colors
|
6
|
+
DEFAULT_THEME = 'spec/fixtures/railscasts.tmTheme'
|
7
|
+
STYLING_TIMEOUT = 4
|
8
|
+
|
9
|
+
def style_map
|
10
|
+
map = super
|
11
|
+
return map if @colors_took_too_long
|
12
|
+
|
13
|
+
# disable colors if syntax-parsing takes too long
|
14
|
+
begin
|
15
|
+
syntax = Timeout.timeout(STYLING_TIMEOUT) do
|
16
|
+
syntax_info[@window.visible_lines]
|
17
|
+
end
|
18
|
+
rescue Timeout::Error
|
19
|
+
# this takes too long, just go on without styles
|
20
|
+
STDERR.puts "Styling takes too long, go on without me!"
|
21
|
+
@colors_took_too_long = true
|
22
|
+
return map
|
23
|
+
end
|
24
|
+
|
25
|
+
add_syntax_highlighting_to_style_map(map, syntax)
|
26
|
+
|
27
|
+
if @selection
|
28
|
+
# add selection a second time so it stays on top
|
29
|
+
@window.add_selection_styles(map, @selection)
|
30
|
+
end
|
31
|
+
map
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def syntax_info
|
37
|
+
if language = @options[:language]
|
38
|
+
@syntax_info ||= {}
|
39
|
+
language = [language.name.downcase, language.lexer]
|
40
|
+
lines.map do |line|
|
41
|
+
@syntax_info[line] ||= SyntaxParser.syntax_for_lines([line], language).first
|
42
|
+
end
|
43
|
+
else
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_syntax_highlighting_to_style_map(map, syntax_info)
|
49
|
+
return unless syntax_info
|
50
|
+
|
51
|
+
$ruco_foreground = theme.foreground
|
52
|
+
$ruco_background = theme.background
|
53
|
+
|
54
|
+
syntax_info.each_with_index do |syntax_positions, line|
|
55
|
+
next unless syntax_positions
|
56
|
+
syntax_positions.each do |syntax_element, columns|
|
57
|
+
columns = columns.move(-@window.left)
|
58
|
+
style = style_for_syntax_element(syntax_element)
|
59
|
+
if style and columns.first >= 0
|
60
|
+
map.add(style, line, columns)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def style_for_syntax_element(syntax_element)
|
67
|
+
@style_for_element ||= {}
|
68
|
+
@style_for_element[syntax_element] ||= begin
|
69
|
+
_, style = theme.styles.detect{|name,style| syntax_element.start_with?(name) }
|
70
|
+
style
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def theme
|
75
|
+
@theme ||= begin
|
76
|
+
file = download_into_file(@options[:color_theme]) if @options[:color_theme]
|
77
|
+
file ||= DEFAULT_THEME
|
78
|
+
Ruco::TMTheme.new(file)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def download_into_file(url)
|
83
|
+
theme_store = FileStore.new(File.expand_path('~/.ruco/themes'), :keep => 5, :pure => true)
|
84
|
+
theme_store.cache(url) do
|
85
|
+
require 'open-uri'
|
86
|
+
require 'openssl'
|
87
|
+
OpenURI.without_ssl_verification do
|
88
|
+
open(url).read
|
89
|
+
end
|
90
|
+
end
|
91
|
+
File.expand_path(theme_store.file(url))
|
92
|
+
rescue => e
|
93
|
+
STDERR.puts "Could not download #{url} -- #{e}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|