Свертывание кода в RichTextBox
Я работаю над редактором кода, созданным на основе Winforms RichTextBox с использованием C#. Я уже реализовал автозаполнение и подсветку синтаксиса, но сворачивание кода - это несколько иной подход. Чего я хочу достичь:
Код ниже:
public static SomeFunction(EventArgs e)
{
//Some code
//Some code
//Some code
//Some code
//Some code
//Some code
}
Должно стать:
public static SomeFunction(EventArgs e)[...]
куда[...]
сокращенный код, который отображается во всплывающей подсказке при наведении на [...]
Есть идеи или предложения, как это сделать, используя Regex или процедурный код?
1 ответ
Решение
Я создал парсер, который будет возвращать индексы мест свертывания кода.
- Разграничители складывания определяются регулярными выражениями.
- Вы можете указать начальный и конечный индексы, чтобы вам не приходилось проверять весь код при обновлении одной области.
- Он выдаст исключения, если код не отформатирован должным образом, смело изменяйте это поведение. Одной из альтернатив может быть то, что он продолжает перемещаться вверх по стеку, пока не будет найден соответствующий конечный токен.
Fold Finder
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;
}
Образец Найти
Пример ниже соответствует складкам, разграниченным {,}
, [,]
и сразу после символа до ;
, Я не вижу слишком много IDE, которые сгибаются для каждой строки, но это может быть удобно при длинных фрагментах кода, таких как запрос LINQ.
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 = ";" },
}));