maint: format code using black
[git-cola.git] / cola / widgets / spellcheck.py
blob2fdbda15209f76e18f4b6487ca0a69d7e0b5716d
1 #!/usr/bin/env python
2 from __future__ import division, absolute_import, unicode_literals
3 import re
5 from qtpy.QtCore import Qt
6 from qtpy.QtCore import QEvent
7 from qtpy.QtCore import Signal
8 from qtpy.QtGui import QMouseEvent
9 from qtpy.QtGui import QSyntaxHighlighter
10 from qtpy.QtGui import QTextCharFormat
11 from qtpy.QtGui import QTextCursor
12 from qtpy.QtWidgets import QAction
14 from .. import qtutils
15 from .. import spellcheck
16 from ..i18n import N_
17 from .text import HintedTextEdit
20 # pylint: disable=too-many-ancestors
21 class SpellCheckTextEdit(HintedTextEdit):
22 def __init__(self, context, hint, parent=None):
23 HintedTextEdit.__init__(self, context, hint, parent)
25 # Default dictionary based on the current locale.
26 self.spellcheck = spellcheck.NorvigSpellCheck()
27 self.highlighter = Highlighter(self.document(), self.spellcheck)
29 def set_dictionary(self, dictionary):
30 self.spellcheck.set_dictionary(dictionary)
32 def mousePressEvent(self, event):
33 if event.button() == Qt.RightButton:
34 # Rewrite the mouse event to a left button event so the cursor is
35 # moved to the location of the pointer.
36 event = QMouseEvent(
37 QEvent.MouseButtonPress,
38 event.pos(),
39 Qt.LeftButton,
40 Qt.LeftButton,
41 Qt.NoModifier,
43 HintedTextEdit.mousePressEvent(self, event)
45 def context_menu(self):
46 popup_menu = HintedTextEdit.createStandardContextMenu(self)
48 # Select the word under the cursor.
49 cursor = self.textCursor()
50 cursor.select(QTextCursor.WordUnderCursor)
51 self.setTextCursor(cursor)
53 # Check if the selected word is misspelled and offer spelling
54 # suggestions if it is.
55 spell_menu = None
56 if self.textCursor().hasSelection():
57 text = self.textCursor().selectedText()
58 if not self.spellcheck.check(text):
59 title = N_('Spelling Suggestions')
60 spell_menu = qtutils.create_menu(title, self)
61 for word in self.spellcheck.suggest(text):
62 action = SpellAction(word, spell_menu)
63 action.result.connect(self.correct)
64 spell_menu.addAction(action)
65 # Only add the spelling suggests to the menu if there are
66 # suggestions.
67 if spell_menu.actions():
68 popup_menu.addSeparator()
69 popup_menu.addMenu(spell_menu)
71 return popup_menu, spell_menu
73 def contextMenuEvent(self, event):
74 popup_menu, _ = self.context_menu()
75 popup_menu.exec_(self.mapToGlobal(event.pos()))
77 def correct(self, word):
78 """Replaces the selected text with word."""
79 cursor = self.textCursor()
80 cursor.beginEditBlock()
82 cursor.removeSelectedText()
83 cursor.insertText(word)
85 cursor.endEditBlock()
88 class Highlighter(QSyntaxHighlighter):
90 WORDS = r"(?iu)[\w']+"
92 def __init__(self, doc, spellcheck_widget):
93 QSyntaxHighlighter.__init__(self, doc)
94 self.spellcheck = spellcheck_widget
95 self.enabled = False
97 def enable(self, enabled):
98 self.enabled = enabled
99 self.rehighlight()
101 def highlightBlock(self, text):
102 if not self.enabled:
103 return
104 fmt = QTextCharFormat()
105 fmt.setUnderlineColor(Qt.red)
106 fmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
108 for word_object in re.finditer(self.WORDS, text):
109 if not self.spellcheck.check(word_object.group()):
110 self.setFormat(
111 word_object.start(), word_object.end() - word_object.start(), fmt
115 class SpellAction(QAction):
116 """QAction that returns the text in a signal."""
118 result = Signal(object)
120 def __init__(self, *args):
121 QAction.__init__(self, *args)
122 # pylint: disable=no-member
123 self.triggered.connect(self.correct)
125 def correct(self):
126 self.result.emit(self.text())