Вопрос

I am working on a Code Editor derived from Winforms RichTextBox using C#. I have already implemented autocompletion and syntax hilighting, but code folding is somewhat a different approach. What I want to achieve is:

The code below:

public static SomeFunction(EventArgs e)
{
    //Some code
    //Some code
    //Some code
    //Some code
    //Some code
    //Some code
}

Should become:

public static SomeFunction(EventArgs e)[...]

Where[...] is a shortened code that is displayed in a tooltip when you hover over at [...] Any ideas or suggestions how to do it, either using Regex or procedural code?

Это было полезно?

Решение

I have created a parser that will return the indices of code folding locations.

  • Folding delimiters are defined by regular expressions.
  • You can specify a start and ending index so that you don't have to check the entire code when one area is updated.
  • It will throw exceptions if the code is not properly formatted, feel free to change that behavior. One alternative could be that it keeps moving up the stack until an appropriate end token is found.

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

Sample Find

The sample below matches folds delimited by {,}, [,], and right after a symbol until a ;. I don't see too many IDE's that fold for each line, but it might be handy at long pieces of code, like a LINQ query.

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 = ";" },
        }));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top