diff --git a/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java b/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java index f21144c1d0a..48327360409 100644 --- a/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -19,6 +19,7 @@ public enum StandardActions implements Action { COPY_KEY_AND_LINK(Localization.lang("Copy citation key and link"), KeyBinding.COPY_CITATION_KEY_AND_LINK), COPY_CITATION_HTML(Localization.lang("Copy citation (html)"), KeyBinding.COPY_PREVIEW), COPY_CITATION_TEXT(Localization.lang("Copy citation (text)")), + COPY_CITATION_MARKDOWN(Localization.lang("Copy citation (markdown)")), COPY_CITATION_PREVIEW(Localization.lang("Copy preview"), KeyBinding.COPY_PREVIEW), EXPORT_TO_CLIPBOARD(Localization.lang("Export to clipboard"), IconTheme.JabRefIcons.EXPORT_TO_CLIPBOARD), EXPORT_SELECTED_TO_CLIPBOARD(Localization.lang("Export selected entries to clipboard"), IconTheme.JabRefIcons.EXPORT_TO_CLIPBOARD), diff --git a/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index ba4d8ab2119..091dde2d992 100644 --- a/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/jabgui/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -192,7 +192,8 @@ private static Menu createCopySubMenu(ActionFactory factory, if (previewPreferences.getSelectedPreviewLayout() instanceof CitationStylePreviewLayout) { copySpecialMenu.getItems().addAll( factory.createMenuItem(StandardActions.COPY_CITATION_HTML, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, clipBoardManager, taskExecutor, preferences, abbreviationRepository)), - factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new CopyCitationAction(CitationStyleOutputFormat.TEXT, dialogService, stateManager, clipBoardManager, taskExecutor, preferences, abbreviationRepository))); + factory.createMenuItem(StandardActions.COPY_CITATION_TEXT, new CopyCitationAction(CitationStyleOutputFormat.TEXT, dialogService, stateManager, clipBoardManager, taskExecutor, preferences, abbreviationRepository)), + factory.createMenuItem(StandardActions.COPY_CITATION_MARKDOWN, new CopyCitationAction(CitationStyleOutputFormat.MARKDOWN, dialogService, stateManager, clipBoardManager, taskExecutor, preferences, abbreviationRepository))); } else { copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, clipBoardManager, taskExecutor, preferences, abbreviationRepository))); } diff --git a/jabgui/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java b/jabgui/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java index f49382d61a4..ce5a61f5def 100644 --- a/jabgui/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java +++ b/jabgui/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java @@ -23,6 +23,7 @@ import com.airhacks.afterburner.injection.Injector; import com.google.common.annotations.VisibleForTesting; +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; public class ClipboardContentGenerator { @@ -45,6 +46,7 @@ public ClipboardContent generate(List selectedEntries, CitationStyleOu return switch (outputFormat) { case HTML -> processHtml(citations); case TEXT -> processText(citations); + case MARKDOWN -> processMarkdown(citations); }; } else { // if it is not a citation style take care of the preview @@ -120,6 +122,32 @@ static ClipboardContent processHtml(List citations) { return content; } + /** + * Insert each citation into HTML. + * convert HTML to markdown using flexmark. + */ + @VisibleForTesting + static ClipboardContent processMarkdown(List citations) { + String result = "" + OS.NEWLINE + + "" + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + OS.NEWLINE; + + result += String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations); + result += OS.NEWLINE + + " " + OS.NEWLINE + + "" + OS.NEWLINE; + + FlexmarkHtmlConverter converter = FlexmarkHtmlConverter.builder().build(); + String markdown = converter.convert(result); + + ClipboardContent content = new ClipboardContent(); + content.putString(markdown); + return content; + } + private List generateTextBasedPreviewLayoutCitations(List selectedEntries, BibDatabaseContext bibDatabaseContext) throws IOException { TextBasedPreviewLayout customPreviewLayout = previewPreferences.getCustomPreviewLayout(); Reader customLayoutReader = Reader.of(customPreviewLayout.getText().replace("__NEWLINE__", "\n")); diff --git a/jabgui/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java b/jabgui/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java index 307ea7a930e..5062c99f663 100644 --- a/jabgui/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java +++ b/jabgui/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java @@ -138,4 +138,32 @@ void processHtmlAsHtml() { Object actual = htmlTransferable.getHtml(); assertEquals(expected, actual); } + + @Test + void processMarkdownAsMarkdown() { + String expected = "\\[1\\] \n" + + "B. Smith, B. Jones, and J. Williams, \"Title of the test entry,\" *BibTeX Journal*, vol. 34, no. 3, pp. 45--67, Jul. 2016.\n" + + "\n" + + "\\[1\\] \n" + + "B. Smith, B. Jones, and J. Williams, \"Title of the test entry,\" *BibTeX Journal*, vol. 34, no. 3, pp. 45--67, Jul. 2016.\n"; + + String citation = "
" + OS.NEWLINE + + "
[1]
B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016.
" + OS.NEWLINE + + "
" + OS.NEWLINE; + + ClipboardContent markdown = ClipboardContentGenerator.processMarkdown(Arrays.asList(citation, citation)); + String actual = markdown.getString(); + + // Print actual output for debugging + System.out.println("=== Actual Markdown Output ==="); + System.out.println(actual); + System.out.println("=== End ==="); + + // Normalize both strings to ignore whitespace and formatting issues + String normalizedActual = actual.replaceAll("\\s+", " ").trim(); + String normalizedExpected = expected.replaceAll("\\s+", " ").trim(); + + assertEquals(normalizedExpected, normalizedActual); + } } + diff --git a/jablib/src/main/java/org/jabref/logic/citationstyle/CitationStyleOutputFormat.java b/jablib/src/main/java/org/jabref/logic/citationstyle/CitationStyleOutputFormat.java index 2c11793df42..3a2edf12063 100644 --- a/jablib/src/main/java/org/jabref/logic/citationstyle/CitationStyleOutputFormat.java +++ b/jablib/src/main/java/org/jabref/logic/citationstyle/CitationStyleOutputFormat.java @@ -5,7 +5,8 @@ public enum CitationStyleOutputFormat { HTML("html", OS.NEWLINE + "
" + OS.NEWLINE), - TEXT("text", ""); + TEXT("text", ""), + MARKDOWN("markdown", ""); private final String format; private final String lineSeparator; diff --git a/jablib/src/main/resources/l10n/JabRef_en.properties b/jablib/src/main/resources/l10n/JabRef_en.properties index 5b983088790..a2fa48ab14e 100644 --- a/jablib/src/main/resources/l10n/JabRef_en.properties +++ b/jablib/src/main/resources/l10n/JabRef_en.properties @@ -171,6 +171,8 @@ Copy=Copy Copy\ title=Copy title Copy\ citation\ (html)=Copy citation (html) Copy\ citation\ (text)=Copy citation (text) +Copy\ citation\ (markdown)=Copy citation (markdown) + Copy\ citation\ key=Copy citation key Copy\ citation\ key\ and\ link=Copy citation key and link Copy\ citation\ key\ and\ title=Copy citation key and title