package com.siberika.idea.pascal.editor.completion; import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.siberika.idea.pascal.PascalBundle; import com.siberika.idea.pascal.lang.lexer.PascalFlexLexerImpl; import com.siberika.idea.pascal.sdk.BasePascalSdkType; import com.siberika.idea.pascal.sdk.Define; import com.siberika.idea.pascal.sdk.Directive; import com.siberika.idea.pascal.util.DocUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Author: George Bakhtadze * Date: 04/09/2016 */ class PascalCompletionInComment { private static final Pattern DIRECTIVE = Pattern.compile("\\{\\$?\\w*"); private static final Pattern DIRECTIVE_PARAM = Pattern.compile("\\{(\\$\\w+)\\s+(\\w*)"); private static final InsertHandler<LookupElement> INSERT_HANDLER_COMMENT = new CommentInsertHandler(); private static final InsertHandler<LookupElement> INSERT_HANDLER_COMMENT_CLOSED = new CommentInsertHandlerClosed(); private static class CommentInsertHandler implements InsertHandler<LookupElement> { @Override public void handleInsert(final InsertionContext context, LookupElement item) { DocUtil.adjustDocument(context.getEditor(), context.getEditor().getCaretModel().getOffset(), DocUtil.PLACEHOLDER_CARET); } } private static class CommentInsertHandlerClosed implements InsertHandler<LookupElement> { @Override public void handleInsert(final InsertionContext context, LookupElement item) { DocUtil.adjustDocument(context.getEditor(), context.getEditor().getCaretModel().getOffset(), DocUtil.PLACEHOLDER_CARET + "}"); } } static void handleComments(CompletionResultSet result, CompletionParameters parameters) { PsiElement comment = parameters.getOriginalPosition(); final boolean needClose = !DocUtil.isSingleLine(parameters.getEditor().getDocument(), comment); int ofs = parameters.getOffset() - comment.getTextOffset(); if (ofs <= 0) { return; } String text = comment.getText().substring(0, ofs); if (DIRECTIVE.matcher(text).matches()) { for (Map.Entry<String, Directive> entry : retrieveDirectives(comment).entrySet()) { result.addElement(withTexts(null, entry.getKey() + (entry.getValue().hasParameters(entry.getKey()) ? " " : ""), entry.getValue().desc) .withInsertHandler(needClose ? INSERT_HANDLER_COMMENT_CLOSED : INSERT_HANDLER_COMMENT)); } } else { Matcher m = DIRECTIVE_PARAM.matcher(text); if (m.matches()) { String id = m.group(1); result = result.withPrefixMatcher(m.group(2)); if (Directive.isDefine(id)) { Map<String, Define> defines = retrieveDefines(comment.getContainingFile() != null ? comment.getContainingFile().getVirtualFile() : null, comment.getProject()); for (Define define : defines.values()) { result.addElement(withTexts(define.name + (needClose ? "}" : ""), define.name, getDesc(comment.getProject(), define))); } } else { Directive dir = retrieveDirectives(comment).get(id); if ((dir != null) && (dir.values != null)) { for (String value : dir.values) { result.addElement(withTexts(value + (needClose ? "}" : ""), value, null)); } } } } } } private static LookupElementBuilder withTexts(String lookup, String text, String desc) { lookup = lookup != null ? lookup : text; return LookupElementBuilder.create(lookup).withLookupString(lookup.toUpperCase()).withLookupString(lookup.toLowerCase()) .withPresentableText(text).withTypeText(desc, true).withCaseSensitivity(true); } private static String getDesc(Project project, Define define) { if (define.virtualFile != null) { VirtualFile root = DirectoryIndex.getInstance(project).getInfoForFile(define.virtualFile).getContentRoot(); String start = root != null ? root.getPath() : null; Document doc = FileDocumentManager.getInstance().getDocument(define.virtualFile); String path = define.virtualFile.getPath(); path = (start != null) && path.startsWith(start) ? root.getPresentableName() + path.substring(start.length()) : path; return path + (doc != null ? ":" + doc.getLineNumber(define.offset) : ""); } return PascalBundle.message("completion.defines.source." + (define.offset < 0 ? "builtin" : "commandLine")); } private static Map<String, Define> retrieveDefines(@Nullable VirtualFile file, @NotNull Project project) { PascalFlexLexerImpl lexer = PascalFlexLexerImpl.processFile(project, file); return lexer != null ? lexer.getAllDefines() : Collections.<String, Define>emptyMap(); } private static Map<String, Directive> retrieveDirectives(PsiElement comment) { Module module = ModuleUtilCore.findModuleForPsiElement(comment); Sdk sdk = module != null ? ModuleRootManager.getInstance(module).getSdk() : null; return sdk != null ? BasePascalSdkType.getDirectives(sdk, sdk.getVersionString()) : Collections.<String, Directive>emptyMap(); } }