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