<commit_before>#!/usr/bin/python
import gtk
import re
from shell_buffer import ShellBuffer, StatementChunk, ResultChunk, CommentChunk, BlankChunk

class ShellView(gtk.TextView):
    __gsignals__ = {
        'backspace' : 'override',
        'expose-event': 'override',
        'key-press-event': 'override',
        'realize': 'override'
   }
        
    def __init__(self, buf):
        buf.connect('chunk-status-changed', self.on_chunk_status_changed)
        buf.connect('add-custom-result', self.on_add_custom_result)
            
        gtk.TextView.__init__(self, buf)
        self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 10)

    def paint_chunk(self, cr, area, chunk, fill_color, outline_color):
        buf = self.get_buffer()
        
        (y, _) = self.get_line_yrange(buf.get_iter_at_line(chunk.start))
        (end_y, end_height) = self.get_line_yrange(buf.get_iter_at_line(chunk.end))
        height = end_y + end_height - y
        
        (_, window_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT, 0, y)
        cr.rectangle(area.x, window_y, area.width, height)
        cr.set_source_rgb(*fill_color)
        cr.fill()
                
        cr.rectangle(0.5, window_y + 0.5, 10 - 1, height - 1)
        cr.set_source_rgb(*outline_color)
        cr.set_line_width(1)
        cr.stroke()

    def do_realize(self):
        gtk.TextView.do_realize(self)

        self.get_window(gtk.TEXT_WINDOW_LEFT).set_background(self.style.white)

    def do_expose_event(self, event):
        if event.window != self.get_window(gtk.TEXT_WINDOW_LEFT):
            return gtk.TextView.do_expose_event(self, event)

        (_, start_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y)
        (start_line, _) = self.get_line_at_y(start_y)
        
        (_, end_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y + event.area.height - 1)
        (end_line, _) = self.get_line_at_y(end_y)

        buf = self.get_buffer()

        cr = event.window.cairo_create()
        
        for chunk in buf.iterate_chunks(start_line.get_line(), end_line.get_line()):
            if isinstance(chunk, StatementChunk):
                if chunk.error_message != None:
                    self.paint_chunk(cr, event.area, chunk, (1, 0, 0), (0.5, 0, 0))
                elif chunk.needs_compile:
                    self.paint_chunk(cr, event.area, chunk, (1, 1, 0), (0.5, 0.5, 0))
                elif chunk.needs_execute:
                    self.paint_chunk(cr, event.area, chunk, (1, 0, 1), (0.5, 0.5, 0))
                else:
                    self.paint_chunk(cr, event.area, chunk, (0, 0, 1), (0, 0, 0.5))
                
        return False

    def __get_line_text(self, iter):
        start = iter.copy()
        start.set_line_index(0)
        end = iter.copy()
        end.forward_to_line_end()
        
        return start.get_slice(end)
    
    # This is likely overengineered, since we're going to try as hard as possible not to
    # have tabs in our worksheets
    def __count_indent(self, text):
        indent = 0
        for c in text:
            if c == ' ':
                indent += 1
            elif c == '\t':
                indent += 8 - (indent % 8)
            else:
                break

        return indent

    def __find_outdent(self, iter):
        buf = self.get_buffer()
        line = iter.get_line()

        current_indent = self.__count_indent(self.__get_line_text(iter))

        previous_line = iter.copy()
        while  line > 0:
            line -= 1
            previous_line.backward_line()
            if not isinstance(buf.get_chunk(line), ResultChunk):
                line_text = self.__get_line_text(previous_line)
                
                indent = self.__count_indent(line_text)
                if indent < current_indent:
                    indent_text = re.match(r"^[\t ]*", line_text).group(0)
                        
                    return indent_text
                
        return ""

    def __find_default_indent(self, iter):
        buf = self.get_buffer()
        line = iter.get_line()

        previous_line = iter.copy()
        while  line > 0:
            line -= 1
            previous_line.backward_line()
            if not isinstance(buf.get_chunk(line), ResultChunk):
                line_text = self.__get_line_text(previous_line)
                
                indent = self.__count_indent(line_text)
                indent_text = re.match(r"^[\t ]*", line_text).group(0)

                if line_text.endswith(":") and not re.match(r"^\s*#", line_text):
                    indent_text += "    "
                        
                return indent_text
                
        return ""

    def __reindent_line(self, iter, indent_text):
        buf = self.get_buffer()

        line_text = self.__get_line_text(iter)
        prefix = re.match(r"^[\t ]*", line_text).group(0)

        diff = self.__count_indent(indent_text) - self.__count_indent(prefix)
        if diff == 0:
            return 0

        common_len = 0
        for a, b in zip(prefix, indent_text):
            if a != b:
                break
            common_len += 1
    
        start = iter.copy()
        start.set_line_offset(common_len)
        end = iter.copy()
        end.set_line_offset(len(prefix))

        # Nitpicky-detail. If the selection starts at the start of the line, and we are
        # inserting white-space there, then the whitespace should be *inside* the selection
        mark_to_start = None
        if common_len == 0 and buf.get_has_selection():
            mark = buf.get_insert()
            if buf.get_iter_at_mark(mark).compare(start) == 0:
                mark_to_start = mark
                
            mark = buf.get_selection_bound()
            if buf.get_iter_at_mark(mark).compare(start) == 0:
                mark_to_start = mark
        
        buf.delete(start, end)
        buf.insert(start, indent_text[common_len:])

        if mark_to_start != None:
            start.set_line_offset(0)
            buf.move_mark(mark_to_start, start)

        return diff

    def __reindent_selection(self, outdent):
        buf = self.get_buffer()

        bounds = buf.get_selection_bounds()
        if bounds == ():
            insert_mark = buf.get_insert()
            bounds = buf.get_iter_at_mark(insert_mark), buf.get_iter_at_mark(insert_mark)
        start, end = bounds

        line = start.get_line()
        end_line = end.get_line()
        if end.starts_line() and end.compare(start) > 0:
            end_line -= 1<commit_msg>Hopeful minor fix for observed traceback; use the iter we revalidate<commit_after>#!/usr/bin/python
