Merge branch 'dm-paste-code-inside-gfm-code' into 'master'
Don't wrap pasted code when it's already inside code tags Closes #32507 See merge request !11524
This commit is contained in:
commit
b39b02d4a4
4 changed files with 81 additions and 9 deletions
|
@ -273,12 +273,12 @@ const gfmRules = {
|
|||
|
||||
class CopyAsGFM {
|
||||
constructor() {
|
||||
$(document).on('copy', '.md, .wiki', (e) => { this.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
|
||||
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { this.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
|
||||
$(document).on('paste', '.js-gfm-input', this.pasteGFM.bind(this));
|
||||
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
|
||||
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
|
||||
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
|
||||
}
|
||||
|
||||
copyAsGFM(e, transformer) {
|
||||
static copyAsGFM(e, transformer) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
|
@ -292,19 +292,36 @@ class CopyAsGFM {
|
|||
e.stopPropagation();
|
||||
|
||||
clipboardData.setData('text/plain', el.textContent);
|
||||
clipboardData.setData('text/x-gfm', CopyAsGFM.nodeToGFM(el));
|
||||
clipboardData.setData('text/x-gfm', this.nodeToGFM(el));
|
||||
}
|
||||
|
||||
pasteGFM(e) {
|
||||
static pasteGFM(e) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = clipboardData.getData('text/plain');
|
||||
const gfm = clipboardData.getData('text/x-gfm');
|
||||
if (!gfm) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
window.gl.utils.insertText(e.target, gfm);
|
||||
window.gl.utils.insertText(e.target, (textBefore, textAfter) => {
|
||||
// If the text before the cursor contains an odd number of backticks,
|
||||
// we are either inside an inline code span that starts with 1 backtick
|
||||
// or a code block that starts with 3 backticks.
|
||||
// This logic still holds when there are one or more _closed_ code spans
|
||||
// or blocks that will have 2 or 6 backticks.
|
||||
// This will break down when the actual code block contains an uneven
|
||||
// number of backticks, but this is a rare edge case.
|
||||
const backtickMatch = textBefore.match(/`/g);
|
||||
const insideCodeBlock = backtickMatch && (backtickMatch.length % 2) === 1;
|
||||
|
||||
if (insideCodeBlock) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return gfm;
|
||||
});
|
||||
}
|
||||
|
||||
static transformGFMSelection(documentFragment) {
|
||||
|
|
|
@ -198,10 +198,12 @@
|
|||
|
||||
const textBefore = value.substring(0, selectionStart);
|
||||
const textAfter = value.substring(selectionEnd, value.length);
|
||||
const newText = textBefore + text + textAfter;
|
||||
|
||||
const insertedText = text instanceof Function ? text(textBefore, textAfter) : text;
|
||||
const newText = textBefore + insertedText + textAfter;
|
||||
|
||||
target.value = newText;
|
||||
target.selectionStart = target.selectionEnd = selectionStart + text.length;
|
||||
target.selectionStart = target.selectionEnd = selectionStart + insertedText.length;
|
||||
|
||||
// Trigger autosave
|
||||
$(target).trigger('input');
|
||||
|
|
4
changelogs/unreleased/dm-paste-code-inside-gfm-code.yml
Normal file
4
changelogs/unreleased/dm-paste-code-inside-gfm-code.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Don't wrap pasted code when it's already inside code tags
|
||||
merge_request:
|
||||
author:
|
49
spec/javascripts/copy_as_gfm_spec.js
Normal file
49
spec/javascripts/copy_as_gfm_spec.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
require('~/copy_as_gfm');
|
||||
|
||||
(() => {
|
||||
describe('gl.CopyAsGFM', () => {
|
||||
describe('gl.CopyAsGFM.pasteGFM', () => {
|
||||
function callPasteGFM() {
|
||||
const e = {
|
||||
originalEvent: {
|
||||
clipboardData: {
|
||||
getData(mimeType) {
|
||||
// When GFM code is copied, we put the regular plain text
|
||||
// on the clipboard as `text/plain`, and the GFM as `text/x-gfm`.
|
||||
// This emulates the behavior of `getData` with that data.
|
||||
if (mimeType === 'text/plain') {
|
||||
return 'code';
|
||||
}
|
||||
if (mimeType === 'text/x-gfm') {
|
||||
return '`code`';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
preventDefault() {},
|
||||
};
|
||||
|
||||
window.gl.CopyAsGFM.pasteGFM(e);
|
||||
}
|
||||
|
||||
it('wraps pasted code when not already in code tags', () => {
|
||||
spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
|
||||
const insertedText = textFunc('This is code: ', '');
|
||||
expect(insertedText).toEqual('`code`');
|
||||
});
|
||||
|
||||
callPasteGFM();
|
||||
});
|
||||
|
||||
it('does not wrap pasted code when already in code tags', () => {
|
||||
spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
|
||||
const insertedText = textFunc('This is code: `', '`');
|
||||
expect(insertedText).toEqual('code');
|
||||
});
|
||||
|
||||
callPasteGFM();
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
Loading…
Reference in a new issue