CSharp 代碼編輯器

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373