From ee4c984bc713b63383b8fac20704a2ff067c85d3 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Wed, 13 Apr 2022 15:42:35 +0300 Subject: [PATCH] feat(theme-classic): code block showLineNumbers (#7007) --- .../src/theme-classic.d.ts | 1 + .../src/theme/CodeBlock/index.tsx | 37 ++++++++++--- .../src/theme/CodeBlock/styles.module.css | 36 ++++++++++++- packages/docusaurus-theme-common/src/index.ts | 1 + .../src/utils/codeBlockUtils.ts | 4 ++ .../_pages tests/code-block-tests.mdx | 48 +++++++++++++++++ .../markdown-features-code-blocks.mdx | 52 +++++++++++++++++-- 7 files changed, 168 insertions(+), 11 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 18da1b9d35..675086f692 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -156,6 +156,7 @@ declare module '@theme/CodeBlock' { readonly metastring?: string; readonly title?: string; readonly language?: string; + readonly showLineNumbers?: boolean; } export default function CodeBlock(props: Props): JSX.Element; diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx index 40ab6581fd..717ef19cea 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx @@ -13,6 +13,7 @@ import { parseCodeBlockTitle, parseLanguage, parseLines, + containsLineNumbers, ThemeClassNames, usePrismTheme, } from '@docusaurus/theme-common'; @@ -26,6 +27,7 @@ export default function CodeBlock({ className: blockClassName = '', metastring, title, + showLineNumbers, language: languageProp, }: Props): JSX.Element { const {prism} = useThemeConfig(); @@ -87,6 +89,8 @@ export default function CodeBlock({ const language = languageProp ?? parseLanguage(blockClassName) ?? prism.defaultLanguage; const {highlightLines, code} = parseLines(content, metastring, language); + const shouldShowLineNumbers = + showLineNumbers || containsLineNumbers(metastring); return ( - + {tokens.map((line, i) => { if (line.length === 1 && line[0]!.content === '\n') { line[0]!.content = ''; } - const lineProps = getLineProps({line, key: i}); + const lineProps = getLineProps({ + line, + key: i, + ...(shouldShowLineNumbers && {className: styles.codeLine}), + }); if (highlightLines.includes(i)) { lineProps.className += ' docusaurus-highlight-code-line'; } + const lineTokens = line.map((token, key) => ( + + )); + return ( - {line.map((token, key) => ( - - ))} -
+ {shouldShowLineNumbers ? ( + <> + + + {lineTokens} + + + ) : ( + <> + {lineTokens} +
+ + )}
); })} diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css index c74136412f..92a92bdd53 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/styles.module.css @@ -28,9 +28,9 @@ } .codeBlock { + --ifm-pre-background: inherit; margin: 0; padding: 0; - background-color: inherit; } .codeBlockTitle + .codeBlockContent .codeBlock { @@ -44,14 +44,48 @@ .codeBlockLines { font: inherit; + background: var(--ifm-pre-background); /* rtl:ignore */ float: left; min-width: 100%; padding: var(--ifm-pre-padding); } +.codeBlockLinesWithNumbering { + display: table; + padding: var(--ifm-pre-padding) 0; +} + @media print { .codeBlockLines { white-space: pre-wrap; } } + +.codeLine { + display: table-row; + counter-increment: line-count; +} + +.codeLineNumber { + display: table-cell; + text-align: right; + width: 1%; + position: sticky; + left: 0; + padding: 0 var(--ifm-pre-padding); + background: var(--ifm-pre-background); +} + +.codeLineNumber::before { + content: counter(line-count); + opacity: 0.4; +} + +:global(.docusaurus-highlight-code-line) .codeLineNumber::before { + opacity: 0.8; +} + +.codeLineContent { + padding-right: var(--ifm-pre-padding); +} diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 804de23703..81af65a8be 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -34,6 +34,7 @@ export { parseCodeBlockTitle, parseLanguage, parseLines, + containsLineNumbers, } from './utils/codeBlockUtils'; export { diff --git a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts index 46277defbf..b593dd315d 100644 --- a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts @@ -77,6 +77,10 @@ export function parseCodeBlockTitle(metastring?: string): string { return metastring?.match(codeBlockTitleRegex)?.groups!.title ?? ''; } +export function containsLineNumbers(metastring?: string): boolean { + return metastring?.includes('showLineNumbers') || false; +} + /** * Gets the language name from the class name (set by MDX). * e.g. `"language-javascript"` => `"javascript"`. diff --git a/website/_dogfooding/_pages tests/code-block-tests.mdx b/website/_dogfooding/_pages tests/code-block-tests.mdx index 4a0355144c..d6f7c0816c 100644 --- a/website/_dogfooding/_pages tests/code-block-tests.mdx +++ b/website/_dogfooding/_pages tests/code-block-tests.mdx @@ -142,3 +142,51 @@ Multi-line text inside `pre` will turn into one-liner, but it's okay (https://gi cd", at which time b is a substring.
+ +## Code blocks with line numbering tests + +```jsx +function PageLayout(props) { + // highlight-next-line + return ; +} +``` + +```jsx showLineNumbers +function PageLayout(props) { + // highlight-next-line + return ; +} +``` + +```jsx {1,3,6} showLineNumbers +function PageLayout(props) { + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); + console.log( + 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test ', + ); +} +``` diff --git a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx index bb586e9ce1..07990cd9b9 100644 --- a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx +++ b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx @@ -275,6 +275,44 @@ In the future, we may extend the magic comment system and let you define custom ::: +## Line numbering {#line-numbering} + +You can enable line numbering for your code block by using `showLineNumbers` key within the language meta string (don't forget to add space directly before the key). + +````md +```jsx {1,4-6,11} showLineNumbers +import React from 'react'; + +function MyComponent(props) { + if (props.isBar) { + return
Bar
; + } + + return
Foo
; +} + +export default MyComponent; +``` +```` + + + +```jsx {1,4-6,11} showLineNumbers +import React from 'react'; + +function MyComponent(props) { + if (props.isBar) { + return
Bar
; + } + + return
Foo
; +} + +export default MyComponent; +``` + +
+ ## Interactive code editor {#interactive-code-editor} (Powered by [React Live](https://github.com/FormidableLabs/react-live)) @@ -596,7 +634,10 @@ export default function MyReactPage() { return (
{/* highlight-start */} - + {`function HelloCodeTitle(props) { return

Hello, {props.name}

; }`} @@ -608,15 +649,18 @@ export default function MyReactPage() { ``` - + {`function HelloCodeTitle(props) { return

Hello, {props.name}

; }`}
-The props accepted are `language` and `title`, in the same way as you write Markdown code blocks. +The props accepted are `language`, `title` and `showLineNumbers`, in the same way as you write Markdown code blocks. -Although discouraged, you can also pass in a `metastring` prop like `metastring='{1-2} title="/src/components/HelloCodeTitle.js"'`, which is how Markdown code blocks are handled under the hood. However, we recommend you [use comments for highlighting lines](#highlighting-with-comments). +Although discouraged, you can also pass in a `metastring` prop like `metastring='{1-2} title="/src/components/HelloCodeTitle.js" showLineNumbers'`, which is how Markdown code blocks are handled under the hood. However, we recommend you [use comments for highlighting lines](#highlighting-with-comments). As [previously stated](#using-jsx-markup), syntax highlighting is only applied when the children is a simple string.