<commit_before>#!/usr/bin/env python3
import fcntl
import termios
import sys
import struct
import urwid
import subprocess
import signal
import re
import os
import fileinput


"""Installation:
test with:
$ python3 selecta.py <(history)

create a symlink:
$ sudo ln -s selecta.py /usr/bin/selecta

Add this to your .bashrc to bind the command to ALT+e:
bind '"\C-[e":"\C-a\C-kselecta <(history)\C-m"'
"""

palette = [
    ('head', '', '', '', '#aaa', '#618'),
    ('body', '', '', '', '#ddd', '#000'),
    ('focus', '', '', '', '#000', '#da0'),
    ('input', '', '', '', '#fff', '#618'),
    ('empty_list', '', '', '', '#ddd', '#b00'),
    ('pattern', '', '', '', '#f91', ''),
    ('pattern_focus', '', '', '', 'bold,#a00', '#da0'),
    ('line','', '', '', '', ''),
    ('line_focus','', '', '', '#000', '#da0'),
]

signal.signal(signal.SIGINT, lambda *_: sys.exit(0))  # die with style


class ItemWidget(urwid.WidgetWrap):
    def __init__(self, list_item, show_hits, match=None):
        self.list_item = list_item
        if match is not None and show_hits is True:
            parts = self.list_item.partition(match)
            text = urwid.AttrMap(
                urwid.Text(
                    [parts[0],
                    ('pattern', parts[1]),
                    parts[2]
                ]
            ), 'line', {'pattern': 'pattern_focus', None: 'line_focus'})
        else:
            text = urwid.AttrMap(urwid.Text(self.list_item), 'line', 'line_focus')

        urwid.WidgetWrap.__init__(self, text)

    def selectable(self):
        return True

    def keypress(self, size, key):
        return key


class SearchEdit(urwid.Edit):
    __metaclass__ = urwid.signals.MetaSignals
    signals = ['done', 'toggle_regexp_modifier', 'toggle_case_modifier']

    def keypress(self, size, key):
        if key == 'enter':
            urwid.emit_signal(self, 'done', self.get_edit_text())
            return
        elif key == 'esc':
            urwid.emit_signal(self, 'done', None)
            return
        elif key == 'tab':
            urwid.emit_signal(self, 'toggle_case_modifier')
            urwid.emit_signal(self, 'change', self, self.get_edit_text())
            return
        elif key == 'ctrl r':
            urwid.emit_signal(self, 'toggle_regexp_modifier')
            urwid.emit_signal(self, 'change', self, self.get_edit_text())
            return
        elif key == 'down':
            urwid.emit_signal(self, 'done', None)
            return

        urwid.Edit.keypress(self, size, key)


class ResultList(urwid.ListBox):
    __metaclass__ = urwid.signals.MetaSignals
    signals = ['resize']

    def __init__(self, *args):
        self.last_size = 0
        urwid.ListBox.__init__(self, *args)

    def render(self, size, focus):
        if size != self.last_size:
            self.last_size = size
            urwid.emit_signal(self, 'resize', size[1])
        return urwid.ListBox.render(self, size, focus)


class LineCountWidget(urwid.Text):
    pass


class Selector(object):
    def __init__(self, revert_order, remove_bash_prefix, remove_zsh_prefix, regexp, case_sensitive,
                 remove_duplicates, show_hits, infile):

        self.show_hits = show_hits
        self.regexp_modifier = regexp
        self.case_modifier = case_sensitive

        self.list_items = []

        for line in infile:
            if remove_bash_prefix:
                line = line.split(None, 1)[1]

            if remove_zsh_prefix:
                line = re.split('\s+', line, maxsplit=4)[-1]

            if 'selecta <(history)' not in line:
                if remove_duplicates:
                    if line not in self.list_items:
                        self.list_items.append(line)
                else:
                    self.list_items.append(line)

        if revert_order:
            self.list_items.reverse()

        self.list_item_widgets = []

        self.line_count_display = LineCountWidget('')
        self.search_edit = SearchEdit(edit_text='')

        self.modifier_display = urwid.Text('')

        urwid.connect_signal(self.search_edit, 'done', self.edit_done)
        urwid.connect_signal(self.search_edit, 'toggle_case_modifier', self.toggle_case_modifier)
        urwid.connect_signal(self.search_edit, 'toggle_regexp_modifier', self.toggle_regexp_modifier)
        urwid.connect_signal(self.search_edit, 'change', self.edit_change)

        header = urwid.AttrMap(urwid.Columns([  # TODO do I really need columns?
            (8, self.line_count_display), # TODO pack at runtime (lines get wrapped)?
            urwid.AttrMap(self.search_edit, 'input', 'input'),  # TODO pack at runtime?
            self.modifier_display,
        ]), 'head', 'head')

        self.item_list = urwid.SimpleListWalker(self.list_item_widgets)
        self.listbox = ResultList(self.item_list)

        urwid.connect_signal(self.listbox, 'resize', self.list_resize)

        self.view = urwid.Frame(body=self.listbox, header=header)

        self.loop = urwid.MainLoop(self.view, palette, unhandled_input=self.on_unhandled_input)
        self.loop.screen.set_terminal_properties(colors=256)

        self.update_list('')
        self.loop.run()

    def list_resize(self, height):
        self.line_count_display.set_text('{}/{} '.format(len(self.list_item_widgets), height))

    def meep(self, *args):
        logger.info(args)

    def toggle_case_modifier(self):
        self.case_modifier = not self.case_modifier
        self.update_modifiers()<commit_msg>fixed line counter<commit_after>#!/usr/bin/env python3
import fcntl
import termios
import sys
import struct
import urwid
import subprocess
import signal
import re
import os
import fileinput


