fetch: add support for the traditional FETCH_HEAD behavior
[git-cola.git] / cola / models / prefs.py
blob175a436c02e90569ad256eee8e5caaed09f66125
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 INOTIFY = 'cola.inotify'
33 LINEBREAK = 'cola.linebreak'
34 LOGDATE = 'cola.logdate'
35 MAXRECENT = 'cola.maxrecent'
36 MERGE_DIFFSTAT = 'merge.diffstat'
37 MERGE_KEEPBACKUP = 'merge.keepbackup'
38 MERGE_SUMMARY = 'merge.summary'
39 MERGE_VERBOSITY = 'merge.verbosity'
40 MERGETOOL = 'merge.tool'
41 MOUSE_ZOOM = 'cola.mousezoom'
42 PATCHES_DIRECTORY = 'cola.patchesdirectory'
43 REFRESH_ON_FOCUS = 'cola.refreshonfocus'
44 RESIZE_BROWSER_COLUMNS = 'cola.resizebrowsercolumns'
45 SAFE_MODE = 'cola.safemode'
46 SAVEWINDOWSETTINGS = 'cola.savewindowsettings'
47 SHOW_PATH = 'cola.showpath'
48 SORT_BOOKMARKS = 'cola.sortbookmarks'
49 SPELL_CHECK = 'cola.spellcheck'
50 STATUS_INDENT = 'cola.statusindent'
51 STATUS_SHOW_TOTALS = 'cola.statusshowtotals'
52 THEME = 'cola.theme'
53 TABWIDTH = 'cola.tabwidth'
54 TEXTWIDTH = 'cola.textwidth'
55 USER_EMAIL = 'user.email'
56 USER_NAME = 'user.name'
59 class DateFormat:
60 DEFAULT = 'default'
61 RELATIVE = 'relative'
62 LOCAL = 'local'
63 ISO = 'iso8601'
64 ISO_STRICT = 'iso8601-strict'
65 RFC = 'rfc2822'
66 SHORT = 'short'
67 RAW = 'raw'
68 HUMAN = 'human'
69 UNIX = 'unix'
72 def date_formats():
73 """Return valid values for git config cola.logdate"""
74 return [
75 DateFormat.DEFAULT,
76 DateFormat.RELATIVE,
77 DateFormat.LOCAL,
78 DateFormat.ISO,
79 DateFormat.ISO_STRICT,
80 DateFormat.RFC,
81 DateFormat.SHORT,
82 DateFormat.RAW,
83 DateFormat.HUMAN,
84 DateFormat.UNIX,
88 def commit_cleanup_modes():
89 """Return valid values for the git config commit.cleanup"""
90 return [
91 'default',
92 'whitespace',
93 'strip',
94 'scissors',
95 'verbatim',
99 class Defaults:
100 """Read-only class for holding defaults that get overridden"""
102 # These should match Git's defaults for git-defined values.
103 autotemplate = False
104 blame_viewer = 'git gui blame'
105 block_cursor = True
106 bold_headers = False
107 check_conflicts = True
108 check_published_commits = True
109 comment_char = '#'
110 commit_cleanup = 'default'
111 display_untracked = True
112 diff_context = 5
113 difftool = 'xxdiff'
114 editor = 'gvim'
115 enable_gravatar = True
116 expandtab = False
117 history_browser = 'gitk'
118 icon_theme = 'default'
119 inotify = True
120 linebreak = True
121 maxrecent = 8
122 mergetool = difftool
123 merge_diffstat = True
124 merge_keep_backup = True
125 merge_summary = True
126 merge_verbosity = 2
127 mouse_zoom = True
128 refresh_on_focus = False
129 resize_browser_columns = False
130 save_window_settings = True
131 safe_mode = False
132 autocomplete_paths = True
133 show_path = True
134 sort_bookmarks = True
135 spellcheck = False
136 tabwidth = 8
137 textwidth = 72
138 theme = 'default'
139 hidpi = hidpi.Option.AUTO
140 patches_directory = 'patches'
141 status_indent = False
142 status_show_totals = False
143 logdate = DateFormat.DEFAULT
146 def blame_viewer(context):
147 """Return the configured "blame" viewer"""
148 default = Defaults.blame_viewer
149 return context.cfg.get(BLAME_VIEWER, default=default)
152 def block_cursor(context):
153 """Should we display a block cursor in diff editors?"""
154 return context.cfg.get(BLOCK_CURSOR, default=Defaults.block_cursor)
157 def bold_headers(context):
158 """Should we bold the Status column headers?"""
159 return context.cfg.get(BOLD_HEADERS, default=Defaults.bold_headers)
162 def check_conflicts(context):
163 """Should we check for merge conflict markers in unmerged files?"""
164 return context.cfg.get(CHECK_CONFLICTS, default=Defaults.check_conflicts)
167 def check_published_commits(context):
168 """Should we check for published commits when amending?"""
169 return context.cfg.get(
170 CHECK_PUBLISHED_COMMITS, default=Defaults.check_published_commits
174 def display_untracked(context):
175 """Should we display untracked files?"""
176 return context.cfg.get(DISPLAY_UNTRACKED, default=Defaults.display_untracked)
179 def editor(context):
180 """Return the configured editor"""
181 app = context.cfg.get(EDITOR, default=fallback_editor())
182 return _remap_editor(app)
185 def background_editor(context):
186 """Return the configured non-blocking background editor"""
187 app = context.cfg.get(BACKGROUND_EDITOR, default=editor(context))
188 return _remap_editor(app)
191 def fallback_editor():
192 """Return a fallback editor for cases where one is not configured
194 GIT_VISUAL and VISUAL are consulted before GIT_EDITOR and EDITOR to allow
195 configuring a visual editor for Git Cola using $GIT_VISUAL and an alternative
196 editor for the Git CLI.
198 editor_variables = (
199 'GIT_VISUAL',
200 'VISUAL',
201 'GIT_EDITOR',
202 'EDITOR',
204 for env in editor_variables:
205 env_editor = core.getenv(env)
206 if env_editor:
207 return env_editor
209 return Defaults.editor
212 def _remap_editor(app):
213 """Remap a configured editor into a visual editor name"""
214 # We do this for vim users because this configuration is convenient for new users.
215 return {
216 'vim': 'gvim -f',
217 'nvim': 'nvim-qt --nofork',
218 }.get(app, app)
221 def comment_char(context):
222 """Return the configured git commit comment character"""
223 return context.cfg.get(COMMENT_CHAR, default=Defaults.comment_char)
226 def commit_cleanup(context):
227 """Return the configured git commit cleanup mode"""
228 return context.cfg.get(COMMIT_CLEANUP, default=Defaults.commit_cleanup)
231 def enable_gravatar(context):
232 """Is gravatar enabled?"""
233 return context.cfg.get(ENABLE_GRAVATAR, default=Defaults.enable_gravatar)
236 def default_history_browser():
237 """Return the default history browser (e.g. git-dag, gitk)"""
238 if utils.is_win32():
239 # On Windows, a sensible default is "python git-cola dag"
240 # which is different than `gitk` below, but is preferred
241 # because we don't have to guess paths.
242 git_cola = sys.argv[0].replace('\\', '/')
243 python = sys.executable.replace('\\', '/')
244 cwd = core.getcwd().replace('\\', '/')
245 argv = [python, git_cola, 'dag', '--repo', cwd]
246 argv = core.prep_for_subprocess(argv)
247 default = core.list2cmdline(argv)
248 else:
249 # The `gitk` script can be launched as-is on unix
250 default = Defaults.history_browser
251 return default
254 def history_browser(context):
255 """Return the configured history browser"""
256 default = default_history_browser()
257 return context.cfg.get(HISTORY_BROWSER, default=default)
260 def linebreak(context):
261 """Should we word-wrap lines in the commit message editor?"""
262 return context.cfg.get(LINEBREAK, default=Defaults.linebreak)
265 def logdate(context):
266 """Return the configured log date format"""
267 return context.cfg.get(LOGDATE, default=Defaults.logdate)
270 def maxrecent(context):
271 """Return the configured maximum number of Recent Repositories"""
272 value = Defaults.maxrecent
273 if context:
274 value = context.cfg.get(MAXRECENT, default=value)
275 return value
278 def mouse_zoom(context):
279 """Should we zoom text when using Ctrl + MouseWheel scroll"""
280 return context.cfg.get(MOUSE_ZOOM, default=Defaults.mouse_zoom)
283 def spellcheck(context):
284 """Should we spellcheck commit messages?"""
285 return context.cfg.get(SPELL_CHECK, default=Defaults.spellcheck)
288 def expandtab(context):
289 """Should we expand tabs in commit messages?"""
290 return context.cfg.get(EXPANDTAB, default=Defaults.expandtab)
293 def patches_directory(context):
294 """Return the patches output directory"""
295 return context.cfg.get(PATCHES_DIRECTORY, default=Defaults.patches_directory)
298 def sort_bookmarks(context):
299 """Should we sort bookmarks by name?"""
300 return context.cfg.get(SORT_BOOKMARKS, default=Defaults.sort_bookmarks)
303 def tabwidth(context):
304 """Return the configured tab width in the commit message editor"""
305 return context.cfg.get(TABWIDTH, default=Defaults.tabwidth)
308 def textwidth(context):
309 """Return the configured text width for word wrapping commit messages"""
310 return context.cfg.get(TEXTWIDTH, default=Defaults.textwidth)
313 def status_indent(context):
314 """Should we indent items in the status widget?"""
315 return context.cfg.get(STATUS_INDENT, default=Defaults.status_indent)
318 def status_show_totals(context):
319 """Should we display count totals in the status widget headers?"""
320 return context.cfg.get(STATUS_SHOW_TOTALS, default=Defaults.status_show_totals)
323 class PreferencesModel(QtCore.QObject):
324 """Interact with repo-local and user-global git config preferences"""
326 config_updated = Signal(str, str, object)
328 def __init__(self, context):
329 super().__init__()
330 self.context = context
331 self.config = context.cfg
333 def set_config(self, source, config, value):
334 """Set a configuration value"""
335 if source == 'local':
336 self.config.set_repo(config, value)
337 else:
338 self.config.set_user(config, value)
339 self.config_updated.emit(source, config, value)
341 def get_config(self, source, config):
342 """Get a configured value"""
343 if source == 'local':
344 value = self.config.get_repo(config)
345 else:
346 value = self.config.get(config)
347 return value
350 class SetConfig(Command):
351 """Store a gitconfig value"""
353 UNDOABLE = True
355 def __init__(self, model, source, config, value):
356 self.source = source
357 self.config = config
358 self.value = value
359 self.old_value = None
360 self.model = model
362 def do(self):
363 """Modify the model and store the updated configuration"""
364 self.old_value = self.model.get_config(self.source, self.config)
365 self.model.set_config(self.source, self.config, self.value)
367 def undo(self):
368 """Restore the configuration change to its original value"""
369 if self.old_value is None:
370 return
371 self.model.set_config(self.source, self.config, self.old_value)