Markdown: Open links to local files in text editor

Task-number: QTCREATORBUG-30119
Change-Id: I942ffcec7c84945c4246b31c23d8f7913642f649
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Mathias Hasselmann
2024-01-04 23:15:15 +01:00
parent 74d17600d8
commit 444da70fcf

View File

@@ -54,11 +54,23 @@ const char TOGGLEEDITOR_ACTION[] = "Markdown.ToggleEditor";
const char TOGGLEPREVIEW_ACTION[] = "Markdown.TogglePreview";
const char SWAPVIEWS_ACTION[] = "Markdown.SwapViews";
class MarkdownEditorWidget : public TextEditorWidget
{
Q_OBJECT
public:
using TextEditorWidget::TextEditorWidget;
void findLinkAt(const QTextCursor &cursor,
const Utils::LinkHandler &processLinkCallback,
bool resolveTarget = true,
bool inNextSplit = false) override;
};
class MarkdownEditor : public IEditor
{
Q_OBJECT
public:
MarkdownEditor()
MarkdownEditor(const TextEditor::TextEditorActionHandler *actionHandler)
: m_document(new TextDocument(MARKDOWNVIEWER_ID))
{
m_document->setMimeType(MARKDOWNVIEWER_MIME_TYPE);
@@ -92,7 +104,8 @@ public:
});
// editor
m_textEditorWidget = new TextEditorWidget;
m_textEditorWidget = new MarkdownEditorWidget;
m_textEditorWidget->setOptionalActions(actionHandler->optionalActions());
m_textEditorWidget->setTextDocument(m_document);
m_textEditorWidget->setupGenericHighlighter();
m_textEditorWidget->setMarksVisible(false);
@@ -485,7 +498,7 @@ private:
MarkdownEditorFactory::MarkdownEditorFactory()
: m_actionHandler(MARKDOWNVIEWER_ID,
MARKDOWNVIEWER_TEXT_CONTEXT,
TextEditor::TextEditorActionHandler::None,
TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor,
[](IEditor *editor) {
return static_cast<MarkdownEditor *>(editor)->textEditorWidget();
})
@@ -493,7 +506,7 @@ MarkdownEditorFactory::MarkdownEditorFactory()
setId(MARKDOWNVIEWER_ID);
setDisplayName(::Core::Tr::tr("Markdown Editor"));
addMimeType(MARKDOWNVIEWER_MIME_TYPE);
setEditorCreator([] { return new MarkdownEditor; });
setEditorCreator([this] { return new MarkdownEditor{&m_actionHandler}; });
const auto textContext = Context(MARKDOWNVIEWER_TEXT_CONTEXT);
const auto context = Context(MARKDOWNVIEWER_ID);
@@ -550,6 +563,54 @@ MarkdownEditorFactory::MarkdownEditorFactory()
});
}
void MarkdownEditorWidget::findLinkAt(const QTextCursor &cursor,
const LinkHandler &processLinkCallback,
bool /*resolveTarget*/,
bool /*inNextSplit*/)
{
static const QStringView CAPTURE_GROUP_LINK = u"link";
static const QRegularExpression markdownLink{R"(\[[^[\]]*\]\((?<link>.+?)\))"};
QTC_ASSERT(markdownLink.isValid(), return);
const int blockOffset = cursor.block().position();
const QString &currentBlock = cursor.block().text();
for (const QRegularExpressionMatch &match : markdownLink.globalMatch(currentBlock)) {
// Ignore matches outside of the current cursor position.
if (cursor.positionInBlock() < match.capturedStart())
break;
if (cursor.positionInBlock() >= match.capturedEnd())
continue;
if (const QStringView link = match.capturedView(CAPTURE_GROUP_LINK); !link.isEmpty()) {
// Process regular Markdown links of the form `[description](link)`.
const QUrl url = link.toString();
const auto linkedPath = [this, url] {
if (url.isRelative())
return textDocument()->filePath().parentDir().resolvePath(url.path());
else if (url.isLocalFile())
return FilePath::fromString(url.toLocalFile());
else
return FilePath{};
}();
if (!linkedPath.isFile())
continue;
Link result = linkedPath;
result.linkTextStart = match.capturedStart() + blockOffset;
result.linkTextEnd = match.capturedEnd() + blockOffset;
processLinkCallback(result);
break;
} else {
QTC_ASSERT_STRING("This line should not be reached unless 'markdownLink' is wrong");
return;
}
}
}
} // namespace TextEditor::Internal
#include "markdowneditor.moc"