Core: filepathify documentmanager guts

Change-Id: I2e01adb0fec592fd8401b60015e8a0464d3c8e4d
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2021-06-17 11:13:23 +02:00
parent f0cb3e7282
commit aa4b926f02

View File

@@ -155,7 +155,7 @@ struct FileStateItem
struct FileState struct FileState
{ {
QString watchedFilePath; FilePath watchedFilePath;
QMap<IDocument *, FileStateItem> lastUpdatedState; QMap<IDocument *, FileStateItem> lastUpdatedState;
FileStateItem expected; FileStateItem expected;
}; };
@@ -174,11 +174,11 @@ public:
void registerSaveAllAction(); void registerSaveAllAction();
QMap<QString, FileState> m_states; // filePathKey -> FileState QMap<FilePath, FileState> m_states; // filePathKey -> FileState
QSet<QString> m_changedFiles; // watched file paths collected from file watcher notifications QSet<FilePath> m_changedFiles; // watched file paths collected from file watcher notifications
QList<IDocument *> m_documentsWithoutWatch; QList<IDocument *> m_documentsWithoutWatch;
QMap<IDocument *, QStringList> m_documentsWithWatch; // document -> list of filePathKeys QMap<IDocument *, FilePaths> m_documentsWithWatch; // document -> list of filePathKeys
QSet<Utils::FilePath> m_expectedFileNames; // set of file paths without normalization QSet<FilePath> m_expectedFileNames; // set of file paths without normalization
QList<DocumentManager::RecentFile> m_recentFiles; QList<DocumentManager::RecentFile> m_recentFiles;
@@ -303,14 +303,13 @@ DocumentManager *DocumentManager::instance()
/* Only called from addFileInfo(IDocument *). Adds the document & state to various caches/lists, /* Only called from addFileInfo(IDocument *). Adds the document & state to various caches/lists,
but does not actually add a watcher. */ but does not actually add a watcher. */
static void addFileInfo(IDocument *document, const QString &filePath, const QString &filePathKey) static void addFileInfo(IDocument *document, const FilePath &filePath, const FilePath &filePathKey)
{ {
FileStateItem state; FileStateItem state;
if (!filePath.isEmpty()) { if (!filePath.isEmpty()) {
qCDebug(log) << "adding document for" << filePath << "(" << filePathKey << ")"; qCDebug(log) << "adding document for" << filePath << "(" << filePathKey << ")";
const QFileInfo fi(filePath); state.modified = filePath.lastModified();
state.modified = fi.lastModified(); state.permissions = filePath.permissions();
state.permissions = fi.permissions();
// Add state if we don't have already // Add state if we don't have already
if (!d->m_states.contains(filePathKey)) { if (!d->m_states.contains(filePathKey)) {
FileState state; FileState state;
@@ -328,27 +327,22 @@ static void addFileInfo(IDocument *document, const QString &filePath, const QStr
(The added file names are guaranteed to be absolute and cleaned.) */ (The added file names are guaranteed to be absolute and cleaned.) */
static void addFileInfos(const QList<IDocument *> &documents) static void addFileInfos(const QList<IDocument *> &documents)
{ {
QStringList pathsToWatch; FilePaths pathsToWatch;
QStringList linkPathsToWatch; FilePaths linkPathsToWatch;
for (IDocument *document : documents) { for (IDocument *document : documents) {
const QString documentFilePath = document->filePath().toString(); const FilePath filePath = DocumentManager::filePathKey(document->filePath(),
const QString filePath = DocumentManager::cleanAbsoluteFilePath(documentFilePath,
DocumentManager::KeepLinks); DocumentManager::KeepLinks);
const QString filePathKey = DocumentManager::filePathKey(documentFilePath, const FilePath resolvedFilePath = filePath.canonicalPath();
DocumentManager::KeepLinks);
const QString resolvedFilePath
= DocumentManager::cleanAbsoluteFilePath(documentFilePath,
DocumentManager::ResolveLinks);
const QString resolvedFilePathKey
= DocumentManager::filePathKey(documentFilePath, DocumentManager::ResolveLinks);
const bool isLink = filePath != resolvedFilePath; const bool isLink = filePath != resolvedFilePath;
addFileInfo(document, filePath, filePathKey); addFileInfo(document, filePath, filePath);
if (isLink) { if (isLink) {
addFileInfo(document, resolvedFilePath, resolvedFilePathKey); addFileInfo(document, resolvedFilePath, resolvedFilePath);
linkPathsToWatch.append(d->m_states.value(filePathKey).watchedFilePath); if (!filePath.needsDevice()) {
pathsToWatch.append(d->m_states.value(resolvedFilePathKey).watchedFilePath); linkPathsToWatch.append(d->m_states.value(filePath).watchedFilePath);
} else { pathsToWatch.append(d->m_states.value(resolvedFilePath).watchedFilePath);
pathsToWatch.append(d->m_states.value(filePathKey).watchedFilePath); }
} else if (!filePath.needsDevice()) {
pathsToWatch.append(d->m_states.value(filePath).watchedFilePath);
} }
} }
// Add or update watcher on file path // Add or update watcher on file path
@@ -356,11 +350,11 @@ static void addFileInfos(const QList<IDocument *> &documents)
// update link targets, even if there are multiple documents registered for it // update link targets, even if there are multiple documents registered for it
if (!pathsToWatch.isEmpty()) { if (!pathsToWatch.isEmpty()) {
qCDebug(log) << "adding full watch for" << pathsToWatch; qCDebug(log) << "adding full watch for" << pathsToWatch;
d->fileWatcher()->addPaths(pathsToWatch); d->fileWatcher()->addPaths(Utils::transform(pathsToWatch, &FilePath::toString));
} }
if (!linkPathsToWatch.isEmpty()) { if (!linkPathsToWatch.isEmpty()) {
qCDebug(log) << "adding link watch for" << linkPathsToWatch; qCDebug(log) << "adding link watch for" << linkPathsToWatch;
d->linkWatcher()->addPaths(linkPathsToWatch); d->linkWatcher()->addPaths(Utils::transform(linkPathsToWatch, &FilePath::toString));
} }
} }
@@ -409,22 +403,27 @@ static void removeFileInfo(IDocument *document)
{ {
if (!d->m_documentsWithWatch.contains(document)) if (!d->m_documentsWithWatch.contains(document))
return; return;
foreach (const QString &fileName, d->m_documentsWithWatch.value(document)) { foreach (const FilePath &filePath, d->m_documentsWithWatch.value(document)) {
if (!d->m_states.contains(fileName)) if (!d->m_states.contains(filePath))
continue; continue;
qCDebug(log) << "removing document (" << fileName << ")"; qCDebug(log) << "removing document (" << filePath << ")";
d->m_states[fileName].lastUpdatedState.remove(document); d->m_states[filePath].lastUpdatedState.remove(document);
if (d->m_states.value(fileName).lastUpdatedState.isEmpty()) { if (d->m_states.value(filePath).lastUpdatedState.isEmpty()) {
const QString &watchedFilePath = d->m_states.value(fileName).watchedFilePath; const FilePath &watchedFilePath = d->m_states.value(filePath).watchedFilePath;
if (d->m_fileWatcher && d->m_fileWatcher->files().contains(watchedFilePath)) { if (!watchedFilePath.needsDevice()) {
qCDebug(log) << "removing watch for" << watchedFilePath; const QString &localFilePath = watchedFilePath.path();
d->m_fileWatcher->removePath(watchedFilePath); if (d->m_fileWatcher
&& d->m_fileWatcher->files().contains(localFilePath)) {
qCDebug(log) << "removing watch for" << localFilePath;
d->m_fileWatcher->removePath(localFilePath);
} }
if (d->m_linkWatcher && d->m_linkWatcher->files().contains(watchedFilePath)) { if (d->m_linkWatcher
qCDebug(log) << "removing watch for" << watchedFilePath; && d->m_linkWatcher->files().contains(localFilePath)) {
d->m_linkWatcher->removePath(watchedFilePath); qCDebug(log) << "removing watch for" << localFilePath;
d->m_linkWatcher->removePath(localFilePath);
} }
d->m_states.remove(fileName); }
d->m_states.remove(filePath);
} }
} }
d->m_documentsWithWatch.remove(document); d->m_documentsWithWatch.remove(document);
@@ -484,7 +483,7 @@ void DocumentManager::renamedFile(const Utils::FilePath &from, const Utils::File
QList<IDocument *> documentsToRename; QList<IDocument *> documentsToRename;
for (auto it = d->m_documentsWithWatch.cbegin(), end = d->m_documentsWithWatch.cend(); for (auto it = d->m_documentsWithWatch.cbegin(), end = d->m_documentsWithWatch.cend();
it != end; ++it) { it != end; ++it) {
if (it.value().contains(fromKey.toString())) if (it.value().contains(fromKey))
documentsToRename.append(it.key()); documentsToRename.append(it.key());
} }
@@ -642,14 +641,14 @@ void DocumentManager::expectFileChange(const Utils::FilePath &filePath)
} }
/* only called from unblock and unexpect file change functions */ /* only called from unblock and unexpect file change functions */
static void updateExpectedState(const QString &filePathKey) static void updateExpectedState(const FilePath &filePathKey)
{ {
if (filePathKey.isEmpty()) if (filePathKey.isEmpty())
return; return;
if (d->m_states.contains(filePathKey)) { if (d->m_states.contains(filePathKey)) {
QFileInfo fi(d->m_states.value(filePathKey).watchedFilePath); const FilePath watched = d->m_states.value(filePathKey).watchedFilePath;
d->m_states[filePathKey].expected.modified = fi.lastModified(); d->m_states[filePathKey].expected.modified = watched.lastModified();
d->m_states[filePathKey].expected.permissions = fi.permissions(); d->m_states[filePathKey].expected.permissions = watched.permissions();
} }
} }
@@ -668,12 +667,11 @@ void DocumentManager::unexpectFileChange(const FilePath &filePath)
if (filePath.isEmpty()) if (filePath.isEmpty())
return; return;
d->m_expectedFileNames.remove(filePath); d->m_expectedFileNames.remove(filePath);
const QString &fileName = filePath.toString(); const FilePath cleanAbsFilePath = filePathKey(filePath, KeepLinks);
const QString cleanAbsFilePath = cleanAbsoluteFilePath(fileName, KeepLinks); updateExpectedState(filePathKey(filePath, KeepLinks));
updateExpectedState(filePathKey(fileName, KeepLinks)); const FilePath resolvedCleanAbsFilePath = cleanAbsFilePath.canonicalPath();
const QString resolvedCleanAbsFilePath = cleanAbsoluteFilePath(fileName, ResolveLinks);
if (cleanAbsFilePath != resolvedCleanAbsFilePath) if (cleanAbsFilePath != resolvedCleanAbsFilePath)
updateExpectedState(filePathKey(fileName, ResolveLinks)); updateExpectedState(filePathKey(filePath, ResolveLinks));
} }
static bool saveModifiedFilesHelper(const QList<IDocument *> &documents, static bool saveModifiedFilesHelper(const QList<IDocument *> &documents,
@@ -1076,11 +1074,12 @@ QStringList DocumentManager::getOpenFileNames(const QString &filters,
void DocumentManager::changedFile(const QString &fileName) void DocumentManager::changedFile(const QString &fileName)
{ {
const FilePath filePath = FilePath::fromString(fileName);
const bool wasempty = d->m_changedFiles.isEmpty(); const bool wasempty = d->m_changedFiles.isEmpty();
if (d->m_states.contains(filePathKey(fileName, KeepLinks))) if (d->m_states.contains(filePathKey(filePath, KeepLinks)))
d->m_changedFiles.insert(fileName); d->m_changedFiles.insert(filePath);
qCDebug(log) << "file change notification for" << fileName; qCDebug(log) << "file change notification for" << filePath;
if (wasempty && !d->m_changedFiles.isEmpty()) if (wasempty && !d->m_changedFiles.isEmpty())
QTimer::singleShot(200, this, &DocumentManager::checkForReload); QTimer::singleShot(200, this, &DocumentManager::checkForReload);
@@ -1116,21 +1115,20 @@ void DocumentManager::checkForReload()
QHash<IDocument*, QString> documentsToSave; QHash<IDocument*, QString> documentsToSave;
// collect file information // collect file information
QMap<QString, FileStateItem> currentStates; QMap<FilePath, FileStateItem> currentStates;
QMap<QString, IDocument::ChangeType> changeTypes; QMap<FilePath, IDocument::ChangeType> changeTypes;
QSet<IDocument *> changedIDocuments; QSet<IDocument *> changedIDocuments;
foreach (const QString &fileName, d->m_changedFiles) { foreach (const FilePath &filePath, d->m_changedFiles) {
const QString fileKey = filePathKey(fileName, KeepLinks); const FilePath fileKey = filePathKey(filePath, KeepLinks);
qCDebug(log) << "handling file change for" << fileName << "(" << fileKey << ")"; qCDebug(log) << "handling file change for" << filePath << "(" << fileKey << ")";
IDocument::ChangeType type = IDocument::TypeContents; IDocument::ChangeType type = IDocument::TypeContents;
FileStateItem state; FileStateItem state;
QFileInfo fi(fileName); if (!filePath.exists()) {
if (!fi.exists()) {
qCDebug(log) << "file was removed"; qCDebug(log) << "file was removed";
type = IDocument::TypeRemoved; type = IDocument::TypeRemoved;
} else { } else {
state.modified = fi.lastModified(); state.modified = filePath.lastModified();
state.permissions = fi.permissions(); state.permissions = filePath.permissions();
qCDebug(log) << "file was modified, time:" << state.modified qCDebug(log) << "file was modified, time:" << state.modified
<< "permissions: " << state.permissions; << "permissions: " << state.permissions;
} }
@@ -1148,14 +1146,13 @@ void DocumentManager::checkForReload()
// we can't do the "resolving" already in expectFileChange, because // we can't do the "resolving" already in expectFileChange, because
// if the resolved names are different when unexpectFileChange is called // if the resolved names are different when unexpectFileChange is called
// we would end up with never-unexpected file names // we would end up with never-unexpected file names
QSet<QString> expectedFileKeys; QSet<FilePath> expectedFileKeys;
foreach (const Utils::FilePath &filePath, d->m_expectedFileNames) { foreach (const FilePath &filePath, d->m_expectedFileNames) {
const QString &fileName = filePath.toString(); const FilePath cleanAbsFilePath = filePathKey(filePath, KeepLinks);
const QString cleanAbsFilePath = cleanAbsoluteFilePath(fileName, KeepLinks); expectedFileKeys.insert(filePathKey(filePath, KeepLinks));
expectedFileKeys.insert(filePathKey(fileName, KeepLinks)); const FilePath resolvedCleanAbsFilePath = cleanAbsFilePath.canonicalPath();
const QString resolvedCleanAbsFilePath = cleanAbsoluteFilePath(fileName, ResolveLinks);
if (cleanAbsFilePath != resolvedCleanAbsFilePath) if (cleanAbsFilePath != resolvedCleanAbsFilePath)
expectedFileKeys.insert(filePathKey(fileName, ResolveLinks)); expectedFileKeys.insert(filePathKey(filePath, ResolveLinks));
} }
// handle the IDocuments // handle the IDocuments
@@ -1168,7 +1165,7 @@ void DocumentManager::checkForReload()
// find out the type & behavior from the two possible files // find out the type & behavior from the two possible files
// behavior is internal if all changes are expected (and none removed) // behavior is internal if all changes are expected (and none removed)
// type is "max" of both types (remove > contents > permissions) // type is "max" of both types (remove > contents > permissions)
foreach (const QString &fileKey, d->m_documentsWithWatch.value(document)) { foreach (const FilePath &fileKey, d->m_documentsWithWatch.value(document)) {
// was the file reported? // was the file reported?
if (!currentStates.contains(fileKey)) if (!currentStates.contains(fileKey))
continue; continue;
@@ -1355,9 +1352,9 @@ void DocumentManager::addToRecentFiles(const Utils::FilePath &filePath, Id edito
{ {
if (filePath.isEmpty()) if (filePath.isEmpty())
return; return;
const QString fileKey = filePathKey(filePath.toString(), KeepLinks); const FilePath fileKey = filePathKey(filePath, KeepLinks);
Utils::erase(d->m_recentFiles, [fileKey](const RecentFile &file) { Utils::erase(d->m_recentFiles, [fileKey](const RecentFile &file) {
return fileKey == filePathKey(file.first.toString(), DocumentManager::KeepLinks); return fileKey == filePathKey(file.first, DocumentManager::KeepLinks);
}); });
while (d->m_recentFiles.count() >= EditorManagerPrivate::maxRecentFiles()) while (d->m_recentFiles.count() >= EditorManagerPrivate::maxRecentFiles())
d->m_recentFiles.removeLast(); d->m_recentFiles.removeLast();