Simple code completion sample in Roslyn

lukebuehler picture lukebuehler · Dec 10, 2013 · Viewed 8.3k times · Source

I'd like to get started with code completion in Roslyn but could not find any simple examples that show how to do code completion.

What would be a good example to finish this code so that I can obtain all possible completion items (AKA Intellisense or CTRL+Space completion) at the caretIndex?

static void Main(string[] args)
{
    var code = @"
    using System;
    public class Test
    {
        public void TestMethod()
        {
            var now = DateTime.Now;
            now.
        }
    }";
    Console.WriteLine(code);

    var st = SyntaxTree.ParseText(code);
    var caretIndex = code.IndexOf("now.") + 4;

    //how to get possible code completions at caret index? (Intellisense)
}

Answer

Trillian picture Trillian · Dec 11, 2013

Roslyn does provide code completion services through the ICompletionService and ICompletionProvider interfaces, but they seem to mostly be internal and meant to be accessed when hosted within Visual Studio. However, it is possible to get a hold of the C# code completion types using a couple of reflection hacks, as illustrated by the ScriptCS Pad project. If you can get this to work, I expect you should get VS-grade code completion.

Alternatively, you can do it "by hand", using the public APIs provided by Roslyn. I am not well-versed in them, but the following should get you started in listing the members of the expression to the left of the member access dot. Note that it ignores extension methods and visibility rules, does no error handling and is probably flawed in many other ways. Doing this reliably probably doesn't qualify as "simple" code completion, though.

var code = @"
    using System;
    public class Test
    {
        public void TestMethod()
        {
            var now = DateTime.Now;
            now.
        }
    }";
Console.WriteLine(code);

var syntaxTree = CSharpSyntaxTree.ParseText(code);
var compilation = CSharpCompilation.Create("foo")
    .AddReferences(MetadataReference.CreateAssemblyReference(typeof(DateTime).Assembly.FullName))
    .AddSyntaxTrees(syntaxTree);
var semanticModel = compilation.GetSemanticModel(syntaxTree);

var dotTextSpan = new TextSpan(code.IndexOf("now.") + 3, 1);
var memberAccessNode = (MemberAccessExpressionSyntax)syntaxTree.GetRoot().DescendantNodes(dotTextSpan).Last();

var lhsType = semanticModel.GetTypeInfo(memberAccessNode.Expression).Type;

foreach (var symbol in lhsType.GetMembers())
{
    if (!symbol.CanBeReferencedByName
        || symbol.DeclaredAccessibility != Accessibility.Public
        || symbol.IsStatic)
        continue;

    Console.WriteLine(symbol.Name);
}

EDIT: Note that this answer was probably made obsolete by the new Roslyn bits.