В качестве очередного отчета решил показать реализацию всплывающих подсказок.

При реализации использованы стандартнее механизмы VS — IQuickInfoSource/IQuickInfoSourceProvider, так что плагины нитры будут интегрироваться с другими малагинами предоставляющими подсказки в студии. Однако я их немного подхачил, так что теперь они поддерживают вложенные подсказки и и навигацию. Потрахаться пришлось вдоволь. Дня три только в отладчике провел отлаживая декомпиленные исходники студии .

Общая идея работы с подсказками следующая. Есть язык разметки которым можно форматировать содержимое хинта и сообщений об ошибках. Символы могут переопределить метод GetHint() : string и вернуть из него то представление символа, которое нужно показывать пользователю. С сообщением об ошибке все еще проще, так как переопределять ничего не надо. Прост в месте где хочется создать такое сообщение можно применять разметку.

Язык разметки я назвал HintML. В нем поддерживаются следующие теги:
* hint — обрамляет текст хинта или вложенный хинт.
* keyword — подкрашивает текст цветом используемым для ключевых слов (по умолчанию синим).
* symbol — описывает символ. Атрибут span-class позволяет задать подсветку (из описываемых в языках спэн-классов). Атрибут id — позволяет задать id символа (берется из соответствующего свойства символа). Если id задано, для символа будет отображаться вложенных хинт.
* br или bl — задают конец строки. В принципе они не нужны, так как конец строки тоже работает. Возможно уберу в будущем.
* ref — ссылка (аналог url в HTML) позволяет вывести ссылку с некоторым действием. Пока что действий два "goto" и "goto line". Их можно использовать для навигации по коду. Атрибут handler — задает действие, а необязательный атрибут hint — всплывающую подсказку.
* b — bold.
* i — italic.
* u — underline.
* font — шрифт. Атрибуты: size, face, color. Ну, это должно быть всем и так ясно.
* code — моноширинный шрифт для кода.

Вот HintML описывающий символ из анимированной приведенной ниже гифки ниже. Приводу описание только одного символа, чтобы не путать. В реальноти таких описания два, так как под курсором два (неоднозначных) символа.

<hint><keyword>class</keyword> <symbol span-class="DotNetLang.Namespace" id="9943">Ns1</symbol>.<symbol span-class="Nitra.Language.Type">TypeA</symbol><br/><br/>Location: <ref handler="goto:c:\!\projects\consoleapplication6\consoleapplication6\program.ncs[143,23]" hint="c:\!\projects\consoleapplication6\consoleapplication6\program.ncs(9, 3)">program.ncs(9, 3)</ref>
</hint>


А это код сообщения об ошибке из того же хинта:

<hint>'TypeA' is an ambiguous reference between <symbol span-class="DotNetLang.Namespace" id="9943">Ns1</symbol>.<symbol span-class="Nitra.Language.Type" id="9944">TypeA</symbol> and <symbol span-class="DotNetLang.Namespace" id="9945">Ns2</symbol>.<symbol span-class="Nitra.Language.Type" id="9946">TypeA</symbol></hint>


Для упрощения генерации HintML я создал модуль:
namespace Nitra
{
  /// <summary>Utility methods for Hint Markup language.</summary>
  public module HintML
  {
    public GetSymbolId(symbol : DeclarationSymbol) : int
    public XmlEscape(text : string) : string
    public HintMlEscape(this builder : StringBuilder, text : object) : StringBuilder
    public HintMlQuote(this builder : StringBuilder, text : string) : StringBuilder
    public JoinAnd[T](this builder : StringBuilder, elems : Seq[T], converter : StringBuilder * T -> StringBuilder) : StringBuilder
    public HintMlAttr(this builder : StringBuilder, name : string, text : string) : StringBuilder
    public SymbolToHintMlWithSubHint(this builder : StringBuilder, symbol : DeclarationSymbol) : StringBuilder
    public SymbolToHintMl(this builder : StringBuilder, symbol : DeclarationSymbol) : StringBuilder
    public IsRoot(symbol : DeclarationSymbol) : bool { symbol.DeclaredInOpt.IsNone }
    public MakeHintMlPath(this builder : StringBuilder, symbol : DeclarationSymbol, separator : string, needSubhint : bool) : StringBuilder
    public MakeLocations(this builder : StringBuilder, symbol : DeclarationSymbol) : StringBuilder
    public HintMlException(this builder : StringBuilder, exception : Exception) : StringBuilder
  }
}


Ну, и собственно, как это выглядит:


PS

Основная работа по хинтам завершена, но баги еще есть. Они будут отлажены по ходу пьесы.
Автор: VladD2    Оценить