From d2d820cb318fbf2c1cf10c2083ca827d3d027ea1 Mon Sep 17 00:00:00 2001 From: Paul Vernin <78180340+Paul-vrn@users.noreply.github.com> Date: Tue, 17 Oct 2023 22:25:09 +0200 Subject: [PATCH] [MM-54474] Fixed behaviour of markdown code button (#24816) * update webapp/../apply_markdown.ts to support codeblock and inline block markdown correctly & added corresponding unit tests to apply_markdown.test.tsx * refactor apply_markdown.ts * refactor applyMarkdownToSelection to support different starting/ending delimiter && delete applyCodeBlockToSelection * updated apply_markdown.test.tsx --------- Co-authored-by: Mattermost Build --- .../utils/markdown/apply_markdown.test.tsx | 96 +++++++++++++++++++ .../src/utils/markdown/apply_markdown.ts | 36 +++++-- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/webapp/channels/src/utils/markdown/apply_markdown.test.tsx b/webapp/channels/src/utils/markdown/apply_markdown.test.tsx index 33f33abbaa..3a63da1aa7 100644 --- a/webapp/channels/src/utils/markdown/apply_markdown.test.tsx +++ b/webapp/channels/src/utils/markdown/apply_markdown.test.tsx @@ -441,4 +441,100 @@ describe('applyMarkdown', () => { selectionEnd: 14, }); }); + + /* *************** + * CODE (inline code and code block) + * ************** */ + test('should apply inline code markdown to the selection', () => { + // "Fafda" is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: 'Jalebi Fafda & Sambharo', + selectionStart: 7, + selectionEnd: 12, + markdownMode: 'code', + }); + + expect(result).toEqual({ + message: 'Jalebi `Fafda` & Sambharo', + selectionStart: 8, + selectionEnd: 13, + }); + }); + + test('should remove inline code markdown to the selection', () => { + // "Fafda" is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: 'Jalebi `Fafda` & Sambharo', + selectionStart: 8, + selectionEnd: 13, + markdownMode: 'code', + }); + + expect(result).toEqual({ + message: 'Jalebi Fafda & Sambharo', + selectionStart: 7, + selectionEnd: 12, + }); + }); + + test('should apply code block markdown to the full message (multi-lines)', () => { + // all message is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: 'Jalebi\nFafda\nSambharo', + selectionStart: 0, + selectionEnd: 21, + markdownMode: 'code', + }); + expect(result).toEqual({ + message: '```\nJalebi\nFafda\nSambharo\n```', + selectionStart: 4, + selectionEnd: 25, + }); + }); + + test('should remove code block markdown to the full message', () => { + // all message is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: '```\nJalebi\nFafda\nSambharo\n```', + selectionStart: 4, + selectionEnd: 25, + markdownMode: 'code', + }); + + expect(result).toEqual({ + message: 'Jalebi\nFafda\nSambharo', + selectionStart: 0, + selectionEnd: 21, + }); + }); + + test('should apply code block markdown to the sub-message (multi-lines)', () => { + // "bi\nFaf" is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: 'Jalebi\nFafda\nSambharo', + selectionStart: 4, + selectionEnd: 10, + markdownMode: 'code', + }); + expect(result).toEqual({ + message: 'Jale```\nbi\nFaf\n```da\nSambharo', + selectionStart: 8, + selectionEnd: 14, + }); + }); + + test('should remove code block markdown to the sub-message', () => { + // "bi\nFaf" is selected with ctrl + alt + C hotkey + const result = applyMarkdown({ + message: 'Jale```\nbi\nFaf\n```da\nSambharo', + selectionStart: 8, + selectionEnd: 14, + markdownMode: 'code', + }); + expect(result).toEqual({ + message: 'Jalebi\nFafda\nSambharo', + selectionStart: 4, + selectionEnd: 10, + }); + }); }); diff --git a/webapp/channels/src/utils/markdown/apply_markdown.ts b/webapp/channels/src/utils/markdown/apply_markdown.ts index 09c89465e8..fa09056dd0 100644 --- a/webapp/channels/src/utils/markdown/apply_markdown.ts +++ b/webapp/channels/src/utils/markdown/apply_markdown.ts @@ -18,6 +18,8 @@ type ApplyMarkdownReturnValue = { type ApplySpecificMarkdownOptions = ApplyMarkdownReturnValue & { delimiter?: string; + delimiterStart?: string; + delimiterEnd?: string; } export type ApplyLinkMarkdownOptions = ApplySpecificMarkdownOptions & { @@ -73,8 +75,7 @@ export function applyMarkdown(options: ApplyMarkdownOptions): ApplyMarkdownRetur delimiter = '~~'; return applyMarkdownToSelection({selectionEnd, selectionStart, message, delimiter}); case 'code': - delimiter = '```'; - return applyMarkdownToSelection({selectionEnd, selectionStart, message, delimiter}); + return applyCodeMarkdown({selectionEnd, selectionStart, message}); } throw Error('Unsupported markdown mode: ' + markdownMode); @@ -271,8 +272,12 @@ const applyMarkdownToSelection = ({ selectionStart, message, delimiter, + delimiterStart, + delimiterEnd, }: ApplySpecificMarkdownOptions) => { - if (!delimiter) { + const openingDelimiter = delimiterStart ?? delimiter; + const closingDelimiter = delimiterEnd ?? delimiter; + if (!openingDelimiter || !closingDelimiter) { /** * in case no delimiter is set return the values without changing anything */ @@ -293,7 +298,7 @@ const applyMarkdownToSelection = ({ let suffix = message.slice(selectionEnd); // Does the selection have current hotkey's markdown? - const hasCurrentMarkdown = prefix.endsWith(delimiter) && suffix.startsWith(delimiter); + const hasCurrentMarkdown = prefix.endsWith(openingDelimiter) && suffix.startsWith(closingDelimiter); let newValue: string; let newStart = selectionStart; @@ -313,14 +318,14 @@ const applyMarkdownToSelection = ({ if (hasCurrentMarkdown) { // selection already has the markdown, so we remove it here - newValue = prefix.slice(0, prefix.length - delimiter.length) + selection + suffix.slice(delimiter.length); - newStart -= delimiter.length; - newEnd -= delimiter.length; + newValue = prefix.slice(0, prefix.length - openingDelimiter.length) + selection + suffix.slice(closingDelimiter.length); + newStart -= openingDelimiter.length; + newEnd -= closingDelimiter.length; } else { // add markdown to the selection - newValue = prefix + delimiter + selection + delimiter + suffix; - newStart += delimiter.length; - newEnd += delimiter.length; + newValue = prefix + openingDelimiter + selection + closingDelimiter + suffix; + newStart += openingDelimiter.length; + newEnd += closingDelimiter.length; } return { @@ -492,6 +497,13 @@ export function applyLinkMarkdown({selectionEnd, selectionStart, message, url = }; } +function applyCodeMarkdown({selectionEnd, selectionStart, message}: ApplySpecificMarkdownOptions) { + if (isSelectionMultiline(message, selectionStart, selectionEnd)) { + return applyMarkdownToSelection({selectionEnd, selectionStart, message, delimiterStart: '```\n', delimiterEnd: '\n```'}); + } + return applyMarkdownToSelection({selectionEnd, selectionStart, message, delimiter: '`'}); +} + function findWordEnd(text: string, start: number) { const wordEnd = text.indexOf(' ', start); return wordEnd === -1 ? text.length : wordEnd; @@ -501,3 +513,7 @@ function findWordStart(text: string, start: number) { const wordStart = text.lastIndexOf(' ', start - 1) + 1; return wordStart === -1 ? 0 : wordStart; } + +function isSelectionMultiline(message: string, selectionStart: number, selectionEnd: number) { + return message.slice(selectionStart, selectionEnd).includes('\n'); +}