Peter - 程序员的扩展文本编辑器
一个强大的文本编辑器,支持插件、代码解析、项目等功能。
引言
在我寻找完美的文本编辑器过程中,我发现了许多潜在的可能,但没有一个完全达到我的要求。有些文本编辑器可以这样做,但不能那样做,等等……因此,Peter 被构思出来,希望能够消除其他文本编辑器中发现的一些弊端。
我希望 Peter 具备的一些主要功能是:
- 带语法高亮的代码编辑器
- 卓越的停靠控件
- 插件接口
- 代码分析
- 以及其他许多……
代码解释
编辑器
在考虑实际的代码编辑器时,我考虑了许多不同的解决方案:
- 自己创建一个——这可能对我的项目范围来说太漫长且困难。
- 使用
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
隐藏或关闭(销毁)DockContent
或Form
。
要添加 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 中有三个插件支持接口:IPeterPlugin
、IPeterPluginHost
和 IPeterPluginTab
。插件在 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
开始。它可以是 DockWindow
或 UserDefined
。如果 PeterPluginType
是 DockWindow
,它将在 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
DockContent
。ctrlInternetBrowser
实现 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 ";"
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 日:更新下载链接。