From 805ec389f00d0c78c16a2c0ff9bb6941d4448311 Mon Sep 17 00:00:00 2001 From: Tomasz Kajtoch Date: Mon, 22 May 2023 22:02:03 +0200 Subject: [PATCH] fix(EuiCopy): fix code block copy button not including the last character if it's a question mark --- src/components/code/code_block_copy.tsx | 8 +++++++- src/components/code/utils.tsx | 20 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/components/code/code_block_copy.tsx b/src/components/code/code_block_copy.tsx index 3a341fafe083..3469810cc70f 100644 --- a/src/components/code/code_block_copy.tsx +++ b/src/components/code/code_block_copy.tsx @@ -11,6 +11,7 @@ import { useInnerText } from '../inner_text'; import { EuiCopy } from '../copy'; import { useEuiI18n } from '../i18n'; import { EuiButtonIcon } from '../button'; +import { NEW_LINE_REGEX_GLOBAL } from './utils'; /** * Hook that returns copy-related state/logic/utils @@ -26,7 +27,12 @@ export const useCopy = ({ }) => { const [innerTextRef, _innerText] = useInnerText(''); const innerText = useMemo( - () => _innerText?.replace(/[\r\n?]{2}|\n\n/g, '\n') || '', + () => + _innerText + // Normalize line terminations to match native JS format + ?.replace(NEW_LINE_REGEX_GLOBAL, '\n') + // Reduce two or more consecutive new line characters to a single one + .replace(/\n{2,}/g, '\n') || '', [_innerText] ); const textToCopy = isVirtualized ? `${children}` : innerText; // Virtualized code blocks do not have inner text diff --git a/src/components/code/utils.tsx b/src/components/code/utils.tsx index 905774eec35b..6daa6156816a 100644 --- a/src/components/code/utils.tsx +++ b/src/components/code/utils.tsx @@ -41,6 +41,22 @@ export type EuiCodeSharedProps = CommonProps & export const SUPPORTED_LANGUAGES = listLanguages(); export const DEFAULT_LANGUAGE = 'text'; +/** + * Platform-agnostic new line regex that safely matches all standard + * line termination conventions: + * - LF: Unix-based platforms and JS-native sources like text areas + * - CRLF: Windows + * - CR: Mac Classic; to support files saved a long time ago + */ +export const NEW_LINE_REGEX = /\r\n|\r|\n/; + +/** + * Platform-agnostic global new line regex that safely matches all standard + * line termination conventions. + * See [NEW_LINE_REGEX]{@link NEW_LINE_REGEX} for more details. + */ +export const NEW_LINE_REGEX_GLOBAL = new RegExp(NEW_LINE_REGEX, 'g'); + export const checkSupportedLanguage = (language: string): string => { return SUPPORTED_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE; }; @@ -139,12 +155,12 @@ const addLineData = ( return nodes.reduce((result, node) => { const lineStart = data.lineNumber; if (node.type === 'text') { - if (!node.value.match(/\r\n?|\n/)) { + if (!node.value.match(NEW_LINE_REGEX)) { node.lineStart = lineStart; node.lineEnd = lineStart; result.push(node); } else { - const lines = node.value.split(/\r\n?|\n/); + const lines = node.value.split(NEW_LINE_REGEX); lines.forEach((line, i) => { const num = i === 0 ? data.lineNumber : ++data.lineNumber; result.push({