/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.grammar; import java.awt.Component; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.concurrent.TimeUnit; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JEditorPane; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JToggleButton; import javax.swing.KeyStroke; import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.StyledDocument; import javax.swing.text.TextAction; import org.antlr.netbeans.editor.commenting.BlockCommentFormat; import org.antlr.netbeans.editor.commenting.ExtendedCommentAction; import org.antlr.netbeans.editor.commenting.ExtendedUncommentAction; import org.antlr.netbeans.editor.commenting.LineCommentFormat; import org.antlr.netbeans.editor.commenting.StandardCommenter; import org.antlr.netbeans.editor.text.DocumentSnapshot; import org.antlr.netbeans.editor.text.VersionedDocumentUtilities; import org.antlr.netbeans.parsing.spi.ParserTaskManager; import org.antlr.netbeans.parsing.spi.ParserTaskScheduler; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.editor.EditorActionRegistration; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.editor.BaseAction; import org.netbeans.editor.BaseKit; import org.netbeans.editor.Utilities; import org.netbeans.editor.ext.ExtKit; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.editor.NbEditorKit; import org.netbeans.modules.editor.NbEditorUtilities; import org.openide.awt.Mnemonics; import org.openide.filesystems.FileObject; import org.openide.util.ContextAwareAction; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; /** * * @author Sam Harwell */ @MimeRegistration(mimeType=GrammarEditorKit.GRAMMAR_MIME_TYPE, service=EditorKit.class) public class GrammarEditorKit extends NbEditorKit { public static final String PROP_LEGACY_MODE = "legacy-mode"; public static final String GRAMMAR_MIME_TYPE = "text/x-antlr3"; private static final LineCommentFormat LINE_COMMENT_FORMAT = new LineCommentFormat("//"); private static final BlockCommentFormat BLOCK_COMMENT_FORMAT = new BlockCommentFormat("/*", "*/"); @Override public String getContentType() { return GRAMMAR_MIME_TYPE; } @Override protected Action[] createActions() { Action[] superActions = super.createActions(); StandardCommenter commenter = new StandardCommenter(LINE_COMMENT_FORMAT, BLOCK_COMMENT_FORMAT); @SuppressWarnings("LocalVariableHidesMemberVariable") ExtendedCommentAction commentAction = new ExtendedCommentAction(commenter); @SuppressWarnings("LocalVariableHidesMemberVariable") ExtendedUncommentAction uncommentAction = new ExtendedUncommentAction(commenter); Action[] actions = { new ToggleLegacyModeAction(), new GrammarGoToDeclarationAction(), }; actions = TextAction.augmentList(superActions, actions); for (int i = 0; i < actions.length; i++) { if (actions[i] instanceof CommentAction) { actions[i] = commentAction; } else if (actions[i] instanceof UncommentAction) { actions[i] = uncommentAction; } } return actions; } public static boolean isLegacyMode(@NonNull DocumentSnapshot snapshot) { Document document = snapshot.getVersionedDocument().getDocument(); if (document == null) { return isDefaultLegacyMode(snapshot.getVersionedDocument().getFileObject()); } return isLegacyMode(document); } public static boolean isLegacyMode(@NonNull Document document) { Boolean mode = (Boolean)document.getProperty(PROP_LEGACY_MODE); if (mode == null) { return isDefaultLegacyMode(NbEditorUtilities.getFileObject(document)); } return mode; } private static boolean isDefaultLegacyMode(@NullAllowed FileObject fileObject) { if (fileObject == null) { return false; } String name = fileObject.getName(); String nameExt = fileObject.getNameExt(); if (name != null && nameExt != null && name.length() < nameExt.length()) { String extension = nameExt.substring(name.length()); boolean v4grammar = ".g4".equals(extension); return !v4grammar; } return false; } @NbBundle.Messages({ "toggle-legacy-mode=Toggle Legacy Mode" }) public static class ToggleLegacyModeAction extends BaseAction implements Presenter.Toolbar, ContextAwareAction, PropertyChangeListener { /** Toggle legacy mode action */ public static final String toggleLegacyModeAction = "toggle-legacy-mode"; // NOI18N private final Document document; private JToggleButton button; public ToggleLegacyModeAction() { this(null); } @SuppressWarnings("LeakingThisInConstructor") public ToggleLegacyModeAction(Document document) { super(toggleLegacyModeAction); putValue(Action.SMALL_ICON, ImageUtilities.loadImageIcon("org/antlr/works/editor/grammar/resources/legacy-mode.png", false)); // NOI18N putValue("noIconInMenu", true); // NOI18N this.document = document; if (document != null) { DocumentUtilities.addPropertyChangeListener(document, this); } } @Override protected Class<?> getShortDescriptionBundleClass() { return ToggleLegacyModeAction.class; } @Override public void actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { boolean current = isLegacyMode(target.getDocument()); target.getDocument().putProperty(PROP_LEGACY_MODE, !current); ParserTaskManager taskManager = Lookup.getDefault().lookup(ParserTaskManager.class); taskManager.reschedule(VersionedDocumentUtilities.getVersionedDocument(target.getDocument()), null, 50, TimeUnit.MILLISECONDS, ParserTaskScheduler.CONTENT_SENSITIVE_TASK_SCHEDULER); } } @Override public Component getToolbarPresenter() { if (button == null) { button = new JToggleButton(); button.putClientProperty("hideActionText", true); button.setIcon((Icon)getValue(SMALL_ICON)); button.setAction(this); updateState(); } return button; } @Override public Action createContextAwareInstance(Lookup actionContext) { JEditorPane pane = actionContext.lookup(JEditorPane.class); @SuppressWarnings("LocalVariableHidesMemberVariable") Document document = pane != null ? pane.getDocument() : null; if (document != null) { ToggleLegacyModeAction action = new ToggleLegacyModeAction(document); return action; } return this; } @Override public void propertyChange(PropertyChangeEvent evt) { if (document == evt.getSource()) { if (PROP_LEGACY_MODE.equals(evt.getPropertyName())) { updateState(); } } } private void updateState() { if (document != null) { boolean legacyMode = isLegacyMode(document); if (button != null) { button.setSelected(legacyMode); button.setContentAreaFilled(legacyMode); button.setBorderPainted(legacyMode); } } } } @NbBundle.Messages({ "generate-goto-popup=Go To", "goto-source=Go to Source", "goto_source_open_source_not_formatted=Go to Source", }) @EditorActionRegistration(name = generateGoToPopupAction, mimeType = GRAMMAR_MIME_TYPE) public static class GrammarGenerateGoToPopupAction extends NbGenerateGoToPopupAction { @Override public void actionPerformed(ActionEvent evt, JTextComponent target) { } private void addAcceleretors(Action a, JMenuItem item, JTextComponent target){ // Try to get the accelerator Keymap km = target.getKeymap(); if (km != null) { KeyStroke[] keys = km.getKeyStrokesForAction(a); if (keys != null && keys.length > 0) { item.setAccelerator(keys[0]); }else if (a!=null){ KeyStroke ks = (KeyStroke)a.getValue(Action.ACCELERATOR_KEY); if (ks!=null) { item.setAccelerator(ks); } } } } private void addAction(JTextComponent target, JMenu menu, Action a){ if (a != null) { String actionName = (String) a.getValue(Action.NAME); JMenuItem item = null; if (a instanceof BaseAction) { item = ((BaseAction)a).getPopupMenuItem(target); } if (item == null) { // gets trimmed text that doesn' contain "go to" String itemText = (String)a.getValue(ExtKit.TRIMMED_TEXT); if (itemText == null){ itemText = getItemText(target, actionName, a); } if (itemText != null) { item = new JMenuItem(itemText); Mnemonics.setLocalizedText(item, itemText); item.addActionListener(a); addAcceleretors(a, item, target); item.setEnabled(a.isEnabled()); Object helpID = a.getValue ("helpID"); // NOI18N if (helpID != null && (helpID instanceof String)) item.putClientProperty ("HelpID", helpID); // NOI18N }else{ if (ExtKit.gotoSourceAction.equals(actionName)){ item = new JMenuItem(Bundle.goto_source_open_source_not_formatted()); addAcceleretors(a, item, target); item.setEnabled(false); } } } if (item != null) { menu.add(item); } } } protected void addAction(JTextComponent target, JMenu menu, String actionName) { BaseKit kit = Utilities.getKit(target); if (kit == null) return; Action a = kit.getActionByName(actionName); if (a!=null){ addAction(target, menu, a); } else { // action-name is null, add the separator menu.addSeparator(); } } protected String getItemText(JTextComponent target, String actionName, Action a) { String itemText; if (a instanceof BaseAction) { itemText = ((BaseAction)a).getPopupMenuText(target); } else { itemText = actionName; } return itemText; } @Override public JMenuItem getPopupMenuItem(final JTextComponent target) { String menuText = Bundle.generate_goto_popup(); JMenu jm = new JMenu(menuText); //addAction(target, jm, ExtKit.gotoSourceAction); addAction(target, jm, ExtKit.gotoDeclarationAction); //addAction(target, jm, gotoSuperImplementationAction); //addAction(target, jm, ExtKit.gotoAction); return jm; } } /** * @deprecated Not actually deprecated... */ @Deprecated public static class GrammarGoToDeclarationAction extends GotoDeclarationAction { public GrammarGoToDeclarationAction() { } @Override public boolean gotoDeclaration(JTextComponent target) { if (!(target.getDocument() instanceof StyledDocument)) return false; GoToSupport.goTo((StyledDocument) target.getDocument(), target.getCaretPosition(), false); return true; } } }