add markdown codeblock renderer extension point (#1492)

Introduces a new extension point that allows developers to overwrite the default syntax highlighting renderer for specific code blocks defined in markdown. The extension point is dynamic and follows the pattern "markdown-renderer.code.{language}".
This feature lays the groundwork for the scm-markdown-plantuml-plugin.
This commit is contained in:
Konstantin Schaper
2021-01-07 09:25:31 +01:00
committed by GitHub
parent 8eb599ff94
commit 831c8b0271
7 changed files with 292 additions and 5035 deletions

View File

@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- add markdown codeblock renderer extension point ([#1492](https://github.com/scm-manager/scm-manager/pull/1492))
## [2.12.0] - 2020-12-17
### Added
- Add repository import via dump file for Subversion ([#1471](https://github.com/scm-manager/scm-manager/pull/1471))

View File

@@ -57,7 +57,10 @@ The following extension points are provided for the frontend:
### roles.route
### user.route
### user.setting
### markdown-renderer.code.{language}
- Dynamic extension point for custom language-specific renderers
- Overrides the default Syntax Highlighter
- Used by the Markdown Plantuml Plugin
# Deprecated

View File

@@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC } from "react";
import SyntaxHighlighter from "./SyntaxHighlighter";
import { ExtensionPoint, useBinder } from "@scm-manager/ui-extensions";
import { connect } from "react-redux";
type Props = {
language?: string;
value: string;
indexLinks: { [key: string]: any };
};
const MarkdownCodeRenderer: FC<Props> = (props) => {
const binder = useBinder();
const { language, indexLinks } = props;
const extensionKey = `markdown-renderer.code.${language}`;
if (binder.hasExtension(extensionKey, props)) {
return <ExtensionPoint name={extensionKey} props={{ ...props, indexLinks }} />;
}
return <SyntaxHighlighter {...props} />;
};
const mapStateToProps = (state: any) => {
const indexLinks = state.indexResources.links;
return {
indexLinks,
};
};
export default connect(mapStateToProps)(MarkdownCodeRenderer);

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React from "react";
import React, { FC } from "react";
import { storiesOf } from "@storybook/react";
import MarkdownView from "./MarkdownView";
import styled from "styled-components";
@@ -29,20 +29,22 @@ import styled from "styled-components";
import TestPage from "./__resources__/test-page.md";
import MarkdownWithoutLang from "./__resources__/markdown-without-lang.md";
import MarkdownXmlCodeBlock from "./__resources__/markdown-xml-codeblock.md";
import MarkdownUmlCodeBlock from "./__resources__/markdown-uml-codeblock.md";
import MarkdownInlineXml from "./__resources__/markdown-inline-xml.md";
import MarkdownLinks from "./__resources__/markdown-links.md";
import MarkdownCommitLinks from "./__resources__/markdown-commit-link.md";
import Title from "./layout/Title";
import { Subtitle } from "./layout";
import { MemoryRouter } from "react-router-dom";
import { Binder, BinderContext } from "@scm-manager/ui-extensions";
const Spacing = styled.div`
padding: 2em;
`;
storiesOf("MarkdownView", module)
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.addDecorator(story => <Spacing>{story()}</Spacing>)
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.addDecorator((story) => <Spacing>{story()}</Spacing>)
.add("Default", () => <MarkdownView content={TestPage} skipHtml={false} />)
.add("Code without Lang", () => <MarkdownView content={MarkdownWithoutLang} skipHtml={false} />)
.add("Xml Code Block", () => <MarkdownView content={MarkdownXmlCodeBlock} />)
@@ -54,4 +56,23 @@ storiesOf("MarkdownView", module)
</>
))
.add("Links", () => <MarkdownView content={MarkdownLinks} basePath="/" />)
.add("Commit Links", () => <MarkdownView content={MarkdownCommitLinks} />);
.add("Commit Links", () => <MarkdownView content={MarkdownCommitLinks} />)
.add("Custom code renderer", () => {
const binder = new Binder("custom code renderer");
const Container: FC<{ value: string }> = ({ value }) => {
return (
<div>
<h4 style={{ border: "1px dashed lightgray", padding: "2px" }}>
To render plantuml as images within markdown, please install the scm-markdown-plantuml-plguin
</h4>
<pre>{value}</pre>
</div>
);
};
binder.bind("markdown-renderer.code.uml", Container);
return (
<BinderContext.Provider value={binder}>
<MarkdownView content={MarkdownUmlCodeBlock} />
</BinderContext.Provider>
);
});

View File

@@ -27,12 +27,12 @@ import { RouteComponentProps, withRouter } from "react-router-dom";
import Markdown from "react-markdown/with-html";
import { binder } from "@scm-manager/ui-extensions";
import ErrorBoundary from "./ErrorBoundary";
import SyntaxHighlighter from "./SyntaxHighlighter";
import MarkdownHeadingRenderer from "./MarkdownHeadingRenderer";
import { create } from "./MarkdownLinkRenderer";
import { useTranslation, WithTranslation, withTranslation } from "react-i18next";
import Notification from "./Notification";
import { createTransformer } from "./remarkChangesetShortLinkParser";
import MarkdownCodeRenderer from "./MarkdownCodeRenderer";
type Props = RouteComponentProps &
WithTranslation & {
@@ -81,13 +81,13 @@ const MarkdownErrorNotification: FC = () => {
class MarkdownView extends React.Component<Props, State> {
static defaultProps: Partial<Props> = {
enableAnchorHeadings: false,
skipHtml: false
skipHtml: false,
};
constructor(props: Props) {
super(props);
this.state = {
contentRef: null
contentRef: null,
};
}
@@ -137,12 +137,12 @@ class MarkdownView extends React.Component<Props, State> {
}
if (!rendererList.code) {
rendererList.code = SyntaxHighlighter;
rendererList.code = MarkdownCodeRenderer;
}
return (
<ErrorBoundary fallback={MarkdownErrorNotification}>
<div ref={el => this.setState({ contentRef: el })}>
<div ref={(el) => this.setState({ contentRef: el })}>
<Markdown
className="content is-word-break"
skipHtml={skipHtml}

View File

@@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
export default `# Uml Code Block in Markdown
\`\`\`uml
actor Foo1
boundary Foo2
control Foo3
entity Foo4
database Foo5
collections Foo6
Foo1 -> Foo2 : To boundary
Foo1 -> Foo3 : To control
Foo1 -> Foo4 : To entity
Foo1 -> Foo5 : To database
Foo1 -> Foo6 : To collections
\`\`\``;

File diff suppressed because it is too large Load Diff