Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,640
|
Comments: 51,260
Privacy Policy · Terms
filter by tags archive
time to read 5 min | 852 words

The last time we looked at this issue, we built all the pieces that were required, except for the most important one, actually handling the contextual menu. I am going to be as open as possible, Intellisense is not a trivial task. Nevertheless, we can get pretty good results without investing too much time if we want to. As a reminder, here is the method that is actually responsible for the magic that is about to happen:

public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
{
        return new ICompletionData[] {
             new DefaultCompletionData("Text", "Description", 0),
             new DefaultCompletionData("Text2", "Description2", 1)
        };
}

Not terribly impressive yet, I know, but let us see what we can figure out now. First, we need to find what is the current expression that the caret is located on. That will give us the information that we need to make a decision. We could try to parse the text ourselves, or use the existing Boo Parser. However, the Boo Parser isn't really suitable for the kind of precise UI work that we need here. There are various incompatibilities along the way ( from the way it handles tabs to the nesting of expressions ). None of them is a blocker, and using the Boo Parser is likely the way you want for the more advance scenarios.

Reusing the #Develop parser gives us all the information we need, and we don't need to define things twice. Because we are going to work on a simple language, this is actually the simplest solution. Let us see what is involved in this.

public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
{
    TextWord prevNonWhitespaceTerm = FindPreviousMethod(textArea);
    if(prevNonWhitespaceTerm==null)
        return EmptySuggestion(textArea.Caret);

    var name = prevNonWhitespaceTerm.Word;
    if (name == "specification" || name == "requires" || name == "same_machine_as" || name == "@")
    {
        return ModulesSuggestions();
    }
    int temp;
    if (name == "users_per_machine" || int.TryParse(name, out temp))
    {
        return NumbersSuggestions();
    }
    return EmptySuggestion(textArea.Caret);
}

private TextWord FindPreviousMethod(TextArea textArea)
{
    var lineSegment = textArea.Document.GetLineSegment(textArea.Caret.Line);
    var currentWord = lineSegment.GetWord(textArea.Caret.Column);
    if (currentWord == null && lineSegment.Words.Count > 1)
        currentWord = lineSegment.Words[lineSegment.Words.Count - 1];
    // we actually want the previous word, not the current one, in order to make decisions on it.
    var currentIndex = lineSegment.Words.IndexOf(currentWord);
    if (currentIndex == -1)
        return null;

    return lineSegment.Words.GetRange(0, currentIndex).FindLast(word => word.Word.Trim() != "") ;
}

Again, allow me to reiterate that this is a fairly primitive solution, but it is a good one for our current needs. I am not going to go over all the suggestion methods, but here is the ModulesSuggestion method, which is responsible for the screenshot below:

private ICompletionData[] ModulesSuggestions()
{
    return new ICompletionData[]
    {
        new DefaultCompletionData("@vacations", null, 2),
        new DefaultCompletionData("@external_connections", null, 2),
        new DefaultCompletionData("@salary", null, 2),
        new DefaultCompletionData("@pension", null, 2),
        new DefaultCompletionData("@scheduling_work", null, 2),
        new DefaultCompletionData("@health_insurance", null, 2),
        new DefaultCompletionData("@taxes", null, 2),
    };
}

And this is how it looks like.

image

It works, it is simple, and it doesn't take too much time to build. If we want to get more than this, we probably need to start utilizing the boo parser directly, which will give us a lot more context than the text tokenizer that #Develop is using for syntax highlighter. Nevertheless, I think this is good work.

time to read 1 min | 147 words

I just took a look at how this feature is exposed. I really wants this feature. I hit the 2,100 parameters limit of SQL Server too many times in the past, always when I had to do some large IN queries. So, I was very happy to hear about that feature, but I didn't really take a look until now.

Unfortunately, the way they are implemented requires a hard reference to them. You have to create the type in the server,  and then you have to reference it by name. Annoying, to say the least, and it looks like there isn't any generic solution that I can accept. This is bad because I can think of quite a few uses for this feature, from applying batches to complex queries, it can be very useful, but it is looked in its own safe, statically typed, world. Urgh!

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. API Design (10):
    29 Jan 2026 - Don't try to guess
  2. Recording (20):
    05 Dec 2025 - Build AI that understands your business
  3. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  4. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  5. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
View all series

Syndication

Main feed ... ...
Comments feed   ... ...