feat(theme-classic): code block showLineNumbers (#7007)

This commit is contained in:
Alexey Pyltsyn 2022-04-13 15:42:35 +03:00 committed by GitHub
parent 4d9a0edf21
commit ee4c984bc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 11 deletions

View file

@ -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;

View file

@ -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 (
<Highlight
@ -116,24 +120,45 @@ export default function CodeBlock({
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
className={clsx(className, styles.codeBlock, 'thin-scrollbar')}>
<code className={styles.codeBlockLines}>
<code
className={clsx(
styles.codeBlockLines,
shouldShowLineNumbers && styles.codeBlockLinesWithNumbering,
)}>
{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) => (
<span key={key} {...getTokenProps({token, key})} />
));
return (
<span key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
{shouldShowLineNumbers ? (
<>
<span className={styles.codeLineNumber} />
<span className={styles.codeLineContent}>
{lineTokens}
</span>
</>
) : (
<>
{lineTokens}
<br />
</>
)}
</span>
);
})}

View file

@ -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);
}

View file

@ -34,6 +34,7 @@ export {
parseCodeBlockTitle,
parseLanguage,
parseLines,
containsLineNumbers,
} from './utils/codeBlockUtils';
export {

View file

@ -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"`.

View file

@ -142,3 +142,51 @@ Multi-line text inside `pre` will turn into one-liner, but it's okay (https://gi
</strong>cd", at which time b is a substring.
<br />
</CodeBlock>
## Code blocks with line numbering tests
```jsx
function PageLayout(props) {
// highlight-next-line
return <Layout title="Awesome Docusaurus page" description="Test Test Test Test Test Test Test Test Test Test Test Test Test Test ">;
}
```
```jsx showLineNumbers
function PageLayout(props) {
// highlight-next-line
return <Layout title="Awesome Docusaurus page" description="Test Test Test Test Test Test Test Test Test Test Test Test Test Test ">;
}
```
```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 ',
);
}
```

View file

@ -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 <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
````
<BrowserWindow>
```jsx {1,4-6,11} showLineNumbers
import React from 'react';
function MyComponent(props) {
if (props.isBar) {
return <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
</BrowserWindow>
## 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 (
<div>
{/* highlight-start */}
<CodeBlock language="jsx" title="/src/components/HelloCodeTitle.js">
<CodeBlock
language="jsx"
title="/src/components/HelloCodeTitle.js"
showLineNumbers>
{`function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}`}
@ -608,15 +649,18 @@ export default function MyReactPage() {
```
<BrowserWindow>
<CodeBlock language="jsx" title="/src/components/HelloCodeTitle.js">
<CodeBlock
language="jsx"
title="/src/components/HelloCodeTitle.js"
showLineNumbers>
{`function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}`}
</CodeBlock>
</BrowserWindow>
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.