From bc2909f8404cf29adc2dc62ec90f24ac9046b3ca Mon Sep 17 00:00:00 2001 From: Mykyta Holubakha Date: Mon, 21 Aug 2017 07:23:17 +0300 Subject: Initial implementation of zugaina picker gui fixes in zugaina fetcher --- pomu/cli.py | 9 ++++ pomu/data/zugaina.py | 11 +++-- pomu/search.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ pomu/util/iquery.py | 14 +++--- 4 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 pomu/search.py diff --git a/pomu/cli.py b/pomu/cli.py index 13a9738..8de900f 100644 --- a/pomu/cli.py +++ b/pomu/cli.py @@ -146,6 +146,15 @@ def show(package): print('Backend:', pkg.backend.__name__) print('Backend detailes:', pkg.backend) +@main.command() +@click.argument('query', required=True) +@click.option('--fetch-only', default=False, is_flag=True) +def search(query): + """Search gpo.zugaina.org""" + ds = ZugainaDataSource(query) + p = PSPrompt(ds) + packages = p.run() + def main_(): try: main.main(standalone_mode=False) diff --git a/pomu/data/zugaina.py b/pomu/data/zugaina.py index 25437fc..6f67444 100644 --- a/pomu/data/zugaina.py +++ b/pomu/data/zugaina.py @@ -23,13 +23,13 @@ class ZugainaDataSource(DataSource): text = self.fetch_page(1) doc = lxml.html.document_fromstring(text) field = doc.xpath('//div[@class="pager"]/span')[0].text - self.pagecount = (field.split(' ')[-1] + 49) // 50 + self.pagecount = (int(field.split(' ')[-1]) + 49) // 50 return self.pagecount def get_page(self, page): text = self.fetch_page(page) doc = lxml.html.document_fromstring(text) - return [(strip(x.text), x.getchildren()[0].text) + return [(x.text.strip(), x.getchildren()[0].text) for x in doc.xpath('//div[@id="search_results"]/a/div')] def list_items(self, ident): @@ -38,12 +38,13 @@ class ZugainaDataSource(DataSource): res = [] for div in doc.xpath('//div[@id="ebuild_list"]/ul/div'): id_ = div.xpath('li/a')[0].get('href').split('/')[3] - pv = div.xpath('li/div/b').text + pv = div.xpath('li/div/b')[0].text overlay = div.xpath('@id') - res.append(id_, pv, overlay) + res.append((id_, pv, overlay)) + return res def get_item(self, ident): - return results.get(BASE_URL + 'AJAX/Ebuild/' + ident).text + return requests.get(BASE_URL + 'AJAX/Ebuild/' + str(ident)).text def fetch_item(self, ident): if ident in self.itemcache: diff --git a/pomu/search.py b/pomu/search.py new file mode 100644 index 0000000..6f0a2b0 --- /dev/null +++ b/pomu/search.py @@ -0,0 +1,130 @@ +from enum import Enum +from pydoc import pager + +from curtsies import FullscreenWindow, width, invert, fmtstr + +from pomu.util.iquery import Prompt, clamp + +class Entry: + def __init__(self, item, source, children=None): + self.expanded = False + self._source = source + self.item = item + self.children = children + + def toggle(self, idx): + if idx == 0: + self.expanded = not self.expanded + else: + child = self.children[idx - 1] + self.children[idx - 1] = (not child[0], child[1]) + if self.expanded and not self.children: + self.children = [(False, x) for x in self._source.list_items(self.item[0])] + + def get_idx(self, idx): + return ((self, None if idx == 0 or not self.children else self.children[idx-1])) + + def __len__(self): + return len(self.children) + 1 if self.expanded else 1 + + def selected(self): + return [child for child in self.children if child[0]] + +class PromptState(Enum): + TOP_PREV=0 + TOP_OK=1 + LIST=2 + +class PSPrompt(Prompt): + def __init__(self, source): + super().__init__([]) + self.data = source + self.page_count = self.data.page_count() + self.pages = {} + self.state = PromptState.LIST + self.set_page(1) + + def set_page(self, page): + self.idx = 0 + self.page = page + if page in self.pages: + self.entries = self.pages[page] + else: + self.entries = [self.process_entry(x) for x in self.data.get_page(page)] + self.pages[page] = self.entries + + def render(self): + title = str(self.page) + if self.page > 1: + title = ('[ ' + + (invert('Prev') if self.state == 0 else ('Prev')) + + ' ] ' + title) + if self.page < self.page_count: + title = (title + ' [ ' + + (invert('Next') if self.state == 1 else ('Next')) + + ' ]') + title = center(title) + bottom = ('[ ' + invert('OK') if self.idx == len(self) else 'OK' + ' ]') + bottom = center(bottom) + items = [render_entry(e, idx) for idx, e in enumerate(self.lens())] + output = fsarray([title] + items + [bottom]) + self.window.render_to_terminal(output) + + def render_entry(self, entry, idx): + winw = self.window.width + if entry[1]: + hld, data = *entry[1] + stt = '*' if hld else ' ' + text = ' [' + invert(stt) if idx == 0 else stt + '] ' + text += '{}::{}'.format(data[1], data[2]) + else if entry[0]: + data = entry[0] + exp = 'v' if entry.expanded else '>' + text = '[' + invert(exp) if idx == 0 else exp + '] ' + text += data[0] + ' ' + strw = fmtstr(text).width + insw = fmtstr(data[1]).width + text += data[1][:winw - strw - 2] + '..' if insw + strw > winw else data[1] + else: + text = '' + return text + + + def __len__(self): + return sum(lun(y) for y in self.entries) + + def center(self, stri): + tw = fmtstr(stri).width + pw = (self.window.width - tw) // 2 + return ' ' * pw + stri + ' ' * pw + + def clamp(self, x): + return clamp(x, 0, len(self)) + + def preview(self): + target = self.get_target() + if target[1]: + data = self.data.get_item(target[1][0]) + pager(data) + + def lens(self): + h = self.window.height - 2 + lst = [self.get_idx(i) for i in range(self.idx, self.clamp(self.idx + h))] + lst += [(None, None)] * clamp(h - len(lst), 0, h) + return lst + + def get_target(self): + return get_idx(self.idx) + + def get_idx(self, idx): + for entry in self.entries(): + if len(entry) > idx: + break + idx -= len(entry) + return entry.get_idx(idx) + + def process_entry(self, entry): + return Entry(item, self.data) + + def run(self): + return super().run(FullscreenWindow, hide_cursor=True) diff --git a/pomu/util/iquery.py b/pomu/util/iquery.py index f5ca50c..606d79d 100644 --- a/pomu/util/iquery.py +++ b/pomu/util/iquery.py @@ -32,14 +32,15 @@ class Prompt: self.idx = 0 self.cursor_pos = Position() - def run(self): + def run(self, window_type=CursorAwareWindow, **args): with open('/dev/tty', 'r') as tty_in, \ open('/dev/tty', 'w') as tty_out, \ Input(in_stream=tty_in) as input_, \ - CursorAwareWindow(in_stream=tty_in, - out_stream=tty_out, - hide_cursor=False, - extra_bytes_callback=input_.unget_bytes) as window: + window_type(in_stream=tty_in, + out_stream=tty_out, + hide_cursor=False, + extra_bytes_callback=input_.unget_bytes, + **args) as window: self.window = window self.render() for event in input_: @@ -163,7 +164,8 @@ class EditSelectPrompt(Prompt): self.list = True elif isinstance(event, str) and not event.startswith('<'): self.add_char(event) - else: + else, + **args: return False return True -- cgit v1.2.3-65-gdbad