- PVSM.RU - https://www.pvsm.ru -

Плагин для работы с Gist в Notepad++

Плагин для работы с Gist в Notepad++ Однажды мне потребовалось создать gist, а еще я активно пользуюсь Notepad++. После того, как найти плагин для работы с gist в Notepad++ мне не удалось (есть только под Sublime), я решил написать свой. Ну и к тому же это было хорошим опытом в написании плагинов и работе с github api.
Сразу выкладываю ссылку на исходники:
NppGist sources [1] и сам плагин: NppGist [2] (для его подключения просто перенесите файл в папку plugins в папке Notepad++).

Под Notepad++ плагины можно писать на нескольких языках: C++, Ada, Delphi, .NET, но я остановился на последнем из-за скорости разработки и из-за того, что я лучше его знаю.

Для разработки было использовано следующее:

  1. NppPlugin.NET [3] — шаблон Notepad++ плагина для .NET платформы.
  2. ServiceStack.Text [4] — сериализация и десериализация JSON (высокая производительность [5] и небольшой размер).
  3. hurl.it [6] — удобный онлайн-инструмент для составления и тестирования GET, POST, DELETE и других запросов.
  4. NUnit — юнит-тестирование.

Тем, кому интересно знать, как писать плагины под Notepad++, добро пожаловать под кат.

Инициализация плагина

Взаимодействие с Notepad++ происходит посредством Win32 сообщений. Но, к счастью, под .NET уже написан готовый шаблон плагина со всеми сообщениями, классами и структурами (NppPlugin.NET.v0.5 [3]). Стоит отметить, что Platform taget нужно установить в x86, вместо Any CPU по умолчанию, а также использовать .NET 4.0, а то плагин работать не будет.

Инициализация плагина происходит в методах CommandMenuInit и SetToolBarIcon. В первом добавляются пункты, которые будут видны в меню плагина следующим образом:

PluginBase.SetCommand(OpenCommandId, "Open Gist", OpenGistCommand, new ShortcutKey(false, false, false, Keys.None));

Там же можно и назначить комбинации клавиш для определенных команд (в разработанном плагине они не используются).

Метод же OpenGistCommand уже описывается разработчиком, и в нем можно делать уже все, что угодно. Для данной команды — открытие окна.

В методе SetToolBarIcon можно добавить иконки с командами плагина в панель инструментов Notepad++.

toolbarIcons tbIcons = new toolbarIcons();
tbIcons.hToolbarBmp = tbLoad.GetHbitmap();
IntPtr pTbIcons = Marshal.AllocHGlobal(Marshal.SizeOf(tbIcons));
Marshal.StructureToPtr(tbIcons, pTbIcons, false);
Win32.SendMessage(PluginBase.nppData._nppHandle, NppMsg.NPPM_ADDTOOLBARICON, PluginBase._funcItems.Items[OpenCommandId]._cmdID, pTbIcons);
Marshal.FreeHGlobal(pTbIcons);
Сохранение и загрузка настроек

Для сохранения и загрузки параметров плагина, используются следующие методы. Строки также можно сохранять.

saveLocally = Convert.ToBoolean(Win32.GetPrivateProfileInt("Settings", "saveLocally", 1, IniFileName));
...
Win32.WritePrivateProfileString("Settings", "SaveLocally", (Convert.ToInt32(saveLocally)).ToString(), Main.IniFileName);
Выполнение команд в Notepad++

На самом деле Notepad++ использует компонент Scintilla, который используется и в других редакторах текста. Таким образом, сообщения для взаимодействия делятся на Notepad++ и Scintilla. Все возможные коды для сообщений перечислены в файле NppPluginNETHelper.cs. Notepad++ сообщения имеют префикс NPPM и служат для команд, связанные с работой с файлами, меню, табами, языками и т.д. Scintilla сообщения, в свою очередь, связаны непосредственно с редактором текста (вставка, удаление, выделение, визуальные стили, фолдинг, скроллинг и т.д.).

Перехват событий Notepad++

Для перехвата сообщениях о событиях в Notepad++, используется метод beNotified в файле UnmanagedExports.cs. Данные сообщения имеют префикс NPPN для Notepad++ событий (открытие, закрытие файла, переключение вкладок) и SCN для Scintilla событий (изменение текста). Правда в данном плагине это не используется.