import gtk
import re
from shell_buffer import ShellBuffer, StatementChunk, ResultChunk, CommentChunk, BlankChunk

class ShellView(gtk.TextView):
    __gsignals__ = {
        'backspace' : 'override',
        'expose-event': 'override',
        'key-press-event': 'override',
        'realize': 'override'
   }
        
    def __init__(self, buf):
        buf.connect('chunk-status-changed', self.on_chunk_status_changed)
        buf.connect('add-custom-result', self.on_add_custom_result)
            
        gtk.TextView.__init__(self, buf)
        self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 10)

    def paint_chunk(self, cr, area, chunk, fill_color, outline_color):
        buf = self.get_buffer()
        
        (y, _) = self.get_line_yrange(buf.get_iter_at_line(chunk.start))
        (end_y, end_height) = self.get_line_yrange(buf.get_iter_at_line(chunk.end))
        height = end_y + end_height - y
        
        (_, window_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT, 0, y)
        cr.rectangle(area.x, window_y, area.width, height)
        cr.set_source_rgb(*fill_color)
        cr.fill()
                
        cr.rectangle(0.5, window_y + 0.5, 10 - 1, height - 1)
        cr.set_source_rgb(*outline_color)
        cr.set_line_width(1)
        cr.stroke()

    def do_realize(self):
        gtk.TextView.do_realize(self)

        self.get_window(gtk.TEXT_WINDOW_LEFT).set_background(self.style.white)

    def do_expose_event(self, event):
        if event.window != self.get_window(gtk.TEXT_WINDOW_LEFT):
            return gtk.TextView.do_expose_event(self, event)

        (_, start_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y)
        (start_line, _) = self.get_line_at_y(start_y)
        
        (_, end_y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, event.area.y + event.area.height - 1)
        (end_line, _) = self.get_line_at_y(end_y)

        buf = self.get_buffer()

        cr = event.window.cairo_create()
        
        for chunk in buf.iterate_chunks(start_line.get_line(), end_line.get_line()):
            if isinstance(chunk, StatementChunk):
                if chunk.error_message != None:
                    self.paint_chunk(cr, event.area, chunk, (1, 0, 0), (0.5, 0, 0))
                elif chunk.needs_compile:
                    self.paint_chunk(cr, event.area, chunk, (1, 1, 0), (0.5, 0.5, 0))
                elif chunk.needs_execute:
                    self.paint_chunk(cr, event.area, chunk, (1, 0, 1), (0.5, 0.5, 0))
                else:
                    self.paint_chunk(cr, event.area, chunk, (0, 0, 1), (0, 0, 0.5))
                
        return False

    def __get_line_text(self, iter):
        start = iter.copy()
        start.set_line_index(0)
        end = iter.copy()
        end.forward_to_line_end()
        
        return start.get_slice(end)
    
    # This is likely overengineered, since we're going to try as hard as possible not to
    # have tabs in our worksheets
    def __count_indent(self, text):
        indent = 0
        for c in text:
            if c == ' ':
                indent += 1
            elif c == '\t':
                indent += 8 - (indent % 8)
            else:
                break

        return indent

    def __find_outdent(self, iter):
        buf = self.get_buffer()
        line = iter.get_line()

        current_indent = self.__count_indent(self.__get_line_text(iter))

        previous_line = iter.copy()
        while  line > 0:
            line -= 1
            previous_line.backward_line()
            if not isinstance(buf.get_chunk(line), ResultChunk):
                line_text = self.__get_line_text(previous_line)
                
                indent = self.__count_indent(line_text)
                if indent < current_indent:
                    indent_text = re.match(r"^[\t ]*", line_text).group(0)
                        
                    return indent_text
                
        return ""

    def __find_default_indent(self, iter):
        buf = self.get_buffer()
        line = iter.get_line()

        previous_line = iter.copy()
        while  line > 0:
            line -= 1
            previous_line.backward_line()
            if not isinstance(buf.get_chunk(line), ResultChunk):
                line_text = self.__get_line_text(previous_line)
                
                indent = self.__count_indent(line_text)
                indent_text = re.match(r"^[\t ]*", line_text).group(0)

                if line_text.endswith(":") and not re.match(r"^\s*#", line_text):
                    indent_text += "    "
                        
                return indent_text
                
        return ""

    def __reindent_line(self, iter, indent_text):
        buf = self.get_buffer()

        line_text = self.__get_line_text(iter)
        prefix = re.match(r"^[\t ]*", line_text).group(0)

        diff = self.__count_indent(indent_text) - self.__count_indent(prefix)
        if diff == 0:
            return 0

        common_len = 0
        for a, b in zip(prefix, indent_text):
            if a != b:
                break
            common_len += 1
    
        start = iter.copy()
        start.set_line_offset(common_len)
        end = iter.copy()
        end.set_line_offset(len(prefix))

        # Nitpicky-detail. If the selection starts at the start of the line, and we are
        # inserting white-space there, then the whitespace should be *inside* the selection
        mark_to_start = None
        if common_len == 0 and buf.get_has_selection():
            mark = buf.get_insert()
            if buf.get_iter_at_mark(mark).compare(start) == 0:
                mark_to_start = mark
                
            mark = buf.get_selection_bound()
            if buf.get_iter_at_mark(mark).compare(start) == 0:
                mark_to_start = mark
        
        buf.delete(start, end)
        buf.insert(end, indent_text[common_len:])

        if mark_to_start != None:
            start.set_line_offset(0)
            buf.move_mark(mark_to_start, start)

        return diff

    def __reindent_selection(self, outdent):
        buf = self.get_buffer()

        bounds = buf.get_selection_bounds()
        if bounds == ():
            insert_mark = buf.get_insert()
            bounds = buf.get_iter_at_mark(insert_mark), buf.get_iter_at_mark(insert_mark)
        start, end = bounds

        line = start.get_line()
        end_line = end.get_line()
        if end.starts_line() and end.compare(start) > 0:
            end_line -= 1
{'fraction_non_alphanumeric': 0.10690876214872297, 'fraction_numerical': 0.01009568296541852, 'mean_word_length': 1.8515574650912996, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 9, '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': '4911022', 'n_tokens_mistral': 4326, 'n_tokens_neox': 4088, 'n_words': 825}