This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 217163
Collapse All | Expand All

(-)a/csl.api/apichanges.xml (+15 lines)
Lines 49-54 Link Here
49
<apidef name="csl.api">Common Scripting Language API</apidef>
49
<apidef name="csl.api">Common Scripting Language API</apidef>
50
</apidefs>
50
</apidefs>
51
<changes>
51
<changes>
52
    <change id="DeleteWordHandler">
53
        <api name="csl.api"/>
54
        <summary>Add DeleteWordHandler</summary>
55
        <version major="2" minor="34"/>
56
        <date day="13" month="11" year="2012"/>
57
        <author login="mkristofic"/>
58
        <compatibility addition="yes" deletion="no" modification="no" binary="compatible" source="compatible" deprecation="yes"/>
59
        <description>
60
            <p>
61
                Add DeleteWordHandler to replace KeystrokeHandler in future. KeystrokeHandler is deprecated since clients can use TypingHooks from editor.lib2
62
            </p>
63
        </description>
64
        <class package="org.netbeans.modules.csl.api" name="DeleteWordHandler"/>
65
        <issue number="217163"/>
66
    </change>
52
    <change id="LanguageRegistration.useMultiview">
67
    <change id="LanguageRegistration.useMultiview">
53
        <api name="csl.api"/>
68
        <api name="csl.api"/>
54
        <summary>Add useMultiview to @LanguageRegistration</summary>
69
        <summary>Add useMultiview to @LanguageRegistration</summary>
(-)a/csl.api/nbproject/project.properties (-1 / +1 lines)
Lines 40-46 Link Here
40
# Version 2 license, then the option applies only if the new code is
40
# Version 2 license, then the option applies only if the new code is
41
# made subject to such option by the copyright holder.
41
# made subject to such option by the copyright holder.
42
42
43
spec.version.base=2.33.0
43
spec.version.base=2.34.0
44
is.autoload=true
44
is.autoload=true
45
javac.source=1.6
45
javac.source=1.6
46
46
(-)a/csl.api/src/org/netbeans/modules/csl/api/CamelCaseOperations.java (-1 / +23 lines)
Lines 44-54 Link Here
44
44
45
package org.netbeans.modules.csl.api;
45
package org.netbeans.modules.csl.api;
46
46
47
import java.util.Collection;
47
import javax.swing.text.BadLocationException;
48
import javax.swing.text.BadLocationException;
48
import javax.swing.text.Document;
49
import javax.swing.text.Document;
49
import javax.swing.text.JTextComponent;
50
import javax.swing.text.JTextComponent;
51
import org.netbeans.api.editor.mimelookup.MimeLookup;
50
import org.netbeans.editor.BaseDocument;
52
import org.netbeans.editor.BaseDocument;
51
import org.netbeans.editor.Utilities;
53
import org.netbeans.editor.Utilities;
54
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
52
import org.openide.ErrorManager;
55
import org.openide.ErrorManager;
53
56
54
/**
57
/**
Lines 68-74 Link Here
68
        if (offset == doc.getLength()) {
71
        if (offset == doc.getLength()) {
69
            return -1;
72
            return -1;
70
        }
73
        }
71
74
        Collection<? extends DeleteWordHandler> deleteWordHandlers = MimeLookup.getLookup(UiUtils.getMimePath(doc, offset)).lookupAll(DeleteWordHandler.class);
75
        if (!deleteWordHandlers.isEmpty()) {
76
            for (DeleteWordHandler dw :deleteWordHandlers) {
77
                int nextOffset = dw.getNextWordOffset(doc, offset);
78
                if (nextOffset != -1) {
79
                    return nextOffset;
80
                }
81
            }
82
        }
83
         
72
        KeystrokeHandler bc = UiUtils.getBracketCompletion(doc, offset);
84
        KeystrokeHandler bc = UiUtils.getBracketCompletion(doc, offset);
73
        if (bc != null) {
85
        if (bc != null) {
74
            int nextOffset = bc.getNextWordOffset(doc, offset, false);
86
            int nextOffset = bc.getNextWordOffset(doc, offset, false);
Lines 96-101 Link Here
96
108
97
        final Document doc = textComponent.getDocument();
109
        final Document doc = textComponent.getDocument();
98
        
110
        
111
        Collection<? extends DeleteWordHandler> deleteWordHandlers = MimeLookup.getLookup(UiUtils.getMimePath(doc, offset)).lookupAll(DeleteWordHandler.class);
112
        if (!deleteWordHandlers.isEmpty()) {
113
            for (DeleteWordHandler dw :deleteWordHandlers) {
114
                int nextOffset = dw.getPreviousWordOffset(doc, offset);
115
                if (nextOffset != -1) {
116
                    return nextOffset;
117
                }
118
            }
119
        }
120
        
99
        KeystrokeHandler bc = UiUtils.getBracketCompletion(doc, offset);
121
        KeystrokeHandler bc = UiUtils.getBracketCompletion(doc, offset);
100
        if (bc != null) {
122
        if (bc != null) {
101
            int nextOffset = bc.getNextWordOffset(
123
            int nextOffset = bc.getNextWordOffset(
(-)a/csl.api/src/org/netbeans/modules/csl/api/KeystrokeHandler.java (-66 / +16 lines)
Lines 49-124 Link Here
49
import javax.swing.text.JTextComponent;
49
import javax.swing.text.JTextComponent;
50
import org.netbeans.api.annotations.common.CheckForNull;
50
import org.netbeans.api.annotations.common.CheckForNull;
51
import org.netbeans.api.annotations.common.NonNull;
51
import org.netbeans.api.annotations.common.NonNull;
52
import org.netbeans.api.editor.mimelookup.MimePath;
53
import org.netbeans.modules.csl.core.CslDeletedTextInterceptor;
52
import org.netbeans.modules.csl.spi.ParserResult;
54
import org.netbeans.modules.csl.spi.ParserResult;
55
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
53
56
54
57
55
/**
58
/**
56
 * Interface that a plugin can implement to assist with bracket completion during
59
 * Interface that a plugin can implement to assist with Delete word.
57
 * editing.
58
 *
59
 * @author Tor Norbye
60
 */
60
 */
61
public interface KeystrokeHandler {
61
public interface DeleteWordHandler {
62
    /**
62
    /**
63
     * (Based on BracketCompletion class in NetBeans' java editor support)
63
     * Compute the next word position, if any. Can be used to implement
64
     *
64
     * camel case motion etc.
65
     * A hook method called after a character was inserted into the
65
     * 
66
     * document. The function checks for special characters for
66
     * @param doc The document to move in
67
     * completion ()[]'"{} and other conditions and optionally performs
67
     * @param caretOffset The caret position corresponding to the current word
68
     * changes to the doc and or caret (complets braces, moves caret,
68
     * @return The next word boundary offset in the given direction, or -1 if this
69
     * etc.)
69
     *   implementation doesn't want to compute word boundaries (the default will be used)
70
     *
71
     * Return true if the character was already inserted (and the IDE
72
     * should not further insert anything)
73
     *
74
     * XXX Fix javadoc.
75
     */
70
     */
76
    boolean beforeCharInserted(@NonNull Document doc, int caretOffset, @NonNull JTextComponent target, char ch)
71
    @CheckForNull
77
        throws BadLocationException;
72
    int getNextWordOffset(Document doc, int caretOffset);
78
79
    /** @todo Rip out the boolean return value? What does it mean? */
80
    boolean afterCharInserted(@NonNull Document doc, int caretOffset, @NonNull JTextComponent target, char ch)
81
        throws BadLocationException;
82
83
    /**
84
     * (Based on KeystrokeHandler class in NetBeans' java editor support)
85
     *
86
     * Hook called after a character *ch* was backspace-deleted from
87
     * *doc*. The function possibly removes bracket or quote pair if
88
     * appropriate.
89
     * @todo Document why both caretOffset and caret is passed in!
90
     * Return the new offset, or -1
91
     */
92
93
    /** @todo Split into before and after? */
94
    public boolean charBackspaced(@NonNull Document doc, int caretOffset, @NonNull JTextComponent target, char ch)
95
        throws BadLocationException;
96
97
    /**
98
     * A line break is being called. Return -1 to do nothing.
99
     * If you want to modify the document first, you can do that, and then
100
     * return the new offset to assign the caret to AFTER the newline has been
101
     * inserted.
102
     *
103
     * @todo rip out return value
104
     * @todo Document why both caretOffset and caret is passed in!
105
     */
106
    int beforeBreak(@NonNull Document doc, int caretOffset, @NonNull JTextComponent target)
107
        throws BadLocationException;
108
109
    /**
110
     * Compute a range matching the caret position. If no eligible range
111
     * is found, return {@link OffsetRange#NONE}.
112
     */
113
    @NonNull
114
    OffsetRange findMatching(@NonNull Document doc, int caretOffset);
115
    
116
    /**
117
     * Compute set of selection ranges for the given parse tree (around the given offset),
118
     * in leaf-to-root order.
119
     */
120
    @NonNull
121
    List<OffsetRange> findLogicalRanges(@NonNull ParserResult info, int caretOffset);
122
    
73
    
123
    /**
74
    /**
124
     * Compute the previous word position, if any. Can be used to implement
75
     * Compute the previous word position, if any. Can be used to implement
Lines 126-135 Link Here
126
     * 
77
     * 
127
     * @param doc The document to move in
78
     * @param doc The document to move in
128
     * @param caretOffset The caret position corresponding to the current word
79
     * @param caretOffset The caret position corresponding to the current word
129
     * @param reverse If true, move forwards, otherwise move backwards (e.g. "previous" word)
80
     * @return The previous word boundary offset in the given direction, or -1 if this
130
     * @return The next word boundary offset in the given direction, or -1 if this
131
     *   implementation doesn't want to compute word boundaries (the default will be used)
81
     *   implementation doesn't want to compute word boundaries (the default will be used)
132
     */
82
     */
133
    @CheckForNull
83
    @CheckForNull
134
    int getNextWordOffset(Document doc, int caretOffset, boolean reverse);
84
    int getPreviousWordOffset(Document doc, int caretOffset);
135
}
85
}
(-)a/csl.api/src/org/netbeans/modules/csl/api/KeystrokeHandler.java (-1 / +4 lines)
Lines 49-61 Link Here
49
import javax.swing.text.JTextComponent;
49
import javax.swing.text.JTextComponent;
50
import org.netbeans.api.annotations.common.CheckForNull;
50
import org.netbeans.api.annotations.common.CheckForNull;
51
import org.netbeans.api.annotations.common.NonNull;
51
import org.netbeans.api.annotations.common.NonNull;
52
import org.netbeans.api.editor.mimelookup.MimePath;
53
import org.netbeans.modules.csl.core.CslDeletedTextInterceptor;
52
import org.netbeans.modules.csl.spi.ParserResult;
54
import org.netbeans.modules.csl.spi.ParserResult;
55
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
53
56
54
57
55
/**
58
/**
56
 * Interface that a plugin can implement to assist with bracket completion during
59
 * Interface that a plugin can implement to assist with bracket completion during
57
 * editing.
60
 * editing.
58
 *
61
 * @deprecated Use Typing Hooks instead, for details see <a href="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a> and csL DeleteWordHandler.
59
 * @author Tor Norbye
62
 * @author Tor Norbye
60
 */
63
 */
61
public interface KeystrokeHandler {
64
public interface KeystrokeHandler {
(-)a/csl.api/src/org/netbeans/modules/csl/api/UiUtils.java (+16 lines)
Lines 59-67 Link Here
59
import javax.swing.text.BadLocationException;
59
import javax.swing.text.BadLocationException;
60
import javax.swing.text.Document;
60
import javax.swing.text.Document;
61
import javax.swing.text.JTextComponent;
61
import javax.swing.text.JTextComponent;
62
import org.netbeans.api.editor.mimelookup.MimePath;
62
import org.netbeans.api.lexer.TokenHierarchy;
63
import org.netbeans.api.lexer.TokenHierarchy;
63
import org.netbeans.api.lexer.TokenSequence;
64
import org.netbeans.api.lexer.TokenSequence;
64
import org.netbeans.api.progress.ProgressUtils;
65
import org.netbeans.api.progress.ProgressUtils;
66
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
65
import org.netbeans.modules.csl.api.DeclarationFinder.DeclarationLocation;
67
import org.netbeans.modules.csl.api.DeclarationFinder.DeclarationLocation;
66
import org.netbeans.modules.csl.core.Language;
68
import org.netbeans.modules.csl.core.Language;
67
import org.netbeans.modules.csl.core.LanguageRegistry;
69
import org.netbeans.modules.csl.core.LanguageRegistry;
Lines 346-349 Link Here
346
            }
348
            }
347
            return result[0] != null ? result[0] : DeclarationLocation.NONE;
349
            return result[0] != null ? result[0] : DeclarationLocation.NONE;
348
        }
350
        }
351
    
352
    static MimePath getMimePath(final Document doc, final int offset) {
353
        final MimePath[] mimePathR = new MimePath[1];
354
        doc.render(new Runnable() {
355
            @Override
356
            public void run() {
357
                List<TokenSequence<?>> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true);
358
                TokenSequence<?> seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1);
359
                seq = seq == null ? TokenHierarchy.get(doc).tokenSequence() : seq;
360
                mimePathR[0] = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath());
361
            }
362
        });
363
        return mimePathR[0];
349
    }
364
    }
365
}
(-)a/csl.api/src/org/netbeans/modules/csl/core/CslDeletedTextInterceptor.java (+87 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.csl.core;
43
44
import javax.swing.text.BadLocationException;
45
import org.netbeans.api.editor.mimelookup.MimePath;
46
import org.netbeans.api.editor.mimelookup.MimeRegistration;
47
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
48
import org.netbeans.modules.csl.api.KeystrokeHandler;
49
import org.netbeans.modules.csl.api.UiUtils;
50
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
51
52
public class CslDeletedTextInterceptor implements DeletedTextInterceptor {
53
54
    @Override
55
    public boolean beforeRemove(Context context) throws BadLocationException {
56
        return false;
57
    }
58
59
    @Override
60
    public void remove(Context context) throws BadLocationException {
61
    }
62
63
    @Override
64
    public void afterRemove(Context context) throws BadLocationException {
65
        if (CslEditorKit.completionSettingEnabled()) {
66
            KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(context.getDocument(), context.getOffset());
67
            if (bracketCompletion != null) {
68
                bracketCompletion.charBackspaced(context.getDocument(), context.getComponent().getCaretPosition(), context.getComponent(), context.getText().charAt(0));
69
            }
70
        }
71
    }
72
73
    @Override
74
    public void cancelled(Context context) {
75
    }
76
77
    @MimeRegistrations(value = {
78
        @MimeRegistration(mimeType = "", service = DeletedTextInterceptor.Factory.class)})
79
    public static class Factory implements DeletedTextInterceptor.Factory {
80
81
        @Override
82
        public DeletedTextInterceptor createDeletedTextInterceptor(MimePath mimePath) {
83
            return new CslDeletedTextInterceptor();
84
        }
85
    }
86
    
87
}
(-)a/csl.api/src/org/netbeans/modules/csl/core/CslEditorKit.java (-172 / +9 lines)
Lines 51-63 Link Here
51
import javax.swing.JMenuItem;
51
import javax.swing.JMenuItem;
52
import javax.swing.KeyStroke;
52
import javax.swing.KeyStroke;
53
import javax.swing.text.BadLocationException;
53
import javax.swing.text.BadLocationException;
54
import javax.swing.text.Caret;
55
import javax.swing.text.Document;
54
import javax.swing.text.Document;
56
import javax.swing.text.EditorKit;
55
import javax.swing.text.EditorKit;
57
import javax.swing.text.JTextComponent;
56
import javax.swing.text.JTextComponent;
58
import javax.swing.text.Keymap;
57
import javax.swing.text.Keymap;
59
import javax.swing.text.TextAction;
58
import javax.swing.text.TextAction;
60
import org.netbeans.api.editor.mimelookup.MimePath;
59
import org.netbeans.api.editor.mimelookup.MimePath;
60
import org.netbeans.api.editor.mimelookup.MimeRegistration;
61
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
61
import org.netbeans.editor.BaseAction;
62
import org.netbeans.editor.BaseAction;
62
import org.netbeans.editor.BaseDocument;
63
import org.netbeans.editor.BaseDocument;
63
import org.netbeans.editor.BaseKit;
64
import org.netbeans.editor.BaseKit;
Lines 69-75 Link Here
69
import org.netbeans.modules.csl.api.CslActions;
70
import org.netbeans.modules.csl.api.CslActions;
70
import org.netbeans.modules.csl.api.DeleteToNextCamelCasePosition;
71
import org.netbeans.modules.csl.api.DeleteToNextCamelCasePosition;
71
import org.netbeans.modules.csl.api.DeleteToPreviousCamelCasePosition;
72
import org.netbeans.modules.csl.api.DeleteToPreviousCamelCasePosition;
72
import org.netbeans.modules.csl.api.GoToDeclarationAction;
73
import org.netbeans.modules.csl.api.InstantRenameAction;
73
import org.netbeans.modules.csl.api.InstantRenameAction;
74
import org.netbeans.modules.csl.api.KeystrokeHandler;
74
import org.netbeans.modules.csl.api.KeystrokeHandler;
75
import org.netbeans.modules.csl.api.NextCamelCasePosition;
75
import org.netbeans.modules.csl.api.NextCamelCasePosition;
Lines 82-90 Link Here
82
import org.netbeans.modules.csl.api.UiUtils;
82
import org.netbeans.modules.csl.api.UiUtils;
83
import org.netbeans.modules.csl.api.GoToMarkOccurrencesAction;
83
import org.netbeans.modules.csl.api.GoToMarkOccurrencesAction;
84
import org.netbeans.modules.editor.NbEditorKit;
84
import org.netbeans.modules.editor.NbEditorKit;
85
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
86
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor.Context;
87
import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor;
88
import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor;
89
import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor.MutableContext;
85
import org.openide.awt.Mnemonics;
90
import org.openide.awt.Mnemonics;
86
import org.openide.filesystems.FileObject;
91
import org.openide.filesystems.FileObject;
87
import org.openide.util.Exceptions;
88
import org.openide.util.NbBundle;
92
import org.openide.util.NbBundle;
89
93
90
/**
94
/**
Lines 164-173 Link Here
164
        Action[] superActions = super.createActions();
168
        Action[] superActions = super.createActions();
165
        Language language = LanguageRegistry.getInstance().getLanguageByMimeType(mimeType);
169
        Language language = LanguageRegistry.getInstance().getLanguageByMimeType(mimeType);
166
        ArrayList<Action> actions = new ArrayList<Action>(30);
170
        ArrayList<Action> actions = new ArrayList<Action>(30);
167
168
        actions.add(new GsfDefaultKeyTypedAction());
169
        actions.add(new GsfInsertBreakAction());
170
        actions.add(new GsfDeleteCharAction(deletePrevCharAction, false));
171
        
171
        
172
        // The php needs to handle special cases of toggle comment. There has to be 
172
        // The php needs to handle special cases of toggle comment. There has to be 
173
        // registered ToggleBlockCommentAction in PHP that handles these special cases,
173
        // registered ToggleBlockCommentAction in PHP that handles these special cases,
Lines 226-397 Link Here
226
    /**
226
    /**
227
     * Returns true if bracket completion is enabled in options.
227
     * Returns true if bracket completion is enabled in options.
228
     */
228
     */
229
    private static boolean completionSettingEnabled() {
229
    static boolean completionSettingEnabled() {
230
        //return ((Boolean)Settings.getValue(GsfEditorKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue();
230
        //return ((Boolean)Settings.getValue(GsfEditorKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue();
231
        return true;
231
        return true;
232
    }
232
    }
233
233
    
234
    private final class GsfDefaultKeyTypedAction extends ExtDefaultKeyTypedAction {
235
        private JTextComponent currentTarget;
236
        private String replacedText = null;
237
238
        @Override
239
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
240
            currentTarget = target;
241
            super.actionPerformed(evt, target);
242
            currentTarget = null;
243
        }
244
245
        @Override
246
        protected void insertString(BaseDocument doc, int dotPos, Caret caret, String str,
247
            boolean overwrite) throws BadLocationException {
248
            if (completionSettingEnabled()) {
249
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
250
251
                if (bracketCompletion != null) {
252
                    // TODO - check if we're in a comment etc. and if so, do nothing
253
                    boolean handled =
254
                        bracketCompletion.beforeCharInserted(doc, dotPos, currentTarget,
255
                            str.charAt(0));
256
257
                    if (!handled) {
258
                        super.insertString(doc, dotPos, caret, str, overwrite);
259
                        handled = bracketCompletion.afterCharInserted(doc, dotPos, currentTarget,
260
                                str.charAt(0));
261
                    }
262
263
                    return;
264
                }
265
            }
266
267
            super.insertString(doc, dotPos, caret, str, overwrite);
268
        }
269
270
        @Override
271
        protected void replaceSelection(JTextComponent target, int dotPos, Caret caret,
272
            String str, boolean overwrite) throws BadLocationException {
273
            if (str.equals("")) {
274
                return;
275
            }
276
            char insertedChar = str.charAt(0);
277
            Document document = target.getDocument();
278
279
            if (document instanceof BaseDocument) {
280
                BaseDocument doc = (BaseDocument)document;
281
282
                if (completionSettingEnabled()) {
283
                    KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
284
285
                    if (bracketCompletion != null) {
286
                        try {
287
                            int caretPosition = caret.getDot();
288
289
                            boolean handled =
290
                                bracketCompletion.beforeCharInserted(doc, caretPosition,
291
                                    target, insertedChar);
292
293
                            int p0 = Math.min(caret.getDot(), caret.getMark());
294
                            int p1 = Math.max(caret.getDot(), caret.getMark());
295
296
                            if (p0 != p1) {
297
                                doc.remove(p0, p1 - p0);
298
                            }
299
300
                            if (!handled) {
301
                                if ((str != null) && (str.length() > 0)) {
302
                                    doc.insertString(p0, str, null);
303
                                }
304
305
                                bracketCompletion.afterCharInserted(doc, caret.getDot() - 1,
306
                                    target, insertedChar);
307
                            }
308
                        } catch (BadLocationException e) {
309
                            e.printStackTrace();
310
                        }
311
312
                        return;
313
                    }
314
                }
315
            }
316
317
            super.replaceSelection(target, dotPos, caret, str, overwrite);
318
        }
319
    }
320
321
    private final class GsfInsertBreakAction extends InsertBreakAction {
322
        static final long serialVersionUID = -1506173310438326380L;
323
324
        @Override
325
        protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) {
326
            if (completionSettingEnabled()) {
327
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, caret.getDot());
328
329
                if (bracketCompletion != null) {
330
                    try {
331
                        int newOffset = bracketCompletion.beforeBreak(doc, caret.getDot(), target);
332
333
                        if (newOffset >= 0) {
334
                            return new Integer(newOffset);
335
                        }
336
                    } catch (BadLocationException ble) {
337
                        Exceptions.printStackTrace(ble);
338
                    }
339
                }
340
            }
341
342
            // return Boolean.TRUE;
343
            return null;
344
        }
345
346
        @Override
347
        protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret,
348
            Object cookie) {
349
            if (completionSettingEnabled()) {
350
                if (cookie != null) {
351
                    if (cookie instanceof Integer) {
352
                        // integer
353
                        int dotPos = ((Integer)cookie).intValue();
354
                        if (dotPos != -1) {
355
                            caret.setDot(dotPos);
356
                        } else {
357
                            int nowDotPos = caret.getDot();
358
                            caret.setDot(nowDotPos + 1);
359
                        }
360
                    }
361
                }
362
            }
363
        }
364
    }
365
366
    private final class GsfDeleteCharAction extends ExtDeleteCharAction {
367
        private JTextComponent currentTarget;
368
369
        public GsfDeleteCharAction(String nm, boolean nextChar) {
370
            super(nm, nextChar);
371
        }
372
373
        @Override
374
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
375
            currentTarget = target;
376
            super.actionPerformed(evt, target);
377
            currentTarget = null;
378
        }
379
380
        @Override
381
        protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch)
382
            throws BadLocationException {
383
            if (completionSettingEnabled()) {
384
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
385
386
                if (bracketCompletion != null) {
387
                    boolean success = bracketCompletion.charBackspaced(doc, dotPos, currentTarget, ch);
388
                    return;
389
                }
390
            }
391
            super.charBackspaced(doc, dotPos, caret, ch);
392
        }
393
    }
394
395
    private final class GenericGenerateGoToPopupAction extends NbGenerateGoToPopupAction {
234
    private final class GenericGenerateGoToPopupAction extends NbGenerateGoToPopupAction {
396
        @Override
235
        @Override
397
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
236
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
Lines 501-506 Link Here
501
            return jm;
340
            return jm;
502
        }
341
        }
503
    }
342
    }
