, m_firstSignificantLeftOnly(-1)
, m_firstSignificantMiddleOnly(-1)
, m_firstSignificantRightOnly(-1)
+, m_firstSignificantConflict(-1)
, m_lastSignificantLeftMiddle(-1)
, m_lastSignificantLeftRight(-1)
, m_lastSignificantMiddleRight(-1)
, m_lastSignificantLeftOnly(-1)
, m_lastSignificantMiddleOnly(-1)
, m_lastSignificantRightOnly(-1)
+, m_lastSignificantConflict(-1)
{
m_diffs.reserve(64); // Reserve some initial space to avoid allocations.
}
m_firstSignificantLeftOnly = -1;
m_firstSignificantMiddleOnly = -1;
m_firstSignificantRightOnly = -1;
+ m_firstSignificantConflict = -1;
m_lastSignificantLeftMiddle = -1;
m_lastSignificantLeftRight = -1;
m_lastSignificantMiddleRight = -1;
m_lastSignificantLeftOnly = -1;
m_lastSignificantMiddleOnly = -1;
m_lastSignificantRightOnly = -1;
+ m_lastSignificantConflict = -1;
}
/**
m_firstSignificantLeftMiddle = -1;
m_firstSignificantLeftRight = -1;
m_firstSignificantMiddleRight = -1;
+ m_firstSignificantConflict = -1;
m_lastSignificantLeftMiddle = -1;
m_lastSignificantLeftRight = -1;
m_lastSignificantMiddleRight = -1;
+ m_lastSignificantConflict = -1;
int prev = -1;
const int size = (int) m_diffs.size();
m_firstSignificantRightOnly = i;
m_lastSignificantRightOnly = i;
}
+ if (m_diffs[i].op == OP_DIFF)
+ {
+ if (m_firstSignificantConflict == -1)
+ m_firstSignificantConflict = i;
+ m_lastSignificantConflict = i;
+ }
}
}
}
if (dfi->op == OP_3RDONLY && dfi->dend[0] <= nLine)
return i;
break;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (dfi->op == OP_DIFF && dfi->dend[0] <= nLine)
+ return i;
+ break;
}
}
return -1;
if (dfi->op == OP_3RDONLY && dfi->dbegin[0] >= nLine)
return i;
break;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (dfi->op == OP_DIFF && dfi->dbegin[0] >= nLine)
+ return i;
+ break;
}
}
return -1;
return m_firstSignificantLeftOnly;
case THREEWAYDIFFTYPE_RIGHTONLY:
return m_firstSignificantRightOnly;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ return m_firstSignificantConflict;
}
return -1;
}
if (m_diffs[nDiff].op == OP_3RDONLY)
return nDiff;
break;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (m_diffs[nDiff].op == OP_DIFF)
+ return nDiff;
+ break;
}
}
return -1;
if (m_diffs[nDiff].op == OP_3RDONLY)
return nDiff;
break;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (m_diffs[nDiff].op == OP_DIFF)
+ return nDiff;
+ break;
}
}
return -1;
return m_lastSignificantLeftOnly;
case THREEWAYDIFFTYPE_RIGHTONLY:
return m_lastSignificantRightOnly;
+ case THREEWAYDIFFTYPE_CONFLICT:
+ return m_lastSignificantRightOnly;
}
return -1;
}
case THREEWAYDIFFTYPE_RIGHTONLY:
if (m_firstSignificantRightOnly == -1) return NULL;
return DiffRangeAt(m_firstSignificantRightOnly);
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (m_firstSignificantConflict == -1) return NULL;
+ return DiffRangeAt(m_firstSignificantConflict);
}
return NULL;
}
case THREEWAYDIFFTYPE_RIGHTONLY:
if (m_lastSignificantRightOnly == -1) return NULL;
return DiffRangeAt(m_lastSignificantRightOnly);
+ case THREEWAYDIFFTYPE_CONFLICT:
+ if (m_lastSignificantConflict == -1) return NULL;
+ return DiffRangeAt(m_lastSignificantConflict);
}
return NULL;
}
AddDiff(dr);
}
}
+
+int DiffList::GetMergeableSrcIndex(int nDiff, int nDestIndex) const
+{
+ const DIFFRANGE *pdr = DiffRangeAt(nDiff);
+ switch (nDestIndex)
+ {
+ case 0:
+ case 2:
+ if (pdr->op == OP_2NDONLY)
+ return 1;
+ return -1;
+ case 1:
+ if (pdr->op == OP_1STONLY)
+ return 0;
+ else if (pdr->op == OP_3RDONLY)
+ return 2;
+ return -1;
+ }
+}
\ No newline at end of file
THREEWAYDIFFTYPE_LEFTONLY,
THREEWAYDIFFTYPE_MIDDLEONLY,
THREEWAYDIFFTYPE_RIGHTONLY,
+ THREEWAYDIFFTYPE_CONFLICT,
};
/**
int LastSignificant3wayDiff(int nDiffType) const;
const DIFFRANGE * FirstSignificant3wayDiffRange(int nDiffType) const;
const DIFFRANGE * LastSignificant3wayDiffRange(int nDiffType) const;
+ int GetMergeableSrcIndex(int nDiff, int nDestIndex) const;
const DIFFRANGE * DiffRangeAt(int nDiff) const;
int m_firstSignificantLeftOnly;
int m_firstSignificantMiddleOnly;
int m_firstSignificantRightOnly;
+ int m_firstSignificantConflict;
int m_lastSignificantLeftMiddle;
int m_lastSignificantLeftRight;
int m_lastSignificantMiddleRight;
int m_lastSignificantLeftOnly;
int m_lastSignificantMiddleOnly;
int m_lastSignificantRightOnly;
+ int m_lastSignificantConflict;
};
#endif // _DIFFLIST_H_
static void LoadToolbarImageList(CMainFrame::TOOLBAR_SIZE size, UINT nIDResource,
CImageList& ImgList)
{
- const int ImageCount = 19;
+ const int ImageCount = 22;
int imageWidth = 0;
int imageHeight = 0;
MENUITEM "&Next Difference\tAlt+Down", ID_NEXTDIFF
MENUITEM "&Previous Difference\tAlt+Up", ID_PREVDIFF
MENUITEM SEPARATOR
+ MENUITEM "Ne&xt Conflict\tAlt+Shift+Down", ID_NEXTCONFLICT
+ MENUITEM "Pre&vious Conflict\tAlt+Shift+Up", ID_PREVCONFLICT
+ MENUITEM SEPARATOR
POPUP "A&dvanced"
BEGIN
MENUITEM "Copy &Left to Middle\tAlt+4", ID_L2M
MENUITEM "Copy &All to Right", ID_ALL_RIGHT
MENUITEM "Cop&y All to Left", ID_ALL_LEFT
MENUITEM SEPARATOR
+ MENUITEM "A&uto Merge\tAlt+Ctrl+M", ID_AUTO_MERGE
+ MENUITEM SEPARATOR
MENUITEM "Add &Synchronization Point\tAlt+S", ID_ADD_SYNCPOINT
MENUITEM "Clear Sync&hronization Points", ID_CLEAR_SYNCPOINTS
END
"G", ID_EDIT_WMGOTO, VIRTKEY, CONTROL, NOINVERT
"H", ID_EDIT_REPLACE, VIRTKEY, CONTROL, NOINVERT
"J", ID_FILE_OPENPROJECT, VIRTKEY, CONTROL, NOINVERT
+ "M", ID_AUTO_MERGE, VIRTKEY, ALT, CONTROL, NOINVERT
"N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"P", ID_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT
VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT
VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT
VK_DOWN, ID_NEXTDIFF, VIRTKEY, ALT, NOINVERT
+ VK_DOWN, ID_NEXTCONFLICT, VIRTKEY, ALT, SHIFT, NOINVERT
VK_F8, ID_NEXTDIFF, VIRTKEY, NOINVERT
+ VK_F8, ID_NEXTCONFLICT, VIRTKEY, SHIFT, NOINVERT
VK_F7, ID_PREVDIFF, VIRTKEY, NOINVERT
+ VK_F7, ID_PREVCONFLICT, VIRTKEY, SHIFT, NOINVERT
VK_END, ID_LASTDIFF, VIRTKEY, ALT, NOINVERT
VK_F2, ID_DIR_ITEM_RENAME, VIRTKEY, NOINVERT
VK_F3, ID_EDIT_REPEAT, VIRTKEY, SHIFT, NOINVERT
VK_RIGHT, ID_L2R, VIRTKEY, ALT, NOINVERT
VK_RIGHT, ID_L2RNEXT, VIRTKEY, CONTROL, ALT, NOINVERT
VK_UP, ID_PREVDIFF, VIRTKEY, ALT, NOINVERT
+ VK_UP, ID_PREVCONFLICT, VIRTKEY, ALT, SHIFT, NOINVERT
VK_ADD, ID_VIEW_ZOOMIN, VIRTKEY, CONTROL, NOINVERT
VK_SUBTRACT, ID_VIEW_ZOOMOUT, VIRTKEY, CONTROL, NOINVERT
VK_MULTIPLY, ID_VIEW_ZOOMNORMAL, VIRTKEY, CONTROL, NOINVERT
IDS_STATUS_CREATEREPORT "Creating the report..."
IDS_STATUS_UNDO "Undoing the last operation..."
IDS_STATUS_REDO "Redoing the previous operation..."
+ IDS_STATUS_AUTOMERGE "Doing Auto Merge..."
END
// STATUS BAR : OTHER PANES (MOSTLY FOR EDITOR)
IDS_MERGE_MODE "You are now entering Merge Mode. If you want to turn off Merge Mode, press F9 key"
END
+
+// EDITOR : AUTO MERGE
+STRINGTABLE
+BEGIN
+ IDS_AUTO_MERGE "The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"
+END
+
// FILE COMPARE: PANE CAPTIONS
STRINGTABLE
BEGIN
BEGIN
ID_PREVDIFF "Scroll to the previous difference\nPrevious Difference (Alt+Up)"
ID_NEXTDIFF "Scroll to the next difference\nNext Difference (Alt+Down)"
+ ID_PREVCONFLICT "Scroll to the previous conflict\nPrevious Conflict (Alt+Shift+Up)"
+ ID_NEXTCONFLICT "Scroll to the next conflict\nNext Conflict (Alt+Shift+Down)"
ID_PREVDIFFLM "Scroll to the previous difference between left and middle\nPrevious Difference Between Left And Middle(Alt+Shift+1)"
ID_NEXTDIFFLM "Scroll to the next difference between left and middle\nNext Difference Between Left And Middle(Alt+1)"
ID_PREVDIFFLR "Scroll to the previous difference between left and right\nPrevious Difference Between Left And Middle(Alt+Shift+2)"
ID_MERGE_COMPARE "Compare selected item (compare first item if multiple items selected)"
ID_ADD_SYNCPOINT "Add a manual point for synchronization of changes between files"
ID_CLEAR_SYNCPOINTS "Clear manual change synchronization points "
+ ID_AUTO_MERGE "Automatically merge changes\nAuto Merge (Ctrl+Alt+M)"
END
// TOOLS MENU
BUTTON ID_NEXTDIFF
BUTTON ID_PREVDIFF
SEPARATOR
+ BUTTON ID_NEXTCONFLICT
+ BUTTON ID_PREVCONFLICT
+ SEPARATOR
BUTTON ID_FIRSTDIFF
BUTTON ID_CURDIFF
BUTTON ID_LASTDIFF
BUTTON ID_ALL_RIGHT
BUTTON ID_ALL_LEFT
SEPARATOR
+ BUTTON ID_AUTO_MERGE
+ SEPARATOR
BUTTON ID_REFRESH
END
, m_pInfoUnpacker(new PackingInfo)
, m_pEncodingErrorBar(NULL)
, m_bHasSyncPoints(false)
+, m_bAutoMerged(false)
{
DIFFOPTIONS options = {0};
}
/**
+ * @brief Do auto-merge.
+ * @param [in] dstPane Destination side
+ */
+void CMergeDoc::DoAutoMerge(int dstPane)
+{
+ const int lastDiff = m_diffList.GetSize() - 1;
+ const int firstDiff = 0;
+ bool bGroupWithPrevious = false;
+ int autoMergedCount = 0;
+ int unresolvedConflictCount = 0;
+
+ RescanSuppress suppressRescan(*this);
+
+ // Note we don't care about m_nDiffs count to become zero,
+ // because we don't rescan() so it does not change
+
+ SetCurrentDiff(lastDiff);
+
+ SetEditedAfterRescan(dstPane);
+
+ CPoint currentPosDst = m_pView[dstPane]->GetCursorPos();
+ currentPosDst.x = 0;
+
+ // copy from bottom up is more efficient
+ for (int i = lastDiff; i >= firstDiff; --i)
+ {
+ const DIFFRANGE *pdi = m_diffList.DiffRangeAt(i);
+ const int srcPane = m_diffList.GetMergeableSrcIndex(i, dstPane);
+ if (srcPane != -1)
+ {
+ SetCurrentDiff(i);
+ if (currentPosDst.y > pdi->dend[dstPane])
+ {
+ if (pdi->blank[dstPane] >= 0)
+ currentPosDst.y -= pdi->dend[dstPane] - pdi->blank[dstPane] + 1;
+ else if (pdi->blank[srcPane] >= 0)
+ currentPosDst.y -= pdi->dend[dstPane] - pdi->blank[srcPane] + 1;
+ }
+ // Group merge with previous (merge undo data to one action)
+ if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, false))
+ break; // sync failure
+ if (!bGroupWithPrevious)
+ bGroupWithPrevious = true;
+ ++autoMergedCount;
+ }
+ if (pdi->op == OP_DIFF)
+ ++unresolvedConflictCount;
+ }
+
+ m_pView[dstPane]->SetCursorPos(currentPosDst);
+ m_pView[dstPane]->SetNewSelection(currentPosDst, currentPosDst, false);
+ m_pView[dstPane]->SetNewAnchor(currentPosDst);
+ m_pDetailView[dstPane]->SetCursorPos(currentPosDst);
+ m_pDetailView[dstPane]->SetNewSelection(currentPosDst, currentPosDst, false);
+ m_pDetailView[dstPane]->SetNewAnchor(currentPosDst);
+
+ suppressRescan.Clear(); // done suppress Rescan
+ FlushAndRescan();
+
+ if (autoMergedCount > 0)
+ m_bAutoMerged = true;
+
+ AfxMessageBox(
+ LangFormatString2(IDS_AUTO_MERGE,
+ string_format(_T("%d"), autoMergedCount).c_str(),
+ string_format(_T("%d"), unresolvedConflictCount).c_str()).c_str(),
+ MB_ICONINFORMATION|MB_DONT_DISPLAY_AGAIN);
+}
+
+/**
* @brief Sanity check difference.
*
* Checks that lines in difference are inside difference in both files.
bool Undo();
void CopyAllList(int srcPane, int dstPane);
void CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff);
+ void DoAutoMerge(int dstPane);
bool SanityCheckDiff(DIFFRANGE dr) const;
bool ListCopy(int srcPane, int dstPane, int nDiff = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
bool TrySaveAs(String& strPath, int &nLastErrorCode, String & sError,
bool IsMixedEOL(int nBuffer) const;
bool OpenWithUnpackerDialog();
bool GenerateReport(LPCTSTR szFileName);
+ void SetAutoMerged(bool bAutoMerged) { m_bAutoMerged = bAutoMerged; }
+ bool GetAutoMerged() const { return m_bAutoMerged; };
+ bool IsModified() const
+ {
+ for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
+ if (m_ptBuf[nBuffer]->IsModified())
+ return true;
+ return false;
+ }
+ bool CanUndo() const
+ {
+ for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
+ if (m_ptBuf[nBuffer]->CanUndo())
+ return true;
+ return false;
+ }
// implementation methods
private:
bool m_bMixedEol; /**< Does this document have mixed EOL style? */
CEncodingErrorBar *m_pEncodingErrorBar;
bool m_bHasSyncPoints;
+ bool m_bAutoMerged;
// friend access
friend class RescanSuppress;
ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
+ ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
+ ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
+ ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
+ ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
+ ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
+ ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
ON_COMMAND(ID_L2R, OnL2r)
ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
ON_COMMAND(ID_R2L, OnR2l)
{
tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
}
+ if (!pDoc->CanUndo())
+ pDoc->SetAutoMerged(false);
}
/**
}
}
+void CMergeEditView::OnNextConflict()
+{
+ OnNext3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
+}
+
+/**
+ * @brief Update "Next Conflict" UI items
+ */
+void CMergeEditView::OnUpdateNextConflict(CCmdUI* pCmdUI)
+{
+ OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
+}
+
+void CMergeEditView::OnPrevConflict()
+{
+ OnPrev3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
+}
+
+/**
+ * @brief Update "Prev Conflict" UI items
+ */
+void CMergeEditView::OnUpdatePrevConflict(CCmdUI* pCmdUI)
+{
+ OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
+}
+
/**
* @brief Go to next 3-way diff and select it.
*/
}
/**
+ * @brief Do Auto merge
+ */
+void CMergeEditView::OnAutoMerge()
+{
+ // Check current pane is not readonly
+ if (GetDocument()->IsModified() || GetDocument()->GetAutoMerged() || IsReadOnly(m_nThisPane))
+ return;
+
+ WaitStatusCursor waitstatus(IDS_STATUS_AUTOMERGE);
+
+ GetDocument()->DoAutoMerge(m_nThisPane);
+}
+
+/**
+ * @brief Called when "Auto Merge" item is updated
+ */
+void CMergeEditView::OnUpdateAutoMerge(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(GetDocument()->m_nBuffers == 3 &&
+ !GetDocument()->IsModified() &&
+ !GetDocument()->GetAutoMerged() &&
+ !IsReadOnly(m_nThisPane));
+}
+
+/**
* @brief Add synchronization point
*/
void CMergeEditView::OnAddSyncPoint()
// If automatic rescan enabled, rescan after edit events
if (m_bAutomaticRescan)
{
- // keep document up to date
+ // keep document up to date
// (Re)start timer to rescan only when user edits text
// If timer starting fails, rescan immediately
if (nAction == CE_ACTION_TYPING ||
afx_msg void OnUpdateNextdiff(CCmdUI* pCmdUI);
afx_msg void OnPrevdiff();
afx_msg void OnUpdatePrevdiff(CCmdUI* pCmdUI);
+ afx_msg void OnNextConflict();
+ afx_msg void OnUpdateNextConflict(CCmdUI* pCmdUI);
+ afx_msg void OnPrevConflict();
+ afx_msg void OnUpdatePrevConflict(CCmdUI* pCmdUI);
afx_msg void OnNextdiffLM();
afx_msg void OnUpdateNextdiffLM(CCmdUI* pCmdUI);
afx_msg void OnPrevdiffLM();
afx_msg void OnUpdateAllLeft(CCmdUI* pCmdUI);
afx_msg void OnAllRight();
afx_msg void OnUpdateAllRight(CCmdUI* pCmdUI);
+ afx_msg void OnAutoMerge();
+ afx_msg void OnUpdateAutoMerge(CCmdUI* pCmdUI);
afx_msg void OnL2r();
afx_msg void OnUpdateL2r(CCmdUI* pCmdUI);
afx_msg void OnR2l();
#define IDS_STATUS_CREATEREPORT 16825
#define IDS_STATUS_UNDO 16826
#define IDS_STATUS_REDO 16827
+#define IDS_STATUS_AUTOMERGE 16828
#define IDS_LINE_STATUS_INFO_EOL 16832
#define IDS_EMPTY_LINE_STATUS_INFO 16833
#define IDS_LINE_STATUS_INFO 16834
#define IDS_CLOSE_RIGHT_TABS 33195
#define IDS_CLOSE_LEFT_TABS 33196
#define IDS_CONFIRM_CLOSE_WINDOW 33197
+#define ID_AUTO_MERGE 33198
+#define ID_NEXTCONFLICT 33199
+#define ID_PREVCONFLICT 33200
+#define IDS_AUTO_MERGE 33201
// Next default values for new objects
//
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS 1
#define _APS_NEXT_RESOURCE_VALUE 239
-#define _APS_NEXT_COMMAND_VALUE 33198
+#define _APS_NEXT_COMMAND_VALUE 33202
#define _APS_NEXT_CONTROL_VALUE 1352
#define _APS_NEXT_SYMED_VALUE 113
#endif