Skip to content

Fix entry editor not closing when last library closed#15325

Merged
calixtus merged 2 commits into
JabRef:mainfrom
Syimyk43:fix/entry-editor-close-on-library-close
Mar 13, 2026
Merged

Fix entry editor not closing when last library closed#15325
calixtus merged 2 commits into
JabRef:mainfrom
Syimyk43:fix/entry-editor-close-on-library-close

Conversation

@Syimyk43

Copy link
Copy Markdown
Contributor

Related issues and pull requests

Closes #13125

PR Description

When the last open library is closed, the entry editor remained visible
instead of closing automatically. In the 'activeTabProperty' listener's 'else' branch, I added 'close()' method, which handles the zero-libraries-open case without affecting the existing behavior of keeping the editor visible when switching between entries.

Steps to test

  1. Open JabRef
  2. Open any library
  3. Click on any entry so the entry editor opens
  4. Go to File → Close Library
  5. Expected: Entry editor closes automatically and Welcome screen is shown
Screenshot 2026-03-12 at 3 22 04 AM

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
  • [/] I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
Screenshot 2026-03-12 at 3 24 03 AM
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)

  • [/] I checked the user documentation for up to dateness

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Mar 12, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Fix entry editor closing and improve groups parsing robustness

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Close entry editor automatically when last library is closed
• Improve GroupsParser to skip unknown group types gracefully
• Add comprehensive tests for GroupsParser behavior
• Update CHANGELOG documenting the entry editor fix
Diagram
flowchart LR
  A["User closes last library"] -->|activeTabProperty listener| B["EntryEditor.close() called"]
  B --> C["Entry editor closes"]
  C --> D["Welcome screen shown"]
  E["Unknown group type encountered"] -->|GroupsParser.fromString| F["Log warning and return null"]
  F --> G["Group skipped in importGroups"]
  G --> H["Parsing continues successfully"]
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java 🐞 Bug fix +1/-0

Close entry editor when no library active

• Added close() method call in the activeTabProperty listener's else branch
• Handles the case when no library tab is active (zero libraries open)
• Ensures entry editor closes automatically when last library is closed

jabgui/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java


2. jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java ✨ Enhancement +5/-5

Skip unknown groups instead of throwing exception

• Changed fromString() method to return null for unknown group types instead of throwing
 ParseException
• Added null check in importGroups() to skip unknown groups with continue statement
• Replaced exception throwing with warning log message for unknown group types
• Removed inline comments for code clarity

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java


3. jablib/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java 🧪 Tests +31/-3

Add tests for unknown group handling

• Changed test fromStringUnknownGroupThrowsException() to fromStringReturnsNullForUnknownGroup()
 with assertNull assertion
• Added assertNotNull() checks in multiple tests to verify non-null parsing results
• Added new test unknownGroupTypeIsSkipped() to verify unknown groups are skipped during import
• Added ArrayList import for test implementation

jablib/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java


View more (1)
4. CHANGELOG.md 📝 Documentation +1/-0

Document entry editor closing fix

• Added entry documenting the fix for entry editor not closing when last library is closed
• References issue #13125 with proper GitHub link

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. fromString() returns null 📘 Rule violation ⛯ Reliability
Description
GroupsParser.fromString now returns null for unknown group types, but the method contract is not
made null-safe (e.g., via JSpecify @Nullable or an Optional return type). This can lead to
unclear/null-unsafe usage and violates the project’s null-handling guidance.
Code

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[R140-141]

+        LOGGER.warn("Unknown group type, skipping: {}", input);
+        return null;
Evidence
PR Compliance requires explicit nullability contracts (JSpecify / avoiding null) and correct
Optional/null usage. The changed code path logs and returns null for unknown group types without
any nullability contract in the signature/Javadoc.

