From ffc55ee17610a8c47a24b42e6b18ee0e9bc9c832 Mon Sep 17 00:00:00 2001 From: Uffe Jakobsen Date: Mon, 13 Jan 2025 15:00:48 +0100 Subject: [PATCH] Improve CodeLite symlink handling This patch improves CodeLite with projects that have symlinks in the path If you open a file in a project that has symlinks in its path - you'll see the expanded/resolved path of the file in the window title. Also you'll typically see that CodeLite is unable find the file in the Workspace View There are several problems in respect to symlinks and the use of realpath() calls that this patch addresses: - Clangd LSP returns realpath() paths - mainbook uses realpath() extensively to figure out if a file is already opened (in another tab). - CodeLite Find-In-Files uses realpath() extensively (and in the end - clicking on the found file opens it) - etc... This patch basically fakes the use of FileUtils::RealPath() calls - on most occasions just returning the unmodified path. An optional argument to FileUtils::RealPath() (defaults to false) can force the realpath() conversion (this is only used in very few occasions in this patch) --- CodeLite/cl_config.h | 1 + CodeLite/fileutils.cpp | 25 +++++++++++++----- CodeLite/fileutils.h | 4 ++- LiteEditor/app.cpp | 5 +++- LiteEditor/editorsettingsmiscpanel.cpp | 12 ++++++++- LiteEditor/mainbook.cpp | 35 ++++++++++++++++---------- Plugin/globals.cpp | 2 +- 7 files changed, 61 insertions(+), 23 deletions(-) diff --git a/CodeLite/cl_config.h b/CodeLite/cl_config.h index 7ac6cc7876..a591180a81 100644 --- a/CodeLite/cl_config.h +++ b/CodeLite/cl_config.h @@ -93,6 +93,7 @@ class WXDLLIMPEXP_CL clConfigItem #define kConfigWorkspaceTabSashPosition "WorkspaceTabSashPosition" #define kConfigTabsPaneSortAlphabetically "TabsPaneSortAlphabetically" #define kConfigFileExplorerBookmarks "FileExplorerBookmarks" +#define kRealPathResolveSymlinks "RealPathResolveSymlinks" class WXDLLIMPEXP_CL clConfig { diff --git a/CodeLite/fileutils.cpp b/CodeLite/fileutils.cpp index 36a77860a6..73f3ed3517 100644 --- a/CodeLite/fileutils.cpp +++ b/CodeLite/fileutils.cpp @@ -62,6 +62,8 @@ #include #include +static bool bRealPathModeResolveSymlinks = true; + thread_local std::unordered_set VALID_CHARS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', @@ -212,7 +214,9 @@ void FileUtils::OpenFileExplorerAndSelect(const wxFileName& filename) #endif } -void FileUtils::OSXOpenDebuggerTerminalAndGetTTY(const wxString& path, const wxString& appname, wxString& tty, +void FileUtils::OSXOpenDebuggerTerminalAndGetTTY(const wxString& path, + const wxString& appname, + wxString& tty, long& pid) { tty.Clear(); @@ -274,7 +278,9 @@ void FileUtils::OSXOpenDebuggerTerminalAndGetTTY(const wxString& path, const wxS clDEBUG() << "TTY is:" << tty; } -void FileUtils::OpenSSHTerminal(const wxString& sshClient, const wxString& connectString, const wxString& password, +void FileUtils::OpenSSHTerminal(const wxString& sshClient, + const wxString& connectString, + const wxString& password, int port) { clConsoleBase::Ptr_t console = clConsoleBase::GetTerminal(); @@ -595,10 +601,10 @@ unsigned int FileUtils::UTF8Length(const wchar_t* uptr, unsigned int tlen) } // This is readlink on steroids: it also makes-absolute, and dereferences any symlinked dirs in the path -wxString FileUtils::RealPath(const wxString& filepath) +wxString FileUtils::RealPath(const wxString& filepath, bool forced) { #if defined(__WXGTK__) || defined(__WXOSX__) - if (!filepath.empty()) { + if (!filepath.empty() && (forced || bRealPathModeResolveSymlinks)) { #if defined(__FreeBSD__) || defined(__WXOSX__) wxStructStat stbuff; if ((::wxLstat(filepath, &stbuff) != 0) || !S_ISLNK(stbuff.st_mode)) { @@ -617,6 +623,10 @@ wxString FileUtils::RealPath(const wxString& filepath) return filepath; } +bool FileUtils::RealPathGetModeResolveSymlinks(void) { return bRealPathModeResolveSymlinks; } + +void FileUtils::RealPathSetModeResolveSymlinks(bool resolveSymlinks) { bRealPathModeResolveSymlinks = resolveSymlinks; } + std::string FileUtils::ToStdString(const wxString& str) { return StringUtils::ToStdString(str); } bool FileUtils::ReadBufferFromFile(const wxFileName& fn, wxString& data, size_t bufferSize) @@ -741,7 +751,9 @@ bool DoFindExe(const wxString& name, wxFileName& exepath, const wxArrayString& h } } // namespace -bool FileUtils::FindExe(const wxString& name, wxFileName& exepath, const wxArrayString& hint, +bool FileUtils::FindExe(const wxString& name, + wxFileName& exepath, + const wxArrayString& hint, const wxArrayString& suffix_list) { wxArrayString possible_suffix; @@ -780,7 +792,8 @@ wxFileName FileUtils::CreateTempFileName(const wxString& folder, const wxString& return wxFileName(folder, full_name); } -size_t FileUtils::FindSimilar(const wxFileName& filename, const std::vector& extensions, +size_t FileUtils::FindSimilar(const wxFileName& filename, + const std::vector& extensions, std::vector& vout) { wxFileName fn(filename); diff --git a/CodeLite/fileutils.h b/CodeLite/fileutils.h index 618e0fed57..d7d1e7a6a9 100644 --- a/CodeLite/fileutils.h +++ b/CodeLite/fileutils.h @@ -247,7 +247,9 @@ class WXDLLIMPEXP_CL FileUtils /** * @brief (on Linux) makes-absolute filepath, and dereferences it and any symlinked dirs in the path */ - static wxString RealPath(const wxString& filepath); + static wxString RealPath(const wxString& filepath, bool forced=false); + static bool RealPathGetModeResolveSymlinks(void); + static void RealPathSetModeResolveSymlinks(bool resolveSymlinks); /** * @brief convert string into std::string diff --git a/LiteEditor/app.cpp b/LiteEditor/app.cpp index 3f0eb452d7..5f09445fc7 100644 --- a/LiteEditor/app.cpp +++ b/LiteEditor/app.cpp @@ -739,6 +739,9 @@ bool CodeLiteApp::OnInit() // If running under Cygwin terminal, adjust the environment variables AdjustPathForMSYSIfNeeded(); + // Ensure that FileUtils::RealPath handling is setup according to config + FileUtils::RealPathSetModeResolveSymlinks(clConfig::Get().Read(kRealPathResolveSymlinks, true)); + // Make sure that the colours and fonts manager is instantiated ColoursAndFontsManager::Get().Load(); @@ -1152,4 +1155,4 @@ void CodeLiteApp::FinalizeShutdown() // Delete the temp folder wxFileName::Rmdir(clStandardPaths::Get().GetTempDir(), wxPATH_RMDIR_RECURSIVE); clDEBUG() << "Finalizing shutdown...success" << endl; -} \ No newline at end of file +} diff --git a/LiteEditor/editorsettingsmiscpanel.cpp b/LiteEditor/editorsettingsmiscpanel.cpp index 90ed26ded2..c6d9471927 100644 --- a/LiteEditor/editorsettingsmiscpanel.cpp +++ b/LiteEditor/editorsettingsmiscpanel.cpp @@ -57,7 +57,17 @@ EditorSettingsMiscPanel::EditorSettingsMiscPanel(wxWindow* parent, OptionsConfig #endif AddProperty(_("Frame title"), clConfig::Get().Read(kConfigFrameTitlePattern, wxString("$workspace $fullpath")), UPDATE_CLCONFIG_TEXT_CB(kConfigFrameTitlePattern)); - + AddHeader(_("File path handling")); + AddProperty(_("Resolve symlinks in file paths"), + clConfig::Get().Read(kRealPathResolveSymlinks, true), + [](const wxString& label, const wxAny& value) { + wxUnusedVar(label); + bool value_bool = true; + if (value.GetAs(&value_bool)) { + clConfig::Get().Write(kRealPathResolveSymlinks, value_bool); + } + FileUtils::RealPathSetModeResolveSymlinks(value_bool); + }); AddHeader(_("Startup")); AddProperty(_("Check for new version on startup"), clConfig::Get().Read(kConfigCheckForNewVersion, true), UPDATE_CLCONFIG_BOOL_CB(kConfigCheckForNewVersion)); diff --git a/LiteEditor/mainbook.cpp b/LiteEditor/mainbook.cpp index 6db5cb2385..277cdf9900 100644 --- a/LiteEditor/mainbook.cpp +++ b/LiteEditor/mainbook.cpp @@ -258,7 +258,7 @@ void MainBook::OnProjectFileAdded(clCommandEvent& e) for (size_t i = 0; i < files.GetCount(); i++) { clEditor* editor = FindEditor(files.Item(i)); if (editor) { - wxString fileName = CLRealPath(editor->GetFileName().GetFullPath()); + wxString fileName = FileUtils::RealPath(editor->GetFileName().GetFullPath()); if (files.Index(fileName) != wxNOT_FOUND) { editor->SetProject(ManagerST::Get()->GetProjectNameByFile(fileName)); } @@ -272,7 +272,7 @@ void MainBook::OnProjectFileRemoved(clCommandEvent& e) const wxArrayString& files = e.GetStrings(); for (size_t i = 0; i < files.GetCount(); ++i) { clEditor* editor = FindEditor(files.Item(i)); - if (editor && files.Index(CLRealPath(editor->GetFileName().GetFullPath())) != wxNOT_FOUND) { + if (editor && files.Index(FileUtils::RealPath(editor->GetFileName().GetFullPath())) != wxNOT_FOUND) { editor->SetProject(wxEmptyString); } } @@ -451,7 +451,7 @@ int MainBook::FindEditorIndexByFullPath(const wxString& fullpath) { #ifdef __WXGTK__ // On gtk either fileName or the editor filepath (or both) may be (or their paths contain) symlinks - wxString fileNameDest = CLRealPath(fullpath); + wxString fileNameDest = FileUtils::RealPath(fullpath, true); #endif for (size_t i = 0; i < m_book->GetPageCount(); ++i) { @@ -465,7 +465,7 @@ int MainBook::FindEditorIndexByFullPath(const wxString& fullpath) } } else { // local path - wxString unixStyleFile(CLRealPath(editor->GetFileName().GetFullPath())); + wxString unixStyleFile(FileUtils::RealPath(editor->GetFileName().GetFullPath())); wxString nativeFile(unixStyleFile); #ifdef __WXMSW__ unixStyleFile.Replace(wxT("\\"), wxT("/")); @@ -485,7 +485,7 @@ int MainBook::FindEditorIndexByFullPath(const wxString& fullpath) #if defined(__WXGTK__) // Try again, dereferencing the editor fpath - wxString editorDest = CLRealPath(unixStyleFile); + wxString editorDest = FileUtils::RealPath(unixStyleFile, true); if (editorDest.Cmp(fullpath) == 0 || editorDest.Cmp(fileNameDest) == 0) { return i; } @@ -509,12 +509,13 @@ wxWindow* MainBook::FindPage(const wxString& text) { for (size_t i = 0; i < m_book->GetPageCount(); i++) { clEditor* editor = dynamic_cast(m_book->GetPage(i)); - if (editor && CLRealPath(editor->GetFileName().GetFullPath()).CmpNoCase(text) == 0) { + if (editor && FileUtils::RealPath(editor->GetFileName().GetFullPath()).CmpNoCase(text) == 0) { return editor; } - if (m_book->GetPageText(i) == text) + if (m_book->GetPageText(i) == text) { return m_book->GetPage(i); + } } return NULL; } @@ -620,7 +621,14 @@ clEditor* MainBook::OpenFile(const wxString& file_name, int bmp /*= wxNullBitmap*/, const wxString& tooltip /* wxEmptyString */) { - wxFileName fileName(CLRealPath(file_name)); + wxFileName fileName(FileUtils::RealPath(file_name)); + + if (fileName.IsRelative()) { + if (clWorkspaceManager::Get().IsWorkspaceOpened()) { + wxFileName wsPath = clWorkspaceManager::Get().GetWorkspace()->GetDir(); + fileName.MakeAbsolute(wsPath.GetFullPath()); + } + } fileName.MakeAbsolute(); #ifdef __WXMSW__ @@ -1220,7 +1228,7 @@ bool MainBook::DoSelectPage(wxWindow* win) } else { wxCommandEvent event(wxEVT_ACTIVE_EDITOR_CHANGED); - event.SetString(CLRealPath(editor->GetFileName().GetFullPath())); + event.SetString(FileUtils::RealPath(editor->GetFileName().GetFullPath())); EventNotifier::Get()->AddPendingEvent(event); } return true; @@ -1737,7 +1745,7 @@ WelcomePage* MainBook::GetWelcomePage(bool createIfMissing) clEditor* MainBook::OpenFileAsync(const wxString& file_name, std::function&& callback) { - wxString real_path = CLRealPath(file_name); + wxString real_path = FileUtils::RealPath(file_name, true); auto editor = FindEditor(real_path); if (editor) { push_callback(std::move(callback), real_path); @@ -1748,7 +1756,7 @@ clEditor* MainBook::OpenFileAsync(const wxString& file_name, std::functionSetSelection(index); } } else { - editor = OpenFile(real_path); + editor = OpenFile(file_name); if (editor) { push_callback(std::move(callback), real_path); } @@ -1813,11 +1821,12 @@ void MainBook::OnIdle(wxIdleEvent& event) auto editor = GetActiveEditor(); CHECK_PTR_RET(editor); - execute_callbacks_for_file(CLRealPath(editor->GetFileName().GetFullPath())); + execute_callbacks_for_file(FileUtils::RealPath(editor->GetFileName().GetFullPath(), true)); } void MainBook::OnEditorModified(clCommandEvent& event) { event.Skip(); } void MainBook::OnEditorSaved(clCommandEvent& event) { event.Skip(); } -void MainBook::OnSessionLoaded(clCommandEvent& event) { event.Skip(); } \ No newline at end of file +void MainBook::OnSessionLoaded(clCommandEvent& event) { event.Skip(); } + diff --git a/Plugin/globals.cpp b/Plugin/globals.cpp index 0d5d01ad33..a2ee25a5ce 100644 --- a/Plugin/globals.cpp +++ b/Plugin/globals.cpp @@ -928,7 +928,7 @@ wxFileName wxReadLink(const wxFileName& filename) if (wxIsFileSymlink(filename)) { #if defined(__WXGTK__) // Use 'realpath' on Linux, otherwise this breaks on relative symlinks, and (untested) on symlinks-to-symlinks - return wxFileName(CLRealPath(filename.GetFullPath())); + return wxFileName(FileUtils::RealPath(filename.GetFullPath(), true)); #else // OSX wxFileName realFileName;