From a35afb71e611d3f5b361f495f84777e8771533e7 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 8 Oct 2009 18:11:45 +0400 Subject: [PATCH] allow using multiple live templates with the same abbreviation (as long as they're in different groups) --- .../template/impl/EditTemplateDialog.java | 22 ++-- .../template/impl/ListTemplatesHandler.java | 28 +++-- .../template/impl/TemplateManagerImpl.java | 91 +++++++------- .../template/impl/TemplateSettings.java | 117 ++++++++++++++---- .../template/impl/TemplateState.java | 4 +- .../impl/editorActions/EnterHandler.java | 2 +- .../impl/editorActions/SpaceHandler.java | 2 +- .../impl/editorActions/TabHandler.java | 2 +- .../src/messages/CodeInsightBundle.properties | 2 +- 9 files changed, 170 insertions(+), 100 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditTemplateDialog.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditTemplateDialog.java index 6a9bb3129fb7..5aac39c26bb7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditTemplateDialog.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditTemplateDialog.java @@ -533,19 +533,19 @@ public class EditTemplateDialog extends DialogWrapper { protected void doOKAction() { String key = myKeyField.getText().trim(); + final String newGroup = (String)myGroupCombo.getSelectedItem(); + for (TemplateGroup templateGroup : myTemplateGroups) { - for (TemplateImpl template : templateGroup.getElements()) { - if (template.getKey().equals(key) && myTemplate != template) { - Messages.showMessageDialog( - getContentPane(), - CodeInsightBundle.message("dialog.edit.template.error.already.exists", key, template.getGroupName()), - CodeInsightBundle.message("dialog.edit.template.error.title"), - Messages.getErrorIcon() - ); - return; + if (templateGroup.getName().equals(newGroup)) { + for (TemplateImpl template : templateGroup.getElements()) { + if (template.getKey().equals(key) && myTemplate != template) { + Messages.showMessageDialog(getContentPane(), + CodeInsightBundle.message("dialog.edit.template.error.already.exists", key, template.getGroupName()), + CodeInsightBundle.message("dialog.edit.template.error.title"), Messages.getErrorIcon()); + return; + } } } - } if (!TemplateImplUtil.validateTemplateText(myTemplateEditor.getDocument().getText())) { @@ -559,7 +559,7 @@ public class EditTemplateDialog extends DialogWrapper { } SchemesManager schemesManager = TemplateSettings.getInstance().getSchemesManager(); - TemplateGroup group = schemesManager.findSchemeByName((String)myGroupCombo.getSelectedItem()); + TemplateGroup group = schemesManager.findSchemeByName(newGroup); if (group != null && schemesManager.isShared(group)) { Messages.showMessageDialog ( getContentPane(), diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/ListTemplatesHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/ListTemplatesHandler.java index c93342db2344..8ffcf6563c22 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/ListTemplatesHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/ListTemplatesHandler.java @@ -18,6 +18,7 @@ import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.List; public class ListTemplatesHandler implements CodeInsightActionHandler{ public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) { @@ -27,17 +28,15 @@ public class ListTemplatesHandler implements CodeInsightActionHandler{ PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); int offset = editor.getCaretModel().getOffset(); String prefix = getPrefix(editor.getDocument(), offset); - - ArrayList array = new ArrayList(); + + List matchingTemplates = new ArrayList(); for (TemplateImpl template : SurroundWithTemplateHandler.getApplicableTemplates(editor, file, false)) { - String key = template.getKey(); - if (key.startsWith(prefix)) { - array.add(new LookupItem(template, key)); + if (template.getKey().startsWith(prefix)) { + matchingTemplates.add(template); } } - LookupElement[] items = array.toArray(new LookupElement[array.size()]); - - if (items.length == 0){ + + if (matchingTemplates.size() == 0) { String text = prefix.length() == 0 ? CodeInsightBundle.message("templates.no.defined") : CodeInsightBundle.message("templates.no.defined.with.prefix", prefix); @@ -45,13 +44,24 @@ public class ListTemplatesHandler implements CodeInsightActionHandler{ return; } + showTemplatesLookup(project, editor, prefix, matchingTemplates); + } + + public static void showTemplatesLookup(final Project project, final Editor editor, String prefix, List matchingTemplates) { + ArrayList array = new ArrayList(); + for (TemplateImpl template: matchingTemplates) { + array.add(new LookupItem(template, template.getKey())); + } + LookupElement[] items = array.toArray(new LookupElement[array.size()]); + final LookupImpl lookup = (LookupImpl) LookupManager.getInstance(project).createLookup(editor, items, prefix, LookupArranger.DEFAULT); lookup.addLookupListener( new LookupAdapter() { public void itemSelected(LookupEvent event) { + final TemplateImpl template = (TemplateImpl)event.getItem().getObject(); new WriteCommandAction(project) { protected void run(Result result) throws Throwable { - TemplateManager.getInstance(project).startTemplate(editor, '\0'); + ((TemplateManagerImpl) TemplateManager.getInstance(project)).startTemplateWithPrefix(editor, template, null); } }.execute(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java index 00b69f8e9e87..0c0719a47824 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java @@ -23,9 +23,7 @@ import com.intellij.util.PairProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class TemplateManagerImpl extends TemplateManager implements ProjectComponent { protected Project myProject; @@ -108,7 +106,7 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo } public boolean startTemplate(@NotNull Editor editor, char shortcutChar) { - return startTemplate(this, editor, shortcutChar, null); + return startTemplate(editor, shortcutChar, null); } public void startTemplate(@NotNull final Editor editor, @NotNull Template template) { @@ -164,11 +162,7 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo startTemplate(editor, null, template, listener, null); } - public boolean startTemplate(TemplateManagerImpl templateManager, final Editor editor, char shortcutChar) { - return startTemplate(templateManager, editor, shortcutChar, null); - } - - public boolean startTemplate(TemplateManagerImpl templateManager, final Editor editor, char shortcutChar, final PairProcessor processor) { + public boolean startTemplate(final Editor editor, char shortcutChar, final PairProcessor processor) { final Document document = editor.getDocument(); PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject); if (file == null) return false; @@ -176,36 +170,25 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo TemplateSettings templateSettings = TemplateSettings.getInstance(); CharSequence text = document.getCharsSequence(); final int caretOffset = editor.getCaretModel().getOffset(); - TemplateImpl template = null; - int wordStart = 0; + String key = null; + List candidates = Collections.emptyList(); for (int i = templateSettings.getMaxKeyLength(); i >= 1 ; i--) { - wordStart = caretOffset - i; + int wordStart = caretOffset - i; if (wordStart < 0) { continue; } - String key = text.subSequence(wordStart, caretOffset).toString(); - template = templateSettings.getTemplate(key); - if (template != null && template.isDeactivated()) { - template = null; - } - if (template != null) { - if (Character.isJavaIdentifierStart(key.charAt(0))) { - if (wordStart > 0 && Character.isJavaIdentifierPart(text.charAt(wordStart - 1))) { - template = null; - continue; - } + key = text.subSequence(wordStart, caretOffset).toString(); + if (Character.isJavaIdentifierStart(key.charAt(0))) { + if (wordStart > 0 && Character.isJavaIdentifierPart(text.charAt(wordStart - 1))) { + continue; } - break; } + + candidates = templateSettings.collectMatchingCandidates(key, shortcutChar); + if (!candidates.isEmpty()) break; } - if (template == null) return false; - - if (shortcutChar != 0 && getShortcutChar(template) != shortcutChar) { - return false; - } - - if (template.isSelectionTemplate()) return false; + if (candidates.isEmpty()) return false; CommandProcessor.getInstance().executeCommand( myProject, new Runnable() { @@ -215,39 +198,53 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo }, "", null ); - if (!isApplicable(file, caretOffset - template.getKey().length(), template)) { + + candidates = filterApplicableCandidates(file, caretOffset - key.length(), candidates); + if (candidates.isEmpty()) { return false; } if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), myProject)) { - return false; + return false; } - final int wordStart0 = wordStart; - final TemplateImpl template0 = template; - final TemplateState templateState0 = templateManager.initTemplateState(editor); + + if (candidates.size() == 1) { + TemplateImpl template = candidates.get(0); + startTemplateWithPrefix(editor, template, processor); + } + else { + ListTemplatesHandler.showTemplatesLookup(myProject, editor, key, candidates); + } + + return true; + } + + public void startTemplateWithPrefix(final Editor editor, final TemplateImpl template, @Nullable final PairProcessor processor) { + final int caretOffset = editor.getCaretModel().getOffset(); + final int wordStart = caretOffset - template.getKey().length(); + final TemplateState templateState = initTemplateState(editor); CommandProcessor commandProcessor = CommandProcessor.getInstance(); commandProcessor.executeCommand( myProject, new Runnable() { public void run() { - editor.getDocument().deleteString(wordStart0, caretOffset); - editor.getCaretModel().moveToOffset(wordStart0); + editor.getDocument().deleteString(wordStart, caretOffset); + editor.getCaretModel().moveToOffset(wordStart); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); editor.getSelectionModel().removeSelection(); - templateState0.start(template0, processor); + templateState.start(template, processor); } }, CodeInsightBundle.message("insert.code.template.command"), null ); - return true; } - private static char getShortcutChar(TemplateImpl template) { - char c = template.getShortcutChar(); - if (c == TemplateSettings.DEFAULT_CHAR) { - return TemplateSettings.getInstance().getDefaultShortcutChar(); - } - else { - return c; + private static List filterApplicableCandidates(PsiFile file, int offset, List candidates) { + List result = new ArrayList(); + for (TemplateImpl candidate : candidates) { + if (isApplicable(file, offset, candidate)) { + result.add(candidate); + } } + return result; } public TemplateContextType getContextType(@NotNull PsiFile file, int offset) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java index 97adcacebcf7..df1d0f7f1230 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java @@ -14,6 +14,7 @@ import com.intellij.openapi.options.SchemesManagerFactory; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.util.containers.MultiMap; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; @@ -45,7 +46,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo private @NonNls static final String TEMPLATE = "template"; private @NonNls static final String DELETED_TEMPLATES = "deleted_templates"; - private final List myDeletedTemplates = new ArrayList(); + private final List myDeletedTemplates = new ArrayList(); public static final char SPACE_CHAR = ' '; public static final char TAB_CHAR = '\t'; @@ -79,7 +80,8 @@ public class TemplateSettings implements PersistentStateComponent, Expo private static final @NonNls String TEMPLATES_CONFIG_FOLDER = "templates"; - private final Map myTemplates = new LinkedHashMap(); + private final List myAllTemplates = new ArrayList(); + private final MultiMap myTemplates = new MultiMap(); private final Map myTemplatesById = new LinkedHashMap(); private final Map myDefaultTemplates = new LinkedHashMap(); @@ -92,6 +94,20 @@ public class TemplateSettings implements PersistentStateComponent, Expo private final SchemeProcessor myProcessor; private static final String FILE_SPEC = "$ROOT_CONFIG$/templates"; + private static class TemplateKey { + final String groupName; + final String key; + + private TemplateKey(String groupName, String key) { + this.groupName = groupName; + this.key = key; + } + + public static TemplateKey keyOf(TemplateImpl template) { + return new TemplateKey(template.getGroupName(), template.getKey()); + } + } + public TemplateSettings(SchemesManagerFactory schemesManagerFactory) { @@ -192,14 +208,22 @@ public class TemplateSettings implements PersistentStateComponent, Expo List children = deleted.getChildren(); for (final Object aChildren : children) { Element child = (Element)aChildren; - myDeletedTemplates.add(child.getAttributeValue(NAME)); + myDeletedTemplates.add(new TemplateKey(child.getAttributeValue(NAME), child.getAttributeValue(GROUP))); } } - for (String name : myDeletedTemplates) { - Template toDelete = myTemplates.get(name); - if (toDelete != null) { - removeTemplate(toDelete); + for (TemplateKey templateKey : myDeletedTemplates) { + if (templateKey.groupName == null) { + final Collection templates = myTemplates.get(templateKey.key); + for (TemplateImpl template : templates) { + removeTemplate(template); + } + } + else { + final TemplateImpl toDelete = getTemplate(templateKey.key, templateKey.groupName); + if (toDelete != null) { + removeTemplate(toDelete); + } } } @@ -220,9 +244,10 @@ public class TemplateSettings implements PersistentStateComponent, Expo if (myDeletedTemplates.size() > 0) { Element deleted = new Element(DELETED_TEMPLATES); - for (final String myDeletedTemplate : myDeletedTemplates) { + for (final TemplateKey deletedTemplate : myDeletedTemplates) { Element template = new Element(TEMPLATE); - template.setAttribute(NAME, myDeletedTemplate); + template.setAttribute(NAME, deletedTemplate.key); + template.setAttribute(GROUP, deletedTemplate.groupName); deleted.addContent(template); } @@ -240,7 +265,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo } public TemplateImpl[] getTemplates() { - return myTemplates.values().toArray(new TemplateImpl[myTemplates.size()]); + return myAllTemplates.toArray(new TemplateImpl[myAllTemplates.size()]); } public char getDefaultShortcutChar() { @@ -251,8 +276,18 @@ public class TemplateSettings implements PersistentStateComponent, Expo myDefaultShortcutChar = defaultShortcutChar; } - public TemplateImpl getTemplate(@NonNls String key) { - return (TemplateImpl) myTemplates.get(key); + public Collection getTemplates(@NonNls String key) { + return myTemplates.get(key); + } + + public TemplateImpl getTemplate(@NonNls String key, String group) { + final Collection templates = myTemplates.get(key); + for (TemplateImpl template : templates) { + if (template.getGroupName().equals(group)) { + return template; + } + } + return null; } public Template getTemplateById(@NonNls String id) { @@ -278,7 +313,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo } private void clearPreviouslyRegistered(final Template template) { - TemplateImpl existing = getTemplate(template.getKey()); + TemplateImpl existing = getTemplate(template.getKey(), ((TemplateImpl) template).getGroupName()); if (existing != null) { LOG.info("Template with key " + template.getKey() + " and id " + template.getId() + " already registered"); TemplateGroup group = mySchemesManager.findSchemeByName(existing.getGroupName()); @@ -288,17 +323,19 @@ public class TemplateSettings implements PersistentStateComponent, Expo mySchemesManager.removeScheme(group); } } - myTemplates.remove(template.getKey()); + myTemplates.removeValue(template.getKey(), existing); } } private void addTemplateImpl(Template template) { - if (!myTemplates.containsKey(template.getKey())) { - myTemplates.put(template.getKey(), template); - - myMaxKeyLength = Math.max(myMaxKeyLength, template.getKey().length()); + final TemplateImpl templateImpl = (TemplateImpl)template; + if (getTemplate(templateImpl.getKey(), templateImpl.getGroupName()) == null) { + myTemplates.putValue(template.getKey(), templateImpl); + myAllTemplates.add(templateImpl); } - myDeletedTemplates.remove(template.getKey()); + + myMaxKeyLength = Math.max(myMaxKeyLength, template.getKey().length()); + myDeletedTemplates.remove(TemplateKey.keyOf((TemplateImpl)template)); } @@ -312,10 +349,11 @@ public class TemplateSettings implements PersistentStateComponent, Expo } public void removeTemplate(Template template) { - myTemplates.remove(template.getKey()); + myTemplates.removeValue(template.getKey(), (TemplateImpl )template); - TemplateImpl templImpl = (TemplateImpl)template; - String groupName = templImpl.getGroupName(); + TemplateImpl templateImpl = (TemplateImpl)template; + myAllTemplates.remove(templateImpl); + String groupName = templateImpl.getGroupName(); TemplateGroup group = mySchemesManager.findSchemeByName(groupName); if (group != null) { @@ -422,14 +460,11 @@ public class TemplateSettings implements PersistentStateComponent, Expo Element element = (Element)o1; TemplateImpl template = readTemplateFromElement(isDefault, groupName, element); - boolean doNotRegister = isDefault && (myDeletedTemplates.contains(template.getKey()) || myTemplates.containsKey(template.getKey())); + boolean doNotRegister = isDefault && (myDeletedTemplates.contains(TemplateKey.keyOf(template)) || myTemplates.containsKey(template.getKey())); if(!doNotRegister) { created.put(template.getKey(), template); } - - - } if (registerTemplate) { @@ -562,7 +597,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo myTemplates.clear(); myDeletedTemplates.clear(); for (TemplateImpl template : myDefaultTemplates.values()) { - myDeletedTemplates.add(template.getKey()); + myDeletedTemplates.add(TemplateKey.keyOf(template)); } mySchemesManager.clearAllSchemes(); myMaxKeyLength = 0; @@ -584,4 +619,32 @@ public class TemplateSettings implements PersistentStateComponent, Expo public List getTemplateGroups() { return mySchemesManager.getAllSchemes(); } + + public List collectMatchingCandidates(String key, char shortcutChar) { + final Collection templates = getTemplates(key); + List candidates = new ArrayList(); + for (TemplateImpl template : templates) { + if (template.isDeactivated()) { + continue; + } + if (getShortcutChar(template) != shortcutChar) { + continue; + } + if (template.isSelectionTemplate()) { + continue; + } + candidates.add(template); + } + return candidates; + } + + private char getShortcutChar(TemplateImpl template) { + char c = template.getShortcutChar(); + if (c == DEFAULT_CHAR) { + return getDefaultShortcutChar(); + } + else { + return c; + } + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index 4854dcddfaec..c32b84e6ccb2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -73,7 +73,7 @@ public class TemplateState implements Disposable { private boolean myTemplateIndented = false; private Document myDocument; private boolean myFinished; - private PairProcessor myProcessor; + @Nullable private PairProcessor myProcessor; public TemplateState(@NotNull Project project, final Editor editor) { myProject = project; @@ -214,7 +214,7 @@ public class TemplateState implements Disposable { } } - public void start(TemplateImpl template, final PairProcessor processor) { + public void start(TemplateImpl template, @Nullable final PairProcessor processor) { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); myProcessor = processor; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/EnterHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/EnterHandler.java index bfdb443acd28..a3d9a7b0fd92 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/EnterHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/EnterHandler.java @@ -31,7 +31,7 @@ public class EnterHandler extends BaseEnterHandler { TemplateManagerImpl templateManager = (TemplateManagerImpl)TemplateManager.getInstance(project); - if (!templateManager.startTemplate(templateManager, editor, TemplateSettings.ENTER_CHAR)) { + if (!templateManager.startTemplate(editor, TemplateSettings.ENTER_CHAR)) { myOriginalHandler.execute(editor, dataContext); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java index f251787ed08d..984fcec0d31b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java @@ -29,7 +29,7 @@ public class SpaceHandler implements TypedActionHandler { } TemplateManagerImpl templateManager = (TemplateManagerImpl) TemplateManagerImpl.getInstance(project); - if (!templateManager.startTemplate(templateManager, editor, TemplateSettings.SPACE_CHAR)) { + if (!templateManager.startTemplate(editor, TemplateSettings.SPACE_CHAR)) { myOriginalHandler.execute(editor, charTyped, dataContext); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/TabHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/TabHandler.java index f15d27bc942f..23f15e80fe4d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/TabHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/TabHandler.java @@ -26,7 +26,7 @@ public class TabHandler extends EditorWriteActionHandler { TemplateManagerImpl templateManager = (TemplateManagerImpl) TemplateManagerImpl.getInstance(project); - if (!templateManager.startTemplate(templateManager, editor, TemplateSettings.TAB_CHAR)) { + if (!templateManager.startTemplate(editor, TemplateSettings.TAB_CHAR)) { myOriginalHandler.execute(editor, dataContext); } } diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index 118f4b5f785b..312f2dcb5bf6 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -295,7 +295,7 @@ dialog.edit.template.checkbox.smart.type.completion=Smart type c&ompletion dialog.edit.template.checkbox.other=O&ther dialog.edit.template.error.title=Cannot Save dialog.edit.template.error.malformed.abbreviation=Cannot save the template.\nTemplate abbreviation should contain only letters, digits, dots and hyphens. -dialog.edit.template.error.already.exists=Cannot save the template.\nTemplate with the abbreviation \"{0}\"\nalready exists in group \"{1}\".\nTry another abbreviation. +dialog.edit.template.error.already.exists=Cannot save the template.\nTemplate with the abbreviation \"{0}\"\nalready exists in group \"{1}\".\nPlease choose a different abbreviation or group. dialog.edit.template.error.malformed.template=Error parsing the template finish.template.command=Finish Template insert.code.template.command=Insert Code Template