美文网首页
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