--- a/apisupport.project/nbproject/project.xml +++ a/apisupport.project/nbproject/project.xml @@ -139,8 +139,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/beans/nbproject/project.xml +++ a/beans/nbproject/project.xml @@ -116,8 +116,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/bpel.debugger.ui/nbproject/project.xml +++ a/bpel.debugger.ui/nbproject/project.xml @@ -96,8 +96,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/bugtracking.bridge/nbproject/project.xml +++ a/bugtracking.bridge/nbproject/project.xml @@ -36,8 +36,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.asm/nbproject/project.xml +++ a/cnd.asm/nbproject/project.xml @@ -27,7 +27,7 @@ 3 - 1.41 + 1.53 @@ -44,8 +44,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.completion/nbproject/project.xml +++ a/cnd.completion/nbproject/project.xml @@ -87,7 +87,7 @@ 3 - 1.41 + 1.53 @@ -122,8 +122,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.debugger.common/nbproject/project.xml +++ a/cnd.debugger.common/nbproject/project.xml @@ -79,8 +79,8 @@ - 2 - 2.7 + 3 + 3.1 --- a/cnd.debugger.gdb/nbproject/project.xml +++ a/cnd.debugger.gdb/nbproject/project.xml @@ -124,8 +124,8 @@ - 2 - 2.7 + 3 + 3.1 --- a/cnd.editor/nbproject/project.xml +++ a/cnd.editor/nbproject/project.xml @@ -36,7 +36,7 @@ 3 - 1.41 + 1.53 @@ -71,8 +71,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.editor/src/org/netbeans/modules/cnd/editor/cplusplus/CCKit.java +++ a/cnd.editor/src/org/netbeans/modules/cnd/editor/cplusplus/CCKit.java @@ -47,7 +47,6 @@ import java.awt.event.ActionEvent; import javax.swing.Action; import javax.swing.text.Caret; -import javax.swing.text.Position; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.TextAction; @@ -228,52 +227,52 @@ return; } - final BaseDocument doc = (BaseDocument) target.getDocument(); + final BaseDocument doc = (BaseDocument)target.getDocument(); + final Reformat formatter = Reformat.get(doc); + // Set hourglass cursor Cursor origCursor = target.getCursor(); target.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + formatter.lock(); + try { + doc.runAtomic(new Runnable() { - doc.runAtomicAsUser(new Runnable() { + public void run() { + try { + Caret caret = target.getCaret(); - public void run() { - try { - Caret caret = target.getCaret(); + int caretLine = Utilities.getLineOffset(doc, caret.getDot()); + int start; + int end; + //if (caret.isSelectionVisible()) { + if (Utilities.isSelectionShowing(caret)) { + start = target.getSelectionStart(); + end = target.getSelectionEnd(); + } else { + start = 0; + end = doc.getLength(); + } - int caretLine = Utilities.getLineOffset(doc, caret.getDot()); - int startPos; - Position endPosition; - //if (caret.isSelectionVisible()) { - if (Utilities.isSelectionShowing(caret)) { - startPos = target.getSelectionStart(); - endPosition = doc.createPosition(target.getSelectionEnd()); - } else { - startPos = 0; - endPosition = doc.createPosition(doc.getLength()); + formatter.reformat(start, end); + + // Restore the line + int pos = Utilities.getRowStartFromLineOffset(doc, caretLine); + if (pos >= 0) { + caret.setDot(pos); + } + } catch (BadLocationException e) { + //failed to format } + } + }); + } finally { + formatter.unlock(); + } - int pos = startPos; - Reformat reformat = Reformat.get(doc); - reformat.lock(); - try { - reformat.reformat(pos, endPosition.getOffset()); - } finally { - reformat.unlock(); - } - - // Restore the line - pos = Utilities.getRowStartFromLineOffset(doc, caretLine); - if (pos >= 0) { - caret.setDot(pos); - } - } catch (BadLocationException e) { - //failed to format - } - } - }); target.setCursor(origCursor); - - } - } + } + } } public static class CCDefaultKeyTypedAction extends ExtDefaultKeyTypedAction { @@ -372,8 +371,7 @@ } doc.insertString(end, "\n" + insString, null); // NOI18N // Lock does not need because method is invoked from BaseKit that already lock indent. - Indent indent = Indent.get(doc); - indent.reindent(end + 1); + Indent.get(doc).indentNewLine(end); caret.setDot(dotPos); return Boolean.TRUE; } --- a/cnd.editor/src/org/netbeans/modules/cnd/editor/cplusplus/InsertSemicolonAction.java +++ a/cnd.editor/src/org/netbeans/modules/cnd/editor/cplusplus/InsertSemicolonAction.java @@ -38,6 +38,7 @@ import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Utilities; import org.netbeans.modules.editor.indent.api.Indent; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** @@ -72,30 +73,34 @@ return; } final BaseDocument doc = (BaseDocument) target.getDocument(); + final Indent indenter = Indent.get(doc); final class R implements Runnable { public void run() { - Caret caret = target.getCaret(); - int dotpos = caret.getDot(); try { + Caret caret = target.getCaret(); + int dotpos = caret.getDot(); int eoloffset = Utilities.getRowEnd(target, dotpos); - String insertString = "" + what; - doc.insertString(eoloffset, insertString, null); //NOI18N + doc.insertString(eoloffset, "" + what, null); //NOI18N if (withNewline) { - Indent indent = Indent.get(doc); - indent.lock(); - try { - int eolDot = Utilities.getRowEnd(target, caret.getDot()); - doc.insertString(eolDot, "\n", null); // NOI18N - caret.setDot(eolDot+1); - indent.reindent(eolDot+1); - } finally { - indent.unlock(); - } + //This is code from the editor module, but it is + //a pretty strange way to do this: + doc.insertString(dotpos, "-", null); //NOI18N + doc.remove(dotpos, 1); + int eolDot = Utilities.getRowEnd(target, caret.getDot()); + int newDotPos = indenter.indentNewLine(eolDot); + caret.setDot(newDotPos); } } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); } } } - doc.runAtomicAsUser(new R()); + + indenter.lock(); + try { + doc.runAtomicAsUser(new R()); + } finally { + indenter.lock(); + } } } --- a/cnd.editor/src/org/netbeans/modules/cnd/editor/options/EditorPropertySheet.java +++ a/cnd.editor/src/org/netbeans/modules/cnd/editor/options/EditorPropertySheet.java @@ -55,6 +55,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; @@ -62,14 +64,11 @@ import javax.swing.JEditorPane; import javax.swing.SwingUtilities; import javax.swing.text.BadLocationException; -import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.editor.BaseDocument; import org.netbeans.modules.cnd.editor.api.CodeStyle; import org.netbeans.modules.cnd.editor.options.PreviewPreferencesModel.Filter; import org.netbeans.modules.cnd.editor.reformat.Reformatter; -import org.netbeans.modules.cnd.utils.MIMENames; -import org.netbeans.modules.editor.indent.api.IndentUtils; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.explorer.propertysheet.PropertySheet; @@ -98,7 +97,6 @@ private PreviewPreferencesModel preferencesModel; private Filter filter; private PropertySheet holder; - private Object[] originalEditorProperties = null; EditorPropertySheet(EditorOptionsPanelController topControler, CodeStyle.Language language, PreviewPreferencesModel preferencesModel, Filter filter) { this.topController = topControler; @@ -383,9 +381,6 @@ void load() { loaded = false; - if (filter == Filter.All) { - originalEditorProperties = preserveEditorProperties(); - } initLanguageMap(); initLanguageCategory(); loaded = true; @@ -449,10 +444,6 @@ if (filter != Filter.All) { return; } - if (originalEditorProperties != null) { - restoreEditorProperties(originalEditorProperties); - originalEditorProperties = null; - } preferencesModel.clear(language); } @@ -581,7 +572,7 @@ is.close(); } } catch (IOException ioe) { - ioe.printStackTrace(); + Logger.getLogger(EditorPropertySheet.class.getName()).log(Level.FINE, null, ioe); } return sb.toString(); } else { @@ -593,127 +584,10 @@ pane.setText(getPreviewText()); BaseDocument bd = (BaseDocument) pane.getDocument(); CodeStyle codeStyle = EditorOptions.createCodeStyle(language, p, false); - setEditorProperties(p); try { - if (TRACE) { - System.err.println("Refreshing preview"); // NOI18N - System.err.println(" tabSize=" + IndentUtils.tabSize(bd)+"/"+codeStyle.getTabSize()); // NOI18N - System.err.println(" expandTabs=" + IndentUtils.isExpandTabs(bd)+"/"+codeStyle.expandTabToSpaces()); // NOI18N - System.err.println(" indentLevelSize=" + IndentUtils.indentLevelSize(bd)+"/"+codeStyle.indentSize()); // NOI18N - System.err.println(" doc=" + bd); //NOI18N - } new Reformatter(bd, codeStyle).reformat(); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); - } catch (Throwable ex) { - ex.printStackTrace(); - } - } - - private void setEditorProperties(Preferences p) { - Preferences def = null; - switch (language){ - case C: - def = MimeLookup.getLookup(MIMENames.C_MIME_TYPE).lookup(Preferences.class); - break; - case HEADER: - def = MimeLookup.getLookup(MIMENames.HEADER_MIME_TYPE).lookup(Preferences.class); - break; - case CPP: - default: - def = MimeLookup.getLookup(MIMENames.CPLUSPLUS_MIME_TYPE).lookup(Preferences.class); - break; - } - if (def != null) { - def.putInt(SimpleValueNames.TAB_SIZE, p.getInt(EditorOptions.tabSize, EditorOptions.tabSizeDefault)); - def.putInt(SimpleValueNames.SPACES_PER_TAB, p.getInt(EditorOptions.tabSize, EditorOptions.tabSizeDefault)); - def.putBoolean(SimpleValueNames.EXPAND_TABS, p.getBoolean(EditorOptions.expandTabToSpaces, EditorOptions.expandTabToSpacesDefault)); - def.putInt(SimpleValueNames.INDENT_SHIFT_WIDTH, p.getInt(EditorOptions.indentSize, EditorOptions.indentSizeDefault)); - } - } - - private Object[] preserveEditorProperties() { - Preferences def = null; - Object oldValues[] = null; - switch (language){ - case C: - def = MimeLookup.getLookup(MIMENames.C_MIME_TYPE).lookup(Preferences.class); - break; - case HEADER: - def = MimeLookup.getLookup(MIMENames.HEADER_MIME_TYPE).lookup(Preferences.class); - break; - case CPP: - default: - def = MimeLookup.getLookup(MIMENames.CPLUSPLUS_MIME_TYPE).lookup(Preferences.class); - break; - } - if (def != null) { - oldValues = new Object[]{null, null, null, null}; - if (null != def.get(SimpleValueNames.TAB_SIZE, null)) { - oldValues[0] = def.getInt(SimpleValueNames.TAB_SIZE, EditorOptions.tabSizeDefault); - } - if (null != def.get(SimpleValueNames.SPACES_PER_TAB, null)) { - oldValues[1] = def.getInt(SimpleValueNames.SPACES_PER_TAB, EditorOptions.tabSizeDefault); - } - if (null != def.get(SimpleValueNames.EXPAND_TABS, null)) { - oldValues[2] = def.getBoolean(SimpleValueNames.EXPAND_TABS, EditorOptions.expandTabToSpacesDefault); - } - if (null != def.get(SimpleValueNames.INDENT_SHIFT_WIDTH, null)) { - oldValues[3] = def.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, EditorOptions.indentSizeDefault); - } - if (TRACE) { - System.err.println("Preserving editor properties:"); //NOI18N - System.err.println(" tabSize=" + oldValues[0]); //NOI18N - System.err.println(" spacesPerTab=" + oldValues[1]); //NOI18N - System.err.println(" expandTabs=" + oldValues[2]); //NOI18N - System.err.println(" indentShiftWidth=" + oldValues[3]); //NOI18N - } - } - return oldValues; - } - - private void restoreEditorProperties(Object[] oldValues) { - Preferences def = null; - switch (language){ - case C: - def = MimeLookup.getLookup(MIMENames.C_MIME_TYPE).lookup(Preferences.class); - break; - case HEADER: - def = MimeLookup.getLookup(MIMENames.HEADER_MIME_TYPE).lookup(Preferences.class); - break; - case CPP: - default: - def = MimeLookup.getLookup(MIMENames.CPLUSPLUS_MIME_TYPE).lookup(Preferences.class); - break; - } - if (def != null && oldValues != null) { - if (TRACE) { - System.err.println("Restoring editor properties:"); //NOI18N - System.err.println(" tabSize=" + oldValues[0]); //NOI18N - System.err.println(" spacesPerTab=" + oldValues[1]); //NOI18N - System.err.println(" expandTabs=" + oldValues[2]); //NOI18N - System.err.println(" indentShiftWidth=" + oldValues[3]); //NOI18N - } - if (oldValues[0] == null) { - def.remove(SimpleValueNames.TAB_SIZE); - } else { - def.putInt(SimpleValueNames.TAB_SIZE, ((Integer)oldValues[0]).intValue()); - } - if (oldValues[1] == null) { - def.remove(SimpleValueNames.SPACES_PER_TAB); - } else { - def.putInt(SimpleValueNames.SPACES_PER_TAB, ((Integer)oldValues[1]).intValue()); - } - if (oldValues[2] == null) { - def.remove(SimpleValueNames.EXPAND_TABS); - } else { - def.putBoolean(SimpleValueNames.EXPAND_TABS, ((Boolean)oldValues[2]).booleanValue()); - } - if (oldValues[3] == null) { - def.remove(SimpleValueNames.INDENT_SHIFT_WIDTH); - } else { - def.putInt(SimpleValueNames.INDENT_SHIFT_WIDTH, ((Integer)oldValues[3]).intValue()); - } } } --- a/cnd.editor/test/unit/src/org/netbeans/modules/cnd/editor/cplusplus/BracketCompletionTestCase.java +++ a/cnd.editor/test/unit/src/org/netbeans/modules/cnd/editor/cplusplus/BracketCompletionTestCase.java @@ -33,6 +33,8 @@ import org.netbeans.modules.cnd.editor.api.CodeStyle; import org.netbeans.modules.cnd.editor.options.EditorOptions; +import org.netbeans.modules.editor.indent.api.Indent; +import org.openide.util.Exceptions; /** * Class was taken from java --- a/cnd.editor/test/unit/src/org/netbeans/modules/cnd/editor/cplusplus/EditorBase.java +++ a/cnd.editor/test/unit/src/org/netbeans/modules/cnd/editor/cplusplus/EditorBase.java @@ -42,6 +42,7 @@ import org.netbeans.modules.cnd.editor.reformat.Reformatter; import org.netbeans.modules.cnd.test.base.BaseDocumentUnitTestCase; import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.modules.editor.indent.api.Reformat; import org.openide.util.Exceptions; /** @@ -145,14 +146,22 @@ /** * Perform reformatting of the whole document's text. */ - protected final void reformat() { - Reformatter f = new Reformatter(getDocument(), CodeStyle.getDefault(getDocument())); + protected void reformat() { + Reformat f = Reformat.get(getDocument()); + f.lock(); try { - f.reformat(); + getDocument().atomicLock(); + try { + f.reformat(0, getDocument().getLength()); + } finally { + getDocument().atomicUnlock(); + } } catch (BadLocationException e) { e.printStackTrace(getLog()); fail(e.getMessage()); - } + } finally { + f.unlock(); + } } // ------- help methods ------------- --- a/cnd.gizmo/nbproject/project.xml +++ a/cnd.gizmo/nbproject/project.xml @@ -152,8 +152,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.gotodeclaration/nbproject/project.xml +++ a/cnd.gotodeclaration/nbproject/project.xml @@ -50,6 +50,33 @@ + org.netbeans.modules.editor.completion + + + + 1 + 1.6.12 + + + + org.netbeans.modules.editor.lib + + + + 3 + 3.1 + + + + org.netbeans.modules.editor.settings + + + + 1 + 1.4.12 + + + org.netbeans.modules.jumpto --- a/cnd.highlight/nbproject/project.xml +++ a/cnd.highlight/nbproject/project.xml @@ -106,8 +106,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.modelimpl/nbproject/project.xml +++ a/cnd.modelimpl/nbproject/project.xml @@ -105,8 +105,8 @@ - 2 - 2.10 + 3 + 3.1 --- a/cnd.modelutil/nbproject/project.xml +++ a/cnd.modelutil/nbproject/project.xml @@ -46,7 +46,7 @@ 3 - 1.41 + 1.53 @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.navigation/nbproject/project.xml +++ a/cnd.navigation/nbproject/project.xml @@ -54,7 +54,7 @@ 3 - 1.42 + 1.53 @@ -62,8 +62,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.refactoring/nbproject/project.xml +++ a/cnd.refactoring/nbproject/project.xml @@ -73,7 +73,7 @@ 3 - 1.41 + 1.53 @@ -90,8 +90,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd.script/nbproject/project.xml +++ a/cnd.script/nbproject/project.xml @@ -37,7 +37,7 @@ 3 - 1.48 + 1.53 @@ -54,8 +54,8 @@ - 2 - 2.2 + 3 + 3.1 --- a/cnd.testrunner/nbproject/project.xml +++ a/cnd.testrunner/nbproject/project.xml @@ -95,8 +95,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/cnd/nbproject/project.xml +++ a/cnd/nbproject/project.xml @@ -61,7 +61,25 @@ 2 - 1.9 + 1.10 + + + + org.netbeans.modules.editor.mimelookup + + + + 1 + 1.10 + + + + org.netbeans.modules.editor.settings + + + + 1 + 1.22 @@ -70,6 +88,15 @@ 2 + 1.22 + + + + org.netbeans.modules.lexer + + + + 2 1.16 --- a/collab.channel.chat/nbproject/project.xml +++ a/collab.channel.chat/nbproject/project.xml @@ -65,7 +65,7 @@ 3 - 1.41 + 1.53 @@ -73,8 +73,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -125,7 +125,7 @@ 6.2 - + org.netbeans.modules.collab.channel.chat org.netbeans.modules.collab.channel.chat.messagetype --- a/compapp.casaeditor/nbproject/project.xml +++ a/compapp.casaeditor/nbproject/project.xml @@ -123,8 +123,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/compapp.projects.base/nbproject/project.xml +++ a/compapp.projects.base/nbproject/project.xml @@ -64,8 +64,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/compapp.projects.jbi/nbproject/project.xml +++ a/compapp.projects.jbi/nbproject/project.xml @@ -174,8 +174,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/csl.api/nbproject/project.xml +++ a/csl.api/nbproject/project.xml @@ -56,7 +56,7 @@ 3 - 1.41 + 1.53 @@ -118,8 +118,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/css.editor/nbproject/project.xml +++ a/css.editor/nbproject/project.xml @@ -29,7 +29,7 @@ 3 - 1.41 + 1.53 @@ -55,8 +55,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/css.visual/nbproject/project.xml +++ a/css.visual/nbproject/project.xml @@ -82,7 +82,7 @@ 3 - 1.41 + 1.53 @@ -95,12 +95,21 @@ + org.netbeans.modules.editor.indent + + + + 2 + 1.9 + + + org.netbeans.modules.editor.lib - 2 - 2.1 + 3 + 3.1 --- a/css.visual/src/org/netbeans/modules/css/editor/CssEditorSupport.java +++ a/css.visual/src/org/netbeans/modules/css/editor/CssEditorSupport.java @@ -63,6 +63,9 @@ import org.netbeans.modules.css.visual.ui.preview.CssPreviewable; import org.netbeans.modules.css.visual.ui.preview.CssPreviewable.Listener; import org.netbeans.modules.editor.NbEditorDocument; +import org.netbeans.modules.editor.indent.api.IndentUtils; +import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; /** @@ -139,7 +142,7 @@ } else if (oldRule == null && newRule != null) { //add the new rule at the end of the rule block: List items = myRule.items(); - final int INDENT = doc.getFormatter().getShiftWidth(); + final int INDENT = IndentUtils.indentLevelSize(document); int insertOffset = myRule.getRuleCloseBracketOffset(); boolean initialNewLine = false; @@ -160,7 +163,9 @@ } String text = (initialNewLine ? LINE_SEPARATOR : "") + - makeIndentString(INDENT) + +// XXX: see the method comment +// makeIndentString(INDENT) + + IndentUtils.createIndentString(document, INDENT) + newRule.key().name() + ": " + newRule.value().name() + ";" + LINE_SEPARATOR; @@ -189,13 +194,17 @@ } }; - private String makeIndentString(int level) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < level; i++) { - sb.append(' '); - } - return sb.toString(); - } + // XXX: This is most likely wrong! Unless CSS only allows spaces for indentation. + // But even then the same should be achieved by correctly setting the indentation + // settings. Otherwise a simple Reformat.get(doc).reformat(...) call on a CSS document + // could break its structure. +// private String makeIndentString(int level) { +// StringBuffer sb = new StringBuffer(); +// for (int i = 0; i < level; i++) { +// sb.append(' '); +// } +// return sb.toString(); +// } void parsed(final CssParserResult result, final int caretOffset) { d("model updated"); --- a/db.core/nbproject/project.xml +++ a/db.core/nbproject/project.xml @@ -80,8 +80,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/db.sql.editor/nbproject/project.xml +++ a/db.sql.editor/nbproject/project.xml @@ -98,7 +98,7 @@ 3 - 1.41 + 1.53 @@ -115,8 +115,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -232,6 +232,15 @@ 8.0 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + --- a/debugger.jpda.ant/nbproject/project.xml +++ a/debugger.jpda.ant/nbproject/project.xml @@ -101,8 +101,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -147,9 +147,9 @@ org.openide.util.lookup - - 8.0 - + + 8.0 + --- a/debugger.jpda.projects/nbproject/project.xml +++ a/debugger.jpda.projects/nbproject/project.xml @@ -96,8 +96,8 @@ - 2 - 2.10 + 3 + 3.1 --- a/debugger.jpda.ui/nbproject/project.xml +++ a/debugger.jpda.ui/nbproject/project.xml @@ -103,8 +103,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/debugger.jpda/nbproject/project.xml +++ a/debugger.jpda/nbproject/project.xml @@ -96,8 +96,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/diff/nbproject/project.xml +++ a/diff/nbproject/project.xml @@ -87,8 +87,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/dlight.annotationsupport/nbproject/project.xml +++ a/dlight.annotationsupport/nbproject/project.xml @@ -60,8 +60,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.actions/nbproject/project.xml +++ a/editor.actions/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.bookmarks/nbproject/project.xml +++ a/editor.bookmarks/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -152,7 +152,7 @@ - 8.0 + 8.4 --- a/editor.bracesmatching/nbproject/project.xml +++ a/editor.bracesmatching/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.41 + 1.53 @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.codetemplates/nbproject/project.xml +++ a/editor.codetemplates/nbproject/project.xml @@ -64,7 +64,7 @@ 3 - 1.41 + 1.53 @@ -76,12 +76,21 @@ + org.netbeans.modules.editor.indent + + + + 2 + 1.9 + + + org.netbeans.modules.editor.lib - 2 - 2.1 + 3 + 3.1 --- a/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateInsertHandler.java +++ a/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateInsertHandler.java @@ -61,7 +61,6 @@ import javax.swing.undo.CannotUndoException; import org.netbeans.api.editor.completion.Completion; import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; import org.netbeans.editor.Utilities; import org.netbeans.lib.editor.codetemplates.api.CodeTemplate; import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateInsertRequest; @@ -77,6 +76,7 @@ import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.CharacterConversions; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Reformat; /** * Code template allows the client to paste itself into the given @@ -125,7 +125,7 @@ private String completeInsertString; - private Formatter formatter; + private Reformat formatter; private TextSyncGroup textSyncGroup; @@ -247,30 +247,19 @@ // Build insert string outside of the atomic lock completeInsertString = getInsertText(); - BaseDocument bdoc = (doc instanceof BaseDocument) - ? (BaseDocument)doc - : null; + // Need to lock formatter first because CT's multiline text will be reformatted - formatter = null; - if (bdoc != null) { - formatter = bdoc.getFormatter(); - if (formatter != null) { - formatter.reformatLock(); - } - } + formatter = Reformat.get(doc); + formatter.lock(); try { - if (bdoc != null) { - bdoc.runAtomicAsUser(this); + if (doc instanceof BaseDocument) { + ((BaseDocument) doc).runAtomicAsUser(this); } else { // Otherwise run without atomic locking this.run(); } } finally { - if (bdoc != null) { - if (formatter != null) { - formatter.reformatUnlock(); - } - formatter = null; - } + formatter.unlock(); + formatter = null; completeInsertString = null; } } @@ -344,8 +333,7 @@ this.inserted = true; if (bdoc != null) { - formatter.reformat(bdoc, pos.getOffset(), - pos.getOffset() + completeInsertString.length()); + formatter.reformat(pos.getOffset(), pos.getOffset() + completeInsertString.length()); } if (!released) { --- a/editor.completion/nbproject/project.xml +++ a/editor.completion/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.deprecated.pre61completion/nbproject/project.xml +++ a/editor.deprecated.pre61completion/nbproject/project.xml @@ -19,8 +19,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.deprecated.pre61settings/nbproject/project.xml +++ a/editor.deprecated.pre61settings/nbproject/project.xml @@ -59,11 +59,20 @@ + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + + org.netbeans.modules.editor.lib - 2 + 3 --- a/editor.deprecated.pre65formatting/build.xml +++ a/editor.deprecated.pre65formatting/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.editor.deprecated.pre65formatting + + --- a/editor.deprecated.pre65formatting/manifest.mf +++ a/editor.deprecated.pre65formatting/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.editor.deprecated.pre65formatting/0 +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Layer: org/netbeans/modules/editor/deprecated/pre65formatting/layer.xml +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/deprecated/pre65formatting/Bundle.properties +OpenIDE-Module-Deprecated: true --- a/editor.deprecated.pre65formatting/nbproject/project.properties +++ a/editor.deprecated.pre65formatting/nbproject/project.properties @@ -0,0 +1,4 @@ +is.autoload=true +javac.source=1.5 +javac.compilerargs=-Xlint -Xlint:-serial +spec.version.base=1.0 --- a/editor.deprecated.pre65formatting/nbproject/project.xml +++ a/editor.deprecated.pre65formatting/nbproject/project.xml @@ -0,0 +1,103 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.editor.deprecated.pre65formatting + + + org.netbeans.modules.editor + + + + 3 + + + + + org.netbeans.modules.editor.indent + + + + 2 + 1.10 + + + + org.netbeans.modules.editor.lib + + + + 3 + + + + + org.netbeans.modules.editor.lib2 + + + + 1 + + + + + org.netbeans.modules.editor.mimelookup + + + + 1 + 1.10 + + + + org.netbeans.modules.editor.settings + + + + 1 + 1.22 + + + + org.netbeans.modules.editor.util + + + + 1 + 1.25 + + + + org.openide.text + + + + 6.21 + + + + org.openide.util + + + + 7.18 + + + + org.openide.util.lookup + + + + 8.4 + + + + + org.netbeans.editor + org.netbeans.editor.ext + org.netbeans.modules.editor + + + + --- a/editor.lib/src/org/netbeans/editor/Formatter.java +++ a/editor.lib/src/org/netbeans/editor/Formatter.java @@ -48,10 +48,8 @@ import java.io.IOException; import java.io.Writer; import java.io.CharArrayWriter; -import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; -import java.util.Stack; import java.util.WeakHashMap; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; @@ -64,6 +62,7 @@ import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.deprecated.pre65formatting.LegacyFormattersProvider; import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib.KitsTracker; import org.netbeans.modules.editor.lib.SettingsConversions; @@ -106,7 +105,8 @@ } else { Formatter formatter = kitClass2Formatter.get(kitClass); if (formatter == null) { - formatter = BaseKit.getKit(kitClass).createFormatter(); + BaseKit kit = BaseKit.getKit(kitClass); + formatter = callCreateFormatterMethod(kit); kitClass2Formatter.put(kitClass, formatter); } return formatter; @@ -130,9 +130,9 @@ if (formatter == null) { EditorKit editorKit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class); if (editorKit instanceof BaseKit) { - formatter = ((BaseKit) editorKit).createFormatter(); + formatter = callCreateFormatterMethod((BaseKit) editorKit); } else { - formatter = BaseKit.getKit(BaseKit.class).createFormatter(); + formatter = callCreateFormatterMethod(BaseKit.getKit(BaseKit.class)); } mimePath2Formatter.put(mimePath, formatter); } @@ -264,7 +264,7 @@ * @see BaseDocument.getTabSize() */ public int getTabSize() { - Document doc = getFormattingContextDocument(); + Document doc = LegacyFormattersProvider.getFormattingContextDocument(); if (doc != null) { Object ret = callIndentUtils("tabSize", doc); //NOI18N if (ret instanceof Integer) { @@ -300,7 +300,7 @@ * @see getSpacesPerTab() */ public int getShiftWidth() { - Document doc = getFormattingContextDocument(); + Document doc = LegacyFormattersProvider.getFormattingContextDocument(); if (doc != null) { Object ret = callIndentUtils("indentLevelSize", doc); //NOI18N if (ret instanceof Integer) { @@ -329,7 +329,7 @@ /** Should the typed tabs be expanded to the spaces? */ public boolean expandTabs() { - Document doc = getFormattingContextDocument(); + Document doc = LegacyFormattersProvider.getFormattingContextDocument(); if (doc != null) { Object ret = callIndentUtils("isExpandTabs", doc); //NOI18N if (ret instanceof Boolean) { @@ -353,7 +353,7 @@ * instead of one typed tab. */ public int getSpacesPerTab() { - Document doc = getFormattingContextDocument(); + Document doc = LegacyFormattersProvider.getFormattingContextDocument(); if (doc != null) { Object ret = callIndentUtils("indentLevelSize", doc); //NOI18N if (ret instanceof Integer) { @@ -378,11 +378,11 @@ // ------------------------------------------------------------------------ public String getIndentString(BaseDocument doc, int indent) { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { return getIndentString(indent, expandTabs(), doc.getTabSize()); } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } @@ -403,7 +403,7 @@ */ public void insertTabString (final BaseDocument doc, final int dotPos) throws BadLocationException { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { final BadLocationException[] badLocationExceptions = new BadLocationException [1]; doc.runAtomic (new Runnable () { @@ -436,7 +436,7 @@ if (badLocationExceptions[0] != null) throw badLocationExceptions [0]; } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } @@ -445,7 +445,7 @@ */ public void changeRowIndent (final BaseDocument doc, final int pos, final int newIndent) throws BadLocationException { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { final BadLocationException[] badLocationExceptions = new BadLocationException [1]; doc.runAtomic (new Runnable () { @@ -484,7 +484,7 @@ if (badLocationExceptions[0] != null) throw badLocationExceptions [0]; } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } @@ -498,7 +498,7 @@ */ public void changeBlockIndent (final BaseDocument doc, final int startPos, final int endPos, final int shiftCnt) throws BadLocationException { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { GuardedDocument gdoc = (doc instanceof GuardedDocument) ? (GuardedDocument)doc : null; @@ -538,14 +538,14 @@ if (badLocationExceptions[0] != null) throw badLocationExceptions [0]; } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } /** Shift line either left or right */ public void shiftLine(BaseDocument doc, int dotPos, boolean right) throws BadLocationException { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { int ind = doc.getShiftWidth(); if (!right) { @@ -560,7 +560,7 @@ ind = Math.max(ind, 0); changeRowIndent(doc, dotPos, ind); } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } @@ -572,7 +572,7 @@ */ public int reformat(BaseDocument doc, int startOffset, int endOffset) throws BadLocationException { - pushFormattingContextDocument(doc); + LegacyFormattersProvider.pushFormattingContextDocument(doc); try { try { CharArrayWriter cw = new CharArrayWriter(); @@ -594,7 +594,7 @@ return 0; } } finally { - popFormattingContextDocument(doc); + LegacyFormattersProvider.popFormattingContextDocument(doc); } } @@ -772,33 +772,6 @@ // private implementation // ------------------------------------------------------------------------ - private static ThreadLocal>> FORMATTING_CONTEXT_DOCUMENT = new ThreadLocal>>() { - @Override - protected Stack> initialValue() { - return new Stack>(); - } - }; - - private static Document getFormattingContextDocument() { - Stack> stack = FORMATTING_CONTEXT_DOCUMENT.get(); - return stack.isEmpty() ? null : stack.peek().get(); - } - - /* package */ static void pushFormattingContextDocument(Document doc) { - FORMATTING_CONTEXT_DOCUMENT.get().push(new WeakReference(doc)); - } - - /* package */ static void popFormattingContextDocument(Document doc) { - Stack> stack = FORMATTING_CONTEXT_DOCUMENT.get(); - assert !stack.empty() : "Calling popFormattingContextDocument without pushFormattingContextDocument"; //NOI18N - - Reference ref = stack.pop(); - Document docFromStack = ref.get(); - assert docFromStack == doc : "Popping " + doc + ", but the stack contains " + docFromStack; - - ref.clear(); - } - private static boolean noIndentUtils = false; private static WeakReference indentUtilsClassRef = null; private static Object callIndentUtils(String methodName, Document doc) { @@ -861,4 +834,12 @@ } } + private static Formatter callCreateFormatterMethod(BaseKit kit) { + try { + Method m = kit.getClass().getMethod("createFormatter"); //NOI18N + return (Formatter) m.invoke(kit); + } catch (Exception e) { + return new Formatter(kit.getClass()); + } + } } --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/Bundle.properties +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/Bundle.properties @@ -0,0 +1,38 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2008 Sun Microsystems, Inc. + +MSG_NegativeValue=Negative value is not allowed --- a/editor/src/org/netbeans/modules/editor/FormatterIndentEngine.java +++ a/editor/src/org/netbeans/modules/editor/FormatterIndentEngine.java @@ -44,12 +44,16 @@ package org.netbeans.modules.editor; +import java.awt.Toolkit; import java.io.*; import javax.swing.text.Document; import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.editor.BaseKit; import org.netbeans.editor.ext.ExtFormatter; +import org.openide.ErrorManager; import org.openide.text.IndentEngine; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; /** * Indent engine that delegates to formatter @@ -168,8 +172,15 @@ public void setSpacesPerTab(int spacesPerTab) { if (spacesPerTab <= 0) { - NbEditorUtilities.invalidArgument("MSG_NegativeValue"); // NOI18N - return; // value unchanged + IllegalArgumentException iae = new IllegalArgumentException("Invalid argument"); //NOI18N + ErrorManager errMan = Lookup.getDefault().lookup(ErrorManager.class); + + if (errMan != null) { + Toolkit.getDefaultToolkit().beep(); + errMan.annotate(iae, ErrorManager.USER, iae.getMessage(), NbBundle.getMessage(FormatterIndentEngine.class, "MSG_NegativeValue"), null, null); //NOI18N + } else { + throw iae; + } } int old = getFormatter().getSpacesPerTab(); --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/Bundle.properties +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/Bundle.properties @@ -0,0 +1,2 @@ +OpenIDE-Module-Display-Category=Editing +OpenIDE-Module-Name=Editor Formatting Prior 6.5 Separation --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/ComplexValueSettingsFactory.java +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/ComplexValueSettingsFactory.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.deprecated.pre65formatting; + +import javax.swing.text.EditorKit; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.modules.editor.FormatterIndentEngine; +import org.netbeans.modules.editor.IndentEngineFormatter; +import org.netbeans.modules.editor.NbEditorDocument; +import org.openide.text.IndentEngine; + +/** + * + * @author vita + */ +public final class ComplexValueSettingsFactory { + + private ComplexValueSettingsFactory() { + // no-op + } + + // ----------------------------------------------------------------------- + // 'formatter' setting + // ----------------------------------------------------------------------- + + public static Object getFormatterValue(MimePath mimePath, String settingName) { + assert settingName.equals(NbEditorDocument.FORMATTER) : "The getFormatter factory called for '" + settingName + "'"; //NOI18N + + IndentEngine eng = org.netbeans.modules.editor.impl.ComplexValueSettingsFactory.getIndentEngine(mimePath); + + if (eng != null) { + if (eng instanceof FormatterIndentEngine) { + return ((FormatterIndentEngine)eng).getFormatter(); + } else { + EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class); + if (kit != null) { + return new IndentEngineFormatter(kit.getClass(), eng); + } + } + } + + return null; + } +} --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/LegacyFormattersProvider.java +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/LegacyFormattersProvider.java @@ -0,0 +1,379 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.deprecated.pre65formatting; + +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Stack; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Element; +import javax.swing.text.Position; +import javax.swing.text.StyledDocument; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.editor.BaseDocument; +import org.netbeans.editor.Formatter; +import org.netbeans.editor.Utilities; +import org.netbeans.editor.ext.ExtFormatter; +import org.netbeans.modules.editor.indent.spi.Context; +import org.netbeans.modules.editor.indent.spi.ExtraLock; +import org.netbeans.modules.editor.indent.spi.IndentTask; +import org.netbeans.modules.editor.indent.spi.ReformatTask; +import org.netbeans.modules.editor.lib.KitsTracker; +import org.netbeans.spi.editor.mimelookup.MimeDataProvider; +import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author vita + */ +@org.openide.util.lookup.ServiceProvider(service=org.netbeans.spi.editor.mimelookup.MimeDataProvider.class) +public final class LegacyFormattersProvider implements MimeDataProvider { + + public LegacyFormattersProvider() { + // no-op + } + + // ------------------------------------------------------------------------ + // MimeDataProvider implementation + // ------------------------------------------------------------------------ + + public Lookup getLookup(MimePath mimePath) { + if (mimePath.size() == 1) { + IndentReformatTaskFactoriesProvider provider = IndentReformatTaskFactoriesProvider.get(mimePath); + if (provider != null) { + IndentTask.Factory legacyIndenter = provider.getIndentTaskFactory(); + ReformatTask.Factory legacyFormatter = provider.getReformatTaskFactory(); + TypedTextInterceptor.Factory legacyAutoIndenter = provider.getTypedTextInterceptorFactory(); + + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("'" + mimePath.getPath() + "' uses legacyIndenter=" + legacyIndenter //NOI18N + + ", legacyFormatter=" + legacyFormatter //NOI18N + + ", legacyAutoIndenter=" + legacyAutoIndenter); //NOI18N + } + + return Lookups.fixed(legacyIndenter, legacyFormatter, legacyAutoIndenter); + } + } + + return null; + } + + // ------------------------------------------------------------------------ + // Formatting context manipulation methods + // ------------------------------------------------------------------------ + + public static Document getFormattingContextDocument() { + Stack> stack = FORMATTING_CONTEXT_DOCUMENT.get(); + return stack.isEmpty() ? null : stack.peek().get(); + } + + public static void pushFormattingContextDocument(Document doc) { + FORMATTING_CONTEXT_DOCUMENT.get().push(new WeakReference(doc)); + } + + public static void popFormattingContextDocument(Document doc) { + Stack> stack = FORMATTING_CONTEXT_DOCUMENT.get(); + assert !stack.empty() : "Calling popFormattingContextDocument without pushFormattingContextDocument"; //NOI18N + + Reference ref = stack.pop(); + Document docFromStack = ref.get(); + assert docFromStack == doc : "Popping " + doc + ", but the stack contains " + docFromStack; + + ref.clear(); + } + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private static final Logger LOG = Logger.getLogger(LegacyFormattersProvider.class.getName()); + + private static ThreadLocal>> FORMATTING_CONTEXT_DOCUMENT = new ThreadLocal>>() { + @Override + protected Stack> initialValue() { + return new Stack>(); + } + }; + + private static final class IndentReformatTaskFactoriesProvider { + + public static IndentReformatTaskFactoriesProvider get(MimePath mimePath) { + Reference ref = cache.get(mimePath); + IndentReformatTaskFactoriesProvider provider = ref == null ? null : ref.get(); + if (provider == null) { + try { + Class kitClass = KitsTracker.getInstance().findKitClass(mimePath.getPath()); + Method createFormatterMethod = kitClass.getDeclaredMethod("createFormatter"); //NOI18N + provider = new IndentReformatTaskFactoriesProvider(mimePath); + cache.put(mimePath, new WeakReference(provider)); + } catch (Exception e) { + // ignore + } + } + return provider; + } + + public IndentTask.Factory getIndentTaskFactory() { + if (indentTaskFactory == null) { + indentTaskFactory = new IndentTask.Factory() { + public IndentTask createTask(Context context) { + Formatter formatter = getFormatter(); + if (formatter != null && context.document() instanceof BaseDocument) { + return new Indenter(context, formatter); + } else { + return null; + } + } + }; + } + return indentTaskFactory; + } + + public ReformatTask.Factory getReformatTaskFactory() { + if (reformatTaskFactory == null) { + reformatTaskFactory = new ReformatTask.Factory() { + public ReformatTask createTask(Context context) { + Formatter formatter = getFormatter(); + if (formatter != null && context.document() instanceof BaseDocument) { + return new Reformatter(context, formatter); + } else { + return null; + } + } + }; + } + return reformatTaskFactory; + } + + public TypedTextInterceptor.Factory getTypedTextInterceptorFactory() { + if (typedTextInterceptorFactory == null) { + typedTextInterceptorFactory = new TypedTextInterceptor.Factory() { + public TypedTextInterceptor createTypedTextInterceptor(MimePath mimePath) { + Formatter formatter = getFormatter(); + if (formatter instanceof ExtFormatter) { + return new AutoIndenter((ExtFormatter)formatter); + } else { + return null; + } + } + }; + } + return typedTextInterceptorFactory; + } + + // ------------------------------------------------------------------- + // private implementation + // ------------------------------------------------------------------- + + private static final Map> cache = new WeakHashMap>(); + private static final String NO_FORMATTER = new String("NO_FORMATTER"); //NOI18N + + private final MimePath mimePath; + + private IndentTask.Factory indentTaskFactory; + private ReformatTask.Factory reformatTaskFactory; + private TypedTextInterceptor.Factory typedTextInterceptorFactory; + private Object legacyFormatter; + + private IndentReformatTaskFactoriesProvider(MimePath mimePath) { + this.mimePath = mimePath; + } + + private Formatter getFormatter() { + if (legacyFormatter == null) { + EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class); + if (kit != null) { + try { + Method createFormatterMethod = kit.getClass().getDeclaredMethod("createFormatter"); //NOI18N + legacyFormatter = createFormatterMethod.invoke(kit); + } catch (Exception e) { + legacyFormatter = e; + } + } else { + legacyFormatter = NO_FORMATTER; + } + } + return legacyFormatter instanceof Formatter ? (Formatter) legacyFormatter : null; + } + + } // End of IndentReformatTaskFactoriesProvider class + + private static final class Indenter implements IndentTask { + + private final Context context; + private final Formatter formatter; + + public Indenter(Context context, Formatter formatter) { + this.context = context; + this.formatter = formatter; + } + + public void reindent() throws BadLocationException { + Document doc = context.document(); + int startOffset = context.startOffset(); + int endOffset = context.endOffset(); + + pushFormattingContextDocument(doc); + try { + // Original formatter does not have reindentation of multiple lines + // so reformat start line and continue for each line. + Element lineRootElem = lineRootElement(doc); + Position endPos = doc.createPosition(endOffset); + do { + startOffset = formatter.indentLine(doc, startOffset); + int startLineIndex = lineRootElem.getElementIndex(startOffset) + 1; + if (startLineIndex >= lineRootElem.getElementCount()) + break; + Element lineElem = lineRootElem.getElement(startLineIndex); + startOffset = lineElem.getStartOffset(); // Move to next line + } while (startOffset < endPos.getOffset()); + } finally { + popFormattingContextDocument(doc); + } + } + + public ExtraLock indentLock() { + return new ExtraLock() { + public void lock() { + formatter.indentLock(); + } + + public void unlock() { + formatter.indentUnlock(); + } + }; + } + + private static Element lineRootElement(Document doc) { + return (doc instanceof StyledDocument) + ? ((StyledDocument)doc).getParagraphElement(0).getParentElement() + : doc.getDefaultRootElement(); + } + } // End of Indenter class + + private static final class Reformatter implements ReformatTask { + + private final Context context; + private final Formatter formatter; + + public Reformatter(Context context, Formatter formatter) { + this.context = context; + this.formatter = formatter; + } + + public void reformat() throws BadLocationException { + pushFormattingContextDocument(context.document()); + try { + formatter.reformat((BaseDocument) context.document(), context.startOffset(), context.endOffset()); + } finally { + popFormattingContextDocument(context.document()); + } + } + + public ExtraLock reformatLock() { + return new ExtraLock() { + public void lock() { + formatter.reformatLock(); + } + + public void unlock() { + formatter.reformatUnlock(); + } + }; + } + } // End of Reformatter class + + private static final class AutoIndenter implements TypedTextInterceptor { + + private final ExtFormatter formatter; + + public AutoIndenter(ExtFormatter formatter) { + this.formatter = formatter; + } + + public boolean beforeInsert(Context context) throws BadLocationException { + // no-op + return false; + } + + public void insert(MutableContext context) throws BadLocationException { + // no-op + } + + public void afterInsert(Context context) throws BadLocationException { + if (context.getDocument() instanceof BaseDocument) { + BaseDocument doc = (BaseDocument) context.getDocument(); + int [] fmtBlk = formatter.getReformatBlock(context.getComponent(), context.getText()); + if (fmtBlk != null) { + try { + + fmtBlk[0] = Utilities.getRowStart(doc, fmtBlk[0]); + fmtBlk[1] = Utilities.getRowEnd(doc, fmtBlk[1]); + + //this was the of #18922, that causes the bug #20198 + //ef.reformat(doc, fmtBlk[0], fmtBlk[1]); + + //bugfix of the bug #20198. Bug #18922 is fixed too as well as #6968 + formatter.reformat(doc, fmtBlk[0], fmtBlk[1], true); + + } catch (BadLocationException e) { + } catch (IOException e) { + } + } + } + } + + public void cancelled(Context context) { + // no-op + } + + } // End of AutoIndenter class +} --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/layer.xml +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/layer.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + --- a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/preferences.xml +++ a/editor.deprecated.pre65formatting/src/org/netbeans/modules/editor/deprecated/pre65formatting/preferences.xml @@ -0,0 +1,52 @@ + + + + + + + + + --- a/editor.errorstripe/nbproject/project.xml +++ a/editor.errorstripe/nbproject/project.xml @@ -71,8 +71,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.highlights/nbproject/project.xml +++ a/editor.highlights/nbproject/project.xml @@ -54,8 +54,8 @@ - 1 - 1.27 + 3 + 3.1 @@ -115,6 +115,15 @@ 1.0 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + --- a/editor.indent/apichanges.xml +++ a/editor.indent/apichanges.xml @@ -108,6 +108,36 @@ + + Adding org.netbeans.modules.editor.indent.spi.support.AutomatedIndenting + + + + + + The AutomatedIndenting support class provides regex-based + automatic re-indentation of lines as user types. This is a replacement for + the deprecated ExtFormatter.getReformatBlock and INDENT_HOT_CHARS_ACCEPTOR + setting. + + + + + + + Adding org.netbeans.modules.editor.indent.api.Indent.indentNewLine + + + + + + The new method int Indent.indentNewLine(int) was added as + a replacement for the old int Formatter.indentNewLine(BaseDocument, int). + + + + + Adding CodeStylePreferences.Provider --- a/editor.indent/manifest.mf +++ a/editor.indent/manifest.mf @@ -4,3 +4,4 @@ OpenIDE-Module-Layer: org/netbeans/modules/editor/indent/resources/layer.xml AutoUpdate-Show-In-Client: false OpenIDE-Module-Recommends: org.netbeans.modules.editor.indent.spi.CodeStylePreferences.Provider +OpenIDE-Module-Specification-Version: 1.20.0 --- a/editor.indent/nbproject/project.properties +++ a/editor.indent/nbproject/project.properties @@ -42,7 +42,6 @@ javac.source=1.6 javac.compilerargs=-Xlint:unchecked -spec.version.base=1.20.0 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml --- a/editor.indent/nbproject/project.xml +++ a/editor.indent/nbproject/project.xml @@ -50,12 +50,12 @@ org.netbeans.modules.editor.indent - org.netbeans.modules.editor.lib + org.netbeans.modules.editor.lib2 - 2 - + 1 + 1.31 @@ -176,6 +176,7 @@ org.netbeans.modules.editor.indent.api org.netbeans.modules.editor.indent.spi + org.netbeans.modules.editor.indent.spi.support --- a/editor.indent/src/org/netbeans/modules/editor/indent/FormatterImpl.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/FormatterImpl.java @@ -1,218 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.editor.indent; - -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Method; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; -import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; -import org.netbeans.editor.GuardedException; -import org.netbeans.editor.ext.ExtFormatter; -import org.netbeans.modules.editor.indent.api.IndentUtils; - -/** - * Indentation and code reformatting services for a swing text document. - * - * @author Miloslav Metelka - */ -public final class FormatterImpl extends ExtFormatter { - - private final Document doc; - private final Formatter defaultFormatter; - private final IndentImpl indentImpl; - - private final boolean tabSizeOverriden; - private final boolean shiftWidthOverriden; - private final boolean expandTabsOverriden; - - FormatterImpl(Formatter defaultFormatter, Document doc) { - super(defaultFormatter.getKitClass()); - - this.defaultFormatter = defaultFormatter; - this.tabSizeOverriden = isOverriden(defaultFormatter.getClass(), "getTabSize"); //NOI18N - this.shiftWidthOverriden = isOverriden(defaultFormatter.getClass(), "getShiftWidth"); //NOI18N - this.expandTabsOverriden = isOverriden(defaultFormatter.getClass(), "expandTabs"); //NOI18N - - this.doc = doc; - assert doc != null; - - this.indentImpl = IndentImpl.get(doc); - indentImpl.setDefaultFormatter(defaultFormatter); - } - - @Override - public int[] getReformatBlock(JTextComponent target, String typedText) { - return (defaultFormatter instanceof ExtFormatter) - ? ((ExtFormatter)defaultFormatter).getReformatBlock(target, typedText) - : null; - } - - @Override - public void indentLock() { - indentImpl.indentLock(); - } - - @Override - public void indentUnlock() { - indentImpl.indentUnlock(); - } - - @Override - public void reformatLock() { - indentImpl.reformatLock(); - } - - @Override - public void reformatUnlock() { - indentImpl.reformatUnlock(); - } - - @Override - public int getTabSize() { - if (tabSizeOverriden) { - return defaultFormatter.getTabSize(); - } else { - return IndentUtils.tabSize(doc); - } - } - - @Override - public int getSpacesPerTab() { - return defaultFormatter.getSpacesPerTab(); - } - - @Override - public int getShiftWidth() { - if (shiftWidthOverriden) { - return defaultFormatter.getShiftWidth(); - } else { - return IndentUtils.indentLevelSize(doc); - } - } - - @Override - public boolean expandTabs() { - if (expandTabsOverriden) { - return defaultFormatter.expandTabs(); - } else { - return IndentUtils.isExpandTabs(doc); - } - } - - @Override - public int indentLine(Document doc, int offset) { - return indentLine(doc, offset, false); - } - - /** Inserts new line at given position and indents the new line with - * spaces. - * - * @param doc the document to work on - * @param offset the offset of a character on the line - * @return new offset to place cursor to - */ - @Override - public int indentNewLine(Document doc, int offset) { - return indentLine(doc, offset, true); - } - - private int indentLine(Document doc, int offset, boolean indentNewLine) { - try { - return indentImpl.reindent(offset, offset, offset, indentNewLine); - } catch (GuardedException e) { - java.awt.Toolkit.getDefaultToolkit().beep(); - } catch (BadLocationException e) { - throw new IllegalStateException(e); - } - return offset; - } - - @Override - public Writer reformat(BaseDocument doc, int startOffset, int endOffset, - boolean indentOnly) throws BadLocationException, IOException { - // TBD delegate somehow - return (defaultFormatter instanceof ExtFormatter) - ? ((ExtFormatter)defaultFormatter).reformat(doc, startOffset, endOffset, indentOnly) - : null; - } - - @Override - public int reformat(BaseDocument doc, int startOffset, int endOffset) - throws BadLocationException { - if (doc != indentImpl.document()) - return endOffset - startOffset; // should not happen in reality - indentImpl.reformat(startOffset, endOffset); - TaskHandler handler = indentImpl.reformatHandler(); - return (handler != null && handler.hasItems()) - ? Math.max(handler.endPos().getOffset() - startOffset, 0) - : endOffset - startOffset; - } - - @Override - public Writer createWriter(Document doc, int offset, Writer writer) { - if (doc != indentImpl.document()) { - throw new IllegalStateException("Unexpected document doc=" + doc); //NOI18N - } - return new FormatterWriterImpl(indentImpl, offset, writer); - } - - private static boolean isOverriden(Class clazz, String methodName) { - while (clazz != Formatter.class && clazz != ExtFormatter.class) { - for(Method m : clazz.getDeclaredMethods()) { - if (m.getName().equals(methodName) && m.getParameterTypes().length == 0) { - return true; - } - } - - clazz = clazz.getSuperclass(); - } - - return false; - } -} --- a/editor.indent/src/org/netbeans/modules/editor/indent/FormatterOverrideImpl.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/FormatterOverrideImpl.java @@ -1,64 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.editor.indent; - -import javax.swing.text.Document; -import org.netbeans.editor.Formatter; -import org.netbeans.modules.editor.lib.FormatterOverride; - -/** - * Indentation and code reformatting services for a swing text document. - * - * @author Miloslav Metelka - */ -@org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.editor.lib.FormatterOverride.class) -public final class FormatterOverrideImpl implements FormatterOverride { - - public Formatter getFormatter(Document doc, Formatter defaultFormatter) { - // Always override and possibly delegate to default formatter - return new FormatterImpl(defaultFormatter, doc); - } - -} --- a/editor.indent/src/org/netbeans/modules/editor/indent/IndentImpl.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/IndentImpl.java @@ -49,12 +49,9 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; -import javax.swing.text.Position; import javax.swing.text.StyledDocument; import org.netbeans.modules.editor.indent.api.Indent; import org.netbeans.modules.editor.indent.api.Reformat; -import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; /** * Indentation and code reformatting services for a swing text document. @@ -72,7 +69,8 @@ indentImpl = new IndentImpl(doc); doc.putProperty(IndentImpl.class, indentImpl); } - indentImpl.refresh(); +// XXX: formatting infra cleanup +// indentImpl.refresh(); return indentImpl; } @@ -85,9 +83,10 @@ private TaskHandler indentHandler; private TaskHandler reformatHandler; + +// XXX: formatting infra cleanup +// private Formatter defaultFormatter; - private Formatter defaultFormatter; - private Thread lockThread; private int lockExtraDepth; @@ -116,17 +115,18 @@ this.reformat = reformat; } - void setDefaultFormatter(Formatter defaultFormatter) { - this.defaultFormatter = defaultFormatter; - } - - void refresh() { - if (defaultFormatter == null) { - if (doc instanceof BaseDocument) { - defaultFormatter = ((BaseDocument)doc).getLegacyFormatter(); - } - } - } +// XXX: formatting infra cleanup +// void setDefaultFormatter(Formatter defaultFormatter) { +// this.defaultFormatter = defaultFormatter; +// } +// +// void refresh() { +// if (defaultFormatter == null) { +// if (doc instanceof BaseDocument) { +// defaultFormatter = ((BaseDocument)doc).getLegacyFormatter(); +// } +// } +// } public synchronized void indentLock() { if (LOG.isLoggable(Level.FINE)) { @@ -221,7 +221,8 @@ // Find begining of line Element lineRootElem = lineRootElement(doc); // Correct the start offset to point to the begining of the start line - boolean done = false; +// XXX: formatting infra cleanup +// boolean done = false; if (indentHandler.hasItems()) { // When indenting newline first insert a plain newline if (indentNewLine) { @@ -257,37 +258,40 @@ // Perform whole reindent on top and possibly embedded levels indentHandler.runTasks(); - done = true; +// XXX: formatting infra cleanup +// done = true; } - // Fallback to Formatter - if (!done && doc instanceof BaseDocument && defaultFormatter != null) { - if (indentNewLine) { - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Defaulting reindent() to indentNewLine() in legacy formatter " + // NOI18N - defaultFormatter + '\n'); - } - // Fallback to indentNewLine() will insert '\n' - int newCaretOffset = defaultFormatter.indentNewLine(doc, caretOffset); - indentHandler.setCaretOffset(newCaretOffset); - } else { // Indent line - // Original formatter does not have reindentation of multiple lines - // so reformat start line and continue for each line. - Position endPos = doc.createPosition(endOffset); - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Defaulting reindent() to indentLine() in legacy formatter " + // NOI18N - defaultFormatter + '\n'); - } - do { - startOffset = defaultFormatter.indentLine(doc, startOffset); - int startLineIndex = lineRootElem.getElementIndex(startOffset) + 1; - if (startLineIndex >= lineRootElem.getElementCount()) - break; - Element lineElem = lineRootElem.getElement(startLineIndex); - startOffset = lineElem.getStartOffset(); // Move to next line - } while (startOffset < endPos.getOffset()); - } - } +// XXX: formatting infra cleanup +// // Fallback to Formatter +// if (!done && doc instanceof BaseDocument && defaultFormatter != null) { +// if (indentNewLine) { +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Defaulting reindent() to indentNewLine() in legacy formatter " + // NOI18N +// defaultFormatter + '\n'); +// } +// // Fallback to indentNewLine() will insert '\n' +// int newCaretOffset = defaultFormatter.indentNewLine(doc, caretOffset); +// indentHandler.setCaretOffset(newCaretOffset); +// } else { // Indent line +// // Original formatter does not have reindentation of multiple lines +// // so reformat start line and continue for each line. +// Position endPos = doc.createPosition(endOffset); +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Defaulting reindent() to indentLine() in legacy formatter " + // NOI18N +// defaultFormatter + '\n'); +// } +// do { +// startOffset = defaultFormatter.indentLine(doc, startOffset); +// int startLineIndex = lineRootElem.getElementIndex(startOffset) + 1; +// if (startLineIndex >= lineRootElem.getElementCount()) +// break; +// Element lineElem = lineRootElem.getElement(startLineIndex); +// startOffset = lineElem.getStartOffset(); // Move to next line +// } while (startOffset < endPos.getOffset()); +// } +// } + return indentHandler.caretOffset(); } finally { if (runUnlocked) @@ -310,7 +314,8 @@ if (runUnlocked) { reformatHandler.collectTasks(); } - boolean done = false; +// XXX: formatting infra cleanup +// boolean done = false; if (reformatHandler.hasItems()) { reformatHandler.setGlobalBounds( doc.createPosition(startOffset), @@ -320,18 +325,20 @@ reformatHandler.runTasks(); // Perform reformatting of the top section and possible embedded sections - done = true; +// XXX: formatting infra cleanup +// done = true; } - // Fallback to Formatter - if (!done && doc instanceof BaseDocument && defaultFormatter != null) { - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Defaulting reformat() to reformat() in legacy formatter " + // NOI18N - defaultFormatter + '\n'); - } - BaseDocument bdoc = (BaseDocument)doc; - defaultFormatter.reformat(bdoc, startOffset, endOffset); - } +// XXX: formatting infra cleanup +// // Fallback to Formatter +// if (!done && doc instanceof BaseDocument && defaultFormatter != null) { +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Defaulting reformat() to reformat() in legacy formatter " + // NOI18N +// defaultFormatter + '\n'); +// } +// BaseDocument bdoc = (BaseDocument)doc; +// defaultFormatter.reformat(bdoc, startOffset, endOffset); +// } } finally { if (runUnlocked) reformatHandler = null; --- a/editor.indent/src/org/netbeans/modules/editor/indent/TaskHandler.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/TaskHandler.java @@ -62,7 +62,6 @@ import org.netbeans.api.lexer.LanguagePath; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; -import org.netbeans.editor.GuardedDocument; import org.netbeans.lib.editor.util.swing.MutablePositionRegion; import org.netbeans.modules.editor.indent.spi.Context; import org.netbeans.modules.editor.indent.spi.ExtraLock; @@ -464,10 +463,10 @@ } // Filter out guarded regions - if (indentRegions.size() > 0 && doc instanceof GuardedDocument) { - MutablePositionRegion region = IndentSpiPackageAccessor.get().positionRegion(indentRegions.get(0)); - int regionStartOffset = region.getStartOffset(); - GuardedDocument gdoc = (GuardedDocument)doc; +// if (indentRegions.size() > 0 && doc instanceof GuardedDocument) { +// MutablePositionRegion region = IndentSpiPackageAccessor.get().positionRegion(indentRegions.get(0)); +// int regionStartOffset = region.getStartOffset(); +// GuardedDocument gdoc = (GuardedDocument)doc; // int gbStartOffset = guardedBlocks.adjustToBlockEnd(region.getEndOffset()); // MarkBlockChain guardedBlocks = gdoc.getGuardedBlockChain(); // if (guardedBlocks != null && guardedBlocks.getChain() != null) { @@ -500,7 +499,7 @@ // } // } // } - } +// } } catch (BadLocationException e) { Exceptions.printStackTrace(e); indentRegions = Collections.emptyList(); --- a/editor.indent/src/org/netbeans/modules/editor/indent/api/Indent.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/api/Indent.java @@ -171,4 +171,23 @@ impl.reindent(startOffset, endOffset, startOffset, false); } + /** + * Creates new line at offset and reindents it. + * + *

This method will insert a line break (ie EOL character) at the specified + * offset and then reindent the newly created line. The method will return the + * offset of the indented beginning of the new line. That is the offset where + * the new text should appear when typing in the document. + * + * @param offset The document offset where the new line will be created. + * + * @return The offset of the first non-white character (or the EOL character) + * on the new line. This is basically where the caret should be moved to. + * @throws javax.swing.text.BadLocationException + * @since 1.10 + */ + public int indentNewLine(int offset) throws BadLocationException { + return impl.reindent(offset, offset, offset, true); + } + } --- a/editor.indent/src/org/netbeans/modules/editor/indent/spi/support/AutomatedIndenting.java +++ a/editor.indent/src/org/netbeans/modules/editor/indent/spi/support/AutomatedIndenting.java @@ -0,0 +1,238 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.indent.spi.support; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor; + +/** + * This class contains factory methods for typing interceptor implementations + * that can be used for automated text indenting. + * + *

The interceptors provided by this class are implementations of Typing Hooks SPI + * interfaces that can be registered in MimeLookup. Typically there are + * two factory methods for each interceptor implementation. One factory method creates + * the implementated interceptor and is suitable for direct use from java code. The + * other factory method creates a factory object that can be registred in an XML layer + * as an .instance file. + * + * @author Vita Stejskal + * @since 1.11 + */ +public final class AutomatedIndenting { + + /** + * Creates TypedTextInterceptor that automatically + * indents a line depending on text typed on that line. + * + *

The text patterns recognized by the intercetor are defined in form + * of regular expressions passed to this method. The interceptor will match + * all text before the caret on the line where a user is typing (including + * the last typed character) against the regular expression patterns. If the text + * matches at least one pattern the interceptor will reindent the line by + * calling {@link Indent#reindent(int)} method. + * + * @param linePatterns The regular expressions that will be used for matching + * text typed on a line. Any matching pattern will trigger the line reindentation. + * + * @return The interceptor that checks text typed on a line and reindents the line + * if it matches any of the linePatterns. + * + * @since 1.11 + */ + public static TypedTextInterceptor createHotCharsIndenter(Pattern... linePatterns) { + return new RegExBasedIndenter(linePatterns); + } + + /** + * This is a version of {@link #createHotCharsIndenter(java.util.regex.Pattern[])} method suitable + * for XML layers registration. + * + *

+ *

Here is an example of an XML layer registration done + * for text/x-java mime type. The registered interceptor will indent + * any line that contains whitespace followed by 'else'. The ending 'e' character is + * the last character typed on the line. + * + *

+     * <folder name="Editors">
+     *  <folder name="text">
+     *   <folder name="x-java">
+     *    <file name="org-something-AutoIndenter.instance">
+     *     <attr name="instanceOf" stringvalue="org.netbeans.spi.editor.typinghooks.TypedTextInterceptor"/>
+     *     <attr name="instanceCreate"
+     *              methodvalue="org.netbeans.modules.editor.indent.spi.support.AutomatedIndenting.createHotCharsIndenter"/>
+     *     <attr name="regex1" stringvalue="\s*else"/>
+     *    </file>
+     *   </folder>
+     *  </folder>
+     * </folder>
+     * 
+ *
+ * + * @param fileAttributes The map of FileObject attributes. This method + * will recognize any attributes, which name starts with regex and will + * try to interpret their value as a regular expression. These regular expressions + * will then be used as linePatterns when calling createHotCharsIndenter(Pattern...) method. + * + * @return The interceptor factory that will provide a regular expressions based + * automated indenter returned from the {@link #createHotCharsIndenter(java.util.regex.Pattern[])} method. + * The list of line patterns will be recovered from the fileAttributes. + * + * @since 1.11 + */ + public static TypedTextInterceptor.Factory createHotCharsIndenter(Map fileAttributes) { + final ArrayList linePatterns = new ArrayList(); + + for(Object key : fileAttributes.keySet()) { + if (key.toString().startsWith("regex")) { //NOI18N + Object value = fileAttributes.get(key); + try { + Pattern pattern = Pattern.compile(value.toString()); + linePatterns.add(pattern); + } catch (PatternSyntaxException pse) { + LOG.log(Level.WARNING, null, pse); + } + } + } + + return new TypedTextInterceptor.Factory() { + public TypedTextInterceptor createTypedTextInterceptor(MimePath mimePath) { + return createHotCharsIndenter(linePatterns.toArray(new Pattern [linePatterns.size()])); + } + }; + } + + // ------------------------------------------------------------------------ + // private + // ------------------------------------------------------------------------ + + private static final Logger LOG = Logger.getLogger(AutomatedIndenting.class.getName()); + + private static final class RegExBasedIndenter implements TypedTextInterceptor { + + private final Pattern [] linePatterns; + + public RegExBasedIndenter(Pattern... linePatterns) { + this.linePatterns = linePatterns; + } + + public boolean beforeInsert(Context context) { + // no-op + return false; + } + + public void insert(MutableContext context) { + // no-op + } + + public void afterInsert(Context context) { + int textLen = context.getText().length(); + if (textLen > 0) { + CharSequence lineText; + final int lineStartOffset; + final int lineEndOffset; + + try { + Element lineElement = DocumentUtilities.getParagraphElement(context.getDocument(), context.getOffset()); + lineText = DocumentUtilities.getText(context.getDocument(), + lineElement.getStartOffset(), + context.getOffset() - lineElement.getStartOffset() + textLen); + lineStartOffset = lineElement.getStartOffset(); + lineEndOffset = Math.max(lineStartOffset, lineElement.getEndOffset() - 1); // without EOL + } catch (Exception e) { + LOG.log(Level.INFO, null, e); + return; + } + + for(Pattern p : linePatterns) { + if (p.matcher(lineText).matches()) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("The line '" + lineText + "' matches '" + p.pattern() //NOI18N + + "' -> calling Indent.reindent(" + lineStartOffset + ", " + lineEndOffset + ")"); //NOI18N + } + + final Indent indenter = Indent.get(context.getDocument()); + indenter.lock(); + try { + runAtomicAsUser(context.getDocument(), new Runnable() { + public void run() { + try { + indenter.reindent(lineStartOffset, lineEndOffset); + } catch (BadLocationException ble) { + LOG.log(Level.INFO, null, ble); + } + } + }); + } finally { + indenter.unlock(); + } + break; + } + } + } + } + + public void cancelled(Context context) { + // no-op + } + + private static void runAtomicAsUser(Document doc, Runnable run) { + try { + Method runAtomicAsUserMethod = doc.getClass().getMethod("runAtomicAsUser", Runnable.class); //NOI18N + runAtomicAsUserMethod.invoke(doc, run); + } catch (Exception e) { + LOG.log(Level.INFO, null, e); + } + } + } // End of RegExBasedIndenter class +} --- a/editor.lib/apichanges.xml +++ a/editor.lib/apichanges.xml @@ -107,6 +107,90 @@ + + + Deprecating old formatting API + + + + + +

+ The old formatting API (org.netbeans.editor.Formatter & co.) + has been deprecated and moved to editor.deprecated.pre65formatting module. +

+

The exact API changes are listed in the sigtest report below.

+
+-sigtest-init:
+     [echo] sigtest check: editor.lib; cnb: org-netbeans-modules-editor-lib; email: api-changes@netbeans.org; type: check
+
+check-sigtest:
+    [mkdir] Created dir: /work/netbeans/main2m/editor.lib/build/test/sigtest/results
+  [sigtest] /work/netbeans/main2m/editor.lib/build/test/sigtest/results/org.netbeans.modules.editor.lib
+  [sigtest] Packages: org.netbeans.editor.*, org.netbeans.editor.ext.*, org.netbeans.editor.view.spi.*, org.netbeans.lib.editor.hyperlink.spi.*, org.netbeans.lib.editor.view.*
+  [sigtest] Missing /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/rt.jar
+  [sigtest] 
+  [sigtest] email: api-changes@netbeans.org
+  [sigtest] SignatureTest report
+  [sigtest] Base version: 2.11.0
+  [sigtest] Tested version: 2.11.0
+  [sigtest] Check mode: bin [throws removed]
+  [sigtest] Constant checking: on
+  [sigtest] 
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.BaseDocument
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getFormatter()
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getLegacyFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.BaseKit
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseKit.createFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.BaseKit$PasteAction
+  [sigtest]   "E1.2 - API type removed" : method public static void org.netbeans.editor.BaseKit$PasteAction.indentBlock(org.netbeans.editor.BaseDocument,int,int)
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.Formatter
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.editor.Formatter
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.GuardedDocument
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getFormatter()
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getLegacyFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.AbstractFormatLayer
+  [sigtest]   "E1.2 - API type removed" : CLASS public abstract org.netbeans.editor.ext.AbstractFormatLayer
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.ExtFormatSupport
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.editor.ext.ExtFormatSupport
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.ExtFormatter
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.editor.ext.ExtFormatter
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.ExtFormatter$Simple
+  [sigtest]   "E1.2 - API type removed" : CLASS public static org.netbeans.editor.ext.ExtFormatter$Simple
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.ExtKit
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseKit.createFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.FormatLayer
+  [sigtest]   "E1.2 - API type removed" : CLASS public abstract interface org.netbeans.editor.ext.FormatLayer
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.FormatSupport
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.editor.ext.FormatSupport
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.FormatTokenPosition
+  [sigtest]   "E1.2 - API type removed" : CLASS public abstract interface org.netbeans.editor.ext.FormatTokenPosition
+  [sigtest] 
+  [sigtest] Class org.netbeans.editor.ext.FormatWriter
+  [sigtest]   "E1.2 - API type removed" : CLASS public final org.netbeans.editor.ext.FormatWriter
+  [sigtest] 
+  [sigtest] /work/netbeans/main2m/editor.lib/build/test/sigtest/results/org-netbeans-modules-editor-lib.xml: 1 failures in /work/netbeans/main2m/editor.lib/nbproject/org-netbeans-modules-editor-lib.sig
+
+BUILD FAILED
+Signature tests return code is wrong (1), check the messages above
+
+
+ +
+ Support for heavyweight tooltips --- a/editor.lib/manifest.mf +++ a/editor.lib/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 -OpenIDE-Module: org.netbeans.modules.editor.lib/2 +OpenIDE-Module: org.netbeans.modules.editor.lib/3 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib/Bundle.properties -OpenIDE-Module-Implementation-Version: 10 +OpenIDE-Module-Implementation-Version: 11 --- a/editor.lib/module-auto-deps.xml +++ a/editor.lib/module-auto-deps.xml @@ -48,7 +48,7 @@ - Old editor settings and completion APIs have been deprecated, see http://wiki.netbeans.org/EditorSettingsUpgrade and update your module. + The old editor settings and completion APIs have been deprecated, see http://wiki.netbeans.org/EditorSettingsUpgrade and update your module. @@ -56,6 +56,8 @@ + + --- a/editor.lib/nbproject/project.properties +++ a/editor.lib/nbproject/project.properties @@ -42,7 +42,7 @@ javac.compilerargs=-Xlint:unchecked javac.source=1.6 -spec.version.base=2.11.0 +spec.version.base=3.1 is.autoload=true javadoc.arch=${basedir}/arch.xml --- a/editor.lib/nbproject/project.xml +++ a/editor.lib/nbproject/project.xml @@ -67,6 +67,15 @@
+ org.netbeans.modules.editor.indent + + + + 2 + 1.9 + + + org.netbeans.modules.editor.lib2 @@ -136,6 +145,14 @@ + org.openide.filesystems + + + + 7.13 + + + org.openide.util --- a/editor.lib/src/org/netbeans/editor/Abbrev.java +++ a/editor.lib/src/org/netbeans/editor/Abbrev.java @@ -56,6 +56,7 @@ import javax.swing.text.Caret; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Reformat; /** * Abbreviation support allowing to expand defined character sequences @@ -296,12 +297,17 @@ } if(ins.indexOf("\n") != -1) { // NOI18N - Formatter formatter = doc.getFormatter(); - formatter.reformatLock(); + Reformat formatter = Reformat.get(doc); + formatter.lock(); try { - formatter.reformat(doc, dotPos, dotPos + ins.length()); + doc.atomicLock(); + try { + formatter.reformat(dotPos, dotPos + ins.length()); + } finally { + doc.atomicUnlock(); + } } finally { - formatter.reformatUnlock(); + formatter.unlock(); } } --- a/editor.lib/src/org/netbeans/editor/ActionFactory.java +++ a/editor.lib/src/org/netbeans/editor/ActionFactory.java @@ -45,6 +45,7 @@ package org.netbeans.editor; import java.awt.Component; +import java.awt.Cursor; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; @@ -52,6 +53,7 @@ import java.util.Map; import java.util.HashMap; import java.util.HashSet; +import java.util.logging.Level; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.ButtonModel; @@ -77,6 +79,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.ImageIcon; import javax.swing.JToggleButton; import javax.swing.event.ChangeListener; import javax.swing.text.AbstractDocument; @@ -89,6 +92,9 @@ import org.netbeans.api.progress.ProgressUtils; import org.netbeans.modules.editor.lib2.search.EditorFindSupport; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.modules.editor.indent.api.Reformat; +import org.netbeans.modules.editor.lib2.typinghooks.TypedBreakInterceptorsManager; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; @@ -107,6 +113,9 @@ public class ActionFactory { + // -J-Dorg.netbeans.editor.ActionFactory.level=FINE + private static final Logger LOG = Logger.getLogger(ActionFactory.class.getName()); + private ActionFactory() { // no instantiation } @@ -133,12 +142,14 @@ doc.runAtomicAsUser (new Runnable () { public void run () { DocumentUtilities.setTypingModification(doc, true); - Formatter.pushFormattingContextDocument(doc); try { if (Utilities.isSelectionShowing(caret)) { // block selected try { - doc.getFormatter().changeBlockIndent(doc, - target.getSelectionStart(), target.getSelectionEnd(), -1); + BaseKit.changeBlockIndent( + doc, + target.getSelectionStart(), + target.getSelectionEnd(), + -1); } catch (GuardedException e) { target.getToolkit().beep(); } catch (BadLocationException e) { @@ -150,7 +161,7 @@ int firstNW = Utilities.getRowFirstNonWhite(doc, caret.getDot()); int endOffset = Utilities.getRowEnd(doc, caret.getDot()); if (firstNW == -1 || (firstNW >= caret.getDot())) - doc.getFormatter().changeBlockIndent(doc, startOffset, endOffset, -1); + BaseKit.changeBlockIndent(doc, startOffset, endOffset, -1); else { // TODO: // after we will have action which will do opposite to "tab" action @@ -164,7 +175,6 @@ } } } finally { - Formatter.popFormattingContextDocument(doc); DocumentUtilities.setTypingModification(doc, false); } } @@ -1447,22 +1457,21 @@ doc.runAtomicAsUser (new Runnable () { public void run () { DocumentUtilities.setTypingModification(doc, true); - Formatter.pushFormattingContextDocument(doc); try { boolean right = BaseKit.shiftLineRightAction.equals(getValue(Action.NAME)); if (Utilities.isSelectionShowing(caret)) { - doc.getFormatter().changeBlockIndent(doc, - target.getSelectionStart(), target.getSelectionEnd(), - right ? +1 : -1); + BaseKit.changeBlockIndent( + doc, + target.getSelectionStart(), target.getSelectionEnd(), + right ? +1 : -1); } else { - doc.getFormatter().shiftLine(doc, caret.getDot(), right); + BaseKit.shiftLine(doc, caret.getDot(), right); } } catch (GuardedException e) { target.getToolkit().beep(); } catch (BadLocationException e) { e.printStackTrace(); } finally { - Formatter.popFormattingContextDocument(doc); DocumentUtilities.setTypingModification(doc, false); } } @@ -1494,9 +1503,8 @@ final GuardedDocument gdoc = (doc instanceof GuardedDocument) ? (GuardedDocument)doc : null; - final Formatter formatter = doc.getFormatter(); - formatter.reformatLock(); - Formatter.pushFormattingContextDocument(doc); + final Reformat formatter = Reformat.get(doc); + formatter.lock(); try { doc.runAtomicAsUser (new Runnable () { public void run () { @@ -1527,8 +1535,9 @@ } } - int reformattedLen = formatter.reformat(doc, pos, stopPos); - pos = pos + reformattedLen; + Position stopPosition = doc.createPosition(stopPos); + formatter.reformat(pos, stopPos); + pos = pos + Math.max(stopPosition.getOffset() - pos, 0); if (gdoc != null) { // adjust to end of current block pos = gdoc.getGuardedBlockChain().adjustToBlockEnd(pos); @@ -1542,8 +1551,7 @@ } }); } finally { - Formatter.popFormattingContextDocument(doc); - formatter.reformatUnlock(); + formatter.unlock(); } } } @@ -1637,15 +1645,18 @@ return; final GuardedDocument gdoc = (doc instanceof GuardedDocument) ? (GuardedDocument)doc : null; + + // Set hourglass cursor + final Cursor origCursor = target.getCursor(); + target.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { final AtomicBoolean canceled = new AtomicBoolean(); ProgressUtils.runOffEventDispatchThread(new Runnable() { public void run() { if (canceled.get()) return; - final Formatter formatter = doc.getFormatter(); - formatter.reformatLock(); - Formatter.pushFormattingContextDocument(doc); + final Reformat formatter = Reformat.get(doc); + formatter.lock(); try { if (canceled.get()) return; doc.runAtomicAsUser (new Runnable () { @@ -1680,8 +1691,9 @@ } if (pos < stopPos) { - int reformattedLen = formatter.reformat(doc, pos, stopPos); - pos = pos + reformattedLen; + Position stopPosition = doc.createPosition(stopPos); + formatter.reformat(pos, stopPos); + pos = pos + Math.max(stopPosition.getOffset() - pos, 0); } else { pos++; //ensure to make progress } @@ -1695,12 +1707,13 @@ target.getToolkit().beep(); } catch (BadLocationException e) { Utilities.annotateLoggable(e); + } finally { + target.setCursor(origCursor); } } }); } finally { - Formatter.popFormattingContextDocument(doc); - formatter.reformatUnlock(); + formatter.unlock(); } } }, NbBundle.getMessage(FormatAction.class, "Format_in_progress"), canceled, false); //NOI18N @@ -1710,7 +1723,6 @@ } } } - } @EditorActionRegistrations({ @@ -2331,7 +2343,12 @@ } } - /** Starts a new line in code. */ + /** + * Starts a new line in code. + * + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ @EditorActionRegistration(name = BaseKit.startNewLineAction) public static class StartNewLine extends LocalBaseAction { public StartNewLine(){ @@ -2345,35 +2362,112 @@ return; } + final int caretOffset; + final int insertionOffset; + + try { + caretOffset = target.getCaretPosition(); + insertionOffset = Utilities.getRowEnd(target, caretOffset); + } catch (BadLocationException ble) { + LOG.log(Level.FINE, null, ble); + return; + } final BaseDocument doc = (BaseDocument)target.getDocument(); - final Formatter formatter = doc.getFormatter(); - formatter.indentLock(); + final TypedBreakInterceptorsManager.Transaction transaction = TypedBreakInterceptorsManager.getInstance().openTransaction( + target, caretOffset, insertionOffset); + try { - doc.runAtomicAsUser (new Runnable () { - public void run () { - try { - //target.replaceSelection(""); //NOI18N -fix of issue #52485 - Caret caret = target.getCaret(); + if (!transaction.beforeInsertion()) { + final Boolean [] result = new Boolean [] { Boolean.FALSE }; //NOI18N + final Indent indenter = Indent.get(doc); + indenter.lock(); + try { + doc.runAtomicAsUser (new Runnable () { + public void run () { + Object [] r = transaction.textTyped(); + String insertionText = r == null ? "\n" : (String) r[0]; //NOI18N + int breakInsertPosition = r == null ? -1 : (Integer) r[1]; + int caretPosition = r == null ? -1 : (Integer) r[2]; + int [] reindentBlocks = r == null ? null : (int []) r[3]; - // insert and remove '-' to remember caret - // position - int dotpos = caret.getDot(); - doc.insertString(dotpos,"-",null); //NOI18N - doc.remove(dotpos,1); - int eolDot = Utilities.getRowEnd(target, caret.getDot()); - int newDotPos = formatter.indentNewLine(doc,eolDot); - caret.setDot(newDotPos); - } catch (BadLocationException ex) { - ex.printStackTrace(); - } + try { + performLineBreakInsertion(target, insertionOffset, insertionText, breakInsertPosition, caretPosition, reindentBlocks, indenter); + result[0] = Boolean.TRUE; + } catch (BadLocationException ble) { + LOG.log(Level.FINE, null, ble); + target.getToolkit().beep(); + } + } + }); + } finally { + indenter.unlock(); } - }); + + if (result[0].booleanValue()) { + transaction.afterInsertion(); + } // else line-break insertion failed + + } } finally { - formatter.indentUnlock(); + transaction.close(); } } - } + + // -------------------------------------------------------------------- + // Private implementation + // -------------------------------------------------------------------- + + private void performLineBreakInsertion( + JTextComponent target, + int insertionOffset, + String insertionText, + int breakInsertPosition, + int caretPosition, + int [] reindentBlocks, + Indent indenter) throws BadLocationException + { + BaseDocument doc = (BaseDocument) target.getDocument(); + DocumentUtilities.setTypingModification(doc, true); + try { + //target.replaceSelection(""); //NOI18N -fix of issue #52485 + Caret caret = target.getCaret(); + + // XXX: WTF is this? + // insert and remove '-' to remember caret + // position + int dotPos = caret.getDot(); + doc.insertString(dotPos, "-", null); //NOI18N + doc.remove(dotPos, 1); + + // insert new line, caret moves to the new line +// int eolDot = Utilities.getRowEnd(target, caret.getDot()); +// doc.insertString(eolDot, "\n", null); //NOI18N + doc.insertString(insertionOffset, insertionText, null); + dotPos = insertionOffset; + dotPos += caretPosition != -1 ? caretPosition : + breakInsertPosition != -1 ? breakInsertPosition + 1 : + insertionText.indexOf('\n') + 1; //NOI18N + + // reindent the new line + Position newDotPos = doc.createPosition(dotPos); + if (reindentBlocks != null && reindentBlocks.length > 0) { + for(int i = 0; i < reindentBlocks.length / 2; i++) { + int startOffset = insertionOffset + reindentBlocks[2 * i]; + int endOffset = insertionOffset + reindentBlocks[2 * i + 1]; + indenter.reindent(startOffset, endOffset); + } + } else { + indenter.reindent(dotPos); + } + + caret.setDot(newDotPos.getOffset()); + } finally { + DocumentUtilities.setTypingModification(doc, false); + } + } + + } // End of StartNewLine class /** * Cut text from the caret position to either begining or end --- a/editor.lib/src/org/netbeans/editor/BaseDocument.java +++ a/editor.lib/src/org/netbeans/editor/BaseDocument.java @@ -100,7 +100,6 @@ import org.netbeans.modules.editor.lib.EditorPackageAccessor; import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; -import org.netbeans.modules.editor.lib.FormatterOverride; import org.netbeans.modules.editor.lib.TrailingWhitespaceRemove; import org.netbeans.modules.editor.lib.SettingsConversions; import org.netbeans.modules.editor.lib.drawing.DrawEngine; @@ -253,11 +252,6 @@ new Integer(3) }; - /** Size of one indentation level. If this variable is null (value - * is not set in Settings, then the default algorithm will be used. - */ - private Integer shiftWidth; - /** How many times current writer requested writing */ private int writeDepth; @@ -353,9 +347,10 @@ private Position lastPositionEditedByTyping = null; - /** Formatter being used. */ - private Formatter formatter; - + /** Size of one indentation level. If this variable is null (value + * is not set in Settings, then the default algorithm will be used. + */ + private int shiftWidth = -1; private int tabSize; private Preferences prefs; @@ -367,9 +362,12 @@ } if (key == null || SimpleValueNames.INDENT_SHIFT_WIDTH.equals(key)) { - int shw = prefs.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, -1); - if (shw >= 0) { - shiftWidth = shw; + shiftWidth = prefs.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, -1); + } + + if (key == null || SimpleValueNames.SPACES_PER_TAB.equals(key)) { + if (shiftWidth == -1) { + shiftWidth = prefs.getInt(SimpleValueNames.SPACES_PER_TAB, EditorPreferencesDefaults.defaultSpacesPerTab); } } @@ -457,9 +455,6 @@ putProperty(EditorPreferencesKeys.PREVIOUS_WORD_FINDER, finder != null ? finder : new FinderFactory.PreviousWordBwdFinder(BaseDocument.this, stopOnEOL, false)); } - // Refresh formatter - formatter = null; - SettingsConversions.callSettingsChange(BaseDocument.this); } }; @@ -621,31 +616,32 @@ // } } - /** - * @deprecated Please use Editor Indentation API instead, for details see - * Editor Indentation. - */ - public Formatter getLegacyFormatter() { - if (formatter == null) { - formatter = (Formatter) SettingsConversions.callFactory(prefs, MimePath.parse(mimeType), FORMATTER, null); - if (formatter == null) { - formatter = Formatter.getFormatter(mimeType); - } - } - return formatter; - } - - /** - * Gets the formatter for this document. - * - * @deprecated Please use Editor Indentation API instead, for details see - * Editor Indentation. - */ - public Formatter getFormatter() { - Formatter f = getLegacyFormatter(); - FormatterOverride fp = Lookup.getDefault().lookup(FormatterOverride.class); - return (fp != null) ? fp.getFormatter(this, f) : f; - } +// XXX: formatting cleanup +// /** +// * @deprecated Please use Editor Indentation API instead, for details see +// * Editor Indentation. +// */ +// public Formatter getLegacyFormatter() { +// if (formatter == null) { +// formatter = (Formatter) SettingsConversions.callFactory(prefs, MimePath.parse(mimeType), FORMATTER, null); +// if (formatter == null) { +// formatter = Formatter.getFormatter(mimeType); +// } +// } +// return formatter; +// } +// +// /** +// * Gets the formatter for this document. +// * +// * @deprecated Please use Editor Indentation API instead, for details see +// * Editor Indentation. +// */ +// public Formatter getFormatter() { +// Formatter f = getLegacyFormatter(); +// FormatterOverride fp = Lookup.getDefault().lookup(FormatterOverride.class); +// return (fp != null) ? fp.getFormatter(this, f) : f; +// } /** * @deprecated Please use Lexer instead, for details see @@ -1570,15 +1566,11 @@ * setting. If so it uses it, otherwise it uses formatter.getSpacesPerTab(). * * @see getTabSize() - * @see Formatter.getSpacesPerTab() + * @deprecated Please use Editor Indentation API instead, for details see + * Editor Indentation. */ public int getShiftWidth() { - if (shiftWidth != null) { - return shiftWidth.intValue(); - - } else { - return getFormatter().getSpacesPerTab(); - } + return shiftWidth; } /** --- a/editor.lib/src/org/netbeans/editor/BaseKit.java +++ a/editor.lib/src/org/netbeans/editor/BaseKit.java @@ -67,10 +67,8 @@ import javax.swing.text.ViewFactory; import javax.swing.text.Caret; import javax.swing.text.JTextComponent; -import java.io.CharArrayWriter; import java.lang.reflect.Method; import java.util.Set; -import java.util.Vector; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -87,14 +85,23 @@ import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.KeyBindingSettings; +import org.netbeans.api.editor.settings.SimpleValueNames; +import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.ListenerList; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.modules.editor.indent.api.IndentUtils; +import org.netbeans.modules.editor.indent.api.Reformat; +import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.KitsTracker; import org.netbeans.modules.editor.lib.NavigationHistory; import org.netbeans.modules.editor.lib.SettingsConversions; import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager; +import org.netbeans.modules.editor.lib2.typinghooks.DeletedTextInterceptorsManager; +import org.netbeans.modules.editor.lib2.typinghooks.TypedBreakInterceptorsManager; +import org.netbeans.modules.editor.lib2.typinghooks.TypedTextInterceptorsManager; import org.openide.awt.StatusDisplayer; import org.openide.util.HelpCtx; import org.openide.util.Lookup; @@ -616,14 +623,15 @@ return new SyntaxSupport(doc); } - /** - * Create the formatter appropriate for this kit - * @deprecated Please use Editor Indentation API instead, for details see - * Editor Indentation. - */ - public Formatter createFormatter() { - return new Formatter(this.getClass()); - } +// XXX: formatting cleanup +// /** +// * Create the formatter appropriate for this kit +// * @deprecated Please use Editor Indentation API instead, for details see +// * Editor Indentation. +// */ +// public Formatter createFormatter() { +// return new Formatter(this.getClass()); +// } /** Create text UI */ protected BaseTextUI createTextUI() { @@ -1020,11 +1028,16 @@ - /** Default typed action */ + /** + * Default typed action + * + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ // @EditorActionRegistration(name = defaultKeyTypedAction) public static class DefaultKeyTypedAction extends LocalBaseAction { - static final long serialVersionUID =3069164318144463899L; + static final long serialVersionUID = 3069164318144463899L; public DefaultKeyTypedAction() { // Construct with defaultKeyTypedAction name to retain full compatibility for extending actions @@ -1033,8 +1046,6 @@ LOG.fine("DefaultKeyTypedAction with enhanced logging, see issue #145306"); //NOI18N } - private static final boolean isMac = System.getProperty("mrj.version") != null; //NOI18N - public void actionPerformed (final ActionEvent evt, final JTextComponent target) { if ((target != null) && (evt != null)) { @@ -1043,7 +1054,7 @@ boolean ctrl = ((mod & ActionEvent.CTRL_MASK) != 0); // On the mac, norwegian and french keyboards use Alt to do bracket characters. // This replicates Apple's modification DefaultEditorKit.DefaultKeyTypedAction - boolean alt = isMac ? ((mod & ActionEvent.META_MASK) != 0) : + boolean alt = org.openide.util.Utilities.isMac() ? ((mod & ActionEvent.META_MASK) != 0) : ((mod & ActionEvent.ALT_MASK) != 0); @@ -1057,67 +1068,50 @@ return; } - final Caret caret = target.getCaret(); - final BaseDocument doc = (BaseDocument)target.getDocument(); - final EditorUI editorUI = Utilities.getEditorUI(target); // determine if typed char is valid final String cmd = evt.getActionCommand(); - if ((cmd != null) && (cmd.length() == 1)) { - // Utilities.clearStatusText(target); + if (cmd != null && cmd.length() == 1 && cmd.charAt(0) >= 0x20 && cmd.charAt(0) != 0x7F) { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "Processing command char: {0}", Integer.toHexString(cmd.charAt(0))); //NOI18N + } + final BaseDocument doc = (BaseDocument)target.getDocument(); + final int insertionOffset = computeInsertionOffset(target.getCaret()); + final TypedTextInterceptorsManager.Transaction transaction = TypedTextInterceptorsManager.getInstance().openTransaction( + target, insertionOffset, cmd); + try { - NavigationHistory.getEdits().markWaypoint(target, caret.getDot(), false, true); - } catch (BadLocationException e) { - LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N - } - - doc.runAtomicAsUser (new Runnable () { - public void run () { - DocumentUtilities.setTypingModification(doc, true); - try { - char ch = cmd.charAt(0); - if ((ch >= 0x20) && (ch != 0x7F)) { // valid character - LOG.fine("Processing command char: " + Integer.toHexString(ch)); //NOI18N - - editorUI.getWordMatch().clear(); // reset word matching - Boolean overwriteMode = (Boolean)editorUI.getProperty( - EditorUI.OVERWRITE_MODE_PROPERTY); + if (!transaction.beforeInsertion()) { + final Object [] result = new Object [] { Boolean.FALSE, "" }; //NOI18N + doc.runAtomicAsUser (new Runnable () { + public void run () { + Object [] r = transaction.textTyped(); + String insertionText = r == null ? cmd : (String) r[0]; + int caretPosition = r == null ? -1 : (Integer) r[1]; + try { - boolean doInsert = true; // editorUI.getAbbrev().checkAndExpand(ch, evt); - if (doInsert) { - if (Utilities.isSelectionShowing(caret)) { // valid selection - boolean ovr = (overwriteMode != null && overwriteMode.booleanValue()); - try { - doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, true); - replaceSelection(target, caret.getDot(), caret, cmd, ovr); - } finally { - doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, null); - } - } else { // no selection - int dotPos = caret.getDot(); - if (overwriteMode != null && overwriteMode.booleanValue() - && dotPos < doc.getLength() && doc.getChars(dotPos, 1)[0] != '\n' - ) { // overwrite current char - insertString(doc, dotPos, caret, cmd, true); - } else { // insert mode - insertString(doc, dotPos, caret, cmd, false); - } - } - } - } catch (BadLocationException e) { - LOG.log(Level.FINE, null, e); + performTextInsertion(target, insertionOffset, insertionText, caretPosition); + result[0] = Boolean.TRUE; + result[1] = insertionText; + } catch (BadLocationException ble) { + LOG.log(Level.FINE, null, ble); target.getToolkit().beep(); } - } else { - LOG.fine("Invalid command char: " + Integer.toHexString(ch)); //NOI18N } + }); + + if (((Boolean)result[0]).booleanValue()) { + transaction.afterInsertion(); - checkIndent(target, cmd); - } finally { - DocumentUtilities.setTypingModification(doc, false); - } + // XXX: this is potentially wrong and we may need to call this with + // the original cmd; or maybe only if insertionText == cmd; but maybe + // it does not matter, because nobody seems to be overwriting this method anyway + checkIndent(target, (String)result[1]); + } // else text insertion failed } - }); + } finally { + transaction.close(); + } } else { if (LOG.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder(); @@ -1128,54 +1122,127 @@ sb.append(" "); } } - LOG.fine("Invalid command: '" + sb + "'"); //NOI18N + LOG.log(Level.FINE, "Invalid command: {0}", sb); //NOI18N } } } } - /** - * Hook to insert the given string at the given position into - * the given document in insert-mode, no selection, writeable - * document. Designed to be overridden by subclasses that want - * to intercept inserted characters. - */ - protected void insertString(BaseDocument doc, + // -------------------------------------------------------------------- + // SPI + // -------------------------------------------------------------------- + + /** + * Hook to insert the given string at the given position into + * the given document in insert-mode, no selection, writeable + * document. Designed to be overridden by subclasses that want + * to intercept inserted characters. + * + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected void insertString(BaseDocument doc, int dotPos, Caret caret, String str, - boolean overwrite) - throws BadLocationException - { - if (overwrite) doc.remove(dotPos, 1); - doc.insertString(dotPos, str, null); - } + boolean overwrite) throws BadLocationException + { + if (overwrite) { + doc.remove(dotPos, 1); + } + + doc.insertString(dotPos, str, null); + } - /** - * Hook to insert the given string at the given position into - * the given document in insert-mode with selection visible - * Designed to be overridden by subclasses that want - * to intercept inserted characters. - */ - protected void replaceSelection(JTextComponent target, - int dotPos, - Caret caret, - String str, - boolean overwrite) - throws BadLocationException - { - target.replaceSelection(str); - } + /** + * Hook to insert the given string at the given position into + * the given document in insert-mode with selection visible + * Designed to be overridden by subclasses that want + * to intercept inserted characters. + * + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected void replaceSelection( + JTextComponent target, + int dotPos, + Caret caret, + String str, + boolean overwrite) throws BadLocationException + { + target.replaceSelection(str); + } - - /** Check whether there was any important character typed - * so that the line should be possibly reformatted. - */ + /** + * Check whether there was any important character typed + * so that the line should be possibly reformatted. + * + * @deprecated Please use AutomatedIndentig + * or Typing Hooks instead, for details see + * Editor Library 2. + */ protected void checkIndent(JTextComponent target, String typedText) { } - } + // -------------------------------------------------------------------- + // Private implementation + // -------------------------------------------------------------------- + private void performTextInsertion(JTextComponent target, int insertionOffset, String insertionText, int caretPosition) throws BadLocationException { + final BaseDocument doc = (BaseDocument)target.getDocument(); + + try { + NavigationHistory.getEdits().markWaypoint(target, insertionOffset, false, true); + } catch (BadLocationException e) { + LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N + } + + DocumentUtilities.setTypingModification(doc, true); + try { + EditorUI editorUI = Utilities.getEditorUI(target); + Caret caret = target.getCaret(); + + editorUI.getWordMatch().clear(); // reset word matching + Boolean overwriteMode = (Boolean)editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY); + boolean ovr = (overwriteMode != null && overwriteMode.booleanValue()); + if (Utilities.isSelectionShowing(caret)) { // valid selection + try { + doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, true); + replaceSelection(target, insertionOffset, caret, insertionText, ovr); + } finally { + doc.putProperty(DOC_REPLACE_SELECTION_PROPERTY, null); + } + } else { // no selection + if (ovr && insertionOffset < doc.getLength() && doc.getChars(insertionOffset, 1)[0] != '\n') { //NOI18N + // overwrite current char + insertString(doc, insertionOffset, caret, insertionText, true); + } else { // insert mode + insertString(doc, insertionOffset, caret, insertionText, false); + } + } + + if (caretPosition != -1) { + assert caretPosition >= 0 && caretPosition < insertionText.length(); + caret.setDot(insertionOffset + caretPosition); + } + } finally { + DocumentUtilities.setTypingModification(doc, false); + } + } + + private int computeInsertionOffset(Caret caret) { + if (Utilities.isSelectionShowing(caret)) { + return Math.min(caret.getMark(), caret.getDot()); + } else { + return caret.getDot(); + } + } + } // End of DefaultKeyTypedAction class + + /** + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ public static class InsertBreakAction extends LocalBaseAction { static final long serialVersionUID =7966576342334158659L; @@ -1192,51 +1259,140 @@ } final BaseDocument doc = (BaseDocument)target.getDocument(); - final Formatter formatter = doc.getFormatter(); - formatter.indentLock(); + final int insertionOffset = computeInsertionOffset(target.getCaret()); + final TypedBreakInterceptorsManager.Transaction transaction = TypedBreakInterceptorsManager.getInstance().openTransaction( + target, insertionOffset, insertionOffset); + try { - doc.runAtomicAsUser (new Runnable () { - public void run () { - DocumentUtilities.setTypingModification(doc, true); - try { - target.replaceSelection(""); - Caret caret = target.getCaret(); - Object cookie = beforeBreak(target, doc, caret); + if (!transaction.beforeInsertion()) { + final Boolean [] result = new Boolean [] { Boolean.FALSE }; //NOI18N + final Indent indenter = Indent.get(doc); + indenter.lock(); + try { + doc.runAtomicAsUser (new Runnable () { + public void run () { + Object [] r = transaction.textTyped(); + String insertionText = r == null ? "\n" : (String) r[0]; //NOI18N + int breakInsertPosition = r == null ? -1 : (Integer) r[1]; + int caretPosition = r == null ? -1 : (Integer) r[2]; + int [] reindentBlocks = r == null ? null : (int []) r[3]; - int dotPos = caret.getDot(); - int newDotPos = formatter.indentNewLine(doc, dotPos); - caret.setDot(newDotPos); - - afterBreak(target, doc, caret, cookie); - } finally { - DocumentUtilities.setTypingModification(doc, false); - } + try { + performLineBreakInsertion(target, insertionOffset, insertionText, breakInsertPosition, caretPosition, reindentBlocks, indenter); + result[0] = Boolean.TRUE; + } catch (BadLocationException ble) { + LOG.log(Level.FINE, null, ble); + target.getToolkit().beep(); + } + } + }); + } finally { + indenter.unlock(); } - }); + + if (result[0].booleanValue()) { + transaction.afterInsertion(); + } // else line-break insertion failed + + } } finally { - formatter.indentUnlock(); + transaction.close(); } + } } - /** - * Hook called before any changes to the document. The value - * returned is passed intact to the other hook. - */ - protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) { - return null; - } + // -------------------------------------------------------------------- + // SPI + // -------------------------------------------------------------------- - /** - * Hook called after the enter was inserted and cursor - * repositioned. *data* is the object returned previously by - * *beforeBreak* hook. By default null. - */ - protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret, Object data) { - } - } + /** + * Hook called before any changes to the document. The value + * returned is passed intact to the other hook. + * + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) { + return null; + } - @EditorActionRegistration(name = splitLineAction) + /** + * Hook called after the enter was inserted and cursor + * repositioned. + * + * @param data the object returned from previously called + * {@link #beforeBreak(javax.swing.text.JTextComponent, org.netbeans.editor.BaseDocument, javax.swing.text.Caret)} hook. + * By default null. + * + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret, Object data) { + } + + // -------------------------------------------------------------------- + // Private implementation + // -------------------------------------------------------------------- + + private void performLineBreakInsertion( + JTextComponent target, + int insertionOffset, + String insertionText, + int breakInsertPosition, + int caretPosition, + int [] reindentBlocks, + Indent indenter) throws BadLocationException + { + BaseDocument doc = (BaseDocument) target.getDocument(); + DocumentUtilities.setTypingModification(doc, true); + try { + target.replaceSelection(""); // NOI18N + Caret caret = target.getCaret(); + Object cookie = beforeBreak(target, doc, caret); + + // insert new line, caret moves to the new line + int dotPos = caret.getDot(); + assert dotPos == insertionOffset : "dotPos=" + dotPos + " != " + "insertionOffset=" + insertionOffset; //NOI18N +// doc.insertString(dotPos, "\n", null); //NOI18N +// dotPos++; + doc.insertString(dotPos, insertionText, null); + dotPos += caretPosition != -1 ? caretPosition : + breakInsertPosition != -1 ? breakInsertPosition + 1 : + insertionText.indexOf('\n') + 1; //NOI18N + + // reindent the new line + Position newDotPos = doc.createPosition(dotPos); + if (reindentBlocks != null && reindentBlocks.length > 0) { + for(int i = 0; i < reindentBlocks.length / 2; i++) { + int startOffset = insertionOffset + reindentBlocks[2 * i]; + int endOffset = insertionOffset + reindentBlocks[2 * i + 1]; + indenter.reindent(startOffset, endOffset); + } + } else { + indenter.reindent(dotPos); + } + + // adjust the caret + caret.setDot(newDotPos.getOffset()); + + afterBreak(target, doc, caret, cookie); + } finally { + DocumentUtilities.setTypingModification(doc, false); + } + } + + private int computeInsertionOffset(Caret caret) { + if (Utilities.isSelectionShowing(caret)) { + return Math.min(caret.getMark(), caret.getDot()); + } else { + return caret.getDot(); + } + } + + } // End of InsertBreakAction class + + @EditorActionRegistration(name = splitLineAction) public static class SplitLineAction extends LocalBaseAction { static final long serialVersionUID =7966576342334158659L; @@ -1255,24 +1411,33 @@ final BaseDocument doc = (BaseDocument)target.getDocument(); final Caret caret = target.getCaret(); - final Formatter formatter = doc.getFormatter(); - formatter.indentLock(); + final Indent formatter = Indent.get(doc); + formatter.lock(); try { doc.runAtomicAsUser (new Runnable () { public void run () { DocumentUtilities.setTypingModification(doc, true); try{ - target.replaceSelection(""); - final int dotPos = caret.getDot(); // dot stays where it was - formatter.indentNewLine(doc, dotPos); // newline + target.replaceSelection(""); //NOI18N + + // insert new line, caret stays where it is + int dotPos = caret.getDot(); + doc.insertString(dotPos, "\n", null); //NOI18N + + // reindent the new line + formatter.reindent(dotPos + 1); // newline + + // make sure the caret stays on its original position caret.setDot(dotPos); + } catch (BadLocationException ble) { + LOG.log(Level.WARNING, null, ble); } finally { DocumentUtilities.setTypingModification(doc, false); } } }); } finally { - formatter.indentUnlock(); + formatter.unlock(); } } } @@ -1300,12 +1465,10 @@ doc.runAtomicAsUser (new Runnable () { public void run () { DocumentUtilities.setTypingModification(doc, true); - Formatter.pushFormattingContextDocument(doc); try { if (Utilities.isSelectionShowing(caret)) { // block selected try { - doc.getFormatter().changeBlockIndent(doc, - target.getSelectionStart(), target.getSelectionEnd(), +1); + changeBlockIndent(doc, target.getSelectionStart(), target.getSelectionEnd(), +1); } catch (GuardedException e) { target.getToolkit().beep(); } catch (BadLocationException e) { @@ -1341,7 +1504,7 @@ // Fix of #32240 - #1 of 2 int rowStart = Utilities.getRowStart(doc, dotPos); - doc.getFormatter().changeRowIndent(doc, dotPos, indent); + changeRowIndent(doc, dotPos, indent); // Fix of #32240 - #2 of 2 int newDotPos = doc.getOffsetFromVisCol(indent, rowStart); @@ -1350,7 +1513,7 @@ } } else { // already chars on the line - doc.getFormatter().insertTabString(doc, dotPos); + insertTabString(doc, dotPos); } } catch (BadLocationException e) { @@ -1358,7 +1521,6 @@ } } } finally { - Formatter.popFormattingContextDocument(doc); DocumentUtilities.setTypingModification(doc, false); } } @@ -1507,7 +1669,10 @@ } } - /** Remove previous or next character */ + /** + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ public static class DeleteCharAction extends LocalBaseAction { protected boolean nextChar; @@ -1531,44 +1696,90 @@ final int dot = caret.getDot(); final int mark = caret.getMark(); - doc.runAtomicAsUser (new Runnable () { - public void run () { - DocumentUtilities.setTypingModification(doc, true); - - try { - if (dot != mark) { // remove selection - doc.remove(Math.min(dot, mark), Math.abs(dot - mark)); - } else { - if (nextChar) { // remove next char - char ch = doc.getChars(dot, 1)[0]; - doc.remove(dot, 1); - charDeleted(doc, dot, caret, ch); - } else { // remove previous char - char ch = doc.getChars(dot-1, 1)[0]; - doc.remove(dot - 1, 1); - charBackspaced(doc, dot-1, caret, ch); + if (dot != mark) { + // remove selection + doc.runAtomicAsUser (new Runnable () { + public void run () { + DocumentUtilities.setTypingModification(doc, true); + try { + doc.remove(Math.min(dot, mark), Math.abs(dot - mark)); + } catch (BadLocationException e) { + target.getToolkit().beep(); + } finally { + DocumentUtilities.setTypingModification(doc, false); } } - } catch (BadLocationException e) { + }); + } else { + char [] removedChar = null; + + try { + removedChar = nextChar ? + dot < doc.getLength() - 1 ? doc.getChars(dot, 1) : null : + dot > 0 ? doc.getChars(dot - 1, 1) : null; + } catch (BadLocationException ble) { target.getToolkit().beep(); - } finally { - DocumentUtilities.setTypingModification(doc, false); } + + if (removedChar != null) { + final String removedText = String.valueOf(removedChar); + final DeletedTextInterceptorsManager.Transaction t = DeletedTextInterceptorsManager.getInstance().openTransaction(target, dot, removedText, !nextChar); + try { + if (!t.beforeRemove()) { + final boolean [] result = new boolean [] { false }; + doc.runAtomicAsUser (new Runnable () { + public void run () { + DocumentUtilities.setTypingModification(doc, true); + try { + if (nextChar) { // remove next char + doc.remove(dot, 1); + } else { // remove previous char + doc.remove(dot - 1, 1); + } + + t.textDeleted(); + + if (nextChar) { + charDeleted(doc, dot, caret, removedText.charAt(0)); + } else { + charBackspaced(doc, dot - 1, caret, removedText.charAt(0)); + } + + result[0] = true; + } catch (BadLocationException e) { + target.getToolkit().beep(); + } finally { + DocumentUtilities.setTypingModification(doc, false); + } + } + }); + + if (result[0]) { + t.afterRemove(); + } + } + } finally { + t.close(); + } } - }); + } } } - protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) - throws BadLocationException - { - } - - protected void charDeleted(BaseDocument doc, int dotPos, Caret caret, char ch) - throws BadLocationException - { - } - } + /** + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException { + } + + /** + * @deprecated Please use Typing Hooks instead, for details see + * Editor Library 2. + */ + protected void charDeleted(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException { + } + } // End of DeleteCharAction class /** * @deprecated this action is no longer used. It is reimplemented in editor.actions module. @@ -1734,10 +1945,10 @@ LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N } - final Formatter formatter = doc.getFormatter(); + final Reformat formatter = Reformat.get(doc); final boolean formatted = pasteFormatedAction.equals(getValue(Action.NAME)); if (formatted) { - formatter.reformatLock(); + formatter.lock(); } try { doc.runAtomicAsUser (new Runnable () { @@ -1758,7 +1969,7 @@ } int endOffset = caret.getDot(); if (formatted) { - formatter.reformat(doc, startOffset, endOffset); + formatter.reformat(startOffset, endOffset); } } catch (Exception e) { target.getToolkit().beep(); @@ -1769,72 +1980,72 @@ }); } finally { if (formatted) { - formatter.reformatUnlock(); + formatter.unlock(); } } } } - public static void indentBlock(BaseDocument doc, int startOffset, int endOffset) - throws BadLocationException - { - char [] text = doc.getChars(startOffset, endOffset-startOffset); - String [] lines = toLines(new String(text)); - - doc.remove(startOffset, endOffset - startOffset); - // System.out.println("Lines:\n"); // NOI18N - // for (int j = 0 ; j < lines.length; j++) System.out.println(lines[j] + "<"); // NOI18N - - int offset = startOffset; - // handle the full lines - for (int i = 0; i < lines.length - 1; i++) { - String indent = getIndentString(doc, offset, lines[i]); - String fragment = indent + lines[i].trim() + '\n'; - // System.out.println(fragment + "|"); // NOI18N - doc.insertString(offset, fragment, null); - offset += fragment.length(); - } - - // the rest just paste without indenting - doc.insertString(offset, lines[lines.length-1], null); - - } - - /** Break string to lines */ - private static String [] toLines(String str) { - Vector v = new Vector(); - int p=0 , p0=0; - for (; p < str.length() ; p++) { - if (str.charAt(p) == '\n') { - v.add(str.substring(p0, p+1)); - p0 = p+1; - } - } - if (p0 < str.length()) v.add(str.substring(p0, str.length())); else v.add(""); - - return (String [])v.toArray(new String [0]); - } - - private static String getIndentString(BaseDocument doc, int startOffset, String str) { - try { - Formatter f = doc.getFormatter(); - CharArrayWriter cw = new CharArrayWriter(); - Writer w = f.createWriter(doc, startOffset, cw); - w.write(str, 0, str.length()); - w.close(); - String out = new String(cw.toCharArray()); - int i = 0; - for (; i < out.length(); i++) { - if (out.charAt(i) != ' ' && out.charAt(i) != '\t') break; - } - // System.out.println(out+"|"); // NOI18N - // System.out.println(out.substring(0,i)+"^"); // NOI18N - - return out.substring(0, i); - } catch (java.io.IOException e) { - return ""; - } - } +// public static void indentBlock(BaseDocument doc, int startOffset, int endOffset) +// throws BadLocationException +// { +// char [] text = doc.getChars(startOffset, endOffset-startOffset); +// String [] lines = toLines(new String(text)); +// +// doc.remove(startOffset, endOffset - startOffset); +// // System.out.println("Lines:\n"); // NOI18N +// // for (int j = 0 ; j < lines.length; j++) System.out.println(lines[j] + "<"); // NOI18N +// +// int offset = startOffset; +// // handle the full lines +// for (int i = 0; i < lines.length - 1; i++) { +// String indent = getIndentString(doc, offset, lines[i]); +// String fragment = indent + lines[i].trim() + '\n'; +// // System.out.println(fragment + "|"); // NOI18N +// doc.insertString(offset, fragment, null); +// offset += fragment.length(); +// } +// +// // the rest just paste without indenting +// doc.insertString(offset, lines[lines.length-1], null); +// +// } +// +// /** Break string to lines */ +// private static String [] toLines(String str) { +// Vector v = new Vector(); +// int p=0 , p0=0; +// for (; p < str.length() ; p++) { +// if (str.charAt(p) == '\n') { +// v.add(str.substring(p0, p+1)); +// p0 = p+1; +// } +// } +// if (p0 < str.length()) v.add(str.substring(p0, str.length())); else v.add(""); +// +// return (String [])v.toArray(new String [0]); +// } +// +// private static String getIndentString(BaseDocument doc, int startOffset, String str) { +// try { +// Formatter f = doc.getFormatter(); +// CharArrayWriter cw = new CharArrayWriter(); +// Writer w = f.createWriter(doc, startOffset, cw); +// w.write(str, 0, str.length()); +// w.close(); +// String out = new String(cw.toCharArray()); +// int i = 0; +// for (; i < out.length(); i++) { +// if (out.charAt(i) != ' ' && out.charAt(i) != '\t') break; +// } +// // System.out.println(out+"|"); // NOI18N +// // System.out.println(out.substring(0,i)+"^"); // NOI18N +// +// return out.substring(0, i); +// } catch (java.io.IOException e) { +// return ""; +// } +// } } @@ -2875,4 +3086,154 @@ } + + /** Modify the line to move the text starting at dotPos one tab + * column to the right. Whitespace preceeding dotPos may be + * replaced by a TAB character if tabs expanding is on. + * @param doc document to operate on + * @param dotPos insertion point + */ + static void insertTabString (final BaseDocument doc, final int dotPos) throws BadLocationException { + final BadLocationException[] badLocationExceptions = new BadLocationException [1]; + doc.runAtomic (new Runnable () { + public void run () { + try { + // Determine first white char before dotPos + int rsPos = Utilities.getRowStart(doc, dotPos); + int startPos = Utilities.getFirstNonWhiteBwd(doc, dotPos, rsPos); + startPos = (startPos >= 0) ? (startPos + 1) : rsPos; + + int startCol = Utilities.getVisualColumn(doc, startPos); + int endCol = Utilities.getNextTabColumn(doc, dotPos); + Preferences prefs = CodeStylePreferences.get(doc).getPreferences(); + String tabStr = Analyzer.getWhitespaceString( + startCol, endCol, + prefs.getBoolean(SimpleValueNames.EXPAND_TABS, EditorPreferencesDefaults.defaultExpandTabs), + prefs.getInt(SimpleValueNames.TAB_SIZE, EditorPreferencesDefaults.defaultTabSize)); + + // Search for the first non-common char + char[] removeChars = doc.getChars(startPos, dotPos - startPos); + int ind = 0; + while (ind < removeChars.length && removeChars[ind] == tabStr.charAt(ind)) { + ind++; + } + + startPos += ind; + doc.remove(startPos, dotPos - startPos); + doc.insertString(startPos, tabStr.substring(ind), null); + } catch (BadLocationException ex) { + badLocationExceptions [0] = ex; + } + } + }); + if (badLocationExceptions[0] != null) + throw badLocationExceptions [0]; + } + + /** Change the indent of the given row. Document is atomically locked + * during this operation. + */ + static void changeRowIndent (final BaseDocument doc, final int pos, final int newIndent) throws BadLocationException { + final BadLocationException[] badLocationExceptions = new BadLocationException [1]; + doc.runAtomic (new Runnable () { + public void run () { + try { + int indent = newIndent < 0 ? 0 : newIndent; + int firstNW = Utilities.getRowFirstNonWhite(doc, pos); + if (firstNW == -1) { // valid first non-blank + firstNW = Utilities.getRowEnd(doc, pos); + } + int replacePos = Utilities.getRowStart(doc, pos); + int removeLen = firstNW - replacePos; + CharSequence removeText = DocumentUtilities.getText(doc, replacePos, removeLen); + String newIndentText = IndentUtils.createIndentString(doc, indent); + if (CharSequenceUtilities.startsWith(newIndentText, removeText)) { + // Skip removeLen chars at start + newIndentText = newIndentText.substring(removeLen); + replacePos += removeLen; + removeLen = 0; + } else if (CharSequenceUtilities.endsWith(newIndentText, removeText)) { + // Skip removeLen chars at the end + newIndentText = newIndentText.substring(0, newIndentText.length() - removeLen); + removeLen = 0; + } + + if (removeLen != 0) { + doc.remove(replacePos, removeLen); + } + + doc.insertString(replacePos, newIndentText, null); + } catch (BadLocationException ex) { + badLocationExceptions [0] = ex; + } + } + }); + if (badLocationExceptions[0] != null) + throw badLocationExceptions [0]; + } + + /** Increase/decrease indentation of the block of the code. Document + * is atomically locked during the operation. + * @param doc document to operate on + * @param startPos starting line position + * @param endPos ending line position + * @param shiftCnt positive/negative count of shiftwidths by which indentation + * should be shifted right/left + */ + static void changeBlockIndent (final BaseDocument doc, final int startPos, final int endPos, + final int shiftCnt) throws BadLocationException { + GuardedDocument gdoc = (doc instanceof GuardedDocument) + ? (GuardedDocument)doc : null; + if (gdoc != null){ + for (int i = startPos; i 0 && Utilities.getRowStart(doc, endPos) == endPos) ? + endPos - 1 : endPos; + + int pos = Utilities.getRowStart(doc, startPos ); + for (int lineCnt = Utilities.getRowCount(doc, startPos, end); + lineCnt > 0; lineCnt-- + ) { + int indent = Utilities.getRowIndent(doc, pos); + if (Utilities.isRowWhite(doc, pos)) { + indent = -indentDelta; // zero indentation for white line + } + changeRowIndent(doc, pos, Math.max(indent + indentDelta, 0)); + pos = Utilities.getRowStart(doc, pos, +1); + } + } catch (BadLocationException ex) { + badLocationExceptions [0] = ex; + } + } + }); + if (badLocationExceptions[0] != null) + throw badLocationExceptions [0]; + } + + /** Shift line either left or right */ + static void shiftLine(BaseDocument doc, int dotPos, boolean right) throws BadLocationException { + int ind = doc.getShiftWidth(); + if (!right) { + ind = -ind; + } + + if (Utilities.isRowWhite(doc, dotPos)) { + ind += Utilities.getVisualColumn(doc, dotPos); + } else { + ind += Utilities.getRowIndent(doc, dotPos); + } + ind = Math.max(ind, 0); + changeRowIndent(doc, dotPos, ind); + } } --- a/editor.lib/src/org/netbeans/editor/Utilities.java +++ a/editor.lib/src/org/netbeans/editor/Utilities.java @@ -58,6 +58,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.prefs.Preferences; import javax.swing.SwingUtilities; import javax.swing.Action; import javax.swing.InputMap; @@ -79,12 +80,17 @@ import javax.swing.plaf.TextUI; import javax.swing.text.AbstractDocument; import javax.swing.text.Element; +import javax.swing.text.Position; import javax.swing.text.View; import org.netbeans.api.editor.EditorRegistry; import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.indent.api.Reformat; +import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; import org.netbeans.modules.editor.lib.drawing.DrawEngineDocView; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager; import org.netbeans.modules.editor.lib2.view.DocumentView; @@ -984,26 +990,29 @@ * @param endOffset offset at which the formatting ends * @return length of the reformatted code */ - public static int reformat (final BaseDocument doc, final int startOffset, final int endOffset) - throws BadLocationException { - final Formatter formatter = doc.getFormatter(); - formatter.reformatLock(); + public static int reformat (final BaseDocument doc, final int startOffset, final int endOffset) throws BadLocationException { + final Reformat formatter = Reformat.get(doc); + formatter.lock(); try { - final Object[] result = new Object [1]; - doc.runAtomicAsUser (new Runnable () { - public @Override void run () { + final Object[] result = new Object[1]; + doc.runAtomicAsUser(new Runnable() { + public @Override void run() { try { - result [0] = formatter.reformat (doc, startOffset, endOffset); + Position endPos = doc.createPosition(endOffset); + formatter.reformat(startOffset, endOffset); + result[0] = Math.max(endPos.getOffset() - startOffset, 0); } catch (BadLocationException ex) { - result [0] = ex; + result[0] = ex; } } }); - if (result [0] instanceof BadLocationException) - throw (BadLocationException) result [0]; - return (Integer) result [0]; + if (result[0] instanceof BadLocationException) { + throw (BadLocationException) result[0]; + } else { + return (Integer) result[0]; + } } finally { - formatter.reformatUnlock(); + formatter.unlock(); } } @@ -1043,10 +1052,10 @@ public static String getTabInsertString(BaseDocument doc, int offset) throws BadLocationException { int col = getVisualColumn(doc, offset); - Formatter f = doc.getFormatter(); - boolean expandTabs = f.expandTabs(); + Preferences prefs = CodeStylePreferences.get(doc).getPreferences(); + boolean expandTabs = prefs.getBoolean(SimpleValueNames.EXPAND_TABS, EditorPreferencesDefaults.defaultExpandTabs); if (expandTabs) { - int spacesPerTab = f.getSpacesPerTab(); + int spacesPerTab = prefs.getInt(SimpleValueNames.SPACES_PER_TAB, EditorPreferencesDefaults.defaultSpacesPerTab); int len = (col + spacesPerTab) / spacesPerTab * spacesPerTab - col; return new String(Analyzer.getSpacesBuffer(len), 0, len); } else { // insert pure tab @@ -1062,7 +1071,8 @@ public static int getNextTabColumn(BaseDocument doc, int offset) throws BadLocationException { int col = getVisualColumn(doc, offset); - int tabSize = doc.getFormatter().getSpacesPerTab(); + Preferences prefs = CodeStylePreferences.get(doc).getPreferences(); + int tabSize = prefs.getInt(SimpleValueNames.SPACES_PER_TAB, EditorPreferencesDefaults.defaultSpacesPerTab); return (col + tabSize) / tabSize * tabSize; } --- a/editor.lib/src/org/netbeans/editor/ext/ExtKit.java +++ a/editor.lib/src/org/netbeans/editor/ext/ExtKit.java @@ -46,7 +46,6 @@ import java.awt.Rectangle; import java.awt.event.ActionEvent; -import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.prefs.Preferences; @@ -67,7 +66,6 @@ import org.netbeans.editor.Utilities; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.SyntaxSupport; -import org.netbeans.editor.Formatter; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.editor.lib.NavigationHistory; @@ -370,7 +368,7 @@ protected final void debugPopupMenuItem(JMenuItem item, Action action) { if (debugPopupMenu) { - StringBuffer sb = new StringBuffer("POPUP: "); // NOI18N + StringBuilder sb = new StringBuilder("POPUP: "); // NOI18N if (item != null) { sb.append('"'); //NOI18N sb.append(item.getText()); @@ -1043,7 +1041,10 @@ } - // Completion customized actions + /** + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ // @EditorActionRegistration( // name = defaultKeyTypedAction, // shortDescription = editorBundleHash + defaultKeyTypedAction @@ -1081,33 +1082,18 @@ } } - /** Check the characters that should cause reindenting the line. */ + // -------------------------------------------------------------------- + // SPI + // -------------------------------------------------------------------- + + /** + * Check the characters that should cause reindenting the line. + * + * @deprecated Please use AutomatedIndentig + * or Typing Hooks instead, for details see + * Editor Library 2. + */ protected void checkIndentHotChars(JTextComponent target, String typedText) { - BaseDocument doc = Utilities.getDocument(target); - if (doc != null) { - Caret caret = target.getCaret(); - Formatter f = doc.getFormatter(); - if (f instanceof ExtFormatter) { - ExtFormatter ef = (ExtFormatter)f; - int[] fmtBlk = ef.getReformatBlock(target, typedText); - - if (fmtBlk != null) { - try { - fmtBlk[0] = Utilities.getRowStart(doc, fmtBlk[0]); - fmtBlk[1] = Utilities.getRowEnd(doc, fmtBlk[1]); - - //this was the of #18922, that causes the bug #20198 - //ef.reformat(doc, fmtBlk[0], fmtBlk[1]); - - //bugfix of the bug #20198. Bug #18922 is fixed too as well as #6968 - ef.reformat(doc, fmtBlk[0], fmtBlk[1], true); - - } catch (BadLocationException e) { - } catch (IOException e) { - } - } - } - } } @@ -1117,37 +1103,6 @@ * Editor Code Completion. */ protected void checkCompletion(JTextComponent target, String typedText) { -// XXX: remove -// Completion completion = ExtUtilities.getCompletion(target); -// -// BaseDocument doc = (BaseDocument)target.getDocument(); -// ExtSyntaxSupport extSup = (ExtSyntaxSupport)doc.getSyntaxSupport(); -// -// if (completion != null && typedText.length() > 0) { -// if( !completion.isPaneVisible() ) { -// if (completion.isAutoPopupEnabled()) { -// int result = extSup.checkCompletion( target, typedText, false ); -// if ( result == ExtSyntaxSupport.COMPLETION_POPUP ) { -// completion.popup(true); -// } else if ( result == ExtSyntaxSupport.COMPLETION_CANCEL ) { -// completion.cancelRequest(); -// } -// } -// } else { -// int result = extSup.checkCompletion( target, typedText, true ); -// switch( result ) { -// case ExtSyntaxSupport.COMPLETION_HIDE: -// completion.setPaneVisible(false); -// break; -// case ExtSyntaxSupport.COMPLETION_REFRESH: -// completion.refresh(false); -// break; -// case ExtSyntaxSupport.COMPLETION_POST_REFRESH: -// completion.refresh(true); -// break; -// } -// } -// } } } @@ -1225,6 +1180,10 @@ } + /** + * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see + * Editor Library 2. + */ public static class ExtDeleteCharAction extends DeleteCharAction { public ExtDeleteCharAction(String nm, boolean nextChar) { --- a/editor.lib/src/org/netbeans/modules/editor/lib/FormatterOverride.java +++ a/editor.lib/src/org/netbeans/modules/editor/lib/FormatterOverride.java @@ -1,69 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.editor.lib; - -import javax.swing.text.Document; -import org.netbeans.editor.Formatter; - -/** - * Class to be searched in lookup that can override a formatter for the given document. - *
- * It is a private contract between editor/lib and editor/indent. - * - * @author Miloslav Metelka - */ -public interface FormatterOverride { - - /** - * Possibly override the default formatter used for the given document. - * - * @param doc non-null document for which the formatter is being searched. - * @param defaultFormatter default formatter found by the infrastructure - * or null if there is none. - * @return overriden formatter or the default formatter passed as the argument. - */ - Formatter getFormatter(Document doc, Formatter defaultFormatter); - -} --- a/editor.lib2/apichanges.xml +++ a/editor.lib2/apichanges.xml @@ -107,6 +107,21 @@ + + Typing Hooks SPI added + + + + + + Typing Hooks SPI allows interception of various key typed events + that are processed in the editor. The SPI is a replacement for + subclassing editor actions such as DefaultKeyTypedAction, + InsertBreakAction, etc. + + + + Adding line/column based dialog bindings --- a/editor.lib2/arch.xml +++ a/editor.lib2/arch.xml @@ -80,8 +80,14 @@
  • + Code Generator SPI +
  • +
  • Highlighting SPI
  • +
  • + Typing Hooks SPI +
@@ -120,9 +126,19 @@ --> - At the moment the Editor Library 2 module contains only the Highlighting SPI, - which use cases can be found in the org.netbeans.spi.editor.highlighting package - overview. + At the moment the Editor Library 2 module contains three distinct SPIs. Each SPI lives + in its own package and the usecases can be found in the packages overview. + --- a/editor.lib2/manifest.mf +++ a/editor.lib2/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor.lib2/1 -OpenIDE-Module-Implementation-Version: 2 +OpenIDE-Module-Implementation-Version: 3 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml OpenIDE-Module-Needs: org.netbeans.modules.editor.actions --- a/editor.lib2/nbproject/project.properties +++ a/editor.lib2/nbproject/project.properties @@ -43,7 +43,7 @@ is.autoload=true javac.source=1.6 javac.compilerargs=-Xlint:unchecked -spec.version.base=1.30.0 +spec.version.base=1.31.0 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml --- a/editor.lib2/nbproject/project.xml +++ a/editor.lib2/nbproject/project.xml @@ -181,6 +181,7 @@ org.netbeans.spi.editor.codegen org.netbeans.spi.editor.highlighting org.netbeans.spi.editor.highlighting.support + org.netbeans.spi.editor.typinghooks
--- a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/DeletedTextInterceptorsManager.java +++ a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/DeletedTextInterceptorsManager.java @@ -0,0 +1,215 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.typinghooks; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor; + +/** + * + * @author vita + */ +public final class DeletedTextInterceptorsManager { + + public static DeletedTextInterceptorsManager getInstance() { + if (instance == null) { + instance = new DeletedTextInterceptorsManager(); + } + return instance; + } + + public Transaction openTransaction(JTextComponent c, int offset, String removedText, boolean backwardDelete) { + synchronized (this) { + if (transaction == null) { + transaction = new Transaction(c, offset, removedText, backwardDelete); + return transaction; + } else { + throw new IllegalStateException("Too many transactions; only one at a time is allowed!"); //NOI18N + } + } + } + + public final class Transaction { + + public boolean beforeRemove() { + for(DeletedTextInterceptor i : interceptors) { + try { + if (i.beforeRemove(context)) { + return true; + } + } catch (Exception e) { + LOG.log(Level.INFO, "DeletedTextInterceptor crashed in beforeRemove(): " + i, e); //NOI18N + } + } + + phase++; + return false; + } + + public void textDeleted() { + Object [] data = null; + + for(DeletedTextInterceptor i : interceptors) { + try { + i.remove(context); + } catch (Exception e) { + LOG.log(Level.INFO, "DeletedTextInterceptor crashed in remove(): " + i, e); //NOI18N + continue; + } + } + + phase++; + } + + public void afterRemove() { + for(DeletedTextInterceptor i : interceptors) { + try { + i.afterRemove(context); + } catch (Exception e) { + LOG.log(Level.INFO, "DeletedTextInterceptor crashed in afterRemove(): " + i, e); //NOI18N + } + } + + phase++; + } + + public void close() { + if (phase < 3) { + for(DeletedTextInterceptor i : interceptors) { + try { + i.cancelled(context); + } catch (Exception e) { + LOG.log(Level.INFO, "DeletedTextInterceptor crashed in cancelled(): " + i, e); //NOI18N + } + } + } + + synchronized (DeletedTextInterceptorsManager.this) { + transaction = null; + } + } + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private final DeletedTextInterceptor.Context context; + private final Collection interceptors; + private int phase = 0; + + private Transaction(JTextComponent c, int offset, String removedText, boolean backwardDelete) { + this.context = TypingHooksSpiAccessor.get().createDtiContext(c, offset, removedText, backwardDelete); + this.interceptors = getInterceptors(c.getDocument(), offset); + } + } // End of Transaction class + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private static final Logger LOG = Logger.getLogger(DeletedTextInterceptorsManager.class.getName()); + + private static DeletedTextInterceptorsManager instance; + + private Transaction transaction = null; + private final Map>> cache = new WeakHashMap>>(); + + private DeletedTextInterceptorsManager() { + + } + + // XXX: listne on changes in MimeLookup + private Collection getInterceptors(Document doc, int offset) { + List> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true); + TokenSequence seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1); + MimePath mimePath = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath()); + + synchronized (cache) { + Reference> ref = cache.get(mimePath); + Collection interceptors = ref == null ? null : ref.get(); + + if (interceptors == null) { + Collection factories = MimeLookup.getLookup(mimePath).lookupAll(DeletedTextInterceptor.Factory.class); + interceptors = new HashSet(factories.size()); + + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "DeletedTextInterceptor.Factory instances for {0} {" , mimePath.getPath()); //NOI18N + } + + for(DeletedTextInterceptor.Factory f : factories) { + DeletedTextInterceptor interceptor = f.createDeletedTextInterceptor(mimePath); + if (interceptor != null) { + interceptors.add(interceptor); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "{0} created: {1}", new Object[] { f, interceptor }); //NOI18N + } + } + + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("}"); //NOI18N + } + + // XXX: this should really be a timed WeakReference + cache.put(mimePath, new SoftReference>(interceptors)); + } + + return interceptors; + } + } +} --- a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypedBreakInterceptorsManager.java +++ a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypedBreakInterceptorsManager.java @@ -0,0 +1,226 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.typinghooks; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor; + +/** + * + * @author Vita Stejskal + */ +public final class TypedBreakInterceptorsManager { + + public static TypedBreakInterceptorsManager getInstance() { + if (instance == null) { + instance = new TypedBreakInterceptorsManager(); + } + return instance; + } + + public Transaction openTransaction(JTextComponent c, int caretOffset, int insertBreakOffset) { + synchronized (this) { + if (transaction == null) { + transaction = new Transaction(c, caretOffset, insertBreakOffset); + return transaction; + } else { + throw new IllegalStateException("Too many transactions; only one at a time is allowed!"); //NOI18N + } + } + } + + public final class Transaction { + + public boolean beforeInsertion() { + for(TypedBreakInterceptor i : interceptors) { + try { + if (i.beforeInsert(context)) { + return true; + } + } catch (Exception e) { + LOG.log(Level.INFO, "TypedBreakInterceptor crashed in beforeInsert(): " + i, e); //NOI18N + } + } + + phase++; + return false; + } + + /** + * + * @return [0] == insertionText, [1] == new caret position (!) within [0] + */ + public Object [] textTyped() { + Object [] data = null; + + for(TypedBreakInterceptor i : interceptors) { + try { + i.insert(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedBreakInterceptor crashed in insert(): " + i, e); //NOI18N + TypingHooksSpiAccessor.get().resetTbiContextData(context); + continue; + } + + data = TypingHooksSpiAccessor.get().getTbiContextData(context); + if (data != null) { + break; + } + } + + phase++; + return data; + } + + public void afterInsertion() { + for(TypedBreakInterceptor i : interceptors) { + try { + i.afterInsert(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedBreakInterceptor crashed in afterInsert(): " + i, e); //NOI18N + } + } + + phase++; + } + + public void close() { + if (phase < 3) { + for(TypedBreakInterceptor i : interceptors) { + try { + i.cancelled(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedBreakInterceptor crashed in cancelled(): " + i, e); //NOI18N + } + } + } + + synchronized (TypedBreakInterceptorsManager.this) { + transaction = null; + } + } + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private final TypedBreakInterceptor.MutableContext context; + private final Collection interceptors; + private int phase = 0; + + private Transaction(JTextComponent c, int caretOffset, int insertBreakOffset) { + this.context = TypingHooksSpiAccessor.get().createTbiContext(c, caretOffset, insertBreakOffset); + this.interceptors = getInterceptors(c.getDocument(), insertBreakOffset); + } + } // End of Transaction class + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private static final Logger LOG = Logger.getLogger(TypedBreakInterceptorsManager.class.getName()); + + private static TypedBreakInterceptorsManager instance; + + private Transaction transaction = null; + private final Map>> cache = new WeakHashMap>>(); + + private TypedBreakInterceptorsManager() { + + } + + // XXX: listne on changes in MimeLookup + private Collection getInterceptors(Document doc, int offset) { + List> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true); + TokenSequence seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1); + MimePath mimePath = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath()); + + synchronized (cache) { + Reference> ref = cache.get(mimePath); + Collection interceptors = ref == null ? null : ref.get(); + + if (interceptors == null) { + Collection factories = MimeLookup.getLookup(mimePath).lookupAll(TypedBreakInterceptor.Factory.class); + interceptors = new HashSet(factories.size()); + + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "TypedBreakInterceptor.Factory instances for {0} {" , mimePath.getPath()); //NOI18N + } + + for(TypedBreakInterceptor.Factory f : factories) { + TypedBreakInterceptor interceptor = f.createTypedBreakInterceptor(mimePath); + if (interceptor != null) { + interceptors.add(interceptor); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "{0} created: {1}", new Object[] { f, interceptor }); //NOI18N + } + } + + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("}"); //NOI18N + } + + // XXX: this should really be a timed WeakReference + cache.put(mimePath, new SoftReference>(interceptors)); + } + + return interceptors; + } + } +} --- a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypedTextInterceptorsManager.java +++ a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypedTextInterceptorsManager.java @@ -0,0 +1,223 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.typinghooks; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor; + +/** + * + * @author vita + */ +public final class TypedTextInterceptorsManager { + + public static TypedTextInterceptorsManager getInstance() { + if (instance == null) { + instance = new TypedTextInterceptorsManager(); + } + return instance; + } + + public Transaction openTransaction(JTextComponent c, int offset, String typedText) { + synchronized (this) { + if (transaction == null) { + transaction = new Transaction(c, offset, typedText); + return transaction; + } else { + throw new IllegalStateException("Too many transactions; only one at a time is allowed!"); //NOI18N + } + } + } + + public final class Transaction { + + public boolean beforeInsertion() { + for(TypedTextInterceptor i : interceptors) { + try { + if (i.beforeInsert(context)) { + return true; + } + } catch (Exception e) { + LOG.log(Level.INFO, "TypedTextInterceptor crashed in beforeInsert(): " + i, e); //NOI18N + } + } + + phase++; + return false; + } + + /** + * + * @return [0] == insertionText, [1] == new caret position (!) within [0] + */ + public Object [] textTyped() { + Object [] data = null; + + for(TypedTextInterceptor i : interceptors) { + try { + i.insert(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedTextInterceptor crashed in insert(): " + i, e); //NOI18N + TypingHooksSpiAccessor.get().resetTtiContextData(context); + continue; + } + + data = TypingHooksSpiAccessor.get().getTtiContextData(context); + if (data != null) { + break; + } + } + + phase++; + return data; + } + + public void afterInsertion() { + for(TypedTextInterceptor i : interceptors) { + try { + i.afterInsert(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedTextInterceptor crashed in afterInsert(): " + i, e); //NOI18N + } + } + + phase++; + } + + public void close() { + if (phase < 3) { + for(TypedTextInterceptor i : interceptors) { + try { + i.cancelled(context); + } catch (Exception e) { + LOG.log(Level.INFO, "TypedTextInterceptor crashed in cancelled(): " + i, e); //NOI18N + } + } + } + + synchronized (TypedTextInterceptorsManager.this) { + transaction = null; + } + } + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private final TypedTextInterceptor.MutableContext context; + private final Collection interceptors; + private int phase = 0; + + private Transaction(JTextComponent c, int offset, String typedText) { + this.context = TypingHooksSpiAccessor.get().createTtiContext(c, offset, typedText); + this.interceptors = getInterceptors(c.getDocument(), offset); + } + } // End of Transaction class + + // ------------------------------------------------------------------------ + // Private implementation + // ------------------------------------------------------------------------ + + private static final Logger LOG = Logger.getLogger(TypedTextInterceptorsManager.class.getName()); + + private static TypedTextInterceptorsManager instance; + + private Transaction transaction = null; + private final Map>> cache = new WeakHashMap>>(); + + private TypedTextInterceptorsManager() { + + } + + // XXX: listne on changes in MimeLookup + private Collection getInterceptors(Document doc, int offset) { + List> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true); + TokenSequence seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1); + MimePath mimePath = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath()); + + synchronized (cache) { + Reference> ref = cache.get(mimePath); + Collection interceptors = ref == null ? null : ref.get(); + + if (interceptors == null) { + Collection factories = MimeLookup.getLookup(mimePath).lookupAll(TypedTextInterceptor.Factory.class); + interceptors = new HashSet(factories.size()); + + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "TypedTextInterceptor.Factory instances for {0} {" , mimePath.getPath()); //NOI18N + } + + for(TypedTextInterceptor.Factory f : factories) { + TypedTextInterceptor interceptor = f.createTypedTextInterceptor(mimePath); + if (interceptor != null) { + interceptors.add(interceptor); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "{0} created: {1}", new Object[] { f, interceptor }); //NOI18N + } + } + + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("}"); //NOI18N + } + + // XXX: this should really be a timed WeakReference + cache.put(mimePath, new SoftReference>(interceptors)); + } + + return interceptors; + } + } +} --- a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypingHooksSpiAccessor.java +++ a/editor.lib2/src/org/netbeans/modules/editor/lib2/typinghooks/TypingHooksSpiAccessor.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.typinghooks; + +import javax.swing.text.JTextComponent; +import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor; +import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor; +import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor; + +/** + * + * @author vita + */ +public abstract class TypingHooksSpiAccessor { + + private static TypingHooksSpiAccessor ACCESSOR = null; + + public static synchronized void register(TypingHooksSpiAccessor accessor) { + assert ACCESSOR == null : "Can't register two package accessors!"; //NOI18N + ACCESSOR = accessor; + } + + public static synchronized TypingHooksSpiAccessor get() { + // Trying to wake up HighlightsLayer ... + try { + Class clazz = Class.forName(TypedTextInterceptor.MutableContext.class.getName()); + } catch (ClassNotFoundException e) { + // ignore + } + + assert ACCESSOR != null : "There is no package accessor available!"; //NOI18N + return ACCESSOR; + } + + /** Creates a new instance of HighlightingSpiPackageAccessor */ + protected TypingHooksSpiAccessor() { + } + + public abstract TypedTextInterceptor.MutableContext createTtiContext(JTextComponent c, int offset, String typedText); + public abstract Object [] getTtiContextData(TypedTextInterceptor.MutableContext context); + public abstract void resetTtiContextData(TypedTextInterceptor.MutableContext context); + + public abstract DeletedTextInterceptor.Context createDtiContext(JTextComponent c, int offset, String removedText, boolean backwardDelete); + + public abstract TypedBreakInterceptor.MutableContext createTbiContext(JTextComponent c, int caretOffset, int insertBreakOffset); + public abstract Object [] getTbiContextData(TypedBreakInterceptor.MutableContext context); + public abstract void resetTbiContextData(TypedBreakInterceptor.MutableContext context); +} --- a/editor.lib2/src/org/netbeans/spi/editor/codegen/package.html +++ a/editor.lib2/src/org/netbeans/spi/editor/codegen/package.html @@ -143,6 +143,10 @@ with the additional data and runs the task obtained as the parameter with the newly created context.

- + +

Use cases

+ +

TBD

+ --- a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/DeletedTextInterceptor.java +++ a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/DeletedTextInterceptor.java @@ -0,0 +1,311 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor.typinghooks; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimePath; + +/** + * An interceptor which is called when user deletes text from a document. You should + * implement this interface if you want to hook in the keyboard input + * processing done in the editor infrastructure that would normally result in removing text + * from a document. Typically, implementations of this interface will be called + * when processing KeyEvents that reach the default editor actions bound to + * VK_DELETE and VK_BACK_SPACE events. + * + *

Registration: DeletedTextInterceptors can be plugged in the editor infrastructure + * by implementing the {@link Factory} interface and registering it in MimeLookup + * under the appropriate mimetype (ie. MimePath). + * + *

The instances created from the Factory will be reused for processing + * the relevant key events received by all documents of the same mime type, which the interceptor + * instances were registered for (including documents that contain embedded sections + * of that mime type). As described in the general concepts of Typing Hooks SPI + * the interceptors are guaranteed to be called in AWT thread only, which means that + * they should not need any internal synchronization model. + * + *

Processing rules: If there are multiple instances of DeletedTextInterceptor registered + * for the same mime type the infrastructure will queue them up in their registration + * order and when processing an event it will call them all until the processing is done + * or terminated. + * + *

The interceptor has several methods that are called at different stages of + * the key typed event processing. When processing an event the infrastructure will call + * the methods in the order as they are listed below. Moreover if there are multiple + * interceptors queued up for processing an event each method is first called on + * all the queued interceptors before moving on to the next stage and calling next + * method. + * + *

    + *
  • {@link #beforeRemove(Context)} - It's called before any text is removed + * from a document. No document lock is held when this method is called. The method + * is not allowed to modify the document (and it's not supposed to do any tricks to + * break this rule). An interceptor can stop further processing of the event by returning + * true from this method. If it does so, no other interceptors' + * beforeRemove method will be called and the processing will be terminated + * without removing any text. + * + *
  • {@link #remove(Context)} - This method is called during the text + * removal stage immediately after the text was removed from a document. At this + * time the document is already write locked and the interceptors can modify it + * if they need to. + * + *
  • {@link #afterRemove(Context)} - This is the last method in the processing + * chain and it will be called when the text has already been removed from the document. + * Similarly as in beforeRemove the document is not locked when + * this method is called. + * + *
  • {@link #cancelled(Context)} - This is an additional method that will be called + * when the processing is terminated in the before-removal stage (ie. by an interceptor + * returning true from its beforeRemove method). + * The infrastructure will only call this method on interceptors that have already + * had their beforeRemove method called, but not on those that + * have not yet been called at all. + *
+ * + *

Errors recovery: If an exception is thrown from any of the methods + * when calling an interceptor the infrastructure will catch it and log it, + * but it will not stop further processing. The infrastructure may blacklist the offending + * interceptor and exclude it from processing future events. + * + * @author Vita Stejskal + * @since 1.31 + */ +public interface DeletedTextInterceptor { + + /** + * This method is called before any text is removed from a document. The context object + * passed to the method provides access to the document and its editor pane. The interceptors + * are not allowed to modify the document in this method. + * + *

This method can be used for stopping further processing of the current + * key typed event. If this method returns true the processing will + * be terminated and {@link #cancelled(Context)} will be called for all the interceptors + * that have already had their beforeRemove method called (including + * the one that terminated the processing). The rest of the interceptors waiting + * in the queue will not be called at all. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. + * + * @return If true the further processing will be stopped. Normally + * the method should return false. + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + boolean beforeRemove(Context context) throws BadLocationException; + + /** + * This method is called immediately after the text is removed from a document. + * Implementors can modify the document as they need. The document and all + * the other useful information is accessible from the Context object + * passed in this method. The document is write-locked. + * + *

Locking: When this method is called the infrastructure has already + * write locked the document. + * + * @param context The context object providing information necessary for processing + * the event and allowing to modify the edited document. + * + * @throws BadLocationException If the processing fails. + */ + void remove(Context context) throws BadLocationException; + + /** + * This method is called after text is removed from a document and its editor's + * caret is adjusted. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. The {@link Context#getText()} method will return text that was + * removed from the document at the beginning of the text-removal stage. + * + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + void afterRemove(Context context) throws BadLocationException; + + /** + * This method is called when the normal processing is terminated by some + * interceptor's beforeRemove method. Please note that this + * method will not be called if the beforeRemove method was not + * called. + * + * @param context The context object used for calling the beforeRemove + * method. + */ + void cancelled(Context context); + + /** + * The context class providing information about the edited document, its + * editor pane and the offset where the delete key event occurred. + */ + public static final class Context { + + /** + * Gets the editor component where the currently processed key typed event + * occurred. + * + * @return The editor pane that contains the edited Document. + */ + public JTextComponent getComponent() { + return component; + } + + /** + * Gets the edited document. It's the document, where the text will be + * removed. + * + * @return The edited document. + */ + public Document getDocument() { + return document; + } + + /** + * Gets the removal offset. This is the offset in the document where + * a user performed the delete action (ie. where the currently processed KeyEvent + * happened). This is also the offset with text, which will be removed. + * + * @return The offset in the edited document. + */ + public int getOffset() { + return offset; + } + +// XXX: since this is always one (character) it make no sense to have it +// public int getLength() { +// return lenght; +// } + + /** + * Determines the type of the character removal action performed by a user. The two + * possible actions are called differently on different platforms, + * but they are always defined by the position of a character, which they are + * applied to. The backspace action deletes a character on the left hand + * side of a caret, while the delete action deletes a character on + * the right hand side of the caret. + * + *

In other words one delete action removes characters backwards moving + * the caret towards the beginning if a document and the other action leaves + * the caret at the same position and removes characters towards the end + * of the document. + * + * @return true if the interceptor is called to handle the + * backspace action. false if the handled action is the + * delete action. + */ + public boolean isBackwardDelete() { + return backwardDelete; + } + + /** + * Gets the text being removed. In beforeRemove method this + * text is still present in the document, while in the other methods this + * text has already been removed from the document. Nevertheless this method + * always returns a copy of the text. + * + * @return The text being removed by the currently processed key typed event. + */ + public String getText() { + return removedText; + } + + // ------------------------------------------------------------------- + // Private implementation + // ------------------------------------------------------------------- + + private final JTextComponent component; + private final Document document; + private final int offset; + private final boolean backwardDelete; + private final String removedText; + + /* package */ Context(JTextComponent component, int offset, String removedText, boolean backwardDelete) { + this.component = component; + this.document = component.getDocument(); + this.offset = offset; + this.backwardDelete = backwardDelete; + this.removedText = removedText; + } + + } // End of Context class + + /** + * The factory interface for registering DeletedTextInterceptors + * in MimeLookup. An example registration in an XML layer shown + * below registers Factory implementation under text/x-something + * mime type in MimeLookup. + * + *

+     * <folder name="Editors">
+     *  <folder name="text">
+     *   <folder name="x-something">
+     *    <file name="org-some-module-DTIFactory.instance" />
+     *   </folder>
+     *  </folder>
+     * </folder>
+     * 
+ */ + public interface Factory { + + /** + * Creates a new interceptor for the given MimePath. + * + * @param mimePath The MimePath for which the infrastructure + * needs the new interceptor. Typically this is the same MimePath + * where this Factory was registered, but in embedded scenarios + * this can be a different MimePath. + * + * @return The new interceptor. + */ + DeletedTextInterceptor createDeletedTextInterceptor(MimePath mimePath); + } // End of Factory interface +} --- a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/TypedBreakInterceptor.java +++ a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/TypedBreakInterceptor.java @@ -0,0 +1,424 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor.typinghooks; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.openide.util.Parameters; + +/** + * An interceptor which is called when a line break is typed into a document. You should + * implement this interface if you want to hook in the keyboard input + * processing done in the editor infrastructure that would normally result in inserting a line break + * into a document. This is in fact a specialized version of {@link TypedTextInterceptor} interface, + * which works the same way, but handles inserting a line break rather than regular characters. + * The {@link TypedTextInterceptor} implementations are never called to handle the line break insertion. + * + *

Registration: TypedBreakInterceptors can be plugged in the editor infrastructure + * by implementing the {@link Factory} interface and registering it in MimeLookup + * under the appropriate mimetype (ie MimePath). + * + *

The instances created from the Factory will be reused for processing + * all keyboard input received by all documents of the same mime type, which the interceptor + * instances were registered for (including documents that contain embedded sections + * of that mime type). As described in the general concepts of Typing Hooks SPI + * the interceptors are guaranteed to be called in AWT thread only, which means that + * they should not need any internal synchronization model. + * + *

Processing rules: If there are multiple instances of TypedBreakInterceptor registered + * for the same mime type the infrastructure will queue them up in their registration + * order and when processing an event it will call them all until the processing is done + * or terminated. + * + *

The interceptor has several methods that are called at different stages of + * the key typed event processing. When processing an event the infrastructure will call + * the methods in the order as they are listed below. Moreover, if there are multiple + * interceptors queued up for processing an event each method is first called on + * all the queued interceptors before moving on to the next stage and calling next + * method. + * + *

    + *
  • {@link #beforeInsert(Context)} - It's called before a line break is inserted + * into a document. No document lock is held when this method is called. The method + * can't modify the text that will be inserted (and it's not supposed to do any tricks to + * break this rule). An interceptor can stop further processing of the event by returning + * true from this method. If it does so, no other interceptors' + * beforeInsert method will be called and the processing will be terminated + * without inserting any text. + * + *
  • {@link #insert(MutableContext)} - This method is called during the text + * insertion stage immediately before the text is inserted into a document. At this + * time the document is already write locked, but the interceptors are not expected + * to modify its content directly. Instead they can change the text that will be + * inserted by calling {@link MutableContext#setText(java.lang.String, int)} method. + * The text insertion is strictly controlled by the infrastructure and has to obey some + * additional rules (eg. correctly replacing selected text, handling insert vs override + * modes of the caret, etc). The first interceptor that modifies the insertion text + * will win and no other interceptor's insert method will be called. + *
    + * The interceptors are allowed to insert more than just a line break, but the text + * they insert has to contain at least one line break. They can also request the + * inserted text to be reindented. Please see {@link MutableContext#setText(java.lang.String, int, int, int...)} + * for details. + * + *
  • {@link #afterInsert(Context)} - This is the last method in the processing + * chain and it will be called when the text is already inserted in the document. + * Similarly as in beforeInsert the document is not locked when + * this method is called. + * + *
  • {@link #cancelled(Context)} - This is an additional method that will be called + * when the processing is terminated in the before-insertion stage (ie. by an interceptor + * returning true from its beforeInsert method). + * The infrastructure will only call this method on interceptors that have already + * had their beforeInsert method called, but not on those that + * have not yet been called at all. + *
+ * + *

Errors recovery: If an exception is thrown from any of the methods + * when calling an interceptor the infrastructure will catch it and log it, + * but it will not stop further processing. The infrastructure may blacklist the offending + * interceptor and exclude it from processing future events. + * + * @author Vita Stejskal + * @since 1.31 + */ +public interface TypedBreakInterceptor { + + /** + * This method is called before any text is inserted into a document. The context object + * passed to the method provides access to the editor pane and the edited document, + * but does not allow to modify the text that will be inserted. + * + *

This method can be used for stopping further processing of the current + * key typed event. If this method returns true the processing will + * be terminated and {@link #cancelled(Context)} will be called for all the intercetors + * that have already had their beforeInsert method called (including + * the one that terminated the processing). The rest of the interceptors waiting + * in the queue will not be called at all. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. + * + * @return If true the further processing will be stopped. Normally + * the method should return false. + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + boolean beforeInsert(Context context) throws BadLocationException; + + /** + * This method is called immediately before a line break is inserted into a document. + * Implementors can use special MutableContext to modify the text + * that will be inserted into a document. The first interceptor that sets + * the insertion text will win and the method will not be called on the rest + * of the queued interceptors. The interceptors are not supposed to modify the + * document directly. + * + *

Locking: When this method is called the infrastructure has already + * write locked the document. + * + * @param context The context object providing information necessary for processing + * the event and allowing to modify the insertion text. + * + * @throws BadLocationException If the processing fails. + */ + void insert(MutableContext context) throws BadLocationException; + + /** + * This method is called after the text is inserted into a document and its editor's + * caret is adjusted. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. The {@link Context#getText()} method will return text that was + * inserted into the document at the end of the text-insertion stage. + * + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + void afterInsert(Context context) throws BadLocationException; + + /** + * This method is called when the normal processing is terminated by some + * interceptor's beforeInsert method. Please note that this + * method will not be called if the beforeInsert method was not + * called. + * + * @param context The context object used for calling the beforeInsert + * method. + */ + void cancelled(Context context); + + /** + * The context class providing information about the edited document, its + * editor pane, caret offset, line break insertion offset and text. + */ + public static class Context { + + /** + * Gets the editor component where the currently processed key typed event + * occurred. + * + * @return The editor pane that contains the edited Document. + */ + public JTextComponent getComponent() { + return component; + } + + /** + * Gets the edited document. It's the document where the line break is going to + * be inserted. + * + * @return The edited document. + */ + public Document getDocument() { + return document; + } + + /** + * Gets the caret offset. This is the offset in the document where + * the caret is at the time when a user performed an action resulting in + * the insertion of a line break (ie. where the currently processed KeyEvent + * happened). This may or may not be the same offset, where the line break + * will be inserted. + * + * @return The offset in the edited document. + */ + public int getCaretOffset() { + return caretOffset; + } + + /** + * Gets the line break insertion offset. This is the offset in the document where + * the line break will be inserted. + * + * @return The offset in the edited document. + */ + public int getBreakInsertOffset() { + return breakInsertOffset; + } + + // ------------------------------------------------------------------- + // Private implementation + // ------------------------------------------------------------------- + + private final JTextComponent component; + private final Document document; + private final int caretOffset; + private final int breakInsertOffset; + + private Context(JTextComponent component, int caretOffset, int breakInsertOffset) { + this.component = component; + this.document = component.getDocument(); + this.caretOffset = caretOffset; + this.breakInsertOffset = breakInsertOffset; + } + + } // End of Context class + + /** + * This context class allows to modify the insertion text and the caret position + * after the text is inserted into a document. Apart from that it provides exactly the same + * information as its superclass Context. + */ + public static final class MutableContext extends Context { + + /** + * Sets the insertion text and adjusted caret position. This method can + * be used for inserting additional text that accompanies the line break typed + * in by a user. + * + *

There is no restriction on the new text + * set by this method, except that it must not be null and must contain at least + * one line break. It can be of any length and can even span multiple lines. + * + *

It is important to remember that the values of the position parameters are + * relative to the new text. Therefore valid values for the caretPosition + * parameter, for example, are <0, text.getLength()>! The position parameters + * are not document offsets. + * + *

The following rules have to be obeyed otherwise an IllegalArgumentException + * will be thrown: + * + *

    + *
  • The text has to contain at least one line break '\n' character. + *
  • The breakInsertPosition, if specified, has to point to the most + * important line break character in the text (eg. an interceptor can actually insert + * several lines, but one of them is always considered the most significant and its + * line break character position is where breakInsertPosition should point at. + *
  • The caretPosition, if specified, has to point somewhere within the text. + *
  • The reindentBlocks, if specified, are pairs of positions within the + * text. In each pair the first number denotes the starting position of a + * region that will be reindented. The ending position of that region is denoted by the + * second number in the pair. Therfore the first number has to be lower or equal to the + * second number. + *
+ * + * @param text The new text that will be inserted to a document. It must contain at least + * one line break '\n' character. + * @param breakInsertPosition The position within the text where the most significant + * line break character is. If -1, the position of the first line break character + * in the text will be used. + * @param caretPosition The position within the text where the caret will be placed + * after the text is inserted in the document. If -1, the breakInsertPosition + 1 + * will be used. + * @param reindentBlocks The list of position pairs that determine areas within the text + * that will be reindented after the text is inserted in the document. Can be null + * or zero length array in which case only the line containing the caretPosition will + * be reindented. If specified, it must contain an even number of elements. + * + * @throws IllegalArgumentException If the parameters passed in violate the rules specified above. + */ + public void setText(String text, int breakInsertPosition, int caretPosition, int... reindentBlocks) { + Parameters.notNull("text", text); //NOI18N + + if (text.indexOf('\n') == -1) { + throw new IllegalArgumentException("The text must contain a new line (\\n) character."); //NOI18N + } + + if (breakInsertPosition != -1) { + if (breakInsertPosition < 0 || breakInsertPosition >= text.length()) { + throw new IllegalArgumentException("The breakInsertPosition=" + breakInsertPosition + " must point in the text=<0, " + text.length() + ")."); //NOI18N + } + if (text.charAt(breakInsertPosition) != '\n') { + throw new IllegalArgumentException("The character at breakInsertPosition=" + breakInsertPosition + " must be the new line (\\n) character."); //NOI18N + } + } + + if (caretPosition != -1) { + if (caretPosition < 0 || caretPosition > text.length()) { + throw new IllegalArgumentException("The caretPosition=" + caretPosition + " must point in the text=<0, " + text.length() + ">."); //NOI18N + } + } + + if (reindentBlocks != null && reindentBlocks.length > 0) { + if (reindentBlocks.length % 2 != 0) { + throw new IllegalArgumentException("The reindentBlocks must contain even number of positions within the text: " + reindentBlocks.length); //NOI18N + } + for(int i = 0; i < reindentBlocks.length / 2; i++) { + int s = reindentBlocks[2 * i]; + if (s < 0 || s > text.length()) { + throw new IllegalArgumentException("The reindentBlocks[" + (2 * i) + "]=" + s + " must point in the text=<0, " + text.length() + ")."); //NOI18N + } + int e = reindentBlocks[2 * i + 1]; + if (e < 0 || e > text.length()) { + throw new IllegalArgumentException("The reindentBlocks[" + (2 * i + 1) + "]=" + e + " must point in the text=<0, " + text.length() + ")."); //NOI18N + } + if (s > e) { + throw new IllegalArgumentException("The reindentBlocks[" + (2 * i) + "]=" + s + " must be smaller than reindentBlocks[" + (2 * i + 1) + "]=" + e); //NOI18N + } + } + + } + + this.insertionText = text; + this.breakInsertPosition = breakInsertPosition; + this.caretPosition = caretPosition; + this.reindentBlocks = reindentBlocks; + } + + // ------------------------------------------------------------------- + // Private implementation + // ------------------------------------------------------------------- + + private String insertionText = null; + private int breakInsertPosition = -1; + private int caretPosition = -1; + private int [] reindentBlocks = null; + + /* package */ MutableContext(JTextComponent component, int caretOffset, int insertBreakOffset) { + super(component, caretOffset, insertBreakOffset); + } + + /* package */ Object [] getData() { + return insertionText != null ? + new Object [] { insertionText, breakInsertPosition, caretPosition, reindentBlocks } : + null; + } + + /* package */ void resetData() { + insertionText = null; + breakInsertPosition = -1; + caretPosition = -1; + reindentBlocks = null; + } + } // End of MutableContext class + + /** + * The factory interface for registering TypedBreakInterceptors + * in MimeLookup. An example registration in an XML layer shown + * below registers Factory implementation under text/x-something + * mime type in MimeLookup. + * + *
+     * <folder name="Editors">
+     *  <folder name="text">
+     *   <folder name="x-something">
+     *    <file name="org-some-module-TBIFactory.instance" />
+     *   </folder>
+     *  </folder>
+     * </folder>
+     * 
+ */ + public interface Factory { + + /** + * Creates a new interceptor for the given MimePath. + * + * @param mimePath The MimePath for which the infrastructure + * needs the new interceptor. Typically this is the same MimePath + * where this Factory was registered, but in embedded scenarios + * this can be a different MimePath. + * + * @return The new interceptor. + */ + TypedBreakInterceptor createTypedBreakInterceptor(MimePath mimePath); + } // End of Factory interface +} --- a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/TypedTextInterceptor.java +++ a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/TypedTextInterceptor.java @@ -0,0 +1,394 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor.typinghooks; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.modules.editor.lib2.typinghooks.TypingHooksSpiAccessor; + +/** + * An interceptor which is called when text is typed into a document. You should + * implement this interface if you want to hook in the keyboard input + * processing done in the editor infrastructure that would normally result in inserting text + * into a document. Typically, implementations of this interface will be called + * when processing KeyEvents that reach a default keymap action (ie. + * there is no entry in the editor's keymap for this particular KeyEvent). + * + *

Registration: TypedTextInterceptors can be plugged in the editor infrastructure + * by implementing the {@link Factory} interface and registering it in MimeLookup + * under the appropriate mimetype (ie MimePath). + * + *

The instances created from the Factory will be reused for processing + * all keyboard input received by all documents of the same mime type, which the interceptor + * instances were registered for (including documents that contain embedded sections + * of that mime type). As described in the general concepts of Typing Hooks SPI + * the interceptors are guaranteed to be called in AWT thread only, which means that + * they should not need any internal synchronization model. + * + *

Processing rules: If there are multiple instances of TypedTextInterceptor registered + * for the same mime type the infrastructure will queue them up in their registration + * order and when processing an event it will call them all until the processing is done + * or terminated. + * + *

The interceptor has several methods that are called at different stages of + * the key typed event processing. When processing an event the infrastructure will call + * the methods in the order as they are listed below. Moreover, if there are multiple + * interceptors queued up for processing an event each method is first called on + * all the queued interceptors before moving on to the next stage and calling next + * method. + * + *

    + *
  • {@link #beforeInsert(Context)} - It's called before any text is inserted + * into a document. No document lock is held when this method is called. The method + * can't modify the text that will be inserted (and it's not supposed to do any tricks to + * break this rule). An interceptor can stop further processing of the event by returning + * true from this method. If it does so, no other interceptors' + * beforeInsert method will be called and the processing will be terminated + * without inserting any text. + * + *
  • {@link #insert(MutableContext)} - This method is called during the text + * insertion stage immediately before the text is inserted into a document. At this + * time the document is already write locked, but the interceptors are not expected + * to modify its content directly. Instead they can change the text that will be + * inserted by calling {@link MutableContext#setText(java.lang.String, int)} method. + * The text insertion is strictly controlled by the infrastructure and has to obey some + * additional rules (eg. correctly replacing selected text, handling insert vs override + * modes of the caret, etc). The first interceptor that modifies the insertion text + * will win and no other interceptor's insert method will be called. + * + *
  • {@link #afterInsert(Context)} - This is the last method in the processing + * chain and it will be called when the text is already inserted in the document. + * Similarly as in beforeInsert the document is not locked when + * this method is called. + * + *
  • {@link #cancelled(Context)} - This is an additional method that will be called + * when the processing is terminated in the before-insertion stage (ie. by an interceptor + * returning true from its beforeInsert method). + * The infrastructure will only call this method on interceptors that have already + * had their beforeInsert method called, but not on those that + * have not yet been called at all. + *
+ * + *

Errors recovery: If an exception is thrown from any of the methods + * when calling an interceptor the infrastructure will catch it and log it, + * but it will not stop further processing. The infrastructure may blacklist the offending + * interceptor and exclude it from processing future events. + * + * @author Vita Stejskal + * @since 1.31 + */ +public interface TypedTextInterceptor { + + /** + * This method is called before any text is inserted into a document. The context object + * passed to the method provides access to the editor pane and the edited document, + * but does not allow to modify the text that will be inserted. + * + *

This method can be used for stopping further processing of the current + * key typed event. If this method returns true the processing will + * be terminated and {@link #cancelled(Context)} will be called for all the intercetors + * that have already had their beforeInsert method called (including + * the one that terminated the processing). The rest of the interceptors waiting + * in the queue will not be called at all. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. + * + * @return If true the further processing will be stopped. Normally + * the method should return false. + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + boolean beforeInsert(Context context) throws BadLocationException; + + /** + * This method is called immediately before the text is inserted into a document. + * Implementors can use special MutableContext to modify the text + * that will be inserted into a document. The first interceptor that mutates + * the insertion text will win and the method will not be called on the rest + * of the queued interceptors. The interceptors are not supposed to modify the + * document directly. + * + *

Locking: When this method is called the infrastructure has already + * write locked the document. + * + * @param context The context object providing information necessary for processing + * the event and allowing to modify the insertion text. + * + * @throws BadLocationException If the processing fails. + */ + void insert(MutableContext context) throws BadLocationException; + + /** + * This method is called after text is inserted into a document and its editor's + * caret is adjusted. + * + *

Locking: When this method is called the document is not locked + * by the infrastructure. + * + * @param context The context object providing information necessary for processing + * the event. The {@link Context#getText()} method will return text that was + * inserted into the document at the end of the text-insertion stage. + * + * @throws BadLocationException Since the document is not locked prior calling this + * method the processing may fail when working with stale context data. + */ + void afterInsert(Context context) throws BadLocationException; + + /** + * This method is called when the normal processing is terminated by some + * interceptor's beforeInsert method. Please note that this + * method will not be called if the beforeInsert method was not + * called. + * + * @param context The context object used for calling the beforeInsert + * method. + */ + void cancelled(Context context); + + /** + * The context class providing information about the edited document, its + * editor pane, insertion offset and text. + */ + public static class Context { + + /** + * Gets the editor component where the currently processed key typed event + * occurred. + * + * @return The editor pane that contains the edited Document. + */ + public JTextComponent getComponent() { + return component; + } + + /** + * Gets the edited document. It's the document that will receive the insertion + * text (ie the text typed by a user or its modification provided by an + * interceptor). + * + * @return The edited document. + */ + public Document getDocument() { + return document; + } + + /** + * Gets the insertion offset. This is the offset in the document where + * user typed the text (ie. where the currently processed KeyEvent + * happened). This is also the offset where the insertion text will end up. + * + * @return The offset in the edited document. + */ + public int getOffset() { + return offset; + } + + /** + * Gets the insertion text. This is the text that was typed + * by the user or its modification provided by one of the interceptors. + * + *

It is guaranteed that the text will have length equal to 1 for contexts + * that are passed to beforeInsert and insert + * methods. In these methods getText returns exactly what + * a user typed in the editor. + * + *

In the afterInsert method the text returned from getText + * method can have any length and will correspond to either the originally typed + * text or to text supplied by one of the interceptors participating in + * the key typed event processing. + * + * @return The insertion text. + */ + public String getText() { + return originallyTypedText; + } + + // ------------------------------------------------------------------- + // Private implementation + // ------------------------------------------------------------------- + + private final JTextComponent component; + private final Document document; + private final int offset; + private final String originallyTypedText; + + /* package */ Context(JTextComponent component, int offset, String typedText) { + this.component = component; + this.document = component.getDocument(); + this.offset = offset; + this.originallyTypedText = typedText; + } + + } // End of Context class + + /** + * This context class allows to modify the insertion text and the caret position + * after the text is inserted into a document. Apart from that it provides exactly the same + * information as its superclass Context. + */ + public static final class MutableContext extends Context { + + public @Override String getText() { + return insertionText != null ? insertionText : super.getText(); + } + + /** + * Sets the insertion text and adjusted caret position. This method can + * be used for modifying text typed by a user that would normally be + * inserted into a document. + * + *

There is no restriction on the new text + * set by this method, except that it must not be null. It can + * be of any length (including an empty string) and can even span multiple lines. + * + *

It is important to remember that the adjusted caret position is + * relative to the new text. Therefore valid values for the caretPosition + * parameter are <0, text.getLength()>! The adjusted position + * is not a document offset. + * + * @param text The new text that will be inserted to a document. + * @param caretPosition The adjusted caret position inside the new text. + * This position is relative to the new text. Valid values for this parameter + * are <0, text.getLength()>. + */ + public void setText(String text, int caretPosition) { + assert text != null : "Invalid text, it must not be null."; //NOI18N + assert caretPosition >= 0 && caretPosition <= text.length() : "Invalid caretPostion=" + caretPosition + ", text.length=" + text.length(); //NOI18N + + this.insertionText = text; + this.caretPosition = caretPosition; + } + + // ------------------------------------------------------------------- + // Private implementation + // ------------------------------------------------------------------- + + private String insertionText = null; + private int caretPosition = -1; + + private MutableContext(JTextComponent c, int offset, String typedText) { + super(c, offset, typedText); + } + + private static final class Accessor extends TypingHooksSpiAccessor { + + @Override + public MutableContext createTtiContext(JTextComponent c, int offset, String typedText) { + return new MutableContext(c, offset, typedText); + } + + @Override + public Object[] getTtiContextData(MutableContext context) { + return context.insertionText != null ? + new Object [] { context.insertionText, context.caretPosition } : + null; + } + + @Override + public void resetTtiContextData(MutableContext context) { + context.insertionText = null; + context.caretPosition = -1; + } + + @Override + public DeletedTextInterceptor.Context createDtiContext(JTextComponent c, int offset, String removedText, boolean backwardDelete) { + return new DeletedTextInterceptor.Context(c, offset, removedText, backwardDelete); + } + + @Override + public TypedBreakInterceptor.MutableContext createTbiContext(JTextComponent c, int caretOffset, int insertBreakOffset) { + return new TypedBreakInterceptor.MutableContext(c, caretOffset, insertBreakOffset); + } + + @Override + public Object [] getTbiContextData(TypedBreakInterceptor.MutableContext context) { + return context.getData(); + } + + @Override + public void resetTbiContextData(TypedBreakInterceptor.MutableContext context) { + context.resetData(); + } + } // End of Accessor class + + static { + TypingHooksSpiAccessor.register(new Accessor()); + } + + } // End of MutableContext class + + /** + * The factory interface for registering TypedTextInterceptors + * in MimeLookup. An example registration in an XML layer shown + * below registers Factory implementation under text/x-something + * mime type in MimeLookup. + * + *

+     * <folder name="Editors">
+     *  <folder name="text">
+     *   <folder name="x-something">
+     *    <file name="org-some-module-TTIFactory.instance" />
+     *   </folder>
+     *  </folder>
+     * </folder>
+     * 
+ */ + public interface Factory { + + /** + * Creates a new interceptor for the given MimePath. + * + * @param mimePath The MimePath for which the infrastructure + * needs the new interceptor. Typically this is the same MimePath + * where this Factory was registered, but in embedded scenarios + * this can be a different MimePath. + * + * @return The new interceptor. + */ + TypedTextInterceptor createTypedTextInterceptor(MimePath mimePath); + } // End of Factory interface + +} --- a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/package.html +++ a/editor.lib2/src/org/netbeans/spi/editor/typinghooks/package.html @@ -0,0 +1,251 @@ + + + + + + + org.netbeans.spi.editor.lib2.typinghooks + + + +

+ The Typing Hooks SPI allows modules to intercept various key typed events + handled by the editor infrastructure. Historically, this was possible by subclassing + editor actions such as DefaultKeyTypedAction or InsertBrakeAction + and overwriting their performAction method. This however is not very + flexible approach. It's main drawback is that modules can't easily intercept + key typed events handled in editors (eg. EditorKits) that are provided + by other modules. The Typing Hooks SPI addresses these drawbacks while providing + semantically compatible functionality with what used to be possible by subclassing editor actions. +

+ +

+ The editor actions superceded by Typing Hooks SPI are still available and the code + that uses them should still work. But they will be deprecated and eventually removed. +

+ + +

Keyboard input processing

+ +

+ Netbeans editor panes are ordinary JTextComponents and therefore + they fully follow keyboard events processing scheme defined by Swing (please read + more about Text Input in JTextComponent's javadoc). For the + purpose of understanding Typing Hooks SPI it is important to note that + the SPI interceptors are at the very end of the KeyEvent processing + pipeline, which means that some KeyEvents may never reach + them. However, in a typical environment these events will not present + printable characters that could be inserted in an edited document. Typically they are + shortcuts bound to special actions in the editor's Keymap (newly + a pair of InputMap, ActionMap). +

+ +

+ In general the SPI provides several different types of interceptors (interfaces) + that clients can implement and register for a document type (mimetype) of their + interest. Each interceptor type is meant to process different types of keyboard + input. The interceptor types currently available are listed below. +

+ +
    +
  • TypedTextInterceptor - processes KeyTyped events + that would normally be processed by DefaultKeyTypedAction registered + for a given editor type. Any other KeyEvent that has an Action associated + in the editor pane's keymap will trigger that Action and will not reach + registered interceptors of this type. +
  • +
  • TypedBreakInterceptor - processes KeyTyped events + that would normally result in inserting a line break in the edited document. +
  • +
  • DeletedTextInterceptor - processes KeyTyped events + that would normally remove text from the edited document. +
  • +
+ + +

Registering interceptors

+ +

+ Interceptors are created by calling a factory implementation registered in + MimeLookup. For example an implementation of TypedTextInterceptor + can be plugged in by registering TypedTextInterceptor.Factory instance + for the appropriate mimetype in MimeLookup. The snippet below shows + how this registration could look. +

+ +
+<folder name="Editors">
+  <folder name="text">
+    <folder name="x-something">
+        <file name="org-some-module-TTIFactory.instance" />
+    </folder>
+  </folder>
+</folder>
+  
+ +

+ The TTIFactory class will simply return a new instance of + the module's implementation of TypedTextInterceptor interface from its + createTypedTextInterceptor + method. +

+ + +

Interceptors lifecycle

+ +

+ In general interceptors are created as they are needed. However, unlike in other editor SPIs where SPI + implementation objects are short lived and get all their information in the form of a context object + from the factory that created them, the interceptors are long lived objects that are created with a minimal + context and reused many times. The reason for this is mostly performance considerations. Typically + there are many keyboard events as users type and their processing has to be as fast as possible. +

+ +

+ The interceptors are created for a document type or more precisely for a MimePath + of the document fragment, where a user is typing. This allows to call different interceptors + depending on where in the document a particular keyboard event happens. Therefore embedded + sections can use different interceptors than the main document. +

+ +

+ The MimePath is the only context that the interceptor factory gets from + the infrastructure and it is the only context that it is supposed to pass to interceptor + instances that it creates. All the other information needed for a particular interceptor type + to do its job will be provided at the time when an event happens and the interceptor is called + to process it. This makes it possible to reuse one interceptor implementation many times + for processing different events in different documents. +

+ + +

Threading and synchronization

+ +

+ As explained earlier Netbeans editors follow swing concepts for handling keyboard + events. All keyboard events processing and editor actions invocation in swing + editors is done in the AWT thread, which means that events are processed in one + thread only and in the order as they happened. The same applies for interceptors + and Typing Hooks SPI in general. +

+ +

+ In particular it is guaranteed that only one interceptor is used at a time and + it's always called from AWT thread. Therefore a typical interceptor does not have to use + any explicit synchronization code (unless it accesses a resource that + can be simultaneously accessed from other threads of course). +

+ +

+ However, interceptor types may define a specific protocol, which determines how interceptors + of that type are chained, what methods are called first, if and what document locks are + acquired prior calling the interceptor, conditions when the event processing + can be interrupted, what happens with the rest of the queued interceptors, etc. + Please see documentation of each interceptor type interface for detailed information. +

+ + +

Use cases

+ +

Use case 1. - Automated indentation

+ +

+ Many languages need to automatically reindent lines as user types certain + statements. For example in java language the 'case' statements in a 'switch' block + should be aligned to the same column similarly as in the code snippet below. + The java editor helps users to do that by automatically indenting the lines that + contain 'case' statements as soon as a user types the double colon at the end of the statement. +

+ +
+    switch(price) {
+      case 100:
+        // do something cheap
+        break;
+
+      case 1000:
+        // do something more expensive
+        break;
+    }
+  
+ +

+ This can easily be done by implementing + TypedTextInterceptor + and its afterInsert method. The implementation can check the text + inserted in the document as a result of the key typed event processing and reindent + the line if its text matches required pattern. +

+ +

+ Moreover, the Editor Indentation API + provides an implementation of TypedTextInterceptor, which reindents lines that + match provided regular expressions. Please see AutomatedIndenting + class for more details. +

+ +

Use case 2. - Pair characters completion

+ +

+ It can safe a lot of typing if the editor correctly completes pair characters + such as (), {}, "", '', etc. + For example when one of the pair character is typed or deleted the editor + automatically adds or removes its couterpart. Additionally, the editor may wrap selected text in + the pair characters such as quotes "" if one of them is typed (eg. in java producing a string literal). +

+ +

+ These features can be implemented by providing specialized + TypedTextInterceptor + and + DeletedTextInterceptor + implementations. +

+ + + + + --- a/editor.macros/nbproject/project.xml +++ a/editor.macros/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.41 + 1.53 @@ -28,7 +28,7 @@ - 2 + 3 --- a/editor.mimelookup.impl/src/org/netbeans/modules/editor/mimelookup/impl/DefaultMimeDataProvider.java +++ a/editor.mimelookup.impl/src/org/netbeans/modules/editor/mimelookup/impl/DefaultMimeDataProvider.java @@ -52,7 +52,7 @@ * * @author vita */ -@org.openide.util.lookup.ServiceProvider(service=org.netbeans.spi.editor.mimelookup.MimeDataProvider.class) +@org.openide.util.lookup.ServiceProvider(service=org.netbeans.spi.editor.mimelookup.MimeDataProvider.class, position=0) public class DefaultMimeDataProvider implements MimeDataProvider { /** Creates a new instance of DefaultMimeDataProvider */ --- a/editor.mimelookup/src/org/netbeans/modules/editor/mimelookup/MimePathLookup.java +++ a/editor.mimelookup/src/org/netbeans/modules/editor/mimelookup/MimePathLookup.java @@ -113,11 +113,18 @@ private void rebuild() { ArrayList lookups = new ArrayList(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Rebuilding MimeLookup for '" + mimePath.getPath() + "'..."); //NOI18N + } + // Add lookups from MimeDataProviders for (MimeDataProvider provider : dataProviders.allInstances()) { if (mimePathBanned && !isDefaultProvider(provider)) { continue; } + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("- Querying MimeDataProvider(" + mimePath.getPath() + "): " + provider); //NOI18N + } Lookup mimePathLookup = provider.getLookup(mimePath); if (mimePathLookup != null) { lookups.add(mimePathLookup); @@ -160,6 +167,9 @@ } for(MimeLookupInitializer mli : initializers) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("- Querying MimeLookupInitializer(" + path + "): " + mli); //NOI18N + } Lookup mimePathLookup = mli.lookup(); if (mimePathLookup != null) { lookups.add(mimePathLookup); @@ -167,6 +177,10 @@ } } + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("MimeLookup for '" + mimePath.getPath() + "' rebuilt."); //NOI18N + } + setLookups(lookups.toArray(new Lookup[lookups.size()])); } --- a/editor.plain.lib/nbproject/project.xml +++ a/editor.plain.lib/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.plain/nbproject/project.xml +++ a/editor.plain/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/editor.structure/nbproject/project.xml +++ a/editor.structure/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -100,6 +100,15 @@ 8.0 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + --- a/editor/apichanges.xml +++ a/editor/apichanges.xml @@ -108,6 +108,68 @@ + + + Deprecating old formatting API + + + + + +

+ The old formatting API (org.netbeans.editor.Formatter & co.) + has been deprecated and moved to editor.deprecated.pre65formatting module. +

+

The exact API changes are listed in the sigtest report below.

+
+-sigtest-init:
+     [echo] sigtest check: editor; cnb: org-netbeans-modules-editor; email: api-changes@netbeans.org; type: check
+
+check-sigtest:
+    [mkdir] Created dir: /work/netbeans/main2m/editor/build/test/sigtest/results
+  [sigtest] /work/netbeans/main2m/editor/build/test/sigtest/results/org.netbeans.modules.editor
+  [sigtest] Packages: org.netbeans.modules.editor.*, org.netbeans.modules.editor.options.*
+  [sigtest] Missing /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/rt.jar
+  [sigtest] 
+  [sigtest] email: api-changes@netbeans.org
+  [sigtest] SignatureTest report
+  [sigtest] Base version: 1.51.0
+  [sigtest] Tested version: 1.51.0
+  [sigtest] Check mode: bin [throws removed]
+  [sigtest] Constant checking: on
+  [sigtest] 
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.FormatterIndentEngine
+  [sigtest]   "E1.2 - API type removed" : CLASS public abstract org.netbeans.modules.editor.FormatterIndentEngine
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.FormatterIndentEngineBeanInfo
+  [sigtest]   "E1.2 - API type removed" : CLASS public abstract org.netbeans.modules.editor.FormatterIndentEngineBeanInfo
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.IndentEngineFormatter
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.modules.editor.IndentEngineFormatter
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.NbEditorDocument
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getFormatter()
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseDocument.getLegacyFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.NbEditorKit
+  [sigtest]   "E1.2 - API type removed" : method public org.netbeans.editor.Formatter org.netbeans.editor.BaseKit.createFormatter()
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.SimpleIndentEngine
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.modules.editor.SimpleIndentEngine
+  [sigtest] 
+  [sigtest] Class org.netbeans.modules.editor.SimpleIndentEngineBeanInfo
+  [sigtest]   "E1.2 - API type removed" : CLASS public org.netbeans.modules.editor.SimpleIndentEngineBeanInfo
+  [sigtest] 
+  [sigtest] /work/netbeans/main2m/editor/build/test/sigtest/results/org-netbeans-modules-editor.xml: 1 failures in /work/netbeans/main2m/editor/nbproject/org-netbeans-modules-editor.sig
+
+BUILD FAILED
+Signature tests return code is wrong (1), check the messages above
+
+
+ +
+ Deprecating old settings API --- a/editor/manifest.mf +++ a/editor/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor/3 -OpenIDE-Module-Implementation-Version: 3 +OpenIDE-Module-Implementation-Version: 5 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/Bundle.properties OpenIDE-Module-Install: org/netbeans/modules/editor/EditorModule.class OpenIDE-Module-Layer: org/netbeans/modules/editor/resources/layer.xml --- a/editor/module-auto-deps.xml +++ a/editor/module-auto-deps.xml @@ -48,7 +48,7 @@ - Old editor settings and completion APIs have been deprecated, see http://wiki.netbeans.org/EditorSettingsUpgrade and update your module. + The old editor settings and completion APIs have been deprecated, see http://wiki.netbeans.org/EditorSettingsUpgrade and update your module. @@ -56,9 +56,25 @@ + + + + + The old editor formatting API have been deprecated, see http://wiki.netbeans.org/EditorFormattingAPIUpgrade and update your module. + + + + + + + + + + + --- a/editor/nbproject/project.properties +++ a/editor/nbproject/project.properties @@ -42,7 +42,7 @@ javac.compilerargs=-Xlint:unchecked javac.source=1.6 -spec.version.base=1.52.0 +spec.version.base=1.53.0 is.autoload=true javadoc.arch=${basedir}/arch.xml --- a/editor/nbproject/project.xml +++ a/editor/nbproject/project.xml @@ -72,7 +72,7 @@ - 2 + 3 --- a/editor/src/org/netbeans/modules/editor/impl/ComplexValueSettingsFactory.java +++ a/editor/src/org/netbeans/modules/editor/impl/ComplexValueSettingsFactory.java @@ -44,15 +44,12 @@ import java.util.Collection; import java.util.prefs.Preferences; -import javax.swing.text.EditorKit; import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.EditorStyleConstants; import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.editor.AcceptorFactory; -import org.netbeans.modules.editor.FormatterIndentEngine; -import org.netbeans.modules.editor.IndentEngineFormatter; import org.netbeans.modules.editor.NbEditorDocument; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.text.IndentEngine; @@ -86,25 +83,29 @@ // 'formatter' setting // ----------------------------------------------------------------------- - public static final Object getFormatterValue(MimePath mimePath, String settingName) { - assert settingName.equals(NbEditorDocument.FORMATTER) : "The getFormatter factory called for '" + settingName + "'"; //NOI18N +// This was moved to editor.deprecated.pre65formatting module as well as the layer preferences.xml +// which accesses this method. +// +// public static final Object getFormatterValue(MimePath mimePath, String settingName) { +// assert settingName.equals(NbEditorDocument.FORMATTER) : "The getFormatter factory called for '" + settingName + "'"; //NOI18N +// +// IndentEngine eng = getIndentEngine(mimePath); +// +// if (eng != null) { +// if (eng instanceof FormatterIndentEngine) { +// return ((FormatterIndentEngine)eng).getFormatter(); +// } else { +// EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class); +// if (kit != null) { +// return new IndentEngineFormatter(kit.getClass(), eng); +// } +// } +// } +// +// return null; +// } - IndentEngine eng = getIndentEngine(mimePath); - - if (eng != null) { - if (eng instanceof FormatterIndentEngine) { - return ((FormatterIndentEngine)eng).getFormatter(); - } else { - EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class); - if (kit != null) { - return new IndentEngineFormatter(kit.getClass(), eng); - } - } - } - - return null; - } - + // This is not called from a layer, but from NbEditorDocument and e.d.pre65formatting/.../ComplexValueSettingsFactory.getFormatterValue public static final IndentEngine getIndentEngine(MimePath mimePath) { IndentEngine eng = null; --- a/editor/src/org/netbeans/modules/editor/resources/NetBeans-preferences.xml +++ a/editor/src/org/netbeans/modules/editor/resources/NetBeans-preferences.xml @@ -261,9 +261,6 @@ - - - --- a/form/nbproject/project.xml +++ a/form/nbproject/project.xml @@ -149,8 +149,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/gototest/nbproject/project.xml +++ a/gototest/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/groovy.editor/nbproject/project.xml +++ a/groovy.editor/nbproject/project.xml @@ -73,7 +73,7 @@ 3 - 1.41 + 1.53 @@ -99,8 +99,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/groovy.grailsproject/nbproject/project.xml +++ a/groovy.grailsproject/nbproject/project.xml @@ -65,7 +65,7 @@ 3 - 1.41 + 1.53 @@ -73,8 +73,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/groovy.gsp/nbproject/project.xml +++ a/groovy.gsp/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/groovy.refactoring/nbproject/project.xml +++ a/groovy.refactoring/nbproject/project.xml @@ -36,8 +36,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/gsf.api/nbproject/project.xml +++ a/gsf.api/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.42 + 1.53 @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/gsf.api/src/org/netbeans/modules/gsf/api/EditList.java +++ a/gsf.api/src/org/netbeans/modules/gsf/api/EditList.java @@ -49,6 +49,7 @@ import javax.swing.text.Position; import javax.swing.text.StyledDocument; import org.netbeans.editor.BaseDocument; +import org.netbeans.modules.editor.indent.api.Reformat; import org.openide.text.NbDocument; import org.openide.util.Exceptions; @@ -139,58 +140,50 @@ Collections.sort(edits); Collections.reverse(edits); - doc.runAtomic(new Runnable() { + final Reformat f = Reformat.get(doc); + f.lock(); + try { + doc.runAtomic(new Runnable() { - public void run() { - try { - int lastOffset = edits.get(0).offset; - Position lastPos = doc.createPosition(lastOffset, Position.Bias.Forward); + public void run() { + try { + int lastOffset = edits.get(0).offset; + Position lastPos = doc.createPosition(lastOffset, Position.Bias.Forward); - // Apply edits in reverse order (to keep offsets accurate) - for (Edit edit : edits) { - if (edit.removeLen > 0) { - doc.remove(edit.offset, edit.removeLen); - } - if (edit.getInsertText() != null) { - doc.insertString(edit.offset, edit.insertText, null); - int end = edit.offset + edit.insertText.length(); - for (int i = 0; i < positions.size(); i++) { - DelegatedPosition pos = positions.get(i); - int positionOffset = pos.originalOffset; - if (edit.getOffset() <= positionOffset && end >= positionOffset) { - pos.delegate = doc.createPosition(positionOffset, pos.bias); // Position of the comment + // Apply edits in reverse order (to keep offsets accurate) + for (Edit edit : edits) { + if (edit.removeLen > 0) { + doc.remove(edit.offset, edit.removeLen); + } + if (edit.getInsertText() != null) { + doc.insertString(edit.offset, edit.insertText, null); + int end = edit.offset + edit.insertText.length(); + for (int i = 0; i < positions.size(); i++) { + DelegatedPosition pos = positions.get(i); + int positionOffset = pos.originalOffset; + if (edit.getOffset() <= positionOffset && end >= positionOffset) { + pos.delegate = doc.createPosition(positionOffset, pos.bias); // Position of the comment + } } - } - if (edit.format) { - @SuppressWarnings("deprecation") // For doc.getFormatter() - final org.netbeans.editor.Formatter f = doc.getFormatter(); - try { - f.reformatLock(); - f.reformat(doc, edit.offset, end); - } finally { - f.reformatUnlock(); + if (edit.format) { + f.reformat(edit.offset, end); } } } + + if (formatAll) { + int firstOffset = edits.get(edits.size() - 1).offset; + lastOffset = lastPos.getOffset(); + f.reformat(firstOffset, lastOffset); + } + } catch (BadLocationException ble) { + Exceptions.printStackTrace(ble); } - - if (formatAll) { - int firstOffset = edits.get(edits.size() - 1).offset; - lastOffset = lastPos.getOffset(); - @SuppressWarnings("deprecation") - final org.netbeans.editor.Formatter f = doc.getFormatter(); - try { - f.reformatLock(); - f.reformat(doc, firstOffset, lastOffset); - } finally { - f.reformatUnlock(); - } - } - } catch (BadLocationException ble) { - Exceptions.printStackTrace(ble); } - } - }); + }); + } finally { + f.unlock(); + } } public OffsetRange getRange() { --- a/gsf.codecoverage/nbproject/project.xml +++ a/gsf.codecoverage/nbproject/project.xml @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/gsf/nbproject/project.xml +++ a/gsf/nbproject/project.xml @@ -38,7 +38,7 @@ 3 - 1.41 + 1.53 @@ -100,8 +100,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/gsf/test/unit/src/org/netbeans/modules/gsf/GsfTestBase.java +++ a/gsf/test/unit/src/org/netbeans/modules/gsf/GsfTestBase.java @@ -155,6 +155,7 @@ import org.netbeans.editor.BaseKit; import org.netbeans.editor.Utilities; import org.netbeans.junit.MockServices; +import org.netbeans.modules.editor.indent.api.Reformat; import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; import org.netbeans.modules.editor.indent.spi.IndentTask; import org.netbeans.modules.editor.indent.spi.ReformatTask; @@ -1989,18 +1990,12 @@ configureIndenters(document, formatter, compilationInfo, indentOnly); // formatter.reformat(doc, startPos, endPos, getInfoForText(source, "unittestdata")); - final org.netbeans.editor.Formatter f = document.getFormatter(); - boolean locked = false; - try { - f.reformatLock(); - locked = true; - int reformattedLen = f.reformat(document, - Math.min(document.getLength(), startPos), - Math.min(document.getLength(), endPos)); + Reformat f = Reformat.get(document); + f.lock(); + try { + f.reformat(Math.min(document.getLength(), startPos), Math.min(document.getLength(), endPos)); } finally { - if (locked) { - f.reformatUnlock(); - } + f.unlock(); } } --- a/hibernate/nbproject/project.xml +++ a/hibernate/nbproject/project.xml @@ -150,7 +150,7 @@ 3 - 1.41 + 1.53 @@ -167,8 +167,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/html.editor.lib/nbproject/project.xml +++ a/html.editor.lib/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/html.editor/nbproject/project.xml +++ a/html.editor/nbproject/project.xml @@ -73,7 +73,7 @@ 3 - 1.41 + 1.53 @@ -116,8 +116,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/html/nbproject/project.xml +++ a/html/nbproject/project.xml @@ -73,7 +73,7 @@ 3 - 1.41 + 1.53 @@ -90,8 +90,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.common/nbproject/project.xml +++ a/j2ee.common/nbproject/project.xml @@ -117,7 +117,7 @@ 3 - 1.41 + 1.53 @@ -133,8 +133,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.ejbcore/nbproject/project.xml +++ a/j2ee.ejbcore/nbproject/project.xml @@ -149,7 +149,7 @@ 3 - 1.41 + 1.53 @@ -165,8 +165,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.ejbjarproject/nbproject/project.xml +++ a/j2ee.ejbjarproject/nbproject/project.xml @@ -124,7 +124,7 @@ 3 - 1.41 + 1.53 @@ -149,8 +149,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.jpa.refactoring/nbproject/project.xml +++ a/j2ee.jpa.refactoring/nbproject/project.xml @@ -36,8 +36,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.persistence/nbproject/project.xml +++ a/j2ee.persistence/nbproject/project.xml @@ -140,7 +140,7 @@ 3 - 1.41 + 1.53 @@ -156,8 +156,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/j2ee.websphere6/nbproject/project.xml +++ a/j2ee.websphere6/nbproject/project.xml @@ -83,6 +83,24 @@ + org.netbeans.modules.editor + + + + 3 + 1.53 + + + + org.netbeans.modules.editor.util + + + + 1 + 1.8.1 + + + org.netbeans.modules.j2ee.api.ejbmodule 1.7 --- a/java.debug/nbproject/project.xml +++ a/java.debug/nbproject/project.xml @@ -43,8 +43,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/java.editor.lib/nbproject/project.xml +++ a/java.editor.lib/nbproject/project.xml @@ -71,8 +71,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -108,6 +108,15 @@ 8.0 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + org.netbeans.editor.ext.java --- a/java.editor/nbproject/project.xml +++ a/java.editor/nbproject/project.xml @@ -88,7 +88,7 @@ 3 - 1.41 + 1.53 @@ -118,6 +118,15 @@ + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + + org.netbeans.modules.editor.errorstripe @@ -148,8 +157,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java +++ a/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java @@ -63,7 +63,6 @@ import org.netbeans.api.java.source.CodeStyle; import org.netbeans.editor.*; import org.netbeans.editor.Utilities; -import org.netbeans.editor.ext.ExtFormatter; import org.netbeans.editor.ext.java.*; import org.netbeans.api.java.queries.SourceLevelQuery; import org.netbeans.api.lexer.PartType; @@ -75,7 +74,7 @@ import org.netbeans.modules.editor.MainMenuAction; import org.netbeans.modules.editor.NbEditorKit; import org.netbeans.modules.editor.NbEditorUtilities; -import org.netbeans.modules.editor.indent.api.Reformat; +import org.netbeans.modules.editor.indent.api.Indent; import org.netbeans.modules.java.editor.codegen.InsertSemicolonAction; import org.netbeans.modules.java.editor.imports.FastImportAction; import org.netbeans.modules.java.editor.imports.JavaFixAllImports; @@ -366,43 +365,6 @@ } } } - - // TODO: remove this method once ExtKit uses the new Editor Indent API - protected void checkIndentHotChars(JTextComponent target, String typedText) { - BaseDocument doc = Utilities.getDocument(target); - if (doc != null) { - Formatter f = doc.getFormatter(); - if (f instanceof ExtFormatter) { - ExtFormatter ef = (ExtFormatter)f; - int[] fmtBlk = ef.getReformatBlock(target, typedText); - - if (fmtBlk != null) { - try { - fmtBlk[0] = Utilities.getRowStart(doc, fmtBlk[0]); - fmtBlk[1] = Utilities.getRowEnd(doc, fmtBlk[1]); - - //this was the of #18922, that causes the bug #20198 - //ef.reformat(doc, fmtBlk[0], fmtBlk[1]); - - //bugfix of the bug #20198. Bug #18922 is fixed too as well as #6968 - Reformat reformat = Reformat.get(doc); - reformat.lock(); - try { - doc.atomicLock(); - try { - reformat.reformat(fmtBlk[0], fmtBlk[1]); - } finally { - doc.atomicUnlock(); - } - } finally { - reformat.unlock(); - } - } catch (BadLocationException e) { - } - } - } - } - } } @EditorActionRegistration(name = generateGoToPopupAction, mimeType = JAVA_MIME_TYPE) @@ -647,7 +609,7 @@ int end = BraceCompletion.getRowOrBlockEnd(doc, dotPos, insert); if (insert[0]) { doc.insertString(end, "}", null); // NOI18N - doc.getFormatter().indentNewLine(doc, end); + Indent.get(doc).indentNewLine(end); } caret.setDot(dotPos); return Boolean.TRUE; @@ -688,7 +650,7 @@ // complete open javadoc // note that the formater will add one line of javadoc doc.insertString(dotPosition, "*/", null); // NOI18N - doc.getFormatter().indentNewLine(doc, dotPosition); + Indent.get(doc).indentNewLine(dotPosition); target.setCaretPosition(dotPosition); isJavadocTouched = true; --- a/java.editor/src/org/netbeans/modules/java/editor/codegen/InsertSemicolonAction.java +++ a/java.editor/src/org/netbeans/modules/java/editor/codegen/InsertSemicolonAction.java @@ -36,8 +36,8 @@ import javax.swing.text.JTextComponent; import org.netbeans.editor.BaseAction; import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; import org.netbeans.editor.Utilities; +import org.netbeans.modules.editor.indent.api.Indent; import org.openide.util.Exceptions; import org.openide.util.NbBundle; @@ -73,12 +73,12 @@ return; } final BaseDocument doc = (BaseDocument) target.getDocument(); - final Formatter formatter = doc.getFormatter(); + final Indent indenter = Indent.get(doc); final class R implements Runnable { - public void run() { - Caret caret = target.getCaret(); - int dotpos = caret.getDot(); + public @Override void run() { try { + Caret caret = target.getCaret(); + int dotpos = caret.getDot(); int eoloffset = Utilities.getRowEnd(target, dotpos); doc.insertString(eoloffset, "" + what, null); //NOI18N if (withNewline) { @@ -87,7 +87,7 @@ doc.insertString(dotpos, "-", null); //NOI18N doc.remove(dotpos, 1); int eolDot = Utilities.getRowEnd(target, caret.getDot()); - int newDotPos = formatter.indentNewLine(doc, eolDot); + int newDotPos = indenter.indentNewLine(eolDot); caret.setDot(newDotPos); } } catch (BadLocationException ex) { @@ -95,11 +95,11 @@ } } } - formatter.indentLock(); + indenter.lock(); try { doc.runAtomicAsUser(new R()); } finally { - formatter.indentUnlock(); + indenter.unlock(); } } } --- a/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaFormatterUnitTestCase.java +++ a/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaFormatterUnitTestCase.java @@ -46,7 +46,7 @@ import javax.swing.text.BadLocationException; import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; +import org.netbeans.modules.editor.indent.api.Indent; import org.netbeans.modules.editor.indent.api.Reformat; /** @@ -70,9 +70,16 @@ // hooked through the ExtKit.ExtDefaultKeyTypedAction.checkIndentHotChars(), // which calls f.getReformatBlock and f.reformat // IMO this should just be replaced by simple doc.insertString(getCaretOffset(), "\n", null) - Formatter f = getDocument().getFormatter(); - int offset = f.indentNewLine(getDocument(), getCaretOffset()); - getCaret().setDot(offset); + Indent indenter = Indent.get(getDocument()); + indenter.lock(); + try { + int offset = indenter.indentNewLine(getCaretOffset()); + getCaret().setDot(offset); + } catch (BadLocationException ble) { + throw new IllegalStateException(ble); + } finally { + indenter.unlock(); + } } /** --- a/java.hints/nbproject/project.xml +++ a/java.hints/nbproject/project.xml @@ -117,7 +117,7 @@ 3 - 1.41 + 1.53 @@ -134,8 +134,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/java.navigation/nbproject/project.xml +++ a/java.navigation/nbproject/project.xml @@ -98,7 +98,7 @@ 3 - 1.41 + 1.53 @@ -106,8 +106,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/java.source/nbproject/project.xml +++ a/java.source/nbproject/project.xml @@ -148,8 +148,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/javascript.editing/nbproject/project.xml +++ a/javascript.editing/nbproject/project.xml @@ -65,7 +65,7 @@ 3 - 1.41 + 1.53 @@ -91,8 +91,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/javascript.hints/nbproject/project.xml +++ a/javascript.hints/nbproject/project.xml @@ -46,8 +46,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/javascript.refactoring/nbproject/project.xml +++ a/javascript.refactoring/nbproject/project.xml @@ -46,8 +46,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/jellytools.ide/nbproject/project.xml +++ a/jellytools.ide/nbproject/project.xml @@ -36,8 +36,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/jumpto/nbproject/project.xml +++ a/jumpto/nbproject/project.xml @@ -73,8 +73,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/kenai.ui/nbproject/project.xml +++ a/kenai.ui/nbproject/project.xml @@ -64,7 +64,7 @@ 3 - 1.43 + 1.53 --- a/languages.refactoring/nbproject/project.xml +++ a/languages.refactoring/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -19,8 +19,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/languages.yaml/nbproject/project.xml +++ a/languages.yaml/nbproject/project.xml @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/languages/nbproject/project.xml +++ a/languages/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.41 + 1.53 @@ -70,8 +70,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/lexer.editorbridge/nbproject/project.xml +++ a/lexer.editorbridge/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 @@ -63,8 +63,8 @@ - 1 - 1.27 + 3 + 3.1 @@ -133,6 +133,15 @@ 1.0 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + org.netbeans.modules.lexer.editorbridge --- a/maven.grammar/nbproject/project.xml +++ a/maven.grammar/nbproject/project.xml @@ -64,7 +64,7 @@ 3 - 1.41 + 1.53 @@ -72,8 +72,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/maven.hints/nbproject/project.xml +++ a/maven.hints/nbproject/project.xml @@ -72,7 +72,7 @@ 3 - 1.42 + 1.53 @@ -89,8 +89,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/maven.jaxws/nbproject/project.xml +++ a/maven.jaxws/nbproject/project.xml @@ -90,7 +90,7 @@ 3 - 1.42 + 1.53 --- a/maven.model/nbproject/project.xml +++ a/maven.model/nbproject/project.xml @@ -10,8 +10,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/mercurial/nbproject/project.xml +++ a/mercurial/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/mobility.editor/nbproject/project.xml +++ a/mobility.editor/nbproject/project.xml @@ -79,7 +79,7 @@ 3 - 1.41 + 1.53 @@ -105,8 +105,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/mobility.project.bridge.impl/nbproject/project.xml +++ a/mobility.project.bridge.impl/nbproject/project.xml @@ -37,7 +37,7 @@ 3 - 1.41 + 1.53 @@ -45,8 +45,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/mobility.svgcore/nbproject/project.xml +++ a/mobility.svgcore/nbproject/project.xml @@ -47,7 +47,7 @@ 3 - 1.41 + 1.53 @@ -64,8 +64,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/mobility.svgcore/src/org/netbeans/modules/mobility/svgcore/model/SVGFileModel.java +++ a/mobility.svgcore/src/org/netbeans/modules/mobility/svgcore/model/SVGFileModel.java @@ -849,7 +849,7 @@ } } doc.insertString(insertPosition, str, null); - Reformat.get(doc).reformat(insertPosition, insertPosition + str.length()+1); + Reformat.get(doc).reformat(insertPosition, insertPosition + str.length() + 1); } else { String docText = doc.getText(0, doc.getLength()); int startOff = svgRoot.getStartOffset(); @@ -862,7 +862,7 @@ if (c == '/') { if (docText.charAt(insertPosition) == '<') { doc.insertString(insertPosition, insertString, null); - Reformat.get(doc).reformat(insertPosition, insertPosition + insertString.length()+1); + Reformat.get(doc).reformat(insertPosition, insertPosition + insertString.length() + 1); } else { StringBuilder sb = new StringBuilder(docText.substring(startOff, insertPosition + 1)); sb.append(">\n"); //NOI18N --- a/mobility.svgcore/src/org/netbeans/modules/mobility/svgcore/palette/SVGPaletteItemDataObject.java +++ a/mobility.svgcore/src/org/netbeans/modules/mobility/svgcore/palette/SVGPaletteItemDataObject.java @@ -86,7 +86,7 @@ return new File( m_data.getFilePath()); } - protected Node createNodeDelegate() { + protected @Override Node createNodeDelegate() { return new SVGPaletteItemDataNode(this, getLookup()); } @@ -124,14 +124,12 @@ public static void insertToTextComponent( final String text, final JTextComponent target) { final Document doc = target.getDocument(); - if ( doc instanceof BaseDocument) { - BaseDocument bDoc = (BaseDocument) doc; - final Reformat formatter = Reformat.get(bDoc); + if (doc instanceof BaseDocument) { + final Reformat formatter = Reformat.get(doc); formatter.lock(); try { - - Runnable run = new Runnable() { - + final boolean [] ok = new boolean [] { false }; + ((BaseDocument) doc).runAtomic(new Runnable() { public void run() { try { Caret caret = target.getCaret(); @@ -141,32 +139,21 @@ int start = caret.getDot(); doc.insertString(start, text, null); - + int end = start + text.length(); formatter.reformat(start, end); - } catch (BadLocationException ex) { - throw new DocumentModificationException(ex); + ok[0] = true; + } catch (BadLocationException ble) { + // ignore } } - }; - try { - bDoc.runAtomic(run); - } catch (DocumentModificationException ex) { - SceneManager.error("Transaction failed.", ex.getCause()); //NOI18N + }); + if (!ok[0]) { + ((BaseDocument) doc).atomicUndo(); } - } finally { formatter.unlock(); } - } } - - private static class DocumentModificationException extends RuntimeException { - - public DocumentModificationException(Throwable cause) { - super(cause); - } - } - } --- a/nbbuild/cluster.properties +++ a/nbbuild/cluster.properties @@ -302,6 +302,7 @@ editor.codetemplates,\ editor.completion,\ editor.deprecated.pre61settings,\ + editor.deprecated.pre65formatting,\ editor.errorstripe,\ editor.errorstripe.api,\ editor.fold,\ --- a/options.editor/nbproject/project.xml +++ a/options.editor/nbproject/project.xml @@ -64,7 +64,7 @@ 3 - 1.41 + 1.53 @@ -81,8 +81,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/parsing.api/nbproject/project.xml +++ a/parsing.api/nbproject/project.xml @@ -46,8 +46,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/php.editor/nbproject/project.xml +++ a/php.editor/nbproject/project.xml @@ -73,7 +73,7 @@ 3 - 1.42 + 1.53 @@ -117,8 +117,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/GeneratingBracketCompleterTest.java +++ a/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/GeneratingBracketCompleterTest.java @@ -51,6 +51,7 @@ import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.csl.spi.GsfUtilities; import org.netbeans.modules.csl.spi.ParserResult; +import org.netbeans.modules.editor.indent.api.Reformat; import org.netbeans.modules.parsing.api.ResultIterator; import org.netbeans.modules.parsing.api.UserTask; import org.netbeans.modules.php.api.util.FileUtils; @@ -220,11 +221,10 @@ } private void insertBreak(ParserResult info, String original, String expected, int insertOffset, int finalCaretPos) throws BadLocationException, DataObjectNotFoundException, IOException { - PHPBracketCompleter bc = new PHPBracketCompleter(); BaseDocument doc = (BaseDocument) info.getSnapshot().getSource().getDocument(false);//PHPBracketCompleterTest.getDocument(original); assertNotNull(doc); - + doc.putProperty(org.netbeans.api.lexer.Language.class, PHPTokenId.language()); doc.putProperty("mimeType", FileUtils.PHP_MIME_TYPE); // doc.putProperty(Document.StreamDescriptionProperty, DataObject.find(info.getFileObject())); @@ -232,44 +232,50 @@ JTextArea ta = new JTextArea(doc); Caret caret = ta.getCaret(); caret.setDot(insertOffset); - int newOffset = bc.beforeBreak(doc, insertOffset, ta); - doc.atomicLock(); - DocumentUtilities.setTypingModification(doc, true); + Reformat f = Reformat.get(doc); + assertNotNull(f); + + f.lock(); try { - doc.insertString(caret.getDot(), "\n", null); - // Indent the new line - PHPFormatter formatter = new PHPFormatter(); - //ParserResult result = parse(fo); + doc.atomicLock(); + try { + DocumentUtilities.setTypingModification(doc, true); + try { + PHPBracketCompleter bc = new PHPBracketCompleter(); + int newOffset = bc.beforeBreak(doc, insertOffset, ta); + doc.insertString(caret.getDot(), "\n", null); + // Indent the new line + PHPFormatter formatter = new PHPFormatter(); + //ParserResult result = parse(fo); - int startPos = caret.getDot()+1; - int endPos = startPos+1; + int startPos = caret.getDot()+1; + int endPos = startPos+1; - //ParserResult result = parse(fo); - final org.netbeans.editor.Formatter f = doc.getFormatter(); - try { - f.reformatLock(); - f.reformat(doc, startPos, endPos); + //ParserResult result = parse(fo); + f.reformat(startPos, endPos); + + int indent = GsfUtilities.getLineIndent(doc, insertOffset+1); + + //bc.afterBreak(doc, insertOffset, caret); + String formatted = doc.getText(0, doc.getLength()); + assertEquals(expected, formatted); + if (newOffset != -1) { + caret.setDot(newOffset); + } else { + caret.setDot(insertOffset+1+indent); + } + if (finalCaretPos != -1) { + assertEquals(finalCaretPos, caret.getDot()); + } + } finally { + DocumentUtilities.setTypingModification(doc, false); + } } finally { - f.reformatUnlock(); - } - - int indent = GsfUtilities.getLineIndent(doc, insertOffset+1); - - //bc.afterBreak(doc, insertOffset, caret); - String formatted = doc.getText(0, doc.getLength()); - assertEquals(expected, formatted); - if (newOffset != -1) { - caret.setDot(newOffset); - } else { - caret.setDot(insertOffset+1+indent); - } - if (finalCaretPos != -1) { - assertEquals(finalCaretPos, caret.getDot()); + doc.atomicUnlock(); } } finally { - DocumentUtilities.setTypingModification(doc, false); - doc.atomicUnlock(); + f.unlock(); } } } --- a/php.project/nbproject/project.xml +++ a/php.project/nbproject/project.xml @@ -100,7 +100,7 @@ 3 - 1.49 + 1.53 @@ -126,8 +126,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/php.refactoring/nbproject/project.xml +++ a/php.refactoring/nbproject/project.xml @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/php.symfony/nbproject/project.xml +++ a/php.symfony/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.43 + 1.53 --- a/php.zend/nbproject/project.xml +++ a/php.zend/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.47 + 1.53 --- a/print.editor/nbproject/project.xml +++ a/print.editor/nbproject/project.xml @@ -61,8 +61,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/profiler.oql.language/nbproject/project.xml +++ a/profiler.oql.language/nbproject/project.xml @@ -29,7 +29,7 @@ 3 - 1.42 + 1.53 @@ -46,8 +46,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/profiler.projectsupport/nbproject/project.xml +++ a/profiler.projectsupport/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/profiler.utilities/nbproject/project.xml +++ a/profiler.utilities/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/profiler/nbproject/project.xml +++ a/profiler/nbproject/project.xml @@ -126,7 +126,7 @@ 3 - 1.41 + 1.53 @@ -134,8 +134,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/properties.syntax/nbproject/project.xml +++ a/properties.syntax/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/python.debugger/nbproject/project.xml +++ a/python.debugger/nbproject/project.xml @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/python.editor/nbproject/project.xml +++ a/python.editor/nbproject/project.xml @@ -55,8 +55,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/python.editor/src/org/netbeans/modules/python/editor/PythonFormatter.java +++ a/python.editor/src/org/netbeans/modules/python/editor/PythonFormatter.java @@ -348,7 +348,6 @@ doc.runAtomic(new Runnable() { public void run() { - org.netbeans.editor.Formatter editorFormatter = null; int[] computedIndents = new int[offsets.size()]; // Process backwards so I don't have to worry about updating offsets affected by // indentation changes @@ -409,14 +408,7 @@ if (computedIndent != indent) { try { - if (context != null) { - context.modifyIndent(offset, computedIndent); - } else { - if (editorFormatter == null) { - editorFormatter = doc.getFormatter(); - } - editorFormatter.changeRowIndent(doc, offset, computedIndent); - } + context.modifyIndent(offset, computedIndent); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } --- a/python.testrunner/nbproject/project.xml +++ a/python.testrunner/nbproject/project.xml @@ -26,8 +26,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/refactoring.api/nbproject/project.xml +++ a/refactoring.api/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.extrahints/nbproject/project.xml +++ a/ruby.extrahints/nbproject/project.xml @@ -46,8 +46,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.hints/nbproject/project.xml +++ a/ruby.hints/nbproject/project.xml @@ -64,8 +64,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.project/nbproject/project.xml +++ a/ruby.project/nbproject/project.xml @@ -73,8 +73,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.railsprojects/nbproject/project.xml +++ a/ruby.railsprojects/nbproject/project.xml @@ -81,8 +81,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.refactoring/nbproject/project.xml +++ a/ruby.refactoring/nbproject/project.xml @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.rhtml/nbproject/project.xml +++ a/ruby.rhtml/nbproject/project.xml @@ -29,7 +29,7 @@ 3 - 1.41 + 1.53 @@ -73,8 +73,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby.rspec/nbproject/project.xml +++ a/ruby.rspec/nbproject/project.xml @@ -29,7 +29,7 @@ 3 - 1.41 + 1.53 @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby/nbproject/project.xml +++ a/ruby/nbproject/project.xml @@ -65,7 +65,7 @@ 3 - 1.41 + 1.53 @@ -91,8 +91,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/ruby/src/org/netbeans/modules/ruby/RubyFormatter.java +++ a/ruby/src/org/netbeans/modules/ruby/RubyFormatter.java @@ -562,7 +562,6 @@ try { // Iterate in reverse order such that offsets are not affected by our edits assert indents.size() == offsets.size(); - org.netbeans.editor.Formatter editorFormatter = null; for (int i = indents.size() - 1; i >= 0; i--) { int indent = indents.get(i); int lineBegin = offsets.get(i); @@ -598,15 +597,8 @@ int currentIndent = GsfUtilities.getLineIndent(doc, lineBegin); if (currentIndent != indent && indent >= 0) { - if (context != null) { - assert lineBegin == Utilities.getRowStart(doc, lineBegin); - context.modifyIndent(lineBegin, indent); - } else { - if (editorFormatter == null) { - editorFormatter = doc.getFormatter(); - } - editorFormatter.changeRowIndent(doc, lineBegin, indent); - } + assert lineBegin == Utilities.getRowStart(doc, lineBegin); + context.modifyIndent(lineBegin, indent); } } --- a/ruby/src/org/netbeans/modules/ruby/options/FmtOptions.java +++ a/ruby/src/org/netbeans/modules/ruby/options/FmtOptions.java @@ -70,12 +70,15 @@ import javax.swing.JViewport; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.api.ruby.platform.RubyInstallation; +import org.netbeans.editor.BaseDocument; +import org.netbeans.modules.editor.indent.api.Reformat; import org.netbeans.modules.options.editor.spi.PreferencesCustomizer; import org.netbeans.modules.options.editor.spi.PreviewProvider; -import org.netbeans.modules.ruby.RubyFormatter; import org.openide.text.CloneableEditorSupport; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; @@ -348,11 +351,25 @@ jep.setIgnoreRepaint(true); jep.setText(previewText); - - CodeStyle codeStyle = CodeStyle.get(previewPrefs); - RubyFormatter formatter = new RubyFormatter(codeStyle, rm); - formatter.reindent(null, jep.getDocument(), 0, jep.getDocument().getLength(), null, false); - + + final Document doc = jep.getDocument(); + final Reformat formatter = Reformat.get(doc); + formatter.lock(); + try { + ((BaseDocument) doc).runAtomic(new Runnable() { + public void run() { + try { + // The formatter is automatically going to use the previewPrefs. + formatter.reformat(0, doc.getLength()); + } catch (BadLocationException ble) { + // ignore + } + } + }); + } finally { + formatter.unlock(); + } + jep.setIgnoreRepaint(false); jep.scrollRectToVisible(new Rectangle(0,0,10,10) ); jep.repaint(100); --- a/soa.jca.base/nbproject/project.xml +++ a/soa.jca.base/nbproject/project.xml @@ -81,8 +81,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/soa.palette.java/nbproject/project.xml +++ a/soa.palette.java/nbproject/project.xml @@ -81,8 +81,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/soa.pojo/nbproject/project.xml +++ a/soa.pojo/nbproject/project.xml @@ -123,8 +123,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/soa.ui/nbproject/project.xml +++ a/soa.ui/nbproject/project.xml @@ -239,8 +239,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spellchecker.bindings.htmlxml/nbproject/project.xml +++ a/spellchecker.bindings.htmlxml/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -19,8 +19,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spellchecker.bindings.java/nbproject/project.xml +++ a/spellchecker.bindings.java/nbproject/project.xml @@ -19,7 +19,7 @@ 3 - 1.41 + 1.53 @@ -27,8 +27,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spellchecker.bindings.properties/nbproject/project.xml +++ a/spellchecker.bindings.properties/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -19,8 +19,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spellchecker.bindings.ruby/nbproject/project.xml +++ a/spellchecker.bindings.ruby/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -19,8 +19,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spellchecker/nbproject/project.xml +++ a/spellchecker/nbproject/project.xml @@ -11,7 +11,7 @@ 3 - 1.41 + 1.53 @@ -28,8 +28,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spi.debugger.ui/nbproject/project.xml +++ a/spi.debugger.ui/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.7 + 3 + 3.1 --- a/spi.editor.hints/nbproject/project.xml +++ a/spi.editor.hints/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spring.beans/nbproject/project.xml +++ a/spring.beans/nbproject/project.xml @@ -105,7 +105,7 @@ 3 - 1.41 + 1.53 @@ -131,8 +131,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/spring.webmvc/nbproject/project.xml +++ a/spring.webmvc/nbproject/project.xml @@ -98,7 +98,7 @@ 3 - 1.48 + 1.53 --- a/subversion/nbproject/project.xml +++ a/subversion/nbproject/project.xml @@ -80,8 +80,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/versioning.system.cvss/nbproject/project.xml +++ a/versioning.system.cvss/nbproject/project.xml @@ -99,7 +99,7 @@ 3 - 1.41 + 1.53 @@ -124,8 +124,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/versioning.util/nbproject/project.xml +++ a/versioning.util/nbproject/project.xml @@ -47,7 +47,7 @@ 3 - 1.44 + 1.53 @@ -72,8 +72,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/versioning/nbproject/project.xml +++ a/versioning/nbproject/project.xml @@ -89,8 +89,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/visualweb.designer.jsf/nbproject/project.xml +++ a/visualweb.designer.jsf/nbproject/project.xml @@ -90,8 +90,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/visualweb.gravy/nbproject/project.xml +++ a/visualweb.gravy/nbproject/project.xml @@ -89,8 +89,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/visualweb.insync/nbproject/project.xml +++ a/visualweb.insync/nbproject/project.xml @@ -107,7 +107,7 @@ 3 - 1.41 + 1.53 @@ -115,8 +115,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/visualweb.palette/nbproject/project.xml +++ a/visualweb.palette/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/visualweb.project.jsfloader/nbproject/project.xml +++ a/visualweb.project.jsfloader/nbproject/project.xml @@ -63,8 +63,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/vmd.midp/nbproject/project.xml +++ a/vmd.midp/nbproject/project.xml @@ -114,8 +114,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/wag.codegen.java/nbproject/project.xml +++ a/wag.codegen.java/nbproject/project.xml @@ -46,7 +46,7 @@ 3 - 1.41 + 1.53 --- a/wag.codegen.php/nbproject/project.xml +++ a/wag.codegen.php/nbproject/project.xml @@ -12,7 +12,7 @@ 3 - 1.41 + 1.53 --- a/wag.codegen/nbproject/project.xml +++ a/wag.codegen/nbproject/project.xml @@ -38,7 +38,7 @@ 3 - 1.41 + 1.53 @@ -55,8 +55,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/wag.manager/nbproject/project.xml +++ a/wag.manager/nbproject/project.xml @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.beans/nbproject/project.xml +++ a/web.beans/nbproject/project.xml @@ -45,7 +45,7 @@ 3 - 1.43 + 1.53 @@ -53,8 +53,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.client.tools.impl/nbproject/project.xml +++ a/web.client.tools.impl/nbproject/project.xml @@ -37,8 +37,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.core.syntax/nbproject/project.xml +++ a/web.core.syntax/nbproject/project.xml @@ -116,7 +116,7 @@ 3 - 1.41 + 1.53 @@ -167,8 +167,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -449,6 +449,15 @@ 6.2 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + --- a/web.core/nbproject/project.xml +++ a/web.core/nbproject/project.xml @@ -113,7 +113,7 @@ 3 - 1.41 + 1.53 @@ -130,8 +130,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.el/nbproject/project.xml +++ a/web.el/nbproject/project.xml @@ -63,7 +63,7 @@ 3 - 1.43 + 1.53 @@ -89,8 +89,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.jsf.editor/nbproject/project.xml +++ a/web.jsf.editor/nbproject/project.xml @@ -112,7 +112,7 @@ 3 - 1.43 + 1.53 @@ -138,8 +138,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.jsf.navigation/nbproject/project.xml +++ a/web.jsf.navigation/nbproject/project.xml @@ -45,7 +45,7 @@ 3 - 1.41 + 1.53 @@ -53,8 +53,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.jsf/nbproject/project.xml +++ a/web.jsf/nbproject/project.xml @@ -124,7 +124,7 @@ 3 - 1.41 + 1.53 @@ -133,7 +133,7 @@ 2 - 1.5 + 1.10 @@ -141,8 +141,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFEditorUtilities.java +++ a/web.jsf/src/org/netbeans/modules/web/jsf/api/editor/JSFEditorUtilities.java @@ -49,9 +49,12 @@ import java.io.StringWriter; import javax.swing.JEditorPane; import javax.swing.text.BadLocationException; +import javax.swing.text.Position; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.TokenItem; import org.netbeans.editor.ext.ExtSyntaxSupport; +import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.modules.editor.indent.api.Reformat; import org.netbeans.modules.schema2beans.BaseBean; import org.openide.cookies.EditorCookie; import org.openide.loaders.DataObject; @@ -338,17 +341,28 @@ private static int writeString(BaseDocument doc, String text, int offset){ int formatLength = 0; - try{ + Indent indenter = Indent.get(doc); + Reformat formatter = Reformat.get(doc); + indenter.lock(); + formatter.lock(); + try { doc.atomicLock(); - offset = doc.getFormatter().indentNewLine(doc, offset+1); - doc.insertString(offset, text, null ); - formatLength = doc.getFormatter().reformat(doc, offset, offset + text.length()-1); - } - catch(BadLocationException ex){ - Exceptions.printStackTrace(ex); - } - finally { - doc.atomicUnlock(); + try{ + offset = indenter.indentNewLine(offset + 1); + doc.insertString(offset, text, null ); + Position endPos = doc.createPosition(offset + text.length() - 1); + formatter.reformat(offset, endPos.getOffset()); + formatLength = Math.max(0, endPos.getOffset() - offset); + } + catch(BadLocationException ex){ + Exceptions.printStackTrace(ex); + } + finally { + doc.atomicUnlock(); + } + } finally { + formatter.unlock(); + indenter.unlock(); } return offset + formatLength + 1; } --- a/web.refactoring/nbproject/project.xml +++ a/web.refactoring/nbproject/project.xml @@ -44,8 +44,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/web.struts/nbproject/project.xml +++ a/web.struts/nbproject/project.xml @@ -104,7 +104,7 @@ 3 - 1.41 + 1.53 @@ -116,12 +116,21 @@ + org.netbeans.modules.editor.indent + + + + 2 + 1.10 + + + org.netbeans.modules.editor.lib - 2 - 2.1 + 3 + 3.1 --- a/web.struts/src/org/netbeans/modules/web/struts/editor/StrutsEditorUtilities.java +++ a/web.struts/src/org/netbeans/modules/web/struts/editor/StrutsEditorUtilities.java @@ -48,10 +48,12 @@ import java.io.IOException; import java.io.StringWriter; import javax.swing.text.BadLocationException; +import javax.swing.text.Position; import org.netbeans.editor.BaseDocument; -import org.netbeans.editor.Formatter; import org.netbeans.editor.TokenItem; import org.netbeans.editor.ext.ExtSyntaxSupport; +import org.netbeans.modules.editor.indent.api.Indent; +import org.netbeans.modules.editor.indent.api.Reformat; import org.netbeans.modules.schema2beans.BaseBean; import org.netbeans.modules.web.struts.config.model.FormProperty; import org.netbeans.modules.web.struts.config.model.Forward; @@ -219,21 +221,23 @@ text.append(addNewLines(bean)); text.append(END_LINE); text.append(""); //NOI18N - Formatter fmt = doc.getFormatter(); - fmt.reformatLock(); + Reformat fmt = Reformat.get(doc); + fmt.lock(); try { + doc.atomicLock(); try{ - doc.atomicLock(); doc.remove(offset, 2); doc.insertString(offset, text.toString(), null); - offset += doc.getFormatter().reformat(doc, offset, offset + text.length()-1); + Position endPos = doc.createPosition(offset + text.length() - 1); + fmt.reformat(offset, endPos.getOffset()); + offset += Math.max(0, endPos.getOffset() - offset); possition = offset; } finally{ doc.atomicUnlock(); } } finally { - fmt.reformatUnlock(); + fmt.unlock(); } } if (token != null && token.getImage().equals(">")){ //NOI18N @@ -371,26 +375,29 @@ } private static int writeString(BaseDocument doc, String text, int offset) throws BadLocationException { - int formatLength = 0; - Formatter fmt = doc.getFormatter(); - fmt.indentLock(); + int formatLength = 0; + Indent indent = Indent.get(doc); + Reformat fmt = Reformat.get(doc); + indent.lock(); try { - fmt.reformatLock(); + fmt.lock(); try { + doc.atomicLock(); try{ - doc.atomicLock(); - offset = doc.getFormatter().indentNewLine(doc, offset+1); + offset = indent.indentNewLine(offset + 1); doc.insertString(Math.min(offset, doc.getLength()), text, null ); - formatLength = doc.getFormatter().reformat(doc, offset, offset + text.length()-1); + Position endPos = doc.createPosition(offset + text.length() - 1); + fmt.reformat(offset, endPos.getOffset()); + formatLength = Math.max(0, endPos.getOffset() - offset); } finally{ doc.atomicUnlock(); } } finally { - fmt.reformatUnlock(); + fmt.unlock(); } } finally { - fmt.indentUnlock(); + indent.unlock(); } return Math.min(offset + formatLength + 1, doc.getLength()); } --- a/webpreview/nbproject/project.xml +++ a/webpreview/nbproject/project.xml @@ -20,7 +20,7 @@ 3 - 1.43 + 1.53 --- a/websvc.core/nbproject/project.xml +++ a/websvc.core/nbproject/project.xml @@ -124,7 +124,7 @@ 3 - 1.41 + 1.53 @@ -141,8 +141,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/websvc.manager/nbproject/project.xml +++ a/websvc.manager/nbproject/project.xml @@ -99,7 +99,7 @@ 3 - 1.41 + 1.53 --- a/websvc.rest/nbproject/project.xml +++ a/websvc.rest/nbproject/project.xml @@ -115,7 +115,7 @@ 3 - 1.41 + 1.53 --- a/websvc.saas.codegen.j2ee/nbproject/project.xml +++ a/websvc.saas.codegen.j2ee/nbproject/project.xml @@ -36,7 +36,7 @@ 3 - 1.41 + 1.53 --- a/websvc.saas.codegen.java/nbproject/project.xml +++ a/websvc.saas.codegen.java/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 1.41 + 1.53 --- a/websvc.saas.codegen/nbproject/project.xml +++ a/websvc.saas.codegen/nbproject/project.xml @@ -47,7 +47,7 @@ 3 - 1.41 + 1.53 @@ -64,8 +64,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xml.retriever/nbproject/project.xml +++ a/xml.retriever/nbproject/project.xml @@ -80,8 +80,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xml.schema.completion/nbproject/project.xml +++ a/xml.schema.completion/nbproject/project.xml @@ -72,8 +72,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xml.schema.ui.basic/nbproject/project.xml +++ a/xml.schema.ui.basic/nbproject/project.xml @@ -58,6 +58,60 @@ + org.netbeans.api.progress + + + + 1 + 1.5.1 + + + + org.netbeans.core.multiview + + + + 1 + 1.6.1 + + + + org.netbeans.modules.editor.lib + + + + 3 + 3.1 + + + + org.netbeans.modules.projectapi + + + + 1 + 1.7.1 + + + + org.netbeans.modules.projectuiapi + + + + 1 + 1.11.1.5 + + + + org.netbeans.modules.queries + + + + 1 + 1.5.1 + + + org.netbeans.modules.refactoring.api @@ -230,7 +284,16 @@ 6.2.1 - + + org.openidex.util + + + + 3 + 3.7.1 + + + org.netbeans.modules.bpel.editors org.netbeans.modules.bpel.editors.api --- a/xml.schema/nbproject/project.xml +++ a/xml.schema/nbproject/project.xml @@ -89,8 +89,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xml.text/nbproject/project.xml +++ a/xml.text/nbproject/project.xml @@ -82,7 +82,7 @@ 3 - 1.41 + 1.53 @@ -126,8 +126,8 @@ - 2 - 2.1 + 3 + 3.1 @@ -276,6 +276,15 @@ 6.2 + + org.netbeans.modules.editor.deprecated.pre65formatting + + + + 0-1 + 1.0 + + --- a/xml.xdm/nbproject/project.xml +++ a/xml.xdm/nbproject/project.xml @@ -54,8 +54,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xml/nbproject/project.xml +++ a/xml/nbproject/project.xml @@ -72,8 +72,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xslt.core/nbproject/project.xml +++ a/xslt.core/nbproject/project.xml @@ -80,8 +80,8 @@ - 2 - 2.1 + 3 + 3.1 --- a/xslt.tmap/nbproject/project.xml +++ a/xslt.tmap/nbproject/project.xml @@ -64,8 +64,8 @@ - 2 - 2.1 + 3 + 3.1