haml-edge 2.3.27 → 2.3.28

Sign up to get free protection for your applications and to get access to all the features.
data/EDGE_GEM_VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.27
1
+ 2.3.28
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.27
1
+ 2.3.28
data/lib/haml/exec.rb CHANGED
@@ -264,7 +264,7 @@ END
264
264
  output.close() if output.is_a? File
265
265
  rescue ::Sass::SyntaxError => e
266
266
  raise e if @options[:trace]
267
- raise "Syntax error on line #{get_line e}: #{e.message}"
267
+ raise e.sass_backtrace_str("standard input")
268
268
  end
269
269
  end
270
270
 
data/lib/sass/engine.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'strscan'
2
2
  require 'digest/sha1'
3
3
  require 'sass/tree/node'
4
+ require 'sass/tree/root_node'
4
5
  require 'sass/tree/rule_node'
5
6
  require 'sass/tree/comment_node'
6
7
  require 'sass/tree/prop_node'
@@ -155,11 +156,14 @@ module Sass
155
156
  # @return [Sass::Tree::Node] The root of the parse tree.
156
157
  # @raise [Sass::SyntaxError] if there's an error in the document
157
158
  def to_tree
158
- root = Tree::Node.new
159
+ root = Tree::RootNode.new(@template)
159
160
  append_children(root, tree(tabulate(@template)).first, true)
160
161
  root.options = @options
161
162
  root
162
- rescue SyntaxError => e; e.add_metadata(@options[:filename], @line)
163
+ rescue SyntaxError => e
164
+ e.modify_backtrace(:filename => @options[:filename], :line => @line)
165
+ e.sass_template = @template
166
+ raise e
163
167
  end
164
168
 
165
169
  private
@@ -179,10 +183,11 @@ module Sass
179
183
  unless line_tab_str.empty?
180
184
  tab_str ||= line_tab_str
181
185
 
182
- raise SyntaxError.new("Indenting at the beginning of the document is illegal.", index) if first
183
- if tab_str.include?(?\s) && tab_str.include?(?\t)
184
- raise SyntaxError.new("Indentation can't use both tabs and spaces.", index)
185
- end
186
+ raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
187
+ :line => index) if first
188
+
189
+ raise SyntaxError.new("Indentation can't use both tabs and spaces.",
190
+ :line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
186
191
  end
187
192
  first &&= !tab_str.nil?
188
193
  if tab_str.nil?
@@ -196,10 +201,14 @@ module Sass
196
201
  end
197
202
 
198
203
  line_tabs = line_tab_str.scan(tab_str).size
199
- raise SyntaxError.new(<<END.strip.gsub("\n", ' '), index) if tab_str * line_tabs != line_tab_str
204
+ if tab_str * line_tabs != line_tab_str
205
+ message = <<END.strip.gsub("\n", ' ')
200
206
  Inconsistent indentation: #{Haml::Shared.human_indentation line_tab_str, true} used for indentation,
201
207
  but the rest of the document was indented using #{Haml::Shared.human_indentation tab_str}.
202
208
  END
209
+ raise SyntaxError.new(message, :line => index)
210
+ end
211
+
203
212
  lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
204
213
  end
205
214
  lines
@@ -212,9 +221,8 @@ END
212
221
  nodes = []
213
222
  while (line = arr[i]) && line.tabs >= base
214
223
  if line.tabs > base
215
- if line.tabs > base + 1
216
- raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.", line.index)
217
- end
224
+ raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
225
+ :line => line.index) if line.tabs > base + 1
218
226
 
219
227
  nodes.last.children, i = tree(arr, i)
220
228
  else
@@ -252,7 +260,8 @@ END
252
260
  child = build_tree(parent, line, root)
253
261
 
254
262
  if child.is_a?(Tree::RuleNode) && child.continued?
255
- raise SyntaxError.new("Rules can't end in commas.", child.line) unless child.children.empty?
263
+ raise SyntaxError.new("Rules can't end in commas.",
264
+ :line => child.line) unless child.children.empty?
256
265
  if continued_rule
257
266
  continued_rule.add_rules child
258
267
  else
@@ -262,7 +271,8 @@ END
262
271
  end
263
272
 
264
273
  if continued_rule
265
- raise SyntaxError.new("Rules can't end in commas.", continued_rule.line) unless child.is_a?(Tree::RuleNode)
274
+ raise SyntaxError.new("Rules can't end in commas.",
275
+ :line => continued_rule.line) unless child.is_a?(Tree::RuleNode)
266
276
  continued_rule.add_rules child
267
277
  continued_rule.children = child.children
268
278
  continued_rule, child = nil, continued_rule
@@ -272,7 +282,8 @@ END
272
282
  validate_and_append_child(parent, child, line, root)
273
283
  end
274
284
 
275
- raise SyntaxError.new("Rules can't end in commas.", continued_rule.line) if continued_rule
285
+ raise SyntaxError.new("Rules can't end in commas.",
286
+ :line => continued_rule.line) if continued_rule
276
287
 
277
288
  parent
278
289
  end
@@ -281,9 +292,11 @@ END
281
292
  unless root
282
293
  case child
283
294
  when Tree::MixinDefNode
284
- raise SyntaxError.new("Mixins may only be defined at the root of a document.", line.index)
295
+ raise SyntaxError.new("Mixins may only be defined at the root of a document.",
296
+ :line => line.index)
285
297
  when Tree::ImportNode
286
- raise SyntaxError.new("Import directives may only be used at the root of a document.", line.index)
298
+ raise SyntaxError.new("Import directives may only be used at the root of a document.",
299
+ :line => line.index)
287
300
  end
288
301
  end
289
302
 
@@ -349,9 +362,9 @@ LONG
349
362
  def parse_property(line, property_regx)
350
363
  name, eq, value = line.text.scan(property_regx)[0]
351
364
 
352
- if name.nil? || value.nil?
353
- raise SyntaxError.new("Invalid property: \"#{line.text}\".", @line)
354
- end
365
+ raise SyntaxError.new("Invalid property: \"#{line.text}\".",
366
+ :line => @line) if name.nil? || value.nil?
367
+
355
368
  expr = if (eq.strip[0] == SCRIPT_CHAR)
356
369
  parse_script(value, :offset => line.offset + line.text.index(value))
357
370
  else
@@ -362,8 +375,10 @@ LONG
362
375
 
363
376
  def parse_variable(line)
364
377
  name, op, value = line.text.scan(Script::MATCH)[0]
365
- raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.", @line + 1) unless line.children.empty?
366
- raise SyntaxError.new("Invalid variable: \"#{line.text}\".", @line) unless name && value
378
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
379
+ :line => @line + 1) unless line.children.empty?
380
+ raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
381
+ :line => @line) unless name && value
367
382
 
368
383
  Tree::VariableNode.new(name, parse_script(value, :offset => line.offset + line.text.index(value)), op == '||=')
369
384
  end
@@ -383,7 +398,8 @@ LONG
383
398
  # If value begins with url( or ",
384
399
  # it's a CSS @import rule and we don't want to touch it.
385
400
  if directive == "import" && value !~ /^(url\(|")/
386
- raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.", @line + 1) unless line.children.empty?
401
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
402
+ :line => @line + 1) unless line.children.empty?
387
403
  value.split(/,\s*/).map {|f| Tree::ImportNode.new(f)}
388
404
  elsif directive == "for"
389
405
  parse_for(line, root, value)
@@ -397,7 +413,8 @@ LONG
397
413
  Tree::IfNode.new(parse_script(value, :offset => offset))
398
414
  elsif directive == "debug"
399
415
  raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
400
- raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.", @line + 1) unless line.children.empty?
416
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
417
+ :line => @line + 1) unless line.children.empty?
401
418
  offset = line.offset + line.text.index(value).to_i
402
419
  Tree::DebugNode.new(parse_script(value, :offset => offset))
403
420
  else
@@ -416,9 +433,9 @@ LONG
416
433
  else
417
434
  expected = "'to <expr>' or 'through <expr>'"
418
435
  end
419
- raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.", @line)
436
+ raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
420
437
  end
421
- raise SyntaxError.new("Invalid variable \"#{var}\".", @line) unless var =~ Script::VALIDATE
438
+ raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
422
439
 
423
440
  parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
424
441
  parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
@@ -431,7 +448,7 @@ LONG
431
448
 
432
449
  if text
433
450
  if text !~ /^if\s+(.+)/
434
- raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.", @line)
451
+ raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
435
452
  end
436
453
  expr = parse_script($1, :offset => line.offset + line.text.index($1))
437
454
  end
@@ -444,7 +461,7 @@ LONG
444
461
 
445
462
  def parse_mixin_definition(line)
446
463
  name, arg_string = line.text.scan(/^=\s*([^(]+)(.*)$/).first
447
- raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".", @line) if name.nil?
464
+ raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
448
465
 
449
466
  offset = line.offset + line.text.size - arg_string.size
450
467
  args = Script::Parser.new(arg_string.strip, @line, offset).parse_mixin_definition_arglist
@@ -454,11 +471,12 @@ LONG
454
471
 
455
472
  def parse_mixin_include(line, root)
456
473
  name, arg_string = line.text.scan(/^\+\s*([^(]+)(.*)$/).first
457
- raise SyntaxError.new("Invalid mixin include \"#{line.text}\".", @line) if name.nil?
474
+ raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
458
475
 
459
476
  offset = line.offset + line.text.size - arg_string.size
460
477
  args = Script::Parser.new(arg_string.strip, @line, offset).parse_mixin_include_arglist
461
- raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.", @line + 1) unless line.children.empty?
478
+ raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
479
+ :line => @line + 1) unless line.children.empty?
462
480
  Tree::MixinNode.new(name, args)
463
481
  end
464
482
 
data/lib/sass/error.rb CHANGED
@@ -4,51 +4,156 @@ module Sass
4
4
  # and the Sass file that was being parsed (if applicable).
5
5
  #
6
6
  # All Sass errors are raised as {Sass::SyntaxError}s.
7
+ #
8
+ # When dealing with SyntaxErrors,
9
+ # it's important to provide filename and line number information.
10
+ # This will be used in various error reports to users, including backtraces;
11
+ # see \{#sass\_backtrace} for details.
12
+ #
13
+ # Some of this information is usually provided as part of the constructor.
14
+ # New backtrace entries can be added with \{#add\_backtrace},
15
+ # which is called when an exception is raised between files (e.g. with `@import`).
16
+ #
17
+ # Often, a chunk of code will all have similar backtrace information -
18
+ # the same filename or even line.
19
+ # It may also be useful to have a default line number set.
20
+ # In those situations, the default values can be used
21
+ # by omitting the information on the original exception,
22
+ # and then calling \{#modify\_backtrace} in a wrapper `rescue`.
23
+ # When doing this, be sure that all exceptions ultimately end up
24
+ # with the information filled in.
7
25
  class SyntaxError < StandardError
