From 6cfc13522c651506413e3bfee6746ce26d08da01 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Mon, 6 May 2019 17:12:27 +0200 Subject: [PATCH] menuconfig: Small Space/Enter improvement + fix for obscure bug Update menuconfig (and Kconfiglib, just to sync) to upstream revision 7e0b9f7ae1, to get these commits in: menuconfig: Improve space/enter behavior slightly Space toggles value is possible, and enters menus otherwise. Enter works the other way around. Make this explicit in the code, which also fixes some corner cases, like space doing nothing on a y-selected menuconfig symbol. ------------------------------------------------------------------- menuconfig: Fix display issue for unsatisfied-deps selected symbol with children A symbol with unsatisfied direct dependencies can end up with visible children in an implicit submenu if it is selected (though that generates a warning), so the optimization in _shown_nodes() isn't safe, and causes the child nodes to not be shown outside show-all mode. Just remove the optimization. Trying things out some more, everything's plenty fast enough anyway. Checking the direct dependencies of the parent instead would be safe. The menu path now says "(Top)" instead of "(top menu)" too, which is a bit more compact. Make the same change in genrest.py. Signed-off-by: Ulf Magnusson --- doc/scripts/genrest.py | 2 +- scripts/kconfig/kconfiglib.py | 190 ++++++++++++++++++---------------- scripts/kconfig/menuconfig.py | 179 +++++++++++++++++--------------- 3 files changed, 199 insertions(+), 172 deletions(-) diff --git a/doc/scripts/genrest.py b/doc/scripts/genrest.py index 6852a0f7ed91..722a48e1f41d 100644 --- a/doc/scripts/genrest.py +++ b/doc/scripts/genrest.py @@ -365,7 +365,7 @@ def menu_path(node): kconfiglib.standard_sc_expr_str(node.item)) + \ path - return "(top menu)" + path + return "(Top)" + path heading = "Kconfig definition" if len(sc.nodes) > 1: heading += "s" diff --git a/scripts/kconfig/kconfiglib.py b/scripts/kconfig/kconfiglib.py index 30b68f0d82af..4083110a7f17 100644 --- a/scripts/kconfig/kconfiglib.py +++ b/scripts/kconfig/kconfiglib.py @@ -51,12 +51,20 @@ currently not supported for the menuconfig). +make guiconfig +-------------- + +This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3 +are supported. To change the Python interpreter used, pass +PYTHONCMD= to 'make'. The default is 'python'. + + make [ARCH=] iscriptconfig -------------------------------- This target gives an interactive Python prompt where a Kconfig instance has been preloaded and is available in 'kconf'. To change the Python interpreter -used, pass PYTHONCMD= to make. The default is "python". +used, pass PYTHONCMD= to 'make'. The default is 'python'. To get a feel for the API, try evaluating and printing the symbols in kconf.defined_syms, and explore the MenuNode menu tree starting at @@ -529,8 +537,7 @@ def my_other_fn(kconf, name, arg_1, arg_2, ...): # Get rid of some attribute lookups. These are obvious in context. from glob import iglob -from os.path import dirname, exists, expandvars, isabs, islink, join, \ - relpath, split +from os.path import dirname, exists, expandvars, isabs, islink, join, relpath # File layout: @@ -1358,12 +1365,11 @@ def write_config(self, filename=None, save_old (default: True): If True and already exists, a copy of it will be saved to - ..old in the same directory before the new configuration is - written. The leading dot is added only if the filename doesn't - already start with a dot. + .old in the same directory before the new configuration is + written. - Errors are silently ignored if ..old cannot be written - (e.g. due to being a directory). + Errors are silently ignored if .old cannot be written (e.g. + due to being a directory). verbose (default: True): If True and filename is None (automatically infer configuration @@ -1502,20 +1508,8 @@ def sync_deps(self, path): if not exists(path): os.mkdir(path, 0o755) - # This setup makes sure that at least the current working directory - # gets reset if things fail - prev_dir = os.getcwd() - try: - # cd'ing into the symbol file directory simplifies - # _sync_deps() and saves some work - os.chdir(path) - self._sync_deps() - finally: - os.chdir(prev_dir) - - def _sync_deps(self): # Load old values from auto.conf, if any - self._load_old_vals() + self._load_old_vals(path) for sym in self.unique_defined_syms: # Note: _write_to_conf is determined when the value is @@ -1546,31 +1540,16 @@ def _sync_deps(self): continue # 'sym' has a new value. Flag it. - _touch_dep_file(sym.name) + _touch_dep_file(path, sym.name) # Remember the current values as the "new old" values. # # This call could go anywhere after the call to _load_old_vals(), but # putting it last means _sync_deps() can be safely rerun if it fails # before this point. - self._write_old_vals() + self._write_old_vals(path) - def _write_old_vals(self): - # Helper for writing auto.conf. Basically just a simplified - # write_config() that doesn't write any comments (including - # '# CONFIG_FOO is not set' comments). The format matches the C - # implementation, though the ordering is arbitrary there (depends on - # the hash table implementation). - # - # A separate helper function is neater than complicating write_config() - # by passing a flag to it, plus we only need to look at symbols here. - - with self._open("auto.conf", "w") as f: - for sym in self.unique_defined_syms: - if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value): - f.write(sym.config_string) - - def _load_old_vals(self): + def _load_old_vals(self, path): # Loads old symbol values from auto.conf into a dedicated # Symbol._old_val field. Mirrors load_config(). # @@ -1581,11 +1560,15 @@ def _load_old_vals(self): for sym in self.unique_defined_syms: sym._old_val = None - if not exists("auto.conf"): - # No old values - return + try: + auto_conf = self._open(join(path, "auto.conf"), "r") + except IOError as e: + if e.errno == errno.ENOENT: + # No old values + return + raise - with self._open("auto.conf", "r") as f: + with auto_conf as f: for line in f: match = self._set_match(line) if not match: @@ -1607,7 +1590,22 @@ def _load_old_vals(self): else: # Flag that the symbol no longer exists, in # case something still depends on it - _touch_dep_file(name) + _touch_dep_file(path, name) + + def _write_old_vals(self, path): + # Helper for writing auto.conf. Basically just a simplified + # write_config() that doesn't write any comments (including + # '# CONFIG_FOO is not set' comments). The format matches the C + # implementation, though the ordering is arbitrary there (depends on + # the hash table implementation). + # + # A separate helper function is neater than complicating write_config() + # by passing a flag to it, plus we only need to look at symbols here. + + with self._open(join(path, "auto.conf"), "w") as f: + for sym in self.unique_defined_syms: + if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value): + f.write(sym.config_string) def node_iter(self, unique_syms=False): """ @@ -1695,8 +1693,8 @@ def eval_string(self, s): def unset_values(self): """ - Resets the user values of all symbols, as if Kconfig.load_config() or - Symbol.set_value() had never been called. + Removes any user values from all symbols, as if Kconfig.load_config() + or Symbol.set_value() had never been called. """ self._warn_for_no_prompt = False try: @@ -2663,8 +2661,8 @@ def _parse_block(self, end_token, parent, prev): self._leave_file() elif t0 is end_token: - # We have reached the end of the block. Terminate the final - # node and return it. + # Reached the end of the block. Terminate the final node and + # return it. if self._tokens[1] is not None: self._trailing_tokens_error() @@ -2759,8 +2757,6 @@ def _parse_block(self, end_token, parent, prev): elif t0 is _T_MAINMENU: self.top_node.prompt = (self._expect_str_and_eol(), self.y) - self.top_node.filename = self._filename - self.top_node.linenr = self._linenr else: # A valid endchoice/endif/endmenu is caught by the 'end_token' @@ -3636,12 +3632,10 @@ def is_num(s): sym.name != "MODULES": msg = "undefined symbol {}:".format(sym.name) - for node in self.node_iter(): if sym in node.referenced: msg += "\n\n- Referenced at {}:{}:\n\n{}" \ .format(node.filename, node.linenr, node) - self._warn(msg) def _warn(self, msg, filename=None, linenr=None): @@ -3828,7 +3822,7 @@ class Symbol(object): ranges: List of (low, high, cond) tuples for the symbol's 'range' properties. For example, 'range 1 2 if A' is represented as (1, 2, A). If there is no - condition, 'cond' is self.config.y. + condition, 'cond' is self.kconfig.y. Note that 'depends on' and parent dependencies are propagated to 'range' conditions. @@ -3849,21 +3843,45 @@ class Symbol(object): Like rev_dep, for imply. direct_dep: - The 'depends on' dependencies. If a symbol is defined in multiple - locations, the dependencies at each location are ORed together. + The direct ('depends on') dependencies for the symbol, or self.kconfig.y + if there are no direct dependencies. + + This attribute includes any dependencies from surrounding menus and if's. + Those get propagated to the direct dependencies, and the resulting direct + dependencies in turn get propagated to the conditions of all properties. - Internally, this is used to implement 'imply', which only applies if the - implied symbol has expr_value(self.direct_dep) != 0. 'depends on' and - parent dependencies are automatically propagated to the conditions of - properties, so normally it's redundant to check the direct dependencies. + If the symbol is defined in multiple locations, the dependencies from the + different locations get ORed together. referenced: A set() with all symbols and choices referenced in the properties and property conditions of the symbol. - Also includes dependencies inherited from surrounding menus and if's. + Also includes dependencies from surrounding menus and if's, because those + get propagated to the symbol (see the 'Intro to symbol values' section in + the module docstring). + Choices appear in the dependencies of choice symbols. + For the following definitions, only B and not C appears in A's + 'referenced'. To get transitive references, you'll have to recursively + expand 'references' until no new items appear. + + config A + bool + depends on B + + config B + bool + depends on C + + config C + bool + + See the Symbol.direct_dep attribute if you're only interested in the + direct dependencies of the symbol (its 'depends on'). You can extract the + symbols in it with the global expr_items() function. + env_var: If the Symbol has an 'option env="FOO"' option, this contains the name ("FOO") of the environment variable. None for symbols without no @@ -4286,8 +4304,8 @@ def set_value(self, value): def unset_value(self): """ - Resets the user value of the symbol, as if the symbol had never gotten - a user value via Kconfig.load_config() or Symbol.set_value(). + Removes any user value from the symbol, as if the symbol had never + gotten a user value via Kconfig.load_config() or Symbol.set_value(). """ if self.user_value is not None: self.user_value = None @@ -4721,7 +4739,7 @@ class Choice(object): defaults: List of (symbol, cond) tuples for the choice's 'defaults' properties. For example, 'default A if B && C' is represented as (A, (AND, B, C)). If - there is no condition, 'cond' is self.config.y. + there is no condition, 'cond' is self.kconfig.y. Note that 'depends on' and parent dependencies are propagated to 'default' conditions. @@ -4733,7 +4751,9 @@ class Choice(object): A set() with all symbols referenced in the properties and property conditions of the choice. - Also includes dependencies inherited from surrounding menus and if's. + Also includes dependencies from surrounding menus and if's, because those + get propagated to the choice (see the 'Intro to symbol values' section in + the module docstring). is_optional: True if the choice has the 'optional' flag set on it and can be in @@ -5124,10 +5144,12 @@ class MenuNode(object): was undocumented. dep: - The 'depends on' dependencies for the menu node, or self.kconfig.y if - there are no dependencies. Parent dependencies are propagated to this - attribute, and this attribute is then in turn propagated to the - properties of symbols and choices. + The direct ('depends on') dependencies for the menu node, or + self.kconfig.y if there are no direct dependencies. + + This attribute includes any dependencies from surrounding menus and if's. + Those get propagated to the direct dependencies, and the resulting direct + dependencies in turn get propagated to the conditions of all properties. If a symbol or choice is defined in multiple locations, only the properties defined at a particular location get the corresponding @@ -5879,13 +5901,13 @@ def _sym_to_num(sym): int(sym.str_value, _TYPE_TO_BASE[sym.orig_type]) -def _touch_dep_file(sym_name): +def _touch_dep_file(path, sym_name): # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the sync_deps() # docstring. - sym_path = sym_name.lower().replace("_", os.sep) + ".h" + sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h" sym_path_dir = dirname(sym_path) - if sym_path_dir and not exists(sym_path_dir): + if not exists(sym_path_dir): os.makedirs(sym_path_dir, 0o755) # A kind of truncating touch, mirroring the C tools @@ -5894,29 +5916,24 @@ def _touch_dep_file(sym_name): def _save_old(path): - # See write_config() - - dirname, basename = split(path) - backup = join(dirname, - basename + ".old" if basename.startswith(".") - else "." + basename + ".old") + # See write_config(). os.replace() would be nice here, but it's Python 3 + # (3.3+) only. - # os.replace() would be nice here, but it's Python 3 (3.3+) only try: - # Use copyfile() if 'path' is a symlink. The intention is probably to - # overwrite the target in that case. if os.name == "posix" and not islink(path): - # Will remove ..old if it already exists on POSIX + # Will remove .old if it already exists on POSIX # systems - os.rename(path, backup) + os.rename(path, path + ".old") else: - # Only import as needed, to save some startup time + # Use copyfile() if 'path' is a symlink. The intention is probably + # to overwrite the target in that case. Only import shutil as + # needed, to save some startup time. import shutil - shutil.copyfile(path, backup) + shutil.copyfile(path, path + ".old") except: # Ignore errors from 'filename' missing as well as other errors. The - # backup file is more of a nice-to-have, and not worth erroring out - # over e.g. if ..old happens to be a directory. + # .old file is usually more of a nice-to-have, and not worth + # erroring out over e.g. if .old happens to be a directory. pass @@ -5994,7 +6011,6 @@ def _auto_menu_dep(node1, node2): # node2 has a prompt, we check its condition. Otherwise, we look directly # at node2.dep. - # If node2 has no prompt, use its menu node dependencies instead return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep, node1.item) diff --git a/scripts/kconfig/menuconfig.py b/scripts/kconfig/menuconfig.py index 74eb38bc4659..8ecf6115f96a 100755 --- a/scripts/kconfig/menuconfig.py +++ b/scripts/kconfig/menuconfig.py @@ -20,14 +20,14 @@ G/End : Jump to end of list g/Home : Jump to beginning of list +[Space] toggles values if possible, and enters menus otherwise. [Enter] works +the other way around. + The mconf feature where pressing a key jumps to a menu entry with that character in it in the current menu isn't supported. A jump-to feature for jumping directly to any symbol (including invisible symbols), choice, menu or comment (as in a Kconfig 'comment "Foo"') is available instead. -Space and Enter are "smart" and try to do what you'd expect for the given menu -entry. - A few different modes are available: F: Toggle show-help mode, which shows the help text of the currently selected @@ -55,6 +55,9 @@ The KCONFIG_CONFIG environment variable specifies the .config file to load (if it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used. +When overwriting a configuration file, the old version is saved to +.old (e.g. .config.old). + $srctree is supported through Kconfiglib. @@ -96,7 +99,7 @@ - fg:COLOR Set the foreground/background colors. COLOR can be one of * or * the basic 16 colors (black, red, green, yellow, blue, - - bg:COLOR magenta,cyan, white and brighter versions, for example, + - bg:COLOR magenta, cyan, white and brighter versions, for example, brightred). On terminals that support more than 8 colors, you can also directly put in a color number, e.g. fg:123 (hexadecimal and octal constants are accepted as well). @@ -193,7 +196,7 @@ import textwrap from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \ - BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \ + BOOL, TRISTATE, STRING, INT, HEX, \ AND, OR, \ expr_str, expr_value, split_expr, \ standard_sc_expr_str, \ @@ -244,7 +247,7 @@ # Lines of help text shown at the bottom of the information dialog _INFO_HELP_LINES = """ -[ESC/q] Return to menu [/] Jump to symbol +[ESC/q] Return to menu [/] Jump to symbol """[1:-1].split("\n") # Lines of help text shown at the bottom of the search dialog @@ -628,7 +631,6 @@ def _style_attr(fg_color, bg_color, attribs, color_attribs={}): # -# Used as the entry point in setup.py def _main(): menuconfig(standard_kconfig()) @@ -728,7 +730,7 @@ def _needs_save(): if sym.config_string: # Unwritten symbol return True - elif sym.type in (BOOL, TRISTATE): + elif sym.orig_type in (BOOL, TRISTATE): if sym.tri_value != sym.user_value: # Written bool/tristate symbol, new value return True @@ -769,7 +771,7 @@ def _needs_save(): # If True, the corresponding mode is on. See the module docstring. # # _conf_filename: -# .config file to save the configuration to +# File to save the configuration to # # _minconf_filename: # File to save minimal configurations to @@ -828,26 +830,17 @@ def _menuconfig(stdscr): elif c in (curses.KEY_HOME, "g"): _select_first_menu_entry() - elif c in (curses.KEY_RIGHT, " ", "\n", "l", "L"): - # Do appropriate node action. Only Space is treated specially, - # preferring to toggle nodes rather than enter menus. - + elif c == " ": + # Toggle the node if possible sel_node = _shown[_sel_node_i] - - if sel_node.is_menuconfig and not \ - (c == " " and _prefer_toggle(sel_node.item)): - + if not _change_node(sel_node): _enter_menu(sel_node) - else: + elif c in (curses.KEY_RIGHT, "\n", "l", "L"): + # Enter the node if possible + sel_node = _shown[_sel_node_i] + if not _enter_menu(sel_node): _change_node(sel_node) - if _is_y_mode_choice_sym(sel_node.item) and not sel_node.list: - # Immediately jump to the parent menu after making a choice - # selection, like 'make menuconfig' does, except if the - # menu node has children (which can happen if a symbol - # 'depends on' a choice symbol that immediately precedes - # it). - _leave_menu() elif c in ("n", "N"): _set_sel_node_tri_val(0) @@ -1059,39 +1052,40 @@ def _width(win): return win.getmaxyx()[1] -def _prefer_toggle(item): - # For nodes with menus, determines whether Space should change the value of - # the node's item or enter its menu. We toggle symbols (which have menus - # when they're defined with 'menuconfig') and choices that can be in more - # than one mode (e.g. optional choices). In other cases, we enter the menu. - - return isinstance(item, Symbol) or \ - (isinstance(item, Choice) and len(item.assignable) > 1) - - def _enter_menu(menu): - # Makes 'menu' the currently displayed menu. "Menu" here includes choices - # and symbols defined with the 'menuconfig' keyword. + # Makes 'menu' the currently displayed menu. In addition to actual 'menu's, + # "menu" here includes choices and symbols defined with the 'menuconfig' + # keyword. + # + # Returns False if 'menu' can't be entered. global _cur_menu global _shown global _sel_node_i global _menu_scroll + if not menu.is_menuconfig: + # Not a menu + return False + shown_sub = _shown_nodes(menu) # Never enter empty menus. We depend on having a current node. - if shown_sub: - # Remember where the current node appears on the screen, so we can try - # to get it to appear in the same place when we leave the menu - _parent_screen_rows.append(_sel_node_i - _menu_scroll) + if not shown_sub: + return False + + # Remember where the current node appears on the screen, so we can try + # to get it to appear in the same place when we leave the menu + _parent_screen_rows.append(_sel_node_i - _menu_scroll) - # Jump into menu - _cur_menu = menu - _shown = shown_sub - _sel_node_i = _menu_scroll = 0 + # Jump into menu + _cur_menu = menu + _shown = shown_sub + _sel_node_i = _menu_scroll = 0 + + if isinstance(menu.item, Choice): + _select_selected_choice_sym() - if isinstance(menu.item, Choice): - _select_selected_choice_sym() + return True def _select_selected_choice_sym(): @@ -1225,7 +1219,7 @@ def _select_prev_menu_entry(): _sel_node_i -= 1 # See _select_next_menu_entry() - if _sel_node_i <= _menu_scroll + _SCROLL_OFFSET: + if _sel_node_i < _menu_scroll + _SCROLL_OFFSET: _menu_scroll = max(_menu_scroll - 1, 0) @@ -1418,7 +1412,7 @@ def _draw_main(): _path_win.erase() - # Draw the menu path ("(top menu) -> menu -> submenu -> ...") + # Draw the menu path ("(Top) -> Menu -> Submenu -> ...") menu_prompts = [] @@ -1430,7 +1424,7 @@ def _draw_main(): menu_prompts.append(menu.prompt[0] if menu.prompt else standard_sc_expr_str(menu.item)) menu = menu.parent - menu_prompts.append("(top menu)") + menu_prompts.append("(Top)") menu_prompts.reverse() # Hack: We can't put ACS_RARROW directly in the string. Temporarily @@ -1471,10 +1465,6 @@ def rec(node): res = [] while node: - # This code is minorly performance-sensitive. Make it too slow - # (e.g., by always recursing the entire tree), and going in and out - # of menus no longer feels instant. - if _visible(node) or _show_all: res.append(node) if node.list and not node.is_menuconfig: @@ -1483,14 +1473,11 @@ def rec(node): # menus and choices as well as 'menuconfig' symbols. res += rec(node.list) - elif node.list and isinstance(node.item, Symbol) and \ - expr_value(node.dep): + elif node.list and isinstance(node.item, Symbol): # Show invisible symbols if they have visible children. This # can happen for an m/y-valued symbol with an optional prompt - # ('prompt "foo" is COND') that is currently disabled. The - # expr_value(node.dep) check safely prunes the search: A node - # with unsatisfied direct dependencies can never have visible - # children. + # ('prompt "foo" is COND') that is currently disabled. Note + # that it applies to both 'config' and 'menuconfig' symbols. shown_children = rec(node.list) if shown_children: res.append(node) @@ -1553,36 +1540,32 @@ def _change_node(node): # Changes the value of the menu node 'node' if it is a symbol. Bools and # tristates are toggled, while other symbol types pop up a text entry # dialog. + # + # Returns False if the value of 'node' can't be changed. - if not isinstance(node.item, (Symbol, Choice)): - return - - # This will hit for invisible symbols, which appear in show-all mode and - # when an invisible symbol has visible children (which can happen e.g. for - # symbols with optional prompts) - if not (node.prompt and expr_value(node.prompt[1])): - return + if not _changeable(node): + return False # sc = symbol/choice sc = node.item - if sc.type in (INT, HEX, STRING): + if sc.orig_type in (INT, HEX, STRING): s = sc.str_value while True: s = _input_dialog( - "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.type]), + "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.orig_type]), s, _range_info(sc)) if s is None: break - if sc.type in (INT, HEX): + if sc.orig_type in (INT, HEX): s = s.strip() # 'make menuconfig' does this too. Hex values not starting with # '0x' are accepted when loading .config files though. - if sc.type == HEX and not s.startswith(("0x", "0X")): + if sc.orig_type == HEX and not s.startswith(("0x", "0X")): s = "0x" + s if _check_valid(sc, s): @@ -1594,13 +1577,42 @@ def _change_node(node): # case: .assignable can be (2,) while .tri_value is 0. _set_val(sc, sc.assignable[0]) - elif sc.assignable: + else: # Set the symbol to the value after the current value in # sc.assignable, with wrapping val_index = sc.assignable.index(sc.tri_value) _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)]) + if _is_y_mode_choice_sym(sc) and not node.list: + # Immediately jump to the parent menu after making a choice selection, + # like 'make menuconfig' does, except if the menu node has children + # (which can happen if a symbol 'depends on' a choice symbol that + # immediately precedes it). + _leave_menu() + + + return True + + +def _changeable(node): + # Returns True if the value if 'node' can be changed + + sc = node.item + + if not isinstance(sc, (Symbol, Choice)): + return False + + # This will hit for invisible symbols, which appear in show-all mode and + # when an invisible symbol has visible children (which can happen e.g. for + # symbols with optional prompts) + if not (node.prompt and expr_value(node.prompt[1])): + return False + + return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \ + or _is_y_mode_choice_sym(sc) + + def _set_sel_node_tri_val(tri_val): # Sets the value of the currently selected menu entry to 'tri_val', if that # value can be assigned @@ -2022,7 +2034,7 @@ def select_prev_match(): if sel_node_i > 0: sel_node_i -= 1 - if sel_node_i <= scroll + _SCROLL_OFFSET: + if sel_node_i < scroll + _SCROLL_OFFSET: scroll = max(scroll - 1, 0) while True: @@ -2670,7 +2682,7 @@ def _split_expr_info(expr, indent): s = "" for i, term in enumerate(split_expr(expr, split_op)): - s += "{}{} {}".format(" "*indent, + s += "{}{} {}".format(indent*" ", " " if i == 0 else op_str, _expr_str(term)) @@ -2769,7 +2781,7 @@ def _menu_path_info(node): path = " -> " + (node.prompt[0] if node.prompt else standard_sc_expr_str(node.item)) + path - return "(top menu)" + path + return "(Top)" + path def _name_and_val_str(sc): @@ -2952,8 +2964,7 @@ def _node_str(node): # Print "(NEW)" next to symbols without a user value (from e.g. a # .config), but skip it for choice symbols in choices in y mode, # and for symbols of UNKNOWN type (which generate a warning though) - if sym.user_value is None and \ - sym.type != UNKNOWN and \ + if sym.user_value is None and sym.orig_type and \ not (sym.choice and sym.choice.tri_value == 2): s += " (NEW)" @@ -3005,10 +3016,10 @@ def _value_str(node): return "" # Wouldn't normally happen, and generates a warning - if item.type == UNKNOWN: + if not item.orig_type: return "" - if item.type in (STRING, INT, HEX): + if item.orig_type in (STRING, INT, HEX): return "({})".format(item.str_value) # BOOL or TRISTATE @@ -3042,16 +3053,16 @@ def _check_valid(sym, s): # Returns True if the string 's' is a well-formed value for 'sym'. # Otherwise, displays an error and returns False. - if sym.type not in (INT, HEX): + if sym.orig_type not in (INT, HEX): # Anything goes for non-int/hex symbols return True - base = 10 if sym.type == INT else 16 + base = 10 if sym.orig_type == INT else 16 try: int(s, base) except ValueError: _error("'{}' is a malformed {} value" - .format(s, TYPE_TO_STR[sym.type])) + .format(s, TYPE_TO_STR[sym.orig_type])) return False for low_sym, high_sym, cond in sym.ranges: @@ -3075,7 +3086,7 @@ def _range_info(sym): # Returns a string with information about the valid range for the symbol # 'sym', or None if 'sym' doesn't have a range - if sym.type in (INT, HEX): + if sym.orig_type in (INT, HEX): for low, high, cond in sym.ranges: if expr_value(cond): return "Range: {}-{}".format(low.str_value, high.str_value)