diff options
author | Mad Camel <madcamel@gmail.com> | 2024-02-18 16:49:33 +0000 |
---|---|---|
committer | Mad Camel <madcamel@gmail.com> | 2024-02-18 16:49:33 +0000 |
commit | 8edebcaf5ecdb7bcc731142921e16390436fc7a3 (patch) | |
tree | eba669dc30a217af0d8a958b324dcaf9772f6c54 /attoconf/help.py | |
parent | 208f846464b96721d93436006365705fde3b6b2c (diff) | |
parent | f1a54c378b7b51b36103ad8111d66aef63e7f041 (diff) | |
download | attobuild-master.tar.gz attobuild-master.tar.bz2 attobuild-master.tar.xz attobuild-master.zip |
Import from specing's attoconf branch
See merge request legacy/attobuild!1
Diffstat (limited to 'attoconf/help.py')
-rw-r--r-- | attoconf/help.py | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/attoconf/help.py b/attoconf/help.py new file mode 100644 index 0000000..a2379a6 --- /dev/null +++ b/attoconf/help.py @@ -0,0 +1,164 @@ +# Copyright 2013 Ben Longbons <b.r.longbons@gmail.com> +# +# This file is part of attoconf. +# +# attoconf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# attoconf is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with attoconf. If not, see <http://www.gnu.org/licenses/>. + + + +def put_line_in_width(file, line, width, indent): + ''' Print a line with wrapping. + ''' + line = line.rstrip(' ') + if len(line) <= width or ' ' not in line.lstrip(' '): + # this is not just an optimization + file.writelines([line, '\n']) + return + line += ' ' + indents = indent * ' ' + + initial_spaces = len(line) - len(line.lstrip(' ')) + while line.lstrip(): + space = line.rfind(' ', initial_spaces, width + 1) + if space == -1: + space = line.find(' ', initial_spaces) + if space == -1: + space = len(line) + file.writelines([line[:space], '\n']) + line = indents + line[space+1:].lstrip(' ') + initial_spaces = indent + + +class HelpSection(object): + ''' A help section contains some header lines and some related options. + ''' + __slots__ = ( + 'headers', + 'options', + ) + def __init__(self): + ''' Create an empty help section. + ''' + self.headers = [] + self.options = [] + + def add_text(self, text, hidden): + ''' Add a header line. + ''' + self.headers.append((hidden, text)) + + def add_option(self, name, text, hidden): + ''' Add an option with its description. + ''' + self.options.append((hidden, name, text)) + + def print(self, file, hidden, width): + ''' Format (some of) the help text prettily. + ''' + if self.options: + for oh, name, ht in self.options: + if oh <= hidden: + break + else: + return False + + # options longer than this will be split into multiple lines + split_width = width / 4 - 2 + # longest option + longest_opt = 0 + for oh, name, ht in self.options: + if oh > hidden: + continue + l = len(name) + 2 + if l > split_width: + continue + if l > longest_opt: + longest_opt = l + if longest_opt == 0: + longest_opt = int(split_width) + + for oh, ht in self.headers: + if oh > hidden: + continue + put_line_in_width(file, ht, width, 0) + + for oh, name, ht in self.options: + if oh > hidden: + continue + # no, it's not really that simple + if len(name) > longest_opt: + file.writelines([' ', name, '\n']) + name = '' + line = ' %-*s%s' % (longest_opt, name, ht) + put_line_in_width(file, line, width, longest_opt + 2) + + return True + + +def detect_terminal_width(fd, DEFAULT_WIDTH=float('inf')): + ''' Detect the width of a terminal. + ''' + import os + import shutil + + if isinstance(fd, int): + try: + return os.get_terminal_size(fd) + except OSError(...): + return DEFAULT_WIDTH + else: + return shutil.get_terminal_size(fallback=(DEFAULT_WIDTH, 24)).columns + + +class Help(object): + ''' Help collects the set of flavor text and option descriptions + related to arguments. + ''' + __slots__ = ( + 'sections', + ) + def __init__(self): + ''' Help does not take any arguments during construction. + ''' + self.sections = [] + + def add_text(self, help, hidden): + ''' Add a header line. + + This creates a new section if the last line wasn't a header. + ''' + if not self.sections or self.sections[-1].options: + self.sections.append(HelpSection()) + self.sections[-1].add_text(help, hidden) + + def add_option(self, name, help, hidden): + ''' Add an option with its description to the current section. + + This creates a new section only if it is first. + ''' + if not self.sections: + self.sections.append(HelpSection()) + self.sections[-1].add_option(name, help, hidden) + + def print(self, file, hidden, width=0): + ''' Print all the help at the given level of hidden-ness. + ''' + if width == 0: + width = detect_terminal_width(file) + if width < 20: + width = 80 + + for s in self.sections: + if s.print(file, hidden, width): + file.write('\n') |