Author Topic: Bug in 'swap header / source'  (Read 26109 times)

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Bug in 'swap header / source'
« on: October 14, 2009, 05:01:59 am »
I have a project that comprises many directories, and in those directories the same file names are generally repeated.  Basically:

<root>
---<sub dir1>
    Config.cpp
    Config.h
---<sub dir2>
    Config.cpp
    Config.h


What I find is that if I open Config.cpp in <sub dir2> and right click on the tab and select 'swap header / source', I get Config.h from <sub dir1>.

What I find interesting is that if I go to the '#include <Config.h>' line in Config.cpp in <sub dir2> and right click and select 'Open #include file: Config.h', the proper Config.h (from <sub dir2>) is opened.

Offline stahta01

  • Lives here!
  • ****
  • Posts: 7591
    • My Best Post
Re: Bug in 'swap header / source'
« Reply #1 on: October 14, 2009, 06:16:30 am »
Might be a Code::Blocks bug.

FYI, You should be using '#include "Config.h"' instead of '#include <Config.h>'  the "<>" means system header in many C compilers. But, this should not cause the problem you are seeing. I have no idea where to look for cause and not the time to duplicate it.

Tim S.
C Programmer working to learn more about C++ and Git.
On Windows 7 64 bit and Windows 10 64 bit.
--
When in doubt, read the CB WiKi FAQ. http://wiki.codeblocks.org

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: Bug in 'swap header / source'
« Reply #2 on: October 14, 2009, 10:07:03 am »
I've observed the same bug/missing feature
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5915
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Bug in 'swap header / source'
« Reply #3 on: October 14, 2009, 10:30:01 am »
rev 5850

Quote
* applied (modified) patch #2803 - faster header source swapping (thanks techy)

This function has recently improved. seems a regression. :D
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #4 on: October 14, 2009, 10:39:00 am »
Confirmed. If there is more than one file with the same name, it is semi-random which file you get.
From looking at the source it seems like Code::Blocks is simply listing all possible directories and then picks a file with a suitable name. At least I guess that is what it is doing. A single function consisting of over 200 lines of codes (several screen pages) is a bit hard to parse. Am I the only one who thinks, that Code::Blocks is in dire need of some refactoring?

A quick fix might be to first look in the same direcotry as the original file. But actually the situation is a bit more complicated. Let's assume this directory layout:

A
-- include
---- foo.h
-- src
---- foo.cpp
B
-- include
---- foo.h
-- src
---- foo.cpp

If you want to support these, you need a way to tell the Swap-feature about your directory layout (some additional configuration data in the project file, maybe). One *could* try to derive an algorithm instead for guessing the right file, but to me this sounds just lke asking for trouble.


Offline stahta01

  • Lives here!
  • ****
  • Posts: 7591
    • My Best Post
Re: Bug in 'swap header / source'
« Reply #5 on: October 14, 2009, 03:07:03 pm »
One *could* try to derive an algorithm instead for guessing the right file, but to me this sounds just lke asking for trouble.

The proper algorithm is the same one the Compiler Uses. Look for it the same way using the search directories from project settings.
Just it seems hard to do from my point of view.

Tim S.
C Programmer working to learn more about C++ and Git.
On Windows 7 64 bit and Windows 10 64 bit.
--
When in doubt, read the CB WiKi FAQ. http://wiki.codeblocks.org

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: Bug in 'swap header / source'
« Reply #6 on: October 14, 2009, 04:13:08 pm »
I would prefer a dialog, so I can choose the file I want
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #7 on: October 14, 2009, 04:14:39 pm »
Quote
The proper algorithm is the same one the Compiler Uses. Look for it the same way using the search directories from project settings.

Sorry, that is not correct. The algorithm for choosing include files (I assume that this is the one you mean) is entirely useless here. IIRC if you have more than one header file with the same name in your collection of include paths, gcc usually picks the first one.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #8 on: October 14, 2009, 04:16:35 pm »
Quote
I would prefer a dialog, so I can choose the file I want

That might be an option, though it would not handle the case where the corresponding h/cpp file does not exist yet (the swap feature used to offer an option to create it in this case).

Offline stahta01

  • Lives here!
  • ****
  • Posts: 7591
    • My Best Post
Re: Bug in 'swap header / source'
« Reply #9 on: October 14, 2009, 04:21:06 pm »
Quote
The proper algorithm is the same one the Compiler Uses. Look for it the same way using the search directories from project settings.

Sorry, that is not correct. The algorithm for choosing include files (I assume that this is the one you mean) is entirely useless here. IIRC if you have more than one header file with the same name in your collection of include paths, gcc usually picks the first one.

If the proper header file exists; it MUST use the same method. Or the solution is in-correct.
The problem is what to do if it does not exist.

Tim S.
C Programmer working to learn more about C++ and Git.
On Windows 7 64 bit and Windows 10 64 bit.
--
When in doubt, read the CB WiKi FAQ. http://wiki.codeblocks.org

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #10 on: October 14, 2009, 05:13:31 pm »
FYI, You should be using '#include "Config.h"' instead of '#include <Config.h>'  the "<>" means system header in many C compilers.

Yep, I use '#include "Config.h"', just a typo in my original post...

Cheers!

Chris

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5915
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Bug in 'swap header / source'
« Reply #11 on: October 14, 2009, 05:29:38 pm »
we can have a look at this function in editormanager.cpp

