Eclipse provides a nice framework to use spell checking in dialog and editors. Chris has tipped about it
here. However, if you are using a custom editor then you have to put few pieces together yourself. Consider an XML editor. We would want the spelling check only for the string in attribute values and not the XML Tags.
Let us try and implement this. We can start with the XML editor template provided by PDE.
1. Create a new plug-in using "Plug-in with an Editor" template.
New Plug-in Wizard
2. This will create a simple XML Editor by extending
TextEditor.
3. The XMLEditor uses XMLConfiguration to set the source viewer configuration.
public XMLEditor() {
super();
colorManager = new ColorManager();
setSourceViewerConfiguration(new XMLConfiguration(colorManager));
setDocumentProvider(new XMLDocumentProvider());
}
Modify the constructor for XMLConfiguration and pass the preference store. It will be needed by its super class.
public XMLEditor() {
super();
colorManager = new ColorManager();
setSourceViewerConfiguration(new XMLConfiguration(colorManager, getPreferenceStore()));
setDocumentProvider(new XMLDocumentProvider());
}
4. The XMLConfiguration basically hooks various listeners, providers and assistants to the Editor. The template generated code will have it sub-classed from
SourceViewerConfiguration. Change it to
TextSourceViewerConfiguration. The TextSourceViewerConfiguration provides out of the box spell checker, spelling annotation, problem hover text, quick fixes, etc.
public class XMLConfiguration extends TextSourceViewerConfiguration { //SourceViewerConfiguration {
...
public XMLConfiguration(ColorManager colorManager, IPreferenceStore preferenceStore) {
super(preferenceStore);
this.colorManager = colorManager;
}
...
The preferenceStore will be used for checking dictionary and other preferences. They can be set from Window > Preferences > General > Editors > Text Editors > Spelling .
5. Override the getReconciler function. The implementation for
getReconciler provided by TextSourceViewerConfiguration uses a
SpellingReconcileStrategy. The SpellingReconcileStrategy will spell check the whole document. Since its an XML Editor, only quoted strings should be spell checked and the XML tags should be skipped.
/**
* Providing custom XMLSpellingReconcileStrategy to enable spell checking for only strings
*/
/* (non-Javadoc)
* @see org.eclipse.jface.text.source.SourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer)
*/
public IReconciler getReconciler(ISourceViewer sourceViewer) {
XMLSpellingReconcileStrategy strategy = new XMLSpellingReconcileStrategy(sourceViewer, EditorsUI.getSpellingService());
Reconciler reconciler = new Reconciler();
reconciler.setReconcilingStrategy(strategy, XMLStringPartitionScanner.XML_STRING);
reconciler.setDocumentPartitioning(XMLStringPartitionScanner.XML_STRING);
return reconciler;
}
6. XMLStringPartitionScanner will be our custom document partition scanner to identify the quoted string. The template generated code will make use of XMLPartitionScanner. This class specifies the rules for identifying the XML tags and comments. Since, the quoted string are part of XML Tag only, we can not use this partition scanner. Therefore, we write our custom document partition scanner and at supply it to the reconcile strategy.
public class XMLStringPartitionScanner extends RuleBasedPartitionScanner {
public final static String XML_STRING = "__xml_string";
public XMLStringPartitionScanner() {
IToken stringToken = new Token(XML_STRING);
IPredicateRule[] rules = new IPredicateRule[1];
rules[0] = new MultiLineRule("\"", "\"", stringToken, '\\');
setPredicateRules(rules);
}
}
7. XMLSpellingReconcileStrategy will extend SpellingReconcileStrategy since it already knows how to collect spelling problem, etc. Only
reconcile is what needs to be overridden.
public class XMLSpellingReconcileStrategy extends SpellingReconcileStrategy {
public XMLSpellingReconcileStrategy(ISourceViewer sourceViewer, SpellingService spellingService) {
super(sourceViewer, spellingService);
}
...
public void reconcile(IRegion region) {
AbstractDocument document = (AbstractDocument) getDocument();
IDocumentPartitioner docPartitioner = document.getDocumentPartitioner(XMLStringPartitionScanner.XML_STRING);
IAnnotationModel model = getAnnotationModel();
if (region.getOffset() == 0 && region.getLength() == document.getLength()) {
//reconciling whole document
super.reconcile(region);
deleteUnwantedAnnotations();
} else {
//partial reconciliation
//preserve spelling annotations first
Iterator iter = model.getAnnotationIterator();
Map spellingErrors = new HashMap(1);
while (iter.hasNext()) {
Annotation annotation = (Annotation) iter.next();
if (annotation instanceof SpellingAnnotation) {
SpellingAnnotation spellingAnnotation = (SpellingAnnotation) annotation;
Position position = model.getPosition(spellingAnnotation);
String contentType = docPartitioner.getContentType(position.getOffset());
if (XMLStringPartitionScanner.XML_STRING.equalsIgnoreCase(contentType)) {
spellingErrors.put(spellingAnnotation, model.getPosition(annotation));
}
}
}
//reconcile
super.reconcile(region);
//restore annotations
model = getAnnotationModel();
iter = spellingErrors.keySet().iterator();
while (iter.hasNext()) {
Annotation annotation = (Annotation) iter.next();
model.addAnnotation(annotation, (Position) spellingErrors.get(annotation));
}
deleteUnwantedAnnotations();
}
}
/**
* Deletes the spelling annotations marked for XML Tags
*/
private void deleteUnwantedAnnotations() {
AbstractDocument document = (AbstractDocument) getDocument();
IDocumentPartitioner docPartitioner = document.getDocumentPartitioner(XMLStringPartitionScanner.XML_STRING);
IAnnotationModel model = getAnnotationModel();
Iterator iter = model.getAnnotationIterator();
while (iter.hasNext()) {
Annotation annotation = (Annotation) iter.next();
if (annotation instanceof SpellingAnnotation) {
SpellingAnnotation spellingAnnotation = (SpellingAnnotation) annotation;
Position position = model.getPosition(spellingAnnotation);
String contentType = docPartitioner.getContentType(position.getOffset());
if (!XMLStringPartitionScanner.XML_STRING.equalsIgnoreCase(contentType)) {
model.removeAnnotation(spellingAnnotation);
}
}
}
}
}
8. Putting it all together
The XMLStringPartitionScanner is our document partition scanner. It uses the
MultiLineRule to locate the quoted string. XMLDocumentProvider adds it to the XML document.
public class XMLDocumentProvider extends FileDocumentProvider {
protected IDocument createDocument(Object element) throws CoreException {
IDocument document = super.createDocument(element);
if (document != null) {
IDocumentPartitioner partitioner = new FastPartitioner(new XMLPartitionScanner(), new String[] {XMLPartitionScanner.XML_TAG, XMLPartitionScanner.XML_COMMENT});
partitioner.connect(document);
document.setDocumentPartitioner(partitioner);
//Adding our string partition scanner to the document
partitioner = new FastPartitioner(new XMLStringPartitionScanner(), new String[] {XMLStringPartitionScanner.XML_STRING});
partitioner.connect(document);
((AbstractDocument) document).setDocumentPartitioner(XMLStringPartitionScanner.XML_STRING, partitioner);
}
return document;
}
}
XMLPartitionScanner is generated by the template and it locates XML tags and comments. Since quoted strings are part of tags, we added XMLStringPartitionScanner too.
9. After thoughts
All the infrastructure is already in place for enabling spell checking. But custom editors need some extra wiring. If finer control is needed on the quick fix assistant override
getQuickAssistAssistant function and for controlling the hover message
getTextHover will do the trick.
10. Source code
The archived project can be downloaded from
here.
11. Disclaimer :-)
This code doesn't cover all the scenarios as it is just an example. These things I figured out while fixing the
bug #286203. Feel free to add your suggestions and point out any mistakes you see.Eclipse provides a nice framework to use spell checking in dialog and editors. Chris has tipped about it here. However, if you are using a custom editor then you have to put few pieces together yourself. Consider an XML editor. We would want the spelling check only for the string in attribute values and not the XML Tags.