1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016, 2019-2021 - TortoiseGit
4 // Copyright (C) 2007, 2009-2013 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "DiffLinesForStaging.h"
24 CDiffLinesForStaging::CDiffLinesForStaging(const char* text
, int numLines
, int firstLineSelected
, int lastLineSelected
)
26 const char* ptr
= text
;
27 size_t last_i
= 0; // beginning of the last line processed
29 int oldCount
= 0, newCount
= 0;
30 bool fileWasAdded
= false, fileWasDeleted
= false;
31 DiffLineTypes lastType
= DiffLineTypes::DEFAULT
; // type of the last line processed, necessary to handle "No newline at end of file" correctly
38 if (ptr
[i
] == '\r' && ptr
[i
+ 1] == '\n' ||
39 ptr
[i
] == '\n' && ptr
[i
+ 1] == '\r')
41 else if (ptr
[i
] == '\r' || ptr
[i
] == '\n')
49 size_t linebuflen
= i
- last_i
+ eol_len
+ 1;
50 auto line
= std::string_view(text
+ last_i
, linebuflen
- 1);
54 DiffLineTypes type
= DiffLineTypes::DEFAULT
;
55 bool exitLoop
= false;
56 // This algorithm is based off CPatch::ParsePatchFile
63 if (strncmp(line
.data(), "diff ", 5) == 0)
65 type
= DiffLineTypes::COMMAND
;
70 if (strncmp(line
.data(), "--- ", 4) == 0)
72 type
= DiffLineTypes::HEADER
;
76 type
= DiffLineTypes::COMMENT
;
79 if (strncmp(line
.data(), "+++ ", 4) == 0)
81 type
= DiffLineTypes::HEADER
;
86 if (strncmp(line
.data(), "@@ ", 3) == 0)
88 if (GetOldAndNewLinesCountFromHunk(line
, &oldCount
, &newCount
, true))
90 type
= DiffLineTypes::POSITION
;
91 fileWasAdded
= oldCount
== 0;
92 fileWasDeleted
= newCount
== 0;
98 if (strncmp(line
.data(), "+", 1) == 0)
101 type
= DiffLineTypes::ADDED
;
103 else if (strncmp(line
.data(), "-", 1) == 0)
106 type
= DiffLineTypes::DELETED
;
108 else if (strncmp(line
.data(), " ", 1) == 0)
112 type
= DiffLineTypes::DEFAULT
;
114 // Regardless of locales, a "\ No newline at end of file" will always begin with "\ " and 10 is a sane minimum length to look for
115 else if (linebuflen
- 1 >= 10 && strncmp(line
.data(), "\\ ", 2) == 0)
118 type
= DiffLineTypes::NO_NEWLINE_NEWFILE
;
119 else if (fileWasDeleted
)
120 type
= DiffLineTypes::NO_NEWLINE_OLDFILE
;
121 else if (oldCount
== 0 && newCount
> 0)
122 type
= DiffLineTypes::NO_NEWLINE_OLDFILE
;
123 else if (newCount
== 0 && oldCount
> 0)
124 type
= DiffLineTypes::NO_NEWLINE_NEWFILE
;
125 else if (oldCount
== 0 && newCount
== 0)
127 if (lastType
== DiffLineTypes::ADDED
)
128 type
= DiffLineTypes::NO_NEWLINE_NEWFILE
;
129 else if (lastType
== DiffLineTypes::DELETED
)
130 type
= DiffLineTypes::NO_NEWLINE_OLDFILE
;
131 else if (lastType
== DiffLineTypes::DEFAULT
)
132 type
= DiffLineTypes::NO_NEWLINE_BOTHFILES
;
135 else if (strncmp(line
.data(), "@@ ", 3) == 0)
146 } // while (!exitLoop)
148 m_linevec
.emplace_back(line
, type
);
149 } // for (int i = 0; ;)
150 m_linevec
.emplace_back("", DiffLineTypes::DEFAULT
); // Scintilla considers an empty document to have 1 line, so add an extra line here
151 VERIFY(m_linevec
.size() == static_cast<size_t>(numLines
));
152 m_firstLineSelected
= firstLineSelected
;
153 m_lastLineSelected
= lastLineSelected
;
156 int CDiffLinesForStaging::GetFirstLineNumberSelected() const
158 return m_firstLineSelected
;
161 int CDiffLinesForStaging::GetLastLineNumberSelected() const
163 return m_lastLineSelected
;
166 // Includes EOL characters of all lines
167 std::string
CDiffLinesForStaging::GetFullTextOfSelectedLines() const
169 return GetFullTextOfLineRange(GetFirstLineNumberSelected(), GetLastLineNumberSelected());
172 // Includes EOL characters of all lines
173 std::string
CDiffLinesForStaging::GetFullTextOfLineRange(int startline
, int endline
) const
175 if (endline
< startline
)
178 for (int i
= startline
; i
<= endline
; ++i
)
179 ret
.append(m_linevec
.at(i
).sLine
);
183 DiffLineTypes
CDiffLinesForStaging::GetLineType(int line
) const
185 return m_linevec
.at(line
).type
;
188 bool CDiffLinesForStaging::IsNoNewlineComment(int line
) const
190 auto type
= m_linevec
.at(line
).type
;
191 return type
== DiffLineTypes::NO_NEWLINE_OLDFILE
|| type
== DiffLineTypes::NO_NEWLINE_NEWFILE
|| type
== DiffLineTypes::NO_NEWLINE_BOTHFILES
;
194 // Includes EOL characters
195 std::string_view
CDiffLinesForStaging::GetFullLineByLineNumber(int line
) const
197 return m_linevec
.at(line
).sLine
;
200 int CDiffLinesForStaging::GetLastDocumentLine() const
202 return static_cast<int>(m_linevec
.size() - 1);
205 // Takes a buffer containing the first line of a hunk (@@xxxxxx@@)
206 // Parses it to extract its old lines count and new lines count and passes them back to the given oldCount and newCount.
207 // Returns true if the line matches the expected format, false otherwise (what should never happen).
208 // If allowSingleLine is false, returns false for hunks missing the start line number in one or both sides (e.g. @@ -x +y,z @@)
209 bool CDiffLinesForStaging::GetOldAndNewLinesCountFromHunk(std::string_view hunk
, int* oldCount
, int* newCount
, bool allowSingleLine
)
211 std::string pattern
= allowSingleLine
? "^@@ -(?:\\d+?,)?(\\d+?) \\+(?:\\d+?,)?(\\d+?) @@" : "^@@ -\\d+?,(\\d+?) \\+\\d+?,(\\d+?) @@";
212 std::regex
rx(pattern
, std::regex_constants::ECMAScript
);
213 std::match_results
<std::string_view::const_iterator
> match
;
215 if (!std::regex_search(hunk
.begin(), hunk
.end(), match
, rx
) || match
.size() != 3) // this should never happen
217 *oldCount
= StrToIntA(match
[1].str().c_str());
218 *newCount
= StrToIntA(match
[2].str().c_str());