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 244054
Collapse All | Expand All

(-)a/options.editor/manifest.mf (-1 / +1 lines)
Lines 2-7 Link Here
2
OpenIDE-Module: org.netbeans.modules.options.editor/1
2
OpenIDE-Module: org.netbeans.modules.options.editor/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/options/editor/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/options/editor/Bundle.properties
4
OpenIDE-Module-Layer: org/netbeans/modules/options/editor/mf-layer.xml
4
OpenIDE-Module-Layer: org/netbeans/modules/options/editor/mf-layer.xml
5
OpenIDE-Module-Specification-Version: 1.59
5
OpenIDE-Module-Specification-Version: 1.60
6
AutoUpdate-Show-In-Client: false
6
AutoUpdate-Show-In-Client: false
7
7
(-)a/options.editor/nbproject/project.xml (+1 lines)
Lines 395-400 Link Here
395
                <friend>org.netbeans.modules.maven</friend>
395
                <friend>org.netbeans.modules.maven</friend>
396
                <friend>org.netbeans.modules.php.editor</friend>
396
                <friend>org.netbeans.modules.php.editor</friend>
397
                <friend>org.netbeans.modules.php.project</friend>
397
                <friend>org.netbeans.modules.php.project</friend>
398
                <friend>org.netbeans.modules.php.twig</friend>
398
                <friend>org.netbeans.modules.python.editor</friend>
399
                <friend>org.netbeans.modules.python.editor</friend>
399
                <friend>org.netbeans.modules.python.source</friend>
400
                <friend>org.netbeans.modules.python.source</friend>
400
                <friend>org.netbeans.modules.ruby</friend>
401
                <friend>org.netbeans.modules.ruby</friend>
(-)a/php.twig/manifest.mf (-1 / +1 lines)
Lines 3-6 Link Here
3
OpenIDE-Module: org.netbeans.modules.php.twig/1
3
OpenIDE-Module: org.netbeans.modules.php.twig/1
4
OpenIDE-Module-Layer: org/netbeans/modules/php/twig/resources/layer.xml
4
OpenIDE-Module-Layer: org/netbeans/modules/php/twig/resources/layer.xml
5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/twig/resources/Bundle.properties
5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/twig/resources/Bundle.properties
6
OpenIDE-Module-Specification-Version: 1.27
6
OpenIDE-Module-Specification-Version: 1.28
(-)a/php.twig/nbproject/project.xml (+18 lines)
Lines 143-148 Link Here
143
                    </run-dependency>
143
                    </run-dependency>
144
                </dependency>
144
                </dependency>
145
                <dependency>
145
                <dependency>
146
                    <code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
147
                    <build-prerequisite/>
148
                    <compile-dependency/>
149
                    <run-dependency>
150
                        <release-version>1</release-version>
151
                        <specification-version>2.18</specification-version>
152
                    </run-dependency>
153
                </dependency>
154
                <dependency>
146
                    <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
155
                    <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
147
                    <build-prerequisite/>
156
                    <build-prerequisite/>
148
                    <compile-dependency/>
157
                    <compile-dependency/>
Lines 188-193 Link Here
188
                    </run-dependency>
197
                    </run-dependency>
189
                </dependency>
198
                </dependency>
190
                <dependency>
199
                <dependency>
200
                    <code-name-base>org.netbeans.modules.options.editor</code-name-base>
201
                    <build-prerequisite/>
202
                    <compile-dependency/>
203
                    <run-dependency>
204
                        <release-version>1</release-version>
205
                        <specification-version>1.59</specification-version>
206
                    </run-dependency>
207
                </dependency>
208
                <dependency>
191
                    <code-name-base>org.netbeans.modules.parsing.api</code-name-base>
209
                    <code-name-base>org.netbeans.modules.parsing.api</code-name-base>
192
                    <build-prerequisite/>
210
                    <build-prerequisite/>
193
                    <compile-dependency/>
211
                    <compile-dependency/>
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/gsf/TwigLanguage.java (+2 lines)
Lines 60-65 Link Here
60
public class TwigLanguage extends DefaultLanguageConfig {
60
public class TwigLanguage extends DefaultLanguageConfig {
61
61
62
    public static final String TWIG_MIME_TYPE = "text/x-twig"; // NOI18N
62
    public static final String TWIG_MIME_TYPE = "text/x-twig"; // NOI18N
63
    public static final String TWIG_BLOCK_MIME_TYPE = "text/x-twig-block"; // NOI18N
64
    public static final String TWIG_VARIABLE_MIME_TYPE = "text/x-twig-variable"; // NOI18N
63
65
64
    public TwigLanguage() {
66
    public TwigLanguage() {
65
    }
67
    }
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/lexer/TwigLexerUtils.java (+61 lines)
Lines 45-56 Link Here
45
import java.util.Collections;
45
import java.util.Collections;
46
import java.util.List;
46
import java.util.List;
47
import java.util.Objects;
47
import java.util.Objects;
48
import javax.swing.text.BadLocationException;
48
import javax.swing.text.Document;
49
import javax.swing.text.Document;
49
import org.netbeans.api.lexer.Language;
50
import org.netbeans.api.lexer.Language;
50
import org.netbeans.api.lexer.Token;
51
import org.netbeans.api.lexer.Token;
51
import org.netbeans.api.lexer.TokenHierarchy;
52
import org.netbeans.api.lexer.TokenHierarchy;
52
import org.netbeans.api.lexer.TokenId;
53
import org.netbeans.api.lexer.TokenId;
53
import org.netbeans.api.lexer.TokenSequence;
54
import org.netbeans.api.lexer.TokenSequence;
55
import org.netbeans.editor.BaseDocument;
54
import org.netbeans.modules.csl.api.OffsetRange;
56
import org.netbeans.modules.csl.api.OffsetRange;
55
import org.netbeans.modules.parsing.api.Snapshot;
57
import org.netbeans.modules.parsing.api.Snapshot;
56
58
Lines 218-223 Link Here
218
        return findBackwardMatching(topTs, start, end, Collections.<TwigTokenText>emptyList());
220
        return findBackwardMatching(topTs, start, end, Collections.<TwigTokenText>emptyList());
219
    }
221
    }
220
222
223
    public static boolean textEquals(CharSequence text1, char... text2) {
224
        int len = text1.length();
225
        if (len == text2.length) {
226
            for (int i = len - 1; i >= 0; i--) {
227
                if (text1.charAt(i) != text2[i]) {
228
                    return false;
229
                }
230
            }
231
            return true;
232
        }
233
        return false;
234
    }
235
236
    public static boolean textStartWith(CharSequence text1, char text2) {
237
        int len = text1.length();
238
        if (len > 0) {
239
            return text1.charAt(0) == text2;
240
        }
241
        return false;
242
    }
243
244
    public static int getTokenBalance(BaseDocument doc, char open, char close, int offset) throws BadLocationException {
245
        TokenSequence<? extends TokenId> ts = TwigLexerUtils.getTwigMarkupTokenSequence(doc, offset);
246
        if (ts == null) {
247
            return 0;
248
        }
249
250
        int balance = 0;
251
        ts.move(offset);
252
        // check next tokens
253
        while (ts.moveNext()) {
254
            TokenId id = ts.token().id();
255
            if (id == TwigVariableTokenId.T_TWIG_PUNCTUATION || id == TwigBlockTokenId.T_TWIG_PUNCTUATION) {
256
                if (TwigLexerUtils.textEquals(ts.token().text(), close)) {
257
                    balance--;
258
                } else if (TwigLexerUtils.textEquals(ts.token().text(), open)) {
259
                    break;
260
                }
261
            }
262
        }
263
264
        // check previous tokens
265
        ts.move(offset);
266
        while (ts.movePrevious()) {
267
            TokenId id = ts.token().id();
268
            if (id == TwigVariableTokenId.T_TWIG_PUNCTUATION || id == TwigBlockTokenId.T_TWIG_PUNCTUATION) {
269
                if (TwigLexerUtils.textEquals(ts.token().text(), close)) {
270
                    balance--;
271
                } else if (TwigLexerUtils.textEquals(ts.token().text(), open)) {
272
                    balance++;
273
                }
274
                if (balance > 0) {
275
                    break;
276
                }
277
            }
278
        }
279
        return balance;
280
    }
