##// END OF EJS Templates
crecord: make enter move cursor down to the next item of the same type...
av6 -
r40289:682f73fa default
parent child Browse files
Show More
@@ -1,1427 +1,1424 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import functools
10 import functools
11 import re
11 import re
12
12
13 from . import (
13 from . import (
14 encoding,
14 encoding,
15 error,
15 error,
16 )
16 )
17
17
18 def loadconfigtable(ui, extname, configtable):
18 def loadconfigtable(ui, extname, configtable):
19 """update config item known to the ui with the extension ones"""
19 """update config item known to the ui with the extension ones"""
20 for section, items in sorted(configtable.items()):
20 for section, items in sorted(configtable.items()):
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownkeys = set(knownitems)
22 knownkeys = set(knownitems)
23 newkeys = set(items)
23 newkeys = set(items)
24 for key in sorted(knownkeys & newkeys):
24 for key in sorted(knownkeys & newkeys):
25 msg = "extension '%s' overwrite config item '%s.%s'"
25 msg = "extension '%s' overwrite config item '%s.%s'"
26 msg %= (extname, section, key)
26 msg %= (extname, section, key)
27 ui.develwarn(msg, config='warn-config')
27 ui.develwarn(msg, config='warn-config')
28
28
29 knownitems.update(items)
29 knownitems.update(items)
30
30
31 class configitem(object):
31 class configitem(object):
32 """represent a known config item
32 """represent a known config item
33
33
34 :section: the official config section where to find this item,
34 :section: the official config section where to find this item,
35 :name: the official name within the section,
35 :name: the official name within the section,
36 :default: default value for this item,
36 :default: default value for this item,
37 :alias: optional list of tuples as alternatives,
37 :alias: optional list of tuples as alternatives,
38 :generic: this is a generic definition, match name using regular expression.
38 :generic: this is a generic definition, match name using regular expression.
39 """
39 """
40
40
41 def __init__(self, section, name, default=None, alias=(),
41 def __init__(self, section, name, default=None, alias=(),
42 generic=False, priority=0):
42 generic=False, priority=0):
43 self.section = section
43 self.section = section
44 self.name = name
44 self.name = name
45 self.default = default
45 self.default = default
46 self.alias = list(alias)
46 self.alias = list(alias)
47 self.generic = generic
47 self.generic = generic
48 self.priority = priority
48 self.priority = priority
49 self._re = None
49 self._re = None
50 if generic:
50 if generic:
51 self._re = re.compile(self.name)
51 self._re = re.compile(self.name)
52
52
53 class itemregister(dict):
53 class itemregister(dict):
54 """A specialized dictionary that can handle wild-card selection"""
54 """A specialized dictionary that can handle wild-card selection"""
55
55
56 def __init__(self):
56 def __init__(self):
57 super(itemregister, self).__init__()
57 super(itemregister, self).__init__()
58 self._generics = set()
58 self._generics = set()
59
59
60 def update(self, other):
60 def update(self, other):
61 super(itemregister, self).update(other)
61 super(itemregister, self).update(other)
62 self._generics.update(other._generics)
62 self._generics.update(other._generics)
63
63
64 def __setitem__(self, key, item):
64 def __setitem__(self, key, item):
65 super(itemregister, self).__setitem__(key, item)
65 super(itemregister, self).__setitem__(key, item)
66 if item.generic:
66 if item.generic:
67 self._generics.add(item)
67 self._generics.add(item)
68
68
69 def get(self, key):
69 def get(self, key):
70 baseitem = super(itemregister, self).get(key)
70 baseitem = super(itemregister, self).get(key)
71 if baseitem is not None and not baseitem.generic:
71 if baseitem is not None and not baseitem.generic:
72 return baseitem
72 return baseitem
73
73
74 # search for a matching generic item
74 # search for a matching generic item
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 for item in generics:
76 for item in generics:
77 # we use 'match' instead of 'search' to make the matching simpler
77 # we use 'match' instead of 'search' to make the matching simpler
78 # for people unfamiliar with regular expression. Having the match
78 # for people unfamiliar with regular expression. Having the match
79 # rooted to the start of the string will produce less surprising
79 # rooted to the start of the string will produce less surprising
80 # result for user writing simple regex for sub-attribute.
80 # result for user writing simple regex for sub-attribute.
81 #
81 #
82 # For example using "color\..*" match produces an unsurprising
82 # For example using "color\..*" match produces an unsurprising
83 # result, while using search could suddenly match apparently
83 # result, while using search could suddenly match apparently
84 # unrelated configuration that happens to contains "color."
84 # unrelated configuration that happens to contains "color."
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 # some match to avoid the need to prefix most pattern with "^".
86 # some match to avoid the need to prefix most pattern with "^".
87 # The "^" seems more error prone.
87 # The "^" seems more error prone.
88 if item._re.match(key):
88 if item._re.match(key):
89 return item
89 return item
90
90
91 return None
91 return None
92
92
93 coreitems = {}
93 coreitems = {}
94
94
95 def _register(configtable, *args, **kwargs):
95 def _register(configtable, *args, **kwargs):
96 item = configitem(*args, **kwargs)
96 item = configitem(*args, **kwargs)
97 section = configtable.setdefault(item.section, itemregister())
97 section = configtable.setdefault(item.section, itemregister())
98 if item.name in section:
98 if item.name in section:
99 msg = "duplicated config item registration for '%s.%s'"
99 msg = "duplicated config item registration for '%s.%s'"
100 raise error.ProgrammingError(msg % (item.section, item.name))
100 raise error.ProgrammingError(msg % (item.section, item.name))
101 section[item.name] = item
101 section[item.name] = item
102
102
103 # special value for case where the default is derived from other values
103 # special value for case where the default is derived from other values
104 dynamicdefault = object()
104 dynamicdefault = object()
105
105
106 # Registering actual config items
106 # Registering actual config items
107
107
108 def getitemregister(configtable):
108 def getitemregister(configtable):
109 f = functools.partial(_register, configtable)
109 f = functools.partial(_register, configtable)
110 # export pseudo enum as configitem.*
110 # export pseudo enum as configitem.*
111 f.dynamicdefault = dynamicdefault
111 f.dynamicdefault = dynamicdefault
112 return f
112 return f
113
113
114 coreconfigitem = getitemregister(coreitems)
114 coreconfigitem = getitemregister(coreitems)
115
115
116 coreconfigitem('alias', '.*',
116 coreconfigitem('alias', '.*',
117 default=dynamicdefault,
117 default=dynamicdefault,
118 generic=True,
118 generic=True,
119 )
119 )
120 coreconfigitem('annotate', 'nodates',
120 coreconfigitem('annotate', 'nodates',
121 default=False,
121 default=False,
122 )
122 )
123 coreconfigitem('annotate', 'showfunc',
123 coreconfigitem('annotate', 'showfunc',
124 default=False,
124 default=False,
125 )
125 )
126 coreconfigitem('annotate', 'unified',
126 coreconfigitem('annotate', 'unified',
127 default=None,
127 default=None,
128 )
128 )
129 coreconfigitem('annotate', 'git',
129 coreconfigitem('annotate', 'git',
130 default=False,
130 default=False,
131 )
131 )
132 coreconfigitem('annotate', 'ignorews',
132 coreconfigitem('annotate', 'ignorews',
133 default=False,
133 default=False,
134 )
134 )
135 coreconfigitem('annotate', 'ignorewsamount',
135 coreconfigitem('annotate', 'ignorewsamount',
136 default=False,
136 default=False,
137 )
137 )
138 coreconfigitem('annotate', 'ignoreblanklines',
138 coreconfigitem('annotate', 'ignoreblanklines',
139 default=False,
139 default=False,
140 )
140 )
141 coreconfigitem('annotate', 'ignorewseol',
141 coreconfigitem('annotate', 'ignorewseol',
142 default=False,
142 default=False,
143 )
143 )
144 coreconfigitem('annotate', 'nobinary',
144 coreconfigitem('annotate', 'nobinary',
145 default=False,
145 default=False,
146 )
146 )
147 coreconfigitem('annotate', 'noprefix',
147 coreconfigitem('annotate', 'noprefix',
148 default=False,
148 default=False,
149 )
149 )
150 coreconfigitem('annotate', 'word-diff',
150 coreconfigitem('annotate', 'word-diff',
151 default=False,
151 default=False,
152 )
152 )
153 coreconfigitem('auth', 'cookiefile',
153 coreconfigitem('auth', 'cookiefile',
154 default=None,
154 default=None,
155 )
155 )
156 # bookmarks.pushing: internal hack for discovery
156 # bookmarks.pushing: internal hack for discovery
157 coreconfigitem('bookmarks', 'pushing',
157 coreconfigitem('bookmarks', 'pushing',
158 default=list,
158 default=list,
159 )
159 )
160 # bundle.mainreporoot: internal hack for bundlerepo
160 # bundle.mainreporoot: internal hack for bundlerepo
161 coreconfigitem('bundle', 'mainreporoot',
161 coreconfigitem('bundle', 'mainreporoot',
162 default='',
162 default='',
163 )
163 )
164 coreconfigitem('censor', 'policy',
164 coreconfigitem('censor', 'policy',
165 default='abort',
165 default='abort',
166 )
166 )
167 coreconfigitem('chgserver', 'idletimeout',
167 coreconfigitem('chgserver', 'idletimeout',
168 default=3600,
168 default=3600,
169 )
169 )
170 coreconfigitem('chgserver', 'skiphash',
170 coreconfigitem('chgserver', 'skiphash',
171 default=False,
171 default=False,
172 )
172 )
173 coreconfigitem('cmdserver', 'log',
173 coreconfigitem('cmdserver', 'log',
174 default=None,
174 default=None,
175 )
175 )
176 coreconfigitem('color', '.*',
176 coreconfigitem('color', '.*',
177 default=None,
177 default=None,
178 generic=True,
178 generic=True,
179 )
179 )
180 coreconfigitem('color', 'mode',
180 coreconfigitem('color', 'mode',
181 default='auto',
181 default='auto',
182 )
182 )
183 coreconfigitem('color', 'pagermode',
183 coreconfigitem('color', 'pagermode',
184 default=dynamicdefault,
184 default=dynamicdefault,
185 )
185 )
186 coreconfigitem('commands', 'grep.all-files',
186 coreconfigitem('commands', 'grep.all-files',
187 default=False,
187 default=False,
188 )
188 )
189 coreconfigitem('commands', 'resolve.confirm',
189 coreconfigitem('commands', 'resolve.confirm',
190 default=False,
190 default=False,
191 )
191 )
192 coreconfigitem('commands', 'resolve.explicit-re-merge',
192 coreconfigitem('commands', 'resolve.explicit-re-merge',
193 default=False,
193 default=False,
194 )
194 )
195 coreconfigitem('commands', 'resolve.mark-check',
195 coreconfigitem('commands', 'resolve.mark-check',
196 default='none',
196 default='none',
197 )
197 )
198 coreconfigitem('commands', 'show.aliasprefix',
198 coreconfigitem('commands', 'show.aliasprefix',
199 default=list,
199 default=list,
200 )
200 )
201 coreconfigitem('commands', 'status.relative',
201 coreconfigitem('commands', 'status.relative',
202 default=False,
202 default=False,
203 )
203 )
204 coreconfigitem('commands', 'status.skipstates',
204 coreconfigitem('commands', 'status.skipstates',
205 default=[],
205 default=[],
206 )
206 )
207 coreconfigitem('commands', 'status.terse',
207 coreconfigitem('commands', 'status.terse',
208 default='',
208 default='',
209 )
209 )
210 coreconfigitem('commands', 'status.verbose',
210 coreconfigitem('commands', 'status.verbose',
211 default=False,
211 default=False,
212 )
212 )
213 coreconfigitem('commands', 'update.check',
213 coreconfigitem('commands', 'update.check',
214 default=None,
214 default=None,
215 )
215 )
216 coreconfigitem('commands', 'update.requiredest',
216 coreconfigitem('commands', 'update.requiredest',
217 default=False,
217 default=False,
218 )
218 )
219 coreconfigitem('committemplate', '.*',
219 coreconfigitem('committemplate', '.*',
220 default=None,
220 default=None,
221 generic=True,
221 generic=True,
222 )
222 )
223 coreconfigitem('convert', 'bzr.saverev',
223 coreconfigitem('convert', 'bzr.saverev',
224 default=True,
224 default=True,
225 )
225 )
226 coreconfigitem('convert', 'cvsps.cache',
226 coreconfigitem('convert', 'cvsps.cache',
227 default=True,
227 default=True,
228 )
228 )
229 coreconfigitem('convert', 'cvsps.fuzz',
229 coreconfigitem('convert', 'cvsps.fuzz',
230 default=60,
230 default=60,
231 )
231 )
232 coreconfigitem('convert', 'cvsps.logencoding',
232 coreconfigitem('convert', 'cvsps.logencoding',
233 default=None,
233 default=None,
234 )
234 )
235 coreconfigitem('convert', 'cvsps.mergefrom',
235 coreconfigitem('convert', 'cvsps.mergefrom',
236 default=None,
236 default=None,
237 )
237 )
238 coreconfigitem('convert', 'cvsps.mergeto',
238 coreconfigitem('convert', 'cvsps.mergeto',
239 default=None,
239 default=None,
240 )
240 )
241 coreconfigitem('convert', 'git.committeractions',
241 coreconfigitem('convert', 'git.committeractions',
242 default=lambda: ['messagedifferent'],
242 default=lambda: ['messagedifferent'],
243 )
243 )
244 coreconfigitem('convert', 'git.extrakeys',
244 coreconfigitem('convert', 'git.extrakeys',
245 default=list,
245 default=list,
246 )
246 )
247 coreconfigitem('convert', 'git.findcopiesharder',
247 coreconfigitem('convert', 'git.findcopiesharder',
248 default=False,
248 default=False,
249 )
249 )
250 coreconfigitem('convert', 'git.remoteprefix',
250 coreconfigitem('convert', 'git.remoteprefix',
251 default='remote',
251 default='remote',
252 )
252 )
253 coreconfigitem('convert', 'git.renamelimit',
253 coreconfigitem('convert', 'git.renamelimit',
254 default=400,
254 default=400,
255 )
255 )
256 coreconfigitem('convert', 'git.saverev',
256 coreconfigitem('convert', 'git.saverev',
257 default=True,
257 default=True,
258 )
258 )
259 coreconfigitem('convert', 'git.similarity',
259 coreconfigitem('convert', 'git.similarity',
260 default=50,
260 default=50,
261 )
261 )
262 coreconfigitem('convert', 'git.skipsubmodules',
262 coreconfigitem('convert', 'git.skipsubmodules',
263 default=False,
263 default=False,
264 )
264 )
265 coreconfigitem('convert', 'hg.clonebranches',
265 coreconfigitem('convert', 'hg.clonebranches',
266 default=False,
266 default=False,
267 )
267 )
268 coreconfigitem('convert', 'hg.ignoreerrors',
268 coreconfigitem('convert', 'hg.ignoreerrors',
269 default=False,
269 default=False,
270 )
270 )
271 coreconfigitem('convert', 'hg.revs',
271 coreconfigitem('convert', 'hg.revs',
272 default=None,
272 default=None,
273 )
273 )
274 coreconfigitem('convert', 'hg.saverev',
274 coreconfigitem('convert', 'hg.saverev',
275 default=False,
275 default=False,
276 )
276 )
277 coreconfigitem('convert', 'hg.sourcename',
277 coreconfigitem('convert', 'hg.sourcename',
278 default=None,
278 default=None,
279 )
279 )
280 coreconfigitem('convert', 'hg.startrev',
280 coreconfigitem('convert', 'hg.startrev',
281 default=None,
281 default=None,
282 )
282 )
283 coreconfigitem('convert', 'hg.tagsbranch',
283 coreconfigitem('convert', 'hg.tagsbranch',
284 default='default',
284 default='default',
285 )
285 )
286 coreconfigitem('convert', 'hg.usebranchnames',
286 coreconfigitem('convert', 'hg.usebranchnames',
287 default=True,
287 default=True,
288 )
288 )
289 coreconfigitem('convert', 'ignoreancestorcheck',
289 coreconfigitem('convert', 'ignoreancestorcheck',
290 default=False,
290 default=False,
291 )
291 )
292 coreconfigitem('convert', 'localtimezone',
292 coreconfigitem('convert', 'localtimezone',
293 default=False,
293 default=False,
294 )
294 )
295 coreconfigitem('convert', 'p4.encoding',
295 coreconfigitem('convert', 'p4.encoding',
296 default=dynamicdefault,
296 default=dynamicdefault,
297 )
297 )
298 coreconfigitem('convert', 'p4.startrev',
298 coreconfigitem('convert', 'p4.startrev',
299 default=0,
299 default=0,
300 )
300 )
301 coreconfigitem('convert', 'skiptags',
301 coreconfigitem('convert', 'skiptags',
302 default=False,
302 default=False,
303 )
303 )
304 coreconfigitem('convert', 'svn.debugsvnlog',
304 coreconfigitem('convert', 'svn.debugsvnlog',
305 default=True,
305 default=True,
306 )
306 )
307 coreconfigitem('convert', 'svn.trunk',
307 coreconfigitem('convert', 'svn.trunk',
308 default=None,
308 default=None,
309 )
309 )
310 coreconfigitem('convert', 'svn.tags',
310 coreconfigitem('convert', 'svn.tags',
311 default=None,
311 default=None,
312 )
312 )
313 coreconfigitem('convert', 'svn.branches',
313 coreconfigitem('convert', 'svn.branches',
314 default=None,
314 default=None,
315 )
315 )
316 coreconfigitem('convert', 'svn.startrev',
316 coreconfigitem('convert', 'svn.startrev',
317 default=0,
317 default=0,
318 )
318 )
319 coreconfigitem('debug', 'dirstate.delaywrite',
319 coreconfigitem('debug', 'dirstate.delaywrite',
320 default=0,
320 default=0,
321 )
321 )
322 coreconfigitem('defaults', '.*',
322 coreconfigitem('defaults', '.*',
323 default=None,
323 default=None,
324 generic=True,
324 generic=True,
325 )
325 )
326 coreconfigitem('devel', 'all-warnings',
326 coreconfigitem('devel', 'all-warnings',
327 default=False,
327 default=False,
328 )
328 )
329 coreconfigitem('devel', 'bundle2.debug',
329 coreconfigitem('devel', 'bundle2.debug',
330 default=False,
330 default=False,
331 )
331 )
332 coreconfigitem('devel', 'cache-vfs',
332 coreconfigitem('devel', 'cache-vfs',
333 default=None,
333 default=None,
334 )
334 )
335 coreconfigitem('devel', 'check-locks',
335 coreconfigitem('devel', 'check-locks',
336 default=False,
336 default=False,
337 )
337 )
338 coreconfigitem('devel', 'check-relroot',
338 coreconfigitem('devel', 'check-relroot',
339 default=False,
339 default=False,
340 )
340 )
341 coreconfigitem('devel', 'default-date',
341 coreconfigitem('devel', 'default-date',
342 default=None,
342 default=None,
343 )
343 )
344 coreconfigitem('devel', 'deprec-warn',
344 coreconfigitem('devel', 'deprec-warn',
345 default=False,
345 default=False,
346 )
346 )
347 coreconfigitem('devel', 'disableloaddefaultcerts',
347 coreconfigitem('devel', 'disableloaddefaultcerts',
348 default=False,
348 default=False,
349 )
349 )
350 coreconfigitem('devel', 'warn-empty-changegroup',
350 coreconfigitem('devel', 'warn-empty-changegroup',
351 default=False,
351 default=False,
352 )
352 )
353 coreconfigitem('devel', 'legacy.exchange',
353 coreconfigitem('devel', 'legacy.exchange',
354 default=list,
354 default=list,
355 )
355 )
356 coreconfigitem('devel', 'servercafile',
356 coreconfigitem('devel', 'servercafile',
357 default='',
357 default='',
358 )
358 )
359 coreconfigitem('devel', 'serverexactprotocol',
359 coreconfigitem('devel', 'serverexactprotocol',
360 default='',
360 default='',
361 )
361 )
362 coreconfigitem('devel', 'serverrequirecert',
362 coreconfigitem('devel', 'serverrequirecert',
363 default=False,
363 default=False,
364 )
364 )
365 coreconfigitem('devel', 'strip-obsmarkers',
365 coreconfigitem('devel', 'strip-obsmarkers',
366 default=True,
366 default=True,
367 )
367 )
368 coreconfigitem('devel', 'warn-config',
368 coreconfigitem('devel', 'warn-config',
369 default=None,
369 default=None,
370 )
370 )
371 coreconfigitem('devel', 'warn-config-default',
371 coreconfigitem('devel', 'warn-config-default',
372 default=None,
372 default=None,
373 )
373 )
374 coreconfigitem('devel', 'user.obsmarker',
374 coreconfigitem('devel', 'user.obsmarker',
375 default=None,
375 default=None,
376 )
376 )
377 coreconfigitem('devel', 'warn-config-unknown',
377 coreconfigitem('devel', 'warn-config-unknown',
378 default=None,
378 default=None,
379 )
379 )
380 coreconfigitem('devel', 'debug.copies',
380 coreconfigitem('devel', 'debug.copies',
381 default=False,
381 default=False,
382 )
382 )
383 coreconfigitem('devel', 'debug.extensions',
383 coreconfigitem('devel', 'debug.extensions',
384 default=False,
384 default=False,
385 )
385 )
386 coreconfigitem('devel', 'debug.peer-request',
386 coreconfigitem('devel', 'debug.peer-request',
387 default=False,
387 default=False,
388 )
388 )
389 coreconfigitem('diff', 'nodates',
389 coreconfigitem('diff', 'nodates',
390 default=False,
390 default=False,
391 )
391 )
392 coreconfigitem('diff', 'showfunc',
392 coreconfigitem('diff', 'showfunc',
393 default=False,
393 default=False,
394 )
394 )
395 coreconfigitem('diff', 'unified',
395 coreconfigitem('diff', 'unified',
396 default=None,
396 default=None,
397 )
397 )
398 coreconfigitem('diff', 'git',
398 coreconfigitem('diff', 'git',
399 default=False,
399 default=False,
400 )
400 )
401 coreconfigitem('diff', 'ignorews',
401 coreconfigitem('diff', 'ignorews',
402 default=False,
402 default=False,
403 )
403 )
404 coreconfigitem('diff', 'ignorewsamount',
404 coreconfigitem('diff', 'ignorewsamount',
405 default=False,
405 default=False,
406 )
406 )
407 coreconfigitem('diff', 'ignoreblanklines',
407 coreconfigitem('diff', 'ignoreblanklines',
408 default=False,
408 default=False,
409 )
409 )
410 coreconfigitem('diff', 'ignorewseol',
410 coreconfigitem('diff', 'ignorewseol',
411 default=False,
411 default=False,
412 )
412 )
413 coreconfigitem('diff', 'nobinary',
413 coreconfigitem('diff', 'nobinary',
414 default=False,
414 default=False,
415 )
415 )
416 coreconfigitem('diff', 'noprefix',
416 coreconfigitem('diff', 'noprefix',
417 default=False,
417 default=False,
418 )
418 )
419 coreconfigitem('diff', 'word-diff',
419 coreconfigitem('diff', 'word-diff',
420 default=False,
420 default=False,
421 )
421 )
422 coreconfigitem('email', 'bcc',
422 coreconfigitem('email', 'bcc',
423 default=None,
423 default=None,
424 )
424 )
425 coreconfigitem('email', 'cc',
425 coreconfigitem('email', 'cc',
426 default=None,
426 default=None,
427 )
427 )
428 coreconfigitem('email', 'charsets',
428 coreconfigitem('email', 'charsets',
429 default=list,
429 default=list,
430 )
430 )
431 coreconfigitem('email', 'from',
431 coreconfigitem('email', 'from',
432 default=None,
432 default=None,
433 )
433 )
434 coreconfigitem('email', 'method',
434 coreconfigitem('email', 'method',
435 default='smtp',
435 default='smtp',
436 )
436 )
437 coreconfigitem('email', 'reply-to',
437 coreconfigitem('email', 'reply-to',
438 default=None,
438 default=None,
439 )
439 )
440 coreconfigitem('email', 'to',
440 coreconfigitem('email', 'to',
441 default=None,
441 default=None,
442 )
442 )
443 coreconfigitem('experimental', 'archivemetatemplate',
443 coreconfigitem('experimental', 'archivemetatemplate',
444 default=dynamicdefault,
444 default=dynamicdefault,
445 )
445 )
446 coreconfigitem('experimental', 'bundle-phases',
446 coreconfigitem('experimental', 'bundle-phases',
447 default=False,
447 default=False,
448 )
448 )
449 coreconfigitem('experimental', 'bundle2-advertise',
449 coreconfigitem('experimental', 'bundle2-advertise',
450 default=True,
450 default=True,
451 )
451 )
452 coreconfigitem('experimental', 'bundle2-output-capture',
452 coreconfigitem('experimental', 'bundle2-output-capture',
453 default=False,
453 default=False,
454 )
454 )
455 coreconfigitem('experimental', 'bundle2.pushback',
455 coreconfigitem('experimental', 'bundle2.pushback',
456 default=False,
456 default=False,
457 )
457 )
458 coreconfigitem('experimental', 'bundle2lazylocking',
458 coreconfigitem('experimental', 'bundle2lazylocking',
459 default=False,
459 default=False,
460 )
460 )
461 coreconfigitem('experimental', 'bundlecomplevel',
461 coreconfigitem('experimental', 'bundlecomplevel',
462 default=None,
462 default=None,
463 )
463 )
464 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
464 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
465 default=None,
465 default=None,
466 )
466 )
467 coreconfigitem('experimental', 'bundlecomplevel.gzip',
467 coreconfigitem('experimental', 'bundlecomplevel.gzip',
468 default=None,
468 default=None,
469 )
469 )
470 coreconfigitem('experimental', 'bundlecomplevel.none',
470 coreconfigitem('experimental', 'bundlecomplevel.none',
471 default=None,
471 default=None,
472 )
472 )
473 coreconfigitem('experimental', 'bundlecomplevel.zstd',
473 coreconfigitem('experimental', 'bundlecomplevel.zstd',
474 default=None,
474 default=None,
475 )
475 )
476 coreconfigitem('experimental', 'changegroup3',
476 coreconfigitem('experimental', 'changegroup3',
477 default=False,
477 default=False,
478 )
478 )
479 coreconfigitem('experimental', 'clientcompressionengines',
479 coreconfigitem('experimental', 'clientcompressionengines',
480 default=list,
480 default=list,
481 )
481 )
482 coreconfigitem('experimental', 'copytrace',
482 coreconfigitem('experimental', 'copytrace',
483 default='on',
483 default='on',
484 )
484 )
485 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
485 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
486 default=100,
486 default=100,
487 )
487 )
488 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
488 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
489 default=100,
489 default=100,
490 )
490 )
491 coreconfigitem('experimental', 'crecordtest',
491 coreconfigitem('experimental', 'crecordtest',
492 default=None,
492 default=None,
493 )
493 )
494 coreconfigitem('experimental', 'directaccess',
494 coreconfigitem('experimental', 'directaccess',
495 default=False,
495 default=False,
496 )
496 )
497 coreconfigitem('experimental', 'directaccess.revnums',
497 coreconfigitem('experimental', 'directaccess.revnums',
498 default=False,
498 default=False,
499 )
499 )
500 coreconfigitem('experimental', 'editortmpinhg',
500 coreconfigitem('experimental', 'editortmpinhg',
501 default=False,
501 default=False,
502 )
502 )
503 coreconfigitem('experimental', 'evolution',
503 coreconfigitem('experimental', 'evolution',
504 default=list,
504 default=list,
505 )
505 )
506 coreconfigitem('experimental', 'evolution.allowdivergence',
506 coreconfigitem('experimental', 'evolution.allowdivergence',
507 default=False,
507 default=False,
508 alias=[('experimental', 'allowdivergence')]
508 alias=[('experimental', 'allowdivergence')]
509 )
509 )
510 coreconfigitem('experimental', 'evolution.allowunstable',
510 coreconfigitem('experimental', 'evolution.allowunstable',
511 default=None,
511 default=None,
512 )
512 )
513 coreconfigitem('experimental', 'evolution.createmarkers',
513 coreconfigitem('experimental', 'evolution.createmarkers',
514 default=None,
514 default=None,
515 )
515 )
516 coreconfigitem('experimental', 'evolution.effect-flags',
516 coreconfigitem('experimental', 'evolution.effect-flags',
517 default=True,
517 default=True,
518 alias=[('experimental', 'effect-flags')]
518 alias=[('experimental', 'effect-flags')]
519 )
519 )
520 coreconfigitem('experimental', 'evolution.exchange',
520 coreconfigitem('experimental', 'evolution.exchange',
521 default=None,
521 default=None,
522 )
522 )
523 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
523 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
524 default=False,
524 default=False,
525 )
525 )
526 coreconfigitem('experimental', 'evolution.report-instabilities',
526 coreconfigitem('experimental', 'evolution.report-instabilities',
527 default=True,
527 default=True,
528 )
528 )
529 coreconfigitem('experimental', 'evolution.track-operation',
529 coreconfigitem('experimental', 'evolution.track-operation',
530 default=True,
530 default=True,
531 )
531 )
532 coreconfigitem('experimental', 'maxdeltachainspan',
532 coreconfigitem('experimental', 'maxdeltachainspan',
533 default=-1,
533 default=-1,
534 )
534 )
535 coreconfigitem('experimental', 'mergetempdirprefix',
535 coreconfigitem('experimental', 'mergetempdirprefix',
536 default=None,
536 default=None,
537 )
537 )
538 coreconfigitem('experimental', 'mmapindexthreshold',
538 coreconfigitem('experimental', 'mmapindexthreshold',
539 default=None,
539 default=None,
540 )
540 )
541 coreconfigitem('experimental', 'narrow',
541 coreconfigitem('experimental', 'narrow',
542 default=False,
542 default=False,
543 )
543 )
544 coreconfigitem('experimental', 'nonnormalparanoidcheck',
544 coreconfigitem('experimental', 'nonnormalparanoidcheck',
545 default=False,
545 default=False,
546 )
546 )
547 coreconfigitem('experimental', 'exportableenviron',
547 coreconfigitem('experimental', 'exportableenviron',
548 default=list,
548 default=list,
549 )
549 )
550 coreconfigitem('experimental', 'extendedheader.index',
550 coreconfigitem('experimental', 'extendedheader.index',
551 default=None,
551 default=None,
552 )
552 )
553 coreconfigitem('experimental', 'extendedheader.similarity',
553 coreconfigitem('experimental', 'extendedheader.similarity',
554 default=False,
554 default=False,
555 )
555 )
556 coreconfigitem('experimental', 'format.compression',
556 coreconfigitem('experimental', 'format.compression',
557 default='zlib',
557 default='zlib',
558 )
558 )
559 coreconfigitem('experimental', 'graphshorten',
559 coreconfigitem('experimental', 'graphshorten',
560 default=False,
560 default=False,
561 )
561 )
562 coreconfigitem('experimental', 'graphstyle.parent',
562 coreconfigitem('experimental', 'graphstyle.parent',
563 default=dynamicdefault,
563 default=dynamicdefault,
564 )
564 )
565 coreconfigitem('experimental', 'graphstyle.missing',
565 coreconfigitem('experimental', 'graphstyle.missing',
566 default=dynamicdefault,
566 default=dynamicdefault,
567 )
567 )
568 coreconfigitem('experimental', 'graphstyle.grandparent',
568 coreconfigitem('experimental', 'graphstyle.grandparent',
569 default=dynamicdefault,
569 default=dynamicdefault,
570 )
570 )
571 coreconfigitem('experimental', 'hook-track-tags',
571 coreconfigitem('experimental', 'hook-track-tags',
572 default=False,
572 default=False,
573 )
573 )
574 coreconfigitem('experimental', 'httppeer.advertise-v2',
574 coreconfigitem('experimental', 'httppeer.advertise-v2',
575 default=False,
575 default=False,
576 )
576 )
577 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
577 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
578 default=None,
578 default=None,
579 )
579 )
580 coreconfigitem('experimental', 'httppostargs',
580 coreconfigitem('experimental', 'httppostargs',
581 default=False,
581 default=False,
582 )
582 )
583 coreconfigitem('experimental', 'mergedriver',
583 coreconfigitem('experimental', 'mergedriver',
584 default=None,
584 default=None,
585 )
585 )
586 coreconfigitem('experimental', 'nointerrupt', default=False)
586 coreconfigitem('experimental', 'nointerrupt', default=False)
587 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
587 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
588
588
589 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
589 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
590 default=False,
590 default=False,
591 )
591 )
592 coreconfigitem('experimental', 'remotenames',
592 coreconfigitem('experimental', 'remotenames',
593 default=False,
593 default=False,
594 )
594 )
595 coreconfigitem('experimental', 'removeemptydirs',
595 coreconfigitem('experimental', 'removeemptydirs',
596 default=True,
596 default=True,
597 )
597 )
598 coreconfigitem('experimental', 'revisions.prefixhexnode',
598 coreconfigitem('experimental', 'revisions.prefixhexnode',
599 default=False,
599 default=False,
600 )
600 )
601 coreconfigitem('experimental', 'revlogv2',
601 coreconfigitem('experimental', 'revlogv2',
602 default=None,
602 default=None,
603 )
603 )
604 coreconfigitem('experimental', 'revisions.disambiguatewithin',
604 coreconfigitem('experimental', 'revisions.disambiguatewithin',
605 default=None,
605 default=None,
606 )
606 )
607 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
607 coreconfigitem('experimental', 'server.filesdata.recommended-batch-size',
608 default=50000,
608 default=50000,
609 )
609 )
610 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
610 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
611 default=100000,
611 default=100000,
612 )
612 )
613 coreconfigitem('experimental', 'single-head-per-branch',
613 coreconfigitem('experimental', 'single-head-per-branch',
614 default=False,
614 default=False,
615 )
615 )
616 coreconfigitem('experimental', 'sshserver.support-v2',
616 coreconfigitem('experimental', 'sshserver.support-v2',
617 default=False,
617 default=False,
618 )
618 )
619 coreconfigitem('experimental', 'spacemovesdown',
620 default=False,
621 )
622 coreconfigitem('experimental', 'sparse-read',
619 coreconfigitem('experimental', 'sparse-read',
623 default=False,
620 default=False,
624 )
621 )
625 coreconfigitem('experimental', 'sparse-read.density-threshold',
622 coreconfigitem('experimental', 'sparse-read.density-threshold',
626 default=0.50,
623 default=0.50,
627 )
624 )
628 coreconfigitem('experimental', 'sparse-read.min-gap-size',
625 coreconfigitem('experimental', 'sparse-read.min-gap-size',
629 default='65K',
626 default='65K',
630 )
627 )
631 coreconfigitem('experimental', 'treemanifest',
628 coreconfigitem('experimental', 'treemanifest',
632 default=False,
629 default=False,
633 )
630 )
634 coreconfigitem('experimental', 'update.atomic-file',
631 coreconfigitem('experimental', 'update.atomic-file',
635 default=False,
632 default=False,
636 )
633 )
637 coreconfigitem('experimental', 'sshpeer.advertise-v2',
634 coreconfigitem('experimental', 'sshpeer.advertise-v2',
638 default=False,
635 default=False,
639 )
636 )
640 coreconfigitem('experimental', 'web.apiserver',
637 coreconfigitem('experimental', 'web.apiserver',
641 default=False,
638 default=False,
642 )
639 )
643 coreconfigitem('experimental', 'web.api.http-v2',
640 coreconfigitem('experimental', 'web.api.http-v2',
644 default=False,
641 default=False,
645 )
642 )
646 coreconfigitem('experimental', 'web.api.debugreflect',
643 coreconfigitem('experimental', 'web.api.debugreflect',
647 default=False,
644 default=False,
648 )
645 )
649 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
646 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
650 default=False,
647 default=False,
651 )
648 )
652 coreconfigitem('experimental', 'xdiff',
649 coreconfigitem('experimental', 'xdiff',
653 default=False,
650 default=False,
654 )
651 )
655 coreconfigitem('extensions', '.*',
652 coreconfigitem('extensions', '.*',
656 default=None,
653 default=None,
657 generic=True,
654 generic=True,
658 )
655 )
659 coreconfigitem('extdata', '.*',
656 coreconfigitem('extdata', '.*',
660 default=None,
657 default=None,
661 generic=True,
658 generic=True,
662 )
659 )
663 coreconfigitem('format', 'chunkcachesize',
660 coreconfigitem('format', 'chunkcachesize',
664 default=None,
661 default=None,
665 )
662 )
666 coreconfigitem('format', 'dotencode',
663 coreconfigitem('format', 'dotencode',
667 default=True,
664 default=True,
668 )
665 )
669 coreconfigitem('format', 'generaldelta',
666 coreconfigitem('format', 'generaldelta',
670 default=False,
667 default=False,
671 )
668 )
672 coreconfigitem('format', 'manifestcachesize',
669 coreconfigitem('format', 'manifestcachesize',
673 default=None,
670 default=None,
674 )
671 )
675 coreconfigitem('format', 'maxchainlen',
672 coreconfigitem('format', 'maxchainlen',
676 default=dynamicdefault,
673 default=dynamicdefault,
677 )
674 )
678 coreconfigitem('format', 'obsstore-version',
675 coreconfigitem('format', 'obsstore-version',
679 default=None,
676 default=None,
680 )
677 )
681 coreconfigitem('format', 'sparse-revlog',
678 coreconfigitem('format', 'sparse-revlog',
682 default=False,
679 default=False,
683 )
680 )
684 coreconfigitem('format', 'usefncache',
681 coreconfigitem('format', 'usefncache',
685 default=True,
682 default=True,
686 )
683 )
687 coreconfigitem('format', 'usegeneraldelta',
684 coreconfigitem('format', 'usegeneraldelta',
688 default=True,
685 default=True,
689 )
686 )
690 coreconfigitem('format', 'usestore',
687 coreconfigitem('format', 'usestore',
691 default=True,
688 default=True,
692 )
689 )
693 coreconfigitem('format', 'internal-phase',
690 coreconfigitem('format', 'internal-phase',
694 default=False,
691 default=False,
695 )
692 )
696 coreconfigitem('fsmonitor', 'warn_when_unused',
693 coreconfigitem('fsmonitor', 'warn_when_unused',
697 default=True,
694 default=True,
698 )
695 )
699 coreconfigitem('fsmonitor', 'warn_update_file_count',
696 coreconfigitem('fsmonitor', 'warn_update_file_count',
700 default=50000,
697 default=50000,
701 )
698 )
702 coreconfigitem('hooks', '.*',
699 coreconfigitem('hooks', '.*',
703 default=dynamicdefault,
700 default=dynamicdefault,
704 generic=True,
701 generic=True,
705 )
702 )
706 coreconfigitem('hgweb-paths', '.*',
703 coreconfigitem('hgweb-paths', '.*',
707 default=list,
704 default=list,
708 generic=True,
705 generic=True,
709 )
706 )
710 coreconfigitem('hostfingerprints', '.*',
707 coreconfigitem('hostfingerprints', '.*',
711 default=list,
708 default=list,
712 generic=True,
709 generic=True,
713 )
710 )
714 coreconfigitem('hostsecurity', 'ciphers',
711 coreconfigitem('hostsecurity', 'ciphers',
715 default=None,
712 default=None,
716 )
713 )
717 coreconfigitem('hostsecurity', 'disabletls10warning',
714 coreconfigitem('hostsecurity', 'disabletls10warning',
718 default=False,
715 default=False,
719 )
716 )
720 coreconfigitem('hostsecurity', 'minimumprotocol',
717 coreconfigitem('hostsecurity', 'minimumprotocol',
721 default=dynamicdefault,
718 default=dynamicdefault,
722 )
719 )
723 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
720 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
724 default=dynamicdefault,
721 default=dynamicdefault,
725 generic=True,
722 generic=True,
726 )
723 )
727 coreconfigitem('hostsecurity', '.*:ciphers$',
724 coreconfigitem('hostsecurity', '.*:ciphers$',
728 default=dynamicdefault,
725 default=dynamicdefault,
729 generic=True,
726 generic=True,
730 )
727 )
731 coreconfigitem('hostsecurity', '.*:fingerprints$',
728 coreconfigitem('hostsecurity', '.*:fingerprints$',
732 default=list,
729 default=list,
733 generic=True,
730 generic=True,
734 )
731 )
735 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
732 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
736 default=None,
733 default=None,
737 generic=True,
734 generic=True,
738 )
735 )
739
736
740 coreconfigitem('http_proxy', 'always',
737 coreconfigitem('http_proxy', 'always',
741 default=False,
738 default=False,
742 )
739 )
743 coreconfigitem('http_proxy', 'host',
740 coreconfigitem('http_proxy', 'host',
744 default=None,
741 default=None,
745 )
742 )
746 coreconfigitem('http_proxy', 'no',
743 coreconfigitem('http_proxy', 'no',
747 default=list,
744 default=list,
748 )
745 )
749 coreconfigitem('http_proxy', 'passwd',
746 coreconfigitem('http_proxy', 'passwd',
750 default=None,
747 default=None,
751 )
748 )
752 coreconfigitem('http_proxy', 'user',
749 coreconfigitem('http_proxy', 'user',
753 default=None,
750 default=None,
754 )
751 )
755
752
756 coreconfigitem('http', 'timeout',
753 coreconfigitem('http', 'timeout',
757 default=None,
754 default=None,
758 )
755 )
759
756
760 coreconfigitem('logtoprocess', 'commandexception',
757 coreconfigitem('logtoprocess', 'commandexception',
761 default=None,
758 default=None,
762 )
759 )
763 coreconfigitem('logtoprocess', 'commandfinish',
760 coreconfigitem('logtoprocess', 'commandfinish',
764 default=None,
761 default=None,
765 )
762 )
766 coreconfigitem('logtoprocess', 'command',
763 coreconfigitem('logtoprocess', 'command',
767 default=None,
764 default=None,
768 )
765 )
769 coreconfigitem('logtoprocess', 'develwarn',
766 coreconfigitem('logtoprocess', 'develwarn',
770 default=None,
767 default=None,
771 )
768 )
772 coreconfigitem('logtoprocess', 'uiblocked',
769 coreconfigitem('logtoprocess', 'uiblocked',
773 default=None,
770 default=None,
774 )
771 )
775 coreconfigitem('merge', 'checkunknown',
772 coreconfigitem('merge', 'checkunknown',
776 default='abort',
773 default='abort',
777 )
774 )
778 coreconfigitem('merge', 'checkignored',
775 coreconfigitem('merge', 'checkignored',
779 default='abort',
776 default='abort',
780 )
777 )
781 coreconfigitem('experimental', 'merge.checkpathconflicts',
778 coreconfigitem('experimental', 'merge.checkpathconflicts',
782 default=False,
779 default=False,
783 )
780 )
784 coreconfigitem('merge', 'followcopies',
781 coreconfigitem('merge', 'followcopies',
785 default=True,
782 default=True,
786 )
783 )
787 coreconfigitem('merge', 'on-failure',
784 coreconfigitem('merge', 'on-failure',
788 default='continue',
785 default='continue',
789 )
786 )
790 coreconfigitem('merge', 'preferancestor',
787 coreconfigitem('merge', 'preferancestor',
791 default=lambda: ['*'],
788 default=lambda: ['*'],
792 )
789 )
793 coreconfigitem('merge', 'strict-capability-check',
790 coreconfigitem('merge', 'strict-capability-check',
794 default=False,
791 default=False,
795 )
792 )
796 coreconfigitem('merge-tools', '.*',
793 coreconfigitem('merge-tools', '.*',
797 default=None,
794 default=None,
798 generic=True,
795 generic=True,
799 )
796 )
800 coreconfigitem('merge-tools', br'.*\.args$',
797 coreconfigitem('merge-tools', br'.*\.args$',
801 default="$local $base $other",
798 default="$local $base $other",
802 generic=True,
799 generic=True,
803 priority=-1,
800 priority=-1,
804 )
801 )
805 coreconfigitem('merge-tools', br'.*\.binary$',
802 coreconfigitem('merge-tools', br'.*\.binary$',
806 default=False,
803 default=False,
807 generic=True,
804 generic=True,
808 priority=-1,
805 priority=-1,
809 )
806 )
810 coreconfigitem('merge-tools', br'.*\.check$',
807 coreconfigitem('merge-tools', br'.*\.check$',
811 default=list,
808 default=list,
812 generic=True,
809 generic=True,
813 priority=-1,
810 priority=-1,
814 )
811 )
815 coreconfigitem('merge-tools', br'.*\.checkchanged$',
812 coreconfigitem('merge-tools', br'.*\.checkchanged$',
816 default=False,
813 default=False,
817 generic=True,
814 generic=True,
818 priority=-1,
815 priority=-1,
819 )
816 )
820 coreconfigitem('merge-tools', br'.*\.executable$',
817 coreconfigitem('merge-tools', br'.*\.executable$',
821 default=dynamicdefault,
818 default=dynamicdefault,
822 generic=True,
819 generic=True,
823 priority=-1,
820 priority=-1,
824 )
821 )
825 coreconfigitem('merge-tools', br'.*\.fixeol$',
822 coreconfigitem('merge-tools', br'.*\.fixeol$',
826 default=False,
823 default=False,
827 generic=True,
824 generic=True,
828 priority=-1,
825 priority=-1,
829 )
826 )
830 coreconfigitem('merge-tools', br'.*\.gui$',
827 coreconfigitem('merge-tools', br'.*\.gui$',
831 default=False,
828 default=False,
832 generic=True,
829 generic=True,
833 priority=-1,
830 priority=-1,
834 )
831 )
835 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
832 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
836 default='basic',
833 default='basic',
837 generic=True,
834 generic=True,
838 priority=-1,
835 priority=-1,
839 )
836 )
840 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
837 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
841 default=dynamicdefault, # take from ui.mergemarkertemplate
838 default=dynamicdefault, # take from ui.mergemarkertemplate
842 generic=True,
839 generic=True,
843 priority=-1,
840 priority=-1,
844 )
841 )
845 coreconfigitem('merge-tools', br'.*\.priority$',
842 coreconfigitem('merge-tools', br'.*\.priority$',
846 default=0,
843 default=0,
847 generic=True,
844 generic=True,
848 priority=-1,
845 priority=-1,
849 )
846 )
850 coreconfigitem('merge-tools', br'.*\.premerge$',
847 coreconfigitem('merge-tools', br'.*\.premerge$',
851 default=dynamicdefault,
848 default=dynamicdefault,
852 generic=True,
849 generic=True,
853 priority=-1,
850 priority=-1,
854 )
851 )
855 coreconfigitem('merge-tools', br'.*\.symlink$',
852 coreconfigitem('merge-tools', br'.*\.symlink$',
856 default=False,
853 default=False,
857 generic=True,
854 generic=True,
858 priority=-1,
855 priority=-1,
859 )
856 )
860 coreconfigitem('pager', 'attend-.*',
857 coreconfigitem('pager', 'attend-.*',
861 default=dynamicdefault,
858 default=dynamicdefault,
862 generic=True,
859 generic=True,
863 )
860 )
864 coreconfigitem('pager', 'ignore',
861 coreconfigitem('pager', 'ignore',
865 default=list,
862 default=list,
866 )
863 )
867 coreconfigitem('pager', 'pager',
864 coreconfigitem('pager', 'pager',
868 default=dynamicdefault,
865 default=dynamicdefault,
869 )
866 )
870 coreconfigitem('patch', 'eol',
867 coreconfigitem('patch', 'eol',
871 default='strict',
868 default='strict',
872 )
869 )
873 coreconfigitem('patch', 'fuzz',
870 coreconfigitem('patch', 'fuzz',
874 default=2,
871 default=2,
875 )
872 )
876 coreconfigitem('paths', 'default',
873 coreconfigitem('paths', 'default',
877 default=None,
874 default=None,
878 )
875 )
879 coreconfigitem('paths', 'default-push',
876 coreconfigitem('paths', 'default-push',
880 default=None,
877 default=None,
881 )
878 )
882 coreconfigitem('paths', '.*',
879 coreconfigitem('paths', '.*',
883 default=None,
880 default=None,
884 generic=True,
881 generic=True,
885 )
882 )
886 coreconfigitem('phases', 'checksubrepos',
883 coreconfigitem('phases', 'checksubrepos',
887 default='follow',
884 default='follow',
888 )
885 )
889 coreconfigitem('phases', 'new-commit',
886 coreconfigitem('phases', 'new-commit',
890 default='draft',
887 default='draft',
891 )
888 )
892 coreconfigitem('phases', 'publish',
889 coreconfigitem('phases', 'publish',
893 default=True,
890 default=True,
894 )
891 )
895 coreconfigitem('profiling', 'enabled',
892 coreconfigitem('profiling', 'enabled',
896 default=False,
893 default=False,
897 )
894 )
898 coreconfigitem('profiling', 'format',
895 coreconfigitem('profiling', 'format',
899 default='text',
896 default='text',
900 )
897 )
901 coreconfigitem('profiling', 'freq',
898 coreconfigitem('profiling', 'freq',
902 default=1000,
899 default=1000,
903 )
900 )
904 coreconfigitem('profiling', 'limit',
901 coreconfigitem('profiling', 'limit',
905 default=30,
902 default=30,
906 )
903 )
907 coreconfigitem('profiling', 'nested',
904 coreconfigitem('profiling', 'nested',
908 default=0,
905 default=0,
909 )
906 )
910 coreconfigitem('profiling', 'output',
907 coreconfigitem('profiling', 'output',
911 default=None,
908 default=None,
912 )
909 )
913 coreconfigitem('profiling', 'showmax',
910 coreconfigitem('profiling', 'showmax',
914 default=0.999,
911 default=0.999,
915 )
912 )
916 coreconfigitem('profiling', 'showmin',
913 coreconfigitem('profiling', 'showmin',
917 default=dynamicdefault,
914 default=dynamicdefault,
918 )
915 )
919 coreconfigitem('profiling', 'sort',
916 coreconfigitem('profiling', 'sort',
920 default='inlinetime',
917 default='inlinetime',
921 )
918 )
922 coreconfigitem('profiling', 'statformat',
919 coreconfigitem('profiling', 'statformat',
923 default='hotpath',
920 default='hotpath',
924 )
921 )
925 coreconfigitem('profiling', 'time-track',
922 coreconfigitem('profiling', 'time-track',
926 default='cpu',
923 default='cpu',
927 )
924 )
928 coreconfigitem('profiling', 'type',
925 coreconfigitem('profiling', 'type',
929 default='stat',
926 default='stat',
930 )
927 )
931 coreconfigitem('progress', 'assume-tty',
928 coreconfigitem('progress', 'assume-tty',
932 default=False,
929 default=False,
933 )
930 )
934 coreconfigitem('progress', 'changedelay',
931 coreconfigitem('progress', 'changedelay',
935 default=1,
932 default=1,
936 )
933 )
937 coreconfigitem('progress', 'clear-complete',
934 coreconfigitem('progress', 'clear-complete',
938 default=True,
935 default=True,
939 )
936 )
940 coreconfigitem('progress', 'debug',
937 coreconfigitem('progress', 'debug',
941 default=False,
938 default=False,
942 )
939 )
943 coreconfigitem('progress', 'delay',
940 coreconfigitem('progress', 'delay',
944 default=3,
941 default=3,
945 )
942 )
946 coreconfigitem('progress', 'disable',
943 coreconfigitem('progress', 'disable',
947 default=False,
944 default=False,
948 )
945 )
949 coreconfigitem('progress', 'estimateinterval',
946 coreconfigitem('progress', 'estimateinterval',
950 default=60.0,
947 default=60.0,
951 )
948 )
952 coreconfigitem('progress', 'format',
949 coreconfigitem('progress', 'format',
953 default=lambda: ['topic', 'bar', 'number', 'estimate'],
950 default=lambda: ['topic', 'bar', 'number', 'estimate'],
954 )
951 )
955 coreconfigitem('progress', 'refresh',
952 coreconfigitem('progress', 'refresh',
956 default=0.1,
953 default=0.1,
957 )
954 )
958 coreconfigitem('progress', 'width',
955 coreconfigitem('progress', 'width',
959 default=dynamicdefault,
956 default=dynamicdefault,
960 )
957 )
961 coreconfigitem('push', 'pushvars.server',
958 coreconfigitem('push', 'pushvars.server',
962 default=False,
959 default=False,
963 )
960 )
964 coreconfigitem('storage', 'new-repo-backend',
961 coreconfigitem('storage', 'new-repo-backend',
965 default='revlogv1',
962 default='revlogv1',
966 )
963 )
967 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
964 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
968 default=True,
965 default=True,
969 alias=[('format', 'aggressivemergedeltas')],
966 alias=[('format', 'aggressivemergedeltas')],
970 )
967 )
971 coreconfigitem('server', 'bookmarks-pushkey-compat',
968 coreconfigitem('server', 'bookmarks-pushkey-compat',
972 default=True,
969 default=True,
973 )
970 )
974 coreconfigitem('server', 'bundle1',
971 coreconfigitem('server', 'bundle1',
975 default=True,
972 default=True,
976 )
973 )
977 coreconfigitem('server', 'bundle1gd',
974 coreconfigitem('server', 'bundle1gd',
978 default=None,
975 default=None,
979 )
976 )
980 coreconfigitem('server', 'bundle1.pull',
977 coreconfigitem('server', 'bundle1.pull',
981 default=None,
978 default=None,
982 )
979 )
983 coreconfigitem('server', 'bundle1gd.pull',
980 coreconfigitem('server', 'bundle1gd.pull',
984 default=None,
981 default=None,
985 )
982 )
986 coreconfigitem('server', 'bundle1.push',
983 coreconfigitem('server', 'bundle1.push',
987 default=None,
984 default=None,
988 )
985 )
989 coreconfigitem('server', 'bundle1gd.push',
986 coreconfigitem('server', 'bundle1gd.push',
990 default=None,
987 default=None,
991 )
988 )
992 coreconfigitem('server', 'bundle2.stream',
989 coreconfigitem('server', 'bundle2.stream',
993 default=True,
990 default=True,
994 alias=[('experimental', 'bundle2.stream')]
991 alias=[('experimental', 'bundle2.stream')]
995 )
992 )
996 coreconfigitem('server', 'compressionengines',
993 coreconfigitem('server', 'compressionengines',
997 default=list,
994 default=list,
998 )
995 )
999 coreconfigitem('server', 'concurrent-push-mode',
996 coreconfigitem('server', 'concurrent-push-mode',
1000 default='strict',
997 default='strict',
1001 )
998 )
1002 coreconfigitem('server', 'disablefullbundle',
999 coreconfigitem('server', 'disablefullbundle',
1003 default=False,
1000 default=False,
1004 )
1001 )
1005 coreconfigitem('server', 'maxhttpheaderlen',
1002 coreconfigitem('server', 'maxhttpheaderlen',
1006 default=1024,
1003 default=1024,
1007 )
1004 )
1008 coreconfigitem('server', 'pullbundle',
1005 coreconfigitem('server', 'pullbundle',
1009 default=False,
1006 default=False,
1010 )
1007 )
1011 coreconfigitem('server', 'preferuncompressed',
1008 coreconfigitem('server', 'preferuncompressed',
1012 default=False,
1009 default=False,
1013 )
1010 )
1014 coreconfigitem('server', 'streamunbundle',
1011 coreconfigitem('server', 'streamunbundle',
1015 default=False,
1012 default=False,
1016 )
1013 )
1017 coreconfigitem('server', 'uncompressed',
1014 coreconfigitem('server', 'uncompressed',
1018 default=True,
1015 default=True,
1019 )
1016 )
1020 coreconfigitem('server', 'uncompressedallowsecret',
1017 coreconfigitem('server', 'uncompressedallowsecret',
1021 default=False,
1018 default=False,
1022 )
1019 )
1023 coreconfigitem('server', 'validate',
1020 coreconfigitem('server', 'validate',
1024 default=False,
1021 default=False,
1025 )
1022 )
1026 coreconfigitem('server', 'zliblevel',
1023 coreconfigitem('server', 'zliblevel',
1027 default=-1,
1024 default=-1,
1028 )
1025 )
1029 coreconfigitem('server', 'zstdlevel',
1026 coreconfigitem('server', 'zstdlevel',
1030 default=3,
1027 default=3,
1031 )
1028 )
1032 coreconfigitem('share', 'pool',
1029 coreconfigitem('share', 'pool',
1033 default=None,
1030 default=None,
1034 )
1031 )
1035 coreconfigitem('share', 'poolnaming',
1032 coreconfigitem('share', 'poolnaming',
1036 default='identity',
1033 default='identity',
1037 )
1034 )
1038 coreconfigitem('smtp', 'host',
1035 coreconfigitem('smtp', 'host',
1039 default=None,
1036 default=None,
1040 )
1037 )
1041 coreconfigitem('smtp', 'local_hostname',
1038 coreconfigitem('smtp', 'local_hostname',
1042 default=None,
1039 default=None,
1043 )
1040 )
1044 coreconfigitem('smtp', 'password',
1041 coreconfigitem('smtp', 'password',
1045 default=None,
1042 default=None,
1046 )
1043 )
1047 coreconfigitem('smtp', 'port',
1044 coreconfigitem('smtp', 'port',
1048 default=dynamicdefault,
1045 default=dynamicdefault,
1049 )
1046 )
1050 coreconfigitem('smtp', 'tls',
1047 coreconfigitem('smtp', 'tls',
1051 default='none',
1048 default='none',
1052 )
1049 )
1053 coreconfigitem('smtp', 'username',
1050 coreconfigitem('smtp', 'username',
1054 default=None,
1051 default=None,
1055 )
1052 )
1056 coreconfigitem('sparse', 'missingwarning',
1053 coreconfigitem('sparse', 'missingwarning',
1057 default=True,
1054 default=True,
1058 )
1055 )
1059 coreconfigitem('subrepos', 'allowed',
1056 coreconfigitem('subrepos', 'allowed',
1060 default=dynamicdefault, # to make backporting simpler
1057 default=dynamicdefault, # to make backporting simpler
1061 )
1058 )
1062 coreconfigitem('subrepos', 'hg:allowed',
1059 coreconfigitem('subrepos', 'hg:allowed',
1063 default=dynamicdefault,
1060 default=dynamicdefault,
1064 )
1061 )
1065 coreconfigitem('subrepos', 'git:allowed',
1062 coreconfigitem('subrepos', 'git:allowed',
1066 default=dynamicdefault,
1063 default=dynamicdefault,
1067 )
1064 )
1068 coreconfigitem('subrepos', 'svn:allowed',
1065 coreconfigitem('subrepos', 'svn:allowed',
1069 default=dynamicdefault,
1066 default=dynamicdefault,
1070 )
1067 )
1071 coreconfigitem('templates', '.*',
1068 coreconfigitem('templates', '.*',
1072 default=None,
1069 default=None,
1073 generic=True,
1070 generic=True,
1074 )
1071 )
1075 coreconfigitem('trusted', 'groups',
1072 coreconfigitem('trusted', 'groups',
1076 default=list,
1073 default=list,
1077 )
1074 )
1078 coreconfigitem('trusted', 'users',
1075 coreconfigitem('trusted', 'users',
1079 default=list,
1076 default=list,
1080 )
1077 )
1081 coreconfigitem('ui', '_usedassubrepo',
1078 coreconfigitem('ui', '_usedassubrepo',
1082 default=False,
1079 default=False,
1083 )
1080 )
1084 coreconfigitem('ui', 'allowemptycommit',
1081 coreconfigitem('ui', 'allowemptycommit',
1085 default=False,
1082 default=False,
1086 )
1083 )
1087 coreconfigitem('ui', 'archivemeta',
1084 coreconfigitem('ui', 'archivemeta',
1088 default=True,
1085 default=True,
1089 )
1086 )
1090 coreconfigitem('ui', 'askusername',
1087 coreconfigitem('ui', 'askusername',
1091 default=False,
1088 default=False,
1092 )
1089 )
1093 coreconfigitem('ui', 'clonebundlefallback',
1090 coreconfigitem('ui', 'clonebundlefallback',
1094 default=False,
1091 default=False,
1095 )
1092 )
1096 coreconfigitem('ui', 'clonebundleprefers',
1093 coreconfigitem('ui', 'clonebundleprefers',
1097 default=list,
1094 default=list,
1098 )
1095 )
1099 coreconfigitem('ui', 'clonebundles',
1096 coreconfigitem('ui', 'clonebundles',
1100 default=True,
1097 default=True,
1101 )
1098 )
1102 coreconfigitem('ui', 'color',
1099 coreconfigitem('ui', 'color',
1103 default='auto',
1100 default='auto',
1104 )
1101 )
1105 coreconfigitem('ui', 'commitsubrepos',
1102 coreconfigitem('ui', 'commitsubrepos',
1106 default=False,
1103 default=False,
1107 )
1104 )
1108 coreconfigitem('ui', 'debug',
1105 coreconfigitem('ui', 'debug',
1109 default=False,
1106 default=False,
1110 )
1107 )
1111 coreconfigitem('ui', 'debugger',
1108 coreconfigitem('ui', 'debugger',
1112 default=None,
1109 default=None,
1113 )
1110 )
1114 coreconfigitem('ui', 'editor',
1111 coreconfigitem('ui', 'editor',
1115 default=dynamicdefault,
1112 default=dynamicdefault,
1116 )
1113 )
1117 coreconfigitem('ui', 'fallbackencoding',
1114 coreconfigitem('ui', 'fallbackencoding',
1118 default=None,
1115 default=None,
1119 )
1116 )
1120 coreconfigitem('ui', 'forcecwd',
1117 coreconfigitem('ui', 'forcecwd',
1121 default=None,
1118 default=None,
1122 )
1119 )
1123 coreconfigitem('ui', 'forcemerge',
1120 coreconfigitem('ui', 'forcemerge',
1124 default=None,
1121 default=None,
1125 )
1122 )
1126 coreconfigitem('ui', 'formatdebug',
1123 coreconfigitem('ui', 'formatdebug',
1127 default=False,
1124 default=False,
1128 )
1125 )
1129 coreconfigitem('ui', 'formatjson',
1126 coreconfigitem('ui', 'formatjson',
1130 default=False,
1127 default=False,
1131 )
1128 )
1132 coreconfigitem('ui', 'formatted',
1129 coreconfigitem('ui', 'formatted',
1133 default=None,
1130 default=None,
1134 )
1131 )
1135 coreconfigitem('ui', 'graphnodetemplate',
1132 coreconfigitem('ui', 'graphnodetemplate',
1136 default=None,
1133 default=None,
1137 )
1134 )
1138 coreconfigitem('ui', 'history-editing-backup',
1135 coreconfigitem('ui', 'history-editing-backup',
1139 default=True,
1136 default=True,
1140 )
1137 )
1141 coreconfigitem('ui', 'interactive',
1138 coreconfigitem('ui', 'interactive',
1142 default=None,
1139 default=None,
1143 )
1140 )
1144 coreconfigitem('ui', 'interface',
1141 coreconfigitem('ui', 'interface',
1145 default=None,
1142 default=None,
1146 )
1143 )
1147 coreconfigitem('ui', 'interface.chunkselector',
1144 coreconfigitem('ui', 'interface.chunkselector',
1148 default=None,
1145 default=None,
1149 )
1146 )
1150 coreconfigitem('ui', 'large-file-limit',
1147 coreconfigitem('ui', 'large-file-limit',
1151 default=10000000,
1148 default=10000000,
1152 )
1149 )
1153 coreconfigitem('ui', 'logblockedtimes',
1150 coreconfigitem('ui', 'logblockedtimes',
1154 default=False,
1151 default=False,
1155 )
1152 )
1156 coreconfigitem('ui', 'logtemplate',
1153 coreconfigitem('ui', 'logtemplate',
1157 default=None,
1154 default=None,
1158 )
1155 )
1159 coreconfigitem('ui', 'merge',
1156 coreconfigitem('ui', 'merge',
1160 default=None,
1157 default=None,
1161 )
1158 )
1162 coreconfigitem('ui', 'mergemarkers',
1159 coreconfigitem('ui', 'mergemarkers',
1163 default='basic',
1160 default='basic',
1164 )
1161 )
1165 coreconfigitem('ui', 'mergemarkertemplate',
1162 coreconfigitem('ui', 'mergemarkertemplate',
1166 default=('{node|short} '
1163 default=('{node|short} '
1167 '{ifeq(tags, "tip", "", '
1164 '{ifeq(tags, "tip", "", '
1168 'ifeq(tags, "", "", "{tags} "))}'
1165 'ifeq(tags, "", "", "{tags} "))}'
1169 '{if(bookmarks, "{bookmarks} ")}'
1166 '{if(bookmarks, "{bookmarks} ")}'
1170 '{ifeq(branch, "default", "", "{branch} ")}'
1167 '{ifeq(branch, "default", "", "{branch} ")}'
1171 '- {author|user}: {desc|firstline}')
1168 '- {author|user}: {desc|firstline}')
1172 )
1169 )
1173 coreconfigitem('ui', 'nontty',
1170 coreconfigitem('ui', 'nontty',
1174 default=False,
1171 default=False,
1175 )
1172 )
1176 coreconfigitem('ui', 'origbackuppath',
1173 coreconfigitem('ui', 'origbackuppath',
1177 default=None,
1174 default=None,
1178 )
1175 )
1179 coreconfigitem('ui', 'paginate',
1176 coreconfigitem('ui', 'paginate',
1180 default=True,
1177 default=True,
1181 )
1178 )
1182 coreconfigitem('ui', 'patch',
1179 coreconfigitem('ui', 'patch',
1183 default=None,
1180 default=None,
1184 )
1181 )
1185 coreconfigitem('ui', 'portablefilenames',
1182 coreconfigitem('ui', 'portablefilenames',
1186 default='warn',
1183 default='warn',
1187 )
1184 )
1188 coreconfigitem('ui', 'promptecho',
1185 coreconfigitem('ui', 'promptecho',
1189 default=False,
1186 default=False,
1190 )
1187 )
1191 coreconfigitem('ui', 'quiet',
1188 coreconfigitem('ui', 'quiet',
1192 default=False,
1189 default=False,
1193 )
1190 )
1194 coreconfigitem('ui', 'quietbookmarkmove',
1191 coreconfigitem('ui', 'quietbookmarkmove',
1195 default=False,
1192 default=False,
1196 )
1193 )
1197 coreconfigitem('ui', 'remotecmd',
1194 coreconfigitem('ui', 'remotecmd',
1198 default='hg',
1195 default='hg',
1199 )
1196 )
1200 coreconfigitem('ui', 'report_untrusted',
1197 coreconfigitem('ui', 'report_untrusted',
1201 default=True,
1198 default=True,
1202 )
1199 )
1203 coreconfigitem('ui', 'rollback',
1200 coreconfigitem('ui', 'rollback',
1204 default=True,
1201 default=True,
1205 )
1202 )
1206 coreconfigitem('ui', 'signal-safe-lock',
1203 coreconfigitem('ui', 'signal-safe-lock',
1207 default=True,
1204 default=True,
1208 )
1205 )
1209 coreconfigitem('ui', 'slash',
1206 coreconfigitem('ui', 'slash',
1210 default=False,
1207 default=False,
1211 )
1208 )
1212 coreconfigitem('ui', 'ssh',
1209 coreconfigitem('ui', 'ssh',
1213 default='ssh',
1210 default='ssh',
1214 )
1211 )
1215 coreconfigitem('ui', 'ssherrorhint',
1212 coreconfigitem('ui', 'ssherrorhint',
1216 default=None,
1213 default=None,
1217 )
1214 )
1218 coreconfigitem('ui', 'statuscopies',
1215 coreconfigitem('ui', 'statuscopies',
1219 default=False,
1216 default=False,
1220 )
1217 )
1221 coreconfigitem('ui', 'strict',
1218 coreconfigitem('ui', 'strict',
1222 default=False,
1219 default=False,
1223 )
1220 )
1224 coreconfigitem('ui', 'style',
1221 coreconfigitem('ui', 'style',
1225 default='',
1222 default='',
1226 )
1223 )
1227 coreconfigitem('ui', 'supportcontact',
1224 coreconfigitem('ui', 'supportcontact',
1228 default=None,
1225 default=None,
1229 )
1226 )
1230 coreconfigitem('ui', 'textwidth',
1227 coreconfigitem('ui', 'textwidth',
1231 default=78,
1228 default=78,
1232 )
1229 )
1233 coreconfigitem('ui', 'timeout',
1230 coreconfigitem('ui', 'timeout',
1234 default='600',
1231 default='600',
1235 )
1232 )
1236 coreconfigitem('ui', 'timeout.warn',
1233 coreconfigitem('ui', 'timeout.warn',
1237 default=0,
1234 default=0,
1238 )
1235 )
1239 coreconfigitem('ui', 'traceback',
1236 coreconfigitem('ui', 'traceback',
1240 default=False,
1237 default=False,
1241 )
1238 )
1242 coreconfigitem('ui', 'tweakdefaults',
1239 coreconfigitem('ui', 'tweakdefaults',
1243 default=False,
1240 default=False,
1244 )
1241 )
1245 coreconfigitem('ui', 'username',
1242 coreconfigitem('ui', 'username',
1246 alias=[('ui', 'user')]
1243 alias=[('ui', 'user')]
1247 )
1244 )
1248 coreconfigitem('ui', 'verbose',
1245 coreconfigitem('ui', 'verbose',
1249 default=False,
1246 default=False,
1250 )
1247 )
1251 coreconfigitem('verify', 'skipflags',
1248 coreconfigitem('verify', 'skipflags',
1252 default=None,
1249 default=None,
1253 )
1250 )
1254 coreconfigitem('web', 'allowbz2',
1251 coreconfigitem('web', 'allowbz2',
1255 default=False,
1252 default=False,
1256 )
1253 )
1257 coreconfigitem('web', 'allowgz',
1254 coreconfigitem('web', 'allowgz',
1258 default=False,
1255 default=False,
1259 )
1256 )
1260 coreconfigitem('web', 'allow-pull',
1257 coreconfigitem('web', 'allow-pull',
1261 alias=[('web', 'allowpull')],
1258 alias=[('web', 'allowpull')],
1262 default=True,
1259 default=True,
1263 )
1260 )
1264 coreconfigitem('web', 'allow-push',
1261 coreconfigitem('web', 'allow-push',
1265 alias=[('web', 'allow_push')],
1262 alias=[('web', 'allow_push')],
1266 default=list,
1263 default=list,
1267 )
1264 )
1268 coreconfigitem('web', 'allowzip',
1265 coreconfigitem('web', 'allowzip',
1269 default=False,
1266 default=False,
1270 )
1267 )
1271 coreconfigitem('web', 'archivesubrepos',
1268 coreconfigitem('web', 'archivesubrepos',
1272 default=False,
1269 default=False,
1273 )
1270 )
1274 coreconfigitem('web', 'cache',
1271 coreconfigitem('web', 'cache',
1275 default=True,
1272 default=True,
1276 )
1273 )
1277 coreconfigitem('web', 'contact',
1274 coreconfigitem('web', 'contact',
1278 default=None,
1275 default=None,
1279 )
1276 )
1280 coreconfigitem('web', 'deny_push',
1277 coreconfigitem('web', 'deny_push',
1281 default=list,
1278 default=list,
1282 )
1279 )
1283 coreconfigitem('web', 'guessmime',
1280 coreconfigitem('web', 'guessmime',
1284 default=False,
1281 default=False,
1285 )
1282 )
1286 coreconfigitem('web', 'hidden',
1283 coreconfigitem('web', 'hidden',
1287 default=False,
1284 default=False,
1288 )
1285 )
1289 coreconfigitem('web', 'labels',
1286 coreconfigitem('web', 'labels',
1290 default=list,
1287 default=list,
1291 )
1288 )
1292 coreconfigitem('web', 'logoimg',
1289 coreconfigitem('web', 'logoimg',
1293 default='hglogo.png',
1290 default='hglogo.png',
1294 )
1291 )
1295 coreconfigitem('web', 'logourl',
1292 coreconfigitem('web', 'logourl',
1296 default='https://mercurial-scm.org/',
1293 default='https://mercurial-scm.org/',
1297 )
1294 )
1298 coreconfigitem('web', 'accesslog',
1295 coreconfigitem('web', 'accesslog',
1299 default='-',
1296 default='-',
1300 )
1297 )
1301 coreconfigitem('web', 'address',
1298 coreconfigitem('web', 'address',
1302 default='',
1299 default='',
1303 )
1300 )
1304 coreconfigitem('web', 'allow-archive',
1301 coreconfigitem('web', 'allow-archive',
1305 alias=[('web', 'allow_archive')],
1302 alias=[('web', 'allow_archive')],
1306 default=list,
1303 default=list,
1307 )
1304 )
1308 coreconfigitem('web', 'allow_read',
1305 coreconfigitem('web', 'allow_read',
1309 default=list,
1306 default=list,
1310 )
1307 )
1311 coreconfigitem('web', 'baseurl',
1308 coreconfigitem('web', 'baseurl',
1312 default=None,
1309 default=None,
1313 )
1310 )
1314 coreconfigitem('web', 'cacerts',
1311 coreconfigitem('web', 'cacerts',
1315 default=None,
1312 default=None,
1316 )
1313 )
1317 coreconfigitem('web', 'certificate',
1314 coreconfigitem('web', 'certificate',
1318 default=None,
1315 default=None,
1319 )
1316 )
1320 coreconfigitem('web', 'collapse',
1317 coreconfigitem('web', 'collapse',
1321 default=False,
1318 default=False,
1322 )
1319 )
1323 coreconfigitem('web', 'csp',
1320 coreconfigitem('web', 'csp',
1324 default=None,
1321 default=None,
1325 )
1322 )
1326 coreconfigitem('web', 'deny_read',
1323 coreconfigitem('web', 'deny_read',
1327 default=list,
1324 default=list,
1328 )
1325 )
1329 coreconfigitem('web', 'descend',
1326 coreconfigitem('web', 'descend',
1330 default=True,
1327 default=True,
1331 )
1328 )
1332 coreconfigitem('web', 'description',
1329 coreconfigitem('web', 'description',
1333 default="",
1330 default="",
1334 )
1331 )
1335 coreconfigitem('web', 'encoding',
1332 coreconfigitem('web', 'encoding',
1336 default=lambda: encoding.encoding,
1333 default=lambda: encoding.encoding,
1337 )
1334 )
1338 coreconfigitem('web', 'errorlog',
1335 coreconfigitem('web', 'errorlog',
1339 default='-',
1336 default='-',
1340 )
1337 )
1341 coreconfigitem('web', 'ipv6',
1338 coreconfigitem('web', 'ipv6',
1342 default=False,
1339 default=False,
1343 )
1340 )
1344 coreconfigitem('web', 'maxchanges',
1341 coreconfigitem('web', 'maxchanges',
1345 default=10,
1342 default=10,
1346 )
1343 )
1347 coreconfigitem('web', 'maxfiles',
1344 coreconfigitem('web', 'maxfiles',
1348 default=10,
1345 default=10,
1349 )
1346 )
1350 coreconfigitem('web', 'maxshortchanges',
1347 coreconfigitem('web', 'maxshortchanges',
1351 default=60,
1348 default=60,
1352 )
1349 )
1353 coreconfigitem('web', 'motd',
1350 coreconfigitem('web', 'motd',
1354 default='',
1351 default='',
1355 )
1352 )
1356 coreconfigitem('web', 'name',
1353 coreconfigitem('web', 'name',
1357 default=dynamicdefault,
1354 default=dynamicdefault,
1358 )
1355 )
1359 coreconfigitem('web', 'port',
1356 coreconfigitem('web', 'port',
1360 default=8000,
1357 default=8000,
1361 )
1358 )
1362 coreconfigitem('web', 'prefix',
1359 coreconfigitem('web', 'prefix',
1363 default='',
1360 default='',
1364 )
1361 )
1365 coreconfigitem('web', 'push_ssl',
1362 coreconfigitem('web', 'push_ssl',
1366 default=True,
1363 default=True,
1367 )
1364 )
1368 coreconfigitem('web', 'refreshinterval',
1365 coreconfigitem('web', 'refreshinterval',
1369 default=20,
1366 default=20,
1370 )
1367 )
1371 coreconfigitem('web', 'server-header',
1368 coreconfigitem('web', 'server-header',
1372 default=None,
1369 default=None,
1373 )
1370 )
1374 coreconfigitem('web', 'static',
1371 coreconfigitem('web', 'static',
1375 default=None,
1372 default=None,
1376 )
1373 )
1377 coreconfigitem('web', 'staticurl',
1374 coreconfigitem('web', 'staticurl',
1378 default=None,
1375 default=None,
1379 )
1376 )
1380 coreconfigitem('web', 'stripes',
1377 coreconfigitem('web', 'stripes',
1381 default=1,
1378 default=1,
1382 )
1379 )
1383 coreconfigitem('web', 'style',
1380 coreconfigitem('web', 'style',
1384 default='paper',
1381 default='paper',
1385 )
1382 )
1386 coreconfigitem('web', 'templates',
1383 coreconfigitem('web', 'templates',
1387 default=None,
1384 default=None,
1388 )
1385 )
1389 coreconfigitem('web', 'view',
1386 coreconfigitem('web', 'view',
1390 default='served',
1387 default='served',
1391 )
1388 )
1392 coreconfigitem('worker', 'backgroundclose',
1389 coreconfigitem('worker', 'backgroundclose',
1393 default=dynamicdefault,
1390 default=dynamicdefault,
1394 )
1391 )
1395 # Windows defaults to a limit of 512 open files. A buffer of 128
1392 # Windows defaults to a limit of 512 open files. A buffer of 128
1396 # should give us enough headway.
1393 # should give us enough headway.
1397 coreconfigitem('worker', 'backgroundclosemaxqueue',
1394 coreconfigitem('worker', 'backgroundclosemaxqueue',
1398 default=384,
1395 default=384,
1399 )
1396 )
1400 coreconfigitem('worker', 'backgroundcloseminfilecount',
1397 coreconfigitem('worker', 'backgroundcloseminfilecount',
1401 default=2048,
1398 default=2048,
1402 )
1399 )
1403 coreconfigitem('worker', 'backgroundclosethreadcount',
1400 coreconfigitem('worker', 'backgroundclosethreadcount',
1404 default=4,
1401 default=4,
1405 )
1402 )
1406 coreconfigitem('worker', 'enabled',
1403 coreconfigitem('worker', 'enabled',
1407 default=True,
1404 default=True,
1408 )
1405 )
1409 coreconfigitem('worker', 'numcpus',
1406 coreconfigitem('worker', 'numcpus',
1410 default=None,
1407 default=None,
1411 )
1408 )
1412
1409
1413 # Rebase related configuration moved to core because other extension are doing
1410 # Rebase related configuration moved to core because other extension are doing
1414 # strange things. For example, shelve import the extensions to reuse some bit
1411 # strange things. For example, shelve import the extensions to reuse some bit
1415 # without formally loading it.
1412 # without formally loading it.
1416 coreconfigitem('commands', 'rebase.requiredest',
1413 coreconfigitem('commands', 'rebase.requiredest',
1417 default=False,
1414 default=False,
1418 )
1415 )
1419 coreconfigitem('experimental', 'rebaseskipobsolete',
1416 coreconfigitem('experimental', 'rebaseskipobsolete',
1420 default=True,
1417 default=True,
1421 )
1418 )
1422 coreconfigitem('rebase', 'singletransaction',
1419 coreconfigitem('rebase', 'singletransaction',
1423 default=False,
1420 default=False,
1424 )
1421 )
1425 coreconfigitem('rebase', 'experimental.inmemory',
1422 coreconfigitem('rebase', 'experimental.inmemory',
1426 default=False,
1423 default=False,
1427 )
1424 )
@@ -1,1784 +1,1804 b''
1 # stuff related specifically to patch manipulation / parsing
1 # stuff related specifically to patch manipulation / parsing
2 #
2 #
3 # Copyright 2008 Mark Edgington <edgimar@gmail.com>
3 # Copyright 2008 Mark Edgington <edgimar@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 #
7 #
8 # This code is based on the Mark Edgington's crecord extension.
8 # This code is based on the Mark Edgington's crecord extension.
9 # (Itself based on Bryan O'Sullivan's record extension.)
9 # (Itself based on Bryan O'Sullivan's record extension.)
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import locale
13 import locale
14 import os
14 import os
15 import re
15 import re
16 import signal
16 import signal
17
17
18 from .i18n import _
18 from .i18n import _
19 from . import (
19 from . import (
20 encoding,
20 encoding,
21 error,
21 error,
22 patch as patchmod,
22 patch as patchmod,
23 scmutil,
23 scmutil,
24 util,
24 util,
25 )
25 )
26 from .utils import (
26 from .utils import (
27 stringutil,
27 stringutil,
28 )
28 )
29 stringio = util.stringio
29 stringio = util.stringio
30
30
31 # This is required for ncurses to display non-ASCII characters in default user
31 # This is required for ncurses to display non-ASCII characters in default user
32 # locale encoding correctly. --immerrr
32 # locale encoding correctly. --immerrr
33 locale.setlocale(locale.LC_ALL, u'')
33 locale.setlocale(locale.LC_ALL, u'')
34
34
35 # patch comments based on the git one
35 # patch comments based on the git one
36 diffhelptext = _("""# To remove '-' lines, make them ' ' lines (context).
36 diffhelptext = _("""# To remove '-' lines, make them ' ' lines (context).
37 # To remove '+' lines, delete them.
37 # To remove '+' lines, delete them.
38 # Lines starting with # will be removed from the patch.
38 # Lines starting with # will be removed from the patch.
39 """)
39 """)
40
40
41 hunkhelptext = _("""#
41 hunkhelptext = _("""#
42 # If the patch applies cleanly, the edited hunk will immediately be
42 # If the patch applies cleanly, the edited hunk will immediately be
43 # added to the record list. If it does not apply cleanly, a rejects file
43 # added to the record list. If it does not apply cleanly, a rejects file
44 # will be generated. You can use that when you try again. If all lines
44 # will be generated. You can use that when you try again. If all lines
45 # of the hunk are removed, then the edit is aborted and the hunk is left
45 # of the hunk are removed, then the edit is aborted and the hunk is left
46 # unchanged.
46 # unchanged.
47 """)
47 """)
48
48
49 patchhelptext = _("""#
49 patchhelptext = _("""#
50 # If the patch applies cleanly, the edited patch will immediately
50 # If the patch applies cleanly, the edited patch will immediately
51 # be finalised. If it does not apply cleanly, rejects files will be
51 # be finalised. If it does not apply cleanly, rejects files will be
52 # generated. You can use those when you try again.
52 # generated. You can use those when you try again.
53 """)
53 """)
54
54
55 try:
55 try:
56 import curses
56 import curses
57 curses.error
57 curses.error
58 except ImportError:
58 except ImportError:
59 # I have no idea if wcurses works with crecord...
59 # I have no idea if wcurses works with crecord...
60 try:
60 try:
61 import wcurses as curses
61 import wcurses as curses
62 curses.error
62 curses.error
63 except ImportError:
63 except ImportError:
64 # wcurses is not shipped on Windows by default, or python is not
64 # wcurses is not shipped on Windows by default, or python is not
65 # compiled with curses
65 # compiled with curses
66 curses = False
66 curses = False
67
67
68 class fallbackerror(error.Abort):
68 class fallbackerror(error.Abort):
69 """Error that indicates the client should try to fallback to text mode."""
69 """Error that indicates the client should try to fallback to text mode."""
70 # Inherits from error.Abort so that existing behavior is preserved if the
70 # Inherits from error.Abort so that existing behavior is preserved if the
71 # calling code does not know how to fallback.
71 # calling code does not know how to fallback.
72
72
73 def checkcurses(ui):
73 def checkcurses(ui):
74 """Return True if the user wants to use curses
74 """Return True if the user wants to use curses
75
75
76 This method returns True if curses is found (and that python is built with
76 This method returns True if curses is found (and that python is built with
77 it) and that the user has the correct flag for the ui.
77 it) and that the user has the correct flag for the ui.
78 """
78 """
79 return curses and ui.interface("chunkselector") == "curses"
79 return curses and ui.interface("chunkselector") == "curses"
80
80
81 class patchnode(object):
81 class patchnode(object):
82 """abstract class for patch graph nodes
82 """abstract class for patch graph nodes
83 (i.e. patchroot, header, hunk, hunkline)
83 (i.e. patchroot, header, hunk, hunkline)
84 """
84 """
85
85
86 def firstchild(self):
86 def firstchild(self):
87 raise NotImplementedError("method must be implemented by subclass")
87 raise NotImplementedError("method must be implemented by subclass")
88
88
89 def lastchild(self):
89 def lastchild(self):
90 raise NotImplementedError("method must be implemented by subclass")
90 raise NotImplementedError("method must be implemented by subclass")
91
91
92 def allchildren(self):
92 def allchildren(self):
93 "Return a list of all of the direct children of this node"
93 "Return a list of all of the direct children of this node"
94 raise NotImplementedError("method must be implemented by subclass")
94 raise NotImplementedError("method must be implemented by subclass")
95
95
96 def nextsibling(self):
96 def nextsibling(self):
97 """
97 """
98 Return the closest next item of the same type where there are no items
98 Return the closest next item of the same type where there are no items
99 of different types between the current item and this closest item.
99 of different types between the current item and this closest item.
100 If no such item exists, return None.
100 If no such item exists, return None.
101 """
101 """
102 raise NotImplementedError("method must be implemented by subclass")
102 raise NotImplementedError("method must be implemented by subclass")
103
103
104 def prevsibling(self):
104 def prevsibling(self):
105 """
105 """
106 Return the closest previous item of the same type where there are no
106 Return the closest previous item of the same type where there are no
107 items of different types between the current item and this closest item.
107 items of different types between the current item and this closest item.
108 If no such item exists, return None.
108 If no such item exists, return None.
109 """
109 """
110 raise NotImplementedError("method must be implemented by subclass")
110 raise NotImplementedError("method must be implemented by subclass")
111
111
112 def parentitem(self):
112 def parentitem(self):
113 raise NotImplementedError("method must be implemented by subclass")
113 raise NotImplementedError("method must be implemented by subclass")
114
114
115 def nextitem(self, skipfolded=True):
115 def nextitem(self, skipfolded=True):
116 """
116 """
117 Try to return the next item closest to this item, regardless of item's
117 Try to return the next item closest to this item, regardless of item's
118 type (header, hunk, or hunkline).
118 type (header, hunk, or hunkline).
119
119
120 If skipfolded == True, and the current item is folded, then the child
120 If skipfolded == True, and the current item is folded, then the child
121 items that are hidden due to folding will be skipped when determining
121 items that are hidden due to folding will be skipped when determining
122 the next item.
122 the next item.
123
123
124 If it is not possible to get the next item, return None.
124 If it is not possible to get the next item, return None.
125 """
125 """
126 try:
126 try:
127 itemfolded = self.folded
127 itemfolded = self.folded
128 except AttributeError:
128 except AttributeError:
129 itemfolded = False
129 itemfolded = False
130 if skipfolded and itemfolded:
130 if skipfolded and itemfolded:
131 nextitem = self.nextsibling()
131 nextitem = self.nextsibling()
132 if nextitem is None:
132 if nextitem is None:
133 try:
133 try:
134 nextitem = self.parentitem().nextsibling()
134 nextitem = self.parentitem().nextsibling()
135 except AttributeError:
135 except AttributeError:
136 nextitem = None
136 nextitem = None
137 return nextitem
137 return nextitem
138 else:
138 else:
139 # try child
139 # try child
140 item = self.firstchild()
140 item = self.firstchild()
141 if item is not None:
141 if item is not None:
142 return item
142 return item
143
143
144 # else try next sibling
144 # else try next sibling
145 item = self.nextsibling()
145 item = self.nextsibling()
146 if item is not None:
146 if item is not None:
147 return item
147 return item
148
148
149 try:
149 try:
150 # else try parent's next sibling
150 # else try parent's next sibling
151 item = self.parentitem().nextsibling()
151 item = self.parentitem().nextsibling()
152 if item is not None:
152 if item is not None:
153 return item
153 return item
154
154
155 # else return grandparent's next sibling (or None)
155 # else return grandparent's next sibling (or None)
156 return self.parentitem().parentitem().nextsibling()
156 return self.parentitem().parentitem().nextsibling()
157
157
158 except AttributeError: # parent and/or grandparent was None
158 except AttributeError: # parent and/or grandparent was None
159 return None
159 return None
160
160
161 def previtem(self):
161 def previtem(self):
162 """
162 """
163 Try to return the previous item closest to this item, regardless of
163 Try to return the previous item closest to this item, regardless of
164 item's type (header, hunk, or hunkline).
164 item's type (header, hunk, or hunkline).
165
165
166 If it is not possible to get the previous item, return None.
166 If it is not possible to get the previous item, return None.
167 """
167 """
168 # try previous sibling's last child's last child,
168 # try previous sibling's last child's last child,
169 # else try previous sibling's last child, else try previous sibling
169 # else try previous sibling's last child, else try previous sibling
170 prevsibling = self.prevsibling()
170 prevsibling = self.prevsibling()
171 if prevsibling is not None:
171 if prevsibling is not None:
172 prevsiblinglastchild = prevsibling.lastchild()
172 prevsiblinglastchild = prevsibling.lastchild()
173 if ((prevsiblinglastchild is not None) and
173 if ((prevsiblinglastchild is not None) and
174 not prevsibling.folded):
174 not prevsibling.folded):
175 prevsiblinglclc = prevsiblinglastchild.lastchild()
175 prevsiblinglclc = prevsiblinglastchild.lastchild()
176 if ((prevsiblinglclc is not None) and
176 if ((prevsiblinglclc is not None) and
177 not prevsiblinglastchild.folded):
177 not prevsiblinglastchild.folded):
178 return prevsiblinglclc
178 return prevsiblinglclc
179 else:
179 else:
180 return prevsiblinglastchild
180 return prevsiblinglastchild
181 else:
181 else:
182 return prevsibling
182 return prevsibling
183
183
184 # try parent (or None)
184 # try parent (or None)
185 return self.parentitem()
185 return self.parentitem()
186
186
187 class patch(patchnode, list): # todo: rename patchroot
187 class patch(patchnode, list): # todo: rename patchroot
188 """
188 """
189 list of header objects representing the patch.
189 list of header objects representing the patch.
190 """
190 """
191 def __init__(self, headerlist):
191 def __init__(self, headerlist):
192 self.extend(headerlist)
192 self.extend(headerlist)
193 # add parent patch object reference to each header
193 # add parent patch object reference to each header
194 for header in self:
194 for header in self:
195 header.patch = self
195 header.patch = self
196
196
197 class uiheader(patchnode):
197 class uiheader(patchnode):
198 """patch header
198 """patch header
199
199
200 xxx shouldn't we move this to mercurial/patch.py ?
200 xxx shouldn't we move this to mercurial/patch.py ?
201 """
201 """
202
202
203 def __init__(self, header):
203 def __init__(self, header):
204 self.nonuiheader = header
204 self.nonuiheader = header
205 # flag to indicate whether to apply this chunk
205 # flag to indicate whether to apply this chunk
206 self.applied = True
206 self.applied = True
207 # flag which only affects the status display indicating if a node's
207 # flag which only affects the status display indicating if a node's
208 # children are partially applied (i.e. some applied, some not).
208 # children are partially applied (i.e. some applied, some not).
209 self.partial = False
209 self.partial = False
210
210
211 # flag to indicate whether to display as folded/unfolded to user
211 # flag to indicate whether to display as folded/unfolded to user
212 self.folded = True
212 self.folded = True
213
213
214 # list of all headers in patch
214 # list of all headers in patch
215 self.patch = None
215 self.patch = None
216
216
217 # flag is False if this header was ever unfolded from initial state
217 # flag is False if this header was ever unfolded from initial state
218 self.neverunfolded = True
218 self.neverunfolded = True
219 self.hunks = [uihunk(h, self) for h in self.hunks]
219 self.hunks = [uihunk(h, self) for h in self.hunks]
220
220
221 def prettystr(self):
221 def prettystr(self):
222 x = stringio()
222 x = stringio()
223 self.pretty(x)
223 self.pretty(x)
224 return x.getvalue()
224 return x.getvalue()
225
225
226 def nextsibling(self):
226 def nextsibling(self):
227 numheadersinpatch = len(self.patch)
227 numheadersinpatch = len(self.patch)
228 indexofthisheader = self.patch.index(self)
228 indexofthisheader = self.patch.index(self)
229
229
230 if indexofthisheader < numheadersinpatch - 1:
230 if indexofthisheader < numheadersinpatch - 1:
231 nextheader = self.patch[indexofthisheader + 1]
231 nextheader = self.patch[indexofthisheader + 1]
232 return nextheader
232 return nextheader
233 else:
233 else:
234 return None
234 return None
235
235
236 def prevsibling(self):
236 def prevsibling(self):
237 indexofthisheader = self.patch.index(self)
237 indexofthisheader = self.patch.index(self)
238 if indexofthisheader > 0:
238 if indexofthisheader > 0:
239 previousheader = self.patch[indexofthisheader - 1]
239 previousheader = self.patch[indexofthisheader - 1]
240 return previousheader
240 return previousheader
241 else:
241 else:
242 return None
242 return None
243
243
244 def parentitem(self):
244 def parentitem(self):
245 """
245 """
246 there is no 'real' parent item of a header that can be selected,
246 there is no 'real' parent item of a header that can be selected,
247 so return None.
247 so return None.
248 """
248 """
249 return None
249 return None
250
250
251 def firstchild(self):
251 def firstchild(self):
252 "return the first child of this item, if one exists. otherwise None."
252 "return the first child of this item, if one exists. otherwise None."
253 if len(self.hunks) > 0:
253 if len(self.hunks) > 0:
254 return self.hunks[0]
254 return self.hunks[0]
255 else:
255 else:
256 return None
256 return None
257
257
258 def lastchild(self):
258 def lastchild(self):
259 "return the last child of this item, if one exists. otherwise None."
259 "return the last child of this item, if one exists. otherwise None."
260 if len(self.hunks) > 0:
260 if len(self.hunks) > 0:
261 return self.hunks[-1]
261 return self.hunks[-1]
262 else:
262 else:
263 return None
263 return None
264
264
265 def allchildren(self):
265 def allchildren(self):
266 "return a list of all of the direct children of this node"
266 "return a list of all of the direct children of this node"
267 return self.hunks
267 return self.hunks
268
268
269 def __getattr__(self, name):
269 def __getattr__(self, name):
270 return getattr(self.nonuiheader, name)
270 return getattr(self.nonuiheader, name)
271
271
272 class uihunkline(patchnode):
272 class uihunkline(patchnode):
273 "represents a changed line in a hunk"
273 "represents a changed line in a hunk"
274 def __init__(self, linetext, hunk):
274 def __init__(self, linetext, hunk):
275 self.linetext = linetext
275 self.linetext = linetext
276 self.applied = True
276 self.applied = True
277 # the parent hunk to which this line belongs
277 # the parent hunk to which this line belongs
278 self.hunk = hunk
278 self.hunk = hunk
279 # folding lines currently is not used/needed, but this flag is needed
279 # folding lines currently is not used/needed, but this flag is needed
280 # in the previtem method.
280 # in the previtem method.
281 self.folded = False
281 self.folded = False
282
282
283 def prettystr(self):
283 def prettystr(self):
284 return self.linetext
284 return self.linetext
285
285
286 def nextsibling(self):
286 def nextsibling(self):
287 numlinesinhunk = len(self.hunk.changedlines)
287 numlinesinhunk = len(self.hunk.changedlines)
288 indexofthisline = self.hunk.changedlines.index(self)
288 indexofthisline = self.hunk.changedlines.index(self)
289
289
290 if (indexofthisline < numlinesinhunk - 1):
290 if (indexofthisline < numlinesinhunk - 1):
291 nextline = self.hunk.changedlines[indexofthisline + 1]
291 nextline = self.hunk.changedlines[indexofthisline + 1]
292 return nextline
292 return nextline
293 else:
293 else:
294 return None
294 return None
295
295
296 def prevsibling(self):
296 def prevsibling(self):
297 indexofthisline = self.hunk.changedlines.index(self)
297 indexofthisline = self.hunk.changedlines.index(self)
298 if indexofthisline > 0:
298 if indexofthisline > 0:
299 previousline = self.hunk.changedlines[indexofthisline - 1]
299 previousline = self.hunk.changedlines[indexofthisline - 1]
300 return previousline
300 return previousline
301 else:
301 else:
302 return None
302 return None
303
303
304 def parentitem(self):
304 def parentitem(self):
305 "return the parent to the current item"
305 "return the parent to the current item"
306 return self.hunk
306 return self.hunk
307
307
308 def firstchild(self):
308 def firstchild(self):
309 "return the first child of this item, if one exists. otherwise None."
309 "return the first child of this item, if one exists. otherwise None."
310 # hunk-lines don't have children
310 # hunk-lines don't have children
311 return None
311 return None
312
312
313 def lastchild(self):
313 def lastchild(self):
314 "return the last child of this item, if one exists. otherwise None."
314 "return the last child of this item, if one exists. otherwise None."
315 # hunk-lines don't have children
315 # hunk-lines don't have children
316 return None
316 return None
317
317
318 class uihunk(patchnode):
318 class uihunk(patchnode):
319 """ui patch hunk, wraps a hunk and keep track of ui behavior """
319 """ui patch hunk, wraps a hunk and keep track of ui behavior """
320 maxcontext = 3
320 maxcontext = 3
321
321
322 def __init__(self, hunk, header):
322 def __init__(self, hunk, header):
323 self._hunk = hunk
323 self._hunk = hunk
324 self.changedlines = [uihunkline(line, self) for line in hunk.hunk]
324 self.changedlines = [uihunkline(line, self) for line in hunk.hunk]
325 self.header = header
325 self.header = header
326 # used at end for detecting how many removed lines were un-applied
326 # used at end for detecting how many removed lines were un-applied
327 self.originalremoved = self.removed
327 self.originalremoved = self.removed
328
328
329 # flag to indicate whether to display as folded/unfolded to user
329 # flag to indicate whether to display as folded/unfolded to user
330 self.folded = True
330 self.folded = True
331 # flag to indicate whether to apply this chunk
331 # flag to indicate whether to apply this chunk
332 self.applied = True
332 self.applied = True
333 # flag which only affects the status display indicating if a node's
333 # flag which only affects the status display indicating if a node's
334 # children are partially applied (i.e. some applied, some not).
334 # children are partially applied (i.e. some applied, some not).
335 self.partial = False
335 self.partial = False
336
336
337 def nextsibling(self):
337 def nextsibling(self):
338 numhunksinheader = len(self.header.hunks)
338 numhunksinheader = len(self.header.hunks)
339 indexofthishunk = self.header.hunks.index(self)
339 indexofthishunk = self.header.hunks.index(self)
340
340
341 if (indexofthishunk < numhunksinheader - 1):
341 if (indexofthishunk < numhunksinheader - 1):
342 nexthunk = self.header.hunks[indexofthishunk + 1]
342 nexthunk = self.header.hunks[indexofthishunk + 1]
343 return nexthunk
343 return nexthunk
344 else:
344 else:
345 return None
345 return None
346
346
347 def prevsibling(self):
347 def prevsibling(self):
348 indexofthishunk = self.header.hunks.index(self)
348 indexofthishunk = self.header.hunks.index(self)
349 if indexofthishunk > 0:
349 if indexofthishunk > 0:
350 previoushunk = self.header.hunks[indexofthishunk - 1]
350 previoushunk = self.header.hunks[indexofthishunk - 1]
351 return previoushunk
351 return previoushunk
352 else:
352 else:
353 return None
353 return None
354
354
355 def parentitem(self):
355 def parentitem(self):
356 "return the parent to the current item"
356 "return the parent to the current item"
357 return self.header
357 return self.header
358
358
359 def firstchild(self):
359 def firstchild(self):
360 "return the first child of this item, if one exists. otherwise None."
360 "return the first child of this item, if one exists. otherwise None."
361 if len(self.changedlines) > 0:
361 if len(self.changedlines) > 0:
362 return self.changedlines[0]
362 return self.changedlines[0]
363 else:
363 else:
364 return None
364 return None
365
365
366 def lastchild(self):
366 def lastchild(self):
367 "return the last child of this item, if one exists. otherwise None."
367 "return the last child of this item, if one exists. otherwise None."
368 if len(self.changedlines) > 0:
368 if len(self.changedlines) > 0:
369 return self.changedlines[-1]
369 return self.changedlines[-1]
370 else:
370 else:
371 return None
371 return None
372
372
373 def allchildren(self):
373 def allchildren(self):
374 "return a list of all of the direct children of this node"
374 "return a list of all of the direct children of this node"
375 return self.changedlines
375 return self.changedlines
376
376
377 def countchanges(self):
377 def countchanges(self):
378 """changedlines -> (n+,n-)"""
378 """changedlines -> (n+,n-)"""
379 add = len([l for l in self.changedlines if l.applied
379 add = len([l for l in self.changedlines if l.applied
380 and l.prettystr()[0] == '+'])
380 and l.prettystr()[0] == '+'])
381 rem = len([l for l in self.changedlines if l.applied
381 rem = len([l for l in self.changedlines if l.applied
382 and l.prettystr()[0] == '-'])
382 and l.prettystr()[0] == '-'])
383 return add, rem
383 return add, rem
384
384
385 def getfromtoline(self):
385 def getfromtoline(self):
386 # calculate the number of removed lines converted to context lines
386 # calculate the number of removed lines converted to context lines
387 removedconvertedtocontext = self.originalremoved - self.removed
387 removedconvertedtocontext = self.originalremoved - self.removed
388
388
389 contextlen = (len(self.before) + len(self.after) +
389 contextlen = (len(self.before) + len(self.after) +
390 removedconvertedtocontext)
390 removedconvertedtocontext)
391 if self.after and self.after[-1] == '\\ No newline at end of file\n':
391 if self.after and self.after[-1] == '\\ No newline at end of file\n':
392 contextlen -= 1
392 contextlen -= 1
393 fromlen = contextlen + self.removed
393 fromlen = contextlen + self.removed
394 tolen = contextlen + self.added
394 tolen = contextlen + self.added
395
395
396 # diffutils manual, section "2.2.2.2 detailed description of unified
396 # diffutils manual, section "2.2.2.2 detailed description of unified
397 # format": "an empty hunk is considered to end at the line that
397 # format": "an empty hunk is considered to end at the line that
398 # precedes the hunk."
398 # precedes the hunk."
399 #
399 #
400 # so, if either of hunks is empty, decrease its line start. --immerrr
400 # so, if either of hunks is empty, decrease its line start. --immerrr
401 # but only do this if fromline > 0, to avoid having, e.g fromline=-1.
401 # but only do this if fromline > 0, to avoid having, e.g fromline=-1.
402 fromline, toline = self.fromline, self.toline
402 fromline, toline = self.fromline, self.toline
403 if fromline != 0:
403 if fromline != 0:
404 if fromlen == 0:
404 if fromlen == 0:
405 fromline -= 1
405 fromline -= 1
406 if tolen == 0 and toline > 0:
406 if tolen == 0 and toline > 0:
407 toline -= 1
407 toline -= 1
408
408
409 fromtoline = '@@ -%d,%d +%d,%d @@%s\n' % (
409 fromtoline = '@@ -%d,%d +%d,%d @@%s\n' % (
410 fromline, fromlen, toline, tolen,
410 fromline, fromlen, toline, tolen,
411 self.proc and (' ' + self.proc))
411 self.proc and (' ' + self.proc))
412 return fromtoline
412 return fromtoline
413
413
414 def write(self, fp):
414 def write(self, fp):
415 # updated self.added/removed, which are used by getfromtoline()
415 # updated self.added/removed, which are used by getfromtoline()
416 self.added, self.removed = self.countchanges()
416 self.added, self.removed = self.countchanges()
417 fp.write(self.getfromtoline())
417 fp.write(self.getfromtoline())
418
418
419 hunklinelist = []
419 hunklinelist = []
420 # add the following to the list: (1) all applied lines, and
420 # add the following to the list: (1) all applied lines, and
421 # (2) all unapplied removal lines (convert these to context lines)
421 # (2) all unapplied removal lines (convert these to context lines)
422 for changedline in self.changedlines:
422 for changedline in self.changedlines:
423 changedlinestr = changedline.prettystr()
423 changedlinestr = changedline.prettystr()
424 if changedline.applied:
424 if changedline.applied:
425 hunklinelist.append(changedlinestr)
425 hunklinelist.append(changedlinestr)
426 elif changedlinestr[0] == "-":
426 elif changedlinestr[0] == "-":
427 hunklinelist.append(" " + changedlinestr[1:])
427 hunklinelist.append(" " + changedlinestr[1:])
428
428
429 fp.write(''.join(self.before + hunklinelist + self.after))
429 fp.write(''.join(self.before + hunklinelist + self.after))
430
430
431 pretty = write
431 pretty = write
432
432
433 def prettystr(self):
433 def prettystr(self):
434 x = stringio()
434 x = stringio()
435 self.pretty(x)
435 self.pretty(x)
436 return x.getvalue()
436 return x.getvalue()
437
437
438 def reversehunk(self):
438 def reversehunk(self):
439 """return a recordhunk which is the reverse of the hunk
439 """return a recordhunk which is the reverse of the hunk
440
440
441 Assuming the displayed patch is diff(A, B) result. The returned hunk is
441 Assuming the displayed patch is diff(A, B) result. The returned hunk is
442 intended to be applied to B, instead of A.
442 intended to be applied to B, instead of A.
443
443
444 For example, when A is "0\n1\n2\n6\n" and B is "0\n3\n4\n5\n6\n", and
444 For example, when A is "0\n1\n2\n6\n" and B is "0\n3\n4\n5\n6\n", and
445 the user made the following selection:
445 the user made the following selection:
446
446
447 0
447 0
448 [x] -1 [x]: selected
448 [x] -1 [x]: selected
449 [ ] -2 [ ]: not selected
449 [ ] -2 [ ]: not selected
450 [x] +3
450 [x] +3
451 [ ] +4
451 [ ] +4
452 [x] +5
452 [x] +5
453 6
453 6
454
454
455 This function returns a hunk like:
455 This function returns a hunk like:
456
456
457 0
457 0
458 -3
458 -3
459 -4
459 -4
460 -5
460 -5
461 +1
461 +1
462 +4
462 +4
463 6
463 6
464
464
465 Note "4" was first deleted then added. That's because "4" exists in B
465 Note "4" was first deleted then added. That's because "4" exists in B
466 side and "-4" must exist between "-3" and "-5" to make the patch
466 side and "-4" must exist between "-3" and "-5" to make the patch
467 applicable to B.
467 applicable to B.
468 """
468 """
469 dels = []
469 dels = []
470 adds = []
470 adds = []
471 for line in self.changedlines:
471 for line in self.changedlines:
472 text = line.linetext
472 text = line.linetext
473 if line.applied:
473 if line.applied:
474 if text[0] == '+':
474 if text[0] == '+':
475 dels.append(text[1:])
475 dels.append(text[1:])
476 elif text[0] == '-':
476 elif text[0] == '-':
477 adds.append(text[1:])
477 adds.append(text[1:])
478 elif text[0] == '+':
478 elif text[0] == '+':
479 dels.append(text[1:])
479 dels.append(text[1:])
480 adds.append(text[1:])
480 adds.append(text[1:])
481 hunk = ['-%s' % l for l in dels] + ['+%s' % l for l in adds]
481 hunk = ['-%s' % l for l in dels] + ['+%s' % l for l in adds]
482 h = self._hunk
482 h = self._hunk
483 return patchmod.recordhunk(h.header, h.toline, h.fromline, h.proc,
483 return patchmod.recordhunk(h.header, h.toline, h.fromline, h.proc,
484 h.before, hunk, h.after)
484 h.before, hunk, h.after)
485
485
486 def __getattr__(self, name):
486 def __getattr__(self, name):
487 return getattr(self._hunk, name)
487 return getattr(self._hunk, name)
488
488
489 def __repr__(self):
489 def __repr__(self):
490 return '<hunk %r@%d>' % (self.filename(), self.fromline)
490 return '<hunk %r@%d>' % (self.filename(), self.fromline)
491
491
492 def filterpatch(ui, chunks, chunkselector, operation=None):
492 def filterpatch(ui, chunks, chunkselector, operation=None):
493 """interactively filter patch chunks into applied-only chunks"""
493 """interactively filter patch chunks into applied-only chunks"""
494 chunks = list(chunks)
494 chunks = list(chunks)
495 # convert chunks list into structure suitable for displaying/modifying
495 # convert chunks list into structure suitable for displaying/modifying
496 # with curses. create a list of headers only.
496 # with curses. create a list of headers only.
497 headers = [c for c in chunks if isinstance(c, patchmod.header)]
497 headers = [c for c in chunks if isinstance(c, patchmod.header)]
498
498
499 # if there are no changed files
499 # if there are no changed files
500 if len(headers) == 0:
500 if len(headers) == 0:
501 return [], {}
501 return [], {}
502 uiheaders = [uiheader(h) for h in headers]
502 uiheaders = [uiheader(h) for h in headers]
503 # let user choose headers/hunks/lines, and mark their applied flags
503 # let user choose headers/hunks/lines, and mark their applied flags
504 # accordingly
504 # accordingly
505 ret = chunkselector(ui, uiheaders, operation=operation)
505 ret = chunkselector(ui, uiheaders, operation=operation)
506 appliedhunklist = []
506 appliedhunklist = []
507 for hdr in uiheaders:
507 for hdr in uiheaders:
508 if (hdr.applied and
508 if (hdr.applied and
509 (hdr.special() or len([h for h in hdr.hunks if h.applied]) > 0)):
509 (hdr.special() or len([h for h in hdr.hunks if h.applied]) > 0)):
510 appliedhunklist.append(hdr)
510 appliedhunklist.append(hdr)
511 fixoffset = 0
511 fixoffset = 0
512 for hnk in hdr.hunks:
512 for hnk in hdr.hunks:
513 if hnk.applied:
513 if hnk.applied:
514 appliedhunklist.append(hnk)
514 appliedhunklist.append(hnk)
515 # adjust the 'to'-line offset of the hunk to be correct
515 # adjust the 'to'-line offset of the hunk to be correct
516 # after de-activating some of the other hunks for this file
516 # after de-activating some of the other hunks for this file
517 if fixoffset:
517 if fixoffset:
518 #hnk = copy.copy(hnk) # necessary??
518 #hnk = copy.copy(hnk) # necessary??
519 hnk.toline += fixoffset
519 hnk.toline += fixoffset
520 else:
520 else:
521 fixoffset += hnk.removed - hnk.added
521 fixoffset += hnk.removed - hnk.added
522
522
523 return (appliedhunklist, ret)
523 return (appliedhunklist, ret)
524
524
525 def chunkselector(ui, headerlist, operation=None):
525 def chunkselector(ui, headerlist, operation=None):
526 """
526 """
527 curses interface to get selection of chunks, and mark the applied flags
527 curses interface to get selection of chunks, and mark the applied flags
528 of the chosen chunks.
528 of the chosen chunks.
529 """
529 """
530 ui.write(_('starting interactive selection\n'))
530 ui.write(_('starting interactive selection\n'))
531 chunkselector = curseschunkselector(headerlist, ui, operation)
531 chunkselector = curseschunkselector(headerlist, ui, operation)
532 origsigtstp = sentinel = object()
532 origsigtstp = sentinel = object()
533 if util.safehasattr(signal, 'SIGTSTP'):
533 if util.safehasattr(signal, 'SIGTSTP'):
534 origsigtstp = signal.getsignal(signal.SIGTSTP)
534 origsigtstp = signal.getsignal(signal.SIGTSTP)
535 try:
535 try:
536 curses.wrapper(chunkselector.main)
536 curses.wrapper(chunkselector.main)
537 if chunkselector.initexc is not None:
537 if chunkselector.initexc is not None:
538 raise chunkselector.initexc
538 raise chunkselector.initexc
539 # ncurses does not restore signal handler for SIGTSTP
539 # ncurses does not restore signal handler for SIGTSTP
540 finally:
540 finally:
541 if origsigtstp is not sentinel:
541 if origsigtstp is not sentinel:
542 signal.signal(signal.SIGTSTP, origsigtstp)
542 signal.signal(signal.SIGTSTP, origsigtstp)
543 return chunkselector.opts
543 return chunkselector.opts
544
544
545 def testdecorator(testfn, f):
545 def testdecorator(testfn, f):
546 def u(*args, **kwargs):
546 def u(*args, **kwargs):
547 return f(testfn, *args, **kwargs)
547 return f(testfn, *args, **kwargs)
548 return u
548 return u
549
549
550 def testchunkselector(testfn, ui, headerlist, operation=None):
550 def testchunkselector(testfn, ui, headerlist, operation=None):
551 """
551 """
552 test interface to get selection of chunks, and mark the applied flags
552 test interface to get selection of chunks, and mark the applied flags
553 of the chosen chunks.
553 of the chosen chunks.
554 """
554 """
555 chunkselector = curseschunkselector(headerlist, ui, operation)
555 chunkselector = curseschunkselector(headerlist, ui, operation)
556 if testfn and os.path.exists(testfn):
556 if testfn and os.path.exists(testfn):
557 testf = open(testfn, 'rb')
557 testf = open(testfn, 'rb')
558 testcommands = [x.rstrip('\n') for x in testf.readlines()]
558 testcommands = [x.rstrip('\n') for x in testf.readlines()]
559 testf.close()
559 testf.close()
560 while True:
560 while True:
561 if chunkselector.handlekeypressed(testcommands.pop(0), test=True):
561 if chunkselector.handlekeypressed(testcommands.pop(0), test=True):
562 break
562 break
563 return chunkselector.opts
563 return chunkselector.opts
564
564
565 _headermessages = { # {operation: text}
565 _headermessages = { # {operation: text}
566 'apply': _('Select hunks to apply'),
566 'apply': _('Select hunks to apply'),
567 'discard': _('Select hunks to discard'),
567 'discard': _('Select hunks to discard'),
568 None: _('Select hunks to record'),
568 None: _('Select hunks to record'),
569 }
569 }
570
570
571 class curseschunkselector(object):
571 class curseschunkselector(object):
572 def __init__(self, headerlist, ui, operation=None):
572 def __init__(self, headerlist, ui, operation=None):
573 # put the headers into a patch object
573 # put the headers into a patch object
574 self.headerlist = patch(headerlist)
574 self.headerlist = patch(headerlist)
575
575
576 self.ui = ui
576 self.ui = ui
577 self.opts = {}
577 self.opts = {}
578
578
579 self.errorstr = None
579 self.errorstr = None
580 # list of all chunks
580 # list of all chunks
581 self.chunklist = []
581 self.chunklist = []
582 for h in headerlist:
582 for h in headerlist:
583 self.chunklist.append(h)
583 self.chunklist.append(h)
584 self.chunklist.extend(h.hunks)
584 self.chunklist.extend(h.hunks)
585
585
586 # dictionary mapping (fgcolor, bgcolor) pairs to the
586 # dictionary mapping (fgcolor, bgcolor) pairs to the
587 # corresponding curses color-pair value.
587 # corresponding curses color-pair value.
588 self.colorpairs = {}
588 self.colorpairs = {}
589 # maps custom nicknames of color-pairs to curses color-pair values
589 # maps custom nicknames of color-pairs to curses color-pair values
590 self.colorpairnames = {}
590 self.colorpairnames = {}
591
591
592 # Honor color setting of ui section. Keep colored setup as
592 # Honor color setting of ui section. Keep colored setup as
593 # long as not explicitly set to a falsy value - especially,
593 # long as not explicitly set to a falsy value - especially,
594 # when not set at all. This is to stay most compatible with
594 # when not set at all. This is to stay most compatible with
595 # previous (color only) behaviour.
595 # previous (color only) behaviour.
596 uicolor = stringutil.parsebool(self.ui.config('ui', 'color'))
596 uicolor = stringutil.parsebool(self.ui.config('ui', 'color'))
597 self.usecolor = uicolor is not False
597 self.usecolor = uicolor is not False
598
598
599 # the currently selected header, hunk, or hunk-line
599 # the currently selected header, hunk, or hunk-line
600 self.currentselecteditem = self.headerlist[0]
600 self.currentselecteditem = self.headerlist[0]
601
601
602 # updated when printing out patch-display -- the 'lines' here are the
602 # updated when printing out patch-display -- the 'lines' here are the
603 # line positions *in the pad*, not on the screen.
603 # line positions *in the pad*, not on the screen.
604 self.selecteditemstartline = 0
604 self.selecteditemstartline = 0
605 self.selecteditemendline = None
605 self.selecteditemendline = None
606
606
607 # define indentation levels
607 # define indentation levels
608 self.headerindentnumchars = 0
608 self.headerindentnumchars = 0
609 self.hunkindentnumchars = 3
609 self.hunkindentnumchars = 3
610 self.hunklineindentnumchars = 6
610 self.hunklineindentnumchars = 6
611
611
612 # the first line of the pad to print to the screen
612 # the first line of the pad to print to the screen
613 self.firstlineofpadtoprint = 0
613 self.firstlineofpadtoprint = 0
614
614
615 # keeps track of the number of lines in the pad
615 # keeps track of the number of lines in the pad
616 self.numpadlines = None
616 self.numpadlines = None
617
617
618 self.numstatuslines = 1
618 self.numstatuslines = 1
619
619
620 # keep a running count of the number of lines printed to the pad
620 # keep a running count of the number of lines printed to the pad
621 # (used for determining when the selected item begins/ends)
621 # (used for determining when the selected item begins/ends)
622 self.linesprintedtopadsofar = 0
622 self.linesprintedtopadsofar = 0
623
623
624 # the first line of the pad which is visible on the screen
624 # the first line of the pad which is visible on the screen
625 self.firstlineofpadtoprint = 0
625 self.firstlineofpadtoprint = 0
626
626
627 # stores optional text for a commit comment provided by the user
627 # stores optional text for a commit comment provided by the user
628 self.commenttext = ""
628 self.commenttext = ""
629
629
630 # if the last 'toggle all' command caused all changes to be applied
630 # if the last 'toggle all' command caused all changes to be applied
631 self.waslasttoggleallapplied = True
631 self.waslasttoggleallapplied = True
632
632
633 # affects some ui text
633 # affects some ui text
634 if operation not in _headermessages:
634 if operation not in _headermessages:
635 raise error.ProgrammingError('unexpected operation: %s' % operation)
635 raise error.ProgrammingError('unexpected operation: %s' % operation)
636 self.operation = operation
636 self.operation = operation
637
637
638 def uparrowevent(self):
638 def uparrowevent(self):
639 """
639 """
640 try to select the previous item to the current item that has the
640 try to select the previous item to the current item that has the
641 most-indented level. for example, if a hunk is selected, try to select
641 most-indented level. for example, if a hunk is selected, try to select
642 the last hunkline of the hunk prior to the selected hunk. or, if
642 the last hunkline of the hunk prior to the selected hunk. or, if
643 the first hunkline of a hunk is currently selected, then select the
643 the first hunkline of a hunk is currently selected, then select the
644 hunk itself.
644 hunk itself.
645 """
645 """
646 currentitem = self.currentselecteditem
646 currentitem = self.currentselecteditem
647
647
648 nextitem = currentitem.previtem()
648 nextitem = currentitem.previtem()
649
649
650 if nextitem is None:
650 if nextitem is None:
651 # if no parent item (i.e. currentitem is the first header), then
651 # if no parent item (i.e. currentitem is the first header), then
652 # no change...
652 # no change...
653 nextitem = currentitem
653 nextitem = currentitem
654
654
655 self.currentselecteditem = nextitem
655 self.currentselecteditem = nextitem
656
656
657 def uparrowshiftevent(self):
657 def uparrowshiftevent(self):
658 """
658 """
659 select (if possible) the previous item on the same level as the
659 select (if possible) the previous item on the same level as the
660 currently selected item. otherwise, select (if possible) the
660 currently selected item. otherwise, select (if possible) the
661 parent-item of the currently selected item.
661 parent-item of the currently selected item.
662 """
662 """
663 currentitem = self.currentselecteditem
663 currentitem = self.currentselecteditem
664 nextitem = currentitem.prevsibling()
664 nextitem = currentitem.prevsibling()
665 # if there's no previous sibling, try choosing the parent
665 # if there's no previous sibling, try choosing the parent
666 if nextitem is None:
666 if nextitem is None:
667 nextitem = currentitem.parentitem()
667 nextitem = currentitem.parentitem()
668 if nextitem is None:
668 if nextitem is None:
669 # if no parent item (i.e. currentitem is the first header), then
669 # if no parent item (i.e. currentitem is the first header), then
670 # no change...
670 # no change...
671 nextitem = currentitem
671 nextitem = currentitem
672
672
673 self.currentselecteditem = nextitem
673 self.currentselecteditem = nextitem
674 self.recenterdisplayedarea()
674 self.recenterdisplayedarea()
675
675
676 def downarrowevent(self):
676 def downarrowevent(self):
677 """
677 """
678 try to select the next item to the current item that has the
678 try to select the next item to the current item that has the
679 most-indented level. for example, if a hunk is selected, select
679 most-indented level. for example, if a hunk is selected, select
680 the first hunkline of the selected hunk. or, if the last hunkline of
680 the first hunkline of the selected hunk. or, if the last hunkline of
681 a hunk is currently selected, then select the next hunk, if one exists,
681 a hunk is currently selected, then select the next hunk, if one exists,
682 or if not, the next header if one exists.
682 or if not, the next header if one exists.
683 """
683 """
684 #self.startprintline += 1 #debug
684 #self.startprintline += 1 #debug
685 currentitem = self.currentselecteditem
685 currentitem = self.currentselecteditem
686
686
687 nextitem = currentitem.nextitem()
687 nextitem = currentitem.nextitem()
688 # if there's no next item, keep the selection as-is
688 # if there's no next item, keep the selection as-is
689 if nextitem is None:
689 if nextitem is None:
690 nextitem = currentitem
690 nextitem = currentitem
691
691
692 self.currentselecteditem = nextitem
692 self.currentselecteditem = nextitem
693
693
694 def downarrowshiftevent(self):
694 def downarrowshiftevent(self):
695 """
695 """
696 select (if possible) the next item on the same level as the currently
696 select (if possible) the next item on the same level as the currently
697 selected item. otherwise, select (if possible) the next item on the
697 selected item. otherwise, select (if possible) the next item on the
698 same level as the parent item of the currently selected item.
698 same level as the parent item of the currently selected item.
699 """
699 """
700 currentitem = self.currentselecteditem
700 currentitem = self.currentselecteditem
701 nextitem = currentitem.nextsibling()
701 nextitem = currentitem.nextsibling()
702 # if there's no next sibling, try choosing the parent's nextsibling
702 # if there's no next sibling, try choosing the parent's nextsibling
703 if nextitem is None:
703 if nextitem is None:
704 try:
704 try:
705 nextitem = currentitem.parentitem().nextsibling()
705 nextitem = currentitem.parentitem().nextsibling()
706 except AttributeError:
706 except AttributeError:
707 # parentitem returned None, so nextsibling() can't be called
707 # parentitem returned None, so nextsibling() can't be called
708 nextitem = None
708 nextitem = None
709 if nextitem is None:
709 if nextitem is None:
710 # if parent has no next sibling, then no change...
710 # if parent has no next sibling, then no change...
711 nextitem = currentitem
711 nextitem = currentitem
712
712
713 self.currentselecteditem = nextitem
713 self.currentselecteditem = nextitem
714 self.recenterdisplayedarea()
714 self.recenterdisplayedarea()
715
715
716 def nextsametype(self):
717 currentitem = self.currentselecteditem
718 sametype = lambda item: isinstance(item, type(currentitem))
719 nextitem = currentitem.nextitem()
720
721 while nextitem is not None and not sametype(nextitem):
722 nextitem = nextitem.nextitem()
723
724 if nextitem is None:
725 nextitem = currentitem
726 else:
727 parent = nextitem.parentitem()
728 if parent.folded:
729 self.togglefolded(parent)
730
731 self.currentselecteditem = nextitem
732 self.recenterdisplayedarea()
733
716 def rightarrowevent(self):
734 def rightarrowevent(self):
717 """
735 """
718 select (if possible) the first of this item's child-items.
736 select (if possible) the first of this item's child-items.
719 """
737 """
720 currentitem = self.currentselecteditem
738 currentitem = self.currentselecteditem
721 nextitem = currentitem.firstchild()
739 nextitem = currentitem.firstchild()
722
740
723 # turn off folding if we want to show a child-item
741 # turn off folding if we want to show a child-item
724 if currentitem.folded:
742 if currentitem.folded:
725 self.togglefolded(currentitem)
743 self.togglefolded(currentitem)
726
744
727 if nextitem is None:
745 if nextitem is None:
728 # if no next item on parent-level, then no change...
746 # if no next item on parent-level, then no change...
729 nextitem = currentitem
747 nextitem = currentitem
730
748
731 self.currentselecteditem = nextitem
749 self.currentselecteditem = nextitem
732
750
733 def leftarrowevent(self):
751 def leftarrowevent(self):
734 """
752 """
735 if the current item can be folded (i.e. it is an unfolded header or
753 if the current item can be folded (i.e. it is an unfolded header or
736 hunk), then fold it. otherwise try select (if possible) the parent
754 hunk), then fold it. otherwise try select (if possible) the parent
737 of this item.
755 of this item.
738 """
756 """
739 currentitem = self.currentselecteditem
757 currentitem = self.currentselecteditem
740
758
741 # try to fold the item
759 # try to fold the item
742 if not isinstance(currentitem, uihunkline):
760 if not isinstance(currentitem, uihunkline):
743 if not currentitem.folded:
761 if not currentitem.folded:
744 self.togglefolded(item=currentitem)
762 self.togglefolded(item=currentitem)
745 return
763 return
746
764
747 # if it can't be folded, try to select the parent item
765 # if it can't be folded, try to select the parent item
748 nextitem = currentitem.parentitem()
766 nextitem = currentitem.parentitem()
749
767
750 if nextitem is None:
768 if nextitem is None:
751 # if no item on parent-level, then no change...
769 # if no item on parent-level, then no change...
752 nextitem = currentitem
770 nextitem = currentitem
753 if not nextitem.folded:
771 if not nextitem.folded:
754 self.togglefolded(item=nextitem)
772 self.togglefolded(item=nextitem)
755
773
756 self.currentselecteditem = nextitem
774 self.currentselecteditem = nextitem
757
775
758 def leftarrowshiftevent(self):
776 def leftarrowshiftevent(self):
759 """
777 """
760 select the header of the current item (or fold current item if the
778 select the header of the current item (or fold current item if the
761 current item is already a header).
779 current item is already a header).
762 """
780 """
763 currentitem = self.currentselecteditem
781 currentitem = self.currentselecteditem
764
782
765 if isinstance(currentitem, uiheader):
783 if isinstance(currentitem, uiheader):
766 if not currentitem.folded:
784 if not currentitem.folded:
767 self.togglefolded(item=currentitem)
785 self.togglefolded(item=currentitem)
768 return
786 return
769
787
770 # select the parent item recursively until we're at a header
788 # select the parent item recursively until we're at a header
771 while True:
789 while True:
772 nextitem = currentitem.parentitem()
790 nextitem = currentitem.parentitem()
773 if nextitem is None:
791 if nextitem is None:
774 break
792 break
775 else:
793 else:
776 currentitem = nextitem
794 currentitem = nextitem
777
795
778 self.currentselecteditem = currentitem
796 self.currentselecteditem = currentitem
779
797
780 def updatescroll(self):
798 def updatescroll(self):
781 "scroll the screen to fully show the currently-selected"
799 "scroll the screen to fully show the currently-selected"
782 selstart = self.selecteditemstartline
800 selstart = self.selecteditemstartline
783 selend = self.selecteditemendline
801 selend = self.selecteditemendline
784
802
785 padstart = self.firstlineofpadtoprint
803 padstart = self.firstlineofpadtoprint
786 padend = padstart + self.yscreensize - self.numstatuslines - 1
804 padend = padstart + self.yscreensize - self.numstatuslines - 1
787 # 'buffered' pad start/end values which scroll with a certain
805 # 'buffered' pad start/end values which scroll with a certain
788 # top/bottom context margin
806 # top/bottom context margin
789 padstartbuffered = padstart + 3
807 padstartbuffered = padstart + 3
790 padendbuffered = padend - 3
808 padendbuffered = padend - 3
791
809
792 if selend > padendbuffered:
810 if selend > padendbuffered:
793 self.scrolllines(selend - padendbuffered)
811 self.scrolllines(selend - padendbuffered)
794 elif selstart < padstartbuffered:
812 elif selstart < padstartbuffered:
795 # negative values scroll in pgup direction
813 # negative values scroll in pgup direction
796 self.scrolllines(selstart - padstartbuffered)
814 self.scrolllines(selstart - padstartbuffered)
797
815
798 def scrolllines(self, numlines):
816 def scrolllines(self, numlines):
799 "scroll the screen up (down) by numlines when numlines >0 (<0)."
817 "scroll the screen up (down) by numlines when numlines >0 (<0)."
800 self.firstlineofpadtoprint += numlines
818 self.firstlineofpadtoprint += numlines
801 if self.firstlineofpadtoprint < 0:
819 if self.firstlineofpadtoprint < 0:
802 self.firstlineofpadtoprint = 0
820 self.firstlineofpadtoprint = 0
803 if self.firstlineofpadtoprint > self.numpadlines - 1:
821 if self.firstlineofpadtoprint > self.numpadlines - 1:
804 self.firstlineofpadtoprint = self.numpadlines - 1
822 self.firstlineofpadtoprint = self.numpadlines - 1
805
823
806 def toggleapply(self, item=None):
824 def toggleapply(self, item=None):
807 """
825 """
808 toggle the applied flag of the specified item. if no item is specified,
826 toggle the applied flag of the specified item. if no item is specified,
809 toggle the flag of the currently selected item.
827 toggle the flag of the currently selected item.
810 """
828 """
811 if item is None:
829 if item is None:
812 item = self.currentselecteditem
830 item = self.currentselecteditem
813
831
814 item.applied = not item.applied
832 item.applied = not item.applied
815
833
816 if isinstance(item, uiheader):
834 if isinstance(item, uiheader):
817 item.partial = False
835 item.partial = False
818 if item.applied:
836 if item.applied:
819 # apply all its hunks
837 # apply all its hunks
820 for hnk in item.hunks:
838 for hnk in item.hunks:
821 hnk.applied = True
839 hnk.applied = True
822 # apply all their hunklines
840 # apply all their hunklines
823 for hunkline in hnk.changedlines:
841 for hunkline in hnk.changedlines:
824 hunkline.applied = True
842 hunkline.applied = True
825 else:
843 else:
826 # un-apply all its hunks
844 # un-apply all its hunks
827 for hnk in item.hunks:
845 for hnk in item.hunks:
828 hnk.applied = False
846 hnk.applied = False
829 hnk.partial = False
847 hnk.partial = False
830 # un-apply all their hunklines
848 # un-apply all their hunklines
831 for hunkline in hnk.changedlines:
849 for hunkline in hnk.changedlines:
832 hunkline.applied = False
850 hunkline.applied = False
833 elif isinstance(item, uihunk):
851 elif isinstance(item, uihunk):
834 item.partial = False
852 item.partial = False
835 # apply all it's hunklines
853 # apply all it's hunklines
836 for hunkline in item.changedlines:
854 for hunkline in item.changedlines:
837 hunkline.applied = item.applied
855 hunkline.applied = item.applied
838
856
839 siblingappliedstatus = [hnk.applied for hnk in item.header.hunks]
857 siblingappliedstatus = [hnk.applied for hnk in item.header.hunks]
840 allsiblingsapplied = not (False in siblingappliedstatus)
858 allsiblingsapplied = not (False in siblingappliedstatus)
841 nosiblingsapplied = not (True in siblingappliedstatus)
859 nosiblingsapplied = not (True in siblingappliedstatus)
842
860
843 siblingspartialstatus = [hnk.partial for hnk in item.header.hunks]
861 siblingspartialstatus = [hnk.partial for hnk in item.header.hunks]
844 somesiblingspartial = (True in siblingspartialstatus)
862 somesiblingspartial = (True in siblingspartialstatus)
845
863
846 #cases where applied or partial should be removed from header
864 #cases where applied or partial should be removed from header
847
865
848 # if no 'sibling' hunks are applied (including this hunk)
866 # if no 'sibling' hunks are applied (including this hunk)
849 if nosiblingsapplied:
867 if nosiblingsapplied:
850 if not item.header.special():
868 if not item.header.special():
851 item.header.applied = False
869 item.header.applied = False
852 item.header.partial = False
870 item.header.partial = False
853 else: # some/all parent siblings are applied
871 else: # some/all parent siblings are applied
854 item.header.applied = True
872 item.header.applied = True
855 item.header.partial = (somesiblingspartial or
873 item.header.partial = (somesiblingspartial or
856 not allsiblingsapplied)
874 not allsiblingsapplied)
857
875
858 elif isinstance(item, uihunkline):
876 elif isinstance(item, uihunkline):
859 siblingappliedstatus = [ln.applied for ln in item.hunk.changedlines]
877 siblingappliedstatus = [ln.applied for ln in item.hunk.changedlines]
860 allsiblingsapplied = not (False in siblingappliedstatus)
878 allsiblingsapplied = not (False in siblingappliedstatus)
861 nosiblingsapplied = not (True in siblingappliedstatus)
879 nosiblingsapplied = not (True in siblingappliedstatus)
862
880
863 # if no 'sibling' lines are applied
881 # if no 'sibling' lines are applied
864 if nosiblingsapplied:
882 if nosiblingsapplied:
865 item.hunk.applied = False
883 item.hunk.applied = False
866 item.hunk.partial = False
884 item.hunk.partial = False
867 elif allsiblingsapplied:
885 elif allsiblingsapplied:
868 item.hunk.applied = True
886 item.hunk.applied = True
869 item.hunk.partial = False
887 item.hunk.partial = False
870 else: # some siblings applied
888 else: # some siblings applied
871 item.hunk.applied = True
889 item.hunk.applied = True
872 item.hunk.partial = True
890 item.hunk.partial = True
873
891
874 parentsiblingsapplied = [hnk.applied for hnk
892 parentsiblingsapplied = [hnk.applied for hnk
875 in item.hunk.header.hunks]
893 in item.hunk.header.hunks]
876 noparentsiblingsapplied = not (True in parentsiblingsapplied)
894 noparentsiblingsapplied = not (True in parentsiblingsapplied)
877 allparentsiblingsapplied = not (False in parentsiblingsapplied)
895 allparentsiblingsapplied = not (False in parentsiblingsapplied)
878
896
879 parentsiblingspartial = [hnk.partial for hnk
897 parentsiblingspartial = [hnk.partial for hnk
880 in item.hunk.header.hunks]
898 in item.hunk.header.hunks]
881 someparentsiblingspartial = (True in parentsiblingspartial)
899 someparentsiblingspartial = (True in parentsiblingspartial)
882
900
883 # if all parent hunks are not applied, un-apply header
901 # if all parent hunks are not applied, un-apply header
884 if noparentsiblingsapplied:
902 if noparentsiblingsapplied:
885 if not item.hunk.header.special():
903 if not item.hunk.header.special():
886 item.hunk.header.applied = False
904 item.hunk.header.applied = False
887 item.hunk.header.partial = False
905 item.hunk.header.partial = False
888 # set the applied and partial status of the header if needed
906 # set the applied and partial status of the header if needed
889 else: # some/all parent siblings are applied
907 else: # some/all parent siblings are applied
890 item.hunk.header.applied = True
908 item.hunk.header.applied = True
891 item.hunk.header.partial = (someparentsiblingspartial or
909 item.hunk.header.partial = (someparentsiblingspartial or
892 not allparentsiblingsapplied)
910 not allparentsiblingsapplied)
893
911
894 def toggleall(self):
912 def toggleall(self):
895 "toggle the applied flag of all items."
913 "toggle the applied flag of all items."
896 if self.waslasttoggleallapplied: # then unapply them this time
914 if self.waslasttoggleallapplied: # then unapply them this time
897 for item in self.headerlist:
915 for item in self.headerlist:
898 if item.applied:
916 if item.applied:
899 self.toggleapply(item)
917 self.toggleapply(item)
900 else:
918 else:
901 for item in self.headerlist:
919 for item in self.headerlist:
902 if not item.applied:
920 if not item.applied:
903 self.toggleapply(item)
921 self.toggleapply(item)
904 self.waslasttoggleallapplied = not self.waslasttoggleallapplied
922 self.waslasttoggleallapplied = not self.waslasttoggleallapplied
905
923
906 def togglefolded(self, item=None, foldparent=False):
924 def togglefolded(self, item=None, foldparent=False):
907 "toggle folded flag of specified item (defaults to currently selected)"
925 "toggle folded flag of specified item (defaults to currently selected)"
908 if item is None:
926 if item is None:
909 item = self.currentselecteditem
927 item = self.currentselecteditem
910 if foldparent or (isinstance(item, uiheader) and item.neverunfolded):
928 if foldparent or (isinstance(item, uiheader) and item.neverunfolded):
911 if not isinstance(item, uiheader):
929 if not isinstance(item, uiheader):
912 # we need to select the parent item in this case
930 # we need to select the parent item in this case
913 self.currentselecteditem = item = item.parentitem()
931 self.currentselecteditem = item = item.parentitem()
914 elif item.neverunfolded:
932 elif item.neverunfolded:
915 item.neverunfolded = False
933 item.neverunfolded = False
916
934
917 # also fold any foldable children of the parent/current item
935 # also fold any foldable children of the parent/current item
918 if isinstance(item, uiheader): # the original or 'new' item
936 if isinstance(item, uiheader): # the original or 'new' item
919 for child in item.allchildren():
937 for child in item.allchildren():
920 child.folded = not item.folded
938 child.folded = not item.folded
921
939
922 if isinstance(item, (uiheader, uihunk)):
940 if isinstance(item, (uiheader, uihunk)):
923 item.folded = not item.folded
941 item.folded = not item.folded
924
942
925 def alignstring(self, instr, window):
943 def alignstring(self, instr, window):
926 """
944 """
927 add whitespace to the end of a string in order to make it fill
945 add whitespace to the end of a string in order to make it fill
928 the screen in the x direction. the current cursor position is
946 the screen in the x direction. the current cursor position is
929 taken into account when making this calculation. the string can span
947 taken into account when making this calculation. the string can span
930 multiple lines.
948 multiple lines.
931 """
949 """
932 y, xstart = window.getyx()
950 y, xstart = window.getyx()
933 width = self.xscreensize
951 width = self.xscreensize
934 # turn tabs into spaces
952 # turn tabs into spaces
935 instr = instr.expandtabs(4)
953 instr = instr.expandtabs(4)
936 strwidth = encoding.colwidth(instr)
954 strwidth = encoding.colwidth(instr)
937 numspaces = (width - ((strwidth + xstart) % width) - 1)
955 numspaces = (width - ((strwidth + xstart) % width) - 1)
938 return instr + " " * numspaces + "\n"
956 return instr + " " * numspaces + "\n"
939
957
940 def printstring(self, window, text, fgcolor=None, bgcolor=None, pair=None,
958 def printstring(self, window, text, fgcolor=None, bgcolor=None, pair=None,
941 pairname=None, attrlist=None, towin=True, align=True, showwhtspc=False):
959 pairname=None, attrlist=None, towin=True, align=True, showwhtspc=False):
942 """
960 """
943 print the string, text, with the specified colors and attributes, to
961 print the string, text, with the specified colors and attributes, to
944 the specified curses window object.
962 the specified curses window object.
945
963
946 the foreground and background colors are of the form
964 the foreground and background colors are of the form
947 curses.color_xxxx, where xxxx is one of: [black, blue, cyan, green,
965 curses.color_xxxx, where xxxx is one of: [black, blue, cyan, green,
948 magenta, red, white, yellow]. if pairname is provided, a color
966 magenta, red, white, yellow]. if pairname is provided, a color
949 pair will be looked up in the self.colorpairnames dictionary.
967 pair will be looked up in the self.colorpairnames dictionary.
950
968
951 attrlist is a list containing text attributes in the form of
969 attrlist is a list containing text attributes in the form of
952 curses.a_xxxx, where xxxx can be: [bold, dim, normal, standout,
970 curses.a_xxxx, where xxxx can be: [bold, dim, normal, standout,
953 underline].
971 underline].
954
972
955 if align == True, whitespace is added to the printed string such that
973 if align == True, whitespace is added to the printed string such that
956 the string stretches to the right border of the window.
974 the string stretches to the right border of the window.
957
975
958 if showwhtspc == True, trailing whitespace of a string is highlighted.
976 if showwhtspc == True, trailing whitespace of a string is highlighted.
959 """
977 """
960 # preprocess the text, converting tabs to spaces
978 # preprocess the text, converting tabs to spaces
961 text = text.expandtabs(4)
979 text = text.expandtabs(4)
962 # strip \n, and convert control characters to ^[char] representation
980 # strip \n, and convert control characters to ^[char] representation
963 text = re.sub(br'[\x00-\x08\x0a-\x1f]',
981 text = re.sub(br'[\x00-\x08\x0a-\x1f]',
964 lambda m:'^' + chr(ord(m.group()) + 64), text.strip('\n'))
982 lambda m:'^' + chr(ord(m.group()) + 64), text.strip('\n'))
965
983
966 if pair is not None:
984 if pair is not None:
967 colorpair = pair
985 colorpair = pair
968 elif pairname is not None:
986 elif pairname is not None:
969 colorpair = self.colorpairnames[pairname]
987 colorpair = self.colorpairnames[pairname]
970 else:
988 else:
971 if fgcolor is None:
989 if fgcolor is None:
972 fgcolor = -1
990 fgcolor = -1
973 if bgcolor is None:
991 if bgcolor is None:
974 bgcolor = -1
992 bgcolor = -1
975 if (fgcolor, bgcolor) in self.colorpairs:
993 if (fgcolor, bgcolor) in self.colorpairs:
976 colorpair = self.colorpairs[(fgcolor, bgcolor)]
994 colorpair = self.colorpairs[(fgcolor, bgcolor)]
977 else:
995 else:
978 colorpair = self.getcolorpair(fgcolor, bgcolor)
996 colorpair = self.getcolorpair(fgcolor, bgcolor)
979 # add attributes if possible
997 # add attributes if possible
980 if attrlist is None:
998 if attrlist is None:
981 attrlist = []
999 attrlist = []
982 if colorpair < 256:
1000 if colorpair < 256:
983 # then it is safe to apply all attributes
1001 # then it is safe to apply all attributes
984 for textattr in attrlist:
1002 for textattr in attrlist:
985 colorpair |= textattr
1003 colorpair |= textattr
986 else:
1004 else:
987 # just apply a select few (safe?) attributes
1005 # just apply a select few (safe?) attributes
988 for textattr in (curses.A_UNDERLINE, curses.A_BOLD):
1006 for textattr in (curses.A_UNDERLINE, curses.A_BOLD):
989 if textattr in attrlist:
1007 if textattr in attrlist:
990 colorpair |= textattr
1008 colorpair |= textattr
991
1009
992 y, xstart = self.chunkpad.getyx()
1010 y, xstart = self.chunkpad.getyx()
993 t = "" # variable for counting lines printed
1011 t = "" # variable for counting lines printed
994 # if requested, show trailing whitespace
1012 # if requested, show trailing whitespace
995 if showwhtspc:
1013 if showwhtspc:
996 origlen = len(text)
1014 origlen = len(text)
997 text = text.rstrip(' \n') # tabs have already been expanded
1015 text = text.rstrip(' \n') # tabs have already been expanded
998 strippedlen = len(text)
1016 strippedlen = len(text)
999 numtrailingspaces = origlen - strippedlen
1017 numtrailingspaces = origlen - strippedlen
1000
1018
1001 if towin:
1019 if towin:
1002 window.addstr(text, colorpair)
1020 window.addstr(text, colorpair)
1003 t += text
1021 t += text
1004
1022
1005 if showwhtspc:
1023 if showwhtspc:
1006 wscolorpair = colorpair | curses.A_REVERSE
1024 wscolorpair = colorpair | curses.A_REVERSE
1007 if towin:
1025 if towin:
1008 for i in range(numtrailingspaces):
1026 for i in range(numtrailingspaces):
1009 window.addch(curses.ACS_CKBOARD, wscolorpair)
1027 window.addch(curses.ACS_CKBOARD, wscolorpair)
1010 t += " " * numtrailingspaces
1028 t += " " * numtrailingspaces
1011
1029
1012 if align:
1030 if align:
1013 if towin:
1031 if towin:
1014 extrawhitespace = self.alignstring("", window)
1032 extrawhitespace = self.alignstring("", window)
1015 window.addstr(extrawhitespace, colorpair)
1033 window.addstr(extrawhitespace, colorpair)
1016 else:
1034 else:
1017 # need to use t, since the x position hasn't incremented
1035 # need to use t, since the x position hasn't incremented
1018 extrawhitespace = self.alignstring(t, window)
1036 extrawhitespace = self.alignstring(t, window)
1019 t += extrawhitespace
1037 t += extrawhitespace
1020
1038
1021 # is reset to 0 at the beginning of printitem()
1039 # is reset to 0 at the beginning of printitem()
1022
1040
1023 linesprinted = (xstart + len(t)) / self.xscreensize
1041 linesprinted = (xstart + len(t)) / self.xscreensize
1024 self.linesprintedtopadsofar += linesprinted
1042 self.linesprintedtopadsofar += linesprinted
1025 return t
1043 return t
1026
1044
1027 def _getstatuslinesegments(self):
1045 def _getstatuslinesegments(self):
1028 """-> [str]. return segments"""
1046 """-> [str]. return segments"""
1029 selected = self.currentselecteditem.applied
1047 selected = self.currentselecteditem.applied
1030 spaceselect = _('space: select')
1048 spaceselect = _('space/enter: select')
1031 spacedeselect = _('space: deselect')
1049 spacedeselect = _('space/enter: deselect')
1032 # Format the selected label into a place as long as the longer of the
1050 # Format the selected label into a place as long as the longer of the
1033 # two possible labels. This may vary by language.
1051 # two possible labels. This may vary by language.
1034 spacelen = max(len(spaceselect), len(spacedeselect))
1052 spacelen = max(len(spaceselect), len(spacedeselect))
1035 selectedlabel = '%-*s' % (spacelen,
1053 selectedlabel = '%-*s' % (spacelen,
1036 spacedeselect if selected else spaceselect)
1054 spacedeselect if selected else spaceselect)
1037 segments = [
1055 segments = [
1038 _headermessages[self.operation],
1056 _headermessages[self.operation],
1039 '-',
1057 '-',
1040 _('[x]=selected **=collapsed'),
1058 _('[x]=selected **=collapsed'),
1041 _('c: confirm'),
1059 _('c: confirm'),
1042 _('q: abort'),
1060 _('q: abort'),
1043 _('arrow keys: move/expand/collapse'),
1061 _('arrow keys: move/expand/collapse'),
1044 selectedlabel,
1062 selectedlabel,
1045 _('?: help'),
1063 _('?: help'),
1046 ]
1064 ]
1047 return segments
1065 return segments
1048
1066
1049 def _getstatuslines(self):
1067 def _getstatuslines(self):
1050 """() -> [str]. return short help used in the top status window"""
1068 """() -> [str]. return short help used in the top status window"""
1051 if self.errorstr is not None:
1069 if self.errorstr is not None:
1052 lines = [self.errorstr, _('Press any key to continue')]
1070 lines = [self.errorstr, _('Press any key to continue')]
1053 else:
1071 else:
1054 # wrap segments to lines
1072 # wrap segments to lines
1055 segments = self._getstatuslinesegments()
1073 segments = self._getstatuslinesegments()
1056 width = self.xscreensize
1074 width = self.xscreensize
1057 lines = []
1075 lines = []
1058 lastwidth = width
1076 lastwidth = width
1059 for s in segments:
1077 for s in segments:
1060 w = encoding.colwidth(s)
1078 w = encoding.colwidth(s)
1061 sep = ' ' * (1 + (s and s[0] not in '-['))
1079 sep = ' ' * (1 + (s and s[0] not in '-['))
1062 if lastwidth + w + len(sep) >= width:
1080 if lastwidth + w + len(sep) >= width:
1063 lines.append(s)
1081 lines.append(s)
1064 lastwidth = w
1082 lastwidth = w
1065 else:
1083 else:
1066 lines[-1] += sep + s
1084 lines[-1] += sep + s
1067 lastwidth += w + len(sep)
1085 lastwidth += w + len(sep)
1068 if len(lines) != self.numstatuslines:
1086 if len(lines) != self.numstatuslines:
1069 self.numstatuslines = len(lines)
1087 self.numstatuslines = len(lines)
1070 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1088 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1071 return [stringutil.ellipsis(l, self.xscreensize - 1) for l in lines]
1089 return [stringutil.ellipsis(l, self.xscreensize - 1) for l in lines]
1072
1090
1073 def updatescreen(self):
1091 def updatescreen(self):
1074 self.statuswin.erase()
1092 self.statuswin.erase()
1075 self.chunkpad.erase()
1093 self.chunkpad.erase()
1076
1094
1077 printstring = self.printstring
1095 printstring = self.printstring
1078
1096
1079 # print out the status lines at the top
1097 # print out the status lines at the top
1080 try:
1098 try:
1081 for line in self._getstatuslines():
1099 for line in self._getstatuslines():
1082 printstring(self.statuswin, line, pairname="legend")
1100 printstring(self.statuswin, line, pairname="legend")
1083 self.statuswin.refresh()
1101 self.statuswin.refresh()
1084 except curses.error:
1102 except curses.error:
1085 pass
1103 pass
1086 if self.errorstr is not None:
1104 if self.errorstr is not None:
1087 return
1105 return
1088
1106
1089 # print out the patch in the remaining part of the window
1107 # print out the patch in the remaining part of the window
1090 try:
1108 try:
1091 self.printitem()
1109 self.printitem()
1092 self.updatescroll()
1110 self.updatescroll()
1093 self.chunkpad.refresh(self.firstlineofpadtoprint, 0,
1111 self.chunkpad.refresh(self.firstlineofpadtoprint, 0,
1094 self.numstatuslines, 0,
1112 self.numstatuslines, 0,
1095 self.yscreensize - self.numstatuslines,
1113 self.yscreensize - self.numstatuslines,
1096 self.xscreensize)
1114 self.xscreensize)
1097 except curses.error:
1115 except curses.error:
1098 pass
1116 pass
1099
1117
1100 def getstatusprefixstring(self, item):
1118 def getstatusprefixstring(self, item):
1101 """
1119 """
1102 create a string to prefix a line with which indicates whether 'item'
1120 create a string to prefix a line with which indicates whether 'item'
1103 is applied and/or folded.
1121 is applied and/or folded.
1104 """
1122 """
1105
1123
1106 # create checkbox string
1124 # create checkbox string
1107 if item.applied:
1125 if item.applied:
1108 if not isinstance(item, uihunkline) and item.partial:
1126 if not isinstance(item, uihunkline) and item.partial:
1109 checkbox = "[~]"
1127 checkbox = "[~]"
1110 else:
1128 else:
1111 checkbox = "[x]"
1129 checkbox = "[x]"
1112 else:
1130 else:
1113 checkbox = "[ ]"
1131 checkbox = "[ ]"
1114
1132
1115 try:
1133 try:
1116 if item.folded:
1134 if item.folded:
1117 checkbox += "**"
1135 checkbox += "**"
1118 if isinstance(item, uiheader):
1136 if isinstance(item, uiheader):
1119 # one of "m", "a", or "d" (modified, added, deleted)
1137 # one of "m", "a", or "d" (modified, added, deleted)
1120 filestatus = item.changetype
1138 filestatus = item.changetype
1121
1139
1122 checkbox += filestatus + " "
1140 checkbox += filestatus + " "
1123 else:
1141 else:
1124 checkbox += " "
1142 checkbox += " "
1125 if isinstance(item, uiheader):
1143 if isinstance(item, uiheader):
1126 # add two more spaces for headers
1144 # add two more spaces for headers
1127 checkbox += " "
1145 checkbox += " "
1128 except AttributeError: # not foldable
1146 except AttributeError: # not foldable
1129 checkbox += " "
1147 checkbox += " "
1130
1148
1131 return checkbox
1149 return checkbox
1132
1150
1133 def printheader(self, header, selected=False, towin=True,
1151 def printheader(self, header, selected=False, towin=True,
1134 ignorefolding=False):
1152 ignorefolding=False):
1135 """
1153 """
1136 print the header to the pad. if countlines is True, don't print
1154 print the header to the pad. if countlines is True, don't print
1137 anything, but just count the number of lines which would be printed.
1155 anything, but just count the number of lines which would be printed.
1138 """
1156 """
1139
1157
1140 outstr = ""
1158 outstr = ""
1141 text = header.prettystr()
1159 text = header.prettystr()
1142 chunkindex = self.chunklist.index(header)
1160 chunkindex = self.chunklist.index(header)
1143
1161
1144 if chunkindex != 0 and not header.folded:
1162 if chunkindex != 0 and not header.folded:
1145 # add separating line before headers
1163 # add separating line before headers
1146 outstr += self.printstring(self.chunkpad, '_' * self.xscreensize,
1164 outstr += self.printstring(self.chunkpad, '_' * self.xscreensize,
1147 towin=towin, align=False)
1165 towin=towin, align=False)
1148 # select color-pair based on if the header is selected
1166 # select color-pair based on if the header is selected
1149 colorpair = self.getcolorpair(name=selected and "selected" or "normal",
1167 colorpair = self.getcolorpair(name=selected and "selected" or "normal",
1150 attrlist=[curses.A_BOLD])
1168 attrlist=[curses.A_BOLD])
1151
1169
1152 # print out each line of the chunk, expanding it to screen width
1170 # print out each line of the chunk, expanding it to screen width
1153
1171
1154 # number of characters to indent lines on this level by
1172 # number of characters to indent lines on this level by
1155 indentnumchars = 0
1173 indentnumchars = 0
1156 checkbox = self.getstatusprefixstring(header)
1174 checkbox = self.getstatusprefixstring(header)
1157 if not header.folded or ignorefolding:
1175 if not header.folded or ignorefolding:
1158 textlist = text.split("\n")
1176 textlist = text.split("\n")
1159 linestr = checkbox + textlist[0]
1177 linestr = checkbox + textlist[0]
1160 else:
1178 else:
1161 linestr = checkbox + header.filename()
1179 linestr = checkbox + header.filename()
1162 outstr += self.printstring(self.chunkpad, linestr, pair=colorpair,
1180 outstr += self.printstring(self.chunkpad, linestr, pair=colorpair,
1163 towin=towin)
1181 towin=towin)
1164 if not header.folded or ignorefolding:
1182 if not header.folded or ignorefolding:
1165 if len(textlist) > 1:
1183 if len(textlist) > 1:
1166 for line in textlist[1:]:
1184 for line in textlist[1:]:
1167 linestr = " "*(indentnumchars + len(checkbox)) + line
1185 linestr = " "*(indentnumchars + len(checkbox)) + line
1168 outstr += self.printstring(self.chunkpad, linestr,
1186 outstr += self.printstring(self.chunkpad, linestr,
1169 pair=colorpair, towin=towin)
1187 pair=colorpair, towin=towin)
1170
1188
1171 return outstr
1189 return outstr
1172
1190
1173 def printhunklinesbefore(self, hunk, selected=False, towin=True,
1191 def printhunklinesbefore(self, hunk, selected=False, towin=True,
1174 ignorefolding=False):
1192 ignorefolding=False):
1175 "includes start/end line indicator"
1193 "includes start/end line indicator"
1176 outstr = ""
1194 outstr = ""
1177 # where hunk is in list of siblings
1195 # where hunk is in list of siblings
1178 hunkindex = hunk.header.hunks.index(hunk)
1196 hunkindex = hunk.header.hunks.index(hunk)
1179
1197
1180 if hunkindex != 0:
1198 if hunkindex != 0:
1181 # add separating line before headers
1199 # add separating line before headers
1182 outstr += self.printstring(self.chunkpad, ' '*self.xscreensize,
1200 outstr += self.printstring(self.chunkpad, ' '*self.xscreensize,
1183 towin=towin, align=False)
1201 towin=towin, align=False)
1184
1202
1185 colorpair = self.getcolorpair(name=selected and "selected" or "normal",
1203 colorpair = self.getcolorpair(name=selected and "selected" or "normal",
1186 attrlist=[curses.A_BOLD])
1204 attrlist=[curses.A_BOLD])
1187
1205
1188 # print out from-to line with checkbox
1206 # print out from-to line with checkbox
1189 checkbox = self.getstatusprefixstring(hunk)
1207 checkbox = self.getstatusprefixstring(hunk)
1190
1208
1191 lineprefix = " "*self.hunkindentnumchars + checkbox
1209 lineprefix = " "*self.hunkindentnumchars + checkbox
1192 frtoline = " " + hunk.getfromtoline().strip("\n")
1210 frtoline = " " + hunk.getfromtoline().strip("\n")
1193
1211
1194 outstr += self.printstring(self.chunkpad, lineprefix, towin=towin,
1212 outstr += self.printstring(self.chunkpad, lineprefix, towin=towin,
1195 align=False) # add uncolored checkbox/indent
1213 align=False) # add uncolored checkbox/indent
1196 outstr += self.printstring(self.chunkpad, frtoline, pair=colorpair,
1214 outstr += self.printstring(self.chunkpad, frtoline, pair=colorpair,
1197 towin=towin)
1215 towin=towin)
1198
1216
1199 if hunk.folded and not ignorefolding:
1217 if hunk.folded and not ignorefolding:
1200 # skip remainder of output
1218 # skip remainder of output
1201 return outstr
1219 return outstr
1202
1220
1203 # print out lines of the chunk preceeding changed-lines
1221 # print out lines of the chunk preceeding changed-lines
1204 for line in hunk.before:
1222 for line in hunk.before:
1205 linestr = " "*(self.hunklineindentnumchars + len(checkbox)) + line
1223 linestr = " "*(self.hunklineindentnumchars + len(checkbox)) + line
1206 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1224 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1207
1225
1208 return outstr
1226 return outstr
1209
1227
1210 def printhunklinesafter(self, hunk, towin=True, ignorefolding=False):
1228 def printhunklinesafter(self, hunk, towin=True, ignorefolding=False):
1211 outstr = ""
1229 outstr = ""
1212 if hunk.folded and not ignorefolding:
1230 if hunk.folded and not ignorefolding:
1213 return outstr
1231 return outstr
1214
1232
1215 # a bit superfluous, but to avoid hard-coding indent amount
1233 # a bit superfluous, but to avoid hard-coding indent amount
1216 checkbox = self.getstatusprefixstring(hunk)
1234 checkbox = self.getstatusprefixstring(hunk)
1217 for line in hunk.after:
1235 for line in hunk.after:
1218 linestr = " "*(self.hunklineindentnumchars + len(checkbox)) + line
1236 linestr = " "*(self.hunklineindentnumchars + len(checkbox)) + line
1219 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1237 outstr += self.printstring(self.chunkpad, linestr, towin=towin)
1220
1238
1221 return outstr
1239 return outstr
1222
1240
1223 def printhunkchangedline(self, hunkline, selected=False, towin=True):
1241 def printhunkchangedline(self, hunkline, selected=False, towin=True):
1224 outstr = ""
1242 outstr = ""
1225 checkbox = self.getstatusprefixstring(hunkline)
1243 checkbox = self.getstatusprefixstring(hunkline)
1226
1244
1227 linestr = hunkline.prettystr().strip("\n")
1245 linestr = hunkline.prettystr().strip("\n")
1228
1246
1229 # select color-pair based on whether line is an addition/removal
1247 # select color-pair based on whether line is an addition/removal
1230 if selected:
1248 if selected:
1231 colorpair = self.getcolorpair(name="selected")
1249 colorpair = self.getcolorpair(name="selected")
1232 elif linestr.startswith("+"):
1250 elif linestr.startswith("+"):
1233 colorpair = self.getcolorpair(name="addition")
1251 colorpair = self.getcolorpair(name="addition")
1234 elif linestr.startswith("-"):
1252 elif linestr.startswith("-"):
1235 colorpair = self.getcolorpair(name="deletion")
1253 colorpair = self.getcolorpair(name="deletion")
1236 elif linestr.startswith("\\"):
1254 elif linestr.startswith("\\"):
1237 colorpair = self.getcolorpair(name="normal")
1255 colorpair = self.getcolorpair(name="normal")
1238
1256
1239 lineprefix = " "*self.hunklineindentnumchars + checkbox
1257 lineprefix = " "*self.hunklineindentnumchars + checkbox
1240 outstr += self.printstring(self.chunkpad, lineprefix, towin=towin,
1258 outstr += self.printstring(self.chunkpad, lineprefix, towin=towin,
1241 align=False) # add uncolored checkbox/indent
1259 align=False) # add uncolored checkbox/indent
1242 outstr += self.printstring(self.chunkpad, linestr, pair=colorpair,
1260 outstr += self.printstring(self.chunkpad, linestr, pair=colorpair,
1243 towin=towin, showwhtspc=True)
1261 towin=towin, showwhtspc=True)
1244 return outstr
1262 return outstr
1245
1263
1246 def printitem(self, item=None, ignorefolding=False, recursechildren=True,
1264 def printitem(self, item=None, ignorefolding=False, recursechildren=True,
1247 towin=True):
1265 towin=True):
1248 """
1266 """
1249 use __printitem() to print the the specified item.applied.
1267 use __printitem() to print the the specified item.applied.
1250 if item is not specified, then print the entire patch.
1268 if item is not specified, then print the entire patch.
1251 (hiding folded elements, etc. -- see __printitem() docstring)
1269 (hiding folded elements, etc. -- see __printitem() docstring)
1252 """
1270 """
1253
1271
1254 if item is None:
1272 if item is None:
1255 item = self.headerlist
1273 item = self.headerlist
1256 if recursechildren:
1274 if recursechildren:
1257 self.linesprintedtopadsofar = 0
1275 self.linesprintedtopadsofar = 0
1258
1276
1259 outstr = []
1277 outstr = []
1260 self.__printitem(item, ignorefolding, recursechildren, outstr,
1278 self.__printitem(item, ignorefolding, recursechildren, outstr,
1261 towin=towin)
1279 towin=towin)
1262 return ''.join(outstr)
1280 return ''.join(outstr)
1263
1281
1264 def outofdisplayedarea(self):
1282 def outofdisplayedarea(self):
1265 y, _ = self.chunkpad.getyx() # cursor location
1283 y, _ = self.chunkpad.getyx() # cursor location
1266 # * 2 here works but an optimization would be the max number of
1284 # * 2 here works but an optimization would be the max number of
1267 # consecutive non selectable lines
1285 # consecutive non selectable lines
1268 # i.e the max number of context line for any hunk in the patch
1286 # i.e the max number of context line for any hunk in the patch
1269 miny = min(0, self.firstlineofpadtoprint - self.yscreensize)
1287 miny = min(0, self.firstlineofpadtoprint - self.yscreensize)
1270 maxy = self.firstlineofpadtoprint + self.yscreensize * 2
1288 maxy = self.firstlineofpadtoprint + self.yscreensize * 2
1271 return y < miny or y > maxy
1289 return y < miny or y > maxy
1272
1290
1273 def handleselection(self, item, recursechildren):
1291 def handleselection(self, item, recursechildren):
1274 selected = (item is self.currentselecteditem)
1292 selected = (item is self.currentselecteditem)
1275 if selected and recursechildren:
1293 if selected and recursechildren:
1276 # assumes line numbering starting from line 0
1294 # assumes line numbering starting from line 0
1277 self.selecteditemstartline = self.linesprintedtopadsofar
1295 self.selecteditemstartline = self.linesprintedtopadsofar
1278 selecteditemlines = self.getnumlinesdisplayed(item,
1296 selecteditemlines = self.getnumlinesdisplayed(item,
1279 recursechildren=False)
1297 recursechildren=False)
1280 self.selecteditemendline = (self.selecteditemstartline +
1298 self.selecteditemendline = (self.selecteditemstartline +
1281 selecteditemlines - 1)
1299 selecteditemlines - 1)
1282 return selected
1300 return selected
1283
1301
1284 def __printitem(self, item, ignorefolding, recursechildren, outstr,
1302 def __printitem(self, item, ignorefolding, recursechildren, outstr,
1285 towin=True):
1303 towin=True):
1286 """
1304 """
1287 recursive method for printing out patch/header/hunk/hunk-line data to
1305 recursive method for printing out patch/header/hunk/hunk-line data to
1288 screen. also returns a string with all of the content of the displayed
1306 screen. also returns a string with all of the content of the displayed
1289 patch (not including coloring, etc.).
1307 patch (not including coloring, etc.).
1290
1308
1291 if ignorefolding is True, then folded items are printed out.
1309 if ignorefolding is True, then folded items are printed out.
1292
1310
1293 if recursechildren is False, then only print the item without its
1311 if recursechildren is False, then only print the item without its
1294 child items.
1312 child items.
1295 """
1313 """
1296
1314
1297 if towin and self.outofdisplayedarea():
1315 if towin and self.outofdisplayedarea():
1298 return
1316 return
1299
1317
1300 selected = self.handleselection(item, recursechildren)
1318 selected = self.handleselection(item, recursechildren)
1301
1319
1302 # patch object is a list of headers
1320 # patch object is a list of headers
1303 if isinstance(item, patch):
1321 if isinstance(item, patch):
1304 if recursechildren:
1322 if recursechildren:
1305 for hdr in item:
1323 for hdr in item:
1306 self.__printitem(hdr, ignorefolding,
1324 self.__printitem(hdr, ignorefolding,
1307 recursechildren, outstr, towin)
1325 recursechildren, outstr, towin)
1308 # todo: eliminate all isinstance() calls
1326 # todo: eliminate all isinstance() calls
1309 if isinstance(item, uiheader):
1327 if isinstance(item, uiheader):
1310 outstr.append(self.printheader(item, selected, towin=towin,
1328 outstr.append(self.printheader(item, selected, towin=towin,
1311 ignorefolding=ignorefolding))
1329 ignorefolding=ignorefolding))
1312 if recursechildren:
1330 if recursechildren:
1313 for hnk in item.hunks:
1331 for hnk in item.hunks:
1314 self.__printitem(hnk, ignorefolding,
1332 self.__printitem(hnk, ignorefolding,
1315 recursechildren, outstr, towin)
1333 recursechildren, outstr, towin)
1316 elif (isinstance(item, uihunk) and
1334 elif (isinstance(item, uihunk) and
1317 ((not item.header.folded) or ignorefolding)):
1335 ((not item.header.folded) or ignorefolding)):
1318 # print the hunk data which comes before the changed-lines
1336 # print the hunk data which comes before the changed-lines
1319 outstr.append(self.printhunklinesbefore(item, selected, towin=towin,
1337 outstr.append(self.printhunklinesbefore(item, selected, towin=towin,
1320 ignorefolding=ignorefolding))
1338 ignorefolding=ignorefolding))
1321 if recursechildren:
1339 if recursechildren:
1322 for l in item.changedlines:
1340 for l in item.changedlines:
1323 self.__printitem(l, ignorefolding,
1341 self.__printitem(l, ignorefolding,
1324 recursechildren, outstr, towin)
1342 recursechildren, outstr, towin)
1325 outstr.append(self.printhunklinesafter(item, towin=towin,
1343 outstr.append(self.printhunklinesafter(item, towin=towin,
1326 ignorefolding=ignorefolding))
1344 ignorefolding=ignorefolding))
1327 elif (isinstance(item, uihunkline) and
1345 elif (isinstance(item, uihunkline) and
1328 ((not item.hunk.folded) or ignorefolding)):
1346 ((not item.hunk.folded) or ignorefolding)):
1329 outstr.append(self.printhunkchangedline(item, selected,
1347 outstr.append(self.printhunkchangedline(item, selected,
1330 towin=towin))
1348 towin=towin))
1331
1349
1332 return outstr
1350 return outstr
1333
1351
1334 def getnumlinesdisplayed(self, item=None, ignorefolding=False,
1352 def getnumlinesdisplayed(self, item=None, ignorefolding=False,
1335 recursechildren=True):
1353 recursechildren=True):
1336 """
1354 """
1337 return the number of lines which would be displayed if the item were
1355 return the number of lines which would be displayed if the item were
1338 to be printed to the display. the item will not be printed to the
1356 to be printed to the display. the item will not be printed to the
1339 display (pad).
1357 display (pad).
1340 if no item is given, assume the entire patch.
1358 if no item is given, assume the entire patch.
1341 if ignorefolding is True, folded items will be unfolded when counting
1359 if ignorefolding is True, folded items will be unfolded when counting
1342 the number of lines.
1360 the number of lines.
1343 """
1361 """
1344
1362
1345 # temporarily disable printing to windows by printstring
1363 # temporarily disable printing to windows by printstring
1346 patchdisplaystring = self.printitem(item, ignorefolding,
1364 patchdisplaystring = self.printitem(item, ignorefolding,
1347 recursechildren, towin=False)
1365 recursechildren, towin=False)
1348 numlines = len(patchdisplaystring) // self.xscreensize
1366 numlines = len(patchdisplaystring) // self.xscreensize
1349 return numlines
1367 return numlines
1350
1368
1351 def sigwinchhandler(self, n, frame):
1369 def sigwinchhandler(self, n, frame):
1352 "handle window resizing"
1370 "handle window resizing"
1353 try:
1371 try:
1354 curses.endwin()
1372 curses.endwin()
1355 self.xscreensize, self.yscreensize = scmutil.termsize(self.ui)
1373 self.xscreensize, self.yscreensize = scmutil.termsize(self.ui)
1356 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1374 self.statuswin.resize(self.numstatuslines, self.xscreensize)
1357 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1375 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1358 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1376 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1359 except curses.error:
1377 except curses.error:
1360 pass
1378 pass
1361
1379
1362 def getcolorpair(self, fgcolor=None, bgcolor=None, name=None,
1380 def getcolorpair(self, fgcolor=None, bgcolor=None, name=None,
1363 attrlist=None):
1381 attrlist=None):
1364 """
1382 """
1365 get a curses color pair, adding it to self.colorpairs if it is not
1383 get a curses color pair, adding it to self.colorpairs if it is not
1366 already defined. an optional string, name, can be passed as a shortcut
1384 already defined. an optional string, name, can be passed as a shortcut
1367 for referring to the color-pair. by default, if no arguments are
1385 for referring to the color-pair. by default, if no arguments are
1368 specified, the white foreground / black background color-pair is
1386 specified, the white foreground / black background color-pair is
1369 returned.
1387 returned.
1370
1388
1371 it is expected that this function will be used exclusively for
1389 it is expected that this function will be used exclusively for
1372 initializing color pairs, and not curses.init_pair().
1390 initializing color pairs, and not curses.init_pair().
1373
1391
1374 attrlist is used to 'flavor' the returned color-pair. this information
1392 attrlist is used to 'flavor' the returned color-pair. this information
1375 is not stored in self.colorpairs. it contains attribute values like
1393 is not stored in self.colorpairs. it contains attribute values like
1376 curses.A_BOLD.
1394 curses.A_BOLD.
1377 """
1395 """
1378
1396
1379 if (name is not None) and name in self.colorpairnames:
1397 if (name is not None) and name in self.colorpairnames:
1380 # then get the associated color pair and return it
1398 # then get the associated color pair and return it
1381 colorpair = self.colorpairnames[name]
1399 colorpair = self.colorpairnames[name]
1382 else:
1400 else:
1383 if fgcolor is None:
1401 if fgcolor is None:
1384 fgcolor = -1
1402 fgcolor = -1
1385 if bgcolor is None:
1403 if bgcolor is None:
1386 bgcolor = -1
1404 bgcolor = -1
1387 if (fgcolor, bgcolor) in self.colorpairs:
1405 if (fgcolor, bgcolor) in self.colorpairs:
1388 colorpair = self.colorpairs[(fgcolor, bgcolor)]
1406 colorpair = self.colorpairs[(fgcolor, bgcolor)]
1389 else:
1407 else:
1390 pairindex = len(self.colorpairs) + 1
1408 pairindex = len(self.colorpairs) + 1
1391 if self.usecolor:
1409 if self.usecolor:
1392 curses.init_pair(pairindex, fgcolor, bgcolor)
1410 curses.init_pair(pairindex, fgcolor, bgcolor)
1393 colorpair = self.colorpairs[(fgcolor, bgcolor)] = (
1411 colorpair = self.colorpairs[(fgcolor, bgcolor)] = (
1394 curses.color_pair(pairindex))
1412 curses.color_pair(pairindex))
1395 if name is not None:
1413 if name is not None:
1396 self.colorpairnames[name] = curses.color_pair(pairindex)
1414 self.colorpairnames[name] = curses.color_pair(pairindex)
1397 else:
1415 else:
1398 cval = 0
1416 cval = 0
1399 if name is not None:
1417 if name is not None:
1400 if name == 'selected':
1418 if name == 'selected':
1401 cval = curses.A_REVERSE
1419 cval = curses.A_REVERSE
1402 self.colorpairnames[name] = cval
1420 self.colorpairnames[name] = cval
1403 colorpair = self.colorpairs[(fgcolor, bgcolor)] = cval
1421 colorpair = self.colorpairs[(fgcolor, bgcolor)] = cval
1404
1422
1405 # add attributes if possible
1423 # add attributes if possible
1406 if attrlist is None:
1424 if attrlist is None:
1407 attrlist = []
1425 attrlist = []
1408 if colorpair < 256:
1426 if colorpair < 256:
1409 # then it is safe to apply all attributes
1427 # then it is safe to apply all attributes
1410 for textattr in attrlist:
1428 for textattr in attrlist:
1411 colorpair |= textattr
1429 colorpair |= textattr
1412 else:
1430 else:
1413 # just apply a select few (safe?) attributes
1431 # just apply a select few (safe?) attributes
1414 for textattrib in (curses.A_UNDERLINE, curses.A_BOLD):
1432 for textattrib in (curses.A_UNDERLINE, curses.A_BOLD):
1415 if textattrib in attrlist:
1433 if textattrib in attrlist:
1416 colorpair |= textattrib
1434 colorpair |= textattrib
1417 return colorpair
1435 return colorpair
1418
1436
1419 def initcolorpair(self, *args, **kwargs):
1437 def initcolorpair(self, *args, **kwargs):
1420 "same as getcolorpair."
1438 "same as getcolorpair."
1421 self.getcolorpair(*args, **kwargs)
1439 self.getcolorpair(*args, **kwargs)
1422
1440
1423 def helpwindow(self):
1441 def helpwindow(self):
1424 "print a help window to the screen. exit after any keypress."
1442 "print a help window to the screen. exit after any keypress."
1425 helptext = _(
1443 helptext = _(
1426 """ [press any key to return to the patch-display]
1444 """ [press any key to return to the patch-display]
1427
1445
1428 crecord allows you to interactively choose among the changes you have made,
1446 crecord allows you to interactively choose among the changes you have made,
1429 and confirm only those changes you select for further processing by the command
1447 and confirm only those changes you select for further processing by the command
1430 you are running (commit/shelve/revert), after confirming the selected
1448 you are running (commit/shelve/revert), after confirming the selected
1431 changes, the unselected changes are still present in your working copy, so you
1449 changes, the unselected changes are still present in your working copy, so you
1432 can use crecord multiple times to split large changes into smaller changesets.
1450 can use crecord multiple times to split large changes into smaller changesets.
1433 the following are valid keystrokes:
1451 the following are valid keystrokes:
1434
1452
1435 [space] : (un-)select item ([~]/[x] = partly/fully applied)
1453 [space] : (un-)select item ([~]/[x] = partly/fully applied)
1454 [enter] : (un-)select item and go to next item of same type
1436 A : (un-)select all items
1455 A : (un-)select all items
1437 up/down-arrow [k/j] : go to previous/next unfolded item
1456 up/down-arrow [k/j] : go to previous/next unfolded item
1438 pgup/pgdn [K/J] : go to previous/next item of same type
1457 pgup/pgdn [K/J] : go to previous/next item of same type
1439 right/left-arrow [l/h] : go to child item / parent item
1458 right/left-arrow [l/h] : go to child item / parent item
1440 shift-left-arrow [H] : go to parent header / fold selected header
1459 shift-left-arrow [H] : go to parent header / fold selected header
1441 f : fold / unfold item, hiding/revealing its children
1460 f : fold / unfold item, hiding/revealing its children
1442 F : fold / unfold parent item and all of its ancestors
1461 F : fold / unfold parent item and all of its ancestors
1443 ctrl-l : scroll the selected line to the top of the screen
1462 ctrl-l : scroll the selected line to the top of the screen
1444 m : edit / resume editing the commit message
1463 m : edit / resume editing the commit message
1445 e : edit the currently selected hunk
1464 e : edit the currently selected hunk
1446 a : toggle amend mode, only with commit -i
1465 a : toggle amend mode, only with commit -i
1447 c : confirm selected changes
1466 c : confirm selected changes
1448 r : review/edit and confirm selected changes
1467 r : review/edit and confirm selected changes
1449 q : quit without confirming (no changes will be made)
1468 q : quit without confirming (no changes will be made)
1450 ? : help (what you're currently reading)""")
1469 ? : help (what you're currently reading)""")
1451
1470
1452 helpwin = curses.newwin(self.yscreensize, 0, 0, 0)
1471 helpwin = curses.newwin(self.yscreensize, 0, 0, 0)
1453 helplines = helptext.split("\n")
1472 helplines = helptext.split("\n")
1454 helplines = helplines + [" "]*(
1473 helplines = helplines + [" "]*(
1455 self.yscreensize - self.numstatuslines - len(helplines) - 1)
1474 self.yscreensize - self.numstatuslines - len(helplines) - 1)
1456 try:
1475 try:
1457 for line in helplines:
1476 for line in helplines:
1458 self.printstring(helpwin, line, pairname="legend")
1477 self.printstring(helpwin, line, pairname="legend")
1459 except curses.error:
1478 except curses.error:
1460 pass
1479 pass
1461 helpwin.refresh()
1480 helpwin.refresh()
1462 try:
1481 try:
1463 with self.ui.timeblockedsection('crecord'):
1482 with self.ui.timeblockedsection('crecord'):
1464 helpwin.getkey()
1483 helpwin.getkey()
1465 except curses.error:
1484 except curses.error:
1466 pass
1485 pass
1467
1486
1468 def commitMessageWindow(self):
1487 def commitMessageWindow(self):
1469 "Create a temporary commit message editing window on the screen."
1488 "Create a temporary commit message editing window on the screen."
1470
1489
1471 curses.raw()
1490 curses.raw()
1472 curses.def_prog_mode()
1491 curses.def_prog_mode()
1473 curses.endwin()
1492 curses.endwin()
1474 self.commenttext = self.ui.edit(self.commenttext, self.ui.username())
1493 self.commenttext = self.ui.edit(self.commenttext, self.ui.username())
1475 curses.cbreak()
1494 curses.cbreak()
1476 self.stdscr.refresh()
1495 self.stdscr.refresh()
1477 self.stdscr.keypad(1) # allow arrow-keys to continue to function
1496 self.stdscr.keypad(1) # allow arrow-keys to continue to function
1478
1497
1479 def confirmationwindow(self, windowtext):
1498 def confirmationwindow(self, windowtext):
1480 "display an informational window, then wait for and return a keypress."
1499 "display an informational window, then wait for and return a keypress."
1481
1500
1482 confirmwin = curses.newwin(self.yscreensize, 0, 0, 0)
1501 confirmwin = curses.newwin(self.yscreensize, 0, 0, 0)
1483 try:
1502 try:
1484 lines = windowtext.split("\n")
1503 lines = windowtext.split("\n")
1485 for line in lines:
1504 for line in lines:
1486 self.printstring(confirmwin, line, pairname="selected")
1505 self.printstring(confirmwin, line, pairname="selected")
1487 except curses.error:
1506 except curses.error:
1488 pass
1507 pass
1489 self.stdscr.refresh()
1508 self.stdscr.refresh()
1490 confirmwin.refresh()
1509 confirmwin.refresh()
1491 try:
1510 try:
1492 with self.ui.timeblockedsection('crecord'):
1511 with self.ui.timeblockedsection('crecord'):
1493 response = chr(self.stdscr.getch())
1512 response = chr(self.stdscr.getch())
1494 except ValueError:
1513 except ValueError:
1495 response = None
1514 response = None
1496
1515
1497 return response
1516 return response
1498
1517
1499 def reviewcommit(self):
1518 def reviewcommit(self):
1500 """ask for 'y' to be pressed to confirm selected. return True if
1519 """ask for 'y' to be pressed to confirm selected. return True if
1501 confirmed."""
1520 confirmed."""
1502 confirmtext = _(
1521 confirmtext = _(
1503 """if you answer yes to the following, the your currently chosen patch chunks
1522 """if you answer yes to the following, the your currently chosen patch chunks
1504 will be loaded into an editor. you may modify the patch from the editor, and
1523 will be loaded into an editor. you may modify the patch from the editor, and
1505 save the changes if you wish to change the patch. otherwise, you can just
1524 save the changes if you wish to change the patch. otherwise, you can just
1506 close the editor without saving to accept the current patch as-is.
1525 close the editor without saving to accept the current patch as-is.
1507
1526
1508 note: don't add/remove lines unless you also modify the range information.
1527 note: don't add/remove lines unless you also modify the range information.
1509 failing to follow this rule will result in the commit aborting.
1528 failing to follow this rule will result in the commit aborting.
1510
1529
1511 are you sure you want to review/edit and confirm the selected changes [yn]?
1530 are you sure you want to review/edit and confirm the selected changes [yn]?
1512 """)
1531 """)
1513 with self.ui.timeblockedsection('crecord'):
1532 with self.ui.timeblockedsection('crecord'):
1514 response = self.confirmationwindow(confirmtext)
1533 response = self.confirmationwindow(confirmtext)
1515 if response is None:
1534 if response is None:
1516 response = "n"
1535 response = "n"
1517 if response.lower().startswith("y"):
1536 if response.lower().startswith("y"):
1518 return True
1537 return True
1519 else:
1538 else:
1520 return False
1539 return False
1521
1540
1522 def toggleamend(self, opts, test):
1541 def toggleamend(self, opts, test):
1523 """Toggle the amend flag.
1542 """Toggle the amend flag.
1524
1543
1525 When the amend flag is set, a commit will modify the most recently
1544 When the amend flag is set, a commit will modify the most recently
1526 committed changeset, instead of creating a new changeset. Otherwise, a
1545 committed changeset, instead of creating a new changeset. Otherwise, a
1527 new changeset will be created (the normal commit behavior).
1546 new changeset will be created (the normal commit behavior).
1528 """
1547 """
1529
1548
1530 try:
1549 try:
1531 ver = float(util.version()[:3])
1550 ver = float(util.version()[:3])
1532 except ValueError:
1551 except ValueError:
1533 ver = 1
1552 ver = 1
1534 if ver < 2.19:
1553 if ver < 2.19:
1535 msg = _("The amend option is unavailable with hg versions < 2.2\n\n"
1554 msg = _("The amend option is unavailable with hg versions < 2.2\n\n"
1536 "Press any key to continue.")
1555 "Press any key to continue.")
1537 elif opts.get('amend') is None:
1556 elif opts.get('amend') is None:
1538 opts['amend'] = True
1557 opts['amend'] = True
1539 msg = _("Amend option is turned on -- committing the currently "
1558 msg = _("Amend option is turned on -- committing the currently "
1540 "selected changes will not create a new changeset, but "
1559 "selected changes will not create a new changeset, but "
1541 "instead update the most recently committed changeset.\n\n"
1560 "instead update the most recently committed changeset.\n\n"
1542 "Press any key to continue.")
1561 "Press any key to continue.")
1543 elif opts.get('amend') is True:
1562 elif opts.get('amend') is True:
1544 opts['amend'] = None
1563 opts['amend'] = None
1545 msg = _("Amend option is turned off -- committing the currently "
1564 msg = _("Amend option is turned off -- committing the currently "
1546 "selected changes will create a new changeset.\n\n"
1565 "selected changes will create a new changeset.\n\n"
1547 "Press any key to continue.")
1566 "Press any key to continue.")
1548 if not test:
1567 if not test:
1549 self.confirmationwindow(msg)
1568 self.confirmationwindow(msg)
1550
1569
1551 def recenterdisplayedarea(self):
1570 def recenterdisplayedarea(self):
1552 """
1571 """
1553 once we scrolled with pg up pg down we can be pointing outside of the
1572 once we scrolled with pg up pg down we can be pointing outside of the
1554 display zone. we print the patch with towin=False to compute the
1573 display zone. we print the patch with towin=False to compute the
1555 location of the selected item even though it is outside of the displayed
1574 location of the selected item even though it is outside of the displayed
1556 zone and then update the scroll.
1575 zone and then update the scroll.
1557 """
1576 """
1558 self.printitem(towin=False)
1577 self.printitem(towin=False)
1559 self.updatescroll()
1578 self.updatescroll()
1560
1579
1561 def toggleedit(self, item=None, test=False):
1580 def toggleedit(self, item=None, test=False):
1562 """
1581 """
1563 edit the currently selected chunk
1582 edit the currently selected chunk
1564 """
1583 """
1565 def updateui(self):
1584 def updateui(self):
1566 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1585 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1567 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1586 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1568 self.updatescroll()
1587 self.updatescroll()
1569 self.stdscr.refresh()
1588 self.stdscr.refresh()
1570 self.statuswin.refresh()
1589 self.statuswin.refresh()
1571 self.stdscr.keypad(1)
1590 self.stdscr.keypad(1)
1572
1591
1573 def editpatchwitheditor(self, chunk):
1592 def editpatchwitheditor(self, chunk):
1574 if chunk is None:
1593 if chunk is None:
1575 self.ui.write(_('cannot edit patch for whole file'))
1594 self.ui.write(_('cannot edit patch for whole file'))
1576 self.ui.write("\n")
1595 self.ui.write("\n")
1577 return None
1596 return None
1578 if chunk.header.binary():
1597 if chunk.header.binary():
1579 self.ui.write(_('cannot edit patch for binary file'))
1598 self.ui.write(_('cannot edit patch for binary file'))
1580 self.ui.write("\n")
1599 self.ui.write("\n")
1581 return None
1600 return None
1582
1601
1583 # write the initial patch
1602 # write the initial patch
1584 patch = stringio()
1603 patch = stringio()
1585 patch.write(diffhelptext + hunkhelptext)
1604 patch.write(diffhelptext + hunkhelptext)
1586 chunk.header.write(patch)
1605 chunk.header.write(patch)
1587 chunk.write(patch)
1606 chunk.write(patch)
1588
1607
1589 # start the editor and wait for it to complete
1608 # start the editor and wait for it to complete
1590 try:
1609 try:
1591 patch = self.ui.edit(patch.getvalue(), "", action="diff")
1610 patch = self.ui.edit(patch.getvalue(), "", action="diff")
1592 except error.Abort as exc:
1611 except error.Abort as exc:
1593 self.errorstr = str(exc)
1612 self.errorstr = str(exc)
1594 return None
1613 return None
1595
1614
1596 # remove comment lines
1615 # remove comment lines
1597 patch = [line + '\n' for line in patch.splitlines()
1616 patch = [line + '\n' for line in patch.splitlines()
1598 if not line.startswith('#')]
1617 if not line.startswith('#')]
1599 return patchmod.parsepatch(patch)
1618 return patchmod.parsepatch(patch)
1600
1619
1601 if item is None:
1620 if item is None:
1602 item = self.currentselecteditem
1621 item = self.currentselecteditem
1603 if isinstance(item, uiheader):
1622 if isinstance(item, uiheader):
1604 return
1623 return
1605 if isinstance(item, uihunkline):
1624 if isinstance(item, uihunkline):
1606 item = item.parentitem()
1625 item = item.parentitem()
1607 if not isinstance(item, uihunk):
1626 if not isinstance(item, uihunk):
1608 return
1627 return
1609
1628
1610 # To go back to that hunk or its replacement at the end of the edit
1629 # To go back to that hunk or its replacement at the end of the edit
1611 itemindex = item.parentitem().hunks.index(item)
1630 itemindex = item.parentitem().hunks.index(item)
1612
1631
1613 beforeadded, beforeremoved = item.added, item.removed
1632 beforeadded, beforeremoved = item.added, item.removed
1614 newpatches = editpatchwitheditor(self, item)
1633 newpatches = editpatchwitheditor(self, item)
1615 if newpatches is None:
1634 if newpatches is None:
1616 if not test:
1635 if not test:
1617 updateui(self)
1636 updateui(self)
1618 return
1637 return
1619 header = item.header
1638 header = item.header
1620 editedhunkindex = header.hunks.index(item)
1639 editedhunkindex = header.hunks.index(item)
1621 hunksbefore = header.hunks[:editedhunkindex]
1640 hunksbefore = header.hunks[:editedhunkindex]
1622 hunksafter = header.hunks[editedhunkindex + 1:]
1641 hunksafter = header.hunks[editedhunkindex + 1:]
1623 newpatchheader = newpatches[0]
1642 newpatchheader = newpatches[0]
1624 newhunks = [uihunk(h, header) for h in newpatchheader.hunks]
1643 newhunks = [uihunk(h, header) for h in newpatchheader.hunks]
1625 newadded = sum([h.added for h in newhunks])
1644 newadded = sum([h.added for h in newhunks])
1626 newremoved = sum([h.removed for h in newhunks])
1645 newremoved = sum([h.removed for h in newhunks])
1627 offset = (newadded - beforeadded) - (newremoved - beforeremoved)
1646 offset = (newadded - beforeadded) - (newremoved - beforeremoved)
1628
1647
1629 for h in hunksafter:
1648 for h in hunksafter:
1630 h.toline += offset
1649 h.toline += offset
1631 for h in newhunks:
1650 for h in newhunks:
1632 h.folded = False
1651 h.folded = False
1633 header.hunks = hunksbefore + newhunks + hunksafter
1652 header.hunks = hunksbefore + newhunks + hunksafter
1634 if self.emptypatch():
1653 if self.emptypatch():
1635 header.hunks = hunksbefore + [item] + hunksafter
1654 header.hunks = hunksbefore + [item] + hunksafter
1636 self.currentselecteditem = header
1655 self.currentselecteditem = header
1637 if len(header.hunks) > itemindex:
1656 if len(header.hunks) > itemindex:
1638 self.currentselecteditem = header.hunks[itemindex]
1657 self.currentselecteditem = header.hunks[itemindex]
1639
1658
1640 if not test:
1659 if not test:
1641 updateui(self)
1660 updateui(self)
1642
1661
1643 def emptypatch(self):
1662 def emptypatch(self):
1644 item = self.headerlist
1663 item = self.headerlist
1645 if not item:
1664 if not item:
1646 return True
1665 return True
1647 for header in item:
1666 for header in item:
1648 if header.hunks:
1667 if header.hunks:
1649 return False
1668 return False
1650 return True
1669 return True
1651
1670
1652 def handlekeypressed(self, keypressed, test=False):
1671 def handlekeypressed(self, keypressed, test=False):
1653 """
1672 """
1654 Perform actions based on pressed keys.
1673 Perform actions based on pressed keys.
1655
1674
1656 Return true to exit the main loop.
1675 Return true to exit the main loop.
1657 """
1676 """
1658 if keypressed in ["k", "KEY_UP"]:
1677 if keypressed in ["k", "KEY_UP"]:
1659 self.uparrowevent()
1678 self.uparrowevent()
1660 if keypressed in ["K", "KEY_PPAGE"]:
1679 if keypressed in ["K", "KEY_PPAGE"]:
1661 self.uparrowshiftevent()
1680 self.uparrowshiftevent()
1662 elif keypressed in ["j", "KEY_DOWN"]:
1681 elif keypressed in ["j", "KEY_DOWN"]:
1663 self.downarrowevent()
1682 self.downarrowevent()
1664 elif keypressed in ["J", "KEY_NPAGE"]:
1683 elif keypressed in ["J", "KEY_NPAGE"]:
1665 self.downarrowshiftevent()
1684 self.downarrowshiftevent()
1666 elif keypressed in ["l", "KEY_RIGHT"]:
1685 elif keypressed in ["l", "KEY_RIGHT"]:
1667 self.rightarrowevent()
1686 self.rightarrowevent()
1668 elif keypressed in ["h", "KEY_LEFT"]:
1687 elif keypressed in ["h", "KEY_LEFT"]:
1669 self.leftarrowevent()
1688 self.leftarrowevent()
1670 elif keypressed in ["H", "KEY_SLEFT"]:
1689 elif keypressed in ["H", "KEY_SLEFT"]:
1671 self.leftarrowshiftevent()
1690 self.leftarrowshiftevent()
1672 elif keypressed in ["q"]:
1691 elif keypressed in ["q"]:
1673 raise error.Abort(_('user quit'))
1692 raise error.Abort(_('user quit'))
1674 elif keypressed in ['a']:
1693 elif keypressed in ['a']:
1675 self.toggleamend(self.opts, test)
1694 self.toggleamend(self.opts, test)
1676 elif keypressed in ["c"]:
1695 elif keypressed in ["c"]:
1677 return True
1696 return True
1678 elif test and keypressed in ['X']:
1697 elif test and keypressed in ['X']:
1679 return True
1698 return True
1680 elif keypressed in ["r"]:
1699 elif keypressed in ["r"]:
1681 if self.reviewcommit():
1700 if self.reviewcommit():
1682 self.opts['review'] = True
1701 self.opts['review'] = True
1683 return True
1702 return True
1684 elif test and keypressed in ['R']:
1703 elif test and keypressed in ['R']:
1685 self.opts['review'] = True
1704 self.opts['review'] = True
1686 return True
1705 return True
1687 elif keypressed in [' '] or (test and keypressed in ["TOGGLE"]):
1706 elif keypressed in [' '] or (test and keypressed in ["TOGGLE"]):
1688 self.toggleapply()
1707 self.toggleapply()
1689 if self.ui.configbool('experimental', 'spacemovesdown'):
1708 elif keypressed in ['\n', 'KEY_ENTER']:
1690 self.downarrowevent()
1709 self.toggleapply()
1710 self.nextsametype()
1691 elif keypressed in ['A']:
1711 elif keypressed in ['A']:
1692 self.toggleall()
1712 self.toggleall()
1693 elif keypressed in ['e']:
1713 elif keypressed in ['e']:
1694 self.toggleedit(test=test)
1714 self.toggleedit(test=test)
1695 elif keypressed in ["f"]:
1715 elif keypressed in ["f"]:
1696 self.togglefolded()
1716 self.togglefolded()
1697 elif keypressed in ["F"]:
1717 elif keypressed in ["F"]:
1698 self.togglefolded(foldparent=True)
1718 self.togglefolded(foldparent=True)
1699 elif keypressed in ["m"]:
1719 elif keypressed in ["m"]:
1700 self.commitMessageWindow()
1720 self.commitMessageWindow()
1701 elif keypressed in ["?"]:
1721 elif keypressed in ["?"]:
1702 self.helpwindow()
1722 self.helpwindow()
1703 self.stdscr.clear()
1723 self.stdscr.clear()
1704 self.stdscr.refresh()
1724 self.stdscr.refresh()
1705 elif curses.unctrl(keypressed) in ["^L"]:
1725 elif curses.unctrl(keypressed) in ["^L"]:
1706 # scroll the current line to the top of the screen
1726 # scroll the current line to the top of the screen
1707 self.scrolllines(self.selecteditemstartline)
1727 self.scrolllines(self.selecteditemstartline)
1708
1728
1709 def main(self, stdscr):
1729 def main(self, stdscr):
1710 """
1730 """
1711 method to be wrapped by curses.wrapper() for selecting chunks.
1731 method to be wrapped by curses.wrapper() for selecting chunks.
1712 """
1732 """
1713
1733
1714 origsigwinch = sentinel = object()
1734 origsigwinch = sentinel = object()
1715 if util.safehasattr(signal, 'SIGWINCH'):
1735 if util.safehasattr(signal, 'SIGWINCH'):
1716 origsigwinch = signal.signal(signal.SIGWINCH,
1736 origsigwinch = signal.signal(signal.SIGWINCH,
1717 self.sigwinchhandler)
1737 self.sigwinchhandler)
1718 try:
1738 try:
1719 return self._main(stdscr)
1739 return self._main(stdscr)
1720 finally:
1740 finally:
1721 if origsigwinch is not sentinel:
1741 if origsigwinch is not sentinel:
1722 signal.signal(signal.SIGWINCH, origsigwinch)
1742 signal.signal(signal.SIGWINCH, origsigwinch)
1723
1743
1724 def _main(self, stdscr):
1744 def _main(self, stdscr):
1725 self.stdscr = stdscr
1745 self.stdscr = stdscr
1726 # error during initialization, cannot be printed in the curses
1746 # error during initialization, cannot be printed in the curses
1727 # interface, it should be printed by the calling code
1747 # interface, it should be printed by the calling code
1728 self.initexc = None
1748 self.initexc = None
1729 self.yscreensize, self.xscreensize = self.stdscr.getmaxyx()
1749 self.yscreensize, self.xscreensize = self.stdscr.getmaxyx()
1730
1750
1731 curses.start_color()
1751 curses.start_color()
1732 try:
1752 try:
1733 curses.use_default_colors()
1753 curses.use_default_colors()
1734 except curses.error:
1754 except curses.error:
1735 self.usecolor = False
1755 self.usecolor = False
1736
1756
1737 # available colors: black, blue, cyan, green, magenta, white, yellow
1757 # available colors: black, blue, cyan, green, magenta, white, yellow
1738 # init_pair(color_id, foreground_color, background_color)
1758 # init_pair(color_id, foreground_color, background_color)
1739 self.initcolorpair(None, None, name="normal")
1759 self.initcolorpair(None, None, name="normal")
1740 self.initcolorpair(curses.COLOR_WHITE, curses.COLOR_MAGENTA,
1760 self.initcolorpair(curses.COLOR_WHITE, curses.COLOR_MAGENTA,
1741 name="selected")
1761 name="selected")
1742 self.initcolorpair(curses.COLOR_RED, None, name="deletion")
1762 self.initcolorpair(curses.COLOR_RED, None, name="deletion")
1743 self.initcolorpair(curses.COLOR_GREEN, None, name="addition")
1763 self.initcolorpair(curses.COLOR_GREEN, None, name="addition")
1744 self.initcolorpair(curses.COLOR_WHITE, curses.COLOR_BLUE, name="legend")
1764 self.initcolorpair(curses.COLOR_WHITE, curses.COLOR_BLUE, name="legend")
1745 # newwin([height, width,] begin_y, begin_x)
1765 # newwin([height, width,] begin_y, begin_x)
1746 self.statuswin = curses.newwin(self.numstatuslines, 0, 0, 0)
1766 self.statuswin = curses.newwin(self.numstatuslines, 0, 0, 0)
1747 self.statuswin.keypad(1) # interpret arrow-key, etc. esc sequences
1767 self.statuswin.keypad(1) # interpret arrow-key, etc. esc sequences
1748
1768
1749 # figure out how much space to allocate for the chunk-pad which is
1769 # figure out how much space to allocate for the chunk-pad which is
1750 # used for displaying the patch
1770 # used for displaying the patch
1751
1771
1752 # stupid hack to prevent getnumlinesdisplayed from failing
1772 # stupid hack to prevent getnumlinesdisplayed from failing
1753 self.chunkpad = curses.newpad(1, self.xscreensize)
1773 self.chunkpad = curses.newpad(1, self.xscreensize)
1754
1774
1755 # add 1 so to account for last line text reaching end of line
1775 # add 1 so to account for last line text reaching end of line
1756 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1776 self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1
1757
1777
1758 try:
1778 try:
1759 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1779 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize)
1760 except curses.error:
1780 except curses.error:
1761 self.initexc = fallbackerror(
1781 self.initexc = fallbackerror(
1762 _('this diff is too large to be displayed'))
1782 _('this diff is too large to be displayed'))
1763 return
1783 return
1764 # initialize selecteditemendline (initial start-line is 0)
1784 # initialize selecteditemendline (initial start-line is 0)
1765 self.selecteditemendline = self.getnumlinesdisplayed(
1785 self.selecteditemendline = self.getnumlinesdisplayed(
1766 self.currentselecteditem, recursechildren=False)
1786 self.currentselecteditem, recursechildren=False)
1767
1787
1768 while True:
1788 while True:
1769 self.updatescreen()
1789 self.updatescreen()
1770 try:
1790 try:
1771 with self.ui.timeblockedsection('crecord'):
1791 with self.ui.timeblockedsection('crecord'):
1772 keypressed = self.statuswin.getkey()
1792 keypressed = self.statuswin.getkey()
1773 if self.errorstr is not None:
1793 if self.errorstr is not None:
1774 self.errorstr = None
1794 self.errorstr = None
1775 continue
1795 continue
1776 except curses.error:
1796 except curses.error:
1777 keypressed = "foobar"
1797 keypressed = "foobar"
1778 if self.handlekeypressed(keypressed):
1798 if self.handlekeypressed(keypressed):
1779 break
1799 break
1780
1800
1781 if self.commenttext != "":
1801 if self.commenttext != "":
1782 whitespaceremoved = re.sub("(?m)^\s.*(\n|$)", "", self.commenttext)
1802 whitespaceremoved = re.sub("(?m)^\s.*(\n|$)", "", self.commenttext)
1783 if whitespaceremoved != "":
1803 if whitespaceremoved != "":
1784 self.opts['message'] = self.commenttext
1804 self.opts['message'] = self.commenttext
@@ -1,444 +1,426 b''
1 #require tic
1 #require tic
2
2
3 Set up a repo
3 Set up a repo
4
4
5 $ cp $HGRCPATH $HGRCPATH.pretest
5 $ cp $HGRCPATH $HGRCPATH.pretest
6 $ cat <<EOF >> $HGRCPATH
6 $ cat <<EOF >> $HGRCPATH
7 > [ui]
7 > [ui]
8 > interactive = true
8 > interactive = true
9 > interface = curses
9 > interface = curses
10 > [experimental]
10 > [experimental]
11 > crecordtest = testModeCommands
11 > crecordtest = testModeCommands
12 > EOF
12 > EOF
13
13
14 Record with noeol at eof (issue5268)
14 Record with noeol at eof (issue5268)
15 $ hg init noeol
15 $ hg init noeol
16 $ cd noeol
16 $ cd noeol
17 $ printf '0' > a
17 $ printf '0' > a
18 $ printf '0\n' > b
18 $ printf '0\n' > b
19 $ hg ci -Aqm initial
19 $ hg ci -Aqm initial
20 $ printf '1\n0' > a
20 $ printf '1\n0' > a
21 $ printf '1\n0\n' > b
21 $ printf '1\n0\n' > b
22 $ cat <<EOF >testModeCommands
22 $ cat <<EOF >testModeCommands
23 > c
23 > c
24 > EOF
24 > EOF
25 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "add hunks" -d "0 0"
25 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "add hunks" -d "0 0"
26 $ cd ..
26 $ cd ..
27
27
28 Normal repo
28 Normal repo
29 $ hg init a
29 $ hg init a
30 $ cd a
30 $ cd a
31
31
32 Committing some changes but stopping on the way
32 Committing some changes but stopping on the way
33
33
34 $ echo "a" > a
34 $ echo "a" > a
35 $ hg add a
35 $ hg add a
36 $ cat <<EOF >testModeCommands
36 $ cat <<EOF >testModeCommands
37 > TOGGLE
37 > TOGGLE
38 > X
38 > X
39 > EOF
39 > EOF
40 $ hg commit -i -m "a" -d "0 0"
40 $ hg commit -i -m "a" -d "0 0"
41 no changes to record
41 no changes to record
42 [1]
42 [1]
43 $ hg tip
43 $ hg tip
44 changeset: -1:000000000000
44 changeset: -1:000000000000
45 tag: tip
45 tag: tip
46 user:
46 user:
47 date: Thu Jan 01 00:00:00 1970 +0000
47 date: Thu Jan 01 00:00:00 1970 +0000
48
48
49
49
50 Committing some changes
50 Committing some changes
51
51
52 $ cat <<EOF >testModeCommands
52 $ cat <<EOF >testModeCommands
53 > X
53 > X
54 > EOF
54 > EOF
55 $ hg commit -i -m "a" -d "0 0"
55 $ hg commit -i -m "a" -d "0 0"
56 $ hg tip
56 $ hg tip
57 changeset: 0:cb9a9f314b8b
57 changeset: 0:cb9a9f314b8b
58 tag: tip
58 tag: tip
59 user: test
59 user: test
60 date: Thu Jan 01 00:00:00 1970 +0000
60 date: Thu Jan 01 00:00:00 1970 +0000
61 summary: a
61 summary: a
62
62
63 Check that commit -i works with no changes
63 Check that commit -i works with no changes
64 $ hg commit -i
64 $ hg commit -i
65 no changes to record
65 no changes to record
66 [1]
66 [1]
67
67
68 Committing only one file
68 Committing only one file
69
69
70 $ echo "a" >> a
70 $ echo "a" >> a
71 >>> open('b', 'wb').write(b"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") and None
71 >>> open('b', 'wb').write(b"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") and None
72 $ hg add b
72 $ hg add b
73 $ cat <<EOF >testModeCommands
73 $ cat <<EOF >testModeCommands
74 > TOGGLE
74 > TOGGLE
75 > KEY_DOWN
75 > KEY_DOWN
76 > X
76 > X
77 > EOF
77 > EOF
78 $ hg commit -i -m "one file" -d "0 0"
78 $ hg commit -i -m "one file" -d "0 0"
79 $ hg tip
79 $ hg tip
80 changeset: 1:fb2705a663ea
80 changeset: 1:fb2705a663ea
81 tag: tip
81 tag: tip
82 user: test
82 user: test
83 date: Thu Jan 01 00:00:00 1970 +0000
83 date: Thu Jan 01 00:00:00 1970 +0000
84 summary: one file
84 summary: one file
85
85
86 $ hg cat -r tip a
86 $ hg cat -r tip a
87 a
87 a
88 $ cat a
88 $ cat a
89 a
89 a
90 a
90 a
91
91
92 Committing only one hunk while aborting edition of hunk
92 Committing only one hunk while aborting edition of hunk
93
93
94 - Untoggle all the hunks, go down to the second file
94 - Untoggle all the hunks, go down to the second file
95 - unfold it
95 - unfold it
96 - go down to second hunk (1 for the first hunk, 1 for the first hunkline, 1 for the second hunk, 1 for the second hunklike)
96 - go down to second hunk (1 for the first hunk, 1 for the first hunkline, 1 for the second hunk, 1 for the second hunklike)
97 - toggle the second hunk
97 - toggle the second hunk
98 - toggle on and off the amend mode (to check that it toggles off)
98 - toggle on and off the amend mode (to check that it toggles off)
99 - edit the hunk and quit the editor immediately with non-zero status
99 - edit the hunk and quit the editor immediately with non-zero status
100 - commit
100 - commit
101
101
102 $ printf "printf 'editor ran\n'; exit 1" > editor.sh
102 $ printf "printf 'editor ran\n'; exit 1" > editor.sh
103 $ echo "x" > c
103 $ echo "x" > c
104 $ cat b >> c
104 $ cat b >> c
105 $ echo "y" >> c
105 $ echo "y" >> c
106 $ mv c b
106 $ mv c b
107 $ cat <<EOF >testModeCommands
107 $ cat <<EOF >testModeCommands
108 > A
108 > A
109 > KEY_DOWN
109 > KEY_DOWN
110 > f
110 > f
111 > KEY_DOWN
111 > KEY_DOWN
112 > KEY_DOWN
112 > KEY_DOWN
113 > KEY_DOWN
113 > KEY_DOWN
114 > KEY_DOWN
114 > KEY_DOWN
115 > TOGGLE
115 > TOGGLE
116 > a
116 > a
117 > a
117 > a
118 > e
118 > e
119 > X
119 > X
120 > EOF
120 > EOF
121 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "one hunk" -d "0 0"
121 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "one hunk" -d "0 0"
122 editor ran
122 editor ran
123 $ rm editor.sh
123 $ rm editor.sh
124 $ hg tip
124 $ hg tip
125 changeset: 2:7d10dfe755a8
125 changeset: 2:7d10dfe755a8
126 tag: tip
126 tag: tip
127 user: test
127 user: test
128 date: Thu Jan 01 00:00:00 1970 +0000
128 date: Thu Jan 01 00:00:00 1970 +0000
129 summary: one hunk
129 summary: one hunk
130
130
131 $ hg cat -r tip b
131 $ hg cat -r tip b
132 1
132 1
133 2
133 2
134 3
134 3
135 4
135 4
136 5
136 5
137 6
137 6
138 7
138 7
139 8
139 8
140 9
140 9
141 10
141 10
142 y
142 y
143 $ cat b
143 $ cat b
144 x
144 x
145 1
145 1
146 2
146 2
147 3
147 3
148 4
148 4
149 5
149 5
150 6
150 6
151 7
151 7
152 8
152 8
153 9
153 9
154 10
154 10
155 y
155 y
156 $ hg commit -m "other hunks"
156 $ hg commit -m "other hunks"
157 $ hg tip
157 $ hg tip
158 changeset: 3:a6735021574d
158 changeset: 3:a6735021574d
159 tag: tip
159 tag: tip
160 user: test
160 user: test
161 date: Thu Jan 01 00:00:00 1970 +0000
161 date: Thu Jan 01 00:00:00 1970 +0000
162 summary: other hunks
162 summary: other hunks
163
163
164 $ hg cat -r tip b
164 $ hg cat -r tip b
165 x
165 x
166 1
166 1
167 2
167 2
168 3
168 3
169 4
169 4
170 5
170 5
171 6
171 6
172 7
172 7
173 8
173 8
174 9
174 9
175 10
175 10
176 y
176 y
177
177
178 Newly added files can be selected with the curses interface
178 Newly added files can be selected with the curses interface
179
179
180 $ hg update -C .
180 $ hg update -C .
181 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
182 $ echo "hello" > x
182 $ echo "hello" > x
183 $ hg add x
183 $ hg add x
184 $ cat <<EOF >testModeCommands
184 $ cat <<EOF >testModeCommands
185 > TOGGLE
185 > TOGGLE
186 > TOGGLE
186 > TOGGLE
187 > X
187 > X
188 > EOF
188 > EOF
189 $ hg st
189 $ hg st
190 A x
190 A x
191 ? testModeCommands
191 ? testModeCommands
192 $ hg commit -i -m "newly added file" -d "0 0"
192 $ hg commit -i -m "newly added file" -d "0 0"
193 $ hg st
193 $ hg st
194 ? testModeCommands
194 ? testModeCommands
195
195
196 Amend option works
196 Amend option works
197 $ echo "hello world" > x
197 $ echo "hello world" > x
198 $ hg diff -c .
198 $ hg diff -c .
199 diff -r a6735021574d -r 2b0e9be4d336 x
199 diff -r a6735021574d -r 2b0e9be4d336 x
200 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
200 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
201 +++ b/x Thu Jan 01 00:00:00 1970 +0000
201 +++ b/x Thu Jan 01 00:00:00 1970 +0000
202 @@ -0,0 +1,1 @@
202 @@ -0,0 +1,1 @@
203 +hello
203 +hello
204 $ cat <<EOF >testModeCommands
204 $ cat <<EOF >testModeCommands
205 > a
205 > a
206 > X
206 > X
207 > EOF
207 > EOF
208 $ hg commit -i -m "newly added file" -d "0 0"
208 $ hg commit -i -m "newly added file" -d "0 0"
209 saved backup bundle to $TESTTMP/a/.hg/strip-backup/2b0e9be4d336-3cf0bc8c-amend.hg
209 saved backup bundle to $TESTTMP/a/.hg/strip-backup/2b0e9be4d336-3cf0bc8c-amend.hg
210 $ hg diff -c .
210 $ hg diff -c .
211 diff -r a6735021574d -r c1d239d165ae x
211 diff -r a6735021574d -r c1d239d165ae x
212 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
212 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
213 +++ b/x Thu Jan 01 00:00:00 1970 +0000
213 +++ b/x Thu Jan 01 00:00:00 1970 +0000
214 @@ -0,0 +1,1 @@
214 @@ -0,0 +1,1 @@
215 +hello world
215 +hello world
216
216
217 Make file empty
217 Make file empty
218 $ printf "" > x
218 $ printf "" > x
219 $ cat <<EOF >testModeCommands
219 $ cat <<EOF >testModeCommands
220 > X
220 > X
221 > EOF
221 > EOF
222 $ hg ci -i -m emptify -d "0 0"
222 $ hg ci -i -m emptify -d "0 0"
223 $ hg update -C '.^' -q
223 $ hg update -C '.^' -q
224
224
225 Editing a hunk puts you back on that hunk when done editing (issue5041)
225 Editing a hunk puts you back on that hunk when done editing (issue5041)
226 To do that, we change two lines in a file, pretend to edit the second line,
226 To do that, we change two lines in a file, pretend to edit the second line,
227 exit, toggle the line selected at the end of the edit and commit.
227 exit, toggle the line selected at the end of the edit and commit.
228 The first line should be recorded if we were put on the second line at the end
228 The first line should be recorded if we were put on the second line at the end
229 of the edit.
229 of the edit.
230
230
231 $ hg update -C .
231 $ hg update -C .
232 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 $ echo "foo" > x
233 $ echo "foo" > x
234 $ echo "hello world" >> x
234 $ echo "hello world" >> x
235 $ echo "bar" >> x
235 $ echo "bar" >> x
236 $ cat <<EOF >testModeCommands
236 $ cat <<EOF >testModeCommands
237 > f
237 > f
238 > KEY_DOWN
238 > KEY_DOWN
239 > KEY_DOWN
239 > KEY_DOWN
240 > KEY_DOWN
240 > KEY_DOWN
241 > KEY_DOWN
241 > KEY_DOWN
242 > e
242 > e
243 > TOGGLE
243 > TOGGLE
244 > X
244 > X
245 > EOF
245 > EOF
246 $ printf "printf 'editor ran\n'; exit 0" > editor.sh
246 $ printf "printf 'editor ran\n'; exit 0" > editor.sh
247 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "edit hunk" -d "0 0" -q
247 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "edit hunk" -d "0 0" -q
248 editor ran
248 editor ran
249 $ hg cat -r . x
249 $ hg cat -r . x
250 foo
250 foo
251 hello world
251 hello world
252
252
253 Testing the review option. The entire final filtered patch should show
253 Testing the review option. The entire final filtered patch should show
254 up in the editor and be editable. We will unselect the second file and
254 up in the editor and be editable. We will unselect the second file and
255 the first hunk of the third file. During review, we will decide that
255 the first hunk of the third file. During review, we will decide that
256 "lower" sounds better than "bottom", and the final commit should
256 "lower" sounds better than "bottom", and the final commit should
257 reflect this edition.
257 reflect this edition.
258
258
259 $ hg update -C .
259 $ hg update -C .
260 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 $ echo "top" > c
261 $ echo "top" > c
262 $ cat x >> c
262 $ cat x >> c
263 $ echo "bottom" >> c
263 $ echo "bottom" >> c
264 $ mv c x
264 $ mv c x
265 $ echo "third a" >> a
265 $ echo "third a" >> a
266 $ echo "we will unselect this" >> b
266 $ echo "we will unselect this" >> b
267
267
268 $ cat > editor.sh <<EOF
268 $ cat > editor.sh <<EOF
269 > cat "\$1"
269 > cat "\$1"
270 > cat "\$1" | sed s/bottom/lower/ > tmp
270 > cat "\$1" | sed s/bottom/lower/ > tmp
271 > mv tmp "\$1"
271 > mv tmp "\$1"
272 > EOF
272 > EOF
273 $ cat > testModeCommands <<EOF
273 $ cat > testModeCommands <<EOF
274 > KEY_DOWN
274 > KEY_DOWN
275 > TOGGLE
275 > TOGGLE
276 > KEY_DOWN
276 > KEY_DOWN
277 > f
277 > f
278 > KEY_DOWN
278 > KEY_DOWN
279 > TOGGLE
279 > TOGGLE
280 > R
280 > R
281 > EOF
281 > EOF
282
282
283 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "review hunks" -d "0 0"
283 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit -i -m "review hunks" -d "0 0"
284 # To remove '-' lines, make them ' ' lines (context).
284 # To remove '-' lines, make them ' ' lines (context).
285 # To remove '+' lines, delete them.
285 # To remove '+' lines, delete them.
286 # Lines starting with # will be removed from the patch.
286 # Lines starting with # will be removed from the patch.
287 #
287 #
288 # If the patch applies cleanly, the edited patch will immediately
288 # If the patch applies cleanly, the edited patch will immediately
289 # be finalised. If it does not apply cleanly, rejects files will be
289 # be finalised. If it does not apply cleanly, rejects files will be
290 # generated. You can use those when you try again.
290 # generated. You can use those when you try again.
291 diff --git a/a b/a
291 diff --git a/a b/a
292 --- a/a
292 --- a/a
293 +++ b/a
293 +++ b/a
294 @@ -1,2 +1,3 @@
294 @@ -1,2 +1,3 @@
295 a
295 a
296 a
296 a
297 +third a
297 +third a
298 diff --git a/x b/x
298 diff --git a/x b/x
299 --- a/x
299 --- a/x
300 +++ b/x
300 +++ b/x
301 @@ -1,2 +1,3 @@
301 @@ -1,2 +1,3 @@
302 foo
302 foo
303 hello world
303 hello world
304 +bottom
304 +bottom
305
305
306 $ hg cat -r . a
306 $ hg cat -r . a
307 a
307 a
308 a
308 a
309 third a
309 third a
310
310
311 $ hg cat -r . b
311 $ hg cat -r . b
312 x
312 x
313 1
313 1
314 2
314 2
315 3
315 3
316 4
316 4
317 5
317 5
318 6
318 6
319 7
319 7
320 8
320 8
321 9
321 9
322 10
322 10
323 y
323 y
324
324
325 $ hg cat -r . x
325 $ hg cat -r . x
326 foo
326 foo
327 hello world
327 hello world
328 lower
328 lower
329
329
330 Check spacemovesdown
331
332 $ cat <<EOF >> $HGRCPATH
333 > [experimental]
334 > spacemovesdown = true
335 > EOF
336 $ cat <<EOF >testModeCommands
337 > TOGGLE
338 > TOGGLE
339 > X
340 > EOF
341 $ hg status -q
342 M b
343 M x
344 $ hg commit -i -m "nothing to commit?" -d "0 0"
345 no changes to record
346 [1]
347
348 Check ui.interface logic for the chunkselector
330 Check ui.interface logic for the chunkselector
349
331
350 The default interface is text
332 The default interface is text
351 $ cp $HGRCPATH.pretest $HGRCPATH
333 $ cp $HGRCPATH.pretest $HGRCPATH
352 $ chunkselectorinterface() {
334 $ chunkselectorinterface() {
353 > "$PYTHON" <<EOF
335 > "$PYTHON" <<EOF
354 > from mercurial import hg, ui;\
336 > from mercurial import hg, ui;\
355 > repo = hg.repository(ui.ui.load(), ".");\
337 > repo = hg.repository(ui.ui.load(), ".");\
356 > print(repo.ui.interface("chunkselector"))
338 > print(repo.ui.interface("chunkselector"))
357 > EOF
339 > EOF
358 > }
340 > }
359 $ chunkselectorinterface
341 $ chunkselectorinterface
360 text
342 text
361
343
362 If only the default is set, we'll use that for the feature, too
344 If only the default is set, we'll use that for the feature, too
363 $ cp $HGRCPATH.pretest $HGRCPATH
345 $ cp $HGRCPATH.pretest $HGRCPATH
364 $ cat <<EOF >> $HGRCPATH
346 $ cat <<EOF >> $HGRCPATH
365 > [ui]
347 > [ui]
366 > interface = curses
348 > interface = curses
367 > EOF
349 > EOF
368 $ chunkselectorinterface
350 $ chunkselectorinterface
369 curses
351 curses
370
352
371 If TERM=dumb, we use text, even if the config says curses
353 If TERM=dumb, we use text, even if the config says curses
372 $ chunkselectorinterface
354 $ chunkselectorinterface
373 curses
355 curses
374 $ TERM=dumb chunkselectorinterface
356 $ TERM=dumb chunkselectorinterface
375 text
357 text
376 (Something is keeping TERM=dumb in the environment unless I do this, it's not
358 (Something is keeping TERM=dumb in the environment unless I do this, it's not
377 scoped to just that previous command like in many shells)
359 scoped to just that previous command like in many shells)
378 $ TERM=xterm chunkselectorinterface
360 $ TERM=xterm chunkselectorinterface
379 curses
361 curses
380
362
381 It is possible to override the default interface with a feature specific
363 It is possible to override the default interface with a feature specific
382 interface
364 interface
383 $ cp $HGRCPATH.pretest $HGRCPATH
365 $ cp $HGRCPATH.pretest $HGRCPATH
384 $ cat <<EOF >> $HGRCPATH
366 $ cat <<EOF >> $HGRCPATH
385 > [ui]
367 > [ui]
386 > interface = text
368 > interface = text
387 > interface.chunkselector = curses
369 > interface.chunkselector = curses
388 > EOF
370 > EOF
389
371
390 $ chunkselectorinterface
372 $ chunkselectorinterface
391 curses
373 curses
392
374
393 $ cp $HGRCPATH.pretest $HGRCPATH
375 $ cp $HGRCPATH.pretest $HGRCPATH
394 $ cat <<EOF >> $HGRCPATH
376 $ cat <<EOF >> $HGRCPATH
395 > [ui]
377 > [ui]
396 > interface = curses
378 > interface = curses
397 > interface.chunkselector = text
379 > interface.chunkselector = text
398 > EOF
380 > EOF
399
381
400 $ chunkselectorinterface
382 $ chunkselectorinterface
401 text
383 text
402
384
403 If a bad interface name is given, we use the default value (with a nice
385 If a bad interface name is given, we use the default value (with a nice
404 error message to suggest that the configuration needs to be fixed)
386 error message to suggest that the configuration needs to be fixed)
405
387
406 $ cp $HGRCPATH.pretest $HGRCPATH
388 $ cp $HGRCPATH.pretest $HGRCPATH
407 $ cat <<EOF >> $HGRCPATH
389 $ cat <<EOF >> $HGRCPATH
408 > [ui]
390 > [ui]
409 > interface = blah
391 > interface = blah
410 > EOF
392 > EOF
411 $ chunkselectorinterface
393 $ chunkselectorinterface
412 invalid value for ui.interface: blah (using text)
394 invalid value for ui.interface: blah (using text)
413 text
395 text
414
396
415 $ cp $HGRCPATH.pretest $HGRCPATH
397 $ cp $HGRCPATH.pretest $HGRCPATH
416 $ cat <<EOF >> $HGRCPATH
398 $ cat <<EOF >> $HGRCPATH
417 > [ui]
399 > [ui]
418 > interface = curses
400 > interface = curses
419 > interface.chunkselector = blah
401 > interface.chunkselector = blah
420 > EOF
402 > EOF
421 $ chunkselectorinterface
403 $ chunkselectorinterface
422 invalid value for ui.interface.chunkselector: blah (using curses)
404 invalid value for ui.interface.chunkselector: blah (using curses)
423 curses
405 curses
424
406
425 $ cp $HGRCPATH.pretest $HGRCPATH
407 $ cp $HGRCPATH.pretest $HGRCPATH
426 $ cat <<EOF >> $HGRCPATH
408 $ cat <<EOF >> $HGRCPATH
427 > [ui]
409 > [ui]
428 > interface = blah
410 > interface = blah
429 > interface.chunkselector = curses
411 > interface.chunkselector = curses
430 > EOF
412 > EOF
431 $ chunkselectorinterface
413 $ chunkselectorinterface
432 invalid value for ui.interface: blah
414 invalid value for ui.interface: blah
433 curses
415 curses
434
416
435 $ cp $HGRCPATH.pretest $HGRCPATH
417 $ cp $HGRCPATH.pretest $HGRCPATH
436 $ cat <<EOF >> $HGRCPATH
418 $ cat <<EOF >> $HGRCPATH
437 > [ui]
419 > [ui]
438 > interface = blah
420 > interface = blah
439 > interface.chunkselector = blah
421 > interface.chunkselector = blah
440 > EOF
422 > EOF
441 $ chunkselectorinterface
423 $ chunkselectorinterface
442 invalid value for ui.interface: blah
424 invalid value for ui.interface: blah
443 invalid value for ui.interface.chunkselector: blah (using text)
425 invalid value for ui.interface.chunkselector: blah (using text)
444 text
426 text
General Comments 0
You need to be logged in to leave comments. Login now