Полный список и подробное описание команд по Notepad++ находится здесь: Messages And Notifications [7]. А по Scintilla здесь: ScintillaDoc [8].

Корректное получение UTF8 текста из Notepad++

В .NET оболочке плагина почему-то нельзя получить текст в UTF8 формате, хотя эта кодировка является самой распространенной. Поэтому был дописано следующее свойство, которое позволило корректно считывать в том числе и русский текст, который используется при сохранении гиста.

public string lpstrTextUtf8
{
	get
	{
		_readNativeStruct();
		int len = 0;
		while (Marshal.ReadByte(_sciTextRange.lpstrText, len) != 0)
			++len;
		if (len == 0)
			return string.Empty;
		byte[] buffer = new byte[len];
		Marshal.Copy(_sciTextRange.lpstrText, buffer, 0, buffer.Length);
		return Encoding.UTF8.GetString(buffer);
	}
}
Встраивание сборок

Notepad++ загружает плагины из всех .dll файлов, находящихся в папке plugins. Причем если плагин из dll загрузить не удалось, выводится сообщение с таким содержанием: The plugin is not compatible with current version of Notepad++. Таким образом, если в эту папку вместе с сами плагином копировать его зависимости (в данном случае JSON), то это будет не очень правильно. Можно было конечно использовать для них папку, но я воспользовался более изящным решением, а именно, объединение всех зависимостей с самой сборкой плагина. Да и к тому же один файл удобней распространять и копировать.

Итак, чтобы плагин занимал только одну dll, сторонние сборки помечались как Embedded Resource, а потом динамически подключались следующим образом:

static Main()
{
	AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;
}

private static Assembly ResolveEventHandler(object sender, ResolveEventArgs args)
{
	string resource = string.Format("{0}.{1}.dll", PluginName, args.Name.Remove(args.Name.IndexOf(',')));
	Assembly currentAssembly = Assembly.GetExecutingAssembly();
	using (Stream stream = currentAssembly.GetManifestResourceStream(resource))
	{
		var bytes = new byte[(int)stream.Length];
		stream.Read(bytes, 0, (int)stream.Length);
		return Assembly.Load(bytes);
	}
}

Подробнее о том, как это делается, можно почитать в статье на CodeProject: Load DLL From Embedded Resource [9].

Можно было, конечно, воспользоваться сторонней программой для объединения сборок в одну, ILmerge [10] например, но ее нужно было бы применять после каждого билда.

Стоит отметить, что, так как ServiceStack.Text является NuGet сборкой и подгружается во время первого билда после клонирования репозитория, то для ее копирования в корневую папку проекта NppGist, использовалось Prebuild событие.

GitHub Api

В реализации взаимодействия с GitHub Api ничего интересного нет, разве что стоит упомянуть, что для авторизации используется AccessToken, который можно получить на сайте [11], который потом передается во все запросах в виде параметра access_token. Анонимные гисты в разработанном плагине не поддерживаются. Полный список используемых API методов перечислен здесь: github gists api [12].

Заключение

Выглядит окно сохранения гиста так (окно открытия аналогичное):
Плагин для работы с Gist в Notepad++

Но в начале нужно ввести ваш access token.

Надеюсь, что после моей статьи, всем желающим станет проще писать плагины под Notepad++. При желании присоединяйтесь к разработке и используйте плагин.

Автор: KvanTTT

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/open-source/57638

Ссылки в тексте:

[1] NppGist sources: https://github.com/KvanTTT/NppGist

[2] NppGist: https://github.com/KvanTTT/NppGist/releases/download/v1.0/NppGist.dll

[3] NppPlugin.NET: http://sourceforge.net/projects/sourcecookifier/files/other%20plugins/NppPlugin.NET.v0.5.zip/download

[4] ServiceStack.Text: https://github.com/ServiceStack/ServiceStack.Text

[5] высокая производительность: http://

[6] hurl.it: http://www.hurl.it/

[7] Messages And Notifications: http://sourceforge.net/apps/mediawiki/notepad-plus/index.php?title=Messages_And_Notifications

[8] ScintillaDoc: http://www.scintilla.org/ScintillaDoc.html

[9] Load DLL From Embedded Resource: http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource

[10] ILmerge: http://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx

[11] на сайте: https://github.com/settings/tokens/new

[12] github gists api: https://developer.github.com/v3/gists/

[13] Источник: http://habrahabr.ru/post/215769/