281
221
    public interface TwigTokenText {
282
    public interface TwigTokenText {
222
        TwigTokenText NONE = new TwigTokenText() {
283
        TwigTokenText NONE = new TwigTokenText() {
223
284
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/typinghooks/TwigDeletedTextInterceptor.java (+373 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
import java.util.concurrent.atomic.AtomicBoolean;
43
import java.util.concurrent.atomic.AtomicReference;
44
import javax.swing.text.BadLocationException;
45
import javax.swing.text.Document;
46
import org.netbeans.api.editor.mimelookup.MimePath;
47
import org.netbeans.api.editor.mimelookup.MimeRegistration;
48
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
49
import org.netbeans.api.lexer.TokenId;
50
import org.netbeans.api.lexer.TokenSequence;
51
import org.netbeans.editor.BaseDocument;
52
import org.netbeans.modules.php.twig.editor.embedding.TwigHtmlEmbeddingProvider;
53
import org.netbeans.modules.php.twig.editor.gsf.TwigLanguage;
54
import org.netbeans.modules.php.twig.editor.lexer.TwigBlockTokenId;
55
import org.netbeans.modules.php.twig.editor.lexer.TwigLexerUtils;
56
import org.netbeans.modules.php.twig.editor.lexer.TwigTopTokenId;
57
import org.netbeans.modules.php.twig.editor.lexer.TwigVariableTokenId;
58
import org.netbeans.modules.php.twig.editor.ui.options.OptionsUtils;
59
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
60
61
/**
62
 * Based on PHP editor.
63
 */
64
public class TwigDeletedTextInterceptor implements DeletedTextInterceptor {
65
66
    private final boolean isTwig;
67
    private final MimePath mimePath;
68
69
    private TwigDeletedTextInterceptor(MimePath mimePath) {
70
        this.mimePath = mimePath;
71
        String path = mimePath.getPath();
72
        isTwig = path.contains(TwigLanguage.TWIG_MIME_TYPE);
73
    }
74
75
    @Override
76
    public boolean beforeRemove(Context context) throws BadLocationException {
77
        if (isTwig) {
78
            char ch = context.getText().charAt(0);
79
            if (TypingHooksUtils.isOpeningDelimiterChar(ch)) {
80
                BaseDocument doc = (BaseDocument) context.getDocument();
81
                final AtomicBoolean removed = new AtomicBoolean();
82
                final AtomicReference<BadLocationException> ble = new AtomicReference<>();
83
                doc.runAtomic(() -> {
84
                    BeforeRemover remover = BeforeRemoverFactory.create(ch);
85
                    try {
86
                        removed.set(remover.beforeRemove(context));
87
                    } catch (BadLocationException ex) {
88
                        ble.set(ex);
89
                    }
90
                });
91
                if (ble.get() != null) {
92
                    throw ble.get();
93
                }
94
                // if true, the further processing will be stopped
95
                return removed.get();
96
            }
97
        }
98
        return false;
99
    }
100
101
    @Override
102
    public void remove(Context context) throws BadLocationException {
103
        if (!isTwig) {
104
            return;
105
        }
106
        char c = context.getText().charAt(0);
107
        Remover remover = CharRemoverFactory.create(c);
108
        remover.remove(context);
109
110
    }
111
112
    @Override
113
    public void afterRemove(Context context) throws BadLocationException {
114
    }
115
116
    @Override
117
    public void cancelled(Context context) {
118
    }
119
120
    private interface BeforeRemover {
121
122
        BeforeRemover NONE = (Context context) -> {
123
            return false;
124
        };
125
126
        boolean beforeRemove(Context context) throws BadLocationException;
127
    }
128
129
    private static final class BeforeRemoverFactory {
130
131
        public static BeforeRemover create(char c) {
132
            switch (c) {
133
                case '%':
134
                    return new BlockDelimiterRemover();
135
                case '{':
136
                    return new VariableDelimiterRemover();
137
                default:
138
                    assert false;
139
                    break;
140
            }
141
            return BeforeRemover.NONE;
142
        }
143
    }
144
145
    private abstract static class DelimiterRemover implements BeforeRemover {
146
147
        @Override
148
        public boolean beforeRemove(Context context) throws BadLocationException {
149
            if (OptionsUtils.autoCompletionSmartDelimiters()) {
150
                int dotPos = context.getOffset();
151
                Document document = context.getDocument();
152
                TokenSequence<? extends TwigTopTokenId> ts = TwigLexerUtils.getTwigTokenSequence(document, dotPos);
153
                // now support {{}} and {%%}, should also check whitespaces?
154
                if (ts != null) {
155
                    ts.move(dotPos);
156
                    if (ts.movePrevious()) {
157
                        if (ts.token().id() == getOpeningId()
158
                                && ts.offset() == dotPos - 2) {
159
                            ts.move(dotPos);
160
                            if (ts.moveNext()) {
161
                                if (ts.token().id() == getClosingId()
162
                                        && ts.offset() == dotPos) {
163
                                    document.remove(dotPos - 2, 4); // {{}} or {%%}
164
                                    return true;
165
                                }
166
                            }
167
                        }
168
                    }
169
                }
170
            }
171
            return false;
172
        }
173
174
        protected abstract TwigTopTokenId getOpeningId();
175
176
        protected abstract TwigTopTokenId getClosingId();
177
    }
178
179
    private static final class BlockDelimiterRemover extends DelimiterRemover {
180
181
        @Override
182
        protected TwigTopTokenId getOpeningId() {
183
            return TwigTopTokenId.T_TWIG_BLOCK_START;
184
        }
185
186
        @Override
187
        protected TwigTopTokenId getClosingId() {
188
            return TwigTopTokenId.T_TWIG_BLOCK_END;
189
        }
190
191
    }
192
193
    private static final class VariableDelimiterRemover extends DelimiterRemover {
194
195
        @Override
196
        protected TwigTopTokenId getOpeningId() {
197
            return TwigTopTokenId.T_TWIG_VAR_START;
198
        }
199
200
        @Override
201
        protected TwigTopTokenId getClosingId() {
202
            return TwigTopTokenId.T_TWIG_VAR_END;
203
        }
204
205
    }
206
207
    private interface Remover {
208
209
        Remover NONE = (Context context) -> {
210
        };
211
212
        void remove(Context context) throws BadLocationException;
213
    }
214
215
    private static final class CharRemoverFactory {
216
217
        static Remover create(char ch) {
218
            Remover charRemover;
219
            switch (ch) {
220
                case '{':
221
                    charRemover = new CurlyBracketRemover();
222
                    break;
223
                case '(':
224
                    charRemover = new RoundBracketRemover();
225
                    break;
226
                case '[':
227
                    charRemover = new SquareBracketRemover();
228
                    break;
229
                case '\"':
230
                    charRemover = new DoubleQuoteRemover();
231
                    break;
232
                case '\'':
233
                    charRemover = new SingleQuoteRemover();
234
                    break;
235
                default:
236
                    charRemover = Remover.NONE;
237
                    break;
238
            }
239
            return charRemover;
240
        }
241
242
    }
243
244
    private abstract static class BracketRemover implements Remover {
245
246
        @Override
247
        public void remove(Context context) throws BadLocationException {
248
            if (TypingHooksUtils.isInsertMatchingEnabled()) {
249
                BaseDocument doc = (BaseDocument) context.getDocument();
250
                int dotPos = context.getOffset() - 1;
251
                if (TypingHooksUtils.sameAsExistingChar(doc, getClosingBracket(), dotPos)
252
                        && TwigLexerUtils.getTokenBalance(doc, getOpeningBracket(), getClosingBracket(), dotPos) < 0) {
253
                    // opening bracket is already removed
254
                    doc.remove(dotPos, 1);
255
                }
256
            }
257
        }
258
259
        protected abstract char getOpeningBracket();
260
261
        protected abstract char getClosingBracket();
262
263
    }
264
265
    private static final class CurlyBracketRemover extends BracketRemover {
266
267
        @Override
268
        protected char getOpeningBracket() {
269
            return '{';
270
        }
271
272
        @Override
273
        protected char getClosingBracket() {
274
            return '}';
275
        }
276
277
    }
278
279
    private static final class SquareBracketRemover extends BracketRemover {
280
281
        @Override
282
        protected char getOpeningBracket() {
283
            return '[';
284
        }
285
286
        @Override
287
        protected char getClosingBracket() {
288
            return ']';
289
        }
290
291
    }
292
293
    private static final class RoundBracketRemover extends BracketRemover {
294
295
        @Override
296
        protected char getOpeningBracket() {
297
            return '(';
298
        }
299
300
        @Override
301
        protected char getClosingBracket() {
302
            return ')';
303
        }
304
305
    }
306
307
    private abstract static class QuoteRemover implements Remover {
308
309
        @Override
310
        public void remove(Context context) throws BadLocationException {
311
            if (OptionsUtils.autoCompletionSmartQuotes()) {
312
                BaseDocument doc = (BaseDocument) context.getDocument();
313
                int dotPos = context.getOffset() - 1;
314
                TokenSequence<? extends TokenId> ts = TwigLexerUtils.getTwigMarkupTokenSequence(doc, dotPos);
315
                // check escape sequence
316
                // e.g. "\"" '\''
317
                if (ts != null) {
318
                    ts.move(dotPos);
319
                    if ((ts.moveNext() || ts.movePrevious())) {
320
                        TokenId id = ts.token().id();
321
                        if (id == TwigBlockTokenId.T_TWIG_STRING || id == TwigVariableTokenId.T_TWIG_STRING) {
322
                            if (TypingHooksUtils.isEscapeSequence(doc, dotPos)) {
323
                                doc.remove(dotPos - 1, 1);
324
                                return;
325
                            }
326
                        }
327
                    }
328
                }
329
330
                char[] match = doc.getChars(dotPos, 1);
331
                if ((match != null) && (match[0] == getQuote())) {
332
                    doc.remove(dotPos, 1);
333
                }
334
            }
335
        }
336
337
        protected abstract char getQuote();
338
339
    }
340
341
    private static final class SingleQuoteRemover extends QuoteRemover {
342
343
        @Override
344
        protected char getQuote() {
345
            return '\'';
346
        }
347
348
    }
349
350
    private static final class DoubleQuoteRemover extends QuoteRemover {
351
352
        @Override
353
        protected char getQuote() {
354
            return '"';
355
        }
356
357
    }
358
359
    @MimeRegistrations(value = {
360
        @MimeRegistration(mimeType = TwigHtmlEmbeddingProvider.TARGET_MIME_TYPE, service = DeletedTextInterceptor.Factory.class),
361
        @MimeRegistration(mimeType = TwigLanguage.TWIG_MIME_TYPE, service = DeletedTextInterceptor.Factory.class),
362
        @MimeRegistration(mimeType = TwigLanguage.TWIG_BLOCK_MIME_TYPE, service = DeletedTextInterceptor.Factory.class),
363
        @MimeRegistration(mimeType = TwigLanguage.TWIG_VARIABLE_MIME_TYPE, service = DeletedTextInterceptor.Factory.class)
364
    })
365
    public static class Factory implements DeletedTextInterceptor.Factory {
366
367
        @Override
368
        public DeletedTextInterceptor createDeletedTextInterceptor(MimePath mimePath) {
369
            return new TwigDeletedTextInterceptor(mimePath);
370
        }
371
372
    }
373
}
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/typinghooks/TwigTypedTextInterceptor.java (+413 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
import java.util.logging.Level;
43
import java.util.logging.Logger;
44
import javax.swing.text.BadLocationException;
45
import javax.swing.text.Caret;
46
import javax.swing.text.Document;
47
import javax.swing.text.JTextComponent;
48
import org.netbeans.api.editor.mimelookup.MimePath;
49
import org.netbeans.api.editor.mimelookup.MimeRegistration;
50
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
51
import org.netbeans.api.lexer.Token;
52
import org.netbeans.api.lexer.TokenId;
53
import org.netbeans.api.lexer.TokenSequence;
54
import org.netbeans.editor.BaseDocument;
55
import org.netbeans.modules.php.twig.editor.embedding.TwigHtmlEmbeddingProvider;
56
import org.netbeans.modules.php.twig.editor.gsf.TwigLanguage;
57
import org.netbeans.modules.php.twig.editor.lexer.TwigBlockTokenId;
58
import org.netbeans.modules.php.twig.editor.lexer.TwigLexerUtils;
59
import org.netbeans.modules.php.twig.editor.lexer.TwigTopTokenId;
60
import org.netbeans.modules.php.twig.editor.lexer.TwigVariableTokenId;
61
import org.netbeans.modules.php.twig.editor.ui.options.OptionsUtils;
62
import org.netbeans.spi.editor.typinghooks.TypedTextInterceptor;
63
64
public class TwigTypedTextInterceptor implements TypedTextInterceptor {
65
66
    private final MimePath mimePath;
67
    private final boolean isTwig;
68
    private boolean codeTemplateEditing;
69
    private static final Logger LOGGER = Logger.getLogger(TwigTypedTextInterceptor.class.getName());
70
71
    private TwigTypedTextInterceptor(MimePath mimePath) {
72
        this.mimePath = mimePath;
73
        String path = mimePath.getPath();
74
        isTwig = path.contains(TwigLanguage.TWIG_MIME_TYPE);
75
    }
76
77
    @Override
78
    public boolean beforeInsert(Context context) throws BadLocationException {
79
        return false;
80
    }
81
82
    @Override
83
    public void insert(MutableContext context) throws BadLocationException {
84
        if (!isTwig) {
85
            return;
86
        }
87
        Document document = context.getDocument();
88
        BaseDocument doc = (BaseDocument) document;
89
        int caretOffset = context.getOffset();
90
        String selection = context.getReplacedText();
91
        char ch = context.getText().charAt(0);
92
        if (doNotAutoCompleteQuotesAndBrackets(ch) || caretOffset == 0) {
93
            return;
94
        }
95
        TokenSequence<? extends TokenId> ts = TwigLexerUtils.getTwigMarkupTokenSequence(doc, caretOffset);
96
        if (ts == null) {
97
            // {{}} or {%%} there is no text between delimiters
98
            ts = TwigLexerUtils.getTwigTokenSequence(document, caretOffset);
99
            if (ts == null) {
100
                return;
101
            }
102
        }
103
        ts.move(caretOffset);
104
        if (!ts.moveNext() && !ts.movePrevious()) {
105
            return;
106
        }
107
108
        Token<? extends TokenId> token = ts.token();
109
        TokenId id = token.id();
110
        int tokenOffset = ts.offset();
111
        boolean skipQuote = false;
112
        boolean isInString = false;
113
114
        if (id instanceof TwigBlockTokenId || id instanceof TwigVariableTokenId) {
115
            // complete quote or bracket
116
            if (isOpeningBracket(ch) || isQuote(ch)) {
117
                if (selection != null && selection.length() > 0) {
118
                    surroundSelectionWithChars(selection, ch, context);
119
                } else {
120
                    if (id != TwigBlockTokenId.T_TWIG_STRING && id != TwigVariableTokenId.T_TWIG_STRING) {
121
                        if (isQuote(ch)) {
122
                            if (TwigLexerUtils.textStartWith(token.text(), ch)) {
123
                                skipQuote = true;
124
                            } else {
125
                                // check backward tokens
126
                                skipQuote = skipQuotes(ts, ch, true);
127
128
                                // check forward tokens
129
                                if (!skipQuote) {
130
                                    ts.move(caretOffset);
131
                                    skipQuote = skipQuotes(ts, ch, false);
132
                                }
133
                            }
134
                        }
135
                        if (!skipQuote) {
136
                            completeQuoteAndBracket(context, ch);
137
                        }
138
                    } else {
139
                        isInString = true;
140
                    }
141
                }
142
            }
143
144
            // skip the same closing char
145
            if ((isClosingBracket(ch) || isQuote(ch))
146
                    && sameAsExistingChar(doc, ch, caretOffset)) {
147
                if (isInString) {
148
                    if (!skipQuote && isQuote(ch) && !TypingHooksUtils.isEscapeSequence(doc, caretOffset)) {
149
                        skipNextChar(context, ch, document, caretOffset);
150
                    }
151
                } else {
152
                    if (!skipQuote && !isClosingBracketMissing(doc, matching(ch), ch, caretOffset)) {
153
                        skipNextChar(context, ch, document, caretOffset);
154
                    }
155
                }
156
            }
157
        } else if(id == TwigTopTokenId.T_TWIG_BLOCK_END || id == TwigTopTokenId.T_TWIG_VAR_END) {
158
            // {{}} or {%%}
159
            if ((isOpeningBracket(ch) || isQuote(ch))
160
                    && tokenOffset == caretOffset) {
161
                completeQuoteAndBracket(context, ch);
162
            }
163
        }
164
    }
165
166
    @Override
167
    public void afterInsert(Context context) throws BadLocationException {
168
        if (!isTwig) {
169
            return;
170
        }
171
        BaseDocument doc = (BaseDocument) context.getDocument();
172
        doc.runAtomicAsUser(() -> {
173
            try {
174
                afterInsertUnderWriteLock(context);
175
            } catch (BadLocationException ex) {
176
                LOGGER.log(Level.FINE, null, ex);
177
            }
178
        });
179
    }
180
181
    @Override
182
    public void cancelled(Context context) {
183
    }
184
185
    /**
186
     * Surround the selected text with chars("", '', (), {}, []).
187
     * <b>NOTE:</b> Replace the surrounding chars if the text is already surrounded with
188
     * chars.
189
     *
190
     * @param selection the selected text
191
     * @param ch the opening bracket
192
     * @param context the context
193
     */
194
    private void surroundSelectionWithChars(String selection, char ch, MutableContext context) {
195
        char firstChar = selection.charAt(0);
196
        if (firstChar != ch) {
197
            char lastChar = selection.charAt(selection.length() - 1);
198
            StringBuilder sb = new StringBuilder();
199
            sb.append(ch);
200
            if (selection.length() > 1
201
                    && (isOpeningBracket(firstChar) || isQuote(firstChar))
202
                    && lastChar == matching(firstChar)) {
203
                String innerText = selection.substring(1, selection.length() - 1);
204
                sb.append(innerText);
205
            } else {
206
                sb.append(selection);
207
            }
208
            sb.append(matching(ch));
209
            String text = sb.toString();
210
            context.setText(text, text.length());
211
        }
212
    }
213
214
    /**
215
     * Skip the quote if there is one quote backward or forward.
216
     *
217
     * @param ts the token sequence
218
     * @param quote " or '
219
     * @param backward {@code true} if check backward tokens
220
     * @return {@code true} if there is one quote, otherwise {@code false}
221
     */
222
    private boolean skipQuotes(TokenSequence<? extends TokenId> ts, char quote, boolean backward) {
223
        boolean skip = false;
224
        while (backward ? ts.movePrevious() : ts.moveNext()) {
225
            Token<? extends TokenId> token = ts.token();
226
            TokenId tokenId = token.id();
227
            if (tokenId == TwigVariableTokenId.T_TWIG_STRING
228
                    || tokenId == TwigBlockTokenId.T_TWIG_STRING) {
229
                break;
230
            }
231
            if (tokenId == TwigVariableTokenId.T_TWIG_WHITESPACE
232
                    || tokenId == TwigBlockTokenId.T_TWIG_WHITESPACE
233
                    || tokenId == TwigVariableTokenId.T_TWIG_OTHER
234
                    || tokenId == TwigBlockTokenId.T_TWIG_OTHER) {
235
                if (token.text().toString().contains(Character.toString(quote))) {
236
                    skip = true;
237
                    break;
238
                }
239
            }
240
        }
241
        return skip;
242
    }
243
244
    private void skipNextChar(MutableContext context, char ch, Document document, int dotPos) throws BadLocationException {
245
        context.setText(Character.toString(ch), 1);
246
        document.remove(dotPos, 1);
247
    }
248
249
    private void afterInsertUnderWriteLock(Context context) throws BadLocationException {
250
        JTextComponent target = context.getComponent();
251
        Caret caret = target.getCaret();
252
        BaseDocument doc = (BaseDocument) context.getDocument();
253
        int dotPos = context.getOffset();
254
        int tokenEndPos = dotPos + 1;
255
        char ch = context.getText().charAt(0);
256
        if (doNotAutoCompleteDelimiters(ch)
257
                || dotPos == 0
258
                || tokenEndPos > doc.getLength()) {
259
            return;
260
        }
261
262
        switch (ch) {
263
            case '{': // no break
264
            case '%':
265
                String mimeType = getMimeType();
266
                // do nothing in {% %} and {{ }}
267
                if (mimeType.equals(TwigLanguage.TWIG_MIME_TYPE) // in case of {{^
268
                        || mimeType.equals(TwigLanguage.TWIG_BLOCK_MIME_TYPE)
269
                        || mimeType.equals(TwigLanguage.TWIG_VARIABLE_MIME_TYPE)) {
270
                    return;
271
                }
272
                TokenSequence<? extends TwigTopTokenId> ts = TwigLexerUtils.getTwigTokenSequence(doc, tokenEndPos);
273
                if (ts == null) {
274
                    return;
275
                }
276
                ts.move(tokenEndPos);
277
                if (!ts.movePrevious() && !ts.moveNext()) {
278
                    return;
279
                }
280
281
                Token<? extends TwigTopTokenId> token = ts.token();
282
                TwigTopTokenId id = token.id();
283
                if (id == TwigTopTokenId.T_TWIG_BLOCK_START) {
284
                    completeOpeningDelimiter(doc, tokenEndPos, tokenEndPos + 1, caret, "  %}"); // NOI18N
285
                } else if (id == TwigTopTokenId.T_TWIG_VAR_START) {
286
                    completeOpeningDelimiter(doc, tokenEndPos, tokenEndPos + 1, caret, "  }}"); // NOI18N
287
                }
288
                break;
289
            default:
290
                break;
291
        }
292
    }
293
294
    private void completeOpeningDelimiter(Document doc, int tokenEndPos, int dotPos, Caret caret, String closingDelimiter) throws BadLocationException {
295
        doc.insertString(tokenEndPos, closingDelimiter, null);
296
        caret.setDot(dotPos);
297
    }
298
299
    private void completeQuoteAndBracket(MutableContext context, char bracket) throws BadLocationException {
300
        if (codeTemplateEditing) {
301
            String text = context.getText() + bracket;
302
            context.setText(text, text.length() - 1);
303
            return;
304
        }
305
306
        StringBuilder sb = new StringBuilder();
307
        sb.append(bracket);
308
        sb.append(matching(bracket));
309
        String text = sb.toString();
310
        context.setText(text, 1);
311
    }
312
313
    private String getMimeType() {
314
        int size = mimePath.size();
315
        if (size <= 0) {
316
            return ""; // NOI18N
317
        }
318
        return mimePath.getMimeType(size - 1);
319
    }
320
321
    private static boolean sameAsExistingChar(BaseDocument doc, char c, int dotPos) throws BadLocationException {
322
        if (dotPos <= 0 || doc.getLength() <= dotPos) {
323
            return false;
324
        }
325
        char nextChar = doc.getChars(dotPos, 1)[0];
326
        return nextChar == c;
327
    }
328
329
    private static boolean isBracket(char c) {
330
        return isOpeningBracket(c) || isClosingBracket(c);
331
    }
332
333
    private static boolean isOpeningBracket(char c) {
334
        switch (c) {
335
            case '(': // no break
336
            case '[': // no break
337
            case '{':
338
                return true;
339
            default:
340
                return false;
341
        }
342
    }
343
344
    private static boolean isClosingBracket(char c) {
345
        switch (c) {
346
            case ')': // no break
347
            case ']': // no break
348
            case '}':
349
                return true;
350
            default:
351
                return false;
352
        }
353
    }
354
355
    private static boolean isQuote(char ch) {
356
        return ch == '"' || ch == '\'';
357
    }
358
359
    private static char matching(char c) {
360
        switch (c) {
361
            case '"':
362
                return '"';
363
            case '\'':
364
                return '\'';
365
            case '(':
366
                return ')';
367
            case '[':
368
                return ']';
369
            case '{':
370
                return '}';
371
            case ')':
372
                return '(';
373
            case ']':
374
                return '[';
375
            case '}':
376
                return '{';
377
            default:
378
                return c;
379
        }
380
    }
381
382
    private static boolean doNotAutoCompleteQuotesAndBrackets(char c) {
383
        return (isQuote(c) && !OptionsUtils.autoCompletionSmartQuotes())
384
                || (isBracket(c) && !TypingHooksUtils.isInsertMatchingEnabled());
385
    }
386
387
    private static boolean doNotAutoCompleteDelimiters(char c) {
388
        return TypingHooksUtils.isOpeningDelimiterChar(c) && !OptionsUtils.autoCompletionSmartDelimiters();
389
    }
390
391
    private static boolean isClosingBracketMissing(BaseDocument docment, char open, char close, int dotPos) throws BadLocationException {
392
        if (!isClosingBracket(close)) {
393
            return false;
394
        }
395
        return TwigLexerUtils.getTokenBalance(docment, open, close, dotPos) > 0;
396
    }
397
398
    @MimeRegistrations(value = {
399
        @MimeRegistration(mimeType = TwigHtmlEmbeddingProvider.TARGET_MIME_TYPE, service = TypedTextInterceptor.Factory.class),
400
        @MimeRegistration(mimeType = TwigLanguage.TWIG_MIME_TYPE, service = TypedTextInterceptor.Factory.class) ,
401
        @MimeRegistration(mimeType = TwigLanguage.TWIG_BLOCK_MIME_TYPE, service = TypedTextInterceptor.Factory.class) ,
402
        @MimeRegistration(mimeType = TwigLanguage.TWIG_VARIABLE_MIME_TYPE, service = TypedTextInterceptor.Factory.class)
403
    })
404
    public static class Factory implements TypedTextInterceptor.Factory {
405
406
        @Override
407
        public TypedTextInterceptor createTypedTextInterceptor(MimePath mimePath) {
408
            return new TwigTypedTextInterceptor(mimePath);
409
        }
410
411
    }
412
413
}
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/typinghooks/TypingHooksUtils.java (+80 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
import javax.swing.text.BadLocationException;
43
import org.netbeans.editor.BaseDocument;
44
import org.netbeans.modules.csl.api.EditorOptions;
45
import org.netbeans.modules.php.twig.editor.gsf.TwigLanguage;
46
47
public final class TypingHooksUtils {
48
49
    private TypingHooksUtils() {
50
    }
51
52
    public static boolean isInsertMatchingEnabled() {
53
        EditorOptions options = EditorOptions.get(TwigLanguage.TWIG_MIME_TYPE);
54
        if (options != null) {
55
            return options.getMatchBrackets();
56
        }
57
        return true;
58
    }
59
60
    public static boolean sameAsExistingChar(BaseDocument doc, char c, int dotPos) throws BadLocationException {
61
        if (dotPos <= 0 || doc.getLength() <= dotPos) {
62
            return false;
63
        }
64
        char[] nextChars = doc.getChars(dotPos, 1);
65
        return nextChars.length > 0 && nextChars[0] == c;
66
    }
67
68
    public static boolean isEscapeSequence(BaseDocument doc, int dotPos) throws BadLocationException {
69
        if (dotPos <= 0) {
70
            return false;
71
        }
72
        char[] previousChars = doc.getChars(dotPos - 1, 1);
73
        return previousChars.length > 0 && previousChars[0] == '\\';
74
    }
75
76
    public static boolean isOpeningDelimiterChar(char c) {
77
        return c == '{' || c == '%';
78
    }
79
80
}
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/ui/options/Bundle.properties (+3 lines)
Lines 42-44 Link Here
42
TwigPanel.jLabel1.text=Toggle Comment:
42
TwigPanel.jLabel1.text=Toggle Comment:
43
TwigPanel.asTwigEverywhereRadioButton.text=as Twig comment everywhere
43
TwigPanel.asTwigEverywhereRadioButton.text=as Twig comment everywhere
44
TwigPanel.languageSensitiveRadioButton.text=language sensitive
44
TwigPanel.languageSensitiveRadioButton.text=language sensitive
45
CodeCompletionPanel.autoCompletionSmartDelimitersCheckBox.text=Use &Smart Delimiters ({{ and {%) Completion
46
CodeCompletionPanel.autoCompletionSmartQuotesCheckBox.text=&Use Smart Quotes (" and ') Completion (after text selection, e.t.c.)
47
CodeCompletionPanel.autoCompletionSmartQuotesDelimitersLabel.text=Quotes and Delimiters Completion:
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/ui/options/CodeCompletionPanel.form (+67 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8" ?>
2
3
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <AuxValues>
5
    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
6
    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
7
    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
8
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
9
    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
10
    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
11
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
12
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
13
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
14
  </AuxValues>
15
16
  <Layout>
17
    <DimensionLayout dim="0">
18
      <Group type="103" groupAlignment="0" attributes="0">
19
          <Group type="102" attributes="0">
20
              <EmptySpace max="-2" attributes="0"/>
21
              <Group type="103" groupAlignment="0" attributes="0">
22
                  <Component id="autoCompletionSmartQuotesDelimitersLabel" alignment="0" min="-2" max="-2" attributes="0"/>
23
                  <Component id="autoCompletionSmartDelimitersCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
24
                  <Component id="autoCompletionSmartQuotesCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
25
              </Group>
26
              <EmptySpace max="32767" attributes="0"/>
27
          </Group>
28
      </Group>
29
    </DimensionLayout>
30
    <DimensionLayout dim="1">
31
      <Group type="103" groupAlignment="0" attributes="0">
32
          <Group type="102" alignment="0" attributes="0">
33
              <EmptySpace max="-2" attributes="0"/>
34
              <Component id="autoCompletionSmartQuotesDelimitersLabel" min="-2" max="-2" attributes="0"/>
35
              <EmptySpace max="-2" attributes="0"/>
36
              <Component id="autoCompletionSmartQuotesCheckBox" min="-2" max="-2" attributes="0"/>
37
              <EmptySpace max="-2" attributes="0"/>
38
              <Component id="autoCompletionSmartDelimitersCheckBox" min="-2" max="-2" attributes="0"/>
39
              <EmptySpace max="32767" attributes="0"/>
40
          </Group>
41
      </Group>
42
    </DimensionLayout>
43
  </Layout>
44
  <SubComponents>
45
    <Component class="javax.swing.JLabel" name="autoCompletionSmartQuotesDelimitersLabel">
46
      <Properties>
47
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
48
          <ResourceString bundle="org/netbeans/modules/php/twig/editor/ui/options/Bundle.properties" key="CodeCompletionPanel.autoCompletionSmartQuotesDelimitersLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
49
        </Property>
50
      </Properties>
51
    </Component>
52
    <Component class="javax.swing.JCheckBox" name="autoCompletionSmartDelimitersCheckBox">
53
      <Properties>
54
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
55
          <ResourceString bundle="org/netbeans/modules/php/twig/editor/ui/options/Bundle.properties" key="CodeCompletionPanel.autoCompletionSmartDelimitersCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
56
        </Property>
57
      </Properties>
58
    </Component>
59
    <Component class="javax.swing.JCheckBox" name="autoCompletionSmartQuotesCheckBox">
60
      <Properties>
61
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
62
          <ResourceString bundle="org/netbeans/modules/php/twig/editor/ui/options/Bundle.properties" key="CodeCompletionPanel.autoCompletionSmartQuotesCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
63
        </Property>
64
      </Properties>
65
    </Component>
66
  </SubComponents>
67
</Form>
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/ui/options/CodeCompletionPanel.java (+205 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.ui.options;
41
42
import java.awt.event.ItemEvent;
43
import java.awt.event.ItemListener;
44
import java.util.HashMap;
45
import java.util.Map;
46
import java.util.prefs.Preferences;
47
import javax.swing.JComponent;
48
import org.netbeans.modules.options.editor.spi.PreferencesCustomizer;
49
import org.openide.util.HelpCtx;
50
51
/**
52
 * Based on PHP editor.
53
 */
54
public class CodeCompletionPanel extends javax.swing.JPanel {
55
56
    private static final long serialVersionUID = -3783460333563884705L;
57
58
    private final Preferences preferences;
59
    private final ItemListener defaultCheckBoxListener = new DefaultCheckBoxListener();
60
    private final Map<String, Object> id2Saved = new HashMap<>();
61
62
    public CodeCompletionPanel(Preferences preferences) {
63
        assert preferences != null;
64
65
        this.preferences = preferences;
66
67
        initComponents();
68
69
        initAutoCompletion();
70
    }
71
72
    public static PreferencesCustomizer.Factory getCustomizerFactory() {
73
        return (Preferences pref) -> new CodeCompletionPreferencesCustomizer(pref);
74
    }
75
76
    private void initAutoCompletion() {
77
        boolean codeCompletionSmartQuotes = preferences.getBoolean(
78
                OptionsUtils.AUTO_COMPLETION_SMART_QUOTES,
79
                OptionsUtils.AUTO_COMPLETION_SMART_QUOTES_DEFAULT
80
        );
81
        boolean codeCompletionSmartDelimiters = preferences.getBoolean(
82
                OptionsUtils.AUTO_COMPLETION_SMART_DELIMITERS,
83
                OptionsUtils.AUTO_COMPLETION_SMART_DELIMITERS_DEFAULT
84
        );
85
        autoCompletionSmartQuotesCheckBox.setSelected(codeCompletionSmartQuotes);
86
        autoCompletionSmartQuotesCheckBox.addItemListener(defaultCheckBoxListener);
87
        autoCompletionSmartDelimitersCheckBox.setSelected(codeCompletionSmartDelimiters);
88
        autoCompletionSmartDelimitersCheckBox.addItemListener(defaultCheckBoxListener);
89
        id2Saved.put(OptionsUtils.AUTO_COMPLETION_SMART_QUOTES, autoCompletionSmartQuotesCheckBox.isSelected());
90
        id2Saved.put(OptionsUtils.AUTO_COMPLETION_SMART_DELIMITERS, autoCompletionSmartDelimitersCheckBox.isSelected());
91
    }
92
93
    void validateData() {
94
        preferences.putBoolean(OptionsUtils.AUTO_COMPLETION_SMART_QUOTES, autoCompletionSmartQuotesCheckBox.isSelected());
95
        preferences.putBoolean(OptionsUtils.AUTO_COMPLETION_SMART_DELIMITERS, autoCompletionSmartDelimitersCheckBox.isSelected());
96
    }
97
98
    /**
99
     * This method is called from within the constructor to initialize the form.
100
     * WARNING: Do NOT modify this code. The content of this method is always
101
     * regenerated by the Form Editor.
102
     */
103
    @SuppressWarnings("unchecked")
104
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
105
    private void initComponents() {
106
107
        autoCompletionSmartQuotesDelimitersLabel = new javax.swing.JLabel();
108
        autoCompletionSmartDelimitersCheckBox = new javax.swing.JCheckBox();
109
        autoCompletionSmartQuotesCheckBox = new javax.swing.JCheckBox();
110
111
        org.openide.awt.Mnemonics.setLocalizedText(autoCompletionSmartQuotesDelimitersLabel, org.openide.util.NbBundle.getMessage(CodeCompletionPanel.class, "CodeCompletionPanel.autoCompletionSmartQuotesDelimitersLabel.text")); // NOI18N
112
113
        org.openide.awt.Mnemonics.setLocalizedText(autoCompletionSmartDelimitersCheckBox, org.openide.util.NbBundle.getMessage(CodeCompletionPanel.class, "CodeCompletionPanel.autoCompletionSmartDelimitersCheckBox.text")); // NOI18N
114
115
        org.openide.awt.Mnemonics.setLocalizedText(autoCompletionSmartQuotesCheckBox, org.openide.util.NbBundle.getMessage(CodeCompletionPanel.class, "CodeCompletionPanel.autoCompletionSmartQuotesCheckBox.text")); // NOI18N
116
117
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
118
        this.setLayout(layout);
119
        layout.setHorizontalGroup(
120
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
121
            .addGroup(layout.createSequentialGroup()
122
                .addContainerGap()
123
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
124
                    .addComponent(autoCompletionSmartQuotesDelimitersLabel)
125
                    .addComponent(autoCompletionSmartDelimitersCheckBox)
126
                    .addComponent(autoCompletionSmartQuotesCheckBox))
127
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
128
        );
129
        layout.setVerticalGroup(
130
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
131
            .addGroup(layout.createSequentialGroup()
132
                .addContainerGap()
133
                .addComponent(autoCompletionSmartQuotesDelimitersLabel)
134
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
135
                .addComponent(autoCompletionSmartQuotesCheckBox)
136
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
137
                .addComponent(autoCompletionSmartDelimitersCheckBox)
138
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
139
        );
140
    }// </editor-fold>//GEN-END:initComponents
141
142
143
    // Variables declaration - do not modify//GEN-BEGIN:variables
144
    private javax.swing.JCheckBox autoCompletionSmartDelimitersCheckBox;
145
    private javax.swing.JCheckBox autoCompletionSmartQuotesCheckBox;
146
    private javax.swing.JLabel autoCompletionSmartQuotesDelimitersLabel;
147
    // End of variables declaration//GEN-END:variables
148
149
    private final class DefaultCheckBoxListener implements ItemListener {
150
151
        @Override
152
        public void itemStateChanged(ItemEvent e) {
153
            validateData();
154
        }
155
    }
156
157
    static final class CodeCompletionPreferencesCustomizer implements PreferencesCustomizer {
158
159
        private final Preferences preferences;
160
        private CodeCompletionPanel component;
161
162
        private CodeCompletionPreferencesCustomizer(Preferences preferences) {
163
            this.preferences = preferences;
164
        }
165
166
        @Override
167
        public String getId() {
168
            throw new UnsupportedOperationException("Not supported yet.");
169
        }
170
171
        @Override
172
        public String getDisplayName() {
173
            throw new UnsupportedOperationException("Not supported yet.");
174
        }
175
176
        @Override
177
        public HelpCtx getHelpCtx() {
178
            return new HelpCtx("org.netbeans.modules.php.twig.editor.ui.options.CodeCompletionPanel");
179
        }
180
181
        @Override
182
        public JComponent getComponent() {
183
            if (component == null) {
184
                component = new CodeCompletionPanel(preferences);
185
            }
186
            return component;
187
        }
188
    }
189
190
    String getSavedValue(String key) {
191
        return id2Saved.get(key).toString();
192
    }
193
194
    public static final class CustomCustomizerImpl extends PreferencesCustomizer.CustomCustomizer {
195
196
        @Override
197
        public String getSavedValue(PreferencesCustomizer customCustomizer, String key) {
198
            if (customCustomizer instanceof CodeCompletionPreferencesCustomizer) {
199
                return ((CodeCompletionPanel) customCustomizer.getComponent()).getSavedValue(key);
200
            }
201
            return null;
202
        }
203
    }
204
205
}
(-)a/php.twig/src/org/netbeans/modules/php/twig/editor/ui/options/OptionsUtils.java (+109 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.ui.options;
41
42
import java.util.concurrent.atomic.AtomicBoolean;
43
import java.util.prefs.PreferenceChangeEvent;
44
import java.util.prefs.PreferenceChangeListener;
45
import java.util.prefs.Preferences;
46
import org.netbeans.api.editor.mimelookup.MimeLookup;
47
import org.netbeans.modules.php.twig.editor.gsf.TwigLanguage;
48
import org.openide.util.WeakListeners;
49
50
/**
51
 * Based on PHP. Options for Auto Completion.
52
 */
53
public final class OptionsUtils {
54
55
    private static final AtomicBoolean INITED = new AtomicBoolean(false);
56
57
    public static final String AUTO_COMPLETION_SMART_QUOTES = "twigAutoCompletionSmartQuotes"; // NOI18N
58
    public static final String AUTO_COMPLETION_SMART_DELIMITERS = "twigAutoCompletionSmartDelimiters"; // NOI18N
59
60
    private static Boolean autoCompletionSmartQuotes = null;
61
    private static Boolean autoCompletionSmartDelimiters = null;
62
63
    // default values
64
    public static final boolean AUTO_COMPLETION_SMART_QUOTES_DEFAULT = true;
65
    public static final boolean AUTO_COMPLETION_SMART_DELIMITERS_DEFAULT = true;
66
67
    private static Preferences PREFERENCES;
68
69
    private static final PreferenceChangeListener PREFERENCES_TRACKER = new PreferenceChangeListener() {
70
        @Override
71
        public void preferenceChange(PreferenceChangeEvent evt) {
72
            String settingName = evt == null ? null : evt.getKey();
73
74
            if (settingName == null || AUTO_COMPLETION_SMART_QUOTES.equals(settingName)) {
75
                autoCompletionSmartQuotes = PREFERENCES.getBoolean(
76
                        AUTO_COMPLETION_SMART_QUOTES,
77
                        AUTO_COMPLETION_SMART_QUOTES_DEFAULT);
78
            }
79
            if (settingName == null || AUTO_COMPLETION_SMART_DELIMITERS.equals(settingName)) {
80
                autoCompletionSmartDelimiters = PREFERENCES.getBoolean(
81
                        AUTO_COMPLETION_SMART_DELIMITERS,
82
                        AUTO_COMPLETION_SMART_DELIMITERS_DEFAULT);
83
            }
84
        }
85
    };
86
87
    private OptionsUtils() {
88
    }
89
90
    public static boolean autoCompletionSmartQuotes() {
91
        lazyInit();
92
        assert autoCompletionSmartQuotes != null;
93
        return autoCompletionSmartQuotes;
94
    }
95
96
    public static boolean autoCompletionSmartDelimiters() {
97
        lazyInit();
98
        assert autoCompletionSmartDelimiters != null;
99
        return autoCompletionSmartDelimiters;
100
    }
101
102
    private static void lazyInit() {
103
        if (INITED.compareAndSet(false, true)) {
104
            PREFERENCES = MimeLookup.getLookup(TwigLanguage.TWIG_MIME_TYPE).lookup(Preferences.class);
105
            PREFERENCES.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, PREFERENCES_TRACKER, PREFERENCES));
106
            PREFERENCES_TRACKER.preferenceChange(null);
107
        }
108
    }
109
}
(-)a/php.twig/src/org/netbeans/modules/php/twig/resources/layer.xml (+17 lines)
Lines 264-269 Link Here
264
                <file name="x-twig" url="example.html.twig"/>
264
                <file name="x-twig" url="example.html.twig"/>
265
            </folder>
265
            </folder>
266
        </folder>
266
        </folder>
267
        <folder name="Editor">
268
            <folder name="CodeCompletion">
269
                <folder name="text">
270
                    <folder name="x-twig">
271
                        <file name="TwigCodeCompletion.instance">
272
                            <attr name="instanceOf" stringvalue="org.netbeans.modules.options.editor.spi.PreferencesCustomizer$Factory"/>
273
                            <attr name="instanceCreate" methodvalue="org.netbeans.modules.php.twig.editor.ui.options.CodeCompletionPanel.getCustomizerFactory"/>
274
                            <attr name="position" intvalue="100"/>
275
                        </file>
276
                        <file name="TwigSpecificCustomCustomizer.instance">
277
                            <attr name="instanceCreate" newvalue="org.netbeans.modules.php.twig.editor.ui.options.CodeCompletionPanel$CustomCustomizerImpl"/>
278
                            <attr name="position" intvalue="110"/>
279
                        </file>
280
                    </folder>
281
                </folder>
282
            </folder>
283
        </folder>
267
    </folder>
284
    </folder>
268
    <folder name="Templates">
285
    <folder name="Templates">
269
        <folder name="Scripting">
286
        <folder name="Scripting">
(-)a/php.twig/test/unit/src/org/netbeans/modules/php/twig/editor/typinghooks/TwigDeletedTextInterceptorTest.java (+391 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
public class TwigDeletedTextInterceptorTest extends TwigTypinghooksTestBase {
43
44
    public TwigDeletedTextInterceptorTest(String testName) {
45
        super(testName);
46
    }
47
48
    // Block
49
    // Delimiter
50
    public void testDeleteBlockDelimiter_01() throws Exception {
51
        deleteChar("{%^%}", "^");
52
    }
53
54
    public void testDeleteBlockDelimiter_02() throws Exception {
55
        deleteChar("{%%^}", "{%^}");
56
    }
57
58
    public void testDeleteBlockDelimiter_03() throws Exception {
59
        deleteChar("{^%%}", "^%%}");
60
    }
61
62
    public void testDeleteBlockDelimiter_04() throws Exception {
63
        deleteChar("{%%}^", "{%%^");
64
    }
65
66
    // Double Quote
67
    public void testDeleteDoubleQuoteInBlock_01() throws Exception {
68
        deleteChar("{% set value = \"^\" %}", "{% set value = ^ %}");
69
    }
70
71
    public void testDeleteDoubleQuoteInBlock_02() throws Exception {
72
        deleteChar("{% set value = \"^test\" %}", "{% set value = ^test\" %}");
73
    }
74
75
    public void testDeleteDoubleQuoteInBlock_03() throws Exception {
76
        deleteChar("{% set value = \"test\"^ %}", "{% set value = \"test^ %}");
77
    }
78
79
    public void testDeleteDoubleQuoteInBlock_04() throws Exception {
80
        deleteChar("{% set value = \"\\\"^\" %}", "{% set value = \"^\" %}");
81
    }
82
83
    public void testDeleteDoubleQuoteInBlock_05() throws Exception {
84
        deleteChar("{% set value = \"te\\\"^st\" %}", "{% set value = \"te^st\" %}");
85
    }
86
87
    public void testDeleteDoubleQuoteInBlock_06() throws Exception {
88
        deleteChar("{% set value = \"\\\"^test\" %}", "{% set value = \"^test\" %}");
89
    }
90
91
    public void testDeleteDoubleQuoteInBlock_07() throws Exception {
92
        deleteChar("{% set value = \"test\\\"^\" %}", "{% set value = \"test^\" %}");
93
    }
94
95
    public void testDeleteDoubleQuoteInBlock_08() throws Exception {
96
        deleteChar("{% set value = '\\\"^' %}", "{% set value = '^' %}");
97
    }
98
99
    public void testDeleteDoubleQuoteInBlock_09() throws Exception {
100
        deleteChar("{% set value = '\\\"^test' %}", "{% set value = '^test' %}");
101
    }
102
103
    public void testDeleteDoubleQuoteInBlock_10() throws Exception {
104
        deleteChar("{% set value = 'te\\\"^st' %}", "{% set value = 'te^st' %}");
105
    }
106
107
    public void testDeleteDoubleQuoteInBlock_11() throws Exception {
108
        deleteChar("{% set value = 'test\\\"^' %}", "{% set value = 'test^' %}");
109
    }
110
111
    // Single Quote
112
    public void testDeleteSingleQuoteInBlock_01() throws Exception {
113
        deleteChar("{% set value = '^' %}", "{% set value = ^ %}");
114
    }
115
116
    public void testDeleteSingleQuoteInBlock_02() throws Exception {
117
        deleteChar("{% set value = '^test' %}", "{% set value = ^test' %}");
118
    }
119
120
    public void testDeleteSingleQuoteInBlock_03() throws Exception {
121
        deleteChar("{% set value = 'test'^ %}", "{% set value = 'test^ %}");
122
    }
123
124
    public void testDeleteSingleQuoteInBlock_04() throws Exception {
125
        deleteChar("{% set value = '\\'^' %}", "{% set value = '^' %}");
126
    }
127
128
    public void testDeleteSingleQuoteInBlock_05() throws Exception {
129
        deleteChar("{% set value = 'te\\'^st' %}", "{% set value = 'te^st' %}");
130
    }
131
132
    public void testDeleteSingleQuoteInBlock_06() throws Exception {
133
        deleteChar("{% set value = '\\'^test' %}", "{% set value = '^test' %}");
134
    }
135
136
    public void testDeleteSingleQuoteInBlock_07() throws Exception {
137
        deleteChar("{% set value = 'test\\'^' %}", "{% set value = 'test^' %}");
138
    }
139
140
    public void testDeleteSingleQuoteInBlock_08() throws Exception {
141
        deleteChar("{% set value = \"\\'^\" %}", "{% set value = \"^\" %}");
142
    }
143
144
    public void testDeleteSingleQuoteInBlock_09() throws Exception {
145
        deleteChar("{% set value = \"\\'^test\" %}", "{% set value = \"^test\" %}");
146
    }
147
148
    public void testDeleteSingleQuoteInBlock_10() throws Exception {
149
        deleteChar("{% set value = \"te\\'^st\" %}", "{% set value = \"te^st\" %}");
150
    }
151
152
    public void testDeleteSingleQuoteInBlock_11() throws Exception {
153
        deleteChar("{% set value = \"test\\'^\" %}", "{% set value = \"test^\" %}");
154
    }
155
156
    // Curly
157
    public void testDeleteCurlyInBlock_01() throws Exception {
158
        deleteChar("{% set value = {^} %}", "{% set value = ^ %}");
159
    }
160
161
    public void testDeleteCurlyInBlock_02() throws Exception {
162
        deleteChar("{% set value = {}^ %}", "{% set value = {^ %}");
163
    }
164
165
    public void testDeleteCurlyInBlock_03() throws Exception {
166
        deleteChar("{% set value = {\"test\": {^}} %}", "{% set value = {\"test\": ^} %}");
167
    }
168
169
    public void testDeleteCurlyInBlock_04() throws Exception {
170
        deleteChar("{% set value = {\"test\": {^} %}", "{% set value = {\"test\": ^} %}");
171
    }
172
173
    public void testDeleteCurlyInBlock_05() throws Exception {
174
        deleteChar("{% set value = {\"test\": {{^} %}", "{% set value = {\"test\": {^} %}");
175
    }
176
177
    // Parenthesis
178
    public void testDeleteParenthesisInBlock_01() throws Exception {
179
        deleteChar("{% (^) %}", "{% ^ %}");
180
    }
181
182
    public void testDeleteParenthesisInBlock_02() throws Exception {
183
        deleteChar("{% ()^ %}", "{% (^ %}");
184
    }
185
186
    public void testDeleteParenthesisInBlock_03() throws Exception {
187
        deleteChar("{% ((^)) %}", "{% (^) %}");
188
    }
189
190
    public void testDeleteParenthesisInBlock_04() throws Exception {
191
        deleteChar("{% ((^) %}", "{% (^) %}");
192
    }
193
194
    public void testDeleteParenthesisInBlock_05() throws Exception {
195
        deleteChar("{% (((^) %}", "{% ((^) %}");
196
    }
197
198
    // Bracket
199
    public void testDeleteBracketInBlock_01() throws Exception {
200
        deleteChar("{% set value = [^] %}", "{% set value = ^ %}");
201
    }
202
203
    public void testDeleteBracketInBlock_02() throws Exception {
204
        deleteChar("{% set value = []^ %}", "{% set value = [^ %}");
205
    }
206
207
    public void testDeleteBracketInBlock_03() throws Exception {
208
        deleteChar("{% set value = [\"test\", [^]] %}", "{% set value = [\"test\", ^] %}");
209
    }
210
211
    public void testDeleteBracketInBlock_04() throws Exception {
212
        deleteChar("{% set value = [\"test\": [^] %}", "{% set value = [\"test\": ^] %}");
213
    }
214
215
    public void testDeleteBracketInBlock_05() throws Exception {
216
        deleteChar("{% set value = [\"test\": [[^] %}", "{% set value = [\"test\": [^] %}");
217
    }
218
219
    // Variable
220
    // Delimiter
221
    public void testDeleteVariableDelimiter_01() throws Exception {
222
        deleteChar("{{^}}", "^");
223
    }
224
225
    public void testDeleteVariableDelimiter_02() throws Exception {
226
        deleteChar("{{}^}", "{{^}");
227
    }
228
229
    public void testDeleteVariableDelimiter_03() throws Exception {
230
        deleteChar("{^{}}", "^{}}");
231
    }
232
233
    public void testDeleteVariableDelimiter_04() throws Exception {
234
        deleteChar("{{}}^", "{{}^");
235
    }
236
237
    // Double Quote
238
    public void testDeleteDoubleQuoteInVariable_01() throws Exception {
239
        deleteChar("{{\"^\"}}", "{{^}}");
240
        deleteChar("{{ \"^\" }}", "{{ ^ }}");
241
    }
242
243
    public void testDeleteDoubleQuoteInVariable_02() throws Exception {
244
        deleteChar("{{ \"^test\" }}", "{{ ^test\" }}");
245
    }
246
247
    public void testDeleteDoubleQuoteInVariable_03() throws Exception {
248
        deleteChar("{{ \"test\"^ }}", "{{ \"test^ }}");
249
    }
250
251
    public void testDeleteDoubleQuoteInVariable_04() throws Exception {
252
        deleteChar("{{ \"\\\"^\" }}", "{{ \"^\" }}");
253
    }
254
255
    public void testDeleteDoubleQuoteInVariable_05() throws Exception {
256
        deleteChar("{{ \"te\\\"^st\" }}", "{{ \"te^st\" }}");
257
    }
258
259
    public void testDeleteDoubleQuoteInVariable_06() throws Exception {
260
        deleteChar("{{ \"\\\"^test\" }}", "{{ \"^test\" }}");
261
    }
262
263
    public void testDeleteDoubleQuoteInVariable_07() throws Exception {
264
        deleteChar("{{ \"test\\\"^\" }}", "{{ \"test^\" }}");
265
    }
266
267
    public void testDeleteDoubleQuoteInVariable_08() throws Exception {
268
        deleteChar("{{ '\\\"^' }}", "{{ '^' }}");
269
    }
270
271
    public void testDeleteDoubleQuoteInVariable_09() throws Exception {
272
        deleteChar("{{ '\\\"^test' }}", "{{ '^test' }}");
273
    }
274
275
    public void testDeleteDoubleQuoteInVariable_10() throws Exception {
276
        deleteChar("{{ 'te\\\"^st' }}", "{{ 'te^st' }}");
277
    }
278
279
    public void testDeleteDoubleQuoteInVariable_11() throws Exception {
280
        deleteChar("{{ 'test\\\"^' }}", "{{ 'test^' }}");
281
    }
282
283
    // Single Quote
284
    public void testDeleteSingleQuoteInVariable_01() throws Exception {
285
        deleteChar("{{ '^' }}", "{{ ^ }}");
286
    }
287
288
    public void testDeleteSingleQuoteInVariable_02() throws Exception {
289
        deleteChar("{{ '^test' }}", "{{ ^test' }}");
290
    }
291
292
    public void testDeleteSingleQuoteInVariable_03() throws Exception {
293
        deleteChar("{{ 'test'^ }}", "{{ 'test^ }}");
294
    }
295
296
    public void testDeleteSingleQuoteInVariable_04() throws Exception {
297
        deleteChar("{{ '\\'^' }}", "{{ '^' }}");
298
    }
299
300
    public void testDeleteSingleQuoteInVariable_05() throws Exception {
301
        deleteChar("{{ 'te\\'^st' }}", "{{ 'te^st' }}");
302
    }
303
304
    public void testDeleteSingleQuoteInVariable_06() throws Exception {
305
        deleteChar("{{ '\\'^test' }}", "{{ '^test' }}");
306
    }
307
308
    public void testDeleteSingleQuoteInVariable_07() throws Exception {
309
        deleteChar("{{ 'test\\'^' }}", "{{ 'test^' }}");
310
    }
311
312
    public void testDeleteSingleQuoteInVariable_08() throws Exception {
313
        deleteChar("{{ \"\\'^\" }}", "{{ \"^\" }}");
314
    }
315
316
    public void testDeleteSingleQuoteInVariable_09() throws Exception {
317
        deleteChar("{{ \"\\'^test\" }}", "{{ \"^test\" }}");
318
    }
319
320
    public void testDeleteSingleQuoteInVariable_10() throws Exception {
321
        deleteChar("{{ \"te\\'^st\" }}", "{{ \"te^st\" }}");
322
    }
323
324
    public void testDeleteSingleQuoteInVariable_11() throws Exception {
325
        deleteChar("{{ \"test\\'^\" }}", "{{ \"test^\" }}");
326
    }
327
328
    // Curly : this can be used in variable?
329
    public void testDeleteCurlyInVariable_01() throws Exception {
330
        deleteChar("{{ {^} }}", "{{ ^ }}");
331
    }
332
333
    public void testDeleteCurlyInVariable_02() throws Exception {
334
        deleteChar("{{ {}^ }}", "{{ {^ }}");
335
    }
336
337
    public void testDeleteCurlyInVariable_03() throws Exception {
338
        deleteChar("{{ {\"test\": {^} } }}", "{{ {\"test\": ^ } }}");
339
    }
340
341
    public void testDeleteCurlyInVariable_04() throws Exception {
342
        deleteChar("{{ {\"test\": {^} }}", "{{ {\"test\": ^} }}");
343
    }
344
345
    public void testDeleteCurlyInVariable_05() throws Exception {
346
        deleteChar("{{ {\"test\": {{^} }}", "{{ {\"test\": {^} }}");
347
    }
348
349
    // Parenthesis
350
    public void testDeleteParenthesisInVariable_01() throws Exception {
351
        deleteChar("{{(^) }}", "{{^ }}");
352
    }
353
354
    public void testDeleteParenthesisInVariable_02() throws Exception {
355
        deleteChar("{{()^ }}", "{{(^ }}");
356
    }
357
358
    public void testDeleteParenthesisInVariable_03() throws Exception {
359
        deleteChar("{{((^)) }}", "{{(^) }}");
360
    }
361
362
    public void testDeleteParenthesisInVariable_04() throws Exception {
363
        deleteChar("{{((^) }}", "{{(^) }}");
364
    }
365
366
    public void testDeleteParenthesisInVariable_05() throws Exception {
367
        deleteChar("{{(((^) }}", "{{((^) }}");
368
    }
369
370
    // Bracket
371
    public void testDeleteBracketInVariable_01() throws Exception {
372
        deleteChar("{{ [^] }}", "{{ ^ }}");
373
    }
374
375
    public void testDeleteBracketInVariable_02() throws Exception {
376
        deleteChar("{{ []^ }}", "{{ [^ }}");
377
    }
378
379
    public void testDeleteBracketInVariable_03() throws Exception {
380
        deleteChar("{{ [\"test\", [^]] }}", "{{ [\"test\", ^] }}");
381
    }
382
383
    public void testDeleteBracketInVariable_04() throws Exception {
384
        deleteChar("{{ [\"test\": [^] }}", "{{ [\"test\": ^] }}");
385
    }
386
387
    public void testDeleteBracketInVariable_05() throws Exception {
388
        deleteChar("{{ [\"test\": [[^] }}", "{{ [\"test\": [^] }}");
389
    }
390
391
}
(-)a/php.twig/test/unit/src/org/netbeans/modules/php/twig/editor/typinghooks/TwigTypedTextInterceptorTest.java (+860 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
public class TwigTypedTextInterceptorTest extends TwigTypinghooksTestBase {
43
44
    public TwigTypedTextInterceptorTest(String testName) {
45
        super(testName);
46
    }
47
48
    private void insertChar(String original, char insertText, String expected) throws Exception {
49
        insertChar(original, insertText, expected, null);
50
    }
51
52
    private void insertChar(String original, char insertText, String expected, String selection) throws Exception {
53
        insertChar(original, insertText, expected, selection, false);
54
    }
55
56
    // surround with quotes or brackets
57
    public void testSurroundWithDoubleQuoteInBlock_01() throws Exception {
58
        String original = "{% test^ %}";
59
        String expected = "{% \"test\"^ %}";
60
        insertChar(original, '"', expected, "test");
61
    }
62
63
    public void testSurroundWithDoubleQuoteInBlock_02() throws Exception {
64
        String original = "{% test^ %}";
65
        String expected = "{% te\"st\"^ %}";
66
        insertChar(original, '"', expected, "st");
67
    }
68
69
    public void testSurroundWithDoubleQuoteInBlock_03() throws Exception {
70
        String original = "{% 'test'^ %}";
71
        String expected = "{% \"test\"^ %}";
72
        insertChar(original, '"', expected, "'test'");
73
    }
74
75
    public void testSurroundWithSingleQuoteInBlock_01() throws Exception {
76
        String original = "{% test^ %}";
77
        String expected = "{% 'test'^ %}";
78
        insertChar(original, '\'', expected, "test");
79
    }
80
81
    public void testSurroundWithSingleQuoteInBlock_02() throws Exception {
82
        String original = "{% test^ %}";
83
        String expected = "{% te'st'^ %}";
84
        insertChar(original, '\'', expected, "st");
85
    }
86
87
    public void testSurroundWithSingleQuoteInBlock_03() throws Exception {
88
        String original = "{% \"test\"^ %}";
89
        String expected = "{% 'test'^ %}";
90
        insertChar(original, '\'', expected, "\"test\"");
91
    }
92
93
    public void testSurroundWithCurlyInBlock_01() throws Exception {
94
        String original = "{% test^ %}";
95
        String expected = "{% {test}^ %}";
96
        insertChar(original, '{', expected, "test");
97
    }
98
99
    public void testSurroundWithCurlyInBlock_02() throws Exception {
100
        String original = "{% test^ %}";
101
        String expected = "{% te{st}^ %}";
102
        insertChar(original, '{', expected, "st");
103
    }
104
105
    public void testSurroundWithCurlyInBlock_03() throws Exception {
106
        String original = "{% \"test\"^ %}";
107
        String expected = "{% {test}^ %}";
108
        insertChar(original, '{', expected, "\"test\"");
109
    }
110
111
    public void testSurroundWithParenthesisInBlock_01() throws Exception {
112
        String original = "{% test^ %}";
113
        String expected = "{% (test)^ %}";
114
        insertChar(original, '(', expected, "test");
115
    }
116
117
    public void testSurroundWithParenthesisInBlock_02() throws Exception {
118
        String original = "{% test^ %}";
119
        String expected = "{% te(st)^ %}";
120
        insertChar(original, '(', expected, "st");
121
    }
122
123
    public void testSurroundWithParenthesisInBlock_03() throws Exception {
124
        String original = "{% \"test\"^ %}";
125
        String expected = "{% (test)^ %}";
126
        insertChar(original, '(', expected, "\"test\"");
127
    }
128
129
    public void testSurroundWithBracketInBlock_01() throws Exception {
130
        String original = "{% test^ %}";
131
        String expected = "{% [test]^ %}";
132
        insertChar(original, '[', expected, "test");
133
    }
134
135
    public void testSurroundWithBracketInBlock_02() throws Exception {
136
        String original = "{% tes^t %}";
137
        String expected = "{% t[es]^t %}";
138
        insertChar(original, '[', expected, "es");
139
    }
140
141
    public void testSurroundWithBracketInBlock_03() throws Exception {
142
        String original = "{% \"test\"^ %}";
143
        String expected = "{% [test]^ %}";
144
        insertChar(original, '[', expected, "\"test\"");
145
    }
146
147
    // complete quotes and brackets
148
    public void testInsertDoubleQuoteInBlock_01() throws Exception {
149
        String original = "{% ^ %}";
150
        String expected = "{% \"^\" %}";
151
        insertChar(original, '"', expected);
152
    }
153
154
    public void testInsertDoubleQuoteInBlock_02() throws Exception {
155
        String original = "{% \"^ %}";
156
        String expected = "{% \"\"^ %}";
157
        insertChar(original, '"', expected);
158
    }
159
160
    public void testInsertDoubleQuoteInBlock_03() throws Exception {
161
        String original = "{% ^\" %}";
162
        String expected = "{% \"^\" %}";
163
        insertChar(original, '"', expected);
164
    }
165
166
    public void testInsertDoubleQuoteInBlock_04() throws Exception {
167
        String original = "{% \"test^ %}";
168
        String expected = "{% \"test\"^ %}";
169
        insertChar(original, '"', expected);
170
    }
171
172
    public void testInsertDoubleQuoteInBlock_05() throws Exception {
173
        String original = "{% ^test\" %}";
174
        String expected = "{% \"^test\" %}";
175
        insertChar(original, '"', expected);
176
    }
177
178
    public void testInsertDoubleQuoteInBlock_06() throws Exception {
179
        String original = "{% \"test\" %}^";
180
        String expected = "{% \"test\" %}\"^";
181
        insertChar(original, '"', expected);
182
    }
183
184
    public void testInsertDoubleQuoteInBlock_07() throws Exception {
185
        String original = "{%^%}";
186
        String expected = "{%\"^\"%}";
187
        insertChar(original, '"', expected);
188
    }
189
190
    public void testInsertSingleQuoteInBlock_01() throws Exception {
191
        String original = "{% ^ %}";
192
        String expected = "{% '^' %}";
193
        insertChar(original, '\'', expected);
194
    }
195
196
    public void testInsertSingleQuoteInBlock_02() throws Exception {
197
        String original = "{% '^ %}";
198
        String expected = "{% ''^ %}";
199
        insertChar(original, '\'', expected);
200
    }
201
202
    public void testInsertSingleQuoteInBlock_03() throws Exception {
203
        String original = "{% ^' %}";
204
        String expected = "{% '^' %}";
205
        insertChar(original, '\'', expected);
206
    }
207
208
    public void testInsertSingleQuoteInBlock_04() throws Exception {
209
        String original = "{% 'test^ %}";
210
        String expected = "{% 'test'^ %}";
211
        insertChar(original, '\'', expected);
212
    }
213
214
    public void testInsertSingleQuoteInBlock_05() throws Exception {
215
        String original = "{% ^test' %}";
216
        String expected = "{% '^test' %}";
217
        insertChar(original, '\'', expected);
218
    }
219
220
    public void testInsertSingleQuoteInBlock_06() throws Exception {
221
        String original = "{% 'test' %}^";
222
        String expected = "{% 'test' %}'^";
223
        insertChar(original, '\'', expected);
224
    }
225
226
    public void testInsertSingleQuoteInBlock_07() throws Exception {
227
        String original = "{%^%}";
228
        String expected = "{%'^'%}";
229
        insertChar(original, '\'', expected);
230
    }
231
232
    public void testInsertCurlyInBlock_01() throws Exception {
233
        String original = "{% ^ %}";
234
        String expected = "{% {^} %}";
235
        insertChar(original, '{', expected);
236
    }
237
238
    public void testInsertCurlyInBlock_02() throws Exception {
239
        String original = "{%^%}";
240
        String expected = "{%{^}%}";
241
        insertChar(original, '{', expected);
242
    }
243
244
    public void testInsertParenthesisInBlock_01() throws Exception {
245
        String original = "{% ^ %}";
246
        String expected = "{% (^) %}";
247
        insertChar(original, '(', expected);
248
    }
249
250
    public void testInsertParenthesisInBlock_02() throws Exception {
251
        String original = "{%^%}";
252
        String expected = "{%(^)%}";
253
        insertChar(original, '(', expected);
254
    }
255
256
    public void testInsertBracketInBlock_01() throws Exception {
257
        String original = "{% ^ %}";
258
        String expected = "{% [^] %}";
259
        insertChar(original, '[', expected);
260
    }
261
262
    public void testInsertBracketInBlock_02() throws Exception {
263
        String original = "{%^%}";
264
        String expected = "{%[^]%}";
265
        insertChar(original, '[', expected);
266
    }
267
268
    // skip closing bracket
269
    public void testSkipClosingCurlyInBlock_01() throws Exception {
270
        String original = "{% {^} %}";
271
        String expected = "{% {}^ %}";
272
        insertChar(original, '}', expected);
273
    }
274
275
    public void testSkipClosingCurlyInBlock_02() throws Exception {
276
        String original = "{% {'foo' : {^}} %}";
277
        String expected = "{% {'foo' : {}^} %}";
278
        insertChar(original, '}', expected);
279
    }
280
281
    public void testSkipClosingCurlyInBlock_03() throws Exception {
282
        String original = "{% {'foo' : {^}} { %}";
283
        String expected = "{% {'foo' : {}^} { %}";
284
        insertChar(original, '}', expected);
285
    }
286
287
    public void testSkipClosingParenthesisInBlock_01() throws Exception {
288
        String original = "{% (^) %}";
289
        String expected = "{% ()^ %}";
290
        insertChar(original, ')', expected);
291
    }
292
293
    public void testSkipClosingParenthesisInBlock_02() throws Exception {
294
        String original = "{% ((^)) %}";
295
        String expected = "{% (()^) %}";
296
        insertChar(original, ')', expected);
297
    }
298
299
    public void testSkipClosingParenthesisInBlock_03() throws Exception {
300
        String original = "{% ((^)) ( %}";
301
        String expected = "{% (()^) ( %}";
302
        insertChar(original, ')', expected);
303
    }
304
305
    public void testSkipClosingBracketInBlock_01() throws Exception {
306
        String original = "{% [^] %}";
307
        String expected = "{% []^ %}";
308
        insertChar(original, ']', expected);
309
    }
310
311
    public void testSkipClosingBracketInBlock_02() throws Exception {
312
        String original = "{% [[^]] %}";
313
        String expected = "{% [[]^] %}";
314
        insertChar(original, ']', expected);
315
    }
316
317
    public void testSkipClosingBracketInBlock_03() throws Exception {
318
        String original = "{% [[(^]] [ %}";
319
        String expected = "{% [[(]^] [ %}";
320
        insertChar(original, ']', expected);
321
    }
322
323
    // missing closing bracket
324
    public void testMissingClosingCurlyInBlock_01() throws Exception {
325
        String original = "{% {'foo' : {^} %}";
326
        String expected = "{% {'foo' : {}^} %}";
327
        insertChar(original, '}', expected);
328
    }
329
330
    public void testMissingClosingCurlyInBlock_02() throws Exception {
331
        String original = "{% {'foo' : {}^ %}";
332
        String expected = "{% {'foo' : {}}^ %}";
333
        insertChar(original, '}', expected);
334
    }
335
336
    public void testMissingClosingCurlyInBlock_03() throws Exception {
337
        String original = "{% {'foo' : {}^ { %}";
338
        String expected = "{% {'foo' : {}}^ { %}";
339
        insertChar(original, '}', expected);
340
    }
341
342
    public void testMissingClosingParenthesisInBlock_01() throws Exception {
343
        String original = "{% ((^) %}";
344
        String expected = "{% (()^) %}";
345
        insertChar(original, ')', expected);
346
    }
347
348
    public void testMissingClosingParenthesisInBlock_02() throws Exception {
349
        String original = "{% (()^ %}";
350
        String expected = "{% (())^ %}";
351
        insertChar(original, ')', expected);
352
    }
353
354
    public void testMissingClosingParenthesisInBlock_03() throws Exception {
355
        String original = "{% (()^ () %}";
356
        String expected = "{% (())^ () %}";
357
        insertChar(original, ')', expected);
358
    }
359
360
    public void testMissingClosingBracketInBlock_01() throws Exception {
361
        String original = "{% [[^] %}";
362
        String expected = "{% [[]^] %}";
363
        insertChar(original, ']', expected);
364
    }
365
366
    public void testMissingClosingBracketInBlock_02() throws Exception {
367
        String original = "{% [[]^ %}";
368
        String expected = "{% [[]]^ %}";
369
        insertChar(original, ']', expected);
370
    }
371
372
    public void testMissingClosingBracketInBlock_03() throws Exception {
373
        String original = "{% [[]^ [] %}";
374
        String expected = "{% [[]]^ [] %}";
375
        insertChar(original, ']', expected);
376
    }
377
378
    // Variable
379
    // surround with quotes or brackets
380
    public void testSurroundWithDoubleQuoteInVariable_01() throws Exception {
381
        String original = "{{ test^ }}";
382
        String expected = "{{ \"test\"^ }}";
383
        insertChar(original, '"', expected, "test");
384
    }
385
386
    public void testSurroundWithDoubleQuoteInVariable_02() throws Exception {
387
        String original = "{{ test^ }}";
388
        String expected = "{{ te\"st\"^ }}";
389
        insertChar(original, '"', expected, "st");
390
    }
391
392
    public void testSurroundWithDoubleQuoteInVariable_03() throws Exception {
393
        String original = "{{ 'test'^ }}";
394
        String expected = "{{ \"test\"^ }}";
395
        insertChar(original, '"', expected, "'test'");
396
    }
397
398
    public void testSurroundWithSingleQuoteInVariable_01() throws Exception {
399
        String original = "{{ test^ }}";
400
        String expected = "{{ 'test'^ }}";
401
        insertChar(original, '\'', expected, "test");
402
    }
403
404
    public void testSurroundWithSingleQuoteInVariable_02() throws Exception {
405
        String original = "{{ test^ }}";
406
        String expected = "{{ te'st'^ }}";
407
        insertChar(original, '\'', expected, "st");
408
    }
409
410
    public void testSurroundWithSingleQuoteInVariable_03() throws Exception {
411
        String original = "{{ \"test\"^ }}";
412
        String expected = "{{ 'test'^ }}";
413
        insertChar(original, '\'', expected, "\"test\"");
414
    }
415
416
    public void testSurroundWithCurlyInVariable_01() throws Exception {
417
        String original = "{{ test^ }}";
418
        String expected = "{{ {test}^ }}";
419
        insertChar(original, '{', expected, "test");
420
    }
421
422
    public void testSurroundWithCurlyInVariable_02() throws Exception {
423
        String original = "{{ test^ }}";
424
        String expected = "{{ te{st}^ }}";
425
        insertChar(original, '{', expected, "st");
426
    }
427
428
    public void testSurroundWithCurlyInVariable_03() throws Exception {
429
        String original = "{{ \"test\"^ }}";
430
        String expected = "{{ {test}^ }}";
431
        insertChar(original, '{', expected, "\"test\"");
432
    }
433
434
    public void testSurroundWithParenthesisInVariable_01() throws Exception {
435
        String original = "{{ test^ }}";
436
        String expected = "{{ (test)^ }}";
437
        insertChar(original, '(', expected, "test");
438
    }
439
440
    public void testSurroundWithParenthesisInVariable_02() throws Exception {
441
        String original = "{{ test^ }}";
442
        String expected = "{{ te(st)^ }}";
443
        insertChar(original, '(', expected, "st");
444
    }
445
446
    public void testSurroundWithParenthesisInVariable_03() throws Exception {
447
        String original = "{{ \"test\"^ }}";
448
        String expected = "{{ (test)^ }}";
449
        insertChar(original, '(', expected, "\"test\"");
450
    }
451
452
    public void testSurroundWithBracketInVariable_01() throws Exception {
453
        String original = "{{ test^ }}";
454
        String expected = "{{ [test]^ }}";
455
        insertChar(original, '[', expected, "test");
456
    }
457
458
    public void testSurroundWithBracketInVariable_02() throws Exception {
459
        String original = "{{ tes^t }}";
460
        String expected = "{{ t[es]^t }}";
461
        insertChar(original, '[', expected, "es");
462
    }
463
464
    public void testSurroundWithBracketInVariable_03() throws Exception {
465
        String original = "{{ \"test\"^ }}";
466
        String expected = "{{ [test]^ }}";
467
        insertChar(original, '[', expected, "\"test\"");
468
    }
469
470
    // complete quotes and brackets
471
    public void testInsertDoubleQuoteInVariable_01() throws Exception {
472
        String original = "{{ ^ }}";
473
        String expected = "{{ \"^\" }}";
474
        insertChar(original, '"', expected);
475
    }
476
477
    public void testInsertDoubleQuoteInVariable_02() throws Exception {
478
        String original = "{{ \"^ }}";
479
        String expected = "{{ \"\"^ }}";
480
        insertChar(original, '"', expected);
481
    }
482
483
    public void testInsertDoubleQuoteInVariable_03() throws Exception {
484
        String original = "{{ ^\" }}";
485
        String expected = "{{ \"^\" }}";
486
        insertChar(original, '"', expected);
487
    }
488
489
    public void testInsertDoubleQuoteInVariable_04() throws Exception {
490
        String original = "{{ \"test^ }}";
491
        String expected = "{{ \"test\"^ }}";
492
        insertChar(original, '"', expected);
493
    }
494
495
    public void testInsertDoubleQuoteInVariable_05() throws Exception {
496
        String original = "{{ ^test\" }}";
497
        String expected = "{{ \"^test\" }}";
498
        insertChar(original, '"', expected);
499
    }
500
501
    public void testInsertDoubleQuoteInVariable_06() throws Exception {
502
        String original = "{{ \"test\" }}^";
503
        String expected = "{{ \"test\" }}\"^";
504
        insertChar(original, '"', expected);
505
    }
506
507
    public void testInsertDoubleQuoteInVariable_07() throws Exception {
508
        String original = "{{^}}";
509
        String expected = "{{\"^\"}}";
510
        insertChar(original, '"', expected);
511
    }
512
513
    public void testInsertSingleQuoteInVariable_01() throws Exception {
514
        String original = "{{ ^ }}";
515
        String expected = "{{ '^' }}";
516
        insertChar(original, '\'', expected);
517
    }
518
519
    public void testInsertSingleQuoteInVariable_02() throws Exception {
520
        String original = "{{ '^ }}";
521
        String expected = "{{ ''^ }}";
522
        insertChar(original, '\'', expected);
523
    }
524
525
    public void testInsertSingleQuoteInVariable_03() throws Exception {
526
        String original = "{{ ^' }}";
527
        String expected = "{{ '^' }}";
528
        insertChar(original, '\'', expected);
529
    }
530
531
    public void testInsertSingleQuoteInVariable_04() throws Exception {
532
        String original = "{{ 'test^ }}";
533
        String expected = "{{ 'test'^ }}";
534
        insertChar(original, '\'', expected);
535
    }
536
537
    public void testInsertSingleQuoteInVariable_05() throws Exception {
538
        String original = "{{ ^test' }}";
539
        String expected = "{{ '^test' }}";
540
        insertChar(original, '\'', expected);
541
    }
542
543
    public void testInsertSingleQuoteInVariable_06() throws Exception {
544
        String original = "{{ 'test' }}^";
545
        String expected = "{{ 'test' }}'^";
546
        insertChar(original, '\'', expected);
547
    }
548
549
    public void testInsertSingleQuoteInVariable_07() throws Exception {
550
        String original = "{{^}}";
551
        String expected = "{{'^'}}";
552
        insertChar(original, '\'', expected);
553
    }
554
555
    public void testInsertCurlyInVariable_01() throws Exception {
556
        String original = "{{ ^ }}";
557
        String expected = "{{ {^} }}";
558
        insertChar(original, '{', expected);
559
    }
560
561
    public void testInsertCurlyInVariable_02() throws Exception {
562
        String original = "{{^}}";
563
        String expected = "{{{^}}}";
564
        insertChar(original, '{', expected);
565
    }
566
567
    public void testInsertParenthesisInVariable_01() throws Exception {
568
        String original = "{{ ^ }}";
569
        String expected = "{{ (^) }}";
570
        insertChar(original, '(', expected);
571
    }
572
573
    public void testInsertParenthesisInVariable_02() throws Exception {
574
        String original = "{{^}}";
575
        String expected = "{{(^)}}";
576
        insertChar(original, '(', expected);
577
    }
578
579
    public void testInsertBracketInVariable_01() throws Exception {
580
        String original = "{{ ^ }}";
581
        String expected = "{{ [^] }}";
582
        insertChar(original, '[', expected);
583
    }
584
585
    public void testInsertBracketInVariable_02() throws Exception {
586
        String original = "{{^}}";
587
        String expected = "{{[^]}}";
588
        insertChar(original, '[', expected);
589
    }
590
591
    // skip closing bracket
592
    // curly can be used in variable?
593
    public void testSkipClosingCurlyInVariable_01() throws Exception {
594
        String original = "{{ {^} }}";
595
        String expected = "{{ {}^ }}";
596
        insertChar(original, '}', expected);
597
    }
598
599
    public void testSkipClosingCurlyInVariable_02() throws Exception {
600
        // }} is delimiter, so add a space between }}
601
        String original = "{{ {'foo' : {^} } }}";
602
        String expected = "{{ {'foo' : {}^ } }}";
603
        insertChar(original, '}', expected);
604
    }
605
606
    public void testSkipClosingCurlyInVariable_03() throws Exception {
607
        // }} is delimiter, so add a space between }}
608
        String original = "{{ {'foo' : {^} } { }}";
609
        String expected = "{{ {'foo' : {}^ } { }}";
610
        insertChar(original, '}', expected);
611
    }
612
613
    public void testSkipClosingParenthesisInVariable_01() throws Exception {
614
        String original = "{{ (^) }}";
615
        String expected = "{{ ()^ }}";
616
        insertChar(original, ')', expected);
617
    }
618
619
    public void testSkipClosingParenthesisInVariable_02() throws Exception {
620
        String original = "{{ ((^)) }}";
621
        String expected = "{{ (()^) }}";
622
        insertChar(original, ')', expected);
623
    }
624
625
    public void testSkipClosingParenthesisInVariable_03() throws Exception {
626
        String original = "{{ ((^)) ( }}";
627
        String expected = "{{ (()^) ( }}";
628
        insertChar(original, ')', expected);
629
    }
630
631
    public void testSkipClosingBracketInVariable_01() throws Exception {
632
        String original = "{{ [^] }}";
633
        String expected = "{{ []^ }}";
634
        insertChar(original, ']', expected);
635
    }
636
637
    public void testSkipClosingBracketInVariable_02() throws Exception {
638
        String original = "{{ [[^]] }}";
639
        String expected = "{{ [[]^] }}";
640
        insertChar(original, ']', expected);
641
    }
642
643
    public void testSkipClosingBracketInVariable_03() throws Exception {
644
        String original = "{{ [[(^]] [ }}";
645
        String expected = "{{ [[(]^] [ }}";
646
        insertChar(original, ']', expected);
647
    }
648
649
    // missing closing bracket
650
    public void testMissingClosingCurlyInVariable_01() throws Exception {
651
        String original = "{{ {'foo' : {^} }}";
652
        String expected = "{{ {'foo' : {}^} }}";
653
        insertChar(original, '}', expected);
654
    }
655
656
    public void testMissingClosingCurlyInVariable_02() throws Exception {
657
        String original = "{{ {'foo' : {}^ }}";
658
        String expected = "{{ {'foo' : {}}^ }}";
659
        insertChar(original, '}', expected);
660
    }
661
662
    public void testMissingClosingCurlyInVariable_03() throws Exception {
663
        String original = "{{ {'foo' : {}^ { }}";
664
        String expected = "{{ {'foo' : {}}^ { }}";
665
        insertChar(original, '}', expected);
666
    }
667
668
    public void testMissingClosingParenthesisInVariable_01() throws Exception {
669
        String original = "{{ ((^) }}";
670
        String expected = "{{ (()^) }}";
671
        insertChar(original, ')', expected);
672
    }
673
674
    public void testMissingClosingParenthesisInVariable_02() throws Exception {
675
        String original = "{{ (()^ }}";
676
        String expected = "{{ (())^ }}";
677
        insertChar(original, ')', expected);
678
    }
679
680
    public void testMissingClosingParenthesisInVariable_03() throws Exception {
681
        String original = "{{ (()^ () }}";
682
        String expected = "{{ (())^ () }}";
683
        insertChar(original, ')', expected);
684
    }
685
686
    public void testMissingClosingBracketInVariable_01() throws Exception {
687
        String original = "{{ [[^] }}";
688
        String expected = "{{ [[]^] }}";
689
        insertChar(original, ']', expected);
690
    }
691
692
    public void testMissingClosingBracketInVariable_02() throws Exception {
693
        String original = "{{ [[]^ }}";
694
        String expected = "{{ [[]]^ }}";
695
        insertChar(original, ']', expected);
696
    }
697
698
    public void testMissingClosingBracketInVariable_03() throws Exception {
699
        String original = "{{ [[]^ [] }}";
700
        String expected = "{{ [[]]^ [] }}";
701
        insertChar(original, ']', expected);
702
    }
703
704
    public void testInsertCurlyInHtml() throws Exception {
705
        String original = "{% {} %}^";
706
        String expected = "{% {} %}{^";
707
        insertChar(original, '{', expected);
708
    }
709
710
    public void testInsertParenthesisInHtml() throws Exception {
711
        String original = "{% () %}^";
712
        String expected = "{% () %}(^";
713
        insertChar(original, '(', expected);
714
    }
715
716
    public void testInsertBracketInHtml() throws Exception {
717
        String original = "{% [] %}^";
718
        String expected = "{% [] %}[^";
719
        insertChar(original, '[', expected);
720
    }
721
722
    public void testCurlyInHtml() throws Exception {
723
        String original = "<html><body>^</body></html>";
724
        String expected = "<html><body>{^</body></html>";
725
        insertChar(original, '{', expected);
726
    }
727
728
    public void testVariableInHtml() throws Exception {
729
        String original = "<html><body>{^</body></html>";
730
        String expected = "<html><body>{{ ^ }}</body></html>";
731
        insertChar(original, '{', expected);
732
    }
733
734
    public void testBlockInHtml() throws Exception {
735
        String original = "<html><body>{^</body></html>";
736
        String expected = "<html><body>{% ^ %}</body></html>";
737
        insertChar(original, '%', expected);
738
    }
739
740
    public void testCurlyInHtmlAttribute() throws Exception {
741
        String original
742
                = "<html>\n"
743
                + "    <body>\n"
744
                + "        <ul id=\"navigation\">\n"
745
                + "        {% for item in navigation %}\n"
746
                + "            <li><a href=\"^\">{{ item.caption }}</a></li>\n" // here
747
                + "        {% endfor %}\n"
748
                + "        </ul>\n"
749
                + "    </body>\n"
750
                + "</html>";
751
        String expected
752
                = "<html>\n"
753
                + "    <body>\n"
754
                + "        <ul id=\"navigation\">\n"
755
                + "        {% for item in navigation %}\n"
756
                + "            <li><a href=\"{^\">{{ item.caption }}</a></li>\n" // here
757
                + "        {% endfor %}\n"
758
                + "        </ul>\n"
759
                + "    </body>\n"
760
                + "</html>";
761
        insertChar(original, '{', expected);
762
    }
763
764
    public void testVariableInHtmlAttribute() throws Exception {
765
        String original
766
                = "<html>\n"
767
                + "    <body>\n"
768
                + "        <ul id=\"navigation\">\n"
769
                + "        {% for item in navigation %}\n"
770
                + "            <li><a href=\"{^\">{{ item.caption }}</a></li>\n" // here
771
                + "        {% endfor %}\n"
772
                + "        </ul>\n"
773
                + "    </body>\n"
774
                + "</html>";
775
        String expected
776
                = "<html>\n"
777
                + "    <body>\n"
778
                + "        <ul id=\"navigation\">\n"
779
                + "        {% for item in navigation %}\n"
780
                + "            <li><a href=\"{{ ^ }}\">{{ item.caption }}</a></li>\n" // here
781
                + "        {% endfor %}\n"
782
                + "        </ul>\n"
783
                + "    </body>\n"
784
                + "</html>";
785
        insertChar(original, '{', expected);
786
    }
787
788
    public void testCurlyInTwigBlock() throws Exception {
789
        String original
790
                = "<html>\n"
791
                + "    <body>\n"
792
                + "        <ul id=\"navigation\">\n"
793
                + "        {% for item in navigation %}\n"
794
                + "            ^\n" // here
795
                + "        {% endfor %}\n"
796
                + "        </ul>\n"
797
                + "    </body>\n"
798
                + "</html>";
799
        String expected
800
                = "<html>\n"
801
                + "    <body>\n"
802
                + "        <ul id=\"navigation\">\n"
803
                + "        {% for item in navigation %}\n"
804
                + "            {^\n" // here
805
                + "        {% endfor %}\n"
806
                + "        </ul>\n"
807
                + "    </body>\n"
808
                + "</html>";
809
        insertChar(original, '{', expected);
810
    }
811
812
    public void testVariableInTwigBlock() throws Exception {
813
        String original
814
                = "<html>\n"
815
                + "    <body>\n"
816
                + "        <ul id=\"navigation\">\n"
817
                + "        {% for item in navigation %}\n"
818
                + "            {^\n" // here
819
                + "        {% endfor %}\n"
820
                + "        </ul>\n"
821
                + "    </body>\n"
822
                + "</html>";
823
        String expected
824
                = "<html>\n"
825
                + "    <body>\n"
826
                + "        <ul id=\"navigation\">\n"
827
                + "        {% for item in navigation %}\n"
828
                + "            {{ ^ }}\n" // here
829
                + "        {% endfor %}\n"
830
                + "        </ul>\n"
831
                + "    </body>\n"
832
                + "</html>";
833
        insertChar(original, '{', expected);
834
    }
835
836
    public void testBlockInTwigBlock() throws Exception {
837
        String original
838
                = "<html>\n"
839
                + "    <body>\n"
840
                + "        <ul id=\"navigation\">\n"
841
                + "        {% for item in navigation %}\n"
842
                + "            {^\n" // here
843
                + "        {% endfor %}\n"
844
                + "        </ul>\n"
845
                + "    </body>\n"
846
                + "</html>";
847
        String expected
848
                = "<html>\n"
849
                + "    <body>\n"
850
                + "        <ul id=\"navigation\">\n"
851
                + "        {% for item in navigation %}\n"
852
                + "            {% ^ %}\n" // here
853
                + "        {% endfor %}\n"
854
                + "        </ul>\n"
855
                + "    </body>\n"
856
                + "</html>";
857
        insertChar(original, '%', expected);
858
    }
859
860
}
(-)a/php.twig/test/unit/src/org/netbeans/modules/php/twig/editor/typinghooks/TwigTypinghooksTestBase.java (+55 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright (c) 2016 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
package org.netbeans.modules.php.twig.editor.typinghooks;
41
42
import org.netbeans.modules.php.twig.editor.TwigTestBase;
43
44
public abstract class TwigTypinghooksTestBase extends TwigTestBase {
45
46
    public TwigTypinghooksTestBase(String testName) {
47
        super(testName);
48
    }
49
50
    @Override
51
    protected boolean runInEQ() {
52
        return true;
53
    }
54
55
}

Return to bug 244054