8
- # The line of the Sass template on which the error occurred.
26
+ # The backtrace of the error within Sass files.
27
+ # This is an array of hashes containing information for a single entry.
28
+ # The hashes have the following keys:
9
29
  #
10
- # @return [Fixnum]
11
- attr_accessor :sass_line
30
+ # `:filename`
31
+ # : The name of the file in which the exception was raised,
32
+ # or `nil` if no filename is available.
33
+ #
34
+ # `:line`
35
+ # : The line of the file on which the error occurred. Never nil.
36
+ #
37
+ # This information is also included in standard backtrace format
38
+ # in the output of \{#backtrace}.
39
+ #
40
+ # @return [Aray<Hash<Symbol, Object>>]
41
+ attr_accessor :sass_backtrace
12
42
 
13
- # The name of the file that was being parsed when the exception was raised.
14
- # This could be `nil` if no filename is available.
43
+ # The text of the template where this error was raised.
15
44
  #
16
45
  # @return [String]
17
- attr_reader :sass_filename
46
+ attr_accessor :sass_template
18
47
 
19
48
  # @param msg [String] The error message
20
- # @param lineno [Fixnum] See \{#sass\_line}
21
- def initialize(msg, lineno = nil)
49
+ # @param attrs [Hash<Symbol, Object>] The information in the backtrace entry.
50
+ # See \{#sass\_backtrace}
51
+ def initialize(msg, attrs = {})
22
52
  @message = msg
23
- @sass_line = lineno
53
+ @sass_backtrace = []
54
+ add_backtrace(attrs)
24
55
  end
25
56
 
26
- # Add information about the filename and line on which the error was raised,
27
- # and re-raises the exception.
57
+ # The name of the file in which the exception was raised.
58
+ # This could be `nil` if no filename is available.
28
59
  #
29
- # @param filename [String] See \{#sass\_filename}
30
- # @param line [Fixnum] See \{#sass\_line}
31
- # @raise [Sass::SyntaxError] self
32
- def add_metadata(filename, line)
33
- self.sass_line ||= line
34
- add_backtrace_entry(filename) unless sass_filename
35
- raise self
60
+ # @return [String]
61
+ def sass_filename
62
+ sass_backtrace.first[:filename]
36
63
  end
37
64
 
38
- # Adds a properly formatted entry to the exception's backtrace.
65
+ # The line of the Sass template on which the error occurred.
39
66
  #
40
- # @param filename [String] The file in which the error occurred,
41
- # if applicable (defaults to "(sass)")
42
- def add_backtrace_entry(filename) # :nodoc:
43
- @sass_filename ||= filename
44
- self.backtrace ||= []
45
- self.backtrace.unshift "#{@sass_filename || '(sass)'}:#{@sass_line}"
67
+ # @return [Fixnum]
68
+ def sass_line
69
+ sass_backtrace.first[:line]
70
+ end
71
+
72
+ # Adds an entry to the exception's Sass backtrace.
73
+ #
74
+ # @param attrs [Hash<Symbol, Object>] The information in the backtrace entry.
75
+ # See \{#sass\_backtrace}
76
+ def add_backtrace(attrs)
77
+ sass_backtrace << attrs.reject {|k, v| v.nil?}
78
+ end
79
+
80
+ # Modify the top Sass backtrace entry (that is, the last one)
81
+ # to have the given attributes.
82
+ # If that entry already has one of the given attributes set,
83
+ # that takes precendence.
84
+ #
85
+ # @param attrs [Hash<Symbol, Object>] The information to add to the backtrace entry.
86
+ # See \{#sass\_backtrace}
87
+ def modify_backtrace(attrs)
88
+ sass_backtrace[-1] = attrs.reject {|k, v| v.nil?}.merge(sass_backtrace.last)
46
89
  end
47
90
 
48
91
  # @return [String] The error message
49
92
  def to_s
50
93
  @message
51
94
  end
95
+
96
+ # Returns the standard exception backtrace,
97
+ # including the Sass backtrace.
98
+ #
99
+ # @return [Array<String>]
100
+ def backtrace
101
+ return nil if super.nil?
102
+ sass_backtrace.map {|h| "#{h[:filename] || "(sass)"}:#{h[:line]}"} + super
103
+ end
104
+
105
+ # Returns a string representation of the Sass backtrace.
106
+ #
107
+ # @param default_filename [String] The filename to use for unknown files
108
+ # @see #sass_backtrace
109
+ # @return [String]
110
+ def sass_backtrace_str(default_filename = "an unknown file")
111
+ "Syntax error: #{message}" +
112
+ Haml::Util.enum_with_index(sass_backtrace).map do |entry, i|
113
+ "\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" +
114
+ " of #{entry[:filename] || default_filename}"
115
+ end.join
116
+ end
117
+
118
+ class << self
119
+ # Returns an error report for an exception in CSS format.
120
+ #
121
+ # @param e [Exception]
122
+ # @param full_exception [Boolean] The value of
123
+ # \{file:SASS\_REFERENCE.md#full_exception-option `options[:full_exception]`}
124
+ def exception_to_css(e, options)
125
+ return "/* Internal stylesheet error */" unless options[:full_exception]
126
+
127
+ header = header_string(e, options)
128
+
129
+ <<END
130
+ /*
131
+ #{header}
132
+
133
+ Backtrace:\n#{e.backtrace.join("\n")}
134
+ */
135
+ body:before {
136
+ white-space: pre;
137
+ font-family: monospace;
138
+ content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; }
139
+ END
140
+ end
141
+
142
+ private
143
+
144
+ def header_string(e, options)
145
+ return "#{e.class}: #{e.message}" unless e.is_a? Sass::SyntaxError
146
+
147
+ line_offset = options[:line] || 1
148
+ line_num = e.sass_line + 1 - line_offset
149
+ min = [line_num - 6, 0].max
150
+ section = e.sass_template.rstrip.split("\n")[min ... line_num + 5]
151
+ return e.sass_backtrace_str if section.nil? || section.empty?
152
+
153
+ e.sass_backtrace_str + "\n\n" + Haml::Util.enum_with_index(section).
154
+ map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
155
+ end
156
+ end
52
157
  end
