65.9K
CodeProject 正在变化。 阅读更多。
Home

Peter - 程序员的扩展文本编辑器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (31投票s)

2008 年 5 月 27 日

GPL3

8分钟阅读

viewsIcon

230221

一个强大的文本编辑器,支持插件、代码解析、项目等功能。

引言

在我寻找完美的文本编辑器过程中,我发现了许多潜在的可能,但没有一个完全达到我的要求。有些文本编辑器可以这样做,但不能那样做,等等……因此,Peter 被构思出来,希望能够消除其他文本编辑器中发现的一些弊端。

我希望 Peter 具备的一些主要功能是:

  1. 带语法高亮的代码编辑器
  2. 卓越的停靠控件
  3. 插件接口
  4. 代码分析
  5. 以及其他许多……

代码解释

编辑器

在考虑实际的代码编辑器时,我考虑了许多不同的解决方案:

  • 自己创建一个——这可能对我的项目范围来说太漫长且困难。
  • 使用 RichTextBox——这是我的第一次尝试,但 RichTextBox 无法满足我的需求。
  • 使用 ScintillaNET(位于 此处)——这是一个很好的可能性,但我注意到该项目自 2004 年以来就没有更新过,而我想要一个更近期的解决方案。

因此,我决定使用 #Develop 项目中的 ICSharpCode.TextEditor,该项目位于 此处。Peter 使用的是 2.0 版的 ICSharpCode.TextEditor,因为 3.0 版存在一些我没有时间去追踪的错误。

要在您的项目中使用该编辑器,请下载 #Develop 的源代码(我知道这对于我们中的一些人来说是个大下载 =P),将文件解压到一个文件夹,然后转到 src\Libraries\ICSharpCode.TextEditor,并打开那里的解决方案文件。构建解决方案,然后回到您解压文件的文件夹,您将在 bin 文件夹(与 src 文件夹相同的目录)中找到 DLL。您可以在设计模式下通过工具箱添加此 DLL,或在解决方案资源管理器中将其添加为引用。

在使用 ICSharpCode.TextEditor 时,需要记住的一点是,TextEditor 是几个不同底层控件的容器。因此,如果您正在寻找某个特定的属性/方法,很可能您需要深入挖掘才能找到它。下面将解释一些重要的属性/方法。

// 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
    }
}

有关如何使用 TextEditor 的更多信息,您可以随时访问 #Develop 论坛

停靠

为了实现停靠,我首先创建了一个允许标签组的标签控件。我的标签控件不如我想要的那么复杂,但在另一篇文章中找到了一个不错的标签控件。在我研究该控件时,我偶然发现了 Weifen Luo 的 DockPanel Suite。这个停靠套件正是我想要的。该项目位于 此处

要使用停靠套件,您需要先将主窗体设置为 MDI 父窗体。接下来,在窗体设计器的工具箱中,右键单击,然后选择“选择项…” (.NET Framework Components 选项卡),点击浏览,然后找到您下载的停靠套件 DLL。选择它并按“确定”。现在,回到工具箱,您应该会找到一个 DockPanel 组件,将其添加到您的窗体(它必须是窗体的控件,而不是面板),其余的就在代码中了。

DockPanel 添加到窗体后,您需要为其创建内容(选项卡),称为 DockContent。要做到这一点,创建一个新类,随意命名,然后将其继承自 DockContent 对象。

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

namespace Docking101
{
    public class MyDockContentTab : DockContent
    {
    }
}

一旦您继承了 DockContent 对象,您的类将被转换为一个窗体,您可以通过代码或设计模式添加/删除项。DockContent 的一些重要属性将在下面解释。

  • ToolTipText - 当您将鼠标悬停在选项卡上时显示的文本。
  • TabText - 显示在选项卡上的文本。
  • TabPageContextMenu/TabPageContextMenuStrip - 当有人右键单击 DockContent 的选项卡时显示的上下文菜单。
  • DockAreas - 决定了选项卡可以停靠的位置。
  • HideOnClose - 当用户通过“X”按钮关闭选项卡时,这将使 DockPanel 隐藏或关闭(销毁)DockContentForm

要添加 DockContent,您可以按照以下代码进行:

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);

这个关于 DockPanel 的简短介绍应该能让您开始使用。

插件接口

要在您的应用程序中使用插件,您基本上需要考虑用户希望通过插件完成的每一件事,并将其放入一个接口中。这有时会很困难,所以我需要说 Peter 的插件接口仍在进行中。

