This article describes the CS-Script C# Intellisense plugin for Notepad++ (CSScriptNpp). CSScriptNpp plugin turns Notepad++ into a light and yet fully functional IDE-like environment for developing and executing C# code.
Introduction
CSScriptNpp is an attempt to extend Notepad++ with the top-quality "code assistance" as well as to give users ability to exercise "free standing" C# code without any deployment or licensing pain usually associated with other IDEs.
While the plugin does not compete either directly or indirectly with any well know IDEs, it offers many core features commonly found in the well established IDEs like Visual Studio, Mono Develop or Eclipse.
The plugin delivers MS Visual Studio style User Experience but does not replicate it. CSScriptNpp will never match 100% the editing power of Visual Studio and Visual Studio will never match the simplicity and high availability of CSScriptNpp.
The high availability of CSScriptNpp is attributed to the Notepad++ plugin deployment model. The only step you need to perform in order to start using CSScriptNpp is to activate it in the Notepad++ Plugin Manager:
Note: after the installation you may want to check for the latest CSScriptNpp version from the About Box.
The plugin allows execution of the C# code (scripts) directly from Notepad++, without the need for any infrastructure (e.g. project file). It also offers convenient launching of the C# scripts with the system CLR Debugger (e.g. VS) attached.
The plugin can also be used to prepare simple deployment packages (or executables) for executing the scripts on the systems without any CSScriptNpp component (or even Notepad++) installed.
The following is a complete list of the all plugin features:
- Intellisense related features:
- CLR type members auto-complete
- Add missing 'using'
- Show CLR type quick info (when hovering mouse over the type member)
- Show Method Overloads popup.
- Go to definition (F12)
- in the source code
- in the reconstructed referenced assembly API interface (including XML documentation)
- Smart Indentation
- Formatting C# source code
- CodeMap - panel with the class members of the current .cs document
- Support for 'plain vanilla' ECMA-compliant C# syntax
- Inclusion of the dependency scripts via CS-Script directives
- Implicit assembly referencing via automatic resolving namesspaces into assemblies
- Explicit assembly referencing via CS-Script directives
- Intercepting Debug output
- Intercepting Console output
- Conventional build/execution error reporting
- Preparing the script deployment package so it can be executed outside of Notepad++.
In this article I will only describe plugin's major features and the technical aspects (and challenges) of their implementation.
Acknowledgement
The CSScriptNpp plugin relies on a number of third-party solutions, of which two deserve special mentioning: CSharpCode.NRefactory and NppPlugin.NET. These two brilliant frameworks made CSScriptNpp possible. While other alternative solutions could be used, I wouldn't try to undertake such a challenging development without these two extraordinary solutions.
Overview
It is difficult to find someone developing on Windows who haven't heard about Notepad++. By many it is considered the best text editor and a "must have" source code editing tool. Its well deserved reputation is attributed to the clever architecture and close to flawless implementation.
Notepad++ is based on the text rendering engine Scintilla, which has been utilized by a number of source code editors. Apart from being very fast and rich in features, Scintilla has a very strong emphasis on the source code editing tasks. Notepad++ delivers all benefits of Scintilla and also extends them by allowing multi document editing as well as many other features. But arguably the most intriguing feature of Notepad++ its pluggable architecture. Thus the features, which are not available out of the box can be delivered via the third party plugins.
While Notepad++ is packed with features, there are still some gaps to be filled. Thus the auto-completion is entirely context ignorant. It is nothing else but a list of the "favourite words" grouped by the type of the source code documents. This may be OK for the interpreted languages it is not so for the compiled languages like C#. The comfort level of editing C# code with Notepad++ is not even comparable with that offered by Visual Studio. And this is exactly what the presented plugin CSScriptNpp is trying to resolve.
Not only it delivers the true context specific code auto-completion (like VS Intellisense) but is also allows convenient code execution, as well as many other features commonly found in the full scale IDEs.
I do believe that despite the fact that Visual Studio offers practically unbeatable code editing power, there is a strong need for an alternative tools with a smaller footprint, lighter runtime and more liberal licencing.
I was sincerely impressed by the unconventional motivation of Rob Eisenberg for his Caliburn.Micro. He said: "My vision was to take 90% of Caliburn’s features and squash them into 10% of the code". It is exactly how I feel about CSScriptNpp. Though in my case I was "squashing" Visual Studio features. I do not think that the plugin delivers 90% of them. May by just 70% of the most commonly used ones. And the effort and the codebase size is probably <1%.
The Intellisese related features implemented with the help of NRefactory but the heart of the plugin is the CS-Script execution engine - my another pet-project.
I always felt that CS-Script is kind of incomplete without a rich dedicated editor. I even made a serious attempt to develop one. I spend quite some time reverse engineering the SharpDevelop and have almost completed a C# dedicated editor - custom WinForm based editor wrapping the pre-version-5 NRefactory.
However I was not satisfied with its performance and not generic enough nature. Thus I canned the project and instead concentrated on the integrating CS-Script with Visual Studio (CS-Script VS Tools). But I always felt jealousy towards Notepad++ for its ultimate availability, impressive performance and extremely light nature. This was my another strong motivation for developing this plugin .
The CSScriptNpp is one of two CS-Script plugins for Notepad++. The second one is NppScripts - the plugin for automating Notepad++ with scripts written in C#. Its development is completed (it will be published in a few weeks) but it is a subject for another article.
Plugin Architecture Highlights
Hosting
The conventional Notepad++ plugin development requires coding in C++. While I am fully comfortable with C++, I do prefer C#. Thus, I used NppPlugin.NET as a plugin container. This container is a rather elegant approach to the Interop challenges. It is nothing else but a special VS project template with a very clever post-build action, which injects special native exporting symbols. Thus it makes possible for unmanaged host applications like Notepad++ to host an ordinary assembly directly, without any need for wrappers. The project template also provides an interface (structs, constants etc.) for the all Notepad++ functionality normally available within any other plugins.
Intellisense All code analysis features (e.g. Intellisense) are based on ICSharpCode.NRefactory (part of SharpDevelop IDE). Arguably it is the most mature Open-Source solution available today for parsing C# code and building SynatxTree. It even has code auto-completion already implemented out of the box. However the reality is a bit more complicated. NRefactory has very little (if any) documentation. Its author(s) provided a comprehensive set of code samples, which seems to be even a better option. However all samples are too SharpDevelop specific and do not help with using the library outside of the SharpDevelop UI controls suite. It took me some time and effort, to reverse engineer the API but eventually I managed to integrate NRefactory with the Notepad++ plugin.
This, however wasn't the only challenge. When I made my first attempt for the C# editor (about 4 years ago) I wrapped ICSharpCode.NRefactory.dll into a more manageable generic adapter. In 2013 I tried to reuse that wrapper and to my disappointment I released that I had to start it all over again because the NRefactory API had changed completely. Now it is called NRefactory5.
Today NRefactory5 still has some features not finalized but it suits CSScriptNpp purpose quite well. Thus, it was possible to implement auto-completion (like MS Intellisense), "Go To Definition", "Find All References", "Add Missing Usings" and all cases of "Show Member Info".
CS-Script integration
The script execution and dependencies management is implemented with CS-Script execution engine. You can find the full details about this in my CodeProject article "C# Script: The Missing Puzzle Piece". But to put it simple, CS-Script allows execution of the plain-vanilla (ECMA-compliant) C# code without building the executable or maintaining any infrastructure (like VS project). With CS-Script it is also possible to express dependencies (referenced assemblies and other scripts) directly from the script code. Note that you do not need to have CS-Script installed as the plugin contains the minimal set of CS-Script assemblies required for the script execution.
Developers who are already familiar with CS-Script and particularly with the Visual Studio extension "CS-Script tools for VS2012-13" will most likely find themselves comfortable with CSScriptNpp as well. The user experience is very similar. In fact CSScriptNpp allows loading the script in Visual Studio with just a single click of the toolbar button. This can be useful when your script needs the full power of Visual Studio (e.g. integrated debugger):
Usage Many plugin features are straight forward and self explanatory. You can find all details about how to use any
specific feature here. But the simplest use-case is as follows:
Execution/Tracing/Debugging When executing the script you have an option to do this as a completely separate external process (Shift+F5) or you can allow CSScriptNpp to listen to the Debut Output messages (F5). This interesting feature is based on the already existing DbMon.Net solution and all credit should go to its creator Christian Birkl. DbMon.Net intercepts all Debug.Write* and Trace.Write* messages and prints them in the Output Panel. You can also use this feature to listen to all Debug messages on the system (similar to SysInternals Dbgview.exe).
CSScriptNpp also allows intercepting Console Output messages.
Though if your console also expects some user input then intercepting StdOut is not very practical as it hides the actual console window.
A part from capturing the debug output later versions of plugin feature the integration with MDbg (by Microsoft).
Code Navigation The code navigation functionality consist of the following features:
-
Advanced text search functionality provided by Notepad++.
-
Conventional "Find All References" and "Go To Definition", which work very similar to the same functionality in Visual Studio. This also includes reconstruction (decompilation) of the referenced assembly interface with its XML documentation.
-
CodeMap - current document class members list. No equivalent feature is available in
Visual Studio and it can only be achieved via VS extensions.
This feature is particularly useful when working with the large size source code, e.g. with reconstructed (decompiled) referenced assemblies:
Formatting
Code auto-formatting is an essential part of any modern IDE. And I considered it as a must-have feature for CSScriptNpp.
I had to make a difficult decision to implement my own solution instead of using ICSharpCode.NRefactory built-in code formatting. Unfortunately NRefactory formatting is not fully completed (at least in the version I used) and is not flexible enough to handle non-standard syntax (e.g. classless C# scripts). Thus I develop a light generic code parser.
It is not a real full scale C# parser either by its reliability or by its functionality (or by the elegancy for that matter). But it does the work. This simple brackets matching engine allows achieving the plugin functionality otherwise impossible without fixing/extending ICSharpCode.NRefactory.
Limitations
It is important to be aware of the plugin limitations. The most obvious one is that the quality of Intellissense is as good as its engine (ICSharpCode.NRefactory). I have identified quite a few NRefactory limitations in the areas relevant to CSScriptNpp. While awaiting NRefactory updates with improvements I have implemented some work around. However, the latest NRefactory update did not bring any improvement but instead broke what already worked. So I had to ignore the update. Unfortunately NRefactory is in the permanent "move" and its API is quite "fluid", thus, I do not plan migrating to the new version of NRefactory unless there is a very strong case for it.
CSScriptNpp is intended for the small to medium scale of scripted applications. It is unlikely it can be as effective for the large products, particularly those based on the heterogeneous source content (Resources, XAML, T4 etc.).
The initial positions of the docked windows in the first release is not handled accurately. This results in the CSScriptNpp panels needing to be docked manually on the first ever loading. After that their proper docking will be persisted and maintained by Notepad++ correctly.
Points of Interest
As part of the plugin development I also completed a few micro-projects with their own distinctive functionality:
Reflector - assembly decompiling solution that reconstructs the assembly interface as C# code. Visual Studio does this as well but I think that CSScriptNpp does this slightly better.
Code Formatting - despite not being ideal it possesses a good balance of functionality and practicality.
Debug Monitor - inexpensive but very functional and reusable solution.
Keyboard Interception - simple reusable solution for the application-wide key stroke monitoring. It allowed Notepad++ and CSScriptNpp overlapping shortcuts (e.g. F5) to be handled without any collisions.
Auto Updates - The Notepad++ Plugin Manager is a fantastic vehicle for delivering a product to the user. However its release schedule (every one or two month) does not favor a responsive product support. Thus, I have develop checking and applying updates from the AboutBox. Updates are sourced from the CS-Script server and implemented as MSI. Interestingly enough, the MSI updates authored with Wix# (my yet another pat project).
Classless scripts - CS-Script always supported classless scripts - free standing C# code without the class definition. But no editing tools (including VS) can fully support such a code. And only Notepad++ offers the same level of support for both normal and classless C#. In fact when I started using CSScriptNpp the classless format became my default choice.
Note classless format allows optional substitution of 'Main' with 'main':
using System;
void main(string[] args)
{
Console.WriteLine("Hello World!");
}
Conclusion
There were a few reasons for CSScriptNpp development. I wanted Notepad++ to be equipped with the first class auto-completion (equal to MS Intellisense). I wanted CS-Script to have a first class dedicated editor capable of utilizing the script engine potential.
But I also had another less obvious but very strong motivation...
We are all caught in the complexity of our day job challenges. Frameworks, factories, patterns, design principles... We almost forgot that programming is fun. I have seen so many CodeProject solutions for batch renaming utilities, downloaders, directory synchronizers or yet another MP3 file tags organizer. This sort of utilities often attracts full scale application development (and even deployment solutions) though in many cases the same result can be achieved with just a few lines of high level language code (see Samples section). And the CSScriptNpp plugin allows executing such a code within Notepad++. It also allows preparing the scripts for running them on PC where Notepad++ is not even installed.
I think Notepad++ with CS-Script give us a good opportunity to jump behind our desks and just code. Code without worrying about any code infrastructure. Code without thinking about where to save the project file or where to find the executable you just compiled. Code just for the sheer fun of it.
Samples
The samples below are not to describe C# scripting as such. They are just to give you an idea about what the type of C# code can now be executed directly from Notepad++:
This tiny script allows embedding the track and name tags of the MP3 file into the file name for all files in the current directory:
using System.IO;
using System;
void main()
{
foreach(string file in Directory.GetFiles(".", "*.mp3"))
{
var mp3 = TagLib.File.Create(file);
string fileName = string.Format("{00}.{1}.mp3", mp3.Tag.Track, mp3.Tag.Title);
string dir = Path.GetDirectoryName(file);
File.Move(file, Path.Combine(dir, fileName));
}
Console.WriteLine("Done...");
}
This script demonstrates the use of Async feature of .NET 4.5:
using System.Threading;
using System.Threading.Tasks;
using System;
void main(string[] args)
{
Execute();
Console.ReadLine();
}
async void Execute()
{
await Task.Run(()=>
{
Thread.Sleep(1000);
Console.WriteLine("Continue");
});
Console.WriteLine("Done");
}