Skip to content

Commit

Permalink
Improve CodeLite symlink handling
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
UffeJakobsen committed Jan 12, 2025
1 parent a835f35 commit e6729ce
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 17 deletions.
18 changes: 16 additions & 2 deletions CodeLite/fileutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
#include <memory>
#include <wx/filename.h>


static bool bRealPathModeResolveSymlinks = true;

thread_local std::unordered_set<wxChar> 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',
Expand Down Expand Up @@ -595,10 +598,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)) {
Expand All @@ -617,6 +620,17 @@ 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)
Expand Down
4 changes: 3 additions & 1 deletion CodeLite/fileutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions LiteEditor/editorsettingsdockingwidows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,8 @@ EditorSettingsDockingWindows::EditorSettingsDockingWindows(wxWindow* parent, Opt
AddProperty(_("Find next/Find previous actions override search string with current selection (like Find next at caret/prev at caret)"),
m_options->GetFindNextOrPreviousUseSelection(),
UPDATE_BOOL_CB(SetFindNextOrPreviousUseSelection));
AddHeader(_("File path handling"));
AddProperty(_("Resolve symlinks in file paths"),
m_options->GetRealPathResolveSymlinks(),
UPDATE_BOOL_CB(SetRealPathResolveSymlinks));
}
35 changes: 22 additions & 13 deletions LiteEditor/mainbook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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("/"));
Expand All @@ -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;
}
Expand All @@ -509,12 +509,13 @@ wxWindow* MainBook::FindPage(const wxString& text)
{
for (size_t i = 0; i < m_book->GetPageCount(); i++) {
clEditor* editor = dynamic_cast<clEditor*>(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;
}
Expand Down Expand Up @@ -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__
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1737,7 +1745,7 @@ WelcomePage* MainBook::GetWelcomePage(bool createIfMissing)

clEditor* MainBook::OpenFileAsync(const wxString& file_name, std::function<void(IEditor*)>&& 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);
Expand All @@ -1748,7 +1756,7 @@ clEditor* MainBook::OpenFileAsync(const wxString& file_name, std::function<void(
m_book->SetSelection(index);
}
} else {
editor = OpenFile(real_path);
editor = OpenFile(file_name);
if (editor) {
push_callback(std::move(callback), real_path);
}
Expand Down Expand Up @@ -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(); }
void MainBook::OnSessionLoaded(clCommandEvent& event) { event.Skip(); }

2 changes: 1 addition & 1 deletion Plugin/globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions Plugin/optionsconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ OptionsConfig::OptionsConfig(wxXmlNode* node)
, m_dontAutoFoldResults(true)
, m_dontOverrideSearchStringWithSelection(false)
, m_findNextOrPreviousUseSelection(true)
, m_realPathResolveSymlinks(true)
, m_showDebugOnRun(true)
, m_caretUseCamelCase(true)
, m_wordWrap(false)
Expand Down Expand Up @@ -211,6 +212,9 @@ OptionsConfig::OptionsConfig(wxXmlNode* node)
node, wxT("DontOverrideSearchStringWithSelection"), m_dontOverrideSearchStringWithSelection);
m_findNextOrPreviousUseSelection = XmlUtils::ReadBool(
node, wxT("FindNextOrPreviousUseSelection"), m_findNextOrPreviousUseSelection);
m_realPathResolveSymlinks = XmlUtils::ReadBool(
node, wxT("RealPathResolveSymlinks"), m_realPathResolveSymlinks);
FileUtils::RealPathSetModeResolveSymlinks(m_realPathResolveSymlinks);
m_showDebugOnRun = XmlUtils::ReadBool(node, wxT("ShowDebugOnRun"), m_showDebugOnRun);
m_caretUseCamelCase = XmlUtils::ReadBool(node, wxT("m_caretUseCamelCase"), m_caretUseCamelCase);
m_wordWrap = XmlUtils::ReadBool(node, wxT("m_wordWrap"), m_wordWrap);
Expand Down Expand Up @@ -315,6 +319,8 @@ wxXmlNode* OptionsConfig::ToXml() const
BoolToString(m_dontOverrideSearchStringWithSelection));
n->AddAttribute(wxT("FindNextOrPreviousUseSelection"),
BoolToString(m_findNextOrPreviousUseSelection));
n->AddAttribute(wxT("RealPathResolveSymlinks"),
BoolToString(m_realPathResolveSymlinks));
n->AddAttribute(wxT("ShowDebugOnRun"), BoolToString(m_showDebugOnRun));
n->AddAttribute(wxT("ConsoleCommand"), m_programConsoleCommand);
n->AddAttribute(wxT("EOLMode"), m_eolMode);
Expand Down
8 changes: 8 additions & 0 deletions Plugin/optionsconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "clEditorConfig.h"
#include "codelite_exports.h"
#include "configuration_object.h"
#include "fileutils.h"

#include <memory>
#include <wx/colour.h>
Expand Down Expand Up @@ -142,6 +143,7 @@ class WXDLLIMPEXP_SDK OptionsConfig : public ConfObject
bool m_dontAutoFoldResults;
bool m_dontOverrideSearchStringWithSelection;
bool m_findNextOrPreviousUseSelection;
bool m_realPathResolveSymlinks;
bool m_showDebugOnRun;
bool m_caretUseCamelCase;
bool m_dontTrimCaretLine;
Expand Down Expand Up @@ -232,6 +234,12 @@ class WXDLLIMPEXP_SDK OptionsConfig : public ConfObject
m_findNextOrPreviousUseSelection = findNextOrPreviousUseSelection;
}
bool GetFindNextOrPreviousUseSelection() const { return m_findNextOrPreviousUseSelection; }
void SetRealPathResolveSymlinks(bool realPathResolveSymlinks)
{
m_realPathResolveSymlinks = realPathResolveSymlinks;
FileUtils::RealPathSetModeResolveSymlinks(m_realPathResolveSymlinks);
}
bool GetRealPathResolveSymlinks() const { return m_realPathResolveSymlinks; }
void SetShowDebugOnRun(bool showDebugOnRun) { this->m_showDebugOnRun = showDebugOnRun; }
bool GetShowDebugOnRun() const { return m_showDebugOnRun; }
bool GetDisableSemicolonShift() const { return m_disableSemicolonShift; }
Expand Down

0 comments on commit e6729ce

Please sign in to comment.