504
505
506
}
343
}
(-)a/csl.api/src/org/netbeans/modules/csl/core/CslTypedBreakInterceptor.java (+97 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.csl.core;
43
44
import javax.swing.text.BadLocationException;
45
import org.netbeans.api.editor.mimelookup.MimePath;
46
import org.netbeans.api.editor.mimelookup.MimeRegistration;
47
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
48
import org.netbeans.modules.csl.api.KeystrokeHandler;
49
import org.netbeans.modules.csl.api.UiUtils;
50
import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor;
51
52
public class CslTypedBreakInterceptor implements TypedBreakInterceptor {
53
54
    private int caretPosition = -1;
55
56
    @Override
57
    public boolean beforeInsert(Context context) throws BadLocationException {
58
        return false;
59
    }
60
61
    @Override
62
    public void insert(MutableContext context) throws BadLocationException {
63
        if (CslEditorKit.completionSettingEnabled()) {
64
            KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(context.getDocument(), context.getCaretOffset());
65
            if (bracketCompletion != null) {
66
                int newOffset = bracketCompletion.beforeBreak(context.getDocument(), context.getCaretOffset(), context.getComponent());
67
                if (newOffset >= 0) {
68
                    caretPosition = newOffset;
69
                }
70
            }
71
        }
72
    }
73
74
    @Override
75
    public void afterInsert(Context context) throws BadLocationException {
76
        if (CslEditorKit.completionSettingEnabled()) {
77
            if (caretPosition != -1) {
78
                context.getComponent().getCaret().setDot(caretPosition);
79
                caretPosition = -1;
80
            }
81
        }
82
    }
83
84
    @Override
85
    public void cancelled(Context context) {
86
    }
87
88
    @MimeRegistrations(value = {
89
        @MimeRegistration(mimeType = "", service = TypedBreakInterceptor.Factory.class)})
90
    public static class Factory implements TypedBreakInterceptor.Factory {
91
92
        @Override
93
        public TypedBreakInterceptor createTypedBreakInterceptor(MimePath mimePath) {
94
            return new CslTypedBreakInterceptor();
95
        }
96
    }
97
}
(-)a/csl.api/src/org/netbeans/modules/csl/core/CslTypedTextInterceptor.java (+116 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.csl.core;
43
44
import javax.swing.text.BadLocationException;
45
import javax.swing.text.Document;
46
import javax.swing.text.JTextComponent;
47
import org.netbeans.api.editor.mimelookup.MimePath;
48
import org.netbeans.api.editor.mimelookup.MimeRegistration;
49
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
50
import org.netbeans.modules.csl.api.KeystrokeHandler;
51
import org.netbeans.modules.csl.api.UiUtils;
52
import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor;
53
54
public class CslTypedTextInterceptor implements TypedTextInterceptor {
55
56
    private boolean handled = true;
57
58
    @Override
59
    public boolean beforeInsert(Context context) throws BadLocationException {
60
        return false;
61
    }
62
63
    @Override
64
    public void insert(MutableContext context) throws BadLocationException {
65
        if (CslEditorKit.completionSettingEnabled()) {
66
            Document doc = context.getDocument();
67
            int dotPos = context.getOffset();
68
            KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
69
            if (bracketCompletion != null) {
70
                JTextComponent currentTarget = context.getComponent();
71
                String str = context.getText();
72
                // TODO - check if we're in a comment etc. and if so, do nothing
73
                handled = bracketCompletion.beforeCharInserted(doc, dotPos, currentTarget, str.charAt(0));
74
                if (handled) {
75
                    context.setText("", 0);
76
                }
77
            }
78
        }
79
    }
80
81
    @Override
82
    public void afterInsert(Context context) throws BadLocationException {
83
        if (CslEditorKit.completionSettingEnabled()) {
84
            if (!handled) {
85
                Document doc = context.getDocument();
86
                int dotPos = context.getOffset();
87
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
88
                if (bracketCompletion != null) {
89
                    JTextComponent currentTarget = context.getComponent();
90
                    String str = context.getText();
91
                    bracketCompletion.afterCharInserted(doc, dotPos, currentTarget, str.charAt(0));
92
                }
93
                handled = true;
94
            } else {
95
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(context.getDocument(), context.getOffset());
96
                if (bracketCompletion != null) {
97
                    context.getComponent().getCaret().setDot(context.getComponent().getCaret().getDot() + 1);
98
                }
99
            }
100
        }
101
    }
102
103
    @Override
104
    public void cancelled(Context context) {
105
    }
106
107
    @MimeRegistrations(value = {
108
        @MimeRegistration(mimeType = "", service = TypedTextInterceptor.Factory.class)})
109
    public static class Factory implements TypedTextInterceptor.Factory {
110
111
        @Override
112
        public TypedTextInterceptor createTypedTextInterceptor(MimePath mimePath) {
113
            return new CslTypedTextInterceptor();
114
        }
115
    }
116
}
(-)a/csl.api/src/org/netbeans/modules/csl/core/Language.java (-2 / +1 lines)
Lines 252-258 Link Here
252
            } else if (language instanceof DefaultLanguageConfig) {
252
            } else if (language instanceof DefaultLanguageConfig) {
253
                languageConfig = (DefaultLanguageConfig)language;
253
                languageConfig = (DefaultLanguageConfig)language;
254
            }
254
            }
255
        }
255
        }      
256
        return language;
256
        return language;
257
    }
257
    }
258
258
Lines 782-786 Link Here
782
    void setOverridingMethodsFile(FileObject fo) {
782
    void setOverridingMethodsFile(FileObject fo) {
783
        this.overridingMethodsFile = fo;
783
        this.overridingMethodsFile = fo;
784
    }
784
    }
785
786
}
785
}
(-)a/csl.api/test/unit/src/org/netbeans/modules/csl/editor/TypingCompletion.java (+199 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.csl.editor;
43
44
import java.awt.EventQueue;
45
import java.awt.event.KeyEvent;
46
import javax.swing.JEditorPane;
47
import javax.swing.SwingUtilities;
48
import javax.swing.text.BadLocationException;
49
import javax.swing.text.Document;
50
import javax.swing.text.EditorKit;
51
import junit.framework.TestCase;
52
import org.netbeans.api.lexer.Language;
53
import org.netbeans.lib.editor.util.CharSequenceUtilities;
54
import org.openide.filesystems.FileObject;
55
import org.openide.filesystems.MIMEResolver;
56
import org.openide.util.lookup.ServiceProvider;
57
58
/**
59
 * Test brackets completion - unlike the original test this one
60
 * emulates real typing and tests the resulting state of the document.
61
 */
62
public class TypingCompletion {
63
    public static final class Context {
64
        
65
        private JEditorPane pane;
66
67
        public Context(final EditorKit kit, final Language language, final String mimetype, final String textWithPipe) {
68
            try {
69
                SwingUtilities.invokeAndWait(new Runnable() {
70
                    @Override
71
                    public void run() {
72
                        pane = new JEditorPane();
73
                        pane.setEditorKit(kit);
74
                        Document doc = pane.getDocument();
75
                        // Required by Java's default key typed
76
                        doc.putProperty(Language.class, language);
77
                        doc.putProperty("mimeType", mimetype);
78
                        int caretOffset = textWithPipe.indexOf('|');
79
                        String text;
80
                        if (caretOffset != -1) {
81
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
82
                        } else {
83
                            text = textWithPipe;
84
                        }
85
                        pane.setText(text);
86
                        pane.setCaretPosition((caretOffset != -1) ? caretOffset : doc.getLength());
87
                    }
88
                });
89
            } catch (Exception e) {
90
                throw new IllegalStateException(e);
91
            }
92
        }
93
        
94
        public JEditorPane pane() {
95
            return pane;
96
        }
97
98
        public Document document() {
99
            return pane.getDocument();
100
        }
101
        
102
        public void typeChar(final char ch) {
103
            try {
104
                SwingUtilities.invokeAndWait(new Runnable() {
105
                    @Override
106
                    public void run() {
107
                        KeyEvent keyEvent;
108
                        switch (ch) {
109
                            case '\n':
110
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
111
                                        EventQueue.getMostRecentEventTime(),
112
                                        0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Enter
113
                                break;
114
                            case '\b':
115
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
116
                                        EventQueue.getMostRecentEventTime(),
117
                                        0, KeyEvent.VK_BACK_SPACE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of BackSpace
118
                                break;
119
                            case '\f':
120
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
121
                                        EventQueue.getMostRecentEventTime(),
122
                                        0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Delete
123
                                break;
124
                            default:
125
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_TYPED,
126
                                        EventQueue.getMostRecentEventTime(),
127
                                        0, KeyEvent.VK_UNDEFINED, ch);
128
                        }
129
                        SwingUtilities.processKeyBindings(keyEvent);
130
                    }
131
                });
132
            } catch (Exception e) {
133
                throw new IllegalStateException(e);
134
            }
135
        }
136
137
        public void typeText(String text) {
138
            for (int i = 0; i < text.length(); i++) {
139
                typeChar(text.charAt(i));
140
            }
141
        }
142
143
        public void assertDocumentTextEquals(final String textWithPipe) {
144
            try {
145
                SwingUtilities.invokeAndWait(new Runnable() {
146
                    @Override
147
                    public void run() {
148
                        int caretOffset = textWithPipe.indexOf('|');
149
                        String text;
150
                        if (caretOffset != -1) {
151
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
152
                        } else {
153
                            text = textWithPipe;
154
                        }
155
                        try {
156
                            // Use debug text to prefix special chars for easier readability
157
                            text = CharSequenceUtilities.debugText(text);
158
                            String docText = document().getText(0, document().getLength());
159
                            docText = CharSequenceUtilities.debugText(docText);
160
                            if (!text.equals(docText)) {
161
                                int diffIndex = 0;
162
                                int minLen = Math.min(docText.length(), text.length());
163
                                while (diffIndex < minLen) {
164
                                    if (text.charAt(diffIndex) != docText.charAt(diffIndex)) {
165
                                        break;
166
                                    }
167
                                    diffIndex++;
168
                                }
169
                                TestCase.fail("Invalid document text - diff at index " + diffIndex +
170
                                        "\nExpected: \"" + text +
171
                                        "\"\n  Actual: \"" + docText + "\""
172
                                );
173
                            }
174
                        } catch (BadLocationException e) {
175
                            throw new IllegalStateException(e);
176
                        }
177
                        if (caretOffset != -1) {
178
                            TestCase.assertEquals("Invalid caret offset", caretOffset, pane.getCaretPosition());
179
                        }
180
                    }
181
                });
182
            } catch (Exception e) {
183
                throw new IllegalStateException(e);
184
            }
185
        }
186
    }
187
188
    @ServiceProvider(service=MIMEResolver.class)
189
    public static final class MIMEResolverImpl extends MIMEResolver {
190
191
        public MIMEResolverImpl() {
192
            super("text/x-nbeditor-keybindingsettings");
193
        }
194
195
        @Override public String findMIMEType(FileObject fo) {
196
            return fo.getPath().contains("Keybindings") ? "text/x-nbeditor-keybindingsettings" : null;
197
        }
198
    }
199
}
(-)a/html.editor/src/org/netbeans/modules/html/editor/api/HtmlKit.java (-199 lines)
Lines 123-147 Link Here
123
        c.setTransferHandler(new HtmlTransferHandler());
123
        c.setTransferHandler(new HtmlTransferHandler());
124
    }
124
    }
125
    
125
    
126
    protected DeleteCharAction createDeletePrevAction() {
127
        return new HtmlDeleteCharAction(deletePrevCharAction, false);
128
    }
129
    
130
    protected ExtDefaultKeyTypedAction createDefaultKeyTypedAction() {
131
        return new HtmlDefaultKeyTypedAction();
132
    }
133
134
    protected InsertBreakAction createInsertBreakAction() {
135
        return new HtmlInsertBreakAction();
136
    }
137
    
126
    
138
    @Override
127
    @Override
139
    protected Action[] createActions() {
128
    protected Action[] createActions() {
140
        Action[] HtmlActions = new Action[]{
129
        Action[] HtmlActions = new Action[]{
141
            createInsertBreakAction(),
142
            createDefaultKeyTypedAction(),
143
            createDeletePrevAction(),
144
            new HtmlDeleteCharAction(deleteNextCharAction, true),
145
            CslActions.createSelectCodeElementAction(true),
130
            CslActions.createSelectCodeElementAction(true),
146
            CslActions.createSelectCodeElementAction(false),
131
            CslActions.createSelectCodeElementAction(false),
147
            CslActions.createInstantRenameAction(),
132
            CslActions.createInstantRenameAction(),
Lines 370-559 Link Here
370
        }
355
        }
371
    }
356
    }
372
357
373
374
    public class HtmlInsertBreakAction extends InsertBreakAction {
375
376
        static final long serialVersionUID = -1506173310438326380L;
377
378
        @Override
379
        protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) {
380
            if (completionSettingEnabled()) {
381
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, caret.getDot());
382
383
                if (bracketCompletion != null) {
384
                    try {
385
                        int newOffset = bracketCompletion.beforeBreak(doc, caret.getDot(), target);
386
387
                        if (newOffset >= 0) {
388
                            return new Integer(newOffset);
389
                        }
390
                    } catch (BadLocationException ble) {
391
                        Exceptions.printStackTrace(ble);
392
                    }
393
                }
394
            }
395
396
            // return Boolean.TRUE;
397
            return null;
398
        }
399
400
        @Override
401
        protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret,
402
                Object cookie) {
403
            if (completionSettingEnabled()) {
404
                if (cookie != null) {
405
                    if (cookie instanceof Integer) {
406
                        // integer
407
                        int dotPos = ((Integer) cookie).intValue();
408
                        if (dotPos != -1) {
409
                            caret.setDot(dotPos);
410
                        } else {
411
                            int nowDotPos = caret.getDot();
412
                            caret.setDot(nowDotPos + 1);
413
                        }
414
                    }
415
                }
416
            }
417
        }
418
    }
419
420
    public static class HtmlDefaultKeyTypedAction extends ExtDefaultKeyTypedAction {
421
422
        private JTextComponent currentTarget;
423
424
        @Override
425
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
426
            currentTarget = target;
427
            super.actionPerformed(evt, target);
428
            currentTarget = null;
429
        }
430
431
        @Override
432
        protected void insertString(BaseDocument doc, int dotPos,
433
                Caret caret, String str,
434
                boolean overwrite) throws BadLocationException {
435
                
436
            if (completionSettingEnabled()) {
437
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
438
439
                if (bracketCompletion != null) {
440
                    // TODO - check if we're in a comment etc. and if so, do nothing
441
                    boolean handled =
442
                            bracketCompletion.beforeCharInserted(doc, dotPos, currentTarget,
443
                            str.charAt(0));
444
445
                    if (!handled) {
446
                            super.insertString(doc, dotPos, caret, str, overwrite);
447
                            handled = bracketCompletion.afterCharInserted(doc, dotPos, currentTarget,
448
                                    str.charAt(0));
449
                        }
450
451
452
                    return;
453
                }
454
            }
455
456
            super.insertString(doc, dotPos, caret, str, overwrite);
457
        }
458
459
        @Override
460
        protected void replaceSelection(JTextComponent target, int dotPos, Caret caret,
461
                String str, boolean overwrite) throws BadLocationException {
462
            
463
            //workaround for #209019 - regression of issue 
464
            //#204450 - Rewrite actions to use TypingHooks SPI
465
            if(str.length() == 0) {
466
                //called from BaseKit.actionPerformed():1160 with empty str argument
467
                //==> ignore this call since we are going to be called a bit later
468
                //from HtmlKit.performTextInsertion() properly with the text typed
469
                return ;
470
            }
471
            
472
            char insertedChar = str.charAt(0);
473
            Document document = target.getDocument();
474
475
            if (document instanceof BaseDocument) {
476
                BaseDocument doc = (BaseDocument) document;
477
478
                if (completionSettingEnabled()) {
479
                    KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
480
481
                    if (bracketCompletion != null) {
482
                        try {
483
                            int caretPosition = caret.getDot();
484
485
                            boolean handled =
486
                                    bracketCompletion.beforeCharInserted(doc, caretPosition,
487
                                    target, insertedChar);
488
489
                            int p0 = Math.min(caret.getDot(), caret.getMark());
490
                            int p1 = Math.max(caret.getDot(), caret.getMark());
491
492
                            if (p0 != p1) {
493
                                doc.remove(p0, p1 - p0);
494
                            }
495
496
                            if (!handled) {
497
                                if ((str != null) && (str.length() > 0)) {
498
                                    doc.insertString(p0, str, null);
499
                                }
500
501
                                bracketCompletion.afterCharInserted(doc, caret.getDot() - 1,
502
                                        target, insertedChar);
503
504
                            }
505
                        } catch (BadLocationException e) {
506
                            e.printStackTrace();
507
                        }
508
509
                        return;
510
                    }
511
                }
512
            }
513
514
            super.replaceSelection(target, dotPos, caret, str, overwrite);
515
        }
516
517
    }
518
519
    public static class HtmlDeleteCharAction extends DeleteCharAction {
520
521
        private JTextComponent currentTarget;
522
        
523
        public HtmlDeleteCharAction(String name, boolean nextChar) {
524
            super(name, nextChar);
525
        }
526
        
527
        @Override
528
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
529
            try {
530
                currentTarget = target;
531
                super.actionPerformed(evt, target);
532
            } finally {
533
                currentTarget = null;
534
            }
535
        }
536
537
        @Override
538
        protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException {
539
              if (completionSettingEnabled()) {
540
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
541
542
                if (bracketCompletion != null) {
543
                    boolean success = bracketCompletion.charBackspaced(doc, dotPos, currentTarget, ch);
544
                    return;
545
                }
546
            }
547
            
548
            
549
            super.charBackspaced(doc, dotPos, caret, ch);
550
        }
551
552
        public boolean getNextChar() {
553
            return nextChar;
554
        }
555
    }
556
557
    /* !!!!!!!!!!!!!!!!!!!!!
358
    /* !!!!!!!!!!!!!!!!!!!!!
558
     *
359
     *
559
     * Inner classes bellow were taken from BasicTextUI and rewritten in the place marked
360
     * Inner classes bellow were taken from BasicTextUI and rewritten in the place marked
(-)a/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java (-1288 / +56 lines)
Lines 42-77 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.editor.java;
45
package org.netbeans.modules.html.editor.gsf;
46
46
47
import java.awt.EventQueue;
48
import java.awt.event.KeyEvent;
49
import java.util.prefs.Preferences;
47
import java.util.prefs.Preferences;
50
import java.util.regex.Pattern;
51
import javax.swing.JEditorPane;
52
import javax.swing.SwingUtilities;
53
import javax.swing.text.BadLocationException;
54
import javax.swing.text.Document;
55
import javax.swing.text.EditorKit;
56
import javax.swing.text.PlainDocument;
57
import junit.framework.TestCase;
58
import org.netbeans.api.editor.mimelookup.MimeLookup;
48
import org.netbeans.api.editor.mimelookup.MimeLookup;
59
import org.netbeans.api.editor.settings.SimpleValueNames;
49
import org.netbeans.api.editor.settings.SimpleValueNames;
60
import org.netbeans.api.java.lexer.JavaTokenId;
50
import org.netbeans.api.html.lexer.HTMLTokenId;
61
import org.netbeans.api.lexer.Language;
62
import org.netbeans.junit.NbTestCase;
51
import org.netbeans.junit.NbTestCase;
63
import org.netbeans.lib.editor.util.CharSequenceUtilities;
52
import org.netbeans.modules.csl.editor.TypingCompletion;
53
import org.netbeans.modules.html.editor.api.HtmlKit;
64
import org.openide.awt.AcceleratorBinding;
54
import org.openide.awt.AcceleratorBinding;
65
import org.openide.filesystems.FileObject;
66
import org.openide.filesystems.MIMEResolver;
67
import org.openide.util.lookup.ServiceProvider;
68
55
69
56
70
/**
57
/**
71
 * Test java brackets completion - unlike the original test this one
58
 * Test java brackets completion - unlike the original test this one
72
 * emulates real typing and tests the resulting state of the document.
59
 * emulates real typing and tests the resulting state of the document.
73
 *
74
 * @autor Miloslav Metelka
75
 */
60
 */
76
public class TypingCompletionUnitTest extends NbTestCase {
61
public class TypingCompletionUnitTest extends NbTestCase {
77
62
Lines 82-1392 Link Here
82
    @Override
67
    @Override
83
    protected void setUp() throws Exception {
68
    protected void setUp() throws Exception {
84
        super.setUp();
69
        super.setUp();
85
        Preferences prefs = MimeLookup.getLookup(JavaKit.JAVA_MIME_TYPE).lookup(Preferences.class);
70
        Preferences prefs = MimeLookup.getLookup(HtmlKit.HTML_MIME_TYPE).lookup(Preferences.class);
86
        prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, true);
71
        prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, true);
87
        Class.forName(AcceleratorBinding.class.getName(), true, AcceleratorBinding.class.getClassLoader());
72
        Class.forName(AcceleratorBinding.class.getName(), true, AcceleratorBinding.class.getClassLoader());
88
    }
73
    }
89
74
90
    // ------- Tests for completion of right parenthesis ')' -------------
75
    // ------- Tests for completion of right parenthesis ')' -------------
91
76
92
    public void testTypeSemicolonInForLoop() { // #146139
77
    public void testQuoteCompletion() {
93
        Context ctx = new Context(new JavaKit(),
78
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
94
                "for (int i = 0|)"
79
                "<div t=|"
95
        );
80
        );
96
        ctx.typeChar(';');
81
        ctx.typeChar('"');
97
        ctx.assertDocumentTextEquals(
82
        ctx.assertDocumentTextEquals(
98
                "for (int i = 0;|)"
83
                "<div t=\"|\""
99
        );
100
    }
101
102
    public void testTypeSecondSemicolonInForLoop() { // #146139
103
        Context ctx = new Context(new JavaKit(),
104
                "for (int i = 0; i <= 0|)"
105
        );
106
        ctx.typeChar(';');
107
        ctx.assertDocumentTextEquals(
108
                "for (int i = 0; i <= 0;|)"
109
        );
110
    }
111
112
    public void testTypeSemicolonInArgs() { // #146139
113
        Context ctx = new Context(new JavaKit(),
114
                "m(|)"
115
        );
116
        ctx.typeChar(';');
117
        ctx.assertDocumentTextEquals(
118
                "m();|"
119
        );
84
        );
120
    }
85
    }
121
    
86
    
122
    public void testSemicolonOnTheEnd() { // #146139
87
    public void testTypingQuote() { 
123
        Context ctx = new Context(new JavaKit(),
88
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
124
                "m()| "
89
                "\"|"
125
        );
90
        );
126
        ctx.typeChar(';');
127
        ctx.assertDocumentTextEquals(
128
                "m();| "
129
        );
130
    }
131
132
    public void testTypeRightParenWithinBraces() { // #146139
133
        Context ctx = new Context(new JavaKit(),
134
                "{(()|; }"
135
        );
136
        ctx.typeChar(')');
137
        ctx.assertDocumentTextEquals(
138
                "{(()); }"
139
        );
140
    }
141
    
142
    public void testTypeLeftParen() {
143
        Context ctx = new Context(new JavaKit(), "m|");
144
        ctx.typeChar('(');
145
        ctx.assertDocumentTextEquals("m(|)");
146
    }
147
148
    public void testTypeSecondRightParen() {
149
        Context ctx = new Context(new JavaKit(),
150
                "m()|)"
151
        );
152
        ctx.typeChar(')');
153
        ctx.assertDocumentTextEquals(
154
                "m())|"
155
        );
156
    }
157
158
    public void testTypeRightParenSwingInvokeLaterRunnable() {
159
        Context ctx = new Context(new JavaKit(),
160
                "SwingUtilities.invokeLater(new Runnable()|))"
161
        );
162
        ctx.typeChar(')');
163
        ctx.assertDocumentTextEquals(
164
                "SwingUtilities.invokeLater(new Runnable())|)"
165
        );
166
    }
167
168
    public void testTypeSimpleAdditionOfOpeningParenthesisAfterWhile () throws Exception {
169
        Context ctx = new Context(new JavaKit(),
170
                "while |"
171
        );
172
        ctx.typeChar('(');
173
        ctx.assertDocumentTextEquals(
174
                "while (|)"
175
        );
176
    }
177
178
    public void testTypeRightParenSwingInvokeLaterRunnableRun() {
179
        Context ctx = new Context(new JavaKit(),
180
                "SwingUtilities.invokeLater(new Runnable() {\n" +
181
                "    public void run()|)\n" +
182
                "})"
183
        );
184
        ctx.typeChar(')');
185
        ctx.assertDocumentTextEquals(
186
                "SwingUtilities.invokeLater(new Runnable() {\n" +
187
                "    public void run())|\n" +
188
                "})"
189
        );
190
    }
191
192
    public void testTypeRightParenIfMethodCall() {
193
        Context ctx = new Context(new JavaKit(),
194
                "if (a()|) + 5 > 6) {\n" +
195
                "}"
196
        );
197
        ctx.typeChar(')');
198
        ctx.assertDocumentTextEquals(
199
                "if (a())| + 5 > 6) {\n" +
200
                "}"
201
        );
202
    }
203
204
    public void testTypeRightParenNoSkipNonBracketChar() {
205
        Context ctx = new Context(new JavaKit(),
206
                "m()|"
207
        );
208
        ctx.typeChar(' ');
209
        ctx.assertDocumentTextEquals(
210
                "m() |"
211
        );
212
    }
213
214
215
216
    // ------- Tests for completion of right brace '}' -------------
217
218
    public void testTypeAddRightBraceIfLeftBrace() {
219
        Context ctx = new Context(new JavaKit(),
220
                "class Test {\n" +
221
                "    {\n" +
222
                "        if (true) {|\n" +
223
                "    }\n" +
224
                "}\n"
225
        );
226
        ctx.typeChar('\n');
227
        ctx.assertDocumentTextEquals(
228
                "class Test {\n" +
229
                "    {\n" +
230
                "        if (true) {\n" +
231
                "            |\n" +
232
                "        }\n" +
233
                "    }\n" +
234
                "}\n"
235
        );
236
    }
237
238
    public void testTypeAddRightBraceIfLeftBraceWhiteSpace() {
239
        Context ctx = new Context(new JavaKit(),
240
                "class Test {\n" +
241
                "    {\n" +
242
                "        if (true) { \t|\n" +
243
                "    }\n" +
244
                "}\n"
245
        );
246
        ctx.typeChar('\n');
247
        ctx.assertDocumentTextEquals(
248
                "class Test {\n" +
249
                "    {\n" +
250
                "        if (true) { \t\n" +
251
                "            |\n" +
252
                "        }\n" +
253
                "    }\n" +
254
                "}\n"
255
        );
256
    }
257
258
    public void testTypeAddRightBraceIfLeftBraceLineComment() {
259
        Context ctx = new Context(new JavaKit(),
260
                "class Test {\n" +
261
                "    {\n" +
262
                "        if (true) { // line-comment|\n" +
263
                "    }\n" +
264
                "}\n"
265
        );
266
        ctx.typeChar('\n');
267
        ctx.assertDocumentTextEquals(
268
                "class Test {\n" +
269
                "    {\n" +
270
                "        if (true) { // line-comment\n" +
271
                "            |\n" +
272
                "        }\n" +
273
                "    }\n" +
274
                "}\n"
275
        );
276
    }
277
278
    public void testTypeAddRightBraceIfLeftBraceBlockComment() {
279
        Context ctx = new Context(new JavaKit(),
280
                "class Test {\n" +
281
                "    {\n" +
282
                "        if (true) { /* block-comment */|\n" +
283
                "    }\n" +
284
                "}\n"
285
        );
286
        ctx.typeChar('\n');
287
        ctx.assertDocumentTextEquals(
288
                "class Test {\n" +
289
                "    {\n" +
290
                "        if (true) { /* block-comment */\n" +
291
                "            |\n" +
292
                "        }\n" +
293
                "    }\n" +
294
                "}\n"
295
        );
296
    }
297
298
    public void testTypeAddRightBraceIfLeftBraceAlreadyPresent() {
299
        Context ctx = new Context(new JavaKit(),
300
                "class Test {\n" +
301
                "    {\n" +
302
                "        if (true) {|\n" +
303
                "        }\n" +
304
                "    }\n" +
305
                "}\n"
306
        );
307
        ctx.typeChar('\n');
308
        ctx.assertDocumentTextEquals(
309
                "class Test {\n" +
310
                "    {\n" +
311
                "        if (true) {\n" +
312
                "            |\n" +
313
                "        }\n" +
314
                "    }\n" +
315
                "}\n"
316
        );
317
    }