53
158
 
54
159
  # The class for Sass errors that are raised due to invalid unit conversions
data/lib/sass/files.rb CHANGED
@@ -13,7 +13,8 @@ module Sass
13
13
  # @param filename [String] The path to the Sass file
14
14
  # @param options [Hash<Symbol, Object>] The options hash.
15
15
  # Only the {file:SASS_REFERENCE.md#cache-option `:cache_location`} option is used
16
- # @raise [Sass::SyntaxError] if there's an error in the document
16
+ # @raise [Sass::SyntaxError] if there's an error in the document.
17
+ # The caller has responsibility for setting backtrace information, if necessary
17
18
  def tree_for(filename, options)
18
19
  options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
19
20
  text = File.read(filename)
@@ -30,15 +31,8 @@ module Sass
30
31
 
31
32
  engine = Sass::Engine.new(text, options.merge(:filename => filename))
32
33
 
33
- begin
34
- root = engine.to_tree
35
- rescue Sass::SyntaxError => err
36
- err.add_backtrace_entry(filename)
37
- raise err
38
- end
39
-
34
+ root = engine.to_tree
40
35
  try_to_write_sassc(root, compiled_filename, sha, options) if options[:cache]
41
-
42
36
  root
43
37
  end
44
38
 
@@ -77,7 +71,7 @@ module Sass
77
71
 
78
72
  return new_filename if new_filename
79
73
  return filename + '.css' unless was_sass
80
- raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.", @line)
74
+ raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.")
81
75
  end
82
76
 
83
77
  private
data/lib/sass/plugin.rb CHANGED
@@ -9,6 +9,7 @@ module Sass
9
9
  # when it's used as a plugin for various frameworks.
10
10
  # Currently Rails and Merb are supported out of the box.
11
11
  module Plugin
12
+ include Haml::Util
12
13
  extend self
13
14
 
