Search code examples
c#autocompletenrefactory

NRefactory code completion with FastColoredTextBox in c#


I am building a simple IDE for C#.

..and I am using FastColoredTextBox control for source code editing, with built-in autocomplete menu (http://www.codeproject.com/Articles/161871/Fast-Colored-TextBox-for-syntax-highlighting)

The problem is, that I want to use NRefactor to do automatic code completion, just like in Visual Studio.

I did some research, I found this: https://github.com/lukebuehler/NRefactory-Completion-Sample ...but I didn't understand the code, so I can't reuse it.

To be exact, what I need?

I need an function, which will take as input list of assemblies (array of strings, which are paths to DLLs), current offset, and editor's text.

And as output, I need an array of objects, which will contain name of the object, and it's type (variable, function, type, ...)

So, how do I do it? Is it necessary to run this autocompletion code in another thread?

Thank you for answers,

VitekST

PS: Don't hate me, I'm not familiar with NRefactor...


Solution

  • I starts to explore NRefactory in same day you asks this question) So, I can't call myself as NRefactory expert. But I can tell you the way, how I use it.

    Theoretical material: http://www.codeproject.com/Articles/408663/Using-NRefactory-for-analyzing-Csharp-code

    First of all you can use a Visual Studio solution model from this sample project to make similar classes for your IDE solution format: https://github.com/icsharpcode/NRefactory/tree/master/ICSharpCode.NRefactory.ConsistencyCheck There are three classes you need: CSharpFile, CSharpProject and Solution. Please look at its source code and you will know, how NRefactory performs syntactic analysis of code files. Note "Compilation" field of CSharpProject and "CreateResolver" method of CSharpFile.

    Second you need to perform semantic analysis of code. For this purpose you need to implement

    ICSharpCode.NRefactory.CSharp.Resolver.IResolveVisitorNavigator
    

    interface. But in your case you better use existing NRefactory implementation:

    ICSharpCode.NRefactory.CSharp.Resolver.FindReferencedEntities
    

    Constructor with two parameters waits two handlers. First being executed then resolved syntax element is reference of type, contained in your solution or in the referenced assembly (like variable declaration). Second being executed when resolved syntax element is reference of some type's member (for example "(new object()).ToString()" as a reference of member "ToString" of "object" instance). First parameter of both handlers is AstNode: unresolved syntax element of code. Second parameter is corresponding resolved semantic element: IType for first handler and IMember for another. So you need to create instance of navigator with handlers, that, for example, saving syntax and corresponding semantic elements in dictionaries.

    Your code can looks like this:

    var solution = new Solution(slnPath);
    
    IDictionary<AstNode, IType> typesMap = new Dictionary<AstNode, IType>();
    
    IDictionary<AstNode, IMember> membersMap = new Dictionary<AstNode, IMember>();
    
    var navigator = new FindReferencedEntities(typesMap.Add, membersMap.Add);
    
    foreach (var codeFile in solution.AllFiles)
    {
        codeFile.CreateResolver().ApplyNavigator(navigator);
    }
    

    After execution of this code dictionaries typesMap and membersMap will containing syntax elements of code, that NRefactory able to resolve, in the keys of dictionaries, and corresponding resolved semantic in values. If you writing autocompletion, in moment of usage processed code can not be compiled with a high probability (because user write it at this moment). So, your app must use not only resolved code, but unresolved too. To get the unresolved syntax elements of code file, than user edit at this moment, you must use:

    var syntaxTree = solution.Projects
        .Where(p => p.Title.Equals(editableProjName))
        .Files
        .Where(f => f.FileName.Equals(editableFileNamePath))
        .SyntaxTree;
    

    SyntaxTree is a "ICSharpCode.NRefactory.CSharp.SyntaxTree", that inherits AstNode class. It is a root syntax node of the code file. Variable syntaxTree will contain all unresolved syntax elements of current file being edited. To move through the syntax tree you can use AstNode enumerable members like Descendants, Children, GetParent() etc. Or you can use "Visitor" pattern and AstNode method "AcceptVisitor".

    While generating list for autocompletion you can use typesMap and membersMap as fast storage of declared types and its members, and syntaxTree as slow storage, if user input stay not found.

    NRefactory is a very broad topic, I can't reveal it fully. And it is very sad that this powerful framework haven't got detailed documentations. But I hope my answer can help you.