318
319
    public void XtestTypeAddRightBraceCaretInComment() {
320
        Context ctx = new Context(new JavaKit(),
321
                "class Test {\n" +
322
                "    {\n" +
323
                "        if (true) { /* unclosed-block-comment|\n" +
324
                "          */\n" +
325
                "    }\n" +
326
                "}\n"
327
        );
328
        ctx.typeChar('\n');
329
        ctx.assertDocumentTextEquals(
330
                "class Test {\n" +
331
                "    {\n" +
332
                "        if (true) { /* unclosed-block-comment\n" +
333
                "                     * |\n" +
334
                "          */\n" +
335
                "    }\n" +
336
                "}\n"
337
        );
338
    }
339
340
    public void testTypeAddRightBraceMultiLine() {
341
        Context ctx = new Context(new JavaKit(),
342
                "class Test {\n" +
343
                "    {\n" +
344
                "        if (true) {| System.out.println(\n" +
345
                "        \"\");\n" +
346
                "    }\n" +
347
                "}\n"
348
                );
349
350
        ctx.typeChar('\n');
351
        ctx.assertDocumentTextEquals(
352
                "class Test {\n" +
353
                "    {\n" +
354
                "        if (true) {\n" +
355
                "            System.out.println(\n" +
356
                "        \"\");\n" +
357
                "    }\n" +
358
                "}\n"
359
                );
360
    }
361
362
    public void testTypeAddRightBraceSingleLine() {
363
        Context ctx = new Context(new JavaKit(),
364
                "class Test {\n" +
365
                "    {\n" +
366
                "        if (true) {| System.out.println(\"\");\n" +
367
                "    }\n" +
368
                "}\n"
369
                );
370
371
        ctx.typeChar('\n');
372
        ctx.assertDocumentTextEquals(
373
                "class Test {\n" +
374
                "    {\n" +
375
                "        if (true) {\n" +
376
                "            System.out.println(\"\");\n" +
377
                "        }\n" +
378
                "    }\n" +
379
                "}\n"
380
                );
381
    }
382
383
384
    // ------- Tests for completion of quote (") -------------
385
    public void testTypeSimpleQuoteInEmptyDoc () throws Exception {
386
        Context ctx = new Context(new JavaKit(),
387
                "|"
388
        );
389
        ctx.typeChar('"');
390
        ctx.assertDocumentTextEquals(
391
                "\"|\""
392
        );
393
    }
394
395
    public void testTypeSimpleQuoteAtBeginingOfDoc () throws Exception {
396
        Context ctx = new Context(new JavaKit(),
397
                "|  "
398
        );
399
        ctx.typeChar('"');
400
        ctx.assertDocumentTextEquals(
401
                "\"|\"  "
402
        );
403
    }
404
405
    public void testTypeSimpleQuoteAtEndOfDoc () throws Exception {
406
        Context ctx = new Context(new JavaKit(),
407
                "  |"
408
        );
409
        ctx.typeChar('"');
410
        ctx.assertDocumentTextEquals(
411
                "  \"|\""
412
        );
413
    }
414
415
    public void testTypeSimpleQuoteInWhiteSpaceArea () throws Exception {
416
        Context ctx = new Context(new JavaKit(),
417
                "  |  "
418
        );
419
        ctx.typeChar('"');
420
        ctx.assertDocumentTextEquals(
421
                "  \"|\"  "
422
        );
423
    }
424
425
    public void testTypeQuoteAtEOL () throws Exception {
426
        Context ctx = new Context(new JavaKit(),
427
                "  |\n"
428
        );
429
        ctx.typeChar('"');
430
        ctx.assertDocumentTextEquals(
431
                "  \"|\"\n"
432
        );
433
    }
434
435
    public void testTypeQuoteWithUnterminatedStringLiteral () throws Exception {
436
        Context ctx = new Context(new JavaKit(),
437
                "  \"unterminated string| \n"
438
        );
439
        ctx.typeChar('"');
440
        ctx.assertDocumentTextEquals(
441
                "  \"unterminated string\"| \n"
442
        );
443
    }
444
445
    public void testTypeQuoteAtEOLWithUnterminatedStringLiteral () throws Exception {
446
        Context ctx = new Context(new JavaKit(),
447
                "  \"unterminated string | \n"
448
        );
449
        ctx.typeChar('"');
450
        ctx.assertDocumentTextEquals(
451
                "  \"unterminated string \"| \n"
452
        );
453
    }
454
455
    public void testTypeQuoteInsideStringLiteral () throws Exception {
456
        Context ctx = new Context(new JavaKit(),
457
                "  \"stri|ng literal\" "
458
        );
459
        ctx.typeChar('"');
460
        ctx.assertDocumentTextEquals(
461
                "  \"stri\"|ng literal\" "
462
        );
463
    }
464
465
    public void testTypeQuoteInsideEmptyParentheses () throws Exception {
466
        Context ctx = new Context(new JavaKit(),
467
                " System.out.println(|) "
468
        );
469
        ctx.typeChar('"');
470
        ctx.assertDocumentTextEquals(
471
                " System.out.println(\"|\") "
472
        );
473
    }
474
475
    public void testTypeQuoteInsideNonEmptyParentheses () throws Exception {
476
        Context ctx = new Context(new JavaKit(),
477
                " System.out.println(|some text) "
478
        );
479
        ctx.typeChar('"');
480
        ctx.assertDocumentTextEquals(
481
                " System.out.println(\"|some text) "
482
        );
483
    }
484
485
    public void testTypeQuoteInsideNonEmptyParenthesesBeforeClosingParentheses () throws Exception {
486
        Context ctx = new Context(new JavaKit(),
487
                " System.out.println(i+|) "
488
        );
489
        ctx.typeChar('"');
490
        ctx.assertDocumentTextEquals(
491
                " System.out.println(i+\"|\") "
492
        );
493
    }
494
495
    public void testTypeQuoteInsideNonEmptyParenthesesBeforeClosingParenthesesAndUnterminatedStringLiteral () throws Exception {
496
        Context ctx = new Context(new JavaKit(),
497
                " System.out.println(\"unterminated string literal |); "
498
        );
499
        ctx.typeChar('"');
500
        ctx.assertDocumentTextEquals(
501
                " System.out.println(\"unterminated string literal \"|); "
502
        );
503
    }
504
505
    public void testTypeQuoteBeforePlus () throws Exception {
506
        Context ctx = new Context(new JavaKit(),
507
                " System.out.println(|+\"string literal\"); "
508
        );
509
        ctx.typeChar('"');
510
        ctx.assertDocumentTextEquals(
511
                " System.out.println(\"|\"+\"string literal\"); "
512
        );
513
    }
514
515
    public void testTypeQuoteBeforeComma () throws Exception {
516
        Context ctx = new Context(new JavaKit(),
517
                "String s[] = new String[]{|,\"two\"};"
518
        );
519
        ctx.typeChar('"');
520
        ctx.assertDocumentTextEquals(
521
                "String s[] = new String[]{\"|\",\"two\"};"
522
        );
523
    }
524
525
    public void testTypeQuoteBeforeBrace () throws Exception {
526
        Context ctx = new Context(new JavaKit(),
527
                "String s[] = new String[]{\"one\",|};"
528
        );
529
        ctx.typeChar('"');
530
        ctx.assertDocumentTextEquals(
531
                "String s[] = new String[]{\"one\",\"|\"};"
532
        );
533
    }
534
535
    public void testTypeQuoteBeforeSemicolon() throws Exception {
536
        Context ctx = new Context(new JavaKit(),
537
                "String s = \"\" + |;"
538
        );
539
        ctx.typeChar('"');
540
        ctx.assertDocumentTextEquals(
541
                "String s = \"\" + \"|\";"
542
        );
543
    }
544
545
    public void testTypeQuoteBeforeSemicolonWithWhitespace() throws Exception {
546
        Context ctx = new Context(new JavaKit(),
547
                "String s = \"\" +| ;"
548
        );
549
        ctx.typeChar('"');
550
        ctx.assertDocumentTextEquals(
551
                "String s = \"\" +\"|\" ;"
552
        );
553
    }
554
555
    public void testTypeQuoteAfterEscapeSequence() throws Exception {
556
        Context ctx = new Context(new JavaKit(),
557
                "\\|"
558
        );
559
        ctx.typeChar('"');
560
        ctx.assertDocumentTextEquals(
561
                "\\\"|"
562
        );
563
    }
564
565
    public void testTypeQuoteEaten() throws Exception {
566
        Context ctx = new Context(new JavaKit(),
567
                "|"
568
        );
569
        ctx.typeChar('"');
570
        ctx.typeChar('"');
91
        ctx.typeChar('"');
571
        ctx.assertDocumentTextEquals(
92
        ctx.assertDocumentTextEquals(
572
                "\"\"|"
93
                "\"\"|"
573
        );
94
        );
574
    }
95
    }
575
96
    
576
    public void testTypeQuoteOnFirstQuote () throws Exception {
97
    public void testBackSlash() { 
577
        Context ctx = new Context(new JavaKit(),
98
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
578
                " |\"asdf\""
99
                "<html |"
100
        );
101
        ctx.typeChar('/');
102
        ctx.assertDocumentTextEquals(
103
                "<html />|"
104
        );
105
    }
106
    
107
    public void testChangeQuote() { 
108
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
109
                "<html t=\"|\""
110
        );
111
        ctx.typeChar('\'');
112
        ctx.assertDocumentTextEquals(
113
                "<html t='|'"
114
        );
115
    }
116
    
117
    public void testJumpingQuote() { 
118
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
119
                "<html t=\"|\""
579
        );
120
        );
580
        ctx.typeChar('"');
121
        ctx.typeChar('"');
581
        ctx.assertDocumentTextEquals(
122
        ctx.assertDocumentTextEquals(
582
                " \"|\"asdf\""
123
                "<html t=\"\"|"
583
        );
584
    }
585
586
    public void testTypeQuoteInsideComments() throws Exception {
587
        Context ctx = new Context(new JavaKit(),
588
                "/** |\n */"
589
        );
590
        ctx.typeChar('"');
591
        ctx.assertDocumentTextEquals(
592
                "/** \"|\n */"
593
        );
594
    }
595
596
    public void testTypeQuoteAtTheEndOfLineCommentLine() throws Exception {
597
        Context ctx = new Context(new JavaKit(),
598
                "// test line comment |\n"
599
        );
600
        ctx.typeChar('"');
601
        ctx.assertDocumentTextEquals(
602
                "// test line comment \"|\n"
603
        );
604
    }
605
606
607
    // ------- Tests for completion of single quote (') -------------
608
609
    public void testTypeSingleQuoteInEmptyDoc () throws Exception {
610
        Context ctx = new Context(new JavaKit(),
611
                "|"
612
        );
613
        ctx.typeChar('\'');
614
        ctx.assertDocumentTextEquals(
615
                "'|'"
616
        );
617
    }
618
619
    public void testTypeSingleQuoteAtBeginingOfDoc () throws Exception {
620
        Context ctx = new Context(new JavaKit(),
621
                "|  "
622
        );
623
        ctx.typeChar('\'');
624
        ctx.assertDocumentTextEquals(
625
                "'|'  "
626
        );
627
    }
628
629
    public void testTypeSingleQuoteAtEndOfDoc () throws Exception {
630
        Context ctx = new Context(new JavaKit(),
631
                "  |"
632
        );
633
        ctx.typeChar('\'');
634
        ctx.assertDocumentTextEquals(
635
                "  '|'"
636
        );
637
    }
638
639
    public void testTypeSingleQuoteInWhiteSpaceArea () throws Exception {
640
        Context ctx = new Context(new JavaKit(),
641
                "  |  "
642
        );
643
        ctx.typeChar('\'');
644
        ctx.assertDocumentTextEquals(
645
                "  '|'  "
646
        );
647
    }
648
649
    public void testTypeSingleQuoteAtEOL () throws Exception {
650
        Context ctx = new Context(new JavaKit(),
651
                "  |\n"
652
        );
653
        ctx.typeChar('\'');
654
        ctx.assertDocumentTextEquals(
655
                "  '|'\n"
656
        );
657
    }
658
659
    public void testTypeSingleQuoteWithUnterminatedCharLiteral () throws Exception {
660
        Context ctx = new Context(new JavaKit(),
661
                "  '| \n"
662
        );
663
        ctx.typeChar('\'');
664
        ctx.assertDocumentTextEquals(
665
                "  ''| \n"
666
        );
667
    }
668
669
    public void testTypeSingleQuoteAtEOLWithUnterminatedCharLiteral () throws Exception {
670
        Context ctx = new Context(new JavaKit(),
671
                "  ' |\n"
672
        );
673
        ctx.typeChar('\'');
674
        ctx.assertDocumentTextEquals(
675
                "  ' '|\n"
676
        );
677
    }
678
679
    public void testTypeSingleQuoteInsideCharLiteral () throws Exception {
680
        Context ctx = new Context(new JavaKit(),
681
                "  '| ' "
682
        );
683
        ctx.typeChar('\'');
684
        ctx.assertDocumentTextEquals(
685
                "  ''| ' "
686
        );
687
    }
688
689
    public void testTypeSingleQuoteInsideEmptyParentheses () throws Exception {
690
        Context ctx = new Context(new JavaKit(),
691
                " System.out.println(|) "
692
        );
693
        ctx.typeChar('\'');
694
        ctx.assertDocumentTextEquals(
695
                " System.out.println('|') "
696
        );
697
    }
698
699
    public void testTypeSingleQuoteInsideNonEmptyParentheses () throws Exception {
700
        Context ctx = new Context(new JavaKit(),
701
                " System.out.println(|some text) "
702
        );
703
        ctx.typeChar('\'');
704
        ctx.assertDocumentTextEquals(
705
                " System.out.println('|some text) "
706
        );
707
    }
708
709
    public void testTypeSingleQuoteInsideNonEmptyParenthesesBeforeClosingParentheses () throws Exception {
710
        Context ctx = new Context(new JavaKit(),
711
                " System.out.println(i+|) "
712
        );
713
        ctx.typeChar('\'');
714
        ctx.assertDocumentTextEquals(
715
                " System.out.println(i+'|') "
716
        );
717
    }
718
719
    public void testTypeSingleQuoteInsideNonEmptyParenthesesBeforeClosingParenthesesAndUnterminatedCharLiteral () throws Exception {
720
        Context ctx = new Context(new JavaKit(),
721
                " System.out.println(' |); "
722
        );
723
        ctx.typeChar('\'');
724
        ctx.assertDocumentTextEquals(
725
                " System.out.println(' '|); "
726
        );
727
    }
728
729
    public void testTypeSingleQuoteBeforePlus () throws Exception {
730
        Context ctx = new Context(new JavaKit(),
731
                " System.out.println(|+\"string literal\"); "
732
        );
733
        ctx.typeChar('\'');
734
        ctx.assertDocumentTextEquals(
735
                " System.out.println('|'+\"string literal\"); "
736
        );
737
    }
738
739
    public void testTypeSingleQuoteBeforeComma () throws Exception {
740
        Context ctx = new Context(new JavaKit(),
741
                "String s[] = new String[]{|,\"two\"};"
742
        );
743
        ctx.typeChar('\'');
744
        ctx.assertDocumentTextEquals(
745
                "String s[] = new String[]{'|',\"two\"};"
746
        );
747
    }
748
749
    public void testTypeSingleQuoteBeforeBrace () throws Exception {
750
        Context ctx = new Context(new JavaKit(),
751
                "String s[] = new String[]{\"one\",|};"
752
        );
753
        ctx.typeChar('\'');
754
        ctx.assertDocumentTextEquals(
755
                "String s[] = new String[]{\"one\",'|'};"
756
        );
757
    }
758
759
    public void testTypeSingleQuoteBeforeSemicolon() throws Exception {
760
        Context ctx = new Context(new JavaKit(),
761
                "String s = \"\" + |;"
762
        );
763
        ctx.typeChar('\'');
764
        ctx.assertDocumentTextEquals(
765
                "String s = \"\" + '|';"
766
        );
767
    }
768
769
    public void testTypeSingleQuoteBeforeSemicolonWithWhitespace() throws Exception {
770
        Context ctx = new Context(new JavaKit(),
771
                "String s = \"\" +| ;"
772
        );
773
        ctx.typeChar('\'');
774
        ctx.assertDocumentTextEquals(
775
                "String s = \"\" +'|' ;"
776
        );
777
    }
778
779
    public void testTypeSingleQuoteAfterEscapeSequence() throws Exception {
780
        Context ctx = new Context(new JavaKit(),
781
                "\\|"
782
        );
783
        ctx.typeChar('\'');
784
        ctx.assertDocumentTextEquals(
785
                "\\'|"
786
        );
787
    }
788
789
    public void testTypeSingleQuoteEaten() throws Exception {
790
        Context ctx = new Context(new JavaKit(),
791
                "|"
792
        );
793
        ctx.typeChar('\'');
794
        ctx.typeChar('\'');
795
        ctx.assertDocumentTextEquals(
796
                "''|"
797
        );
798
    }
799
800
    public void testTypeSingleQuoteInsideComments() throws Exception {
801
        Context ctx = new Context(new JavaKit(),
802
                "/* |\n */"
803
        );
804
        ctx.typeChar('\'');
805
        ctx.assertDocumentTextEquals(
806
                "/* \'|\n */"
807
        );
808
    }
809
810
    public void testTypeSingleQuoteAtTheEndOfLineCommentLine() throws Exception {
811
        Context ctx = new Context(new JavaKit(),
812
                "// test line comment |\n"
813
        );
814
        ctx.typeChar('\'');
815
        ctx.assertDocumentTextEquals(
816
                "// test line comment \'|\n"
817
        );
818
    }
819
820
    public void testDisable147641() throws Exception {
821
        boolean orig = TypingCompletion.isCompletionSettingEnabled();
822
        Preferences prefs = MimeLookup.getLookup(JavaKit.JAVA_MIME_TYPE).lookup(Preferences.class);
823
824
        try {
825
            prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, false);
826
827
            Context ctx = new Context(new JavaKit(),
828
                    "while |"
829
            );
830
            ctx.typeChar('(');
831
            ctx.assertDocumentTextEquals(
832
                    "while (|"
833
            );
834
        } finally {
835
            prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, orig);
836
        }
837
    }
838
839
    public void testDoNotSkipWhenNotBalanced147683a() throws Exception {
840
        Context ctx = new Context(new JavaKit(),
841
                "System.err.println((true|);"
842
        );
843
        ctx.typeChar(')');
844
        ctx.assertDocumentTextEquals(
845
                "System.err.println((true)|);"
846
        );
847
    }
848
849
    public void testSkipWhenBalanced46517() throws Exception {
850
        Context ctx = new Context(new JavaKit(),
851
                "if (a(|) )"
852
        );
853
        ctx.typeChar(')');
854
        ctx.assertDocumentTextEquals(
855
                "if (a()| )"
856
        );
857
    }
858
859
    public void testDoNotSkipWhenNotBalanced147683b() throws Exception {
860
        Context ctx = new Context(new JavaKit(),
861
                "if (a(|) ; )"
862
        );
863
        ctx.typeChar(')');
864
        ctx.assertDocumentTextEquals(
865
                "if (a()|) ; )"
866
        );
867
    }
868
869
    public void testDoNotSkipWhenNotBalanced147683c() throws Exception {
870
        Context ctx = new Context(new JavaKit(),
871
                "if (a(|) \n )"
872
        );
873
        ctx.typeChar(')');
874
        ctx.assertDocumentTextEquals(
875
                "if (a()|) \n )"
876
        );
877
    }
878
//problem
879
    public void testSkipWhenBalanced198194a() throws Exception {
880
        Context ctx = new Context(new JavaKit(),
881
                "for (int i = a(|); i < 10; i++)"
882
        );
883
        ctx.typeChar(')');
884
        ctx.assertDocumentTextEquals(
885
                "for (int i = a()|; i < 10; i++)"
886
        );
887
    }
888
889
    public void testSkipWhenNotBalanced198194a() throws Exception {
890
        Context ctx = new Context(new JavaKit(),
891
                "for (int i = a(|; i < 10; i++)"
892
        );
893
        ctx.typeChar(')');
894
        ctx.assertDocumentTextEquals(
895
                "for (int i = a()|; i < 10; i++)"
896
        );
897
    }
898
899
    public void testSkipWhenBalanced198194b() throws Exception {
900
        Context ctx = new Context(new JavaKit(),
901
                "for (int i = a(); i < 10; i = a(|))"
902
        );
903
        ctx.typeChar(')');
904
        ctx.assertDocumentTextEquals(
905
                "for (int i = a(); i < 10; i = a()|)"
906
        );
907
    }
908
909
    public void testSkipWhenNotBalanced198194b() throws Exception {
910
        Context ctx = new Context(new JavaKit(),
911
                "for (int i = a(); i < 10; i = a(|)"
912
        );
913
        ctx.typeChar(')');
914
        ctx.assertDocumentTextEquals(
915
                "for (int i = a(); i < 10; i = a()|)"
916
        );
917
    }
918
919
    public void testSkipWhenNotBalanced198194c() throws Exception {
920
        Context ctx = new Context(new JavaKit(),
921
                "for (int i = a(); i < 10; i++) a(|;"
922
        );
923
        ctx.typeChar(')');
924
        ctx.assertDocumentTextEquals(
925
                "for (int i = a(); i < 10; i++) a()|;"
926
        );
927
    }
928
929
    public void testSkipWhenBalanced198194c() throws Exception {
930
        Context ctx = new Context(new JavaKit(),
931
                "for (int i = a(); i < 10; i++) a(|);"
932
        );
933
        ctx.typeChar(')');
934
        ctx.assertDocumentTextEquals(
935
                "for (int i = a(); i < 10; i++) a()|;"
936
        );
937
    }
938
939
    public void testKeepBalance148878() throws Exception {
940
        Context ctx = new Context(new JavaKit(),
941
                "Map[|] m = new HashMap[1];"
942
        );
943
        ctx.typeChar(']');
944
        ctx.assertDocumentTextEquals(
945
                "Map[]| m = new HashMap[1];"
946
        );
124
        );
947
    }
125
    }
948
    
126
    
949
    public void testQuotes148878() throws Exception {
127
    public void testCurlyBrace() { 
950
        Context ctx = new Context(new JavaKit(),
128
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
951
                "if (c == '\\\\|')"
129
                "<html><style type=\"text/css\">body|"
952
        );
130
        );
953
        ctx.typeChar('\'');
131
        ctx.typeChar('{');
954
        ctx.assertDocumentTextEquals(
132
        ctx.assertDocumentTextEquals(
955
                "if (c == '\\\\'|)"
133
                "<html><style type=\"text/css\">body{|}"
956
        );
134
        );
957
    }
135
    }
958
   
959
    public void testPositionInString() throws Exception {
960
        Context ctx = new Context(new JavaKit(),
961
                "class Test {\n"
962
                + "    {\n"
963
                + "        \"H|\"\n"
964
                + "    }\n"
965
                + "}\n");
966
        ctx.typeChar('\n');
967
        ctx.assertDocumentTextEquals(
968
                "class Test {\n"
969
                + "    {\n"
970
                + "        \"H\"\n"
971
                + "        + \"|\"\n"
972
                + "    }\n"
973
                + "}\n");
974
    }
975
    
136
    
976
    public void testPositionInEmptyString() throws Exception {
137
    public void testNewLineInCurlyBrace() { 
977
        Context ctx = new Context(new JavaKit(),
138
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
978
                "class Test {\n"
139
                "<html><style type=\"text/css\">body{|}"
979
                + "    {\n"
980
                + "        \"|\"\n"
981
                + "    }\n"
982
                + "}\n");
983
        ctx.typeChar('\n');
984
        ctx.assertDocumentTextEquals(
985
                "class Test {\n"
986
                + "    {\n"
987
                + "        \"\"\n"
988
                + "        + \"|\"\n"
989
                + "    }\n"
990
                + "}\n");
991
    }
992
    
993
    public void testCommentBlockCompletion() throws Exception {
994
        Context ctx = new Context(new JavaKit(),
995
                "class Test {\n"
996
                + "    {\n"
997
                + "        /*|\n"
998
                + "    }\n"
999
                + "}\n");
1000
        ctx.typeChar('\n');
1001
        ctx.assertDocumentTextEquals(
1002
                "class Test {\n"
1003
                + "    {\n"
1004
                + "        /*\n"
1005
                + "         * |\n"
1006
                + "         */\n"
1007
                + "    }\n"
1008
                + "}\n");
1009
    }
1010
   
1011
    public void testCommentBlockCompletionNotNeeded() throws Exception {
1012
        Context ctx = new Context(new JavaKit(),
1013
                "class Test {\n"
1014
                + "    {\n"
1015
                + "        /*|\n"
1016
                + "         */\n"
1017
                + "    }\n"
1018
                + "}\n");
1019
        ctx.typeChar('\n');
1020
        ctx.assertDocumentTextEquals(
1021
                "class Test {\n"
1022
                + "    {\n"
1023
                + "        /*\n"
1024
                + "         * |\n"
1025
                + "         */\n"
1026
                + "    }\n"
1027
                + "}\n");
1028
    }
1029
    
1030
1031
    public void testCommentBlockCompletionTwoComments () {
1032
        Context ctx = new Context(new JavaKit(),
1033
                "/*|\n" +
1034
                "/*\n" +
1035
                " */"
1036
        );
140
        );
1037
        ctx.typeChar('\n');
141
        ctx.typeChar('\n');
1038
        ctx.assertDocumentTextEquals(
142
        ctx.assertDocumentTextEquals(
1039
                "/*\n" +
143
                "<html><style type=\"text/css\">body{\n            |\n        }"
1040
                " * |\n" +
1041
                " */\n" +
1042
                "/*\n" +
1043
                " */"
1044
        );
1045
    }
1046
1047
    public void testCommentBlockCompletionTwoComments2 () {
1048
        Context ctx = new Context(new JavaKit(),
1049
                "/*|\n" +
1050
                "\n" +
1051
                "/*\n" +
1052
                " */"
1053
        );
1054
        ctx.typeChar('\n');
1055
        ctx.assertDocumentTextEquals(
1056
                "/*\n" +
1057
                " * |\n" +
1058
                " */\n" +
1059
                "\n" +
1060
                "/*\n" +
1061
                " */"
1062
        );
144
        );
1063
    }
145
    }
1064
    
146
    
