summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2014-12-10 14:06:42 -0800
committerEevee (Alex Munroe) <eevee.git@veekun.com>2014-12-10 14:06:42 -0800
commit77a58288ded8506cf8ca161fa2df5d8bd9876ece (patch)
tree3c600bddfbe6f5fc4def7bb9d5602db876b09ded
parentbe4f062b8caafb88299b4047b5944d9f73dc6a3b (diff)
downloadpyscss-77a58288ded8506cf8ca161fa2df5d8bd9876ece.tar.gz
Make line numbers in errors moderately less wrong.
-rw-r--r--scss/compiler.py3
-rw-r--r--scss/cssdefs.py1
-rw-r--r--scss/errors.py9
-rw-r--r--scss/rule.py24
-rw-r--r--scss/source.py23
5 files changed, 36 insertions, 24 deletions
diff --git a/scss/compiler.py b/scss/compiler.py
index 551f407..9bc912d 100644
--- a/scss/compiler.py
+++ b/scss/compiler.py
@@ -340,6 +340,7 @@ class Compilation(object):
for source_file in self.sources:
rule = SassRule(
source_file=source_file,
+ lineno=1,
unparsed_contents=source_file.contents,
namespace=root_namespace,
@@ -1163,6 +1164,7 @@ class Compilation(object):
source_file=rule.source_file,
import_key=rule.import_key,
lineno=block.lineno,
+ num_header_lines=block.header.num_lines,
unparsed_contents=block.unparsed_contents,
legacy_compiler_options=rule.legacy_compiler_options,
@@ -1204,6 +1206,7 @@ class Compilation(object):
source_file=rule.source_file,
import_key=rule.import_key,
lineno=block.lineno,
+ num_header_lines=block.header.num_lines,
unparsed_contents=block.unparsed_contents,
legacy_compiler_options=rule.legacy_compiler_options,
diff --git a/scss/cssdefs.py b/scss/cssdefs.py
index 34dfe91..d94206b 100644
--- a/scss/cssdefs.py
+++ b/scss/cssdefs.py
@@ -474,7 +474,6 @@ _sl_comment_re = re.compile(r'(?<!\burl[(])(?<!\w{2}:)\/\/.*')
_escape_chars_re = re.compile(r'([^-a-zA-Z0-9_])')
_interpolate_re = re.compile(r'(#\{\s*)?(\$[-\w]+)(?(1)\s*\})')
_spaces_re = re.compile(r'\s+')
-_expand_rules_space_re = re.compile(r'\s*{')
_collapse_properties_space_re = re.compile(r'([:#])\s*{')
_variable_re = re.compile('^\\$[-a-zA-Z0-9_]+$')
diff --git a/scss/errors.py b/scss/errors.py
index b06793c..6d3960e 100644
--- a/scss/errors.py
+++ b/scss/errors.py
@@ -86,9 +86,12 @@ class SassBaseError(Exception):
last_file = self.rule_stack[0].source_file
# TODO this could go away if rules knew their import chains...
- # TODO mixins and the like here too?
- # TODO the line number is wrong here, since this doesn't include the
- # *block* being parsed!
+ # TODO this doesn't mention mixins or function calls. really need to
+ # track the call stack better. atm we skip other calls in the same
+ # file because most of them are just nesting, but they might not be!
+ # TODO the line number is wrong here for @imports, because we don't
+ # have access to the UnparsedBlock representing the import!
+ # TODO @content is completely broken; it's basically textual inclusion
for rule in self.rule_stack[1:]:
if rule.source_file is not last_file:
ret.extend((
diff --git a/scss/rule.py b/scss/rule.py
index e39e121..712f4ba 100644
--- a/scss/rule.py
+++ b/scss/rule.py
@@ -34,6 +34,7 @@ class SassRule(object):
def __init__(
self, source_file, import_key=None, unparsed_contents=None,
+ num_header_lines=0,
options=None, legacy_compiler_options=None, properties=None,
namespace=None,
lineno=0, extends_selectors=frozenset(),
@@ -48,6 +49,7 @@ class SassRule(object):
self.import_key = import_key
self.lineno = lineno
+ self.num_header_lines = num_header_lines
self.unparsed_contents = unparsed_contents
self.legacy_compiler_options = legacy_compiler_options or {}
self.options = options or {}
@@ -211,6 +213,7 @@ class BlockHeader(object):
@classmethod
def parse(cls, prop, has_contents=False):
+ num_lines = prop.count('\n')
prop = prop.strip()
# Simple pre-processing
@@ -247,25 +250,27 @@ class BlockHeader(object):
directive, argument = prop, None
directive = directive.lower()
- return BlockAtRuleHeader(directive, argument)
+ return BlockAtRuleHeader(directive, argument, num_lines)
elif prop.split(None, 1)[0].endswith(':'):
# Syntax is "<scope>: [prop]" -- if the optional prop exists, it
# becomes the first rule with no suffix
scope, unscoped_value = prop.split(':', 1)
scope = scope.rstrip()
unscoped_value = unscoped_value.lstrip()
- return BlockScopeHeader(scope, unscoped_value)
+ return BlockScopeHeader(scope, unscoped_value, num_lines)
else:
- return BlockSelectorHeader(prop)
+ return BlockSelectorHeader(prop, num_lines)
class BlockAtRuleHeader(BlockHeader):
is_atrule = True
- def __init__(self, directive, argument):
+ def __init__(self, directive, argument, num_lines=0):
self.directive = directive
self.argument = argument
+ self.num_lines = num_lines
+
def __repr__(self):
return "<%s %r %r>" % (type(self).__name__, self.directive, self.argument)
@@ -279,9 +284,11 @@ class BlockAtRuleHeader(BlockHeader):
class BlockSelectorHeader(BlockHeader):
is_selector = True
- def __init__(self, selectors):
+ def __init__(self, selectors, num_lines=0):
self.selectors = tuple(selectors)
+ self.num_lines = num_lines
+
def __repr__(self):
return "<%s %r>" % (type(self).__name__, self.selectors)
@@ -295,7 +302,7 @@ class BlockSelectorHeader(BlockHeader):
class BlockScopeHeader(BlockHeader):
is_scope = True
- def __init__(self, scope, unscoped_value):
+ def __init__(self, scope, unscoped_value, num_lines=0):
self.scope = scope
if unscoped_value:
@@ -303,6 +310,8 @@ class BlockScopeHeader(BlockHeader):
else:
self.unscoped_value = None
+ self.num_lines = num_lines
+
class UnparsedBlock(object):
"""A Sass block whose contents have not yet been parsed.
@@ -329,7 +338,8 @@ class UnparsedBlock(object):
self.header = BlockHeader.parse(prop, has_contents=bool(unparsed_contents))
# Basic properties
- self.lineno = lineno
+ self.lineno = (
+ parent_rule.lineno - parent_rule.num_header_lines + lineno - 1)
self.prop = prop
self.unparsed_contents = unparsed_contents
diff --git a/scss/source.py b/scss/source.py
index 7f99c1f..462278d 100644
--- a/scss/source.py
+++ b/scss/source.py
@@ -12,7 +12,7 @@ import six
from scss.cssdefs import (
_ml_comment_re, _sl_comment_re,
- _expand_rules_space_re, _collapse_properties_space_re,
+ _collapse_properties_space_re,
_strings_re,
)
from scss.cssdefs import determine_encoding
@@ -261,7 +261,7 @@ class SourceFile(object):
if line is None:
line = ''
- line = state['line_buffer'] + line.rstrip() # remove EOL character
+ line = state['line_buffer'] + line
if line and line[-1] == '\\':
state['line_buffer'] = line[:-1]
@@ -274,10 +274,8 @@ class SourceFile(object):
state['prev_line'] = line
- if output:
- output += '\n'
- ret += output
-
+ ret += output
+ ret += '\n'
return ret
def parse_sass_line(self, line, state):
@@ -286,7 +284,7 @@ class SourceFile(object):
if line is None:
line = ''
- line = state['line_buffer'] + line.rstrip() # remove EOL character
+ line = state['line_buffer'] + line
if line and line[-1] == '\\':
state['line_buffer'] = line[:-1]
@@ -327,9 +325,8 @@ class SourceFile(object):
state['prev_indent'] = indent
state['prev_line'] = line
- if output:
- output += '\n'
- ret += output
+ ret += output
+ ret += '\n'
return ret
def prepare_source(self, codestr, sass=False):
@@ -351,6 +348,9 @@ class SourceFile(object):
# parse the last line stored in prev_line buffer
codestr += parse_line(None, state)
+ # pop off the extra \n parse_line puts at the beginning
+ codestr = codestr[1:]
+
# protects codestr: "..." strings
codestr = _strings_re.sub(
lambda m: _reverse_safe_strings_re.sub(
@@ -366,9 +366,6 @@ class SourceFile(object):
codestr = _safe_strings_re.sub(
lambda m: _safe_strings[m.group(0)], codestr)
- # expand the space in rules
- codestr = _expand_rules_space_re.sub(' {', codestr)
-
# collapse the space in properties blocks
codestr = _collapse_properties_space_re.sub(r'\1{', codestr)