1 /**
2  *  Yudit Unicode Editor Source File
3  *
4  *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License, version 2,
8  *  dated June 1991. See file COPYYING for details.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "stoolkit/syntax/SSyntaxMarker.h"
21 #include "stoolkit/syntax/SSyntax.h"
22 
23 
24 /**
25  * The _textData and _lines arrays should be in sync, they
26  * should have the same sizes.
27  * @param _around is around which we should mark unmarked in our buffer.
28  */
SSyntaxMarker(SSyntaxData & _lines,const SUnicodeData & _ulines,const STextIndex & _around)29 SSyntaxMarker::SSyntaxMarker (SSyntaxData& _lines, const SUnicodeData& _ulines,
30   const STextIndex& _around)
31    : syntaxLines (_lines), dataLines (_ulines)
32 {
33   STextIndex first = _around;
34   // look backwards for a sync
35   if (decrement(&first)) while ((getSyntaxAt (first) & SGC_BEGIN_MARK) == 0)
36   {
37     if (!decrement(&first)) break;
38   }
39   // hack, commment this out if you have a proper parser.
40   // FIXME FIXME
41   //first = STextIndex (0,0);
42 //  fprintf (stderr, "SGC syntax started around=%u,%u found=%u,%u\n",
43 //     _around.line, _around.index, first.line, first.index);
44 
45   startIndex = first;
46   unsigned int firstLineSize = getLineSize (startIndex.line)
47         - startIndex.index;
48   // linesizes always contains 1 more lines than real lines.
49   lineSizes.append (0);
50   lineSizes.append (firstLineSize);
51 /*
52   fprintf (stderr, "firstLineSize=%u getLineSize=%u\n",
53      firstLineSize, getLineSize (startIndex.line));
54 */
55 
56   isStarted = true;
57   actionMap.put ("none", ((int) SSyntax::SD_NONE) << 8);
58   actionMap.put ("error", ((int) SSyntax::SD_ERROR) << 8);
59   actionMap.put ("number", ((int) SSyntax::SD_NUMBER) << 8);
60   actionMap.put ("string", ((int) SSyntax::SD_STRING) << 8);
61   actionMap.put ("comment", ((int) SSyntax::SD_COMMENT) << 8);
62   actionMap.put ("keyword", ((int) SSyntax::SD_KEYWORD) << 8);
63   actionMap.put ("variable", ((int) SSyntax::SD_VARIABLE) << 8);
64   actionMap.put ("define", ((int) SSyntax::SD_DEFINE) << 8);
65   actionMap.put ("control", ((int) SSyntax::SD_CONTROL) << 8);
66   actionMap.put ("other", ((int) SSyntax::SD_OTHER) << 8);
67 }
68 
~SSyntaxMarker()69 SSyntaxMarker::~SSyntaxMarker ()
70 {
71 }
72 
73 /* SMatcherIterator */
74 // FIXME FIXME FIXME
75 int
getNextCharacter()76 SSyntaxMarker::getNextCharacter ()
77 {
78   if (isStarted)
79   {
80     currentIndex = startIndex;
81     isStarted = false;
82     if (isEOD (currentIndex)) return -1;
83   }
84   else
85   {
86     if (!increment (&currentIndex)) return -1;
87   }
88   // lineSizes keeps track of incremental sizes.
89   if (currentIndex.line - startIndex.line == lineSizes.size()-1)
90   {
91     lineSizes.append (getLineSize (currentIndex.line)
92           + lineSizes[currentIndex.line - startIndex.line ]);
93   }
94   return getCharAt (currentIndex);
95 }
96 
97 bool
isEOD(const STextIndex & idx) const98 SSyntaxMarker::isEOD (const STextIndex& idx) const
99 {
100   if (idx.line >= dataLines.size()) return true;
101   if (idx.index >= getLineSize(idx.line)) return true;
102   return false;
103 }
104 
105 void
beginActionBlock()106 SSyntaxMarker::beginActionBlock ()
107 {
108   minModified = STextIndex (dataLines.size(), 0);
109   maxModified = STextIndex (0, 0);
110 }
111 
112 
113 /* SMatcherAction */
114 // Apply action from from till till exclusive
115 // 1. Mark shadow syntax
116 // 2. modify minModified and maxModified
117 void
applyAction(const SString & name,unsigned int markFrom,unsigned int markTill)118 SSyntaxMarker::applyAction (const SString& name,
119     unsigned int markFrom, unsigned int markTill)
120 {
121   STextIndex from = position2Index (markFrom);
122   STextIndex till = position2Index (markTill);
123 
124 #if DEBUG_PARSER
125 fprintf (stderr, "applyAction %*.*s %u..%u  %u.%u..%u,%u\n",
126   SSARGS(name), markFrom, markTill,
127   from.line, from.index, till.line, till.index);
128 #endif
129   if (from < minModified)
130   {
131     minModified = from;
132   }
133   if (till > maxModified)
134   {
135     maxModified = till;
136   }
137   int shadow = actionMap.get (name);
138   while (from < till)
139   {
140     int old = getSyntaxAt (from);
141     shadow = (old & 0xff) | shadow;
142     // This will wipe out extra marks
143     setSyntaxAt (from, shadow);
144     if (!increment (&from)) break;
145   }
146 }
147 
148 // 1. move shadow syntax to real
149 // 2. update minModified and maxModified
150 // 3. update the minLimits and minLimits to know where stuff changed.
151 void
endActionBlock()152 SSyntaxMarker::endActionBlock ()
153 {
154 #if DEBUG_PARSER
155 fprintf (stderr, "finish %u.%u..%u,%u\n",
156   minModified.line, minModified.index,
157   maxModified.line, maxModified.index);
158 #endif
159 
160   STextIndex min = STextIndex (dataLines.size(), 0);
161   STextIndex max = STextIndex (0, 0);
162 
163   STextIndex from = minModified;
164   while (from < maxModified)
165   {
166     int vle = getSyntaxAt (from);
167     int o = (vle & 0xff);
168     int n = (vle & 0xff00) >> 8;
169     if (o == n)
170     {
171       if (!increment (&from)) break;
172       continue;
173     }
174     if (from < min) min = from;
175     if (from > max) max = from;
176     setSyntaxAt (from, n);
177     if (!increment (&from)) break;
178   }
179   // multiline, scrolled editor. write yuko in Hungarian kmap
180   // yuko is not first line. o is not error with test SPattern.
181   // dont know why...
182 //  minModified = min;
183 //  maxModified = max;
184 }
185 
186 // Increment the in index
187 // return false if in is already at the end
188 // For non expanded lines we generate virtual new index if
189 // it is has a proper ending.
190 bool
decrement(STextIndex * in)191 SSyntaxMarker::decrement (STextIndex* in)
192 {
193   if (in->line == 0 && in->index==0) return false;
194   if (in->index == 0)
195   {
196     in->line--;
197     in->index = getLineSize (in->line);
198     if (in->index==0)
199     {
200 #if DEBUG_PARSER
201       fprintf (stderr, "SSyntaxMarker::decrement: empty line detected.\n");
202 #endif
203       return false;
204     }
205   }
206   // There can be no empty lines in the middle of the file
207   in->index = in->index-1;
208   return true;
209 }
210 
211 // Decrement the in index
212 // return false if in is already at the end
213 // For non expanded lines we generate virtual new index if
214 // it is has a proper ending.
215 bool
increment(STextIndex * in)216 SSyntaxMarker::increment (STextIndex* in)
217 {
218   if (in->line >= dataLines.size()) return false;
219   unsigned int ls = getLineSize (in->line);
220   if (in->index+1 == ls)
221   {
222     in->line++;
223     in->index=0;
224     if (in->line >= dataLines.size()) return false;
225     ls = getLineSize (in->line);
226     if (ls == 0) return false;
227     return true;
228   }
229   in->index++;
230   if (in->index >= ls) return false;
231   return true;
232 }
233 
234 // Get character at the index.
235 // return -1 if in is out of bounds.
236 int
getCharAt(const STextIndex & in) const237 SSyntaxMarker::getCharAt (const STextIndex& in) const
238 {
239   if (in.line >= dataLines.size()) return -1;
240   if (in.index >= dataLines[in.line]->size()) return -1;
241   // todo sanity check here
242   SS_UCS4 ret = dataLines[in.line]->peek (in.index);
243   // Paragraph separator
244 //fprintf (stderr, "%u,%u=[%c]", in.line, in.index, (char) ret);
245   if (ret == 0x2029) return (int) '\n';
246   return (int) ret;
247 }
248 
249 // Get character at the index.
250 // return -1 if in is out of bounds.
251 int
getSyntaxAt(const STextIndex & in) const252 SSyntaxMarker::getSyntaxAt (const STextIndex& in) const
253 {
254   if (in.line >= syntaxLines.size()) return -1;
255   if (in.index >= syntaxLines[in.line]->size()) return -1;
256   // todo sanity check here
257   return syntaxLines[in.line]->peek (in.index);
258 }
259 
260 // Get character at the index.
261 // return false if in is out of bounds.
262 bool
setSyntaxAt(const STextIndex & in,int syn)263 SSyntaxMarker::setSyntaxAt (const STextIndex& in, int syn)
264 {
265   if (in.line >= syntaxLines.size()) return false;
266   if (in.index >= syntaxLines[in.line]->size()) return false;
267   syntaxLines[in.line]->replace (in.index, syn);
268   return true;
269 }
270 
271 // get the line size, adjusted for non-expanded chanacters.
272 // an extra functionality is to sync expanded lines, as
273 // they are not always reported elsewhere.
274 unsigned int
getLineSize(unsigned int lineno) const275 SSyntaxMarker::getLineSize  (unsigned int lineno) const
276 {
277   if (lineno >= dataLines.size()) return 0;
278   // line is expaned
279   return dataLines[lineno]->size();
280 }
281 
282 // linsizes[line] contain the accumulated number of characters
283 // from startIndex at the end of the line.
284 STextIndex
position2Index(unsigned int position)285 SSyntaxMarker::position2Index (unsigned int position)
286 {
287   if (position < lineSizes[1])
288   {
289     return STextIndex (startIndex.line, startIndex.index + position);
290   }
291   // This can return a bigger value
292   unsigned int mapIndex = lineSizes.findSorted (position);
293   if (mapIndex>=lineSizes.size())
294   {
295     return STextIndex (startIndex.line+lineSizes.size()-1, 0);
296   }
297   while (position < lineSizes[mapIndex] && mapIndex > 0) mapIndex--;
298   unsigned int addValue =  lineSizes[mapIndex];
299   return STextIndex (mapIndex+startIndex.line, position-addValue);
300 }
301