Skip to content

Commit a44f68f

Browse files
committed
compose: Implement inlineLink, for Markdown "inline link" syntax
And use it in the one place where we've been creating inline links. This is a small amount of code, but drawing it out into a helper gives a convenient place to write down its shortcomings. We'll also use this for zulip#116 quote-and-reply, coming up.
1 parent cd46e8d commit a44f68f

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

lib/model/compose.dart

+19
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,22 @@ Uri narrowLink(PerAccountStore store, Narrow narrow, {int? nearMessageId}) {
180180
String mention(User user, {bool silent = false}) {
181181
return '@${silent ? '_' : ''}**${user.fullName}|${user.userId}**';
182182
}
183+
184+
/// https://spec.commonmark.org/0.30/#inline-link
185+
///
186+
/// The "link text" is made by enclosing [visibleText] in square brackets.
187+
/// If [visibleText] has unexpected features, such as square brackets, the
188+
/// result may be surprising.
189+
///
190+
/// The part between "(" and ")" is just a "link destination" (no "link title").
191+
/// That destination is simply the stringified [destination], if provided.
192+
/// If that has parentheses in it, the result may be surprising.
193+
// TODO (?) try harder to guarantee output that creates an inline link,
194+
// and in particular, the intended one. Tricky in part because that depends on
195+
// nearby content in a line. From the spec:
196+
// > Backtick code spans, autolinks, and raw HTML tags bind more tightly
197+
// > than the brackets in link text. Thus, for example, [foo`]` could not be
198+
// > a link text, since the second ] is part of a code span.
199+
String inlineLink(String visibleText, Uri? destination) {
200+
return '[$visibleText](${destination?.toString() ?? ''})';
201+
}

lib/widgets/compose_box.dart

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:image_picker/image_picker.dart';
66

77
import '../api/route/messages.dart';
88
import '../model/autocomplete.dart';
9+
import '../model/compose.dart';
910
import '../model/narrow.dart';
1011
import 'dialog.dart';
1112
import 'store.dart';
@@ -135,7 +136,7 @@ class ComposeContentController extends ComposeController<ContentValidationError>
135136
int registerUploadStart(String filename) {
136137
final tag = _nextUploadTag;
137138
_nextUploadTag += 1;
138-
final placeholder = '[Uploading $filename...]()'; // TODO(i18n)
139+
final placeholder = inlineLink('Uploading $filename...', null); // TODO(i18n)
139140
_uploads[tag] = (filename: filename, placeholder: placeholder);
140141
notifyListeners(); // _uploads change could affect validationErrors
141142
value = value.replaced(_insertionIndex(), '$placeholder\n\n');
@@ -158,8 +159,8 @@ class ComposeContentController extends ComposeController<ContentValidationError>
158159
value = value.replaced(
159160
replacementRange,
160161
url == null
161-
? '[Failed to upload file: $filename]()' // TODO(i18n)
162-
: '[$filename](${url.toString()})');
162+
? inlineLink('Failed to upload file: $filename', null) // TODO(i18n)
163+
: inlineLink(filename, url));
163164
_uploads.remove(tag);
164165
notifyListeners(); // _uploads change could affect validationErrors
165166
}

test/model/compose_test.dart

+5
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,9 @@ hello
306306
check(mention(user, silent: false)).equals('@**Full Name|123**');
307307
check(mention(user, silent: true)).equals('@_**Full Name|123**');
308308
});
309+
310+
group('inlineLink', () {
311+
check(inlineLink('CZO', Uri.parse('https://chat.zulip.org/'))).equals('[CZO](https://chat.zulip.org/)');
312+
check(inlineLink('Uploading file.txt…', null)).equals('[Uploading file.txt…]()');
313+
});
309314
}

0 commit comments

Comments
 (0)