Introduction
The .NET Framework provides a mind numbing array of classes to do most anything in windows. I spend a lot of time delving deep into those classes, learning their usage and nuances of them. Unfortunately, I have to do this at home because at work we use Object Pascal and PL/SQL. I use a simple text editor to write my C# code and even though it provides for my basic needs, sometimes it can be a bit lacking when dealing with larger source files.
So, I put my late night C# sessions to work for me and wrote a text editor extender application that allows me to navigate around large text files at break neck speeds. The application will allow you bring in a source file and if the plugin is available for your programming language, quickly bring up methods (or any piece of info the plugin provides) and navigate to them in your text editor. Overall, the application is pretty simple, but it provides a ton of utility.
While putting the application together, I had the opportunity to write in some interesting concepts.
- Dynamic assembly loading using
Assembly
and Activator
. The plugin mechanism operates on this.
- Abstract Classes.
- A text box that does not "ding" when hitting enter. (see the referenced newsgroup in the source to see where I got the idea originally)
- Starting external processes.
- Use of the
ConfigurationSettings
Class.
Background
For some time now I have been using the amazing and highly configurable text editor SciTE (thanks Neil Hodgson) to write my C# code (I am to poor (or cheap) to buy Visual Studio and obviously my company only has moderate interest). Because I got tired of switching between multiple IDEs, I recently started to write the majority of my Pascal and PL/SQL code in SciTE as well, but found that the standard search mechanism just wasn't doing it for some of our larger source files.
Enter GExperts. Programming in the Object Pascal, the IDE has a great set of plugins called GExperts. In paticular there is one called Procedure List. It takes the pascal source and presents you with a list of procedure calls that can be narrowed down by typing in the search box. Over time programming in the IDE, I came to rely on this plugin quite a bit for navigation. Unfortunately my text editor, and probably yours, does not provide this extremly useful functionality.
So with C# and a bit of forethought, I hacked out MethodFinder. With a couple tweaks, you can use it to enhance any text editor that provides commandline support for launching external tools and has the ability to specify files and line numbers when starting a file from the commandline.
Using MethodFinder and its code
To use MethodFinder, download the zip file and double click the run command file. It will by default load the source file passed in. Because using the application is pretty simple, below describes some of the concepts in the source files (hopefully it peak interest in those who don't write code in a plain editor)
No dinging text box
A text box that doesn't ding when you hit enter in it. I found the basic code on google and modified it to suit my needs. I trap the enter and escape keys and then use a delegate to notify the main form when those keys are pressed. Always call the base.PreProcessMessage method to process messages you are not going to handle. Strange things will happen if you forget.
public delegate void EnterNoDing(Keys keyCode);
public class NoDingTextBox : System.Windows.Forms.TextBox {
const int WM_KEYDOWN=0x100;
const int WM_KEYUP=0x101;
public EnterNoDing NoDing;
public override bool PreProcessMessage(<BR> ref System.Windows.Forms.Message msg) {
Keys keyCode=(Keys)(int)msg.WParam & Keys.KeyCode;
if((msg.Msg==WM_KEYDOWN || msg.Msg==WM_KEYUP) &&<BR> (keyCode==Keys.Enter || keyCode==Keys.Escape)) {
msg.WParam=(System.IntPtr)0;
NoDing(keyCode);
return true;
}
return base.PreProcessMessage(ref msg);
}
}
Dynamic assembly loading
The snippet below loads up a plugin and checks its SupportedExtensions property to see if it is the correct plugin. It loads the assembly using the static Assembly.LoadFrom and creates an real instance using the static Activator.CreateInstance. If the plugin supports our files extension, it is returned and used to parse the code.
Assembly ass = Assembly.LoadFrom(file);
Type[] types = ass.GetTypes();
foreach(Type t in types){
Object o = Activator.CreateInstance(t);
if (o is MethodFinder){
MethodFinder mf = (MethodFinder)o;
if (mf.SupportedExtensions().ToLower().IndexOf(extension.ToLower()) >= 0){
mf.CodeText = codeText;
return mf;
}
}
}
Start the editor
This simple snippet starts the editor defined in the configuration file and starts it with the parameters defined. Check out the configuration file to see how the string is formatted.
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = Editor;
line = (Convert.ToInt32(line) + LineOffset).ToString();
psi.Arguments = string.Format(Arguments, FileName, line, text);
Console.WriteLine(psi.FileName + " " + psi.Arguments);
Process.Start (psi);
Writing a simple plugin.
While you can make it as complicated as you want, it is generally pretty simple to write a plugin that parses source for the MethodFinder (for example, I just care about statements with access modifiers in the C# plugin). All you need to do is derive from the abstract base
MethodFinder
,
public class CSharpMethodFinder : MethodFinder{
override the SupportedExtensions property with the file extensions your source typically uses,
public override string SupportedExtensions(){
return "cs";
}
and provide an implementation for the overriden CodeText method.
public override string CodeText{
set {
string[] r = value.Split('\n');
string line;
bool bInComment = false;
for (int i = 0; i < r.Length; i++){
line = r[i].Trim(' ', '\t','\r', '{', '}').ToLower();
if (line.StartsWith("//") || line.StartsWith("/*") || <BR> line.StartsWith("///")){
bInComment = true;
}
if (line.IndexOf("*/") >= 0){
bInComment = false;
}
if ((!bInComment) && (line.StartsWith("public")
|| line.StartsWith("private")
|| line.StartsWith("internal")
|| line.StartsWith("protected")
)
){
MethodFinderMatches.Add(<BR> new MethodFinderMatchEventArgs((i + 1).ToString(), line));
}
if (bInComment && (line.StartsWith("//") || line.StartsWith("///")) ){
bInComment = false;
}
}
}
}
Points of Interest
I had fun writing this and it is not an original idea, but hopefully somebody out there will find it useful enough to write more (and more advanced) plugins. If anyone has any other ideas or feature request, just let me know. If anyone writes a plugin, just send it to me and I will include it, with credit of course, in the application's zip.
Credits
If you are interested in using SciTE, it is available at http://www.scintilla.org. If you still have to slave away in Object Pascal's IDE and don't know about GExperts, checkout http://www.gexperts.org.
History
Posted 2/24/2003 - initial version.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.