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 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
- rake (0.8.7)
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
- if Curses::has_colors?
25
- Curses::start_color
26
- # initialize every color we want to use
27
- # id, foreground, background
28
- Curses::init_pair( Curses::COLOR_BLACK, Curses::COLOR_BLACK, Curses::COLOR_BLACK )
29
- Curses::init_pair( Curses::COLOR_RED, Curses::COLOR_RED, Curses::COLOR_BLACK )
30
- Curses::init_pair( Curses::COLOR_GREEN, Curses::COLOR_GREEN, Curses::COLOR_BLACK )
31
- end
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.color_pair(Curses::COLOR_RED)) # fetch color pair with the id xxx
38
+ Curses.attrset(Curses::color_pair( 32 )) # fetch color pair with the id xxx
35
39
  Curses.addstr("xxxxxxxx\nyyyyyyy");
36
- Curses.attrset(Curses.color_pair(Curses::COLOR_GREEN))
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.14
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
- @options = parse_options
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(@options) do |screen|
61
+ Ruco::Screen.open(options) do |screen|
53
62
  app = Ruco::Application.new(ARGV[0],
54
- :convert_tabs => @options[:convert_tabs],
55
- :undo_stack_size => @options[: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 @options[:debug_keys]
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'
@@ -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
@@ -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
@@ -3,4 +3,19 @@ class Range
3
3
  def overlap?(other)
4
4
  (first <= other.last) and (other.first <= last)
5
5
  end
6
- end
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
@@ -21,6 +21,7 @@ module Ruco
21
21
  end
22
22
 
23
23
  content = (File.exist?(@file) ? File.binary_read(@file) : '')
24
+ @options[:language] ||= LanguageSniffer.detect(@file, :content => content).language
24
25
  content.tabs_to_spaces! if @options[:convert_tabs]
25
26
 
26
27
  # cleanup newline formats
@@ -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