Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/components/code/code_block_copy.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ describe('EuiCodeBlock copy UX', () => {
cy.realMount(<EuiCodeBlock isCopyable>{questionContent}</EuiCodeBlock>);

cy.get('[data-test-subj="euiCodeBlockCopy"]').realClick();
// TODO: Remove incorrect assertion and uncomment correct assertion once bug is fixed
assertClipboardContentEquals('hello\n\nworld');
// assertClipboardContentEquals(questionContent);
assertClipboardContentEquals(questionContent);
});
});
10 changes: 9 additions & 1 deletion src/components/code/code_block_copy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,7 +27,14 @@ 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
// This is needed primarily because of how syntax highlighting
// generated DOM elements affect `innerText` output.
.replace(/\n{2,}/g, '\n') || '',
[_innerText]
);
const textToCopy = isVirtualized ? `${children}` : innerText; // Virtualized code blocks do not have inner text
Expand Down
20 changes: 18 additions & 2 deletions src/components/code/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -143,12 +159,12 @@ const addLineData = (
return nodes.reduce<ExtendedRefractorNode[]>((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({
Expand Down
3 changes: 3 additions & 0 deletions upcoming_changelogs/6794.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Bug fixes**

- Fixed `EuiCodeBlock` potentially incorrectly ignoring lines ending with a question mark when using the Copy button.