Restructure highlight code to enable line wrapping (#265)

* Restructure highlight code to enable line wrapping

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
mrdrogdrog 2020-06-23 22:09:56 +02:00 committed by GitHub
parent 6bf214542e
commit eb56da7871
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 63 additions and 60 deletions

View file

@ -27,6 +27,7 @@
- A new table view for the history (besides the card view)
- Better support for RTL-languages (and LTR-content in a RTL-page)
- Users may now change their display name and password (only email accounts) on the new profile page
- Highlighted code blocks can now use line wrapping and line numbers at once
### Changed

View file

@ -1,33 +1,42 @@
.markdown-body pre code {
.markdown-body {
@import '../../../../node_modules/highlight.js/styles/github-gist';
}
&.hljs {
display: flex;
flex-direction: row;
}
.markdown-body pre code.hljs {
.linenumbers {
text-align: right;
position: relative;
cursor: default;
z-index: 4;
padding: 0 8px 0 0;
min-width: 20px;
box-sizing: content-box;
color: #afafaf;
border-right: 3px solid #6ce26c;
display: flex;
flex-direction: column;
float: left;
overflow: hidden;
user-select: none;
padding: 16px;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
& > span:before {
content: attr(data-line-number);
&.showGutter {
.linenumber {
position: relative;
cursor: default;
z-index: 4;
padding: 0 8px 0 0;
min-width: 20px;
box-sizing: content-box;
color: #afafaf;
border-right: 3px solid #6ce26c;
flex-direction: column;
overflow: hidden;
user-select: none;
display: flex;
align-items: flex-end;
&:before {
content: attr(data-line-number);
}
}
}
.code {
float: left;
&.showGutter .line {
margin: 0 0 0 16px;
}
&.wrapLines .line {
white-space: pre-wrap;
}
}

View file

@ -1,13 +1,13 @@
import hljs from 'highlight.js'
import React, { useMemo } from 'react'
import React, { Fragment, useMemo } from 'react'
import ReactHtmlParser from 'react-html-parser'
import { ShowIf } from '../show-if/show-if'
import './highlighted-code.scss'
export interface HighlightedCodeProps {
code: string,
language?: string,
showGutter: boolean
wrapLines: boolean
}
export const escapeHtml = (unsafe: string): string => {
@ -19,11 +19,11 @@ export const escapeHtml = (unsafe: string): string => {
.replace(/'/g, '&#039;')
}
const checkIfLanguageIsSupported = (language: string):boolean => {
const checkIfLanguageIsSupported = (language: string): boolean => {
return hljs.listLanguages().indexOf(language) > -1
}
const correctLanguage = (language: string|undefined): string|undefined => {
const correctLanguage = (language: string | undefined): string | undefined => {
switch (language) {
case 'html':
return 'xml'
@ -32,7 +32,7 @@ const correctLanguage = (language: string|undefined): string|undefined => {
}
}
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, showGutter }) => {
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, showGutter, wrapLines }) => {
const highlightedCode = useMemo(() => {
const replacedLanguage = correctLanguage(language)
return ((!!replacedLanguage && checkIfLanguageIsSupported(replacedLanguage)) ? hljs.highlight(replacedLanguage, code).value : escapeHtml(code))
@ -42,25 +42,18 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
}, [code, language])
return (
<code className={'hljs'}>
<ShowIf condition={showGutter}>
<span className={'linenumbers'}>
{
highlightedCode
.map((line, index) => {
return <span key={index} data-line-number={index + 1}/>
})
}
</span>
</ShowIf>
<span className={'code'}>
{
highlightedCode
.map((line, index) =>
<div key={index} className={'line'}>
<code className={`hljs ${showGutter ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
{
highlightedCode
.map((line, index) => {
return <Fragment key={index}>
<span className={'linenumber'} data-line-number={index + 1}/>
<div className={'line'}>
{line}
</div>)
}
</span>
</div>
</Fragment>
})
}
</code>)
}

View file

@ -1,6 +1,6 @@
import MarkdownIt from 'markdown-it/lib'
const highlightRegex = /^(\w*)(=?)$/
const highlightRegex = /^(\w*)(=?)(!?)$/
export const highlightedCode: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
md.core.ruler.push('highlighted-code', (state) => {
@ -16,6 +16,9 @@ export const highlightedCode: MarkdownIt.PluginSimple = (md: MarkdownIt) => {
if (highlightInfos[2]) {
token.attrJoin('data-show-gutter', '')
}
if (highlightInfos[3]) {
token.attrJoin('data-wrap-lines', '')
}
}
})
return true

View file

@ -9,15 +9,15 @@ import footnote from 'markdown-it-footnote'
import imsize from 'markdown-it-imsize'
import inserted from 'markdown-it-ins'
import marked from 'markdown-it-mark'
import mathJax from 'markdown-it-mathjax'
import markdownItRegex from 'markdown-it-regex'
import subscript from 'markdown-it-sub'
import superscript from 'markdown-it-sup'
import toc from 'markdown-it-table-of-contents'
import taskList from 'markdown-it-task-lists'
import mathJax from 'markdown-it-mathjax'
import React, { ReactElement, useMemo } from 'react'
import MathJaxReact from 'react-mathjax'
import ReactHtmlParser, { convertNodeToElement, Transform } from 'react-html-parser'
import MathJaxReact from 'react-mathjax'
import { createRenderContainer, validAlertLevels } from './container-plugins/alert'
import { highlightedCode } from './markdown-it-plugins/highlighted-code'
import { MarkdownItParserDebugger } from './markdown-it-plugins/parser-debugger'
@ -35,12 +35,12 @@ import { replaceQuoteExtraTime } from './regex-plugins/replace-quote-extra-time'
import { replaceVimeoLink } from './regex-plugins/replace-vimeo-link'
import { replaceYouTubeLink } from './regex-plugins/replace-youtube-link'
import { getGistReplacement } from './replace-components/gist/gist-frame'
import { getHighlightedFence } from './replace-components/highlighted-fence/highlighted-fence'
import { getMathJaxReplacement } from './replace-components/mathjax/mathjax-replacer'
import { getHighlightedCodeBlock } from './replace-components/highlighted-code/highlighted-code'
import { getPDFReplacement } from './replace-components/pdf/pdf-frame'
import { getQuoteOptionsReplacement } from './replace-components/quote-options/quote-options'
import { getTOCReplacement } from './replace-components/toc/toc-replacer'
import { getVimeoReplacement } from './replace-components/vimeo/vimeo-frame'
import { getQuoteOptionsReplacement } from './replace-components/quote-options/quote-options'
import { getYouTubeReplacement } from './replace-components/youtube/youtube-frame'
export interface MarkdownPreviewProps {
@ -51,7 +51,7 @@ export type SubNodeConverter = (node: DomElement, index: number) => ReactElement
export type ComponentReplacer = (node: DomElement, index: number, counterMap: Map<string, number>, nodeConverter: SubNodeConverter) => (ReactElement | undefined);
type ComponentReplacer2Identifier2CounterMap = Map<ComponentReplacer, Map<string, number>>
const allComponentReplacers: ComponentReplacer[] = [getYouTubeReplacement, getVimeoReplacement, getGistReplacement, getPDFReplacement, getTOCReplacement, getHighlightedCodeBlock, getQuoteOptionsReplacement, getMathJaxReplacement]
const allComponentReplacers: ComponentReplacer[] = [getYouTubeReplacement, getVimeoReplacement, getGistReplacement, getPDFReplacement, getTOCReplacement, getHighlightedFence, getQuoteOptionsReplacement, getMathJaxReplacement]
const tryToReplaceNode = (node: DomElement, index:number, componentReplacer2Identifier2CounterMap: ComponentReplacer2Identifier2CounterMap, nodeConverter: SubNodeConverter) => {
return allComponentReplacers

View file

@ -1,3 +0,0 @@
.markdown-body {
@import '../../../../../../node_modules/highlight.js/styles/github-gist';
}

View file

@ -1,7 +1,6 @@
import { DomElement } from 'domhandler'
import React, { ReactElement } from 'react'
import { HighlightedCode } from '../../../../common/highlighted-code/highlighted-code'
import './highlighted-code.scss'
const getElementReplacement = (codeNode: DomElement, index: number, counterMap: Map<string, number>): (ReactElement | undefined) => {
if (codeNode.name !== 'code' || !codeNode.attribs || !codeNode.attribs['data-highlight-language'] || !codeNode.children || !codeNode.children[0]) {
@ -10,7 +9,8 @@ const getElementReplacement = (codeNode: DomElement, index: number, counterMap:
const language = codeNode.attribs['data-highlight-language']
const showGutter = codeNode.attribs['data-show-gutter'] !== undefined
return <HighlightedCode key={index} language={language} showGutter={showGutter} code={codeNode.children[0].data as string}/>
const wrapLines = codeNode.attribs['data-wrap-lines'] !== undefined
return <HighlightedCode key={index} language={language} showGutter={showGutter} wrapLines={wrapLines} code={codeNode.children[0].data as string}/>
}
export { getElementReplacement as getHighlightedCodeBlock }
export { getElementReplacement as getHighlightedFence }