green_shoes 0.227.0 → 0.233.0

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/README.md CHANGED
@@ -68,6 +68,7 @@ Except:
68
68
  - hh/static/(all).png (c) 2008 why the lucky stiff
69
69
  - lib/ext/hpricot/(all) (c) 2008 why the lucky stiff
70
70
  - lib/ext/projector/(all).rb (c) 2010 MIZUTANI Tociyuki
71
+ - lib/ext/highlighter/(all) (c) 2008 why the lucky stiff and 2011 Steve Klabnik
71
72
  - samples/akatsukiface.png (c) 2010 MIZUTANI Tociyuki
72
73
  - samples/class-book.yaml (c) 2008 why the lucky stiff
73
74
  - samples/splash-hand.png (c) 2008 why the lucky stiff
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.227.0
1
+ 0.233.0
@@ -0,0 +1,2 @@
1
+ module ::HH end
2
+ require_relative 'highlighter/markup'
@@ -0,0 +1,197 @@
1
+ require 'strscan'
2
+
3
+ module HH::Syntax
4
+
5
+ # A single token extracted by a tokenizer. It is simply the lexeme
6
+ # itself, decorated with a 'group' attribute to identify the type of the
7
+ # lexeme.
8
+ class Token < String
9
+
10
+ # the type of the lexeme that was extracted.
11
+ attr_reader :group
12
+
13
+ # the instruction associated with this token (:none, :region_open, or
14
+ # :region_close)
15
+ attr_reader :instruction
16
+
17
+ # Create a new Token representing the given text, and belonging to the
18
+ # given group.
19
+ def initialize( text, group, instruction = :none )
20
+ super text
21
+ @group = group
22
+ @instruction = instruction
23
+ end
24
+
25
+ end
26
+
27
+ # The base class of all tokenizers. It sets up the scanner and manages the
28
+ # looping until all tokens have been extracted. It also provides convenience
29
+ # methods to make sure adjacent tokens of identical groups are returned as
30
+ # a single token.
31
+ class Tokenizer
32
+
33
+ # The current group being processed by the tokenizer
34
+ attr_reader :group
35
+
36
+ # The current chunk of text being accumulated
37
+ attr_reader :chunk
38
+
39
+ # Start tokenizing. This sets up the state in preparation for tokenization,
40
+ # such as creating a new scanner for the text and saving the callback block.
41
+ # The block will be invoked for each token extracted.
42
+ def start( text, &block )
43
+ @chunk = ""
44
+ @group = :normal
45
+ @callback = block
46
+ @text = StringScanner.new( text )
47
+ setup
48
+ end
49
+
50
+ # Subclasses may override this method to provide implementation-specific
51
+ # setup logic.
52
+ def setup
53
+ end
54
+
55
+ # Finish tokenizing. This flushes the buffer, yielding any remaining text
56
+ # to the client.
57
+ def finish
58
+ start_group nil
59
+ teardown
60
+ end
61
+
62
+ # Subclasses may override this method to provide implementation-specific
63
+ # teardown logic.
64
+ def teardown
65
+ end
66
+
67
+ # Subclasses must implement this method, which is called for each iteration
68
+ # of the tokenization process. This method may extract multiple tokens.
69
+ def step
70
+ raise NotImplementedError, "subclasses must implement #step"
71
+ end
72
+
73
+ # Begins tokenizing the given text, calling #step until the text has been
74
+ # exhausted.
75
+ def tokenize( text, &block )
76
+ start text, &block
77
+ step until @text.eos?
78
+ finish
79
+ end
80
+
81
+ # Specify a set of tokenizer-specific options. Each tokenizer may (or may
82
+ # not) publish any options, but if a tokenizer does those options may be
83
+ # used to specify optional behavior.
84
+ def set( opts={} )
85
+ ( @options ||= Hash.new ).update opts
86
+ end
87
+
88
+ # Get the value of the specified option.
89
+ def option(opt)
90
+ @options ? @options[opt] : nil
91
+ end
92
+
93
+ private
94
+
95
+ EOL = /(?=\r\n?|\n|$)/
96
+
97
+ # A convenience for delegating method calls to the scanner.
98
+ def self.delegate( sym )
99
+ define_method( sym ) { |*a| @text.__send__( sym, *a ) }
100
+ end
101
+
102
+ delegate :bol?
103
+ delegate :eos?
104
+ delegate :scan
105
+ delegate :scan_until
106
+ delegate :check
107
+ delegate :check_until
108
+ delegate :getch
109
+ delegate :matched
110
+ delegate :pre_match
111
+ delegate :peek
112
+ delegate :pos
113
+
114
+ # Access the n-th subgroup from the most recent match.
115
+ def subgroup(n)
116
+ @text[n]
117
+ end
118
+
119
+ # Append the given data to the currently active chunk.
120
+ def append( data )
121
+ @chunk << data
122
+ end
123
+
124
+ # Request that a new group be started. If the current group is the same
125
+ # as the group being requested, a new group will not be created. If a new
126
+ # group is created and the current chunk is not empty, the chunk's
127
+ # contents will be yielded to the client as a token, and then cleared.
128
+ #
129
+ # After the new group is started, if +data+ is non-nil it will be appended
130
+ # to the chunk.
131
+ def start_group( gr, data=nil )
132
+ flush_chunk if gr != @group
133
+ @group = gr
134
+ @chunk << data if data
135
+ end
136
+
137
+ def start_region( gr, data=nil )
138
+ flush_chunk
139
+ @group = gr
140
+ @callback.call( Token.new( data||"", @group, :region_open ) )
141
+ end
142
+
143
+ def end_region( gr, data=nil )
144
+ flush_chunk
145
+ @group = gr
146
+ @callback.call( Token.new( data||"", @group, :region_close ) )
147
+ end
148
+
149
+ def flush_chunk
150
+ @callback.call( Token.new( @chunk, @group ) ) unless @chunk.empty?
151
+ @chunk = ""
152
+ end
153
+
154
+ def subtokenize( syntax, text )
155
+ tokenizer = Syntax.load( syntax )
156
+ tokenizer.set @options if @options
157
+ flush_chunk
158
+ tokenizer.tokenize( text, &@callback )
159
+ end
160
+ end
161
+
162
+
163
+ # A default tokenizer for handling syntaxes that are not explicitly handled
164
+ # elsewhere. It simply yields the given text as a single token.
165
+ class Default
166
+
167
+ # Yield the given text as a single token.
168
+ def tokenize( text )
169
+ yield Token.new( text, :normal )
170
+ end
171
+
172
+ end
173
+
174
+ # A hash for registering syntax implementations.
175
+ SYNTAX = Hash.new( Default )
176
+
177
+ # Load the implementation of the requested syntax. If the syntax cannot be
178
+ # found, or if it cannot be loaded for whatever reason, the Default syntax
179
+ # handler will be returned.
180
+ def load( syntax )
181
+ begin
182
+ require_relative "lang/#{syntax}"
183
+ rescue LoadError
184
+ end
185
+ SYNTAX[ syntax ].new
186
+ end
187
+ module_function :load
188
+
189
+ # Return an array of the names of supported syntaxes.
190
+ def all
191
+ lang_dir = File.join(File.dirname(__FILE__), "syntax", "lang")
192
+ Dir["#{lang_dir}/*.rb"].map { |path| File.basename(path, ".rb") }
193
+ end
194
+ module_function :all
195
+
196
+
197
+ end
@@ -0,0 +1,316 @@
1
+
2
+ module HH::Syntax
3
+
4
+ # A tokenizer for the Ruby language. It recognizes all common syntax
5
+ # (and some less common syntax) but because it is not a true lexer, it
6
+ # will make mistakes on some ambiguous cases.
7
+ class Ruby < Tokenizer
8
+
9
+ # The list of all identifiers recognized as keywords.
10
+ KEYWORDS =
11
+ %w{if then elsif else end begin do rescue ensure while for
12
+ class module def yield raise until unless and or not when
13
+ case super undef break next redo retry in return alias
14
+ defined?}
15
+
16
+ # Perform ruby-specific setup
17
+ def setup
18
+ @selector = false
19
+ @allow_operator = false
20
+ @heredocs = []
21
+ end
22
+
23
+ # Step through a single iteration of the tokenization process.
24
+ def step
25
+ case
26
+ when bol? && check( /=begin/ )
27
+ start_group( :comment, scan_until( /^=end#{EOL}/ ) )
28
+ when bol? && check( /__END__#{EOL}/ )
29
+ start_group( :comment, scan_until( /\Z/ ) )
30
+ else
31
+ case
32
+ when check( /def\s+/ )
33
+ start_group :keyword, scan( /def\s+/ )
34
+ start_group :method, scan_until( /(?=[;(\s]|#{EOL})/ )
35
+ when check( /class\s+/ )
36
+ start_group :keyword, scan( /class\s+/ )
37
+ start_group :class, scan_until( /(?=[;\s<]|#{EOL})/ )
38
+ when check( /module\s+/ )
39
+ start_group :keyword, scan( /module\s+/ )
40
+ start_group :module, scan_until( /(?=[;\s]|#{EOL})/ )
41
+ when check( /::/ )
42
+ start_group :punct, scan(/::/)
43
+ when check( /:"/ )
44
+ start_group :symbol, scan(/:/)
45
+ scan_delimited_region :symbol, :symbol, "", true
46
+ @allow_operator = true
47
+ when check( /:'/ )
48
+ start_group :symbol, scan(/:/)
49
+ scan_delimited_region :symbol, :symbol, "", false
50
+ @allow_operator = true
51
+ when scan( /:[_a-zA-Z@$][$@\w]*[=!?]?/ )
52
+ start_group :symbol, matched
53
+ @allow_operator = true
54
+ when scan( /\?(\\[^\n\r]|[^\\\n\r\s])/ )
55
+ start_group :char, matched
56
+ @allow_operator = true
57
+ when check( /(__FILE__|__LINE__|true|false|nil|self)[?!]?/ )
58
+ if @selector || matched[-1] == ?? || matched[-1] == ?!
59
+ start_group :ident,
60
+ scan(/(__FILE__|__LINE__|true|false|nil|self)[?!]?/)
61
+ else
62
+ start_group :constant,
63
+ scan(/(__FILE__|__LINE__|true|false|nil|self)/)
64
+ end
65
+ @selector = false
66
+ @allow_operator = true
67
+ when scan(/0([bB][01]+|[oO][0-7]+|[dD][0-9]+|[xX][0-9a-fA-F]+)/)
68
+ start_group :number, matched
69
+ @allow_operator = true
70
+ else
71
+ case peek(2)
72
+ when "%r"
73
+ scan_delimited_region :punct, :regex, scan( /../ ), true
74
+ @allow_operator = true
75
+ when "%w", "%q"
76
+ scan_delimited_region :punct, :string, scan( /../ ), false
77
+ @allow_operator = true
78
+ when "%s"
79
+ scan_delimited_region :punct, :symbol, scan( /../ ), false
80
+ @allow_operator = true
81
+ when "%W", "%Q", "%x"
82
+ scan_delimited_region :punct, :string, scan( /../ ), true
83
+ @allow_operator = true
84
+ when /%[^\sa-zA-Z0-9]/
85
+ scan_delimited_region :punct, :string, scan( /./ ), true
86
+ @allow_operator = true
87
+ when "<<"
88
+ saw_word = ( chunk[-1,1] =~ /[\w!?]/ )
89
+ start_group :punct, scan( /<</ )
90
+ if saw_word
91
+ @allow_operator = false
92
+ return
93
+ end
94
+
95
+ float_right = scan( /-/ )
96
+ append "-" if float_right
97
+ if ( type = scan( /['"]/ ) )
98
+ append type
99
+ delim = scan_until( /(?=#{type})/ )
100
+ if delim.nil?
101
+ append scan_until( /\Z/ )
102
+ return
103
+ end
104
+ else
105
+ delim = scan( /\w+/ ) or return
106
+ end
107
+ start_group :constant, delim
108
+ start_group :punct, scan( /#{type}/ ) if type
109
+ @heredocs << [ float_right, type, delim ]
110
+ @allow_operator = true
111
+ else
112
+ case peek(1)
113
+ when /[\n\r]/
114
+ unless @heredocs.empty?
115
+ scan_heredoc(*@heredocs.shift)
116
+ else
117
+ start_group :normal, scan( /\s+/ )
118
+ end
119
+ @allow_operator = false
120
+ when /\s/
121
+ start_group :normal, scan( /\s+/ )
122
+ when "#"
123
+ start_group :comment, scan( /#[^\n\r]*/ )
124
+ when /[A-Z]/
125
+ start_group @selector ? :ident : :constant, scan( /\w+/ )
126
+ @allow_operator = true
127
+ when /[a-z_]/
128
+ word = scan( /\w+[?!]?/ )
129
+ if !@selector && KEYWORDS.include?( word )
130
+ start_group :keyword, word
131
+ @allow_operator = false
132
+ elsif
133
+ start_group :ident, word
134
+ @allow_operator = true
135
+ end
136
+ @selector = false
137
+ when /\d/
138
+ start_group :number,
139
+ scan( /[\d_]+(\.[\d_]+)?([eE][\d_]+)?/ )
140
+ @allow_operator = true
141
+ when '"'
142
+ scan_delimited_region :punct, :string, "", true
143
+ @allow_operator = true
144
+ when '/'
145
+ if @allow_operator
146
+ start_group :punct, scan(%r{/})
147
+ @allow_operator = false
148
+ else
149
+ scan_delimited_region :punct, :regex, "", true
150
+ @allow_operator = true
151
+ end
152
+ when "'"
153
+ scan_delimited_region :punct, :string, "", false
154
+ @allow_operator = true
155
+ when "."
156
+ dots = scan( /\.{1,3}/ )
157
+ start_group :punct, dots
158
+ @selector = ( dots.length == 1 )
159
+ when /[@]/
160
+ start_group :attribute, scan( /@{1,2}\w*/ )
161
+ @allow_operator = true
162
+ when /[$]/
163
+ start_group :global, scan(/\$/)
164
+ start_group :global, scan( /\w+|./ ) if check(/./)
165
+ @allow_operator = true
166
+ when /[-!?*\/+=<>(\[\{}:;,&|%]/
167
+ start_group :punct, scan(/./)
168
+ @allow_operator = false
169
+ when /[)\]]/
170
+ start_group :punct, scan(/./)
171
+ @allow_operator = true
172
+ else
173
+ # all else just falls through this, to prevent
174
+ # infinite loops...
175
+ append getch
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ # Scan a delimited region of text. This handles the simple cases (strings
185
+ # delimited with quotes) as well as the more complex cases of %-strings
186
+ # and here-documents.
187
+ #
188
+ # * +delim_group+ is the group to use to classify the delimiters of the
189
+ # region
190
+ # * +inner_group+ is the group to use to classify the contents of the
191
+ # region
192
+ # * +starter+ is the text to use as the starting delimiter
193
+ # * +exprs+ is a boolean flag indicating whether the region is an
194
+ # interpolated string or not
195
+ # * +delim+ is the text to use as the delimiter of the region. If +nil+,
196
+ # the next character will be treated as the delimiter.
197
+ # * +heredoc+ is either +false+, meaning the region is not a heredoc, or
198
+ # <tt>:flush</tt> (meaning the delimiter must be flushed left), or
199
+ # <tt>:float</tt> (meaning the delimiter doens't have to be flush left).
200
+ def scan_delimited_region( delim_group, inner_group, starter, exprs,
201
+ delim=nil, heredoc=false )
202
+ # begin
203
+ if !delim
204
+ start_group delim_group, starter
205
+ delim = scan( /./ )
206
+ append delim
207
+
208
+ delim = case delim
209
+ when '{' then '}'
210
+ when '(' then ')'
211
+ when '[' then ']'
212
+ when '<' then '>'
213
+ else delim
214
+ end
215
+ end
216
+
217
+ start_region inner_group
218
+
219
+ items = "\\\\|"
220
+ if heredoc
221
+ items << "(^"
222
+ items << '\s*' if heredoc == :float
223
+ items << "#{Regexp.escape(delim)}\s*?)#{EOL}"
224
+ else
225
+ items << "#{Regexp.escape(delim)}"
226
+ end
227
+ items << "|#(\\$|@@?|\\{)" if exprs
228
+ items = Regexp.new( items )
229
+
230
+ loop do
231
+ p = pos
232
+ match = scan_until( items )
233
+ if match.nil?
234
+ start_group inner_group, scan_until( /\Z/ )
235
+ break
236
+ else
237
+ text = pre_match[p..-1]
238
+ start_group inner_group, text if text.length > 0
239
+ case matched.strip
240
+ when "\\"
241
+ unless exprs
242
+ case peek(1)
243
+ when "'"
244
+ scan(/./)
245
+ start_group :escape, "\\'"
246
+ when "\\"
247
+ scan(/./)
248
+ start_group :escape, "\\\\"
249
+ else
250
+ start_group inner_group, "\\"
251
+ end
252
+ else
253
+ start_group :escape, "\\"
254
+ c = getch
255
+ append c
256
+ case c
257
+ when 'x'
258
+ append scan( /[a-fA-F0-9]{1,2}/ )
259
+ when /[0-7]/
260
+ append scan( /[0-7]{0,2}/ )
261
+ end
262
+ end
263
+ when delim
264
+ end_region inner_group
265
+ start_group delim_group, matched
266
+ break
267
+ when /^#/
268
+ do_highlight = (option(:expressions) == :highlight)
269
+ start_region :expr if do_highlight
270
+ start_group :expr, matched
271
+ case matched[1]
272
+ when ?{
273
+ depth = 1
274
+ content = ""
275
+ while depth > 0
276
+ p = pos
277
+ c = scan_until( /[\{}]/ )
278
+ if c.nil?
279
+ content << scan_until( /\Z/ )
280
+ break
281
+ else
282
+ depth += ( matched == "{" ? 1 : -1 )
283
+ content << pre_match[p..-1]
284
+ content << matched if depth > 0
285
+ end
286
+ end
287
+ if do_highlight
288
+ subtokenize "ruby", content
289
+ start_group :expr, "}"
290
+ else
291
+ append content + "}"
292
+ end
293
+ when ?$, ?@
294
+ append scan( /\w+/ )
295
+ end
296
+ end_region :expr if do_highlight
297
+ else raise "unexpected match on #{matched}"
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ # Scan a heredoc beginning at the current position.
304
+ #
305
+ # * +float+ indicates whether the delimiter may be floated to the right
306
+ # * +type+ is +nil+, a single quote, or a double quote
307
+ # * +delim+ is the delimiter to look for
308
+ def scan_heredoc(float, type, delim)
309
+ scan_delimited_region( :constant, :string, "", type != "'",
310
+ delim, float ? :float : :flush )
311
+ end
312
+ end
313
+
314
+ SYNTAX["ruby"] = Ruby
315
+
316
+ end
@@ -0,0 +1,222 @@
1
+ # syntax highlighting
2
+
3
+ require_relative 'common'
4
+
5
+ module HH::Markup
6
+
7
+ TOKENIZER = HH::Syntax.load "ruby"
8
+ COLORS = {
9
+ :comment => {:stroke => "#887"},
10
+ :keyword => {:stroke => "#111"},
11
+ :method => {:stroke => "#C09", :weight => "bold"},
12
+ # :class => {:stroke => "#0c4", :weight => "bold"},
13
+ # :module => {:stroke => "#050"},
14
+ # :punct => {:stroke => "#668", :weight => "bold"},
15
+ :symbol => {:stroke => "#C30"},
16
+ :string => {:stroke => "#C90"},
17
+ :number => {:stroke => "#396" },
18
+ :regex => {:stroke => "#000", :fill => "#FFC" },
19
+ # :char => {:stroke => "#f07"},
20
+ :attribute => {:stroke => "#369" },
21
+ # :global => {:stroke => "#7FB" },
22
+ :expr => {:stroke => "#722" },
23
+ # :escape => {:stroke => "#277" }
24
+ :ident => {:stroke => "#994c99"},
25
+ :constant => {:stroke => "#630", :weight => "bold"},
26
+ :class => {:stroke => "#630", :weight => "bold"},
27
+ :matching => {:stroke => "#ff0", :weight => "bold"},
28
+ }
29
+
30
+
31
+ def highlight str, pos=nil, colors=COLORS
32
+ tokens = []
33
+ TOKENIZER.tokenize(str) do |t|
34
+ if t.group == :punct
35
+ # split punctuation into single characters tokens
36
+ # TODO: to it in the parser
37
+ tokens += t.split('').map{|s| HH::Syntax::Token.new(s, :punct)}
38
+ else
39
+ # add token as is
40
+ tokens << t
41
+ end
42
+ end
43
+
44
+ res = []
45
+ tokens.each do |token|
46
+ res <<
47
+ if colors[token.group]
48
+ #span(token, colors[token.group])
49
+ tmp = fg(token, tr_color(colors[token.group][:stroke]))
50
+ colors[token.group][:fill] ? bg(tmp, tr_color(colors[token.group][:fill])) : tmp
51
+ elsif colors[:any]
52
+ #span(token, colors[:any])
53
+ tmp = fg(token, tr_color(colors[:any][:stroke]))
54
+ colors[:any][:fill] ? bg(tmp, tr_color(colors[:any][:fill])) : tmp
55
+ else
56
+ token
57
+ end
58
+ end
59
+
60
+ if pos.nil? or pos < 0
61
+ return res.join
62
+ end
63
+
64
+ token_index, matching_index = matching_token(tokens, pos)
65
+
66
+ if token_index
67
+ #res[token_index] = span(tokens[token_index], colors[:matching])
68
+ tmp = fg(tokens[token_index], tr_color(colors[:matching][:stroke]))
69
+ res[token_index] = colors[:matching][:fill] ? bg(tmp, tr_color(colors[:matching][:fill])) : tmp
70
+ if matching_index
71
+ #res[matching_index] = span(tokens[matching_index], colors[:matching])
72
+ tmp = fg(tokens[matching_index], tr_color(colors[:matching][:stroke]))
73
+ res[matching_index] = colors[:matching][:fill] ? bg(tmp, tr_color(colors[:matching][:fill])) : tmp
74
+ end
75
+ end
76
+
77
+ res.join
78
+ end
79
+
80
+ private
81
+ def matching_token(tokens, pos)
82
+ curr_pos = 0
83
+ token_index = nil
84
+ tokens.each_with_index do |t, i|
85
+ curr_pos += t.size
86
+ if token_index.nil? and curr_pos >= pos
87
+ token_index = i
88
+ break
89
+ end
90
+ end
91
+ if token_index.nil? then return nil end
92
+
93
+ match = matching_token_at_index(tokens, token_index);
94
+ if match.nil? and curr_pos == pos and token_index < tokens.size-1
95
+ # try the token before the cursor, instead of the one after
96
+ token_index += 1
97
+ match = matching_token_at_index(tokens, token_index)
98
+ end
99
+
100
+ if match
101
+ [token_index, match]
102
+ else
103
+ nil
104
+ end
105
+ end
106
+
107
+
108
+ def matching_token_at_index(tokens, index)
109
+ starts, ends, direction = matching_tokens(tokens, index)
110
+ if starts.nil?
111
+ return nil
112
+ end
113
+
114
+ stack_level = 1
115
+ index += direction
116
+ while index >= 0 and index < tokens.size
117
+ # TODO separate space in the tokenizer
118
+ t = tokens[index].gsub(/\s/, '')
119
+ if ends.include?(t) and not as_modifier?(tokens, index)
120
+ stack_level -= 1
121
+ return index if stack_level == 0
122
+ elsif starts.include?(t) and not as_modifier?(tokens, index)
123
+ stack_level += 1
124
+ end
125
+ index += direction
126
+ end
127
+ # no matching token found
128
+ return nil
129
+ end
130
+
131
+ # returns an array of tokens matching and the direction
132
+ def matching_tokens(tokens, index)
133
+ # TODO separate space in the tokenizer
134
+ token = tokens[index].gsub(/\s/, '')
135
+ starts = [token]
136
+ if OPEN_BRACKETS[token]
137
+ direction = 1
138
+ ends = [OPEN_BRACKETS[token]]
139
+ elsif CLOSE_BRACKETS[token]
140
+ direction = -1
141
+ ends = [CLOSE_BRACKETS[token]]
142
+ elsif OPEN_BLOCK.include?(token)
143
+ if as_modifier?(tokens, index)
144
+ return nil
145
+ end
146
+ direction = 1
147
+ ends = ['end']
148
+ starts = OPEN_BLOCK
149
+ elsif token == 'end'
150
+ direction = -1
151
+ ends = OPEN_BLOCK
152
+ else
153
+ return nil
154
+ end
155
+
156
+ [starts, ends, direction]
157
+ end
158
+
159
+ def as_modifier?(tokens, index)
160
+
161
+ if not MODIFIERS.include? tokens[index].gsub(/\s/, '')
162
+ return false
163
+ end
164
+
165
+ index -= 1
166
+ # find last index before the token that is no space
167
+ index -= 1 while index >= 0 and tokens[index] =~ /\A[ \t]*\z/
168
+
169
+ if index < 0
170
+ # first character of the string
171
+ false
172
+ elsif tokens[index] =~ /\n[ \t]*\Z/
173
+ # first token of the line
174
+ false
175
+ elsif tokens[index].group == :punct
176
+ # preceded by a punctuation token on the same line
177
+ i = tokens[index].rindex(/\S/)
178
+ punc = tokens[index][i, 1]
179
+ # true if the preceeding statement was terminating
180
+ not NON_TERMINATING.include?(punc)
181
+ else
182
+ # preceded by a non punctuation token on the same line
183
+ true
184
+ end
185
+ end
186
+
187
+
188
+ OPEN_BRACKETS = {
189
+ '{' => '}',
190
+ '(' => ')',
191
+ '[' => ']',
192
+ }
193
+
194
+ #close_bracket = {}
195
+ #OPEN_BRACKETS.each{|open, close| opens_bracket[close] = open}
196
+ #CLOSE_BRACKETS = opens_bracket
197
+ # the following is more readable :)
198
+ CLOSE_BRACKETS = {
199
+ '}' => '{',
200
+ ')' => '(',
201
+ ']' => '[',
202
+ }
203
+
204
+ BRACKETS = CLOSE_BRACKETS.keys + OPEN_BRACKETS.keys
205
+
206
+ OPEN_BLOCK = [
207
+ 'def',
208
+ 'class',
209
+ 'module',
210
+ 'do',
211
+ 'if',
212
+ 'unless',
213
+ 'while',
214
+ 'until',
215
+ 'begin',
216
+ 'for'
217
+ ]
218
+
219
+ MODIFIERS = %w[if unless while until]
220
+
221
+ NON_TERMINATING = %w{+ - * / , . = ~ < > ( [}
222
+ end
data/lib/green_shoes.rb CHANGED
@@ -48,3 +48,4 @@ autoload :ChipMunk, File.join(Shoes::DIR, 'ext/chipmunk')
48
48
  autoload :Bloops, File.join(Shoes::DIR, 'ext/bloops')
49
49
  autoload :Projector, File.join(Shoes::DIR, 'ext/projector')
50
50
  autoload :Hpricot, File.join(Shoes::DIR, 'ext/hpricot')
51
+ autoload :HH, File.join(Shoes::DIR, 'ext/highlighter')
data/lib/shoes/basic.rb CHANGED
@@ -92,6 +92,8 @@ class Shoes
92
92
  end
93
93
  end
94
94
 
95
+ alias :clear_all :clear
96
+
95
97
  def positioning x, y, max
96
98
  if parent.is_a?(Flow) and x + @width <= parent.left + parent.width
97
99
  move3 x + parent.margin_left, max.top + parent.margin_top
data/lib/shoes/help.rb CHANGED
@@ -3,6 +3,7 @@ class Manual < Shoes
3
3
  url '/manual/(\d+)', :index
4
4
 
5
5
  include Hpricot
6
+ include HH::Markup
6
7
 
7
8
  def index pnum = 0
8
9
  font LANG == 'ja' ? 'MS UI Gothic' : 'Arial'
@@ -84,7 +85,11 @@ class Manual < Shoes
84
85
  flow do
85
86
  background rgb(190, 190, 190), curve: 5
86
87
  inscription link(fg('Run this', green)){eval mk_executable(_code), TOPLEVEL_BINDING}, margin_left: 480
87
- para fg(code(' ' + _code), maroon), NL, margin_left: -10
88
+ if _code.include? 'te-su-to'
89
+ para fg(code(' ' + _code), maroon), NL, margin_left: -10
90
+ else
91
+ para code(highlight(' ' + _code, nil)), NL, margin_left: -10
92
+ end
88
93
  end
89
94
  para
90
95
  end
@@ -329,7 +334,7 @@ class Manual < Shoes
329
334
 
330
335
  def mk_sidebar_list num
331
336
  toc = []
332
- [0..3, 4..9, 10..16, 17..32, 33..35].each do |r|
337
+ [0..3, 4..9, 10..16, 17..32, 33..36].each do |r|
333
338
  toc.push TOC_LIST[r.first][0]
334
339
  toc.push(TOC_LIST[r.first+1..r.last].to_a.map &:first) if r.include?(num)
335
340
  end
@@ -194,7 +194,7 @@ class Shoes
194
194
  mask_control app
195
195
  repaint_all_by_order app
196
196
  repaint_textcursors app
197
- app.canvas.set_size 0, scrollable_height unless app.prjct
197
+ app.canvas.set_size 0, scrollable_height unless(app.prjct or app.trvw)
198
198
  true
199
199
  end
200
200
 
@@ -284,6 +284,7 @@ class Shoes
284
284
  def self.mouse_on? e
285
285
  if e.is_a? Slot
286
286
  mouse_x, mouse_y = e.app.win.pointer
287
+ mouse_y += e.app.scroll_top
287
288
  (e.left..e.left+e.width).include?(mouse_x) and (e.top..e.top+e.height).include?(mouse_y)
288
289
  else
289
290
  mouse_x, mouse_y = e.real.pointer
data/lib/shoes/main.rb CHANGED
@@ -12,7 +12,8 @@ class Shoes
12
12
  args[:left] ||= 0
13
13
  args[:top] ||= 0
14
14
  projector = args[:prjct] = args[:projector]
15
- args.delete :projector
15
+ treeview = args[:trvw] = args[:treeview]
16
+ [:projector, :treeview].each{|x| args.delete x}
16
17
 
17
18
  app = App.new args
18
19
  @apps.push app
@@ -62,7 +63,11 @@ class Shoes
62
63
  mouse_leave_control app
63
64
  end
64
65
 
65
- app.canvas = projector ? Gtk::DrawingArea.new : Gtk::Layout.new
66
+ app.canvas = if treeview
67
+ Gtk::TreeView.new
68
+ else
69
+ projector ? Gtk::DrawingArea.new : Gtk::Layout.new
70
+ end
66
71
  swin = Gtk::ScrolledWindow.new
67
72
  swin.set_policy Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC
68
73
  swin.vadjustment.step_increment = 10
data/lib/shoes/ruby.rb CHANGED
@@ -143,6 +143,10 @@ class Array
143
143
  def clear
144
144
  self.each &:clear
145
145
  end
146
+
147
+ def clear_all
148
+ self.each &:clear_all
149
+ end
146
150
  end
147
151
 
148
152
  class NilClass
data/lib/shoes/slot.rb CHANGED
@@ -74,9 +74,8 @@ class Shoes
74
74
 
75
75
  def fix_size; end
76
76
 
77
- def clear &blk
78
- @app.delete_mouse_events self
79
- @contents.each &:clear
77
+ def clear all = false, &blk
78
+ all ? @contents.each(&:clear_all) : @contents.each(&:clear)
80
79
  @contents.each{|e| @app.mlcs.delete e; @app.mhcs.delete e}
81
80
  @contents = []
82
81
  if blk
@@ -91,6 +90,11 @@ class Shoes
91
90
  end
92
91
  end
93
92
 
93
+ def clear_all &blk
94
+ @app.delete_mouse_events self
95
+ clear true, &blk
96
+ end
97
+
94
98
  def append &blk
95
99
  prepend contents.length, &blk
96
100
  end
@@ -0,0 +1,32 @@
1
+ require '../lib/green_shoes'
2
+
3
+ Shoes.app do
4
+ extend HH::Markup
5
+ colors = {
6
+ :comment => {:stroke => "#bba"},
7
+ :keyword => {:stroke => "#FCF91F"},
8
+ :method => {:stroke => "#C09"},
9
+ :symbol => {:stroke => "#9DF3C6"},
10
+ :string => {:stroke => "#C9F5A5"},
11
+ :number => {:stroke => "#C9F5A5"},
12
+ :regex => {:stroke => "#000", :fill => "#FFC" },
13
+ :attribute => {:stroke => "#C9F5A5"},
14
+ :expr => {:stroke => "#f33" },
15
+ :ident => {:stroke => "#6e7"},
16
+ :any => {:stroke => "#FFF"},
17
+ :constant => {:stroke => "#55f"},
18
+ :class => {:stroke => "#55f"},
19
+ :matching => {:stroke => "#f00"},
20
+ }
21
+ code = IO.read(ask_open_file)
22
+ button 'change color' do
23
+ @slot.clear do
24
+ background gray 0.1
25
+ para highlight code, nil, colors
26
+ end
27
+ end
28
+ @slot = stack do
29
+ background gainsboro
30
+ para highlight code, nil
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ require '../lib/green_shoes'
2
+
3
+ Shoes.app do
4
+ button 'Open TreeView Window' do
5
+ Shoes.app width: 275, height: 300, treeview: true do
6
+ $app = app
7
+ load './treeview.rb'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ # The following code was quoted from http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk2-treev-trees
2
+ # and edited a little bit for Green Shoes
3
+
4
+ def setup_tree_view(treeview)
5
+ renderer = Gtk::CellRendererText.new
6
+ column = Gtk::TreeViewColumn.new("Buy", renderer, "text" => $buy_index)
7
+ treeview.append_column(column)
8
+ renderer = Gtk::CellRendererText.new
9
+ column = Gtk::TreeViewColumn.new("Count", renderer, "text" => $qty_index)
10
+ treeview.append_column(column)
11
+ renderer = Gtk::CellRendererText.new
12
+ column = Gtk::TreeViewColumn.new("Product", renderer, "text" => $prod_index)
13
+ treeview.append_column(column)
14
+ end
15
+
16
+ class GroceryItem
17
+ attr_accessor :product_type, :buy, :quantity, :product
18
+ def initialize(t,b,q,p)
19
+ @product_type, @buy, @quantity, @product = t, b, q, p
20
+ end
21
+ end
22
+ $buy_index = 0; $qty_index = 1; $prod_index = 2
23
+ $p_category = 0; $p_child = 1
24
+
25
+ list = Array.new
26
+ list[0] = GroceryItem.new($p_category, true, 0, "Cleaning Supplies")
27
+ list[1] = GroceryItem.new($p_child, true, 1, "Paper Towels")
28
+ list[2] = GroceryItem.new($p_child, true, 3, "Toilet Paper")
29
+ list[3] = GroceryItem.new($p_category, true, 0, "Food")
30
+ list[4] = GroceryItem.new($p_child, true, 2, "Bread")
31
+ list[5] = GroceryItem.new($p_child, false, 1, "Butter")
32
+ list[6] = GroceryItem.new($p_child, true, 1, "Milk")
33
+ list[7] = GroceryItem.new($p_child, false, 3, "Chips")
34
+ list[8] = GroceryItem.new($p_child, true, 4, "Soda")
35
+
36
+ setup_tree_view($app.canvas)
37
+
38
+ store = Gtk::TreeStore.new(TrueClass, Integer, String)
39
+ parent = child = nil
40
+
41
+ list.each_with_index do |e, i|
42
+ if (e.product_type == $p_category)
43
+ j = i + 1
44
+ while j < list.size && list[j].product_type != $p_category
45
+ list[i].quantity += list[j].quantity if list[j].buy
46
+ j += 1
47
+ end
48
+ parent = store.append(nil)
49
+ parent[$buy_index] = list[i].buy
50
+ parent[$qty_index] = list[i].quantity
51
+ parent[$prod_index] = list[i].product
52
+ else
53
+ child = store.append(parent)
54
+ child[$buy_index] = list[i].buy
55
+ child[$qty_index] = list[i].quantity
56
+ child[$prod_index] = list[i].product
57
+ end
58
+ end
59
+
60
+ $app.canvas.model = store
Binary file
Binary file
data/static/manual-en.txt CHANGED
@@ -3877,3 +3877,34 @@ Hope this helps:
3877
3877
  * You can join [[http://librelist.com/browser/shoes/ Shoes ML]] and feel free ask your questions.
3878
3878
  * [[https://github.com/ashbb/green_shoes/ Current Source Code]] is on GitHub.
3879
3879
  * Green Shoes Gem is on [[http://rubygems.org/gems/green_shoes RubyGems.org]].
3880
+
3881
+ == vs.RedShoes ==
3882
+
3883
+ Green Shoes is following Red Shoes, but not fully compatible.
3884
+
3885
+ ==== TextBlock ====
3886
+
3887
+ The following two snippets are same in Red Shoes.
3888
+
3889
+ {{{
3890
+ Shoes.app do
3891
+ para 'hello ' * 20
3892
+ end
3893
+ }}}
3894
+
3895
+ {{{
3896
+ Shoes.app do
3897
+ 20.times{para 'hello '}
3898
+ end
3899
+ }}}
3900
+
3901
+ But in Green Shoes, need to add `:width` size explicitly.
3902
+
3903
+ {{{
3904
+ Shoes.app do
3905
+ 20.times{para 'hello ', width: 40}
3906
+ end
3907
+ }}}
3908
+
3909
+ If you don't specify the `:width` size, Green Shoes makes a TextBlock object with
3910
+ the `parent.width`.
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 227
7
+ - 233
8
8
  - 0
9
- version: 0.227.0
9
+ version: 0.233.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - ashbb
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-06-06 00:00:00 +09:00
17
+ date: 2011-06-14 00:00:00 +09:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -50,6 +50,10 @@ files:
50
50
  - lib/ext/bloops/songs/simpsons_theme_song_by_why.rb
51
51
  - lib/ext/chipmunk.rb
52
52
  - lib/ext/chipmunk/chipmunk.so
53
+ - lib/ext/highlighter.rb
54
+ - lib/ext/highlighter/common.rb
55
+ - lib/ext/highlighter/lang/ruby.rb
56
+ - lib/ext/highlighter/markup.rb
53
57
  - lib/ext/hpricot.rb
54
58
  - lib/ext/hpricot/blankslate.rb
55
59
  - lib/ext/hpricot/builder.rb
@@ -196,6 +200,8 @@ files:
196
200
  - samples/sample5.rb
197
201
  - samples/sample50.rb
198
202
  - samples/sample51.rb
203
+ - samples/sample52.rb
204
+ - samples/sample53.rb
199
205
  - samples/sample6.rb
200
206
  - samples/sample7.rb
201
207
  - samples/sample8.rb
@@ -203,6 +209,7 @@ files:
203
209
  - samples/sample99.rb
204
210
  - samples/shell.png
205
211
  - samples/splash-hand.png
212
+ - samples/treeview.rb
206
213
  - snapshots/helloworld.png
207
214
  - snapshots/mini-hh.png
208
215
  - snapshots/sample1.png
@@ -255,6 +262,8 @@ files:
255
262
  - snapshots/sample5.png
256
263
  - snapshots/sample50.png
257
264
  - snapshots/sample51.png
265
+ - snapshots/sample52.png
266
+ - snapshots/sample53.png
258
267
  - snapshots/sample6.png
259
268
  - snapshots/sample7.png
260
269
  - snapshots/sample8.png