mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-10-19 05:40:16 -04:00
Add "start with line number" and "continue line number" syntax to highlighted code blocks (#267)
Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
parent
cdadc7b41a
commit
312c86e702
4 changed files with 31 additions and 17 deletions
|
@ -25,17 +25,17 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: attr(data-line-number);
|
content: attr(data-line-number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.showGutter .line {
|
&.showGutter .codeline {
|
||||||
margin: 0 0 0 16px;
|
margin: 0 0 0 16px;
|
||||||
}
|
}
|
||||||
&.wrapLines .line {
|
|
||||||
|
&.wrapLines .codeline {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import './highlighted-code.scss'
|
||||||
export interface HighlightedCodeProps {
|
export interface HighlightedCodeProps {
|
||||||
code: string,
|
code: string,
|
||||||
language?: string,
|
language?: string,
|
||||||
showGutter: boolean
|
startLineNumber?: number
|
||||||
wrapLines: boolean
|
wrapLines: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ const correctLanguage = (language: string | undefined): string | undefined => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, showGutter, wrapLines }) => {
|
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, startLineNumber, wrapLines }) => {
|
||||||
const highlightedCode = useMemo(() => {
|
const highlightedCode = useMemo(() => {
|
||||||
const replacedLanguage = correctLanguage(language)
|
const replacedLanguage = correctLanguage(language)
|
||||||
return ((!!replacedLanguage && checkIfLanguageIsSupported(replacedLanguage)) ? hljs.highlight(replacedLanguage, code).value : escapeHtml(code))
|
return ((!!replacedLanguage && checkIfLanguageIsSupported(replacedLanguage)) ? hljs.highlight(replacedLanguage, code).value : escapeHtml(code))
|
||||||
|
@ -42,13 +42,13 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
|
||||||
}, [code, language])
|
}, [code, language])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<code className={`hljs ${showGutter ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
|
<code className={`hljs ${startLineNumber !== undefined ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
|
||||||
{
|
{
|
||||||
highlightedCode
|
highlightedCode
|
||||||
.map((line, index) => {
|
.map((line, index) => {
|
||||||
return <Fragment key={index}>
|
return <Fragment key={index}>
|
||||||
<span className={'linenumber'} data-line-number={index + 1}/>
|
<span className={'linenumber'} data-line-number={(startLineNumber || 1) + index}/>
|
||||||
<div className={'line'}>
|
<div className={'codeline'}>
|
||||||
{line}
|
{line}
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import MarkdownIt from 'markdown-it/lib'
|
import MarkdownIt from 'markdown-it/lib'
|
||||||
|
|
||||||
const highlightRegex = /^(\w*)(=?)(!?)$/
|
const highlightRegex = /^(\w*)(=(\d*|\+))?(!?)$/
|
||||||
|
|
||||||
export const highlightedCode: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
|
export const highlightedCode: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
|
||||||
md.core.ruler.push('highlighted-code', (state) => {
|
md.core.ruler.push('highlighted-code', (state) => {
|
||||||
|
@ -14,9 +14,12 @@ export const highlightedCode: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
|
||||||
token.attrJoin('data-highlight-language', highlightInfos[1])
|
token.attrJoin('data-highlight-language', highlightInfos[1])
|
||||||
}
|
}
|
||||||
if (highlightInfos[2]) {
|
if (highlightInfos[2]) {
|
||||||
token.attrJoin('data-show-gutter', '')
|
token.attrJoin('data-show-line-numbers', '')
|
||||||
}
|
}
|
||||||
if (highlightInfos[3]) {
|
if (highlightInfos[3]) {
|
||||||
|
token.attrJoin('data-start-line-number', highlightInfos[3])
|
||||||
|
}
|
||||||
|
if (highlightInfos[4]) {
|
||||||
token.attrJoin('data-wrap-lines', '')
|
token.attrJoin('data-wrap-lines', '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
import { DomElement } from 'domhandler'
|
import { DomElement } from 'domhandler'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { HighlightedCode } from '../../../../common/highlighted-code/highlighted-code'
|
import { HighlightedCode } from '../../../../common/highlighted-code/highlighted-code'
|
||||||
import { ComponentReplacer, SubNodeConverter } from '../ComponentReplacer'
|
import { ComponentReplacer } from '../ComponentReplacer'
|
||||||
|
|
||||||
export class HighlightedCodeReplacer implements ComponentReplacer {
|
export class HighlightedCodeReplacer implements ComponentReplacer {
|
||||||
getReplacement (codeNode: DomElement, index: number, subNodeConverter: SubNodeConverter): React.ReactElement | undefined {
|
private lastLineNumber = 0;
|
||||||
|
|
||||||
|
getReplacement (codeNode: DomElement, index: number): React.ReactElement | undefined {
|
||||||
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || !codeNode.children || !codeNode.children[0]) {
|
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || !codeNode.children || !codeNode.children[0]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const language = codeNode.attribs['data-highlight-language']
|
const language = codeNode.attribs['data-highlight-language']
|
||||||
const showGutter = codeNode.attribs['data-show-gutter'] !== undefined
|
const showLineNumbers = codeNode.attribs['data-show-line-numbers'] !== undefined
|
||||||
const wrapLines = codeNode.attribs['data-wrap-lines'] !== undefined
|
const startLineNumberAttribute = codeNode.attribs['data-start-line-number']
|
||||||
|
|
||||||
return <HighlightedCode key={index} language={language} showGutter={showGutter} wrapLines={wrapLines} code={codeNode.children[0].data as string}/>
|
const startLineNumber = startLineNumberAttribute === '+' ? this.lastLineNumber : (parseInt(startLineNumberAttribute) || 1)
|
||||||
|
const wrapLines = codeNode.attribs['data-wrap-lines'] !== undefined
|
||||||
|
const code = codeNode.children[0].data as string
|
||||||
|
|
||||||
|
if (showLineNumbers) {
|
||||||
|
this.lastLineNumber = startLineNumber + code.split('\n')
|
||||||
|
.filter(line => !!line).length
|
||||||
|
}
|
||||||
|
|
||||||
|
return <HighlightedCode key={index} language={language} startLineNumber={showLineNumbers ? startLineNumber : undefined} wrapLines={wrapLines} code={code}/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue