Skip to content

Commit

Permalink
Pasting a URL from the clipboard wraps the selected text as a link (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ahus1 committed Dec 8, 2024
1 parent 342e72a commit 8690860
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This document provides a high-level view of the changes introduced by release.
- Adding an intent to add the option to disable validation on individual code blocks (#830)
- Be more restrictive when highlighting missing images (#1727)
- Fix the intention to add block IDs to recognize the current section (#1681)
- Pasting a URL from the clipboard wraps the selected text as a link (#1738)

=== 0.43.3

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package org.asciidoc.intellij.pasteProvider;

import com.intellij.ide.PasteProvider;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.actions.PasteAction;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import org.asciidoc.intellij.actions.asciidoc.DocumentWriteAction;
import org.asciidoc.intellij.file.AsciiDocFileType;
import org.asciidoc.intellij.psi.AsciiDocUtil;
import org.jetbrains.annotations.NotNull;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Objects;

public class AsciiDocPasteLinkProvider implements PasteProvider {
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}

@Override
public void performPaste(@NotNull DataContext dataContext) {
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
return;
}
DocumentWriteAction.run(editor.getProject(), () -> {
Document document = editor.getDocument();
Caret currentCaret = editor.getCaretModel().getCurrentCaret();
if (currentCaret.getSelectionRange().getLength() == 0) {
return;
}
Transferable produce = Objects.requireNonNull(dataContext.getData(PasteAction.TRANSFERABLE_PROVIDER)).produce();
if (produce.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
String url = produce.getTransferData(DataFlavor.stringFlavor).toString();
SelectionModel selectionModel = editor.getSelectionModel();
if (AsciiDocUtil.URL_PREFIX_PATTERN.matcher(url).find() || AsciiDocUtil.EMAIL_PATTERN.matcher(url).matches()) {
int start = selectionModel.getSelectionStart();
int end = selectionModel.getSelectionEnd();
String oldText = document.getText(TextRange.create(start, end));
String newText = oldText;
if (oldText.contains("]")) {
newText = newText.replaceAll("\\\\]", "\\\\\\\\]");
newText = newText.replaceAll("]", "\\\\]");
}
String prefix = url + "[";
if (url.contains("[")) {
prefix = "link:++" + url + "++[";
} else if (AsciiDocUtil.EMAIL_PATTERN.matcher(url).matches()) {
prefix = "link:mailto:" + url + "[";
}
if (!oldText.equals(newText)) {
document.replaceString(start, end, newText);
}
document.insertString(start, prefix);
document.insertString(start + prefix.length() + newText.length(), "]");
// If the cursor was at the beginning of the selected text, it is now at the beginning of the URL.
// Move it to the beginning of the text again.
if (editor.getCaretModel().getCurrentCaret().getOffset() < start + prefix.length()) {
editor.getCaretModel().getCurrentCaret().moveToOffset(start + prefix.length());
}
}
} catch (IOException | UnsupportedFlavorException ignored) {
// ignored
}
}
}, "Paste link");
}

@Override
public boolean isPasteEnabled(@NotNull DataContext dataContext) {
if (!isPastePossible(dataContext)) {
return false;
}
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
return false;
}
Caret currentCaret = editor.getCaretModel().getCurrentCaret();
if (currentCaret.getSelectionRange().getLength() == 0) {
return false;
}
Transferable produce = Objects.requireNonNull(dataContext.getData(PasteAction.TRANSFERABLE_PROVIDER)).produce();
if (produce.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
String data = produce.getTransferData(DataFlavor.stringFlavor).toString();
if (AsciiDocUtil.URL_PREFIX_PATTERN.matcher(data).find() || AsciiDocUtil.EMAIL_PATTERN.matcher(data).matches()) {
return true;
}
} catch (IOException | UnsupportedFlavorException e) {
return false;
}
}
return false;
}

@Override
public boolean isPastePossible(@NotNull DataContext dataContext) {
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
return false;
}
final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
if (file == null) {
return false;
}
if (file.getFileType() != AsciiDocFileType.INSTANCE) {
return false;
}
return true;
}
}
2 changes: 2 additions & 0 deletions src/main/java/org/asciidoc/intellij/psi/AsciiDocUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,8 @@ public static VirtualFile findAntoraModuleDir(PsiElement element) {

public static final Pattern URL_PREFIX_PATTERN = Pattern.compile("^((https?|file|ftp|irc)://|mailto:)");

public static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$");

public static final Pattern URL_PREFIX_PATTERN_WITHOUT_FILE = Pattern.compile("^((https?|ftp|irc)://|mailto:)");

// can include attributes
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
<typedHandler implementation="org.asciidoc.intellij.actions.AsciiDocTriggerAutoCompleteTypedHandler" order="after asciidocformattingquoted"/>
<webBrowserUrlProvider implementation="org.asciidoc.intellij.web.AsciiDocBrowserUrlProvider"/>
<customPasteProvider implementation="org.asciidoc.intellij.pasteProvider.AsciiDocPasteImageProvider" order="before asciidoc-paste-html-provider"/>
<customPasteProvider implementation="org.asciidoc.intellij.pasteProvider.AsciiDocPasteLinkProvider" order="before asciidoc-paste-html-provider"/>
<customPasteProvider implementation="org.asciidoc.intellij.pasteProvider.AsciiDocPasteHtmlProvider" id="asciidoc-paste-html-provider" />
<filePasteProvider implementation="org.asciidoc.intellij.pasteProvider.AsciiDocPasteImageProvider" />
<fileType name="AsciiDoc" extensions="adoc;asciidoc;ad" fileNames=".asciidoctorconfig" language="AsciiDoc"
Expand Down

0 comments on commit 8690860

Please sign in to comment.