cola/sequenceeditor.py: TODO -> FIXME
[git-cola.git] / cola / models / prefs.py
bloba62bd51e4269c369fbba392a9208b646b8a9c423
1 import sys
3 from qtpy import QtCore
4 from qtpy.QtCore import Signal
6 from .. import core
7 from .. import hidpi
8 from .. import utils
9 from ..cmd import Command
12 AUTOCOMPLETE_PATHS = 'cola.autocompletepaths'
13 AUTOTEMPLATE = 'cola.autoloadcommittemplate'
14 BACKGROUND_EDITOR = 'cola.backgroundeditor'
15 BLAME_VIEWER = 'cola.blameviewer'
16 BLOCK_CURSOR = 'cola.blockcursor'
17 BOLD_HEADERS = 'cola.boldheaders'
18 CHECK_CONFLICTS = 'cola.checkconflicts'
19 CHECK_PUBLISHED_COMMITS = 'cola.checkpublishedcommits'
20 COMMENT_CHAR = 'core.commentchar'
21 COMMIT_CLEANUP = 'commit.cleanup'
22 DIFFCONTEXT = 'gui.diffcontext'
23 DIFFTOOL = 'diff.tool'
24 DISPLAY_UNTRACKED = 'gui.displayuntracked'
25 EDITOR = 'gui.editor'
26 ENABLE_GRAVATAR = 'cola.gravatar'
27 EXPANDTAB = 'cola.expandtab'
28 FONTDIFF = 'cola.fontdiff'
29 HIDPI = 'cola.hidpi'
30 HISTORY_BROWSER = 'gui.historybrowser'
31 ICON_THEME = 'cola.icontheme'
32 LINEBREAK = 'cola.linebreak'
33 LOGDATE = 'cola.logdate'
34 MAXRECENT = 'cola.maxrecent'
35 MERGE_DIFFSTAT = 'merge.diffstat'
36 MERGE_KEEPBACKUP = 'merge.keepbackup'
37 MERGE_SUMMARY = 'merge.summary'
38 MERGE_VERBOSITY = 'merge.verbosity'
39 MERGETOOL = 'merge.tool'
40 MOUSE_ZOOM = 'cola.mousezoom'
41 PATCHES_DIRECTORY = 'cola.patchesdirectory'
42 RESIZE_BROWSER_COLUMNS = 'cola.resizebrowsercolumns'
43 SAFE_MODE = 'cola.safemode'
44 SAVEWINDOWSETTINGS = 'cola.savewindowsettings'
45 SHOW_PATH = 'cola.showpath'
46 SORT_BOOKMARKS = 'cola.sortbookmarks'
47 SPELL_CHECK = 'cola.spellcheck'
48 STATUS_INDENT = 'cola.statusindent'
49 STATUS_SHOW_TOTALS = 'cola.statusshowtotals'
50 THEME = 'cola.theme'
51 TABWIDTH = 'cola.tabwidth'
52 TEXTWIDTH = 'cola.textwidth'
53 USER_EMAIL = 'user.email'
54 USER_NAME = 'user.name'
57 class DateFormat:
58 DEFAULT = 'default'
59 RELATIVE = 'relative'
60 LOCAL = 'local'
61 ISO = 'iso8601'
62 ISO_STRICT = 'iso8601-strict'
63 RFC = 'rfc2822'
64 SHORT = 'short'
65 RAW = 'raw'
66 HUMAN = 'human'
67 UNIX = 'unix'
70 def date_formats():
71 """Return valid values for git config cola.logdate"""
72 return [
73 DateFormat.DEFAULT,
74 DateFormat.RELATIVE,
75 DateFormat.LOCAL,
76 DateFormat.ISO,
77 DateFormat.ISO_STRICT,
78 DateFormat.RFC,
79 DateFormat.SHORT,
80 DateFormat.RAW,
81 DateFormat.HUMAN,
82 DateFormat.UNIX,
86 def commit_cleanup_modes():
87 """Return valid values for the git config commit.cleanup"""
88 return [
89 'default',
90 'whitespace',
91 'strip',
92 'scissors',
93 'verbatim',
97 class Defaults:
98 """Read-only class for holding defaults that get overridden"""
100 # These should match Git's defaults for git-defined values.
101 autotemplate = False
102 blame_viewer = 'git gui blame'
103 block_cursor = True
104 bold_headers = False
105 check_conflicts = True
106 check_published_commits = True
107 comment_char = '#'
108 commit_cleanup = 'default'
109 display_untracked = True
110 diff_context = 5
111 difftool = 'xxdiff'
112 editor = 'gvim'
113 enable_gravatar = True
114 expandtab = False
115 history_browser = 'gitk'
116 icon_theme = 'default'
117 linebreak = True
118 maxrecent = 8
119 mergetool = difftool
120 merge_diffstat = True
121 merge_keep_backup = True
122 merge_summary = True
123 merge_verbosity = 2
124 mouse_zoom = True
125 resize_browser_columns = False
126 save_window_settings = True
127 safe_mode = False
128 autocomplete_paths = True
129 show_path = True
130 sort_bookmarks = True
131 spellcheck = False
132 tabwidth = 8
133 textwidth = 72
134 theme = 'default'
135 hidpi = hidpi.Option.AUTO
136 patches_directory = 'patches'
137 status_indent = False
138 status_show_totals = False
139 logdate = DateFormat.DEFAULT
142 def blame_viewer(context):
143 """Return the configured "blame" viewer"""
144 default = Defaults.blame_viewer
145 return context.cfg.get(BLAME_VIEWER, default=default)
148 def block_cursor(context):
149 """Should we display a block cursor in diff editors?"""
150 return context.cfg.get(BLOCK_CURSOR, default=Defaults.block_cursor)
153 def bold_headers(context):
154 """Should we bold the Status column headers?"""
155 return context.cfg.get(BOLD_HEADERS, default=Defaults.bold_headers)
158 def check_conflicts(context):
159 """Should we check for merge conflict markers in unmerged files?"""
160 return context.cfg.get(CHECK_CONFLICTS, default=Defaults.check_conflicts)
163 def check_published_commits(context):
164 """Should we check for published commits when amending?"""
165 return context.cfg.get(
166 CHECK_PUBLISHED_COMMITS, default=Defaults.check_published_commits
170 def display_untracked(context):
171 """Should we display untracked files?"""
172 return context.cfg.get(DISPLAY_UNTRACKED, default=Defaults.display_untracked)
175 def editor(context):
176 """Return the configured editor"""
177 app = context.cfg.get(EDITOR, default=fallback_editor())
178 return _remap_editor(app)
181 def background_editor(context):
182 """Return the configured non-blocking background editor"""
183 app = context.cfg.get(BACKGROUND_EDITOR, default=editor(context))
184 return _remap_editor(app)
187 def fallback_editor():
188 """Return a fallback editor for cases where one is not configured
190 GIT_VISUAL and VISUAL are consulted before GIT_EDITOR and EDITOR to allow
191 configuring a visual editor for Git Cola using $GIT_VISUAL and an alternative
192 editor for the Git CLI.
194 editor_variables = (
195 'GIT_VISUAL',
196 'VISUAL',
197 'GIT_EDITOR',
198 'EDITOR',
200 for env in editor_variables:
201 env_editor = core.getenv(env)
202 if env_editor:
203 return env_editor
205 return Defaults.editor
208 def _remap_editor(app):
209 """Remap a configured editor into a visual editor name"""
210 # We do this for vim users because this configuration is convenient for new users.
211 return {
212 'vim': 'gvim -f',
213 'nvim': 'nvim-qt --nofork',
214 }.get(app, app)
217 def comment_char(context):
218 """Return the configured git commit comment character"""
219 return context.cfg.get(COMMENT_CHAR, default=Defaults.comment_char)
222 def commit_cleanup(context):
223 """Return the configured git commit cleanup mode"""
224 return context.cfg.get(COMMIT_CLEANUP, default=Defaults.commit_cleanup)
227 def enable_gravatar(context):
228 """Is gravatar enabled?"""
229 return context.cfg.get(ENABLE_GRAVATAR, default=Defaults.enable_gravatar)
232 def default_history_browser():
233 """Return the default history browser (e.g. git-dag, gitk)"""
234 if utils.is_win32():
235 # On Windows, a sensible default is "python git-cola dag"
236 # which is different than `gitk` below, but is preferred
237 # because we don't have to guess paths.
238 git_cola = sys.argv[0].replace('\\', '/')
239 python = sys.executable.replace('\\', '/')
240 cwd = core.getcwd().replace('\\', '/')
241 argv = [python, git_cola, 'dag', '--repo', cwd]
242 argv = core.prep_for_subprocess(argv)
243 default = core.list2cmdline(argv)
244 else:
245 # The `gitk` script can be launched as-is on unix
246 default = Defaults.history_browser
247 return default
250 def history_browser(context):
251 """Return the configured history browser"""
252 default = default_history_browser()
253 return context.cfg.get(HISTORY_BROWSER, default=default)
256 def linebreak(context):
257 """Should we word-wrap lines in the commit message editor?"""
258 return context.cfg.get(LINEBREAK, default=Defaults.linebreak)
261 def logdate(context):
262 """Return the configured log date format"""
263 return context.cfg.get(LOGDATE, default=Defaults.logdate)
266 def maxrecent(context):
267 """Return the configured maximum number of Recent Repositories"""
268 value = Defaults.maxrecent
269 if context:
270 value = context.cfg.get(MAXRECENT, default=value)
271 return value
274 def mouse_zoom(context):
275 """Should we zoom text when using Ctrl + MouseWheel scroll"""
276 return context.cfg.get(MOUSE_ZOOM, default=Defaults.mouse_zoom)
279 def spellcheck(context):
280 """Should we spellcheck commit messages?"""
281 return context.cfg.get(SPELL_CHECK, default=Defaults.spellcheck)
284 def expandtab(context):
285 """Should we expand tabs in commit messages?"""
286 return context.cfg.get(EXPANDTAB, default=Defaults.expandtab)
289 def patches_directory(context):
290 """Return the patches output directory"""
291 return context.cfg.get(PATCHES_DIRECTORY, default=Defaults.patches_directory)
294 def sort_bookmarks(context):
295 """Should we sort bookmarks by name?"""
296 return context.cfg.get(SORT_BOOKMARKS, default=Defaults.sort_bookmarks)
299 def tabwidth(context):
300 """Return the configured tab width in the commit message editor"""
301 return context.cfg.get(TABWIDTH, default=Defaults.tabwidth)
304 def textwidth(context):
305 """Return the configured text width for word wrapping commit messages"""
306 return context.cfg.get(TEXTWIDTH, default=Defaults.textwidth)
309 def status_indent(context):
310 """Should we indent items in the status widget?"""
311 return context.cfg.get(STATUS_INDENT, default=Defaults.status_indent)
314 def status_show_totals(context):
315 """Should we display count totals in the status widget headers?"""
316 return context.cfg.get(STATUS_SHOW_TOTALS, default=Defaults.status_show_totals)
319 class PreferencesModel(QtCore.QObject):
320 """Interact with repo-local and user-global git config preferences"""
322 config_updated = Signal(str, str, object)
324 def __init__(self, context):
325 super().__init__()
326 self.context = context
327 self.config = context.cfg
329 def set_config(self, source, config, value):
330 """Set a configuration value"""
331 if source == 'local':
332 self.config.set_repo(config, value)
333 else:
334 self.config.set_user(config, value)
335 self.config_updated.emit(source, config, value)
337 def get_config(self, source, config):
338 """Get a configured value"""
339 if source == 'local':
340 value = self.config.get_repo(config)
341 else:
342 value = self.config.get(config)
343 return value
346 class SetConfig(Command):
347 """Store a gitconfig value"""
349 UNDOABLE = True
351 def __init__(self, model, source, config, value):
352 self.source = source
353 self.config = config
354 self.value = value
355 self.old_value = None
356 self.model = model
358 def do(self):
359 """Modify the model and store the updated configuration"""
360 self.old_value = self.model.get_config(self.source, self.config)
361 self.model.set_config(self.source, self.config, self.value)
363 def undo(self):
364 """Restore the configuration change to its original value"""
365 if self.old_value is None:
366 return
367 self.model.set_config(self.source, self.config, self.old_value)