ساخت یک code completion در#C
جمعه 30 بهمن 1394در این مقاله ، قصد داریم با استفاده از زبان #C ، یک code completion بسازیم. این code completion برای ما ،سرعت و اطمینان بیشتر در کد نویسی را فراهم می کند.
در این مقاله ، یک code completion با استفاده از C# ایجاد می کنیم که به صورت خودکار ، یک لیست از کلمات کلیدی و یا syntax ها را نمایش می دهد و آن کلمات کلیدی را در یک RichTextBox درج می کند. هر بار که عملیات مورد نظر کاربر، به طور کامل انجام شود ، این برنامه ، لیست موجود را پاک می کند و دوباره برای اجرا آماده می شود.
مقدمه
در این مقاله ، ما یک code completion ایجاد می کنیم. یک code completion ، یک برنامه ی تکمیل گر کد " آگاه به متن " است که در برخی محیط های برنامه نویسی استفاده می شود و سرعت فرآیند کدنویسی را به صورت قابل توجهی افزایش می دهد. این برنامه همچنین از میزان اشتباهات متداول و غلط های املایی کاربران کم می کند. این برنامه اصولا از طریق pop up های خودکار ظاهر می شود. مواردی نظیر پرسش درباره ی پارامترهای توابع و پرسش های مربوط به خطاهای syntax و غیره نیز توسط این برنامه پوشش داده می شود.
یک code completion در بسیاری از محیط های برنامه نویسی ظاهر می شود ، به عنوان مثال این ویژگی با IntelliSense در Visual Studio ادغام شده است . پیش از این، این ویژگی به عنوان "picklist" در بین کاربران معروف شده بود و هنوز هم برخی افراد از این ویژگی، با این نام یاد می کنند.
بیایید بررسی کنیم ساختن یک code completion در C# چقدر سخت است ؟من در اینترنت به دنبال جواب این سوال گشتم و چیزی به جز ویرایشگرهای پیشرفته پیدا نکردم .
این ویژگی ، در حقیقت، یک لیست است که در هنگام تایپ کردن کد نمایش داده می شود ، اما چگونه می توانیم این لیست را در RichTextBox نمایش بدهیم؟
قبل از این که این کد را تهیه کنم ، فکر می کردم که ایجاد همچین برنامه ای ، بسیار دشوار است و به تجربه و مهارت بالایی نیاز دارد، اما اشتباه می کردم ، این کار به ایده ی شما بستگی دارد. من ادعا می کنم که این کار بسیار آسان است.حالا بیایید ببینیم من چه دلیلی برای این ادعا دارم.
فقط کافی است یک شی ListBox ایجاد کنید و آن را به یک RichTextBox اضافه کنید و آیتم های انتخاب شده را از ListBox بگیرید و آن ها را در RichTextBox درج کنید و سپس ListBox را پاک کنید. در حقیقت ،شما فقط نیاز دارید تا یک سری از آیتم ها را از ListBox حذف کنید و یا آیتم هایی را به آن اضافه کنید. شما می توانید code completion خودتان را به شیوه ای که دوست دارید ، ایجاد کنید. (همان طور که در تصویر 3 آمده است .)
در این مقاله ما یکcode completion ساده در C# ایجاد می کنیم . کد برنامه را دانلود کنید و code completion ای که با استفاده از XML نوشته شده است را ببینید. این کار ، چیزی به جز حذف و اضافه کردن آیتم ها در ListBox نیست. الگوریتم زیر را ببینید تا بهتر متوجه بشوید که روند کار به چه صورتی است.
برای فهم این مطلب ، همه ی کامنت های موجود در کد را با دقت بخوانید. (CCRichTextBox)
الگوریتم
یک شی ListBox ایجاد کنید. (من از نام CodeCompleteBox برای این شی استفاده می کنم.)
مختصات X-Y را از RichTextBox بخوانید.
یک متغیر Boolean برای مشخص کردن اضافه شدن CodeCompleteBox در نظر بگیرید . ( من از متغیر isCodeCompleteBoxAdded برای این کار استفاده می کنم . ) و یک متغیر String برای آگاهی از String کامل در نظر می گیریم . ( من از متغیر EnteredKey استفاده می کنم .)
یک لیست آرایه از کلمات کلیدی در نظر بگیرید. ( من از keywordslist استفاده می کنم .)
رویدادهای زیر را به RichTextBox اضافه کنید.
(i) فشار دادن کلید (Key Press)
1-تشخیص می دهد که کلید فشرده شده، الفبایی است یا نه.
2- همه ی آیتم های موجود در CodeCompleteBox را حذف می کند.
3-همه ی آیتم ها را از keywordslist می خواند.
4-اگر هر آیتم از keywordslist با کلید فشرده شده شروع می شود ، سپس آن آیتم را به CodeCompleteBox اضافه می کند.
5-همه ی آیتم ها را از keywordslist می خواند.
6-اگر هر آیتم از keywordslist با کلید فشرده شده شروع می شود ، آن آیتم را به آیتم انتخاب شده تغییر می دهد.
7-مکان نمای Default را بر روی CodeCompleteBox قرار می دهد.
8-اندازه ی CodeCompleteBox را مشخص می کند.
9-محل قرارگیری CodeCompleteBox را با استفاده از مختصاتی که از مرحله ی 2 می خواند ، تعیین می کند.
10-محتویات CodeCompleteBox را به RichTextBox اضافه می کند.
11-مقدار isCodeCompleteBoxAdded را برابر با true قرار می دهد.
(ii) تغییر در متن (Text Changed)
اگر متن RichTextBox خالی است ، CodeCompleteBox را از RichTextBox حذف می کند. قبل از پاک کردن، بررسی می کند که isCodeCompleteBoxAdded ، برابر با true باشد.
(iii) فشرده شدن کلید به سمت پایین (Key Down)
1-بررسی می کند که اگر کلیدهای Space , Enter , Escape و Backفشرده شده اند، به دستور بعدی می رود.
2-اگر مقدار isCodeCompleteBoxAdded ، برابر با true است ، CodeCompleteBox را از RichTextBox حذف می کند.
(iv) کلیک Mouse :
اگر مقدار isCodeCompleteBoxAdded ، برابر با true است ، CodeCompleteBox را از RichTextBox حذف می کند.
(v) اسکرول(VScroll)
اگر مقدار isCodeCompleteBoxAdded ، برابر با true است ، CodeCompleteBox را از RichTextBox حذف می کند.
6-رویدادهای زیر را به CodeCompleteBox اضافه کنید.
فشردن کلید به سمت پایین (Key Down):
1-بررسی می کند که isCodeCompleteBoxAdded ، برابر با true است یا نه، اگر برابر با true باشد ، به دستور بعدی می رود.
2-اگر کلید Enter یا Space فشرده شده است ، به دستور بعدی می رود.
3- آیتم های انتخاب شده را از CodeCompleteBox می خواند و آن ها را در RichTextBox در موقعیت SelectionStart درج می کند.
4- CodeCompleteBox را از RichTextBox حذف می کند.
فشردن کامل کلید(Key Press)
این ویژگی برای زمانی استفاده می شود که بخواهیم یک کاراکتر انتخاب شده را در RichTextBox درج کنیم .همچنین برای انتخاب یک آیتم در CodeCompleteBox نیز به کار می رود.
کلیک کردن ماوس (Mouse Click)
مانند رویداد Key Down در مرحله ی 3
7: توقف برنامه (Stop)
Tool Tip
در اینجا ، برای نمایش یک tooltip و یا برای نمایش اطلاعات درباره ی آیتم های انتخاب شده در مجموعه ی CodeCompleteBox ، ما از label استفاده می کنیم و این label را به Panel اضافه می کنیم و سپس panel را به مکان مخصوص خودش در RichTextBox و یا مکان بعدی در CodeCompleteBox اضافه می کنیم.
در این مقاله ما یک tooltip ایجاد نمی کنیم .ما یک source دانلود می کنیم که شامل تمام کد و همچنین tooltip ها است. همه ی اعمالی که باید در این قسمت انجام بگیرد، مانند CodeCompleteBox است و تنها یک رویداد Key Up به آن اضافه می شود.
کد نویسی :
من یک کلاس CCRichTextBox با یک سوپر کلاس RichTextBox ایجاد می کنم .
در ابتدا ، بیایید چند تابع مهم را با هم بررسی کنیم.
من از تابع ProcessCodeCompletionAction(String key) در هر جایی که رویداد فشردن کلید اتفاق افتاده است ،برای فراخوانی استفاده می کنم .من از توابع getWidth() و getHeight() برای تنظیم ارتفاع و عرض CodeCompleteBox استفاده کرده ام ، اما شما می توانید آن ها را به صورت پیش فرض در نظر بگیرید. همچنین فشردن همزمان یک کاراکتر و EnteredKey ، همه ی مراحلی که در Key Press در الگوریتم بالا گفته شد را دوباره انجام می دهد.
در اینجا ما tooltip به CodeCompleteBox اضافه نمی کنیم، بنابراین اگر با خطا مواجه شدید، همه ی تجهیزات مربوط به tooltip ها را حذف کنید.
public void ProcessCodeCompletionAction(String key) { EnteredKey = ""; // concat the key & EnteredKey postfix EnteredKey = EnteredKey + key; char ch; //check pressed key on CCRichTextBox is lower case alphabet or not for (ch = 'a'; ch <= 'z'; ch++) { if (key == ch.ToString()) { // Clear the CodeCompleteBox Items CodeCompleteBox.Items.Clear(); //add each item to CodeCompleteBox foreach (String item in keywordslist) { //check item is starts with EnteredKey or not if (item.StartsWith(EnteredKey)) { CodeCompleteBox.Items.Add(item); } } // read each item from CodeCompleteBox to set SelectedItem foreach (String item in keywordslist) { if (item.StartsWith(EnteredKey)) { CodeCompleteBox.SelectedItem = item; // set Default cursor to CodeCompleteBox CodeCompleteBox.Cursor = Cursors.Default; // set Size to CodeCompleteBox // width=this.getWidth() & height=this.getHeight()+(int)this.Font.Size CodeCompleteBox.Size = new System.Drawing.Size(this.getWidth(), this.getHeight() + (int)this.Font.Size); // set Location to CodeCompleteBox by calling getXYPoints() function CodeCompleteBox.Location = this.getXYPoints(); // adding controls of CodeCompleteBox to CCRichTextBox this.Controls.Add(CodeCompleteBox); // set Focus to CodeCompleteBox CodeCompleteBox.Focus(); // set isCodeCompleteBoxAdded to true isCodeCompleteBoxAdded = true; break; } else { isCodeCompleteBoxAdded = false; } } } // check pressed key character is upper case letter or not else if (key == ch.ToString().ToUpper()) { // Clear the CodeCompleteBox Items CodeCompleteBox.Items.Clear(); //add each item to CodeCompleteBox foreach (String item in keywordslist) { //check item is starts with EnteredKey or not if (item.StartsWith(EnteredKey)) { CodeCompleteBox.Items.Add(item); } } // read each item from CodeCompleteBox to set SelectedItem foreach (String item in keywordslist) { if (item.StartsWith(EnteredKey)) { CodeCompleteBox.SelectedItem = item; // set Default cursor to CodeCompleteBox CodeCompleteBox.Cursor = Cursors.Default; // set Size to CodeCompleteBox // width=this.getWidth() & height=this.getHeight()+(int)this.Font.Size CodeCompleteBox.Size = new System.Drawing.Size(this.getWidth(), this.getHeight() + (int)this.Font.Size); // set Location to CodeCompleteBox by calling getXYPoints() function CodeCompleteBox.Location = this.getXYPoints(); // adding controls of CodeCompleteBox to CCRichTextBox this.Controls.Add(CodeCompleteBox); // set Focus to CodeCompleteBox CodeCompleteBox.Focus(); // set isCodeCompleteBoxAdded to true isCodeCompleteBoxAdded = true; break; } else { isCodeCompleteBoxAdded = false; } } } } }
بسیار خب ، بیایید همه ی مراحل الگوریتم بالا را با هم انجام بدهیم.
1- یک شی Listbox ایجاد کنید.
public ListBox CodeCompleteBox = new ListBox();
2- مختصات X-Y را از RichTextBox بخوانید.
این تابع ، مختصات (x,y) را با اضافه کردن و یا کم کردن فونت RichTextBox به ما برمیگرداند. در تصویر پایین ، مکان x-y در CodeCompleteBox مشخص شده است.
public Point getXYPoints() { //get current caret position point from CCRichTextBox Point pt = this.GetPositionFromCharIndex(this.SelectionStart); // increase the Y co-ordinate size by 10 & Font size of CCRichTextBox pt.Y = pt.Y + (int)this.Font.Size + 10; // check Y co-ordinate value is greater than CCRichTextBox Height - CodeCompleteBox // for add CodeCompleteBox at the Bottom of CCRichTextBox if (pt.Y > this.Height - CodeCompleteBox.Height) { pt.Y = pt.Y - CodeCompleteBox.Height - (int)this.Font.Size - 10; } return pt; }
3-یک متغیر Boolean تعریف کنید برای تشخیص این که CodeCompleteBox اضافه شده است یا نه.
public static Boolean isCodeCompleteBoxAdded = false;
یک متغیر از نوع String تعریف کنید که کل رشته را مشخص کند.
public static String EnteredKey = "";
4- یک لیست از کلمات کلیدی تعریف کنید. در این جا شما می توانید لیست را از قبل در برنامه تعریف کنید و یا می توانید آن را با استفاده از فایلXML بخوانید. در اینجا من لیستی از آن را تعریف کردم.
public String[] keywordslist = { "bool", "break", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "float", "for", "goto", "if", "int" };
5-رویداد های Key Press ,Text Changed, Key Down, Mouse Click, VScroll را به CCRichTextBox اضافه کنید.
رویداد Key Press :
در اینجا ما به صورت مستقیم تابع ProcessCodeCompletionAction() را صدا خواهیم زد. من دو متغیر اضافه از نوع Boolean تعریف کردم که عبارتند از : isClassCreated = false Boolean و Boolean isDataTypeDeclared = false . برای تشخیص این که آیتمی که از CodeCompleteBox درج شده است ، class/datatype هست یا نه. برای این کار شما نیاز دارید که لیستی از کلاس ها و datatype ها را هم ایجاد کنید.
به عنوان مثال : یک بار که شما یک آیتم را از CodeCompleteBox انتخاب میکنید ، سپس CodeCompleteBox تا زمانی که شما ";"را فشار ندهید ، ظاهر نمی شود.زیرا برای ایجاد یک شی از Form ، شما دو حالت دارید :
Form frm; or Form frm=new Form();
کدبرنامه را دانلود کنید . در کد برنامه می توانید ببینید که یک بار که ما CCRichTextBox را به فرم خودمان می کشیم و می آوریم ، در Properties می توانیم لیستی از keywords/classes/datatypes را به آن اضافه کنیم .
protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); ProcessAutoCompleteBrackets(e); String key = e.KeyChar.ToString(); if (isClassCreated && (key == "=" || key == ";")) { ProcessCodeCompletionAction(key); isClassCreated = false; } else if (isClassCreated && key != "=") { } else if (isDataTypeDeclared && (key == ";" || key == "{"||key=="}" || key == "(" || key == ")")) { ProcessCodeCompletionAction(key); isDataTypeDeclared = false; } else if (isDataTypeDeclared && key != ";") { } else { ProcessCodeCompletionAction(key); } }
رویداد Text Changed : در صورتی که متن ، خالی باشد ، CodeCompleteBox را از CCRichTextBox حذف می کند.
protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); if (this.Text == "") { if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } } }
رویداد Key Down :
بررسی می کند که کلیدهای Space, Enter, Escape و Backفشرده شده اند یا نه ، اگر جواب True بود CodeCompleteBox را از CCRichTextBox حذف می کند. من در اینجا فقط طرز کار برای کلید Space را نشان داده ام ، بقیه ی کلیدها را خودتان اضافه کنید.
protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); switch(e.KeyCode) { case Keys.Space: if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } break; } }
رویداد Mouse Click: اگر مقدار isCodeCompleteBoxAdded برابر با true است ، همان طور که در بالا توضیح داده شده است ، CodeCompleteBox را از CCRichTextBox حذف می کند.
رویداد VScroll : کد این بخش ، همانند Mouse Click است.
6- رویدادهای زیر را به CodeCompleteBox اضافه کنید.
public CCRichTextBox() { CodeCompleteBox.KeyDown += new KeyEventHandler(CodeCompleteBox_KeyDown); CodeCompleteBox.KeyUp += new KeyEventHandler(CodeCompleteBox_KeyUp); CodeCompleteBox.KeyPress += new KeyPressEventHandler(CodeCompleteBox_KeyPress); CodeCompleteBox.MouseClick += new MouseEventHandler(CodeCompleteBox_MouseClick); }
Key Down:
ابتدا مشخص می کند که کلید فشرده شده ، Enter/Space است یا نه. سپس اضافه شدن CodeCompleteBox به CCRichTextBox را بررسی می کند. در گام بعدی مشخص می کند که آیتم انتخاب شده از CodeCompleteBox با EnteredKey شروع می شود یا نه. سپس آیتم انتخاب شده را از CodeCompleteBox می خواند . بعد از این کار ، آیتم انتخاب شده را در CCRichTextBox در قسمت SelectionStart درج می کند و سپس CodeCompleteBox را از CCRichTextBox حذف می کند.
اگر کلید فشرده شده ، Space است ، یک Space در کنار آیتم درج می کند. اگر کلید فشرده شده Left/Right است ، CodeCompleteBox را از CCRichTextBox حذف می کند.
در این جا نحوه ی نوشتن کد برای کلید Enter آورده شده است .
private void CodeCompleteBox_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Enter: if (isCodeCompleteBoxAdded) { if (EnteredKey != "") { if (EnteredKey.Length == 1) { int sel = this.SelectionStart; String text = CodeCompleteBox.SelectedItem.ToString(); text = text.Remove(0, 1); this.Text = this.Text.Insert(sel, text); this.SelectionStart = sel + text.Length; this.Controls.Remove(CodeCompleteBox); this.ProcessDeclaredClasses(CodeCompleteBox.SelectedItem.ToString()); this.ProcessDeclaredDataTypes(CodeCompleteBox.SelectedItem.ToString()); } else if (EnteredKey.Length == 2) { int sel = this.SelectionStart; String text = CodeCompleteBox.SelectedItem.ToString(); text = text.Remove(0, 2); this.Text = this.Text.Insert(sel, text); this.SelectionStart = sel + text.Length; this.Controls.Remove(CodeCompleteBox); this.ProcessDeclaredClasses(CodeCompleteBox.SelectedItem.ToString()); this.ProcessDeclaredDataTypes(CodeCompleteBox.SelectedItem.ToString()); } else if (EnteredKey.Length == 3) { int sel = this.SelectionStart; String text = CodeCompleteBox.SelectedItem.ToString(); text = text.Remove(0, 3); this.Text = this.Text.Insert(sel, text); this.SelectionStart = sel + text.Length; this.Controls.Remove(CodeCompleteBox); this.ProcessDeclaredClasses(CodeCompleteBox.SelectedItem.ToString()); this.ProcessDeclaredDataTypes(CodeCompleteBox.SelectedItem.ToString()); } else { int sel = this.SelectionStart; String text = CodeCompleteBox.SelectedItem.ToString(); if (text.Contains(EnteredKey)) { text = text.Replace(EnteredKey, ""); } this.Text = this.Text.Insert(sel, text); this.SelectionStart = sel + text.Length; this.Controls.Remove(CodeCompleteBox); this.ProcessDeclaredClasses(CodeCompleteBox.SelectedItem.ToString()); this.ProcessDeclaredDataTypes(CodeCompleteBox.SelectedItem.ToString()); } } } break; // if Left key is down then remove CodeCompleteBox from this case Keys.Left: if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } break; // if Right key is down then remove CodeCompleteBox from this case Keys.Right: if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } break; } }
رویداد Key Press :
این رویداد برای انتخاب آیتمی استفاده می شود که با EnteredKey شروع می شود ، سپس همه ی آیتم های موجود در CodeCompleteBox را می خواند و آیتمی که با EnteredKey شروع می شود را جزو آیتم های انتخاب شده قرار می دهد و همچنین تشخیص می دهد که کاراکتر خاصی فشار داده شده است یا نه. اگر فشار داده شده است ، CodeCompleteBox را از CCRichTextBox حذف می کند.
private void CodeCompleteBox_KeyPress(object sender, KeyPressEventArgs e) { String str = e.KeyChar.ToString(); // in this event we must insert pressed key to this because Focus is on CodeCompleteBox // first check pressed key is not Space,Enter,Escape & Back // Space=32, Enter=13, Escape=27, Back=8 if (Convert.ToInt32(e.KeyChar) != 13 && Convert.ToInt32(e.KeyChar) != 32 && Convert.ToInt32(e.KeyChar) != 27 && Convert.ToInt32(e.KeyChar) != 8) { if (isCodeCompleteBoxAdded) { // insert pressed key to CCRichTextBox at SelectionStart position int sel = this.SelectionStart; this.Text = this.Text.Insert(sel, str); this.SelectionStart = sel + 1; e.Handled = true; // concat the EnteredKey and pressed key on CodeCompleteBox EnteredKey = EnteredKey + str; // search item in CodeCompleteBox which starts with EnteredKey and set it to selected foreach (String item in CodeCompleteBox.Items) { if (item.StartsWith(EnteredKey)) { CodeCompleteBox.SelectedItem = item; break; } } } } // if pressed key is Back then set focus to CCRichTextBox else if (Convert.ToInt32(e.KeyChar) == 8) { this.Focus(); } // if pressed key is not Back then remove CodeCompleteBox from CCRichTextBox else if (Convert.ToInt32(e.KeyChar) != 8) { if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } } // check pressed key on CodeCompleteBox is special character or not // if it is a special character then remove CodeCompleteBox from CCRichTextBox switch (str) { case "~": case "`": case "!": case "@": case "#": case "$": case "%": case "^": case "&": case "*": case "-": case "_": case "+": case "=": case "(": case ")": case "[": case "]": case "{": case "}": case ":": case ";": case "\"": case "'": case "|": case "\\": case "<": case ">": case ",": case ".": case "/": case "?": if (isCodeCompleteBoxAdded) { this.Controls.Remove(CodeCompleteBox); EnteredKey = ""; } break; } }
رویداد Mouse Click
این رویداد همانند فشردن کلید اینتر در CodeCompleteBox است. همین ! همه ی مراحل الگوریتم ، گام به گام انجام شدند.اگر شما طبق همین مراحل پیش بروید ، می توانید به سادگی برنامه را بسازید. در Properties شما می توانید تنظیمات مربوط به رنگ برنامه را مطابق با سلیقه ی خودتان تغییر بدهید.
- C#.net
- 2k بازدید
- 2 تشکر