Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Peter - Programmers Extensive Text Editor

0.00/5 (No votes)
8 Feb 2012 8  
A robust text editor that supports plug-ins, code parsing, projects, and more.

Introduction

During my quest for that perfect text editor, I found many potential possibilities, but none that quite hit the mark. Some text editors would do this, but not that, and so on... Thus, Peter was conceived, in hope to eliminate some of the downfalls found in other text editors.

Some of the main items that I wanted in Peter are:

  1. Code editor with highlighting
  2. Superior docking control
  3. Plug-in interface
  4. Code analysis
  5. And many others...

Code Explained

Editor

When thinking about the actual code editor, I thought of many different solutions:

  • Create my own - which would probably be too long and difficult for my project scope.
  • Use the RichTextBox - this was my first attempt, but the RichTextBox could just not handle what I needed it to.
  • Use ScintillaNET (located here) - this was a very good possibility, but I noticed that the project had not been updated since 2004, and I wanted a more recent solution.

So, I decided to use the ICSharpCode.TextEditor found in the #Develop project located here. Peter uses the ICSharpCode.TextEditor from version 2.0, because version 3.0 has some errors that I did not have time to track down.

To use the editor in your project, download the source code for #Develop (I know it's a big download for some of us =P), extract the files to a folder, then go to src\Libraries\ICSharpCode.TextEditor, and open the solution file found there. Build the solution, then go back to the folder you extracted the files to, and you will find the DLL in the bin folder (same directory as the src folder). You can add this DLL via the Toolbox in Design mode, or add it as a Reference in the Solution Explorer.

When using the ICSharpCode.TextEditor, a good thing to remember is that the TextEditor is a house for several different underlying controls. So, if you are looking for a particular property/method, chances are pretty good that you will have to do a little digging before you find it. Some of the important properties/methods are explained below:

// We need this for the editor...
using ICSharpCode.TextEditor;

namespace Editor101
{
    public class MyEditor
    {
        private TextEditorControl m_Editor;
        
        #region -= Constructor =-
        
        public MyEditor(string pathToFile) : UserControl
        {
            // Create and add the Editor...
            this.m_Editor = new TextEditorControl();
            this.m_Editor.Dock = System.Windows.Forms.DockStyle.Fill;
            this.Controls.Add(this.m_Editor);
            
            // Setup basic configuration...
            this.SetupEditor();
            
            // Load the given file...
            // Arguments are:
            // 1 - Path to file
            // 2 - Auto Load Highlighting
            // 3 - Auto Dected Encoding
            this.m_Editor.LoadFile(pathToFile, true, true);
            
            // If you want drag and drop support for the editor 
            // (these methods have not been implemented in this code)...
            this.m_Editor.ActiveTextAreaControl.TextArea.AllowDrop = true;
            this.m_Editor.ActiveTextAreaControl.TextArea.DragEnter += 
              new System.Windows.Forms.DragEventHandler(TextArea_DragEnter);
            this.m_Editor.ActiveTextAreaControl.TextArea.DragDrop += 
              new System.Windows.Forms.DragEventHandler(TextArea_DragDrop);
            
            // Caret Change notifications
            // (these methods have not been implemented in this code)...
            this.m_Editor.ActiveTextAreaControl.Caret.PositionChanged += 
              new EventHandler(Caret_Change);
            this.m_Editor.ActiveTextAreaControl.Caret.CaretModeChanged += 
              new EventHandler(Caret_CaretModeChanged);
            
            // Document Change notification...
            this.m_Editor.Document.DocumentChanged += 
              new DocumentEventHandler(Document_DocumentChanged);
            
            // I had to implement these methods, because in version 2 (I don't 
            // know about version 3) the editor would not redraw itself after an
            // undo or redo...
            this.m_Editor.Document.UndoStack.ActionRedone += 
              new EventHandler(UndoStack_ActionRedone);
            this.m_Editor.Document.UndoStack.ActionUndone += 
              new EventHandler(UndoStack_ActionRedone);
        }
        
