Ticket #2594: template_whitespace-2006-08-22.diff
File template_whitespace-2006-08-22.diff, 5.3 KB (added by , 18 years ago) |
---|
-
django/template/__init__.py
66 66 TOKEN_TEXT = 0 67 67 TOKEN_VAR = 1 68 68 TOKEN_BLOCK = 2 69 TOKEN_ENDLINE = 3 69 70 70 71 # template syntax constants 71 72 FILTER_SEPARATOR = '|' … … 85 86 UNKNOWN_SOURCE="<unknown source>" 86 87 87 88 # match a variable or block tag and capture the entire tag, including start/end delimiters 88 tag_re = re.compile('(%s.*?%s|%s.*?%s )' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),89 tag_re = re.compile('(%s.*?%s|%s.*?%s|\n)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 89 90 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) 91 whitespace_only_re = re.compile('^\s+$') 90 92 91 93 # global dictionary of libraries that have been loaded using get_library 92 94 libraries = {} … … 157 159 def compile_string(template_string, origin): 158 160 "Compiles template_string into NodeList ready for rendering" 159 161 lexer = lexer_factory(template_string, origin) 160 parser = parser_factory(lexer.tokenize()) 162 tokens = lexer.tokenize() 163 tokens = trim_whitespace(tokens) 164 parser = parser_factory(tokens) 161 165 return parser.parse() 162 166 167 def trim_whitespace(tokens): 168 num_tokens = len(tokens) 169 keep_tokens = [] 170 line_start = 0 171 for pos, token in enumerate(tokens): 172 is_endline = token.token_type is TOKEN_ENDLINE 173 is_last_token = pos is (num_tokens-1) 174 # If we have reached the end of a line or the end of the file then 175 # process the line. 176 if is_endline or is_last_token: 177 # Add one to the slice to include the endline token we are currently at. 178 keep_tokens += get_line_tokens(tokens[line_start:pos+1]) 179 # The next line starts at the next token. 180 line_start = pos + 1 181 continue 182 return keep_tokens 183 184 def get_line_tokens(tokens): 185 line_has_block = False 186 # We don't need to look at the last (TOKEN_ENDLINE) token. 187 for token in tokens[:-1]: 188 if token.token_type is TOKEN_TEXT: 189 # Keep the line if we have non-whitespace text. 190 if not whitespace_only_re.match(token.contents): 191 return tokens 192 elif token.token_type is TOKEN_BLOCK: 193 line_has_block = True 194 # If we are at this point then the line only contains whitespace, blocks, 195 # and variables. Continuing on... 196 # If the line doesn't have blocks then it must only have variables and 197 # whitespace, return the whole line. This also catches the case where 198 # the line consists of only a TOKEN_ENDLINE. 199 if not line_has_block: 200 return tokens 201 # At this point we have only blocks and/or text and know we don't want the endline. 202 del tokens[-1] 203 # We also don't wan't beginning and trailing whitespace on the line. 204 start = first_non_whitespace_token(tokens) 205 tokens.reverse() 206 from_end = first_non_whitespace_token(tokens) 207 tokens.reverse() 208 return tokens[start:len(tokens)-from_end] 209 210 def first_non_whitespace_token(tokens): 211 pos = 0 212 for token in tokens: 213 if token.token_type is TOKEN_TEXT and whitespace_only_re.match(token.contents): 214 pos += 1 215 else: 216 break 217 return pos 218 163 219 class Token(object): 164 220 def __init__(self, token_type, contents): 165 "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK "221 "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK, or TOKEN_ENDLINE." 166 222 self.token_type, self.contents = token_type, contents 167 223 168 224 def __str__(self): 169 225 return '<%s token: "%s...">' % \ 170 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block' }[self.token_type],226 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_ENDLINE: 'Endline'}[self.token_type], 171 227 self.contents[:20].replace('\n', '')) 172 228 173 229 def split_contents(self): … … 190 246 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 191 247 elif token_string.startswith(BLOCK_TAG_START): 192 248 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 249 elif token_string == '\n': 250 token = Token(TOKEN_ENDLINE, token_string) 193 251 else: 194 252 token = Token(TOKEN_TEXT, token_string) 195 253 return token … … 233 291 token = self.next_token() 234 292 if token.token_type == TOKEN_TEXT: 235 293 self.extend_nodelist(nodelist, TextNode(token.contents), token) 294 elif token.token_type == TOKEN_ENDLINE: 295 self.extend_nodelist(nodelist, EndlineNode(token.contents), token) 236 296 elif token.token_type == TOKEN_VAR: 237 297 if not token.contents: 238 298 self.empty_variable(token) … … 726 786 def render(self, context): 727 787 return self.s 728 788 789 class EndlineNode(Node): 790 def __init__(self, s): 791 self.s = s 792 793 def __repr__(self): 794 return "<Endline Node: %r>" % self.s 795 796 def render(self, context): 797 return self.s 798 729 799 class VariableNode(Node): 730 800 def __init__(self, filter_expression): 731 801 self.filter_expression = filter_expression