In This Article

IntelliPrompt Navigable Symbol Provider

Languages can choose to support an IntelliPrompt navigable symbol provider that lists the accessible symbols within an editor's document. Once a language implements this provider service, the Navigable Symbol Selector control can be paired with a SyntaxEditor to provide VS type/member dropdown-like functionality.

Basic Concepts

Navigable symbols are any object that implements the INavigableSymbol interface. The NavigableSymbol class is a default implementation. Symbols represents a type or member within a document's text. They each provide these bits of information:

Member Description
ContentProvider Property Gets a Content Provider that creates the content to render for the symbol within UI.
NavigationSnapshotOffset Property Gets the TextSnapshotOffset within the symbol's source file to navigate when the symbol is selected.
SnapshotRange Property Gets a TextSnapshotRange indicating the offset range of the symbol within its source file.

So for instance in a C# language, a symbol that represented a class declaration would have a content provider that listed the name of the class, a navigation snapshot offset that was the offset of the class name within the document, and a snapshot range that covered everything from the type's XML comments through to its closing curly brace.

When an INavigableSymbolProvider is called to obtain accessible symbols, it is passed an INavigableRequestContext object. This object provides additional information about what is requesting the symbols.

The NavigableRequestContexts.NavigableSymbolSelector is currently the only pre-defined request context, and indicates that a Navigable Symbol Selector control is making the request.

The INavigableSymbolProvider interface is the language service used to return accessible symbols to callers. It has a single GetSymbols method that takes in:

If the parent symbol is a null value, it means the request is for all root symbols within the document. If a parent symbol is specified, then the request is for the members of that root symbol.

The ITextSnapshot provides access to its container ICodeDocument, and via that, the document's parse data, which usually contains information such as an AST. That is assuming that the language has a parser that constructs an AST.

The GetSymbols method implementation should use information such as the AST to create INavigableSymbol objects based on the parameters passed in, and then return those symbols. By doing so, controls such as Navigable Symbol Selector can consume this information and provide an advanced editing experience for end users.

This code shows a sample GetSymbols implementation. It assumes that whatever type you use for parse data (in this case our ILLParseData type) has an AST available. Then it calls a AddTypeSymbolsFromAst or AddMemberSymbolsFromAst method (both would need implementation based on your language) and passes along the relevant information needed to build the symbol list.

public IEnumerable<INavigableSymbol> GetSymbols(INavigableRequestContext context, ITextSnapshot snapshot, INavigableSymbol parentSymbol) {
	if (context == NavigableRequestContexts.NavigableSymbolSelector) {
		var document = snapshot.Document as ICodeDocument;
		if (document != null) {
			// If there is AST data...
			var parseData = document.ParseData as ILLParseData;
			if ((parseData != null) && (parseData.Ast != null)) {
				// Recurse into the AST
				var symbols = new List<INavigableSymbol>();
				if (parentSymbol != null)
					this.AddMemberSymbolsFromAst(symbols, parseData.Snapshot ?? snapshot, parseData.Ast, parentSymbol);
				else
					this.AddTypeSymbolsFromAst(symbols, parseData.Snapshot ?? snapshot, parseData.Ast);
				
				// Sort (navigationSymbolComparer is a cached NavigableSymbolContentProviderComparer instance)
				symbols.Sort(navigationSymbolComparer);

				return new NavigableSymbolSet(symbols);
			}
		}
	}

	// No results
	return new NavigableSymbolSet(null);
}

Registering with a Language

Any object that implements INavigableSymbolProvider can be associated with a syntax language by registering it as an INavigableSymbolProvider service on the language.

This code creates a custom navigable symbol provider and registers it with the syntax language that is already declared in the language variable:

INavigableSymbolProvider provider = new CustomNavigableSymbolProvider();
language.RegisterNavigableSymbolProvider(provider);
Note

The SyntaxLanguageExtensions.RegisterNavigableSymbolProvider method in the code snippet above is a helper extension method that gets added to ISyntaxLanguage objects when the ActiproSoftware.Text namespace is imported. See the Service Locator Architecture topic for details on registering and retrieving various service object instances, both via extension methods and generically, as there are some additional requirements for using the extension methods.