14
15
  @options = {
@@ -82,7 +83,7 @@ module Sass
82
83
  result = begin
83
84
  Sass::Files.tree_for(filename, engine_options(:css_filename => css, :filename => filename)).render
84
85
  rescue Exception => e
85
- exception_string(e)
86
+ Sass::SyntaxError.exception_to_css(e, options)
86
87
  end
87
88
 
88
89
  # Create any directories that might be necessary
@@ -122,49 +123,6 @@ module Sass
122
123
  end
123
124
  end
124
125
 
125
- def exception_string(e)
126
- if options[:full_exception]
127
- e_string = "#{e.class}: #{e.message}"
128
-
129
- if e.is_a? Sass::SyntaxError
130
- e_string << "\non line #{e.sass_line}"
131
-
132
- if e.sass_filename
133
- e_string << " of #{e.sass_filename}"
134
-
135
- if File.exists?(e.sass_filename)
136
- e_string << "\n\n"
137
-
138
- min = [e.sass_line - 5, 0].max
139
- begin
140
- File.read(e.sass_filename).rstrip.split("\n")[
141
- min .. e.sass_line + 5
142
- ].each_with_index do |line, i|
143
- e_string << "#{min + i + 1}: #{line}\n"
144
- end
145
- rescue
146
- e_string << "Couldn't read sass file: #{e.sass_filename}"
147
- end
148
- end
149
- end
150
- end
151
- <<END
152
- /*
153
- #{e_string}
154
-
155
- Backtrace:\n#{e.backtrace.join("\n")}
156
- */
157
- body:before {
158
- white-space: pre;
159
- font-family: monospace;
160
- content: "#{e_string.gsub('"', '\"').gsub("\n", '\\A ')}"; }
161
- END
162
- # Fix an emacs syntax-highlighting hiccup: '
163
- else
164
- "/* Internal stylesheet error */"
165
- end
166
- end
167
-
168
126
  def template_filename(name, path)
169
127
  "#{path}/#{name}.sass"
170
128
  end
@@ -158,7 +158,7 @@ END
158
158
  if try_tok(:single_eq)
159
159
  val = assert_expr(:concat)
160
160
  elsif must_have_default
161
- raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.", @line)
161
+ raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
162
162
  end
163
163
 
164
164
  return [[var, val]] unless try_tok(:comma)
data/lib/sass/script.rb CHANGED
@@ -47,12 +47,8 @@ module Sass
47
47
  def self.parse(value, line, offset, filename = nil)
48
48
  Parser.parse(value, line, offset, filename)
49
49
  rescue Sass::SyntaxError => e
50
- if e.message == "SassScript error"
51
- e.instance_eval do
52
- @message += ": #{value.dump}."
53
- end
54
- end
55
- e.sass_line = line
50
+ e.message << ": #{value.inspect}." if e.message == "SassScript error"
51
+ e.modify_backtrace(:line => line, :filename => filename)
56
52
  raise e
57
53
  end
58
54
  end
@@ -38,6 +38,16 @@ module Sass::Tree
38
38
  self.class == other.class && value == other.value && silent == other.silent && lines == other.lines
39
39
  end
40
40
 
41
+ # Returns `true` if this is a silent comment
42
+ # or the current style doesn't render comments.
43
+ #
44
+ # @return [Boolean]
45
+ def invisible?
46
+ style == :compressed || @silent
47
+ end
48
+
49
+ protected
50
+
41
51
  # Computes the CSS for the comment.
42
52
  #
43
53
  # Returns `nil` if this is a silent comment
@@ -47,7 +57,7 @@ module Sass::Tree
47
57
  # @param tabs [Fixnum] The level of indentation for the CSS
48
58
  # @return [String, nil] The resulting CSS
49
59
  # @see #invisible?
50
- def to_s(tabs = 0, _ = nil)
60
+ def _to_s(tabs = 0, _ = nil)
51
61
  return if invisible?
52
62
  spaces = ' ' * (tabs - 1)
53
63
 
@@ -60,16 +70,6 @@ module Sass::Tree
60
70
  spaces + "/* " + content.join(style == :compact ? '' : "\n#{spaces} *") + " */"
61
71
  end
62
72
 
63
- # Returns `true` if this is a silent comment
64
- # or the current style doesn't render comments.
65
- #
66
- # @return [Boolean]
67
- def invisible?
68
- style == :compressed || @silent
69
- end
70
-
71
- protected
72
-
73
73
  # Removes this node from the tree if it's a silent comment.
74
74
  #
75
75
  # @param environment [Sass::Environment] The lexical environment containing
@@ -20,11 +20,13 @@ module Sass::Tree
20
20
  super()
21
21
  end
22
22
 
23
+ protected
24
+
23
25
  # Computes the CSS for the directive.
24
26
  #
25
27
  # @param tabs [Fixnum] The level of indentation for the CSS
26
28
  # @return [String] The resulting CSS
27
- def to_s(tabs)
29
+ def _to_s(tabs)
28
30
  if children.empty?
29
31
  value + ";"
30
32
  else
@@ -3,27 +3,28 @@ module Sass
3
3
  # A static node that wraps the {Sass::Tree} for an `@import`ed file.
4
4
  # It doesn't have a functional purpose other than to add the `@import`ed file
5
5
  # to the backtrace if an error occurs.
6
- class ImportNode < Node
6
+ class ImportNode < RootNode
7
7
  # @param imported_filename [String] The name of the imported file
8
8
  def initialize(imported_filename)
9
9
  @imported_filename = imported_filename
10
- super()
10
+ super(nil)
11
11
  end
12
12
 
13
+ def invisible?; to_s.empty?; end
14
+
15
+ protected
16
+
13
17
  # Computes the CSS for the imported file.
14
18
  #
15
19
  # @param args [Array] Ignored
16
- def to_s(*args)
17
- @to_s ||= (style == :compressed ? super().strip : super())
20
+ def _to_s(*args)
21
+ @to_s ||= (style == :compressed ? super.strip : super)
18
22
  rescue Sass::SyntaxError => e
19
- e.add_backtrace_entry(@filename)
23
+ e.modify_backtrace(:filename => children.first.filename)
24
+ e.add_backtrace(:filename => @filename, :line => @line)
20
25
  raise e
21
26
  end
22
27
 
23
- def invisible?; to_s.empty?; end
24
-
25
- protected
26
-
27
28
  # Parses the imported file
28
29
  # and runs the dynamic Sass for it.
29
30
  #
@@ -31,10 +32,13 @@ module Sass
31
32
  # variable and mixin values
32
33
  def perform!(environment)
33
34
  return unless full_filename = import
34
- self.children = Sass::Files.tree_for(full_filename, @options).children
35
+ root = Sass::Files.tree_for(full_filename, @options)
36
+ @template = root.template
37
+ self.children = root.children
35
38
  self.children = perform_children(environment)
36
39
  rescue Sass::SyntaxError => e
37
- e.add_backtrace_entry(@filename)
40
+ e.modify_backtrace(:filename => full_filename)
41
+ e.add_backtrace(:filename => @filename, :line => @line)
38
42
  raise e
39
43
  end
40
44
 
@@ -50,7 +54,7 @@ module Sass
50
54
  begin
51
55
  full_filename = Sass::Files.find_file_to_import(@imported_filename, import_paths)
52
56
  rescue Exception => e
53
- raise SyntaxError.new(e.message, self.line)
57
+ raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
54
58
  end
55
59
 
56
60
  if full_filename =~ /\.css$/
@@ -24,7 +24,7 @@ module Sass::Tree
24
24
  # @raise [Sass::SyntaxError] if an incorrect number of arguments was passed
25
25
  # @see Sass::Tree
26
26
  def _perform(environment)
27
- raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.", @line) unless mixin = environment.mixin(@name)
27
+ raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
28
28
 
29
29
  raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < @args.size
30
30
  Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
@@ -15,8 +15,7 @@ module Sass
15
15
  # Nodes that only appear in the pre-perform state are called **dynamic nodes**;
16
16
  # those that appear in both states are called **static nodes**.
17
17
  module Tree
18
- # This class doubles as the root node of the parse tree
19
- # and the superclass of all other parse-tree nodes.
18
+ # The abstract superclass of all parse-tree nodes.
20
19
  class Node
21
20
  # The child nodes of this node.
22
21
  #
@@ -66,7 +65,7 @@ module Sass
66
65
  # @see #invalid_child?
67
66
  def <<(child)
68
67
  if msg = invalid_child?(child)
69
- raise Sass::SyntaxError.new(msg, child.line)
68
+ raise Sass::SyntaxError.new(msg, :line => child.line)
70
69
  end
71
70
  @children << child
72
71
  end
@@ -110,30 +109,28 @@ module Sass
110
109
  # @return [Boolean]
111
110
  def invisible?; false; end
112
111
 
112
+ # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
113
+ #
114
+ # @return [Symbol]
115
+ def style
116
+ @options[:style]
117
+ end
118
+
113
119
  # Computes the CSS corresponding to this Sass tree.
114
120
  #
115
121
  # Only static-node subclasses need to implement \{#to\_s}.
116
122
  #
117
123
  # This may return `nil`, but it will only do so if \{#invisible?} is true.
118
124
  #
125
+ # @param args [Array] Passed on to \{#\_to\_s}
119
126
  # @return [String, nil] The resulting CSS
120
127
  # @raise [Sass::SyntaxError] if some element of the tree is invalid
121
128
  # @see Sass::Tree
122
- def to_s
123
- result = String.new
124
- children.each do |child|
125
- if child.is_a? PropNode
126
- raise Sass::SyntaxError.new('Properties aren\'t allowed at the root of a document.', child.line)
127
- else
128
- next if child.invisible?
129
- child_str = child.to_s(1)
130
- result << child_str + (style == :compressed ? '' : "\n")
131
- end
132
- end
133
- result.rstrip!
134
- return "" if result.empty?
135
- return result + "\n"
136
- rescue Sass::SyntaxError => e; e.add_metadata(filename, line)
129
+ def to_s(*args)
130
+ _to_s(*args)
131
+ rescue Sass::SyntaxError => e
132
+ e.modify_backtrace(:filename => filename, :line => line)
133
+ raise e
137
134
  end
138
135
 
139
136
  # Runs the dynamic Sass code:
@@ -154,18 +151,24 @@ module Sass
154
151
  def perform(environment)
155
152
  environment.options = @options if self.class == Tree::Node
156
153
  _perform(environment)
157
- rescue Sass::SyntaxError => e; e.add_metadata(filename, line)
154
+ rescue Sass::SyntaxError => e
155
+ e.modify_backtrace(:filename => filename, :line => line)
156
+ raise e
158
157
  end
159
158
 
160
- # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
159
+ protected
160
+
161
+ # Computes the CSS corresponding to this particular Sass node.
161
162
  #
162
- # @return [Symbol]
163
- def style
164
- @options[:style]
163
+ # @param args [Array] ignored
164
+ # @return [String, nil] The resulting CSS
165
+ # @raise [Sass::SyntaxError] if some element of the tree is invalid
166
+ # @see #to_s
167
+ # @see Sass::Tree
168
+ def _to_s
169
+ raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #_to_s or #to_s.")
165
170
  end
166
171
 
167
- protected
168
-
169
172
  # Runs any dynamic Sass code in this particular node.
170
173
  # This doesn't modify this node or any of its children.
171
174
  #
@@ -228,7 +231,7 @@ module Sass
228
231
  def balance(*args)
229
232
  res = Haml::Shared.balance(*args)
230
233
  return res if res
231
- raise Sass::SyntaxError.new("Unbalanced brackets.", line)
234
+ raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
232
235
  end
233
236
 
234
237
  # Returns an error message if the given child node is invalid,
@@ -34,29 +34,28 @@ module Sass::Tree
34
34
  self.class == other.class && name == other.name && value == other.value && super
35
35
  end
36
36
 
37
+ protected
38
+
37
39
  # Computes the CSS for the property.
38
40
  #
39
41
  # @param tabs [Fixnum] The level of indentation for the CSS
40
42
  # @param parent_name [String] The name of the parent property (e.g. `text`) or nil
41
43
  # @return [String] The resulting CSS
42
44
  # @raise [Sass::SyntaxError] if the property uses invalid syntax
43
- def to_s(tabs, parent_name = nil)
45
+ def _to_s(tabs, parent_name = nil)
44
46
  if @options[:property_syntax] == :old && @prop_syntax == :new
45
47
  raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
46
48
  elsif @options[:property_syntax] == :new && @prop_syntax == :old
47
49
  raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
50
+ elsif value[-1] == ?;
51
+ raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).")
52
+ elsif value.empty? && children.empty?
53
+ raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value).")
48
54
  end
