Zim

Merge lp:~fmos/zim/bugfixes into lp:~jaap.karssenberg/zim/pyzim

Proposed by Fabian Stanke
Status: Merged
Approved by: Jaap Karssenberg
Approved revision: 275
Merged at revision: 300
Proposed branch: lp:~fmos/zim/bugfixes
Merge into: lp:~jaap.karssenberg/zim/pyzim
Diff against target: 193 lines (+80/-29)
3 files modified
zim/formats/__init__.py (+2/-2)
zim/formats/wiki.py (+29/-14)
zim/gui/pageview.py (+49/-13)
To merge this branch: bzr merge lp:~fmos/zim/bugfixes
Reviewer Review Type Date Requested Status
Jaap Karssenberg Approve
Review via email: mp+32600@code.launchpad.net

Commit message

Improved verbatim handling

Description of the change

This deals exclusively with improved verbatim handling.
It implements all definitions of the blueprint http://www.zim-wiki.org/wiki/doku.php?id=verbatim_syntax
and fixes #297932 , #586296 and partly also #589525

To post a comment you must log in.
Revision history for this message
Jaap Karssenberg (jaap.karssenberg) wrote :

Thanks for submitting this patch. I'll be mostly offline for the next
~10 days, will go over this patch in detail when I'm back.

-- Jaap

Revision history for this message
Jaap Karssenberg (jaap.karssenberg) wrote :

Will make some small style adjustments, but logic looks good to me. Thanks again for looking into this.

review: Approve
Revision history for this message
sneakypete (sneakypete81) wrote :

On 13 August 2010 16:57, Fabian Moser <email address hidden> wrote:
> Fabian Moser has proposed merging lp:~fmos/zim/bugfixes into lp:zim.
> This deals exclusively with improved verbatim handling.
> It implements all definitions of the blueprint http://www.zim-wiki.org/wiki/doku.php?id=verbatim_syntax
> and fixes #297932 , #586296 and partly also #589525

