diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp
index a027427ef65..ca8088d3264 100644
--- a/src/plugins/texteditor/markdowneditor.cpp
+++ b/src/plugins/texteditor/markdowneditor.cpp
@@ -568,8 +568,13 @@ void MarkdownEditorWidget::findLinkAt(const QTextCursor &cursor,
bool /*resolveTarget*/,
bool /*inNextSplit*/)
{
- static const QStringView CAPTURE_GROUP_LINK = u"link";
- static const QRegularExpression markdownLink{R"(\[[^[\]]*\]\((?.+?)\))"};
+ static const QStringView CAPTURE_GROUP_LINK = u"link";
+ static const QStringView CAPTURE_GROUP_ANCHOR = u"anchor";
+
+ static const QRegularExpression markdownLink{
+ R"(\[[^[\]]*\]\((?.+?)\))"
+ R"(|(?\[\^[^\]]+\])(?:[^:]|$))"
+ };
QTC_ASSERT(markdownLink.isValid(), return);
@@ -604,6 +609,25 @@ void MarkdownEditorWidget::findLinkAt(const QTextCursor &cursor,
result.linkTextEnd = match.capturedEnd() + blockOffset;
processLinkCallback(result);
break;
+ } else if (const QStringView anchor = match.capturedView(CAPTURE_GROUP_ANCHOR);
+ !anchor.isEmpty()) {
+ // Process local anchor links of the form `[^footnote]` that point
+ // to anchors in the current document: `[^footnote]: Description`.
+
+ const QTextCursor target = cursor.document()->find(anchor + u':');
+
+ if (target.isNull())
+ continue;
+
+ int line = 0;
+ int column = 0;
+
+ convertPosition(target.position(), &line, &column);
+ Link result{textDocument()->filePath(), line, column};
+ result.linkTextStart = match.capturedStart(CAPTURE_GROUP_ANCHOR) + blockOffset;
+ result.linkTextEnd = match.capturedEnd(CAPTURE_GROUP_ANCHOR) + blockOffset;
+ processLinkCallback(result);
+ break;
} else {
QTC_ASSERT_STRING("This line should not be reached unless 'markdownLink' is wrong");
return;