C# RichTextBox中的代码折叠
我正在使用C#开发一个从Winforms RichTextBox派生的代码编辑器。我已经实现了自动补全和语法hilighting,但是代码折叠是一种不同的方法。我想要实现的是: 代码如下:C# RichTextBox中的代码折叠,c#,regex,code-folding,C#,Regex,Code Folding,我正在使用C#开发一个从Winforms RichTextBox派生的代码编辑器。我已经实现了自动补全和语法hilighting,但是代码折叠是一种不同的方法。我想要实现的是: 代码如下: public static SomeFunction(EventArgs e) { //Some code //Some code //Some code //Some code //Some code //Some code } 应成为: public st
public static SomeFunction(EventArgs e)
{
//Some code
//Some code
//Some code
//Some code
//Some code
//Some code
}
应成为:
public static SomeFunction(EventArgs e)[...]
其中[…]
是一个缩短的代码,当您将鼠标悬停在[…]
使用正则表达式或过程代码有什么想法或建议吗?我创建了一个解析器,它将返回代码折叠位置的索引
- 折叠分隔符由正则表达式定义
- 您可以指定开始和结束索引,以便在更新一个区域时不必检查整个代码
- 如果代码格式不正确,它将抛出异常,请随意更改该行为。另一种选择是,它会一直向上移动堆栈,直到找到合适的结束标记
用正则表达式很难实现这一点。在注释或带引号的字符串中放置一个零散的
{
或}
,可以提供任何正则表达式解决方案。您最好使用lex并解析源代码。解决方案似乎相当明显:您将需要维护代码块的知识。你试过什么?天真的实现是使用一个堆栈,当你有非注释/string{
时,它会被推送,然后在}
上弹出。嘿,lukegravitt,你能给我一些关于如何做的提示吗?它不会直接回答你的问题,但你可以绘制自己的(或扩展其他人定制的)文本编辑器。我会把文本分成几行和几段。其中,跨距是类似文本的段,例如保留关键字、已定义类、大括号、折叠代码块等。从长远来看,能够给你的跨度提供这样的直接属性将使你的项目更容易。感谢,+1的简要描述。
public class FoldFinder
{
public static FoldFinder Instance { get; private set; }
static FoldFinder()
{
Instance = new FoldFinder();
}
public List<SectionPosition> Find(string code, List<SectionDelimiter> delimiters, int start = 0,
int end = -1)
{
List<SectionPosition> positions = new List<SectionPosition>();
Stack<SectionStackItem> stack = new Stack<SectionStackItem>();
int regexGroupIndex;
bool isStartToken;
SectionDelimiter matchedDelimiter;
SectionStackItem currentItem;
Regex scanner = RegexifyDelimiters(delimiters);
foreach (Match match in scanner.Matches(code, start))
{
// the pattern for every group is that 0 corresponds to SectionDelimter, 1 corresponds to Start
// and 2, corresponds to End.
regexGroupIndex =
match.Groups.Cast<Group>().Select((g, i) => new {
Success = g.Success,
Index = i
})
.Where(r => r.Success && r.Index > 0).First().Index;
matchedDelimiter = delimiters[(regexGroupIndex - 1) / 3];
isStartToken = match.Groups[regexGroupIndex + 1].Success;
if (isStartToken)
{
stack.Push(new SectionStackItem()
{
Delimter = matchedDelimiter,
Position = new SectionPosition() { Start = match.Index }
});
}
else
{
currentItem = stack.Pop();
if (currentItem.Delimter == matchedDelimiter)
{
currentItem.Position.End = match.Index + match.Length;
positions.Add(currentItem.Position);
// if searching for an end, and we've passed it, and the stack is empty then quit.
if (end > -1 && currentItem.Position.End >= end && stack.Count == 0) break;
}
else
{
throw new Exception(string.Format("Invalid Ending Token at {0}", match.Index));
}
}
}
if (stack.Count > 0) throw new Exception("Not enough closing symbols.");
return positions;
}
public Regex RegexifyDelimiters(List<SectionDelimiter> delimiters)
{
return new Regex(
string.Join("|", delimiters.Select(d =>
string.Format("(({0})|({1}))", d.Start, d.End))));
}
}
public class SectionStackItem
{
public SectionPosition Position;
public SectionDelimiter Delimter;
}
public class SectionPosition
{
public int Start;
public int End;
}
public class SectionDelimiter
{
public string Start;
public string End;
}
var sectionPositions =
FoldFinder.Instance.Find("abc { def { qrt; ghi [ abc ] } qrt }", new List<SectionDelimiter>(
new SectionDelimiter[3] {
new SectionDelimiter() { Start = "\\{", End = "\\}" },
new SectionDelimiter() { Start = "\\[", End = "\\]" },
new SectionDelimiter() { Start = "(?<=\\[|\\{|;|^)[^[{;]*(?=;)", End = ";" },
}));