haml-edge 2.3.27 → 2.3.28

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/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