49
55
 
50
- if value[-1] == ?;
51
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).", @line)
52
- end
53
56
  real_name = name
54
57
  real_name = "#{parent_name}-#{real_name}" if parent_name
55
-
56
- if value.empty? && children.empty?
57
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value).", @line)
58
- end
59
-
58
+
60
59
  join_string = case style
61
60
  when :compact; ' '
62
61
  when :compressed; ''
@@ -76,8 +75,6 @@ module Sass::Tree
76
75
  (style == :compressed && parent_name) ? to_return : to_return[0...-1]
77
76
  end
78
77
 
79
- protected
80
-
81
78
  # Runs any SassScript that may be embedded in the property.
82
79
  #
83
80
  # @param environment [Sass::Environment] The lexical environment containing
@@ -0,0 +1,56 @@
1
+ module Sass
2
+ module Tree
3
+ # A static node that is the root node of the Sass document.
4
+ class RootNode < Node
5
+ # The Sass template from which this node was created
6
+ #
7
+ # @param template [String]
8
+ attr_reader :template
9
+
10
+ # @param template [String] The Sass template from which this node was created
11
+ def initialize(template)
12
+ super()
13
+ @template = template
14
+ end
15
+
16
+ # @see \{Node#to\_s}
17
+ def to_s(*args)
18
+ super
19
+ rescue Sass::SyntaxError => e
20
+ e.sass_template = @template
21
+ raise e
22
+ end
23
+
24
+ # @see \{Node#perform}
25
+ def perform(*args)
26
+ super
27
+ rescue Sass::SyntaxError => e
28
+ e.sass_template = @template
29
+ raise e
30
+ end
31
+
32
+ protected
33
+
34
+ # Computes the CSS corresponding to this Sass tree.
35
+ #
36
+ # @param args [Array] ignored
37
+ # @return [String] The resulting CSS
38
+ # @raise [Sass::SyntaxError] if some element of the tree is invalid
39
+ # @see Sass::Tree
40
+ def _to_s(*args)
41
+ result = String.new
42
+ children.each do |child|
43
+ raise Sass::SyntaxError.new('Properties aren\'t allowed at the root of a document.',
44
+ :line => child.line) if child.is_a? PropNode
45
+
46
+ next if child.invisible?
47
+ child_str = child.to_s(1)
48
+ result << child_str + (style == :compressed ? '' : "\n")
49
+ end
50
+ result.rstrip!
51
+ return "" if result.empty?
52
+ return result + "\n"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -69,6 +69,8 @@ module Sass::Tree
69
69
  @rules.last[-1] == ?,
