Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Highlight Todos in Java Exercises #95

Merged
merged 11 commits into from
Nov 8, 2023
10 changes: 6 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ intellij {
pluginName.set(properties("pluginName").get())

version.set(properties("platformVersion").get())
plugins.set(listOf("Git4Idea", "PythonCore:232.9921.47", "Pythonid:232.10072.27", "maven", "gradle"))
// PythonCore: https://plugins.jetbrains.com/plugin/7322-python-community-edition/versions
// Pythonid: https://plugins.jetbrains.com/plugin/631-python/versions
plugins.set(listOf("Git4Idea", "PythonCore:232.10203.2", "Pythonid:232.10203.10", "maven", "gradle"))
}

tasks {
Expand All @@ -56,11 +58,11 @@ tasks {
version.set(properties("pluginVersion").get())
changeNotes.set(
"""<p>
<h1>Added Review Mode</h1>
<h1>Added Todos for Tutors</h1>
<h2>Improvements</h2>
<ul>
<li>Added Review Mode for Students</li>
<li>Refactored Assessment Editor a bit</li>
<li>Updated dependencies for IntelliJ 2023.2.4</li>
<li>Todos for Tutors</li>
</ul>
</p>"""
)
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ pluginGroup=de.tum.www1.artemis.plugin.intellij
pluginName=orion
pluginRepositoryUrl=https://github.com/ls1intum/Orion
# SemVer format -> https://semver.org
pluginVersion=1.2.7
pluginVersion=1.2.8
# Last 2 digits of the year and the major version digit, 211-211.* equals (20)21.1.*
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild=232
pluginUntilBuild=
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType=
platformVersion=2023.2.3
platformVersion=2023.2.4
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins=
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
12 changes: 8 additions & 4 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

Expand Down Expand Up @@ -133,10 +130,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi

# Increase the maximum file descriptors if we can.
Expand Down Expand Up @@ -197,6 +197,10 @@ if "$cygwin" || "$msys" ; then
done
fi


# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.intellij.ui.jcef.JBCefJSQuery
import de.tum.www1.orion.connector.ide.OrionConnector
import de.tum.www1.orion.dto.Feedback
import de.tum.www1.orion.dto.ProgrammingExercise
import de.tum.www1.orion.exercise.OrionAssessmentService
import de.tum.www1.orion.exercise.assessment.OrionAssessmentService
import de.tum.www1.orion.exercise.OrionExerciseService
import de.tum.www1.orion.exercise.OrionFeedbackService
import de.tum.www1.orion.messaging.OrionIntellijStateNotifier
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/de/tum/www1/orion/dto/TodoDataObjects.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.tum.www1.orion.dto

/**
* An enum specifying different types a TODO can be attached to
*
*/
enum class AttachToType { CLASS, INTERFACE, ENUM, METHOD, FILE }

/**
* A Data-Object containing a reference to a specific java construct or to the general file
* @param file The file the task to do belongs to
* @param attachedTo the name of the object it is attached to
* @param attachToType the type of the object
* @param todo a string containing the content
*/
data class TodoDataObject(
val file: String,
val attachedTo: String,
val attachToType: AttachToType,
val todo: String
)

/**
* @param line an integer referencing a line in a file
* @param todoText the text of a task to do
*/
data class TodoReference(
val line: Int,
val todoText: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import de.tum.www1.orion.util.translate
import de.tum.www1.orion.vcs.OrionGitAdapter
import de.tum.www1.orion.vcs.OrionGitAdapter.clone
import com.intellij.openapi.diagnostic.Logger
import de.tum.www1.orion.exercise.assessment.OrionAssessmentService
import java.io.File
import java.io.IOException
import java.nio.file.Files
Expand All @@ -50,7 +51,7 @@ class OrionExerciseService(private val project: Project) {
val registry = service<OrionGlobalExerciseRegistryService>()

if (!registry.isImported(exercise.id, exerciseView)) {
runInEdt(ModalityState.NON_MODAL) {
runInEdt(ModalityState.nonModal()) {
val chooser = ImportPathChooser(project, exercise, exerciseView)
if (chooser.showAndGet()) {
FileUtil.ensureExists(File(chooser.chosenPath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ abstract class OrionInlineCommentService(private val project: Project) {
}

/**
* Closes the assementEditor
* Closes the assessmentEditor
* @param reopen specifies if the editor should be opened again after closing
*/
fun closeEditors(reopen: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.tum.www1.orion.exercise
package de.tum.www1.orion.exercise.assessment

import com.intellij.collaboration.ui.codereview.diff.EditorComponentInlaysManager
import com.intellij.openapi.application.invokeAndWaitIfNeeded
Expand All @@ -8,10 +8,11 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import de.tum.www1.orion.dto.Feedback
import de.tum.www1.orion.exercise.OrionInlineCommentService
import de.tum.www1.orion.exercise.registry.OrionTutorExerciseRegistry
import de.tum.www1.orion.messaging.OrionIntellijStateNotifier
import de.tum.www1.orion.ui.assessment.InlineAssessmentComment
import de.tum.www1.orion.ui.assessment.OrionAssessmentEditor
import de.tum.www1.orion.ui.comment.InlineAssessmentComment
import de.tum.www1.orion.ui.util.YesNoChooser
import de.tum.www1.orion.ui.util.notify
import de.tum.www1.orion.util.JsonUtils.gson
Expand Down Expand Up @@ -141,7 +142,7 @@ class OrionAssessmentService(private val project: Project) : OrionInlineCommentS
}

/**
* Synchronizes tutorfeedback with Artemis
* Synchronizes tutor-feedback with Artemis
*/
fun synchronizeWithArtemis() {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package de.tum.www1.orion.exercise.assessment

import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import de.tum.www1.orion.dto.AttachToType
import de.tum.www1.orion.dto.TodoDataObject
import de.tum.www1.orion.util.OrionAssessmentUtils
import de.tum.www1.orion.util.StaticRegex.Companion.JAVA_METHOD_REGEX
import de.tum.www1.orion.util.StaticRegex.Companion.JAVA_TODO_PREFIX
import de.tum.www1.orion.util.StaticRegex.Companion.JAVA_TODO_REGEX
import de.tum.www1.orion.util.StaticRegex.Companion.SLASH_SLASH_COMMENT_REGEX
import kotlinx.collections.immutable.toImmutableList
import java.io.File
import java.io.FileNotFoundException

/**
* A service that can extract todos from a file
* At the moment it is limited to JAVA files.
* It might work with Kotlin too as its structured similar
*
* @param project the currently opened project
*/
@Service(Service.Level.PROJECT)
class OrionTodoProviderService(private val project: Project) {


// A mapping from known Files to Todo-objects
private var todoMapping: MutableMap<String, MutableList<TodoDataObject>> = mutableMapOf()

/**
* @param path the path to the file we take a look at
* @return A list of todo references. these need to be parsed by some util class to work as comment objects
*/
private fun initializeTodoForFile(path: String) {

val todos: MutableList<TodoDataObject> = mutableListOf()
val lines: MutableList<String>?
try {
lines =
FileUtil.loadLines("${project.basePath}${File.separatorChar}${OrionAssessmentUtils.TEMPLATE}${File.separatorChar}${path}")
} catch (e: FileNotFoundException) {
todoMapping[path] = todos
return
}
var structure = ""
var structureType: AttachToType = AttachToType.FILE
var currentTodos = ""
for (line in lines) {
//filter out comments as comments shouldn't be valid attach tos for code
// if it matches a task to do regex add a task to do object
val lineWithoutComments = line.replace(SLASH_SLASH_COMMENT_REGEX, "")

if (line.matches(JAVA_TODO_REGEX)) {
currentTodos = currentTodos + line.replaceFirst(JAVA_TODO_PREFIX, "") + "\n"
continue
}

// check if its a new class/interface/enum
else if (lineWithoutComments.contains(" class ")) {
if (currentTodos != "") {
todos.add(TodoDataObject(path, structure, structureType, currentTodos.removeSuffix("\n")))
}
structure = lineWithoutComments.split(" class ")[1]
structureType = AttachToType.CLASS
} else if (lineWithoutComments.contains(" interface ")) {
if (currentTodos != "") {
todos.add(TodoDataObject(path, structure, structureType, currentTodos.removeSuffix("\n")))
}
structure = lineWithoutComments.split(" interface ")[1]
structureType = AttachToType.INTERFACE
} else if (lineWithoutComments.contains(" enum ")) {
if (currentTodos != "") {
todos.add(TodoDataObject(path, structure, structureType, currentTodos.removeSuffix("\n")))
}
structure = lineWithoutComments.split(" enum ")[1]
structureType = AttachToType.ENUM
} else if (lineWithoutComments.matches(JAVA_METHOD_REGEX)) {
// figure out name of the structure (its the first string in front of the ( bracket
if (currentTodos != "") {
todos.add(TodoDataObject(path, structure, structureType, currentTodos.removeSuffix("\n")))
}
val methodSplit = lineWithoutComments.split("(")[0].trim().split(" ")
structure = methodSplit[methodSplit.size - 1]
structureType = AttachToType.METHOD
}
// if the line is from no interest continue
else {
continue
}
currentTodos = ""
}
if (currentTodos != "") {
todos.add(TodoDataObject(path, structure, structureType, currentTodos.removeSuffix("\n")))
}

todoMapping[path] = todos
}

/**
* @param path the path to the file we take a look at
*/
fun getTodoForFile(path: String): List<TodoDataObject> {
// filter out non java files
if (!path.endsWith(".java")) {
return emptyList()
}
if (!todoMapping.keys.contains(path)) {
initializeTodoForFile(path)
}
return todoMapping[path]?.toImmutableList() ?: emptyList()
}
}
Loading
Loading