SharpDevelop Community

Get your problems solved!
Welcome to SharpDevelop Community Sign in | Join | Help
in Search

Siegfried Pammer

July 2009 - Posts

  • API Changes explained: Language Bindings and Project Bindings

    Important note: The changes discussed in this post apply only to SharpDevelop 4.0.0.4537 and later revisions.

    Why?

    As you might have noticed, I am one of the accepted Google Summer of Code 2009 students. While working on my project, the XAML binding, I had the idea of implementing an Outline Pad for XAML. After a short discussion with my mentor, we decided to add a general solution for implementing this functionality for C#, VB .NET, Boo, Windows Forms Designer and the (upcoming) WPF Designer too.

    For instance, normal backend bindings like the C#, VB .NET and Boo binding could display a tree of the current document structure with namespaces, classes, member variables, methods properties, etc. This would allow fast physical restructuring of a file. With XAML you could easily move a part of the XAML tree to another location. Same would go for the Windows Forms and WPF Designer.

    So each language binding or display binding should be able to tell the outline pad what content it should display to the user. The existing ILanguageBinding only represented a language from the point of view of the project system, there was no support for things like code completion, formatting or highlighting. So I changed the name of the existing ILanguageBinding to "IProjectBinding".

    The New Language Binding API

    The new ILanguageBinding is not project-based, but file- or, to be more specific, ITextEditor-based. What does this mean? It simply means, that every ITextEditor instance (that is, every file opened by the default text editor in SharpDevelop), gets its own ILanguageBinding instance. Furthermore, if you use the split-view functionality present in SharpDevelop, both views get their own ILanguageBinding instance too. Please keep that in mind, when implementing your own language bindings.

    So let's move on the practical part: In fact, the "API" only consists of one interface, plus wrappers for the SharpDevelop addin system, to make it easy for backend binding addins to provide their own language-specific implementation. We will discuss its structure first:

    public interface ILanguageBinding
    {
            IFormattingStrategy FormattingStrategy {
                get;
            }
           
            LanguageProperties Properties {
                get;
            }
           
            void Attach(ITextEditor editor);
           
            void Detach();
    }

    It is not very complex, but there are a few things to keep in mind:

    Attach is called only once on every ILanguageBinding instance (before any other method calls).

    SharpDevelop will never Detach() and re-Attach() a language binding; instead, it will detach the old binding and attach a new instance. A new instance is created in the following situations:

    • an ITextEditor is created by SharpDevelop,
    • the filename, of the file being edited, has changed
    • or the user switches to split-view mode.

    You can attach additional functionality like a custom highlighter or an IOutlineContentHost, to provide content displayed in the Outline pad, as mentioned at the beginning.

    If you override Attach, you should also override Detach. It is called only once in the life-cycle of an ILanguageBinding instance:

    • when an ITextEditor is closed (disposed by SharpDevelop),
    • before a new language binding is attached, after the filename, of the file being edited, has changed. (So SharpDevelop disposes the old one before creating a new one.)
    • after switching from split-view to normal view mode, to dispose the second view, as it is not needed anymore.

    You should detach (and dispose) all services and additional functionality, added by your language binding, from the ITextEditor at this point.

    FormattingStrategy should return an instance of the IFormattingStrategy implementation for your language binding.

    Properties should return a reference to the LanguageProperties implementation for your language binding.

    Example: XamlLanguageBinding

    I pasted the Attach and Detach method from the XamlLanguageBinding as a small example. Please note that adding custom syntax highlighting (in this case: XamlColorizer) and additional services, like IOutlineContentHost, requires a reference to the TextView of the AvalonEdit.TextEditor class. So this part can only be implemented for specific text editor controls.

    public override void Attach(ITextEditor editor)
    {
        base.Attach(editor);
        
        // try to access the ICSharpCode.AvalonEdit.Rendering.TextView
        // of this ITextEditor
        this.textView = editor.GetService(typeof(TextView)) as TextView;
        
        // if editor is not an AvalonEdit.TextEditor
        // GetService returns null
        if (textView != null) {
            colorizer = new XamlColorizer(editor, textView);
            // attach the colorizer
            textView.LineTransformers.Add(colorizer);
            // add the XamlOutlineContentHost, which manages the tree view
            textView.Services.AddService(typeof(IOutlineContentHost), new XamlOutlineContentHost(editor));
        }
    }

    public override void Detach()
    {
        base.Detach();
        
        // if we added something before
        if (textView != null && colorizer != null) {
            // remove and dispose everything we added
            textView.LineTransformers.Remove(colorizer);
            textView.Services.RemoveService(typeof(IOutlineContentHost));
            colorizer.Dispose();
        }
    }

    Nice, But How Do I Register My Language Binding With SharpDevelop?

    Similar to all other parts of SharpDevelop, just add it to the addin path /SharpDevelop/Workbench/LanguageBindings like this:

    <Path name="/SharpDevelop/Workbench/LanguageBindings">
        <LanguageBinding
            id="XAML"
            class="ICSharpCode.XamlBinding.XamlLanguageBinding"
            extensions=".xaml" />
    </Path>

    short explanation:

    id: The name of the LanguageBinding, must be unique, preferably the same as your project binding or code completion binding.

    class: The fully-qualified name of your language binding class.

    extensions: A semicolon-separated list of file extensions, that are handled by your language binding. Please note that multiple LanguageBindings can handle a file extension. But only the first non-null FormattingStrategy is used or only the first non-null LanguageProperties instance is used. You can control the order of the language bindings using the insertbefore and insertafter attributes.

    Important: In the past you were able to register a formatting strategy by adding it to /AddIns/DefaultTextEditor/Formatter/, this was removed. Now the IFormattingStrategy gets set by overriding the FormattingStrategy property in the Language Binding implementation.

    How To Access The Features From AddIns?

    When dealing with text displayed inside a SharpDevelop text editor, the easiest way to do this is to use the ITextEditor interface. The active language binding can be accessed by using ITextEditor.Language property.

    Project Bindings

    I have not done any changes to the project binding API, except renaming everything. Project bindings are responsible for project management (reading project files, compilation management, etc.). XAML does not need its own project format (there is no .xamlproj ;-)), so I cannot provide an example and explain it to you, please take a look at existing project bindings such as CSharpProjectBinding, VBNetProjectBinding or BooProjectBinding.

    Conclusion

    I think the language bindings make it easier to extend ITextEditor with language specific features and also allow other AddIns to access these features. In my next post I will explain my work on the XAML binding more in detail and guide you through the features I implemented.

    If you have any questions concerning language bindings and these changes, please feel free to ask.

Powered by Community Server (Commercial Edition), by Telligent Systems
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.