Merge pull request #146 from nornagon/escape-spaces

Escape spaces
This commit is contained in:
Evan Martin
2011-12-20 10:33:42 -08:00
5 changed files with 80 additions and 14 deletions

View File

@@ -8,6 +8,10 @@ use Python.
"""
import textwrap
import re
def escape_spaces(word):
return word.replace('$ ','$$ ').replace(' ','$ ')
class Writer(object):
def __init__(self, output, width=78):
@@ -43,15 +47,19 @@ class Writer(object):
variables=None):
outputs = self._as_list(outputs)
all_inputs = self._as_list(inputs)[:]
out_outputs = map(escape_spaces, outputs)
all_inputs = map(escape_spaces, all_inputs)
if implicit:
implicit = map(escape_spaces, self._as_list(implicit))
all_inputs.append('|')
all_inputs.extend(self._as_list(implicit))
all_inputs.extend(implicit)
if order_only:
order_only = map(escape_spaces, self._as_list(order_only))
all_inputs.append('||')
all_inputs.extend(self._as_list(order_only))
all_inputs.extend(order_only)
self._line('build %s: %s %s' % (' '.join(outputs),
self._line('build %s: %s %s' % (' '.join(out_outputs),
rule,
' '.join(all_inputs)))
@@ -76,22 +84,43 @@ class Writer(object):
while len(text) > self.width:
# The text is too wide; wrap if possible.
# Find the rightmost space that would obey our width constraint.
available_space = self.width - len(leading_space) - len(' $')
space = text.rfind(' ', 0, available_space)
if space < 0:
# No such space; just use the first space we can find.
space = text.find(' ', available_space)
if space < 0:
# Give up on breaking.
break
self.output.write(leading_space)
self.output.write(leading_space + text[0:space] + ' $\n')
text = text[space+1:]
available_space = self.width - len(leading_space) - len(' $')
# Write as much as we can into this line.
done = False
written_stuff = False
while available_space > 0:
space = re.search('((\$\$)+([^$]|^)|[^$]|^) ', text)
if space:
space_idx = space.start() + 1
else:
# No spaces left.
done = True
break
if space_idx > available_space:
# We're out of space.
if written_stuff:
# See if we can fit it on the next line.
break
# If we haven't written anything yet on this line, don't
# try to wrap.
self.output.write(text[0:space_idx] + ' ')
written_stuff = True
text = text[space_idx+1:]
available_space -= space_idx+1
self.output.write('$\n')
# Subsequent lines are continuations, so indent them.
leading_space = ' ' * (indent+2)
if done:
# No more spaces, so bail.
break
self.output.write(leading_space + text + '\n')
def _as_list(self, input):

View File

@@ -20,6 +20,7 @@ from StringIO import StringIO
import ninja_syntax
LONGWORD = 'a' * 10
LONGWORDWITHSPACES = 'a'*5 + '$ ' + 'a'*5
INDENT = ' '
class TestLineWordWrap(unittest.TestCase):
@@ -48,5 +49,22 @@ class TestLineWordWrap(unittest.TestCase):
' ' + INDENT + 'y']) + '\n',
self.out.getvalue())
def test_escaped_spaces(self):
self.n._line(' '.join(['x', LONGWORDWITHSPACES, 'y']))
self.assertEqual(' $\n'.join(['x',
INDENT + LONGWORDWITHSPACES,
INDENT + 'y']) + '\n',
self.out.getvalue())
def test_fit_many_words(self):
self.n = ninja_syntax.Writer(self.out, width=78)
self.n._line('command = cd ../../chrome; python ../tools/grit/grit/format/repack.py ../out/Debug/obj/chrome/chrome_dll.gen/repack/theme_resources_large.pak ../out/Debug/gen/chrome/theme_resources_large.pak', 1)
self.assertEqual('''\
command = cd ../../chrome; python ../tools/grit/grit/format/repack.py $
../out/Debug/obj/chrome/chrome_dll.gen/repack/theme_resources_large.pak $
../out/Debug/gen/chrome/theme_resources_large.pak
''',
self.out.getvalue())
if __name__ == '__main__':
unittest.main()

View File

@@ -58,6 +58,9 @@ bool EvalString::Parse(const string& input, string* err, size_t* err_index) {
} else if (start < input.size() && input[start] == '$') {
parsed_.push_back(make_pair("$", RAW));
end = start + 1;
} else if (start < input.size() && input[start] == ' ') {
parsed_.push_back(make_pair(" ", RAW));
end = start + 1;
} else {
for (end = start; end < input.size(); ++end) {
char c = input[end];

View File

@@ -217,6 +217,9 @@ Token::Type Tokenizer::PeekToken() {
if (IsIdentChar(*cur_)) {
while (cur_ < end_ && IsIdentChar(*cur_)) {
if (*cur_ == '$' && cur_ + 1 < end_ && cur_[1] == ' ') {
++cur_;
}
++cur_;
}
token_.end_ = cur_;

View File

@@ -149,6 +149,19 @@ TEST_F(ParserTest, Dollars) {
EXPECT_EQ("$dollarbar$baz$blah", state.edges_[0]->EvaluateCommand());
}
TEST_F(ParserTest, EscapeSpaces) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule has$ spaces\n"
" command = something\n"
"build foo$ bar: has$ spaces $$one two$$$ three\n"
));
EXPECT_TRUE(state.LookupNode("foo bar"));
EXPECT_EQ(state.edges_[0]->outputs_[0]->file_->path_, "foo bar");
EXPECT_EQ(state.edges_[0]->inputs_[0]->file_->path_, "$one");
EXPECT_EQ(state.edges_[0]->inputs_[1]->file_->path_, "two$ three");
EXPECT_EQ(state.edges_[0]->EvaluateCommand(), "something");
}
TEST_F(ParserTest, CanonicalizeFile) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule cat\n"