package tk.eclipse.plugin.htmleditor.editors;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextDoubleClickStrategy;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import tk.eclipse.plugin.htmleditor.ColorProvider;
import tk.eclipse.plugin.htmleditor.HTMLHyperlinkDetector;
import tk.eclipse.plugin.htmleditor.HTMLPlugin;
import tk.eclipse.plugin.htmleditor.assist.HTMLAssistProcessor;
import tk.eclipse.plugin.htmleditor.assist.InnerCSSAssistProcessor;
import tk.eclipse.plugin.htmleditor.assist.InnerJavaScriptAssistProcessor;
/**
* <code>SourceViewerConfiguration</code> for <code>HTMLSourceEditor</code>.
*
* @author Naoki Takezoe
* @see HTMLSourceEditor
*/
public class HTMLConfiguration extends SourceViewerConfiguration {
private HTMLDoubleClickStrategy _doubleClickStrategy;
private HTMLScanner _scanner;
private HTMLTagScanner _tagScanner;
private RuleBasedScanner _commentScanner;
private RuleBasedScanner _scriptScanner;
private RuleBasedScanner _doctypeScanner;
private RuleBasedScanner _directiveScanner;
private RuleBasedScanner _javaScriptScanner;
private RuleBasedScanner _cssScanner;
private ColorProvider _colorProvider;
private IEditorPart _editor;
private ContentAssistant _assistant;
private HTMLAssistProcessor _processor;
private InnerJavaScriptAssistProcessor _jsProcessor;
private InnerCSSAssistProcessor _cssProcessor;
private HTMLAutoEditStrategy _autoEditStrategy;
private HTMLHyperlinkDetector _hyperlinkDetector;
public HTMLConfiguration(ColorProvider colorProvider) {
this._colorProvider = colorProvider;
}
public IEditorPart getEditorPart() {
return _editor;
}
public void setEditorPart(IEditorPart editor){
this._editor = editor;
}
@Override
public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
return new HTMLAnnotationHover(_editor);
}
/**
* @since 2.0,3
*/
protected HTMLAutoEditStrategy createAutoEditStrategy(){
return new HTMLAutoEditStrategy();
}
/**
* @since 2.0.3
*/
protected HTMLHyperlinkDetector createHyperlinkDetector(){
return new HTMLHyperlinkDetector();
}
/**
* @since 2.0.3
*/
public final HTMLHyperlinkDetector getHyperlinkDetector(){
if(this._hyperlinkDetector==null){
this._hyperlinkDetector = createHyperlinkDetector();
}
return this._hyperlinkDetector;
}
/**
* @since 2.0.3
*/
public final HTMLAutoEditStrategy getAutoEditStrategy(){
if(this._autoEditStrategy==null){
this._autoEditStrategy = createAutoEditStrategy();
}
return this._autoEditStrategy;
}
/**
* Returns all supportted content types.
*/
@Override
public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
return new String[] {
IDocument.DEFAULT_CONTENT_TYPE,
HTMLPartitionScanner.HTML_COMMENT,
HTMLPartitionScanner.HTML_TAG,
HTMLPartitionScanner.HTML_SCRIPT,
HTMLPartitionScanner.HTML_DOCTYPE,
HTMLPartitionScanner.HTML_DIRECTIVE,
HTMLPartitionScanner.JAVASCRIPT,
HTMLPartitionScanner.HTML_CSS};
}
/**
* @since 2.0.3
*/
@Override
public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
if (sourceViewer == null){
return null;
}
IHyperlinkDetector[] detectors = super.getHyperlinkDetectors(sourceViewer);
IHyperlinkDetector[] result = new IHyperlinkDetector[detectors.length + 1];
System.arraycopy(detectors, 0, result, 0, detectors.length);
result[result.length-1] = getHyperlinkDetector();
return result;
}
/**
* @since 2.0.3
*/
@Override
public final IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
return new IAutoEditStrategy[]{ createAutoEditStrategy() };
}
/**
* Creates or Returns the assist processor for HTML.
*/
public HTMLAssistProcessor getAssistProcessor(){
if(_processor==null){
_processor = createAssistProcessor();
}
return _processor;
}
/**
* Creates the assist processor for HTML.
*/
protected HTMLAssistProcessor createAssistProcessor(){
HTMLAssistProcessor processor = new HTMLAssistProcessor();
return processor;
}
/**
* Creates or Returns the assist processor for inner JavaScript.
*/
public InnerJavaScriptAssistProcessor getJavaScriptAssistProcessor(){
if(_jsProcessor==null){
_jsProcessor = new InnerJavaScriptAssistProcessor(getAssistProcessor());
}
return _jsProcessor;
}
/**
* Creates or Returns the assist processor for inner JavaScript.
*/
public InnerCSSAssistProcessor getCSSAssistProcessor(){
if(_cssProcessor==null){
_cssProcessor = new InnerCSSAssistProcessor(getAssistProcessor());
}
return _cssProcessor;
}
/**
* Creates or Returns the <code>IContentAssistant</code>.
*/
@Override
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
if(_assistant==null){
_assistant = new ContentAssistant();
_assistant.setInformationControlCreator(new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent);
}});
_assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE);
_assistant.enableAutoInsert(true);
HTMLAssistProcessor processor = getAssistProcessor();
_assistant.setContentAssistProcessor(processor,IDocument.DEFAULT_CONTENT_TYPE);
_assistant.setContentAssistProcessor(processor,HTMLPartitionScanner.HTML_TAG);
InnerJavaScriptAssistProcessor jsProcessor = getJavaScriptAssistProcessor();
_assistant.setContentAssistProcessor(jsProcessor,HTMLPartitionScanner.JAVASCRIPT);
InnerCSSAssistProcessor cssProcessor = getCSSAssistProcessor();
_assistant.setContentAssistProcessor(cssProcessor,HTMLPartitionScanner.HTML_CSS);
_assistant.enableColoredLabels(true);
_assistant.install(sourceViewer);
IPreferenceStore store = HTMLPlugin.getDefault().getPreferenceStore();
_assistant.enableAutoActivation(store.getBoolean(HTMLPlugin.PREF_ASSIST_AUTO));
_assistant.setAutoActivationDelay(store.getInt(HTMLPlugin.PREF_ASSIST_TIMES));
processor.setAutoAssistChars(store.getString(HTMLPlugin.PREF_ASSIST_CHARS).toCharArray());
processor.setAssistCloseTag(store.getBoolean(HTMLPlugin.PREF_ASSIST_CLOSE));
}
return _assistant;
}
/**
* Returns <code>ITextDoubleClickStrategy</code>.
*/
@Override
public ITextDoubleClickStrategy getDoubleClickStrategy(ISourceViewer sourceViewer,String contentType) {
if (_doubleClickStrategy == null){
_doubleClickStrategy = new HTMLDoubleClickStrategy();
}
return _doubleClickStrategy;
}
/**
* Creates or Returns the scanner for HTML.
*/
protected HTMLScanner getHTMLScanner() {
if (_scanner == null) {
_scanner = new HTMLScanner(_colorProvider);
_scanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_FG));
}
return _scanner;
}
/**
* Creates or Returns the scanner for HTML tags.
*/
protected HTMLTagScanner getTagScanner() {
if (_tagScanner == null) {
_tagScanner = new HTMLTagScanner(_colorProvider);
_tagScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_TAG));
}
return _tagScanner;
}
/**
* Creates or Returns the scanner for HTML comments.
*/
protected RuleBasedScanner getCommentScanner() {
if (_commentScanner == null) {
_commentScanner = new RuleBasedScanner();
_commentScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_COMMENT));
}
return _commentScanner;
}
/**
* Creates or Returns the scanner for scriptlets (<% ... %>).
*/
protected RuleBasedScanner getScriptScanner() {
if (_scriptScanner == null) {
_scriptScanner = new RuleBasedScanner();
_scriptScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_SCRIPT));
}
return _scriptScanner;
}
/**
* Creates or Returns the scanner for directives (<%@ ... %>).
*/
protected RuleBasedScanner getDirectiveScanner(){
if (_directiveScanner == null) {
_directiveScanner = new RuleBasedScanner();
_directiveScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_SCRIPT));
}
return _directiveScanner;
}
/**
* Creates or Returns the scanner for DOCTYPE decl.
*/
protected RuleBasedScanner getDoctypeScanner(){
if (_doctypeScanner == null) {
_doctypeScanner = new RuleBasedScanner();
_doctypeScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_DOCTYPE));
}
return _doctypeScanner;
}
/**
* Creates or Returns the scanner for inner JavaScript.
*/
protected RuleBasedScanner getJavaScriptScanner() {
if (_javaScriptScanner == null) {
_javaScriptScanner = new InnerJavaScriptScanner(_colorProvider);
_javaScriptScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_FG));
}
return _javaScriptScanner;
}
/**
* Creates or Returns the scanner for inner CSS.
*/
protected RuleBasedScanner getCSSScanner() {
if (_cssScanner == null) {
_cssScanner = new InnerCSSScanner(_colorProvider);
_cssScanner.setDefaultReturnToken(
_colorProvider.getToken(HTMLPlugin.PREF_COLOR_FG));
}
return _cssScanner;
}
@Override
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
PresentationReconciler reconciler = new PresentationReconciler();
DefaultDamagerRepairer dr = null;
dr = new HTMLTagDamagerRepairer(getTagScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_TAG);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_TAG);
dr = new HTMLTagDamagerRepairer(getHTMLScanner());
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
dr = new HTMLTagDamagerRepairer(getCommentScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_COMMENT);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_COMMENT);
dr = new DefaultDamagerRepairer(getScriptScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_SCRIPT);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_SCRIPT);
dr = new DefaultDamagerRepairer(getDoctypeScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_DOCTYPE);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_DOCTYPE);
dr = new DefaultDamagerRepairer(getDirectiveScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_DIRECTIVE);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_DIRECTIVE);
dr = new JavaScriptDamagerRepairer(getJavaScriptScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.JAVASCRIPT);
reconciler.setRepairer(dr, HTMLPartitionScanner.JAVASCRIPT);
dr = new JavaScriptDamagerRepairer(getCSSScanner());
reconciler.setDamager(dr, HTMLPartitionScanner.HTML_CSS);
reconciler.setRepairer(dr, HTMLPartitionScanner.HTML_CSS);
return reconciler;
}
/**
* Returns the <code>ColorProvider</code>.
*/
protected ColorProvider getColorProvider(){
return this._colorProvider;
}
private class HTMLTagDamagerRepairer extends DefaultDamagerRepairer {
public HTMLTagDamagerRepairer(ITokenScanner scanner) {
super(scanner);
}
// TODO This method works with 3.0 and 3.1.2 but does't work well with Eclipse 3.1.1.
@Override
public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent e, boolean documentPartitioningChanged) {
if (!documentPartitioningChanged) {
String source = fDocument.get();
int start = source.substring(0, e.getOffset()).lastIndexOf('<');
if(start == -1){
start = 0;
}
int end = source.indexOf('>', e.getOffset());
int nextEnd = source.indexOf('>', end + 1);
if(nextEnd >= 0 && nextEnd > end){
end = nextEnd;
}
int end2 = e.getOffset() + (e.getText() == null ? e.getLength() : e.getText().length());
if(end == -1){
end = source.length();
} else if(end2 > end){
end = end2;
} else {
end++;
}
return new Region(start, end - start);
}
return partition;
}
}
private class JavaScriptDamagerRepairer extends DefaultDamagerRepairer {
public JavaScriptDamagerRepairer(ITokenScanner scanner) {
super(scanner);
}
// TODO This method works with 3.0 and 3.1.2 but does't work well with Eclipse 3.1.1.
@Override
public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent e, boolean documentPartitioningChanged) {
if (!documentPartitioningChanged) {
String source = fDocument.get();
int start = source.substring(0, e.getOffset()).lastIndexOf("/*");
if(start == -1){
start = 0;
}
int end = source.indexOf("*/", e.getOffset());
int end2 = e.getOffset() + (e.getText() == null ? e.getLength() : e.getText().length());
if(end == -1){
end = source.length();
} else if(end2 > end){
end = end2;
} else {
end++;
}
return new Region(start, end - start);
}
return partition;
}
}
}