Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom link regexes via TextEditorOptions. #399

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;

using NUnit.Framework;

namespace ICSharpCode.AvalonEdit.Tests.Rendering
{
[TestFixture]
[Apartment(System.Threading.ApartmentState.STA)]
public class LinkElementGeneratorTests
{
[Test]
public void ParsingOfCustomLinkRegexes()
{
var linkElementGenerator = new LinkElementGenerator();
const string link = "example://mylink";
const string input = "Visit " + link + " please";
var textSource = GetTextSource(input);
linkElementGenerator.StartGeneration(textSource);

var interestedOffsetBeforeCustomRegex = linkElementGenerator.GetFirstInterestedOffset(0);
Assert.That(interestedOffsetBeforeCustomRegex, Is.EqualTo(-1));

var optionsWithCustomLink = new TextEditorOptions {
// Check for example:// protocol instead of http:// protocol.
LinkRegex = @"\bexample://\w+\b",
};
((IBuiltinElementGenerator)linkElementGenerator).FetchOptions(optionsWithCustomLink);

var interestedOffsetAfterCustomRegex = linkElementGenerator.GetFirstInterestedOffset(0);
var expectedOffset = input.IndexOf(link);
Assert.That(interestedOffsetAfterCustomRegex, Is.EqualTo(expectedOffset));

linkElementGenerator.FinishGeneration();
}

[Test]
public void ParsingOfCustomMailRegexes()
{
var linkElementGenerator = new MailLinkElementGenerator();
const string link = "[email protected]";
const string input = "Email " + link + " please";
var textSource = GetTextSource(input);
linkElementGenerator.StartGeneration(textSource);

var interestedOffsetBeforeCustomRegex = linkElementGenerator.GetFirstInterestedOffset(0);
Assert.That(interestedOffsetBeforeCustomRegex, Is.EqualTo(-1));

var optionsWithCustomLink = new TextEditorOptions {
// The same as the default except that TLDs can be up to 12 chars now.
MailRegex = @"\b[\w\d\.\-]+\@[\w\d\.\-]+\.[a-z]{2,12}\b",
};
((IBuiltinElementGenerator)linkElementGenerator).FetchOptions(optionsWithCustomLink);

var interestedOffsetAfterCustomRegex = linkElementGenerator.GetFirstInterestedOffset(0);
var expectedOffset = input.IndexOf(link);
Assert.That(interestedOffsetAfterCustomRegex, Is.EqualTo(expectedOffset));

linkElementGenerator.FinishGeneration();
}

private static VisualLineTextSource GetTextSource(string input)
{
var textDocument = new TextDocument(input);
var textView = new TextView {
Document = textDocument,
};
var documentLine = textDocument.Lines[0];
var visualLine = textView.GetOrConstructVisualLine(documentLine);
var textSource = new VisualLineTextSource(visualLine) {
Document = textDocument,
TextView = textView,
};
return textSource;
}
}
}
14 changes: 12 additions & 2 deletions ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ public class LinkElementGenerator : VisualLineElementGenerator, IBuiltinElementG
// try to detect email addresses
internal readonly static Regex defaultMailRegex = new Regex(@"\b[\w\d\.\-]+\@[\w\d\.\-]+\.[a-z]{2,6}\b");

readonly Regex linkRegex;
/// <summary>
/// The regex used to parse the type of link this class represents.
/// </summary>
protected Regex linkRegex;

/// <summary>
/// Gets/Sets whether the user needs to press Control to click the link.
Expand Down Expand Up @@ -71,6 +74,7 @@ protected LinkElementGenerator(Regex regex) : this()
void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
{
this.RequireControlModifierForClick = options.RequireControlModifierForHyperlinkClick;
this.linkRegex = new Regex(options.LinkRegex);
}

Match GetMatch(int startOffset, out int matchOffset)
Expand Down Expand Up @@ -142,7 +146,7 @@ protected virtual Uri GetUriFromMatch(Match match)
/// This element generator can be easily enabled and configured using the
/// <see cref="TextEditorOptions"/>.
/// </remarks>
sealed class MailLinkElementGenerator : LinkElementGenerator
sealed class MailLinkElementGenerator : LinkElementGenerator, IBuiltinElementGenerator
{
/// <summary>
/// Creates a new MailLinkElementGenerator.
Expand All @@ -152,6 +156,12 @@ public MailLinkElementGenerator()
{
}

void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
{
this.RequireControlModifierForClick = options.RequireControlModifierForHyperlinkClick;
this.linkRegex = new Regex(options.MailRegex);
}

protected override Uri GetUriFromMatch(Match match)
{
var targetUrl = "mailto:" + match.Value;
Expand Down
31 changes: 31 additions & 0 deletions ICSharpCode.AvalonEdit/TextEditorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;

using ICSharpCode.AvalonEdit.Rendering;

namespace ICSharpCode.AvalonEdit
{
Expand Down Expand Up @@ -166,6 +169,20 @@ public virtual bool EnableHyperlinks {
}
}

string linkRegex = LinkElementGenerator.defaultLinkRegex.ToString();
/// <summary>
/// Gets/Sets the regex used to parse hyperlinks.
/// </summary>
public virtual string LinkRegex {
get { return linkRegex; }
set {
if (!Equals(linkRegex, value)) {
linkRegex = value;
OnPropertyChanged(nameof(LinkRegex));
}
}
}

bool enableEmailHyperlinks = true;

/// <summary>
Expand All @@ -183,6 +200,20 @@ public virtual bool EnableEmailHyperlinks {
}
}

string mailRegex = LinkElementGenerator.defaultMailRegex.ToString();
/// <summary>
/// Gets/Sets the regex used to parse mail hyperlinks.
/// </summary>
public virtual string MailRegex {
get { return mailRegex; }
set {
if (!Equals(mailRegex, value)) {
mailRegex = value;
OnPropertyChanged(nameof(MailRegex));
}
}
}

bool requireControlModifierForHyperlinkClick = true;

/// <summary>
Expand Down