Ответ 1
У меня был тот же самый вопрос, и теперь я просматривал веб несколько часов, пока не смог понять и объяснить, как вам нужно начинать с такого расширения.
В следующем примере мы создадим небольшое и немое расширение, которое всегда будет добавлять "Hello" к началу файла кода при редактировании. Это очень просто, но должно дать вам представление о том, как продолжать развивать эту вещь.
Будьте осторожны: вам необходимо полностью проанализировать файлы кода - Visual Studio не дает вам никакой информации о том, где классы, методы или что-то еще и что они содержат. Это самое большое препятствие, которое нужно предпринять при создании инструмента форматирования кода и не будет рассмотрено в этом ответе. [*]
Для тех, кто пропустил этот ответ, убедитесь, что вы сначала загрузили и установили SDK Visual Studio, или вы не найдете тип проекта, упомянутый на первом шаге.
Создание проекта
-
Начните с создания нового проекта типа "Visual С# > Расширяемость > VSIX Project" (отображается только в том случае, если в качестве целевой среды выбрана .NET Framework 4). Обратите внимание, что вам может потребоваться выбрать тип проекта "Редактор классификатора" вместо типа "VSIX Project", чтобы он работал. комментарий ниже.
-
После создания проекта откроется файл "source.extension.vsixmanifest", который даст вам возможность указать имя продукта, автора, версию, описание, значок и т.д. Я думаю, что этот шаг довольно самообучающийся, вы можете закрыть вкладку и восстановить ее позже, открыв файл vsixmanifest.
Создание класса слушателя для получения уведомления о создании экземпляра текстового редактора
Далее, нам нужно слушать всякий раз, когда текстовый редактор был создан в Visual Studio и привязан к нему инструмент форматирования кода. Текстовым редактором в VS2010 является экземпляр IWpfTextView
.
-
Добавьте новый класс в наш проект и назовите его
TextViewCreationListener
. Этот класс должен реализовать интерфейсMicrosoft.VisualStudio.Text.Editor.IWpfTextViewCreationListener
. Вам нужно добавить ссылку на Microsoft.VisualStudio.Text.UI.Wpf в свой проект. Сборка DLL находится в вашем каталоге SDK Visual Studio в разделе VisualStudioIntegration\Common\Assemblies\v4.0. -
Вы должны реализовать метод
TextViewCreated
интерфейса. Этот метод имеет параметр, определяющий экземпляр созданного текстового редактора. Мы создадим новый класс форматирования кода, который этот экземпляр будет передан позже. -
Нам нужно сделать класс
TextViewCreationListener
видимым для Visual Studio, указав атрибут[Export(typeof(IWpfTextViewCreationListener))]
. Добавьте ссылку на System.ComponentModel.Composition для вашего проекта для атрибутаExport
. -
Кроме того, нам нужно указать, с какими типами файлов форматировщик кода должен быть привязан к текстовому редактору. Нам просто нравится форматировать файлы кода, а не текстовые файлы, поэтому мы добавляем атрибут
[ContentType("code")]
в класс слушателя. Для этого вам нужно добавить ссылку на Microsoft.VisualStudio.CoreUtility для вашего проекта. -
Кроме того, мы хотим изменить только редактируемый код, а не цвета или украшения вокруг него (как показано в примерах проектов), поэтому мы добавляем в класс атрибут
[TextViewRole(PredefinedTextViewRoles.Editable)]
. Снова вам нужна новая ссылка, на этот раз в Microsoft.VisualStudio.Text.UI. -
Отметьте класс как запечатанный. По крайней мере, моя рекомендация. Теперь ваш класс должен выглядеть примерно так:
[ContentType("code")] [Export(typeof(IWpfTextViewCreationListener))] [TextViewRole(PredefinedTextViewRoles.Editable)] internal sealed class TextViewCreationListener : IWpfTextViewCreationListener { public void TextViewCreated(IWpfTextView textView) { } }
Создание класса для форматирования кода
Далее нам нужен класс, обрабатывающий логику форматирования кода, методы сортировки и т.д. Опять же, в этом примере он будет просто добавлять "Hello" к началу файла всякий раз, когда будет сделано редактирование.
-
Добавьте в свой проект новый класс
Formatter
. -
Добавьте конструктор, который принимает один аргумент
IWpfTextView
. Помните, что мы хотели передать созданный экземпляр редактора этому классу форматирования в методеTextViewCreated
нашего класса слушателя (просто добавьтеnew Formatter(textView);
к этому методу). -
Сохраните прошедший экземпляр в переменной-члене. Это станет удобно при форматировании кода позже (например, для получения позиции каретки). Также свяжите события
Changed
иPostChanged
свойстваTextBuffer
экземпляра редактора:public Formatter(IWpfTextView view) { _view = view; _view.TextBuffer.Changed += new EventHandler<TextContentChangedEventArgs>(TextBuffer_Changed); _view.TextBuffer.PostChanged += new EventHandler(TextBuffer_PostChanged); }
-
Событие
Changed
вызывается каждый раз, когда выполняется редактирование (например, ввод char, вставка кода или программные изменения). Поскольку он также реагирует на программные изменения, я использую определение bool, если наше расширение или пользователь/что-то еще меняет код в настоящий момент и вызывают мой собственный методFormatCode()
, только если наше расширение еще не редактирует. В противном случае вы будете рекурсивно вызывать этот метод, который приведет к сбою Visual Studio:private void TextBuffer_Changed(object sender, TextContentChangedEventArgs e) { if (!_isChangingText) { _isChangingText = true; FormatCode(e); } }
-
Мы должны reset изменить эту переменную-член bool в обработчике события
PostChanged
наfalse
. -
Передайте события args события
Changed
нашему пользовательскому методуFormatCode
, потому что они содержат то, что изменилось между последним и теперь. Эти изменения хранятся в массивеe.Changes
типаINormalizedTextChangeCollection
(s. Ссылка в конце моего сообщения для получения дополнительной информации об этом типе). Мы перебираем все эти изменения и вызываем наш собственный методHandleChange
с новым текстом, который это редактирование произвело.private void FormatCode(TextContentChangedEventArgs e) { if (e.Changes != null) { for (int i = 0; i < e.Changes.Count; i++) { HandleChange(e.Changes[0].NewText); } } }
-
В методе
HandleChange
мы могли бы сканировать ключевые слова, чтобы обрабатывать их определенным образом (помните, что вам нужно разобрать любой код на себя!), но здесь мы просто тупо добавляем "Hello" к начало файла для целей тестирования. Например. мы должны изменитьTextBuffer
нашего экземпляра редактора. Для этого нам нужно создать объектITextEdit
, с помощью которого мы можем манипулировать текстом и применять его впоследствии. Код довольно самообучающийся:private void HandleChange(string newText) { ITextEdit edit = _view.TextBuffer.CreateEdit(); edit.Insert(0, "Hello"); edit.Apply(); }
При компиляции этой надстройки экспериментальный куст Visual Studio запускается только с загруженным расширением. Создайте новый файл С# и начните вводить текст, чтобы увидеть результаты.
Надеюсь, это даст вам некоторые идеи о продолжении этой темы. Я должен сам исследовать его сейчас.
Я очень рекомендую документацию текстовой модели редактора на MSDN, чтобы получить подсказки о том, как вы могли бы это сделать и что. http://msdn.microsoft.com/en-us/library/dd885240.aspx#textmodel
Сноска
[*] Обратите внимание, что Visual Studio 2015 или новее поставляются с платформой Compiler Rosyln, которая уже уже анализирует файлы С# и VB.NET для вас (и, возможно, другие предварительно установленные языки тоже) и раскрывает их иерархическую синтаксическую структуру, но Я еще не специалист в этой теме, чтобы дать ответ о том, как использовать эти новые службы. Основной ход запуска расширения редактора остается таким же, как и в этом ответе. Имейте в виду, что - если вы используете эти службы - вы станете зависимыми от Visual Studio 2015+, а расширение не будет работать в более ранних версиях.