AGENTS.md
AGENTS.md
jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[98-142]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GroupsParser.fromString(...)` now returns `null` for unknown group types, but the method signature/Javadoc does not declare nullable behavior using JSpecify (or an `Optional` return type). This makes the API contract unclear and can lead to null-unsafe usage.

## Issue Context
The PR changed the behavior for unknown group types from throwing a `ParseException` to logging and returning `null`.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[98-142]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Null groups root crash 🐞 Bug ⛯ Reliability
Description
GroupsParser.importGroups can now return null when all group lines are skipped (unknown types),
but MetaDataParser passes that value into MetaData.setGroups(@NonNull ...), leading to a
NullPointerException during metadata parsing.
Code

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[R73-77]

                int level = Integer.parseInt(string.substring(0, spaceIndex));
                AbstractGroup group = GroupsParser.fromString(string.substring(spaceIndex + 1), keywordSeparator, fileMonitor, metaData, userAndHost);
+                if (group == null) {
+                    continue;
+                }
Evidence
importGroups initializes root to null and returns it without ensuring it was set; with the new
if (group == null) { continue; }, all lines can be skipped leaving root null. MetaDataParser
unconditionally calls metaData.setGroups(...), and MetaData.setGroups requires a non-null root
and dereferences it immediately.

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[54-96]
jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java[132-145]
jablib/src/main/java/org/jabref/model/metadata/MetaData.java[110-117]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`GroupsParser.importGroups(...)` can return `null` when unknown group types are skipped, and this `null` is passed to `MetaData.setGroups(@NonNull ...)`, causing an NPE.

### Issue Context
Unknown group types are now mapped to `null` by `GroupsParser.fromString(...)`. When the first/root group is unknown (or all groups are unknown), `root` stays `null` and is returned.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[54-96]
- jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java[141-145]

### Expected fix shape
- Make `importGroups` always return a non-null `GroupTreeNode` (e.g., `GroupTreeNode.fromGroup(GroupsFactory.createAllEntriesGroup())`) when parsing yields no root, OR
- Change the API to return `Optional<GroupTreeNode>` and adjust `MetaDataParser` accordingly, OR
- Throw a `ParseException` when `root` is still null at end (so the existing parse-failure flow is used).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Group hierarchy mis-nesting 🐞 Bug ✓ Correctness
Description
When importGroups skips an unknown group, it does not adjust the cursor according to the skipped
node’s level, so subsequent groups can be attached under the wrong parent, corrupting the imported
groups tree.
Code

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[R73-85]

                int level = Integer.parseInt(string.substring(0, spaceIndex));
                AbstractGroup group = GroupsParser.fromString(string.substring(spaceIndex + 1), keywordSeparator, fileMonitor, metaData, userAndHost);
+                if (group == null) {
+                    continue;
+                }
                GroupTreeNode newNode = GroupTreeNode.fromGroup(group);
                if (cursor == null) {
-                    // create new root
                    cursor = newNode;
                    root = cursor;
                } else {
-                    // insert at desired location
                    while ((level <= cursor.getLevel()) && (cursor.getParent().isPresent())) {
                        cursor = cursor.getParent().get();
                    }
Evidence
The new continue happens before the level-based cursor rewind (`while (level <= cursor.getLevel())
...`). If an unknown group appears at a shallower level than the current cursor, skipping it leaves
cursor too deep, and the next group’s insertion will be based on the wrong ancestor. Example:
levels 0 root, 1 A, 2 B, 1 <unknown skipped>, 2 C will attach C under A (due to cursor
rewinding from B to A) instead of under root (the intended parent for a level-1 sibling’s
children when that sibling is skipped).

jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[54-89]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Skipping unknown groups with `continue` prevents `cursor` from being updated for that line’s level, which can misplace subsequent nodes in the tree.

### Issue Context
`importGroups` uses indentation levels to decide where to attach the next node. The current algorithm assumes every level line yields a node; skipping violates that assumption.

### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/util/GroupsParser.java[54-89]

### Expected fix shape
Implement one of:
- Move the `while ((level &lt;= cursor.getLevel()) ... )` cursor-rewind *before* the `group == null` early-continue so cursor reflects the current line’s intended depth even when skipped.
- Preferably, maintain a `List&lt;GroupTreeNode&gt; lastNodeAtLevel` (or stack) so `parent = lastNodeAtLevel[level-1]` (falling back to the nearest lower non-null level), and on skip update the stack entry for that level appropriately.
- Add/adjust a unit test that includes an unknown group at a shallower level than the current cursor and verifies the resulting parent/child relationships.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. No test for editor close 📘 Rule violation ⛯ Reliability
Description
The entry editor closing behavior was changed, but no automated test was added/updated to prevent
regressions. This weakens confidence that the editor reliably closes when the last library is closed
across future changes.
Code

jabgui/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java[R163-166]

            } else {
                this.allPossibleTabs.clear();
+                close();
            }
Evidence
PR Compliance requires behavior changes to be covered by updated or new tests when applicable. The
PR introduces new closing behavior via close() in EntryEditor, while the PR checklist indicates
tests were not fully added.

AGENTS.md
jabgui/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java[155-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A user-visible behavior change was made (entry editor closes when the last library is closed), but there is no automated test to lock in the expected behavior.

## Issue Context
`EntryEditor` now calls `close()` when `activeTabProperty()` becomes empty.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java[155-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@github-actions github-actions Bot added the good second issue Issues that involve a tour of two or three interweaved components in JabRef label Mar 12, 2026
When no library tab is active, the entry editor remained visible.
Added close() call in the activeTabProperty listener's else branch
to handle the zero-libraries-open case.

Fixes JabRef#13125
@Syimyk43 Syimyk43 force-pushed the fix/entry-editor-close-on-library-close branch from 08800ec to 9d3ab6d Compare March 12, 2026 08:53
@jabref-machine

Copy link
Copy Markdown
Collaborator

Hey, we noticed that you force-pushed your changes. Force pushing is a bad practice when working together on a project (mainly because it is not supported well by GitHub itself). Commits are lost and comments on commits lose their context, thus making it harder to review changes.

When the pull request is getting integrated into main, all commits will be squashed anyway. Thus, your individual commit history will not be visible in main.

In future, please avoid that. For now, you can continue working.

@testlens-app

testlens-app Bot commented Mar 12, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 9d3ab6d
▶️ Tests: 10129 executed
⚪️ Checks: 52/52 completed


Learn more about TestLens at testlens.app.

@calixtus calixtus added this pull request to the merge queue Mar 13, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label Mar 13, 2026
Merged via the queue into JabRef:main with commit bd88b1c Mar 13, 2026
51 of 52 checks passed
@Syimyk43 Syimyk43 deleted the fix/entry-editor-close-on-library-close branch March 15, 2026 17:28
AnvitaPrasad pushed a commit to AnvitaPrasad/jabref that referenced this pull request Mar 18, 2026
* Close entry editor when last library is closed

When no library tab is active, the entry editor remained visible.
Added close() call in the activeTabProperty listener's else branch
to handle the zero-libraries-open case.

Fixes JabRef#13125

* Update CHANGELOG for JabRef#13125
FynnianB pushed a commit to FynnianB/jabref that referenced this pull request Mar 19, 2026
* Close entry editor when last library is closed

When no library tab is active, the entry editor remained visible.
Added close() call in the activeTabProperty listener's else branch
to handle the zero-libraries-open case.

Fixes JabRef#13125

* Update CHANGELOG for JabRef#13125
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good second issue Issues that involve a tour of two or three interweaved components in JabRef status: changes-required Pull requests that are not yet complete status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI inconsistency on the main window upon library closure

3 participants