Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## [Unreleased]

## [1.1.0] - 2025-11-22

- Show VCS changes in IDE popup

## [1.0.11] - 2025-11-19

- Fix bug with time tracking after project closure
Expand Down Expand Up @@ -54,7 +58,8 @@

- Support IntelliJ Platform 2024.3.5

[Unreleased]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.0.11...HEAD
[Unreleased]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.0.11...v1.1.0
[1.0.11]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.0.10...v1.0.11
[1.0.10]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.0.9...v1.0.10
[1.0.9]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.0.8...v1.0.9
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.codeclocker
pluginName = CodeClocker
pluginRepositoryUrl = https://github.com/codeclocker/codeclocker-intellij-plugin
# SemVer format -> https://semver.org
pluginVersion = 1.0.11
pluginVersion = 1.1.0

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 242
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.codeclocker.plugin.intellij.git;

import com.codeclocker.plugin.intellij.git.LineDifferenceCalculator.LineDifferenceResult;
import com.codeclocker.plugin.intellij.services.ChangesActivityTracker;
import com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CheckinProjectPanel;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.codeclocker.plugin.intellij.git;

import com.codeclocker.plugin.intellij.services.ChangesActivityTracker;
import com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.changes.CommitContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle;
import com.codeclocker.plugin.intellij.config.Config;
import com.codeclocker.plugin.intellij.config.ConfigProvider;
import com.codeclocker.plugin.intellij.services.ChangesActivityTracker;
import com.codeclocker.plugin.intellij.services.ChangesSample;
import com.codeclocker.plugin.intellij.services.TimeSpentPerProjectLogger;
import com.codeclocker.plugin.intellij.services.TimeSpentPerProjectSample;
import com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker;
import com.codeclocker.plugin.intellij.subscription.CheckSubscriptionStateHttpClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.intellij.openapi.Disposable;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.codeclocker.plugin.intellij.services;

import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_REMOVALS;
import static com.codeclocker.plugin.intellij.services.TimeSpentPerProjectLogger.GLOBAL_STOP_WATCH;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_REMOVALS;

import com.codeclocker.plugin.intellij.stopwatch.SafeStopWatch;
import com.codeclocker.plugin.intellij.widget.TimeTrackerWidget;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.codeclocker.plugin.intellij.services;
package com.codeclocker.plugin.intellij.services.vcs;

import com.codeclocker.plugin.intellij.services.ChangesSample;
import com.intellij.openapi.diagnostic.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -10,11 +12,15 @@

