mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-16 15:24:10 +02:00
IDE file recovery
This commit is contained in:
parent
1b171d5b20
commit
b975acb711
6 changed files with 331 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,6 +12,7 @@
|
||||||
**/Release */*
|
**/Release */*
|
||||||
**/Release_*/*
|
**/Release_*/*
|
||||||
**/build/*
|
**/build/*
|
||||||
|
**/recovery/*
|
||||||
**/.vs/*
|
**/.vs/*
|
||||||
BeefSpace_User.toml
|
BeefSpace_User.toml
|
||||||
lld-link.exe
|
lld-link.exe
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace IDE
|
||||||
public bool mHadRefusedFileChange;
|
public bool mHadRefusedFileChange;
|
||||||
public bool mFileDeleted;
|
public bool mFileDeleted;
|
||||||
|
|
||||||
|
public MD5Hash mRecoveryHash;
|
||||||
public MD5Hash mMD5Hash;
|
public MD5Hash mMD5Hash;
|
||||||
public SHA256Hash mSHA256Hash;
|
public SHA256Hash mSHA256Hash;
|
||||||
|
|
||||||
|
|
270
IDE/src/FileRecovery.bf
Normal file
270
IDE/src/FileRecovery.bf
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.IO;
|
||||||
|
using Beefy;
|
||||||
|
|
||||||
|
namespace IDE
|
||||||
|
{
|
||||||
|
class FileRecovery
|
||||||
|
{
|
||||||
|
public class Entry
|
||||||
|
{
|
||||||
|
public FileRecovery mFileRecovery;
|
||||||
|
public String mPath ~ delete _;
|
||||||
|
public String mContents ~ delete _;
|
||||||
|
public String mRecoveryFileName ~ delete _;
|
||||||
|
public MD5Hash mLastSavedHash;
|
||||||
|
public MD5Hash mContentHash;
|
||||||
|
public int mCursorPos;
|
||||||
|
|
||||||
|
public this()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(FileRecovery fileRecovery, StringView path, MD5Hash lastSavedHash, StringView contents, int cursorPos)
|
||||||
|
{
|
||||||
|
mFileRecovery = fileRecovery;
|
||||||
|
mPath = new String(path);
|
||||||
|
mContents = new String(contents);
|
||||||
|
mLastSavedHash = lastSavedHash;
|
||||||
|
mCursorPos = cursorPos;
|
||||||
|
using (mFileRecovery.mMonitor.Enter())
|
||||||
|
{
|
||||||
|
mFileRecovery.mFileSet.Add(this);
|
||||||
|
mFileRecovery.mDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
if (mFileRecovery != null)
|
||||||
|
{
|
||||||
|
using (mFileRecovery.mMonitor.Enter())
|
||||||
|
{
|
||||||
|
mFileRecovery.mFileSet.Remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor mMonitor = new .() ~ delete _;
|
||||||
|
HashSet<FileRecovery.Entry> mFileSet = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
bool mDirty = false;
|
||||||
|
WaitEvent mProcessingEvent ~ delete _;
|
||||||
|
String mWorkspaceDir = new String() ~ delete _;
|
||||||
|
bool mWantWorkspaceCleanup;
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
if (mProcessingEvent != null)
|
||||||
|
mProcessingEvent.WaitFor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetRecoveryFileName(StringView filePath, String recoveryFileName)
|
||||||
|
{
|
||||||
|
Path.GetFileNameWithoutExtension(filePath, recoveryFileName);
|
||||||
|
recoveryFileName.Append("_");
|
||||||
|
var hash = MD5.Hash(.((.)filePath.Ptr, filePath.Length));
|
||||||
|
hash.ToString(recoveryFileName);
|
||||||
|
Path.GetExtension(filePath, recoveryFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process()
|
||||||
|
{
|
||||||
|
defer mProcessingEvent.Set(true);
|
||||||
|
|
||||||
|
HashSet<String> serializedFileNames = scope .();
|
||||||
|
List<Entry> indexEntries = scope .();
|
||||||
|
defer ClearAndDeleteItems(indexEntries);
|
||||||
|
|
||||||
|
bool wantWorkspaceCleanup = false;
|
||||||
|
using (mMonitor.Enter())
|
||||||
|
{
|
||||||
|
for (var entry in mFileSet)
|
||||||
|
{
|
||||||
|
if (entry.mRecoveryFileName == null)
|
||||||
|
{
|
||||||
|
entry.mRecoveryFileName = new String();
|
||||||
|
GetRecoveryFileName(entry.mPath, entry.mRecoveryFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.mContents != null)
|
||||||
|
{
|
||||||
|
entry.mContentHash = MD5.Hash(.((.)entry.mContents.Ptr, entry.mContents.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.mRecoveryFileName != null)
|
||||||
|
serializedFileNames.Add(entry.mRecoveryFileName);
|
||||||
|
|
||||||
|
Entry indexEntry = new Entry();
|
||||||
|
indexEntry.mPath = new String(entry.mPath);
|
||||||
|
indexEntry.mRecoveryFileName = new String(entry.mRecoveryFileName);
|
||||||
|
indexEntry.mLastSavedHash = entry.mLastSavedHash;
|
||||||
|
indexEntry.mContents = entry.mContents;
|
||||||
|
indexEntry.mContentHash = entry.mContentHash;
|
||||||
|
indexEntry.mCursorPos = entry.mCursorPos;
|
||||||
|
entry.mContents = null;
|
||||||
|
indexEntries.Add(indexEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
wantWorkspaceCleanup = mWantWorkspaceCleanup;
|
||||||
|
mDirty = false;
|
||||||
|
mWantWorkspaceCleanup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String recoverPath = scope String();
|
||||||
|
recoverPath.Append(mWorkspaceDir);
|
||||||
|
recoverPath.Append("/recovery");
|
||||||
|
|
||||||
|
if (wantWorkspaceCleanup)
|
||||||
|
{
|
||||||
|
DateTime timeNow = DateTime.Now;
|
||||||
|
for (var entry in Directory.EnumerateFiles(recoverPath))
|
||||||
|
{
|
||||||
|
String filePath = scope .();
|
||||||
|
entry.GetFilePath(filePath);
|
||||||
|
// Just being paranoid
|
||||||
|
if ((!filePath.Contains('_')) || (filePath.Length < 33))
|
||||||
|
continue;
|
||||||
|
DateTime lastWriteTime = entry.GetLastWriteTime();
|
||||||
|
if ((timeNow - lastWriteTime).TotalDays > 7)
|
||||||
|
File.Delete(filePath).IgnoreError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFileSet.IsEmpty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if (Directory.CreateDirectory(recoverPath) case .Err)
|
||||||
|
return;
|
||||||
|
|
||||||
|
indexEntries.Sort(scope (lhs, rhs) => lhs.mRecoveryFileName <=> rhs.mRecoveryFileName);
|
||||||
|
|
||||||
|
String index = scope .();
|
||||||
|
|
||||||
|
for (var indexEntry in indexEntries)
|
||||||
|
{
|
||||||
|
if (indexEntry.mContents != null)
|
||||||
|
{
|
||||||
|
String outPath = scope String()..AppendF("{}/{}", recoverPath, indexEntry.mRecoveryFileName);
|
||||||
|
File.WriteAllText(outPath, indexEntry.mContents).IgnoreError();
|
||||||
|
}
|
||||||
|
|
||||||
|
index.AppendF("{}\t{}\t{}\t{}\n", indexEntry.mPath, indexEntry.mLastSavedHash, indexEntry.mContentHash, indexEntry.mCursorPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
String indexPath = scope .();
|
||||||
|
indexPath.AppendF("{}/index.txt", recoverPath);
|
||||||
|
File.WriteAllText(indexPath, index).IgnoreError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WorkspaceClosed()
|
||||||
|
{
|
||||||
|
if (!gApp.mWorkspace.IsInitialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String indexPath = scope String();
|
||||||
|
indexPath.Append(gApp.mWorkspace.mDir);
|
||||||
|
indexPath.Append("/recovery/index.txt");
|
||||||
|
File.Delete(indexPath).IgnoreError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckWorkspace()
|
||||||
|
{
|
||||||
|
mWantWorkspaceCleanup = true;
|
||||||
|
|
||||||
|
String recoverPath = scope String();
|
||||||
|
recoverPath.Append(gApp.mWorkspace.mDir);
|
||||||
|
recoverPath.Append("/recovery");
|
||||||
|
|
||||||
|
String indexPath = scope .();
|
||||||
|
indexPath.AppendF("{}/index.txt", recoverPath);
|
||||||
|
|
||||||
|
String index = scope .();
|
||||||
|
File.ReadAllText(indexPath, index).IgnoreError();
|
||||||
|
|
||||||
|
bool ReadFile(StringView path, String outText, out MD5Hash hash)
|
||||||
|
{
|
||||||
|
if (Utils.LoadTextFile(path, outText) case .Err)
|
||||||
|
{
|
||||||
|
hash = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hash = MD5.Hash(.((.)outText.Ptr, outText.Length));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var line in index.Split('\n'))
|
||||||
|
{
|
||||||
|
var lineEnum = line.Split('\t');
|
||||||
|
StringView savedFilePath;
|
||||||
|
StringView indexSavedHashStr;
|
||||||
|
StringView indexRecoveryHashStr;
|
||||||
|
StringView cursorPosStr;
|
||||||
|
if (!(lineEnum.GetNext() case .Ok(out savedFilePath))) continue;
|
||||||
|
if (!(lineEnum.GetNext() case .Ok(out indexSavedHashStr))) continue;
|
||||||
|
if (!(lineEnum.GetNext() case .Ok(out indexRecoveryHashStr))) continue;
|
||||||
|
if (!(lineEnum.GetNext() case .Ok(out cursorPosStr))) continue;
|
||||||
|
|
||||||
|
MD5Hash indexSavedHash = MD5Hash.Parse(indexSavedHashStr).GetValueOrDefault();
|
||||||
|
if (indexSavedHash.IsZero)
|
||||||
|
continue;
|
||||||
|
MD5Hash indexRecoveryHash = MD5Hash.Parse(indexRecoveryHashStr).GetValueOrDefault();
|
||||||
|
if (indexRecoveryHash.IsZero)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MD5Hash savedHash;
|
||||||
|
String savedFileContents = scope String();
|
||||||
|
ReadFile(savedFilePath, savedFileContents, out savedHash);
|
||||||
|
if (savedHash != indexSavedHash)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String recoveryFilePath = scope String()..AppendF("{}/", recoverPath);
|
||||||
|
GetRecoveryFileName(savedFilePath, recoveryFilePath);
|
||||||
|
MD5Hash recoveryFileHash;
|
||||||
|
String recoveryFileContents = scope String();
|
||||||
|
ReadFile(recoveryFilePath, recoveryFileContents, out recoveryFileHash);
|
||||||
|
if (recoveryFileHash != indexRecoveryHash)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var sourceViewPanel = gApp.ShowSourceFile(scope String(savedFilePath));
|
||||||
|
if (sourceViewPanel == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sourceViewPanel.mEditData.mRecoveryHash = savedHash;
|
||||||
|
sourceViewPanel.mEditWidget.SetText(recoveryFileContents);
|
||||||
|
sourceViewPanel.mEditWidget.mEditWidgetContent.CursorTextPos = int.Parse(cursorPosStr).GetValueOrDefault();
|
||||||
|
sourceViewPanel.mEditWidget.mEditWidgetContent.EnsureCursorVisible();
|
||||||
|
|
||||||
|
gApp.OutputLine("File recovered: {}", savedFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (mProcessingEvent != null)
|
||||||
|
{
|
||||||
|
if (!mProcessingEvent.WaitFor(0))
|
||||||
|
return;
|
||||||
|
DeleteAndNullify!(mProcessingEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (mMonitor.Enter())
|
||||||
|
{
|
||||||
|
if ((!mDirty) && (!mWantWorkspaceCleanup))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gApp.mWorkspace.IsInitialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mWorkspaceDir.Set(gApp.mWorkspace.mDir);
|
||||||
|
mProcessingEvent = new WaitEvent();
|
||||||
|
ThreadPool.QueueUserWorkItem(new => Process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -217,6 +217,9 @@ namespace IDE
|
||||||
public Settings mSettings = new Settings() ~ delete _;
|
public Settings mSettings = new Settings() ~ delete _;
|
||||||
public Workspace mWorkspace = new Workspace() ~ delete _;
|
public Workspace mWorkspace = new Workspace() ~ delete _;
|
||||||
public FileWatcher mFileWatcher = new FileWatcher() ~ delete _;
|
public FileWatcher mFileWatcher = new FileWatcher() ~ delete _;
|
||||||
|
#if !CLI
|
||||||
|
public FileRecovery mFileRecovery = new FileRecovery() ~ delete _;
|
||||||
|
#endif
|
||||||
public int mLastFileChangeId;
|
public int mLastFileChangeId;
|
||||||
public bool mHaveSourcesChangedInternallySinceLastCompile;
|
public bool mHaveSourcesChangedInternallySinceLastCompile;
|
||||||
public bool mHaveSourcesChangedExternallySinceLastCompile;
|
public bool mHaveSourcesChangedExternallySinceLastCompile;
|
||||||
|
@ -630,6 +633,7 @@ namespace IDE
|
||||||
mSettings.Save();
|
mSettings.Save();
|
||||||
SaveDefaultLayoutData();
|
SaveDefaultLayoutData();
|
||||||
}
|
}
|
||||||
|
mFileRecovery.WorkspaceClosed();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*WithTabs(scope (tabButton) =>
|
/*WithTabs(scope (tabButton) =>
|
||||||
|
@ -2245,6 +2249,10 @@ namespace IDE
|
||||||
mDockingFrame = mMainFrame.mDockingFrame;
|
mDockingFrame = mMainFrame.mDockingFrame;
|
||||||
//mMainFrame.AddedToParent;
|
//mMainFrame.AddedToParent;
|
||||||
|
|
||||||
|
#if !CLI
|
||||||
|
mFileRecovery.WorkspaceClosed();
|
||||||
|
#endif
|
||||||
|
|
||||||
delete mWorkspace;
|
delete mWorkspace;
|
||||||
mWorkspace = new Workspace();
|
mWorkspace = new Workspace();
|
||||||
|
|
||||||
|
@ -2594,6 +2602,9 @@ namespace IDE
|
||||||
#if !CLI
|
#if !CLI
|
||||||
if (mBfResolveCompiler != null)
|
if (mBfResolveCompiler != null)
|
||||||
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
|
mBfResolveCompiler.QueueSetWorkspaceOptions(null, 0);
|
||||||
|
|
||||||
|
if (mMainWindow != null)
|
||||||
|
mFileRecovery.CheckWorkspace();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String relaunchCmd = scope .();
|
String relaunchCmd = scope .();
|
||||||
|
@ -6184,7 +6195,7 @@ namespace IDE
|
||||||
tabbedView.AddTab(newTabButton);
|
tabbedView.AddTab(newTabButton);
|
||||||
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(sourceViewPanel));
|
newTabButton.mCloseClickedEvent.Add(new () => DocumentCloseClicked(sourceViewPanel));
|
||||||
newTabButton.Activate(setFocus);
|
newTabButton.Activate(setFocus);
|
||||||
if (setFocus)
|
if ((setFocus) && (sourceViewPanel.mWidgetWindow != null))
|
||||||
sourceViewPanel.FocusEdit();
|
sourceViewPanel.FocusEdit();
|
||||||
|
|
||||||
return sourceViewPanel;
|
return sourceViewPanel;
|
||||||
|
@ -11198,6 +11209,9 @@ namespace IDE
|
||||||
ShowPanel(mOutputPanel, false);
|
ShowPanel(mOutputPanel, false);
|
||||||
UpdateRecentFileMenuItems();
|
UpdateRecentFileMenuItems();
|
||||||
ShowStartupFile();
|
ShowStartupFile();
|
||||||
|
#if !CLI
|
||||||
|
mFileRecovery.CheckWorkspace();
|
||||||
|
#endif
|
||||||
|
|
||||||
if ((mIsFirstRun) && (!mWorkspace.IsInitialized))
|
if ((mIsFirstRun) && (!mWorkspace.IsInitialized))
|
||||||
ShowWelcome();
|
ShowWelcome();
|
||||||
|
@ -12587,6 +12601,9 @@ namespace IDE
|
||||||
void UpdateWorkspace()
|
void UpdateWorkspace()
|
||||||
{
|
{
|
||||||
mFileWatcher.Update();
|
mFileWatcher.Update();
|
||||||
|
#if !CLI
|
||||||
|
mFileRecovery.Update();
|
||||||
|
#endif
|
||||||
|
|
||||||
bool appHasFocus = false;
|
bool appHasFocus = false;
|
||||||
for (var window in mWindows)
|
for (var window in mWindows)
|
||||||
|
|
|
@ -515,6 +515,12 @@ namespace IDE.ui
|
||||||
mSourceViewPanel.mLockFlashPct = 0.00001f;
|
mSourceViewPanel.mLockFlashPct = 0.00001f;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSourceViewPanel != null)
|
||||||
|
{
|
||||||
|
mSourceViewPanel.CheckSavedContents();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (mSourceViewPanel != null)
|
if (mSourceViewPanel != null)
|
||||||
|
|
|
@ -300,6 +300,7 @@ namespace IDE.ui
|
||||||
public SourceEditWidget mEditWidget;
|
public SourceEditWidget mEditWidget;
|
||||||
public ProjectSource mProjectSource;
|
public ProjectSource mProjectSource;
|
||||||
public FileEditData mEditData;
|
public FileEditData mEditData;
|
||||||
|
public FileRecovery.Entry mFileRecoveryEntry ~ delete _;
|
||||||
public List<TrackedTextElementView> mTrackedTextElementViewList ~ DeleteContainerAndItems!(_);
|
public List<TrackedTextElementView> mTrackedTextElementViewList ~ DeleteContainerAndItems!(_);
|
||||||
public List<BfPassInstance.BfError> mErrorList = new List<BfPassInstance.BfError>() ~ DeleteContainerAndItems!(_);
|
public List<BfPassInstance.BfError> mErrorList = new List<BfPassInstance.BfError>() ~ DeleteContainerAndItems!(_);
|
||||||
public List<ResolveParams> mDeferredResolveResults = new .() ~ DeleteContainerAndItems!(_);
|
public List<ResolveParams> mDeferredResolveResults = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
@ -314,6 +315,7 @@ namespace IDE.ui
|
||||||
public int32 mLastTextVersionId;
|
public int32 mLastTextVersionId;
|
||||||
public int32 mAutocompleteTextVersionId;
|
public int32 mAutocompleteTextVersionId;
|
||||||
public int32 mClassifiedTextVersionId;
|
public int32 mClassifiedTextVersionId;
|
||||||
|
public int32 mLastRecoveryTextVersionId;
|
||||||
public bool mLoadFailed;
|
public bool mLoadFailed;
|
||||||
String mOldVerLoadCmd ~ delete _;
|
String mOldVerLoadCmd ~ delete _;
|
||||||
HTTPRequest mOldVerHTTPRequest ~ delete _;
|
HTTPRequest mOldVerHTTPRequest ~ delete _;
|
||||||
|
@ -857,7 +859,20 @@ namespace IDE.ui
|
||||||
|
|
||||||
return !char8IdData.IsRangeEqual(mEditWidget.mEditWidgetContent.mData.mTextIdData.GetPrepared(), char8IdStart, char8IdEnd);
|
return !char8IdData.IsRangeEqual(mEditWidget.mEditWidgetContent.mData.mTextIdData.GetPrepared(), char8IdStart, char8IdEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CheckSavedContents()
|
||||||
|
{
|
||||||
|
if (mEditData.mLastFileTextVersion == mEditWidget.Content.mData.mCurTextVersionId)
|
||||||
|
{
|
||||||
|
if ((mEditData != null) && (mEditData.mRecoveryHash.IsZero))
|
||||||
|
{
|
||||||
|
String text = scope .();
|
||||||
|
mEditWidget.GetText(text);
|
||||||
|
mEditData.mRecoveryHash = MD5.Hash(.((uint8*)text.Ptr, text.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void FileSaved()
|
public void FileSaved()
|
||||||
{
|
{
|
||||||
ClearLoadFailed();
|
ClearLoadFailed();
|
||||||
|
@ -880,6 +895,9 @@ namespace IDE.ui
|
||||||
}
|
}
|
||||||
|
|
||||||
gApp.FileChanged(mFilePath);
|
gApp.FileChanged(mFilePath);
|
||||||
|
DeleteAndNullify!(mFileRecoveryEntry);
|
||||||
|
mLastRecoveryTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||||||
|
mEditData.mRecoveryHash = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassifyThreadDone()
|
void ClassifyThreadDone()
|
||||||
|
@ -2936,6 +2954,7 @@ namespace IDE.ui
|
||||||
|
|
||||||
if ((mEditData != null) && (mFilePath != null))
|
if ((mEditData != null) && (mFilePath != null))
|
||||||
Debug.Assert(Path.Equals(mFilePath, mEditData.mFilePath));
|
Debug.Assert(Path.Equals(mFilePath, mEditData.mFilePath));
|
||||||
|
mLastRecoveryTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -6130,6 +6149,21 @@ namespace IDE.ui
|
||||||
mTicksSinceTextChanged++;
|
mTicksSinceTextChanged++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((mLastRecoveryTextVersionId != mEditWidget.Content.mData.mCurTextVersionId) && (mTicksSinceTextChanged >= 16))
|
||||||
|
{
|
||||||
|
#if !CLI
|
||||||
|
DeleteAndNullify!(mFileRecoveryEntry);
|
||||||
|
if ((mFilePath != null) && (mEditData != null) && (!mEditData.mRecoveryHash.IsZero))
|
||||||
|
{
|
||||||
|
String contents = scope .();
|
||||||
|
mEditWidget.GetText(contents);
|
||||||
|
mFileRecoveryEntry = new .(gApp.mFileRecovery, mFilePath, mEditData.mRecoveryHash, contents, mEditWidget.mEditWidgetContent.CursorTextPos);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mLastRecoveryTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: This is just a test!
|
//TODO: This is just a test!
|
||||||
/*if (Rand.Float() <so 0.05f)
|
/*if (Rand.Float() <so 0.05f)
|
||||||
Classify(false, true);
|
Classify(false, true);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue