Skip to content

Commit e1dac6e

Browse files
authored
Merge pull request #4 from iTrace-Dev/develop
Create first release of iTrace-JetBrains
2 parents a603e81 + a00417b commit e1dac6e

File tree

6 files changed

+770
-21
lines changed

6 files changed

+770
-21
lines changed

.idea/vcs.xml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LICENSE.md

Lines changed: 675 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,37 @@
11
# iTrace-JetBrains
2-
iTrace support of the JetBrains family of IDEs
2+
iTrace-JetBrains is a plugin for the [JetBrains](https://www.jetbrains.com/) family of IDEs. The plugin will establish a connection to the [iTrace Core](https://github.com/iTrace-Dev/iTrace-Core) desktop application. Once connected to the Core, the plugin will accept eye-tracking information from the Core and translate it to editor-specific data and output said data to an XML file.
3+
4+
# Installation
5+
1. Download the latest version of the plugin. The file is named `iTrace-JetBrains-X.X.X.zip`.
6+
2. Open the desired JetBrains editor. iTrace-JetBrains was tested on IntelliJ IDEA, PyCharm, WebStorm, and RustRover - however, it should work on any of the various JetBrains IDEs.
7+
3. Open the Settings menu and navigate to "Plugins".
8+
4. Click on the gear icon next to "Installed". Then, select "Install Plugin from Disk...".
9+
5. Navigate to and select the .zip file you downloaded earlier.
10+
6. Accept any prompts you are given, and click "Apply" in the Settings menu.
11+
12+
# Usage
13+
To use iTrace-JetBrains, make sure you have iTrace-Core installed.
14+
1. Open the project/files you wish to view.
15+
2. Run iTrace-Core and set up the parameters of your tracking session.
16+
3. Go to the "Tools" tab in your JetBrains editor.
17+
4. Select "Connect to iTrace-Core". If the connection is successful, a notification at the bottom of the screen should alert you.
18+
5. iTrace-JetBrains is now connected, you can control the tracking session using iTrace-Core.
19+
6. Once a tracking session is started, iTrace-JetBrains will begin writing to a file in the location specified in iTrace-Core. When the tracking session is finished, two files will be present - one from iTrace-JetBrains and the other from iTrace-Core. iTrace-JetBrains will recongize which JetBrains IDE you are using and automatically name the file appropriately.
20+
21+
# How to Install From Source
22+
If you want to install or run the plugin from source, follow these steps:
23+
1. Download or clone the source code.
24+
2. Open IntelliJ IDEA.
25+
3. Select "Open" and select the folder containing iTrace-JetBrains
26+
You should now have the project open in IntelliJ. You have two options for building the plugin.
27+
## Running the Pluign
28+
If you want to run and debug the plugin, go to the top and select the "Current File" dropdown. Select "Run Plugin" and then click the green play button. This will launch a new instance of IntelliJ with the plugin running, and you can debug it.
29+
## Building the Plugin for Distribution
30+
To build the plugin for distribution and installation, follow these steps:
31+
1. Open the project in IntelliJ.
32+
2. Select View->Tool Windows->Terminal.
33+
3. Run `.\gradlew buildPlugin` in the opened terminal.
34+
4. After running, the built plugin will be in the `build\distributions`.
35+
36+
# Further Steps
37+
After gathering your data, you can use our other tools [iTrace-Toolkit](https://github.com/iTrace-Dev/iTrace-Toolkit) and [iTrace-Visualize](https://github.com/iTrace-Dev/iTrace-Visualize) to analyze and process the tracking sessions.

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
group = "org.itrace"
8-
version = "1.0-SNAPSHOT"
8+
version = "0.2.0"
99

1010
repositories {
1111
mavenCentral()

src/main/java/org/itrace/ConnectionSingleton.java

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
import java.awt.*;
44
import java.io.*;
55
import java.net.Socket;
6+
import java.util.Locale;
67
import java.util.concurrent.atomic.AtomicReference;
78

9+
import com.intellij.notification.NotificationType;
10+
import com.intellij.notification.Notification;
11+
import com.intellij.notification.Notifications;
12+
import com.intellij.openapi.application.ApplicationInfo;
813
import com.intellij.openapi.application.ApplicationManager;
14+
import com.intellij.openapi.editor.Document;
915
import com.intellij.openapi.editor.LogicalPosition;
1016
import com.intellij.openapi.fileEditor.FileEditorManager;
1117
import com.intellij.openapi.progress.ProgressIndicator;
@@ -15,6 +21,8 @@
1521
import org.jetbrains.annotations.NotNull;
1622
import org.jetbrains.annotations.Nullable;
1723

24+
25+
1826
public class ConnectionSingleton {
1927
private static ConnectionSingleton instance;
2028
private Socket socket;
@@ -29,7 +37,9 @@ public static ConnectionSingleton getInstance() {
2937
return instance;
3038
}
3139

32-
private ConnectionSingleton() {
40+
private ConnectionSingleton() { }
41+
42+
public void ProcessCoreData(@Nullable Project project, @NotNull ProgressIndicator indicator) {
3343
try {
3444
socket = new Socket(hostName,port);
3545
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
@@ -38,9 +48,7 @@ private ConnectionSingleton() {
3848
System.err.println("Connection Error - iTrace-Core might not be running");
3949
throw new RuntimeException(e);
4050
}
41-
}
4251

43-
public void ProcessCoreData(@Nullable Project project, @NotNull ProgressIndicator indicator) {
4452
while(socket != null && !socket.isClosed()) {
4553
String data = null;
4654
try {
@@ -53,13 +61,15 @@ public void ProcessCoreData(@Nullable Project project, @NotNull ProgressIndicato
5361

5462
if(tokens[0].equals("session_start")) {
5563
indicator.setText("Session starting");
64+
String ide = ApplicationInfo.getInstance().getFullApplicationName().split(" ")[0];
5665
try {
57-
xmlFile = new BufferedWriter(new FileWriter(tokens[3]+String.format("\\itrace_jetbrains-%s.xml",System.currentTimeMillis()), true));
66+
xmlFile = new BufferedWriter(new FileWriter(tokens[3]+String.format("\\itrace_%s-%d.xml",ide.toLowerCase(),System.currentTimeMillis()), true));
5867
xmlFile.write("<?xml version=\"1.0\"?>\n");
5968
xmlFile.write("<itrace_plugin session_id=\"" + tokens[1] + "\">\n");
60-
xmlFile.write(String.format(" <environment screen_width=\"%d\" screen_height=\"%d\" plugin_type=\"JETBRAINS\"/>\n",
69+
xmlFile.write(String.format(" <environment screen_width=\"%d\" screen_height=\"%d\" plugin_type=\"%s\"/>\n",
6170
Toolkit.getDefaultToolkit().getScreenSize().width,
62-
Toolkit.getDefaultToolkit().getScreenSize().height));
71+
Toolkit.getDefaultToolkit().getScreenSize().height,
72+
ide.toUpperCase()));
6373
xmlFile.write(" <gazes>\n");
6474
}
6575
catch (IOException e) {
@@ -78,33 +88,59 @@ else if(tokens[0].equals("session_end")) {
7888
throw new RuntimeException(e);
7989
}
8090
indicator.setText("Session ended.");
91+
System.out.println("Session ended");
8192
}
8293

8394
else if(tokens[0].equals("gaze")) {
8495
try {
8596
int x = Integer.parseInt(tokens[2]);
8697
int y = Integer.parseInt(tokens[3]);
8798
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
99+
if(editor == null) {
100+
continue;
101+
}
88102
int line_height = -1;//editor.getComponent().getFontMetrics(editor.getColorsScheme().getFontPreferences().getFontType()).getHeight();
89103
float font_size = editor.getColorsScheme().getEditorFontSize2D();
90104
String filename = FileEditorManager.getInstance(project).getSelectedFiles()[0].getPath();
91105
int editor_x = editor.getContentComponent().getLocationOnScreen().x;
92106
int editor_y = editor.getContentComponent().getLocationOnScreen().y;
93-
Point gaze_point = new Point(x-editor_x,y-editor_y);
94-
//LogicalPosition logicalPosition = editor.xyToLogicalPosition(gaze_point);
95107

96-
AtomicReference<LogicalPosition> logicalPositionRef = new AtomicReference<>();
108+
int line;
109+
int column;
110+
111+
if(x - editor_x < 0 || y - editor_y < 0) {
112+
line = -1;
113+
column = -1;
114+
}
115+
else {
116+
117+
Point gaze_point = new Point(x - editor_x, y - editor_y);
97118

98-
ApplicationManager.getApplication().invokeAndWait(() -> {
99-
// Safely access xyToLogicalPosition on the EDT
100-
logicalPositionRef.set(editor.xyToLogicalPosition(gaze_point));
101-
});
119+
AtomicReference<LogicalPosition> logicalPositionRef = new AtomicReference<>();
102120

103-
LogicalPosition logicalPosition = logicalPositionRef.get();
121+
ApplicationManager.getApplication().invokeAndWait(() -> {
122+
// Safely access xyToLogicalPosition on the EDT
123+
logicalPositionRef.set(editor.xyToLogicalPosition(gaze_point));
124+
});
104125

105-
indicator.setText(tokens[2]+","+tokens[3]+" -> " + String.valueOf(logicalPosition.line+1) +","+String.valueOf(logicalPosition.column+1));
126+
LogicalPosition logicalPosition = logicalPositionRef.get();
106127

128+
line = logicalPosition.line + 1;
129+
column = logicalPosition.column + 1;
107130

131+
Document doc = editor.getDocument();
132+
String[] text_lines = doc.getText().split("\n");
133+
if (logicalPosition.line >= text_lines.length) {
134+
line = -1;
135+
column = -1;
136+
} else if (logicalPosition.column >= text_lines[logicalPosition.line].length()) {
137+
line = -1;
138+
column = -1;
139+
}
140+
}
141+
142+
indicator.setText(tokens[2]+","+tokens[3]+" -> " + String.valueOf(line) +","+String.valueOf(column));
143+
// indicator.setText(String.valueOf(editor_x)+","+String.valueOf(editor_y)+" -> " + String.valueOf(line) +","+String.valueOf(column));
108144
xmlFile.write(String.format(" <response event_id=\"%s\" plugin_time=\"%d\" x=\"%d\" y=\"%d\" gaze_target=\"%s\" gaze_target_type=\"%s\" source_file_path=\"%s\" source_file_line=\"%d\" source_file_col=\"%d\" editor_line_height=\"%s\" editor_font_height=\"%f\" editor_line_base_x=\"\" editor_line_base_y=\"\"/>\n",
109145
tokens[1], // event ID
110146
System.currentTimeMillis(), //Plugin Time
@@ -113,8 +149,8 @@ else if(tokens[0].equals("gaze")) {
113149
filename.split("/")[filename.split("/").length-1], // Gaze Target
114150
filename.split("\\.")[filename.split("\\.").length-1], // Gaze Target Type
115151
filename, // Source File Path
116-
logicalPosition.line+1, // Source File Line
117-
logicalPosition.column+1, // Source File Column
152+
line, // Source File Line
153+
column, // Source File Column
118154
line_height, // Line Height
119155
font_size // Editor Font Height
120156
));
@@ -133,6 +169,7 @@ private void CloseConnection() {
133169
}
134170
catch (IOException e) {
135171
System.err.println("Error closing connection: " + e.getMessage());
172+
throw new RuntimeException(e);
136173
}
137174
finally {
138175
socket = null;

src/main/java/org/itrace/ConnectionTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class ConnectionTask extends Task.Backgroundable {
99
private Project project;
1010

1111
public ConnectionTask(@NotNull Project project) {
12-
super(project, "Connecting to iTrace Core", true); // "true" means the task can be canceled
12+
super(project, "Connecting to iTrace Core", false); // "true" means the task can be canceled
1313
this.project = project;
1414
}
1515

0 commit comments

Comments
 (0)