|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2019 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a typing completer for TOML. |
|
8 """ |
|
9 |
|
10 import re |
|
11 |
|
12 from PyQt6.Qsci import QsciScintilla |
|
13 |
|
14 from eric7 import Preferences |
|
15 |
|
16 from .CompleterBase import CompleterBase |
|
17 |
|
18 |
|
19 class CompleterYaml(CompleterBase): |
|
20 """ |
|
21 Class implementing typing completer for TOML. |
|
22 """ |
|
23 |
|
24 def __init__(self, editor, parent=None): |
|
25 """ |
|
26 Constructor |
|
27 |
|
28 @param editor reference to the editor object |
|
29 @type QScintilla.Editor |
|
30 @param parent reference to the parent object |
|
31 @type QObject |
|
32 """ |
|
33 super().__init__(editor, parent) |
|
34 |
|
35 self.__autoIndentationRe = re.compile(r"(?:\(|\[|{)(\s*)\r?\n") |
|
36 self.__trailingBlankRe = re.compile(r"(?:[=:,])(\s*)\r?\n") |
|
37 |
|
38 self.readSettings() |
|
39 |
|
40 def readSettings(self): |
|
41 """ |
|
42 Public slot called to reread the configuration parameters. |
|
43 """ |
|
44 self.setEnabled(Preferences.getEditorTyping("Toml/EnabledTypingAids")) |
|
45 self.__insertClosingBrace = Preferences.getEditorTyping( |
|
46 "Toml/InsertClosingBrace" |
|
47 ) |
|
48 self.__skipBrace = Preferences.getEditorTyping("Toml/SkipBrace") |
|
49 self.__insertQuote = Preferences.getEditorTyping("Toml/InsertQuote") |
|
50 self.__autoIndentation = Preferences.getEditorTyping("Toml/AutoIndentation") |
|
51 self.__colonDetection = Preferences.getEditorTyping("Toml/ColonDetection") |
|
52 self.__insertBlankEqual = Preferences.getEditorTyping("Toml/InsertBlankEqual") |
|
53 self.__insertBlankColon = Preferences.getEditorTyping("Toml/InsertBlankColon") |
|
54 self.__insertBlankComma = Preferences.getEditorTyping("Toml/InsertBlankComma") |
|
55 |
|
56 def charAdded(self, charNumber): |
|
57 """ |
|
58 Public slot called to handle the user entering a character. |
|
59 |
|
60 @param charNumber value of the character entered |
|
61 @type int |
|
62 """ |
|
63 char = chr(charNumber) |
|
64 if char not in ["{", "}", "[", "]", "(", ")", "'", '"', "=", ":", ",", "\n"]: |
|
65 return # take the short route |
|
66 |
|
67 line, col = self.editor.getCursorPosition() |
|
68 |
|
69 if self.__inComment(line, col): |
|
70 return |
|
71 |
|
72 # open parenthesis |
|
73 # insert closing parenthesis |
|
74 if char == "(" and self.__insertClosingBrace: |
|
75 self.editor.insert(")") |
|
76 |
|
77 # open curly bracket |
|
78 # insert closing bracket |
|
79 if char == "{" and self.__insertClosingBrace: |
|
80 self.editor.insert("}") |
|
81 |
|
82 # open bracket |
|
83 # insert closing bracket |
|
84 elif char == "[" and self.__insertClosingBrace: |
|
85 self.editor.insert("]") |
|
86 |
|
87 # closing parenthesis |
|
88 # skip matching closing parenthesis |
|
89 elif char in [")", "}", "]"]: |
|
90 txt = self.editor.text(line) |
|
91 if col < len(txt) and char == txt[col] and self.__skipBrace: |
|
92 self.editor.setSelection(line, col, line, col + 1) |
|
93 self.editor.removeSelectedText() |
|
94 |
|
95 # colon |
|
96 # 1. skip colon if not last character |
|
97 # 2. insert blank if last character |
|
98 elif char == ":": |
|
99 text = self.editor.text(line) |
|
100 if col < len(text) and char == text[col]: |
|
101 if self.__colonDetection: |
|
102 self.editor.setSelection(line, col, line, col + 1) |
|
103 self.editor.removeSelectedText() |
|
104 elif self.__insertBlankColon and col == len(text.rstrip()): |
|
105 self.editor.insert(" ") |
|
106 self.editor.setCursorPosition(line, col + 1) |
|
107 |
|
108 # equal sign or comma |
|
109 # insert blank |
|
110 elif (char == "=" and self.__insertBlankEqual) or ( |
|
111 char == "," and self.__insertBlankComma |
|
112 ): |
|
113 self.editor.insert(" ") |
|
114 self.editor.setCursorPosition(line, col + 1) |
|
115 |
|
116 # double quote |
|
117 # insert double quote |
|
118 elif char == '"' and self.__insertQuote: |
|
119 self.editor.insert('"') |
|
120 |
|
121 # single quote |
|
122 # insert single quote |
|
123 elif char == "'" and self.__insertQuote: |
|
124 self.editor.insert("'") |
|
125 |
|
126 # new line |
|
127 elif char == "\n": |
|
128 txt = self.editor.text(line - 1) |
|
129 if self.__autoIndentation and self.__autoIndentationRe.search(txt): |
|
130 # indent after line ending with auto indentation character |
|
131 match = self.__autoIndentationRe.search(txt) |
|
132 if match is not None: |
|
133 startBlanks = match.start(1) |
|
134 endBlanks = match.end(1) |
|
135 if startBlanks != -1 and startBlanks != endBlanks: |
|
136 # previous line ends with whitespace, e.g. caused by |
|
137 # blank insertion above |
|
138 self.editor.setSelection( |
|
139 line - 1, startBlanks, line - 1, endBlanks |
|
140 ) |
|
141 self.editor.removeSelectedText() |
|
142 |
|
143 self.editor.indent(line) |
|
144 self.editor.setCursorPosition(line, 0) |
|
145 self.editor.editorCommand(QsciScintilla.SCI_VCHOME) |
|
146 |
|
147 elif ( |
|
148 self.__insertBlankColon |
|
149 or self.__insertBlankComma |
|
150 or self.__insertBlankEqual |
|
151 ) and self.__trailingBlankRe.search(txt): |
|
152 # remove blank at end of line inserted by blank insertion above |
|
153 match = self.__trailingBlankRe.search(txt) |
|
154 if match is not None: |
|
155 startBlanks = match.start(1) |
|
156 endBlanks = match.end(1) |
|
157 if startBlanks != -1 and startBlanks != endBlanks: |
|
158 self.editor.setSelection( |
|
159 line - 1, startBlanks, line - 1, endBlanks |
|
160 ) |
|
161 self.editor.removeSelectedText() |
|
162 |
|
163 self.editor.setCursorPosition(line, 0) |
|
164 self.editor.editorCommand(QsciScintilla.SCI_VCHOME) |
|
165 |
|
166 def __inComment(self, line, col): |
|
167 """ |
|
168 Private method to check, if the cursor is inside a comment. |
|
169 |
|
170 @param line current line |
|
171 @type int |
|
172 @param col current position within line |
|
173 @type int |
|
174 @return flag indicating, if the cursor is inside a comment |
|
175 @rtype bool |
|
176 """ |
|
177 txt = self.editor.text(line) |
|
178 if col == len(txt): |
|
179 col -= 1 |
|
180 while col >= 0: |
|
181 if txt[col] == "#": |
|
182 return True |
|
183 col -= 1 |
|
184 return False |
|
185 |
|
186 |
|
187 def createCompleter(editor, parent=None): |
|
188 """ |
|
189 Function to instantiate a typing completer object. |
|
190 |
|
191 @param editor reference to the editor object |
|
192 @type QScintilla.Editor |
|
193 @param parent reference to the parent object (defaults to None) |
|
194 @type QObject (optional) |
|
195 @return reference to the instantiated typing completer object |
|
196 @rtype CompleterYaml |
|
197 """ |
|
198 return CompleterYaml(editor, parent=parent) |