<commit_before># Copyright (c) 2017 Akuli

# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:

# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""The big text widget in the middle of the editor."""

import tkinter as tk


def spacecount(string):
    """Count how many spaces the string starts with.

    >>> spacecount('  123')
    2
    >>> spacecount('  \n')
    2
    """
    result = 0
    for char in string:
        # we don't use isspace() here because '\n'.isspace() is True
        if char != ' ':
            return result
        result += 1


class EditorText(tk.Text):

    def __init__(self, master, settings, **kwargs):
        self._settings = settings
        super().__init__(master, **kwargs)

        # These will contain callback functions that are ran after the
        # text in the textview is updated. on_other_insert callbacks are
        # ran when text is inserted to the textwidget without running
        # other callbacks, e.g. by pasting.
        self.on_cursor_move = []        # callback(lineno, column)
        self.on_linecount_changed = []  # callback(linecount)
        self.on_other_insert = []       # callback()
        self._cursorpos = (1, 0)
        self._linecount = 1

        def cursor_move(event):
            self.after_idle(self.do_cursor_move)

        self.bind('<Button-1>', cursor_move)
        self.bind('<Key>', cursor_move)
        self.bind('<Control-a>', self._on_ctrl_a)
        self.bind('<BackSpace>', self._on_backspace)
        self.bind('<Delete>', self._on_delete)
        self.bind('<Return>', self._on_return)
        self.bind('<parenright>', self._on_closing_brace)
        self.bind('<bracketright>', self._on_closing_brace)
        self.bind('<braceright>', self._on_closing_brace)
        self.bind('<Tab>', lambda event: self._on_tab(False))
        if self.tk.call('tk', 'windowingsystem') == 'x11':
            # even though the event keysym says Left, holding down right
            # shift and pressing tab also runs this event... 0_o
            self.bind('<ISO_Left_Tab>', lambda event: self._on_tab(True))
        else:
            self.bind('<Shift-Tab>', lambda event: self._on_tab(True))

        # Undoing or redoing is already taken care of with bind_all in
        # editor.py, but if tkinter has bound Ctrl+Z by default it gets
        # called twice.
        def _do_only_this(binding, callback):
            def real_callback(event):
                callback()
                return 'break'

            self.bind(binding, real_callback)

    def do_cursor_move(self):
        line, column = map(int, self.index('insert').split('.'))
        if self._cursorpos != (line, column):
            self._cursorpos = (line, column)
            for callback in self.on_cursor_move:
                callback(line, column)

    def do_linecount_changed(self):
        linecount = int(self.index('end-1c').split('.')[0])
        if self._linecount != linecount:
            self._linecount = linecount
            for callback in self.on_linecount_changed:
                callback(linecount)

    def do_other_insert(self):
        for callback in self.on_other_insert:
            callback()

    def _on_backspace(self, event):
        if not self.tag_ranges('sel'):
            # nothing is selected, we can do non-default stuff
            prevchar = self.get('insert-1c', 'insert')
            if prevchar:
                # not beginning of file
                lineno = int(self.index('insert').split('.')[0])
                before_cursor = self.get('%d.0' % lineno,
                                         '%d.0+1l-1c' % lineno)
                if before_cursor.isspace():
                    self.dedent(lineno)
                    return 'break'

        self.after_idle(self.do_linecount_changed)
        self.after_idle(self.do_cursor_move)
        return None

    def _on_ctrl_a(self, event):
        """Select all."""
        self.tag_add('sel', '0.0', 'end-1c')
        return 'break'     # don't run _on_key or move cursor

    def _on_delete(self, event):
        nextchar = self.get('insert', 'insert+1c')
        if nextchar:
            # not end of file
            self.after_idle(self.do_linecount_changed)
            self.after_idle(self.do_cursor_move)<commit_msg>git wanted me 2 add this shit<commit_after># Copyright (c) 2017 Akuli

# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:

# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""The big text widget in the middle of the editor."""

import tkinter as tk


def spacecount(string):
    """Count how many spaces the string starts with.

    >>> spacecount('  123')
    2
    >>> spacecount('  \n')
    2
    """
    result = 0
    for char in string:
        # we don't use isspace() here because '\n'.isspace() is True
        if char != ' ':
            return result
        result += 1


class EditorText(tk.Text):

    def __init__(self, master, settings, **kwargs):
        self._settings = settings
        super().__init__(master, **kwargs)

        # These will contain callback functions that are ran after the
        # text in the textview is updated. on_other_insert callbacks are
        # ran when text is inserted to the textwidget without running
        # other callbacks, e.g. by pasting.
        self.on_cursor_move = []        # callback(lineno, column)
        self.on_linecount_changed = []  # callback(linecount)
        self.on_other_insert = []       # callback()
        self._cursorpos = (1, 0)
        self._linecount = 1

        def cursor_move(event):
            self.after_idle(self.do_cursor_move)

        self.bind('<Button-1>', cursor_move)
        self.bind('<Key>', cursor_move)
        self.bind('<Control-a>', self._on_ctrl_a)
        self.bind('<BackSpace>', self._on_backspace)
        self.bind('<Return>', self._on_return)
        self.bind('<parenright>', self._on_closing_brace)
        self.bind('<bracketright>', self._on_closing_brace)
        self.bind('<braceright>', self._on_closing_brace)
        self.bind('<Tab>', lambda event: self._on_tab(False))

        if self.tk.call('tk', 'windowingsystem') == 'x11':
            # even though the event keysym says Left, holding down right
            # shift and pressing tab also runs this event... 0_o
            self.bind('<ISO_Left_Tab>', lambda event: self._on_tab(True))
        else:
            self.bind('<Shift-Tab>', lambda event: self._on_tab(True))

        # Undoing or redoing is already taken care of with bind_all in
        # editor.py, but if tkinter has bound Ctrl+Z by default it gets
        # called twice.
        def _do_only_this(binding, callback):
            def real_callback(event):
                callback()
                return 'break'

            self.bind(binding, real_callback)

    def do_cursor_move(self):
        line, column = map(int, self.index('insert').split('.'))
        if self._cursorpos != (line, column):
            self._cursorpos = (line, column)
            for callback in self.on_cursor_move:
                callback(line, column)

    def do_linecount_changed(self):
        linecount = int(self.index('end-1c').split('.')[0])
        if self._linecount != linecount:
            self._linecount = linecount
            for callback in self.on_linecount_changed:
                callback(linecount)

    def do_other_insert(self):
        for callback in self.on_other_insert:
            callback()

    def _on_backspace(self, event):
        if not self.tag_ranges('sel'):
            # nothing is selected, we can do non-default stuff
            lineno = int(self.index('insert').split('.')[0])
            before_cursor = self.get('%d.0' % lineno, 'insert')
            if before_cursor and before_cursor.isspace():
                self.dedent(lineno)
                return 'break'

        self.after_idle(self.do_linecount_changed)
        self.after_idle(self.do_cursor_move)
        return None

    def _on_ctrl_a(self, event):
        """Select all."""
        self.tag_add('sel', '0.0', 'end-1c')
        return 'break'     # don't run _on_key or move cursor

    def _on_delete(self, event):
        nextchar = self.get('insert', 'insert+1c')
        if nextchar:
            # not end of file
            self.after_idle(self.do_linecount_changed)
            self.after_idle(self.do_cursor_move)
{'fraction_non_alphanumeric': 0.10159906396255851, 'fraction_numerical': 0.0055577223088923555, 'mean_word_length': 2.481670061099796, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1424719', 'n_tokens_mistral': 2933, 'n_tokens_neox': 2837, 'n_words': 945}