/* * Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.goide.runconfig; import com.goide.codeInsight.imports.GoGetPackageFix; import com.goide.sdk.GoPackageUtil; import com.goide.util.GoPathResolveScope; import com.goide.util.GoUtil; import com.intellij.execution.filters.Filter; import com.intellij.execution.filters.HyperlinkInfo; import com.intellij.execution.filters.OpenFileHyperlinkInfo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.temp.TempFileSystem; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.ObjectUtils; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GoConsoleFilter implements Filter { private static final Pattern MESSAGE_PATTERN = Pattern.compile("(?:^|\\s)(\\S+\\.\\w+):(\\d+)(:(\\d+))?(?=[:\\s]|$).*"); private static final Pattern GO_GET_MESSAGE_PATTERN = Pattern.compile("^[ \t]*(go get (.*))\n?$"); private static final Pattern APP_ENGINE_PATH_PATTERN = Pattern.compile("/tmp[A-z0-9]+appengine-go-bin/"); private static final Pattern GO_FILE_PATTERN = Pattern.compile("\\((\\w+\\.go)\\)"); @NotNull private final Project myProject; @Nullable private final Module myModule; @Nullable private final String myWorkingDirectoryUrl; @SuppressWarnings("unused") //used by pico container public GoConsoleFilter(@NotNull Project project) { this(project, null, null); } public GoConsoleFilter(@NotNull Project project, @Nullable Module module, @Nullable String workingDirectoryUrl) { myProject = project; myModule = module; myWorkingDirectoryUrl = ObjectUtils.chooseNotNull(workingDirectoryUrl, VfsUtilCore.pathToUrl(System.getProperty("user.dir"))); } @Override public Result applyFilter(@NotNull String line, int entireLength) { Matcher goGetMatcher = GO_GET_MESSAGE_PATTERN.matcher(line); if (goGetMatcher.find() && myModule != null) { String packageName = goGetMatcher.group(2).trim(); HyperlinkInfo hyperlinkInfo = new GoGetHyperlinkInfo(packageName, myModule); int lineStart = entireLength - line.length(); return new Result(lineStart + goGetMatcher.start(1), lineStart + goGetMatcher.end(2), hyperlinkInfo); } Matcher matcher = MESSAGE_PATTERN.matcher(line); if (!matcher.find()) { Matcher fileMatcher = GO_FILE_PATTERN.matcher(line); List<ResultItem> resultItems = ContainerUtil.newArrayList(); while (fileMatcher.find()) { VirtualFile file = findSingleFile(fileMatcher.group(1)); if (file != null) { resultItems.add(createResult(line, entireLength, fileMatcher.start(1), fileMatcher.end(1), 0, 0, file)); } } return !resultItems.isEmpty() ? new Result(resultItems) : null; } int startOffset = matcher.start(1); int endOffset = matcher.end(2); String fileName = matcher.group(1); int lineNumber = StringUtil.parseInt(matcher.group(2), 1) - 1; if (lineNumber < 0) { return null; } int columnNumber = -1; if (matcher.groupCount() > 3) { columnNumber = StringUtil.parseInt(matcher.group(4), 1) - 1; endOffset = Math.max(endOffset, matcher.end(4)); } Matcher appEnginePathMatcher = APP_ENGINE_PATH_PATTERN.matcher(fileName); if (appEnginePathMatcher.find()) { fileName = fileName.substring(appEnginePathMatcher.end()); } VirtualFile virtualFile = null; if (FileUtil.isAbsolutePlatformIndependent(fileName)) { virtualFile = ApplicationManager.getApplication().isUnitTestMode() ? TempFileSystem.getInstance().refreshAndFindFileByPath(fileName) : VirtualFileManager.getInstance().refreshAndFindFileByUrl(VfsUtilCore.pathToUrl(fileName)); } else { if (myWorkingDirectoryUrl != null) { virtualFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(myWorkingDirectoryUrl + "/" + fileName); } if (virtualFile == null && myModule != null) { virtualFile = findInGoPath(fileName); if (virtualFile == null && fileName.startsWith("src/")) { virtualFile = findInGoPath(StringUtil.trimStart(fileName, "src/")); } } if (virtualFile == null) { VirtualFile baseDir = myProject.getBaseDir(); if (baseDir != null) { virtualFile = baseDir.findFileByRelativePath(fileName); if (virtualFile == null && fileName.startsWith("src/")) { // exclude src virtualFile = baseDir.findFileByRelativePath(StringUtil.trimStart(fileName, "src/")); } } } } if (virtualFile == null) { virtualFile = findSingleFile(fileName); } if (virtualFile == null) { return null; } return createResult(line, entireLength, startOffset, endOffset, lineNumber, columnNumber, virtualFile); } @NotNull private Result createResult(@NotNull String line, int entireLength, int startOffset, int endOffset, int lineNumber, int columnNumber, @NotNull VirtualFile virtualFile) { HyperlinkInfo hyperlinkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber, columnNumber); int lineStart = entireLength - line.length(); return new Result(lineStart + startOffset, lineStart + endOffset, hyperlinkInfo); } @Nullable private VirtualFile findSingleFile(@NotNull String fileName) { if (PathUtil.isValidFileName(fileName)) { Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(myProject, fileName, GlobalSearchScope.allScope(myProject)); if (files.size() == 1) { return ContainerUtil.getFirstItem(files); } if (!files.isEmpty()) { GlobalSearchScope goPathScope = GoPathResolveScope.create(myProject, myModule, null); files = ContainerUtil.filter(files, goPathScope::accept); if (files.size() == 1) { return ContainerUtil.getFirstItem(files); } } if (!files.isEmpty()) { GlobalSearchScope smallerScope = GoUtil.moduleScopeWithoutLibraries(myProject, myModule); files = ContainerUtil.filter(files, smallerScope::accept); if (files.size() == 1) { return ContainerUtil.getFirstItem(files); } } } return null; } @Nullable private VirtualFile findInGoPath(@NotNull String fileName) { return GoPackageUtil.findByImportPath(fileName, myProject, myModule); } public static class GoGetHyperlinkInfo implements HyperlinkInfo { private final String myPackageName; private final Module myModule; public GoGetHyperlinkInfo(@NotNull String packageName, @NotNull Module module) { myPackageName = packageName; myModule = module; } @NotNull public String getPackageName() { return myPackageName; } @Override public void navigate(Project project) { GoGetPackageFix.applyFix(project, myModule, myPackageName, false); } } }