        #endregion
        
        #region -= Set up the Editor =-
        
        private void SetupEditor()
        {
            // Setup the Highlighting for the editor (if we did not auto detect)...
            // This will look in a folder for all available highlighting files
            string path = SCHEME_FOLDER;
            HighlightingManager.Manager.AddSyntaxModeFileProvider(
                                new FileSyntaxModeProvider(path));
            // Now we can set the Highlighting scheme...
            this.m_Editor.Document.HighlightingStrategy = 
              HighlightingManager.Manager.FindHighlighter("HTML");
            
            // Show or Hide the End of Line Markers...
            this.m_Editor.ShowEOLMarkers = false;
            
            // Show or Hide Invalid Line Markers...
            this.m_Editor.ShowInvalidLines = false;
            
            // Show or Hide a little dot where spaces are...
            this.m_Editor.ShowSpaces = false;
            
            // Show or Hide ">>" where tabs are...
            this.m_Editor.ShowTabs = true;
            
            // Highlight the matching bracket or not...
            this.m_Editor.ShowMatchingBracket = true;
            // When should we highlight the matching bracket...
            switch (BracketMatchingStyle.ToLower())
            {
                case "before":
                    this.m_Editor.BracketMatchingStyle = 
                                      BracketMatchingStyle.Before;
                    break;
                case "after":
                    this.m_Editor.BracketMatchingStyle = 
                                       BracketMatchingStyle.After;
                    break;
            }
            
            // Show or Hide Line Numbers...
            this.m_Editor.ShowLineNumbers = true;
            
            // Show or Hide a Ruler at the top of the editor...
            this.m_Editor.ShowHRuler = false;
            
            // Show or Hide the vertical line in the text editor...
            this.m_Editor.ShowVRuler = true;
            
            // Enable Code Folding, if enabled, you must set the folding strategy
            this.m_Editor.EnableFolding = false;
            // The Folding Strategy is a class using the IFoldingStrategy Interface
            // You must create your own folding strategies...
            // this.m_Editor.Document.FoldingManager.FoldingStrategy = 
            //                            new CustomFoldingStrategy();
            
            // Editor's font...
            this.m_Editor.Font = this.Font;
            
            // If you want to convert tabs to spaces or not...
            this.m_Editor.ConvertTabsToSpaces = false;
            
            // How many spaces should make up a tab...
            this.m_Editor.TabIndent = 4;
            
            // What column to place the vertical ruler at...
            this.m_Editor.VRulerRow = 80;
            
            // Allow the caret "|" beyong the end of the line or not...
            this.m_Editor.AllowCaretBeyondEOL = false;
            
            // Automatically instert a curly bracket when one is typed or not...
            this.m_Editor.TextEditorProperties.AutoInsertCurlyBracket = false;
            
            // Highlight the current line, or not..
            this.m_Editor.LineViewerStyle = (HighlightCurrentLine) ? 
                           LineViewerStyle.FullRow : LineViewerStyle.None;
            
            // Using Anti-Alias Font or not...
            this.m_Editor.UseAntiAliasFont = true; // #develop 2
            /* // #develop 3
            if (UseAntiAlias)
            {
                this.m_Editor.TextRenderingHint = 
                   System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
            }*/
            
            // Set the Automatic indentation...
            switch (IndentStyle.ToLower())
            {
                case "auto":
                    this.m_Editor.IndentStyle = IndentStyle.Auto;
                    break;
                case "none":
                    this.m_Editor.IndentStyle = IndentStyle.None;
                    break;
                case "smart":
                    this.m_Editor.IndentStyle = IndentStyle.Smart;
                    break;
            }
        }
        
        #endregion
        
        #region -= Misc =-
        
        /// <summary>
        /// Invalidate after Undo or Redo...
        /// </summary>
        private void UndoStack_ActionRedone(object sender, EventArgs e)
        {
            this.m_Editor.ActiveTextAreaControl.TextArea.Invalidate();
        }
        
        /// <summary>
        /// Save the Document to a File...
        /// </summary>
        /// <param name="filePath">Path to File.</param>
        public void Save(string filePath)
        {
            this.m_Editor.SaveFile(filePath);
        }
        
        /// <summary>
        /// Print the Document...
        /// </summary>
        public void Print()
        {
            PrintPreviewDialog dlg = new PrintPreviewDialog();
            dlg.Document = this.m_Editor.PrintDocument;
            dlg.ShowDialog();
        }
        
        /// <summary>
        /// This will show you how to get the selected text, and insert text...
        /// </summary>
        public void Duplicate()
        {
            if (this.m_Editor.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
            {
                string selection = this.m_Editor.ActiveTextAreaControl.
                                                   SelectionManager.SelectedText;
                int pos = this.m_Editor.ActiveTextAreaControl.
                               SelectionManager.SelectionCollection[0].EndOffset;
                this.m_Editor.Document.Insert(pos, selection);

                this.m_Editor.ActiveTextAreaControl.TextArea.Invalidate();
            }
        }

        /// <summary>
        /// Scroll to a particular offset in the document...
        /// </summary>
        public void ScrollTo(int offset)
        {
            if (offset > this.m_Editor.Document.TextLength)
            {
                return;
            }
            int line = this.m_Editor.Document.GetLineNumberForOffset(offset);
            this.m_Editor.ActiveTextAreaControl.Caret.Position = 
                        this.m_Editor.Document.OffsetToPosition(offset);
            this.m_Editor.ActiveTextAreaControl.ScrollTo(line);//.CenterViewOn(line, 0);
        }
        
        #endregion
    }
}

For more information about how to use the TextEditor, you can always go to the #Develop forums.

Docking

To accomplish Docking, I first created a tab control that allowed tabbed groups. My tab control was not as sophisticated as I wanted, but a good tab control was found in another article. As I was doing some research for the control, I stumbled across Weifen Luo's DockPanel Suite. This dock suite was exactly what I was looking for. The project can be found here.

To use the dock suite, you need to first set your main form as a MDI Parent. Next, in the Toolbox for the form designer, right click, and select Choose Items... In the .NET Framework Components tab, click Browse, and find the dock suite DLL that you downloaded. Select it and press OK. Now, back in the Toolbox, you should find a DockPanel component, add this to your form (it has to be a control of the form, not a panel), and the rest is in the code.

Once the DockPanel has been added to your form, you need to create content (tabs) for it, called DockContent. To do this, create a new class, call it whatever you want, and then extend it with the DockContent object.

// We need this for the docking...
using WeifenLuo.WinFormsUI.Docking;

namespace Docking101
{
    public class MyDockContentTab : DockContent
    {
    }
}

Once you extend the DockContent object, your class will be converted to a form, and you can add/remove items via code, or in Design mode. Some of the important properties of the DockContent are explained below:

