Syntax Highlighting for go in vb.net

Viewed 55

Ok so I have been making a simple code editor in vb.net for go.. (for personal uses) I tried this code -

Dim tokens As String = "(break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var)"
        Dim rex As New Regex(tokens)
        Dim mc As MatchCollection = rex.Matches(TextBox2.Text)
        Dim StartCursorPosition As Integer = TextBox2.SelectionStart
        For Each m As Match In mc
            Dim startIndex As Integer = m.Index
            Dim StopIndex As Integer = m.Length
            TextBox2.[Select](startIndex, StopIndex)
            TextBox2.SelectionColor = Color.FromArgb(0, 122, 204)
            TextBox2.SelectionStart = StartCursorPosition

            TextBox2.SelectionColor = Color.RebeccaPurple
        Next

but I couldn't add something like print statements say I want a fmt.Println("Hello World"), that is not possible, anyone help me?

I want a simple result that will do proper syntax without glitching text colors like this current code does.

1 Answers

Here's a code showing how to update highlighting with strings and numbers. You would need to tweak it further to support syntax like comments, etc.

private Regex BuildExpression()
{
    string[] exprs = { 
        "(break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var)", 
        @"([0-9]+\.[0-9]*(e|E)(\+|\-)?[0-9]+)|([0-9]+\.[0-9]*)|([0-9]+)",
        "(\"\")|\"((((\\\\\")|(\"\")|[^\"])*\")|(((\\\\\")|(\"\")|[^\"])*))"
    };

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < exprs.Length; i++)
    {
        string expr = exprs[i];
        if ((expr != null) && (expr != string.Empty))
            sb.Append(string.Format("(?<{0}>{1})", "_" + i.ToString(), expr) + "|");
    }

    if (sb.Length > 0)
        sb.Remove(sb.Length - 1, 1);

    RegexOptions options = RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase;
    return new Regex(sb.ToString(), options);
}

private void HighlightSyntax()
{
    var colors = new Dictionary<int, Color>();

    var expression = BuildExpression();
    Color[] clrs = { Color.Teal, Color.Red, Color.Blue };
    int[] intarray = expression.GetGroupNumbers();
    foreach (int i in intarray)
    {
        var name = expression.GroupNameFromNumber(i);
        if ((name != null) && (name.Length > 0) && (name[0] == '_'))
        {
            var idx = int.Parse(name.Substring(1));
            if (idx < clrs.Length)
                colors.Add(i, clrs[idx]);
        }
    }

    foreach (Match match in expression.Matches(richTextBox1.Text))
    {
        int index = match.Index;
        int length = match.Length;
        richTextBox1.Select(index, length);
        for (int i = 0; i < match.Groups.Count; i++)
        {
            if (match.Groups[i].Success)
            {
                if (colors.ContainsKey(i))
                {
                    richTextBox1.SelectionColor = colors[i];
                    break;
                }
            }
        }
    }
}

What we found during development of our Code Editor libraries, is that the regular expression-based parsers are hard to adapt to fully support advanced syntax like contextual keywords (LINQ) or interpolated strings.

You might find a bit more information here: https://www.alternetsoft.com/blog/code-parsing-explained

The most accurate syntax highlighting for VB.NET can be implemented using Microsoft.CodeAnalysis API, it's the same API used internally by Visual Studio text editor. Below is sample code showing how to get classified spans for VB.NET code (every span contains start/end position within the text and classification type, i.e. keyword, string, etc.). These spans then can be used to highlight text inside a textbox.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;

public class VBClassifier
{

    private Workspace workspace;

    private static string FileContent = @"
Public Sub Run()
    Dim test as TestClass = new TestClass()   
End Sub";

    public void Classify()
    {
        var project = InitProject();
        var doc = AddDocument(project, "file1.vb", FileContent);
        var spans = Classify(doc);
    }


    protected IEnumerable<ClassifiedSpan> Classify(Document document)
    {
        var text = document.GetTextAsync().Result;
        var span = new TextSpan(0, text.Length);
        return Classifier.GetClassifiedSpansAsync(document, span).Result;
    }

    protected Document AddDocument(Project project, string fileName, string code)
    {
        var documentId = DocumentId.CreateNewId(project.Id, fileName);

        ApplySolutionChanges(s => s.AddDocument(documentId, fileName, code, filePath: fileName));

        return workspace.CurrentSolution.GetDocument(documentId);
    }

    protected virtual void ApplySolutionChanges(Func<Solution, Solution> action)
    {
        var solution = workspace.CurrentSolution;
        solution = action(solution);
        workspace.TryApplyChanges(solution);
    }

    protected MefHostServices GetRoslynCompositionHost()
    {
        IEnumerable<Assembly> assemblies = MefHostServices.DefaultAssemblies;
        var compositionHost = MefHostServices.Create(assemblies);

        return compositionHost;
    }

    protected Project CreateDefaultProject()
    {
        var solution = workspace.CurrentSolution;
        var projectId = ProjectId.CreateNewId();
        var projectName = "VBTest";

        ProjectInfo projectInfo = ProjectInfo.Create(
            projectId,
            VersionStamp.Default,
            projectName,
            projectName,
            LanguageNames.VisualBasic,
            filePath: null);

        ApplySolutionChanges(s => s.AddProject(projectInfo));

        return workspace.CurrentSolution.Projects.FirstOrDefault();
    }

    protected Project InitProject()
    {
        var host = GetRoslynCompositionHost();
        workspace = new AdhocWorkspace(host);

        return CreateDefaultProject();
    }
}

Update: Here's a Visual Studio project demonstrating both approaches: https://drive.google.com/file/d/1LLuzy7yDFAE-v40I7EswECYQSthxheEf/view?usp=sharing

enter image description here

Related