1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2003 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the lexer mixin class.
8"""
9
10import Preferences
11
12
13class Lexer:
14    """
15    Class to implement the lexer mixin class.
16    """
17    def __init__(self):
18        """
19        Constructor
20        """
21        self.commentString = ''
22        self.streamCommentString = {
23            'start': '',
24            'end': ''
25        }
26        self.boxCommentString = {
27            'start': '',
28            'middle': '',
29            'end': ''
30        }
31
32        # last indented line wrapper
33        self.lastIndented = -1
34        self.lastIndentedIndex = -1
35
36        # always keep tabs (for languages where tabs are esential
37        self._alwaysKeepTabs = False
38
39        # descriptions for keyword sets
40        self.keywordSetDescriptions = []
41
42    def initProperties(self):
43        """
44        Public slot to initialize the properties.
45        """
46        # default implementation is a do nothing
47        return
48
49    def commentStr(self):
50        """
51        Public method to return the comment string.
52
53        @return comment string (string)
54        """
55        return self.commentString
56
57    def canBlockComment(self):
58        """
59        Public method to determine, whether the lexer language supports a
60        block comment.
61
62        @return flag (boolean)
63        """
64        return self.commentString != ""
65
66    def streamCommentStr(self):
67        """
68        Public method to return the stream comment strings.
69
70        @return stream comment strings (dictionary with two strings)
71        """
72        return self.streamCommentString
73
74    def canStreamComment(self):
75        """
76        Public method to determine, whether the lexer language supports a
77        stream comment.
78
79        @return flag (boolean)
80        """
81        return (
82            self.streamCommentString['start'] != "" and
83            self.streamCommentString['end'] != ""
84        )
85
86    def boxCommentStr(self):
87        """
88        Public method to return the box comment strings.
89
90        @return box comment strings (dictionary with three QStrings)
91        """
92        return self.boxCommentString
93
94    def canBoxComment(self):
95        """
96        Public method to determine, whether the lexer language supports a
97        box comment.
98
99        @return flag (boolean)
100        """
101        return (
102            (self.boxCommentString['start'] != "") and
103            (self.boxCommentString['middle'] != "") and
104            (self.boxCommentString['end'] != "")
105        )
106
107    def alwaysKeepTabs(self):
108        """
109        Public method to check, if tab conversion is allowed.
110
111        @return flag indicating to keep tabs (boolean)
112        """
113        return self._alwaysKeepTabs
114
115    def hasSmartIndent(self):
116        """
117        Public method indicating whether lexer can do smart indentation.
118
119        @return flag indicating availability of smartIndentLine and
120            smartIndentSelection methods (boolean)
121        """
122        return hasattr(self, 'getIndentationDifference')
123
124    def smartIndentLine(self, editor):
125        """
126        Public method to handle smart indentation for a line.
127
128        @param editor reference to the QScintilla editor object
129        """
130        cline, cindex = editor.getCursorPosition()
131
132        # get leading spaces
133        lead_spaces = editor.indentation(cline)
134
135        # get the indentation difference
136        indentDifference = self.getIndentationDifference(cline, editor)
137
138        if indentDifference != 0:
139            editor.setIndentation(cline, lead_spaces + indentDifference)
140            editor.setCursorPosition(cline, cindex + indentDifference)
141
142        self.lastIndented = cline
143
144    def smartIndentSelection(self, editor):
145        """
146        Public method to handle smart indentation for a selection of lines.
147
148        Note: The assumption is, that the first line determines the new
149              indentation level.
150
151        @param editor reference to the QScintilla editor object
152        """
153        if not editor.hasSelectedText():
154            return
155
156        # get the selection
157        lineFrom, indexFrom, lineTo, indexTo = editor.getSelection()
158        if lineFrom != self.lastIndented:
159            self.lastIndentedIndex = indexFrom
160
161        endLine = lineTo if indexTo else lineTo - 1
162
163        # get the indentation difference
164        indentDifference = self.getIndentationDifference(lineFrom, editor)
165
166        editor.beginUndoAction()
167        # iterate over the lines
168        for line in range(lineFrom, endLine + 1):
169            editor.setIndentation(
170                line, editor.indentation(line) + indentDifference)
171        editor.endUndoAction()
172
173        indexStart = (
174            indexFrom + indentDifference
175            if self.lastIndentedIndex != 0 else
176            0
177        )
178        if indexStart < 0:
179            indexStart = 0
180        indexEnd = indexTo != 0 and (indexTo + indentDifference) or 0
181        if indexEnd < 0:
182            indexEnd = 0
183        editor.setSelection(lineFrom, indexStart, lineTo, indexEnd)
184
185        self.lastIndented = lineFrom
186
187    def autoCompletionWordSeparators(self):
188        """
189        Public method to return the list of separators for autocompletion.
190
191        @return list of separators (list of strings)
192        """
193        return []
194
195    def isCommentStyle(self, style):
196        """
197        Public method to check, if a style is a comment style.
198
199        @param style style to check (integer)
200        @return flag indicating a comment style (boolean)
201        """
202        return True
203
204    def isStringStyle(self, style):
205        """
206        Public method to check, if a style is a string style.
207
208        @param style style to check (integer)
209        @return flag indicating a string style (boolean)
210        """
211        return True
212
213    def keywords(self, kwSet):
214        """
215        Public method to get the keywords.
216
217        @param kwSet number of the keyword set
218        @type int
219        @return space separated list of keywords
220        @rtype str or None
221        """
222        keywords_ = Preferences.getEditorKeywords(self.language())
223        if keywords_ and len(keywords_) > kwSet:
224            kw = keywords_[kwSet]
225            if kw == "":
226                return self.defaultKeywords(kwSet)
227            else:
228                return kw
229        else:
230            return self.defaultKeywords(kwSet)
231
232    def keywordsDescription(self, kwSet):
233        """
234        Public method to get the description for a keywords set.
235
236        @param kwSet number of the keyword set
237        @type int
238        @return description of the keyword set
239        @rtype str
240        """
241        if kwSet > len(self.keywordSetDescriptions):
242            return ""
243        else:
244            return self.keywordSetDescriptions[kwSet - 1]
245
246    def defaultKeywords(self, kwSet):
247        """
248        Public method to get the default keywords.
249
250        @param kwSet number of the keyword set
251        @type int
252        @return space separated list of keywords
253        @rtype str or None
254        """
255        return None     # __IGNORE_WARNING_M831__
256
257    def maximumKeywordSet(self):
258        """
259        Public method to get the maximum keyword set.
260
261        Note: A return value of 0 indicates to determine this dynamically.
262
263        @return maximum keyword set
264        @rtype int
265        """
266        return len(self.keywordSetDescriptions)
267
268    def lexerName(self):
269        """
270        Public method to return the lexer name.
271
272        @return lexer name (string)
273        """
274        return self.lexer()
275
276    def hasSubstyles(self):
277        """
278        Public method to indicate the support of sub-styles.
279
280        @return flag indicating sub-styling support
281        @rtype bool
282        """
283        return False
284