  • ToolTipText - This is the text that will be displayed when you hover your mouse over the tab.
  • TabText - This is the text that will be displayed on the tab.
  • TabPageContextMenu/TabPageContextMenuStrip - This is the ContextMenu that will be displayed when someone right clicks on the tab of the DockContent.
  • DockAreas - This determines where the tab can be docked.
  • HideOnClose - When the user closes the tab via the "X" button, this will make the DockPanel either Hide or Close (Dispose) the DockContent or Form.

To add the DockContent, you can follow the code below:

MyDockContentTab tabToAdd = new MyDockContentTab();

// Add Content to Main Panel in Default Way...
tabToAdd.Show(this.DockPanel);

// Add Contetn to Main Panel in a certain State...
DockState state = DockState.DockLeftAutoHide;
tabToAdd.Show(this.DockPanel, state);

// Add Content to a Floating Window...
tabToAdd.Show(this.DockPanel, new Rectangle(left, top, width, height));

// Add Content to a particular group...
DockPane pane = anotherTab.Pane;
tabToAdd.Show(pane, anotherTab);

This short intro to the DockPanel should get you up and running.

Plug-in Interface

To use plug-ins in your application, you basically need to think of every thing someone would want to do via a plug-in, and put it into an interface. This can be hard at times, so I would like to say that the Plug-in Interface for Peter is still a work in progress.

There are three interfaces for plug-in support in Peter: IPeterPlugin, IPeterPluginHost, and IPeterPluginTab. Plug-ins are loaded out of the Plugins folder when Peter is starting up, so no Plug-in manager is needed.

/// <summary>
/// Loads the Plugins in the Plugin Directory...
/// </summary>
private void LoadPlugins()
{
    string[] files = Directory.GetFiles(PLUGIN_FOLDER, "*.dll");
    foreach (string file in files)
    {
        this.LoadPlugin(file);
    }
}

/// <summary>
/// Loads a Plugin...
/// </summary>
/// <param name="pluginPath">Full Path to Plugin</param>
/// <returns>True if Plugin Loaded, otherwise false</returns>
public bool LoadPlugin(string pluginPath)
{
    Assembly asm;

    if (!File.Exists(pluginPath))
    {
        return false;
    }

    asm = Assembly.LoadFile(pluginPath);
    if (asm != null)
    {
        foreach (Type type in asm.GetTypes())
        {
            if (type.IsAbstract)
                continue;
            object[] attrs = type.GetCustomAttributes(typeof(PeterPluginAttribute), true);
            if (attrs.Length > 0)
            {
                IPeterPlugin plugin = Activator.CreateInstance(type) as IPeterPlugin;
                plugin.Host = this;
                if (plugin.HasMenu)
                {
                    this.mnuPlugins.DropDownItems.Add(plugin.GetMenu());
                }

                if (plugin.HasTabMenu)
                {
                    this.ctxTab.Items.Add(new ToolStripSeparator());
                    foreach (ToolStripMenuItem tsmi in plugin.GetTabMenu())
                    {
                        this.ctxTab.Items.Add(tsmi);
                    }
                }

                if (plugin.HasContextMenu)
                {
                    this.ctxEditor.Items.Add(new ToolStripSeparator());
                    foreach (ToolStripMenuItem tsmi in plugin.GetContextMenu())
                    {
                        this.ctxEditor.Items.Add(tsmi);
                    }
                }
                this.m_Plugins.Add(plugin);
                plugin.Start();
            }
        }

        return true;
    }
    else
    {
        return false;
    }
}

We first need to start off with the PeterPluginType. This can be DockWindow or UserDefined. If PeterPluginType is a DockWindow, it will be displayed as a tab in Peter; otherwise, the user can control how the plug-in will be displayed, if needed at all. To specify the PeterPluginType, you need to add the PeterPlugin attribute to your class (see below) and specify which PeterPluginType you are using. If this is not done, Peter will not recognize your plug-in.

The first interface is the IPeterPlugin. This is the interface that your actual plug-in will need to use in order to work with Peter. For a plug-in example, we will use the InternetBrowser plug-in (located at Sourceforge).

using PeterInterface;

namespace InternetBrowser
{
    // This is needed, for peter to determine
    // if we can support the plugin or not...
    [PeterPlugin(PeterPluginType.DockWindow)]
    public class InternetBrowser : IPeterPlugin
    // This is a Peter Plugin...
    {
        // Link to the Host Application (PETER)...
        private IPeterPluginHost m_Host;
        
        // Active Tab in PETER...
        private IDockContent m_ActiveTab;

        public InternetBrowser()
        {
            this.m_ActiveTab = null;
        }
        
        #region -= IPeterPlugin Members =-
        ...
        #endregion
    }
}

Here are the IPeterPlugin properties/methods:

/// <summary>
/// Interface for a Peter Plugin...
/// </summary>
public interface IPeterPlugin
{
    /// <summary>
    /// This method is called when the plugin is loaded...
    /// </summary>
    void Start();

