[276702] [editor] Closing brace should come up automatically
diff --git a/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java b/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java index 9502c89..661e60d 100644 --- a/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java +++ b/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java
@@ -13,11 +13,13 @@ */ package org.eclipse.wst.jsdt.web.support.jsp; +import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jst.jsp.core.text.IJSPPartitions; import org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP; import org.eclipse.wst.html.core.text.IHTMLPartitions; import org.eclipse.wst.jsdt.web.ui.StructuredTextViewerConfigurationJSDT; +import org.eclipse.wst.jsdt.web.ui.internal.autoedit.AutoEditStrategyForJs; import org.eclipse.wst.sse.ui.StructuredTextViewerConfiguration; import org.eclipse.wst.sse.ui.internal.provisional.style.LineStyleProvider; /** @@ -58,4 +60,17 @@ return providers; } + + /** + * @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) + */ + public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { + if(contentType.equals(IHTMLPartitions.SCRIPT) || contentType.equals(IHTMLPartitions.SCRIPT_EVENTHANDLER)) { + IAutoEditStrategy[] strategies = new IAutoEditStrategy[1]; + strategies[0] = new AutoEditStrategyForJs(); + return strategies; + } else { + return super.getAutoEditStrategies(sourceViewer, contentType); + } + } }
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml b/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml index 577e477..604fd98 100644 --- a/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml +++ b/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml
@@ -720,4 +720,16 @@ </contentType> </proposalComputer> </extension> +<extension point="org.eclipse.wst.sse.ui.characterPairing"> + <inserter class="org.eclipse.wst.jsdt.web.ui.internal.text.JsCharacterPairInserter" id="org.eclipse.wst.jsdt.web.ui.inserter"> + <contentTypeIdentifier + id="org.eclipse.wst.html.core.htmlsource" + partitions="org.eclipse.wst.html.SCRIPT, org.eclipse.wst.html.SCRIPT.EVENTHANDLER"> + </contentTypeIdentifier> + <contentTypeIdentifier + id="org.eclipse.jst.jsp.core.jspsource" + partitions="org.eclipse.wst.html.SCRIPT, org.eclipse.wst.html.SCRIPT.EVENTHANDLER"> + </contentTypeIdentifier> + </inserter> +</extension> </plugin>
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java index ce46e32..a1d8070 100644 --- a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java +++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java
@@ -10,7 +10,11 @@ *******************************************************************************/ package org.eclipse.wst.jsdt.web.ui; +import org.eclipse.jface.text.IAutoEditStrategy; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.wst.html.core.text.IHTMLPartitions; import org.eclipse.wst.html.ui.StructuredTextViewerConfigurationHTML; +import org.eclipse.wst.jsdt.web.ui.internal.autoedit.AutoEditStrategyForJs; /** * @@ -37,4 +41,17 @@ // Must have empty constructor to createExecutableExtension super(); } + + /** + * @see org.eclipse.wst.html.ui.StructuredTextViewerConfigurationHTML#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) + */ + public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { + if(contentType.equals(IHTMLPartitions.SCRIPT) || contentType.equals(IHTMLPartitions.SCRIPT_EVENTHANDLER)) { + IAutoEditStrategy[] strategies = new IAutoEditStrategy[1]; + strategies[0] = new AutoEditStrategyForJs(); + return strategies; + } else { + return super.getAutoEditStrategies(sourceViewer, contentType); + } + } }
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java index b662203..9c1f97c 100644 --- a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java +++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2008 IBM Corporation and others. + * Copyright (c) 2007, 2010 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 @@ -53,8 +53,8 @@ return fStrategies; } String partitioning = IHTMLPartitions.SCRIPT; - fStrategies = new IAutoEditStrategy[] { new JavaDocAutoIndentStrategy(partitioning), new SmartSemicolonAutoEditStrategy(partitioning), - new JavaAutoIndentStrategy(partitioning, getJavaProject(document), null) }; + fStrategies = new IAutoEditStrategy[] { new SmartSemicolonAutoEditStrategy(partitioning), + new JavaAutoIndentStrategy(partitioning, getJavaProject(document), null), new JavaDocAutoIndentStrategy(partitioning) }; /* new AutoEditStrategyForTabs() */ return fStrategies; }
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java new file mode 100644 index 0000000..38d02d7 --- /dev/null +++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java
@@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.wst.jsdt.web.ui.internal.text; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.link.ILinkedModeListener; +import org.eclipse.jface.text.link.LinkedModeModel; +import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; +import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.wst.jsdt.core.JavaScriptCore; +import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; +import org.eclipse.wst.jsdt.internal.ui.text.JavaHeuristicScanner; +import org.eclipse.wst.jsdt.internal.ui.text.Symbols; +import org.eclipse.wst.jsdt.ui.PreferenceConstants; +import org.eclipse.wst.jsdt.web.ui.internal.Logger; +import org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter; + +/** + * <p>Inserts character pairs in script regions in HTML and JSP documents based on the Javascript + * character pairing preferences.</p> + */ +public class JsCharacterPairInserter extends AbstractCharacterPairInserter implements IPropertyChangeListener{ + + private boolean fCloseStrings; + private boolean fCloseBrackets; + private boolean fCloseBraces; + private boolean fCloseAngularBrackets; + + /** + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#hasPair(char) + */ + public boolean hasPair(char c) { + switch (c) { + case '(': + case '<': + case '[': + case '\'': + case '\"': + case '{': + return true; + default: + return false; + } + } + + /** + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#shouldPair(org.eclipse.jface.text.source.ISourceViewer, char) + */ + protected boolean shouldPair(ISourceViewer viewer, char c) { + IDocument document= viewer.getDocument(); + + final Point selection= viewer.getSelectedRange(); + final int offset= selection.x; + final int length= selection.y; + + try { + IRegion startLine= document.getLineInformationOfOffset(offset); + IRegion endLine= document.getLineInformationOfOffset(offset + length); + + JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); + int nextToken= scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength()); + String next= nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim(); + int prevToken= scanner.previousToken(offset - 1, startLine.getOffset()); + int prevTokenOffset= scanner.getPosition() + 1; + String previous= prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim(); + + switch (c) { + case '(': + if (!fCloseBrackets + || nextToken == Symbols.TokenLPAREN + || nextToken == Symbols.TokenIDENT + || next != null && next.length() > 1) + return false; + break; + + case '<': + if (!(fCloseAngularBrackets && fCloseBrackets) + || nextToken == Symbols.TokenLESSTHAN + || prevToken != Symbols.TokenLBRACE + && prevToken != Symbols.TokenRBRACE + && prevToken != Symbols.TokenSEMICOLON + && prevToken != Symbols.TokenSYNCHRONIZED + && prevToken != Symbols.TokenSTATIC + && (prevToken != Symbols.TokenIDENT || !isAngularIntroducer(previous)) + && prevToken != Symbols.TokenEOF) + return false; + break; + + case '{': + if (!fCloseBraces + || nextToken == Symbols.TokenIDENT + || next != null && next.length() > 1) + return false; + break; + case '[': + if (!fCloseBrackets + || nextToken == Symbols.TokenIDENT + || next != null && next.length() > 1) + return false; + break; + + case '\'': + case '"': + if (!fCloseStrings + || nextToken == Symbols.TokenIDENT + || prevToken == Symbols.TokenIDENT + || next != null && next.length() > 1 + || previous != null && previous.length() > 1) + return false; + break; + + default: + return false; + } + } catch (BadLocationException e) { + return false; + } + + return true; + } + + /** + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#getPair(char) + */ + protected char getPair(char c) { + switch (c) { + case '(': + return ')'; + case '<': + return '>'; + case '[': + return ']'; + case '{': + return '}'; + case '\'': + return c; + case '\"': + return c; + default: + throw new IllegalArgumentException(); + } + } + + /** + * <p>Initialize the preference listener</p> + * + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#initialize() + */ + public void initialize() { + super.initialize(); + IPreferenceStore preferenceStore = JavaScriptPlugin.getDefault().getPreferenceStore(); + this.fCloseStrings = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_STRINGS); + this.fCloseBrackets = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS); + this.fCloseBraces = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES); + this.fCloseAngularBrackets = JavaScriptCore.VERSION_1_5.compareTo( + preferenceStore.getString(JavaScriptCore.COMPILER_SOURCE)) <= 0; + preferenceStore.addPropertyChangeListener(this); + } + + /** + * <p>Dispose the preference listener</p> + * + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#dispose() + */ + public void dispose() { + JavaScriptPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); + super.dispose(); + } + + /** + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + if (PreferenceConstants.EDITOR_CLOSE_STRINGS.equals(event.getProperty())){ + this.fCloseStrings = ((Boolean) event.getNewValue()).booleanValue(); + } else if (PreferenceConstants.EDITOR_CLOSE_BRACKETS.equals(event.getProperty())) { + this.fCloseBrackets = ((Boolean) event.getNewValue()).booleanValue(); + } else if (PreferenceConstants.EDITOR_CLOSE_BRACES.equals(event.getProperty())) { + this.fCloseBraces = ((Boolean) event.getNewValue()).booleanValue(); + } else if (JavaScriptCore.COMPILER_SOURCE.equals(event.getProperty())) { + IPreferenceStore preferenceStore = JavaScriptPlugin.getDefault().getPreferenceStore(); + this.fCloseAngularBrackets = JavaScriptCore.VERSION_1_5.compareTo(preferenceStore.getString( + JavaScriptCore.COMPILER_SOURCE)) <= 0; + } + } + + /** + * TODO: IAN: comment me + * @param identifier + * @return + */ + private boolean isAngularIntroducer(String identifier) { + return identifier.length() > 0 + && (Character.isUpperCase(identifier.charAt(0)) + || identifier.startsWith("final") //$NON-NLS-1$ + || identifier.startsWith("public") //$NON-NLS-1$ + || identifier.startsWith("public") //$NON-NLS-1$ + || identifier.startsWith("protected") //$NON-NLS-1$ + || identifier.startsWith("private")); //$NON-NLS-1$ + } + + /** + * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#getExitPolicy(char, char, org.eclipse.jface.text.IDocument) + */ + protected IExitPolicy getExitPolicy(char exit, char escape, IDocument document) { + return new ExitPolicy(exit, escape, document); + } + + /** + * <p>An exit policy that deals with a user adding a carriage return between {}. + * In that case rather then exiting to the exit position the linked mode is exited + * and the carriage return inserted.</p> + */ + private static class ExitPolicy implements IExitPolicy { + + /** exit character */ + private char fExit; + + /** escape character for the exit character */ + private char fEscape; + + /** document to execute this exit policy on */ + private IDocument fDocument; + + /** + * <p>Default constructor</p> + * + * @param exit exit character + * @param escape escape character for the exit character + * @param document document to execute this exit policy on + */ + public ExitPolicy(char exit, char escape, IDocument document) { + fExit = exit; + fEscape = escape; + fDocument = document; + } + + /** + * @see org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy#doExit(org.eclipse.jface.text.link.LinkedModeModel, org.eclipse.swt.events.VerifyEvent, int, int) + */ + public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { + if(!isMasked(offset)) { + // if exit character then exit to exit location + if (event.character == fExit) { + return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false); + } + + // if carriage return and previous character is { then exit linked mode and insert return + if (event.character == SWT.CR && offset > 0) { + try { + if (this.fDocument.getChar(offset - 1) == '{') { + return new ExitFlags(ILinkedModeListener.EXIT_ALL, true); + } + } catch (BadLocationException e) { + Logger.logException("Error while trying to exit linked mode", e); //$NON-NLS-1$ + } + } + } + return null; + } + + /** + * <p>Determine if the exit character has been escaped.<p> + * + * @param offset current offset in the document + * @return <code>true</code> if exit character escaped, <code>false</code> otherwise + */ + private boolean isMasked(int offset) { + try { + return fEscape == fDocument.getChar(offset - 1); + } catch (BadLocationException e) { + } + return false; + } + } +}