##// END OF EJS Templates
Factor out handling of line continuations
Thomas Kluyver -
Show More
@@ -93,12 +93,33 b' def _find_assign_op(token_line):'
93 elif s in ')]}':
93 elif s in ')]}':
94 paren_level -= 1
94 paren_level -= 1
95
95
96 def find_end_of_continued_line(lines, start_line: int):
97 """Find the last line of a line explicitly extended using backslashes.
98
99 Uses 0-indexed line numbers.
100 """
101 end_line = start_line
102 while lines[end_line].endswith('\\\n'):
103 end_line += 1
104 if end_line >= len(lines):
105 break
106 return end_line
107
108 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
109 """Assemble pieces of a continued line into a single line.
110
111 Uses 0-indexed line numbers. *start* is (lineno, colno).
112 """
113 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
114 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
115 + [parts[-1][:-1]]) # Strip newline from last line
116
96 class MagicAssign:
117 class MagicAssign:
97 @staticmethod
118 @staticmethod
98 def find(tokens_by_line):
119 def find(tokens_by_line):
99 """Find the first magic assignment (a = %foo) in the cell.
120 """Find the first magic assignment (a = %foo) in the cell.
100
121
101 Returns (line, column) of the % if found, or None.
122 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
102 """
123 """
103 for line in tokens_by_line:
124 for line in tokens_by_line:
104 assign_ix = _find_assign_op(line)
125 assign_ix = _find_assign_op(line)
@@ -114,21 +135,12 b' class MagicAssign:'
114 """
135 """
115 start_line = start[0] - 1 # Shift from 1-index to 0-index
136 start_line = start[0] - 1 # Shift from 1-index to 0-index
116 start_col = start[1]
137 start_col = start[1]
117
138
118 print("Start at", start_line, start_col)
139 lhs = lines[start_line][:start_col]
119 print("Line", lines[start_line])
140 end_line = find_end_of_continued_line(lines, start_line)
120
141 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
121 lhs, rhs = lines[start_line][:start_col], lines[start_line][start_col:-1]
122 assert rhs.startswith('%'), rhs
142 assert rhs.startswith('%'), rhs
123 magic_name, _, args = rhs[1:].partition(' ')
143 magic_name, _, args = rhs[1:].partition(' ')
124 args_parts = [args]
125 end_line = start_line
126 # Follow explicit (backslash) line continuations
127 while end_line < len(lines) and args_parts[-1].endswith('\\'):
128 end_line += 1
129 args_parts[-1] = args_parts[-1][:-1] # Trim backslash
130 args_parts.append(lines[end_line][:-1]) # Trim newline
131 args = ' '.join(args_parts)
132
144
133 lines_before = lines[:start_line]
145 lines_before = lines[:start_line]
134 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
146 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
@@ -143,7 +155,7 b' class SystemAssign:'
143 def find(tokens_by_line):
155 def find(tokens_by_line):
144 """Find the first system assignment (a = !foo) in the cell.
156 """Find the first system assignment (a = !foo) in the cell.
145
157
146 Returns (line, column) of the ! if found, or None.
158 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
147 """
159 """
148 for line in tokens_by_line:
160 for line in tokens_by_line:
149 assign_ix = _find_assign_op(line)
161 assign_ix = _find_assign_op(line)
@@ -166,20 +178,11 b' class SystemAssign:'
166 start_line = start[0] - 1 # Shift from 1-index to 0-index
178 start_line = start[0] - 1 # Shift from 1-index to 0-index
167 start_col = start[1]
179 start_col = start[1]
168
180
169 print("Start at", start_line, start_col)
181 lhs = lines[start_line][:start_col]
170 print("Line", lines[start_line])
182 end_line = find_end_of_continued_line(lines, start_line)
171
183 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
172 lhs, rhs = lines[start_line][:start_col], lines[start_line][
173 start_col:-1]
174 assert rhs.startswith('!'), rhs
184 assert rhs.startswith('!'), rhs
175 cmd_parts = [rhs[1:]]
185 cmd = rhs[1:]
176 end_line = start_line
177 # Follow explicit (backslash) line continuations
178 while end_line < len(lines) and cmd_parts[-1].endswith('\\'):
179 end_line += 1
180 cmd_parts[-1] = cmd_parts[-1][:-1] # Trim backslash
181 cmd_parts.append(lines[end_line][:-1]) # Trim newline
182 cmd = ' '.join(cmd_parts)
183
186
184 lines_before = lines[:start_line]
187 lines_before = lines[:start_line]
185 call = "get_ipython().getoutput({!r})".format(cmd)
188 call = "get_ipython().getoutput({!r})".format(cmd)
@@ -25,6 +25,12 b" b = get_ipython().getoutput('foo bar')"
25 g()
25 g()
26 """.splitlines(keepends=True))
26 """.splitlines(keepends=True))
27
27
28 def test_continued_line():
29 lines = MULTILINE_MAGIC_ASSIGN[0]
30 nt.assert_equal(ipt2.find_end_of_continued_line(lines, 1), 2)
31
32 nt.assert_equal(ipt2.assemble_continued_line(lines, (1, 5), 2), "foo bar")
33
28 def test_find_assign_magic():
34 def test_find_assign_magic():
29 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0])
35 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0])
30 nt.assert_equal(ipt2.MagicAssign.find(tbl), (2, 4))
36 nt.assert_equal(ipt2.MagicAssign.find(tbl), (2, 4))
General Comments 0
You need to be logged in to leave comments. Login now