教程需求
在繼續前,請確保您重新檢查了本節的需求。
先決條件
本 教程假設您已經有一些使用 IDE 的基本知識和 Java 編程經驗。
本教程所需的軟 件
在開始前,您需要安裝 NetBeans 6.0。您將同時需要 C/C++ 和 Java SE 支持,所 以最佳選擇是選擇“下載全部”選項並在安裝時排除 Base IDE、Java SE 和 C/C++ 包之外的所有模塊。
准備項目
對本教程,我們需要兩個項目。一個是管 理我們的插件的源代碼的 NetBeans 模塊。另一個是用來測試它的 C++ 項目。
創 建 NetBeans 插件模塊
選擇“文件”>“新建項目”。在 “新建項目”向導中,選擇“類別”下的“NetBeans 模塊 ”和“項目”下的“模塊”。單擊“下一步”。
在“名稱和位置”頁,在“項目名稱”字段中鍵入 MarkOccurrences,並把“項目位置”設置到磁盤上一個適當的文件夾裡。請 選中“獨立模塊”和“設置為主項目”,如果沒有選中的話。單擊 “下一步”。
在“基本模塊配置”頁,在“代碼名稱 基”字段中鍵入 org.netbeans.modules.markoccurrences。單擊“完成 ”。
在本項目中,我們需要一些依賴關系。在“項目”窗口,右 鍵單擊“庫”節點並在“添加模塊依賴關系”對話框中選擇 “庫”,然後添加屏幕快照裡列出的各個庫。C/C++ 模塊 API 正處於開發中 ,所以您需要在對話框中選擇“顯示非-API 模塊”,以便在模塊列表中看到 它們。
右鍵 單擊各個 C/C++ 模塊,選擇“編輯”,然後選擇“實現版本”。
創建測試應用程序
選擇“文件”>“新建項目”。選 擇“樣例”>“C/C++”>“C/C++”類別下的 Args 項目。單擊“下一步”。
在“項目名稱和位置”頁, 把“項目位置”設置到磁盤上一個適當的文件夾裡。單擊“完成” 。
現已創建了 Args_1 項目。在編輯器中打開 arg.c 源文件。我們將使用這個文 件來測試我們的模塊。
創建高亮基礎結構
現在我們將使用 NetBeans API 來向 C/C++ 編輯器添加高亮顯示。
創建高亮提供者
在標記實例項目的“源包 ”節點中右鍵單擊包 org.netbeans.modules.markoccurrences,然後選擇“ 新建”>“Java 類”。
將新類命名為 MarkOccurrencesHighlighter 並單擊“完成”。
用下面的代碼替換新 類中的代碼:package org.netbeans.modules.markoccurrences;
本類尚未 提供任何智能功能。它只是向光標事件注冊了一個偵聽器,並高亮顯示文檔開頭的符號。
import java.awt.Color;
import java.lang.ref.WeakReference;
import javax.swing.JEditorPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.modules.cnd.modelutil.CsmUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.cookies.EditorCookie;
import org.openide.loaders.DataObject;
public class MarkOccurrencesHighlighter implements CaretListener {
private static final AttributeSet defaultColors = AttributesUtilities.createImmutable(StyleConstants.Background, new Color(236, 235, 163));
public void caretUpdate(CaretEvent e) {
bag.clear();
bag.addHighlight(0, 5, defaultColors);
}
private final WeakReference<Document> weakDoc;
public MarkOccurrencesHighlighter(Document doc) {
bag = new OffsetsBag(doc);
weakDoc = new WeakReference<Document> ((Document) doc);
DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
JEditorPane[] panes = CsmUtilities.getOpenedPanesInEQ(dobj.getCookie (EditorCookie.class));
if (panes != null && panes.length > 0) {
panes[0].addCaretListener(this);
}
}
private final OffsetsBag bag;
public OffsetsBag getHighlightsBag() {
return bag;
}
}
創建並注冊 HighlightsLayerFactory
現在我們創建 HighlightsLayerFactory ,來讓 NetBeans 知道我們的高亮顯示提供者。
向項目源文件添加一個新的 Java 類,並將其命名為 MarkOccurrencesHighlightsLayerFactory。
用下面的代碼替 換新類中的代碼:package org.netbeans.modules.markoccurrences;
import javax.swing.text.Document;
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
import org.netbeans.spi.editor.highlighting.ZOrder;
public class MarkOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory {
public static MarkOccurrencesHighlighter getMarkOccurrencesHighlighter(Document doc) {
MarkOccurrencesHighlighter highlighter = (MarkOccurrencesHighlighter) doc.getProperty(MarkOccurrencesHighlighter.class);
if (highlighter == null) {
doc.putProperty (MarkOccurrencesHighlighter.class, highlighter = new MarkOccurrencesHighlighter(doc));
}
return highlighter;
}
public HighlightsLayer[] createLayers(Context context) {
return new HighlightsLayer[] {
HighlightsLayer.create(
MarkOccurrencesHighlighter.class.getName(),
ZOrder.CARET_RACK.forPosition(2000),
true,
getMarkOccurrencesHighlighter(context.getDocument ()).getHighlightsBag())
};
}
}
我們已 經提供了一個 HighlightsLayerFactory 的實現,它用 MarkOccurrencesHighlighte類提 供的數據創建了一個高亮顯示層。現在我們需要在 layer.xml 中注冊這個類。打開 org.netbeans.modules.markoccurrences 包中的 layer.xml,並將其內容更改為以下內 容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem- 1_1.dtd">
<filesystem>
<folder name="Editors">
<folder name="text">
<folder name="x- c++">
<file name="org-netbeans-modules- markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" />
</folder>
<folder name="x- c">
<file name="org-netbeans-modules- markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" />
</folder>
</folder>
</folder>
</filesystem>
現在我們已經准備好第一次 運行我們的高亮顯示器了。
生成項目。
項目成功生成後,運行它。
打開我們在前一節中創建的 Args 項目。
在編輯器中打開 args.c 文件, 並單擊文件中的任一位置。高亮顯示看起來就會像下面的例子一樣:
太棒 了。我們的高亮顯示工作了。現在我們來教它變得更有用。
從 C/C++ 語言模型收 集信息
在 MarkOccurrencesHighlighter.java 類中,刪除我們粗略實現的 caretUpdate() ,並添加以下代碼:private WeakReference<CsmFile> weakFile;
public void caretUpdate(CaretEvent e) {
bag.clear ();
CsmFile file = getCsmFile();
if (file != null) {
CsmReference ref = CsmReferenceResolver.getDefault().findReference(file, e.getDot());
if (ref != null && ref.getReferencedObject() != null) {
Collection<CsmReference> out = CsmReferenceRepository.getDefault().getReferences(ref.getReferencedObject(), file, true);
for (CsmReference csmReference : out) {
bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset(), defaultColors);
}
}
}
}
private CsmFile getCsmFile() {
if (weakFile == null || weakFile.get() == null) {
if (weakDoc == null || weakDoc.get() == null) {
return null;
}
DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get ());
CsmFile file = CsmUtilities.getCsmFile(dobj, false);
if (file != null) {
weakFile = new WeakReference<CsmFile>(file);
} else {
return null;
}
}
return weakFile.get();
}
在 caretUpdate() 方法中,我們使用 CsmReferenceResolver 來查找光標下對語 言實體的引用。如果存在有效的實體,我們就向 CsmReferenceRepository 詢問文件中所 有相同實體的出現位置並存儲它們的偏移量。getCsmFile() 方法是一段銜接代碼,用來 確保我們不保留任何語言模型數據。
按下 Ctrl-Shift-I 以修復導入(或者右鍵 單擊,然後選擇“修復導入”)。
生成並運行項目。
如果把鼠 標放到 main() 的 argc 參數上,您就將看到如下的高亮顯示:
單擊 文件的不同位置以查看標記實例是怎樣工作的。您也許想嘗試更復雜的項目中以查看它怎 樣與類、宏等等協同工作。
提高性能
對靜態文本來說,我們當前的代碼足夠好 了,但將在編輯文件的過程中產生嚴重的延遲。出現延遲的原因是我們每按下一個鍵,就 立即開始搜索。要解決這個問題,我們將推遲任務以分析代碼,如果鼠標位置在任務開始 前改變了,我們就取消並重新計劃任務。
在 MarkOccurrencesHighlighter.java 類中,把先前的 caretUpdate() 實現更改為以下代碼:
public void caretUpdate(CaretEvent e) {
bag.clear();
lastCaret = e.getDot();
scheduleUpdate();
}
private int lastCaret;
private RequestProcessor.Task task = null;
private final static int DELAY = 1000;
public void scheduleUpdate() {
if (task==null) {
task = RequestProcessor.getDefault().create(new Runnable() {
public void run() {
CsmFile file = getCsmFile();
if (file != null) {
CsmReference ref = CsmReferenceResolver.getDefault().findReference (file, lastCaret);
if (ref!=null && ref.getReferencedObject()!=null) {
Collection<CsmReference> out = CsmReferenceRepository.getDefault ().getReferences(ref.getReferencedObject(), file, true);
for (CsmReference csmReference : out) {
bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset (), defaultColors);
}
}
}
}
}, true);
task.setPriority(Thread.MIN_PRIORITY);
}
task.cancel();
task.schedule(DELAY);
}
在這個代碼塊中,我們使用 org.openide.util.RequestProcessor 來處理代碼分析任務。如果得到幾個光標更新,我 們就取消先前的任務,記住鼠標位置,然後在稍後的時間重新計劃任務。
修復導 入,然後生成並運行項目。
現在您將注意到,在鍵入大塊代碼時沒有延遲了。