ruco 0.2.0.beta2 → 0.2.0.beta3

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 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,6 +8,9 @@ GEM
8
8
  bundler (~> 1.0.0)
9
9
  git (>= 1.2.5)
10
10
  rake
11
+ language_sniffer (1.0.0)
12
+ oniguruma (1.1.0)
13
+ plist (3.1.0)
11
14
  rake (0.9.2)
12
15
  rspec (2.6.0)
13
16
  rspec-core (~> 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
@@ -1,41 +1,29 @@
1
1
  Simple, extendable, test-driven commandline editor written in ruby, for Linux/Mac/Windows.
2
2
 
3
- ### Features
3
+ Features:
4
4
 
5
- - **Desktop-like / intuitive interface**
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
9
10
  - keeps indentation (+ paste-detection e.g. via Cmd+v)
10
11
  - change (*) + writable (!) indicators
11
12
  - find / go to line / delete line / search & replace
12
- - configuration via `~/.ruco.rb` in Ruby
13
- - Full clipboard support -> Ctrl+x/c/v
14
- - undo / redo -> Ctrl+z/y
13
+ - configuration via `~/.ruco.rb`
14
+ - cut, copy and paste -> Ctrl+x/c/v
15
+ - undo / redo
15
16
  - stays at last position when reopening a file
16
- - opens file at line e.g. `ruco foo/bar.rb:32`
17
- - supports all newline formats
17
+ - opens file at line with `ruco foo/bar.rb:32` syntax
18
+ - keeps whatever newline format you use (\r \n \r\n)
18
19
  - surrounds selection with quotes/braces (select abc + " --> "abc")
19
20
  - shortens long file names in the middle
20
21
  - (optional) remove trailing whitespace on save
21
22
  - (optional) blank line before eof on save
22
23
  - (optional) line numbers
23
24
 
24
- ### Architecture
25
- Ruco is basically a lot of String manipulation separated in HTML style elements.
26
- The only component dealing with the commandline interface are Screen and Keyboard. Therefore
27
- everything is very simple to build and test since it returns a string or a e.g. cursor position.
28
-
29
- Writing/reading is done in a TextArea or a TextField (a TextArea with 1 line)
30
- which are placed in Form`s.
31
-
32
- The Application consists of a StatusBar, Editor, CommandBar and delegates actions to the currently active element.
33
-
34
- To build the commandline output Editor/CommandBar return:
35
-
36
- - view -- text that should be displayed on the screen (complete content cropped via Window)
37
- - style_map -- a big Array with style infos, e.g. 'on line 1 chars 5 to 7 are red'
38
- - cursor -- where to draw the cursor
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/))
39
27
 
40
28
  Install
41
29
  =======
@@ -56,6 +44,10 @@ Customize
56
44
  options.editor_remove_trailing_whitespace_on_save = true # default false
57
45
  options.editor_blank_line_before_eof_on_save = true # default false
58
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"
59
51
  ...
60
52
 
61
53
  # bind a key
@@ -91,16 +83,20 @@ Customize
91
83
  TIPS
92
84
  ====
93
85
  - [Mac] arow-keys + Shift/Alt does not work in default terminal (use iTerm)
94
- - [Tabs] Ruco does not like tabs. Existing tabs are displayed as single space and pressing tab inserts 2*space
86
+ - [Tabs] Ruco does not like tabs. Existing tabs are displayed as ' ' and pressing tab inserts 2*space
95
87
  - [RVM] `alias r="rvm 1.9.2 exec ruco"` and you only have to install ruco once
96
- - [Ruby1.9] Unicode support -> install libncursesw5-dev before installing ruby
88
+ - [Ruby1.9] Unicode support -> install libncursesw5-dev before installing ruby (does not work for 1.8)
97
89
  - [ssh vs clipboard] access your desktops clipboard by installing `xauth` on the server and then using `ssh -X`
98
90
  - [Alt key] if Alt does not work try your Meta/Win/Cmd key
99
91
 
100
-
101
-
102
92
  TODO
103
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)
104
100
  - do not fall back to 0:0 after undoing the first change
105
101
  - check writable status every x seconds (e.g. in background) -> faster while typing
106
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.beta3
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'
55
+ # and ENV['TERM'] !~ /-color$/ # it seems many lion users set xterm-color to fix bugs ...
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
@@ -9,4 +9,13 @@ class Range
9
9
  def last_element
10
10
  exclude_end? ? last.pred : last
11
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
12
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 = File.expand_path('../../../../spec/fixtures/railscasts.tmTheme',__FILE__)
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