OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / Src / MergeDocLineDiffs.cpp
1 /** 
2  * @file  MergeDocLineDiffs.cpp
3  *
4  * @brief Implementation file for word diff highlighting (F4) for merge edit & detail views
5  *
6  */
7 // RCS ID line follows -- this is updated by CVS
8 // $Id: MergeDocLineDiffs.cpp 7067 2009-12-29 14:22:46Z kimmov $
9
10 #include "StdAfx.h"
11 #include "MergeDoc.h"
12 #include <vector>
13 #include <memory>
14 #include "Merge.h"
15 #include "MergeEditView.h"
16 #include "DiffTextBuffer.h"
17 #include "stringdiffs.h"
18 #include "UnicodeString.h"
19
20 #ifdef _DEBUG
21 #define new DEBUG_NEW
22 #undef THIS_FILE
23 static char THIS_FILE[] = __FILE__;
24 #endif
25
26 using std::vector;
27
28 /**
29  * @brief Display the line/word difference highlight in edit view
30  */
31 static void
32 HighlightDiffRect(CMergeEditView * pView, const CRect & rc)
33 {
34         if (rc.top == -1)
35         {
36                 // Should we remove existing selection ?
37         }
38         else
39         {
40                 // select the area
41                 // with anchor at left and caret at right
42                 // this seems to be conventional behavior in Windows editors
43                 pView->SelectArea(rc.TopLeft(), rc.BottomRight());
44                 pView->SetCursorPos(rc.BottomRight());
45                 pView->SetNewAnchor(rc.TopLeft());
46                 // try to ensure that selected area is visible
47                 pView->EnsureVisible(rc.TopLeft(), rc.BottomRight());
48         }
49 }
50
51 /**
52  * @brief Highlight difference in current line (left & right panes)
53  */
54 void CMergeDoc::Showlinediff(CMergeEditView *pView, bool bReversed)
55 {
56         CRect rc[3];
57         int pane;
58
59         Computelinediff(pView, rc, bReversed);
60
61         IF_IS_TRUE_ALL ((rc[pane].top == -1), pane, m_nBuffers)
62         {
63                 String caption = _("Line difference");
64                 String msg = _("No difference");
65                 MessageBox(pView->GetSafeHwnd(), msg.c_str(), caption.c_str(), MB_OK);
66                 return;
67         }
68
69         // Actually display selection areas on screen in both edit panels
70         for (pane = 0; pane < m_nBuffers; pane++)
71                 HighlightDiffRect(m_pView[pane], rc[pane]);
72 }
73
74
75 /**
76  * @brief Returns rectangles to highlight in both views (to show differences in line specified)
77  */
78 void CMergeDoc::Computelinediff(CMergeEditView *pView, CRect rc[], bool bReversed)
79 {
80         int file;
81         for (file = 0; file < m_nBuffers; file++)
82                 rc[file].top = -1;
83
84         CPoint ptStart, ptEnd;
85         pView->GetSelection(ptStart, ptEnd);
86
87         vector<WordDiff> worddiffs;
88         GetWordDiffArray(ptStart.y, &worddiffs);
89
90         if (worddiffs.empty())
91                 return;
92
93         int nActivePane = pView->m_nThisPane;
94
95         std::vector<WordDiff>::iterator it;
96         for (it = worddiffs.begin(); it != worddiffs.end(); ++it)
97         {
98                 if ((*it).beginline[nActivePane] <= ptStart.y && ptStart.y <= (*it).endline[nActivePane])
99                 {
100                         int begin = ((*it).beginline[nActivePane] < ptStart.y) ? 0 : (*it).begin[nActivePane];
101                         int end   = ((*it).endline[nActivePane]   > ptStart.y) ? m_ptBuf[nActivePane]->GetLineLength(ptStart.y) : (*it).end[nActivePane];
102                         if (begin <= ptStart.x && ptStart.x <= end)
103                                 break;
104                 }
105         }
106
107         if (!bReversed)
108         {
109                 if (it != worddiffs.end())
110                         ++it;
111         
112                 if (it == worddiffs.end())
113                         it = worddiffs.begin();
114         }
115         else
116         {
117                 if (it == worddiffs.begin() || it == worddiffs.end())
118                         it = worddiffs.end() - 1;
119                 else
120                         --it;
121         }
122
123         for (file = 0; file < m_nBuffers; file++)
124         {
125                 rc[file].left = (*it).begin[file];
126                 rc[file].top = (*it).beginline[file];
127                 rc[file].right =(*it).end[file];
128                 rc[file].bottom = (*it).endline[file];
129         }
130 }
131
132 void CMergeDoc::ClearWordDiffCache(int nDiff/* = -1 */)
133 {
134         if (nDiff == -1)
135         {
136                 m_cacheWordDiffs.clear();
137         }
138         else
139         {
140                 std::map<int, std::vector<WordDiff> >::iterator it = m_cacheWordDiffs.find(nDiff);
141                 if (it != m_cacheWordDiffs.end())
142                         m_cacheWordDiffs.erase(it);
143         }
144 }
145
146 /**
147  * @brief Return array of differences in specified line
148  * This is used by algorithm for line diff coloring
149  * (Line diff coloring is distinct from the selection highlight code)
150  */
151 void CMergeDoc::GetWordDiffArray(int nLineIndex, vector<WordDiff> *pWordDiffs)
152 {
153         int file;
154         DIFFRANGE cd;
155         int nDiff = m_diffList.LineToDiff(nLineIndex);
156         if (nDiff == -1)
157                 return;
158         std::map<int, std::vector<WordDiff> >::iterator itmap = m_cacheWordDiffs.find(nDiff);
159         if (itmap != m_cacheWordDiffs.end())
160         {
161                 pWordDiffs->resize((*itmap).second.size());
162                 std::copy((*itmap).second.begin(), (*itmap).second.end(), pWordDiffs->begin());
163                 return;
164         }
165
166         m_diffList.GetDiff(nDiff, cd);
167
168         DIFFOPTIONS diffOptions = {0};
169         m_diffWrapper.GetOptions(&diffOptions);
170         String str[3];
171         std::unique_ptr<int[]> nOffsets[3];
172         const int LineLimit = 20;
173         bool diffPerLine = false;
174         
175         for (file = 0; file < m_nBuffers; file++)
176         {
177                 if (cd.dend[file] - cd.dbegin[file] > LineLimit)
178                 {
179                         diffPerLine = true;
180                         break;
181                 }
182         }
183
184         int nLineBegins[3], nLineEnds[3];
185         for (file = 0; file < m_nBuffers; file++)
186         {
187                 if (!diffPerLine)
188                 {
189                         nLineBegins[file] = cd.dbegin[file];
190                         nLineEnds[file] = cd.dend[file];
191                 }
192                 else
193                 {
194                         nLineBegins[file] = nLineEnds[file] = nLineIndex;
195                 }
196         }
197
198         for (file = 0; file < m_nBuffers; file++)
199         {
200                 int nLineBegin = nLineBegins[file], nLineEnd = nLineEnds[file];
201                 nOffsets[file].reset(new int[nLineEnd - nLineBegin + 1]);
202                 CString strText;
203                 if (nLineBegin != nLineEnd || m_ptBuf[file]->GetLineLength(nLineEnd) > 0)
204                         m_ptBuf[file]->GetTextWithoutEmptys(nLineBegin, 0, nLineEnd, m_ptBuf[file]->GetLineLength(nLineEnd), strText);
205                 strText += m_ptBuf[file]->GetLineEol(nLineEnd);
206                 str[file] = strText;
207
208                 nOffsets[file][0] = 0;
209                 for (int nLine = nLineBegin; nLine < nLineEnd; nLine++)
210                         nOffsets[file][nLine-nLineBegin+1] = nOffsets[file][nLine-nLineBegin] + m_ptBuf[file]->GetFullLineLength(nLine);
211         }
212
213         // Options that affect comparison
214         bool casitive = !diffOptions.bIgnoreCase;
215         int xwhite = diffOptions.nIgnoreWhitespace;
216         int breakType = GetBreakType(); // whitespace only or include punctuation
217         bool byteColoring = GetByteColoringOption();
218
219         std::vector<wdiff> worddiffs;
220         // Make the call to stringdiffs, which does all the hard & tedious computations
221         sd_ComputeWordDiffs(m_nBuffers, str, casitive, xwhite, breakType, byteColoring, &worddiffs);
222
223         int i;
224         std::vector<wdiff>::iterator it;
225         for (i = 0, it = worddiffs.begin(); it != worddiffs.end(); i++, it++)
226         {
227                 WordDiff wd;
228                 for (file = 0; file < m_nBuffers; file++)
229                 {
230                         int nLine;
231                         int nLineBegin = nLineBegins[file], nLineEnd = nLineEnds[file];
232                         for (nLine = nLineBegin; nLine < nLineEnd; nLine++)
233                         {
234                                 if (it->begin[file] == nOffsets[file][nLine-nLineBegin] || it->begin[file] < nOffsets[file][nLine-nLineBegin+1])
235                                         break;
236                         }
237                         wd.beginline[file] = nLine;
238                         wd.begin[file] = it->begin[file] - nOffsets[file][nLine-nLineBegin];
239                         if (m_ptBuf[file]->GetLineLength(nLine) < wd.begin[file])
240                                 wd.begin[file] = m_ptBuf[file]->GetLineLength(nLine);
241
242                         for (; nLine < nLineEnd; nLine++)
243                         {
244                                 if (it->end[file] + 1 == nOffsets[file][nLine-nLineBegin] || it->end[file] + 1 < nOffsets[file][nLine-nLineBegin+1])
245                                         break;
246                         }
247                         wd.endline[file] = nLine;
248                         wd.end[file] = it->end[file]  + 1 - nOffsets[file][nLine-nLineBegin];
249                         if (m_ptBuf[file]->GetLineLength(nLine) < wd.end[file])
250                                 wd.end[file] = m_ptBuf[file]->GetLineLength(nLine);
251                 }
252                 wd.op = it->op;
253
254                 pWordDiffs->push_back(wd);
255         }
256
257         if (!diffPerLine)
258         {
259                 m_cacheWordDiffs[nDiff].resize(pWordDiffs->size());
260                 std::copy(pWordDiffs->begin(), pWordDiffs->end(), m_cacheWordDiffs[nDiff].begin());
261         }
262
263         return;
264 }
265