rope-qickfix / ropeide / spelldialog.py

import threading
import Tkinter

from ropeide.spellchecker import SpellChecker


class _SpellCheckingDialog(object):

    def __init__(self, context, start, end):
        self.editor = context.editor
        self.offset = start
        self.text = self.editor.get_text()[start:end]
        self.checker = SpellChecker(self.text)
        self.typos = self.checker.check()
        self.changed_offset = start
        self.semaphore = threading.Semaphore(0)

    def do(self):
        try:
            while True:
                typo = self.typos.next()
                typo_dialog = _TypoDialog(typo, self)
                typo_dialog()
        except StopIteration:
            pass

    def quit(self):
        self.checker.quit()


class _TypoDialog(object):

    def __init__(self, typo, checking):
        self.typo = typo
        self.checking = checking
        self.editor = checking.editor
        self.toplevel = None

    def __call__(self):
        self._highlight_in_editor()
        toplevel = Tkinter.Toplevel()
        self.toplevel = toplevel
        toplevel.title('Spell Checker <%s>' % self.typo.original)
        suggestions = ''
        for index, name in enumerate(self.typo.suggestions[:10]):
            suggestions += '%d : %s\n' % (index, name)
            toplevel.bind('<KeyPress-%d>' % index,
                          lambda event, name=name: self._use(name))
        actions = 'SPC : Leave unchanged\n'
        actions += 'r : Replace word\n'
        actions += 'a : Accept for this session\n'
        actions += 'i : Insert into private dictionary\n'
        actions += 'l : Insert lower cased version into private dictionary\n'
        actions += 'q : quit session\n'
        toplevel.bind('<space>', self._skip)
        toplevel.bind('r', self._replace)
        toplevel.bind('a', self._add_session)
        toplevel.bind('i', self._add_dictionary)
        toplevel.bind('l', self._add_lower_to_dictionary)
        toplevel.bind('q', self._quit_session)
        toplevel.bind('<Control-g>', self._quit_session)
        toplevel.bind('<Escape>', self._quit_session)
        label0 = Tkinter.Label(toplevel, text='Word <%s>' %
                               self.typo.original, justify=Tkinter.LEFT)
        label1 = Tkinter.Label(toplevel, text=suggestions, justify=Tkinter.LEFT)
        label2 = Tkinter.Label(toplevel, text=actions, justify=Tkinter.LEFT)
        label0.grid(row=0, columnspan=2)
        label1.grid(row=1, column=0)
        label2.grid(row=1, column=1)
        # For blocking the main thread
        toplevel.focus_set()
        toplevel.grab_set()
        toplevel.mainloop()

    def _highlight_in_editor(self):
        start, end = self._get_word_index()
        self.editor.select_range(start, end)

    def _get_word_index(self):
        length = len(self.typo.original)
        start = self.editor.get_index(self.typo.offset + self.checking.changed_offset)
        end = self.editor.get_relative(start, length)
        return start, end

    def _add_session(self, event=None):
        self.checking.checker.accept_word(self.typo.original)
        self._skip()

    def _add_dictionary(self, event=None):
        self.checking.checker.insert_dictionary(self.typo.original)
        self._skip()

    def _add_lower_to_dictionary(self, event=None):
        self.checking.checker.insert_dictionary(self.typo.original.lower())
        self._skip()

    def _replace(self, event=None):
        toplevel = Tkinter.Toplevel()
        toplevel.title('Replace <%s>' % self.typo.original)
        label = Tkinter.Label(toplevel, text='Replace <%s> With :' %
                              self.typo.original)
        entry = Tkinter.Entry(toplevel)
        entry.insert(0, self.typo.original)
        entry.select_range(0, Tkinter.END)
        def cancel(event=None):
            toplevel.destroy()
        def done(event=None):
            replacement = entry.get()
            toplevel.destroy()
            self._use(replacement)
        entry.bind('<Control-g>', cancel)
        entry.bind('<Escape>', cancel)
        entry.bind('<Return>', done)
        label.grid(row=0, column=0)
        entry.grid(row=0, column=1)
        entry.focus_set()
        entry.grab_set()

    def _use(self, name):
        start, end = self._get_word_index()
        length = len(self.typo.original)
        new_length = len(name)
        self.editor.delete(start, end)
        self.editor.insert(start, name)
        self.checking.changed_offset += new_length - length
        self._skip()

    def _skip(self, event=None):
        self.toplevel.destroy()
        self.toplevel.quit()

    def _quit_session(self, event=None):
        self.checking.quit()
        self._skip()


def check_word(context):
    text = context.editor.get_text()
    offset = context.editor.get_current_offset()
    end = min(len(text) - 1, offset)
    start = max(0, offset - 1)
    for index in range(start, -1, -1):
        c = text[index]
        if c.isalnum():
            start = index
        else:
            break
    for index in range(start, len(text)):
        c = text[index]
        if c.isalnum():
            end = index + 1
        else:
            break
    _SpellCheckingDialog(context, start, end).do()    

def check_region(context):
    start, end = context.editor.get_region_offset()
    _SpellCheckingDialog(context, start, end).do()

def check_buffer(context):
    end = len(context.editor.get_text())
    _SpellCheckingDialog(context, 0, end).do()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.