PageRenderTime 63ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/scintilla/lexers/LexErrorList.cxx

https://gitlab.com/cjeight/tortoisegit
C++ | 394 lines | 323 code | 22 blank | 49 comment | 225 complexity | 9159eeaef89054bf3cccec90d34cab0c MD5 | raw file
  1. // Scintilla source code edit control
  2. /** @file LexErrorList.cxx
  3. ** Lexer for error lists. Used for the output pane in SciTE.
  4. **/
  5. // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <stdarg.h>
  11. #include <assert.h>
  12. #include <ctype.h>
  13. #include "ILexer.h"
  14. #include "Scintilla.h"
  15. #include "SciLexer.h"
  16. #include "WordList.h"
  17. #include "LexAccessor.h"
  18. #include "Accessor.h"
  19. #include "StyleContext.h"
  20. #include "CharacterSet.h"
  21. #include "LexerModule.h"
  22. #ifdef SCI_NAMESPACE
  23. using namespace Scintilla;
  24. #endif
  25. static bool strstart(const char *haystack, const char *needle) {
  26. return strncmp(haystack, needle, strlen(needle)) == 0;
  27. }
  28. static bool Is0To9(char ch) {
  29. return (ch >= '0') && (ch <= '9');
  30. }
  31. static bool Is1To9(char ch) {
  32. return (ch >= '1') && (ch <= '9');
  33. }
  34. static bool IsAlphabetic(int ch) {
  35. return IsASCII(ch) && isalpha(ch);
  36. }
  37. static inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
  38. return (styler[i] == '\n') ||
  39. ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
  40. }
  41. static int RecogniseErrorListLine(const char *lineBuffer, Sci_PositionU lengthLine, Sci_Position &startValue) {
  42. if (lineBuffer[0] == '>') {
  43. // Command or return status
  44. return SCE_ERR_CMD;
  45. } else if (lineBuffer[0] == '<') {
  46. // Diff removal.
  47. return SCE_ERR_DIFF_DELETION;
  48. } else if (lineBuffer[0] == '!') {
  49. return SCE_ERR_DIFF_CHANGED;
  50. } else if (lineBuffer[0] == '+') {
  51. if (strstart(lineBuffer, "+++ ")) {
  52. return SCE_ERR_DIFF_MESSAGE;
  53. } else {
  54. return SCE_ERR_DIFF_ADDITION;
  55. }
  56. } else if (lineBuffer[0] == '-') {
  57. if (strstart(lineBuffer, "--- ")) {
  58. return SCE_ERR_DIFF_MESSAGE;
  59. } else {
  60. return SCE_ERR_DIFF_DELETION;
  61. }
  62. } else if (strstart(lineBuffer, "cf90-")) {
  63. // Absoft Pro Fortran 90/95 v8.2 error and/or warning message
  64. return SCE_ERR_ABSF;
  65. } else if (strstart(lineBuffer, "fortcom:")) {
  66. // Intel Fortran Compiler v8.0 error/warning message
  67. return SCE_ERR_IFORT;
  68. } else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) {
  69. return SCE_ERR_PYTHON;
  70. } else if (strstr(lineBuffer, " in ") && strstr(lineBuffer, " on line ")) {
  71. return SCE_ERR_PHP;
  72. } else if ((strstart(lineBuffer, "Error ") ||
  73. strstart(lineBuffer, "Warning ")) &&
  74. strstr(lineBuffer, " at (") &&
  75. strstr(lineBuffer, ") : ") &&
  76. (strstr(lineBuffer, " at (") < strstr(lineBuffer, ") : "))) {
  77. // Intel Fortran Compiler error/warning message
  78. return SCE_ERR_IFC;
  79. } else if (strstart(lineBuffer, "Error ")) {
  80. // Borland error message
  81. return SCE_ERR_BORLAND;
  82. } else if (strstart(lineBuffer, "Warning ")) {
  83. // Borland warning message
  84. return SCE_ERR_BORLAND;
  85. } else if (strstr(lineBuffer, "at line ") &&
  86. (strstr(lineBuffer, "at line ") < (lineBuffer + lengthLine)) &&
  87. strstr(lineBuffer, "file ") &&
  88. (strstr(lineBuffer, "file ") < (lineBuffer + lengthLine))) {
  89. // Lua 4 error message
  90. return SCE_ERR_LUA;
  91. } else if (strstr(lineBuffer, " at ") &&
  92. (strstr(lineBuffer, " at ") < (lineBuffer + lengthLine)) &&
  93. strstr(lineBuffer, " line ") &&
  94. (strstr(lineBuffer, " line ") < (lineBuffer + lengthLine)) &&
  95. (strstr(lineBuffer, " at ") + 4 < (strstr(lineBuffer, " line ")))) {
  96. // perl error message:
  97. // <message> at <file> line <line>
  98. return SCE_ERR_PERL;
  99. } else if ((memcmp(lineBuffer, " at ", 6) == 0) &&
  100. strstr(lineBuffer, ":line ")) {
  101. // A .NET traceback
  102. return SCE_ERR_NET;
  103. } else if (strstart(lineBuffer, "Line ") &&
  104. strstr(lineBuffer, ", file ")) {
  105. // Essential Lahey Fortran error message
  106. return SCE_ERR_ELF;
  107. } else if (strstart(lineBuffer, "line ") &&
  108. strstr(lineBuffer, " column ")) {
  109. // HTML tidy style: line 42 column 1
  110. return SCE_ERR_TIDY;
  111. } else if (strstart(lineBuffer, "\tat ") &&
  112. strstr(lineBuffer, "(") &&
  113. strstr(lineBuffer, ".java:")) {
  114. // Java stack back trace
  115. return SCE_ERR_JAVA_STACK;
  116. } else if (strstart(lineBuffer, "In file included from ") ||
  117. strstart(lineBuffer, " from ")) {
  118. // GCC showing include path to following error
  119. return SCE_ERR_GCC_INCLUDED_FROM;
  120. } else if (strstr(lineBuffer, "warning LNK")) {
  121. // Microsoft linker warning:
  122. // {<object> : } warning LNK9999
  123. return SCE_ERR_MS;
  124. } else {
  125. // Look for one of the following formats:
  126. // GCC: <filename>:<line>:<message>
  127. // Microsoft: <filename>(<line>) :<message>
  128. // Common: <filename>(<line>): warning|error|note|remark|catastrophic|fatal
  129. // Common: <filename>(<line>) warning|error|note|remark|catastrophic|fatal
  130. // Microsoft: <filename>(<line>,<column>)<message>
  131. // CTags: <identifier>\t<filename>\t<message>
  132. // Lua 5 traceback: \t<filename>:<line>:<message>
  133. // Lua 5.1: <exe>: <filename>:<line>:<message>
  134. bool initialTab = (lineBuffer[0] == '\t');
  135. bool initialColonPart = false;
  136. bool canBeCtags = !initialTab; // For ctags must have an identifier with no spaces then a tab
  137. enum { stInitial,
  138. stGccStart, stGccDigit, stGccColumn, stGcc,
  139. stMsStart, stMsDigit, stMsBracket, stMsVc, stMsDigitComma, stMsDotNet,
  140. stCtagsStart, stCtagsFile, stCtagsStartString, stCtagsStringDollar, stCtags,
  141. stUnrecognized
  142. } state = stInitial;
  143. for (Sci_PositionU i = 0; i < lengthLine; i++) {
  144. char ch = lineBuffer[i];
  145. char chNext = ' ';
  146. if ((i + 1) < lengthLine)
  147. chNext = lineBuffer[i + 1];
  148. if (state == stInitial) {
  149. if (ch == ':') {
  150. // May be GCC, or might be Lua 5 (Lua traceback same but with tab prefix)
  151. if ((chNext != '\\') && (chNext != '/') && (chNext != ' ')) {
  152. // This check is not completely accurate as may be on
  153. // GTK+ with a file name that includes ':'.
  154. state = stGccStart;
  155. } else if (chNext == ' ') { // indicates a Lua 5.1 error message
  156. initialColonPart = true;
  157. }
  158. } else if ((ch == '(') && Is1To9(chNext) && (!initialTab)) {
  159. // May be Microsoft
  160. // Check against '0' often removes phone numbers
  161. state = stMsStart;
  162. } else if ((ch == '\t') && canBeCtags) {
  163. // May be CTags
  164. state = stCtagsStart;
  165. } else if (ch == ' ') {
  166. canBeCtags = false;
  167. }
  168. } else if (state == stGccStart) { // <filename>:
  169. state = Is0To9(ch) ? stGccDigit : stUnrecognized;
  170. } else if (state == stGccDigit) { // <filename>:<line>
  171. if (ch == ':') {
  172. state = stGccColumn; // :9.*: is GCC
  173. startValue = i + 1;
  174. } else if (!Is0To9(ch)) {
  175. state = stUnrecognized;
  176. }
  177. } else if (state == stGccColumn) { // <filename>:<line>:<column>
  178. if (!Is0To9(ch)) {
  179. state = stGcc;
  180. if (ch == ':')
  181. startValue = i + 1;
  182. break;
  183. }
  184. } else if (state == stMsStart) { // <filename>(
  185. state = Is0To9(ch) ? stMsDigit : stUnrecognized;
  186. } else if (state == stMsDigit) { // <filename>(<line>
  187. if (ch == ',') {
  188. state = stMsDigitComma;
  189. } else if (ch == ')') {
  190. state = stMsBracket;
  191. } else if ((ch != ' ') && !Is0To9(ch)) {
  192. state = stUnrecognized;
  193. }
  194. } else if (state == stMsBracket) { // <filename>(<line>)
  195. if ((ch == ' ') && (chNext == ':')) {
  196. state = stMsVc;
  197. } else if ((ch == ':' && chNext == ' ') || (ch == ' ')) {
  198. // Possibly Delphi.. don't test against chNext as it's one of the strings below.
  199. char word[512];
  200. Sci_PositionU j, chPos;
  201. unsigned numstep;
  202. chPos = 0;
  203. if (ch == ' ')
  204. numstep = 1; // ch was ' ', handle as if it's a delphi errorline, only add 1 to i.
  205. else
  206. numstep = 2; // otherwise add 2.
  207. for (j = i + numstep; j < lengthLine && IsAlphabetic(lineBuffer[j]) && chPos < sizeof(word) - 1; j++)
  208. word[chPos++] = lineBuffer[j];
  209. word[chPos] = 0;
  210. if (!CompareCaseInsensitive(word, "error") || !CompareCaseInsensitive(word, "warning") ||
  211. !CompareCaseInsensitive(word, "fatal") || !CompareCaseInsensitive(word, "catastrophic") ||
  212. !CompareCaseInsensitive(word, "note") || !CompareCaseInsensitive(word, "remark")) {
  213. state = stMsVc;
  214. } else {
  215. state = stUnrecognized;
  216. }
  217. } else {
  218. state = stUnrecognized;
  219. }
  220. } else if (state == stMsDigitComma) { // <filename>(<line>,
  221. if (ch == ')') {
  222. state = stMsDotNet;
  223. break;
  224. } else if ((ch != ' ') && !Is0To9(ch)) {
  225. state = stUnrecognized;
  226. }
  227. } else if (state == stCtagsStart) {
  228. if (ch == '\t') {
  229. state = stCtagsFile;
  230. }
  231. } else if (state == stCtagsFile) {
  232. if ((lineBuffer[i - 1] == '\t') &&
  233. ((ch == '/' && chNext == '^') || Is0To9(ch))) {
  234. state = stCtags;
  235. break;
  236. } else if ((ch == '/') && (chNext == '^')) {
  237. state = stCtagsStartString;
  238. }
  239. } else if ((state == stCtagsStartString) && ((lineBuffer[i] == '$') && (lineBuffer[i + 1] == '/'))) {
  240. state = stCtagsStringDollar;
  241. break;
  242. }
  243. }
  244. if (state == stGcc) {
  245. return initialColonPart ? SCE_ERR_LUA : SCE_ERR_GCC;
  246. } else if ((state == stMsVc) || (state == stMsDotNet)) {
  247. return SCE_ERR_MS;
  248. } else if ((state == stCtagsStringDollar) || (state == stCtags)) {
  249. return SCE_ERR_CTAG;
  250. } else if (initialColonPart && strstr(lineBuffer, ": warning C")) {
  251. // Microsoft warning without line number
  252. // <filename>: warning C9999
  253. return SCE_ERR_MS;
  254. } else {
  255. return SCE_ERR_DEFAULT;
  256. }
  257. }
  258. }
  259. #define CSI "\033["
  260. namespace {
  261. bool SequenceEnd(int ch) {
  262. return (ch == 0) || ((ch >= '@') && (ch <= '~'));
  263. }
  264. int StyleFromSequence(const char *seq) {
  265. int bold = 0;
  266. int colour = 0;
  267. while (!SequenceEnd(*seq)) {
  268. if (Is0To9(*seq)) {
  269. int base = *seq - '0';
  270. if (Is0To9(seq[1])) {
  271. base = base * 10;
  272. base += seq[1] - '0';
  273. seq++;
  274. }
  275. if (base == 0) {
  276. colour = 0;
  277. bold = 0;
  278. }
  279. else if (base == 1) {
  280. bold = 1;
  281. }
  282. else if (base >= 30 && base <= 37) {
  283. colour = base - 30;
  284. }
  285. }
  286. seq++;
  287. }
  288. return SCE_ERR_ES_BLACK + bold * 8 + colour;
  289. }
  290. }
  291. static void ColouriseErrorListLine(
  292. char *lineBuffer,
  293. Sci_PositionU lengthLine,
  294. Sci_PositionU endPos,
  295. Accessor &styler,
  296. bool valueSeparate,
  297. bool escapeSequences) {
  298. Sci_Position startValue = -1;
  299. int style = RecogniseErrorListLine(lineBuffer, lengthLine, startValue);
  300. if (escapeSequences && strstr(lineBuffer, CSI)) {
  301. const int startPos = endPos - lengthLine;
  302. const char *linePortion = lineBuffer;
  303. int startPortion = startPos;
  304. int portionStyle = style;
  305. while (const char *startSeq = strstr(linePortion, CSI)) {
  306. if (startSeq > linePortion) {
  307. styler.ColourTo(startPortion + static_cast<int>(startSeq - linePortion), portionStyle);
  308. }
  309. const char *endSeq = startSeq + 2;
  310. while (!SequenceEnd(*endSeq))
  311. endSeq++;
  312. const int endSeqPosition = startPortion + static_cast<int>(endSeq - linePortion) + 1;
  313. switch (*endSeq) {
  314. case 0:
  315. styler.ColourTo(endPos, SCE_ERR_ESCSEQ_UNKNOWN);
  316. return;
  317. case 'm': // Colour command
  318. styler.ColourTo(endSeqPosition, SCE_ERR_ESCSEQ);
  319. portionStyle = StyleFromSequence(startSeq+2);
  320. break;
  321. case 'K': // Erase to end of line -> ignore
  322. styler.ColourTo(endSeqPosition, SCE_ERR_ESCSEQ);
  323. break;
  324. default:
  325. styler.ColourTo(endSeqPosition, SCE_ERR_ESCSEQ_UNKNOWN);
  326. portionStyle = style;
  327. }
  328. startPortion = endSeqPosition;
  329. linePortion = endSeq + 1;
  330. }
  331. styler.ColourTo(endPos, portionStyle);
  332. } else {
  333. if (valueSeparate && (startValue >= 0)) {
  334. styler.ColourTo(endPos - (lengthLine - startValue), style);
  335. styler.ColourTo(endPos, SCE_ERR_VALUE);
  336. } else {
  337. styler.ColourTo(endPos, style);
  338. }
  339. }
  340. }
  341. static void ColouriseErrorListDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
  342. char lineBuffer[10000];
  343. styler.StartAt(startPos);
  344. styler.StartSegment(startPos);
  345. Sci_PositionU linePos = 0;
  346. // property lexer.errorlist.value.separate
  347. // For lines in the output pane that are matches from Find in Files or GCC-style
  348. // diagnostics, style the path and line number separately from the rest of the
  349. // line with style 21 used for the rest of the line.
  350. // This allows matched text to be more easily distinguished from its location.
  351. bool valueSeparate = styler.GetPropertyInt("lexer.errorlist.value.separate", 0) != 0;
  352. // property lexer.errorlist.escape.sequences
  353. // Set to 1 to interpret escape sequences.
  354. const bool escapeSequences = styler.GetPropertyInt("lexer.errorlist.escape.sequences") != 0;
  355. for (Sci_PositionU i = startPos; i < startPos + length; i++) {
  356. lineBuffer[linePos++] = styler[i];
  357. if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
  358. // End of line (or of line buffer) met, colourise it
  359. lineBuffer[linePos] = '\0';
  360. ColouriseErrorListLine(lineBuffer, linePos, i, styler, valueSeparate, escapeSequences);
  361. linePos = 0;
  362. }
  363. }
  364. if (linePos > 0) { // Last line does not have ending characters
  365. lineBuffer[linePos] = '\0';
  366. ColouriseErrorListLine(lineBuffer, linePos, startPos + length - 1, styler, valueSeparate, escapeSequences);
  367. }
  368. }
  369. static const char *const emptyWordListDesc[] = {
  370. 0
  371. };
  372. LexerModule lmErrorList(SCLEX_ERRORLIST, ColouriseErrorListDoc, "errorlist", 0, emptyWordListDesc);