public class ChangesActivityTracker {

private static final Logger LOG = Logger.getInstance(ChangesActivityTracker.class);

public static final AtomicLong GLOBAL_ADDITIONS = new AtomicLong(0);
public static final AtomicLong GLOBAL_REMOVALS = new AtomicLong(0);

private final Map<String, Map<String, ChangesSample>> fileNameByChangesSample =
new ConcurrentHashMap<>();
private final Map<String, ProjectChangesCounters> projectChangesCounters =
new ConcurrentHashMap<>();
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public void incrementAdditions(
Expand All @@ -24,6 +30,12 @@ public void incrementAdditions(
lock.lock();
GLOBAL_ADDITIONS.addAndGet(additions);

// Update per-project counters
projectChangesCounters
.computeIfAbsent(project, p -> new ProjectChangesCounters(0, 0))
.additions()
.addAndGet(additions);

fileNameByChangesSample
.computeIfAbsent(project, p -> new ConcurrentHashMap<>())
.computeIfAbsent(filePath, f -> ChangesSample.create(extension))
Expand All @@ -39,6 +51,12 @@ public void incrementRemovals(String project, String fileName, String extension,
lock.lock();
GLOBAL_REMOVALS.addAndGet(removals);

// Update per-project counters
projectChangesCounters
.computeIfAbsent(project, p -> new ProjectChangesCounters(0, 0))
.removals()
.addAndGet(removals);

fileNameByChangesSample
.computeIfAbsent(project, p -> new ConcurrentHashMap<>())
.computeIfAbsent(fileName, f -> ChangesSample.create(extension))
Expand All @@ -60,4 +78,28 @@ public Map<String, Map<String, ChangesSample>> drain() {
lock.unlock();
}
}

public ProjectChangesCounters getProjectChanges(String projectName) {
ProjectChangesCounters counters = projectChangesCounters.get(projectName);
if (counters == null) {
return new ProjectChangesCounters(0, 0);
}
return new ProjectChangesCounters(counters.additions().get(), counters.removals().get());
}

public void initializeProjectChanges(String projectName, long additions, long removals) {
projectChangesCounters
.computeIfAbsent(projectName, p -> new ProjectChangesCounters(additions, removals))
.initialize(additions, removals);
LOG.debug(
"Initialized VCS counters for project {} with additions: {}, removals: {}",
projectName,
additions,
removals);
}

public void clearAllProjectChanges() {
LOG.debug("Clearing all per-project VCS counters");
projectChangesCounters.clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.codeclocker.plugin.intellij.services.vcs;

import java.util.concurrent.atomic.AtomicLong;

public class ProjectChangesCounters {

private final AtomicLong additions = new AtomicLong(0);
private final AtomicLong removals = new AtomicLong(0);

public ProjectChangesCounters(long initialAdditions, long initialRemovals) {
this.additions.set(initialAdditions);
this.removals.set(initialRemovals);
}

public void initialize(long initialAdditions, long initialRemovals) {
this.additions.set(initialAdditions);
this.removals.set(initialRemovals);
}

public AtomicLong additions() {
return additions;
}

public AtomicLong removals() {
return removals;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.codeclocker.plugin.intellij.widget;

import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_REMOVALS;
import static com.codeclocker.plugin.intellij.services.TimeSpentPerProjectLogger.GLOBAL_STOP_WATCH;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_REMOVALS;
import static org.apache.commons.collections.MapUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isBlank;

import com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle;
import com.codeclocker.plugin.intellij.config.Config;
import com.codeclocker.plugin.intellij.reporting.DailyTimeHttpClient;
import com.codeclocker.plugin.intellij.reporting.DailyTimeHttpClient.DailyTimeResponse;
import com.codeclocker.plugin.intellij.reporting.DailyTimeHttpClient.ProjectStats;
import com.codeclocker.plugin.intellij.services.TimeTrackerWidgetService;
import com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
Expand Down Expand Up @@ -134,10 +136,17 @@ private static void initializeAllProjectWidgets(
LOG.debug(
"Initialized GLOBAL_ADDITIONS: {}, GLOBAL_REMOVALS: {}", totalAdditions, totalRemovals);

initializeVcsChanges(projectStats);
initializeCodingTime(projectStats, totalTime);

initialized = true;
}

private static void initializeCodingTime(Map<String, ProjectStats> projectStats, long totalTime) {
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
for (Project project : openProjects) {
String projectName = project.getName();
DailyTimeHttpClient.ProjectStats stats = projectStats.get(projectName);
ProjectStats stats = projectStats.get(projectName);
long initialSeconds = stats != null ? stats.timeSpentSeconds() : 0L;

TimeTrackerWidgetService service = project.getService(TimeTrackerWidgetService.class);
Expand All @@ -150,8 +159,18 @@ private static void initializeAllProjectWidgets(
totalTime);
}
}
}

initialized = true;
private static void initializeVcsChanges(Map<String, ProjectStats> projectStats) {
ChangesActivityTracker changesTracker =
ApplicationManager.getApplication().getService(ChangesActivityTracker.class);
changesTracker.clearAllProjectChanges();

for (Map.Entry<String, ProjectStats> entry : projectStats.entrySet()) {
String projectName = entry.getKey();
ProjectStats stats = entry.getValue();
changesTracker.initializeProjectChanges(projectName, stats.additions(), stats.removals());
}
}

private static synchronized void startRetryTask() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.codeclocker.plugin.intellij.widget;

import static com.codeclocker.plugin.intellij.HubHost.HUB_UI_HOST;
import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.ChangesActivityTracker.GLOBAL_REMOVALS;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_ADDITIONS;
import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_REMOVALS;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import com.codeclocker.plugin.intellij.apikey.ApiKeyPersistence;
import com.codeclocker.plugin.intellij.apikey.EnterApiKeyAction;
import com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker;
import com.codeclocker.plugin.intellij.services.vcs.ProjectChangesCounters;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
Expand All @@ -24,10 +27,15 @@ public class TimeTrackerPopup {
private static final String ADD_API_KEY = "Add API Key";

public static ListPopup create(Project project, String totalTime, String projectTime) {
ChangesActivityTracker tracker =
ApplicationManager.getApplication().getService(ChangesActivityTracker.class);
ProjectChangesCounters projectChanges = tracker.getProjectChanges(project.getName());

List<String> items = new ArrayList<>();
items.add("Total: " + totalTime);
items.add(project.getName() + ": " + projectTime);
items.add("Committed Lines: " + getFormattedVcsChanges());
items.add("Total: " + getFormattedVcsChanges());
items.add(project.getName() + ": " + formatProjectVcsChanges(projectChanges));
items.add(OPEN_DETAILED_VIEW);

boolean hasApiKey = isNotBlank(ApiKeyPersistence.getApiKey());
Expand All @@ -36,7 +44,7 @@ public static ListPopup create(Project project, String totalTime, String project
}

BaseListPopupStep<String> step =
new BaseListPopupStep<>("Coding Activity Today", items) {
new BaseListPopupStep<>("Activity Today", items) {
@Override
public boolean isSelectable(String value) {
return OPEN_DETAILED_VIEW.equals(value) || ADD_API_KEY.equals(value);
Expand All @@ -59,7 +67,19 @@ public boolean hasSubstep(String selectedValue) {

@Override
public @Nullable ListSeparator getSeparatorAbove(String value) {
return OPEN_DETAILED_VIEW.equals(value) ? new ListSeparator() : null;
if (OPEN_DETAILED_VIEW.equals(value)) {
return new ListSeparator();
}

if (value.contains("Total: ") && value.contains("/")) {
return new ListSeparator("Committed Lines");
}

if (value.contains("Total: ") && !value.contains("/")) {
return new ListSeparator("Coding Time");
}

return null;
}
};

Expand All @@ -69,4 +89,8 @@ public boolean hasSubstep(String selectedValue) {
public static String getFormattedVcsChanges() {
return String.format("+%d / -%d", GLOBAL_ADDITIONS.get(), GLOBAL_REMOVALS.get());
}

private static String formatProjectVcsChanges(ProjectChangesCounters changes) {
return String.format("+%d / -%d", changes.additions().get(), changes.removals().get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public Icon getIcon() {
public String getTooltipText() {
String totalTime = service.getFormattedTotalTime();
String projectTime = service.getFormattedProjectTime();

return "Total coding time today: "
+ totalTime
+ ". Time on "
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<applicationService
serviceImplementation="com.codeclocker.plugin.intellij.services.TimeSpentActivityTracker"/>
<applicationService
serviceImplementation="com.codeclocker.plugin.intellij.services.ChangesActivityTracker"/>
serviceImplementation="com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker"/>
<applicationService serviceImplementation="com.codeclocker.plugin.intellij.subscription.CheckSubscriptionStateHttpClient"/>
<applicationService serviceImplementation="com.codeclocker.plugin.intellij.reporting.ActivitySampleHttpClient"/>
<applicationService serviceImplementation="com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle"/>
Expand Down
Loading