1 // Scintilla source code edit control
2 /** @file Lexr.cxx
3  ** Lexer for R, S, SPlus Statistics Program (Heavily derived from CPP Lexer).
4  **
5  **/
6 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 using namespace Scintilla;
28 
IsAWordChar(const int ch)29 static inline bool IsAWordChar(const int ch) {
30 	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
31 }
32 
IsAWordStart(const int ch)33 static inline bool IsAWordStart(const int ch) {
34 	return (ch < 0x80) && (isalnum(ch) || ch == '_');
35 }
36 
IsAnOperator(const int ch)37 static inline bool IsAnOperator(const int ch) {
38 	if (IsASCII(ch) && isalnum(ch))
39 		return false;
40 	// '.' left out as it is used to make up numbers
41 	if (ch == '-' || ch == '+' || ch == '!' || ch == '~' ||
42 	        ch == '?' || ch == ':' || ch == '*' || ch == '/' ||
43 	        ch == '^' || ch == '<' || ch == '>' || ch == '=' ||
44 	        ch == '&' || ch == '|' || ch == '$' || ch == '(' ||
45 	        ch == ')' || ch == '}' || ch == '{' || ch == '[' ||
46 		ch == ']')
47 		return true;
48 	return false;
49 }
50 
ColouriseRDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)51 static void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
52                             Accessor &styler) {
53 
54 	WordList &keywords   = *keywordlists[0];
55 	WordList &keywords2 = *keywordlists[1];
56 	WordList &keywords3 = *keywordlists[2];
57 
58 
59 	// Do not leak onto next line
60 	if (initStyle == SCE_R_INFIXEOL)
61 		initStyle = SCE_R_DEFAULT;
62 
63 
64 	StyleContext sc(startPos, length, initStyle, styler);
65 
66 	for (; sc.More(); sc.Forward()) {
67 
68 		if (sc.atLineStart && (sc.state == SCE_R_STRING)) {
69 			// Prevent SCE_R_STRINGEOL from leaking back to previous line
70 			sc.SetState(SCE_R_STRING);
71 		}
72 
73 		// Determine if the current state should terminate.
74 		if (sc.state == SCE_R_OPERATOR) {
75 			sc.SetState(SCE_R_DEFAULT);
76 		} else if (sc.state == SCE_R_NUMBER) {
77 			if (!IsADigit(sc.ch) && !(sc.ch == '.' && IsADigit(sc.chNext))) {
78 				sc.SetState(SCE_R_DEFAULT);
79 			}
80 		} else if (sc.state == SCE_R_IDENTIFIER) {
81 			if (!IsAWordChar(sc.ch)) {
82 				char s[100];
83 				sc.GetCurrent(s, sizeof(s));
84 				if (keywords.InList(s)) {
85 					sc.ChangeState(SCE_R_KWORD);
86 				} else if  (keywords2.InList(s)) {
87 					sc.ChangeState(SCE_R_BASEKWORD);
88 				} else if  (keywords3.InList(s)) {
89 					sc.ChangeState(SCE_R_OTHERKWORD);
90 				}
91 				sc.SetState(SCE_R_DEFAULT);
92 			}
93 		} else if (sc.state == SCE_R_COMMENT) {
94 			if (sc.ch == '\r' || sc.ch == '\n') {
95 				sc.SetState(SCE_R_DEFAULT);
96 			}
97 		} else if (sc.state == SCE_R_STRING) {
98 			if (sc.ch == '\\') {
99 				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
100 					sc.Forward();
101 				}
102 			} else if (sc.ch == '\"') {
103 				sc.ForwardSetState(SCE_R_DEFAULT);
104 			}
105 		} else if (sc.state == SCE_R_INFIX) {
106 			if (sc.ch == '%') {
107 				sc.ForwardSetState(SCE_R_DEFAULT);
108 			} else if (sc.atLineEnd) {
109 				sc.ChangeState(SCE_R_INFIXEOL);
110 				sc.ForwardSetState(SCE_R_DEFAULT);
111 			}
112 		}else if (sc.state == SCE_R_STRING2) {
113 			if (sc.ch == '\\') {
114 				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
115 					sc.Forward();
116 				}
117 			} else if (sc.ch == '\'') {
118 				sc.ForwardSetState(SCE_R_DEFAULT);
119 			}
120 		}
121 
122 		// Determine if a new state should be entered.
123 		if (sc.state == SCE_R_DEFAULT) {
124 			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
125 				sc.SetState(SCE_R_NUMBER);
126 			} else if (IsAWordStart(sc.ch) ) {
127 				sc.SetState(SCE_R_IDENTIFIER);
128 			} else if (sc.Match('#')) {
129 					sc.SetState(SCE_R_COMMENT);
130 			} else if (sc.ch == '\"') {
131 				sc.SetState(SCE_R_STRING);
132 			} else if (sc.ch == '%') {
133 				sc.SetState(SCE_R_INFIX);
134 			} else if (sc.ch == '\'') {
135 				sc.SetState(SCE_R_STRING2);
136 			} else if (IsAnOperator(sc.ch)) {
137 				sc.SetState(SCE_R_OPERATOR);
138 			}
139 		}
140 	}
141 	sc.Complete();
142 }
143 
144 // Store both the current line's fold level and the next lines in the
145 // level store to make it easy to pick up with each increment
146 // and to make it possible to fiddle the current level for "} else {".
FoldRDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)147 static void FoldRDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[],
148                        Accessor &styler) {
149 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
150 	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
151 	Sci_PositionU endPos = startPos + length;
152 	int visibleChars = 0;
153 	Sci_Position lineCurrent = styler.GetLine(startPos);
154 	int levelCurrent = SC_FOLDLEVELBASE;
155 	if (lineCurrent > 0)
156 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
157 	int levelMinCurrent = levelCurrent;
158 	int levelNext = levelCurrent;
159 	char chNext = styler[startPos];
160 	int styleNext = styler.StyleAt(startPos);
161 	for (Sci_PositionU i = startPos; i < endPos; i++) {
162 		char ch = chNext;
163 		chNext = styler.SafeGetCharAt(i + 1);
164 		int style = styleNext;
165 		styleNext = styler.StyleAt(i + 1);
166 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
167 		if (style == SCE_R_OPERATOR) {
168 			if (ch == '{') {
169 				// Measure the minimum before a '{' to allow
170 				// folding on "} else {"
171 				if (levelMinCurrent > levelNext) {
172 					levelMinCurrent = levelNext;
173 				}
174 				levelNext++;
175 			} else if (ch == '}') {
176 				levelNext--;
177 			}
178 		}
179 		if (atEOL) {
180 			int levelUse = levelCurrent;
181 			if (foldAtElse) {
182 				levelUse = levelMinCurrent;
183 			}
184 			int lev = levelUse | levelNext << 16;
185 			if (visibleChars == 0 && foldCompact)
186 				lev |= SC_FOLDLEVELWHITEFLAG;
187 			if (levelUse < levelNext)
188 				lev |= SC_FOLDLEVELHEADERFLAG;
189 			if (lev != styler.LevelAt(lineCurrent)) {
190 				styler.SetLevel(lineCurrent, lev);
191 			}
192 			lineCurrent++;
193 			levelCurrent = levelNext;
194 			levelMinCurrent = levelCurrent;
195 			visibleChars = 0;
196 		}
197 		if (!isspacechar(ch))
198 			visibleChars++;
199 	}
200 }
201 
202 
203 static const char * const RWordLists[] = {
204             "Language Keywords",
205             "Base / Default package function",
206             "Other Package Functions",
207             "Unused",
208             "Unused",
209             0,
210         };
211 
212 
213 
214 LexerModule lmR(SCLEX_R, ColouriseRDoc, "r", FoldRDoc, RWordLists);
215