70
70
  end
71
71
 
72
+ protected
73
+
72
74
  # Computes the CSS for the rule.
73
75
  #
74
76
  # @param tabs [Fixnum] The level of indentation for the CSS
@@ -76,7 +78,7 @@ module Sass::Tree
76
78
  # (see \{#rules}), or `nil` if there are no parents
77
79
  # @return [String] The resulting CSS
78
80
  # @raise [Sass::SyntaxError] if the rule has no parents but uses `&`
79
- def to_s(tabs, super_rules = nil)
81
+ def _to_s(tabs, super_rules = nil)
80
82
  resolved_rules = resolve_parent_refs(super_rules)
81
83
 
82
84
  properties = []
@@ -144,8 +146,6 @@ module Sass::Tree
144
146
  to_return
145
147
  end
146
148
 
147
- protected
148
-
149
149
  # Runs any SassScript that may be embedded in the rule,
150
150
  # and parses the selectors for commas.
151
151
  #
@@ -163,7 +163,7 @@ module Sass::Tree
163
163
  return @parsed_rules.map do |line|
164
164
  line.map do |rule|
165
165
  if rule.include?(:parent)
166
- raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.", self.line)
166
+ raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.")
167
167
  end
168
168
 
169
169
  rule.join
@@ -180,18 +180,78 @@ SASS
180
180
  end
181
181
 
182
182
  def test_imported_exception
183
- [nil, 2].each do |i|
183
+ [1, 2, 3].each do |i|
184
184
  begin
