ruco 0.1.14 → 0.2.0.beta
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/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
|
|