美文网首页
CSharp 代码编辑器

CSharp 代码编辑器

作者: 饭板板 | 来源:发表于2020-10-13 17:46 被阅读0次

How to build a Editor with smart tips

In this article, I will try to use AvalonEdit control to create a C# Editor.

  • Support method nested
  • Support method overload

My EditorControl

This control contains avalonEdit:TextEditor, we can do more things in this editor. Assume that this control named TextEditor, we must subscribe to TextEditor.TextArea.TextEntered event with OnTextEntered method.

private void OnTextEntered(object sender, TextCompositionEventArgs e)
{
    if (e.Text == ".")
    {
        // Open code completion after the user has pressed dot:
        myCompletionWindow = new CompletionWindow(TextEditor.TextArea);
        var datas = myCompletionWindow.CompletionList.CompletionData;
        var docText = TextEditor.TextArea.Document.Text;
        if (docText.EndsWith($"{AnalyzeAccess.ANALYZE}{e.Text}"))
        {
            AddCustomizedCompletionData(datas, typeof(IAnalyzeFunctionalityProvider));
            myCompletionWindow.Show();
        }
        else if (docText.EndsWith($"{AnalyzeAccess.CALCULATE}{e.Text}"))
        {
            AddCustomizedCompletionData(datas, typeof(ICalculateFunctionalityProvider));
            myCompletionWindow.Show();
        }
        else { }

        return;
    }

    if (myCompletionWindow != null)
    {
        var result = GetParameterIndex();
        if (result == -1)
        {
            if (insightWindow != null)
            {
                insightWindow.Close();
                insightWindow = null;
            }
        }
        else if (result == 0)
        {
            insightWindow = new OverloadInsightWindow(TextEditor.TextArea);
            selectedDatas.Push(myCompletionWindow.CompletionList.SelectedItem);
            insightWindow.Provider = methodDics[selectedDatas.Peek().Text];
            insightWindow.Show();
        }
        else // result == 1
        {
            insightWindow = new OverloadInsightWindow(TextEditor.TextArea);
            insightWindow.Provider = methodDics[selectedDatas.Pop().Text];
            insightWindow.Show();
        }
    }
}

private void AddCustomizedCompletionData(IList<ICompletionData> datas, Type type)
{
    methodDics.Clear();
    var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
        .OrderBy((m) => m.Name).ToArray();

    for (int j = 0; j < methods.Count(); j++)
    {
        var method = methods[j];
        if (j > 0 && method.Name == methods[j - 1].Name) // method overload
        {
            var preMP = methodDics[method.Name];
            preMP.Items.Add(new InsightItem(method));
            continue;
        }

        var desc = GetMethodDisplayName(method);
        var mp = new DefaultMethodProvider(desc);
        mp.Items.Add(new InsightItem(method));
        methodDics.Add(method.Name, mp);

        var data = new CustomizedCompletionData(method.Name, mp.Items)
        {
            Desc = desc
        };
        datas.Add(data);
    }
}

CustomizedCompletionData

This class displayed after entered '.'

    public class CustomizedCompletionData : ICompletionData
    {
        public CustomizedCompletionData(string text, IList<InsightItem> insights)
        {
            Text = text;
            overloadedData = insights;
        }

        readonly IList<InsightItem> overloadedData = new List<InsightItem>();
        public IEnumerable<InsightItem> OverloadedData
        {
            get { return overloadedData; }
        }

        public ImageSource Image => null;

        public string Text { get; private set; }

        // Use this property if you want to show a fancy UIElement in the list.
        public object Content => Text;

        public string Desc { get; set; }

        private string description;
        public object Description
        {
            get
            {
                if (description == null)
                {
                    description = string.Empty;
                    if (overloadedData.Count > 1)
                    {
                        description = " (+" + OverloadedData.Count() + " overloads)";
                    }
                    description = $"{Desc}{description}";
                }
                return description;
            }
        }

        public double Priority => 1;

        public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
        {
            textArea.Document.Replace(completionSegment, Text);
        }
    }

DefaultMethodProvider

This class used for aid OverloadInsightWindow. By this class, we can implement method overload.

    public class DefaultMethodProvider : IOverloadProvider
    {
        public DefaultMethodProvider(string headerText)
        {
            HeaderText = headerText;
            Items = new List<InsightItem>();
        }

        public IList<InsightItem> Items { get; internal set; }

        /// <summary>
        /// the summary of Method
        /// </summary>
        public string Documentation { get; set; } = string.Empty;

        /// <summary>
        /// Display method's name and parameters information
        /// </summary>
        public string HeaderText { get; set; }

        private int selectedIndex;

        /// <summary>
        ///  Gets the text 'SelectedIndex of Count'
        /// </summary>
        public int SelectedIndex
        {
            get { return selectedIndex; }
            set
            {
                selectedIndex = value;
                if (selectedIndex >= Count)
                    selectedIndex = Count - 1;
                if (selectedIndex < 0)
                    selectedIndex = 0;
                OnPropertyChanged(nameof(SelectedIndex));
                OnPropertyChanged(nameof(CurrentIndexText));
                OnPropertyChanged(nameof(CurrentHeader));
                OnPropertyChanged(nameof(CurrentContent));
            }
        }

        /// <summary>
        /// Gets the number of overloads.
        /// </summary>
        public int Count => Items.Count;

        /// <summary>
        /// Override method
        /// </summary>
        public string CurrentIndexText
        {
            get { return (selectedIndex + 1).ToString() + " of " + this.Count.ToString(); }
        }

        /// <summary>
        /// Gets the current header.
        /// </summary>
        public object CurrentHeader
        {
            get { return Items[selectedIndex].Header; }
        }

        /// <summary>
        /// Method'summary
        /// </summary>
        public object CurrentContent
        {
            get { return Items[selectedIndex].Content; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            var args = new PropertyChangedEventArgs(propertyName);
            if (PropertyChanged != null)
                PropertyChanged(this, args);
        }

InsightItem

This class map to Method. This class contains more information about Method.

    public sealed class InsightItem
    {
        public readonly MethodInfo MethodInfo;

        public InsightItem(MethodInfo mi)
        {
            this.MethodInfo = mi;
        }

        private TextBlock header;

        public object Header
        {
            get
            {
                if (header == null)
                    header = new TextBlock() { Text = GetMethodDisplayName(MethodInfo) };

                return header;
            }
        }

        /// <summary>
        /// Method'summary
        /// </summary>
        public object Content
        {
            get { return Documentation; }
        }

        public string Documentation
        {
            get
            {
                return string.Empty;
            }
        }

        private string GetMethodDisplayName(MethodInfo method)
        {
            var sb = new StringBuilder();
            sb.Append($"{method.ReturnType.Name} {method.DeclaringType.Name}.{method.Name}(");
            for (int i = 0; i < method.GetParameters().Length; i++)
            {
                var paraInfo = method.GetParameters()[i];
                var paraType = paraInfo.ParameterType.Name;
                var paraName = paraInfo.Name;

                if (i == method.GetParameters().Length - 1)
                {
                    sb.Append($"{paraType} {paraName}");
                }
                else
                {
                    sb.Append($"{paraType} {paraName}, ");
                }
            }
            sb.Append(")");

            return sb.ToString();
        }
    }

相关文章

网友评论

      本文标题:CSharp 代码编辑器

      本文链接:https://www.haomeiwen.com/subject/akzvpktx.html