"""Installation:
test with:
$ python3 selecta.py <(history)

create a symlink:
$ sudo ln -s selecta.py /usr/bin/selecta

Add this to your .bashrc to bind the command to ALT+e:
bind '"\C-[e":"\C-a\C-kselecta <(history)\C-m"'
"""

palette = [
    ('head', '', '', '', '#aaa', '#618'),
    ('body', '', '', '', '#ddd', '#000'),
    ('focus', '', '', '', '#000', '#da0'),
    ('input', '', '', '', '#fff', '#618'),
    ('empty_list', '', '', '', '#ddd', '#b00'),
    ('pattern', '', '', '', '#f91', ''),
    ('pattern_focus', '', '', '', 'bold,#a00', '#da0'),
    ('line','', '', '', '', ''),
    ('line_focus','', '', '', '#000', '#da0'),
]

signal.signal(signal.SIGINT, lambda *_: sys.exit(0))  # die with style


class ItemWidget(urwid.WidgetWrap):
    def __init__(self, list_item, show_hits, match=None):
        self.list_item = list_item
        if match is not None and show_hits is True:
            parts = self.list_item.partition(match)
            text = urwid.AttrMap(
                urwid.Text(
                    [parts[0],
                    ('pattern', parts[1]),
                    parts[2]
                ]
            ), 'line', {'pattern': 'pattern_focus', None: 'line_focus'})
        else:
            text = urwid.AttrMap(urwid.Text(self.list_item), 'line', 'line_focus')

        urwid.WidgetWrap.__init__(self, text)

    def selectable(self):
        return True

    def keypress(self, size, key):
        return key


class SearchEdit(urwid.Edit):
    __metaclass__ = urwid.signals.MetaSignals
    signals = ['done', 'toggle_regexp_modifier', 'toggle_case_modifier']

    def keypress(self, size, key):
        if key == 'enter':
            urwid.emit_signal(self, 'done', self.get_edit_text())
            return
        elif key == 'esc':
            urwid.emit_signal(self, 'done', None)
            return
        elif key == 'tab':
            urwid.emit_signal(self, 'toggle_case_modifier')
            urwid.emit_signal(self, 'change', self, self.get_edit_text())
            return
        elif key == 'ctrl r':
            urwid.emit_signal(self, 'toggle_regexp_modifier')
            urwid.emit_signal(self, 'change', self, self.get_edit_text())
            return
        elif key == 'down':
            urwid.emit_signal(self, 'done', None)
            return

        urwid.Edit.keypress(self, size, key)


class ResultList(urwid.ListBox):
    __metaclass__ = urwid.signals.MetaSignals
    signals = ['resize']

    def __init__(self, *args):
        self.last_size = 0
        urwid.ListBox.__init__(self, *args)

    def render(self, size, focus):
        if size != self.last_size:
            self.last_size = size
            urwid.emit_signal(self, 'resize', size[1])
        return urwid.ListBox.render(self, size, focus)


class LineCountWidget(urwid.Text):
    pass


class Selector(object):
    def __init__(self, revert_order, remove_bash_prefix, remove_zsh_prefix, regexp, case_sensitive,
                 remove_duplicates, show_hits, infile):

        self.show_hits = show_hits
        self.regexp_modifier = regexp
        self.case_modifier = case_sensitive

        self.list_items = []

        for line in infile:
            if remove_bash_prefix:
                line = line.split(None, 1)[1]

            if remove_zsh_prefix:
                line = re.split('\s+', line, maxsplit=4)[-1]

            if 'selecta <(history)' not in line:
                if remove_duplicates:
                    if line not in self.list_items:
                        self.list_items.append(line)
                else:
                    self.list_items.append(line)

        if revert_order:
            self.list_items.reverse()

        self.list_item_widgets = []

        self.line_count_display = LineCountWidget('')
        self.search_edit = SearchEdit(edit_text='')

        self.modifier_display = urwid.Text('')

        urwid.connect_signal(self.search_edit, 'done', self.edit_done)
        urwid.connect_signal(self.search_edit, 'toggle_case_modifier', self.toggle_case_modifier)
        urwid.connect_signal(self.search_edit, 'toggle_regexp_modifier', self.toggle_regexp_modifier)
        urwid.connect_signal(self.search_edit, 'change', self.edit_change)

        header = urwid.AttrMap(urwid.Columns([  # TODO do I really need columns?
            (8, self.line_count_display), # TODO pack at runtime (lines get wrapped)?
            urwid.AttrMap(self.search_edit, 'input', 'input'),  # TODO pack at runtime?
            self.modifier_display,
        ]), 'head', 'head')

        self.item_list = urwid.SimpleListWalker(self.list_item_widgets)
        self.listbox = ResultList(self.item_list)

        urwid.connect_signal(self.listbox, 'resize', self.list_resize)

        self.view = urwid.Frame(body=self.listbox, header=header)

        self.loop = urwid.MainLoop(self.view, palette, unhandled_input=self.on_unhandled_input)
        self.loop.screen.set_terminal_properties(colors=256)

        self.update_list('')
        self.loop.run()

    def list_resize(self, height):
        self.line_count_display.set_text('{}/{} '.format(height, len(self.list_items)))

    def meep(self, *args):
        logger.info(args)

    def toggle_case_modifier(self):
        self.case_modifier = not self.case_modifier
        self.update_modifiers()
{'fraction_non_alphanumeric': 0.14091156709642275, 'fraction_numerical': 0.007263482840021791, 'mean_word_length': 2.475859892710634, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5030835', 'n_tokens_mistral': 3600, 'n_tokens_neox': 3440, 'n_words': 703}