Package ideah.imports

Source Code of ideah.imports.HaskellImportOptimizer

package ideah.imports;

import com.intellij.lang.ImportOptimizer;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import ideah.compiler.GHCMessage;
import ideah.compiler.LaunchGHC;
import ideah.parser.HaskellFile;
import ideah.util.CompilerLocation;
import ideah.util.DeclarationPosition;
import ideah.util.LineColRange;
import ideah.util.ProcessLauncher;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HaskellImportOptimizer implements ImportOptimizer {

    private static final Logger LOG = Logger.getInstance("ideah.imports.HaskellImportOptimizer");

    @Override
    public boolean supports(PsiFile psiFile) {
        return psiFile instanceof HaskellFile;
    }

    @NotNull
    @Override
    public Runnable processFile(final PsiFile psiFile) {
        Runnable empty = EmptyRunnable.getInstance();
        final String path = psiFile.getVirtualFile().getPath();
        Module module = DeclarationPosition.getDeclModule(psiFile);
        List<GHCMessage> ghcMessages = LaunchGHC.compile(null, path, module, true);
        final List<Import> redundantImports = getRedundantImports(ghcMessages);
        if (redundantImports.isEmpty())
            return empty;
        final CompilerLocation compiler = CompilerLocation.get(module);
        if (compiler == null)
            return empty;
        return new Runnable() {

            @Override
            public void run() {
                final Map<Import, LineColRange> importRanges = getImportRanges(compiler, path);
                Collections.sort(redundantImports, new Comparator<Import>() {
                    @Override
                    public int compare(Import i1, Import i2) {
                        return importRanges.get(i2).end.compareTo(importRanges.get(i1).end);
                    }
                });
                for (Import redImp : redundantImports) {
                    LineColRange range = importRanges.get(redImp);
                    if (range != null) {
                        removeImport(range);
                    }
                }
            }

            private void removeImport(LineColRange range) {
                PsiDocumentManager manager = PsiDocumentManager.getInstance(psiFile.getProject());
                Document document = manager.getDocument(psiFile);
                if (document == null)
                    return;
                manager.commitDocument(document);
                TextRange textRange = range.getRange(psiFile);
                int start = textRange.getStartOffset();
                int end = textRange.getEndOffset();
                int line = document.getLineNumber(end);
                int lineStart = document.getLineStartOffset(line);
                int lineEnd = document.getLineEndOffset(line);
                String functionName = document.getText(textRange);
                String importLine = document.getText().substring(lineStart, lineEnd);
                int rightWhites = getWhites(Pattern.compile(".*" + functionName + "([ ]*,[ ]*).*"), importLine);
                if (rightWhites == 0) {
                    start -= getWhites(Pattern.compile(".*(,[ ]*)" + functionName + ".*"), importLine);
                } else {
                    end += rightWhites;
                }
                boolean isEndOfLine = document.getLineEndOffset(line) == end;
                int endOffset = isEndOfLine ? end + document.getLineSeparatorLength(line) : end;
                document.replaceString(start, endOffset, "");
            }
        };
    }

    private static int getWhites(Pattern whitespacePattern, String importLine) {
        Matcher whitespaceMatcher = whitespacePattern.matcher(importLine);
        if (whitespaceMatcher.matches())
            return whitespaceMatcher.group(1).length();
        return 0;
    }

    private Map<Import, LineColRange> getImportRanges(CompilerLocation compiler, String path) { // todo ugly and terrible
        List<String> args = compiler.getCompileOptionsList(
            "-m", "AllImports",
            path
        );
        Map<Import, LineColRange> ranges = new HashMap<Import, LineColRange>();
        try {
            ProcessLauncher launcher = new ProcessLauncher(true, null, args);
            BufferedReader bf = new BufferedReader(new StringReader(launcher.getStdOut()));
            String line = bf.readLine();
            while (line != null) {
                if (line.startsWith(ProcessLauncher.NEW_MSG_INDICATOR)) {
                    String moduleName = bf.readLine();
                    if (moduleName == null)
                        break;
                    String mrange = bf.readLine();
                    if (mrange == null)
                        break;
                    LineColRange mLineColRange = new LineColRange(mrange);
                    line = bf.readLine();
                    while (line != null && !ProcessLauncher.NEW_MSG_INDICATOR.equals(line)) {
                        String functionName = line;
                        String frange = bf.readLine();
                        if (frange == null)
                            break;
                        LineColRange fLineColRange = new LineColRange(frange);
                        ranges.put(new Import(moduleName, functionName), fLineColRange);
                        line = bf.readLine();
                    }
                    ranges.put(new Import(moduleName), mLineColRange);
                }
            }
        } catch (Exception e) {
            LOG.error(e);
        }
        return ranges;
    }

    private List<Import> getRedundantImports(List<GHCMessage> ghcMessages) {
        List<Import> redundantImports = new ArrayList<Import>();
        for (GHCMessage ghcMessage : ghcMessages) {
            if (ghcMessage.getCategory() == CompilerMessageCategory.WARNING) {
                String errorMessage = ghcMessage.getErrorMessage();
                Matcher moduleWarnMatcher = Pattern.compile("The (qualified )?import of `([^ ]+)' is redundant.*", Pattern.DOTALL).matcher(errorMessage);
                if (moduleWarnMatcher.matches()) {
                    redundantImports.add(new Import(moduleWarnMatcher.group(2)));
                } else {
                    Matcher functionWarnMatcher = Pattern.compile("The import of `(.+)'[ \n]+from module `([^ ]+)' is redundant.*", Pattern.DOTALL).matcher(errorMessage);
                    if (functionWarnMatcher.matches()) {
                        String functionGroup = functionWarnMatcher.group(1);
                        String module = functionWarnMatcher.group(2);
                        String[] functions = functionGroup.split(",[ \n]+");
                        if (functions.length == 0) {
                            redundantImports.add(new Import(module, functionGroup));
                        } else {
                            for (String function : functions) {
                                redundantImports.add(new Import(module, function));
                            }
                        }
                    }
                }
            }
        }
        return redundantImports;
    }

    private class Import {
        final String module;
        final String function;

        private Import(@NotNull String module) {
            this(module, null);
        }

        private Import(@NotNull String module, String function) {
            this.module = module;
            this.function = function;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Import anImport = (Import) o;
            if (function != null ? !function.equals(anImport.function) : anImport.function != null) return false;
            return module.equals(anImport.module);

        }

        @Override
        public int hashCode() {
            int result = module.hashCode();
            result = 31 * result + (function != null ? function.hashCode() : 0);
            return result;
        }
    }
}
TOP

Related Classes of ideah.imports.HaskellImportOptimizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.