Live a simple life

          沉默(zhu_xing@live.cn)
          隨筆 - 48, 文章 - 0, 評(píng)論 - 132, 引用 - 0
          數(shù)據(jù)加載中……

          【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動(dòng)提示

                      前面介紹的內(nèi)容集中在兩點(diǎn):StructuredTextEditor框架和WTP數(shù)據(jù)模型,在本節(jié)中就可以定制一個(gè)我們最常用的WTP StructuredTextEditor的功能,那就是自動(dòng)提示。

                      【W(wǎng)TP StructuredTextEditor提示功能實(shí)現(xiàn)分析】
                      有關(guān)Eclipse文本編輯器框架、JFace Text Framework和WTP StructuredTextEditor的簡(jiǎn)要知識(shí),參見:
                     【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(二):基于WTP StructuredTextEditor創(chuàng)建自己的JSPEditor 
                      
                      【SourceViewer提示策略配置】
                      在章節(jié)二中,我們說過如果要對(duì)一個(gè)ISourceViewer進(jìn)行自動(dòng)提示策略的定制,在ISourceViewer對(duì)應(yīng)的SourceViewerConfiguration中配置就可以了。對(duì)于WTP JSP StructuredTextEditor而言,這里的ISourceViewer就是StructuredTextViewer,這里的SourceViewerConfiguration就是StructuredTextViewerConfigurationJSP。那我們來看一下WTP StructuredTextViewerConfigurationJSP中對(duì)自動(dòng)提示策略的配置:
                     (以下代碼摘取子StructuredTextViewerConfigurationJSP類中):
          protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
                  IContentAssistProcessor[] processors 
          = null;
                  
                  //其他代碼省略......
                  
          else if ((partitionType == IXMLPartitions.XML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_COMMENT) || (partitionType == IJSPPartitions.JSP_DEFAULT) || (partitionType == IJSPPartitions.JSP_DIRECTIVE) || (partitionType == IJSPPartitions.JSP_CONTENT_DELIMITER) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVASCRIPT) || (partitionType == IJSPPartitions.JSP_COMMENT)) {
                      
          // jsp
                      processors = new IContentAssistProcessor[]{new JSPContentAssistProcessor()};
                  }
                  
          else if ((partitionType == IXMLPartitions.XML_CDATA) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVA)) {
                      
          // jsp java
                      processors = new IContentAssistProcessor[]{new JSPJavaContentAssistProcessor()};
                  }
                  //其他代碼省略......

                  
          return processors;
              }
                      以上代碼,我們可以看的出來,IContentAssistProcessor是和具體分區(qū)類型(partition type)相關(guān)聯(lián)的。想搞懂這個(gè)問題,就需要看一下這個(gè)具體分區(qū)類型(partition type)是怎么計(jì)算出來的。
                      PS:分區(qū)類型是JFace Text Framework中的概念,相關(guān)的知識(shí)大家有興趣可以進(jìn)一步去了解一下JFace Text Framework。

                      【分區(qū)類型(partition type)】
                      我們先來看一下JFace Text Framework中的基礎(chǔ)知識(shí)吧。再JFace Text Framework中有個(gè)分區(qū)劃分器的角色(org.eclipse.jface.text.IDocumentPartitioner),這個(gè)角色中一個(gè)核心操作就是判斷文檔(org.eclipse.jface.text.IDocument)中特定位置所在區(qū)域(region)的分區(qū)類型是什么,其實(shí)這里的分區(qū)類型說白了就是在一定程度上反應(yīng)了該區(qū)域(region)的內(nèi)容是什么語義性質(zhì)的^_^。    
                      我們接著看一下,WTP提供了什么樣的IDocumentPartitioner呢?
                      
                      
                     上圖中的org.eclipse.jst.jsp.core.internal.text.StructuredTextPartitionerForJSP就是我們針對(duì)jsp文件類型的分區(qū)器了,看一下相應(yīng)的實(shí)現(xiàn)代碼:    
          public String getPartitionType(ITextRegion region, int offset) {
                  String result 
          = null;
                  
          final String region_type = region.getType();
                  
          if (region_type == DOMJSPRegionContexts.JSP_CONTENT) {
                      result 
          = getPartitionTypeForDocumentLanguage();
                  }
                  
          else if (region_type == DOMJSPRegionContexts.JSP_COMMENT_TEXT || region_type == DOMJSPRegionContexts.JSP_COMMENT_OPEN || region_type == DOMJSPRegionContexts.JSP_COMMENT_CLOSE)
                      result 
          = IJSPPartitions.JSP_COMMENT;
                  
          else if (region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE)
                      result 
          =
           IJSPPartitions.JSP_DIRECTIVE
          ;
                  
          else if (region_type == DOMJSPRegionContexts.JSP_CLOSE || region_type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || region_type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || region_type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN)
                      result 
          = IJSPPartitions.JSP_CONTENT_DELIMITER;
                  
          else if (region_type == DOMJSPRegionContexts.JSP_ROOT_TAG_NAME)
                      result 
          = IJSPPartitions.JSP_DEFAULT;
                  
          else if (region_type == DOMJSPRegionContexts.JSP_EL_OPEN || region_type == DOMJSPRegionContexts.JSP_EL_CONTENT || region_type == DOMJSPRegionContexts.JSP_EL_CLOSE || region_type == DOMJSPRegionContexts.JSP_EL_DQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_SQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_QUOTED_CONTENT)
                      result 
          = IJSPPartitions.JSP_DEFAULT_EL;
                  
                      //其他代碼省略。。。
                  
          else {
                      result 
          = getEmbeddedPartitioner().getPartitionType(region, offset);
                  }
                  
          return result;
              }
                      
                      我們可以看到,對(duì)于WTP結(jié)構(gòu)化文本(當(dāng)然包括JSP)而言,分區(qū)類型(partition type)基本上是根據(jù)ITextRegion的type信息確定的。有關(guān)ITextRegion的type相關(guān)知識(shí),也是我們前面在介紹語法Document(IStructuredDocument)的時(shí)候重點(diǎn)內(nèi)容之一,忘記的話,去看一下。

                      【自動(dòng)提示流程】
                      既然在StructuredTextViewerConfigurationJSP中根據(jù)分區(qū)類型(partition type)對(duì)提示進(jìn)行了配置,那么是如何來利用這個(gè)配置的呢?
                      我們?cè)趕ource viewer中觸發(fā)提示的時(shí)候,會(huì)有一個(gè)相應(yīng)的offset信息,前面也說過IDocumentPartitioner(WTP 對(duì)應(yīng)于JSP的實(shí)現(xiàn)為StructuredTextPartitionerForJSP)提供了根據(jù)offset判斷相應(yīng)區(qū)域分區(qū)類型(partition type)的接口操作,那提示的流程也就出來了:
                      
                      
                                 上圖中的WTP StructuredTextViewerConfigurationJSP對(duì)應(yīng)于我們自己的jsp編輯器中的類型為jspeditor.configuration.JSPStructuredTextViewerConfiguration,這個(gè)我們?cè)谇懊娴诙?jié)中就定義了。

                  【定制WTP StructuredTextEditor的提示功能】
                          通過上面的自動(dòng)提示流程的分析,我們可以看的出來,如果想在我們自己的JSP編輯器中定制WTP提供的特定分區(qū)類型下的自動(dòng)提示,只要覆寫WTP StructuredTextViewerConfigurationJSP中的getContentAssistProcessors實(shí)現(xiàn),用我們自定義的IContentAssistProcessor實(shí)現(xiàn)和特定的分區(qū)類型想綁定就可以了
                         【需求】
                          1、提供針對(duì)標(biāo)簽屬性值提示的定制。
                          2、提供屬性值提示擴(kuò)展點(diǎn),允許用戶以動(dòng)態(tài)掛入的方式提供特定標(biāo)簽的屬性值提示

                          【實(shí)現(xiàn)摘要】
                          1、定義自己的IContentAssistProcessor實(shí)現(xiàn)
                              
          public class CustomizedJSPContentAssistantProcessor extends AbstractContentAssistProcessor{
              
              
          /* 
               * 定制自動(dòng)提示策略:提供自定義的屬性值提示。
               * 
               * @see org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
               
          */
              
          public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
                  
          if (!this.isOffsetValid(viewer, offset))
                      
          return new ICompletionProposal[0];
                  
                  
          //利用IModelManager獲取對(duì)應(yīng)的模型
                  IStructuredDocument structuredDocument = (IStructuredDocument)viewer.getDocument();
                  IStructuredModel structuredModel 
          = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
                  
          if (structuredModel == null)
                      
          return new ICompletionProposal[0];
                  
                  
          try {
                      
          //如果當(dāng)前offset不是位于屬性區(qū)域
                      IDOMAttr attrNode = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
                      
          if (attrNode != null)
                          
          return this.computeCustomizedCompletionProposals(viewer, offset, attrNode);
                      
          else
                          
          return new ICompletionProposal[0];
                  } 
          catch (Exception e) {
                      
          //log exception
                      IStatus status = new Status(IStatus.ERROR, "jspeditor"100"自動(dòng)提示失敗", e);
                      Activator.getDefault().getLog().log(status);
                      
                      
          return new ICompletionProposal[0];
                  } 
          finally {
                      
          //注意,削減引用計(jì)數(shù)
                      if (structuredModel != null)
                          structuredModel.releaseFromRead();
                  }
              }
              
              
          /**
               * 判斷當(dāng)前位置是否需要啟動(dòng)我們自定義的JSP標(biāo)簽屬性值自動(dòng)提示,標(biāo)準(zhǔn):
               * 1、當(dāng)前offset對(duì)應(yīng)的區(qū)域的分區(qū)類型(partition type)為IJSPPartitions.JSP_DIRECTIVE
               * 2、當(dāng)前offset對(duì)應(yīng)的text region的類型為DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE
               * 
               * 
          @param viewer
               * 
          @param offset
               * 
          @return
               
          */
              
          private boolean isOffsetValid(ITextViewer viewer, int offset) {
                  
          try {
                      IStructuredDocument structuredDocument 
          = (IStructuredDocument)viewer.getDocument();
                      
                      
          //判斷分區(qū)類型
                      if (IJSPPartitions.JSP_DIRECTIVE != structuredDocument.getPartition(offset).getType()) 
                          
          return false;
                      
          //判斷葉子text region對(duì)應(yīng)的region type信息
                      ITextRegion textRegion = StructuredDocumentUtil.getTextRegion(structuredDocument, offset);
                      
          return DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE == textRegion.getType();
                  } 
          catch (Exception e) {
                      
          return false;
                  }
              }
              
              
          /**
               * 自定義提示結(jié)果:自定義屬性值提示
               * 
               * 
          @param viewer
               * 
          @param offset
               * 
          @param attrNode
               * 
          @return
               * 
          @throws Exception
               
          */
              
          private ICompletionProposal[] computeCustomizedCompletionProposals(ITextViewer viewer, int offset, IDOMAttr attrNode) throws Exception{
                  
          //準(zhǔn)備上下文數(shù)據(jù)
                  String tagName = attrNode.getOwnerElement().getNodeName();
                  String attrbuteName 
          = attrNode.getName();
                  String inputText 
          = attrNode.getStructuredDocument().get(attrNode.getValueRegionStartOffset() + 1, offset - attrNode.getValueRegionStartOffset() - 1);
                  
                  
          //獲取相應(yīng)通過擴(kuò)展點(diǎn)注冊(cè)的屬性值提示擴(kuò)展
                  IAssistantContributor contributor = AssistantContributorManager.getInstance().getAssistantContributor(tagName);
                  
          return contributor.computeProposals(attrbuteName, inputText, viewer, offset);
              }
              
          }
             
                        關(guān)于細(xì)節(jié)實(shí)現(xiàn)暫且不說,后面會(huì)附上相應(yīng)的源碼。目前只需要了解大致的算法流程就可以:1、如果offset位于特定的JSP標(biāo)簽屬性值范圍內(nèi),則去獲取對(duì)應(yīng)的屬性值自定義提示。

                      2、將自定義的IContentAssistProcessor配置到自定義的SourceViewerConfiguration中。再配置之前,我們首先看一下如果offset位于一個(gè)屬性值的ITextRegion中,那么分區(qū)類型(partition type)是怎樣的。回過頭看一下,上面WTP StructuredTextPartitionerForJSP代碼黑體加粗部分,我們的屬性值區(qū)域?qū)?yīng)的分區(qū)類型為org.eclipse.jst.jsp.JSP_DIRECTIVE(IJSPPartitions.JSP_DIRECTIVE)。所以在我們自定義的JSPStructuredTextViewerConfiguration中覆寫WTP提供的StructuredTextViewerConfigurationJSP中的相應(yīng)方法:
          public class JSPStructuredTextViewerConfiguration extends
                  StructuredTextViewerConfigurationJSP {
              
          /* 
               * 
               * 
               * @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getContentAssistProcessors(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
               
          */
              
          protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
                  
          //我們目前只自定義JSP標(biāo)簽屬性值自動(dòng)提示的情況
                  if (partitionType == IJSPPartitions.JSP_DIRECTIVE) {
                      
          return new IContentAssistProcessor[]{new CustomizedJSPContentAssistantProcessor(), new JSPContentAssistProcessor()};
                  }
                  
                  
          return super.getContentAssistProcessors(sourceViewer, partitionType);
              }
          }
                          由于我們只想定制JSP標(biāo)簽的屬性值提示,所以我們將我們自定義的Content Assistant Processor配置為和IJSPPartitions.JSP_DIRECTIVE分區(qū)類型相綁定。

                          說明:如果你想定制類型的自動(dòng)提示呢? 前面已經(jīng)說過了^_^

                      3、定義相關(guān)抽象接口
                            由于針對(duì)不同JSP標(biāo)簽的屬性值自動(dòng)提示的邏輯是不同的,因?yàn)樘崾镜膬?nèi)容相同,但是屬性值提示這一概念是一致的。所以,我們就定義了IAssistantContributor接口來代表這一抽象概念,行為的變化通過對(duì)該接口的繼承來封裝。
          public interface IAssistantContributor {
              
          /**
               * 提供屬性值內(nèi)容提示
               * 
               * 
          @param attrbuteName 屬性名
               * 
          @param inputText    已輸入屬性值
               * 
          @param viewer       structured text viewer
               * 
          @param offset       光標(biāo)位置
               * 
          @return
               
          */
              
          public ICompletionProposal[] computeProposals(String attributeName, String inputText, ITextViewer viewer, int offset);
          }
                          上面接口一看就知道是用的策略模式的手法,我在博客的前面的文章中說明了在使用策略模式時(shí)候的注意點(diǎn),其中重點(diǎn)之一就是要關(guān)注上下文信息。  我們?cè)诒窘涌谥刑峁┝藢傩悦╝ttributeName)和用戶已經(jīng)輸入的屬性值(inputText),這兩個(gè)上下文信息對(duì)于一般的簡(jiǎn)單提示情況下已經(jīng)足夠了。但是,也有可能有比較為復(fù)雜的情況,例如要處理嵌套標(biāo)簽或者標(biāo)簽之間有依賴等等情況,通過viewer參數(shù)可以獲取到對(duì)應(yīng)的IStructuredDocument和IStructuredModel,在加上offset信息,用戶可以利用這兩個(gè)信息自己去分析出進(jìn)一步的上下文信息^_^。

                         PS:我們?cè)诖a中針對(duì)IAssistantContributor接口提供了一個(gè)默認(rèn)適配器類,針對(duì)的也就是簡(jiǎn)單的提示情況,即只提供屬性名(attributeName)和用戶已經(jīng)輸入的屬性值(inputText)就可以完成提示的情況了。
                          
          public abstract class AbstractAssistantContributor implements
                  IAssistantContributor {
              
              
          /* 
               * 子類可以覆寫,提供較為簡(jiǎn)單的模版方法,分為計(jì)算替代字符串和構(gòu)建completion proposals兩步。
               * 
               * @see jspeditor.assist.contributor.IAssistantContributor#computeProposals(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
               
          */
              
          public ICompletionProposal[] computeProposals(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
                  String[] replaceStrings 
          = this.computeReplaceStrings(attrbuteName, inputText, viewer, offset);
                  
          return this.buildCompletionProposals(replaceStrings, inputText, viewer, offset);
              } 
              
              
          /**
               * 子類可以覆寫.
               * 說明:如果需要提供自定義的display string、image等信息,請(qǐng)直接覆寫computeProposals方法。
               * 
               * 
          @param attrbuteName
               * 
          @param inputText
               * 
          @return
               
          */
              
          protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
                  
          return new String[0];
              }
              
              
          protected final IDOMAttr getDOMAttr(ITextViewer viewer, int offset) {
                  IStructuredDocument structuredDocument 
          = (IStructuredDocument)viewer.getDocument();
                  IStructuredModel structuredModel 
          = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
                  
                  IDOMAttr attr 
          = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
                  
                  structuredModel.releaseFromRead();
                  
          return attr;
              }
              

              
          /**
               * 構(gòu)造ICompletionProposal實(shí)例,只提供簡(jiǎn)單的ICompletionProposal實(shí)例
               * 
               * 
          @param replaceStrings
               * 
          @param inputText
               * 
          @param viewer
               * 
          @param offset
               * 
          @return
               
          */
              
          protected ICompletionProposal[] buildCompletionProposals(String[] replaceStrings, String inputText, ITextViewer viewer, int offset) {
                  
          if (replaceStrings == null || replaceStrings.length == 0)
                      
          return new ICompletionProposal[0];
                  
                  
          //計(jì)算ICompletionProposal相關(guān)參數(shù)
                  IDOMAttr attrNode = getDOMAttr(viewer, offset);
                  
          int replaceOffset = attrNode.getValueRegionStartOffset() + 1;
                  
          int replaceLength = attrNode.getValueSource().length();
                  
                  List
          <ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
                  
          for (int i = 0; i < replaceStrings.length; i++) {
                      
          //根據(jù)用戶輸入執(zhí)行過濾
                      if (replaceStrings[i].toLowerCase().startsWith(inputText.trim().toLowerCase())) {
                          
          int cursorPosition = replaceStrings[i].length();
                          ICompletionProposal proposal 
          = new CompletionProposal(replaceStrings[i], replaceOffset, replaceLength, cursorPosition);
                          proposals.add(proposal);
                      }
                  }
                  
          return proposals.toArray(new ICompletionProposal[proposals.size()]);
              }
          }
                      可以看的出來,我們這個(gè)默認(rèn)適配器類簡(jiǎn)單的應(yīng)用了模版方法的手法去處理簡(jiǎn)單情景下的自動(dòng)提示。
                      
                    4、定義屬性值自動(dòng)提示擴(kuò)展點(diǎn)
                      由于JSP默認(rèn)提供的一些標(biāo)簽實(shí)際業(yè)務(wù)意義不強(qiáng),而我們自己在開發(fā)應(yīng)用的時(shí)候,往往會(huì)不斷提供有業(yè)務(wù)意義的標(biāo)簽供用戶使用,所以我們假設(shè)我們要處理的屬性值自動(dòng)提示需要允許后續(xù)開發(fā)人員以動(dòng)態(tài)開發(fā)的方式掛入,支持方便擴(kuò)展。我們想到了擴(kuò)展點(diǎn)機(jī)制^_^。
                       
                      上面的擴(kuò)展點(diǎn)定義其實(shí)很簡(jiǎn)單,就是針對(duì)特定的JSP tag id(也就是tag名稱,這是唯一的)提供一個(gè) IAssistantContributor接口的實(shí)現(xiàn)。
                      
                        我們針對(duì)用戶掛入的擴(kuò)展提供了一個(gè)管理器AssistantContributorManager,能夠支持以tag id獲取對(duì)應(yīng)的IAssistantContributor實(shí)現(xiàn),看一下上面我們定義的content assistant processor實(shí)現(xiàn)中黑體部分代碼就利用了該manager實(shí)例。具體請(qǐng)?jiān)诟郊创a中看吧^_^

                      5、示例
                       我們首先提供一個(gè)非常簡(jiǎn)單的tld(test.tld),里面包含了一個(gè)簡(jiǎn)單的測(cè)試標(biāo)簽test,如下
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
          <taglib>
              
          <tlibversion>1.0</tlibversion>
              
          <jspversion>1.0</jspversion>
              
          <shortname>test</shortname>
              
          <uri>http://www.aygfsteel.com/zhuxing/tags/test</uri>
              <tag>
                  
          <name>test</name>
                  
          <tagclass>any</tagclass>
                  
          <bodycontent>empty</bodycontent>
                  
          <attribute>
                      
          <name>scope</name>
                      
          <required>true</required>
                      
          <rtexprvalue>true</rtexprvalue>
                  
          </attribute>
              
          </tag>
          </taglib>
                       我們?cè)谄渲卸x了一個(gè)scope屬性,下面我們就以擴(kuò)展點(diǎn)的方式掛入我們的擴(kuò)展。
                      
                       首先實(shí)現(xiàn)提供一個(gè)IAssistantContributor接口實(shí)現(xiàn):
          public class TestTagAssistantContributor extends AbstractAssistantContributor {
              
          /**
               * scope attribute name
               
          */
              
          private static final String ATTR_NAME_SCOPE = "scope";
              
              
          /**
               * scope attribute value
               
          */
              
          private static final String[] ATTR_VALUE_SCOPE = {"request""session"};
              
              
          /* (non-Javadoc)
               * @see jspeditor.assist.contributor.AbstractAssistantContributor#computeReplaceStrings(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
               
          */
              
          protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
                  
          if (ATTR_NAME_SCOPE.equals(attrbuteName)) {
                      
          return ATTR_VALUE_SCOPE;
                  }
                  
                  
          return new String[0];
              }

          }
                      通過上面簡(jiǎn)單的代碼可以看的出來,我們的提示邏輯很簡(jiǎn)單,如果是scope屬性,就提供“requst”和“session”兩種選擇。

                      下面,我們將我們提供的針對(duì)test標(biāo)簽的屬性值提示擴(kuò)展掛入:  

                      6、效果演示

                      看到了嗎,我們?yōu)閠est標(biāo)簽掛入了自動(dòng)提示實(shí)現(xiàn)之后,還真的提示了哈^_^            
                      
                      【后記】
                        其實(shí)本節(jié)中的內(nèi)容為定制自動(dòng)提示提供了一個(gè)完整解決方案的雛形^_^               

                     【源碼下載】

                        源碼下載(自動(dòng)提示定制)

          本博客中的所有文章、隨筆除了標(biāo)題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請(qǐng)注明出處,謝謝!

          posted on 2008-09-22 18:17 zhuxing 閱讀(3515) 評(píng)論(4)  編輯  收藏 所屬分類: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

          評(píng)論

          # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動(dòng)提示  回復(fù)  更多評(píng)論   

          下班了,先把源碼附上,里面有測(cè)試用test.tld
          2008-09-22 18:36 | zhuxing

          # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動(dòng)提示  回復(fù)  更多評(píng)論   

          期待作者的下文
          2008-09-22 21:54 | 冷月

          # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動(dòng)提示  回復(fù)  更多評(píng)論   

          內(nèi)容已經(jīng)補(bǔ)充完畢,并更新了代碼,希望對(duì)大家有幫助

          下一節(jié)內(nèi)容:為我們的編輯器配置自定義即時(shí)校驗(yàn)
          2008-09-23 15:42 | zhuxing

          # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(八):定制StructuredTextEditor自動(dòng)提示  回復(fù)  更多評(píng)論   

          我喜歡這篇, zhuxing 哥,這兩天我要好好看看。
          2008-09-24 17:28 | srdrm
          主站蜘蛛池模板: 宁河县| 承德县| 济宁市| 玛沁县| 佛冈县| 小金县| 延寿县| 汕尾市| 九寨沟县| 澎湖县| 红桥区| 九台市| 永川市| 定西市| 桦甸市| 辽宁省| 江西省| 青浦区| 胶州市| 九龙县| 封开县| 修文县| 内乡县| 奉化市| 博乐市| 江孜县| 青阳县| 昌图县| 凤冈县| 赣榆县| 兴业县| 广河县| 平远县| 手游| 大悟县| 南江县| 合作市| 隆德县| 青岛市| 湘阴县| 井研县|