Gist plugin in Notepad ++

    Once I needed to create a gist, and I also actively use Notepad ++. After I could not find a plugin for working with gist in Notepad ++ (there is only under Sublime), I decided to write my own. Well, besides, it was a good experience in writing plugins and working with github api.
    I immediately post the link to the sources:
    NppGist sources and the plugin itself: NppGist (to connect it, just transfer the file to the plugins folder in the Notepad ++ folder).



    Under Notepad ++, plugins can be written in several languages: C ++, Ada, Delphi, .NET, but I settled on the latter because of the speed of development and because I know it better.

    The following was used for development:
    1. NppPlugin.NET - Notepad ++ plugin template for .NET platform.
    2. ServiceStack.Text - serialization and deserialization of JSON ( high performance and small size).
    3. hurl.it is a convenient online tool for compiling and testing GET, POST, DELETE and other requests.
    4. NUnit - unit testing.

    For those who are interested in knowing how to write plugins under Notepad ++, welcome to cat.

    Plugin initialization

    Interaction with Notepad ++ occurs through Win32 messages. But, fortunately, a ready-made plug-in template with all messages, classes and structures ( NppPlugin.NET.v0.5 ) is already written under .NET . It is worth noting that the Platform taget needs to be installed in x86 , instead of Any CPU by default, and also use .NET 4.0, otherwise the plugin will not work.

    The plugin is initialized in the CommandMenuInit and SetToolBarIcon methods . The first one adds items that will be visible in the plugin menu as follows:
    PluginBase.SetCommand(OpenCommandId, "Open Gist", OpenGistCommand, new ShortcutKey(false, false, false, Keys.None));
    

    There you can also assign key combinations for certain commands (they are not used in the developed plugin).

    The OpenGistCommand method is already described by the developer, and you can already do anything in it. For this command - opening a window.

    In the SetToolBarIcon method, you can add icons with the plugin commands to the Notepad ++ toolbar.
    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);
    


    Saving and loading settings

    To save and load plug-in parameters, the following methods are used. Lines can also be saved.
    saveLocally = Convert.ToBoolean(Win32.GetPrivateProfileInt("Settings", "saveLocally", 1, IniFileName));
    ...
    Win32.WritePrivateProfileString("Settings", "SaveLocally", (Convert.ToInt32(saveLocally)).ToString(), Main.IniFileName);
    


    Executing Commands in Notepad ++

    In fact, Notepad ++ uses the Scintilla component, which is also used in other text editors. Thus, messages for interaction are divided into Notepad ++ and Scintilla. All possible codes for messages are listed in the NppPluginNETHelper.cs file . Notepad ++ messages have the NPPM prefix and are used for commands related to working with files, menus, tabs, languages, etc. Scintilla messages, in turn, are directly related to the text editor (insert, delete, highlight, visual styles, folding, scrolling, etc.).

    Notepad ++ event hook

    To intercept event messages in Notepad ++, the beNotified method in the UnmanagedExports.cs file is used . These messages are prefixed with NPPN for Notepad ++ events (opening, closing a file, switching tabs) and SCN for Scintilla events (changing text). True, this plugin is not used.

    A complete list and detailed description of Notepad ++ commands is here: Messages And Notifications . And for Scintilla here: ScintillaDoc .

    Correctly receiving UTF8 text from Notepad ++

    For some reason, in the .NET plugin shell it is impossible to get text in UTF8 format, although this encoding is the most common. Therefore, the following property was added, which made it possible to correctly read, including the Russian text, which is used to save the hist.
    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);
    	}
    }
    


    Build Assemblies

    Notepad ++ loads plugins from all .dll files located in the plugins folder. Moreover, if the plugin from the dll could not be loaded, a message is displayed with the following content: The plugin is not compatible with current version of Notepad ++ . Thus, if you copy its dependencies (in this case JSON) into this folder together with the plug-in itself, then this will not be very correct. Of course, you could use a folder for them, but I used a more elegant solution, namely, combining all the dependencies with the plug-in assembly itself. And besides, it’s more convenient to distribute and copy one file.

    So, so that the plugin takes up only one dll, third-party assemblies are marked as Embedded Resource , and then dynamically connected as follows:
    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);
    	}
    }
    


    Read more about how this is done in the CodeProject article: Load DLL From Embedded Resource . Of

    course, you could use a third-party program to combine assemblies into one, ILmerge for example, but it would have to be applied after each build.

    It is worth noting that, since ServiceStack.Text is a NuGet assembly and is loaded during the first build after cloning the repository, the Prebuild event was used to copy it to the root folder of the NppGist project.

    Github api

    There is nothing interesting in the implementation of interaction with GitHub Api, unless it is worth mentioning that AccessToken is used for authorization, which can be obtained on the site , which is then transmitted in all requests in the form of the access_token parameter . Anonymous histories in the developed plugin are not supported. A complete list of API methods used is listed here: github gists api .

    Conclusion

    The histogram save window looks like this (the opening window is similar):


    But at the beginning you need to enter your access token.

    I hope that after my article, it will become easier for everyone to write plug-ins for Notepad ++. If you wish, join the development and use the plugin.

    Also popular now: