1 // Scintilla source code edit control
2 /** @file LexMatlab.cxx
4 ** Written by José Fonseca
6 ** Changes by Christoph Dalitz 2003/12/04:
7 ** - added support for Octave
8 ** - Strings can now be included both in single or double quotes
10 ** Changes by John Donoghue 2012/04/02
11 ** - added block comment (and nested block comments)
12 ** - added ... displayed as a comment
13 ** - removed unused IsAWord functions
14 ** - added some comments
16 ** Changes by John Donoghue 2014/08/01
17 ** - fix allowed transpose ' after {} operator
19 ** Changes by John Donoghue 2016/11/15
20 ** - update matlab code folding
22 ** Changes by John Donoghue 2017/01/18
23 ** - update matlab block comment detection
25 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
26 // The License.txt file describes the conditions under which this software may be distributed.
36 #include "Scintilla.h"
40 #include "LexAccessor.h"
42 #include "StyleContext.h"
43 #include "CharacterSet.h"
44 #include "LexerModule.h"
47 using namespace Scintilla
;
50 static bool IsMatlabCommentChar(int c
) {
54 static bool IsOctaveCommentChar(int c
) {
55 return (c
== '%' || c
== '#') ;
58 static inline int LowerCase(int c
) {
59 if (c
>= 'A' && c
<= 'Z')
64 static int CheckKeywordFoldPoint(char *str
) {
65 if (strcmp ("if", str
) == 0 ||
66 strcmp ("for", str
) == 0 ||
67 strcmp ("switch", str
) == 0 ||
68 strcmp ("try", str
) == 0 ||
69 strcmp ("do", str
) == 0 ||
70 strcmp ("parfor", str
) == 0 ||
71 strcmp ("function", str
) == 0)
73 if (strncmp("end", str
, 3) == 0 ||
74 strcmp("until", str
) == 0)
79 static bool IsSpaceToEOL(Sci_Position startPos
, Accessor
&styler
) {
80 Sci_Position line
= styler
.GetLine(startPos
);
81 Sci_Position eol_pos
= styler
.LineStart(line
+ 1) - 1;
82 for (Sci_Position i
= startPos
; i
< eol_pos
; i
++) {
84 if(!IsASpace(ch
)) return false;
89 static void ColouriseMatlabOctaveDoc(
90 Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
91 WordList
*keywordlists
[], Accessor
&styler
,
92 bool (*IsCommentChar
)(int),
95 WordList
&keywords
= *keywordlists
[0];
97 styler
.StartAt(startPos
);
99 // boolean for when the ' is allowed to be transpose vs the start/end
101 bool transpose
= false;
103 // approximate position of first non space character in a line
104 int nonSpaceColumn
= -1;
105 // approximate column position of the current character in a line
108 // use the line state of each line to store the block comment depth
109 Sci_Position curLine
= styler
.GetLine(startPos
);
110 int commentDepth
= curLine
> 0 ? styler
.GetLineState(curLine
-1) : 0;
113 StyleContext
sc(startPos
, length
, initStyle
, styler
);
115 for (; sc
.More(); sc
.Forward(), column
++) {
118 // set the line state to the current commentDepth
119 curLine
= styler
.GetLine(sc
.currentPos
);
120 styler
.SetLineState(curLine
, commentDepth
);
122 // reset the column to 0, nonSpace to -1 (not set)
127 // save the column position of first non space character in a line
128 if((nonSpaceColumn
== -1) && (! IsASpace(sc
.ch
)))
130 nonSpaceColumn
= column
;
133 // check for end of states
134 if (sc
.state
== SCE_MATLAB_OPERATOR
) {
135 if (sc
.chPrev
== '.') {
136 if (sc
.ch
== '*' || sc
.ch
== '/' || sc
.ch
== '\\' || sc
.ch
== '^') {
137 sc
.ForwardSetState(SCE_MATLAB_DEFAULT
);
139 } else if (sc
.ch
== '\'') {
140 sc
.ForwardSetState(SCE_MATLAB_DEFAULT
);
142 } else if(sc
.ch
== '.' && sc
.chNext
== '.') {
143 // we werent an operator, but a '...'
144 sc
.ChangeState(SCE_MATLAB_COMMENT
);
147 sc
.SetState(SCE_MATLAB_DEFAULT
);
150 sc
.SetState(SCE_MATLAB_DEFAULT
);
152 } else if (sc
.state
== SCE_MATLAB_KEYWORD
) {
153 if (!isalnum(sc
.ch
) && sc
.ch
!= '_') {
155 sc
.GetCurrentLowered(s
, sizeof(s
));
156 if (keywords
.InList(s
)) {
157 sc
.SetState(SCE_MATLAB_DEFAULT
);
160 sc
.ChangeState(SCE_MATLAB_IDENTIFIER
);
161 sc
.SetState(SCE_MATLAB_DEFAULT
);
165 } else if (sc
.state
== SCE_MATLAB_NUMBER
) {
166 if (!isdigit(sc
.ch
) && sc
.ch
!= '.'
167 && !(sc
.ch
== 'e' || sc
.ch
== 'E')
168 && !((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E'))) {
169 sc
.SetState(SCE_MATLAB_DEFAULT
);
172 } else if (sc
.state
== SCE_MATLAB_STRING
) {
174 if (sc
.chNext
== '\'') {
177 sc
.ForwardSetState(SCE_MATLAB_DEFAULT
);
180 } else if (sc
.state
== SCE_MATLAB_DOUBLEQUOTESTRING
) {
182 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
185 } else if (sc
.ch
== '\"') {
186 sc
.ForwardSetState(SCE_MATLAB_DEFAULT
);
188 } else if (sc
.state
== SCE_MATLAB_COMMAND
) {
190 sc
.SetState(SCE_MATLAB_DEFAULT
);
193 } else if (sc
.state
== SCE_MATLAB_COMMENT
) {
194 // end or start of a nested a block comment?
195 if( IsCommentChar(sc
.ch
) && sc
.chNext
== '}' && nonSpaceColumn
== column
&& IsSpaceToEOL(sc
.currentPos
+2, styler
)) {
196 if(commentDepth
> 0) commentDepth
--;
198 curLine
= styler
.GetLine(sc
.currentPos
);
199 styler
.SetLineState(curLine
, commentDepth
);
202 if (commentDepth
== 0) {
203 sc
.ForwardSetState(SCE_D_DEFAULT
);
207 else if( IsCommentChar(sc
.ch
) && sc
.chNext
== '{' && nonSpaceColumn
== column
&& IsSpaceToEOL(sc
.currentPos
+2, styler
))
211 curLine
= styler
.GetLine(sc
.currentPos
);
212 styler
.SetLineState(curLine
, commentDepth
);
216 } else if(commentDepth
== 0) {
217 // single line comment
218 if (sc
.atLineEnd
|| sc
.ch
== '\r' || sc
.ch
== '\n') {
219 sc
.SetState(SCE_MATLAB_DEFAULT
);
225 // check start of a new state
226 if (sc
.state
== SCE_MATLAB_DEFAULT
) {
227 if (IsCommentChar(sc
.ch
)) {
228 // ncrement depth if we are a block comment
229 if(sc
.chNext
== '{' && nonSpaceColumn
== column
) {
230 if(IsSpaceToEOL(sc
.currentPos
+2, styler
)) {
234 curLine
= styler
.GetLine(sc
.currentPos
);
235 styler
.SetLineState(curLine
, commentDepth
);
236 sc
.SetState(SCE_MATLAB_COMMENT
);
237 } else if (sc
.ch
== '!' && sc
.chNext
!= '=' ) {
239 sc
.SetState(SCE_MATLAB_COMMAND
);
241 sc
.SetState(SCE_MATLAB_OPERATOR
);
243 } else if (sc
.ch
== '\'') {
245 sc
.SetState(SCE_MATLAB_OPERATOR
);
247 sc
.SetState(SCE_MATLAB_STRING
);
249 } else if (sc
.ch
== '"') {
250 sc
.SetState(SCE_MATLAB_DOUBLEQUOTESTRING
);
251 } else if (isdigit(sc
.ch
) || (sc
.ch
== '.' && isdigit(sc
.chNext
))) {
252 sc
.SetState(SCE_MATLAB_NUMBER
);
253 } else if (isalpha(sc
.ch
)) {
254 sc
.SetState(SCE_MATLAB_KEYWORD
);
255 } else if (isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '@' || sc
.ch
== '\\') {
256 if (sc
.ch
== ')' || sc
.ch
== ']' || sc
.ch
== '}') {
261 sc
.SetState(SCE_MATLAB_OPERATOR
);
270 static void ColouriseMatlabDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
271 WordList
*keywordlists
[], Accessor
&styler
) {
272 ColouriseMatlabOctaveDoc(startPos
, length
, initStyle
, keywordlists
, styler
, IsMatlabCommentChar
, true);
275 static void ColouriseOctaveDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
276 WordList
*keywordlists
[], Accessor
&styler
) {
277 ColouriseMatlabOctaveDoc(startPos
, length
, initStyle
, keywordlists
, styler
, IsOctaveCommentChar
, false);
280 static void FoldMatlabOctaveDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
281 WordList
*[], Accessor
&styler
,
282 bool (*IsComment
)(int ch
)) {
284 Sci_PositionU endPos
= startPos
+ length
;
285 int visibleChars
= 0;
286 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
287 int levelCurrent
= SC_FOLDLEVELBASE
;
289 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
290 int levelNext
= levelCurrent
;
291 char chNext
= styler
[startPos
];
292 int styleNext
= styler
.StyleAt(startPos
);
293 int style
= initStyle
;
296 for (Sci_PositionU i
= startPos
; i
< endPos
; i
++) {
298 chNext
= styler
.SafeGetCharAt(i
+ 1);
300 styleNext
= styler
.StyleAt(i
+ 1);
301 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
303 // a line that starts with a comment
304 if (style
== SCE_MATLAB_COMMENT
&& IsComment(ch
) && visibleChars
== 0) {
305 // start/end of block comment
306 if (chNext
== '{' && IsSpaceToEOL(i
+2, styler
))
308 if (chNext
== '}' && IsSpaceToEOL(i
+2, styler
))
312 if(style
== SCE_MATLAB_KEYWORD
) {
313 word
[wordlen
++] = static_cast<char>(LowerCase(ch
));
314 if (wordlen
== 100) { // prevent overflow
318 if (styleNext
!= SCE_MATLAB_KEYWORD
) {
319 word
[wordlen
] = '\0';
322 levelNext
+= CheckKeywordFoldPoint(word
);
327 if (atEOL
|| (i
== endPos
-1)) {
328 int levelUse
= levelCurrent
;
329 int lev
= levelUse
| levelNext
<< 16;
330 if (visibleChars
== 0)
331 lev
|= SC_FOLDLEVELWHITEFLAG
;
332 if (levelUse
< levelNext
)
333 lev
|= SC_FOLDLEVELHEADERFLAG
;
334 if (lev
!= styler
.LevelAt(lineCurrent
)) {
335 styler
.SetLevel(lineCurrent
, lev
);
338 levelCurrent
= levelNext
;
339 if (atEOL
&& (i
== static_cast<Sci_PositionU
>(styler
.Length() - 1))) {
340 // There is an empty line at end of file so give it same level and empty
341 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
348 static void FoldMatlabDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
349 WordList
*keywordlists
[], Accessor
&styler
) {
350 FoldMatlabOctaveDoc(startPos
, length
, initStyle
, keywordlists
, styler
, IsMatlabCommentChar
);
353 static void FoldOctaveDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
354 WordList
*keywordlists
[], Accessor
&styler
) {
355 FoldMatlabOctaveDoc(startPos
, length
, initStyle
, keywordlists
, styler
, IsOctaveCommentChar
);
358 static const char * const matlabWordListDesc
[] = {
363 static const char * const octaveWordListDesc
[] = {
368 LexerModule
lmMatlab(SCLEX_MATLAB
, ColouriseMatlabDoc
, "matlab", FoldMatlabDoc
, matlabWordListDesc
);
370 LexerModule
lmOctave(SCLEX_OCTAVE
, ColouriseOctaveDoc
, "octave", FoldOctaveDoc
, octaveWordListDesc
);