package com.siberika.idea.pascal.util; import com.intellij.codeInsight.template.Template; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.TextExpression; import com.intellij.codeInsight.template.impl.Variable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.FileContentUtil; import com.intellij.util.SmartList; import com.siberika.idea.pascal.PascalBundle; import com.siberika.idea.pascal.lang.psi.PasModule; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Author: George Bakhtadze * Date: 08/12/2015 */ public class DocUtil { public static final Pattern RE_LF = Pattern.compile("\n"); public static final Pattern RE_WHITESPACE = Pattern.compile("\\s"); public static final Pattern RE_SEMICOLON = Pattern.compile(";"); private static final Map<String, String> DUP_MAP = getDupMap(); public static final String PLACEHOLDER_CARET = "__CARET__"; private static final int SPACES = 3; private static Map<String, String> getDupMap() { HashMap<String, String> res = new HashMap<String, String>(); res.put("()", "("); // don't add "()" if there is already "(" res.put(";", ";"); res.put("end", "end"); res.put("end;", "end"); return res; } // Adjusts content and inserts it into document placing cursor to placeholder position public static void adjustDocument(Editor editor, int offset, String content) { final Document document = editor.getDocument(); int caretOffset = content.indexOf(PLACEHOLDER_CARET); content = content.replaceAll(PLACEHOLDER_CARET, ""); adjustDocument(document, offset, content); if (caretOffset >= 0) { editor.getCaretModel().moveToOffset(offset + caretOffset); editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); } } // Adjusts content and inserts it into document public static void adjustDocument(Document document, int offset, String content) { document.insertString(offset, adjustContent(document, offset, content)); } // for each entry in DUP_MAP if content ends with key and document starts with value truncate content by key length private static String adjustContent(Document document, int offset, String content) { for (Map.Entry<String, String> entry : DUP_MAP.entrySet()) { String trimmedContent = StringUtils.stripEnd(content, null); if (trimmedContent.endsWith(entry.getKey())) { TextRange r = TextRange.from(offset, entry.getValue().length() + SPACES); if (r.getEndOffset() > document.getTextLength()) { r = TextRange.create(r.getStartOffset(), document.getTextLength()); } String trimmedDoc = StringUtils.stripStart(document.getText(r), " "); if (trimmedDoc.startsWith(entry.getValue())) { return content.substring(0, trimmedContent.length() - entry.getKey().length()); } } } return content; } public static void reformatInSeparateCommand(@NotNull final Project project, @NotNull final PsiFile file, @NotNull final Editor editor) { runCommandLaterInWriteAction(project, PascalBundle.message("action.reformat"), new Runnable() { @Override public void run() { PsiElement el = file.findElementAt(editor.getCaretModel().getOffset()); el = PsiUtil.skipToExpressionParent(el); PsiManager manager = el != null ? el.getManager() : null; if ((el != null) && (manager != null)) { CodeStyleManager.getInstance(manager).reformat(el, true); } } }); } public static void reformat(@NotNull final PsiElement block, boolean inSeparateCommand) { Runnable r = getReformatCode(block); if (inSeparateCommand) { runCommandLaterInWriteAction(block.getProject(), PascalBundle.message("action.reformat"), r); } else { r.run(); } } private static Runnable getReformatCode(final PsiElement element) { return new Runnable() { @Override public void run() { PsiManager manager = element.getManager(); if (manager != null) { CodeStyleManager.getInstance(manager).reformat(element, true); } } }; } public static int expandRangeEnd(Document doc, int endOffset, Pattern pattern) { while ((endOffset < doc.getTextLength()) && (pattern.matcher(doc.getText(TextRange.create(endOffset, endOffset+1)))).matches()) { endOffset++; } return endOffset; } // Expands range's start for symbols matching pattern public static int expandRangeStart(Document doc, int start, Pattern pattern) { while ((start > 0) && (pattern.matcher(doc.getText(TextRange.create(start-1, start)))).matches()) { start--; } return start; } public static void reformatRange(final PasModule module, final int start, final int end) { runCommandLaterInWriteAction(module.getProject(), PascalBundle.message("action.reformat"), new Runnable() { @Override public void run() { PsiManager manager = module.getManager(); if (manager != null) { CodeStyleManager.getInstance(manager).reformatRange(module, start, end, true); } } }); } public static void runCommandLaterInWriteAction(@NotNull final Project project, @NotNull final String name, final Runnable runnable) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { CommandProcessor.getInstance().executeCommand(project, runnable, name, null, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION); } }); } }); } public static Document getDocument(PsiElement parent) { PsiFile file = parent != null ? parent.getContainingFile() : null; return file != null ? PsiDocumentManager.getInstance(parent.getProject()).getDocument(file) : null; } public static Template createTemplate(String template, Map<String, String> defaults, boolean inline) { TemplateImpl tpl = new TemplateImpl("", template, ""); tpl.setToIndent(false); tpl.setToReformat(false); tpl.setToShortenLongNames(true); List<Variable> vars = new SmartList<Variable>(); for (int i = 0; i < tpl.getSegmentsCount(); i++) { String varName = tpl.getSegmentName(i); String def = defaults != null ? defaults.get(varName) : null; TextExpression expr = new TextExpression(def != null ? def : ""); Variable var = new Variable(varName, expr, expr, true, false); vars.add(var); } tpl.removeAllParsed(); for (Variable var : vars) { if (!tpl.getVariables().contains(var)) { tpl.addVariable(var.getName(), var.getExpression(), var.getDefaultValueExpression(), var.isAlwaysStopAt(), var.skipOnStart()); } } tpl.parseSegments(); tpl.setInline(inline); return tpl; } public static void reparsePsi(final Project project, final VirtualFile file) { ApplicationManager.getApplication().invokeLater( new Runnable() { @Override public void run() { new WriteCommandAction(project) { @Override protected void run(@NotNull Result result) throws Throwable { //final FileDocumentManager documentManager = FileDocumentManager.getInstance(); //((VirtualFileListener) documentManager).contentsChanged(new VirtualFileEvent(null, file, file.getName(), file.getParent())); FileContentUtil.reparseFiles(file); } }.execute(); } } ); } private static final Pattern PATTERN_TEMPLATE_VARIABLE = Pattern.compile("\\$\\w+\\$"); // Removes all template variables from the given range of the document public static void removeTemplateVariables(Document document, TextRange textRange) { String text = document.getText(textRange); Matcher m = PATTERN_TEMPLATE_VARIABLE.matcher(text); int offs = textRange.getStartOffset(); while (m.find()) { //ranges.add(TextRange.create(m.start(), m.end())); int len = m.end() - m.start(); document.deleteString(offs + m.start(), offs + m.end()); offs -= len; } } private static final Pattern EMPTY_TEXT = Pattern.compile("(\\s*(\\{.*}|\\(\\*.*\\*\\)))*\\s*"); public static boolean isFirstOnLine(Editor editor, PsiElement element) { Document d = editor.getDocument(); int offs = element.getTextRange().getStartOffset(); int line = d.getLineNumber(offs); int lineStart = d.getLineStartOffset(line); String text = d.getText(TextRange.create(lineStart, offs)); return EMPTY_TEXT.matcher(text).matches(); } public static boolean isSingleLine(@NotNull Document document, @NotNull PsiElement element) { return document.getLineNumber(element.getTextRange().getStartOffset()) == document.getLineNumber(element.getTextRange().getEndOffset()); } }