日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

devc代码补全没效果_从零开始写文本编辑器(二十八):自动补全(上)

發(fā)布時(shí)間:2024/9/19 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 devc代码补全没效果_从零开始写文本编辑器(二十八):自动补全(上) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

我本沒打算這么早就寫“自動(dòng)補(bǔ)全”功能的。

但是在寫XML資源編輯時(shí),為了實(shí)現(xiàn)自動(dòng)引用已有資源@string/xxx,需要一個(gè)合適的列表來讓我選擇。這樣能防止拼寫錯(cuò)誤。

也就是說,初衷是為了防止拼寫錯(cuò)誤,結(jié)果分析了性價(jià)比,還是上自動(dòng)補(bǔ)全功能吧。

XML自動(dòng)補(bǔ)全是一個(gè)較大的模塊,它分為多個(gè)子模塊,本篇發(fā)稿時(shí),全部模塊還遠(yuǎn)沒完成。在本篇中只講述自動(dòng)補(bǔ)全的GUI模塊,并演示 java 關(guān)鍵字代碼補(bǔ)全作結(jié)尾。

調(diào)研自動(dòng)補(bǔ)全 Auto Complete

“自動(dòng)補(bǔ)全”是一個(gè)寬泛的說法,具體到代碼編輯器,就是“代碼補(bǔ)全”,本篇統(tǒng)稱為“自動(dòng)補(bǔ)全”。

自動(dòng)補(bǔ)全的好處:

  • 自動(dòng)匹配已知輸入字符串,猜測(cè)完整字符串
    • 簡單如:從首字符開始連續(xù)匹配
    • 高級(jí)如:不連續(xù)匹配
  • 自動(dòng)彈出選擇列表
    • 用簡單的 UP/DOWN 按鍵,瀏覽選擇項(xiàng)
    • 用簡單的 ENTER 鍵,選擇補(bǔ)全項(xiàng),并自動(dòng)插入光標(biāo)位置。

“自動(dòng)補(bǔ)全”的流程圖

這是一張粗糙的流程圖,還有小細(xì)節(jié),用代碼更直觀表述。但在上代碼之前,先總體說下功能類。

類清單

  • Complete:補(bǔ)全。提供“補(bǔ)全”的列表數(shù)據(jù)
  • EatEnter:“吃掉回車”。因?yàn)榛剀嚪c退出符不同,回車符是可顯示字符,當(dāng)用于確認(rèn)插入操作時(shí),要主動(dòng)吃掉。
  • Focus:焦點(diǎn)。當(dāng)顯示彈出菜單列表時(shí),要把焦點(diǎn)交還給編輯器,否則無法持續(xù)編輯。
  • FrameCode:代碼窗口。這是我個(gè)人習(xí)慣用Frame前綴表示某窗口類。
  • Insert:插入。完成代碼插入,內(nèi)部記憶了插入的光標(biāo)位置。
  • ListComplete:補(bǔ)全列表。同F(xiàn)rame一樣,List前綴表示它是一個(gè)JList,是顯示補(bǔ)全數(shù)據(jù)的容器。
  • Location:位置。它計(jì)算出補(bǔ)全列表彈出的坐標(biāo)x, y位置,讓左上角臨近光標(biāo)。
  • PopupMenuComplete:補(bǔ)全彈出菜單。同F(xiàn)rame一樣,PopupMenu前綴表示,它是一個(gè)JPopupMenu,它是ListComplete的容器,自動(dòng)處理了 Escape 等邏輯。
  • TextEditor:編輯器。這是不是前些篇中的編輯,它沒有行號(hào)等功能。只是我在“自動(dòng)補(bǔ)全”中臨時(shí)編寫的。

OK,全部類就是這些了。

用例:彈出菜單顯示補(bǔ)全列表

public void keyReleased(java.awt.event.KeyEvent e) {int keyCode = e.getKeyCode();if (keyCode == KeyEvent.VK_UP) {popupMenuComplete.selectPrevious();} else if (keyCode == KeyEvent.VK_DOWN) {popupMenuComplete.selectNext();} else if (keyCode == KeyEvent.VK_ESCAPE) {popupMenuComplete.setVisible(false);popupMenuComplete.clean();} else if (Character.isWhitespace(e.getKeyChar())) {popupMenuComplete.setVisible(false);popupMenuComplete.clean();} else {String headString = textEditor.getHeadString();if (headString != null && headString.length() > 0) {insert.setPosition(textEditor.getCaretPosition());popupMenuComplete.receiveInputString(headString);Point point = location.getLocation();popupMenuComplete.show(textEditor, point.x, point.y);focus.backToTextComponent();}} };

當(dāng)用戶輸入后,偵聽到可見字符輸入,編輯器從光標(biāo)處向前搜索已知輸入字符串 headString。

為了方便記憶,我把補(bǔ)全字符串分解為:頭部(head string)和 尾部(tail string),這是headString命名的由來。

/*** * @return null if condition failed.*/ public String getHeadString() {int caretPosition = getCaretPosition();String text = getText();int start = indexOfWordStart();if (start > caretPosition) {return null;}return text.substring(start, caretPosition); }

比如:輸入'p',則返回 "p"。

然后彈出菜單(此時(shí)不可見),開始接收已知字符串,進(jìn)行匹配。

