OSDN Git Service

Avoid infinite loops in the RegularExpression::subst() function when the length of...
[winmerge-jp/winmerge-jp.git] / Src / MergeDoc.h
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997  Dean P. Grimm
4 //    SPDX-License-Identifier: GPL-2.0-or-later
5 /////////////////////////////////////////////////////////////////////////////
6 /** 
7  * @file  MergeDoc.h
8  *
9  * @brief Declaration of CMergeDoc class
10  */
11 #pragma once
12
13 #include "IMergeDoc.h"
14 #include "DiffTextBuffer.h"
15 #include "DiffWrapper.h"
16 #include "DiffList.h"
17 #include "TempFile.h"
18 #include "PathContext.h"
19 #include "FileLoadResult.h"
20 #include <vector>
21 #include <map>
22 #include <memory>
23 #include <optional>
24
25 /**
26  * @brief Additional action codes for WinMerge.
27  * @note Reserve first 100 for CrystalEditor
28  */
29 enum
30 {
31         CE_ACTION_MERGE = 100, /**< Merging action */
32 };
33
34 /**
35  * @brief Return statuses of file rescan
36  */
37 enum
38 {
39         RESCAN_OK = 0, /**< Rescan succeeded */
40         RESCAN_SUPPRESSED, /**< Rescan not done - suppressed */
41         RESCAN_FILE_ERR, /**< Error reading file */
42         RESCAN_TEMP_ERR, /**< Error saving to temp file */
43 };
44
45 /**
46  * @brief File saving statuses
47  */
48 enum
49 {
50         SAVE_DONE = 0, /**< Saving succeeded */  
51         SAVE_FAILED, /**< Saving failed */  
52         SAVE_PACK_FAILED, /**< Plugin failed to pack file */  
53         SAVE_NO_FILENAME, /**< File has no filename */  
54         SAVE_CANCELLED, /**< Saving was cancelled */  
55 };
56
57 /**
58  * @brief Types for buffer. Buffer's type defines behavior
59  * of buffer when saving etc.
60  * 
61  * Difference between BUFFERTYPE::NORMAL and BUFFERTYPE::NORMAL_NAMED is
62  * that _NAMED has description text given and which is shown
63  * instead of filename.
64  *
65  * BUFFERTYPE::UNNAMED is created empty buffer (scratchpad), it has
66  * no filename, and default description is given for it. After
67  * this buffer is saved it becomes _SAVED. It is not equal to
68  * NORMAL_NAMED, since scratchpads don't have plugins etc.
69  */
70 enum class BUFFERTYPE
71 {
72         NORMAL = 0, /**< Normal, file loaded from disk */
73         NORMAL_NAMED, /**< Normal, description given */
74         UNNAMED, /**< Empty, created buffer */
75         UNNAMED_SAVED, /**< Empty buffer saved with filename */
76 };
77
78 struct WordDiff {
79         std::array<int, 3> begin; // 0-based, eg, begin[0] is from str1
80         std::array<int, 3> end; // 0-based, eg, end[1] is from str2
81         std::array<int, 3> beginline;
82         std::array<int, 3> endline;
83         int op;
84
85         WordDiff(int s1=0, int e1=0, int bl1=0, int el1=0, int s2=0, int e2=0, int bl2=0, int el2=0, int s3=0, int e3=0, int bl3=0, int el3=0, int op=0)
86                 : begin{s1, s2, s3}
87                 , beginline{bl1, bl2, bl3}
88                 , endline{el1, el2, el3}
89                 , op(op)
90         {
91                 if (s1>e1) e1=s1;
92                 if (s2>e2) e2=s2;
93                 if (s3>e3) e3=s3;
94                 end[0] = e1;
95                 end[1] = e2;
96                 end[2] = e3;
97         }
98 };
99
100 struct CurrentWordDiff
101 {
102         int nDiff;
103         size_t nWordDiff;
104         int nPane;
105         CEPoint ptStart;
106         CEPoint ptEnd;
107 };
108
109 struct DiffFileInfo;
110 class CMergeEditView;
111 class PackingInfo;
112 class PrediffingInfo;
113 class CMergeEditFrame;
114 class CDirDoc;
115 class CEncodingErrorBar;
116 class CLocationView;
117 class CMergeEditSplitterView;
118
119 /**
120  * @brief Document class for merging two files
121  */
122 class CMergeDoc : public CDocument, public IMergeDoc
123 {
124 public:
125         struct TableProps { bool istable; tchar_t delimiter; tchar_t quote; bool allowNewlinesInQuotes; };
126 // Attributes
127 public:
128         static int m_nBuffersTemp;
129
130 // Begin declaration of CMergeDoc
131
132         std::unique_ptr<CDiffTextBuffer> m_ptBuf[3]; /**< Left/Middle/Right side text buffer */
133         int m_nBuffers;
134         int m_nGroups;
135
136 protected: // create from serialization only
137         CMergeDoc();
138         DECLARE_DYNCREATE(CMergeDoc)
139
140         // Operations
141 public: 
142         std::unique_ptr<DiffFileInfo> m_pSaveFileInfo[3];
143         std::unique_ptr<DiffFileInfo> m_pRescanFileInfo[3];
144         DiffList m_diffList;
145         UINT m_nTrivialDiffs; /**< Amount of trivial (ignored) diffs */
146         PathContext m_filePaths; /**< Filepaths for this document */
147         /// String of concatenated filenames as text to apply plugins filter to
148         String m_strBothFilenames;
149
150         CMergeEditView * GetActiveMergeView();
151         CMergeEditView * GetActiveMergeGroupView(int nBuffer);
152         void UpdateHeaderPath(int pane);
153         void UpdateHeaderActivity(int pane, bool bActivate);
154         void RefreshOptions();
155         void UpdateResources();
156         bool OpenDocs(int nFiles, const FileLocation fileloc[],
157                 const bool bRO[], const String strDesc[]);
158         int LoadFile(const String& sFileName, int nBuffer, bool & readOnly, const FileTextEncoding & encoding);
159         void MoveOnLoad(int nPane = -1, int nLinIndex = -1, bool bRealLine = false, int nCharIndex = -1);
160         void ChangeFile(int nBuffer, const String& path, int nLineIndex = -1);
161         void RescanIfNeeded(float timeOutInSecond);
162         int Rescan(bool &bBinary, IDENTLEVEL &identical, bool bForced = false);
163         void CheckFileChanged(void) override;
164         int ShowMessageBox(const String& sText, unsigned nType = MB_OK, unsigned nIDHelp = 0);
165         void ShowRescanError(int nRescanResult, IDENTLEVEL identical);
166         bool Undo();
167         void CopyAllList(int srcPane, int dstPane);
168         void CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstWordDiff = -1, int lastWordDiff = -1);
169         void CopyMultiplePartialList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstLineDiff = -1, int lastLineDiff = -1);
170         void DoAutoMerge(int dstPane);
171         bool SanityCheckDiff(const DIFFRANGE& dr) const;
172         bool WordListCopy(int srcPane, int dstPane, int nDiff, int nFirstWordDiff, int nLastWordDiff, const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious = false, bool bUpdateView = true);
173         bool PartialListCopy(int srcPane, int dstPane, int nDiff, int firstLine, int lastLine = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
174         bool ListCopy(int srcPane, int dstPane, int nDiff = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
175         bool TrySaveAs(String& strPath, int &nLastErrorCode, String & sError,
176                 int nBuffer, PackingInfo& infoTempUnpacker);
177         bool DoSave(const tchar_t* szPath, bool &bSaveSuccess, int nBuffer);
178         bool DoSaveAs(const tchar_t* szPath, bool &bSaveSuccess, int nBuffer);
179         int RightLineInMovedBlock(int pane, int line);
180         int LeftLineInMovedBlock(int pane, int line);
181         void SetEditedAfterRescan(int nBuffer);
182         bool IsEditedAfterRescan(int nBuffer = -1) const;
183
184         const PackingInfo* GetUnpacker() const override { return &m_infoUnpacker; }
185         void SetUnpacker(const PackingInfo * infoUnpacker) override;
186         void SetPrediffer(const PrediffingInfo * infoPrediffer);
187         void GetPrediffer(PrediffingInfo * infoPrediffer) const;
188         const PrediffingInfo *GetPrediffer() const override;
189         void AddMergeViews(CMergeEditSplitterView* pMergeEditSplitterView, CMergeEditView* pView[3]);
190         void RemoveMergeViews(CMergeEditSplitterView* pMergeEditSplitterView);
191         void SetLocationView(CLocationView *pLocationView) { m_pLocationView = pLocationView; }
192
193         CDirDoc * GetDirDoc() const override { return m_pDirDoc; }
194         void SetDirDoc(CDirDoc * pDirDoc) override;
195         void DirDocClosing(CDirDoc * pDirDoc) override;
196         bool CloseNow() override;
197         int GetFileCount() const override { return m_filePaths.GetSize(); }
198         String GetPath(int pane) const override { return m_filePaths[pane]; } 
199         bool GetReadOnly(int pane) const override { return m_ptBuf[pane]->m_bReadOnly; }
200         CString GetTooltipString() const override;
201         void SwapFiles(int nFromIndex, int nToIndex);
202
203         CMergeEditView * GetView(int group, int buffer) const { return m_pView[group][buffer]; }
204         CLocationView * GetLocationView() { return m_pLocationView; }
205         std::vector<CMergeEditView *> GetViewList(int nGroup = -1, int nBuffer = -1) const {
206                 std::vector<CMergeEditView *> list;
207                 if (nGroup != -1)
208                         for (int nBuffer2 = 0; nBuffer2 < m_nBuffers; ++nBuffer2)
209                                 list.push_back(m_pView[nGroup][nBuffer2]);
210                 else if (nBuffer != -1)
211                         for (int nGroup2 = 0; nGroup2 < m_nGroups; ++nGroup2)
212                                 list.push_back(m_pView[nGroup2][nBuffer]);
213                 else
214                 {
215                         for (int nGroup3 = 0; nGroup3 < m_nGroups; nGroup3++)
216                                 for (int nBuffer3 = 0; nBuffer3 < m_nBuffers; ++nBuffer3)
217                                         list.push_back(m_pView[nGroup3][nBuffer3]);
218                 }
219                 return list;
220         }
221         template <typename Function>
222         void ForEachView(int nBuffer, Function func) {
223                 for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
224                         func(m_pView[nGroup][nBuffer]);
225         }
226         template <typename Function>
227         void ForEachView(Function func) {
228                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
229                 {
230                         for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
231                                 func(m_pView[nGroup][nBuffer]);
232                 }
233         }
234         template <typename Function>
235         void ForEachActiveGroupView(Function func) {
236                 int nGroup = GetActiveMergeView()->m_nThisGroup;
237                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
238                         func(m_pView[nGroup][nBuffer]);
239         }
240         CMergeEditFrame * GetParentFrame();
241
242         void AddSyncPoint();
243         bool DeleteSyncPoint(int pane, int nLine, bool bRescan = true);
244         void ClearSyncPoints();
245         bool HasSyncPoints();
246         std::vector<std::vector<int> > GetSyncPointList();
247         String GetDescription(int pane) const override { return m_strDesc[pane]; }
248         void SetDescription(int pane, const String& sText) { m_strDesc[pane] = sText; }
249
250         // Overrides
251         // ClassWizard generated virtual function overrides
252         //{{AFX_VIRTUAL(CMergeDoc)
253         public:
254         virtual BOOL OnNewDocument();
255         virtual void Serialize(CArchive& ar);
256         virtual BOOL SaveModified();
257         virtual void DeleteContents ();
258         virtual void SetTitle(LPCTSTR lpszTitle);
259         //}}AFX_VIRTUAL
260
261 // Implementation in MergeDocLineDiffs.cpp
262 public:
263         typedef enum { BYTEDIFF, WORDDIFF } DIFFLEVEL;
264         void Showlinediff(CMergeEditView *pView, bool bReversed = false);
265         void AddToSubstitutionFilters(CMergeEditView* pView, bool bReversed = false);
266         void AddToLineFilters(const String& text);
267         std::vector<WordDiff> GetWordDiffArrayInDiffBlock(int nDiff);
268         std::vector<WordDiff> GetWordDiffArray(int nLineIndex);
269         std::vector<WordDiff> GetWordDiffArrayInRange(const int begin[3], const int end[3], int pane1 = -1, int pane2 = -1);
270         void ClearWordDiffCache(int nDiff = -1);
271 private:
272         void Computelinediff(CMergeEditView *pView, std::pair<CEPoint, CEPoint> rc[], bool bReversed);
273         std::map<int, std::vector<WordDiff> > m_cacheWordDiffs;
274 // End MergeDocLineDiffs.cpp
275
276 // Implementation in MergeDocEncoding.cpp
277 public:
278         bool DoFileEncodingDialog(int pane = -1);
279 // End MergeDocEncoding.cpp
280
281 // Implementation
282 public:
283         FileChange IsFileChangedOnDisk(const tchar_t* szPath, DiffFileInfo &dfi,
284                 bool bSave, int nBuffer);
285         bool PromptAndSaveIfNeeded(bool bAllowCancel);
286         std::vector<int> undoTgt;
287         std::vector<int>::iterator curUndo;
288         void FlushAndRescan(bool bForced = false);
289         void SetCurrentDiff(int nDiff);
290         int GetCurrentDiff() const { return m_nCurDiff; }
291         const CurrentWordDiff& GetCurrentWordDiff() const { return m_CurWordDiff; }
292         bool EqualCurrentWordDiff(int nBuffer, const CEPoint& ptStart, const CEPoint& ptEnd) const
293         {
294                 return (m_CurWordDiff.nPane == nBuffer && m_CurWordDiff.ptStart == ptStart && m_CurWordDiff.ptEnd == ptEnd);
295         }
296         virtual ~CMergeDoc();
297         void SetDetectMovedBlocks(bool bDetectMovedBlocks);
298         bool IsMixedEOL(int nBuffer) const;
299         bool GenerateReport(const String& sFileName) const override;
300         void SetAutoMerged(bool bAutoMerged) { m_bAutoMerged = bAutoMerged; }
301         bool GetAutoMerged() const { return m_bAutoMerged; };
302         bool IsModified() const
303         {
304                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
305                         if (m_ptBuf[nBuffer]->IsModified())
306                                 return true;
307                 return false;
308         }
309         bool CanUndo() const
310         {
311                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
312                         if (m_ptBuf[nBuffer]->CanUndo())
313                                 return true;
314                 return false;
315         }
316
317         std::optional<bool> GetEnableTableEditing() const { return m_bEnableTableEditing; }
318         void SetEnableTableEditing(std::optional<bool> bEnableTableEditing) { m_bEnableTableEditing = bEnableTableEditing; }
319         static TableProps MakeTablePropertiesByFileName(const String& path, const std::optional<bool>& enableTableEditing, bool showDialog = true);
320         void SetPreparedTableProperties(const TableProps& props) { m_pTablePropsPrepared.reset(new TableProps(props)); }
321
322         void SetTextType(int textType);
323         void SetTextType(const String& ext);
324         bool GetChangedSchemeManually() const { return m_bChangedSchemeManually; }
325
326         bool GetAutomaticRescan() const { return m_bAutomaticRescan; }
327         // to customize the mergeview menu
328         HMENU createPrediffersSubmenu(HMENU hMenu);
329         String GetSaveAsPath() const { return m_strSaveAsPath; }
330         void SetSaveAsPath(const String& strSaveAsPath) { m_strSaveAsPath = strSaveAsPath; }
331
332 // implementation methods
333 private:
334         bool GetBreakType() const;
335         bool GetByteColoringOption() const;
336         bool IsValidCodepageForMergeEditor(unsigned cp) const;
337         void SanityCheckCodepage(FileLocation & fileinfo);
338         FileLoadResult::flags_t LoadOneFile(int index, const String& filename, bool readOnly, const String& strDesc, const FileTextEncoding & encoding);
339         void SetTableProperties();
340
341 // Implementation data
342 protected:
343         int m_nCurDiff; /**< Selected diff, 0-based index, -1 if no diff selected */
344         CurrentWordDiff m_CurWordDiff;
345         CMergeEditView * m_pView[3][3]; /**< Pointer to left/middle/right view */
346         CMergeEditSplitterView * m_pMergeEditSplitterView[3];
347         CLocationView * m_pLocationView; /**< Pointer to locationview */
348         CDirDoc * m_pDirDoc;
349         bool m_bEnableRescan; /**< Automatic rescan enabled/disabled */
350         COleDateTime m_LastRescan; /**< Time of last rescan (for delaying) */ 
351         CDiffWrapper m_diffWrapper;
352         /// information about the file packer/unpacker
353         PackingInfo m_infoUnpacker;
354         String m_strDesc[3]; /**< Left/Middle/Right side description text */
355         BUFFERTYPE m_nBufferType[3];
356         bool m_bEditAfterRescan[3]; /**< Left/middle/right doc edited after rescanning */
357         TempFile m_tempFiles[3]; /**< Temp files for compared files */
358         String m_strSaveAsPath; /**< "3rd path" where output saved if given */
359         int m_nDiffContext;
360         bool m_bInvertDiffContext;
361         bool m_bMixedEol; /**< Does this document have mixed EOL style? */
362         std::unique_ptr<CEncodingErrorBar> m_pEncodingErrorBar;
363         bool m_bHasSyncPoints;
364         bool m_bAutoMerged;
365         std::optional<bool> m_bEnableTableEditing;
366         std::unique_ptr<TableProps> m_pTablePropsPrepared;
367         /**
368          * Are automatic rescans enabled?
369          * If automatic rescans are enabled then we rescan files after edit
370          * events, unless timer suppresses rescan. We suppress rescans within
371          * certain time from previous rescan.
372          */
373         bool m_bAutomaticRescan;
374         /// active prediffer ID : helper to check the radio button
375         int m_CurrentPredifferID;
376         bool m_bChangedSchemeManually;  /**< `true` if the syntax highlighting scheme is changed manually */
377         String m_sCurrentHeaderTitle[3];
378
379 // friend access
380         friend class RescanSuppress;
381
382
383 // Generated message map functions
384 protected:
385         //{{AFX_MSG(CMergeDoc)
386         afx_msg void OnFileSave();
387         afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
388         afx_msg void OnFileSaveLeft();
389         afx_msg void OnUpdateFileSaveLeft(CCmdUI* pCmdUI);
390         afx_msg void OnFileSaveMiddle();
391         afx_msg void OnUpdateFileSaveMiddle(CCmdUI* pCmdUI);
392         afx_msg void OnFileSaveRight();
393         afx_msg void OnUpdateFileSaveRight(CCmdUI* pCmdUI);
394         afx_msg void OnFileSaveAsLeft();
395         afx_msg void OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI);
396         afx_msg void OnFileSaveAsMiddle();
397         afx_msg void OnFileSaveAsRight();
398         afx_msg void OnUpdateStatusNum(CCmdUI* pCmdUI);
399         afx_msg void OnFileReload();
400         afx_msg void OnFileEncoding();
401         afx_msg void OnFileReadOnlyLeft();
402         afx_msg void OnUpdateFileReadOnlyLeft(CCmdUI* pCmdUI);
403         afx_msg void OnFileReadOnlyMiddle();
404         afx_msg void OnUpdateFileReadOnlyMiddle(CCmdUI* pCmdUI);
405         afx_msg void OnFileReadOnlyRight();
406         afx_msg void OnUpdateFileReadOnlyRight(CCmdUI* pCmdUI);
407         afx_msg void OnUpdateStatusRO(CCmdUI* pCmdUI);
408         afx_msg void OnDiffContext(UINT nID);
409         afx_msg void OnUpdateDiffContext(CCmdUI* pCmdUI);
410         afx_msg void OnToolsGenerateReport();
411         afx_msg void OnToolsGeneratePatch();
412         afx_msg void OnOpenWithUnpacker();
413         afx_msg void OnApplyPrediffer();
414         afx_msg void OnBnClickedFileEncoding();
415         afx_msg void OnBnClickedPlugin();
416         afx_msg void OnBnClickedHexView();
417         afx_msg void OnOK();
418         afx_msg void OnFileRecompareAsText();
419         afx_msg void OnFileRecompareAsTable();
420         afx_msg void OnUpdateFileRecompareAsText(CCmdUI* pCmdUI);
421         afx_msg void OnUpdateFileRecompareAsTable(CCmdUI* pCmdUI);
422         afx_msg void OnFileRecompareAs(UINT nID);
423         template<int srcPane, int dstPane>
424         afx_msg void OnViewSwapPanes();
425         afx_msg void OnUpdateSwapContext(CCmdUI* pCmdUI);
426         afx_msg void OnRefresh();
427         afx_msg void OnUpdatePrediffer(CCmdUI* pCmdUI);
428         afx_msg void OnPrediffer(UINT nID );
429         //}}AFX_MSG
430         DECLARE_MESSAGE_MAP()
431 private:
432         void PrimeTextBuffers();
433         void HideLines();
434         void AdjustDiffBlocks();
435         void AdjustDiffBlocks3way();
436         void AdjustDiffBlock(DiffMap & diffmap, const DIFFRANGE & diffrange,
437                 const std::vector<WordDiff>& worddiffs,
438                 int i0, int i1, int lo0, int hi0, int lo1, int hi1);
439         int GetMatchCost(const DIFFRANGE& dr, int i0, int i1, int line0, int line1, const std::vector<WordDiff>& worddiffs);
440         OP_TYPE ComputeOpType3way(const std::vector<std::array<int, 3>>& vlines, size_t index,
441                 const DIFFRANGE& diffrange, const DIFFOPTIONS& diffOptions);
442         void FlagTrivialLines();
443         void FlagMovedLines();
444         String GetFileExt(const tchar_t* sFileName, const tchar_t* sDescription) const;
445         void DoFileSave(int pane);
446         void SetPredifferByMenu(UINT nID);
447 };
448
449 /**
450  * @brief return true if there are synchronization points
451  */
452 inline bool CMergeDoc::HasSyncPoints()
453 {
454         return m_bHasSyncPoints;
455 }
456