1// Scintilla source code edit control
2/** @file LexDiff.cxx
3 ** Lexer for diff results.
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
8#include <stdlib.h>
9#include <string.h>
10#include <stdio.h>
11#include <stdarg.h>
12#include <assert.h>
13#include <ctype.h>
14
15#include <string>
16#include <string_view>
17
18#include "ILexer.h"
19#include "Scintilla.h"
20#include "SciLexer.h"
21
22#include "WordList.h"
23#include "LexAccessor.h"
24#include "Accessor.h"
25#include "StyleContext.h"
26#include "CharacterSet.h"
27#include "LexerModule.h"
28
29using namespace Lexilla;
30
31static inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
32 return (styler[i] == '\n') ||
33 ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
34}
35
36#define DIFF_BUFFER_START_SIZE 16
37// Note that ColouriseDiffLine analyzes only the first DIFF_BUFFER_START_SIZE
38// characters of each line to classify the line.
39
40static void ColouriseDiffLine(char *lineBuffer, Sci_Position endLine, Accessor &styler) {
41 // It is needed to remember the current state to recognize starting
42 // comment lines before the first "diff " or "--- ". If a real
43 // difference starts then each line starting with ' ' is a whitespace
44 // otherwise it is considered a comment (Only in..., Binary file...)
45 if (0 == strncmp(lineBuffer, "diff ", 5)) {
46 styler.ColourTo(endLine, SCE_DIFF_COMMAND);
47 } else if (0 == strncmp(lineBuffer, "Index: ", 7)) { // For subversion's diff
48 styler.ColourTo(endLine, SCE_DIFF_COMMAND);
49 } else if (0 == strncmp(lineBuffer, "---", 3) && lineBuffer[3] != '-') {
50 // In a context diff, --- appears in both the header and the position markers
51 if (lineBuffer[3] == ' ' && atoi(lineBuffer + 4) && !strchr(lineBuffer, '/'))
52 styler.ColourTo(endLine, SCE_DIFF_POSITION);
53 else if (lineBuffer[3] == '\r' || lineBuffer[3] == '\n')
54 styler.ColourTo(endLine, SCE_DIFF_POSITION);
55 else if (lineBuffer[3] == ' ')
56 styler.ColourTo(endLine, SCE_DIFF_HEADER);
57 else
58 styler.ColourTo(endLine, SCE_DIFF_DELETED);
59 } else if (0 == strncmp(lineBuffer, "+++ ", 4)) {
60 // I don't know of any diff where "+++ " is a position marker, but for
61 // consistency, do the same as with "--- " and "*** ".
62 if (atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
63 styler.ColourTo(endLine, SCE_DIFF_POSITION);
64 else
65 styler.ColourTo(endLine, SCE_DIFF_HEADER);
66 } else if (0 == strncmp(lineBuffer, "====", 4)) { // For p4's diff
67 styler.ColourTo(endLine, SCE_DIFF_HEADER);
68 } else if (0 == strncmp(lineBuffer, "***", 3)) {
69 // In a context diff, *** appears in both the header and the position markers.
70 // Also ******** is a chunk header, but here it's treated as part of the
71 // position marker since there is no separate style for a chunk header.
72 if (lineBuffer[3] == ' ' && atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
73 styler.ColourTo(endLine, SCE_DIFF_POSITION);
74 else if (lineBuffer[3] == '*')
75 styler.ColourTo(endLine, SCE_DIFF_POSITION);
76 else
77 styler.ColourTo(endLine, SCE_DIFF_HEADER);
78 } else if (0 == strncmp(lineBuffer, "? ", 2)) { // For difflib
79 styler.ColourTo(endLine, SCE_DIFF_HEADER);
80 } else if (lineBuffer[0] == '@') {
81 styler.ColourTo(endLine, SCE_DIFF_POSITION);
82 } else if (lineBuffer[0] >= '0' && lineBuffer[0] <= '9') {
83 styler.ColourTo(endLine, SCE_DIFF_POSITION);
84 } else if (0 == strncmp(lineBuffer, "++", 2)) {
85 styler.ColourTo(endLine, SCE_DIFF_PATCH_ADD);
86 } else if (0 == strncmp(lineBuffer, "+-", 2)) {
87 styler.ColourTo(endLine, SCE_DIFF_PATCH_DELETE);
88 } else if (0 == strncmp(lineBuffer, "-+", 2)) {
89 styler.ColourTo(endLine, SCE_DIFF_REMOVED_PATCH_ADD);
90 } else if (0 == strncmp(lineBuffer, "--", 2)) {
91 styler.ColourTo(endLine, SCE_DIFF_REMOVED_PATCH_DELETE);
92 } else if (lineBuffer[0] == '-' || lineBuffer[0] == '<') {
93 styler.ColourTo(endLine, SCE_DIFF_DELETED);
94 } else if (lineBuffer[0] == '+' || lineBuffer[0] == '>') {
95 styler.ColourTo(endLine, SCE_DIFF_ADDED);
96 } else if (lineBuffer[0] == '!') {
97 styler.ColourTo(endLine, SCE_DIFF_CHANGED);
98 } else if (lineBuffer[0] != ' ') {
99 styler.ColourTo(endLine, SCE_DIFF_COMMENT);
100 } else {
101 styler.ColourTo(endLine, SCE_DIFF_DEFAULT);
102 }
103}
104
105static void ColouriseDiffDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
106 char lineBuffer[DIFF_BUFFER_START_SIZE] = "";
107 styler.StartAt(startPos);
108 styler.StartSegment(startPos);
109 Sci_PositionU linePos = 0;
110 for (Sci_PositionU i = startPos; i < startPos + length; i++) {
111 if (AtEOL(styler, i)) {
112 if (linePos < DIFF_BUFFER_START_SIZE) {
113 lineBuffer[linePos] = 0;
114 }
115 ColouriseDiffLine(lineBuffer, i, styler);
116 linePos = 0;
117 } else if (linePos < DIFF_BUFFER_START_SIZE - 1) {
118 lineBuffer[linePos++] = styler[i];
119 } else if (linePos == DIFF_BUFFER_START_SIZE - 1) {
120 lineBuffer[linePos++] = 0;
121 }
122 }
123 if (linePos > 0) { // Last line does not have ending characters
124 if (linePos < DIFF_BUFFER_START_SIZE) {
125 lineBuffer[linePos] = 0;
126 }
127 ColouriseDiffLine(lineBuffer, startPos + length - 1, styler);
128 }
129}
130
131static void FoldDiffDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
132 Sci_Position curLine = styler.GetLine(startPos);
133 Sci_Position curLineStart = styler.LineStart(curLine);
134 int prevLevel = curLine > 0 ? styler.LevelAt(curLine - 1) : SC_FOLDLEVELBASE;
135 int nextLevel;
136
137 do {
138 const int lineType = styler.StyleAt(curLineStart);
139 if (lineType == SCE_DIFF_COMMAND)
140 nextLevel = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
141 else if (lineType == SCE_DIFF_HEADER)
142 nextLevel = (SC_FOLDLEVELBASE + 1) | SC_FOLDLEVELHEADERFLAG;
143 else if (lineType == SCE_DIFF_POSITION && styler[curLineStart] != '-')
144 nextLevel = (SC_FOLDLEVELBASE + 2) | SC_FOLDLEVELHEADERFLAG;
145 else if (prevLevel & SC_FOLDLEVELHEADERFLAG)
146 nextLevel = (prevLevel & SC_FOLDLEVELNUMBERMASK) + 1;
147 else
148 nextLevel = prevLevel;
149
150 if ((nextLevel & SC_FOLDLEVELHEADERFLAG) && (nextLevel == prevLevel))
151 styler.SetLevel(curLine-1, prevLevel & ~SC_FOLDLEVELHEADERFLAG);
152
153 styler.SetLevel(curLine, nextLevel);
154 prevLevel = nextLevel;
155
156 curLineStart = styler.LineStart(++curLine);
157 } while (static_cast<Sci_Position>(startPos)+length > curLineStart);
158}
159
160static const char *const emptyWordListDesc[] = {
161 0
162};
163
164LexerModule lmDiff(SCLEX_DIFF, ColouriseDiffDoc, "diff", FoldDiffDoc, emptyWordListDesc);
165