Thanks Fabian, I've been looking forward to this for a while. Zim gets
better every month!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'zim/formats/__init__.py'
2--- zim/formats/__init__.py 2010-07-06 20:36:53 +0000
3+++ zim/formats/__init__.py 2010-08-13 15:57:43 +0000
4@@ -411,14 +411,14 @@
5 self._seen_eol = need_eol
6
7 # Fix prefix newlines
8- if self._tail and self._last.tag in ('h', 'p', 'pre') \
9+ if self._tail and self._last.tag in ('h', 'p') \
10 and not text.startswith('\n'):
11 if text:
12 text = '\n' + text
13 else:
14 text = '\n'
15 self._seen_eol = 1
16- if self._tail and self._last.tag == 'li' \
17+ elif self._tail and self._last.tag == 'li' \
18 and text.startswith('\n'):
19 text = text[1:]
20 if not text.strip('\n'):
21
22=== modified file 'zim/formats/wiki.py'
23--- zim/formats/wiki.py 2010-06-14 21:03:10 +0000
24+++ zim/formats/wiki.py 2010-08-13 15:57:43 +0000
25@@ -39,8 +39,8 @@
26 bullet_types[bullets[bullet]] = bullet
27
28 parser_re = {
29- 'blockstart': re.compile("\A(\t*''')\s*?\n"),
30- 'pre': re.compile("\A(?P<escape>\t*''')\s*?(?P<content>^.*?)^(?P=escape)\s*\Z", re.M | re.S),
31+ 'blockstart': re.compile("^(\t*''')\s*?\n", re.M),
32+ 'pre': re.compile("^(?P<escape>\t*''')\s*?(?P<content>^.*?)^(?P=escape)\s*\n", re.M | re.S),
33 'splithead': re.compile('^(==+[ \t]+\S.*?\n)', re.M),
34 'heading': re.compile("\A((==+)[ \t]+(.*?)([ \t]+==+)?[ \t]*\n?)\Z"),
35 'splitlist': re.compile("((?:^[ \t]*(?:%s)[ \t]+.*\n?)+)" % bullet_re, re.M),
36@@ -107,11 +107,21 @@
37 # Else append empty paragraph to start new para
38 paras.append('')
39 return True
40-
41+
42+ def blocks_closed():
43+ # This function checks if there are unfinished blocks in the last
44+ # paragraph.
45+ if len(paras[-1]) == 0:
46+ return True
47+ # Eliminate closed blocks
48+ nonblock = parser_re['pre'].split(paras[-1])
49+ # Blocks are closed if none is opened at the end
50+ return parser_re['blockstart'].search(nonblock[-1]) == None
51+
52 para_isspace = False
53 for line in input:
54 # Try start new para when switching between text and empty lines or back
55- if line.isspace() != para_isspace or parser_re['blockstart'].match(line):
56+ if line.isspace() != para_isspace and blocks_closed():
57 if para_start():
58 para_isspace = line.isspace() # decide type of new para
59 paras[-1] += line
60@@ -126,19 +136,24 @@
61 # exceptions like it (crosses fingers)
62 para = para.replace(u'\u2028', '\n')
63
64- if not self.backward and parser_re['blockstart'].search(para):
65- self._parse_block(builder, para)
66- elif self.backward and not para.isspace() \
67+ if self.backward and not para.isspace() \
68 and not parser_re['unindented_line'].search(para):
69 self._parse_block(builder, para)
70 else:
71- parts = parser_re['splithead'].split(para)
72- for i, p in enumerate(parts):
73- if i % 2:
74- # odd elements in the list are headings after split
75- self._parse_head(builder, p)
76- elif len(p) > 0:
77- self._parse_para(builder, p)
78+ block_parts = parser_re['pre'].split(para)
79+ for i, b in enumerate(block_parts):
80+ if i % 3 == 0:
81+ # Text paragraph
82+ parts = parser_re['splithead'].split(b)
83+ for j, p in enumerate(parts):
84+ if j % 2:
85+ # odd elements in the list are headings after split
86+ self._parse_head(builder, p)
87+ elif len(p) > 0:
88+ self._parse_para(builder, p)
89+ elif i % 3 == 1:
90+ # Block
91+ self._parse_block(builder, b + '\n' + block_parts[i+1] + b + '\n')
92
93 builder.end('zim-tree')
94 return ParseTree(builder.close())
95
96=== modified file 'zim/gui/pageview.py'
97--- zim/gui/pageview.py 2010-08-06 21:10:48 +0000
98+++ zim/gui/pageview.py 2010-08-13 15:57:43 +0000
99@@ -1079,7 +1079,9 @@
100 # First call parent for the actual insert
101 if string == '\n':
102 # Break tags that are not allowed to span over multiple lines
103- self._editmode_tags = filter(_is_not_style_tag, self._editmode_tags)
104+ self._editmode_tags = filter(
105+ lambda tag: _is_pre_tag(tag) or _is_not_style_tag(tag),
106+ self._editmode_tags)
107 self._editmode_tags = filter(_is_not_link_tag, self._editmode_tags)
108 self.emit('textstyle-changed', None)
109 # TODO make this more robust for multiline inserts
110@@ -2194,7 +2196,8 @@
111 # Tab at start of line indents
112 iter = buffer.get_insert_iter()
113 home, ourhome = self.get_visual_home_positions(iter)
114- if home.starts_line() and iter.compare(ourhome) < 1:
115+ if home.starts_line() and iter.compare(ourhome) < 1 \
116+ and not filter(_is_pre_tag, iter.get_tags()):
117 row, list = TextBufferList.new_from_iter(buffer, iter)
118 if list and self.preferences['recursive_indentlist']:
119 list.indent(row)
120@@ -2208,7 +2211,8 @@
121 # Backspace or Ctrl-Tab unindents line
122 iter = buffer.get_iter_at_mark(buffer.get_insert())
123 home, ourhome = self.get_visual_home_positions(iter)
124- if home.starts_line() and iter.compare(ourhome) < 1:
125+ if home.starts_line() and iter.compare(ourhome) < 1 \
126+ and not filter(_is_pre_tag, iter.get_tags()):
127 row, list = TextBufferList.new_from_iter(buffer, iter)
128 if list and self.preferences['recursive_indentlist']:
129 done = list.unindent(row)
130@@ -2304,21 +2308,53 @@
131 # > Quotes whole selection with '>'
132 handled = True
133 buffer = self.get_buffer()
134-
135+
136+ def delete_char(iter):
137+ # Deletes the character at the iterator position
138+ next = iter.copy()
139+ if next.forward_char():
140+ buffer.delete(iter, next)
141+
142 def decrement_indent():
143- # For selection decrement first check if all lines have indent
144- level = []
145- buffer.strip_selection()
146- buffer.foreach_line_in_selection(
147- lambda i: level.append(buffer.get_indent(i)) )
148- if level and min(level) > 0:
149- return buffer.foreach_line_in_selection(buffer.decrement_indent)
150+ # Check if inside verbatim block AND entire selection without tag toggle
151+ iter = buffer.get_insert_iter()
152+ if filter(_is_pre_tag, iter.get_tags()) \
153+ and not find_tag_toggle():
154+ missing_tabs = []
155+ check_tab = lambda iter: (iter.get_char() == '\t') or missing_tabs.append(1)
156+ buffer.foreach_line_in_selection(check_tab)
157+ if len(missing_tabs) == 0:
158+ return buffer.foreach_line_in_selection(delete_char)
159+ else:
160+ return False
161 else:
162- return False
163+ # For selection decrement first check if all lines have indent
164+ level = []
165+ buffer.strip_selection()
166+ buffer.foreach_line_in_selection(
167+ lambda i: level.append(buffer.get_indent(i)) )
168+ if level and min(level) > 0:
169+ return buffer.foreach_line_in_selection(buffer.decrement_indent)
170+ else:
171+ return False
172+
173+ def find_tag_toggle():
174+ # Checks if there are any tag changes within the selection
175+ start, end = buffer.get_selection_bounds()
176+ toggle = start.copy()
177+ toggle.forward_to_tag_toggle(None)
178+ return toggle.compare(end) < 0
179
180 with buffer.user_action:
181 if event.keyval in KEYVALS_TAB:
182- buffer.foreach_line_in_selection(buffer.increment_indent)
183+ # Check if inside verbatim block AND entire selection without tag toggle
184+ iter = buffer.get_insert_iter()
185+ if filter(_is_pre_tag, iter.get_tags()) \
186+ and not find_tag_toggle():
187+ prepend_tab = lambda iter: buffer.insert(iter, '\t')
188+ buffer.foreach_line_in_selection(prepend_tab)
189+ else:
190+ buffer.foreach_line_in_selection(buffer.increment_indent)
191 elif event.keyval in KEYVALS_LEFT_TAB:
192 decrement_indent()
193 elif event.keyval in KEYVALS_BACKSPACE \