错误标识(Error Marking)
Error Marking用来对编辑的文档根据一定的规则进行验证,比如对于XML文档来说,可能是XML DTD或者XML Schema.其实现跟内容大纲比较类似,首先在解析文档的时候对error加以标识.这里我们使用了SAX ErrorHandler来收集和定位所有的error, 接着在生成内容大纲的同时进行验证和error marking,这个工作在文档被加载和文档保存的时候都会进行.
在XMLEditor的validateAndMark()方法中完成对error marking的初始化:
- protected void validateAndMark()
- {
- IDocument document = getInputDocument();
- MarkingErrorHandler markingErrorHandler =
- new MarkingErrorHandler(getInputFile(), document);
- markingErrorHandler.removeExistingMarkers();
- XMLParser parser = new XMLParser();
- parser.setErrorHandler(markingErrorHandler);
- String text = document.get();
- parser.doParse(text);
- }
MarkingErrorHandler的实例化需要两个参数:一个是IFile实例,用来执行marking(Eclipse Marker API将通过IFile来引用底层的Resource对象),另一个是编辑的IDocument实例(用来确定插入到文档中的marker的位置)
在文档被解析之前,已有的error marker都必须先清掉, 在解析文档的时候如果发现错误,将调用MarkingErrorHandler的handleError()方法:
- protected void handleError(SAXParseException e, boolean isFatal)
- {
- int lineNumber = e.getLineNumber();
- int columnNumber = e.getColumnNumber();
- Map map = new HashMap();
- MarkerUtilities.setLineNumber(map, lineNumber);
- MarkerUtilities.setMessage(map, e.getMessage());
- map.put(IMarker.MESSAGE, e.getMessage());
- map.put(IMarker.LOCATION, file.getFullPath().toString());
- Integer charStart = getCharStart(lineNumber, columnNumber);
- if (charStart != null)
- map.put(IMarker.CHAR_START, charStart);
- Integer charEnd = getCharEnd(lineNumber, columnNumber);
- if (charEnd != null)
- map.put(IMarker.CHAR_END, charEnd);
- map.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
- try
- {
- MarkerUtilities.createMarker(file, map, ERROR_MARKER_ID);
- }
- catch (CoreException ee)
- {
- ee.printStackTrace();
- }
- }
这里我们的编辑器通过XML解析器(Xerces)不仅取得了error信息,而且还得到了发生错误的位置信息,因此上面的代码看起来非常的清晰:首先取得错误信息的行号和列号,然后使用Eclipse Marker API创建一个Error Marker
内容辅助
最后我们将要介绍的一个功能是内容辅助, 下图是我们的实现效果, 这里我们只是一个简单的实现,对于一个商业的XML编辑器来说,更强悍的就是能够根据当前光标的位置以及定义的DTD做更精确的内容辅助
为了让我们的内容辅助功能做的更智能,我们需要知道当前文档的结构以及当前光标在文档结构中的位置
跟其他功能类似,内容辅助功能也是通过SourceViewerConfiguration来提供的,下面是我们的实现代码:
- public IContentAssistant getContentAssistant(ISourceViewer sourceViewer)
- {
- ContentAssistant assistant = new ContentAssistant();
- IContentAssistProcessor tagContentAssistProcessor
- = new TagContentAssistProcessor(getXMLTagScanner());
- assistant.setContentAssistProcessor(tagContentAssistProcessor,
- XMLPartitionScanner.XML_START_TAG);
- assistant.enableAutoActivation(true);
- assistant.setAutoActivationDelay(500);
- assistant.setProposalPopupOrientation(IContentAssistant.CONTEXT_INFO_BELOW);
- assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_BELOW);
- return assistant;
- }
上面的代码比较简单,首先创建一个ContentAssistant实例,然后设置一些UI属性,这里主要注意IContentAssistProcessor的实现,我们实现的内容辅助只是针对节点,而且内容辅助也是建立在对编辑文档的分割处理的基础上.分割处理我们前面已经讲的够多了,这里我们就不再做说明
内容辅助的UI处理都在ContentAssistant中实现,一般情况下我们不需要子类化,除非当前的功能无法满足我们的要求
内容辅助的智能之处主要体现IContentAssistProcessor的实现上,而一般我们最感兴趣的就是ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset);方法,内容辅助的提示内容列表就是在该方法中提供,这里是我们的代码实现:
- public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset)
- {
- IDocument document = viewer.getDocument();
- boolean isAttribute = isAttribute(offset, document);
- TextInfo currentText = currentText(document, offset);
- if (!isAttribute)
- {
- List allElements = dtdTree.getAllElements();
- ICompletionProposal[] result = new ICompletionProposal[allElements.size()];
- int i = 0;
- for (Iterator iter = allElements.iterator(); iter.hasNext();)
- {
- XMLElement element = (XMLElement) iter.next();
- String name = element.getName();
- String text = "" + name + ">" + "</" + name + ">";
- }
- result[i++] = new CompletionProposal(text,
- currentText.documentOffset,
- currentText.text.length(),
- text.length());
- }
- return result;
- }
- else
- {
- List allAttributes = dtdTree.getAllAttributes();
- ICompletionProposal[] result = new ICompletionProposal[allAttributes.size()];
- int i = 0;
- for (Iterator iter = allAttributes.iterator(); iter.hasNext();)
- {
- String name = (String) iter.next();
- String text = name + "= \"\" ";
- result[i++] = new CompletionProposal(text,
- currentText.documentOffset,
- currentText.text.length(),
- text.length());
- }
- return result;
- }
- }
上面的代码非常的简单,首先根据当前位置是否为属性,是则列出已知的所有属性名,否则列出所有的节点名.
当然这里我们的做法非常简单,更高级的实现是对整个文档进行扫描来确定当前光标在整个文档结构中所处的位置, 然后使用DTD验证计算当前需要提示的更精确的内容列表, 这就需要根据DTD来理解我们的文档
总结
构建一个强大的文本编辑器在Eclipse插件开发中常常会碰到, 而JFace Text Editor是我们展开工作的基础, 它是Eclipse非常强大,非常重要的一套API, 同时也是非常复杂的一套API.
这里我们从Eclipse PDE提供的XML Editor向导例子入手,通过对其进行扩展, 演示了高亮显示, 内容格式化, 内容大纲, 错误标记, 内容辅助几个功能的实现, 希望这篇文章对你来实现自己强大的文本编辑器能有所帮助
安徽新华电脑学校专业职业规划师为你提供更多帮助【在线咨询】