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/lib/ruco/editor_area.rb
CHANGED
@@ -2,6 +2,7 @@ module Ruco
|
|
2
2
|
# everything that does not belong to a text-area
|
3
3
|
# but is needed for Ruco::Editor
|
4
4
|
class EditorArea < TextArea
|
5
|
+
include Ruco::Editor::Colors if defined? Ruco::Editor::Colors # only colorize if all color libs are loaded
|
5
6
|
include Ruco::Editor::LineNumbers
|
6
7
|
include Ruco::Editor::History
|
7
8
|
|
data/lib/ruco/file_store.rb
CHANGED
@@ -19,6 +19,28 @@ module Ruco
|
|
19
19
|
deserialize File.binary_read(file) if File.exist?(file)
|
20
20
|
end
|
21
21
|
|
22
|
+
def cache(key, &block)
|
23
|
+
value = get(key)
|
24
|
+
if value.nil?
|
25
|
+
value = yield
|
26
|
+
set(key, value)
|
27
|
+
end
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(key)
|
32
|
+
FileUtils.rm(file(key))
|
33
|
+
rescue Errno::ENOENT
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
FileUtils.rm_rf(@folder)
|
38
|
+
end
|
39
|
+
|
40
|
+
def file(key)
|
41
|
+
"#{@folder}/#{Digest::MD5.hexdigest(key)}.yml"
|
42
|
+
end
|
43
|
+
|
22
44
|
private
|
23
45
|
|
24
46
|
def entries
|
@@ -32,12 +54,8 @@ module Ruco
|
|
32
54
|
delete.each{|f| File.delete(f) }
|
33
55
|
end
|
34
56
|
|
35
|
-
def file(key)
|
36
|
-
"#{@folder}/#{Digest::MD5.hexdigest(key)}.yml"
|
37
|
-
end
|
38
|
-
|
39
57
|
def serialize(value)
|
40
|
-
Marshal.dump(value)
|
58
|
+
@options[:string] ? value : Marshal.dump(value)
|
41
59
|
end
|
42
60
|
|
43
61
|
def deserialize(value)
|
data/lib/ruco/screen.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Ruco
|
2
2
|
class Screen
|
3
|
+
@@styles = {}
|
4
|
+
|
3
5
|
def initialize(options)
|
4
6
|
@options = options
|
5
7
|
@cache = []
|
@@ -10,12 +12,16 @@ module Ruco
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def open(&block)
|
15
|
+
if $ruco_colors and ENV['TERM'] == 'xterm'
|
16
|
+
ENV['TERM'] += '-256color' # activate 256 colors
|
17
|
+
end
|
13
18
|
Curses.noecho # do not show typed chars
|
14
19
|
Curses.nonl # turn off newline translation
|
15
20
|
Curses.stdscr.keypad(true) # enable arrow keys
|
16
21
|
Curses.raw # give us all other keys
|
17
22
|
Curses.stdscr.nodelay = 1 # do not block -> we can use timeouts
|
18
23
|
Curses.init_screen
|
24
|
+
Curses.start_color if $ruco_colors and Curses.has_colors?
|
19
25
|
yield self
|
20
26
|
ensure
|
21
27
|
Curses.clear # needed to clear the menu/status bar on windows
|
@@ -88,14 +94,62 @@ module Ruco
|
|
88
94
|
yield # render the line
|
89
95
|
end
|
90
96
|
|
91
|
-
STYLES = {
|
92
|
-
:normal => 0,
|
93
|
-
:reverse => Curses::A_REVERSE
|
94
|
-
}
|
95
|
-
|
96
97
|
def self.curses_style(style)
|
97
|
-
|
98
|
-
|
98
|
+
@@styles[style] ||= begin
|
99
|
+
if $ruco_colors
|
100
|
+
foreground = $ruco_foreground || '#ffffff'
|
101
|
+
background = $ruco_background || '#000000'
|
102
|
+
|
103
|
+
foreground, background = if style == :normal
|
104
|
+
[foreground, background]
|
105
|
+
elsif style == :reverse
|
106
|
+
['#000000', '#ffffff']
|
107
|
+
else
|
108
|
+
# :red or [:red, :blue]
|
109
|
+
f,b = style
|
110
|
+
b ||= background
|
111
|
+
[f,b]
|
112
|
+
end
|
113
|
+
|
114
|
+
foreground = html_to_terminal_color(foreground)
|
115
|
+
background = html_to_terminal_color(background)
|
116
|
+
color_id(foreground, background)
|
117
|
+
else # no colors
|
118
|
+
if style == :reverse
|
119
|
+
Curses::A_REVERSE
|
120
|
+
else
|
121
|
+
Curses::A_NORMAL
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# create a new color from foreground+background or reuse old
|
128
|
+
# and return color-id
|
129
|
+
def self.color_id(foreground, background)
|
130
|
+
@@color_ids ||= {}
|
131
|
+
@@color_ids[[foreground, background]] ||= begin
|
132
|
+
# make a new pair with a unique id
|
133
|
+
@@max_color_id ||= 0
|
134
|
+
id = (@@max_color_id += 1)
|
135
|
+
unless defined? RSpec # stops normal text-output, do not use in tests
|
136
|
+
Curses::init_pair(id, foreground, background)
|
137
|
+
end
|
138
|
+
Curses.color_pair(id)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
COLOR_SOURCE_VALUES = 256
|
143
|
+
COLOR_TARGET_VALUES = 5
|
144
|
+
COLOR_DIVIDE = COLOR_SOURCE_VALUES / COLOR_TARGET_VALUES
|
145
|
+
TERM_COLOR_BASE = 16
|
146
|
+
|
147
|
+
def self.html_to_terminal_color(html_color)
|
148
|
+
return unless html_color
|
149
|
+
r = (html_color[1..2].to_i(16) / COLOR_DIVIDE) * 36
|
150
|
+
g = (html_color[3..4].to_i(16) / COLOR_DIVIDE) * 6
|
151
|
+
b = (html_color[5..6].to_i(16) / COLOR_DIVIDE) * 1
|
152
|
+
TERM_COLOR_BASE + r + g + b
|
99
153
|
end
|
100
154
|
end
|
101
155
|
end
|
data/lib/ruco/style_map.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ruco
|
2
2
|
class StyleMap
|
3
|
-
attr_accessor :lines
|
3
|
+
attr_accessor :lines, :foreground, :background
|
4
4
|
|
5
5
|
def initialize(lines)
|
6
6
|
@lines = Array.new(lines)
|
@@ -20,20 +20,19 @@ module Ruco
|
|
20
20
|
@lines.map do |styles|
|
21
21
|
next unless styles
|
22
22
|
|
23
|
-
# start and one after end
|
24
|
-
points_of_change = styles.map{|s,c| [c.first, c.
|
23
|
+
# change to style at start and recalculate one after the end
|
24
|
+
points_of_change = styles.map{|s,c| [c.first, c.last_element+1] }.flatten.uniq
|
25
25
|
|
26
26
|
flat = []
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
points_of_change.each do |point|
|
29
|
+
flat[point] = :normal # set default
|
30
|
+
styles.each do |style, columns|
|
30
31
|
next unless columns.include?(point)
|
31
32
|
flat[point] = style
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
max = styles.map{|_,columns| columns.last }.max
|
36
|
-
flat[max+1] = :normal
|
37
36
|
flat
|
38
37
|
end
|
39
38
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ruco
|
2
|
+
module SyntaxParser
|
3
|
+
# uv only offers certain syntax
|
4
|
+
UV_CONVERT = {
|
5
|
+
'scss' => 'sass',
|
6
|
+
'html+erb' => 'html_rails',
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.syntax_for_lines(lines, languages)
|
10
|
+
if syntax = syntax_node(languages)
|
11
|
+
processor = syntax.parse(lines.join("\n"), Ruco::ArrayProcessor.new)
|
12
|
+
processor.lines
|
13
|
+
else
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.syntax_node(languages)
|
19
|
+
@@syntax_node ||= {}
|
20
|
+
@@syntax_node[languages] ||= begin
|
21
|
+
found = nil
|
22
|
+
fallbacks = languages.map{|l| UV_CONVERT[l] }.compact
|
23
|
+
|
24
|
+
(languages + fallbacks).detect do |language|
|
25
|
+
syntax = File.join(Uv.syntax_path, "#{language}.syntax")
|
26
|
+
found = Textpow::SyntaxNode.load(syntax) if File.exist?(syntax)
|
27
|
+
end
|
28
|
+
|
29
|
+
found
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'plist'
|
2
|
+
|
3
|
+
module Ruco
|
4
|
+
class TMTheme
|
5
|
+
attr_accessor :background, :foreground, :styles
|
6
|
+
|
7
|
+
# TODO maybe later...
|
8
|
+
#attr_accessor :name, :uuid, :comment, :line_highlight
|
9
|
+
|
10
|
+
# not supported in Curses ...
|
11
|
+
#attr_accessor :invisibles, :caret, :selection
|
12
|
+
|
13
|
+
def initialize(file)
|
14
|
+
raw = Plist.parse_xml(file)
|
15
|
+
rules = raw['settings']
|
16
|
+
@styles = {}
|
17
|
+
|
18
|
+
# set global styles
|
19
|
+
global = rules.shift['settings']
|
20
|
+
self.background = global['background']
|
21
|
+
self.foreground = global['foreground']
|
22
|
+
|
23
|
+
# set scope styles
|
24
|
+
rules.each do |rules|
|
25
|
+
style = [
|
26
|
+
rules['settings']['foreground'],
|
27
|
+
rules['settings']['background'],
|
28
|
+
]
|
29
|
+
|
30
|
+
next if style == [nil, nil] # some weird themes have rules without colors...
|
31
|
+
next unless scope = rules['scope'] # some weird themes have rules without scopes...
|
32
|
+
|
33
|
+
scope.split(/, ?/).map(&:strip).each do |scope|
|
34
|
+
@styles[scope] = style unless nested_scope?(scope)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def nested_scope?(scope)
|
42
|
+
scope.include?(' ')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/ruco/window.rb
CHANGED
@@ -50,9 +50,15 @@ module Ruco
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def style_map(selection)
|
53
|
-
|
54
|
-
|
53
|
+
map = StyleMap.new(lines)
|
54
|
+
if selection
|
55
|
+
add_selection_styles(map, selection)
|
56
|
+
else
|
57
|
+
map
|
58
|
+
end
|
59
|
+
end
|
55
60
|
|
61
|
+
def add_selection_styles(map, selection)
|
56
62
|
lines.times do |line|
|
57
63
|
visible = visible_area(line)
|
58
64
|
next unless selection.overlap?(visible)
|
@@ -62,10 +68,9 @@ module Ruco
|
|
62
68
|
last = [selection.last, visible.last].min
|
63
69
|
last = last[1] - left
|
64
70
|
|
65
|
-
|
71
|
+
map.add(:reverse, line, first...last)
|
66
72
|
end
|
67
|
-
|
68
|
-
mask
|
73
|
+
map
|
69
74
|
end
|
70
75
|
|
71
76
|
def left=(x)
|
@@ -77,6 +82,14 @@ module Ruco
|
|
77
82
|
@top = [[line, max_top].min, 0].max
|
78
83
|
end
|
79
84
|
|
85
|
+
def visible_lines
|
86
|
+
@top..(@top+@lines-1)
|
87
|
+
end
|
88
|
+
|
89
|
+
def visible_columns
|
90
|
+
@left..(@left+@columns-1)
|
91
|
+
end
|
92
|
+
|
80
93
|
private
|
81
94
|
|
82
95
|
def adjustment(current, allowed, threshold, offset)
|
@@ -95,13 +108,5 @@ module Ruco
|
|
95
108
|
end_of_line = [line, last_visible_column]
|
96
109
|
start_of_line..end_of_line
|
97
110
|
end
|
98
|
-
|
99
|
-
def visible_lines
|
100
|
-
@top..(@top+@lines-1)
|
101
|
-
end
|
102
|
-
|
103
|
-
def visible_columns
|
104
|
-
@left..(@left+@columns-1)
|
105
|
-
end
|
106
111
|
end
|
107
|
-
end
|
112
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH << 'lib'
|
2
|
+
require 'oniguruma' if RUBY_VERSION < '1.9.0'
|
3
|
+
require 'language_sniffer'
|
4
|
+
gem 'plist'
|
5
|
+
require 'plist'
|
6
|
+
gem 'textpow1x'
|
7
|
+
require 'textpow'
|
8
|
+
gem 'ultraviolet1x'
|
9
|
+
require 'uv'
|
10
|
+
|
11
|
+
file = ARGV[0]
|
12
|
+
text = File.read(file)
|
13
|
+
language = LanguageSniffer.detect(file).language
|
14
|
+
|
15
|
+
require 'ruco/syntax_parser'
|
16
|
+
require 'ruco/array_processor'
|
17
|
+
t = Time.now.to_f
|
18
|
+
Ruco::SyntaxParser.syntax_for_lines(text.split("\n"), [language.name.downcase, language.lexer])
|
19
|
+
Ruco::SyntaxParser.syntax_for_lines(text.split("\n"), [language.name.downcase, language.lexer])
|
20
|
+
Ruco::SyntaxParser.syntax_for_lines(text.split("\n"), [language.name.downcase, language.lexer])
|
21
|
+
Ruco::SyntaxParser.syntax_for_lines(text.split("\n"), [language.name.downcase, language.lexer])
|
22
|
+
Ruco::SyntaxParser.syntax_for_lines(text.split("\n"), [language.name.downcase, language.lexer])
|
23
|
+
puts (Time.now.to_f - t)
|
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.
|
8
|
+
s.version = "0.2.0.beta"
|
9
9
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date = %q{2011-09-
|
12
|
+
s.date = %q{2011-09-23}
|
13
13
|
s.default_executable = %q{ruco}
|
14
14
|
s.email = %q{michael@grosser.it}
|
15
15
|
s.executables = ["ruco"]
|
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
|
|
22
22
|
"bin/ruco",
|
23
23
|
"lib/ruco.rb",
|
24
24
|
"lib/ruco/application.rb",
|
25
|
+
"lib/ruco/array_processor.rb",
|
25
26
|
"lib/ruco/command_bar.rb",
|
26
27
|
"lib/ruco/core_ext/array.rb",
|
27
28
|
"lib/ruco/core_ext/file.rb",
|
@@ -30,6 +31,7 @@ Gem::Specification.new do |s|
|
|
30
31
|
"lib/ruco/core_ext/range.rb",
|
31
32
|
"lib/ruco/core_ext/string.rb",
|
32
33
|
"lib/ruco/editor.rb",
|
34
|
+
"lib/ruco/editor/colors.rb",
|
33
35
|
"lib/ruco/editor/history.rb",
|
34
36
|
"lib/ruco/editor/line_numbers.rb",
|
35
37
|
"lib/ruco/editor_area.rb",
|
@@ -42,14 +44,22 @@ Gem::Specification.new do |s|
|
|
42
44
|
"lib/ruco/screen.rb",
|
43
45
|
"lib/ruco/status_bar.rb",
|
44
46
|
"lib/ruco/style_map.rb",
|
47
|
+
"lib/ruco/syntax_parser.rb",
|
45
48
|
"lib/ruco/text_area.rb",
|
46
49
|
"lib/ruco/text_field.rb",
|
50
|
+
"lib/ruco/tm_theme.rb",
|
47
51
|
"lib/ruco/version.rb",
|
48
52
|
"lib/ruco/window.rb",
|
53
|
+
"playground/benchmark_syntax_parser.rb",
|
49
54
|
"ruco.gemspec",
|
55
|
+
"spec/fixtures/railscasts.tmTheme",
|
56
|
+
"spec/fixtures/slow.js",
|
57
|
+
"spec/fixtures/test.tmTheme",
|
50
58
|
"spec/ruco/application_spec.rb",
|
59
|
+
"spec/ruco/array_processor_spec.rb",
|
51
60
|
"spec/ruco/command_bar_spec.rb",
|
52
61
|
"spec/ruco/core_ext/array_spec.rb",
|
62
|
+
"spec/ruco/core_ext/range_spec.rb",
|
53
63
|
"spec/ruco/core_ext/string_spec.rb",
|
54
64
|
"spec/ruco/editor_spec.rb",
|
55
65
|
"spec/ruco/file_store_spec.rb",
|
@@ -60,7 +70,9 @@ Gem::Specification.new do |s|
|
|
60
70
|
"spec/ruco/screen_spec.rb",
|
61
71
|
"spec/ruco/status_bar_spec.rb",
|
62
72
|
"spec/ruco/style_map_spec.rb",
|
73
|
+
"spec/ruco/syntax_parser_spec.rb",
|
63
74
|
"spec/ruco/text_area_spec.rb",
|
75
|
+
"spec/ruco/tm_theme_spec.rb",
|
64
76
|
"spec/ruco/window_spec.rb",
|
65
77
|
"spec/ruco_spec.rb",
|
66
78
|
"spec/spec_helper.rb"
|
@@ -74,36 +86,23 @@ Gem::Specification.new do |s|
|
|
74
86
|
s.require_paths = ["lib"]
|
75
87
|
s.rubygems_version = %q{1.6.2}
|
76
88
|
s.summary = %q{Commandline editor written in ruby}
|
77
|
-
s.test_files = [
|
78
|
-
"spec/ruco/application_spec.rb",
|
79
|
-
"spec/ruco/command_bar_spec.rb",
|
80
|
-
"spec/ruco/core_ext/array_spec.rb",
|
81
|
-
"spec/ruco/core_ext/string_spec.rb",
|
82
|
-
"spec/ruco/editor_spec.rb",
|
83
|
-
"spec/ruco/file_store_spec.rb",
|
84
|
-
"spec/ruco/form_spec.rb",
|
85
|
-
"spec/ruco/history_spec.rb",
|
86
|
-
"spec/ruco/keyboard_spec.rb",
|
87
|
-
"spec/ruco/option_accessor_spec.rb",
|
88
|
-
"spec/ruco/screen_spec.rb",
|
89
|
-
"spec/ruco/status_bar_spec.rb",
|
90
|
-
"spec/ruco/style_map_spec.rb",
|
91
|
-
"spec/ruco/text_area_spec.rb",
|
92
|
-
"spec/ruco/window_spec.rb",
|
93
|
-
"spec/ruco_spec.rb",
|
94
|
-
"spec/spec_helper.rb"
|
95
|
-
]
|
96
89
|
|
97
90
|
if s.respond_to? :specification_version then
|
98
91
|
s.specification_version = 3
|
99
92
|
|
100
93
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
101
94
|
s.add_runtime_dependency(%q<clipboard>, [">= 0.9.8"])
|
95
|
+
s.add_runtime_dependency(%q<ultraviolet1x>, [">= 0"])
|
96
|
+
s.add_runtime_dependency(%q<language_sniffer>, [">= 0"])
|
102
97
|
else
|
103
98
|
s.add_dependency(%q<clipboard>, [">= 0.9.8"])
|
99
|
+
s.add_dependency(%q<ultraviolet1x>, [">= 0"])
|
100
|
+
s.add_dependency(%q<language_sniffer>, [">= 0"])
|
104
101
|
end
|
105
102
|
else
|
106
103
|
s.add_dependency(%q<clipboard>, [">= 0.9.8"])
|
104
|
+
s.add_dependency(%q<ultraviolet1x>, [">= 0"])
|
105
|
+
s.add_dependency(%q<language_sniffer>, [">= 0"])
|
107
106
|
end
|
108
107
|
end
|
109
108
|
|