Peter 中有三个插件支持接口:IPeterPluginIPeterPluginHostIPeterPluginTab。插件在 Peter 启动时从 Plugins 文件夹加载,因此不需要插件管理器。

/// <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;
    }
}

我们首先需要从 PeterPluginType 开始。它可以是 DockWindowUserDefined。如果 PeterPluginTypeDockWindow,它将在 Peter 中显示为一个选项卡;否则,用户可以控制插件的显示方式(如果需要的话)。要指定 PeterPluginType,您需要在类(见下文)上添加 PeterPlugin 属性,并指定您使用的 PeterPluginType。如果未完成此操作,Peter 将无法识别您的插件。

第一个接口是 IPeterPlugin。这是您的实际插件需要使用以与 Peter 一起工作的接口。对于插件示例,我们将使用 InternetBrowser 插件(位于 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
    }
}

以下是 IPeterPlugin 的属性/方法:

/// <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();
}

IPeterPluginHost 是用于您的插件与 Peter 通信的接口。我将插件和 Peter 之间的接口保持得很简短,以便最大限度地减少潜在威胁。主机在上面的 IPeterPlugin 接口中设置。其属性/方法如下所示:

/// <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);
}

最后一个接口是 IPeterPluginTab。如果您计划在 Peter 中实现一个选项卡,则需要此接口。如果您打开 InternetBrowser 插件的解决方案,您会看到一个 InternetBrowser 类和一个 ctrlInternetBrowser DockContentctrlInternetBrowser 实现 IPeterPluginTab 接口,因为它将是承载网络浏览器的选项卡。您可以认为 IPeterPlugin 是幕后工作,而 IPeterPluginTab 是所有人看到的内容。这样,我们就可以在插件的选项卡中使用查找、复制、保存……等菜单项。您的选项卡需要实现以下属性/方法(它们都相当直观):

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);
}

有关插件的更多信息,请打开 InternetBrowser 插件并查看其代码。欢迎提出改进插件接口的建议。

代码分析

Peter 的最后几个主要功能之一是代码分析。这变得非常棘手,而且永远不会 100% 完成。探索了许多选项,但我最终决定使用 Coco/R。Coco/R 是一个编译器生成器,可以为您生成扫描器和解析器。唯一的问题是您必须自己实现解析器。要创建特定代码风格的解析器,您需要创建一个 ATG(属性语法——别太较真)文件。此文件将告诉 Coco 如何创建解析器的代码。一旦有了 ATG 文件,您将其提供给 Coco/R,就会生成一个用您下载 Coco/R 的语言版本(C#、C++、Java 等)编写的扫描器和解析器。有关更多信息,请访问 Coco/R 网站。代码分析器实际上是一个内部插件。我将其设为内部插件是为了更好地控制它。因此,当活动文档更改时(参见插件接口),我们将要解析的文档发送给解析器插件。它将确定文件扩展名并相应地解析代码。到目前为止,我已经实现了 XML、C#、CSS、Java 和 .C/.H 文件解析器。

一个需要注意的重要事项是,当 Coco/R 生成的解析器解析文件时,它不会保存已解析的信息。所以,您必须自己创建一个保存已解析信息的方法。我通过添加一些 ArrayList 来保存我需要的信息,例如一个用于保存方法的 ArrayList。所以,整个工作流程是这样的:

  • 下载 Coco/R(我下载了 C# 版的 Coco/R,它将为我生成 C# 解析器)
  • 为您的代码风格创建 ATG 文件
  • /* 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 ";"
  • 将 ATG 文件提供给 Coco/R,它将生成两个 C# 文件——scanner.csparser.cs
  • coco CSharp.ATG
  • 将这两个文件添加到您的项目中
  • 编辑文件以保存所需信息
  • // 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);
    }
  • 创建扫描器和解析器
  • // 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);
  • 解析文件
  • parser.Parse();
  • 将收集到的信息添加到树中
  • // 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);
    }

结论

好了,这就是我目前关于 Peter 的全部内容。还有一些其他我没有解释的功能,也许我以后会解释,但我认为它们不太重要。这些功能包括:文件浏览器、项目支持、查找对话框、命令提示符、文件差异和折叠策略。如果您需要这些方面的帮助,请告诉我,我会发布一些相关信息。

历史

  • 2008 年 5 月 27 日:文章创建。
  • 2012 年 2 月 8 日:更新下载链接。
© . All rights reserved.