    /// <summary>
    /// This method is called when Peter Closes...
    /// </summary>
    void Close();
    
    /// <summary>
    /// This is the Name of the plugin...
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Gets if the Plugin can load files or not...
    /// </summary>
    bool AbleToLoadFiles { get; }

    /// <summary>
    /// Loads the Given File...
    /// </summary>
    /// <param name="filePath">Path to file to load.</param>
    /// <returns />True if Loaded, otherwise false</returns />
    bool LoadFile(string filePath);

    /// <summary>
    /// Does this plugin have a menu item for the plugin menu...
    /// </summary>
    bool HasMenu { get; }

    /// <summary>
    /// Does this plugin have a menu item for the tab menu...
    /// </summary>
    bool HasTabMenu { get; }

    /// <summary>
    /// Does this plugin have a menu item for the context menu...
    /// </summary>
    bool HasContextMenu { get; }

    /// <summary>
    /// The Author of the Plugin...
    /// </summary>
    string Author { get; }

    /// <summary>
    /// The Version of the Plugin...
    /// </summary>
    string Version { get; }

    /// <summary>
    /// The Image for the Plugin...
    /// </summary>
    Image PluginImage { get; }

    /// <summary>
    /// Gets the Plugin Menu Item...
    /// </summary>
    /// <returns />Tool Strip Menu Item</returns />
    ToolStripMenuItem GetMenu();

    /// <summary>
    /// Gets the Tab Menu Items...
    /// </summary>
    /// <returns />Tool Strip Menu Item Array</returns />
    ToolStripMenuItem[] GetTabMenu();

    /// <summary>
    /// Gets the Context Menu Items...
    /// </summary>
    /// <returns />Tool Strip Menu Item Array</returns />
    ToolStripMenuItem[] GetContextMenu();

    /// <summary>
    /// Gets the Type of the Plugin...
    /// </summary>
    PeterPluginType Type { get; }

    /// <summary>
    /// Gets or Sets the Host Appliction Interface...
    /// </summary>
    IPeterPluginHost Host { get; set; }

    /// <summary>
    /// Occurs when the Active Content has changed...
    /// </summary>
    /// <param name="tab">Active Content</param>
    void ActiveContentChanged(IDockContent tab);

    /// <summary>
    /// Checks the content string when loading Application...
    /// </summary>
    /// <param name="contentString">Content String</param>
    /// <returns />True if Match, other wise false</returns />
    bool CheckContentString(string contentString);

    /// <summary>
    /// Gets the Starting Content...
    /// </summary>
    /// <param name="contentString">Content String</param>
    /// <returns />The Newly created content</returns />
    IDockContent GetContent(string contentString);

    /// <summary>
    /// Gets the Option Panel to add to the Main Option Dialog...
    /// </summary>
    /// <returns />Option Panel Control.</returns />
    Control OptionPanel { get; }

    /// <summary>
    /// Occurs when the Apply button on the options dialog is pressed...
    /// </summary>
    void ApplyOptions();
}

The IPeterPluginHost is the interface that is used for your plug-in to talk with Peter. I kept the interface between the plug-in and Peter very short and sweet so as to minimize on potential threats. The host is set in the IPeterPlugin interface above. Its properties/methods are shown below:

/// <summary>
/// Interface for the Host Peter Application...
/// </summary>
public interface IPeterPluginHost
{
    /// <summary>
    /// Gets the Type for a Editor in string format (typeof(Editor))...
    /// </summary>
    string EditorType { get; }

    /// <summary>
    /// Gets the path the Application started in...
    /// </summary>
    string ApplicationExeStartPath { get; }

    /// <summary>
    /// Creates a new blank editor...
    /// </summary>
    void NewDocument();

    /// <summary>
    /// Writes the given text in the status bar...
    /// </summary>
    /// <param name="text">Text to Write.</param>
    void Trace(string text);

