* @param [in] firstDiff First diff copied (0-based index)
* @param [in] lastDiff Last diff copied (0-based index)
*/
-void CMergeDoc::CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff)
+void CMergeDoc::CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstWordDiff, int lastWordDiff)
{
#ifdef _DEBUG
if (firstDiff > lastDiff)
SetCurrentDiff(lastDiff);
bool bGroupWithPrevious = false;
- if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, true))
- return; // sync failure
+ if (firstWordDiff <= 0 && lastWordDiff == -1)
+ {
+ if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, true))
+ return; // sync failure
+ }
+ else
+ {
+ if (!WordListCopy(srcPane, dstPane, lastDiff, firstWordDiff, lastWordDiff, nullptr, bGroupWithPrevious, true))
+ return; // sync failure
+ }
SetEditedAfterRescan(dstPane);
}
// Group merge with previous (merge undo data to one action)
bGroupWithPrevious = true;
- if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, false))
- break; // sync failure
+ if (i > firstDiff || firstWordDiff <= 0)
+ {
+ if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, false))
+ break; // sync failure
+ }
+ else
+ {
+ if (!WordListCopy(srcPane, dstPane, firstDiff, firstWordDiff, -1, nullptr, bGroupWithPrevious, false))
+ break; // sync failure
+ }
}
}
return true;
}
+bool CMergeDoc::WordListCopy(int srcPane, int dstPane, int nDiff, int firstWordDiff, int lastWordDiff,
+ const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious /*= false*/, bool bUpdateView /*= true*/)
+{
+ int nGroup = GetActiveMergeView()->m_nThisGroup;
+ CMergeEditView *pViewSrc = m_pView[nGroup][srcPane];
+ CMergeEditView *pViewDst = m_pView[nGroup][dstPane];
+ CCrystalTextView *pSource = bUpdateView ? pViewDst : nullptr;
+
+ // suppress Rescan during this method
+ // (Not only do we not want to rescan a lot of times, but
+ // it will wreck the line status array to rescan as we merge)
+ RescanSuppress suppressRescan(*this);
+
+ DIFFRANGE cd;
+ VERIFY(m_diffList.GetDiff(nDiff, cd));
+ CDiffTextBuffer& sbuf = *m_ptBuf[srcPane];
+ CDiffTextBuffer& dbuf = *m_ptBuf[dstPane];
+ bool bSrcWasMod = sbuf.IsModified();
+ const int cd_dbegin = cd.dbegin;
+ const int cd_dend = cd.dend;
+ const int cd_blank = cd.blank[srcPane];
+ bool bInSync = SanityCheckDiff(cd);
+
+ if (!bInSync)
+ {
+ LangMessageBox(IDS_VIEWS_OUTOFSYNC, MB_ICONSTOP);
+ return false; // abort copying
+ }
+
+ std::vector<WordDiff> worddiffs = GetWordDiffArrayInDiffBlock(nDiff);
+
+ if (worddiffs.empty())
+ return false;
+
+ if (cd.end[srcPane] < cd.begin[srcPane])
+ return ListCopy(srcPane, dstPane, nDiff, bGroupWithPrevious, bUpdateView);
+
+ if (firstWordDiff == -1)
+ firstWordDiff = 0;
+ if (lastWordDiff == -1)
+ lastWordDiff = static_cast<int>(worddiffs.size() - 1);
+
+ // If we remove whole diff from current view, we must fix cursor
+ // position first. Normally we would move to end of previous line,
+ // but we want to move to begin of that line for usability.
+ if (bUpdateView)
+ {
+ CPoint currentPos = pViewDst->GetCursorPos();
+ currentPos.x = 0;
+ if (currentPos.y > cd_dend)
+ {
+ if (cd.blank[dstPane] >= 0)
+ currentPos.y -= cd_dend - cd.blank[dstPane] + 1;
+ else if (cd.blank[srcPane] >= 0)
+ currentPos.y -= cd_dend - cd.blank[srcPane] + 1;
+ }
+ ForEachView(dstPane, [currentPos](auto& pView) { pView->SetCursorPos(currentPos); });
+ }
+
+ // if the current diff contains missing lines, remove them from both sides
+ int limit = cd_dend;
+
+ // curView is the view which is changed, so the opposite of the source view
+ dbuf.BeginUndoGroup(bGroupWithPrevious);
+
+ CString srcText, dstText;
+ CPoint ptDstStart, ptDstEnd;
+ CPoint ptSrcStart, ptSrcEnd;
+
+ ptDstStart.x = worddiffs[firstWordDiff].begin[dstPane];
+ ptDstStart.y = worddiffs[firstWordDiff].beginline[dstPane];
+ ptDstEnd.x = worddiffs[lastWordDiff].end[dstPane];
+ ptDstEnd.y = worddiffs[lastWordDiff].endline[dstPane];
+ ptSrcStart.x = worddiffs[firstWordDiff].begin[srcPane];
+ ptSrcStart.y = worddiffs[firstWordDiff].beginline[srcPane];
+ ptSrcEnd.x = worddiffs[lastWordDiff].end[srcPane];
+ ptSrcEnd.y = worddiffs[lastWordDiff].endline[srcPane];
+
+ std::vector<int> nDstOffsets(ptDstEnd.y - ptDstStart.y + 2);
+ std::vector<int> nSrcOffsets(ptSrcEnd.y - ptSrcStart.y + 2);
+
+ dbuf.GetTextWithoutEmptys(ptDstStart.y, ptDstStart.x, ptDstEnd.y, ptDstEnd.x, dstText);
+ sbuf.GetTextWithoutEmptys(ptSrcStart.y, ptSrcStart.x, ptSrcEnd.y, ptSrcEnd.x, srcText);
+
+ nDstOffsets[0] = 0;
+ for (int nLine = ptDstStart.y; nLine <= ptDstEnd.y; nLine++)
+ nDstOffsets[nLine-ptDstStart.y+1] = nDstOffsets[nLine-ptDstStart.y] + dbuf.GetFullLineLength(nLine);
+ nSrcOffsets[0] = 0;
+ for (int nLine = ptSrcStart.y; nLine <= ptSrcEnd.y; nLine++)
+ nSrcOffsets[nLine-ptSrcStart.y+1] = nSrcOffsets[nLine-ptSrcStart.y] + sbuf.GetFullLineLength(nLine);
+
+ for (int i = lastWordDiff; i != firstWordDiff-1; --i)
+ {
+ if (pWordDiffIndice && std::find(pWordDiffIndice->begin(), pWordDiffIndice->end(), i) == pWordDiffIndice->end())
+ continue;
+ int srcBegin = nSrcOffsets[worddiffs[i].beginline[srcPane] - ptSrcStart.y] + worddiffs[i].begin[srcPane];
+ int srcEnd = nSrcOffsets[worddiffs[i].endline[srcPane] - ptSrcStart.y] + worddiffs[i].end[srcPane];
+ int dstBegin = nDstOffsets[worddiffs[i].beginline[dstPane] - ptDstStart.y] + worddiffs[i].begin[dstPane];
+ int dstEnd = nDstOffsets[worddiffs[i].endline[dstPane] - ptDstStart.y] + worddiffs[i].end[dstPane];
+ dstText = dstText.Mid(0, dstBegin - ptDstStart.x)
+ + srcText.Mid(srcBegin - ptSrcStart.x, srcEnd - srcBegin)
+ + dstText.Mid(dstEnd - ptDstStart.x);
+ }
+
+ dbuf.DeleteText(pSource, ptDstStart.y, ptDstStart.x, ptDstEnd.y, ptDstEnd.x, CE_ACTION_MERGE);
+
+ int endl,endc;
+ dbuf.InsertText(pSource, ptDstStart.y, ptDstStart.x, dstText, dstText.GetLength(), endl, endc, CE_ACTION_MERGE);
+
+ dbuf.FlushUndoGroup(pSource);
+
+ // reset the mod status of the source view because we do make some
+ // changes, but none that concern the source text
+ sbuf.SetModified(bSrcWasMod);
+
+ suppressRescan.Clear(); // done suppress Rescan
+ FlushAndRescan();
+
+ return true;
+}
+
/**
* @brief Save file with new filename.
*
void ShowRescanError(int nRescanResult, IDENTLEVEL identical);
bool Undo();
void CopyAllList(int srcPane, int dstPane);
- void CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff);
+ void CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstWordDiff = -1, int lastWordDiff = -1);
void DoAutoMerge(int dstPane);
bool SanityCheckDiff(DIFFRANGE dr) const;
+ bool WordListCopy(int srcPane, int dstPane, int nDiff, int nFirstWordDiff, int nLastWordDiff, const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious = false, bool bUpdateView = true);
bool ListCopy(int srcPane, int dstPane, int nDiff = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
bool TrySaveAs(String& strPath, int &nLastErrorCode, String & sError,
int nBuffer, PackingInfo * pInfoTempUnpacker);
public:
typedef enum { BYTEDIFF, WORDDIFF } DIFFLEVEL;
void Showlinediff(CMergeEditView *pView, bool bReversed = false);
+ std::vector<WordDiff> GetWordDiffArrayInDiffBlock(int nDiff);
std::vector<WordDiff> GetWordDiffArray(int nLineIndex);
void ClearWordDiffCache(int nDiff = -1);
private:
}
}
+std::vector<WordDiff> CMergeDoc::GetWordDiffArrayInDiffBlock(int nDiff)
+{
+ DIFFRANGE cd;
+ const int LineLimit = 20;
+ m_diffList.GetDiff(nDiff, cd);
+
+ bool diffPerLine = (cd.dend - cd.dbegin > LineLimit) ? true : false;
+ if (!diffPerLine)
+ return GetWordDiffArray(cd.dbegin);
+
+ std::vector<WordDiff> worddiffs;
+ for (int nLine = cd.dbegin; nLine <= cd.dend; ++nLine)
+ {
+ std::vector<WordDiff> worddiffsPerLine = GetWordDiffArray(nLine);
+ worddiffs.insert(worddiffs.end(), worddiffsPerLine.begin(), worddiffsPerLine.end());
+ }
+ return worddiffs;
+}
+
/**
* @brief Return array of differences in specified line
* This is used by algorithm for line diff coloring
* @brief Get diffs inside selection.
* @param [out] firstDiff First diff inside selection
* @param [out] lastDiff Last diff inside selection
+ * @param [out] firstWordDiff First word level diff inside selection
+ * @param [out] lastWordDiff Last word level diff inside selection
* @note -1 is returned in parameters if diffs cannot be determined
* @todo This shouldn't be called when there is no diffs, so replace
* first 'if' with ASSERT()?
*/
-void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
+void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart, const CPoint *pptEnd)
{
firstDiff = -1;
lastDiff = -1;
+ firstWordDiff = -1;
+ lastWordDiff = -1;
CMergeDoc *pd = GetDocument();
const int nDiffs = pd->m_diffList.GetSignificantDiffs();
return;
int firstLine, lastLine;
- GetFullySelectedLines(firstLine, lastLine);
- if (lastLine < firstLine)
+ CPoint ptStart, ptEnd;
+ GetSelection(ptStart, ptEnd);
+ if (pptStart != nullptr)
+ ptStart = *pptStart;
+ if (pptEnd != nullptr)
+ ptEnd = *pptEnd;
+ firstLine = ptStart.y;
+ lastLine = ptEnd.y;
+
+ firstDiff = pd->m_diffList.LineToDiff(firstLine);
+ if (firstDiff == -1)
+ {
+ firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
+ if (firstDiff == -1)
+ return;
+ firstWordDiff = 0;
+ }
+ lastDiff = pd->m_diffList.LineToDiff(lastLine);
+ if (lastDiff == -1)
+ lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
+ if (lastDiff < firstDiff)
+ {
+ firstDiff = -1;
+ firstWordDiff = -1;
return;
+ }
- firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
- lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
if (firstDiff != -1 && lastDiff != -1)
{
DIFFRANGE di;
- // Check that first selected line is first diff's first line or above it
- VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
- if ((int)di.dbegin < firstLine)
+ if (ptStart != ptEnd)
{
- if (firstDiff < lastDiff)
- ++firstDiff;
- }
+ if (firstWordDiff == -1)
+ {
+ VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
+ vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(firstDiff);
+ for (size_t i = 0; i < worddiffs.size(); ++i)
+ {
+ if (worddiffs[i].endline[m_nThisPane] > firstLine ||
+ (firstLine == worddiffs[i].endline[m_nThisPane] && worddiffs[i].end[m_nThisPane] - 1 >= ptStart.x))
+ {
+ firstWordDiff = static_cast<int>(i);
+ break;
+ }
+ }
- // Check that last selected line is last diff's last line or below it
- VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
- if ((int)di.dend > lastLine)
- {
- if (firstDiff < lastDiff)
+ if (firstLine >= di.dbegin && firstWordDiff == -1)
+ {
+ ++firstDiff;
+ firstWordDiff = 0;
+ }
+ }
+
+ VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
+ vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(lastDiff);
+ for (size_t i = worddiffs.size() - 1; i != (size_t)-1; --i)
+ {
+ if (worddiffs[i].beginline[m_nThisPane] < lastLine ||
+ (lastLine == worddiffs[i].beginline[m_nThisPane] && worddiffs[i].begin[m_nThisPane] + 1 <= ptEnd.x))
+ {
+ lastWordDiff = static_cast<int>(i);
+ break;
+ }
+ }
+
+ if (lastLine <= di.dend && lastWordDiff == -1)
--lastDiff;
- }
- // Special case: one-line diff is not selected if cursor is in it
- if (firstLine == lastLine)
+ if (firstDiff == lastDiff && (lastWordDiff != -1 && lastWordDiff < firstWordDiff))
+ {
+ firstDiff = -1;
+ lastDiff = -1;
+ firstWordDiff = -1;
+ lastWordDiff = -1;
+ }
+ else if (lastDiff < firstDiff || (firstDiff == lastDiff && firstWordDiff == -1 && lastWordDiff == -1))
+ {
+ firstDiff = -1;
+ lastDiff = -1;
+ firstWordDiff = -1;
+ lastWordDiff = -1;
+ }
+ }
+ else
{
firstDiff = -1;
lastDiff = -1;
+ firstWordDiff = -1;
+ lastWordDiff = -1;
}
}
+
+ ASSERT(firstDiff == -1 ? (lastDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
+ ASSERT(lastDiff == -1 ? (firstDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
+ ASSERT(firstDiff != -1 ? firstWordDiff != -1 : true);
+}
+
+std::map<int, std::vector<int>> CMergeEditView::GetColumnSelectedWordDiffIndice()
+{
+ CMergeDoc *pDoc = GetDocument();
+ std::map<int, std::vector<int>> ret;
+ std::map<int, std::vector<int> *> list;
+ CPoint ptStart, ptEnd;
+ GetSelection(ptStart, ptEnd);
+ for (int nLine = ptStart.y; nLine <= ptEnd.y; ++nLine)
+ {
+ if (pDoc->m_diffList.LineToDiff(nLine) != -1)
+ {
+ int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
+ int nLeft, nRight;
+ GetColumnSelection(nLine, nLeft, nRight);
+ CPoint ptStart2, ptEnd2;
+ ptStart2.x = nLeft;
+ ptEnd2.x = nRight;
+ ptStart2.y = ptEnd2.y = nLine;
+ GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff, &ptStart2, &ptEnd2);
+ if (firstDiff != -1 && lastDiff != -1)
+ {
+ std::vector<int> *pWordDiffs;
+ if (list.find(firstDiff) == list.end())
+ list.insert(std::pair<int, std::vector<int> *>(firstDiff, new std::vector<int>()));
+ pWordDiffs = list[firstDiff];
+ for (int i = firstWordDiff; i <= lastWordDiff; ++i)
+ {
+ if (pWordDiffs->empty() || i != (*pWordDiffs)[pWordDiffs->size() - 1])
+ pWordDiffs->push_back(i);
+ }
+ }
+ }
+ }
+ for (auto& it : list)
+ ret.insert(std::pair<int, std::vector<int>>(it.first, *it.second));
+ return ret;
}
void CMergeEditView::OnInitialUpdate()
}
}
- int firstDiff, lastDiff;
- GetFullySelectedDiffs(firstDiff, lastDiff);
-
- if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
+ if (IsSelection())
{
- CWaitCursor waitstatus;
- if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff) && !IsSelection())
- pDoc->ListCopy(srcPane, dstPane, currentDiff);
+ int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
+ if (!m_bColumnSelection)
+ {
+ GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
+ if (firstDiff != -1 && lastDiff != -1)
+ {
+ CWaitCursor waitstatus;
+ pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff, firstWordDiff, lastWordDiff);
+ }
+ }
else
- pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff);
+ {
+ CWaitCursor waitstatus;
+ auto wordDiffs = GetColumnSelectedWordDiffIndice();
+ int i = 0;
+ std::for_each(wordDiffs.rbegin(), wordDiffs.rend(), [&](auto& it) {
+ pDoc->WordListCopy(srcPane, dstPane, it.first, it.second[0], it.second[it.second.size() - 1], &it.second, i != 0, i == 0);
+ ++i;
+ });
+ }
}
else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
{
// Check that right side is not readonly
if (!IsReadOnly(dstPane))
{
- int firstDiff, lastDiff;
- GetFullySelectedDiffs(firstDiff, lastDiff);
-
// If one or more diffs inside selection OR
// there is an active diff OR
// cursor is inside diff
- if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
- pCmdUI->Enable(true);
+ if (IsSelection())
+ {
+ int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
+ GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
+
+ pCmdUI->Enable(firstDiff != -1 && lastDiff != -1);
+ }
else
{
const int currDiff = GetDocument()->GetCurrentDiff();
void SelectDiff(int nDiff, bool bScroll = true, bool bSelectText = true);
virtual CCrystalTextBuffer *LocateTextBuffer ();
const CCrystalTextBuffer *LocateTextBuffer () const { return const_cast<CMergeEditView *>(this)->LocateTextBuffer(); };
- void GetFullySelectedDiffs(int & firstDiff, int & lastDiff);
+ void GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart = nullptr, const CPoint *ppEnd = nullptr);
+ std::map<int, std::vector<int>> GetColumnSelectedWordDiffIndice();
CString GetSelectedText();
CString GetLineText(int idx);
CMergeDoc* GetDocument();