<commit_before>import curses
import settings as settings_module
import os
import re
from __init__ import VERSION


class Editor:
    def __init__(self, start_note=None, debug=False):
        import settings
        self.settings = settings
        
        self.start_app()
        
        self.cursor = (0,0)
        self.pad_position = (0,0)
        self.status = ''
        self.last_search = ''
        self.altered = False
        self.history = [None]
        self.history_position = 0
        self.links = []
        self.cutting = False
        self.cuts = []
        
        self.load_note(start_note)
        self.current_note = start_note if start_note else 'untitled'
        
        if not start_note and debug:
            self.buffer = ['This is a test note.', '', 
                           'You can link to other notes like this: [[school:homework]].', 
                           'Put the cursor over a link and press ENTER to follow it.',
                           'Surround text in asterisks to *bold it*; underscores _underline text_.',
                           '',
                           'Make *bulleted lists* with indentation and asterisks, like this:',
                           '* one', '    * two', '        * three', '            * four',
                           ]


    def start_app(self):
        # start the application
        self.screen = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.meta(1)
        self.screen.keypad(1)

    def end_app(self):
        # end the application
        curses.nocbreak()
        self.screen.keypad(0)
        curses.echo()
        curses.endwin()
        
    def draw_screen(self):
        settings = self.settings
        
        self.screen.refresh()
        
        screen_size = self.screen.getmaxyx()
        self.height, self.width = screen_size
        height, width = self.height, self.width
        cy, cx = self.cursor
        py, px = self.pad_position

        self.buffer_pad = curses.newwin(height-4, width, 1, 0)
        self.title_win = curses.newwin(1, width, 0, 0)
        self.shortcut_win = curses.newwin(3, width, height-3, 0)

        columns = int((len(settings.shortcuts)+.5)/2)
        column_width = width / (columns + 1)
        for n, shortcut in enumerate(settings.shortcuts):
            x = (n/2) * column_width
            y = 1 if not n%2 else 2
            self.shortcut_win.addstr(y, x, '^' + shortcut[0], curses.A_REVERSE)
            self.shortcut_win.addstr(' ' + shortcut[1])
        
        if self.status: status_text = self.status
        else: status_text = ('"%s"  line %s/%s, col %s/%s, chars %s' % 
                             (self.current_note, cy+1, len(self.buffer) + 1, 
                              cx+1, len(self.buffer[cy]) if cy < len(self.buffer) else 0,
                              sum([len(buffer) for buffer in self.buffer]))
                            )
        
        total_gap = width - len(status_text) - 1
        left_gap = total_gap/2
        right_gap = total_gap-left_gap
        self.shortcut_win.addstr(0, 0, ' '*(left_gap) + status_text + ' '*(right_gap), curses.A_REVERSE)
        self.status = ''
            
        self.shortcut_win.noutrefresh()

        note_name = self.current_note if self.current_note else 'untitled'
        title_text = '  nanote %s' % (VERSION)
        note_name_text = str(note_name) + ('*' if self.altered else '')
        total_gap = width - len(note_name_text) - 1
        left_gap = total_gap/2 - len(title_text)
        right_gap = total_gap-left_gap-len(title_text)
        self.title_win.addstr(title_text + ' '*left_gap + note_name_text + ' '*right_gap, curses.A_REVERSE)
        self.title_win.noutrefresh()

        onscreen_range = range(py, 
                               min(py + height-3 + 1, len(self.buffer) - 1)+1)

        for n, i in enumerate(onscreen_range):
            try:
                start_x = None if cx < width-1 else cx-width+1 
                end_x = max(cx, width-1)
                self.buffer_pad.addstr(n, 0, self.buffer[i][start_x:end_x])
            except: pass
        self.links = []

        args = settings.args

        def draw_trimmed_text(n, pos, text, style):
            start_x = None if cx < width-1 else cx-width+1 
            end_x = max(cx, width-1)
            
            try:
                for i, char in enumerate(text):
                    if start_x <= pos+i < end_x:
                        self.buffer_pad.addstr(n, pos+i, char, style)
            except: pass
            
        for n, i in enumerate(onscreen_range):
            for m in re.compile(args['link_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos, text, curses.A_REVERSE)
                if i == cy: self.links.append((pos, text))
            for m in re.compile(args['bold_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos+1, text[1:-1], curses.A_BOLD)
            for m in re.compile(args['underline_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos+1, text[1:-1], curses.A_UNDERLINE)
            for m in re.compile(args['bullet_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                stripped_text = text.lstrip(' ')
                indentation = len(text)-len(stripped_text)
                indent_level = (indentation / settings.args['tab_width']) % len(args['bullet_symbols'])
                draw_trimmed_text(n, pos+indentation, args['bullet_symbols'][indent_level], curses.A_BOLD)
            for m in re.compile(args['comment_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos, text, curses.A_DIM)

        self.buffer_pad.noutrefresh()

        self.screen.move(cy+1-py, min(cx, width-1))
        self.screen.noutrefresh()
        
        curses.doupdate()


    def find_next(self, find):
        # search for next instance of a regex
        self.last_search = find
        cy, cx = self.cursor
        find_re = re.compile(find)
        current_pos = sum([len(self.buffer[y]) if y < cy else 0 for y in range(len(self.buffer))]) + cy + cx
        first_result = None

        def answer(pos):
            for i, line in enumerate(self.buffer):
                this_len = len(line)+1
                if pos >= this_len:
                    pos -= this_len
                else:<commit_msg>Draw links last, so that other formatting doesn't get in the way.<commit_after>import curses
import settings as settings_module
import os
import re
from __init__ import VERSION


class Editor:
    def __init__(self, start_note=None, debug=False):
        import settings
        self.settings = settings
        
        self.start_app()
        
        self.cursor = (0,0)
        self.pad_position = (0,0)
        self.status = ''
        self.last_search = ''
        self.altered = False
        self.history = [None]
        self.history_position = 0
        self.links = []
        self.cutting = False
        self.cuts = []
        
        self.load_note(start_note)
        self.current_note = start_note if start_note else 'untitled'
        
        if not start_note and debug:
            self.buffer = ['This is a test note.', '', 
                           'You can link to other notes like this: [[school:homework]].', 
                           'Put the cursor over a link and press ENTER to follow it.',
                           'Surround text in asterisks to *bold it*; underscores _underline text_.',
                           '',
                           'Make *bulleted lists* with indentation and asterisks, like this:',
                           '* one', '    * two', '        * three', '            * four',
                           ]


    def start_app(self):
        # start the application
        self.screen = curses.initscr()
        curses.noecho()
        curses.cbreak()
        curses.meta(1)
        self.screen.keypad(1)

    def end_app(self):
        # end the application
        curses.nocbreak()
        self.screen.keypad(0)
        curses.echo()
        curses.endwin()
        
    def draw_screen(self):
        settings = self.settings
        
        self.screen.refresh()
        
        screen_size = self.screen.getmaxyx()
        self.height, self.width = screen_size
        height, width = self.height, self.width
        cy, cx = self.cursor
        py, px = self.pad_position

        self.buffer_pad = curses.newwin(height-4, width, 1, 0)
        self.title_win = curses.newwin(1, width, 0, 0)
        self.shortcut_win = curses.newwin(3, width, height-3, 0)

        columns = int((len(settings.shortcuts)+.5)/2)
        column_width = width / (columns + 1)
        for n, shortcut in enumerate(settings.shortcuts):
            x = (n/2) * column_width
            y = 1 if not n%2 else 2
            self.shortcut_win.addstr(y, x, '^' + shortcut[0], curses.A_REVERSE)
            self.shortcut_win.addstr(' ' + shortcut[1])
        
        if self.status: status_text = self.status
        else: status_text = ('"%s"  line %s/%s, col %s/%s, chars %s' % 
                             (self.current_note, cy+1, len(self.buffer) + 1, 
                              cx+1, len(self.buffer[cy]) if cy < len(self.buffer) else 0,
                              sum([len(buffer) for buffer in self.buffer]))
                            )
        
        total_gap = width - len(status_text) - 1
        left_gap = total_gap/2
        right_gap = total_gap-left_gap
        self.shortcut_win.addstr(0, 0, ' '*(left_gap) + status_text + ' '*(right_gap), curses.A_REVERSE)
        self.status = ''
            
        self.shortcut_win.noutrefresh()

        note_name = self.current_note if self.current_note else 'untitled'
        title_text = '  nanote %s' % (VERSION)
        note_name_text = str(note_name) + ('*' if self.altered else '')
        total_gap = width - len(note_name_text) - 1
        left_gap = total_gap/2 - len(title_text)
        right_gap = total_gap-left_gap-len(title_text)
        self.title_win.addstr(title_text + ' '*left_gap + note_name_text + ' '*right_gap, curses.A_REVERSE)
        self.title_win.noutrefresh()

        onscreen_range = range(py, 
                               min(py + height-3 + 1, len(self.buffer) - 1)+1)

        for n, i in enumerate(onscreen_range):
            try:
                start_x = None if cx < width-1 else cx-width+1 
                end_x = max(cx, width-1)
                self.buffer_pad.addstr(n, 0, self.buffer[i][start_x:end_x])
            except: pass
        self.links = []

        args = settings.args

        def draw_trimmed_text(n, pos, text, style):
            start_x = None if cx < width-1 else cx-width+1 
            end_x = max(cx, width-1)
            
            try:
                for i, char in enumerate(text):
                    if start_x <= pos+i < end_x:
                        self.buffer_pad.addstr(n, pos+i, char, style)
            except: pass
            
        for n, i in enumerate(onscreen_range):
            for m in re.compile(args['bold_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos+1, text[1:-1], curses.A_BOLD)
            for m in re.compile(args['underline_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos+1, text[1:-1], curses.A_UNDERLINE)
            for m in re.compile(args['bullet_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                stripped_text = text.lstrip(' ')
                indentation = len(text)-len(stripped_text)
                indent_level = (indentation / settings.args['tab_width']) % len(args['bullet_symbols'])
                draw_trimmed_text(n, pos+indentation, args['bullet_symbols'][indent_level], curses.A_BOLD)
            for m in re.compile(args['comment_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos, text, curses.A_DIM)
            for m in re.compile(args['link_re']).finditer(self.buffer[i]):
                pos = m.start(); text = m.group()
                draw_trimmed_text(n, pos, text, curses.A_REVERSE)
                if i == cy: self.links.append((pos, text))

        self.buffer_pad.noutrefresh()

        self.screen.move(cy+1-py, min(cx, width-1))
        self.screen.noutrefresh()
        
        curses.doupdate()


    def find_next(self, find):
        # search for next instance of a regex
        self.last_search = find
        cy, cx = self.cursor
        find_re = re.compile(find)
        current_pos = sum([len(self.buffer[y]) if y < cy else 0 for y in range(len(self.buffer))]) + cy + cx
        first_result = None

        def answer(pos):
            for i, line in enumerate(self.buffer):
                this_len = len(line)+1
                if pos >= this_len:
                    pos -= this_len
                else:
{'fraction_non_alphanumeric': 0.11671948091142297, 'fraction_numerical': 0.008601177003168854, 'mean_word_length': 1.6794016575702446, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 5, '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': '1611490', 'n_tokens_mistral': 4228, 'n_tokens_neox': 4033, 'n_words': 916}