/******************************************************************************* * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package com.cisco.yangide.editor.editors.text.help; import static com.cisco.yangide.editor.editors.text.help.YangLanguageHelpLoader.DefinitionKind.KEYWORD; import static com.cisco.yangide.editor.editors.text.help.YangLanguageHelpLoader.DefinitionKind.TYPE; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ContextInformation; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateCompletionProcessor; import org.eclipse.jface.text.templates.TemplateContext; import org.eclipse.jface.text.templates.TemplateContextType; import org.eclipse.jface.text.templates.TemplateException; import org.eclipse.swt.graphics.Image; import com.cisco.yangide.core.dom.ASTNode; import com.cisco.yangide.core.dom.Module; import com.cisco.yangide.core.dom.ModuleImport; import com.cisco.yangide.core.dom.SimpleNode; import com.cisco.yangide.core.dom.SubModule; import com.cisco.yangide.core.dom.SubModuleInclude; import com.cisco.yangide.core.indexing.ElementIndexInfo; import com.cisco.yangide.core.indexing.ElementIndexType; import com.cisco.yangide.core.model.YangModelManager; import com.cisco.yangide.core.model.YangModelUtil; import com.cisco.yangide.core.parser.YangParserUtil; import com.cisco.yangide.editor.YangEditorPlugin; import com.cisco.yangide.editor.editors.YangScanner; import com.cisco.yangide.editor.editors.text.Symbols; import com.cisco.yangide.editor.editors.text.YangHeuristicScanner; import com.cisco.yangide.editor.editors.text.YangIndenter; import com.cisco.yangide.editor.templates.GeneralContextType; import com.cisco.yangide.editor.templates.YangTemplateAccess; import com.cisco.yangide.ui.internal.IYangUIConstants; import com.cisco.yangide.ui.internal.YangUIImages; /** * @author Alexey Kholupko * * TODO: 'base' keyword completion proposals: existing 'identity' */ public class YangSimpleCompletionProcessor extends TemplateCompletionProcessor implements IContentAssistProcessor { private final static ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0]; /** * Simple content assist tip closer. The tip is valid in a range of 5 characters around its * popup location. */ protected static class Validator implements IContextInformationValidator { protected int fInstallOffset; /* * @see IContextInformationValidator#isContextInformationValid(int) */ @Override public boolean isContextInformationValid(int offset) { return Math.abs(fInstallOffset - offset) < 5; } /* * @see IContextInformationValidator#install(IContextInformation, ITextViewer, int) */ @Override public void install(IContextInformation info, ITextViewer viewer, int offset) { fInstallOffset = offset; } }; private Comparator<ICompletionProposal> proposalComparator = new Comparator<ICompletionProposal>() { @Override public int compare(ICompletionProposal o1, ICompletionProposal o2) { String string1 = o1.getDisplayString(); String string2 = o2.getDisplayString(); return string1.compareToIgnoreCase(string2); } }; private static class TypedProposalsList { List<ICompletionProposal> proposals; CompletionKind type; } private final static String[] fgKeywordProposals = YangScanner.getKeywords(); private final static String[] fgBuiltinTypes = YangScanner.getTypes(); private final static Map<String, List<String>> keywordHierarchyMap = createKeywordHierarchyMap(); enum CompletionKind { None, KeywordScope, Import, Type, Uses, Include, BelongsTo, Keyword } protected IContextInformationValidator fValidator = new Validator(); private ITextViewer viewer; private int cursorPosition; private int lineNumber; private String currentPrefix = null; /** * The proposal mode for the current content assist * * @see #determineProposalMode(IDocument, int, String) */ private CompletionKind currentProposalMode = CompletionKind.None; private Module module = null; /* * (non-Javadoc) Method declared on IContentAssistProcessor */ @Override public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) { this.viewer = viewer; currentPrefix = null; TypedProposalsList result = determineProposals(); if (result == null) { return NO_PROPOSALS; } switch (result.type) { case KeywordScope: return mergeProposals(result.proposals, determineTemplateProposalsForContext(documentOffset)); default: if (result.proposals == null) { return NO_PROPOSALS; } return result.proposals.toArray(new ICompletionProposal[0]); } } /** * @return */ private static Map<String, List<String>> createKeywordHierarchyMap() { Map<String, List<String>> keywordHierarchyMap = new HashMap<String, List<String>>(); keywordHierarchyMap.put( "module", Arrays.asList(new String[] { "anyxml", "augment", "choice", "contact", "container", "description", "deviation", "extension", "feature", "grouping", "identity", "import", "include", "leaf", "leaf-list", "list", "namespace", "notification", "organization", "prefix", "reference", "revision", "rpc", "typedef", "uses", "yang-version" })); keywordHierarchyMap.put("import", Arrays.asList(new String[] { "prefix", "revision-date" })); keywordHierarchyMap.put("include", Arrays.asList(new String[] { "revision-date" })); keywordHierarchyMap.put("revision", Arrays.asList(new String[] { "description", "reference" })); keywordHierarchyMap.put( "submodule", Arrays.asList(new String[] { "anyxml", "augment", "belongs-to", "choice", "contact", "container", "description", "deviation", "extension", "feature", "grouping", "identity", "import", "include", "leaf", "leaf-list", "list", "notification", "organization", "reference", "revision", "rpc", "typedef", "uses", "yang-version" })); keywordHierarchyMap.put("belongs-to", Arrays.asList(new String[] { "prefix" })); keywordHierarchyMap.put("typedef", Arrays.asList(new String[] { "default", "description", "reference", "status", "type", "units" })); keywordHierarchyMap.put( "type", Arrays.asList(new String[] { "bit", "enum", "length", "path", "pattern", "range", "require", "instance", "type" })); keywordHierarchyMap.put( "container", Arrays.asList(new String[] { "anyxml", "choice", "config", "container", "description", "grouping", "if-feature", "leaf", "leaf-list", "list", "must", "presence", "reference", "status", "typedef", "uses", "when" })); keywordHierarchyMap.put("must", Arrays.asList(new String[] { "description", "error-app-tag", "error-message", "reference" })); keywordHierarchyMap.put( "leaf", Arrays.asList(new String[] { "config", "default", "description", "if-feature", "mandatory", "must", "reference", "status", "type", "units", "when" })); keywordHierarchyMap.put( "leaf-list", Arrays.asList(new String[] { "config", "description", "if-feature", "max-elements", "min-elements", "must", "ordered-by", "reference", "status", "type", "units", "when" })); keywordHierarchyMap.put( "list", Arrays.asList(new String[] { "anyxml", "choice", "config", "container", "description", "grouping", "if-feature", "key", "leaf", "leaf-list", "list", "max-elements", "min-elements", "must", "ordered-by", "reference", "status", "typedef", "unique", "uses", "when" })); keywordHierarchyMap.put( "choice", Arrays.asList(new String[] { "anyxml", "case", "config", "container", "default", "description", "if-feature", "leaf", "leaf-list", "list", "mandatory", "reference", "status", "when" })); keywordHierarchyMap.put( "case", Arrays.asList(new String[] { "anyxml", "choice", "container", "description", "if-feature", "leaf", "leaf-list", "list", "reference", "status", "uses", "when" })); keywordHierarchyMap.put( "anyxml", Arrays.asList(new String[] { "config", "description", "if-feature", "mandatory", "must", "reference", "status", "when" })); keywordHierarchyMap.put( "grouping", Arrays.asList(new String[] { "anyxml", "choice", "container", "description", "grouping", "leaf", "leaf-list", "list", "reference", "status", "typedef", "uses" })); keywordHierarchyMap.put( "uses", Arrays.asList(new String[] { "augment", "description", "if-feature", "refine", "reference", "status", "when" })); keywordHierarchyMap.put( "rpc", Arrays.asList(new String[] { "description", "grouping", "if-feature", "input", "output", "reference", "status", "typedef" })); keywordHierarchyMap.put( "input", Arrays.asList(new String[] { "anyxml", "choice", "container", "grouping", "leaf", "leaf-list", "list", "typedef", "uses" })); keywordHierarchyMap.put( "output", Arrays.asList(new String[] { "anyxml", "choice", "container", "grouping", "leaf", "leaf-list", "list", "typedef", "uses" })); keywordHierarchyMap.put( "notification", Arrays.asList(new String[] { "anyxml", "choice", "container", "description", "grouping", "if-feature", "leaf", "leaf-list", "list", "reference", "status", "typedef", "uses" })); keywordHierarchyMap.put( "notification", Arrays.asList(new String[] { "anyxml", "choice", "container", "description", "grouping", "if-feature", "leaf", "leaf-list", "list", "reference", "status", "typedef", "uses" })); keywordHierarchyMap.put( "augment", Arrays.asList(new String[] { "anyxml", "case", "choice", "container", "description", "if-feature", "leaf", "leaf-list", "list", "reference", "status", "uses", "when" })); keywordHierarchyMap.put("identity", Arrays.asList(new String[] { "base", "description", "reference", "status" })); keywordHierarchyMap.put("extension", Arrays.asList(new String[] { "argument", "description", "reference", "status" })); keywordHierarchyMap.put("argument", Arrays.asList(new String[] { "yin-element" })); keywordHierarchyMap.put("feature", Arrays.asList(new String[] { "description", "if-feature", "status", "reference" })); keywordHierarchyMap .put("deviation", Arrays.asList(new String[] { "description", "deviate", "n", "reference" })); keywordHierarchyMap.put( "deviate", Arrays.asList(new String[] { "config", "default", "mandatory", "max-elements", "min-elements", "must", "type", "unique", "units" })); keywordHierarchyMap.put("range", Arrays.asList(new String[] { "description", "error-app-tag", "error-message", "reference" })); keywordHierarchyMap.put("length", Arrays.asList(new String[] { "description", "error-app-tag", "error-message", "reference" })); keywordHierarchyMap.put("pattern", Arrays.asList(new String[] { "description", "error-app-tag", "error-message", "reference" })); keywordHierarchyMap.put("enum", Arrays.asList(new String[] { "description", "reference", "status", "value" })); keywordHierarchyMap .put("bit", Arrays.asList(new String[] { "description", "reference", "status", "position" })); return keywordHierarchyMap; } private ICompletionProposal[] mergeProposals(List<ICompletionProposal> a, List<ICompletionProposal> b) { List<ICompletionProposal> combinedProposals = new ArrayList<>(); if (a != null) { combinedProposals.addAll(a); } if (b != null) { combinedProposals.addAll(b); } // Collections.sort(combinedProposals, proposalComparator); return combinedProposals.toArray(new ICompletionProposal[0]); } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ @Override public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) { IContextInformation[] result = new IContextInformation[5]; for (int i = 0; i < result.length; i++) { result[i] = new ContextInformation(MessageFormat.format( "{0} {1}", new Object[] { new Integer(i), new Integer(documentOffset) }), //$NON-NLS-1$ MessageFormat .format("{0} {1}", new Object[] { new Integer(i), new Integer(documentOffset - 5), new Integer(documentOffset + 5) })); //$NON-NLS-1$ } return result; } @Override public char[] getCompletionProposalAutoActivationCharacters() { return new char[] { ':' }; } @Override public char[] getContextInformationAutoActivationCharacters() { return new char[] { '#' }; } @Override public IContextInformationValidator getContextInformationValidator() { return fValidator; } @Override public String getErrorMessage() { return null; } /** * @return new determined proposals */ private TypedProposalsList determineProposals() { ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); cursorPosition = selection.getOffset() + selection.getLength(); IDocument doc = viewer.getDocument(); try { lineNumber = doc.getLineOfOffset(cursorPosition); String prefix = getCurrentPrefix(); if (prefix == null || cursorPosition == -1) { return null; } String previousWord = determinePreviousWord(doc, cursorPosition - prefix.length()); return getProposalsFromDocument(doc, prefix, previousWord); } catch (BadLocationException e) { YangEditorPlugin.log(e); } return null; } private String determinePreviousWord(IDocument doc, int offset) throws BadLocationException { try { String previousText = doc.get(doc.getLineOffset(lineNumber), offset - doc.getLineOffset(lineNumber)).trim(); String previousWord = null; int lastPos = previousText.lastIndexOf(" "); if (lastPos == -1) { lastPos = previousText.lastIndexOf("\t"); } if (lastPos != -1) { previousWord = previousText.substring(lastPos); } else { previousWord = previousText; } return previousWord.trim(); } catch (Exception e) { return ""; } } /** * FIXME dirty and ugly */ private String determineProposalScopeKeyword(IDocument doc, int offset) { try { YangHeuristicScanner yangHeuristicScanner = new YangHeuristicScanner(doc); int blockStartPosition = yangHeuristicScanner.findOpeningPeer(offset, '{', '}'); int curPos = yangHeuristicScanner.findNonWhitespaceBackward(blockStartPosition - 1, YangHeuristicScanner.UNBOUND); int currentToken = yangHeuristicScanner.previousToken(curPos, YangHeuristicScanner.UNBOUND); // assuming identifier between keyword and { int keywordEndPos = yangHeuristicScanner.getPosition(); int keywordStartPos = keywordEndPos; if (currentToken == Symbols.TokenIDENT) { yangHeuristicScanner.previousToken(keywordEndPos, YangHeuristicScanner.UNBOUND); keywordStartPos = yangHeuristicScanner.getPosition(); } // if there was no identifier (empty or string partition) if (currentToken == Symbols.TokenKEYWORD) { keywordEndPos = curPos + 1; } String keyword = doc.get(keywordStartPos, keywordEndPos - keywordStartPos).trim(); return keyword; } catch (Exception e) { return ""; } } /** * @param document * @param prefix * @return proposals for the specified document */ protected TypedProposalsList getProposalsFromDocument(IDocument document, String prefix, String previousWord) { currentProposalMode = determineProposalMode(document, cursorPosition, prefix, previousWord); return getProposalsByMode(prefix, currentProposalMode); } protected TypedProposalsList getProposalsByMode(String prefix, CompletionKind proposalMode) { TypedProposalsList proposals = null; switch (proposalMode) { case Import: proposals = getImportProposals(prefix, true); break; case BelongsTo: proposals = getImportProposals(prefix, false); break; case Type: proposals = getTypeProposals(prefix); break; case Uses: proposals = getUsesProposals(prefix); break; case Include: proposals = getIncludeProposals(prefix); break; case KeywordScope: proposals = getKeywordScopeProposals(prefix); break; default: break; } return proposals; } /** * @param prefix * @param addModuleRevision * @return importable module names */ private TypedProposalsList getImportProposals(String prefix, boolean addModuleRevision) { // FIXME KOS: need set current project to get correct proposal ElementIndexInfo[] importModules = YangModelManager.search(null, null, null, ElementIndexType.MODULE, null, null); List<ICompletionProposal> moduleProposals = new ArrayList<ICompletionProposal>(); try { // import, prefix or revision-date String previousWord = determinePreviousWord(viewer.getDocument(), cursorPosition - prefix.length()); Map<String, String> usedImports = new HashMap<>(); for (int i = 0; i < importModules.length; i++) { ElementIndexInfo info = importModules[i]; String usedRevision = usedImports.get(info.getName()); if (usedRevision == null || !usedRevision.equals(info.getRevision())) { usedImports.put(info.getName(), info.getRevision()); ICompletionProposal proposal = generateImportProposal(previousWord, prefix, info, addModuleRevision); if (proposal != null) { moduleProposals.add(proposal); } } } // TODO // if (moduleProposals.isEmpty() && previousWord != "import" && previousWord != // "revision-date") // return getProposalsByMode(prefix, CompletionKind.KeywordScope); //or // CompletionKind.Keyword } catch (BadLocationException e) { YangEditorPlugin.log(e); } Collections.sort(moduleProposals, proposalComparator); TypedProposalsList result = new TypedProposalsList(); result.proposals = moduleProposals; result.type = CompletionKind.Import; return result; } private ICompletionProposal generateImportProposal(String previousWord, String prefix, ElementIndexInfo info, boolean addModuleRevision) { ICompletionProposal resultProposal = null; String importModuleName = info.getName(); String revision = info.getRevision(); String displayString = importModuleName; String replacementString = importModuleName; // default for "import" int replacementOffset = cursorPosition - prefix.length(); int replacementLength = prefix.length(); int replCursorPosition = cursorPosition; ASTNode nodeAtPos; switch (previousWord) { case "import": if (revision != null && !revision.isEmpty()) { String moduleRevision = ""; if (addModuleRevision) { moduleRevision = "revision-date " + revision + "; "; } String modulePrefix = YangModelUtil.retrieveModulePrefix(info); replacementString = importModuleName + " { prefix " + modulePrefix + "; " + moduleRevision + "}"; displayString = importModuleName += " (" + revision + ")"; replCursorPosition = replacementString.length(); break; } case "revision-date": nodeAtPos = module.getNodeAtPosition(cursorPosition); if (nodeAtPos instanceof ModuleImport) { if (importModuleName.equals(((ModuleImport) nodeAtPos).getName())) { replacementString = revision + "; "; displayString = revision; replCursorPosition = revision.length(); break; } } case "prefix": nodeAtPos = module.getNodeAtPosition(cursorPosition); if (nodeAtPos instanceof ModuleImport) { if (importModuleName.equals(((ModuleImport) nodeAtPos).getName())) { String modulePrefix = YangModelUtil.retrieveModulePrefix(info); replacementString = modulePrefix + "; "; displayString = importModuleName; replCursorPosition = replacementString.length(); break; } } default: return null; } if (prefix.length() == 0 || importModuleName.startsWith(prefix)) { resultProposal = new YangCompletionProposal(replacementString, replacementOffset, replacementLength, replCursorPosition, YangUIImages.getImage(IYangUIConstants.IMG_IMPORT_PROPOSAL), displayString, IndexInfoProposalHelpGenerator.module(info)); } return resultProposal; } /** * @param prefix * @return general keyword proposals */ private TypedProposalsList getKeywordScopeProposals(String prefix) { String scopeKeyword = determineProposalScopeKeyword(viewer.getDocument(), cursorPosition); List<String> keywordProposals = YangSimpleCompletionProcessor.keywordHierarchyMap.get(scopeKeyword); if (keywordProposals == null) { keywordProposals = Arrays.asList(fgKeywordProposals); } List<ICompletionProposal> proposalsList = new ArrayList<ICompletionProposal>(); for (String proposal : keywordProposals) { if (proposal.startsWith(prefix)) { String replacement = proposal + ' '; proposalsList.add(new YangCompletionProposal(replacement, cursorPosition - prefix.length(), prefix .length(), replacement.length(), YangUIImages.getImage(IYangUIConstants.IMG_KEYWORD_PROPOSAL), proposal, new LanguageProposalHelpGenerator(proposal, KEYWORD))); } } Collections.sort(proposalsList, proposalComparator); TypedProposalsList result = new TypedProposalsList(); result.proposals = proposalsList; result.type = CompletionKind.KeywordScope; return result; } private TypedProposalsList getTypeProposals(String prefix) { // bult-in types List<ICompletionProposal> bltInTypesProposals = new ArrayList<ICompletionProposal>(); for (String proposal : fgBuiltinTypes) { if (proposal.startsWith(prefix)) { bltInTypesProposals.add(new YangCompletionProposal(proposal, cursorPosition - prefix.length(), prefix .length(), proposal.length(), YangUIImages.getImage(IYangUIConstants.IMG_TYPE_PROPOSAL), proposal, new LanguageProposalHelpGenerator(proposal, TYPE))); } } Collections.sort(bltInTypesProposals, proposalComparator); // custom defined by user in typedef types ElementIndexInfo[] customTypes = YangModelManager.search(null, null, null, ElementIndexType.TYPE, null, null); List<ICompletionProposal> customTypesProposals = new ArrayList<ICompletionProposal>(); Set<String> addedCustomType = new HashSet<>(); for (int i = 0; i < customTypes.length; i++) { ElementIndexInfo info = customTypes[i]; if (addedCustomType.add(info.getName())) { String proposal = computeProposalForElement(info); if (proposal != null && (prefix.length() == 0 || proposal.startsWith(prefix))) { customTypesProposals.add(new YangCompletionProposal(proposal, cursorPosition - prefix.length(), prefix.length(), proposal.length(), YangUIImages .getImage(IYangUIConstants.IMG_CUSTOM_TYPE_PROPOSAL), proposal, IndexInfoProposalHelpGenerator.type(info))); } } } Collections.sort(customTypesProposals, proposalComparator); TypedProposalsList result = new TypedProposalsList(); result.proposals = new ArrayList<ICompletionProposal>(bltInTypesProposals.size() + customTypesProposals.size()); result.proposals.addAll(bltInTypesProposals); result.proposals.addAll(customTypesProposals); result.type = CompletionKind.Type; return result; } /** * @param prefix * @return */ private TypedProposalsList getUsesProposals(String prefix) { ElementIndexInfo[] groupings = YangModelManager.search(null, null, null, ElementIndexType.GROUPING, null, null); List<ICompletionProposal> groupingProposals = new ArrayList<ICompletionProposal>(); Set<String> addedCustomType = new HashSet<>(); for (int i = 0; i < groupings.length; i++) { ElementIndexInfo info = groupings[i]; if (addedCustomType.add(info.getName())) { String proposal = computeProposalForElement(info); if (proposal != null && (prefix.length() == 0 || proposal.startsWith(prefix))) { groupingProposals.add(new YangCompletionProposal(proposal, cursorPosition - prefix.length(), prefix .length(), proposal.length(), YangUIImages.getImage(IYangUIConstants.IMG_GROUPING_PROPOSAL), proposal, IndexInfoProposalHelpGenerator.grouping(info))); } } } Collections.sort(groupingProposals, proposalComparator); TypedProposalsList result = new TypedProposalsList(); result.proposals = groupingProposals; result.type = CompletionKind.Uses; return result; } /** * @param prefix * @return */ private TypedProposalsList getIncludeProposals(String prefix) { ElementIndexInfo[] submodules = YangModelManager.search(null, null, null, ElementIndexType.SUBMODULE, null, null); List<ICompletionProposal> submoduleProposals = new ArrayList<ICompletionProposal>(); Set<String> addedCustomType = new HashSet<>(); for (int i = 0; i < submodules.length; i++) { ElementIndexInfo info = submodules[i]; if (addedCustomType.add(info.getName())) { String proposal = info.getName(); String revision = info.getRevision(); String displayString = proposal; String replacementString = proposal; if (revision != null && !revision.isEmpty()) { replacementString = proposal + " { revision-date " + revision + "; }"; displayString = proposal += " (" + revision + ")"; } if (prefix.length() == 0 || proposal.startsWith(prefix)) { submoduleProposals.add(new YangCompletionProposal(replacementString, cursorPosition - prefix.length(), prefix.length(), proposal.length(), YangUIImages .getImage(IYangUIConstants.IMG_GROUPING_PROPOSAL), displayString, IndexInfoProposalHelpGenerator.submodule(info))); } } } Collections.sort(submoduleProposals, proposalComparator); TypedProposalsList result = new TypedProposalsList(); result.proposals = submoduleProposals; result.type = CompletionKind.Include; return result; } /** * Generate proposals for types, groupings, etc. If element defined in other module, which is * imported, adds respective prefix If element is neither imported nor local, returns null * * @param elementIndexInfo * @return */ protected String computeProposalForElement(ElementIndexInfo elementIndexInfo) { String result = null; if (module != null) { if (module.getName().equals(elementIndexInfo.getModule())) { result = elementIndexInfo.getName(); } else if (module instanceof SubModule) { SimpleNode<String> pmNode = ((SubModule) module).getParentModule(); if (pmNode.getValue().equals(elementIndexInfo.getModule())) { result = ((SubModule) module).getParentPrefix() + ":" + elementIndexInfo.getName(); } } else { SubModuleInclude includedSubmodule = module.getIncludeByName(elementIndexInfo.getModule()); if (includedSubmodule != null) { result = elementIndexInfo.getName(); } else { ModuleImport importedModule = module.getImportByName(elementIndexInfo.getModule()); if (importedModule != null) { result = importedModule.getPrefix() + ":" + elementIndexInfo.getName(); } } } } return result; } /** * returns proposal mode basing on current cursor position (context) */ protected CompletionKind determineProposalMode(IDocument document, int cursorPosition, String prefix, String previousWord) { module = YangParserUtil.parseYangFile(viewer.getDocument().get().toCharArray()); if (module != null) { ASTNode nodeAtPos = module.getNodeAtPosition(cursorPosition); if (nodeAtPos instanceof ModuleImport && cursorPosition > nodeAtPos.getStartPosition() + nodeAtPos.getNodeName().length()) { return CompletionKind.Import; } } // Dirty previous word based determination if ("import".equalsIgnoreCase(previousWord)) { return CompletionKind.Import; } if ("belongs-to".equalsIgnoreCase(previousWord)) { return CompletionKind.BelongsTo; } if ("type".equalsIgnoreCase(previousWord)) { return CompletionKind.Type; } if ("uses".equalsIgnoreCase(previousWord)) { return CompletionKind.Uses; } // TODO check if only current module if ("include".equalsIgnoreCase(previousWord)) { return CompletionKind.Include; } if (Arrays.asList(fgKeywordProposals).contains(previousWord)) { return CompletionKind.Keyword; } return CompletionKind.KeywordScope; } /** * @return current prefix that should be used for completion */ private String getCurrentPrefix() { if (currentPrefix != null) { return currentPrefix; } ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); IDocument doc = viewer.getDocument(); return getPrefixFromDocument(doc.get(), selection.getOffset() + selection.getLength()).toLowerCase(); } /** * @param aDocumentText the whole content of the edited file as String * @param anOffset the cursor position * @return prefix in the specified document text with respect to the specified offset */ protected String getPrefixFromDocument(String aDocumentText, int anOffset) { if (currentPrefix != null) { return currentPrefix; } int startOfWordToken = anOffset; char token = 'a'; if (startOfWordToken > 0) { token = aDocumentText.charAt(startOfWordToken - 1); } while (startOfWordToken > 0 && (Character.isJavaIdentifierPart(token) || '.' == token || '-' == token || ';' == token || ':' == token) && !('$' == token)) { startOfWordToken--; if (startOfWordToken == 0) { break; // word goes right to the beginning of the doc } token = aDocumentText.charAt(startOfWordToken - 1); } if (startOfWordToken != anOffset) { currentPrefix = aDocumentText.substring(startOfWordToken, anOffset).toLowerCase(); } else { currentPrefix = ""; } return currentPrefix; } private List<ICompletionProposal> determineTemplateProposalsForContext(int offset) { ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); // adjust offset to end of normalized selection if (selection.getOffset() == offset) { offset = selection.getOffset() + selection.getLength(); } String prefix = extractPrefix(viewer, offset); Region region = new Region(offset - prefix.length(), prefix.length()); TemplateContext context = createContext(viewer, region); if (context == null) { return null; } context.setVariable("selection", selection.getText()); // name of the selection variables {line, word_selection //$NON-NLS-1$ Template[] templates = getTemplates(context.getContextType().getId()); List<ICompletionProposal> matches = new ArrayList<ICompletionProposal>(); for (int i = 0; i < templates.length; i++) { Template template = generateIndentedTemplate(templates[i]); try { context.getContextType().validate(template.getPattern()); } catch (TemplateException e) { continue; } if ((template.getName().startsWith(prefix) && template.matches(prefix, context.getContextType().getId()))) { matches.add(// ! all proposals should be treated createProposal(template, context, (IRegion) region, getRelevance(template, prefix))); } } Collections.sort(matches, proposalComparator); return matches; } /** * returns template with correctly indented pattern TODO should be invoked when pattern is being * inserted, not computed (perfomance) */ private Template generateIndentedTemplate(Template template) { Template indentedTemplate = template; String pattern = template.getPattern(); String[] patternLines = pattern.split("\n"); int linesCount = patternLines.length; if (linesCount > 1) { YangIndenter indenteter = new YangIndenter(viewer.getDocument(), new YangHeuristicScanner( viewer.getDocument())); StringBuffer intendation = indenteter.computeIndentation(cursorPosition); StringBuffer indentedPattern = new StringBuffer(); indentedPattern.append(patternLines[0]); for (int i = 1; i < linesCount; i++) { indentedPattern.append("\n" + intendation + patternLines[i]); } indentedTemplate = new Template(template.getName(), template.getDescription(), template.getContextTypeId(), indentedPattern.toString(), template.isAutoInsertable()); } return indentedTemplate; } @Override protected String extractPrefix(ITextViewer textViewer, int offset) { return getPrefixFromDocument(textViewer.getDocument().get(), offset); } @Override protected Template[] getTemplates(String contextTypeId) { YangTemplateAccess access = YangTemplateAccess.getDefault(); return access.getTemplateStore().getTemplates(); } @Override protected TemplateContextType getContextType(ITextViewer viewer, IRegion region) { YangTemplateAccess access = YangTemplateAccess.getDefault(); return access.getContextTypeRegistry().getContextType(GeneralContextType.CONTEXT_TYPE); } /* * (non-Javadoc) * * @see * org.eclipse.jface.text.templates.TemplateCompletionProcessor#getImage(org.eclipse.jface.text * .templates.Template) */ @Override protected Image getImage(Template template) { return YangUIImages.getImage(IYangUIConstants.IMG_TEMPLATE_PROPOSAL); } }