/******************************************************************************* * Copyright (c) 2005, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.eclipse.dltk.javascript.internal.ui.text; import java.util.Map; import org.eclipse.dltk.javascript.internal.ui.text.completion.JavaScriptCompletionProcessor; import org.eclipse.dltk.javascript.internal.ui.text.completion.JavaScriptContentAssistPreference; import org.eclipse.dltk.javascript.ui.text.IJavaScriptPartitions; import org.eclipse.dltk.ui.text.AbstractScriptScanner; import org.eclipse.dltk.ui.text.IColorManager; import org.eclipse.dltk.ui.text.ScriptPresentationReconciler; import org.eclipse.dltk.ui.text.ScriptSourceViewerConfiguration; import org.eclipse.dltk.ui.text.SingleTokenScriptScanner; import org.eclipse.dltk.ui.text.TodoTaskPreferencesOnPreferenceStore; import org.eclipse.dltk.ui.text.completion.ContentAssistPreference; import org.eclipse.dltk.ui.text.completion.ContentAssistProcessor; import org.eclipse.dltk.ui.text.spelling.SpellCheckDelegate; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPartitioningException; import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension3; 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.IContentAssistProcessor; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.rules.DefaultDamagerRepairer; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.ui.texteditor.ITextEditor; public class JavascriptSourceViewerConfiguration extends ScriptSourceViewerConfiguration { private JavascriptCodeScanner fCodeScanner; private AbstractScriptScanner fStringScanner; private AbstractScriptScanner fCommentScanner; private JavascriptDocScanner fDocScanner; public JavascriptSourceViewerConfiguration(IColorManager colorManager, IPreferenceStore preferenceStore, ITextEditor editor, String partitioning) { super(colorManager, preferenceStore, editor, partitioning); } /** * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getDoubleClickStrategy(org.eclipse.jface.text.source.ISourceViewer, * java.lang.String) * @since 2.0 */ @Override public ITextDoubleClickStrategy getDoubleClickStrategy( ISourceViewer sourceViewer, String contentType) { if (IJavaScriptPartitions.JS_DOC.equals(contentType)) { return new JSDocDoubleClickStrategy( getConfiguredDocumentPartitioning(sourceViewer)); } return new DefaultTextDoubleClickStrategy() { /** * @see org.eclipse.jface.text.DefaultTextDoubleClickStrategy#findExtendedDoubleClickSelection(org.eclipse.jface.text.IDocument, * int) */ @Override protected IRegion findExtendedDoubleClickSelection( IDocument document, int offset) { int start = -1; int end = -1; String text = document.get(); if (document instanceof IDocumentExtension3) { try { String contentType = ((IDocumentExtension3) document) .getContentType( IJavaScriptPartitions.JS_PARTITIONING, offset, true); if (IJavaScriptPartitions.JS_STRING_SINGLE .equals(contentType) || IJavaScriptPartitions.JS_STRING .equals(contentType) // || IJavaScriptPartitions.JS_REGEXP // .equals(contentType) ) { ITypedRegion region = ((IDocumentExtension3) document) .getPartition( IJavaScriptPartitions.JS_PARTITIONING, offset, true); if (region != null && region.getLength() > 0) { // if reg exp return as is. // if (IJavaScriptPartitions.JS_REGEXP // .equals(contentType)) // return region; // if it is a string, strip the quotes. return new Region(region.getOffset() + 1, region.getLength() - 2); } } } catch (BadLocationException ex) { ex.printStackTrace(); } catch (BadPartitioningException ex) { ex.printStackTrace(); } } // try to find if the text just before the offset is 1 of (,[ or // { if (offset > 0) { char ch = text.charAt(offset - 1); char closingChar = 0; if (ch == '(') closingChar = ')'; else if (ch == '{') closingChar = '}'; else if (ch == '[') closingChar = ']'; if (closingChar != 0) { // it was one of the grouping chars, try to find the // closing. int closing = findClosing(text, offset, ch, closingChar, 1); if (closing != -1) return new Region(offset, closing - offset); } } // try to find if the text under the offset is 1 of ),] or } if (offset >= text.length()) { return null; } char ch = text.charAt(offset); char startChar = 0; if (ch == ')') startChar = '('; else if (ch == '}') startChar = '{'; else if (ch == ']') startChar = '['; if (startChar != 0) { // it was one now try to find the start. int closing = findClosing(text, offset - 1, ch, startChar, -1); if (closing != -1) return new Region(closing + 1, offset - closing - 1); } // fall back on just words int i = offset; while (Character.isJavaIdentifierPart(text.charAt(i--))) { if (i == -1) { break; } } start = i + 2; i = offset; while (Character.isJavaIdentifierPart(text.charAt(i++))) { if (i == text.length()) { break; } } end = i - 1; return new Region(start, end - start); } // pretty simple find next { or }, doesnt skip over strings yet. private int findClosing(String text, int offset, char beginChar, char endChar, int next) { int skip = 0; int counter = offset; while (counter != -1 && counter < text.length()) { char ch = text.charAt(counter); if (ch == endChar) { if (skip == 0) return counter; skip--; } if (ch == beginChar) skip++; counter += next; } return -1; } }; } @Override public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { return IJavaScriptPartitions.JS_PARTITION_TYPES; } @Override public String getCommentPrefix() { return "//"; //$NON-NLS-1$ } @Override protected void alterContentAssistant(ContentAssistant assistant) { IContentAssistProcessor scriptProcessor = new JavaScriptCompletionProcessor( getEditor(), assistant, IDocument.DEFAULT_CONTENT_TYPE); assistant.setContentAssistProcessor(scriptProcessor, IDocument.DEFAULT_CONTENT_TYPE); ContentAssistProcessor singleLineProcessor = new JavaScriptCompletionProcessor( getEditor(), assistant, IJavaScriptPartitions.JS_MULTI_LINE_COMMENT); assistant.setContentAssistProcessor(singleLineProcessor, IJavaScriptPartitions.JS_MULTI_LINE_COMMENT); ContentAssistProcessor stringProcessor = new JavaScriptCompletionProcessor( getEditor(), assistant, IJavaScriptPartitions.JS_STRING); assistant.setContentAssistProcessor(stringProcessor, IJavaScriptPartitions.JS_STRING); ContentAssistProcessor stringSingleProcessor = new JavaScriptCompletionProcessor( getEditor(), assistant, IJavaScriptPartitions.JS_STRING_SINGLE); assistant.setContentAssistProcessor(stringSingleProcessor, IJavaScriptPartitions.JS_STRING_SINGLE); // TODO JS DOC completion?? ContentAssistProcessor jsProcessor = new JavaScriptCompletionProcessor( getEditor(), assistant, IJavaScriptPartitions.JS_DOC); assistant.setContentAssistProcessor(jsProcessor, IJavaScriptPartitions.JS_DOC); } @Override protected ContentAssistPreference getContentAssistPreference() { return JavaScriptContentAssistPreference.getDefault(); } @Override protected void initializeScanners() { fCodeScanner = new JavascriptCodeScanner(getColorManager(), fPreferenceStore); fStringScanner = new SingleTokenScriptScanner(getColorManager(), fPreferenceStore, JavascriptColorConstants.JS_STRING); fCommentScanner = new JavaScriptScriptCommentScanner(getColorManager(), fPreferenceStore, JavascriptColorConstants.JS_SINGLE_LINE_COMMENT, JavascriptColorConstants.JS_TODO_TAG, new TodoTaskPreferencesOnPreferenceStore(fPreferenceStore)); fDocScanner = new JavascriptDocScanner(this); } @Override public IPresentationReconciler getPresentationReconciler( ISourceViewer sourceViewer) { ScriptPresentationReconciler reconciler = new ScriptPresentationReconciler(); reconciler .setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); DefaultDamagerRepairer dr = new DefaultDamagerRepairer( this.fCodeScanner); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); dr = new DefaultDamagerRepairer(fStringScanner); reconciler.setDamager(dr, IJavaScriptPartitions.JS_STRING); reconciler.setRepairer(dr, IJavaScriptPartitions.JS_STRING); dr = new DefaultDamagerRepairer(fStringScanner); reconciler.setDamager(dr, IJavaScriptPartitions.JS_STRING_SINGLE); reconciler.setRepairer(dr, IJavaScriptPartitions.JS_STRING_SINGLE); dr = new DefaultDamagerRepairer(fCommentScanner); reconciler.setDamager(dr, IJavaScriptPartitions.JS_MULTI_LINE_COMMENT); reconciler.setRepairer(dr, IJavaScriptPartitions.JS_MULTI_LINE_COMMENT); dr = new DefaultDamagerRepairer(fCommentScanner); reconciler.setDamager(dr, IJavaScriptPartitions.JS_SINGLE_LINE_COMMENT); reconciler .setRepairer(dr, IJavaScriptPartitions.JS_SINGLE_LINE_COMMENT); dr = new DefaultDamagerRepairer(fDocScanner); reconciler.setDamager(dr, IJavaScriptPartitions.JS_DOC); reconciler.setRepairer(dr, IJavaScriptPartitions.JS_DOC); return reconciler; } /** * Adapts the behavior of the contained components to the change encoded in * the given event. * <p> * Clients are not allowed to call this method if the old setup with text * tools is in use. * </p> * * @param event * the event to which to adapt * @see JavascriptSourceViewerConfiguration#ScriptSourceViewerConfiguration(IColorManager, * IPreferenceStore, ITextEditor, String) */ @Override public void handlePropertyChangeEvent(PropertyChangeEvent event) { if (fCodeScanner.affectsBehavior(event)) fCodeScanner.adaptToPreferenceChange(event); if (fCommentScanner.affectsBehavior(event)) fCommentScanner.adaptToPreferenceChange(event); if (fDocScanner.affectsBehavior(event)) fDocScanner.adaptToPreferenceChange(event); if (fStringScanner.affectsBehavior(event)) fStringScanner.adaptToPreferenceChange(event); } /** * Determines whether the preference change encoded by the given event * changes the behavior of one of its contained components. * * @param event * the event to be investigated * @return <code>true</code> if event causes a behavioral change * */ @Override public boolean affectsTextPresentation(PropertyChangeEvent event) { return fCodeScanner.affectsBehavior(event) || fCommentScanner.affectsBehavior(event) || fDocScanner.affectsBehavior(event) || fStringScanner.affectsBehavior(event); } @Override public IAutoEditStrategy[] getAutoEditStrategies( ISourceViewer sourceViewer, String contentType) { if (IJavaScriptPartitions.JS_MULTI_LINE_COMMENT.equals(contentType) || IJavaScriptPartitions.JS_DOC.equals(contentType)) { return new IAutoEditStrategy[] { new JSDocAutoIndentStrategy( fPreferenceStore) }; } else if (IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) { String partitioning = getConfiguredDocumentPartitioning(sourceViewer); return new IAutoEditStrategy[] { new JavascriptAutoEditStrategy( partitioning, null) }; } else { return super.getAutoEditStrategies(sourceViewer, contentType); } } @Override @SuppressWarnings({ "unchecked", "rawtypes" }) protected Map getHyperlinkDetectorTargets(final ISourceViewer sourceViewer) { final Map targets = super.getHyperlinkDetectorTargets(sourceViewer); targets.put("org.eclipse.dltk.javascript.code", getEditor()); //$NON-NLS-1$ return targets; } @Override protected SpellCheckDelegate createSpellCheckDelegate() { return new SpellCheckDelegate(IDocument.DEFAULT_CONTENT_TYPE, IJavaScriptPartitions.JS_STRING, IJavaScriptPartitions.JS_STRING_SINGLE); } }