public void receiveInputString(String headString) {this.headString = headString; // String[] data = complete.createListData(headString);Vector<String> data = complete.createListKeywordsJava(headString);listComplete.setListData(data);listComplete.setSelectedIndex(0); }

本篇中匹配數(shù)據(jù)集為 java 關(guān)鍵字,使用簡單的起始字符串匹配。

public Vector<String> createListKeywordsJava(String headString) {Vector<String> vector = new Vector<String>();for (String string : keywords) {if (string.startsWith(headString)) {vector.add(string);}}return vector; }private String[] keywords = new String[] { "abstract", "assert", "boolean", "break", "byte", "case", "catch","char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final","finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long","native", "new", "package", "private", "protected", "public", "return", "strictfp", "short", "static","super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile","while" };

返回的字符串向量送給列表顯示,并默認(rèn)選中第1項(xiàng)(index=0)。

返回窗口領(lǐng)空后,把焦點(diǎn)還給編輯器(JTextPane textEditor)。

package editor.xml.visual.autocomplete2;import javax.swing.text.JTextComponent;public class Focus {private JTextComponent textComponent;public Focus(JTextComponent textComponent) {this.textComponent = textComponent;}/*** after show pop up menu, move focus back on text component.*/public void backToTextComponent() {textComponent.requestFocus();} }

用例:彈出菜單的位置計(jì)算

package editor.xml.visual.autocomplete2;import java.awt.Point; import java.awt.geom.Rectangle2D;import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent;public class Location {private JTextComponent textComponent;public Location(JTextComponent textComponent) {this.textComponent = textComponent;}/*** * @return point location in text component, not base on screen.*/private Point getCaretLocation() {try {int caretPosition = textComponent.getCaretPosition();Rectangle2D rectangle2d = textComponent.modelToView2D(caretPosition);int x = (int) rectangle2d.getMaxX();int y = (int) rectangle2d.getMaxY();return new Point(x, y);} catch (BadLocationException e2) {e2.printStackTrace();return null;}}/*** <pre>* 提示:invoker 要使用 textComponent為參數(shù)* </pre>* * @return point of location left-top on invoker.* */public Point getLocation() {Point point = getCaretLocation();int baseY = textComponent.getBaseline(0, 0);point.y += baseY;return point;}}

這段邏輯主要是控件坐標(biāo)API的使用,invoker 的目標(biāo)容器會(huì)影響顯示的父位置,進(jìn)而會(huì)影響總的坐標(biāo)計(jì)算,為了靈活性,我不想寫“死“,所以只好加注釋說明。

Point point = location.getLocation(); popupMenuComplete.show(textEditor, point.x, point.y);

上述的 show(textEditor, ...,如果把 textEditor換成了窗口等父控件,則顯示坐標(biāo)就會(huì)計(jì)算偏離。

用例:瀏覽列表

if (keyCode == KeyEvent.VK_UP) {popupMenuComplete.selectPrevious(); } else if (keyCode == KeyEvent.VK_DOWN) {popupMenuComplete.selectNext();// PopupMenuComplete.java public void selectPrevious() {int hopeIndex = listComplete.getSelectedIndex() - 1;int index = Math.max(hopeIndex, 0);listComplete.setSelectedIndex(index); }public void selectNext() {int hopeIndex = listComplete.getSelectedIndex() + 1;int index = Math.max(hopeIndex, 0);listComplete.setSelectedIndex(index); }

此處可以寫成循環(huán)瀏覽,eclipse 就是可循環(huán)瀏覽,我這里就不復(fù)雜化。

用例:取消補(bǔ)全

} else if (keyCode == KeyEvent.VK_ESCAPE) {popupMenuComplete.setVisible(false);popupMenuComplete.clean();

用例:插入補(bǔ)全

package editor.xml.visual.autocomplete2;import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent;public class Insert {public Insert(JTextComponent textComponent) {this.textComponent = textComponent;}private JTextComponent textComponent;private int position;public int getPosition() {return position;}public void setPosition(int position) {this.position = position;}public void insert(String tailString) throws BadLocationException {textComponent.getDocument().insertString(position, tailString, null);} }

記得要“吃掉回車符”哦!

package editor.xml.visual.autocomplete2;import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent;public class EatEnter {private JTextComponent textComponent;public EatEnter(JTextComponent textComponent) {this.textComponent = textComponent;}public void eat() {final int position = textComponent.getCaretPosition();try {textComponent.getDocument().remove(position - 1, 1);} catch (BadLocationException e) {e.printStackTrace();}} }

看看效果

在演示中,好像在第二行處有BUG,in的前綴怎么會(huì)出現(xiàn) if 關(guān)鍵字。這些BUG我后續(xù)再迭代修正,本篇主要邏輯就是這些。

這是一個(gè)非常粗糙的膠水式模塊,后續(xù)慢慢會(huì)演變成一個(gè)自動(dòng)補(bǔ)全框架。

以上~

參考資料

  • stackoverflow 搜索 "auto complete" 若干文章

工具

  • Gif錄制軟件:ScreenToGif

總結(jié)

以上是生活随笔為你收集整理的devc代码补全没效果_从零开始写文本编辑器(二十八):自动补全(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。