Skip to content

Commit b04489f

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix-diff-highlight-16728
* upstream/main: Document improve AI Usage Policy (JabRef#16852) Fix openfasttrace buildtime concurrency issues (JabRef#16854) Render the JabCon gource video every 15 minutes Add "Show diff" to the save-before-closing dialog (JabRef#16832) Exclude Kotlin scripts from IDEA formatting (JabRef#16790) Support modifier keys entry drag drop (JabRef#16286) Chore(deps): Bump jablib/src/main/resources/csl-styles from `0819c0e` to `db768d4` (JabRef#16820) Add per-library keyword separator (JabRef#16835) Improve logging to find out which linked file has a flaw (JabRef#15680) (JabRef#16702) Fix accented arXiv title searches (JabRef#16825) Show progress indicator during identifier lookup in New Entry dialog (JabRef#16795) Open online file links via URL-aware browser call (JabRef#16774) Chore(deps): Bump org.controlsfx:controlsfx in /versions (JabRef#16847) Keep daytime continuous in the JabCon gource video
2 parents 5fd9e55 + b52e3d2 commit b04489f

101 files changed

Lines changed: 1611 additions & 200 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/gource-jabcon.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
# Renders a gource video of the JabCon period every three hours for the JabCon board
1+
# Renders a gource video of the JabCon period every 15 minutes for the JabCon board
22
# (https://github.com/JabRef/jabcon-board). Remove or switch to workflow_dispatch only after JabCon.
33
name: Gource (JabCon)
44

55
on:
66
schedule:
7-
- cron: '0 */3 * * *'
7+
- cron: '*/15 * * * *'
88
workflow_dispatch:
99

1010
permissions:
1111
contents: read
1212

13+
# a render can take longer than the interval as the video grows; let runs queue rather than cancel each other
1314
concurrency:
1415
group: gource-jabcon
15-
cancel-in-progress: true
16+
cancel-in-progress: false
1617

1718
jobs:
1819
gource:
@@ -45,6 +46,9 @@ jobs:
4546
gource_start_date: ${{ matrix.start }}
4647
gource_stop_date: ${{ matrix.end }}
4748
gource_seconds_per_day: ${{ matrix.seconds_per_day }}
49+
# skip only long idle stretches (nights); the default 3 s would compress the whole day into jumps
50+
gource_auto_skip_seconds: '30'
51+
gource_fps: '30'
4852
gource_file_filter: 'buildres/csl|\.csl'
4953

5054
- name: Store video

.github/workflows/on-issue-comment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jobs:
4646
We're excited to have you on board.
4747
Start by exploring our [Contributing guidelines](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md), and set up your local development workspace by following the steps in our [workspace setup guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) to get started smoothly.
4848
49+
When using AI tools, please use them as an assistant rather than simply copying their output. See [Don't paste the AI, please.](https://dontpastetheai.com/).
4950
5051
For questions on JabRef functionality, you can consult the [JabRef Guru](https://gurubase.io/g/jabref).
5152
For questions related to the codebase, please start using [DeepWiki](https://deepwiki.com/JabRef/jabref).

.idea/codeStyles/Project.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
1111

1212
### Added
1313

14+
- We added a per-library keyword separator to the library properties, so opening a library no longer rewrites keyword fields. [#16835](https://github.com/JabRef/jabref/pull/16835)
15+
- We added a "Normalize keyword delimiters" cleanup and save action that rewrites keyword fields to the keyword separator of the library. [#16835](https://github.com/JabRef/jabref/pull/16835)
1416
- We added a "Commit and push" button which allows to commit and then push in one go for Git operations. [#16339](https://github.com/JabRef/jabref/issues/16339)
17+
- We added a progress spinner and status indicator to the "Enter Identifier" tab in the New Entry dialog. [#16756](https://github.com/JabRef/jabref/issues/16756)
1518
- We added the option to close and reopen the PDF preview in the unlinked files dialog. [#16159](https://github.com/JabRef/jabref/issues/16159)
1619
- We added the ability for LibreOffice BST citations to use style-defined labels. [forum#3764]([https://github.com/JabRef/jabref/issues/16357](https://discourse.jabref.org/t/feature-request-custom-citation-styles-from-bst/3764))
1720
- We added support for bibliography-defined ordering of numeric CSL styles in the LibreOffice integration. [#16692](https://github.com/JabRef/jabref/pull/16692)
1821
- We added a new "Primer" theme, based on [AtlantaFX](https://mkpaz.github.io/atlantafx/)'s Primer theme. [#15625](https://github.com/JabRef/jabref/issues/15625)
1922
- We added a semantic diff preview for Git commits of the current library. [#16341](https://github.com/JabRef/jabref/issues/16341)
23+
- We added a "Show diff" button to the "Save before closing" dialog to review unsaved changes. [#16341](https://github.com/JabRef/jabref/issues/16341)
2024
- The remote instance listener now answers the plain-text health check `JABREF/1 PING` with `JABREF/1 PONG jabref`, so external tools (e.g. browser extensions) can detect a running JabRef without speaking the Java serialization protocol. [#16654](https://github.com/JabRef/jabref/pull/16654)
2125
- We added some missing tooltips to buttons such as "Export Cited" and "Bibliography properties" in the OpenOffice/LibreOffice panel. [#16492](https://github.com/JabRef/jabref/pull/16492)
2226
- We added support for the `Export cited` functionality with CSL and BST styles in the OpenOffice/LibreOffice integration. [#16491](https://github.com/JabRef/jabref/issues/16491)
@@ -102,6 +106,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
102106
- Resolving of Math Subject Classification (MSC) needs to be activated in the references now. [#15883](https://github.com/JabRef/jabref/pull/15883)
103107
- We improved JabRef's startup performance by loading MSC codes only when activated. [#15883](https://github.com/JabRef/jabref/pull/15883)
104108
- Embedded postgres is no longer started with JabRef unless "Experimental search (Postgres)" is enabled in General preferences. [#12844](https://github.com/JabRef/jabref/issues/12844)
109+
- We changed the logging during full-text search indexing to identify which linked files cause errors. [#15680](https://github.com/JabRef/jabref/issues/15680)
105110

106111
### Fixed
107112

@@ -148,6 +153,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
148153
- We fixed an issue in the LibreOffice integration where citations generated via CSL styles were not properly formatted. [#16379](https://github.com/JabRef/jabref/issues/16379)
149154
- We fixed an issue in the LibreOffice integration where generating bibliography or inserting citations made superscript citations smaller and smaller. [#16351](https://github.com/JabRef/jabref/issues/16351)
150155
- We fixed an issue where opening preferences could break arXiv web search later. [#16059](https://github.com/JabRef/jabref/issues/16059)
156+
- We fixed arXiv title searches containing accented characters. [#16477](https://github.com/JabRef/jabref/issues/16477)
151157
- We fixed an issue where generating AI embeddings for an entry with a linked URL logged a misleading error about a missing file. [#16123](https://github.com/JabRef/jabref/issues/16123)
152158
- We fixed an issue with the LibreOffice integration where the ordering for numeric CSL styles in footnotes was broken. [#12484](https://github.com/JabRef/jabref/issues/12484)
153159
- We fixed an issue where `git push` did not report rejected remote updates. [#16367](https://github.com/JabRef/jabref/pull/16367)
@@ -193,6 +199,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
193199
- We fixed handling of `exit` in the LSP server. [#16268](https://github.com/JabRef/jabref/pull/16268)
194200
- We fixed an issue where `LinkedFile.isOnlineLink()` did not recognize `ftp://` links as online links. [#16400](https://github.com/JabRef/jabref/issues/16400)
195201
- We fixed an issue where deleting an entry removed the group filter and defaulted to the home view. [#16036](https://github.com/JabRef/jabref/issues/16036)
202+
- We fixed an issue where opening an online link with a query string could open a truncated URL. [#16774](https://github.com/JabRef/jabref/pull/16774)
196203

197204
### Removed
198205

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ In short, please keep these two principles in mind when you contribute:
179179
180180
More reading on that is available at <https://roe.dev/blog/using-ai-in-open-source>.
181181

182+
The [Don't paste the AI, please.](https://dontpastetheai.com/) website offers a practical perspective on using AI as an assistant rather than copying its output.
183+
182184
We reserve the right to reject pull requests that contain little or no genuine and original contribution from the contributor.
183185

184186
### After submission of a pull request

build-logic/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ dependencies {
2828
implementation("org.gradlex:java-module-testing:1.8.1")
2929
implementation("org.gradlex:jvm-dependency-conflict-resolution:2.5")
3030
implementation("org.gradle.toolchains:foojay-resolver:1.0.0")
31+
implementation("org.itsallcode:openfasttrace-gradle:3.2.0")
32+
implementation("org.itsallcode.openfasttrace:openfasttrace-api:4.9.0")
33+
implementation("org.itsallcode.openfasttrace:openfasttrace-core:4.9.0")
34+
implementation("org.itsallcode.openfasttrace:openfasttrace-exporter-specobject:4.9.0")
35+
implementation("org.itsallcode.openfasttrace:openfasttrace:4.9.0")
3136
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import java.io.File
2+
import java.util.Locale
3+
4+
import org.gradle.api.Project
5+
import org.gradle.api.plugins.ExtensionAware
6+
import org.gradle.kotlin.dsl.getByType
7+
import org.gradle.kotlin.dsl.register
8+
import org.itsallcode.openfasttrace.api.core.ItemStatus
9+
import org.itsallcode.openfasttrace.gradle.config.TagPathConfiguration
10+
import org.itsallcode.openfasttrace.gradle.config.TracingConfig
11+
import org.itsallcode.openfasttrace.gradle.task.CollectTask
12+
import org.itsallcode.openfasttrace.gradle.task.TraceTask
13+
import org.itsallcode.openfasttrace.gradle.task.config.SerializableTagPathConfig
14+
15+
val taskGroupName = "trace"
16+
val requirementConfigName = "oftRequirementConfig"
17+
18+
allprojects {
19+
val tracingConfig = extensions.create("requirementTracing", TracingConfig::class.java, project)
20+
(tracingConfig as ExtensionAware).extensions.create("tags", TagPathConfiguration::class.java, project)
21+
}
22+
23+
gradle.projectsEvaluated {
24+
val collectTask = tasks.register<CollectTask>("collectRequirements") {
25+
group = taskGroupName
26+
description = "Collect requirements and generate specobject file"
27+
inputDirectories.set(getAllInputDirectories(rootProject.allprojects))
28+
outputFile.set(rootProject.layout.buildDirectory.file("reports/requirements.xml"))
29+
pathConfig.set(getPathConfig(rootProject.allprojects))
30+
}
31+
32+
tasks.register<TraceTask>("traceRequirements") {
33+
group = taskGroupName
34+
description = "Trace requirements and generate tracing report"
35+
dependsOn(collectTask)
36+
37+
val tracingConfig = rootProject.getTracingConfig()
38+
failBuild.set(tracingConfig.failBuild)
39+
requirementsFile.set(collectTask.flatMap { task -> task.outputFile })
40+
41+
if (tracingConfig.reportFile.isPresent) {
42+
outputFile.set(tracingConfig.reportFile)
43+
} else {
44+
val extension = if (tracingConfig.reportFormat.get() == "html") "html" else "txt"
45+
outputFile.set(rootProject.layout.buildDirectory.file("reports/tracing.$extension"))
46+
}
47+
48+
reportVerbosity.set(tracingConfig.reportVerbosity)
49+
reportFormat.set(tracingConfig.reportFormat)
50+
importedRequirements.set(getImportedRequirements(rootProject.allprojects))
51+
filteredArtifactTypes.set(tracingConfig.filteredArtifactTypes)
52+
filteredTags.set(tracingConfig.filteredTags)
53+
filterAcceptsItemsWithoutTag.set(tracingConfig.filterAcceptsItemsWithoutTag)
54+
filterWantedStatuses.set(getWantedStatuses(tracingConfig))
55+
detailsSectionDisplay.set(tracingConfig.detailsSectionDisplay)
56+
}
57+
}
58+
59+
fun Project.getTracingConfig(): TracingConfig = extensions.getByType()
60+
61+
fun getAllInputDirectories(allProjects: Set<Project>): Set<File> = allProjects
62+
.flatMap { project -> project.getTracingConfig().inputDirectories.files }
63+
.toSet()
64+
65+
fun getImportedRequirements(allProjects: Set<Project>): Set<File> = allProjects
66+
.flatMap(::getImportedRequirements)
67+
.toSet()
68+
69+
fun getImportedRequirements(project: Project): Set<File> {
70+
val importedRequirements = project.getTracingConfig().importedRequirements.orNull.orEmpty()
71+
if (importedRequirements.isEmpty()) {
72+
return emptySet()
73+
}
74+
75+
val configuration = project.configurations.findByName(requirementConfigName)
76+
?: project.configurations.create(requirementConfigName)
77+
78+
importedRequirements.forEach { dependency ->
79+
project.dependencies.add(requirementConfigName, dependency)
80+
}
81+
82+
return configuration.files
83+
}
84+
85+
fun getPathConfig(allProjects: Set<Project>): List<SerializableTagPathConfig> = allProjects
86+
.mapNotNull(::getTagPathConfig)
87+
88+
fun getTagPathConfig(project: Project): SerializableTagPathConfig? {
89+
val tagPathConfig = project.getTracingConfig().tagPathConfig
90+
if (tagPathConfig.pathConfig.isEmpty()) {
91+
return null
92+
}
93+
return SerializableTagPathConfig(tagPathConfig)
94+
}
95+
96+
fun getWantedStatuses(tracingConfig: TracingConfig): Set<ItemStatus> =
97+
tracingConfig.filterWantedStatuses.getOrElse(emptySet()).map(::convertStatus).toSet()
98+
99+
fun convertStatus(value: String): ItemStatus = try {
100+
ItemStatus.valueOf(value.uppercase(Locale.ROOT))
101+
} catch (exception: IllegalArgumentException) {
102+
val validStatuses = ItemStatus.values().joinToString(", ") { status -> status.name }
103+
throw IllegalArgumentException(
104+
"Invalid status '$value'. Valid statuses are: $validStatuses",
105+
exception,
106+
)
107+
}

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
plugins {
22
id("org.jabref.gradle.base.repositories")
33
id("org.jabref.gradle.feature.compile") // for openrewrite
4+
id("org.jabref.gradle.feature.requirementtracing")
45
id("org.openrewrite.rewrite") version "7.39.0"
5-
id("org.itsallcode.openfasttrace") version "3.2.0"
66
id("org.cyclonedx.bom") version "3.4.1"
77
}
88

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
nav_order: 71
3+
parent: Decision Records
4+
---
5+
6+
# Keyword delimiter normalization is a cleanup, applied on load and as a save action
7+
8+
## Context and Problem Statement
9+
10+
JabRef's keyword separator was a global preference. Opening a library whose `keywords` fields used a different character (typically `;` instead of `,`) rewrote every keyword field on load, which broke the promise that JabRef does not change a `.bib` file unless the user edits something. The rewrite lived in the BibTeX importer, outside the cleanup framework, so users could neither see nor disable it.
11+
12+
The keyword separator is now stored per library. The remaining question is where delimiter normalization belongs: on load, on save, in the cleanup framework, or not at all.
13+
14+
## Decision Drivers
15+
16+
* An untouched entry must stay byte-identical on disk.
17+
* Groups, search, and the keyword editor split on the library's separator, so a library mixing delimiters misbehaves until it is consistent in memory.
18+
* Whatever rewrites field contents should be visible and configurable, the same way as every other cleanup.
19+
20+
## Considered Options
21+
22+
* Keep the rewrite in the importer, guarded so consistent fields are untouched
23+
* Normalize only as a save action
24+
* Normalize as a cleanup, applied on load and offered as a save action
25+
26+
## Decision Outcome
27+
28+
Chosen option: "Normalize as a cleanup, applied on load and offered as a save action".
29+
30+
`NormalizeKeywordDelimitersFormatter` is a regular field formatter on the `keywords` field. It returns a field unchanged when the field already uses the library's separator, so it never produces a diff for a consistent library. The importer applies the same formatter to every loaded entry, which keeps the in-memory model consistent; because untouched entries are not written back, this does not change the file. The formatter is also part of the default save actions and of the cleanup dialog, so users see it and can switch it off per library.
31+
32+
Formatters are stateless singletons looked up by key, so they cannot hold a library's separator. Formatters whose result depends on the separator implement `KeywordSeparatorAware`; the save and cleanup paths, which know the library, bind them to the library's separator before running them. An unbound formatter falls back to the global preference.
33+
34+
### Consequences
35+
36+
* Good, because the file on disk changes only for entries the user saved, and only in the way every other save action does.
37+
* Good, because the behavior is discoverable and can be disabled or run on demand.
38+
* Bad, because load is not strictly verbatim: entries with mixed delimiters are normalized in memory before the user touches them. This is accepted for user convenience; the alternative (save action only) reintroduces wrong group membership and search results for mixed libraries.
39+
40+
## Pros and Cons of the Options
41+
42+
### Keep the rewrite in the importer, guarded
43+
44+
* Good, because it is the smallest change.
45+
* Bad, because the rewrite stays invisible and cannot be disabled.
46+
47+
### Normalize only as a save action
48+
49+
* Good, because load is verbatim.
50+
* Bad, because mixed libraries stay inconsistent in memory until every entry has been saved once.
51+
52+
### Normalize as a cleanup, applied on load and offered as a save action
53+
54+
* Good, because one implementation serves load, save, and the cleanup dialog.
55+
* Bad, because the load step is a deliberate exception to "load is verbatim" and needs the no-change guard to be safe.

docs/requirements/import.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ parent: Requirements
66
## Normalize imported BibTeX keyword delimiters
77
`req~import.bibtex.keywords.normalize-delimiters~1`
88

9-
When importing BibTeX entries, JabRef interprets the configured accepted keyword delimiters and stores the resulting keywords using the configured keyword separator.
9+
When importing BibTeX entries, JabRef applies the "Normalize keyword delimiters" cleanup (see `req~save.keywords.normalize-delimiters~1`) to every imported entry, so groups, search, and the keyword editor split the field on the library's separator from the start.
10+
The library's separator is the one declared in the library's metadata; if none is declared, it is the accepted delimiter that the library's keyword fields already use most; if the keyword fields contain no delimiter, it is the globally configured keyword separator.
11+
Keyword fields that already use the library's separator are left untouched, so opening a library does not rewrite them.
1012

1113
Delimiter characters that are part of a keyword remain part of that keyword and are escaped when necessary.
1214

0 commit comments

Comments
 (0)