diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java index a510307b67a7..0b3998dff70f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/LocalVariablesUtil.java @@ -150,13 +150,12 @@ public class LocalVariablesUtil { com.sun.jdi.Method method = frameProxy.location().method(); final int firstLocalVariableSlot = getFirstLocalsSlot(method); - final int firstArgsSlot = getFirstArgsSlot(method); // gather code variables names - MultiMap namesMap = calcNames(new SimpleStackFrameContext(frameProxy, process), firstLocalVariableSlot, firstArgsSlot); + MultiMap namesMap = calcNames(new SimpleStackFrameContext(frameProxy, process), firstLocalVariableSlot); // first add arguments - int slot = firstArgsSlot; + int slot = getFirstArgsSlot(method); List typeNames = method.argumentTypeNames(); List argValues = frameProxy.getArgumentValues(); for (int i = 0; i < argValues.size(); i++) { @@ -317,9 +316,7 @@ public class LocalVariablesUtil { } @NotNull - private static MultiMap calcNames(@NotNull final StackFrameContext context, - final int firstLocalsSlot, - final int firstArgsSlot) { + private static MultiMap calcNames(@NotNull final StackFrameContext context, final int firstLocalsSlot) { return ApplicationManager.getApplication().runReadAction(new Computable>() { @Override public MultiMap compute() { @@ -329,7 +326,7 @@ public class LocalVariablesUtil { PsiElement method = DebuggerUtilsEx.getContainingMethod(element); if (method != null) { MultiMap res = new MultiMap<>(); - int slot = Math.max(0, firstArgsSlot + firstLocalsSlot - getFirstLocalsSlot(method)); + int slot = Math.max(0, firstLocalsSlot - getParametersStackSize(method)); for (PsiParameter parameter : DebuggerUtilsEx.getParameters(method)) { res.putValue(slot, parameter.getName()); slot += getTypeSlotSize(parameter.getType()); @@ -481,11 +478,8 @@ public class LocalVariablesUtil { } } - private static int getFirstLocalsSlot(PsiElement method) { + private static int getParametersStackSize(PsiElement method) { int startSlot = 0; - if (method instanceof PsiModifierListOwner) { - startSlot = ((PsiModifierListOwner)method).hasModifierProperty(PsiModifier.STATIC) ? 0 : 1; - } for (PsiParameter parameter : DebuggerUtilsEx.getParameters(method)) { startSlot += getTypeSlotSize(parameter.getType()); } diff --git a/java/java-tests/testData/inspection/defUse/TryWithFinally.java b/java/java-tests/testData/inspection/defUse/TryWithFinally.java new file mode 100644 index 000000000000..e0301e5d7a11 --- /dev/null +++ b/java/java-tests/testData/inspection/defUse/TryWithFinally.java @@ -0,0 +1,15 @@ +class TryWithFinally { + static class Resource implements AutoCloseable { + public void close() throws Exception { } + boolean find() { throw new UnsupportedOperationException(); } + } + + void test() throws Exception { + boolean found = false; + try (Resource r = new Resource()) { + found = r.find(); + } + finally { } + System.out.println(found); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/defUse/TryWithoutFinally.java b/java/java-tests/testData/inspection/defUse/TryWithoutFinally.java new file mode 100644 index 000000000000..1fd425f56e5b --- /dev/null +++ b/java/java-tests/testData/inspection/defUse/TryWithoutFinally.java @@ -0,0 +1,14 @@ +class TryWithoutFinally { + static class Resource implements AutoCloseable { + public void close() throws Exception { } + boolean find() { throw new UnsupportedOperationException(); } + } + + void test() throws Exception { + boolean found = false; + try (Resource r = new Resource()) { + found = r.find(); + } + System.out.println(found); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DefUseTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DefUseTest.java index f5381b03f6c6..62ddd52b6d8a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DefUseTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DefUseTest.java @@ -17,8 +17,11 @@ package com.intellij.codeInspection; import com.intellij.JavaTestUtil; import com.intellij.codeInspection.defUse.DefUseInspection; +import com.intellij.idea.Bombed; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import java.util.Calendar; + public class DefUseTest extends LightCodeInsightFixtureTestCase { @Override protected String getBasePath() { @@ -35,6 +38,8 @@ public class DefUseTest extends LightCodeInsightFixtureTestCase { public void testUsedInArrayInitializer() { doTest(); } public void testHang() { doTest(); } public void testOperatorAssignment() { doTest(); } + @Bombed(user="roman.shevchenko@jetbrains.com", day=1, month=Calendar.AUGUST, year=2017) public void testTryWithFinally() { doTest(); } + public void testTryWithoutFinally() { doTest(); } private void doTest() { myFixture.enableInspections(new DefUseInspection()); diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java index d6f4cb219f07..1ac38560d441 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java @@ -109,6 +109,18 @@ public class JavaFormatterTest extends AbstractJavaFormatterTest { "}" ); } + + public void test_format_only_selected_range() { + myTextRange = new TextRange(18, 19); + doTextTest( + "public class X {\n" + + " public int a = 2;\n" + + "}", + "public class X {\n" + + " public int a = 2;\n" + + "}" + ); + } public void testNew() throws Exception { final CommonCodeStyleSettings settings = getSettings(); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java b/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java index c63084b525f2..2d32c89e3606 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterEx.java @@ -18,8 +18,8 @@ package com.intellij.formatting; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.util.IncorrectOperationException; @@ -113,8 +113,10 @@ public abstract class FormatterEx{ final CommonCodeStyleSettings.IndentOptions indentOptions, final TextRange rangeToAdjust); - public abstract void formatAroundRange(final FormattingModel model, final CodeStyleSettings settings, - final TextRange textRange, final FileType fileType); + public abstract void formatAroundRange(final FormattingModel model, + final CodeStyleSettings settings, + final PsiFile file, + final TextRange textRange); public abstract void adjustTextRange(FormattingModel model, CodeStyleSettings settings, diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index fd153cfa9b80..7ac8fdfe1dbd 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -22,7 +22,6 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; @@ -393,17 +392,17 @@ public class FormatterImpl extends FormatterEx } @Override - public void formatAroundRange(final FormattingModel model, - final CodeStyleSettings settings, - final TextRange textRange, - final FileType fileType) { + public void formatAroundRange(FormattingModel model, + CodeStyleSettings settings, + PsiFile file, + TextRange textRange) { disableFormatting(); try { validateModel(model); final FormattingDocumentModel documentModel = model.getDocumentModel(); final Block block = model.getRootBlock(); final FormatProcessor processor = buildProcessorAndWrapBlocks( - documentModel, block, settings, settings.getIndentOptions(fileType), null + documentModel, block, settings, settings.getIndentOptionsByFile(file), null ); LeafBlockWrapper tokenBlock = processor.getFirstTokenBlock(); while (tokenBlock != null) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java index 8357337c45e6..0855aef076ce 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerImpl.java @@ -319,7 +319,7 @@ public class CodeStyleManagerImpl extends CodeStyleManager { final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(containingFile); if (builder != null) { final FormattingModel model = CoreFormatterUtil.buildModel(builder, containingFile, getSettings(), FormattingMode.REFORMAT); - FormatterEx.getInstanceEx().formatAroundRange(model, getSettings(), textRange, containingFile.getFileType()); + FormatterEx.getInstanceEx().formatAroundRange(model, getSettings(), containingFile, textRange); } adjustLineIndent(containingFile, textRange); diff --git a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java index c186bbaa5f1b..2e52936a9d81 100644 --- a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java +++ b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java @@ -20,6 +20,7 @@ import com.intellij.ide.IdeBundle; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.FileUtilRt; @@ -54,6 +55,8 @@ import java.util.zip.GZIPInputStream; * } */ public final class HttpRequests { + private static final Logger LOG = Logger.getInstance(HttpRequests.class); + private static final int BLOCK_SIZE = 16 * 1024; private static final Pattern CHARSET_PATTERN = Pattern.compile("charset=([^;]+)"); @@ -353,6 +356,9 @@ public final class HttpRequests { } private static T process(RequestBuilderImpl builder, RequestProcessor processor) throws IOException { + LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed(), + "Network shouldn't be accessed in EDT or inside read action"); + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); if (Patches.JDK_BUG_ID_8032832 && !UrlClassLoader.isRegisteredAsParallelCapable(contextLoader)) { // hack-around for class loader lock in sun.net.www.protocol.http.NegotiateAuthentication (IDEA-131621) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectImportErrorHandler.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectImportErrorHandler.java index be9121b52b42..38959f0a0728 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectImportErrorHandler.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectImportErrorHandler.java @@ -64,10 +64,11 @@ public abstract class AbstractProjectImportErrorHandler { if (location == null) { location = getLocationFrom(rootCause); } - if (rootCause.getCause() == null || rootCause.getCause().getMessage() == null) { + Throwable cause = rootCause.getCause(); + if (cause == null || cause.getMessage() == null && !(cause instanceof StackOverflowError)) { break; } - rootCause = rootCause.getCause(); + rootCause = cause; } //noinspection ConstantConditions return Pair.create(rootCause, location); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java index 3dec53619b9f..de9b472fd0ce 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java @@ -17,6 +17,7 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -104,6 +105,7 @@ public class CCProjectComponent extends AbstractProjectComponent { public void projectOpened() { migrateIfNeeded(); VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); + EduUsagesCollector.projectTypeOpened(CCUtils.COURSE_MODE); } public void projectClosed() { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index 66e036be3f03..c3cce2b1be2a 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -224,7 +224,8 @@ public class CCUtils { Document patternDocument = StudyUtils.getPatternDocument(entry.getValue(), name); Document document = FileDocumentManager.getInstance().getDocument(child); if (document == null || patternDocument == null) { - return; + LOG.info("pattern file for " + child.getPath() + " not found"); + continue; } DocumentUtil.writeInRunUndoTransparentAction(() -> { patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence()); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index 50e2336444f5..013ccf4682ba 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -27,6 +27,7 @@ import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; import java.io.*; @@ -71,6 +72,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { return; } createCourseArchive(project, module, myZipName, myLocationDir, true); + EduUsagesCollector.createdCourseArchive(); } public static void createCourseArchive(final Project project, Module module, String zipName, String locationDir, boolean showMessage) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index f1a1f3006bf6..2a24bb066e3d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -10,6 +10,7 @@ import com.intellij.openapi.project.Project; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -37,6 +38,7 @@ public class CCPushCourse extends DumbAwareAction { return; } EduStepicConnector.postCourseWithProgress(project, course); + EduUsagesCollector.courseUploaded(); } } \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java index 09a6012c5ca7..f932a185d02d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java @@ -17,6 +17,7 @@ public class CCCreateAnswerPlaceholderPanel extends JPanel { myHintText.setLineWrap(true); myHintText.setWrapStyleWord(true); myHintText.setBorder(BorderFactory.createLineBorder(JBColor.border())); + myHintText.setFont(myAnswerPlaceholderText.getFont()); myAnswerPlaceholderText.grabFocus(); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.form b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.form index 67b2e6c3e5fe..d4c33c0524d1 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.form +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.form @@ -1,6 +1,6 @@
- + @@ -20,7 +20,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -60,14 +60,6 @@ - - - - - - - - diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.java index 5e7de6356b55..b3b141d6cb8c 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCNewProjectPanel.java @@ -27,6 +27,7 @@ public class CCNewProjectPanel { myAuthorField.getDocument().addDocumentListener(new MyValidator()); myDescription.setBorder(BorderFactory.createLineBorder(JBColor.border())); + myDescription.setFont(myAuthorField.getFont()); } public CCNewProjectPanel(String name, String author, String description) { diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml index aafd06787e4e..5e67a898cc09 100644 --- a/python/educational-core/student/resources/META-INF/plugin.xml +++ b/python/educational-core/student/resources/META-INF/plugin.xml @@ -98,6 +98,9 @@ + + + diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 5a505d0bb7f3..be6f45a3abce 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -35,6 +35,7 @@ import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import javafx.application.Platform; @@ -82,6 +83,7 @@ public class StudyProjectComponent implements ProjectComponent { UISettings.getInstance().HIDE_TOOL_STRIPES = false; UISettings.getInstance().fireUISettingsChanged(); registerShortcuts(); + EduUsagesCollector.projectTypeOpened(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); } } }); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java index cb4524aab5cb..d9e6e10d07ae 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java @@ -334,7 +334,7 @@ public class StudySerializationUtils { for (Map.Entry taskFile : taskObject.getAsJsonObject(TASK_FILES).entrySet()) { String name = taskFile.getKey(); String filePath = FileUtil.join(myCourseFile.getParent(), EduNames.LESSON + lessonIndex, EduNames.TASK + taskIndex, name); - VirtualFile resourceFile = LocalFileSystem.getInstance().findFileByIoFile(new File(filePath)); + VirtualFile resourceFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(filePath)); if (resourceFile == null) { continue; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java index ab390c5b0405..35b3a70bd532 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java @@ -18,6 +18,7 @@ import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,6 +60,7 @@ public class StudyShowHintAction extends StudyActionWithShortcut { if (file == null) { return; } + EduUsagesCollector.hintShown(); String hintText = ourWarningMessage; if (answerPlaceholder != null) { String hint = answerPlaceholder.getHint(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 614d78671fab..9e85361f40a1 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -15,6 +15,7 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.editor.StudyEditor; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,16 +37,16 @@ abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut if (!studyState.isValid()) { return; } - Task nextTask = getTargetTask(studyState.getTask()); - if (nextTask == null) { + Task targetTask = getTargetTask(studyState.getTask()); + if (targetTask == null) { return; } for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) { FileEditorManager.getInstance(project).closeFile(file); } - int nextTaskIndex = nextTask.getIndex(); - int lessonIndex = nextTask.getLesson().getIndex(); - Map nextTaskFiles = nextTask.getTaskFiles(); + int nextTaskIndex = targetTask.getIndex(); + int lessonIndex = targetTask.getLesson().getIndex(); + Map nextTaskFiles = targetTask.getTaskFiles(); VirtualFile projectDir = project.getBaseDir(); String lessonDirName = EduNames.LESSON + String.valueOf(lessonIndex); if (projectDir == null) { @@ -64,7 +65,7 @@ abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut ProjectView.getInstance(project).select(taskDir, taskDir, false); return; } - + EduUsagesCollector.taskNavigation(); VirtualFile shouldBeActive = getFileToActivate(project, nextTaskFiles, taskDir); updateProjectView(project, shouldBeActive); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java index cf6d4fd21c2e..33dddd6a6a19 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java @@ -40,10 +40,12 @@ public class EduNames { public static final String SANDBOX_DIR = "Sandbox"; public static final String COURSE_META_FILE = "course.json"; - public static final String STUDY = "Study"; public static final String PYCHARM_ADDITIONAL = "PyCharm additional materials"; public static final String PYCHARM = "PyCharm"; + public static final String STUDY = "Study"; + public static final String ADAPTIVE = "Adaptive"; + public static final String PLACEHOLDER = "Answer Placeholder"; public static final String SRC = "src"; private EduNames() { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 8cef6f48da9c..6f5ccf8c6a16 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -33,6 +33,7 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.stepic.StepicUser; @@ -102,6 +103,7 @@ public class StudyProjectGenerator { VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); StudyProjectComponent.getInstance(project).registerStudyToolWindow(course); openFirstTask(course, project); + EduUsagesCollector.projectTypeCreated(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); }); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java new file mode 100644 index 000000000000..f500a1ee179e --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * 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.jetbrains.edu.learning.statistics; + +import com.intellij.internal.statistic.CollectUsagesException; +import com.intellij.internal.statistic.UsagesCollector; +import com.intellij.internal.statistic.beans.GroupDescriptor; +import com.intellij.internal.statistic.beans.UsageDescriptor; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.util.containers.FactoryMap; +import com.intellij.util.containers.hash.HashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public class EduUsagesCollector extends UsagesCollector { + private static final String GROUP_ID = "educational"; + + private final FactoryMap myUsageDescriptors = new FactoryMap() { + @Nullable + @Override + protected UsageDescriptor create(String key) { + return new UsageDescriptor(key, 0); + } + }; + + public static void projectTypeCreated(@NotNull String projectTypeId) { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("project.created." + projectTypeId).advance(); + } + + public static void projectTypeOpened(@NotNull String projectTypeId) { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("project.opened." + projectTypeId).advance(); + } + + public static void taskChecked() { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("checkTask.").advance(); + } + + public static void hintShown() { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("showHint.").advance(); + } + + public static void taskNavigation() { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("navigateToTask.").advance(); + } + + public static void courseUploaded() { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("uploadCourse.").advance(); + } + + public static void createdCourseArchive() { + ServiceManager.getService(EduUsagesCollector.class).myUsageDescriptors.get("courseArchive.").advance(); + } + + @NotNull + @Override + public Set getUsages() throws CollectUsagesException { + HashSet descriptors = new HashSet<>(); + descriptors.addAll(myUsageDescriptors.values()); + myUsageDescriptors.clear(); + return descriptors; + } + + + @NotNull + @Override + public GroupDescriptor getGroupId() { + return GroupDescriptor.create(GROUP_ID); + } +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 665586af22a2..e472f4350d0d 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -1,6 +1,8 @@ package com.jetbrains.edu.learning.stepic; -import com.google.gson.*; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; @@ -297,6 +299,9 @@ public class EduStepicConnector { info.addAuthor(author); } + String name = info.getName().replaceAll("[^a-zA-Z0-9\\s]", ""); + info.setName(name.trim()); + result.add(info); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index 5b89e8bf62a8..fce2da4a59db 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -105,6 +105,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { myDescriptionPane.setEnabled(true); myAuthorLabel.setEnabled(true); myDescriptionPane.setPreferredSize(new Dimension(150, 100)); + myDescriptionPane.setFont(coursesCombo.getFont()); myInfoPanel.add(myAuthorLabel); myInfoPanel.add(myDescriptionPane); myInfoPanel.setBorder(BorderFactory.createLineBorder(new JBColor(10067616, 10067616))); diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index 4af7914d2da4..b0b612df6f2e 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -24,6 +24,7 @@ import com.jetbrains.edu.learning.StudyProjectComponent; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.newProject.PythonProjectGenerator; import icons.CourseCreatorPythonIcons; @@ -63,6 +64,7 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire @NotNull final String name, @NotNull final String[] authors, @NotNull final String description) { final Course course = getCourse(project, name, authors, description); + EduUsagesCollector.projectTypeCreated(CCUtils.COURSE_MODE); final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir); if (projectDir == null) return; diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java index f4678a97a665..bb2264dbc18d 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java @@ -10,6 +10,7 @@ import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -22,11 +23,13 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.python.run.CommandLinePatcher; import com.jetbrains.python.run.PythonCommandLineState; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Map; public class PyCCCommandLineState extends PythonCommandLineState { private final PyCCRunTestConfiguration myRunConfiguration; @@ -58,16 +61,36 @@ public class PyCCCommandLineState extends PythonCommandLineState { group.addParameter(myRunConfiguration.getPathToTest()); group.addParameter(new File(course.getCourseDirectory()).getPath()); - group.addParameter(getFirstTaskFilePath()); + String path = getFirstTaskFilePath(); + if (path != null) { + group.addParameter(path); + } } - @NotNull + @Nullable private String getFirstTaskFilePath() { - String firstTaskFileName = StudyUtils.getFirst(myTask.getTaskFiles().keySet()); + for (Map.Entry entry : myTask.getTaskFiles().entrySet()) { + String path = getTaskFilePath(entry.getKey()); + if (!entry.getValue().getAnswerPlaceholders().isEmpty()) { + return path; + } + VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path); + if (virtualFile == null) { + continue; + } + if (TextEditorProvider.isTextFile(virtualFile)) { + return path; + } + } + return null; + } + + + private String getTaskFilePath(String name) { String taskDirPath = FileUtil.toSystemDependentName(myTaskDir.getPath()); return myTaskDir.findChild(EduNames.SRC) != null ? - FileUtil.join(taskDirPath, EduNames.SRC, firstTaskFileName) : - FileUtil.join(taskDirPath, firstTaskFileName); + FileUtil.join(taskDirPath, EduNames.SRC, name) : + FileUtil.join(taskDirPath, name); } @Override diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java index b3252a761426..9bea1b5a731a 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java @@ -21,6 +21,7 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.editor.StudyEditor; +import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,6 +33,7 @@ public class PyStudyCheckAction extends StudyCheckAction { public static final String ACTION_ID = "PyCheckAction"; public void check(@NotNull Project project) { + EduUsagesCollector.taskChecked(); ApplicationManager.getApplication().runWriteAction(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> { final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project); if (selectedEditor == null) return;