# Gather code related to command dealing with configuration. from __future__ import annotations import os from typing import Any, Collection, Dict, Optional from ..i18n import _ from .. import ( cmdutil, error, formatter, pycompat, requirements, ui as uimod, util, ) from . import ( ConfigLevelT, EDIT_LEVELS, LEVEL_SHARED, NO_REPO_EDIT_LEVELS, rcutil, ) EDIT_FLAG = 'edit' def find_edit_level( ui: uimod.ui, repo, opts: Dict[str, Any], ) -> Optional[ConfigLevelT]: """return the level we should edit, if any. Parse the command option to detect when an edit is requested, and if so the configuration level we should edit. """ if opts.get(EDIT_FLAG) or any(opts.get(o) for o in EDIT_LEVELS): cmdutil.check_at_most_one_arg(opts, *EDIT_LEVELS) for level in EDIT_LEVELS: if opts.get(level): return level return EDIT_LEVELS[0] return None def edit_config(ui: uimod.ui, repo, level: ConfigLevelT) -> None: """let the user edit configuration file for the given level""" # validate input if repo is None and level not in NO_REPO_EDIT_LEVELS: msg = b"can't use --%s outside a repository" % pycompat.bytestr(level) raise error.InputError(_(msg)) if level == LEVEL_SHARED: if not repo.shared(): msg = _(b"repository is not shared; can't use --shared") raise error.InputError(msg) if requirements.SHARESAFE_REQUIREMENT not in repo.requirements: raise error.InputError( _( b"share safe feature not enabled; " b"unable to edit shared source repository config" ) ) # find rc files paths repo_path = None if repo is not None: repo_path = repo.root all_rcs = rcutil.all_rc_components(repo_path) rc_by_level = {} for lvl, rc_type, values in all_rcs: if rc_type != b'path': continue rc_by_level.setdefault(lvl, []).append(values) if level not in rc_by_level: msg = 'unknown config level: %s' % level raise error.ProgrammingError(msg) paths = rc_by_level[level] for f in paths: if os.path.exists(f): break else: samplehgrc = uimod.samplehgrcs.get(level) f = paths[0] if samplehgrc is not None: util.writefile(f, util.tonativeeol(samplehgrc)) editor = ui.geteditor() ui.system( b"%s \"%s\"" % (editor, f), onerr=error.InputError, errprefix=_(b"edit failed"), blockedtag=b'config_edit', ) def show_component(ui: uimod.ui, repo) -> None: """show the component used to build the config XXX this skip over various source and ignore the repository config, so it XXX is probably useless old code. """ for _lvl, t, f in rcutil.rccomponents(): if t == b'path': ui.debug(b'read config from: %s\n' % f) elif t == b'resource': ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1])) elif t == b'items': # Don't print anything for 'items'. pass else: raise error.ProgrammingError(b'unknown rctype: %s' % t) def show_config( ui: uimod.ui, repo, value_filters: Collection[bytes], formatter_options: dict, untrusted: bool = False, all_known: bool = False, show_source: bool = False, ) -> bool: """Display config value to the user The display is done using a dedicated `formatter` object. :value_filters: if non-empty filter the display value according to these filters. If the filter does not match any value, the function return False. True otherwise. :formatter_option: options passed to the formatter :untrusted: When set, use untrusted value instead of ignoring them :all_known: Display all known config item, not just the one with an explicit value. :show_source: Show where each value has been defined. """ fm = ui.formatter(b'config', formatter_options) selsections = selentries = [] filtered = False if value_filters: selsections = [v for v in value_filters if b'.' not in v] selentries = [v for v in value_filters if b'.' in v] filtered = True uniquesel = len(selentries) == 1 and not selsections selsections = set(selsections) selentries = set(selentries) matched = False entries = ui.walkconfig(untrusted=untrusted, all_known=all_known) for section, name, value in entries: source = ui.configsource(section, name, untrusted) value = pycompat.bytestr(value) defaultvalue = ui.configdefault(section, name) if fm.isplain(): source = source or b'none' value = value.replace(b'\n', b'\\n') entryname = section + b'.' + name if filtered and not (section in selsections or entryname in selentries): continue fm.startitem() fm.condwrite(show_source, b'source', b'%s: ', source) if uniquesel: fm.data(name=entryname) fm.write(b'value', b'%s\n', value) else: fm.write(b'name value', b'%s=%s\n', entryname, value) if formatter.isprintable(defaultvalue): fm.data(defaultvalue=defaultvalue) elif isinstance(defaultvalue, list) and all( formatter.isprintable(e) for e in defaultvalue ): fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value')) # TODO: no idea how to process unsupported defaultvalue types matched = True fm.end() return matched