Hi Lex,

> Or you can load tags files instead, so long as the symbols are in
> memory, and I am told (I don't use them myself) that the project
> plugins (there are two with slightly differing behaviour) will arrange
> for this to happen for you, rather like the msvc system.

Ok, that makes more sense!


>> (As an aside, for myself, I'm managed to hack something up that uses
>> libClang to lookup references, that doesn't need know about the whole set of >> project files in order to work. It actually just executes the corresponding >> compile command within libClang each time you click 'lookup reference', and >> so fits in quite nicely with the 'no list of files in project' paradigm..)
>
> That sounds interesting, are you going to make a plugin from it?

I made a plugin.
It turns out it doesn't actually require a lot of code, once you have libClang.

The key source files are attached, for reference.
(Will need a bit hacking to build, sorry, I have a fairly non standard build setup, but the key stuff is there. Note that this refers to custom vector and string headers but it should be straightforward to replace these with stl containers.)

Some notes:

* For source files, the plugin needs to know the compile command to use. In this case this is hardcoded, and is essentially just some include paths.

* For header files, we need to generate a source file in memory that includes the header. In the build setup I am using the paradigm is that there is one standard base header required at the top of each .cpp, and apart from that each header should make sure that it includes anything else that it needs. The path to the base header is then hardcoded here.

For a plugin to be useful more generally (i.e. not depending on my specific build setup), I guess it could maybe take compile info from the project compile command setup, and allow to edit some kind of template for generating cpp for headers.


> Maybe you could load the symbols info into Geany and then not have to
> run clang again and again.  Matthew was also looking at something
> similar I think.

Well I just built a new machine, and Clang is pretty fast (much faster than Visual Studio compiler for example), so it turns out that this is just not an issue for me in practice. ;-)

Note that we only have to compile one single source file to do this, no other objects need to be compiled or referenced.

Caching symbols would obviously make things more complicated, and would also require to do some kind of dependency check for changes for everything that is included by the source file..


Best regards,

Thomas
#include "base/Header.h"
#include "externalDependant/Clang/LookupReference.h"
#include "common/Container/PythonStyleString.h"
#include "base/Container/String.h"
#include <clang-c/Index.h>

bool
LookupReference(
        int compilationArgC, char** compilationArgV,
        const char* baseInclude,
        const char* referencingFile, tSigned32 referencingLine, tSigned32 referencingColumn,
        tString& referencedFileName, tSigned32& referencedLine, tSigned32& referencedColumn,
        tString* otherOutput
        )
{
    CXIndex Index = clang_createIndex(0, 0);
    CXTranslationUnit TU;
    if(EndsWith(referencingFile, ".h"))
    {
        tString cppFileName = StringSlice(referencingFile, 0, -1);
        cppFileName.append("cpp");
        tString cppContents("#include \"");
        if(baseInclude)
        {
            cppContents.append(baseInclude);
            cppContents.append("\"\n#include \"");
        }
        cppContents.append(referencingFile);
        cppContents.append("\"\n");
        CXUnsavedFile unsavedFile;
        unsavedFile.Filename = cppFileName.c_str();
        unsavedFile.Contents = cppContents.c_str();
        unsavedFile.Length = cppContents.size();
        TU = clang_parseTranslationUnit(Index, cppFileName.c_str(), compilationArgV, compilationArgC, &unsavedFile, 1, CXTranslationUnit_None);
    }
    else
    {
        TU = clang_parseTranslationUnit(Index, referencingFile, compilationArgV, compilationArgC, 0, 0, CXTranslationUnit_None);
    }

    if(TU == 0)
    {
      // couldn't do anything with this set of args, e.g. can happen if file to compile was not found
        clang_disposeIndex(Index);
        return false;
    }
 
    if(otherOutput)
    {
        for(unsigned i = 0, N = clang_getNumDiagnostics(TU); i != N; ++i)
        {
            CXDiagnostic Diag = clang_getDiagnostic(TU, i);
            CXString String = clang_formatDiagnostic(Diag,
            clang_defaultDiagnosticDisplayOptions());
            otherOutput->append(clang_getCString(String));
            otherOutput->append("\n");
            clang_disposeString(String);
        }
    }
    
    CXFile file = clang_getFile(TU, referencingFile);
    unsigned line, column;
    CXSourceLocation location = clang_getLocation(TU, file, referencingLine, referencingColumn);
    CXCursor cursor = clang_getCursor(TU, location);

    CXString str;

    CXSourceRange range;
    unsigned offset;

    bool result = false;

    CXCursor referencedCursor = clang_getCursorReferenced(cursor);
    if(!clang_equalCursors(referencedCursor, clang_getNullCursor()))
    {
        CXSourceLocation referencedLocation = clang_getCursorLocation(referencedCursor);
        CXFile referencedFile;
        clang_getSpellingLocation(referencedLocation, &referencedFile, &line, &column, &offset);
        str = clang_getFileName(referencedFile);
        referencedLine = static_cast<tSigned32>(line);
        referencedColumn = static_cast<tSigned32>(column) - 1;
        result = true;
        referencedFileName = clang_getCString(str);
    }
    
    clang_disposeTranslationUnit(TU);
    clang_disposeIndex(Index);
    
    return result;
}

#include "base/Header.h"
#include "externalDependant/Clang/LookupReference.h"
#include "base/Container/ReplacementVector.h"
#include "base/Container/String.h"
#include <geanyplugin.h>
#include <gdk/gdkkeysyms.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

GeanyPlugin         *geany_plugin;
GeanyData           *geany_data;
GeanyFunctions      *geany_functions;

PLUGIN_VERSION_CHECK(211)

PLUGIN_SET_INFO("Lookup Reference", "Lookup reference plugin",
                "1.0", "Thomas Young <thomasyo...@free.fr>");

/* IDs for keybindings */
enum
{
	KEY_ID_LOOKUP_REFERENCE,
	NB_KEY_IDS
};


static void
LookupReferenceMenuCallback(GtkMenuItem *menuitem, gpointer user_data)
{
    const gchar *fileName;
    gint pos, line, col;

    GeanyDocument *doc = document_get_current();
    if(!doc)
    {
        return;
    }
        
    fileName = DOC_FILENAME(doc);
    pos = sci_get_current_position(doc->editor->sci);
    line = sci_get_line_from_position(doc->editor->sci, pos);
    ++line;
    col = sci_get_col_from_position(doc->editor->sci, pos);
    
    cVector<char*> args;
	args.push_back((char*)"-I../interface");
	args.push_back((char*)"-I../code");
    tString referencedFile;
    tSigned32 referencedLine, referencedColumn;
    tString otherOutput;
    
    const char* defaultDir = utils_get_default_dir_utf8();
    char workingDirectory[FILENAME_MAX];
    if(defaultDir)
    {
        getcwd(workingDirectory, sizeof(workingDirectory));
        chdir(defaultDir);
    }
    
    bool success = LookupReference(
            args.size(), &args[0], 
            "/home/thomas/code/base/Header.h",
            fileName, line, col,
            referencedFile, referencedLine, referencedColumn,
            &otherOutput
            );
            
    if(success)
    {
        char absolutePath[FILENAME_MAX]; 
        realpath(referencedFile.c_str(), absolutePath); 

        GeanyDocument* newDoc = document_open_file(absolutePath, FALSE, NULL, NULL);
        if(newDoc)
        {
            pos = sci_get_position_from_line(newDoc->editor->sci, referencedLine - 1);
            pos += referencedColumn;
            sci_set_current_position(newDoc->editor->sci, pos, FALSE);
        }
    }
    else
    {
        fprintf(stdout, "reference lookup failed, output: %s", otherOutput.c_str());
    }

    if(defaultDir)
    {
        chdir(workingDirectory);
    }
}

static void
LookupReferenceKeyCallback(G_GNUC_UNUSED guint key_id)
{
	LookupReferenceMenuCallback(NULL, NULL);
}


static GtkWidget *main_menu_item;

extern "C" void
plugin_init(GeanyData *data)
{
	gchar *kb_label = (gchar*)_("Lookup reference");
	GtkWidget *menu_lookup = NULL;
	GeanyKeyGroup *key_group;

	/* Build up menu entry */
	menu_lookup = gtk_menu_item_new_with_mnemonic(_("_Lookup reference"));
	gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), menu_lookup);
	gtk_widget_set_tooltip_text(menu_lookup, _("Looks up reference for cursor in Clang AST"));
	g_signal_connect(G_OBJECT(menu_lookup), "activate", G_CALLBACK(LookupReferenceMenuCallback), NULL);

	/* setup keybindings */
	key_group = plugin_set_key_group(geany_plugin, "lookup_reference", NB_KEY_IDS, NULL);
	keybindings_set_item(key_group, KEY_ID_LOOKUP_REFERENCE, LookupReferenceKeyCallback, 0, (GdkModifierType)0, "lookup_reference", kb_label, menu_lookup);

	gtk_widget_show_all(menu_lookup);
	ui_add_document_sensitive(menu_lookup);
	main_menu_item = menu_lookup;
}
extern "C" void plugin_cleanup(void)
{
    gtk_widget_destroy(main_menu_item);    
}
#include "base/Container/String.h"

inline tString
StringSlice(const tString& s, tSigned32 i1, tSigned32 i2)
{
    if(i2 > SizeL(s))
    {
        i2 = SizeL(s);
    }
    if(i1 < 0)
    {
        i1 = SizeL(s) + i1;
        if(i1 < 0)
        {
            i1 = 0;
        }
    }
    if(i2 < 0)
    {
        i2 = SizeL(s) + i2;
        if(i2 < 0)
        {
            i2 = 0;
        }
    }
    if(i2 <= i1)
    {
        return "";
    }
    //return s.substr(i1, i2 - i1);
    return tString(&s[i1], i2 - i1);
}

inline tString
StringSlice(const tString& s, tSigned32 i1)
{
    return StringSlice(s, i1, SizeL(s));
}

inline bool
StartsWith(const tString& s, const tString& prefix)
{
    if(SizeL(s) < SizeL(prefix))
    {
        return false;
    }
    for(tSigned32 i = 0; i != SizeL(prefix); ++i)
    {
        if(s[i] != prefix[i])
        {
            return false;
        }
    }
    return true;
}

inline bool
EndsWith(const tString& s, const tString& suffix)
{
    tSigned32 offset = SizeL(s) - SizeL(suffix);
    if(offset < 0)
    {
        return false;
    }
    for(tSigned32 i = 0; i != SizeL(suffix); ++i)
    {
        if(s[offset + i] != suffix[i])
        {
            return false;
        }
    }
    return true;
}

inline tSigned32
Find(const tString& s, const tString& subString, tSigned32 startI = 0)
{
    tSigned32 startSize = SizeL(s) - SizeL(subString); // (note: can be negative)
    for(tSigned32 i = startI; i < startSize; ++i)
    {
        tSigned32 j;
        for(j = 0; j < SizeL(subString); ++j)
        {
            if(s[i + j] != subString[j])
            {
                break;
            }
        }
        if(j == SizeL(subString))
        {
            return i;
        }
    }
    return -1;
}

inline tSigned32
Len(const tString& s)
{
    return SizeL(s);
}
#pragma once

#include "base/Container/String_Header.h"

bool
LookupReference(
        int compilationArgC, char** compilationArgV,
        const char* baseInclude,
        const char* referencingFile, tSigned32 referencingLine, tSigned32 referencingColumn,
        tString& referencedFileName, tSigned32& referencedLine, tSigned32& referencedColumn,
        tString* otherOutput
        );
_______________________________________________
Devel mailing list
Devel@lists.geany.org
https://lists.geany.org/cgi-bin/mailman/listinfo/devel

Reply via email to