I was surprised how easy it is to build a simple code search solution based on Lucene and Roslyn. By code search solution I mean indexing C# files on disk and searching through them. Here's how it works.

After typing search term and hitting search I show the total number of hits (files found), file names and highlited terms:

CodeView

Details page is for seeing the complete file with the search terms highlighted:

Details

As expected, there are boolean and wildcard searchers. So, it is pretty simple, but here's an interesting twist. You can also search by class, function, parameter, return value and so on:

Field Purpose Example
class Find files where this class is declared and defined class:Bindings
method Where is this method defined? method:GetValueOrNull
parameter Show me methods for the given parameter name parameter:dictionary
return Locate methods returning this type return:double
base Look for the classes derived from the given class base:doubledocvalues
code Search within methods only. This can be helpful to ignore noice when too many hits are returned by general query. code:"prime*result"
comment I need to see where the given search term used as multi-line or single-line comment comment:todo

Essentially I ported to .Net this Java code where instead of Java AST parser I use Roslyn. For example, some of the indexing code looks like this:

public void Index(string contentPath)  
{
    var indexDirectory = new SimpleFSDirectory(new DirectoryInfo(_configuration.IndexPath));
    Log(string.Format("Begining to index {0}. Index location: {1}", contentPath, indexDirectory.Directory.FullName));
    using (var writer = new IndexWriter(indexDirectory, new CSharpAnalyzer(), true, IndexWriter.MaxFieldLength.UNLIMITED))
    {
        IndexDirectory(writer, new DirectoryInfo(contentPath));
    }
    Log(string.Format("Indexed {0:N0} files.", _fileCount));
}

private void IndexFile(IndexWriter writer, FileInfo file)  
{
    if (writer == null || file == null || Path.GetExtension(file.Name) != ".cs" 
        || !file.Exists || (file.Attributes & FileAttributes.Hidden) != 0)
    {
        return;
    }
    var doc = new Document();
    doc.Add(new Field(Fields.Content, file.OpenText(), Field.TermVector.WITH_OFFSETS));
    var parser = new CSharpParser();
    var syntax = parser.Parse(file.FullName);
    AddComments(doc, syntax);
    AddUsings(doc, syntax);
    AddClasses(doc, syntax);
    doc.Add(new Field(Fields.Path, Path.Combine(file.DirectoryName, file.Name), Field.Store.YES, Field.Index.NO));
    writer.AddDocument(doc); // here we can specify an analyzer
    _fileCount++;
}

CSharpParser is a simple Roslyn wrapper. I am going to publish this code on Github soon.