    /// <summary>
    /// Saves the Given Content As...
    /// </summary>
    /// <param name="tab">Content to Save</param>
    void SaveAs(IPeterPluginTab tab);

    /// <summary>
    /// Creates a new Editor with the given file...
    /// </summary>
    /// <param name="fileName">File to load in Editor.</param>
    /// <param name="tabName">Name of Tab.</param>
    void CreateEditor(string path, string tabName);

    /// <summary>
    /// Creates a new Editor with the given file...
    /// </summary>
    /// <param name="fileName">File to load in Editor.</param>
    /// <param name="tabName">Name of Tab.</param>
    /// <param name="image">Icon for Tab.</param>
    void CreateEditor(string path, string tabName, Icon image);

    /// <summary>
    /// Creates a new Editor with the given file...
    /// </summary>
    /// <param name="fileName">File to load in Editor.</param>
    /// <param name="tabName">Name of Tab.</param>
    /// <param name="image">Icon for Tab.</param>
    /// <param name="addToContent">Content to add to group.</param>
    void CreateEditor(string path, string tabName, Icon image, IDockContent addToContent);

    /// <summary>
    /// Gets the Shell Icon for the given file...
    /// </summary>
    /// <param name="filePath">Path to File.</param>
    /// <param name="linkOverlay">Link Overlay or not.</param>
    /// <returns />Shell Icon for File.</returns />
    Icon GetFileIcon(string path, bool linkOverlay);

    /// <summary>
    /// Adds the given Dock Content to the form...
    /// </summary>
    /// <param name="content">Content to Add.</param>
    void AddDockContent(DockContent content);

    /// <summary>
    /// Adds the given Dock Content to the form...
    /// </summary>
    /// <param name="content">Content to Add.</param>
    /// <param name="state">State of Content</param>
    void AddDockContent(DockContent content, DockState state);

