Commit 67b16f7e authored by Sean McGivern's avatar Sean McGivern

Only use backslash escapes in autocomplete when needed

Autocompletion for references happens on the frontend. Those references
are turned into actual references on the backend, but only after
Markdown processing has happened. That means that if a reference
contains a character that Markdown might consume, it won't render
correctly. So we need to do some escaping on the frontend.

We have these potential problem characters:
https://docs.gitlab.com/ee/user/markdown.html#emphasis

1. ~ - this is ~~strikethrough~~, but only when doubled.
2. _ - used for _emphasis_, doubled is __bold__.
3. * - also used for *emphasis*, doubled is **bold** also.
4. ` - used for `code spans`, any number works.

We don't need to escape `-` any more. When it comes to being inside a
word:

1. a~~b~~ has strikethrough, so it needs to be escaped everywhere.
2. a_b_ has no emphasis (see [a]) so it only needs to be escaped at the
   start and end of words.
3. a*b* has emphasis, so it needs to be escaped everywhere.
4. a`b` has a code span, so it needs to be escaped everywhere.

Or, in code terms:

1. Always escape ~~, *, and ` when being inserted by autocomplete.
2. Escape _ when it's either at the beginning or the end of a word.

[a]: https://docs.gitlab.com/ee/user/markdown.html#multiple-underscores-in-words
parent cfa5c307
...@@ -461,7 +461,10 @@ class GfmAutoComplete { ...@@ -461,7 +461,10 @@ class GfmAutoComplete {
// We can ignore this for quick actions because they are processed // We can ignore this for quick actions because they are processed
// before Markdown. // before Markdown.
if (!this.setting.skipMarkdownCharacterTest) { if (!this.setting.skipMarkdownCharacterTest) {
withoutAt = withoutAt.replace(/([~\-_*`])/g, '\\$&'); withoutAt = withoutAt
.replace(/(~~|`|\*)/g, '\\$1')
.replace(/(\b)(_+)/g, '$1\\$2') // only escape underscores at the start
.replace(/(_+)(\b)/g, '\\$1$2'); // or end of words
} }
return `${at}${withoutAt}`; return `${at}${withoutAt}`;
......
---
title: Only escape Markdown emphasis characters in autocomplete when necessary
merge_request: 27457
author:
type: changed
...@@ -94,7 +94,7 @@ describe('GfmAutoComplete', () => { ...@@ -94,7 +94,7 @@ describe('GfmAutoComplete', () => {
}); });
it('should quote if value contains any non-alphanumeric characters', () => { it('should quote if value contains any non-alphanumeric characters', () => {
expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"'); expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label-20"');
expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"'); expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
}); });
...@@ -102,12 +102,21 @@ describe('GfmAutoComplete', () => { ...@@ -102,12 +102,21 @@ describe('GfmAutoComplete', () => {
expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"'); expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
}); });
it('should escape Markdown emphasis characters, except in the first character', () => { it('escapes Markdown strikethroughs when needed', () => {
expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group'); expect(beforeInsert(atwhoInstance, '~a~bug')).toEqual('~"a~bug"');
expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug'); expect(beforeInsert(atwhoInstance, '~a~~bug~~')).toEqual('~"a\\~~bug\\~~"');
});
it('escapes Markdown emphasis when needed', () => {
expect(beforeInsert(atwhoInstance, '~a_bug_')).toEqual('~a_bug\\_');
expect(beforeInsert(atwhoInstance, '~a _bug_')).toEqual('~"a \\_bug\\_"');
expect(beforeInsert(atwhoInstance, '~a*bug*')).toEqual('~"a\\*bug\\*"');
expect(beforeInsert(atwhoInstance, '~a *bug*')).toEqual('~"a \\*bug\\*"');
});
it('escapes Markdown code spans when needed', () => {
expect(beforeInsert(atwhoInstance, '~a`bug`')).toEqual('~"a\\`bug\\`"');
expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"'); expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"');
expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"');
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment