001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.editor.cplusplus;
043:
044: import java.awt.Cursor;
045: import java.awt.event.ActionEvent;
046: import java.util.*;
047: import javax.swing.Action;
048: import javax.swing.text.Caret;
049: import javax.swing.text.Position;
050: import javax.swing.text.Document;
051: import javax.swing.text.JTextComponent;
052: import javax.swing.text.TextAction;
053: import javax.swing.text.BadLocationException;
054: import org.netbeans.api.lexer.InputAttributes;
055: import org.netbeans.api.lexer.Language;
056: import org.netbeans.cnd.api.lexer.CndLexerUtilities;
057: import org.netbeans.cnd.api.lexer.CppTokenId;
058: import org.netbeans.cnd.api.lexer.Filter;
059: import org.netbeans.editor.TokenItem;
060:
061: import org.openide.util.Lookup;
062:
063: import org.netbeans.editor.BaseAction;
064: import org.netbeans.editor.BaseDocument;
065: import org.netbeans.editor.BaseKit;
066: import org.netbeans.editor.BaseKit.InsertBreakAction;
067: import org.netbeans.editor.Formatter;
068: import org.netbeans.editor.Syntax;
069: import org.netbeans.editor.SyntaxSupport;
070: import org.netbeans.editor.SyntaxUpdateTokens;
071: import org.netbeans.editor.TokenContextPath;
072: import org.netbeans.editor.TokenID;
073: import org.netbeans.editor.Utilities;
074: import org.netbeans.editor.ext.ExtKit.CommentAction;
075: import org.netbeans.editor.ext.ExtKit.ExtDefaultKeyTypedAction;
076: import org.netbeans.editor.ext.ExtKit.ExtDeleteCharAction;
077: import org.netbeans.editor.ext.ExtKit.UncommentAction;
078: import org.netbeans.modules.editor.NbEditorKit;
079:
080: import org.netbeans.modules.cnd.MIMENames;
081: import org.netbeans.modules.cnd.editor.spi.cplusplus.CCSyntaxSupport;
082: import org.netbeans.modules.cnd.editor.spi.cplusplus.CndEditorActionsProvider;
083: import org.netbeans.modules.cnd.editor.spi.cplusplus.SyntaxSupportProvider;
084:
085: /** C++ editor kit with appropriate document */
086: public class CCKit extends NbEditorKit {
087: private InputAttributes lexerAttrs = null;
088:
089: @Override
090: public String getContentType() {
091: return MIMENames.CPLUSPLUS_MIME_TYPE;
092: }
093:
094: // Work-in-progress...
095: // public HelpCtx getHelpCtx() {
096: // System.err.println("CCKit.getHelpCts: Using JavaKit help ID");
097: // return new HelpCtx("org.netbeans.modules.editor.java.JavaKit");
098: // }
099:
100: @Override
101: public Document createDefaultDocument() {
102: Document doc = super .createDefaultDocument();
103: return doc;
104: }
105:
106: /** Initialize document by adding the draw-layers for example. */
107: @Override
108: protected void initDocument(BaseDocument doc) {
109: super .initDocument(doc);
110: doc.putProperty(InputAttributes.class, getLexerAttributes());
111: doc.putProperty(Language.class, getLanguage());
112: doc.putProperty(SyntaxUpdateTokens.class,
113: new SyntaxUpdateTokens() {
114: private List<TokenInfo> tokenList = new ArrayList<TokenInfo>();
115:
116: public void syntaxUpdateStart() {
117: tokenList.clear();
118: }
119:
120: public List syntaxUpdateEnd() {
121: return tokenList;
122: }
123:
124: public void syntaxUpdateToken(TokenID id,
125: TokenContextPath contextPath, int offset,
126: int length) {
127: if (CCTokenContext.LINE_COMMENT == id) {
128: tokenList.add(new TokenInfo(id,
129: contextPath, offset, length));
130: }
131: }
132: });
133: }
134:
135: protected Language<CppTokenId> getLanguage() {
136: return CppTokenId.languageCpp();
137: }
138:
139: protected final synchronized InputAttributes getLexerAttributes() {
140: // for now use shared attributes for all documents to save memory
141: // in future we can make attributes per document based on used compiler info
142: if (lexerAttrs == null) {
143: lexerAttrs = new InputAttributes();
144: lexerAttrs.setValue(getLanguage(), "lexer-filter",
145: getFilter(), true); // NOI18N
146: }
147: return lexerAttrs;
148: }
149:
150: protected Filter<CppTokenId> getFilter() {
151: return CndLexerUtilities.getStdCppFilter();
152: }
153:
154: /** Create new instance of syntax coloring scanner
155: * @param doc document to operate on. It can be null in the cases the syntax
156: * creation is not related to the particular document
157: */
158: @Override
159: public Syntax createSyntax(Document doc) {
160: return new CCSyntax();
161: }
162:
163: /** Create syntax support */
164: @Override
165: public SyntaxSupport createSyntaxSupport(BaseDocument doc) {
166: SyntaxSupportProvider ss = (SyntaxSupportProvider) Lookup
167: .getDefault().lookup(SyntaxSupportProvider.class);
168: SyntaxSupport sup = null;
169: if (ss != null) {
170: sup = ss.createSyntaxSupport(doc);
171: }
172: if (sup == null) {
173: sup = new CCSyntaxSupport(doc);
174: }
175: return sup;
176: }
177:
178: /** Create the formatter appropriate for this kit */
179: @Override
180: public Formatter createFormatter() {
181: return new CCFormatter(this .getClass());
182: }
183:
184: protected Action getCommentAction() {
185: return new CommentAction("//"); // NOI18N
186: }
187:
188: protected Action getUncommentAction() {
189: return new UncommentAction("//"); // NOI18N
190: }
191:
192: protected Action getToggleCommentAction() {
193: return new ToggleCommentAction("//"); // NOI18N
194: }
195:
196: protected @Override
197: Action[] createActions() {
198: Action[] ccActions = new Action[] {
199: new CCDefaultKeyTypedAction(),
200: new CCFormatAction(),
201: // new CppFoldTestAction(),
202: new CCInsertBreakAction(),
203: new CCDeleteCharAction(deletePrevCharAction, false),
204: getToggleCommentAction(), getCommentAction(),
205: getUncommentAction() };
206: ccActions = TextAction.augmentList(super .createActions(),
207: ccActions);
208: Action[] extra = CndEditorActionsProvider.getDefault()
209: .getActions(getContentType());
210: if (extra.length > 0) {
211: ccActions = TextAction.augmentList(ccActions, extra);
212: }
213:
214: return ccActions;
215: }
216:
217: // public static class CppFoldTestAction extends BaseAction {
218: // public CppFoldTestAction() {
219: // super("cpp-fold-test-action"); // NOI18N
220: // String sdesc = NbBundle.getBundle(CCKit.class).getString("CppFoldTest"); //NOI18N
221: // String menutext = NbBundle.getBundle(CCKit.class).getString("menu_CppFoldTest"); //NOI18N
222: //
223: // putValue(SHORT_DESCRIPTION, sdesc);
224: // putValue(BaseAction.POPUP_MENU_TEXT, menutext);
225: // }
226: //
227: // public void actionPerformed(ActionEvent evt, JTextComponent target) {
228: // FoldHierarchy hierarchy = FoldHierarchy.get(target);
229: //
230: // // Hierarchy locking done in the utility method
231: // List types = new ArrayList();
232: // types.add(CppFoldManagerBase.CODE_BLOCK_FOLD_TYPE);
233: // types.add(CppFoldManagerBase.INCLUDES_FOLD_TYPE);
234: // FoldUtilities.expand(hierarchy, types);
235: // }
236: // }
237:
238: /** Holds action classes to be created as part of createAction.
239: This allows dependent modules to add editor actions to this
240: kit on startup.
241: */
242:
243: @Override
244: protected void updateActions() {
245: super .updateActions();
246: addSystemActionMapping(formatAction, CCFormatAction.class);
247: }
248:
249: public class CCFormatAction extends BaseAction {
250:
251: public CCFormatAction() {
252: super (BaseKit.formatAction, MAGIC_POSITION_RESET
253: | UNDO_MERGE_RESET);
254: putValue("helpID", CCFormatAction.class.getName()); // NOI18N
255: }
256:
257: public void actionPerformed(ActionEvent evt,
258: JTextComponent target) {
259: if (target != null) {
260:
261: if (!target.isEditable() || !target.isEnabled()) {
262: target.getToolkit().beep();
263: return;
264: }
265:
266: Caret caret = target.getCaret();
267: BaseDocument doc = (BaseDocument) target.getDocument();
268: // Set hourglass cursor
269: Cursor origCursor = target.getCursor();
270: target.setCursor(Cursor
271: .getPredefinedCursor(Cursor.WAIT_CURSOR));
272:
273: doc.atomicLock();
274: try {
275:
276: int caretLine = Utilities.getLineOffset(doc, caret
277: .getDot());
278: int startPos;
279: Position endPosition;
280: //if (caret.isSelectionVisible()) {
281: if (Utilities.isSelectionShowing(caret)) {
282: startPos = target.getSelectionStart();
283: endPosition = doc.createPosition(target
284: .getSelectionEnd());
285: } else {
286: startPos = 0;
287: endPosition = doc.createPosition(doc
288: .getLength());
289: }
290:
291: int pos = startPos;
292: Formatter formatter = doc.getFormatter();
293: formatter.reformatLock();
294: try {
295: while (pos < endPosition.getOffset()) {
296: int stopPos = endPosition.getOffset();
297: int reformattedLen = formatter.reformat(
298: doc, pos, stopPos);
299: pos = pos + reformattedLen;
300: }
301: } finally {
302: formatter.reformatUnlock();
303: }
304:
305: // Restore the line
306: pos = Utilities.getRowStartFromLineOffset(doc,
307: caretLine);
308: if (pos >= 0) {
309: caret.setDot(pos);
310: }
311: } catch (BadLocationException e) {
312: //failed to format
313: } finally {
314: doc.atomicUnlock();
315: target.setCursor(origCursor);
316: }
317:
318: }
319: }
320: }
321:
322: public static class CCDefaultKeyTypedAction extends
323: ExtDefaultKeyTypedAction {
324:
325: @Override
326: protected void checkIndentHotChars(JTextComponent target,
327: String typedText) {
328: boolean reindent = false;
329:
330: BaseDocument doc = Utilities.getDocument(target);
331: int dotPos = target.getCaret().getDot();
332: if (doc != null) {
333: reindent = CCFormatter.getKeywordBasedReformatBlock(
334: doc, dotPos, typedText) != null;
335:
336: // Reindent the line if necessary
337: if (reindent) {
338: try {
339: Utilities.reformatLine(doc, dotPos);
340: } catch (BadLocationException e) {
341: }
342: }
343: }
344:
345: super .checkIndentHotChars(target, typedText);
346: }
347:
348: @Override
349: protected void insertString(BaseDocument doc, int dotPos,
350: Caret caret, String str, boolean overwrite)
351: throws BadLocationException {
352: super .insertString(doc, dotPos, caret, str, overwrite);
353: BracketCompletion.charInserted(doc, dotPos, caret, str
354: .charAt(0));
355: }
356:
357: } // end class CCDefaultKeyTypedAction
358:
359: public static class CCInsertBreakAction extends InsertBreakAction {
360:
361: static final long serialVersionUID = -1506173310438326380L;
362: static final boolean DEBUG = false;
363:
364: @Override
365: protected Object beforeBreak(JTextComponent target,
366: BaseDocument doc, Caret caret) {
367: int dotPos = caret.getDot();
368: if (BracketCompletion.posWithinString(doc, dotPos)) {
369: try {
370: doc.insertString(dotPos, "\"\"", null); //NOI18N
371: dotPos += 1;
372: caret.setDot(dotPos);
373: return new Integer(dotPos);
374: } catch (BadLocationException ex) {
375: }
376: } else {
377: try {
378: if (BracketCompletion.isAddRightBrace(doc, dotPos)) {
379: int end = BracketCompletion.getRowOrBlockEnd(
380: doc, dotPos);
381: String insString = "}"; // NOI18N
382: // XXX: vv159170 simplest hack
383: // insert "};" for "{" when in "enum", "class", "struct" and union completion
384: CCSyntaxSupport sup = (CCSyntaxSupport) Utilities
385: .getSyntaxSupport(target);
386: TokenItem item = sup.getTokenChain(dotPos - 1,
387: dotPos);
388: while (item != null
389: && item.getTokenID() == CCTokenContext.WHITESPACE) {
390: item = item.getPrevious();
391: }
392: if (item == null
393: || item.getTokenID() != CCTokenContext.LBRACE) {
394: return Boolean.FALSE;
395: }
396: int lBracePos = item.getOffset();
397: int lastSepOffset = sup
398: .getLastCommandSeparator(lBracePos - 1);
399: if (lastSepOffset == -1 && lBracePos > 0) {
400: lastSepOffset = 0;
401: }
402: if (lastSepOffset != -1
403: && lastSepOffset < dotPos) {
404: TokenItem keyword = sup.getTokenChain(
405: lastSepOffset, lBracePos);
406: while (keyword != null
407: && keyword.getOffset() < lBracePos) {
408: if (keyword.getTokenID() == CCTokenContext.CLASS
409: || keyword.getTokenID() == CCTokenContext.UNION
410: || keyword.getTokenID() == CCTokenContext.STRUCT
411: || keyword.getTokenID() == CCTokenContext.ENUM) {
412: insString = "};"; // NOI18N
413: break;
414: }
415: keyword = keyword.getNext();
416: }
417: // String text = doc.getText(lastSepOffset, dotPos - lastSepOffset);
418: // if (DEBUG) System.out.println("current text " + text); // NOI18N
419: // String regexp=".*\\b(class|union|struct|enum)\\b.*";//NOI18N
420: // if (text != null && text.matches(regexp)) {
421: // insString = "};"; // NOI18N
422: // }
423: }
424: doc.insertString(end, insString, null); // NOI18N
425: // Lock does not need because method is invoked from BaseKit that already lock indent.
426: doc.getFormatter().indentNewLine(doc, end);
427: caret.setDot(dotPos);
428: return Boolean.TRUE;
429: }
430: } catch (BadLocationException ex) {
431: }
432: }
433: return null;
434: }
435:
436: @Override
437: protected void afterBreak(JTextComponent target,
438: BaseDocument doc, Caret caret, Object cookie) {
439: if (cookie != null) {
440: if (cookie instanceof Integer) {
441: // integer
442: int nowDotPos = caret.getDot();
443: caret.setDot(nowDotPos + 1);
444: }
445: }
446: }
447:
448: } // end class CCInsertBreakAction
449:
450: public static class CCDeleteCharAction extends ExtDeleteCharAction {
451:
452: public CCDeleteCharAction(String nm, boolean nextChar) {
453: super (nm, nextChar);
454: }
455:
456: @Override
457: protected void charBackspaced(BaseDocument doc, int dotPos,
458: Caret caret, char ch) throws BadLocationException {
459: BracketCompletion.charBackspaced(doc, dotPos, caret, ch);
460: }
461: } // end class CCDeleteCharAction
462:
463: }
|