OSDN Git Service

Word-level merging did not work correctly if the number of lines in the diff block...
authorTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sat, 6 Apr 2019 04:02:17 +0000 (13:02 +0900)
committerTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sat, 6 Apr 2019 04:02:17 +0000 (13:02 +0900)
--HG--
branch : jp

Src/MergeDoc.cpp
Src/MergeDoc.h
Src/MergeDocLineDiffs.cpp
Src/MergeEditView.cpp
Src/MergeEditView.h

index 1da3863..9259792 100644 (file)
@@ -800,7 +800,7 @@ void CMergeDoc::CopyAllList(int srcPane, int dstPane)
  * @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)
@@ -824,8 +824,16 @@ void CMergeDoc::CopyMultipleList(int srcPane, int dstPane, int firstDiff, int la
        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);
 
@@ -858,8 +866,16 @@ void CMergeDoc::CopyMultipleList(int srcPane, int dstPane, int firstDiff, int la
                        }                       
                        // 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
+                       }
                }
        }
 
@@ -1168,6 +1184,127 @@ bool CMergeDoc::ListCopy(int srcPane, int dstPane, int nDiff /* = -1*/,
        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.
  *
index aa75593..0dc69ed 100644 (file)
@@ -190,9 +190,10 @@ public:
        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);
@@ -277,6 +278,7 @@ public:
 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:
index e831ce4..7035bc0 100644 (file)
@@ -137,6 +137,25 @@ void CMergeDoc::ClearWordDiffCache(int nDiff/* = -1 */)
        }
 }
 
+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
index c313c1c..0cee83b 100644 (file)
@@ -296,14 +296,18 @@ CString CMergeEditView::GetSelectedText()
  * @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();
@@ -311,39 +315,140 @@ void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
                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()
@@ -1669,16 +1774,28 @@ void CMergeEditView::OnX2Y(int srcPane, int dstPane)
                }
        }
 
-       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))
        {
@@ -1692,14 +1809,16 @@ void CMergeEditView::OnUpdateX2Y(int dstPane, CCmdUI* pCmdUI)
        // 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();
index 2f45c1a..1d6f7f6 100644 (file)
@@ -123,7 +123,8 @@ public:
        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();