Biblioteca Java para analizar y representar texto Markdown de acuerdo con la especificación CommonMark (y algunas extensiones).
Proporciona clases para analizar la entrada a un árbol de sintaxis abstracta (AST), visitar y manipular nodos y renderizar en HTML o volver a Markdown. Comenzó como una adaptación de commonmark.js, pero desde entonces ha evolucionado hasta convertirse en una biblioteca extensible con las siguientes características:
Pequeño (el núcleo no tiene dependencias, extensiones en artefactos separados)
Rápido (entre 10 y 20 veces más rápido que el pegdown, que solía ser una biblioteca Markdown popular; consulte los puntos de referencia en el repositorio)
Flexible (manipular el AST después del análisis, personalizar la representación HTML)
Extensible (tablas, tachado, enlace automático y más, ver más abajo)
La biblioteca es compatible con Java 11 y versiones posteriores. También funciona en Android, pero es lo mejor que puede hacer; informe los problemas. Para Android, el nivel mínimo de API es 19; consulte el directorio commonmark-android-test.
Coordenadas de la biblioteca principal (ver todo en Maven Central):
<dependencia> <groupId>org.commonmark</groupId> <artifactId>marca común</artifactId> <versión>0.24.0</versión> </dependencia>
Los nombres de los módulos a utilizar en Java 9 son org.commonmark
, org.commonmark.ext.autolink
, etc., correspondientes a los nombres de los paquetes.
Tenga en cuenta que para las versiones 0.x de esta biblioteca, la API aún no se considera estable y puede interrumpirse entre versiones menores. Después de la versión 1.0, se seguirá el control de versiones semántico. Un paquete que contiene beta
significa que aún no está sujeto a garantías de API estables; pero para un uso normal no debería ser necesario su uso.
Consulte el archivo spec.txt si se pregunta qué versión de la especificación está implementada actualmente. Consulte también el dingus de CommonMark para familiarizarse con la sintaxis o probar casos extremos. Si clonas el repositorio, también puedes usar la clase DingusApp
para probar cosas de forma interactiva.
importar org.commonmark.node.*;importar org.commonmark.parser.Parser;importar org.commonmark.renderer.html.HtmlRenderer;Parser parser = Parser.builder().build();Node document = parser.parse(" Esto es *Markdown*");HtmlRenderer renderizador = HtmlRenderer.builder().build();renderer.render(documento); // "<p>Esto es <em>Markdown</em></p>n"
Esto utiliza el analizador y el renderizador con opciones predeterminadas. Ambos constructores tienen métodos para configurar su comportamiento:
escapeHtml(true)
en HtmlRenderer
escapará de etiquetas y bloques HTML sin formato.
sanitizeUrls(true)
en HtmlRenderer
eliminará las URL potencialmente inseguras de las etiquetas <a>
y <img>
Para conocer todas las opciones disponibles, consulte los métodos de los constructores.
Tenga en cuenta que esta biblioteca no intenta desinfectar el HTML resultante con respecto a qué etiquetas están permitidas, etc. Eso es responsabilidad de la persona que llama, y si expone el HTML resultante, probablemente desee ejecutar un desinfectante después de esto. .
importar org.commonmark.node.*;importar org.commonmark.renderer.markdown.MarkdownRenderer;Representador MarkdownRenderer = MarkdownRenderer.builder().build();Documento de nodo = nuevo documento();Encabezado encabezado = nuevo encabezado();encabezado .setLevel(2);heading.appendChild(nuevo Texto("Mi título"));document.appendChild(encabezado);renderer.render(documento); // "## Mi títulon"
Para renderizar en texto plano con un marcado mínimo, también existe TextContentRenderer
.
Una vez analizado el texto fuente, el resultado es un árbol de nodos. Ese árbol se puede modificar antes de renderizar o simplemente inspeccionar sin renderizar:
Nodo nodo = parser.parse("Ejemplon=======nnAlgo de texto más");WordCountVisitor visitante = nuevo WordCountVisitor();node.accept(visitor);visitor.wordCount; // 4class WordCountVisitor extiende AbstractVisitor { int wordCount = 0; @Override public void visit(Text text) { // Esto se llama para todos los nodos de Text. Anule otros métodos de visita para otros tipos de nodos. // Cuenta palabras (esto es solo un ejemplo, en realidad no lo hagas de esta manera por varias razones). wordCount += text.getLiteral().split("\W+").length; // Descender a hijos (podría omitirse en este caso porque los nodos de texto no tienen hijos). visitarNiños(texto); } }
Si desea saber dónde apareció un Node
analizado en el texto fuente de entrada, puede solicitar al analizador que devuelva posiciones fuente como esta:
var analizador = Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
Luego analice los nodos e inspeccione las posiciones de origen:
var fuente = "foonnbar *baz*";var doc = parser.parse(fuente);var énfasis = doc.getLastChild().getLastChild();var s = énfasis.getSourceSpans().get(0) ;s.getLineIndex(); // 2 (tercera línea)s.getColumnIndex(); // 4 (quinta columna)s.getInputIndex(); // 9 (índice de cadena 9)s.getLength(); // 5source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()); // "*baz*"
Si solo está interesado en bloques y no en líneas, use IncludeSourceSpans.BLOCKS
.
A veces es posible que desees personalizar cómo se representa HTML. Si todo lo que desea hacer es agregar o cambiar atributos en algunos elementos, existe una manera sencilla de hacerlo.
En este ejemplo, registramos una fábrica para un AttributeProvider
en el renderizador para establecer un atributo class="border"
en elementos img
.
Analizador analizador = Parser.builder().build();HtmlRenderer renderizador = HtmlRenderer.builder() .attributeProviderFactory(new AttributeProviderFactory() { crear AttributeProvider público (contexto AttributeProviderContext) { devolver nuevo ImageAttributeProvider(); } }) .build();Node document = parser.parse("![text](/url.png)");renderer.render(document);// "<p><img src="/url.png " alt="text" class="border" /></p>n"class ImageAttributeProvider implements AttributeProvider { @Override public void setAttributes(Node nodo, nombre de etiqueta de cadena, atributos de mapa<cadena, cadena>) { if (instancia de nodo de imagen) { atributos.put("clase", "borde"); } } }
Si desea hacer algo más que cambiar atributos, también existe una manera de tomar control total sobre cómo se representa HTML.
En este ejemplo, estamos cambiando la representación de bloques de código con sangría para envolverlos solo en pre
en lugar de pre
y code
:
Analizador analizador = Parser.builder().build();HtmlRenderer renderizador = HtmlRenderer.builder() .nodeRendererFactory(new HtmlNodeRendererFactory() { crear NodeRenderer público (contexto HtmlNodeRendererContext) { devolver nuevo IndentedCodeBlockNodeRenderer(contexto); } }) .build();Node document = parser.parse("Ejemplo:nn código");renderer.render(document);// "<p>Ejemplo:</p>n<pre>códigon </pre>n"class IndentedCodeBlockNodeRenderer implementa NodeRenderer {private final HtmlWriter html; IndentedCodeBlockNodeRenderer (contexto HtmlNodeRendererContext) { this.html = context.getWriter(); } @Override public Set<Clase<? extends Node>> getNodeTypes() { // Devuelve los tipos de nodos para los que queremos usar este renderizador. return Conjunto.de(IndentedCodeBlock.class); } @Override public void render(Node node) { // Solo manejamos un tipo según getNodeTypes, por lo que podemos transmitirlo aquí. IndentedCodeBlock codeBlock = nodo (IndentedCodeBlock); html.line(); html.tag("pre"); html.text(codeBlock.getLiteral()); html.tag("/pre"); html.line(); } }
En caso de que desee almacenar datos adicionales en el documento o tener elementos personalizados en el HTML resultante, puede crear su propia subclase de CustomNode
y agregar instancias como nodos secundarios a los nodos existentes.
Para definir la representación HTML para ellos, puede utilizar un NodeRenderer
como se explicó anteriormente.
Hay algunas formas de ampliar el análisis o incluso anular el análisis integrado, todas ellas a través de métodos en Parser.Builder
(consulte Bloques y líneas integradas en la especificación para obtener una descripción general de los bloques/líneas integradas):
El análisis de tipos de bloques específicos (por ejemplo, encabezados, bloques de código, etc.) se puede habilitar/deshabilitar con enabledBlockTypes
El análisis de bloques se puede ampliar/anular con customBlockParserFactory
El análisis de contenido en línea se puede ampliar/anular con customInlineContentParserFactory
El análisis de delimitadores en contenido en línea se puede ampliar con customDelimiterProcessor
El procesamiento de enlaces se puede personalizar con linkProcessor
y linkMarker
Tanto Parser
como HtmlRenderer
están diseñados para que pueda configurarlos una vez usando los constructores y luego usarlos varias veces/desde múltiples subprocesos. Esto se hace separando el estado para analizar/renderizar de la configuración.
Dicho esto, puede haber errores, por supuesto. Si encuentra uno, informe un problema.
Los Javadocs están disponibles en línea en javadoc.io.
Las extensiones deben ampliar el analizador, el procesador HTML o ambos. Para usar una extensión, los objetos del constructor se pueden configurar con una lista de extensiones. Como las extensiones son opcionales, se encuentran en artefactos separados, por lo que también es necesario agregar dependencias adicionales.
Veamos cómo habilitar tablas de GitHub Flavored Markdown. Primero, agregue una dependencia adicional (consulte Maven Central para obtener más información):
<dependencia> <groupId>org.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <versión>0.24.0</versión> </dependencia>
Luego, configure la extensión en los constructores:
importar org.commonmark.ext.gfm.tables.TablesExtension;List<Extension> extensiones = List.of(TablesExtension.create());Parser parser = Parser.builder() .extensiones(extensiones) .build();HtmlRenderer renderizador = HtmlRenderer.builder() .extensiones(extensiones) .construir();
Para configurar otra extensión en el ejemplo anterior, simplemente agréguela a la lista.
Las siguientes extensiones se desarrollan con esta biblioteca, cada una en su propio artefacto.
Convierte enlaces simples como URL y direcciones de correo electrónico en enlaces (basados en autolink-java).
Utilice la clase AutolinkExtension
del artefacto commonmark-ext-autolink
.
Permite tachar el texto encerrándolo en ~~
. Por ejemplo, en hey ~~you~~
, you
como texto tachado.
Utilice la clase StrikethroughExtension
en el artefacto commonmark-ext-gfm-strikethrough
.
Habilita tablas que utilizan tuberías como en GitHub Flavored Markdown.
Utilice la clase TablesExtension
en el artefacto commonmark-ext-gfm-tables
.
Habilita notas al pie como en GitHub o Pandoc:
Main text[^1] [^1]: Additional text in a footnote
Las notas al pie en línea como ^[inline footnote]
también se admiten cuando se habilitan mediante FootnotesExtension.Builder#inlineFootnotes
.
Utilice la clase FootnotesExtension
en el artefacto commonmark-ext-footnotes
.
Permite agregar atributos "id" generados automáticamente a las etiquetas de encabezado. La "id" se basa en el texto del título.
# Heading
se representará como:
<h1 id="heading">Heading</h1>
Utilice la clase HeadingAnchorExtension
en el artefacto commonmark-ext-heading-anchor
.
En caso de que desee una representación personalizada del encabezado, puede usar la clase IdGenerator
directamente junto con HtmlNodeRendererFactory
(consulte el ejemplo anterior).
Permite subrayar el texto encerrándolo en ++
. Por ejemplo, en hey ++you++
, you
como texto subrayado. Utiliza la etiqueta <ins>.
Utilice la clase InsExtension
en el artefacto commonmark-ext-ins
.
Agrega soporte para metadatos a través de un bloque frontal de YAML. Esta extensión solo admite un subconjunto de sintaxis YAML. A continuación se muestra un ejemplo de lo que se admite:
--- key: value list: - value 1 - value 2 literal: | this is literal value. literal values 2 --- document start here
Utilice la clase YamlFrontMatterExtension
en el artefacto commonmark-ext-yaml-front-matter
. Para recuperar metadatos, use YamlFrontMatterVisitor
.
Agrega soporte para especificar atributos (específicamente alto y ancho) para imágenes.
Los elementos de atributo se proporcionan como pares key=value
entre llaves { }
después del nodo de imagen al que se aplican, por ejemplo:
![text](/url.png){width=640 height=480}
se representará como:
<img src="/url.png" alt="text" width="640" height="480" />
Utilice la clase ImageAttributesExtension
en el artefacto commonmark-ext-image-attributes
.
Nota: dado que esta extensión usa llaves {
}
como delimitadores (en StylesDelimiterProcessor
), esto significa que otros procesadores delimitadores no pueden usar llaves para delimitar.
Agrega soporte para tareas como elementos de lista.
Una tarea se puede representar como un elemento de lista donde el primer carácter que no es un espacio en blanco es un corchete izquierdo [
, luego un solo carácter de espacio en blanco o la letra x
en minúscula o mayúscula, luego un corchete derecho ]
seguido de al menos un espacio en blanco antes de cualquier otro. contenido.
Por ejemplo:
- [ ] task #1 - [x] task #2
se representará como:
<ul> <li><input type="checkbox" disabled=""> task #1</li> <li><input type="checkbox" disabled="" checked=""> task #2</li> </ul>
Utilice la clase TaskListItemsExtension
en el artefacto commonmark-ext-task-list-items
.
También puedes encontrar otras extensiones disponibles:
commonmark-ext-notifications: esta extensión permite crear fácilmente párrafos de notificaciones/advertencias como INFO
, SUCCESS
, WARNING
o ERROR
Algunos usuarios de esta biblioteca (no dude en generar un PR si desea que lo agreguen):
Atlassian (donde se desarrolló inicialmente la biblioteca)
Java (OpenJDK) (enlace)
Revisión del código Gerrit/Gitiles (enlace)
Clerk moldeable programación en vivo para Clojure
Znai
Markwon: biblioteca de Android para representar Markdown como Spannables nativos del sistema
flexmark-java: bifurcación que agregó soporte para mucha más sintaxis y flexibilidad
Consulte el archivo CONTRIBUTING.md.
Copyright (c) 2015, Robin Stocker
Licencia BSD (2 cláusulas), consulte el archivo LICENSE.txt.