1 | // ASEnhancer.cpp |
2 | // Copyright (c) 2018 by Jim Pattee <jimp03@email.com>. |
3 | // This code is licensed under the MIT License. |
4 | // License.md describes the conditions under which this software may be distributed. |
5 | |
6 | //----------------------------------------------------------------------------- |
7 | // headers |
8 | //----------------------------------------------------------------------------- |
9 | |
10 | #include "astyle.h" |
11 | |
12 | //----------------------------------------------------------------------------- |
13 | // astyle namespace |
14 | //----------------------------------------------------------------------------- |
15 | |
16 | namespace astyle { |
17 | // |
18 | //----------------------------------------------------------------------------- |
19 | // ASEnhancer class |
20 | //----------------------------------------------------------------------------- |
21 | |
22 | /** |
23 | * initialize the ASEnhancer. |
24 | * |
25 | * init() is called each time an ASFormatter object is initialized. |
26 | */ |
27 | void ASEnhancer::init(int _fileType, |
28 | int _indentLength, |
29 | int _tabLength, |
30 | bool _useTabs, |
31 | bool _forceTab, |
32 | bool _namespaceIndent, |
33 | bool _caseIndent, |
34 | bool _preprocBlockIndent, |
35 | bool _preprocDefineIndent, |
36 | bool _emptyLineFill, |
37 | vector<const pair<const string, const string>* >* _indentableMacros) |
38 | { |
39 | // formatting variables from ASFormatter and ASBeautifier |
40 | ASBase::init(_fileType); |
41 | indentLength = _indentLength; |
42 | tabLength = _tabLength; |
43 | useTabs = _useTabs; |
44 | forceTab = _forceTab; |
45 | namespaceIndent = _namespaceIndent; |
46 | caseIndent = _caseIndent; |
47 | preprocBlockIndent = _preprocBlockIndent; |
48 | preprocDefineIndent = _preprocDefineIndent; |
49 | emptyLineFill = _emptyLineFill; |
50 | indentableMacros = _indentableMacros; |
51 | quoteChar = '\''; |
52 | |
53 | // unindent variables |
54 | lineNumber = 0; |
55 | braceCount = 0; |
56 | isInComment = false; |
57 | isInQuote = false; |
58 | switchDepth = 0; |
59 | eventPreprocDepth = 0; |
60 | lookingForCaseBrace = false; |
61 | unindentNextLine = false; |
62 | shouldUnindentLine = false; |
63 | shouldUnindentComment = false; |
64 | |
65 | // switch struct and vector |
66 | sw.switchBraceCount = 0; |
67 | sw.unindentDepth = 0; |
68 | sw.unindentCase = false; |
69 | switchStack.clear(); |
70 | |
71 | // other variables |
72 | nextLineIsEventIndent = false; |
73 | isInEventTable = false; |
74 | nextLineIsDeclareIndent = false; |
75 | isInDeclareSection = false; |
76 | } |
77 | |
78 | /** |
79 | * additional formatting for line of source code. |
80 | * every line of source code in a source code file should be sent |
81 | * one after the other to this function. |
82 | * indents event tables |
83 | * unindents the case blocks |
84 | * |
85 | * @param line the original formatted line will be updated if necessary. |
86 | */ |
87 | void ASEnhancer::enhance(string& line, bool isInNamespace, bool isInPreprocessor, bool isInSQL) |
88 | { |
89 | shouldUnindentLine = true; |
90 | shouldUnindentComment = false; |
91 | lineNumber++; |
92 | |
93 | // check for beginning of event table |
94 | if (nextLineIsEventIndent) |
95 | { |
96 | isInEventTable = true; |
97 | nextLineIsEventIndent = false; |
98 | } |
99 | |
100 | // check for beginning of SQL declare section |
101 | if (nextLineIsDeclareIndent) |
102 | { |
103 | isInDeclareSection = true; |
104 | nextLineIsDeclareIndent = false; |
105 | } |
106 | |
107 | if (line.length() == 0 |
108 | && !isInEventTable |
109 | && !isInDeclareSection |
110 | && !emptyLineFill) |
111 | return; |
112 | |
113 | // test for unindent on attached braces |
114 | if (unindentNextLine) |
115 | { |
116 | sw.unindentDepth++; |
117 | sw.unindentCase = true; |
118 | unindentNextLine = false; |
119 | } |
120 | |
121 | // parse characters in the current line |
122 | parseCurrentLine(line, isInPreprocessor, isInSQL); |
123 | |
124 | // check for SQL indentable lines |
125 | if (isInDeclareSection) |
126 | { |
127 | size_t firstText = line.find_first_not_of(" \t" ); |
128 | if (firstText == string::npos || line[firstText] != '#') |
129 | indentLine(line, 1); |
130 | } |
131 | |
132 | // check for event table indentable lines |
133 | if (isInEventTable |
134 | && (eventPreprocDepth == 0 |
135 | || (namespaceIndent && isInNamespace))) |
136 | { |
137 | size_t firstText = line.find_first_not_of(" \t" ); |
138 | if (firstText == string::npos || line[firstText] != '#') |
139 | indentLine(line, 1); |
140 | } |
141 | |
142 | if (shouldUnindentComment && sw.unindentDepth > 0) |
143 | unindentLine(line, sw.unindentDepth - 1); |
144 | else if (shouldUnindentLine && sw.unindentDepth > 0) |
145 | unindentLine(line, sw.unindentDepth); |
146 | } |
147 | |
148 | /** |
149 | * convert a force-tab indent to spaces |
150 | * |
151 | * @param line a reference to the line that will be converted. |
152 | */ |
153 | void ASEnhancer::convertForceTabIndentToSpaces(string& line) const |
154 | { |
155 | // replace tab indents with spaces |
156 | for (size_t i = 0; i < line.length(); i++) |
157 | { |
158 | if (!isWhiteSpace(line[i])) |
159 | break; |
160 | if (line[i] == '\t') |
161 | { |
162 | line.erase(i, 1); |
163 | line.insert(i, tabLength, ' '); |
164 | i += tabLength - 1; |
165 | } |
166 | } |
167 | } |
168 | |
169 | /** |
170 | * convert a space indent to force-tab |
171 | * |
172 | * @param line a reference to the line that will be converted. |
173 | */ |
174 | void ASEnhancer::convertSpaceIndentToForceTab(string& line) const |
175 | { |
176 | assert(tabLength > 0); |
177 | |
178 | // replace leading spaces with tab indents |
179 | size_t newSpaceIndentLength = line.find_first_not_of(" \t" ); |
180 | size_t tabCount = newSpaceIndentLength / tabLength; // truncate extra spaces |
181 | line.replace(0U, tabCount * tabLength, tabCount, '\t'); |
182 | } |
183 | |
184 | /** |
185 | * find the colon following a 'case' statement |
186 | * |
187 | * @param line a reference to the line. |
188 | * @param caseIndex the line index of the case statement. |
189 | * @return the line index of the colon. |
190 | */ |
191 | size_t ASEnhancer::findCaseColon(const string& line, size_t caseIndex) const |
192 | { |
193 | size_t i = caseIndex; |
194 | bool isInQuote_ = false; |
195 | char quoteChar_ = ' '; |
196 | for (; i < line.length(); i++) |
197 | { |
198 | if (isInQuote_) |
199 | { |
200 | if (line[i] == '\\') |
201 | { |
202 | i++; |
203 | continue; |
204 | } |
205 | if (line[i] == quoteChar_) // check ending quote |
206 | { |
207 | isInQuote_ = false; |
208 | quoteChar_ = ' '; |
209 | continue; |
210 | } |
211 | continue; // must close quote before continuing |
212 | } |
213 | if (line[i] == '"' // check opening quote |
214 | || (line[i] == '\'' && !isDigitSeparator(line, i))) |
215 | { |
216 | isInQuote_ = true; |
217 | quoteChar_ = line[i]; |
218 | continue; |
219 | } |
220 | if (line[i] == ':') |
221 | { |
222 | if ((i + 1 < line.length()) && (line[i + 1] == ':')) |
223 | i++; // bypass scope resolution operator |
224 | else |
225 | break; // found it |
226 | } |
227 | } |
228 | return i; |
229 | } |
230 | |
231 | /** |
232 | * indent a line by a given number of tabsets |
233 | * by inserting leading whitespace to the line argument. |
234 | * |
235 | * @param line a reference to the line to indent. |
236 | * @param indent the number of tabsets to insert. |
237 | * @return the number of characters inserted. |
238 | */ |
239 | int ASEnhancer::indentLine(string& line, int indent) const |
240 | { |
241 | if (line.length() == 0 |
242 | && !emptyLineFill) |
243 | return 0; |
244 | |
245 | size_t charsToInsert = 0; |
246 | |
247 | if (forceTab && indentLength != tabLength) |
248 | { |
249 | // replace tab indents with spaces |
250 | convertForceTabIndentToSpaces(line); |
251 | // insert the space indents |
252 | charsToInsert = indent * indentLength; |
253 | line.insert(line.begin(), charsToInsert, ' '); |
254 | // replace leading spaces with tab indents |
255 | convertSpaceIndentToForceTab(line); |
256 | } |
257 | else if (useTabs) |
258 | { |
259 | charsToInsert = indent; |
260 | line.insert(line.begin(), charsToInsert, '\t'); |
261 | } |
262 | else // spaces |
263 | { |
264 | charsToInsert = indent * indentLength; |
265 | line.insert(line.begin(), charsToInsert, ' '); |
266 | } |
267 | |
268 | return charsToInsert; |
269 | } |
270 | |
271 | /** |
272 | * check for SQL "BEGIN DECLARE SECTION". |
273 | * must compare case insensitive and allow any spacing between words. |
274 | * |
275 | * @param line a reference to the line to indent. |
276 | * @param index the current line index. |
277 | * @return true if a hit. |
278 | */ |
279 | bool ASEnhancer::isBeginDeclareSectionSQL(const string& line, size_t index) const |
280 | { |
281 | string word; |
282 | size_t hits = 0; |
283 | size_t i; |
284 | for (i = index; i < line.length(); i++) |
285 | { |
286 | i = line.find_first_not_of(" \t" , i); |
287 | if (i == string::npos) |
288 | return false; |
289 | if (line[i] == ';') |
290 | break; |
291 | if (!isCharPotentialHeader(line, i)) |
292 | continue; |
293 | word = getCurrentWord(line, i); |
294 | for (char& character : word) |
295 | character = (char) toupper(character); |
296 | if (word == "EXEC" || word == "SQL" ) |
297 | { |
298 | i += word.length() - 1; |
299 | continue; |
300 | } |
301 | if (word == "DECLARE" || word == "SECTION" ) |
302 | { |
303 | hits++; |
304 | i += word.length() - 1; |
305 | continue; |
306 | } |
307 | if (word == "BEGIN" ) |
308 | { |
309 | hits++; |
310 | i += word.length() - 1; |
311 | continue; |
312 | } |
313 | return false; |
314 | } |
315 | if (hits == 3) |
316 | return true; |
317 | return false; |
318 | } |
319 | |
320 | /** |
321 | * check for SQL "END DECLARE SECTION". |
322 | * must compare case insensitive and allow any spacing between words. |
323 | * |
324 | * @param line a reference to the line to indent. |
325 | * @param index the current line index. |
326 | * @return true if a hit. |
327 | */ |
328 | bool ASEnhancer::isEndDeclareSectionSQL(const string& line, size_t index) const |
329 | { |
330 | string word; |
331 | size_t hits = 0; |
332 | size_t i; |
333 | for (i = index; i < line.length(); i++) |
334 | { |
335 | i = line.find_first_not_of(" \t" , i); |
336 | if (i == string::npos) |
337 | return false; |
338 | if (line[i] == ';') |
339 | break; |
340 | if (!isCharPotentialHeader(line, i)) |
341 | continue; |
342 | word = getCurrentWord(line, i); |
343 | for (char& character : word) |
344 | character = (char) toupper(character); |
345 | if (word == "EXEC" || word == "SQL" ) |
346 | { |
347 | i += word.length() - 1; |
348 | continue; |
349 | } |
350 | if (word == "DECLARE" || word == "SECTION" ) |
351 | { |
352 | hits++; |
353 | i += word.length() - 1; |
354 | continue; |
355 | } |
356 | if (word == "END" ) |
357 | { |
358 | hits++; |
359 | i += word.length() - 1; |
360 | continue; |
361 | } |
362 | return false; |
363 | } |
364 | if (hits == 3) |
365 | return true; |
366 | return false; |
367 | } |
368 | |
369 | /** |
370 | * check if a one-line brace has been reached, |
371 | * i.e. if the currently reached '{' character is closed |
372 | * with a complimentary '}' elsewhere on the current line, |
373 | *. |
374 | * @return false = one-line brace has not been reached. |
375 | * true = one-line brace has been reached. |
376 | */ |
377 | bool ASEnhancer::isOneLineBlockReached(const string& line, int startChar) const |
378 | { |
379 | assert(line[startChar] == '{'); |
380 | |
381 | bool = false; |
382 | bool isInQuote_ = false; |
383 | int _braceCount = 1; |
384 | int lineLength = line.length(); |
385 | char quoteChar_ = ' '; |
386 | char ch = ' '; |
387 | |
388 | for (int i = startChar + 1; i < lineLength; ++i) |
389 | { |
390 | ch = line[i]; |
391 | |
392 | if (isInComment_) |
393 | { |
394 | if (line.compare(i, 2, "*/" ) == 0) |
395 | { |
396 | isInComment_ = false; |
397 | ++i; |
398 | } |
399 | continue; |
400 | } |
401 | |
402 | if (ch == '\\') |
403 | { |
404 | ++i; |
405 | continue; |
406 | } |
407 | |
408 | if (isInQuote_) |
409 | { |
410 | if (ch == quoteChar_) |
411 | isInQuote_ = false; |
412 | continue; |
413 | } |
414 | |
415 | if (ch == '"' |
416 | || (ch == '\'' && !isDigitSeparator(line, i))) |
417 | { |
418 | isInQuote_ = true; |
419 | quoteChar_ = ch; |
420 | continue; |
421 | } |
422 | |
423 | if (line.compare(i, 2, "//" ) == 0) |
424 | break; |
425 | |
426 | if (line.compare(i, 2, "/*" ) == 0) |
427 | { |
428 | isInComment_ = true; |
429 | ++i; |
430 | continue; |
431 | } |
432 | |
433 | if (ch == '{') |
434 | ++_braceCount; |
435 | else if (ch == '}') |
436 | --_braceCount; |
437 | |
438 | if (_braceCount == 0) |
439 | return true; |
440 | } |
441 | |
442 | return false; |
443 | } |
444 | |
445 | /** |
446 | * parse characters in the current line to determine if an indent |
447 | * or unindent is needed. |
448 | */ |
449 | void ASEnhancer::parseCurrentLine(string& line, bool isInPreprocessor, bool isInSQL) |
450 | { |
451 | bool isSpecialChar = false; // is a backslash escape character |
452 | |
453 | for (size_t i = 0; i < line.length(); i++) |
454 | { |
455 | char ch = line[i]; |
456 | |
457 | // bypass whitespace |
458 | if (isWhiteSpace(ch)) |
459 | continue; |
460 | |
461 | // handle special characters (i.e. backslash+character such as \n, \t, ...) |
462 | if (isSpecialChar) |
463 | { |
464 | isSpecialChar = false; |
465 | continue; |
466 | } |
467 | if (!(isInComment) && line.compare(i, 2, "\\\\" ) == 0) |
468 | { |
469 | i++; |
470 | continue; |
471 | } |
472 | if (!(isInComment) && ch == '\\') |
473 | { |
474 | isSpecialChar = true; |
475 | continue; |
476 | } |
477 | |
478 | // handle quotes (such as 'x' and "Hello Dolly") |
479 | if (!isInComment |
480 | && (ch == '"' |
481 | || (ch == '\'' && !isDigitSeparator(line, i)))) |
482 | { |
483 | if (!isInQuote) |
484 | { |
485 | quoteChar = ch; |
486 | isInQuote = true; |
487 | } |
488 | else if (quoteChar == ch) |
489 | { |
490 | isInQuote = false; |
491 | continue; |
492 | } |
493 | } |
494 | |
495 | if (isInQuote) |
496 | continue; |
497 | |
498 | // handle comments |
499 | |
500 | if (!(isInComment) && line.compare(i, 2, "//" ) == 0) |
501 | { |
502 | // check for windows line markers |
503 | if (line.compare(i + 2, 1, "\xf0" ) > 0) |
504 | lineNumber--; |
505 | // unindent if not in case braces |
506 | if (line.find_first_not_of(" \t" ) == i |
507 | && sw.switchBraceCount == 1 |
508 | && sw.unindentCase) |
509 | shouldUnindentComment = true; |
510 | break; // finished with the line |
511 | } |
512 | if (!(isInComment) && line.compare(i, 2, "/*" ) == 0) |
513 | { |
514 | // unindent if not in case braces |
515 | if (sw.switchBraceCount == 1 && sw.unindentCase) |
516 | shouldUnindentComment = true; |
517 | isInComment = true; |
518 | size_t = line.find("*/" , i); |
519 | if (commentEnd == string::npos) |
520 | i = line.length() - 1; |
521 | else |
522 | i = commentEnd - 1; |
523 | continue; |
524 | } |
525 | if ((isInComment) && line.compare(i, 2, "*/" ) == 0) |
526 | { |
527 | // unindent if not in case braces |
528 | if (sw.switchBraceCount == 1 && sw.unindentCase) |
529 | shouldUnindentComment = true; |
530 | isInComment = false; |
531 | i++; |
532 | continue; |
533 | } |
534 | if (isInComment) |
535 | { |
536 | // unindent if not in case braces |
537 | if (sw.switchBraceCount == 1 && sw.unindentCase) |
538 | shouldUnindentComment = true; |
539 | size_t = line.find("*/" , i); |
540 | if (commentEnd == string::npos) |
541 | i = line.length() - 1; |
542 | else |
543 | i = commentEnd - 1; |
544 | continue; |
545 | } |
546 | |
547 | // if we have reached this far then we are NOT in a comment or string of special characters |
548 | |
549 | if (line[i] == '{') |
550 | braceCount++; |
551 | |
552 | if (line[i] == '}') |
553 | braceCount--; |
554 | |
555 | // check for preprocessor within an event table |
556 | if (isInEventTable && line[i] == '#' && preprocBlockIndent) |
557 | { |
558 | string preproc; |
559 | preproc = line.substr(i + 1); |
560 | if (preproc.substr(0, 2) == "if" ) // #if, #ifdef, #ifndef) |
561 | eventPreprocDepth += 1; |
562 | if (preproc.substr(0, 5) == "endif" && eventPreprocDepth > 0) |
563 | eventPreprocDepth -= 1; |
564 | } |
565 | |
566 | bool isPotentialKeyword = isCharPotentialHeader(line, i); |
567 | |
568 | // ---------------- wxWidgets and MFC macros ---------------------------------- |
569 | |
570 | if (isPotentialKeyword) |
571 | { |
572 | for (const auto* indentableMacro : *indentableMacros) |
573 | { |
574 | // 'first' is the beginning macro |
575 | if (findKeyword(line, i, indentableMacro->first)) |
576 | { |
577 | nextLineIsEventIndent = true; |
578 | break; |
579 | } |
580 | // 'second' is the ending macro |
581 | if (findKeyword(line, i, indentableMacro->second)) |
582 | { |
583 | isInEventTable = false; |
584 | eventPreprocDepth = 0; |
585 | break; |
586 | } |
587 | } |
588 | } |
589 | |
590 | // ---------------- process SQL ----------------------------------------------- |
591 | |
592 | if (isInSQL) |
593 | { |
594 | if (isBeginDeclareSectionSQL(line, i)) |
595 | nextLineIsDeclareIndent = true; |
596 | if (isEndDeclareSectionSQL(line, i)) |
597 | isInDeclareSection = false; |
598 | break; |
599 | } |
600 | |
601 | // ---------------- process switch statements --------------------------------- |
602 | |
603 | if (isPotentialKeyword && findKeyword(line, i, ASResource::AS_SWITCH)) |
604 | { |
605 | switchDepth++; |
606 | switchStack.emplace_back(sw); // save current variables |
607 | sw.switchBraceCount = 0; |
608 | sw.unindentCase = false; // don't clear case until end of switch |
609 | i += 5; // bypass switch statement |
610 | continue; |
611 | } |
612 | |
613 | // just want unindented case statements from this point |
614 | |
615 | if (caseIndent |
616 | || switchDepth == 0 |
617 | || (isInPreprocessor && !preprocDefineIndent)) |
618 | { |
619 | // bypass the entire word |
620 | if (isPotentialKeyword) |
621 | { |
622 | string name = getCurrentWord(line, i); |
623 | i += name.length() - 1; |
624 | } |
625 | continue; |
626 | } |
627 | |
628 | i = processSwitchBlock(line, i); |
629 | |
630 | } // end of for loop * end of for loop * end of for loop * end of for loop |
631 | } |
632 | |
633 | /** |
634 | * process the character at the current index in a switch block. |
635 | * |
636 | * @param line a reference to the line to indent. |
637 | * @param index the current line index. |
638 | * @return the new line index. |
639 | */ |
640 | size_t ASEnhancer::processSwitchBlock(string& line, size_t index) |
641 | { |
642 | size_t i = index; |
643 | bool isPotentialKeyword = isCharPotentialHeader(line, i); |
644 | |
645 | if (line[i] == '{') |
646 | { |
647 | sw.switchBraceCount++; |
648 | if (lookingForCaseBrace) // if 1st after case statement |
649 | { |
650 | sw.unindentCase = true; // unindenting this case |
651 | sw.unindentDepth++; |
652 | lookingForCaseBrace = false; // not looking now |
653 | } |
654 | return i; |
655 | } |
656 | lookingForCaseBrace = false; // no opening brace, don't indent |
657 | |
658 | if (line[i] == '}') |
659 | { |
660 | sw.switchBraceCount--; |
661 | if (sw.switchBraceCount == 0) // if end of switch statement |
662 | { |
663 | int lineUnindent = sw.unindentDepth; |
664 | if (line.find_first_not_of(" \t" ) == i |
665 | && !switchStack.empty()) |
666 | lineUnindent = switchStack[switchStack.size() - 1].unindentDepth; |
667 | if (shouldUnindentLine) |
668 | { |
669 | if (lineUnindent > 0) |
670 | i -= unindentLine(line, lineUnindent); |
671 | shouldUnindentLine = false; |
672 | } |
673 | switchDepth--; |
674 | sw = switchStack.back(); |
675 | switchStack.pop_back(); |
676 | } |
677 | return i; |
678 | } |
679 | |
680 | if (isPotentialKeyword |
681 | && (findKeyword(line, i, ASResource::AS_CASE) |
682 | || findKeyword(line, i, ASResource::AS_DEFAULT))) |
683 | { |
684 | if (sw.unindentCase) // if unindented last case |
685 | { |
686 | sw.unindentCase = false; // stop unindenting previous case |
687 | sw.unindentDepth--; |
688 | } |
689 | |
690 | i = findCaseColon(line, i); |
691 | |
692 | i++; |
693 | for (; i < line.length(); i++) // bypass whitespace |
694 | { |
695 | if (!isWhiteSpace(line[i])) |
696 | break; |
697 | } |
698 | if (i < line.length()) |
699 | { |
700 | if (line[i] == '{') |
701 | { |
702 | braceCount++; |
703 | sw.switchBraceCount++; |
704 | if (!isOneLineBlockReached(line, i)) |
705 | unindentNextLine = true; |
706 | return i; |
707 | } |
708 | } |
709 | lookingForCaseBrace = true; |
710 | i--; // need to process this char |
711 | return i; |
712 | } |
713 | if (isPotentialKeyword) |
714 | { |
715 | string name = getCurrentWord(line, i); // bypass the entire name |
716 | i += name.length() - 1; |
717 | } |
718 | return i; |
719 | } |
720 | |
721 | /** |
722 | * unindent a line by a given number of tabsets |
723 | * by erasing the leading whitespace from the line argument. |
724 | * |
725 | * @param line a reference to the line to unindent. |
726 | * @param unindent the number of tabsets to erase. |
727 | * @return the number of characters erased. |
728 | */ |
729 | int ASEnhancer::unindentLine(string& line, int unindent) const |
730 | { |
731 | size_t whitespace = line.find_first_not_of(" \t" ); |
732 | |
733 | if (whitespace == string::npos) // if line is blank |
734 | whitespace = line.length(); // must remove padding, if any |
735 | |
736 | if (whitespace == 0) |
737 | return 0; |
738 | |
739 | size_t charsToErase = 0; |
740 | |
741 | if (forceTab && indentLength != tabLength) |
742 | { |
743 | // replace tab indents with spaces |
744 | convertForceTabIndentToSpaces(line); |
745 | // remove the space indents |
746 | size_t spaceIndentLength = line.find_first_not_of(" \t" ); |
747 | charsToErase = unindent * indentLength; |
748 | if (charsToErase <= spaceIndentLength) |
749 | line.erase(0, charsToErase); |
750 | else |
751 | charsToErase = 0; |
752 | // replace leading spaces with tab indents |
753 | convertSpaceIndentToForceTab(line); |
754 | } |
755 | else if (useTabs) |
756 | { |
757 | charsToErase = unindent; |
758 | if (charsToErase <= whitespace) |
759 | line.erase(0, charsToErase); |
760 | else |
761 | charsToErase = 0; |
762 | } |
763 | else // spaces |
764 | { |
765 | charsToErase = unindent * indentLength; |
766 | if (charsToErase <= whitespace) |
767 | line.erase(0, charsToErase); |
768 | else |
769 | charsToErase = 0; |
770 | } |
771 | |
772 | return charsToErase; |
773 | } |
774 | |
775 | } // end namespace astyle |
776 | |