    /// <summary>
    /// Adds the given Dock Content to the form...
    /// </summary>
    /// <param name="content">Content to Add.</param>
    /// <param name="floatingRec">Floating Rectangle</param>
    void AddDockContent(DockContent content, Rectangle floatingRec);
}

The last interface is the IPeterPluginTab. This interface is needed if you plan on implementing a tab in Peter. If you open the solution for the InternetBrowser plug-in, you will see that there is a InternetBrowser class and a ctrlInternetBrowser DockContent. The ctrlInternetBrowser implements the IPeterPluginTab interface because it will be the tab that hosts the web browser. You can think of the IPeterPlugin as behind the scene, and IPeterPluginTab as what everyone sees. This way, we can use the menu items like Find, Copy, Save... in your plug-in's tab. Your tab will need to implement the properties/methods below (they are all pretty self-explanatory):

public interface IPeterPluginTab
{
    void Save();
    void SaveAs(string filePath);
    void Cut();
    void Copy();
    void Paste();
    void Undo();
    void Redo();
    void Delete();
    void Print();
    void SelectAll();
    void Duplicate();
    bool CloseTab();
    IPeterPluginHost Host { get; set; }
    string FileName { get; }
    string Selection { get; }
    bool AbleToUndo { get; }
    bool AbleToRedo { get; }
    bool AbleToPaste { get; }
    bool AbleToCut { get; }
    bool AbleToCopy { get; }
    bool AbleToSelectAll { get; }
    bool AbleToSave { get; }
    bool AbleToDelete { get; }
    bool NeedsSaving { get; }
    string TabText { get; set; }
    void MarkAll(Regex reg);
    bool FindNext(Regex reg, bool searchUp);
    void ReplaceNext(Regex reg, string replaceWith, bool searchUp);
    void ReplaceAll(Regex reg, string replaceWith);
    void SelectWord(int line, int offset, int wordLeng);
}

For more info on plug-ins, open the InternetBrowser plug-in and take a look at its code. Suggestions for improving the plug-in interface are welcome.

Code Analysis

One of the last major feats of Peter was the code analysis. This became very tricky, and will never be 100 percent complete. Many options were explored, but I finally decided to use Coco/R. Coco/R is a compiler generator that will create a scanner and parser for you. The only problem is that you have to implement the parser yourself. To create a parser for a certain style of code, you need to create an ATG (attributed grammar - don't quote me on this) file. This file will tell Coco how it needs to create the parser's code. Once you have your ATG file, you give it to Coco/R, and out will pop a scanner and parser written in the version of code you downloaded Coco/R in (C#, C++, Java, etc.). More info on this can be found at the Coco/R website. The code analyzer is actually an internal plug-in. I made it internal so I could have more control over it. So, when the active document is changed (see Plug-in Interface), we send the parser plug-in the document we want to parse. It will then determine the file extension and parse the code accordingly. So far, I have implemented XML, C#, CSS, Java, and .C/.H file parsers.

An important item to note is that when the parser that Coco/R creates parses the file, it does nothing to save the information that was parsed. So, you have to go in and create a way to save the parsed info. I did this by just adding some ArrayLists that will hold the info I need, like an ArrayList to hold the methods. So, how the whole thing works is:

  • Download Coco/R (I downloaded Coco/R for C#, so it will give me C# parsers)
  • Create an ATG file for the code style of your choice
  • /* A small sample of the ATG File for C#... */
    CS2
    =
      {IF (IsExternAliasDirective()) ExternAliasDirective}
      {UsingDirective}
      {IF (IsGlobalAttrTarget()) GlobalAttributes}
      {NamespaceMemberDeclaration}
    .
    
    ExternAliasDirective
    =
      "extern" ident                                        (.
                                                               if (t.val != "alias") {
                                                                 Error("alias expected");
                                                               }
                                                            .)
      ident ";"
    .
    
    UsingDirective
    =
      "using" [ IF (IsAssignment()) ident "=" ]
      TypeName ";"
  • Give the ATG file to Coco/R, it will create two C# files -- scanner.cs and parser.cs
  • coco CSharp.ATG
  • Add these two files to your project
  • Edit the files to save the needed information
  • // This in parser.cs
    // When the using keyword is found in a C# file,
    // this method will be called...
    void UsingDirective()
    {
        Expect(78);
        if (IsAssignment())
        {
            Expect(1);
            Expect(85);
        }
        
        // A TokenMatch is a class I created to save
        // info about a find we are interested in...
        TokenMatch tm = new TokenMatch();
        
        // Set position we found it at...
        tm.Position = la.pos;
        this.m_Current = la.val;
        TypeName();
        
        // Set the value we found...
        tm.Value = this.m_Current;
        
        // Add the Match to the saved info...
        this.m_CodeInfo.Usings.Add(tm);
        Expect(114);
    }
  • Create a scanner and a parser
  • // You could modify this to parse a stream instead of a file...
    Peter.CSParser.Scanner scanner = new Peter.CSParser.Scanner(pathToFile);
    // The parser uses the scanner to find tokens,
    // Tokens are things such as Keywords, comments, and so forth...
    Peter.CSParser.Parser parser = new Peter.CSParser.Parser(scanner);
  • Parse the file
  • parser.Parse();
  • Add the collected information to a tree
  • // Using...
    TreeNode nUsing = new TreeNode("Usings");
    foreach (TokenMatch tm in parser.CodeInfo.Usings)
    {
        TreeNode n = new TreeNode(tm.Value);
        n.Tag = tm.Position;
        nUsing.Nodes.Add(n);
    }
    if (nUsing.Nodes.Count > 0)
    {
        myTree.Nodes.Add(nUsing);
    }

Conclusion

Well, that's all I have for Peter right now. There are still some other features that I did not explain, maybe I will at another date, but I did not think they were too important. These features include: the file explorer, project support, the Find dialog, command prompt, file difference, and folding strategies. If you need help with these, please let me know, and I will publish some info on it.

History

  • 27 May 2008: Article created.
  • 8 Feb 2012: Download links updated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here