Code
bool EditorManager::SwapActiveHeaderSource()
{
    cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
    if (!ed)
        return false;

    FileType ft = FileTypeOf(ed->GetFilename());
    if (ft != ftHeader && ft != ftSource)
        return false;

    // because ft == ftHeader || ftSource, the extension has at least 1 character
    bool extStartsWithCapital = wxIsupper(wxFileName(ed->GetFilename()).GetExt()[0]);

    // create a list of search dirs
    wxArrayString dirs;
    cbProject* project = 0;

    // if the file in question belongs to a different open project,
    // then use that project instead.
    // this fixes locating the file's pair in a workspace when the active file
    // does not belong to the active project.
    ProjectFile* opf = ed->GetProjectFile();
    if (opf)
        project = opf->GetParentProject();

    // if we didn't get a valid project, try the active one
    if (!project)
        project = Manager::Get()->GetProjectManager()->GetActiveProject();

    if (project)
    {
        // get project's include dirs
        dirs = project->GetIncludeDirs();

        if (opf)
        {
            wxString const &activeName = opf->file.GetName();

            // first try to find the file among the opened files
            for (int i = 0; i < GetEditorsCount(); ++i)
            {
                cbEditor* edit = GetBuiltinEditor(GetEditor(i));
                if (!edit)
                    continue;

                ProjectFile* pf = edit->GetProjectFile();
                if (!pf)
                    continue;

                if (pf->file.GetName() == activeName)
                {
                    wxFileName const & fname = pf->file;
                    FileType ft_other = FileTypeOf(fname.GetFullName());
                    if (   (    ((ft == ftHeader) && (ft_other == ftSource))
                             || ((ft == ftSource) && (ft_other == ftHeader)) )
                        && (wxIsupper(fname.GetExt()[0]) == extStartsWithCapital) )
                    {
                        if (fname.FileExists())
                        {
                            cbEditor* newEd = Open(fname.GetFullPath());
                            if (newEd!=0L) // we found and were able to open it
                                return true; // --> RETURN
                        }
                    }
                }
            }

            // second try to find in the project files - at the same time
            // build the directory list for further searching if not
            // successful now
            for (int i = 0; i < project->GetFilesCount(); ++i)
            {
                ProjectFile* pf = project->GetFile(i);
                if (!pf)
                    continue;

                wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
                if (dirs.Index(dir) == wxNOT_FOUND)
                    dirs.Add(dir);

                if (pf->file.GetName() == activeName)
                {
                    wxFileName const & fname = pf->file;
                    FileType ft_other = FileTypeOf(fname.GetFullName());
                    if (   (    ((ft == ftHeader) && (ft_other == ftSource))
                             || ((ft == ftSource) && (ft_other == ftHeader)) )
                        && (wxIsupper(fname.GetExt()[0]) == extStartsWithCapital) )
                    {
                        if (fname.FileExists())
                        {
                            cbEditor* newEd = Open(fname.GetFullPath());
                            if (newEd!=0L) // we found and were able to open it
                                return true; // --> RETURN
                        }
                    }
                }
            }
        }
        else // no opf
        {
            // build the directory list for further searching if opf not available
            for (int i = 0; i < project->GetFilesCount(); ++i)
            {
                ProjectFile* pf = project->GetFile(i);
                if (!pf)
                    continue;

                wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
                if (dirs.Index(dir) == wxNOT_FOUND)
                    dirs.Add(dir);
            }
        }

        // if not found, continue building the list of directories for further searching

        // get targets include dirs
        for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
        {
            ProjectBuildTarget* target = project->GetBuildTarget(i);
            if (target)
            {
                for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
                {
                    // TODO (Morten#5#): target include dirs might override project include dirs, take append/prepend option into account
                    wxString dir = target->GetIncludeDirs()[ti];
                    if (dirs.Index(dir) == wxNOT_FOUND)
                        dirs.Add(dir);
                }
            }
        }
    } // project

    wxFileName fname;
    wxFileName fn(ed->GetFilename());
    dirs.Insert(fn.GetPath(wxPATH_GET_VOLUME), 0); // add file's dir

    for (unsigned int i = 0; i < dirs.GetCount(); ++i)
    {
        ProjectManager *pm = Manager::Get()->GetProjectManager();
        if ( !pm )
            break;

        wxString dir = dirs[i]; // might contain macros -> replace them
        Manager::Get()->GetMacrosManager()->ReplaceMacros(dir);

        fname.Assign(dir + wxFileName::GetPathSeparator() + fn.GetFullName());
//        Manager::Get()->GetLogManager()->DebugLog(F(_T("Looking for '%s', dir='%s'."), fname.GetFullPath().c_str(), dir.c_str()));
        if (!fname.IsAbsolute() && project)
        {
            fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, project->GetBasePath());
//            Manager::Get()->GetLogManager()->DebugLog(F(_T("Normalizing dir to '%s'."), fname.GetFullPath().c_str()));
        }

        wxString HeaderSource = pm->GetHeaderSource(fname);
        if (!HeaderSource.IsEmpty())
        {
            fname.SetFullName(HeaderSource);
//            Manager::Get()->GetLogManager()->DebugLog(F(_T("Located '%s'."), fname.GetFullPath().c_str()));
            break;
        }
    }

    if (fname.FileExists())
    {
        //Manager::Get()->GetLogManager()->DebugLog("ed=%s, pair=%s", ed->GetFilename().c_str(), pair.c_str());
        cbEditor* newEd = Open(fname.GetFullPath());
        if (newEd!=0L) // we found and were able to open it
            return true; // --> RETURN;
    }

    // We couldn't find the file, maybe it does not exist. Ask the user if we
    // should create it:
    if (cbMessageBox(_("The file seems not to exist. Do you want to create it?"),
                _("Error"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
    {
        cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (project)
            wxSetWorkingDirectory(project->GetBasePath());

        // Create a suggestion for the new file name:
        if (ft == ftHeader)
            fn.SetExt(FileFilters::CPP_EXT);
        else if (ft == ftSource)
            fn.SetExt(FileFilters::H_EXT);
        // else? Well, if the filename is not changed we could possibly
        // overwrite an existing file with our suggestion.

        cbEditor* newEd = New(fn.GetFullPath());
        if (project)
        {
            if (cbMessageBox(_("Do you want to add this new file in the active project?"),
                        _("Add file to project"),
                        wxYES_NO | wxICON_QUESTION) == wxID_YES)
            {
                wxArrayInt targets;
                if (Manager::Get()->GetProjectManager()->AddFileToProject(newEd->GetFilename(), project, targets) != 0)
                {
                    ProjectFile* pf = project->GetFileByFilename(newEd->GetFilename(), false);
                    newEd->SetProjectFile(pf);
                    Manager::Get()->GetProjectManager()->RebuildTree();
                }
            }
        }

        // verify that the open files are still in sync
        // the new file might have overwritten an existing one)
        Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
    }

    return false;
}
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #12 on: October 14, 2009, 09:52:02 pm »
Quote
The proper algorithm is the same one the Compiler Uses. Look for it the same way using the search directories from project settings.

Sorry, that is not correct. The algorithm for choosing include files (I assume that this is the one you mean) is entirely useless here. IIRC if you have more than one header file with the same name in your collection of include paths, gcc usually picks the first one.

If the proper header file exists; it MUST use the same method. Or the solution is in-correct.


No, it can't. If you have two header files with the same name, you can't use this method for swapping. And it doesn't work well for the compiler either. Therefore most people specify the name of the directory explicitly, when writing include instructions, e.g.:

#include "A/Header.h"
#include "B/Header.h"

This resolves the ambiguousity. But obviously this don't help with the swap feature (unless someone wants to imlement oBFusCATed's selection dialogue proposal, which would offer a way to explicitly specify the directory too).


Edit: Just noticed that my example a few post above was bad. Should look like this:

include
-- A
---- foo.h
-- B
---- foo.h
src
-- A
---- foo.cpp
-- B
---- foo.cpp

« Last Edit: October 14, 2009, 09:57:44 pm by Zini »

Offline stahta01

  • Lives here!
  • ****
  • Posts: 7591
    • My Best Post
Re: Bug in 'swap header / source'
« Reply #13 on: October 14, 2009, 10:35:41 pm »
#include "A/Header.h"
#include "B/Header.h"

The above is NOT legal for many embedded C Compilers.

But, it is a valid example of another issue to code around to solve the problem in most cases of using wxWidgets like includes.

I suggest someone makeup an dummy project with all the problem(s) and empty files except for includes and comment of the project paths in the file.

Tim S.
C Programmer working to learn more about C++ and Git.
On Windows 7 64 bit and Windows 10 64 bit.
--
When in doubt, read the CB WiKi FAQ. http://wiki.codeblocks.org

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #14 on: October 15, 2009, 11:19:09 am »
Quote
I suggest someone makeup an dummy project with all the problem(s) and empty files except for includes and comment of the project paths in the file.

Okay. These are all source layout-options I can think of.

Edit: re-uploaded the file with an additional edge-case.


[attachment deleted by admin]
« Last Edit: October 15, 2009, 11:27:05 am by Zini »

Offline Jenna

  • Administrator
  • Lives here!
  • *****
  • Posts: 7255
Re: Bug in 'swap header / source'
« Reply #15 on: October 15, 2009, 03:13:37 pm »
I looked into the code yesterday and see why it not asks to create the missing header-/source-file.

Creating (or more exactly asking for creating) the missing file in the same directoy as the original file is not a big problem, any other solution (like chosing the directory) would need a little (not much) more coding effort.

Offline Jenna

  • Administrator
  • Lives here!
  • *****
  • Posts: 7255
Re: Bug in 'swap header / source'
« Reply #16 on: October 15, 2009, 09:29:10 pm »
Should work in svn r5865 again.

The opposite file is created in the same directory as the file we tried to switch from.
The behaviour should now be the same as before.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #17 on: October 16, 2009, 04:34:13 am »
we can have a look at this function in editormanager.cpp

Code
bool EditorManager::SwapActiveHeaderSource()
{
    cbEditor* ed = GetBuiltinEditor(GetActiveEditor());
    if (!ed)
        return false;

    FileType ft = FileTypeOf(ed->GetFilename());
    if (ft != ftHeader && ft != ftSource)
        return false;

    // because ft == ftHeader || ftSource, the extension has at least 1 character
    bool extStartsWithCapital = wxIsupper(wxFileName(ed->GetFilename()).GetExt()[0]);

    // create a list of search dirs
    wxArrayString dirs;
    cbProject* project = 0;

    // if the file in question belongs to a different open project,
    // then use that project instead.
    // this fixes locating the file's pair in a workspace when the active file
    // does not belong to the active project.
    ProjectFile* opf = ed->GetProjectFile();
    if (opf)
        project = opf->GetParentProject();

    // if we didn't get a valid project, try the active one
    if (!project)
        project = Manager::Get()->GetProjectManager()->GetActiveProject();

    if (project)
    {
        // get project's include dirs
        dirs = project->GetIncludeDirs();

        if (opf)
        {
            wxString const &activeName = opf->file.GetName();

            // first try to find the file among the opened files
            for (int i = 0; i < GetEditorsCount(); ++i)
            {
                cbEditor* edit = GetBuiltinEditor(GetEditor(i));
                if (!edit)
                    continue;

                ProjectFile* pf = edit->GetProjectFile();
                if (!pf)
                    continue;

                if (pf->file.GetName() == activeName)
                {
                    wxFileName const & fname = pf->file;
                    FileType ft_other = FileTypeOf(fname.GetFullName());
                    if (   (    ((ft == ftHeader) && (ft_other == ftSource))
                             || ((ft == ftSource) && (ft_other == ftHeader)) )
                        && (wxIsupper(fname.GetExt()[0]) == extStartsWithCapital) )
                    {
                        if (fname.FileExists())
                        {
                            cbEditor* newEd = Open(fname.GetFullPath());
                            if (newEd!=0L) // we found and were able to open it
                                return true; // --> RETURN
                        }
                    }
                }
            }

            // second try to find in the project files - at the same time
            // build the directory list for further searching if not
            // successful now
            for (int i = 0; i < project->GetFilesCount(); ++i)
            {
                ProjectFile* pf = project->GetFile(i);
                if (!pf)
                    continue;

                wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
                if (dirs.Index(dir) == wxNOT_FOUND)
                    dirs.Add(dir);

                if (pf->file.GetName() == activeName)
                {
                    wxFileName const & fname = pf->file;
                    FileType ft_other = FileTypeOf(fname.GetFullName());
                    if (   (    ((ft == ftHeader) && (ft_other == ftSource))
                             || ((ft == ftSource) && (ft_other == ftHeader)) )
                        && (wxIsupper(fname.GetExt()[0]) == extStartsWithCapital) )
                    {
                        if (fname.FileExists())
                        {
                            cbEditor* newEd = Open(fname.GetFullPath());
                            if (newEd!=0L) // we found and were able to open it
                                return true; // --> RETURN
                        }
                    }
                }
            }
        }
        else // no opf
        {
            // build the directory list for further searching if opf not available
            for (int i = 0; i < project->GetFilesCount(); ++i)
            {
                ProjectFile* pf = project->GetFile(i);
                if (!pf)
                    continue;

                wxString dir = pf->file.GetPath(wxPATH_GET_VOLUME);
                if (dirs.Index(dir) == wxNOT_FOUND)
                    dirs.Add(dir);
            }
        }

        // if not found, continue building the list of directories for further searching

        // get targets include dirs
        for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
        {
            ProjectBuildTarget* target = project->GetBuildTarget(i);
            if (target)
            {
                for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
                {
                    // TODO (Morten#5#): target include dirs might override project include dirs, take append/prepend option into account
                    wxString dir = target->GetIncludeDirs()[ti];
                    if (dirs.Index(dir) == wxNOT_FOUND)
                        dirs.Add(dir);
                }
            }
        }
    } // project

    wxFileName fname;
    wxFileName fn(ed->GetFilename());
    dirs.Insert(fn.GetPath(wxPATH_GET_VOLUME), 0); // add file's dir

    for (unsigned int i = 0; i < dirs.GetCount(); ++i)
    {
        ProjectManager *pm = Manager::Get()->GetProjectManager();
        if ( !pm )
            break;

        wxString dir = dirs[i]; // might contain macros -> replace them
        Manager::Get()->GetMacrosManager()->ReplaceMacros(dir);

        fname.Assign(dir + wxFileName::GetPathSeparator() + fn.GetFullName());
//        Manager::Get()->GetLogManager()->DebugLog(F(_T("Looking for '%s', dir='%s'."), fname.GetFullPath().c_str(), dir.c_str()));
        if (!fname.IsAbsolute() && project)
        {
            fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, project->GetBasePath());
//            Manager::Get()->GetLogManager()->DebugLog(F(_T("Normalizing dir to '%s'."), fname.GetFullPath().c_str()));
        }

        wxString HeaderSource = pm->GetHeaderSource(fname);
        if (!HeaderSource.IsEmpty())
        {
            fname.SetFullName(HeaderSource);
//            Manager::Get()->GetLogManager()->DebugLog(F(_T("Located '%s'."), fname.GetFullPath().c_str()));
            break;
        }
    }

    if (fname.FileExists())
    {
        //Manager::Get()->GetLogManager()->DebugLog("ed=%s, pair=%s", ed->GetFilename().c_str(), pair.c_str());
        cbEditor* newEd = Open(fname.GetFullPath());
        if (newEd!=0L) // we found and were able to open it
            return true; // --> RETURN;
    }

    // We couldn't find the file, maybe it does not exist. Ask the user if we
    // should create it:
    if (cbMessageBox(_("The file seems not to exist. Do you want to create it?"),
                _("Error"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
    {
        cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (project)
            wxSetWorkingDirectory(project->GetBasePath());

        // Create a suggestion for the new file name:
        if (ft == ftHeader)
            fn.SetExt(FileFilters::CPP_EXT);
        else if (ft == ftSource)
            fn.SetExt(FileFilters::H_EXT);
        // else? Well, if the filename is not changed we could possibly
        // overwrite an existing file with our suggestion.

        cbEditor* newEd = New(fn.GetFullPath());
        if (project)
        {
            if (cbMessageBox(_("Do you want to add this new file in the active project?"),
                        _("Add file to project"),
                        wxYES_NO | wxICON_QUESTION) == wxID_YES)
            {
                wxArrayInt targets;
                if (Manager::Get()->GetProjectManager()->AddFileToProject(newEd->GetFilename(), project, targets) != 0)
                {
                    ProjectFile* pf = project->GetFileByFilename(newEd->GetFilename(), false);
                    newEd->SetProjectFile(pf);
                    Manager::Get()->GetProjectManager()->RebuildTree();
                }
            }
        }

        // verify that the open files are still in sync
        // the new file might have overwritten an existing one)
        Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
    }

    return false;
}

Having a quick look at the code, I think the issue I'm encountering is due to the fact that the directory is not being taken in to account, both the first (searching open files) and second (searching the project) seem to rely on filename only (i.e. not the relative path to the file).

Going back to my original example:

root
-- subdir1
    Config.cpp
    Config.h
-- subdir2
    Config.cpp
    Config.h
-- subdir3
    Config.cpp
    Config.h


If I open Config.cpp in either subdir1 or subdir2 with no other files open and hit 'swap...' Config.h in subdir1 is opened, I assume because the second search (the one through the project) is matching on 'Config.' without paying attention to the directory in which it resides.

Similarly, if I have Config.h from subdir1 open and Config.cpp from subdir3 open, if I hit 'swap..' on Config.h from subdir1, it switches to Config.cpp from subdir3 because it's matching on 'Config' (based on the first search which uses the open files).

I think for my issue the relative path must be taken into consideration.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #18 on: October 17, 2009, 06:32:08 am »
Based on my analysis of the issue in my previous post, I've submitted a patch:

http://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2841&group_id=5358

That includes the file path as criteria for the match.  This addresses the issues that I've encountered.

Cheers!

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5915
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Bug in 'swap header / source'
« Reply #19 on: October 17, 2009, 02:59:17 pm »
Based on my analysis of the issue in my previous post, I've submitted a patch:

http://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2841&group_id=5358

That includes the file path as criteria for the match.  This addresses the issues that I've encountered.

Cheers!


I have applied in my working copy, and works fine!! Thanks!!!
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #20 on: October 17, 2009, 03:10:07 pm »
Haven't tried it myself, but from what I see you are limiting the search to one directory per file. Wouldn't that break the feature entirely for source layouts, where h and cpp files are separated into different directories?

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #21 on: October 18, 2009, 12:23:25 am »
Haven't tried it myself, but from what I see you are limiting the search to one directory per file. Wouldn't that break the feature entirely for source layouts, where h and cpp files are separated into different directories?

Yes, the patch I supplied would not correct this issue, but the original implementation would only work if you only had one occurrence of each file.  Let's say you had:

root
--- subdir1
    --- src
         Config.cpp
    --- headers
         Config.h
--- subdir2
    --- src
         Config.cpp
    --- headers
         Config.h


In the original implementation, if you opened Config.h in subdir2 and hit 'Swap...' you would get Config.cpp in subdir1/src I suspect.

To correct this, I believe you would need to implement what Tim S. suggested and search the include path for the project / build target.  However, even this implementation I suspect will have issues (i.e. I don't think it would work well for my particular issue).

I'm open to suggestions on how to more generically address this issue. ;)


Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #22 on: October 18, 2009, 12:45:11 am »
Well, you could make a list of all candidate files and then apply some heuristics to find the most likely candidate. For a start you could look for identical path elements (subdir1 in your example).

But that still doesn't handle the case, where the corresponding file does not exist yet. At this point you only have two options:

1. Offer the user a new dialogue, in which he can specify the right directory. Additionally you might want to use the heuristics mentioned above to find the most likely candidate.

Example:

include
-- A
---- foo.h
---- bar.h
-- B
---- foo.h
src
-- A
---- foo.cpp
-- B
---- foo.cpp

From the include/A/foo.h src/A/foo.cpp pair a smart algorithm may deduce, that the newly created cpp file for bar.h goes into src/A/bar.cpp

2. Enhance the Code::Blocks project file format with some information about the source layout. This information could be used to improve other functionality too. For example it could help to automatically pick the right directory for files newly created from the File -> New dialgoue.

Both options sound like a lot of work unfortunately.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #23 on: October 18, 2009, 01:03:45 am »
Looking at the code again, it actually already searches the include path if it doesn't find it in the list of currently open editors or in the project files (i.e. in the same directory):

Code
        // get targets include dirs
        for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
        {
            ProjectBuildTarget* target = project->GetBuildTarget(i);
            if (target)
            {
                for (unsigned int ti = 0; ti < target->GetIncludeDirs().GetCount(); ++ti)
                {
                    // TODO (Morten#5#): target include dirs might override project include dirs, take append/prepend option into account
                    wxString dir = target->GetIncludeDirs()[ti];
                    if (dirs.Index(dir) == wxNOT_FOUND)
                        dirs.Add(dir);
                }
            }
        }

With my patch applied, would it be possible for you to validate if my patch actually breaks the scenario you proposed?

As for the scenario where the file doesn't exist, I wouldn't suggest any automation in terms of guessing where the file should be created.  I would leave it up to the developer and simply provide a file dialogue to create the file.


Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #24 on: October 18, 2009, 02:00:43 am »
Quote
Looking at the code again, it actually already searches the include path if it doesn't find it in the list of currently open editors or in the project files (i.e. in the same directory):

That is a part of the problem. Why the treat open files differently from non-open files? If it only works if the file isn't open, the algorithm is already broken.

Quote
With my patch applied, would it be possible for you to validate if my patch actually breaks the scenario you proposed?

Guess it will be less work, if you try it out. Please note that I have uploaded a test project, that demonstrates the problem (a few posts above).


Anyway, I couldn't sleep, so I wrote a new algorithm. Hope you can understand my python-pseudo-code. No guarantees about the correctness though (despite not being able to get to sleep I am awful sleepy already):

Code
listCandidates (file):
    e = extension (file)
    if e in header_extension_list:
        extension_list = source_extension_list
    elseif e in source_extension_list:
        extension_list = header_extension_list
    else
        throw error (unknown extension)

    l = leaf_without_extension (file)
       
    for f in project: # iterate over all files in the project
        if leaf_without_extension (f)==l and extension (f) in extension_list:
            add f to list
           
    return list

findCandidate (file):
    list = listCandidates (file)
    n = len (file) # len (file) is the number of path elements
   
    if n>1:
        for f in list if len (f)==n:
            for i in range (0, n-2):
                if f[i]==file[i]:
                    return f # success
    else
        for f in list if len (f)==1:
            return f # success (top level directory)

    # maybe add other methods to determine the file in case the above fails

    return null # no file found; either does match no known pattern or it does not exist
   
swap (file):
    c = findCandidate (file):
   
    if c!=null:
        open c
        return
       
    # file does not exist
    parent = file[0:n-2] # one level up (the directory where the file is located)
    for f in parent if f!=file: # iterating over the files in the directory
        c = findCandidate (f)
        if c!=null:
            create corresponding file in directory c[0:n-2] and open it
            return
           
    # give up; either ask the user for a directory or report that the file can't be created


Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #25 on: October 18, 2009, 05:19:18 am »
Quote
Looking at the code again, it actually already searches the include path if it doesn't find it in the list of currently open editors or in the project files (i.e. in the same directory):

That is a part of the problem. Why the treat open files differently from non-open files? If it only works if the file isn't open, the algorithm is already broken.

Just to clarify, the patch I provided works with both open and non-open files in the same manor.

Quote
With my patch applied, would it be possible for you to validate if my patch actually breaks the scenario you proposed?

Guess it will be less work, if you try it out. Please note that I have uploaded a test project, that demonstrates the problem (a few posts above).

Hrm, the patch doesn't handle the include/A with src/A correctly, so there is definitely room for improvement.  :)

Anyway, I couldn't sleep, so I wrote a new algorithm. Hope you can understand my python-pseudo-code. No guarantees about the correctness though (despite not being able to get to sleep I am awful sleepy already):

Code
listCandidates (file):
    e = extension (file)
    if e in header_extension_list:
        extension_list = source_extension_list
    elseif e in source_extension_list:
        extension_list = header_extension_list
    else
        throw error (unknown extension)

    l = leaf_without_extension (file)
       
    for f in project: # iterate over all files in the project
        if leaf_without_extension (f)==l and extension (f) in extension_list:
            add f to list
           
    return list

findCandidate (file):
    list = listCandidates (file)
    n = len (file) # len (file) is the number of path elements
   
    if n>1:
        for f in list if len (f)==n:
            for i in range (0, n-2):
                if f[i]==file[i]:
                    return f # success
    else
        for f in list if len (f)==1:
            return f # success (top level directory)

    # maybe add other methods to determine the file in case the above fails

    return null # no file found; either does match no known pattern or it does not exist
   
swap (file):
    c = findCandidate (file):
   
    if c!=null:
        open c
        return
       
    # file does not exist
    parent = file[0:n-2] # one level up (the directory where the file is located)
    for f in parent if f!=file: # iterating over the files in the directory
        c = findCandidate (f)
        if c!=null:
            create corresponding file in directory c[0:n-2] and open it
            return
           
    # give up; either ask the user for a directory or report that the file can't be created

I'm afraid my python knowledge is non-existent, but I'll take a closer look at this tomorrow once I've had a chance to catch up on some sleep myself.  Thank you for the pseudo code!

Cheers!

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #26 on: October 18, 2009, 03:17:35 pm »
Putting some more thought into the 'include/A/Unit.h' / 'src/A/Unit.cpp' I'm not sure there is a sure fire solution.  For the swapping to the header, you could use the project / build target's include paths and add the relevant relative path (i.e. the 'A'), but the for the other direction, how would C::B implicitly know that the source files for 'include/A' are in 'src/A'?

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #27 on: October 18, 2009, 03:20:27 pm »
Because of the 'A'? ;)

That might not be 100% correct all the time, but you would need some rather exotic layout for the test to fail.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #28 on: October 20, 2009, 03:52:00 pm »
Even the approach of using the immediate parent directory isn't full proof either.

Lets say you had:
<root>
--- <A>
     --- <src>
          Config.cpp
     --- <include>
          Config.h


Again, the include path would determine where Config.h is relative to Config.cpp, but how would C::B know that for Config.h, Config.cpp is in <src>?

At this point I really don't see a better way of handling things then what I've already proposed in my patch.  I fully admit that it does not handle all cases, but I don't know of an algorithm that would, and I believe the patch makes it better than what is there today.

If anyone else is willing to take a stab at implementing a better algorithm, I'd be happy to help out in any way I can.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #29 on: October 20, 2009, 03:58:48 pm »
Still haven't got around to test your patch (sorry, so busy atm. its insane!), but IIRC it stops the feature from working when h and cpp files sit in different directories. That would break the feature completely for me (and most likely a lot of other people).

Quote
At this point I really don't see a better way of handling things then what I've already proposed in my patch.  I fully admit that it does not handle all cases, but I don't know of an algorithm that would, and I believe the patch makes it better than what is there today.

I agree that a solutation that handles all cases isn't possible without storing information about the source layout in the project file. However the situation you mentioned is handled perfectly by my algorithm. Did you have to time to take a look at it?

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #30 on: October 20, 2009, 04:10:31 pm »
Still haven't got around to test your patch (sorry, so busy atm. its insane!), but IIRC it stops the feature from working when h and cpp files sit in different directories. That would break the feature completely for me (and most likely a lot of other people).

I think it all depends on your file structure, since the existing implementation also breaks if you have multiple subdirectories with source and include directories in the subdirectories.

Quote
I agree that a solutation that handles all cases isn't possible without storing information about the source layout in the project file. However the situation you mentioned is handled perfectly by my algorithm. Did you have to time to take a look at it?

I'm in much the same position as you at the moment unfortunately (having very little time to work on things), hopefully sometime early next week I can take a look at implementing it.  If you have some time before that and feel inclined at taking a stab at it, I'd be happy to help out. :D

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #31 on: October 20, 2009, 06:00:29 pm »
Quote
I think it all depends on your file structure, since the existing implementation also breaks if you have multiple subdirectories with source and include directories in the subdirectories.

Not really. I am currently on SVN rev 5679. The swap feature works here most of the time (without automatically creating missing files though), unless there is more than one file with the same name (which has never worked properly with any version of Code::Blocks). That is still better than to break the feature entirely for certain layouts.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #32 on: October 20, 2009, 06:23:56 pm »
Quote
If you have some time before that and feel inclined at taking a stab at it, I'd be happy to help out.

Rather unlikely. I just had another look at the code. The time I would need to familiarize myself with WxWidgets API (never worked with it before) and the relevant Code::Blocks API (know nothing about it either) would most likely exceed any amount of time I can make available.
However I might be able to refine the algorithm a bit, if you are willing to try implementing it. I have a few more ideas, that should cover some additional edge-cases.

Offline Zini

  • Multiple posting newcomer
  • *
  • Posts: 64
Re: Bug in 'swap header / source'
« Reply #33 on: October 21, 2009, 01:50:42 pm »
There we go ...

Code
listCandidates (file):
    e = extension (file)
    if e in header_extension_list:
        extension_list = source_extension_list
    elif e in source_extension_list:
        extension_list = header_extension_list
    else:
        raise error (unknown extension)

    l = leaf_without_extension (file)
        
    for f in project: # iterate over all files in the project
        if leaf_without_extension (f)==l and extension (f) in extension_list:
            add f to list
            
    return list

findCandidate (file, list):
    if not strictMode and len (list)==1:
        return list[0]
    
    n = len (file) # len (file) is the number of path elements

    if n>1: # search for shared path elements
        for f in list if len (f)==n:
            for i in range (n-1): # for loop over the first n-1 integers starting with 0
                if f[i]==file[i]:
                    return f # success
    else # name consists of leaf elements only
        for f in list if len (f)==1:
            return f # success (top level directory)

    # maybe add other methods to determine the file in case the above fails

    return null # no file found; either does match no known pattern or it does not exist
    
swap (file):
    list = listCandidates (file)

    c = findCandidate (file, list):
    
    if c!=null:
        open c
        return
        
    # file does not exist -> try to find another file pair
    parent = file[0:n-2] # one level up (the directory where the file is located)
    for f in parent if f!=file: # iterating over the files in the directory
        c = findCandidate (f, list)
        if c!=null:
            # may want to include a sanity check here: warn or abort if file already exists
            create corresponding file in directory c[0:n-2] and open it
            return
            
    # give up; either ask the user for a directory or report that the file can't be created

Optimized, better documented and with a little bug fix. This should work.

Regarding the strictMode variable: This should be either a Code.:Blocks-wide or a project-wide setting. In strict mode the algorithm will not handle some less symmetric source layouts correctly. In non-strict mode multiple files with the same name won't be handles correctly, if one of the files still needs to be created. I think that is a good compromise.

Edit: File names are assumed to be relative to the project directory.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #34 on: October 22, 2009, 06:04:35 am »
Cool, thank you for the detailed pseudo code.  I'll take a look at implementing it next week (I have a couple of deadlines to take care of this week).

Cheers!

Offline CuteAlien

  • Multiple posting newcomer
  • *
  • Posts: 57
Re: Bug in 'swap header / source'
« Reply #35 on: November 17, 2009, 11:35:00 am »
Testing this with latest nightly build (svn 5911) it seems that currently c::b always prefers switching to a header/source which is already opened. If several files with a fitting name are open then it switches to the one which was opened first. Regardless if those files are in the same project or folder. It only works correct (for my case) when no file with a fitting name is already opened.

Offline ironhead

  • Almost regular
  • **
  • Posts: 210
Re: Bug in 'swap header / source'
« Reply #36 on: November 18, 2009, 03:09:35 pm »
The behaviour on the trunk is still unchanged.  I unfortunately have not had time to work on a new version of the patch.  If someone else has time and is willing to take a stab at it, I'd be happy to help out.

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9694
Re: Bug in 'swap header / source'
« Reply #37 on: November 18, 2009, 05:16:30 pm »
Please have a look at Patch #2803 at BerliOS patch tracker. From my point of view this has most chances to implement a "correct" behaviour. I am testing this for a while now. Most likely it solves your problems.
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: Bug in 'swap header / source'
« Reply #38 on: December 11, 2012, 08:50:45 pm »
Sorry for resurrecting this topic, but I wanted to make this feature work for file where the case doesn't match.
The code seems pretty complex and I don't won't to change it, is anyone willing to implement such feature?

My current problem is that these to files: myfile.h and MyFile.cpp are not swapped correctly.

The example project is also missing.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline Alpha

  • Developer
  • Lives here!
  • *****
  • Posts: 1513
Re: Bug in 'swap header / source'
« Reply #39 on: December 11, 2012, 10:56:02 pm »
Maybe something like this?  (Warning, not yet heavily tested.)
Code
Index: src/sdk/editormanager.cpp
===================================================================
--- src/sdk/editormanager.cpp (revision 8646)
+++ src/sdk/editormanager.cpp (working copy)
@@ -1014,8 +1014,8 @@
 
 bool EditorManager::IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive)
 {
-    // Verify the base name mathes
-    if (candidateFile.GetName() == activeFile.GetName())
+    // Verify the base name matches
+    if (candidateFile.GetName().CmpNoCase(activeFile.GetName()) == 0)
     {
         // Verify:
         // If looking for a header we have a source OR

Offline p2rkw

  • Almost regular
  • **
  • Posts: 142
Re: Bug in 'swap header / source'
« Reply #40 on: December 11, 2012, 11:12:03 pm »
Hmm... No. Only windows isn't case sensitive.

Btw. Newly opened file tab (via F11) might be placed next to current file, not at end.
« Last Edit: December 11, 2012, 11:13:37 pm by p2rkw »

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9694
Re: Bug in 'swap header / source'
« Reply #41 on: December 12, 2012, 08:49:24 am »
Hmm... No. Only windows isn't case sensitive.
That's not correct, too.

The right way is to first look for a case sensitive and then as backup for a case non-sensitive on Linux platforms. On Windows, you can start with case-insensitive directly. So - these are two cases that cannot be handled together.
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ

Offline Alpha

  • Developer
  • Lives here!
  • *****
  • Posts: 1513
Re: Bug in 'swap header / source'
« Reply #42 on: December 15, 2012, 11:16:43 pm »
The right way is to first look for a case sensitive and then as backup for a case non-sensitive on Linux platforms. On Windows, you can start with case-insensitive directly. So - these are two cases that cannot be handled together.
Unless someone already has begun work on this, I will see if I can pull something together.

Offline Alpha

  • Developer
  • Lives here!
  • *****
  • Posts: 1513
Re: Bug in 'swap header / source'
« Reply #43 on: December 16, 2012, 12:14:32 am »
I will see if I can pull something together.
I believe this meets the criteria, and it passes my test cases.  oBFusCATed, does it work on your project?
Code
Index: src/sdk/editormanager.cpp
===================================================================
--- src/sdk/editormanager.cpp (revision 8669)
+++ src/sdk/editormanager.cpp (working copy)
@@ -1012,11 +1012,14 @@
     m_isCheckingForExternallyModifiedFiles = false;
 }
 
-bool EditorManager::IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive)
+bool EditorManager::IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive, bool& isCandidate)
 {
-    // Verify the base name mathes
-    if (candidateFile.GetName() == activeFile.GetName())
+    // Verify the base name matches
+    if (candidateFile.GetName().CmpNoCase(activeFile.GetName()) == 0)
     {
+        // non-Windows platforms: case-insensitive match -> isCandidate
+        isCandidate = !( platform::windows || (candidateFile.GetName() == activeFile.GetName()) );
+
         // Verify:
         // If looking for a header we have a source OR
         // If looking for a source we have a header
@@ -1056,13 +1059,12 @@
     {
         wxFileName currentCandidateFile(candidateFilesArray[i]);
 
-        if (IsHeaderSource(currentCandidateFile, activeFile, ftActive))
+        if (IsHeaderSource(currentCandidateFile, activeFile, ftActive, isCandidate))
         {
             bool isUpper = wxIsupper(currentCandidateFile.GetExt()[0]);
-            if (isUpper == extStartsWithCapital)
+            if (isUpper == extStartsWithCapital && !isCandidate)
             {
                 // we definitely found the header/source we were searching for
-                isCandidate = false;
                 return currentCandidateFile;
             }
             else
Index: src/include/editormanager.h
===================================================================
--- src/include/editormanager.h (revision 8669)
+++ src/include/editormanager.h (working copy)
@@ -194,7 +194,7 @@
         int FindInFiles(cbFindReplaceData* data);
         int Replace(cbStyledTextCtrl* control, cbFindReplaceData* data);
         int ReplaceInFiles(cbFindReplaceData* data);
-        bool IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive);
+        bool IsHeaderSource(const wxFileName& candidateFile, const wxFileName& activeFile, FileType ftActive, bool& isCandidate);
         wxFileName FindHeaderSource(const wxArrayString& candidateFilesArray, const wxFileName& activeFile, bool& isCandidate);
 
         cbAuiNotebook*             m_pNotebook;