Fix file can be marked as saved even it's been deleted outside
This PR make buffer always dirty (with any undo/redo operation) if the editing buffer is unsyncronized with file on disk. By "unsyncronized", it means: 1. the file is deleted outside but the buffer in Notepad++ is kept. 2. the file is modified by another app but the buffer is not reloaded in Notepad++. Note that if the buffer is untitled, there's no correspondent file on the disk so the buffer is considered as independent therefore synchronized. Fix #10401, close #10616
This commit is contained in:
parent
97ad1d922e
commit
6c3031f01b
|
@ -5874,11 +5874,18 @@ void Notepad_plus::notifyBufferChanged(Buffer * buffer, int mask)
|
||||||
// Since the file content has changed but the user doesn't want to reload it, set state to dirty
|
// Since the file content has changed but the user doesn't want to reload it, set state to dirty
|
||||||
buffer->setDirty(true);
|
buffer->setDirty(true);
|
||||||
|
|
||||||
|
// buffer in Notepad++ is not syncronized anymore with the file on disk
|
||||||
|
buffer->setUnsync(true);
|
||||||
|
|
||||||
break; //abort
|
break; //abort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set _isLoadedDirty false so when the document clean state is reached the icon will be set to blue
|
// Set _isLoadedDirty false so when the document clean state is reached the icon will be set to blue
|
||||||
buffer->setLoadedDirty(false);
|
buffer->setLoadedDirty(false);
|
||||||
|
|
||||||
|
// buffer in Notepad++ is syncronized with the file on disk
|
||||||
|
buffer->setUnsync(false);
|
||||||
|
|
||||||
doReload(buffer->getID(), false);
|
doReload(buffer->getID(), false);
|
||||||
if (mainActive || subActive)
|
if (mainActive || subActive)
|
||||||
{
|
{
|
||||||
|
@ -5924,6 +5931,12 @@ void Notepad_plus::notifyBufferChanged(Buffer * buffer, int mask)
|
||||||
doClose(buffer->getID(), currentView(), isSnapshotMode);
|
doClose(buffer->getID(), currentView(), isSnapshotMode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// buffer in Notepad++ is not syncronized anymore with the file on disk
|
||||||
|
buffer->setUnsync(true);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,10 @@ BOOL Notepad_plus::notify(SCNotification *notification)
|
||||||
if (!canUndo && buf->isLoadedDirty() && buf->isDirty())
|
if (!canUndo && buf->isLoadedDirty() && buf->isDirty())
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buf->isUnsync()) // buffer in Notepad++ is not syncronized with the file on disk - in this case the buffer is always dirty
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
buf->setDirty(isDirty);
|
buf->setDirty(isDirty);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1050,6 +1050,7 @@ SavingStatus FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool i
|
||||||
|
|
||||||
buffer->setFileName(fullpath, language);
|
buffer->setFileName(fullpath, language);
|
||||||
buffer->setDirty(false);
|
buffer->setDirty(false);
|
||||||
|
buffer->setUnsync(false);
|
||||||
buffer->setStatus(DOC_REGULAR);
|
buffer->setStatus(DOC_REGULAR);
|
||||||
buffer->checkFileState();
|
buffer->checkFileState();
|
||||||
_pscratchTilla->execute(SCI_SETSAVEPOINT);
|
_pscratchTilla->execute(SCI_SETSAVEPOINT);
|
||||||
|
|
|
@ -27,8 +27,7 @@ typedef Buffer* BufferID; //each buffer has unique ID by which it can be retriev
|
||||||
|
|
||||||
typedef sptr_t Document;
|
typedef sptr_t Document;
|
||||||
|
|
||||||
enum DocFileStatus
|
enum DocFileStatus {
|
||||||
{
|
|
||||||
DOC_REGULAR = 0x01, // should not be combined with anything
|
DOC_REGULAR = 0x01, // should not be combined with anything
|
||||||
DOC_UNNAMED = 0x02, // not saved (new ##)
|
DOC_UNNAMED = 0x02, // not saved (new ##)
|
||||||
DOC_DELETED = 0x04, // doesn't exist in environment anymore, but not DOC_UNNAMED
|
DOC_DELETED = 0x04, // doesn't exist in environment anymore, but not DOC_UNNAMED
|
||||||
|
@ -36,8 +35,7 @@ enum DocFileStatus
|
||||||
DOC_NEEDRELOAD = 0x10 // File is modified & needed to be reload (by log monitoring)
|
DOC_NEEDRELOAD = 0x10 // File is modified & needed to be reload (by log monitoring)
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BufferStatusInfo
|
enum BufferStatusInfo {
|
||||||
{
|
|
||||||
BufferChangeLanguage = 0x001, // Language was altered
|
BufferChangeLanguage = 0x001, // Language was altered
|
||||||
BufferChangeDirty = 0x002, // Buffer has changed dirty state
|
BufferChangeDirty = 0x002, // Buffer has changed dirty state
|
||||||
BufferChangeFormat = 0x004, // EOL type was changed
|
BufferChangeFormat = 0x004, // EOL type was changed
|
||||||
|
@ -51,8 +49,7 @@ enum BufferStatusInfo
|
||||||
BufferChangeMask = 0x3FF // Mask: covers all changes
|
BufferChangeMask = 0x3FF // Mask: covers all changes
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SavingStatus
|
enum SavingStatus {
|
||||||
{
|
|
||||||
SaveOK = 0,
|
SaveOK = 0,
|
||||||
SaveOpenFailed = 1,
|
SaveOpenFailed = 1,
|
||||||
SaveWrittingFailed = 2
|
SaveWrittingFailed = 2
|
||||||
|
@ -61,8 +58,7 @@ enum SavingStatus
|
||||||
const TCHAR UNTITLED_STR[] = TEXT("new ");
|
const TCHAR UNTITLED_STR[] = TEXT("new ");
|
||||||
|
|
||||||
//File manager class maintains all buffers
|
//File manager class maintains all buffers
|
||||||
class FileManager final
|
class FileManager final {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
void init(Notepad_plus* pNotepadPlus, ScintillaEditView* pscratchTilla);
|
void init(Notepad_plus* pNotepadPlus, ScintillaEditView* pscratchTilla);
|
||||||
|
|
||||||
|
@ -108,7 +104,6 @@ public:
|
||||||
int docLength(Buffer * buffer) const;
|
int docLength(Buffer * buffer) const;
|
||||||
size_t nextUntitledNewNumber() const;
|
size_t nextUntitledNewNumber() const;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct LoadedFileFormat {
|
struct LoadedFileFormat {
|
||||||
LoadedFileFormat() = default;
|
LoadedFileFormat() = default;
|
||||||
|
@ -144,8 +139,7 @@ private:
|
||||||
|
|
||||||
#define MainFileManager FileManager::getInstance()
|
#define MainFileManager FileManager::getInstance()
|
||||||
|
|
||||||
class Buffer final
|
class Buffer final {
|
||||||
{
|
|
||||||
friend class FileManager;
|
friend class FileManager;
|
||||||
public:
|
public:
|
||||||
//Loading a document:
|
//Loading a document:
|
||||||
|
@ -162,9 +156,7 @@ public:
|
||||||
// 3. gets the last modified time
|
// 3. gets the last modified time
|
||||||
void setFileName(const TCHAR *fn, LangType defaultLang = L_TEXT);
|
void setFileName(const TCHAR *fn, LangType defaultLang = L_TEXT);
|
||||||
|
|
||||||
const TCHAR * getFullPathName() const {
|
const TCHAR * getFullPathName() const { return _fullPathName.c_str(); }
|
||||||
return _fullPathName.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TCHAR * getFileName() const { return _fileName; }
|
const TCHAR * getFileName() const { return _fileName; }
|
||||||
|
|
||||||
|
@ -179,70 +171,48 @@ public:
|
||||||
|
|
||||||
bool checkFileState();
|
bool checkFileState();
|
||||||
|
|
||||||
bool isDirty() const {
|
bool isDirty() const { return _isDirty; }
|
||||||
return _isDirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isReadOnly() const {
|
bool isReadOnly() const { return (_isUserReadOnly || _isFileReadOnly); };
|
||||||
return (_isUserReadOnly || _isFileReadOnly);
|
|
||||||
};
|
|
||||||
|
|
||||||
bool isUntitled() const {
|
bool isUntitled() const { return (_currentStatus == DOC_UNNAMED); }
|
||||||
return (_currentStatus == DOC_UNNAMED);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getFileReadOnly() const {
|
bool getFileReadOnly() const { return _isFileReadOnly; }
|
||||||
return _isFileReadOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFileReadOnly(bool ro) {
|
void setFileReadOnly(bool ro) {
|
||||||
_isFileReadOnly = ro;
|
_isFileReadOnly = ro;
|
||||||
doNotify(BufferChangeReadonly);
|
doNotify(BufferChangeReadonly);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getUserReadOnly() const {
|
bool getUserReadOnly() const { return _isUserReadOnly; }
|
||||||
return _isUserReadOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUserReadOnly(bool ro) {
|
void setUserReadOnly(bool ro) {
|
||||||
_isUserReadOnly = ro;
|
_isUserReadOnly = ro;
|
||||||
doNotify(BufferChangeReadonly);
|
doNotify(BufferChangeReadonly);
|
||||||
}
|
}
|
||||||
|
|
||||||
EolType getEolFormat() const {
|
EolType getEolFormat() const { return _eolFormat; }
|
||||||
return _eolFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEolFormat(EolType format) {
|
void setEolFormat(EolType format) {
|
||||||
_eolFormat = format;
|
_eolFormat = format;
|
||||||
doNotify(BufferChangeFormat);
|
doNotify(BufferChangeFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
LangType getLangType() const {
|
LangType getLangType() const { return _lang; }
|
||||||
return _lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLangType(LangType lang, const TCHAR * userLangName = TEXT(""));
|
void setLangType(LangType lang, const TCHAR * userLangName = TEXT(""));
|
||||||
|
|
||||||
UniMode getUnicodeMode() const {
|
UniMode getUnicodeMode() const { return _unicodeMode; }
|
||||||
return _unicodeMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUnicodeMode(UniMode mode);
|
void setUnicodeMode(UniMode mode);
|
||||||
|
|
||||||
int getEncoding() const {
|
int getEncoding() const { return _encoding; }
|
||||||
return _encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEncoding(int encoding);
|
void setEncoding(int encoding);
|
||||||
|
|
||||||
DocFileStatus getStatus() const {
|
DocFileStatus getStatus() const { return _currentStatus; }
|
||||||
return _currentStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
Document getDocument() {
|
Document getDocument() { return _doc; }
|
||||||
return _doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDirty(bool dirty);
|
void setDirty(bool dirty);
|
||||||
|
|
||||||
|
@ -252,47 +222,34 @@ public:
|
||||||
void setHeaderLineState(const std::vector<size_t> & folds, ScintillaEditView * identifier);
|
void setHeaderLineState(const std::vector<size_t> & folds, ScintillaEditView * identifier);
|
||||||
const std::vector<size_t> & getHeaderLineState(const ScintillaEditView * identifier) const;
|
const std::vector<size_t> & getHeaderLineState(const ScintillaEditView * identifier) const;
|
||||||
|
|
||||||
bool isUserDefineLangExt() const
|
bool isUserDefineLangExt() const { return (_userLangExt[0] != '\0'); }
|
||||||
{
|
|
||||||
return (_userLangExt[0] != '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
const TCHAR * getUserDefineLangName() const
|
const TCHAR * getUserDefineLangName() const { return _userLangExt.c_str(); }
|
||||||
{
|
|
||||||
return _userLangExt.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TCHAR * getCommentLineSymbol() const
|
const TCHAR * getCommentLineSymbol() const {
|
||||||
{
|
|
||||||
Lang *l = getCurrentLang();
|
Lang *l = getCurrentLang();
|
||||||
if (!l)
|
if (!l)
|
||||||
return NULL;
|
return NULL;
|
||||||
return l->_pCommentLineSymbol;
|
return l->_pCommentLineSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TCHAR * getCommentStart() const
|
const TCHAR * getCommentStart() const {
|
||||||
{
|
|
||||||
Lang *l = getCurrentLang();
|
Lang *l = getCurrentLang();
|
||||||
if (!l)
|
if (!l)
|
||||||
return NULL;
|
return NULL;
|
||||||
return l->_pCommentStart;
|
return l->_pCommentStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TCHAR * getCommentEnd() const
|
const TCHAR * getCommentEnd() const {
|
||||||
{
|
|
||||||
Lang *l = getCurrentLang();
|
Lang *l = getCurrentLang();
|
||||||
if (!l)
|
if (!l)
|
||||||
return NULL;
|
return NULL;
|
||||||
return l->_pCommentEnd;
|
return l->_pCommentEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getNeedsLexing() const
|
bool getNeedsLexing() const { return _needLexer; }
|
||||||
{
|
|
||||||
return _needLexer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNeedsLexing(bool lex)
|
void setNeedsLexing(bool lex) {
|
||||||
{
|
|
||||||
_needLexer = lex;
|
_needLexer = lex;
|
||||||
doNotify(BufferChangeLexing);
|
doNotify(BufferChangeLexing);
|
||||||
}
|
}
|
||||||
|
@ -305,18 +262,10 @@ public:
|
||||||
|
|
||||||
void setDeferredReload();
|
void setDeferredReload();
|
||||||
|
|
||||||
bool getNeedReload()
|
bool getNeedReload() const { return _needReloading; }
|
||||||
{
|
void setNeedReload(bool reload) { _needReloading = reload; }
|
||||||
return _needReloading;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNeedReload(bool reload)
|
int docLength() const {
|
||||||
{
|
|
||||||
_needReloading = reload;
|
|
||||||
}
|
|
||||||
|
|
||||||
int docLength() const
|
|
||||||
{
|
|
||||||
assert(_pManager != nullptr);
|
assert(_pManager != nullptr);
|
||||||
return _pManager->docLength(_id);
|
return _pManager->docLength(_id);
|
||||||
}
|
}
|
||||||
|
@ -330,28 +279,24 @@ public:
|
||||||
|
|
||||||
bool isModified() const { return _isModified; }
|
bool isModified() const { return _isModified; }
|
||||||
void setModifiedStatus(bool isModified) { _isModified = isModified; }
|
void setModifiedStatus(bool isModified) { _isModified = isModified; }
|
||||||
|
|
||||||
generic_string getBackupFileName() const { return _backupFileName; }
|
generic_string getBackupFileName() const { return _backupFileName; }
|
||||||
void setBackupFileName(const generic_string& fileName) { _backupFileName = fileName; }
|
void setBackupFileName(const generic_string& fileName) { _backupFileName = fileName; }
|
||||||
|
|
||||||
FILETIME getLastModifiedTimestamp() const { return _timeStamp; }
|
FILETIME getLastModifiedTimestamp() const { return _timeStamp; }
|
||||||
|
|
||||||
bool isLoadedDirty() const
|
bool isLoadedDirty() const { return _isLoadedDirty; }
|
||||||
{
|
void setLoadedDirty(bool val) { _isLoadedDirty = val; }
|
||||||
return _isLoadedDirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLoadedDirty(bool val)
|
bool isUnsync() const { return _isUnsync; }
|
||||||
{
|
void setUnsync(bool val) { _isUnsync = val; }
|
||||||
_isLoadedDirty = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
void startMonitoring() {
|
void startMonitoring() {
|
||||||
_isMonitoringOn = true;
|
_isMonitoringOn = true;
|
||||||
_eventHandle = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
_eventHandle = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
HANDLE getMonitoringEvent() const {
|
HANDLE getMonitoringEvent() const { return _eventHandle; };
|
||||||
return _eventHandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
void stopMonitoring() {
|
void stopMonitoring() {
|
||||||
_isMonitoringOn = false;
|
_isMonitoringOn = false;
|
||||||
|
@ -420,6 +365,13 @@ private:
|
||||||
bool _isModified = false;
|
bool _isModified = false;
|
||||||
bool _isLoadedDirty = false; // it's the indicator for finding buffer's initial state
|
bool _isLoadedDirty = false; // it's the indicator for finding buffer's initial state
|
||||||
|
|
||||||
|
bool _isUnsync = false; // Buffer should be always dirty (with any undo/redo operation) if the editing buffer is unsyncronized with file on disk.
|
||||||
|
// By "unsyncronized" it means :
|
||||||
|
// 1. the file is deleted outside but the buffer in Notepad++ is kept.
|
||||||
|
// 2. the file is modified by another app but the buffer is not reloaded in Notepad++.
|
||||||
|
// Note that if the buffer is untitled, there's no correspondent file on the disk so the buffer is considered as independent therefore synchronized.
|
||||||
|
|
||||||
|
|
||||||
// For the monitoring
|
// For the monitoring
|
||||||
HANDLE _eventHandle = nullptr;
|
HANDLE _eventHandle = nullptr;
|
||||||
bool _isMonitoringOn = false;
|
bool _isMonitoringOn = false;
|
||||||
|
|
Loading…
Reference in New Issue