1065
    public void testCommentBlockCompletionNoClose () {
147
    public void testDeleteCurlyBrace() { 
1066
        Context ctx = new Context(new JavaKit(),
148
        TypingCompletion.Context ctx = new TypingCompletion.Context(new HtmlKit(), HTMLTokenId.language(), HtmlKit.HTML_MIME_TYPE,
1067
                "/*| a"
149
                "<html><style type=\"text/css\">body|"
1068
        );
150
        );
1069
        ctx.typeChar('\n');
151
        ctx.typeChar('{');
1070
        ctx.assertDocumentTextEquals(
152
        ctx.assertDocumentTextEquals(
1071
                "/*\n" +
153
                "<html><style type=\"text/css\">body{|}"
1072
                " * |a"
154
        );
155
        ctx.typeChar('\b');
156
        ctx.assertDocumentTextEquals(
157
                "<html><style type=\"text/css\">body|"
1073
        );
158
        );
1074
    }
159
    }
1075
    
1076
    public void testJavaDocCompletion() throws Exception {
1077
        Context ctx = new Context(new JavaKit(),
1078
                "class Test {\n"
1079
                + "    {\n"
1080
                + "        /**|\n"
1081
                + "    }\n"
1082
                + "}\n");
1083
        ctx.typeChar('\n');
1084
        ctx.assertDocumentTextEquals(
1085
                "class Test {\n"
1086
                + "    {\n"
1087
                + "        /**\n"
1088
                + "         * |\n"
1089
                + "         */\n"
1090
                + "    }\n"
1091
                + "}\n");
1092
    }
1093
    
1094
    public void testJavaDocCompletionNotNeeded() throws Exception {
1095
        Context ctx = new Context(new JavaKit(),
1096
                "class Test {\n"
1097
                + "    {\n"
1098
                + "        /**|\n"
1099
                + "         */\n"
1100
                + "    }\n"
1101
                + "}\n");
1102
        ctx.typeChar('\n');
1103
        ctx.assertDocumentTextEquals(
1104
                "class Test {\n"
1105
                + "    {\n"
1106
                + "        /**\n"
1107
                + "         * |\n"
1108
                + "         */\n"
1109
                + "    }\n"
1110
                + "}\n");
1111
    }
1112
  
1113
    public void insertBreakJavadocComplete() throws Exception {
1114
        Context ctx = new Context(new JavaKit(),
1115
                "class Test {\n"
1116
                + "    {\n"
1117
                + "        /**|\n"
1118
                + "        public static void main(String[] args) {\n"
1119
                + "        }\n"
1120
                + "    }\n"
1121
                + "}\n");
1122
        ctx.typeChar('\n');
1123
        ctx.assertDocumentTextEquals (
1124
            "class Test {\n"
1125
                + "    {\n"
1126
                + "        /**\n"
1127
                + "         * |\n"
1128
                + "         * @param args\n"
1129
                + "         */\n"
1130
                + "        public static void main(String[] args) {\n"
1131
                + "        }\n"
1132
                + "    }\n"
1133
                + "}\n"
1134
        );
1135
    }
1136
    
1137
    public void testRemoveBracketBackSpace() throws Exception {
1138
        Context ctx = new Context(new JavaKit(),
1139
                "()(|)");
1140
        ctx.typeChar('\b');
1141
        ctx.assertDocumentTextEquals("()|");
1142
    }
1143
1144
    public void testRemoveBracketDelete() throws Exception {
1145
        Context ctx = new Context(new JavaKit(),
1146
                "()|()");
1147
        ctx.typeChar('\f');
1148
        ctx.assertDocumentTextEquals("()|");
1149
    }
1150
    
1151
    public void testRemoveQuotesBackSpace() throws Exception {
1152
        Context ctx = new Context(new JavaKit(),
1153
                "\"\"\"|\"");
1154
        ctx.typeChar('\b');
1155
        ctx.assertDocumentTextEquals("\"\"|");
1156
    }
1157
    
1158
    public void testRemoveQuotesDelete() throws Exception {
1159
        Context ctx = new Context(new JavaKit(),
1160
                "\"\"|\"\"");
1161
        ctx.typeChar('\f');
1162
        ctx.assertDocumentTextEquals("\"\"|");
1163
    }
1164
    
1165
    public void testRemoveQuotes2BackSpace() throws Exception {
1166
        Context ctx = new Context(new JavaKit(),
1167
                "\'\'\'|\'");
1168
        ctx.typeChar('\b');
1169
        ctx.assertDocumentTextEquals("\'\'|");
1170
    }
1171
    
1172
    public void testRemoveQuotes2Delete() throws Exception {
1173
        Context ctx = new Context(new JavaKit(),
1174
                "\'\'|\'\'");
1175
        ctx.typeChar('\f');
1176
        ctx.assertDocumentTextEquals("\'\'|");
1177
    }
1178
    
1179
    public void testJumpCharacters() throws Exception {
1180
        Context ctx = new Context(new JavaKit(), "m(\"p|\");");
1181
        ctx.typeChar('"');
1182
        ctx.assertDocumentTextEquals("m(\"p\"|);");
1183
        ctx.typeChar(')');
1184
        ctx.assertDocumentTextEquals("m(\"p\")|;");
1185
    }
1186
    
1187
    public void testJumpQuote() throws Exception {
1188
        Context ctx = new Context(new JavaKit(), "\"|\"");
1189
        ctx.typeChar('"');
1190
        ctx.assertDocumentTextEquals("\"\"|");
1191
    }
1192
    
1193
    public void testInsertSquareBracket() throws Exception {
1194
        Context ctx = new Context(new JavaKit(), "|");
1195
        ctx.typeChar('[');
1196
        ctx.assertDocumentTextEquals("[|]");
1197
    }
1198
1199
    public void testBackspaceSquareBracket() throws Exception {
1200
        Context ctx = new Context(new JavaKit(), "[|]");
1201
        ctx.typeChar('\b');
1202
        ctx.assertDocumentTextEquals("|");
1203
    }
1204
    
1205
    public void testDeleteSquareBracket() throws Exception {
1206
        Context ctx = new Context(new JavaKit(), "|[]");
1207
        ctx.typeChar('\f');
1208
        ctx.assertDocumentTextEquals("|");
1209
    }
1210
    
1211
    public void testInsertBracketInString() throws Exception {
1212
        Context ctx = new Context(new JavaKit(), "\"|\"");
1213
        ctx.typeChar('(');
1214
        ctx.assertDocumentTextEquals("\"(|\"");
1215
        ctx = new Context(new JavaKit(), "\" |\"");
1216
        ctx.typeChar('(');
1217
        ctx.assertDocumentTextEquals("\" (|\"");
1218
    }
1219
    
1220
    public void testInsertBracketInComment() throws Exception {
1221
        Context ctx = new Context(new JavaKit(), "//|");
1222
        ctx.typeChar('(');
1223
        ctx.assertDocumentTextEquals("//(|");
1224
    }
1225
    
1226
    public void testSkipBracketInComment() throws Exception {
1227
        Context ctx = new Context(new JavaKit(), "//(|)");
1228
        ctx.typeChar(')');
1229
        ctx.assertDocumentTextEquals("//()|)");
1230
    }
1231
     
1232
    public void testCorrectHandlingOfStringEscapes184059() throws Exception {
1233
        assertTrue(isInsideString("foo\n\"bar|\""));
1234
        assertTrue(isInsideString("foo\n\"bar\\\"|\""));
1235
        assertFalse(isInsideString("foo\n\"bar\\\\\"|"));
1236
        assertFalse(isInsideString("foo\n|\"bar\\\\\""));
1237
        assertTrue(isInsideString("foo\n\"|bar\\\\\""));
1238
    }
1239
1240
    private boolean isInsideString(String code) throws BadLocationException {
1241
        int pos = code.indexOf('|');
1242
1243
        assertNotSame(-1, pos);
1244
1245
        code = code.replaceAll(Pattern.quote("|"), "");
1246
1247
        Document doc = new PlainDocument();
1248
1249
        doc.putProperty(Language.class, JavaTokenId.language());
1250
        doc.insertString(0, code, null);
1251
1252
        return TypingCompletion.posWithinString(doc, pos);
1253
    }
1254
1255
    private static final class Context {
1256
        
1257
        private JEditorPane pane;
1258
1259
        public Context(final EditorKit kit, final String textWithPipe) {
1260
            try {
1261
                SwingUtilities.invokeAndWait(new Runnable() {
1262
                    @Override
1263
                    public void run() {
1264
                        pane = new JEditorPane();
1265
                        pane.setEditorKit(kit);
1266
                        Document doc = pane.getDocument();
1267
                        // Required by Java's default key typed
1268
                        doc.putProperty(Language.class, JavaTokenId.language());
1269
                        doc.putProperty("mimeType", "text/x-java");
1270
                        int caretOffset = textWithPipe.indexOf('|');
1271
                        String text;
1272
                        if (caretOffset != -1) {
1273
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
1274
                        } else {
1275
                            text = textWithPipe;
1276
                        }
1277
                        pane.setText(text);
1278
                        pane.setCaretPosition((caretOffset != -1) ? caretOffset : doc.getLength());
1279
                    }
1280
                });
1281
            } catch (Exception e) {
1282
                throw new IllegalStateException(e);
1283
            }
1284
        }
1285
        
1286
        public JEditorPane pane() {
1287
            return pane;
1288
        }
1289
1290
        public Document document() {
1291
            return pane.getDocument();
1292
        }
1293
        
1294
        public void typeChar(final char ch) {
1295
            try {
1296
                SwingUtilities.invokeAndWait(new Runnable() {
1297
                    @Override
1298
                    public void run() {
1299
                        KeyEvent keyEvent;
1300
                        switch (ch) {
1301
                            case '\n':
1302
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1303
                                        EventQueue.getMostRecentEventTime(),
1304
                                        0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Enter
1305
                                break;
1306
                            case '\b':
1307
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1308
                                        EventQueue.getMostRecentEventTime(),
1309
                                        0, KeyEvent.VK_BACK_SPACE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of BackSpace
1310
                                break;
1311
                            case '\f':
1312
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1313
                                        EventQueue.getMostRecentEventTime(),
1314
                                        0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Delete
1315
                                break;
1316
                            default:
1317
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_TYPED,
1318
                                        EventQueue.getMostRecentEventTime(),
1319
                                        0, KeyEvent.VK_UNDEFINED, ch);
1320
                        }
1321
                        SwingUtilities.processKeyBindings(keyEvent);
1322
                    }
1323
                });
1324
            } catch (Exception e) {
1325
                throw new IllegalStateException(e);
1326
            }
1327
        }
1328
1329
        public void typeText(String text) {
1330
            for (int i = 0; i < text.length(); i++) {
1331
                typeChar(text.charAt(i));
1332
            }
1333
        }
1334
1335
        public void assertDocumentTextEquals(final String textWithPipe) {
1336
            try {
1337
                SwingUtilities.invokeAndWait(new Runnable() {
1338
                    @Override
1339
                    public void run() {
1340
                        int caretOffset = textWithPipe.indexOf('|');
1341
                        String text;
1342
                        if (caretOffset != -1) {
1343
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
1344
                        } else {
1345
                            text = textWithPipe;
1346
                        }
1347
                        try {
1348
                            // Use debug text to prefix special chars for easier readability
1349
                            text = CharSequenceUtilities.debugText(text);
1350
                            String docText = document().getText(0, document().getLength());
1351
                            docText = CharSequenceUtilities.debugText(docText);
1352
                            if (!text.equals(docText)) {
1353
                                int diffIndex = 0;
1354
                                int minLen = Math.min(docText.length(), text.length());
1355
                                while (diffIndex < minLen) {
1356
                                    if (text.charAt(diffIndex) != docText.charAt(diffIndex)) {
1357
                                        break;
1358
                                    }
1359
                                    diffIndex++;
1360
                                }
1361
                                TestCase.fail("Invalid document text - diff at index " + diffIndex +
1362
                                        "\nExpected: \"" + text +
1363
                                        "\"\n  Actual: \"" + docText + "\""
1364
                                );
1365
                            }
1366
                        } catch (BadLocationException e) {
1367
                            throw new IllegalStateException(e);
1368
                        }
1369
                        if (caretOffset != -1) {
1370
                            TestCase.assertEquals("Invalid caret offset", caretOffset, pane.getCaretPosition());
1371
                        }
1372
                    }
1373
                });
1374
            } catch (Exception e) {
1375
                throw new IllegalStateException(e);
1376
            }
1377
        }
1378
    }
1379
1380
    @ServiceProvider(service=MIMEResolver.class)
1381
    public static final class MIMEResolverImpl extends MIMEResolver {
1382
1383
        public MIMEResolverImpl() {
1384
            super("text/x-nbeditor-keybindingsettings");
1385
        }
1386
1387
        @Override public String findMIMEType(FileObject fo) {
1388
            return fo.getPath().contains("Keybindings") ? "text/x-nbeditor-keybindingsettings" : null;
1389
        }
1390
    }
1391
1392
}
160
}
(-)a/java.editor/nbproject/project.properties (-1 / +1 lines)
Lines 42-48 Link Here
42
42
43
javadoc.title=Java Editor
43
javadoc.title=Java Editor
44
44
45
spec.version.base=2.47.0
45
spec.version.base=2.48.0
46
test.qa-functional.cp.extra=${editor.dir}/modules/org-netbeans-modules-editor-fold.jar
46
test.qa-functional.cp.extra=${editor.dir}/modules/org-netbeans-modules-editor-fold.jar
47
javac.source=1.6
47
javac.source=1.6
48
#test.unit.cp.extra=
48
#test.unit.cp.extra=
(-)a/java.editor/src/org/netbeans/modules/editor/java/BraceCompletion.java (-993 lines)
Lines 1-993 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * Contributor(s):
28
 *
29
 * The Original Software is NetBeans. The Initial Developer of the Original
30
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
31
 * Microsystems, Inc. All Rights Reserved.
32
 *
33
 * If you wish your version of this file to be governed by only the CDDL
34
 * or only the GPL Version 2, indicate your decision by adding
35
 * "[Contributor] elects to include this software in this distribution
36
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
37
 * single choice of license, a recipient has the option to distribute
38
 * your version of this file under either the CDDL, the GPL Version 2 or
39
 * to extend the choice of license to its licensees as provided above.
40
 * However, if you add GPL Version 2 code and therefore, elected the GPL
41
 * Version 2 license, then the option applies only if the new code is
42
 * made subject to such option by the copyright holder.
43
 */
44
package org.netbeans.modules.editor.java;
45
46
import java.util.EnumSet;
47
import java.util.List;
48
import java.util.Set;
49
import java.util.prefs.Preferences;
50
import javax.swing.text.BadLocationException;
51
import javax.swing.text.Caret;
52
import javax.swing.text.Document;
53
import javax.swing.text.JTextComponent;
54
import org.netbeans.api.editor.mimelookup.MimeLookup;
55
import org.netbeans.api.editor.settings.SimpleValueNames;
56
import org.netbeans.api.java.lexer.JavaTokenId;
57
import org.netbeans.api.lexer.PartType;
58
import org.netbeans.api.lexer.Token;
59
import org.netbeans.api.lexer.TokenHierarchy;
60
import org.netbeans.api.lexer.TokenSequence;
61
import org.netbeans.editor.BaseDocument;
62
import org.netbeans.editor.Utilities;
63
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
64
import org.netbeans.modules.editor.indent.api.Indent;
65
import org.openide.util.Exceptions;
66
67
/**
68
 * This static class groups the whole aspect of bracket
69
 * completion. It is defined to clearly separate the functionality
70
 * and keep actions clean. 
71
 * The methods of the class are called from different actions as
72
 * KeyTyped, DeletePreviousChar.
73
 * <br/>
74
 * This class is similar to old BracketCompletion but works solely
75
 * with the token hierarchy.
76
 */
77
class BraceCompletion {
78
79
    /**
80
     * A hook method called after a character was inserted into the
81
     * document. The function checks for special characters for
82
     * completion ()[]'"{} and other conditions and optionally performs
83
     * changes to the doc and or caret (complets braces, moves caret,
84
     * etc.)
85
     * @param doc the document where the change occurred
86
     * @param caretOffset position of the character that was inserted.
87
     * @param caret caret
88
     * @param ch the character that was inserted
89
     * @throws BadLocationException if caretOffset is not correct
90
     */
91
    static void charInserted(BaseDocument doc, int caretOffset, Caret caret, char ch) throws BadLocationException {
92
        if (!completionSettingEnabled()) {
93
          return;
94
        }
95
96
        if (ch == ')' || ch == ']' || ch == '(' || ch == '[' || ch == ';') {
97
            TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, caretOffset, false);
98
            if (javaTS != null) {
99
                switch (javaTS.token().id()) {
100
                    case RPAREN:
101
                    case RBRACKET:
102
                        skipClosingBracket(doc, caret, ch, javaTS);
103
                        break;
104
                    case LPAREN:
105
                    case LBRACKET:
106
                        completeOpeningBracket(doc, caretOffset, caret, ch, javaTS);
107
                        break;
108
                    case SEMICOLON:
109
                        moveSemicolon(doc, caretOffset, caret, javaTS);
110
                }
111
            }
112
        }
113
    }
114
115
    private static void moveSemicolon(BaseDocument doc, int caretOffset, Caret caret,
116
    TokenSequence<JavaTokenId> ts) throws BadLocationException {
117
        int eolOffset = Utilities.getRowEnd(doc, caretOffset);
118
        int lastParenPos = caretOffset;
119
        int index = ts.index();
120
        // Move beyond semicolon
121
        while (ts.moveNext() && ts.offset() <= eolOffset) {
122
            Token<JavaTokenId> token = ts.token();
123
            switch (token.id()) {
124
                case RPAREN:
125
                    lastParenPos = ts.offset();
126
                    break;
127
                case WHITESPACE:
128
                    break;
129
                default:
130
                    return; //
131
            }
132
        }
133
        // Restore ts position
134
        ts.moveIndex(index);
135
        ts.moveNext();
136
        if (isForLoopSemicolon(ts) || posWithinAnyQuote(doc, caretOffset)) {
137
            return;
138
        }
139
        doc.remove(caretOffset, 1);
140
        doc.insertString(lastParenPos, ";", null); // NOI18N
141
        caret.setDot(lastParenPos + 1);
142
    }
143
144
    private static boolean isForLoopSemicolon(TokenSequence<JavaTokenId> ts) {
145
        Token<JavaTokenId> token = ts.token();
146
        if (token == null || token.id() != JavaTokenId.SEMICOLON) {
147
            return false;
148
        }
149
        int parenDepth = 0; // parenthesis depth
150
        int braceDepth = 0; // brace depth
151
        boolean semicolonFound = false; // next semicolon
152
        int tsOrigIndex = ts.index();
153
        try {
154
            while (ts.movePrevious()) {
155
                token = ts.token();
156
                switch (token.id()) {
157
                    case LPAREN:
158
                        if (parenDepth == 0) { // could be a 'for ('
159
                            while (ts.movePrevious()) {
160
                                token = ts.token();
161
                                switch (token.id()) {
162
                                    case WHITESPACE:
163
                                    case BLOCK_COMMENT:
164
                                    case JAVADOC_COMMENT:
165
                                    case LINE_COMMENT:
166
                                        break; // skip
167
                                    case FOR:
168
                                        return true;
169
                                    default:
170
                                        return false;
171
                                }
172
                            }
173
                            return false;
174
                        } else { // non-zero depth
175
                            parenDepth--;
176
                        }
177
                        break;
178
179
                    case RPAREN:
180
                        parenDepth++;
181
                        break;
182
183
                    case LBRACE:
184
                        if (braceDepth == 0) { // unclosed left brace
185
                            return false;
186
                        }
187
                        braceDepth--;
188
                        break;
189
190
                    case RBRACE:
191
                        braceDepth++;
192
                        break;
193
194
                    case SEMICOLON:
195
                        if (semicolonFound) { // one semicolon already found
196
                            return false;
197
                        }
198
                        semicolonFound = true;
199
                        break;
200
                }
201
            }
202
        } finally {
203
            // Restore orig TS's location
204
            ts.moveIndex(tsOrigIndex);
205
            ts.moveNext();
206
        }
207
        return false;
208
    }
209
210
    /**
211
     * Hook called after a character *ch* was backspace-deleted from
212
     * *doc*. The function possibly removes bracket or quote pair if
213
     * appropriate.
214
     * @param doc the document
215
     * @param caretOffset position of the change
216
     * @param caret caret
217
     * @param ch the character that was deleted
218
     */
219
    static void charBackspaced(BaseDocument doc, int caretOffset, Caret caret, char ch) throws BadLocationException {
220
        if (!completionSettingEnabled()) {
221
            return;
222
        }
223
        // Get token (forward bias) behind removed char
224
        TokenSequence<JavaTokenId> ts = javaTokenSequence(doc, caretOffset, false);
225
        if (ts == null) {
226
            return;
227
        }
228
        if (ch == '(' || ch == '[') {
229
            switch (ts.token().id()) {
230
                case RPAREN:
231
                    if (tokenBalance(doc, JavaTokenId.LPAREN) != 0) {
232
                        doc.remove(caretOffset, 1);
233
                    }
234
                    break;
235
                case RBRACKET:
236
                    if (tokenBalance(doc, JavaTokenId.LBRACKET) != 0) {
237
                        doc.remove(caretOffset, 1);
238
                    }
239
                    break;
240
            }
241
        } else if (ch == '\"') {
242
            if (ts.token().id() == JavaTokenId.STRING_LITERAL && ts.offset() == caretOffset) {
243
                doc.remove(caretOffset, 1);
244
            }
245
        } else if (ch == '\'') {
246
            if (ts.token().id() == JavaTokenId.CHAR_LITERAL && ts.offset() == caretOffset) {
247
                doc.remove(caretOffset, 1);
248
            }
249
        }
250
    }
251
252
    static int tokenBalance(BaseDocument doc, JavaTokenId leftTokenId) {
253
        TokenBalance tb = TokenBalance.get(doc);
254
        if (!tb.isTracked(JavaTokenId.language())) {
255
            tb.addTokenPair(JavaTokenId.language(), JavaTokenId.LPAREN, JavaTokenId.RPAREN);
256
            tb.addTokenPair(JavaTokenId.language(), JavaTokenId.LBRACKET, JavaTokenId.RBRACKET);
257
            tb.addTokenPair(JavaTokenId.language(), JavaTokenId.LBRACE, JavaTokenId.RBRACE);
258
        }
259
        int balance = tb.balance(JavaTokenId.language(), leftTokenId);
260
        assert (balance != Integer.MAX_VALUE);
261
        return balance;
262
    }
263
    /**
264
     * Resolve whether pairing right curly should be added automatically
265
     * at the caret position or not.
266
     * <br>
267
     * There must be only whitespace or line comment or block comment
268
     * between the caret position
269
     * and the left brace and the left brace must be on the same line
270
     * where the caret is located.
271
     * <br>
272
     * The caret must not be "contained" in the opened block comment token.
273
     *
274
     * @param doc document in which to operate.
275
     * @param caretOffset offset of the caret.
276
     * @return true if a right brace '}' should be added
277
     *  or false if not.
278
     */
279
    static boolean isAddRightBrace(BaseDocument doc, int caretOffset)
280
            throws BadLocationException
281
    {
282
        if (!completionSettingEnabled()) {
283
            return false;
284
        }
285
        if (tokenBalance(doc, JavaTokenId.LBRACE) <= 0) {
286
            return false;
287
        }
288
        int caretRowStartOffset = Utilities.getRowStart(doc, caretOffset);
289
        TokenSequence<JavaTokenId> ts = javaTokenSequence(doc, caretOffset, true);
290
        if (ts == null) {
291
            return false;
292
        }
293
        boolean first = true;
294
        do {
295
            if (ts.offset() < caretRowStartOffset) {
296
                return false;
297
            }
298
            switch (ts.token().id()) {
299
                case WHITESPACE:
300
                case LINE_COMMENT:
301
                    break;
302
                case BLOCK_COMMENT:
303
                case JAVADOC_COMMENT:
304
                    if (first && caretOffset > ts.offset() && caretOffset < ts.offset() + ts.token().length()) {
305
                        // Caret contained within block comment -> do not add anything
306
                        return false;
307
                    }
308
                    break; // Skip
309
                case LBRACE:
310
                    return true;
311
            }
312
            first = false;
313
        } while (ts.movePrevious());
314
        return false;
315
    }
316
317
    /**
318
     * Returns position of the first unpaired closing paren/brace/bracket from the caretOffset
319
     * till the end of caret row. If there is no such element, position after the last non-white
320
     * character on the caret row is returned.
321
     */
322
    static int getRowOrBlockEnd(BaseDocument doc, int caretOffset, boolean[] insert) throws BadLocationException {
323
        int rowEnd = Utilities.getRowLastNonWhite(doc, caretOffset);
324
        if (rowEnd == -1 || caretOffset >= rowEnd) {
325
            return caretOffset;
326
        }
327
        rowEnd += 1;
328
        int parenBalance = 0;
329
        int braceBalance = 0;
330
        int bracketBalance = 0;
331
        TokenSequence<JavaTokenId> ts = javaTokenSequence(doc, caretOffset, false);
332
        if (ts == null) {
333
            return caretOffset;
334
        }
335
        while (ts.offset() < rowEnd) {
336
            switch (ts.token().id()) {
337
                case SEMICOLON:
338
                    return ts.offset() + 1;
339
                case LPAREN:
340
                    parenBalance++;
341
                    break;
342
                case RPAREN:
343
                    if (parenBalance-- == 0) {
344
                        return ts.offset();
345
                    }
346
                    break;
347
                case LBRACE:
348
                    braceBalance++;
349
                    break;
350
                case RBRACE:
351
                    if (braceBalance-- == 0) {
352
                        return ts.offset();
353
                    }
354
                    break;
355
                case LBRACKET:
356
                    bracketBalance++;
357
                    break;
358
                case RBRACKET:
359
                    if (bracketBalance-- == 0) {
360
                        return ts.offset();
361
                    }
362
                    break;
363
            }
364
            if (!ts.moveNext())
365
                break;
366
        }
367
368
        insert[0] = false;
369
        return rowEnd;
370
    }
371
372
    /**
373
     * Get token sequence positioned over a token.
374
     *
375
     * @param doc
376
     * @param offset
377
     * @param backwardBias
378
     * @return token sequence positioned over a token that "contains" the offset
379
     *  or null if the document does not contain any java token sequence
380
     *  or the offset is at doc-or-section-start-and-bwd-bias
381
     *  or doc-or-section-end-and-fwd-bias.
382
     */
383
    private static TokenSequence<JavaTokenId> javaTokenSequence(Document doc, int offset, boolean backwardBias) {
384
        TokenHierarchy<?> hi = TokenHierarchy.get(doc);
385
        List<TokenSequence<?>> tsList = hi.embeddedTokenSequences(offset, backwardBias);
386
        // Go from inner to outer TSes
387
        for (int i = tsList.size() - 1; i >= 0; i--) {
388
            TokenSequence<?> ts = tsList.get(i);
389
            if (ts.languagePath().innerLanguage() == JavaTokenId.language()) {
390
                TokenSequence<JavaTokenId> javaInnerTS = (TokenSequence<JavaTokenId>) ts;
391
                return javaInnerTS;
392
            }
393
        }
394
        return null;
395
    }
396
397
    /** 
398
     * Counts the number of braces starting at caretOffset to the end of the
399
     * document. Every occurence of { increses the count by 1, every
400
     * occurrence of } decreses the count by 1. The result is returned.
401
     * @return The number of { - number of } (>0 more { than } ,<0 more } than {)
402
     */
403
    private static int braceBalance(BaseDocument doc)
404
            throws BadLocationException {
405
        return tokenBalance(doc, JavaTokenId.LBRACE);
406
    }
407
408
    /**
409
     * A hook to be called after closing bracket ) or ] was inserted into
410
     * the document. The method checks if the bracket should stay there
411
     * or be removed and some exisitng bracket just skipped.
412
     *
413
     * @param doc the document
414
     * @param caretOffset position of the inserted bracket
415
     * @param caret caret
416
     * @param bracket the bracket character ']' or ')'
417
     */
418
    private static void skipClosingBracket(BaseDocument doc, Caret caret, char rightBracketChar,
419
            TokenSequence<JavaTokenId> innerTS) throws BadLocationException
420
    {
421
        JavaTokenId bracketId = bracketCharToId(rightBracketChar);
422
        int caretOffset = caret.getDot();
423
        if (isSkipClosingBracket(doc, caretOffset, bracketId)) {
424
            doc.remove(caretOffset - 1, 1);
425
            caret.setDot(caretOffset); // skip closing bracket
426
        }
427
    }
428
429
    private static Set<JavaTokenId> STOP_TOKENS_FOR_SKIP_CLOSING_BRACKET = EnumSet.of(JavaTokenId.LBRACE, JavaTokenId.RBRACE, JavaTokenId.SEMICOLON);
430
    
431
    /**
432
     * Check whether the typed bracket should stay in the document
433
     * or be removed.
434
     * <br>
435
     * This method is called by <code>skipClosingBracket()</code>.
436
     *
437
     * @param doc document into which typing was done.
438
     * @param caretOffset
439
     */
440
    static boolean isSkipClosingBracket(BaseDocument doc, int caretOffset, JavaTokenId rightBracketId) throws BadLocationException {
441
        // First check whether the caret is not after the last char in the document
442
        // because no bracket would follow then so it could not be skipped.
443
        if (caretOffset == doc.getLength()) {
444
            return false; // no skip in this case
445
        }
446
        boolean skipClosingBracket = false; // by default do not remove
447
        // Examine token at the caret offset
448
        TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, caretOffset, false);
449
        if (javaTS != null && javaTS.token().id() == rightBracketId) {
450
            JavaTokenId leftBracketId = matching(rightBracketId);
451
            // Skip all the brackets of the same type that follow the last one
452
            do {
453
                if (   STOP_TOKENS_FOR_SKIP_CLOSING_BRACKET.contains(javaTS.token().id())
454
                    || (javaTS.token().id() == JavaTokenId.WHITESPACE && javaTS.token().text().toString().contains("\n"))) {
455
                    while (javaTS.token().id() != rightBracketId && javaTS.movePrevious())
456
                        ;
457
                    break;
458
                }
459
            } while (javaTS.moveNext());
460
            // token var points to the last bracket in a group of two or more right brackets
461
            // Attempt to find the left matching bracket for it
462
            // Search would stop on an extra opening left brace if found
463
            int braceBalance = 0; // balance of '{' and '}'
464
            int bracketBalance = -1; // balance of the brackets or parenthesis
465
            int numOfSemi = 0;
466
            boolean finished = false;
467
            while (!finished && javaTS.movePrevious()) {
468
                JavaTokenId id = javaTS.token().id();
469
                switch (id) {
470
                    case LPAREN:
471
                    case LBRACKET:
472
                        if (id == leftBracketId) {
473
                            bracketBalance++;
474
                            if (bracketBalance == 0) {
475
                                if (braceBalance != 0) {
476
                                    // Here the bracket is matched but it is located
477
                                    // inside an unclosed brace block
478
                                    // e.g. ... ->( } a()|)
479
                                    // which is in fact illegal but it's a question
480
                                    // of what's best to do in this case.
481
                                    // We chose to leave the typed bracket
482
                                    // by setting bracketBalance to 1.
483
                                    // It can be revised in the future.
484
                                    bracketBalance = 1;
485
                                }
486
                                finished = javaTS.offset() < caretOffset;
487
                            }
488
                        }
489
                        break;
490
491
                    case RPAREN:
492
                    case RBRACKET:
493
                        if (id == rightBracketId) {
494
                            bracketBalance--;
495
                        }
496
                        break;
497
498
                    case LBRACE:
499
                        braceBalance++;
500
                        if (braceBalance > 0) { // stop on extra left brace
501
                            finished = true;
502
                        }
503
                        break;
504
505
                    case RBRACE:
506
                        braceBalance--;
507
                        break;
508
                        
509
                    case SEMICOLON:
510
                        numOfSemi++;
511
                        break;
512
                }
513
            }
514
515
            if (bracketBalance == 0 && numOfSemi < 2) {
516
                finished = false;
517
                while (!finished && javaTS.movePrevious()) {
518
                    switch (javaTS.token().id()) {
519
                        case WHITESPACE:
520
                        case LINE_COMMENT:
521
                        case BLOCK_COMMENT:
522
                        case JAVADOC_COMMENT:
523
                            break;
524
                        case FOR:
525
                            bracketBalance--;
526
                        default:
527
                            finished = true;
528
                            break;
529
                    }
530
                }
531
            }
532
            
533
            skipClosingBracket = bracketBalance != 0;
534
535
            //commented out due to #147683:
536
//            if (bracketBalance != 0) { // not found matching bracket
537
//                // Remove the typed bracket as it's unmatched
538
//                skipClosingBracket = true;
539
//
540
//            } else { // the bracket is matched
541
//                // Now check whether the bracket would be matched
542
//                // when the closing bracket would be removed
543
//                // i.e. starting from the original lastRBracketIndex token
544
//                // and search for the same bracket to the right in the text
545
//                // The search would stop on an extra right brace if found
546
//                braceBalance = 0;
547
//                bracketBalance = 1; // simulate one extra left bracket
548
//                finished = false;
549
//                // Relocate behind original rbracket and one token to right
550
//                if (lastRBracketIndex + 2 <= javaTS.tokenCount()) {
551
//                    javaTS.moveIndex(lastRBracketIndex + 2);
552
//                } else { // mark as finished
553
//                    finished = true;
554
//                }
555
//                while (!finished && javaTS.movePrevious()) {
556
//                    JavaTokenId id = javaTS.token().id();
557
//                    switch (id) {
558
//                        case LPAREN:
559
//                        case LBRACKET:
560
//                            if (id == leftBracketId) {
561
//                                bracketBalance++;
562
//                            }
563
//                            break;
564
//
565
//                        case RPAREN:
566
//                        case RBRACKET:
567
//                            if (id == rightBracketId) {
568
//                                bracketBalance--;
569
//                                if (bracketBalance == 0) {
570
//                                    if (braceBalance != 0) {
571
//                                        // Here the bracket is matched but it is located
572
//                                        // inside an unclosed brace block
573
//                                        // which is in fact illegal but it's a question
574
//                                        // of what's best to do in this case.
575
//                                        // We chose to leave the typed bracket
576
//                                        // by setting bracketBalance to -1.
577
//                                        // It can be revised in the future.
578
//                                        bracketBalance = -1;
579
//                                    }
580
//                                    finished = true;
581
//                                }
582
//                            }
583
//                            break;
584
//
585
//                        case LBRACE:
586
//                            braceBalance++;
587
//                            break;
588
//
589
//                        case RBRACE:
590
//                            braceBalance--;
591
//                            if (braceBalance < 0) { // stop on extra right brace
592
//                                finished = true;
593
//                            }
594
//                            break;
595
//                    }
596
//                }
597
//                // If bracketBalance == 0 the bracket would be matched
598
//                // by the bracket that follows the last right bracket.
599
//                skipClosingBracket = (bracketBalance == 0);
600
//            }
601
        }
602
        return skipClosingBracket;
603
    }
604
605
    /**
606
     * Check for various conditions and possibly add a pairing bracket.
607
     * to the already inserted.
608
     * @param doc the document
609
     * @param caretOffset position of the opening bracket (already in the doc)
610
     * @param caret caret
611
     * @param bracket the bracket that was inserted
612
     */
613
    private static void completeOpeningBracket(BaseDocument doc, int caretOffset, Caret caret, char bracketChar,
614
        TokenSequence<JavaTokenId> innerTS) throws BadLocationException
615
    {
616
        if (isCompletablePosition(doc, caretOffset + 1)) {
617
            doc.insertString(caretOffset + 1, String.valueOf(matching(bracketChar)), null);
618
            caret.setDot(caretOffset + 1);
619
        }
620
    }
621
622
    private static boolean isEscapeSequence(BaseDocument doc, int caretOffset) throws BadLocationException {
623
        if (caretOffset <= 0) {
624
            return false;
625
        }
626
627
        for (int i = 2; caretOffset - i >= 0; i += 2) {
628
            char[] previousChars = doc.getChars(caretOffset - i, 2);
629
            if (previousChars[1] != '\\')
630
                return false;
631
            if (previousChars[0] != '\\')
632
                return true;
633
        }
634
635
        char previousChar = doc.getChars(caretOffset - 1, 1)[0];
636
        return previousChar == '\\';
637
    }
638
639
    /** 
640
     * Called to decide whether either single bracket should be inserted
641
     * or whether bracket pair should be inserted instead.
642
     * It's called before anything was inserted into the document.
643
     *
644
     * @param doc the document
645
     * @param caretOffset position of the opening bracket (already in the doc)
646
     * @param caret caret
647
     * @param bracket the character that was inserted
648
     * @return true if the method serviced insertions or removals or false
649
     *  if there's nothing special to be done and the parent should handle the insertion regularly.
650
     */
651
    static boolean completeQuote(BaseDocument doc, int caretOffset, Caret caret, char bracket)
652
        throws BadLocationException
653
    {
654
        if (!completionSettingEnabled()) {
655
            return false;
656
        }
657
        if (isEscapeSequence(doc, caretOffset)) { // \" or \' typed
658
            return false;
659
        }
660
        // Examine token id at the caret offset
661
        TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, caretOffset, true);
662
        JavaTokenId id = (javaTS != null) ? javaTS.token().id() : null;
663
        int lastNonWhite = Utilities.getRowLastNonWhite(doc, caretOffset);
664
        // eol - true if the caret is at the end of line (ignoring whitespaces)
665
        boolean eol = lastNonWhite < caretOffset;
666
667
        // If caret within comment return false
668
        boolean caretInsideToken = (id != null) &&
669
                (javaTS.offset() + javaTS.token().length() > caretOffset ||
670
                 javaTS.token().partType() == PartType.START);
671
        if (caretInsideToken && (id == JavaTokenId.BLOCK_COMMENT ||
672
                id == JavaTokenId.JAVADOC_COMMENT ||
673
                id == JavaTokenId.LINE_COMMENT)
674
        ) {
675
            return false;
676
        }
677
        boolean completablePosition = isQuoteCompletablePosition(doc, caretOffset);
678
        boolean insideString = caretInsideToken &&
679
                (id == JavaTokenId.STRING_LITERAL ||
680
                 id == JavaTokenId.CHAR_LITERAL);
681
        if (insideString) {
682
            if (eol) {
683
                return false; // do not complete
684
            } else {
685
                //#69524
686
                char chr = doc.getChars(caretOffset, 1)[0];
687
                if (chr == bracket) {
688
                    //#83044
689
                    if (caretOffset > 0) {
690
                        javaTS.move(caretOffset - 1);
691
                        if (javaTS.moveNext()) {
692
                            id = javaTS.token().id();
693
                            if (id == JavaTokenId.STRING_LITERAL ||
694
                                id == JavaTokenId.CHAR_LITERAL)
695
                            {
696
                                doc.insertString(caretOffset, String.valueOf(bracket), null); //NOI18N
697
                                doc.remove(caretOffset, 1);
698
                                return true;
699
                            }
700
                        }
701
                    }
702
                //end of #83044
703
                }
704
            //end of #69524
705
            }
706
        }
707
708
        if ((completablePosition && !insideString) || eol) {
709
            doc.insertString(caretOffset, String.valueOf(bracket) + bracket, null); //NOI18N
710
            return true;
711
        }
712
713
        return false;
714
    }
715
716
    /** 
717
     * Checks whether caretOffset is a position at which bracket and quote
718
     * completion is performed. Brackets and quotes are not completed
719
     * everywhere but just at suitable places .
720
     * @param doc the document
721
     * @param caretOffset position to be tested
722
     */
723
    private static boolean isCompletablePosition(BaseDocument doc, int caretOffset)
724
            throws BadLocationException
725
    {
726
        if (caretOffset == doc.getLength()) // there's no other character to test
727
        {
728
            return true;
729
        } else {
730
            // test that we are in front of ) , " or '
731
            char chr = doc.getChars(caretOffset, 1)[0];
732
            return (chr == ')' ||
733
                    chr == ',' ||
734
                    chr == '\"' ||
735
                    chr == '\'' ||
736
                    chr == ' ' ||
737
                    chr == ']' ||
738
                    chr == '}' ||
739
                    chr == '\n' ||
740
                    chr == '\t' ||
741
                    chr == ';');
742
        }
743
    }
744
745
    private static boolean isQuoteCompletablePosition(BaseDocument doc, int caretOffset)
746
            throws BadLocationException
747
    {
748
        if (caretOffset == doc.getLength()) { // there's no other character to test
749
            return true;
750
        } else {
751
            // test that we are in front of ) , " or ' ... etc.
752
            int eolOffset = Utilities.getRowEnd(doc, caretOffset);
753
            if (caretOffset == eolOffset || eolOffset == -1) {
754
                return false;
755
            }
756
            int firstNonWhiteFwdOffset = Utilities.getFirstNonWhiteFwd(doc, caretOffset, eolOffset);
757
            if (firstNonWhiteFwdOffset == -1) {
758
                return false;
759
            }
760
            char chr = doc.getChars(firstNonWhiteFwdOffset, 1)[0];
761
            return (chr == ')' ||
762
                    chr == ',' ||
763
                    chr == '+' ||
764
                    chr == '}' ||
765
                    chr == ';');
766
        }
767
    }
768
769
    /** 
770
     * Returns true if bracket completion is enabled in options.
771
     */
772
    static boolean completionSettingEnabled() {
773
        Preferences prefs = MimeLookup.getLookup(JavaKit.JAVA_MIME_TYPE).lookup(Preferences.class);
774
        return prefs.getBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, false);
775
    }
776
777
    /**
778
     * Returns for an opening bracket or quote the appropriate closing
779
     * character.
780
     */
781
    private static char matching(char bracket) {
782
        switch (bracket) {
783
            case '(':
784
                return ')';
785
            case '[':
786
                return ']';
787
            case '\"':
788
                return '\"'; // NOI18N
789
            case '\'':
790
                return '\'';
791
            default:
792
                return ' ';
793
        }
794
    }
795
796
    private static JavaTokenId matching(JavaTokenId id) {
797
        switch (id) {
798
            case LPAREN:
799
                return JavaTokenId.RPAREN;
800
            case LBRACKET:
801
                return JavaTokenId.RBRACKET;
802
            case RPAREN:
803
                return JavaTokenId.LPAREN;
804
            case RBRACKET:
805
                return JavaTokenId.LBRACKET;
806
            default:
807
                return null;
808
        }
809
    }
810
811
    private static JavaTokenId bracketCharToId(char bracket) {
812
        switch (bracket) {
813
            case '(':
814
                return JavaTokenId.LPAREN;
815
            case ')':
816
                return JavaTokenId.RPAREN;
817
            case '[':
818
                return JavaTokenId.LBRACKET;
819
            case ']':
820
                return JavaTokenId.RBRACKET;
821
            case '{':
822
                return JavaTokenId.LBRACE;
823
            case '}':
824
                return JavaTokenId.RBRACE;
825
            default:
826
                throw new IllegalArgumentException("Not a bracket char '" + bracket + '\'');
827
        }
828
    }
829
830
    /**
831
     * posWithinString(doc, pos) iff position *pos* is within a string
832
     * literal in document doc.
833
     * @param doc the document
834
     * @param caretOffset position to be tested (before '\n' gets inserted into doc.
835
     */
836
    static boolean posWithinString(Document doc, int caretOffset) {
837
        return posWithinQuotes(doc, caretOffset, '"', JavaTokenId.STRING_LITERAL);
838
    }
839
840
    /**
841
     * Generalized posWithingString to any token and delimiting
842
     * character. It works for tokens are delimited by *quote* and
843
     * extend up to the other *quote* or whitespace in case of an
844
     * incomplete token.
845
     * @param doc the document
846
     * @param caretOffset position of typed quote
847
     */
848
    static boolean posWithinQuotes(Document doc, int caretOffset, char quote, JavaTokenId tokenId) {
849
        TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, caretOffset, false);
850
        if (javaTS != null) {
851
            if (javaTS.token().id() != tokenId) return false;
852
            if (caretOffset > javaTS.offset() && caretOffset < javaTS.offset() + javaTS.token().length()) return true;
853
            else return false;
854
        }
855
        return false;
856
    }
857
858
    static boolean posWithinAnyQuote(BaseDocument doc, int caretOffset) {
859
        TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, caretOffset - 1, false);
860
        if (javaTS != null) {
861
            JavaTokenId id = javaTS.token().id();
862
            if (id == JavaTokenId.STRING_LITERAL ||
863
                id == JavaTokenId.CHAR_LITERAL
864
            ) {
865
                char ch = DocumentUtilities.getText(doc).charAt(caretOffset - 1);
866
                return (caretOffset - javaTS.offset() == 1 || (ch != '"' && ch != '\''));
867
            }
868
        }
869
        return false;
870
    }
871
872
    static boolean isUnclosedStringAtLineEnd(BaseDocument doc, int caretOffset) {
873
        int lastNonWhiteOffset;
874
        try {
875
            lastNonWhiteOffset = Utilities.getRowLastNonWhite(doc, caretOffset);
876
        } catch (BadLocationException e) {
877
            return false;
878
        }
879
        TokenSequence<JavaTokenId> javaTS = javaTokenSequence(doc, lastNonWhiteOffset, true);
880
        if (javaTS != null) {
881
            return (javaTS.token().id() == JavaTokenId.STRING_LITERAL);
882
        }
883
        return false;
884
    }
885
886
    static boolean blockCommentCompletion(JTextComponent target, BaseDocument doc, final int dotPosition) {
887
        return blockCommentCompletionImpl(target, doc, dotPosition, false);
888
    }
889
890
    static boolean javadocBlockCompletion(JTextComponent target, BaseDocument doc, final int dotPosition) {
891
        return blockCommentCompletionImpl(target, doc, dotPosition, true);
892
    }
893
894
    private static boolean blockCommentCompletionImpl(JTextComponent target, BaseDocument doc, final int dotPosition, boolean javadoc) {
895
        try {
896
            TokenHierarchy<BaseDocument> tokens = TokenHierarchy.get(doc);
897
            TokenSequence ts = tokens.tokenSequence();
898
            if (ts == null) {
899
                return false;
900
            }
901
            ts.move(dotPosition);
902
            if (!((ts.moveNext() || ts.movePrevious()) && ts.token().id() == (javadoc ? JavaTokenId.JAVADOC_COMMENT : JavaTokenId.BLOCK_COMMENT))) {
903
                return false;
904
            }
905
906
            int jdoffset = dotPosition - (javadoc ? 3 : 2);
907
            if (jdoffset >= 0) {
908
                CharSequence content = org.netbeans.lib.editor.util.swing.DocumentUtilities.getText(doc);
909
                if (isOpenBlockComment(content, dotPosition - 1, javadoc) && !isClosedBlockComment(content, dotPosition) && isAtRowEnd(content, dotPosition)) {
910
                    // note that the formater will add one line of javadoc
911
                    doc.insertString(dotPosition, "*/", null); // NOI18N
912
                    Indent.get(doc).indentNewLine(dotPosition);
913
                    target.setCaretPosition(dotPosition);
914
915
                    return true;
916
                }
917
            }
918
        } catch (BadLocationException ex) {
919
            // ignore
920
            Exceptions.printStackTrace(ex);
921
        }
922
        return false;
923
    }
924
925
    private static boolean isOpenBlockComment(CharSequence content, int pos, boolean javadoc) {
926
        for (int i = pos; i >= 0; i--) {
927
            char c = content.charAt(i);
928
            if (c == '*' && (javadoc ? i - 2 >= 0 && content.charAt(i - 1) == '*' && content.charAt(i - 2) == '/' : i - 1 >= 0 && content.charAt(i - 1) == '/')) {
929
                // matched /*
930
                return true;
931
            } else if (c == '\n') {
932
                // no javadoc, matched start of line
933
                return false;
934
            } else if (c == '/' && i - 1 >= 0 && content.charAt(i - 1) == '*') {
935
                // matched javadoc enclosing tag
936
                return false;
937
            }
938
        }
939
940
        return false;
941
    }
942
943
    private static boolean isClosedBlockComment(CharSequence txt, int pos) {
944
        int length = txt.length();
945
        int quotation = 0;
946
        for (int i = pos; i < length; i++) {
947
            char c = txt.charAt(i);
948
            if (c == '*' && i < length - 1 && txt.charAt(i + 1) == '/') {
949
                if (quotation == 0 || i < length - 2) {
950
                    return true;
951
                }
952
                // guess it is not just part of some text constant
953
                boolean isClosed = true;
954
                for (int j = i + 2; j < length; j++) {
955
                    char cc = txt.charAt(j);
956
                    if (cc == '\n') {
957
                        break;
958
                    } else if (cc == '"' && j < length - 1 && txt.charAt(j + 1) != '\'') {
959
                        isClosed = false;
960
                        break;
961
                    }
962
                }
963
964
                if (isClosed) {
965
                    return true;
966
                }
967
            } else if (c == '/' && i < length - 1 && txt.charAt(i + 1) == '*') {
968
                // start of another comment block
969
                return false;
970
            } else if (c == '\n') {
971
                quotation = 0;
972
            } else if (c == '"' && i < length - 1 && txt.charAt(i + 1) != '\'') {
973
                quotation = ++quotation % 2;
974
            }
975
        }
976
977
        return false;
978
    }
979
980
    private static boolean isAtRowEnd(CharSequence txt, int pos) {
981
        int length = txt.length();
982
        for (int i = pos; i < length; i++) {
983
            char c = txt.charAt(i);
984
            if (c == '\n') {
985
                return true;
986
            }
987
            if (!Character.isWhitespace(c)) {
988
                return false;
989
            }
990
        }
991
        return true;
992
    }
993
}
(-)a/java.editor/src/org/netbeans/modules/editor/java/JavaKit.java (-180 / +1 lines)
Lines 307-381 Link Here
307
        super.install(c);
307
        super.install(c);
308
        ClipboardHandler.install(c);
308
        ClipboardHandler.install(c);
309
    }
309
    }
310
    
311
    /**
312
     * @Deprecated This action is no longer used. It is reimplemented as JavaDefaultKeyTypedInterceptor.
313
     */
314
    @Deprecated
315
    public static class JavaDefaultKeyTypedAction extends ExtDefaultKeyTypedAction {
316
317
        @Override
318
        protected void insertString(BaseDocument doc, int dotPos,
319
                                    Caret caret, String str,
320
                                    boolean overwrite) throws BadLocationException {
321
            char insertedChar = str.charAt(0);
322
            if (insertedChar == '\"' || insertedChar == '\''){
323
                boolean inserted = BraceCompletion.completeQuote(doc, dotPos, caret, insertedChar);
324
                if (inserted){
325
                    caret.setDot(dotPos+1);
326
                }else{
327
                    super.insertString(doc, dotPos, caret, str, overwrite);
328
329
                }
330
            } else {
331
                super.insertString(doc, dotPos, caret, str, overwrite);
332
                BraceCompletion.charInserted(doc, dotPos, caret, insertedChar);
333
            }
334
        }
335
336
        protected void replaceSelection(JTextComponent target,
337
                int dotPos,
338
                Caret caret,
339
                String str,
340
                boolean overwrite)
341
                throws BadLocationException {
342
            char insertedChar = str.charAt(0);
343
            Document doc = target.getDocument();
344
            if (insertedChar == '\"' || insertedChar == '\''){
345
                if (doc != null) {
346
                    try {
347
                        boolean inserted = false;
348
                        int p0 = Math.min(caret.getDot(), caret.getMark());
349
                        int p1 = Math.max(caret.getDot(), caret.getMark());
350
                        if (p0 != p1) {
351
                            doc.remove(p0, p1 - p0);
352
                        }
353
                        int caretPosition = caret.getDot();
354
                        if (doc instanceof BaseDocument){
355
                            inserted = BraceCompletion.completeQuote(
356
                                    (BaseDocument)doc,
357
                                    caretPosition,
358
                                    caret, insertedChar);
359
                        }
360
                        if (inserted){
361
                            caret.setDot(caretPosition+1);
362
                        } else {
363
                            if (str != null && str.length() > 0) {
364
                                doc.insertString(p0, str, null);
365
                            }
366
                        }
367
                    } catch (BadLocationException e) {
368
                        e.printStackTrace();
369
                    }
370
                }
371
            } else {
372
                super.replaceSelection(target, dotPos, caret, str, overwrite);
373
                if (doc instanceof BaseDocument){
374
                    BraceCompletion.charInserted((BaseDocument)doc, caret.getDot()-1, caret, insertedChar);
375
                }
376
            }
377
        }
378
    }
379
310
380
    @EditorActionRegistration(name = generateGoToPopupAction, mimeType = JAVA_MIME_TYPE)
311
    @EditorActionRegistration(name = generateGoToPopupAction, mimeType = JAVA_MIME_TYPE)
381
    public static class JavaGenerateGoToPopupAction extends NbGenerateGoToPopupAction {
312
    public static class JavaGenerateGoToPopupAction extends NbGenerateGoToPopupAction {
Lines 705-821 Link Here
705
            }
636
            }
706
        }
637
        }
707
    }
638
    }
708
639
    
709
    /**
710
     * @Deprecated This action is no longer used. It is reimplemented as JavaTypedBreakInterceptor.
711
     */
712
    @Deprecated
713
    public static class JavaInsertBreakAction extends InsertBreakAction {
714
715
        static final long serialVersionUID = -1506173310438326380L;
716
        private boolean isJavadocTouched = false;
717
718
        @Override
719
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
720
            try {
721
                super.actionPerformed(evt, target);
722
                Document doc = target.getDocument();
723
                if (isJavadocTouched && !org.netbeans.lib.editor.util.swing.DocumentUtilities.isWriteLocked(doc)) {
724
                    // XXX temporary solution until the editor will provide a SPI to plug. See issue #115739
725
                    // This must run outside the document lock
726
                    Lookup.Result<TextAction> res = MimeLookup.getLookup(MimePath.parse("text/x-javadoc")).lookupResult(TextAction.class);
727
                    ActionEvent newevt = new ActionEvent(target, ActionEvent.ACTION_PERFORMED, "fix-javadoc");
728
                    for (TextAction action : res.allInstances()) {
729
                        action.actionPerformed(newevt);
730
                    }
731
                }
732
            } finally {
733
                isJavadocTouched = false;
734
            }
735
        }
736
737
        @Override
738
        protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) {
739
            int dotPos = caret.getDot();
740
            if (BraceCompletion.posWithinString(doc, dotPos)) {
741
                try {
742
                    doc.insertString(dotPos, "\" + \"", null); //NOI18N
743
                    CodeStyle cs = CodeStyle.getDefault(doc);
744
                    if (cs.wrapAfterBinaryOps()) {
745
                        dotPos += 3;
746
                        caret.setDot(dotPos);
747
                        return new Integer(1);
748
                    } else {
749
                        dotPos += 1;
750
                        caret.setDot(dotPos);
751
                        return new Integer(3);
752
                    }
753
                } catch (BadLocationException ex) {
754
                }
755
            } else {
756
                try {
757
                    if (BraceCompletion.isAddRightBrace(doc, dotPos)) {
758
                        boolean insert[] = {true};
759
                        int end = BraceCompletion.getRowOrBlockEnd(doc, dotPos, insert);
760
                        if (insert[0]) {
761
                            doc.insertString(end, "}", null); // NOI18N
762
                            Indent.get(doc).indentNewLine(end);
763
                        }
764
                        caret.setDot(dotPos);
765
                        return Boolean.TRUE;
766
                    }
767
                } catch (BadLocationException ex) {
768
                }
769
            }
770
            BraceCompletion.blockCommentCompletion(target, doc, dotPos);
771
            isJavadocTouched = BraceCompletion.javadocBlockCompletion(target, doc, dotPos);
772
            return null;
773
        }
774
775
        @Override
776
        protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret, Object cookie) {
777
            if (cookie != null) {
778
                if (cookie instanceof Integer) {
779
                    // integer
780
                    int nowDotPos = caret.getDot();
781
                    caret.setDot(nowDotPos+((Integer)cookie).intValue());
782
                }
783
            }
784
        }
785
    }
786
787
    /**
788
     * @Deprecated This action is no longer used. It is reimplemented as JavaDeleteCharInterceptor.
789
     */
790
    @Deprecated
791
    public static class JavaDeleteCharAction extends ExtDeleteCharAction {
792
793
        public JavaDeleteCharAction(String nm, boolean nextChar) {
794
            super(nm, nextChar);
795
        }
796
797
        @Override
798
        protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch)
799
        throws BadLocationException {
800
            BraceCompletion.charBackspaced(doc, dotPos, caret, ch);
801
        }
802
803
        @Override
804
        public void actionPerformed(ActionEvent evt, JTextComponent target) {
805
            target.putClientProperty(JavaDeleteCharAction.class, this);
806
807
            try {
808
                super.actionPerformed(evt, target);
809
            } finally {
810
                target.putClientProperty(JavaDeleteCharAction.class, null);
811
            }
812
        }
813
814
        public boolean getNextChar() {
815
            return nextChar;
816
        }
817
    }
818
819
    @EditorActionRegistration(
640
    @EditorActionRegistration(
820
            name = expandAllJavadocFolds,
641
            name = expandAllJavadocFolds,
821
            mimeType = JAVA_MIME_TYPE,
642
            mimeType = JAVA_MIME_TYPE,
(-)a/javascript2.editor/manifest.mf (-1 / +1 lines)
Lines 3-8 Link Here
3
OpenIDE-Module-Install: org/netbeans/modules/javascript2/editor/ModuleInstaller.class
3
OpenIDE-Module-Install: org/netbeans/modules/javascript2/editor/ModuleInstaller.class
4
OpenIDE-Module-Layer: org/netbeans/modules/javascript2/editor/resources/layer.xml
4
OpenIDE-Module-Layer: org/netbeans/modules/javascript2/editor/resources/layer.xml
5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/javascript2/editor/Bundle.properties
5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/javascript2/editor/Bundle.properties
6
OpenIDE-Module-Specification-Version: 0.3
6
OpenIDE-Module-Specification-Version: 0.4
7
AutoUpdate-Show-In-Client: false
7
AutoUpdate-Show-In-Client: false
8
8
(-)a/javascript2.editor/nbproject/project.xml (-1 / +1 lines)
Lines 47-53 Link Here
47
                    <compile-dependency/>
47
                    <compile-dependency/>
48
                    <run-dependency>
48
                    <run-dependency>
49
                        <release-version>2</release-version>
49
                        <release-version>2</release-version>
50
                        <specification-version>2.25</specification-version>
50
                        <specification-version>2.34</specification-version>
51
                    </run-dependency>
51
                    </run-dependency>
52
                </dependency>
52
                </dependency>
53
                <dependency>
53
                <dependency>
(-)a/javascript2.editor/src/org/netbeans/modules/javascript2/editor/JsDeleteWordHandler.java (+195 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.javascript2.editor;
43
44
import javax.swing.text.Document;
45
import org.netbeans.api.editor.mimelookup.MimeRegistration;
46
import org.netbeans.api.lexer.Token;
47
import org.netbeans.api.lexer.TokenId;
48
import org.netbeans.api.lexer.TokenSequence;
49
import org.netbeans.editor.BaseDocument;
50
import org.netbeans.modules.csl.api.DeleteWordHandler;
51
import org.netbeans.modules.javascript2.editor.lexer.JsTokenId;
52
import org.netbeans.modules.javascript2.editor.lexer.LexUtilities;
53
54
@MimeRegistration(mimeType = JsTokenId.JAVASCRIPT_MIME_TYPE, service = DeleteWordHandler.class)
55
public class JsDeleteWordHandler implements DeleteWordHandler {
56
57
    @Override
58
    public int getNextWordOffset(Document doc, int caretOffset) {
59
        return getNextWordOffset(doc, caretOffset, false);
60
    }
61
62
    @Override
63
    public int getPreviousWordOffset(Document doc, int caretOffset) {
64
        return getNextWordOffset(doc, caretOffset, true);
65
    }
66
    
67
    // UGH - this method has gotten really ugly after successive refinements based on unit tests - consider cleaning up
68
    public int getNextWordOffset(Document document, int offset, boolean reverse) {
69
        BaseDocument doc = (BaseDocument)document;
70
        TokenSequence<?extends JsTokenId> ts = LexUtilities.getJsTokenSequence(doc, offset);
71
        if (ts == null) {
72
            return -1;
73
        }
74
        ts.move(offset);
75
        if (!ts.moveNext() && !ts.movePrevious()) {
76
            return -1;
77
        }
78
        if (reverse && ts.offset() == offset) {
79
            if (!ts.movePrevious()) {
80
                return -1;
81
            }
82
        }
83
84
        Token<? extends JsTokenId> token = ts.token();
85
        TokenId id = token.id();
86
87
        if (id == JsTokenId.WHITESPACE) {
88
            // Just eat up the space in the normal IDE way
89
            if ((reverse && ts.offset() < offset) || (!reverse && ts.offset() > offset)) {
90
                return ts.offset();
91
            }
92
            while (id == JsTokenId.WHITESPACE) {
93
                if (reverse && !ts.movePrevious()) {
94
                    return -1;
95
                } else if (!reverse && !ts.moveNext()) {
96
                    return -1;
97
                }
98
99
                token = ts.token();
100
                id = token.id();
101
            }
102
            if (reverse) {
103
                int start = ts.offset()+token.length();
104
                if (start < offset) {
105
                    return start;
106
                }
107
            } else {
108
                int start = ts.offset();
109
                if (start > offset) {
110
                    return start;
111
                }
112
            }
113
            
114
        }
115
116
        if (id == JsTokenId.IDENTIFIER) {
117
            String s = token.text().toString();
118
            int length = s.length();
119
            int wordOffset = offset-ts.offset();
120
            if (reverse) {
121
                // Find previous
122
                int offsetInImage = offset - 1 - ts.offset(); 
123
                if (offsetInImage < 0) {
124
                    return -1;
125
                }
126
                if (offsetInImage < length && Character.isUpperCase(s.charAt(offsetInImage))) {
127
                    for (int i = offsetInImage - 1; i >= 0; i--) {
128
                        char charAtI = s.charAt(i);
129
                        if (charAtI == '_') {
130
                            // return offset of previous uppercase char in the identifier
131
                            return ts.offset() + i + 1;
132
                        } else if (!Character.isUpperCase(charAtI)) {
133
                            // return offset of previous uppercase char in the identifier
134
                            return ts.offset() + i + 1;
135
                        }
136
                    }
137
                    return ts.offset();
138
                } else {
139
                    for (int i = offsetInImage - 1; i >= 0; i--) {
140
                        char charAtI = s.charAt(i);
141
                        if (charAtI == '_') {
142
                            return ts.offset() + i + 1;
143
                        }
144
                        if (Character.isUpperCase(charAtI)) {
145
                            // now skip over previous uppercase chars in the identifier
146
                            for (int j = i; j >= 0; j--) {
147
                                char charAtJ = s.charAt(j);
148
                                if (charAtJ == '_') {
149
                                    return ts.offset() + j+1;
150
                                }
151
                                if (!Character.isUpperCase(charAtJ)) {
152
                                    // return offset of previous uppercase char in the identifier
153
                                    return ts.offset() + j + 1;
154
                                }
155
                            }
156
                            return ts.offset();
157
                        }
158
                    }
159
                    
160
                    return ts.offset();
161
                }
162
            } else {
163
                // Find next
164
                int start = wordOffset+1;
165
                if (wordOffset < 0 || wordOffset >= s.length()) {
166
                    // Probably the end of a token sequence, such as this:
167
                    // <%s|%>
168
                    return -1;
169
                }
170
                if (Character.isUpperCase(s.charAt(wordOffset))) { 
171
                    // if starting from a Uppercase char, first skip over follwing upper case chars
172
                    for (int i = start; i < length; i++) {
173
                        char charAtI = s.charAt(i);
174
                        if (!Character.isUpperCase(charAtI)) {
175
                            break;
176
                        }
177
                        if (s.charAt(i) == '_') {
178
                            return ts.offset()+i;
179
                        }
180
                        start++;
181
                    }
182
                }
183
                for (int i = start; i < length; i++) {
184
                    char charAtI = s.charAt(i);
185
                    if (charAtI == '_' || Character.isUpperCase(charAtI)) {
186
                        return ts.offset()+i;
187
                    }
188
                }
189
            }
190
        }
191
        
192
        // Default handling in the IDE
193
        return -1;
194
    }
195
}
(-)a/javascript2.editor/src/org/netbeans/modules/javascript2/editor/resources/layer.xml (+4 lines)
Lines 8-13 Link Here
8
        <folder name="text">
8
        <folder name="text">
9
            <folder name="javascript">
9
            <folder name="javascript">
10
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.javascript2.editor.resources.Bundle"/>
10
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.javascript2.editor.resources.Bundle"/>
11
                <file name="org-netbeans-modules-javascript2-editor-JsDeleteWordHandler.instance">
12
                    <attr name="instanceOf" stringvalue="org.netbeans.modules.csl.api.DeleteWordPositionHandler"/>
13
                </file>
14
                <file name="org-netbeans-modules-javascript2-editor-JsDeleteWordPositionHandler.instance_hidden"/>
11
                <file name="org-netbeans-modules-javascript2-editor-index-JsIndexer$Factory.instance">
15
                <file name="org-netbeans-modules-javascript2-editor-index-JsIndexer$Factory.instance">
12
                    <attr name="instanceOf" stringvalue="org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexerFactory"/>
16
                    <attr name="instanceOf" stringvalue="org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexerFactory"/>
13
                </file>
17
                </file>
(-)a/java.editor/test/unit/src/org/netbeans/modules/editor/java/TypingCompletionUnitTest.java (-1308 / +31 lines)
Lines 42-77 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.editor.java;
45
package org.netbeans.modules.php.editor;
46
46
47
import java.awt.EventQueue;
47
import org.netbeans.modules.csl.editor.TypingCompletion.Context;
48
import java.awt.event.KeyEvent;
49
import java.util.prefs.Preferences;
48
import java.util.prefs.Preferences;
50
import java.util.regex.Pattern;
51
import javax.swing.JEditorPane;
52
import javax.swing.SwingUtilities;
53
import javax.swing.text.BadLocationException;
54
import javax.swing.text.Document;
55
import javax.swing.text.EditorKit;
56
import javax.swing.text.PlainDocument;
57
import junit.framework.TestCase;
58
import org.netbeans.api.editor.mimelookup.MimeLookup;
49
import org.netbeans.api.editor.mimelookup.MimeLookup;
59
import org.netbeans.api.editor.settings.SimpleValueNames;
50
import org.netbeans.api.editor.settings.SimpleValueNames;
60
import org.netbeans.api.java.lexer.JavaTokenId;
61
import org.netbeans.api.lexer.Language;
62
import org.netbeans.junit.NbTestCase;
51
import org.netbeans.junit.NbTestCase;
63
import org.netbeans.lib.editor.util.CharSequenceUtilities;
52
import org.netbeans.modules.csl.core.CslEditorKit;
53
import org.netbeans.modules.php.api.util.FileUtils;
54
import org.netbeans.modules.php.editor.lexer.PHPTokenId;
64
import org.openide.awt.AcceleratorBinding;
55
import org.openide.awt.AcceleratorBinding;
65
import org.openide.filesystems.FileObject;
66
import org.openide.filesystems.MIMEResolver;
67
import org.openide.util.lookup.ServiceProvider;
68
56
69
57
70
/**
58
/**
71
 * Test java brackets completion - unlike the original test this one
59
 * Test brackets completion - unlike the original test this one
72
 * emulates real typing and tests the resulting state of the document.
60
 * emulates real typing and tests the resulting state of the document.
73
 *
74
 * @autor Miloslav Metelka
75
 */
61
 */
76
public class TypingCompletionUnitTest extends NbTestCase {
62
public class TypingCompletionUnitTest extends NbTestCase {
77
63
Lines 82-1392 Link Here
82
    @Override
68
    @Override
83
    protected void setUp() throws Exception {
69
    protected void setUp() throws Exception {
84
        super.setUp();
70
        super.setUp();
85
        Preferences prefs = MimeLookup.getLookup(JavaKit.JAVA_MIME_TYPE).lookup(Preferences.class);
71
        Preferences prefs = MimeLookup.getLookup(FileUtils.PHP_MIME_TYPE).lookup(Preferences.class);
86
        prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, true);
72
        prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, true);
87
        Class.forName(AcceleratorBinding.class.getName(), true, AcceleratorBinding.class.getClassLoader());
73
        Class.forName(AcceleratorBinding.class.getName(), true, AcceleratorBinding.class.getClassLoader());
88
    }
74
    }
89
75
90
    // ------- Tests for completion of right parenthesis ')' -------------
76
    public void testBracketCompletion() {
91
77
        Context ctx = new Context(new CslEditorKit(FileUtils.PHP_MIME_TYPE), PHPTokenId.language(), FileUtils.PHP_MIME_TYPE,
92
    public void testTypeSemicolonInForLoop() { // #146139
78
                "<?php | ?>"
93
        Context ctx = new Context(new JavaKit(),
94
                "for (int i = 0|)"
95
        );
79
        );
96
        ctx.typeChar(';');
80
        ctx.typeChar('(');
97
        ctx.assertDocumentTextEquals(
81
        ctx.assertDocumentTextEquals(
98
                "for (int i = 0;|)"
82
                "<?php (|) ?>"
83
        );
84
    }
85
    
86
    public void testQuoteCompletion() {
87
        Context ctx = new Context(new CslEditorKit(FileUtils.PHP_MIME_TYPE), PHPTokenId.language(), FileUtils.PHP_MIME_TYPE,
88
                "<?php |"
89
        );
90
        ctx.typeChar('"');
91
        ctx.assertDocumentTextEquals(
92
                "<?php \"|\""
99
        );
93
        );
100
    }
94
    }
101
95
102
    public void testTypeSecondSemicolonInForLoop() { // #146139
96
    public void testDeleteQuote() {
103
        Context ctx = new Context(new JavaKit(),
97
        Context ctx = new Context(new CslEditorKit(FileUtils.PHP_MIME_TYPE), PHPTokenId.language(), FileUtils.PHP_MIME_TYPE,
104
                "for (int i = 0; i <= 0|)"
98
                "<?php \"|\" ?>"
105
        );
99
        );
106
        ctx.typeChar(';');
100
        ctx.typeChar('\b');
107
        ctx.assertDocumentTextEquals(
101
        ctx.assertDocumentTextEquals(
108
                "for (int i = 0; i <= 0;|)"
102
                "<?php | ?>"
109
        );
110
    }
111
112
    public void testTypeSemicolonInArgs() { // #146139
113
        Context ctx = new Context(new JavaKit(),
114
                "m(|)"
115
        );
116
        ctx.typeChar(';');
117
        ctx.assertDocumentTextEquals(
118
                "m();|"
119
        );
103
        );
120
    }
104
    }
121
    
105
    
122
    public void testSemicolonOnTheEnd() { // #146139
106
    public void testIfBreakInsert() {
123
        Context ctx = new Context(new JavaKit(),
107
        Context ctx = new Context(new CslEditorKit(FileUtils.PHP_MIME_TYPE), PHPTokenId.language(), FileUtils.PHP_MIME_TYPE,
124
                "m()| "
108
                "<?php if(true):|?>"
125
        );
126
        ctx.typeChar(';');
127
        ctx.assertDocumentTextEquals(
128
                "m();| "
129
        );
130
    }
131
132
    public void testTypeRightParenWithinBraces() { // #146139
133
        Context ctx = new Context(new JavaKit(),
134
                "{(()|; }"
135
        );
136
        ctx.typeChar(')');
137
        ctx.assertDocumentTextEquals(
138
                "{(()); }"
139
        );
140
    }
141
    
142
    public void testTypeLeftParen() {
143
        Context ctx = new Context(new JavaKit(), "m|");
144
        ctx.typeChar('(');
145
        ctx.assertDocumentTextEquals("m(|)");
146
    }
147
148
    public void testTypeSecondRightParen() {
149
        Context ctx = new Context(new JavaKit(),
150
                "m()|)"
151
        );
152
        ctx.typeChar(')');
153
        ctx.assertDocumentTextEquals(
154
                "m())|"
155
        );
156
    }
157
158
    public void testTypeRightParenSwingInvokeLaterRunnable() {
159
        Context ctx = new Context(new JavaKit(),
160
                "SwingUtilities.invokeLater(new Runnable()|))"
161
        );
162
        ctx.typeChar(')');
163
        ctx.assertDocumentTextEquals(
164
                "SwingUtilities.invokeLater(new Runnable())|)"
165
        );
166
    }
167
168
    public void testTypeSimpleAdditionOfOpeningParenthesisAfterWhile () throws Exception {
169
        Context ctx = new Context(new JavaKit(),
170
                "while |"
171
        );
172
        ctx.typeChar('(');
173
        ctx.assertDocumentTextEquals(
174
                "while (|)"
175
        );
176
    }
177
178
    public void testTypeRightParenSwingInvokeLaterRunnableRun() {
179
        Context ctx = new Context(new JavaKit(),
180
                "SwingUtilities.invokeLater(new Runnable() {\n" +
181
                "    public void run()|)\n" +
182
                "})"
183
        );
184
        ctx.typeChar(')');
185
        ctx.assertDocumentTextEquals(
186
                "SwingUtilities.invokeLater(new Runnable() {\n" +
187
                "    public void run())|\n" +
188
                "})"
189
        );
190
    }
191
192
    public void testTypeRightParenIfMethodCall() {
193
        Context ctx = new Context(new JavaKit(),
194
                "if (a()|) + 5 > 6) {\n" +
195
                "}"
196
        );
197
        ctx.typeChar(')');
198
        ctx.assertDocumentTextEquals(
199
                "if (a())| + 5 > 6) {\n" +
200
                "}"
201
        );
202
    }
203
204
    public void testTypeRightParenNoSkipNonBracketChar() {
205
        Context ctx = new Context(new JavaKit(),
206
                "m()|"
207
        );
208
        ctx.typeChar(' ');
209
        ctx.assertDocumentTextEquals(
210
                "m() |"
211
        );
212
    }
213
214
215
216
    // ------- Tests for completion of right brace '}' -------------
217
218
    public void testTypeAddRightBraceIfLeftBrace() {
219
        Context ctx = new Context(new JavaKit(),
220
                "class Test {\n" +
221
                "    {\n" +
222
                "        if (true) {|\n" +
223
                "    }\n" +
224
                "}\n"
225
        );
109
        );
226
        ctx.typeChar('\n');
110
        ctx.typeChar('\n');
227
        ctx.assertDocumentTextEquals(
111
        ctx.assertDocumentTextEquals(
228
                "class Test {\n" +
112
                "<?php if(true):\n    |\nendif;\n?>"
229
                "    {\n" +
230
                "        if (true) {\n" +
231
                "            |\n" +
232
                "        }\n" +
233
                "    }\n" +
234
                "}\n"
235
        );
113
        );
236
    }
114
    }
237
238
    public void testTypeAddRightBraceIfLeftBraceWhiteSpace() {
239
        Context ctx = new Context(new JavaKit(),
240
                "class Test {\n" +
241
                "    {\n" +
242
                "        if (true) { \t|\n" +
243
                "    }\n" +
244
                "}\n"
245
        );
246
        ctx.typeChar('\n');
247
        ctx.assertDocumentTextEquals(
248
                "class Test {\n" +
249
                "    {\n" +
250
                "        if (true) { \t\n" +
251
                "            |\n" +
252
                "        }\n" +
253
                "    }\n" +
254
                "}\n"
255
        );
256
    }
257
258
    public void testTypeAddRightBraceIfLeftBraceLineComment() {
259
        Context ctx = new Context(new JavaKit(),
260
                "class Test {\n" +
261
                "    {\n" +
262
                "        if (true) { // line-comment|\n" +
263
                "    }\n" +
264
                "}\n"
265
        );
266
        ctx.typeChar('\n');
267
        ctx.assertDocumentTextEquals(
268
                "class Test {\n" +
269
                "    {\n" +
270
                "        if (true) { // line-comment\n" +
271
                "            |\n" +
272
                "        }\n" +
273
                "    }\n" +
274
                "}\n"
275
        );
276
    }
277
278
    public void testTypeAddRightBraceIfLeftBraceBlockComment() {
279
        Context ctx = new Context(new JavaKit(),
280
                "class Test {\n" +
281
                "    {\n" +
282
                "        if (true) { /* block-comment */|\n" +
283
                "    }\n" +
284
                "}\n"
285
        );
286
        ctx.typeChar('\n');
287
        ctx.assertDocumentTextEquals(
288
                "class Test {\n" +
289
                "    {\n" +
290
                "        if (true) { /* block-comment */\n" +
291
                "            |\n" +
292
                "        }\n" +
293
                "    }\n" +
294
                "}\n"
295
        );
296
    }
297
298
    public void testTypeAddRightBraceIfLeftBraceAlreadyPresent() {
299
        Context ctx = new Context(new JavaKit(),
300
                "class Test {\n" +
301
                "    {\n" +
302
                "        if (true) {|\n" +
303
                "        }\n" +
304
                "    }\n" +
305
                "}\n"
306
        );
307
        ctx.typeChar('\n');
308
        ctx.assertDocumentTextEquals(
309
                "class Test {\n" +
310
                "    {\n" +
311
                "        if (true) {\n" +
312
                "            |\n" +
313
                "        }\n" +
314
                "    }\n" +
315
                "}\n"
316
        );
317
    }
318
319
    public void XtestTypeAddRightBraceCaretInComment() {
320
        Context ctx = new Context(new JavaKit(),
321
                "class Test {\n" +
322
                "    {\n" +
323
                "        if (true) { /* unclosed-block-comment|\n" +
324
                "          */\n" +
325
                "    }\n" +
326
                "}\n"
327
        );
328
        ctx.typeChar('\n');
329
        ctx.assertDocumentTextEquals(
330
                "class Test {\n" +
331
                "    {\n" +
332
                "        if (true) { /* unclosed-block-comment\n" +
333
                "                     * |\n" +
334
                "          */\n" +
335
                "    }\n" +
336
                "}\n"
337
        );
338
    }
339
340
    public void testTypeAddRightBraceMultiLine() {
341
        Context ctx = new Context(new JavaKit(),
342
                "class Test {\n" +
343
                "    {\n" +
344
                "        if (true) {| System.out.println(\n" +
345
                "        \"\");\n" +
346
                "    }\n" +
347
                "}\n"
348
                );
349
350
        ctx.typeChar('\n');
351
        ctx.assertDocumentTextEquals(
352
                "class Test {\n" +
353
                "    {\n" +
354
                "        if (true) {\n" +
355
                "            System.out.println(\n" +
356
                "        \"\");\n" +
357
                "    }\n" +
358
                "}\n"
359
                );
360
    }
361
362
    public void testTypeAddRightBraceSingleLine() {
363
        Context ctx = new Context(new JavaKit(),
364
                "class Test {\n" +
365
                "    {\n" +
366
                "        if (true) {| System.out.println(\"\");\n" +
367
                "    }\n" +
368
                "}\n"
369
                );
370
371
        ctx.typeChar('\n');
372
        ctx.assertDocumentTextEquals(
373
                "class Test {\n" +
374
                "    {\n" +
375
                "        if (true) {\n" +
376
                "            System.out.println(\"\");\n" +
377
                "        }\n" +
378
                "    }\n" +
379
                "}\n"
380
                );
381
    }
382
383
384
    // ------- Tests for completion of quote (") -------------
385
    public void testTypeSimpleQuoteInEmptyDoc () throws Exception {
386
        Context ctx = new Context(new JavaKit(),
387
                "|"
388
        );
389
        ctx.typeChar('"');
390
        ctx.assertDocumentTextEquals(
391
                "\"|\""
392
        );
393
    }
394
395
    public void testTypeSimpleQuoteAtBeginingOfDoc () throws Exception {
396
        Context ctx = new Context(new JavaKit(),
397
                "|  "
398
        );
399
        ctx.typeChar('"');
400
        ctx.assertDocumentTextEquals(
401
                "\"|\"  "
402
        );
403
    }
404
405
    public void testTypeSimpleQuoteAtEndOfDoc () throws Exception {
406
        Context ctx = new Context(new JavaKit(),
407
                "  |"
408
        );
409
        ctx.typeChar('"');
410
        ctx.assertDocumentTextEquals(
411
                "  \"|\""
412
        );
413
    }
414
415
    public void testTypeSimpleQuoteInWhiteSpaceArea () throws Exception {
416
        Context ctx = new Context(new JavaKit(),
417
                "  |  "
418
        );
419
        ctx.typeChar('"');
420
        ctx.assertDocumentTextEquals(
421
                "  \"|\"  "
422
        );
423
    }
424
425
    public void testTypeQuoteAtEOL () throws Exception {
426
        Context ctx = new Context(new JavaKit(),
427
                "  |\n"
428
        );
429
        ctx.typeChar('"');
430
        ctx.assertDocumentTextEquals(
431
                "  \"|\"\n"
432
        );
433
    }
434
435
    public void testTypeQuoteWithUnterminatedStringLiteral () throws Exception {
436
        Context ctx = new Context(new JavaKit(),
437
                "  \"unterminated string| \n"
438
        );
439
        ctx.typeChar('"');
440
        ctx.assertDocumentTextEquals(
441
                "  \"unterminated string\"| \n"
442
        );
443
    }
444
445
    public void testTypeQuoteAtEOLWithUnterminatedStringLiteral () throws Exception {
446
        Context ctx = new Context(new JavaKit(),
447
                "  \"unterminated string | \n"
448
        );
449
        ctx.typeChar('"');
450
        ctx.assertDocumentTextEquals(
451
                "  \"unterminated string \"| \n"
452
        );
453
    }
454
455
    public void testTypeQuoteInsideStringLiteral () throws Exception {
456
        Context ctx = new Context(new JavaKit(),
457
                "  \"stri|ng literal\" "
458
        );
459
        ctx.typeChar('"');
460
        ctx.assertDocumentTextEquals(
461
                "  \"stri\"|ng literal\" "
462
        );
463
    }
464
465
    public void testTypeQuoteInsideEmptyParentheses () throws Exception {
466
        Context ctx = new Context(new JavaKit(),
467
                " System.out.println(|) "
468
        );
469
        ctx.typeChar('"');
470
        ctx.assertDocumentTextEquals(
471
                " System.out.println(\"|\") "
472
        );
473
    }
474
475
    public void testTypeQuoteInsideNonEmptyParentheses () throws Exception {
476
        Context ctx = new Context(new JavaKit(),
477
                " System.out.println(|some text) "
478
        );
479
        ctx.typeChar('"');
480
        ctx.assertDocumentTextEquals(
481
                " System.out.println(\"|some text) "
482
        );
483
    }
484
485
    public void testTypeQuoteInsideNonEmptyParenthesesBeforeClosingParentheses () throws Exception {
486
        Context ctx = new Context(new JavaKit(),
487
                " System.out.println(i+|) "
488
        );
489
        ctx.typeChar('"');
490
        ctx.assertDocumentTextEquals(
491
                " System.out.println(i+\"|\") "
492
        );
493
    }
494
495
    public void testTypeQuoteInsideNonEmptyParenthesesBeforeClosingParenthesesAndUnterminatedStringLiteral () throws Exception {
496
        Context ctx = new Context(new JavaKit(),
497
                " System.out.println(\"unterminated string literal |); "
498
        );
499
        ctx.typeChar('"');
500
        ctx.assertDocumentTextEquals(
501
                " System.out.println(\"unterminated string literal \"|); "
502
        );
503
    }
504
505
    public void testTypeQuoteBeforePlus () throws Exception {
506
        Context ctx = new Context(new JavaKit(),
507
                " System.out.println(|+\"string literal\"); "
508
        );
509
        ctx.typeChar('"');
510
        ctx.assertDocumentTextEquals(
511
                " System.out.println(\"|\"+\"string literal\"); "
512
        );
513
    }
514
515
    public void testTypeQuoteBeforeComma () throws Exception {
516
        Context ctx = new Context(new JavaKit(),
517
                "String s[] = new String[]{|,\"two\"};"
518
        );
519
        ctx.typeChar('"');
520
        ctx.assertDocumentTextEquals(
521
                "String s[] = new String[]{\"|\",\"two\"};"
522
        );
523
    }
524
525
    public void testTypeQuoteBeforeBrace () throws Exception {
526
        Context ctx = new Context(new JavaKit(),
527
                "String s[] = new String[]{\"one\",|};"
528
        );
529
        ctx.typeChar('"');
530
        ctx.assertDocumentTextEquals(
531
                "String s[] = new String[]{\"one\",\"|\"};"
532
        );
533
    }
534
535
    public void testTypeQuoteBeforeSemicolon() throws Exception {
536
        Context ctx = new Context(new JavaKit(),
537
                "String s = \"\" + |;"
538
        );
539
        ctx.typeChar('"');
540
        ctx.assertDocumentTextEquals(
541
                "String s = \"\" + \"|\";"
542
        );
543
    }
544
545
    public void testTypeQuoteBeforeSemicolonWithWhitespace() throws Exception {
546
        Context ctx = new Context(new JavaKit(),
547
                "String s = \"\" +| ;"
548
        );
549
        ctx.typeChar('"');
550
        ctx.assertDocumentTextEquals(
551
                "String s = \"\" +\"|\" ;"
552
        );
553
    }
554
555
    public void testTypeQuoteAfterEscapeSequence() throws Exception {
556
        Context ctx = new Context(new JavaKit(),
557
                "\\|"
558
        );
559
        ctx.typeChar('"');
560
        ctx.assertDocumentTextEquals(
561
                "\\\"|"
562
        );
563
    }
564
565
    public void testTypeQuoteEaten() throws Exception {
566
        Context ctx = new Context(new JavaKit(),
567
                "|"
568
        );
569
        ctx.typeChar('"');
570
        ctx.typeChar('"');
571
        ctx.assertDocumentTextEquals(
572
                "\"\"|"
573
        );
574
    }
575
576
    public void testTypeQuoteOnFirstQuote () throws Exception {
577
        Context ctx = new Context(new JavaKit(),
578
                " |\"asdf\""
579
        );
580
        ctx.typeChar('"');
581
        ctx.assertDocumentTextEquals(
582
                " \"|\"asdf\""
583
        );
584
    }
585
586
    public void testTypeQuoteInsideComments() throws Exception {
587
        Context ctx = new Context(new JavaKit(),
588
                "/** |\n */"
589
        );
590
        ctx.typeChar('"');
591
        ctx.assertDocumentTextEquals(
592
                "/** \"|\n */"
593
        );
594
    }
595
596
    public void testTypeQuoteAtTheEndOfLineCommentLine() throws Exception {
597
        Context ctx = new Context(new JavaKit(),
598
                "// test line comment |\n"
599
        );
600
        ctx.typeChar('"');
601
        ctx.assertDocumentTextEquals(
602
                "// test line comment \"|\n"
603
        );
604
    }
605
606
607
    // ------- Tests for completion of single quote (') -------------
608
609
    public void testTypeSingleQuoteInEmptyDoc () throws Exception {
610
        Context ctx = new Context(new JavaKit(),
611
                "|"
612
        );
613
        ctx.typeChar('\'');
614
        ctx.assertDocumentTextEquals(
615
                "'|'"
616
        );
617
    }
618
619
    public void testTypeSingleQuoteAtBeginingOfDoc () throws Exception {
620
        Context ctx = new Context(new JavaKit(),
621
                "|  "
622
        );
623
        ctx.typeChar('\'');
624
        ctx.assertDocumentTextEquals(
625
                "'|'  "
626
        );
627
    }
628
629
    public void testTypeSingleQuoteAtEndOfDoc () throws Exception {
630
        Context ctx = new Context(new JavaKit(),
631
                "  |"
632
        );
633
        ctx.typeChar('\'');
634
        ctx.assertDocumentTextEquals(
635
                "  '|'"
636
        );
637
    }
638
639
    public void testTypeSingleQuoteInWhiteSpaceArea () throws Exception {
640
        Context ctx = new Context(new JavaKit(),
641
                "  |  "
642
        );
643
        ctx.typeChar('\'');
644
        ctx.assertDocumentTextEquals(
645
                "  '|'  "
646
        );
647
    }
648
649
    public void testTypeSingleQuoteAtEOL () throws Exception {
650
        Context ctx = new Context(new JavaKit(),
651
                "  |\n"
652
        );
653
        ctx.typeChar('\'');
654
        ctx.assertDocumentTextEquals(
655
                "  '|'\n"
656
        );
657
    }
658
659
    public void testTypeSingleQuoteWithUnterminatedCharLiteral () throws Exception {
660
        Context ctx = new Context(new JavaKit(),
661
                "  '| \n"
662
        );
663
        ctx.typeChar('\'');
664
        ctx.assertDocumentTextEquals(
665
                "  ''| \n"
666
        );
667
    }
668
669
    public void testTypeSingleQuoteAtEOLWithUnterminatedCharLiteral () throws Exception {
670
        Context ctx = new Context(new JavaKit(),
671
                "  ' |\n"
672
        );
673
        ctx.typeChar('\'');
674
        ctx.assertDocumentTextEquals(
675
                "  ' '|\n"
676
        );
677
    }
678
679
    public void testTypeSingleQuoteInsideCharLiteral () throws Exception {
680
        Context ctx = new Context(new JavaKit(),
681
                "  '| ' "
682
        );
683
        ctx.typeChar('\'');
684
        ctx.assertDocumentTextEquals(
685
                "  ''| ' "
686
        );
687
    }
688
689
    public void testTypeSingleQuoteInsideEmptyParentheses () throws Exception {
690
        Context ctx = new Context(new JavaKit(),
691
                " System.out.println(|) "
692
        );
693
        ctx.typeChar('\'');
694
        ctx.assertDocumentTextEquals(
695
                " System.out.println('|') "
696
        );
697
    }
698
699
    public void testTypeSingleQuoteInsideNonEmptyParentheses () throws Exception {
700
        Context ctx = new Context(new JavaKit(),
701
                " System.out.println(|some text) "
702
        );
703
        ctx.typeChar('\'');
704
        ctx.assertDocumentTextEquals(
705
                " System.out.println('|some text) "
706
        );
707
    }
708
709
    public void testTypeSingleQuoteInsideNonEmptyParenthesesBeforeClosingParentheses () throws Exception {
710
        Context ctx = new Context(new JavaKit(),
711
                " System.out.println(i+|) "
712
        );
713
        ctx.typeChar('\'');
714
        ctx.assertDocumentTextEquals(
715
                " System.out.println(i+'|') "
716
        );
717
    }
718
719
    public void testTypeSingleQuoteInsideNonEmptyParenthesesBeforeClosingParenthesesAndUnterminatedCharLiteral () throws Exception {
720
        Context ctx = new Context(new JavaKit(),
721
                " System.out.println(' |); "
722
        );
723
        ctx.typeChar('\'');
724
        ctx.assertDocumentTextEquals(
725
                " System.out.println(' '|); "
726
        );
727
    }
728
729
    public void testTypeSingleQuoteBeforePlus () throws Exception {
730
        Context ctx = new Context(new JavaKit(),
731
                " System.out.println(|+\"string literal\"); "
732
        );
733
        ctx.typeChar('\'');
734
        ctx.assertDocumentTextEquals(
735
                " System.out.println('|'+\"string literal\"); "
736
        );
737
    }
738
739
    public void testTypeSingleQuoteBeforeComma () throws Exception {
740
        Context ctx = new Context(new JavaKit(),
741
                "String s[] = new String[]{|,\"two\"};"
742
        );
743
        ctx.typeChar('\'');
744
        ctx.assertDocumentTextEquals(
745
                "String s[] = new String[]{'|',\"two\"};"
746
        );
747
    }
748
749
    public void testTypeSingleQuoteBeforeBrace () throws Exception {
750
        Context ctx = new Context(new JavaKit(),
751
                "String s[] = new String[]{\"one\",|};"
752
        );
753
        ctx.typeChar('\'');
754
        ctx.assertDocumentTextEquals(
755
                "String s[] = new String[]{\"one\",'|'};"
756
        );
757
    }
758
759
    public void testTypeSingleQuoteBeforeSemicolon() throws Exception {
760
        Context ctx = new Context(new JavaKit(),
761
                "String s = \"\" + |;"
762
        );
763
        ctx.typeChar('\'');
764
        ctx.assertDocumentTextEquals(
765
                "String s = \"\" + '|';"
766
        );
767
    }
768
769
    public void testTypeSingleQuoteBeforeSemicolonWithWhitespace() throws Exception {
770
        Context ctx = new Context(new JavaKit(),
771
                "String s = \"\" +| ;"
772
        );
773
        ctx.typeChar('\'');
774
        ctx.assertDocumentTextEquals(
775
                "String s = \"\" +'|' ;"
776
        );
777
    }
778
779
    public void testTypeSingleQuoteAfterEscapeSequence() throws Exception {
780
        Context ctx = new Context(new JavaKit(),
781
                "\\|"
782
        );
783
        ctx.typeChar('\'');
784
        ctx.assertDocumentTextEquals(
785
                "\\'|"
786
        );
787
    }
788
789
    public void testTypeSingleQuoteEaten() throws Exception {
790
        Context ctx = new Context(new JavaKit(),
791
                "|"
792
        );
793
        ctx.typeChar('\'');
794
        ctx.typeChar('\'');
795
        ctx.assertDocumentTextEquals(
796
                "''|"
797
        );
798
    }
799
800
    public void testTypeSingleQuoteInsideComments() throws Exception {
801
        Context ctx = new Context(new JavaKit(),
802
                "/* |\n */"
803
        );
804
        ctx.typeChar('\'');
805
        ctx.assertDocumentTextEquals(
806
                "/* \'|\n */"
807
        );
808
    }
809
810
    public void testTypeSingleQuoteAtTheEndOfLineCommentLine() throws Exception {
811
        Context ctx = new Context(new JavaKit(),
812
                "// test line comment |\n"
813
        );
814
        ctx.typeChar('\'');
815
        ctx.assertDocumentTextEquals(
816
                "// test line comment \'|\n"
817
        );
818
    }
819
820
    public void testDisable147641() throws Exception {
821
        boolean orig = TypingCompletion.isCompletionSettingEnabled();
822
        Preferences prefs = MimeLookup.getLookup(JavaKit.JAVA_MIME_TYPE).lookup(Preferences.class);
823
824
        try {
825
            prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, false);
826
827
            Context ctx = new Context(new JavaKit(),
828
                    "while |"
829
            );
830
            ctx.typeChar('(');
831
            ctx.assertDocumentTextEquals(
832
                    "while (|"
833
            );
834
        } finally {
835
            prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, orig);
836
        }
837
    }
838
839
    public void testDoNotSkipWhenNotBalanced147683a() throws Exception {
840
        Context ctx = new Context(new JavaKit(),
841
                "System.err.println((true|);"
842
        );
843
        ctx.typeChar(')');
844
        ctx.assertDocumentTextEquals(
845
                "System.err.println((true)|);"
846
        );
847
    }
848
849
    public void testSkipWhenBalanced46517() throws Exception {
850
        Context ctx = new Context(new JavaKit(),
851
                "if (a(|) )"
852
        );
853
        ctx.typeChar(')');
854
        ctx.assertDocumentTextEquals(
855
                "if (a()| )"
856
        );
857
    }
858
859
    public void testDoNotSkipWhenNotBalanced147683b() throws Exception {
860
        Context ctx = new Context(new JavaKit(),
861
                "if (a(|) ; )"
862
        );
863
        ctx.typeChar(')');
864
        ctx.assertDocumentTextEquals(
865
                "if (a()|) ; )"
866
        );
867
    }
868
869
    public void testDoNotSkipWhenNotBalanced147683c() throws Exception {
870
        Context ctx = new Context(new JavaKit(),
871
                "if (a(|) \n )"
872
        );
873
        ctx.typeChar(')');
874
        ctx.assertDocumentTextEquals(
875
                "if (a()|) \n )"
876
        );
877
    }
878
//problem
879
    public void testSkipWhenBalanced198194a() throws Exception {
880
        Context ctx = new Context(new JavaKit(),
881
                "for (int i = a(|); i < 10; i++)"
882
        );
883
        ctx.typeChar(')');
884
        ctx.assertDocumentTextEquals(
885
                "for (int i = a()|; i < 10; i++)"
886
        );
887
    }
888
889
    public void testSkipWhenNotBalanced198194a() throws Exception {
890
        Context ctx = new Context(new JavaKit(),
891
                "for (int i = a(|; i < 10; i++)"
892
        );
893
        ctx.typeChar(')');
894
        ctx.assertDocumentTextEquals(
895
                "for (int i = a()|; i < 10; i++)"
896
        );
897
    }
898
899
    public void testSkipWhenBalanced198194b() throws Exception {
900
        Context ctx = new Context(new JavaKit(),
901
                "for (int i = a(); i < 10; i = a(|))"
902
        );
903
        ctx.typeChar(')');
904
        ctx.assertDocumentTextEquals(
905
                "for (int i = a(); i < 10; i = a()|)"
906
        );
907
    }
908
909
    public void testSkipWhenNotBalanced198194b() throws Exception {
910
        Context ctx = new Context(new JavaKit(),
911
                "for (int i = a(); i < 10; i = a(|)"
912
        );
913
        ctx.typeChar(')');
914
        ctx.assertDocumentTextEquals(
915
                "for (int i = a(); i < 10; i = a()|)"
916
        );
917
    }
918
919
    public void testSkipWhenNotBalanced198194c() throws Exception {
920
        Context ctx = new Context(new JavaKit(),
921
                "for (int i = a(); i < 10; i++) a(|;"
922
        );
923
        ctx.typeChar(')');
924
        ctx.assertDocumentTextEquals(
925
                "for (int i = a(); i < 10; i++) a()|;"
926
        );
927
    }
928
929
    public void testSkipWhenBalanced198194c() throws Exception {
930
        Context ctx = new Context(new JavaKit(),
931
                "for (int i = a(); i < 10; i++) a(|);"
932
        );
933
        ctx.typeChar(')');
934
        ctx.assertDocumentTextEquals(
935
                "for (int i = a(); i < 10; i++) a()|;"
936
        );
937
    }
938
939
    public void testKeepBalance148878() throws Exception {
940
        Context ctx = new Context(new JavaKit(),
941
                "Map[|] m = new HashMap[1];"
942
        );
943
        ctx.typeChar(']');
944
        ctx.assertDocumentTextEquals(
945
                "Map[]| m = new HashMap[1];"
946
        );
947
    }
948
    
949
    public void testQuotes148878() throws Exception {
950
        Context ctx = new Context(new JavaKit(),
951
                "if (c == '\\\\|')"
952
        );
953
        ctx.typeChar('\'');
954
        ctx.assertDocumentTextEquals(
955
                "if (c == '\\\\'|)"
956
        );
957
    }
958
   
959
    public void testPositionInString() throws Exception {
960
        Context ctx = new Context(new JavaKit(),
961
                "class Test {\n"
962
                + "    {\n"
963
                + "        \"H|\"\n"
964
                + "    }\n"
965
                + "}\n");
966
        ctx.typeChar('\n');
967
        ctx.assertDocumentTextEquals(
968
                "class Test {\n"
969
                + "    {\n"
970
                + "        \"H\"\n"
971
                + "        + \"|\"\n"
972
                + "    }\n"
973
                + "}\n");
974
    }
975
    
976
    public void testPositionInEmptyString() throws Exception {
977
        Context ctx = new Context(new JavaKit(),
978
                "class Test {\n"
979
                + "    {\n"
980
                + "        \"|\"\n"
981
                + "    }\n"
982
                + "}\n");
983
        ctx.typeChar('\n');
984
        ctx.assertDocumentTextEquals(
985
                "class Test {\n"
986
                + "    {\n"
987
                + "        \"\"\n"
988
                + "        + \"|\"\n"
989
                + "    }\n"
990
                + "}\n");
991
    }
992
    
993
    public void testCommentBlockCompletion() throws Exception {
994
        Context ctx = new Context(new JavaKit(),
995
                "class Test {\n"
996
                + "    {\n"
997
                + "        /*|\n"
998
                + "    }\n"
999
                + "}\n");
1000
        ctx.typeChar('\n');
1001
        ctx.assertDocumentTextEquals(
1002
                "class Test {\n"
1003
                + "    {\n"
1004
                + "        /*\n"
1005
                + "         * |\n"
1006
                + "         */\n"
1007
                + "    }\n"
1008
                + "}\n");
1009
    }
1010
   
1011
    public void testCommentBlockCompletionNotNeeded() throws Exception {
1012
        Context ctx = new Context(new JavaKit(),
1013
                "class Test {\n"
1014
                + "    {\n"
1015
                + "        /*|\n"
1016
                + "         */\n"
1017
                + "    }\n"
1018
                + "}\n");
1019
        ctx.typeChar('\n');
1020
        ctx.assertDocumentTextEquals(
1021
                "class Test {\n"
1022
                + "    {\n"
1023
                + "        /*\n"
1024
                + "         * |\n"
1025
                + "         */\n"
1026
                + "    }\n"
1027
                + "}\n");
1028
    }
1029
    
1030
1031
    public void testCommentBlockCompletionTwoComments () {
1032
        Context ctx = new Context(new JavaKit(),
1033
                "/*|\n" +
1034
                "/*\n" +
1035
                " */"
1036
        );
1037
        ctx.typeChar('\n');
1038
        ctx.assertDocumentTextEquals(
1039
                "/*\n" +
1040
                " * |\n" +
1041
                " */\n" +
1042
                "/*\n" +
1043
                " */"
1044
        );
1045
    }
1046
1047
    public void testCommentBlockCompletionTwoComments2 () {
1048
        Context ctx = new Context(new JavaKit(),
1049
                "/*|\n" +
1050
                "\n" +
1051
                "/*\n" +
1052
                " */"
1053
        );
1054
        ctx.typeChar('\n');
1055
        ctx.assertDocumentTextEquals(
1056
                "/*\n" +
1057
                " * |\n" +
1058
                " */\n" +
1059
                "\n" +
1060
                "/*\n" +
1061
                " */"
1062
        );
1063
    }
1064
    
1065
    public void testCommentBlockCompletionNoClose () {
1066
        Context ctx = new Context(new JavaKit(),
1067
                "/*| a"
1068
        );
1069
        ctx.typeChar('\n');
1070
        ctx.assertDocumentTextEquals(
1071
                "/*\n" +
1072
                " * |a"
1073
        );
1074
    }
1075
    
1076
    public void testJavaDocCompletion() throws Exception {
1077
        Context ctx = new Context(new JavaKit(),
1078
                "class Test {\n"
1079
                + "    {\n"
1080
                + "        /**|\n"
1081
                + "    }\n"
1082
                + "}\n");
1083
        ctx.typeChar('\n');
1084
        ctx.assertDocumentTextEquals(
1085
                "class Test {\n"
1086
                + "    {\n"
1087
                + "        /**\n"
1088
                + "         * |\n"
1089
                + "         */\n"
1090
                + "    }\n"
1091
                + "}\n");
1092
    }
1093
    
1094
    public void testJavaDocCompletionNotNeeded() throws Exception {
1095
        Context ctx = new Context(new JavaKit(),
1096
                "class Test {\n"
1097
                + "    {\n"
1098
                + "        /**|\n"
1099
                + "         */\n"
1100
                + "    }\n"
1101
                + "}\n");
1102
        ctx.typeChar('\n');
1103
        ctx.assertDocumentTextEquals(
1104
                "class Test {\n"
1105
                + "    {\n"
1106
                + "        /**\n"
1107
                + "         * |\n"
1108
                + "         */\n"
1109
                + "    }\n"
1110
                + "}\n");
1111
    }
1112
  
1113
    public void insertBreakJavadocComplete() throws Exception {
1114
        Context ctx = new Context(new JavaKit(),
1115
                "class Test {\n"
1116
                + "    {\n"
1117
                + "        /**|\n"
1118
                + "        public static void main(String[] args) {\n"
1119
                + "        }\n"
1120
                + "    }\n"
1121
                + "}\n");
1122
        ctx.typeChar('\n');
1123
        ctx.assertDocumentTextEquals (
1124
            "class Test {\n"
1125
                + "    {\n"
1126
                + "        /**\n"
1127
                + "         * |\n"
1128
                + "         * @param args\n"
1129
                + "         */\n"
1130
                + "        public static void main(String[] args) {\n"
1131
                + "        }\n"
1132
                + "    }\n"
1133
                + "}\n"
1134
        );
1135
    }
1136
    
1137
    public void testRemoveBracketBackSpace() throws Exception {
1138
        Context ctx = new Context(new JavaKit(),
1139
                "()(|)");
1140
        ctx.typeChar('\b');
1141
        ctx.assertDocumentTextEquals("()|");
1142
    }
1143
1144
    public void testRemoveBracketDelete() throws Exception {
1145
        Context ctx = new Context(new JavaKit(),
1146
                "()|()");
1147
        ctx.typeChar('\f');
1148
        ctx.assertDocumentTextEquals("()|");
1149
    }
1150
    
1151
    public void testRemoveQuotesBackSpace() throws Exception {
1152
        Context ctx = new Context(new JavaKit(),
1153
                "\"\"\"|\"");
1154
        ctx.typeChar('\b');
1155
        ctx.assertDocumentTextEquals("\"\"|");
1156
    }
1157
    
1158
    public void testRemoveQuotesDelete() throws Exception {
1159
        Context ctx = new Context(new JavaKit(),
1160
                "\"\"|\"\"");
1161
        ctx.typeChar('\f');
1162
        ctx.assertDocumentTextEquals("\"\"|");
1163
    }
1164
    
1165
    public void testRemoveQuotes2BackSpace() throws Exception {
1166
        Context ctx = new Context(new JavaKit(),
1167
                "\'\'\'|\'");
1168
        ctx.typeChar('\b');
1169
        ctx.assertDocumentTextEquals("\'\'|");
1170
    }
1171
    
1172
    public void testRemoveQuotes2Delete() throws Exception {
1173
        Context ctx = new Context(new JavaKit(),
1174
                "\'\'|\'\'");
1175
        ctx.typeChar('\f');
1176
        ctx.assertDocumentTextEquals("\'\'|");
1177
    }
1178
    
1179
    public void testJumpCharacters() throws Exception {
1180
        Context ctx = new Context(new JavaKit(), "m(\"p|\");");
1181
        ctx.typeChar('"');
1182
        ctx.assertDocumentTextEquals("m(\"p\"|);");
1183
        ctx.typeChar(')');
1184
        ctx.assertDocumentTextEquals("m(\"p\")|;");
1185
    }
1186
    
1187
    public void testJumpQuote() throws Exception {
1188
        Context ctx = new Context(new JavaKit(), "\"|\"");
1189
        ctx.typeChar('"');
1190
        ctx.assertDocumentTextEquals("\"\"|");
1191
    }
1192
    
1193
    public void testInsertSquareBracket() throws Exception {
1194
        Context ctx = new Context(new JavaKit(), "|");
1195
        ctx.typeChar('[');
1196
        ctx.assertDocumentTextEquals("[|]");
1197
    }
1198
1199
    public void testBackspaceSquareBracket() throws Exception {
1200
        Context ctx = new Context(new JavaKit(), "[|]");
1201
        ctx.typeChar('\b');
1202
        ctx.assertDocumentTextEquals("|");
1203
    }
1204
    
1205
    public void testDeleteSquareBracket() throws Exception {
1206
        Context ctx = new Context(new JavaKit(), "|[]");
1207
        ctx.typeChar('\f');
1208
        ctx.assertDocumentTextEquals("|");
1209
    }
1210
    
1211
    public void testInsertBracketInString() throws Exception {
1212
        Context ctx = new Context(new JavaKit(), "\"|\"");
1213
        ctx.typeChar('(');
1214
        ctx.assertDocumentTextEquals("\"(|\"");
1215
        ctx = new Context(new JavaKit(), "\" |\"");
1216
        ctx.typeChar('(');
1217
        ctx.assertDocumentTextEquals("\" (|\"");
1218
    }
1219
    
1220
    public void testInsertBracketInComment() throws Exception {
1221
        Context ctx = new Context(new JavaKit(), "//|");
1222
        ctx.typeChar('(');
1223
        ctx.assertDocumentTextEquals("//(|");
1224
    }
1225
    
1226
    public void testSkipBracketInComment() throws Exception {
1227
        Context ctx = new Context(new JavaKit(), "//(|)");
1228
        ctx.typeChar(')');
1229
        ctx.assertDocumentTextEquals("//()|)");
1230
    }
1231
     
1232
    public void testCorrectHandlingOfStringEscapes184059() throws Exception {
1233
        assertTrue(isInsideString("foo\n\"bar|\""));
1234
        assertTrue(isInsideString("foo\n\"bar\\\"|\""));
1235
        assertFalse(isInsideString("foo\n\"bar\\\\\"|"));
1236
        assertFalse(isInsideString("foo\n|\"bar\\\\\""));
1237
        assertTrue(isInsideString("foo\n\"|bar\\\\\""));
1238
    }
1239
1240
    private boolean isInsideString(String code) throws BadLocationException {
1241
        int pos = code.indexOf('|');
1242
1243
        assertNotSame(-1, pos);
1244
1245
        code = code.replaceAll(Pattern.quote("|"), "");
1246
1247
        Document doc = new PlainDocument();
1248
1249
        doc.putProperty(Language.class, JavaTokenId.language());
1250
        doc.insertString(0, code, null);
1251
1252
        return TypingCompletion.posWithinString(doc, pos);
1253
    }
1254
1255
    private static final class Context {
1256
        
1257
        private JEditorPane pane;
1258
1259
        public Context(final EditorKit kit, final String textWithPipe) {
1260
            try {
1261
                SwingUtilities.invokeAndWait(new Runnable() {
1262
                    @Override
1263
                    public void run() {
1264
                        pane = new JEditorPane();
1265
                        pane.setEditorKit(kit);
1266
                        Document doc = pane.getDocument();
1267
                        // Required by Java's default key typed
1268
                        doc.putProperty(Language.class, JavaTokenId.language());
1269
                        doc.putProperty("mimeType", "text/x-java");
1270
                        int caretOffset = textWithPipe.indexOf('|');
1271
                        String text;
1272
                        if (caretOffset != -1) {
1273
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
1274
                        } else {
1275
                            text = textWithPipe;
1276
                        }
1277
                        pane.setText(text);
1278
                        pane.setCaretPosition((caretOffset != -1) ? caretOffset : doc.getLength());
1279
                    }
1280
                });
1281
            } catch (Exception e) {
1282
                throw new IllegalStateException(e);
1283
            }
1284
        }
1285
        
1286
        public JEditorPane pane() {
1287
            return pane;
1288
        }
1289
1290
        public Document document() {
1291
            return pane.getDocument();
1292
        }
1293
        
1294
        public void typeChar(final char ch) {
1295
            try {
1296
                SwingUtilities.invokeAndWait(new Runnable() {
1297
                    @Override
1298
                    public void run() {
1299
                        KeyEvent keyEvent;
1300
                        switch (ch) {
1301
                            case '\n':
1302
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1303
                                        EventQueue.getMostRecentEventTime(),
1304
                                        0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Enter
1305
                                break;
1306
                            case '\b':
1307
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1308
                                        EventQueue.getMostRecentEventTime(),
1309
                                        0, KeyEvent.VK_BACK_SPACE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of BackSpace
1310
                                break;
1311
                            case '\f':
1312
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_PRESSED,
1313
                                        EventQueue.getMostRecentEventTime(),
1314
                                        0, KeyEvent.VK_DELETE, KeyEvent.CHAR_UNDEFINED); // Simulate pressing of Delete
1315
                                break;
1316
                            default:
1317
                                keyEvent = new KeyEvent(pane, KeyEvent.KEY_TYPED,
1318
                                        EventQueue.getMostRecentEventTime(),
1319
                                        0, KeyEvent.VK_UNDEFINED, ch);
1320
                        }
1321
                        SwingUtilities.processKeyBindings(keyEvent);
1322
                    }
1323
                });
1324
            } catch (Exception e) {
1325
                throw new IllegalStateException(e);
1326
            }
1327
        }
1328
1329
        public void typeText(String text) {
1330
            for (int i = 0; i < text.length(); i++) {
1331
                typeChar(text.charAt(i));
1332
            }
1333
        }
1334
1335
        public void assertDocumentTextEquals(final String textWithPipe) {
1336
            try {
1337
                SwingUtilities.invokeAndWait(new Runnable() {
1338
                    @Override
1339
                    public void run() {
1340
                        int caretOffset = textWithPipe.indexOf('|');
1341
                        String text;
1342
                        if (caretOffset != -1) {
1343
                            text = textWithPipe.substring(0, caretOffset) + textWithPipe.substring(caretOffset + 1);
1344
                        } else {
1345
                            text = textWithPipe;
1346
                        }
1347
                        try {
1348
                            // Use debug text to prefix special chars for easier readability
1349
                            text = CharSequenceUtilities.debugText(text);
1350
                            String docText = document().getText(0, document().getLength());
1351
                            docText = CharSequenceUtilities.debugText(docText);
1352
                            if (!text.equals(docText)) {
1353
                                int diffIndex = 0;
1354
                                int minLen = Math.min(docText.length(), text.length());
1355
                                while (diffIndex < minLen) {
1356
                                    if (text.charAt(diffIndex) != docText.charAt(diffIndex)) {
1357
                                        break;
1358
                                    }
1359
                                    diffIndex++;
1360
                                }
1361
                                TestCase.fail("Invalid document text - diff at index " + diffIndex +
1362
                                        "\nExpected: \"" + text +
1363
                                        "\"\n  Actual: \"" + docText + "\""
1364
                                );
1365
                            }
1366
                        } catch (BadLocationException e) {
1367
                            throw new IllegalStateException(e);
1368
                        }
1369
                        if (caretOffset != -1) {
1370
                            TestCase.assertEquals("Invalid caret offset", caretOffset, pane.getCaretPosition());
1371
                        }
1372
                    }
1373
                });
1374
            } catch (Exception e) {
1375
                throw new IllegalStateException(e);
1376
            }
1377
        }
1378
    }
1379
1380
    @ServiceProvider(service=MIMEResolver.class)
1381
    public static final class MIMEResolverImpl extends MIMEResolver {
1382
1383
        public MIMEResolverImpl() {
1384
            super("text/x-nbeditor-keybindingsettings");
1385
        }
1386
1387
        @Override public String findMIMEType(FileObject fo) {
1388
            return fo.getPath().contains("Keybindings") ? "text/x-nbeditor-keybindingsettings" : null;
1389
        }
1390
    }
1391
1392
}
115
}
(-)a/web.core.syntax/nbproject/project.properties (-1 / +1 lines)
Lines 49-55 Link Here
49
javac.source=1.6
49
javac.source=1.6
50
javadoc.arch=${basedir}/arch.xml
50
javadoc.arch=${basedir}/arch.xml
51
51
52
spec.version.base=2.33.0
52
spec.version.base=2.34.0
53
53
54
test.config.validation.includes=\
54
test.config.validation.includes=\
55
**/AutoCompletionTest.class,**/CompletionTest.class
55
**/AutoCompletionTest.class,**/CompletionTest.class
(-)a/web.core.syntax/src/org/netbeans/modules/web/core/syntax/JspKit.java (-284 / +12 lines)
Lines 58-64 Link Here
58
import javax.swing.Action;
58
import javax.swing.Action;
59
import javax.swing.SwingUtilities;
59
import javax.swing.SwingUtilities;
60
import javax.swing.text.*;
60
import javax.swing.text.*;
61
import org.netbeans.api.editor.mimelookup.MimeLookup;
62
import org.netbeans.api.editor.mimelookup.MimePath;
63
import org.netbeans.api.editor.mimelookup.MimeRegistration;
64
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
61
import org.netbeans.api.java.lexer.JavaTokenId;
65
import org.netbeans.api.java.lexer.JavaTokenId;
66
import org.netbeans.api.java.source.CodeStyle;
62
import org.netbeans.api.lexer.TokenHierarchy;
67
import org.netbeans.api.lexer.TokenHierarchy;
63
import org.netbeans.api.lexer.TokenSequence;
68
import org.netbeans.api.lexer.TokenSequence;
64
import org.netbeans.editor.BaseDocument;
69
import org.netbeans.editor.BaseDocument;
Lines 80-87 Link Here
80
import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction;
85
import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction;
81
import org.netbeans.editor.ext.ExtKit.ExtDeleteCharAction;
86
import org.netbeans.editor.ext.ExtKit.ExtDeleteCharAction;
82
import org.netbeans.modules.csl.api.*;
87
import org.netbeans.modules.csl.api.*;
88
import org.netbeans.modules.editor.indent.api.Indent;
89
import org.netbeans.modules.editor.indent.spi.Context;
83
import org.netbeans.modules.web.core.syntax.gsf.JspCommentHandler;
90
import org.netbeans.modules.web.core.syntax.gsf.JspCommentHandler;
91
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
92
import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor;
93
import org.netbeans.spi.editor.typinghooks.TypedBreakInterceptor.MutableContext;
94
import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor;
84
import org.netbeans.spi.lexer.MutableTextInput;
95
import org.netbeans.spi.lexer.MutableTextInput;
96
import org.openide.util.Lookup;
85
97
86
/**
98
/**
87
 * Editor kit implementation for JSP content type
99
 * Editor kit implementation for JSP content type
Lines 177-186 Link Here
177
    @Override
189
    @Override
178
    protected Action[] createActions() {
190
    protected Action[] createActions() {
179
        Action[] javaActions = new Action[] {
191
        Action[] javaActions = new Action[] {
180
            new JspInsertBreakAction(),
181
            new JspDefaultKeyTypedAction(),
182
            new JspDeleteCharAction(deletePrevCharAction, false),
183
            new JspDeleteCharAction(deleteNextCharAction, true),
184
            new SelectCodeElementAction(SelectCodeElementAction.selectNextElementAction, true),
192
            new SelectCodeElementAction(SelectCodeElementAction.selectNextElementAction, true),
185
            new SelectCodeElementAction(SelectCodeElementAction.selectPreviousElementAction, false),
193
            new SelectCodeElementAction(SelectCodeElementAction.selectPreviousElementAction, false),
186
            new InstantRenameAction(),
194
            new InstantRenameAction(),
Lines 423-707 Link Here
423
        return new org.openide.util.HelpCtx(JspKit.class);
431
        return new org.openide.util.HelpCtx(JspKit.class);
424
    }
432
    }
425
433
426
    /**
427
     * Returns true if bracket completion is enabled in options.
428
     */
429
    private static boolean completionSettingEnabled() {
430
        //return ((Boolean)Settings.getValue(JspKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue();
431
        return true;
432
    }
433
434
    public static class JspInsertBreakAction extends InsertBreakAction {
435
436
        @Override
437
        public void actionPerformed(ActionEvent e, JTextComponent target) {
438
            if (target != null) {
439
                TokenSequence javaTokenSequence;
440
                AbstractDocument adoc = (AbstractDocument)target.getDocument();
441
                adoc.readLock();
442
                try {
443
                    javaTokenSequence = JspSyntaxSupport.tokenSequence(TokenHierarchy.get(target.getDocument()), JavaTokenId.language(), target.getCaret().getDot() - 1);
444
                } finally {
445
                    adoc.readUnlock();
446
                }
447
448
                if (javaTokenSequence != null) {
449
                    JavaKit jkit = (JavaKit) getKit(JavaKit.class);
450
                    if (jkit != null) {
451
                        Action action = jkit.getActionByName(DefaultEditorKit.insertBreakAction);
452
                        if (action != null && action instanceof JavaKit.JavaInsertBreakAction) {
453
                            ((JavaKit.JavaInsertBreakAction) action).actionPerformed(e, target);
454
                            return;
455
                        }
456
                    }
457
                }
458
            }
459
            super.actionPerformed(e, target);
460
        }
461
462
        @Override
463
        protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) {
464
            if (completionSettingEnabled()) {
465
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, caret.getDot());
466
467
                if (bracketCompletion != null) {
468
                    try {
469
                        int newOffset = bracketCompletion.beforeBreak(doc, caret.getDot(), target);
470
471
                        if (newOffset >= 0) {
472
                            return new Integer(newOffset);
473
                        }
474
                    } catch (BadLocationException ble) {
475
                        Exceptions.printStackTrace(ble);
476
                    }
477
                }
478
            }
479
480
            return null;
481
        }
482
483
        @Override
484
        protected void afterBreak(JTextComponent target, BaseDocument doc, Caret caret,
485
                Object cookie) {
486
            if (completionSettingEnabled()) {
487
                if (cookie != null) {
488
                    if (cookie instanceof Integer) {
489
                        // integer
490
                        int dotPos = ((Integer) cookie).intValue();
491
                        if (dotPos != -1) {
492
                            caret.setDot(dotPos);
493
                        } else {
494
                            int nowDotPos = caret.getDot();
495
                            caret.setDot(nowDotPos + 1);
496
                        }
497
                    }
498
                }
499
            }
500
        }
501
502
    }
503
504
    public static class JspDefaultKeyTypedAction extends ExtDefaultKeyTypedAction {
505
506
        private JTextComponent currentTarget;
507
508
        @Override
509
        public void actionPerformed(final ActionEvent e, final JTextComponent target) {
510
            // Preliminary checks that avoid interpreting e.g. Ctrl+W that would then invoke runAtomic()
511
            if ((target != null) && (e != null)) {
512
                    // Check whether the modifiers are OK
513
                    int mod = e.getModifiers();
514
                    boolean ctrl = ((mod & ActionEvent.CTRL_MASK) != 0);
515
                    // On the mac, norwegian and french keyboards use Alt to do bracket characters.
516
                    // This replicates Apple's modification DefaultEditorKit.DefaultKeyTypedAction
517
                    boolean alt = org.openide.util.Utilities.isMac() ? ((mod & ActionEvent.META_MASK) != 0) :
518
                        ((mod & ActionEvent.ALT_MASK) != 0);
519
                    if (alt || ctrl) {
520
                        return;
521
                    }
522
                    // Check whether the target is enabled and editable
523
                    if (!target.isEditable() || !target.isEnabled()) {
524
                        target.getToolkit().beep();
525
                        return;
526
                    }
527
            }
528
529
            currentTarget = target;
530
            try {
531
                if (!triggerJavaDefaultKeyTypedAction(e, target)) {
532
                    super.actionPerformed(e, target);
533
                } else {
534
                    return; //java triggered this already
535
                }
536
            } finally {
537
                currentTarget = null;
538
            }
539
        }
540
541
        private boolean triggerJavaDefaultKeyTypedAction(final ActionEvent e, final JTextComponent target) {
542
            BaseDocument bdoc = (BaseDocument) target.getDocument();
543
            final boolean[] retcode = new boolean[1];
544
            bdoc.runAtomic(new Runnable() {
545
                public void run() {
546
                    TokenSequence javaTokenSequence = JspSyntaxSupport.tokenSequence(TokenHierarchy.get(target.getDocument()), JavaTokenId.language(), target.getCaret().getDot() - 1);
547
                    if (javaTokenSequence != null) {
548
                        JavaKit jkit = (JavaKit) getKit(JavaKit.class);
549
                        if (jkit != null) {
550
                            Action action = jkit.getActionByName(DefaultEditorKit.defaultKeyTypedAction);
551
                            if (action != null && action instanceof JavaKit.JavaDefaultKeyTypedAction) {
552
                                ((JavaKit.JavaDefaultKeyTypedAction) action).actionPerformed(e, target);
553
                                retcode[0] = true;
554
                            }
555
                        }
556
                    }
557
                }
558
            });
559
            return retcode[0];
560
        }
561
562
        /** called under document atomic lock */
563
        @Override
564
        protected void insertString(BaseDocument doc, int dotPos,
565
                Caret caret, String str,
566
                boolean overwrite) throws BadLocationException {
567
            // see issue #211036 - inserted string can be empty since #204450
568
            if (str.isEmpty()) {
569
                return;
570
            }
571
572
            if (completionSettingEnabled()) {
573
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
574
575
                if (bracketCompletion != null) {
576
                    // TODO - check if we're in a comment etc. and if so, do nothing
577
                    boolean handled =
578
                            bracketCompletion.beforeCharInserted(doc, dotPos, currentTarget,
579
                            str.charAt(0));
580
581
                    if (!handled) {
582
                        super.insertString(doc, dotPos, caret, str, overwrite);
583
                        handled = bracketCompletion.afterCharInserted(doc, dotPos, currentTarget,
584
                                    str.charAt(0));
585
                    }
586
587
                    return;
588
                }
589
            }
590
591
            super.insertString(doc, dotPos, caret, str, overwrite);
592
        }
593
594
        @Override
595
        protected void replaceSelection(JTextComponent target, int dotPos, Caret caret,
596
                String str, boolean overwrite) throws BadLocationException {
597
            //workaround for #209019 - regression of issue 
598
            //#204450 - Rewrite actions to use TypingHooks SPI
599
            if(str.length() == 0) {
600
                //called from BaseKit.actionPerformed():1160 with empty str argument
601
                //==> ignore this call since we are going to be called a bit later
602
                //from HtmlKit.performTextInsertion() properly with the text typed
603
                return ;
604
            }            
605
            char insertedChar = str.charAt(0);
606
            Document document = target.getDocument();
607
608
            if (document instanceof BaseDocument) {
609
                BaseDocument doc = (BaseDocument) document;
610
611
                if (completionSettingEnabled()) {
612
                    KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
613
614
                    if (bracketCompletion != null) {
615
                        try {
616
                            int caretPosition = caret.getDot();
617
618
                            boolean handled =
619
                                    bracketCompletion.beforeCharInserted(doc, caretPosition,
620
                                    target, insertedChar);
621
622
                            int p0 = Math.min(caret.getDot(), caret.getMark());
623
                            int p1 = Math.max(caret.getDot(), caret.getMark());
624
625
                            if (p0 != p1) {
626
                                doc.remove(p0, p1 - p0);
627
                            }
628
629
                            if (!handled) {
630
                                if (str.length() > 0) {
631
                                    doc.insertString(p0, str, null);
632
                                    handled = bracketCompletion.afterCharInserted(doc, dotPos, currentTarget, str.charAt(0));
633
                                }
634
635
636
                            }
637
                        } catch (BadLocationException e) {
638
                            e.printStackTrace();
639
                        }
640
641
                        return;
642
                    }
643
                }
644
            }
645
646
            super.replaceSelection(target, dotPos, caret, str, overwrite);
647
        }
648
649
    }
650
651
    public static class JspDeleteCharAction extends ExtDeleteCharAction {
652
653
        JTextComponent currentTarget;
654
655
        public JspDeleteCharAction(String nm, boolean nextChar) {
656
            super(nm, nextChar);
657
        }
658
659
        @Override
660
        public void actionPerformed(ActionEvent e, JTextComponent target) {
661
            currentTarget = target;
662
            if (target!=null){
663
                TokenSequence javaTokenSequence;
664
                AbstractDocument adoc = (AbstractDocument)target.getDocument();
665
                adoc.readLock();
666
                try {
667
                    javaTokenSequence = JspSyntaxSupport.tokenSequence(
668
                        TokenHierarchy.get(target.getDocument()),
669
                        JavaTokenId.language(),
670
                        target.getCaret().getDot() - 1);
671
                } finally {
672
                    adoc.readUnlock();
673
                }
674
675
                if (javaTokenSequence != null){
676
                    JavaKit jkit = (JavaKit)getKit(JavaKit.class);
677
                    if (jkit!=null){
678
                        Action action = jkit.getActionByName(nextChar ? DefaultEditorKit.deleteNextCharAction : DefaultEditorKit.deletePrevCharAction);
679
                        if (action != null && action instanceof JavaKit.JavaDeleteCharAction){
680
                            ((JavaKit.JavaDeleteCharAction)action).actionPerformed(e, target);
681
                            return;
682
                        }
683
                    }
684
                }
685
            }
686
            super.actionPerformed(e, target);
687
            currentTarget = null;
688
        }
689
690
        @Override
691
         protected void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException {
692
              if (completionSettingEnabled()) {
693
                KeystrokeHandler bracketCompletion = UiUtils.getBracketCompletion(doc, dotPos);
694
695
                if (bracketCompletion != null) {
696
                    bracketCompletion.charBackspaced(doc, dotPos, currentTarget, ch);
697
                    return;
698
                }
699
            }
700
701
            super.charBackspaced(doc, dotPos, caret, ch);
702
        }
703
704
    }
705
706
}
434
}
707
435
(-)a/web.core.syntax/test/unit/src/org/netbeans/modules/web/core/syntax/TypingCompletionUnitTest.java (+114 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2011 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * Contributor(s):
28
 *
29
 * The Original Software is NetBeans. The Initial Developer of the Original
30
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2011 Sun
31
 * Microsystems, Inc. All Rights Reserved.
32
 *
33
 * If you wish your version of this file to be governed by only the CDDL
34
 * or only the GPL Version 2, indicate your decision by adding
35
 * "[Contributor] elects to include this software in this distribution
36
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
37
 * single choice of license, a recipient has the option to distribute
38
 * your version of this file under either the CDDL, the GPL Version 2 or
39
 * to extend the choice of license to its licensees as provided above.
40
 * However, if you add GPL Version 2 code and therefore, elected the GPL
41
 * Version 2 license, then the option applies only if the new code is
42
 * made subject to such option by the copyright holder.
43
 */
44
45
package org.netbeans.modules.web.core.syntax;
46
47
import org.netbeans.modules.csl.editor.TypingCompletion.Context;
48
import java.util.prefs.Preferences;
49
import org.netbeans.api.editor.mimelookup.MimeLookup;
50
import org.netbeans.api.editor.settings.SimpleValueNames;
51
import org.netbeans.api.jsp.lexer.JspTokenId;
52
import org.netbeans.junit.NbTestCase;
53
import org.netbeans.modules.csl.core.CslEditorKit;
54
import org.openide.awt.AcceleratorBinding;
55
56
57
/**
58
 * Test brackets completion - unlike the original test this one
59
 * emulates real typing and tests the resulting state of the document.
60
 */
61
public class TypingCompletionUnitTest extends NbTestCase {
62
63
    public TypingCompletionUnitTest(String testMethodName) {
64
        super(testMethodName);
65
    }
66
67
    @Override
68
    protected void setUp() throws Exception {
69
        super.setUp();
70
        Preferences prefs = MimeLookup.getLookup(JspKit.JSP_MIME_TYPE).lookup(Preferences.class);
71
        prefs.putBoolean(SimpleValueNames.COMPLETION_PAIR_CHARACTERS, true);
72
        Class.forName(AcceleratorBinding.class.getName(), true, AcceleratorBinding.class.getClassLoader());
73
    }
74
75
    public void testBracketCompletion() {
76
        Context ctx = new Context(new CslEditorKit(JspKit.JSP_MIME_TYPE), JspTokenId.language(), JspKit.JSP_MIME_TYPE,
77
                "<% | %>"
78
        );
79
        ctx.typeChar('(');
80
        ctx.assertDocumentTextEquals(
81
                "<% (|) %>"
82
        );
83
    }
84
    
85
    public void testQuoteCompletion() {
86
        Context ctx = new Context(new CslEditorKit(JspKit.JSP_MIME_TYPE), JspTokenId.language(), JspKit.JSP_MIME_TYPE,
87
                "<% |"
88
        );
89
        ctx.typeChar('"');
90
        ctx.assertDocumentTextEquals(
91
                "<% \"|\""
92
        );
93
    }
94
95
    public void testDeleteQuote() {
96
        Context ctx = new Context(new CslEditorKit(JspKit.JSP_MIME_TYPE), JspTokenId.language(), JspKit.JSP_MIME_TYPE,
97
                "<% \"|\" %>"
98
        );
99
        ctx.typeChar('\b');
100
        ctx.assertDocumentTextEquals(
101
                "<% | %>"
102
        );
103
    }
104
    
105
    public void testIfBreakInsert() {
106
        Context ctx = new Context(new CslEditorKit(JspKit.JSP_MIME_TYPE), JspTokenId.language(), JspKit.JSP_MIME_TYPE,
107
                "<% \"|\" %>"
108
        );
109
        ctx.typeChar('\n');
110
        ctx.assertDocumentTextEquals(
111
                "<% \"\"\n + \"|\" %>"
112
        );
113
    }
114
}

Return to bug 217163