185
185
  Sass::Engine.new("@import bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
186
186
  rescue Sass::SyntaxError => err
187
187
  assert_equal(2, err.sass_line)
188
- assert_match(/bork#{i}\.sass$/, err.sass_filename)
188
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
189
+
190
+ assert_equal(err.sass_filename, err.sass_backtrace.first[:filename])
191
+ assert_equal(err.sass_line, err.sass_backtrace.first[:line])
192
+
193
+ assert_nil(err.sass_backtrace[1][:filename])
194
+ assert_equal(1, err.sass_backtrace[1][:line])
195
+
196
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
197
+ assert_equal("(sass):1", err.backtrace[1])
198
+ else
199
+ assert(false, "Exception not raised for imported template: bork#{i}")
200
+ end
201
+ end
202
+ end
203
+
204
+ def test_double_imported_exception
205
+ [1, 2, 3].each do |i|
206
+ begin
207
+ Sass::Engine.new("@import nested_bork#{i}", :load_paths => [File.dirname(__FILE__) + '/templates/']).render
208
+ rescue Sass::SyntaxError => err
209
+ assert_equal(2, err.sass_line)
210
+ assert_match(/(\/|^)bork#{i}\.sass$/, err.sass_filename)
211
+
212
+ assert_equal(err.sass_filename, err.sass_backtrace.first[:filename])
213
+ assert_equal(err.sass_line, err.sass_backtrace.first[:line])
214
+
215
+ assert_match(/(\/|^)nested_bork#{i}\.sass$/, err.sass_backtrace[1][:filename])
216
+ assert_equal(2, err.sass_backtrace[1][:line])
217
+
218
+ assert_nil(err.sass_backtrace[2][:filename])
219
+ assert_equal(1, err.sass_backtrace[2][:line])
220
+
221
+ assert_match(/(\/|^)bork#{i}\.sass:2$/, err.backtrace.first)
222
+ assert_match(/(\/|^)nested_bork#{i}\.sass:2$/, err.backtrace[1])
223
+ assert_equal("(sass):1", err.backtrace[2])
189
224
  else
190
225
  assert(false, "Exception not raised for imported template: bork#{i}")
191
226
  end
192
227
  end
193
228
  end
194
229
 
230
+ def test_exception_css_with_offset
231
+ opts = {:full_exception => true, :line => 362}
232
+ render(("a\n b: c\n" * 10) + "d\n e:\n" + ("f\n g: h\n" * 10), opts)
233
+ rescue Sass::SyntaxError => e
234
+ assert_equal(<<CSS, Sass::SyntaxError.exception_to_css(e, opts).split("\n")[0..15].join("\n"))
235
+ /*
236
+ Syntax error: Invalid property: "e: " (no value).
237
+ on line 383 of test_exception_css_with_offset_inline.sass
238
+
239
+ 378: a
240
+ 379: b: c
241
+ 380: a
242
+ 381: b: c
243
+ 382: d
244
+ 383: e:
245
+ 384: f
246
+ 385: g: h
247
+ 386: f
248
+ 387: g: h
249
+ 388: f
250
+ CSS
251
+ else
252
+ assert(false, "Exception not raised for test_exception_css_with_offset")
253
+ end
254
+
195
255
  def test_css_import
196
256
  assert_equal("@import url(./fonts.css) screen;\n", render("@import url(./fonts.css) screen"))
197
257
  assert_equal("@import \"./fonts.css\" screen;\n", render("@import \"./fonts.css\" screen"))
@@ -255,12 +315,14 @@ SASS
255
315
  rescue Sass::SyntaxError => e
256
316
  assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.",
257
317
  e.message)
318
+ assert_equal(2, e.sass_line)
258
319
  else
259
320
  assert(false, "SyntaxError not raised for :property_syntax => :old")
260
321
  end
261
322
 
262
323
  begin
263
324
  render("a\n :b c", :property_syntax => :new)
325
+ assert_equal(2, e.sass_line)
264
326
  rescue Sass::SyntaxError => e
265
327
  assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.",
266
328
  e.message)
@@ -52,21 +52,28 @@ class SassPluginTest < Test::Unit::TestCase
52
52
  end
53
53
 
54
54
  def test_full_exception_handling
55
- File.delete(tempfile_loc('bork'))
55
+ File.delete(tempfile_loc('bork1'))
56
56
  Sass::Plugin.update_stylesheets
57
- File.open(tempfile_loc('bork')) do |file|
58
- assert_equal("/*\nSass::SyntaxError: Undefined variable: \"!bork\".\non line 2 of #{template_loc('bork')}\n\n1: bork\n2: :bork= !bork", file.read.split("\n")[0...6].join("\n"))
57
+ File.open(tempfile_loc('bork1')) do |file|
58
+ assert_equal(<<CSS.strip, file.read.split("\n")[0...6].join("\n"))
59
+ /*
60
+ Syntax error: Undefined variable: "!bork".
61
+ on line 2 of #{template_loc('bork1')}
62
+
63
+ 1: bork
64
+ 2: :bork= !bork
65
+ CSS
59
66
  end
60
- File.delete(tempfile_loc('bork'))
67
+ File.delete(tempfile_loc('bork1'))
61
68
  end
62
69
 
63
70
  def test_nonfull_exception_handling
64
71
  Sass::Plugin.options[:full_exception] = false
65
72
 
66
- File.delete(tempfile_loc('bork'))
73
+ File.delete(tempfile_loc('bork1'))
67
74
  Sass::Plugin.update_stylesheets
68
- assert_equal("/* Internal stylesheet error */", File.read(tempfile_loc('bork')))
69
- File.delete(tempfile_loc('bork'))
75
+ assert_equal("/* Internal stylesheet error */", File.read(tempfile_loc('bork1')))
76
+ File.delete(tempfile_loc('bork1'))
70
77
 
71
78
  Sass::Plugin.options[:full_exception] = true
72
79
  end
File without changes
@@ -0,0 +1,2 @@
1
+ bork
2
+ bork:
@@ -0,0 +1,2 @@
1
+
2
+ @import bork1
@@ -0,0 +1,2 @@
1
+
2
+ @import bork2
@@ -0,0 +1,2 @@
1
+
2
+ @import bork3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml-edge
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.27
4
+ version: 2.3.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-08-28 00:00:00 -04:00
13
+ date: 2009-09-14 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -110,6 +110,7 @@ files:
110
110
  - lib/sass/tree/node.rb
111
111
  - lib/sass/tree/prop_node.rb
112
112
  - lib/sass/tree/rule_node.rb
113
+ - lib/sass/tree/root_node.rb
113
114
  - lib/sass/tree/variable_node.rb
114
115
  - lib/sass/tree/while_node.rb
115
116
  - bin/css2sass
@@ -221,8 +222,9 @@ files:
221
222
  - test/sass/templates/_partial.sass
222
223
  - test/sass/templates/alt.sass
223
224
  - test/sass/templates/basic.sass
224
- - test/sass/templates/bork.sass
225
+ - test/sass/templates/bork1.sass
225
226
  - test/sass/templates/bork2.sass
227
+ - test/sass/templates/bork3.sass
226
228
  - test/sass/templates/compact.sass
227
229
  - test/sass/templates/complex.sass
228
230
  - test/sass/templates/compressed.sass
@@ -233,6 +235,9 @@ files:
233
235
  - test/sass/templates/mixins.sass
234
236
  - test/sass/templates/multiline.sass
235
237
  - test/sass/templates/nested.sass
238
+ - test/sass/templates/nested_bork1.sass
239
+ - test/sass/templates/nested_bork2.sass
240
+ - test/sass/templates/nested_bork3.sass
236
241
  - test/sass/templates/parent_ref.sass
237
242
  - test/sass/templates/script.sass
238
243
  - test/sass/templates/subdir