Merge pull request #162 from noties/develop
* `markwon-ext-tables`: fix padding between subsequent table blocks ([#159]) * `markwon-images`: print a single warning instead full stacktrace in case when SVG or GIF are not present in the classpath ([#160]) * Make `Markwon` instance thread-safe by using a single `MarkwonVisitor` for each `render` call ([#157]) * Add `CoreProps.CODE_BLOCK_INFO` with code-block info (language) [#159]: https://github.com/noties/Markwon/issues/159 [#160]: https://github.com/noties/Markwon/issues/160 [#157]: https://github.com/noties/Markwon/issues/157
This commit is contained in:
		
						commit
						4348555b75
					
				
							
								
								
									
										20
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | name: Release | ||||||
|  | 
 | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  | 
 | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  | 
 | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v1 | ||||||
|  |       - name: set up JDK 1.8 | ||||||
|  |         uses: actions/setup-java@v1 | ||||||
|  |         with: | ||||||
|  |           java-version: 1.8 | ||||||
|  |       - name: Build with Gradle | ||||||
|  |         run: ./gradlew build | ||||||
							
								
								
									
										20
									
								
								.github/workflows/snapshot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/snapshot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | name: Snapshot | ||||||
|  | 
 | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - develop | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  | 
 | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  | 
 | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v1 | ||||||
|  |       - name: set up JDK 1.8 | ||||||
|  |         uses: actions/setup-java@v1 | ||||||
|  |         with: | ||||||
|  |           java-version: 1.8 | ||||||
|  |       - name: Build with Gradle | ||||||
|  |         run: ./gradlew build | ||||||
							
								
								
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -1,5 +1,16 @@ | |||||||
| # Changelog | # Changelog | ||||||
| 
 | 
 | ||||||
|  | # 4.1.1 | ||||||
|  | * `markwon-ext-tables`: fix padding between subsequent table blocks ([#159]) | ||||||
|  | * `markwon-images`: print a single warning instead full stacktrace in case when SVG or GIF  | ||||||
|  | are not present in the classpath ([#160]) | ||||||
|  | * Make `Markwon` instance thread-safe by using a single `MarkwonVisitor` for each `render` call ([#157]) | ||||||
|  | * Add `CoreProps.CODE_BLOCK_INFO` with code-block info (language) | ||||||
|  | 
 | ||||||
|  | [#159]: https://github.com/noties/Markwon/issues/159 | ||||||
|  | [#160]: https://github.com/noties/Markwon/issues/160 | ||||||
|  | [#157]: https://github.com/noties/Markwon/issues/157 | ||||||
|  | 
 | ||||||
| # 4.1.0 | # 4.1.0 | ||||||
| * Add `Markwon.TextSetter` interface to be able to use PrecomputedText/PrecomputedTextCompat | * Add `Markwon.TextSetter` interface to be able to use PrecomputedText/PrecomputedTextCompat | ||||||
| * Add `PrecomputedTextSetterCompat` and `compileOnly` dependency on `androidx.core:core`  | * Add `PrecomputedTextSetterCompat` and `compileOnly` dependency on `androidx.core:core`  | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ buildscript { | |||||||
|         jcenter() |         jcenter() | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'com.android.tools.build:gradle:3.4.1' |         classpath 'com.android.tools.build:gradle:3.5.0' | ||||||
|         classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' |         classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -30,7 +30,7 @@ task clean(type: Delete) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| wrapper { | wrapper { | ||||||
|     gradleVersion '5.1.1' |     gradleVersion '5.5.1' | ||||||
|     distributionType 'all' |     distributionType 'all' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|     <div class="warning custom-block"> |     <div class="warning custom-block"> | ||||||
|         <p class="custom-block-title">WARNING</p> |         <p class="custom-block-title">WARNING</p> | ||||||
|         <p>This is documentation for <u>legacy 2.x.x</u> versions. For the most current version <a :href="$withBase('/')">click here.</a></p> |         <p>This is documentation for <u>legacy</u> versions. For the most current version <a :href="$withBase('/')">click here.</a></p> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,9 +16,17 @@ module.exports = { | |||||||
|             { |             { | ||||||
|                 text: 'API Version', |                 text: 'API Version', | ||||||
|                 items: [ |                 items: [ | ||||||
|                     { text: 'Current (4.x.x)', link: '/' }, |                     { | ||||||
|                     { text: 'Legacy (3.x.x)', link: '/docs/v3/install.md' }, |                         text: 'Latest', items: [ | ||||||
|                     { text: 'Legacy (2.x.x)', link: '/docs/v2/' } |                             { text: '4.x.x', link: '/' }, | ||||||
|  |                         ] | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         text: 'Legacy', items: [ | ||||||
|  |                             { text: '3.x.x', link: '/docs/v3/install.md' }, | ||||||
|  |                             { text: '2.x.x', link: '/docs/v2/' } | ||||||
|  |                         ] | ||||||
|  |                     } | ||||||
|                 ] |                 ] | ||||||
|             }, |             }, | ||||||
|             { text: 'Changelog', link: 'https://github.com/noties/Markwon/blob/master/CHANGELOG.md' }, |             { text: 'Changelog', link: 'https://github.com/noties/Markwon/blob/master/CHANGELOG.md' }, | ||||||
| @ -83,7 +91,8 @@ module.exports = { | |||||||
|                         '/docs/v4/core/spans-factory.md', |                         '/docs/v4/core/spans-factory.md', | ||||||
|                         '/docs/v4/core/core-plugin.md', |                         '/docs/v4/core/core-plugin.md', | ||||||
|                         '/docs/v4/core/movement-method-plugin.md', |                         '/docs/v4/core/movement-method-plugin.md', | ||||||
|                         '/docs/v4/core/render-props.md' |                         '/docs/v4/core/render-props.md', | ||||||
|  |                         '/docs/v4/core/text-setter.md' | ||||||
|                     ] |                     ] | ||||||
|                 }, |                 }, | ||||||
|                 '/docs/v4/ext-latex/', |                 '/docs/v4/ext-latex/', | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Configuration | # Configuration | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| `MarkwonConfiguration` class holds common Markwon functionality. | `MarkwonConfiguration` class holds common Markwon functionality. | ||||||
| These are _configurable_ properties: | These are _configurable_ properties: | ||||||
| * `SyntaxHighlight` | * `SyntaxHighlight` | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Core plugin <Badge text="3.0.0" /> | # Core plugin <Badge text="3.0.0" /> | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Since <Badge text="3.0.0" /> with introduction of _plugins_, Markwon | Since <Badge text="3.0.0" /> with introduction of _plugins_, Markwon | ||||||
| **core** functionality was moved to a dedicated plugin. | **core** functionality was moved to a dedicated plugin. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Getting started | # Getting started | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| :::tip Installation | :::tip Installation | ||||||
| Please follow [installation](/docs/v3/install.md) instructions | Please follow [installation](/docs/v3/install.md) instructions | ||||||
| to learn how to add `Markwon` to your project | to learn how to add `Markwon` to your project | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # HTML Renderer | # HTML Renderer | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Starting with <Badge text="3.0.0" /> `MarkwonHtmlRenderer` controls how HTML | Starting with <Badge text="3.0.0" /> `MarkwonHtmlRenderer` controls how HTML | ||||||
| is rendered: | is rendered: | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Images | # Images | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Starting with <Badge text="3.0.0" /> `Markwon` comes with `ImagesPlugin` | Starting with <Badge text="3.0.0" /> `Markwon` comes with `ImagesPlugin` | ||||||
| which supports `http(s)`, `file` and `data` schemes and default media | which supports `http(s)`, `file` and `data` schemes and default media | ||||||
| decoder (for simple images, no [SVG](/docs/v3/image/svg.md) or [GIF](/docs/v3/image/gif.md) which | decoder (for simple images, no [SVG](/docs/v3/image/svg.md) or [GIF](/docs/v3/image/gif.md) which | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Movement method plugin | # Movement method plugin | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| `MovementMethodPlugin` can be used to apply a `MovementMethod` to a TextView | `MovementMethodPlugin` can be used to apply a `MovementMethod` to a TextView | ||||||
| (important if you have links inside your markdown). By default `CorePlugin` | (important if you have links inside your markdown). By default `CorePlugin` | ||||||
| will set a `LinkMovementMethod` on a TextView if one is missing. If you have | will set a `LinkMovementMethod` on a TextView if one is missing. If you have | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Plugins <Badge text="3.0.0" /> | # Plugins <Badge text="3.0.0" /> | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Since <Badge text="3.0.0" /> `MarkwonPlugin` takes the key role in | Since <Badge text="3.0.0" /> `MarkwonPlugin` takes the key role in | ||||||
| processing and rendering markdown. Even **core** functionaly is abstracted | processing and rendering markdown. Even **core** functionaly is abstracted | ||||||
| into a `CorePlugin`. So it's still possible to use `Markwon` with a completely | into a `CorePlugin`. So it's still possible to use `Markwon` with a completely | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # RenderProps <Badge text="3.0.0" /> | # RenderProps <Badge text="3.0.0" /> | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| `RenderProps` encapsulates passing arguments from a node visitor to a node renderer. | `RenderProps` encapsulates passing arguments from a node visitor to a node renderer. | ||||||
| Without hardcoding arguments into an API method calls. | Without hardcoding arguments into an API method calls. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Spans Factory | # Spans Factory | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Starting with <Badge text="3.0.0" /> `MarkwonSpansFactory` controls what spans are displayed | Starting with <Badge text="3.0.0" /> `MarkwonSpansFactory` controls what spans are displayed | ||||||
| for markdown nodes. | for markdown nodes. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Theme | # Theme | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Here is the list of properties that can be configured via `MarkwonTheme.Builder` class.  | Here is the list of properties that can be configured via `MarkwonTheme.Builder` class.  | ||||||
| 
 | 
 | ||||||
| :::tip | :::tip | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Visitor | # Visitor | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| Starting with <Badge text="3.0.0" /> _visiting_ of parsed markdown | Starting with <Badge text="3.0.0" /> _visiting_ of parsed markdown | ||||||
| nodes does not require creating own instance of commonmark-java `Visitor`, | nodes does not require creating own instance of commonmark-java `Visitor`, | ||||||
| instead a composable/configurable `MarkwonVisitor` is used. | instead a composable/configurable `MarkwonVisitor` is used. | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # LaTeX extension | # LaTeX extension | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'ext-latex'" /> | <MavenBadge :artifact="'ext-latex'" /> | ||||||
| 
 | 
 | ||||||
| This is an extension that will help you display LaTeX formulas in your markdown. | This is an extension that will help you display LaTeX formulas in your markdown. | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Strikethrough extension | # Strikethrough extension | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'ext-strikethrough'" /> | <MavenBadge :artifact="'ext-strikethrough'" /> | ||||||
| 
 | 
 | ||||||
| This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`: | This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`: | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Tables extension | # Tables extension | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'ext-tables'" /> | <MavenBadge :artifact="'ext-tables'" /> | ||||||
| 
 | 
 | ||||||
| This extension adds support for GFM tables. | This extension adds support for GFM tables. | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Task list extension | # Task list extension | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'ext-tasklist'" /> | <MavenBadge :artifact="'ext-tasklist'" /> | ||||||
| 
 | 
 | ||||||
| Adds support for GFM (Github-flavored markdown) task-lists: | Adds support for GFM (Github-flavored markdown) task-lists: | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # HTML | # HTML | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| This artifact encapsulates HTML parsing from the core artifact and provides | This artifact encapsulates HTML parsing from the core artifact and provides | ||||||
| few predefined `TagHandlers` | few predefined `TagHandlers` | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Image GIF | # Image GIF | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'image-gif'" /> | <MavenBadge :artifact="'image-gif'" /> | ||||||
| 
 | 
 | ||||||
| Adds support for GIF images inside markdown.  | Adds support for GIF images inside markdown.  | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Image OkHttp | # Image OkHttp | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'image-okhttp'" /> | <MavenBadge :artifact="'image-okhttp'" /> | ||||||
| 
 | 
 | ||||||
| Uses [okhttp library](https://github.com/square/okhttp) as the network transport fro images. Since <Badge text="3.0.0" /> | Uses [okhttp library](https://github.com/square/okhttp) as the network transport fro images. Since <Badge text="3.0.0" /> | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Image SVG | # Image SVG | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'image-svg'" /> | <MavenBadge :artifact="'image-svg'" /> | ||||||
| 
 | 
 | ||||||
| Adds support for SVG images inside markdown.  | Adds support for SVG images inside markdown.  | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ prev: false | |||||||
| next: /docs/v3/core/getting-started.md | next: /docs/v3/core/getting-started.md | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| # Installation | # Installation | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Migration 2.x.x -> 3.x.x | # Migration 2.x.x -> 3.x.x | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| * strikethrough moved to standalone module | * strikethrough moved to standalone module | ||||||
| * tables moved to standalone module | * tables moved to standalone module | ||||||
| * core functionality of `AsyncDrawableLoader` moved to `core` module | * core functionality of `AsyncDrawableLoader` moved to `core` module | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Recycler Table <Badge text="3.0.0" /> | # Recycler Table <Badge text="3.0.0" /> | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'recycler-table'" /> | <MavenBadge :artifact="'recycler-table'" /> | ||||||
| 
 | 
 | ||||||
| Artifact that provides [MarkwonAdapter.Entry](/docs/v3/recycler/) to render `TableBlock` inside  | Artifact that provides [MarkwonAdapter.Entry](/docs/v3/recycler/) to render `TableBlock` inside  | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Recycler <Badge text="3.0.0" /> | # Recycler <Badge text="3.0.0" /> | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'recycler'" /> | <MavenBadge :artifact="'recycler'" /> | ||||||
| 
 | 
 | ||||||
| This artifact allows displaying markdown in a set of Android widgets | This artifact allows displaying markdown in a set of Android widgets | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| # Syntax highlight | # Syntax highlight | ||||||
| 
 | 
 | ||||||
|  | <LegacyWarning /> | ||||||
|  | 
 | ||||||
| <MavenBadge :artifact="'syntax-highlight'" /> | <MavenBadge :artifact="'syntax-highlight'" /> | ||||||
| 
 | 
 | ||||||
| This is a simple module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance. | This is a simple module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance. | ||||||
|  | |||||||
							
								
								
									
										40
									
								
								docs/docs/v4/core/text-setter.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								docs/docs/v4/core/text-setter.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | # TextSetter <Badge text="4.1.0" /> | ||||||
|  | 
 | ||||||
|  | Since <Badge text="4.1.0" /> it is possible to control how text is applied to a `TextView`. | ||||||
|  | This is done via `Markwon.TextSetter` interface. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(/**/) | ||||||
|  |         .textSetter(PrecomputedTextSetterCompat.create(Executors.newCachedThreadPool())) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | public interface TextSetter { | ||||||
|  |     /** | ||||||
|  |      * @param textView   TextView | ||||||
|  |      * @param markdown   prepared markdown | ||||||
|  |      * @param bufferType BufferType specified when building {@link Markwon} instance | ||||||
|  |      *                   via {@link Builder#bufferType(TextView.BufferType)} | ||||||
|  |      * @param onComplete action to run when set-text is finished (required to call in order | ||||||
|  |      *                   to execute {@link MarkwonPlugin#afterSetText(TextView)}) | ||||||
|  |      */ | ||||||
|  |     void setText( | ||||||
|  |             @NonNull TextView textView, | ||||||
|  |             @NonNull Spanned markdown, | ||||||
|  |             @NonNull TextView.BufferType bufferType, | ||||||
|  |             @NonNull Runnable onComplete); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Primary target for this functionality is to use [PrecomputedText] and [PrecomputedTextCompat]. | ||||||
|  | `Markwon` comes with `PrecomputedTextSetterCompat` implementation. | ||||||
|  | 
 | ||||||
|  | :::tip Note | ||||||
|  | Please note that `PrecomputedTextCompat` belongs to the `androidx.core:core` artifact. Make | ||||||
|  | sure that you have it in your project's dependencies (explicitly or implicitly) | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | [PrecomputedText]: https://developer.android.com/reference/android/text/PrecomputedText | ||||||
|  | [PrecomputedTextCompat]: https://developer.android.com/reference/androidx/core/text/PrecomputedTextCompat | ||||||
| @ -8,7 +8,7 @@ android.enableJetifier=true | |||||||
| android.enableBuildCache=true | android.enableBuildCache=true | ||||||
| android.buildCacheDir=build/pre-dex-cache | android.buildCacheDir=build/pre-dex-cache | ||||||
| 
 | 
 | ||||||
| VERSION_NAME=4.1.0 | VERSION_NAME=4.1.1 | ||||||
| 
 | 
 | ||||||
| GROUP=io.noties.markwon | GROUP=io.noties.markwon | ||||||
| POM_DESCRIPTION=Markwon markdown for Android | POM_DESCRIPTION=Markwon markdown for Android | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,5 @@ | |||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
|  | |||||||
| @ -141,21 +141,21 @@ public abstract class Markwon { | |||||||
|      * @see PrecomputedTextSetterCompat |      * @see PrecomputedTextSetterCompat | ||||||
|      * @since 4.1.0 |      * @since 4.1.0 | ||||||
|      */ |      */ | ||||||
|     public interface TextSetter { | public interface TextSetter { | ||||||
|         /** |     /** | ||||||
|          * @param textView   TextView |      * @param textView   TextView | ||||||
|          * @param markdown   prepared markdown |      * @param markdown   prepared markdown | ||||||
|          * @param bufferType BufferType specified when building {@link Markwon} instance |      * @param bufferType BufferType specified when building {@link Markwon} instance | ||||||
|          *                   via {@link Builder#bufferType(TextView.BufferType)} |      *                   via {@link Builder#bufferType(TextView.BufferType)} | ||||||
|          * @param onComplete action to run when set-text is finished (required to call in order |      * @param onComplete action to run when set-text is finished (required to call in order | ||||||
|          *                   to execute {@link MarkwonPlugin#afterSetText(TextView)}) |      *                   to execute {@link MarkwonPlugin#afterSetText(TextView)}) | ||||||
|          */ |      */ | ||||||
|         void setText( |     void setText( | ||||||
|                 @NonNull TextView textView, |             @NonNull TextView textView, | ||||||
|                 @NonNull Spanned markdown, |             @NonNull Spanned markdown, | ||||||
|                 @NonNull TextView.BufferType bufferType, |             @NonNull TextView.BufferType bufferType, | ||||||
|                 @NonNull Runnable onComplete); |             @NonNull Runnable onComplete); | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Builder for {@link Markwon}. |      * Builder for {@link Markwon}. | ||||||
|  | |||||||
| @ -104,11 +104,17 @@ class MarkwonBuilderImpl implements Markwon.Builder { | |||||||
| 
 | 
 | ||||||
|         final RenderProps renderProps = new RenderPropsImpl(); |         final RenderProps renderProps = new RenderPropsImpl(); | ||||||
| 
 | 
 | ||||||
|  |         // @since 4.1.1 | ||||||
|  |         final MarkwonVisitorFactory visitorFactory = MarkwonVisitorFactory.create( | ||||||
|  |                 visitorBuilder, | ||||||
|  |                 configuration, | ||||||
|  |                 renderProps); | ||||||
|  | 
 | ||||||
|         return new MarkwonImpl( |         return new MarkwonImpl( | ||||||
|                 bufferType, |                 bufferType, | ||||||
|                 textSetter, |                 textSetter, | ||||||
|                 parserBuilder.build(), |                 parserBuilder.build(), | ||||||
|                 visitorBuilder.build(configuration, renderProps), |                 visitorFactory, | ||||||
|                 Collections.unmodifiableList(plugins) |                 Collections.unmodifiableList(plugins) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ class MarkwonImpl extends Markwon { | |||||||
| 
 | 
 | ||||||
|     private final TextView.BufferType bufferType; |     private final TextView.BufferType bufferType; | ||||||
|     private final Parser parser; |     private final Parser parser; | ||||||
|     private final MarkwonVisitor visitor; |     private final MarkwonVisitorFactory visitorFactory; // @since 4.1.1 | ||||||
|     private final List<MarkwonPlugin> plugins; |     private final List<MarkwonPlugin> plugins; | ||||||
| 
 | 
 | ||||||
|     // @since 4.1.0 |     // @since 4.1.0 | ||||||
| @ -31,12 +31,12 @@ class MarkwonImpl extends Markwon { | |||||||
|             @NonNull TextView.BufferType bufferType, |             @NonNull TextView.BufferType bufferType, | ||||||
|             @Nullable TextSetter textSetter, |             @Nullable TextSetter textSetter, | ||||||
|             @NonNull Parser parser, |             @NonNull Parser parser, | ||||||
|             @NonNull MarkwonVisitor visitor, |             @NonNull MarkwonVisitorFactory visitorFactory, | ||||||
|             @NonNull List<MarkwonPlugin> plugins) { |             @NonNull List<MarkwonPlugin> plugins) { | ||||||
|         this.bufferType = bufferType; |         this.bufferType = bufferType; | ||||||
|         this.textSetter = textSetter; |         this.textSetter = textSetter; | ||||||
|         this.parser = parser; |         this.parser = parser; | ||||||
|         this.visitor = visitor; |         this.visitorFactory = visitorFactory; | ||||||
|         this.plugins = plugins; |         this.plugins = plugins; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -60,16 +60,22 @@ class MarkwonImpl extends Markwon { | |||||||
|             plugin.beforeRender(node); |             plugin.beforeRender(node); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // @since 4.1.1 obtain visitor via factory | ||||||
|  |         final MarkwonVisitor visitor = visitorFactory.create(); | ||||||
|  | 
 | ||||||
|         node.accept(visitor); |         node.accept(visitor); | ||||||
| 
 | 
 | ||||||
|         for (MarkwonPlugin plugin : plugins) { |         for (MarkwonPlugin plugin : plugins) { | ||||||
|             plugin.afterRender(node, visitor); |             plugin.afterRender(node, visitor); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         //noinspection UnnecessaryLocalVariable | ||||||
|         final Spanned spanned = visitor.builder().spannableStringBuilder(); |         final Spanned spanned = visitor.builder().spannableStringBuilder(); | ||||||
| 
 | 
 | ||||||
|         // clear render props and builder after rendering |         // clear render props and builder after rendering | ||||||
|         visitor.clear(); |         // @since 4.1.1 as we no longer reuse visitor - there is no need to clean it | ||||||
|  |         //  we might still do it if we introduce a thread-local storage though | ||||||
|  | //        visitor.clear(); | ||||||
| 
 | 
 | ||||||
|         return spanned; |         return spanned; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -0,0 +1,26 @@ | |||||||
|  | package io.noties.markwon; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @since 4.1.1 | ||||||
|  |  */ | ||||||
|  | abstract class MarkwonVisitorFactory { | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     abstract MarkwonVisitor create(); | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     static MarkwonVisitorFactory create( | ||||||
|  |             @NonNull final MarkwonVisitorImpl.Builder builder, | ||||||
|  |             @NonNull final MarkwonConfiguration configuration, | ||||||
|  |             @NonNull final RenderProps renderProps) { | ||||||
|  |         return new MarkwonVisitorFactory() { | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             MarkwonVisitor create() { | ||||||
|  |                 return builder.build(configuration, renderProps); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -269,6 +269,11 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | |||||||
|         @NonNull |         @NonNull | ||||||
|         @Override |         @Override | ||||||
|         public <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor) { |         public <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor) { | ||||||
|  | 
 | ||||||
|  |             // @since 4.1.1 we might actually introduce a local flag to check if it's been built | ||||||
|  |             //  and throw an exception here if some modification is requested | ||||||
|  |             //  NB, as we might be built from different threads this flag must be synchronized | ||||||
|  | 
 | ||||||
|             // we should allow `null` to exclude node from being visited (for example to disable |             // we should allow `null` to exclude node from being visited (for example to disable | ||||||
|             // some functionality) |             // some functionality) | ||||||
|             if (nodeVisitor == null) { |             if (nodeVisitor == null) { | ||||||
|  | |||||||
| @ -328,6 +328,9 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|         visitor.builder().append('\u00a0'); |         visitor.builder().append('\u00a0'); | ||||||
| 
 | 
 | ||||||
|  |         // @since 4.1.1 | ||||||
|  |         CoreProps.CODE_BLOCK_INFO.set(visitor.renderProps(), info); | ||||||
|  | 
 | ||||||
|         visitor.setSpansForNodeOptional(node, length); |         visitor.setSpansForNodeOptional(node, length); | ||||||
| 
 | 
 | ||||||
|         if (visitor.hasNext(node)) { |         if (visitor.hasNext(node)) { | ||||||
|  | |||||||
| @ -19,6 +19,11 @@ public abstract class CoreProps { | |||||||
| 
 | 
 | ||||||
|     public static final Prop<Boolean> PARAGRAPH_IS_IN_TIGHT_LIST = Prop.of("paragraph-is-in-tight-list"); |     public static final Prop<Boolean> PARAGRAPH_IS_IN_TIGHT_LIST = Prop.of("paragraph-is-in-tight-list"); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 4.1.1 | ||||||
|  |      */ | ||||||
|  |     public static final Prop<String> CODE_BLOCK_INFO = Prop.of("code-block-info"); | ||||||
|  | 
 | ||||||
|     public enum ListItemType { |     public enum ListItemType { | ||||||
|         BULLET, |         BULLET, | ||||||
|         ORDERED |         ORDERED | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.eq; | |||||||
| import static org.mockito.Mockito.RETURNS_MOCKS; | import static org.mockito.Mockito.RETURNS_MOCKS; | ||||||
| import static org.mockito.Mockito.doAnswer; | import static org.mockito.Mockito.doAnswer; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
|  | import static org.mockito.Mockito.never; | ||||||
| import static org.mockito.Mockito.times; | import static org.mockito.Mockito.times; | ||||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||||
| import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||||
| @ -47,7 +48,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class), |                 mock(MarkwonVisitorFactory.class), | ||||||
|                 Collections.singletonList(plugin)); |                 Collections.singletonList(plugin)); | ||||||
| 
 | 
 | ||||||
|         impl.parse("whatever"); |         impl.parse("whatever"); | ||||||
| @ -70,7 +71,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 parser, |                 parser, | ||||||
|                 mock(MarkwonVisitor.class), |                 mock(MarkwonVisitorFactory.class), | ||||||
|                 Arrays.asList(first, second)); |                 Arrays.asList(first, second)); | ||||||
| 
 | 
 | ||||||
|         impl.parse("zero"); |         impl.parse("zero"); | ||||||
| @ -89,6 +90,7 @@ public class MarkwonImplTest { | |||||||
| 
 | 
 | ||||||
|         final MarkwonPlugin plugin = mock(MarkwonPlugin.class); |         final MarkwonPlugin plugin = mock(MarkwonPlugin.class); | ||||||
| 
 | 
 | ||||||
|  |         final MarkwonVisitorFactory visitorFactory = mock(MarkwonVisitorFactory.class); | ||||||
|         final MarkwonVisitor visitor = mock(MarkwonVisitor.class); |         final MarkwonVisitor visitor = mock(MarkwonVisitor.class); | ||||||
|         final SpannableBuilder builder = mock(SpannableBuilder.class); |         final SpannableBuilder builder = mock(SpannableBuilder.class); | ||||||
| 
 | 
 | ||||||
| @ -96,9 +98,10 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 visitor, |                 visitorFactory, | ||||||
|                 Collections.singletonList(plugin)); |                 Collections.singletonList(plugin)); | ||||||
| 
 | 
 | ||||||
|  |         when(visitorFactory.create()).thenReturn(visitor); | ||||||
|         when(visitor.builder()).thenReturn(builder); |         when(visitor.builder()).thenReturn(builder); | ||||||
| 
 | 
 | ||||||
|         final Node node = mock(Node.class); |         final Node node = mock(Node.class); | ||||||
| @ -132,24 +135,30 @@ public class MarkwonImplTest { | |||||||
|     public void render_clears_visitor() { |     public void render_clears_visitor() { | ||||||
|         // each render call should have empty-state visitor (no previous rendering info) |         // each render call should have empty-state visitor (no previous rendering info) | ||||||
| 
 | 
 | ||||||
|  |         final MarkwonVisitorFactory visitorFactory = mock(MarkwonVisitorFactory.class); | ||||||
|         final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); |         final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); | ||||||
| 
 | 
 | ||||||
|  |         when(visitorFactory.create()).thenReturn(visitor); | ||||||
|  | 
 | ||||||
|         final MarkwonImpl impl = new MarkwonImpl( |         final MarkwonImpl impl = new MarkwonImpl( | ||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 visitor, |                 visitorFactory, | ||||||
|                 Collections.<MarkwonPlugin>emptyList()); |                 Collections.<MarkwonPlugin>emptyList()); | ||||||
| 
 | 
 | ||||||
|         impl.render(mock(Node.class)); |         impl.render(mock(Node.class)); | ||||||
| 
 | 
 | ||||||
|         verify(visitor, times(1)).clear(); |         // obsolete starting with 4.1.1 | ||||||
|  | //        verify(visitor, times(1)).clear(); | ||||||
|  |         verify(visitor, never()).clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void render_props() { |     public void render_props() { | ||||||
|         // render props are configured properly and cleared after render function |         // render props are configured properly and cleared after render function | ||||||
| 
 | 
 | ||||||
|  |         final MarkwonVisitorFactory visitorFactory = mock(MarkwonVisitorFactory.class); | ||||||
|         final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); |         final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); | ||||||
| 
 | 
 | ||||||
|         final RenderProps renderProps = mock(RenderProps.class); |         final RenderProps renderProps = mock(RenderProps.class); | ||||||
| @ -161,6 +170,7 @@ public class MarkwonImplTest { | |||||||
|             } |             } | ||||||
|         }).when(visitor).clear(); |         }).when(visitor).clear(); | ||||||
| 
 | 
 | ||||||
|  |         when(visitorFactory.create()).thenReturn(visitor); | ||||||
|         when(visitor.renderProps()).thenReturn(renderProps); |         when(visitor.renderProps()).thenReturn(renderProps); | ||||||
| 
 | 
 | ||||||
|         final MarkwonPlugin plugin = mock(MarkwonPlugin.class); |         final MarkwonPlugin plugin = mock(MarkwonPlugin.class); | ||||||
| @ -169,7 +179,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 visitor, |                 visitorFactory, | ||||||
|                 Collections.singletonList(plugin)); |                 Collections.singletonList(plugin)); | ||||||
| 
 | 
 | ||||||
|         final AtomicBoolean flag = new AtomicBoolean(false); |         final AtomicBoolean flag = new AtomicBoolean(false); | ||||||
| @ -191,7 +201,9 @@ public class MarkwonImplTest { | |||||||
| 
 | 
 | ||||||
|         assertTrue(flag.get()); |         assertTrue(flag.get()); | ||||||
| 
 | 
 | ||||||
|         verify(renderProps, times(1)).clearAll(); |         // obsolete starting with 4.1.1 | ||||||
|  | //        verify(renderProps, times(1)).clearAll(); | ||||||
|  |         verify(renderProps, never()).clearAll(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
| @ -205,7 +217,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.EDITABLE, |                 TextView.BufferType.EDITABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class, RETURNS_MOCKS), |                 mock(MarkwonVisitorFactory.class, RETURNS_MOCKS), | ||||||
|                 Collections.singletonList(plugin)); |                 Collections.singletonList(plugin)); | ||||||
| 
 | 
 | ||||||
|         final TextView textView = mock(TextView.class); |         final TextView textView = mock(TextView.class); | ||||||
| @ -252,7 +264,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class), |                 mock(MarkwonVisitorFactory.class), | ||||||
|                 plugins); |                 plugins); | ||||||
| 
 | 
 | ||||||
|         assertTrue("First", impl.hasPlugin(First.class)); |         assertTrue("First", impl.hasPlugin(First.class)); | ||||||
| @ -274,7 +286,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.EDITABLE, |                 TextView.BufferType.EDITABLE, | ||||||
|                 textSetter, |                 textSetter, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class), |                 mock(MarkwonVisitorFactory.class), | ||||||
|                 Collections.singletonList(plugin)); |                 Collections.singletonList(plugin)); | ||||||
| 
 | 
 | ||||||
|         final TextView textView = mock(TextView.class); |         final TextView textView = mock(TextView.class); | ||||||
| @ -317,7 +329,8 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class), plugins); |                 mock(MarkwonVisitorFactory.class), | ||||||
|  |                 plugins); | ||||||
| 
 | 
 | ||||||
|         // should be returned |         // should be returned | ||||||
|         assertNotNull(impl.requirePlugin(MarkwonPlugin.class)); |         assertNotNull(impl.requirePlugin(MarkwonPlugin.class)); | ||||||
| @ -346,7 +359,7 @@ public class MarkwonImplTest { | |||||||
|                 TextView.BufferType.SPANNABLE, |                 TextView.BufferType.SPANNABLE, | ||||||
|                 null, |                 null, | ||||||
|                 mock(Parser.class), |                 mock(Parser.class), | ||||||
|                 mock(MarkwonVisitor.class), |                 mock(MarkwonVisitorFactory.class), | ||||||
|                 plugins); |                 plugins); | ||||||
| 
 | 
 | ||||||
|         final List<? extends MarkwonPlugin> list = impl.getPlugins(); |         final List<? extends MarkwonPlugin> list = impl.getPlugins(); | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| package io.noties.markwon.core; | package io.noties.markwon.core; | ||||||
| 
 | 
 | ||||||
|  | import android.text.method.MovementMethod; | ||||||
|  | import android.widget.TextView; | ||||||
|  | 
 | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| import android.text.method.MovementMethod; |  | ||||||
| import android.widget.ImageView; |  | ||||||
| import android.widget.TextView; |  | ||||||
| 
 | 
 | ||||||
| import org.commonmark.node.BlockQuote; | import org.commonmark.node.BlockQuote; | ||||||
| import org.commonmark.node.BulletList; | import org.commonmark.node.BulletList; | ||||||
| @ -38,15 +38,15 @@ import java.util.List; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| 
 | 
 | ||||||
| import ix.Ix; |  | ||||||
| import ix.IxFunction; |  | ||||||
| import ix.IxPredicate; |  | ||||||
| import io.noties.markwon.MarkwonConfiguration; | import io.noties.markwon.MarkwonConfiguration; | ||||||
| import io.noties.markwon.MarkwonSpansFactory; | import io.noties.markwon.MarkwonSpansFactory; | ||||||
| import io.noties.markwon.MarkwonVisitor; | import io.noties.markwon.MarkwonVisitor; | ||||||
| import io.noties.markwon.RenderProps; | import io.noties.markwon.RenderProps; | ||||||
| import io.noties.markwon.SpanFactory; | import io.noties.markwon.SpanFactory; | ||||||
| import io.noties.markwon.SpannableBuilder; | import io.noties.markwon.SpannableBuilder; | ||||||
|  | import ix.Ix; | ||||||
|  | import ix.IxFunction; | ||||||
|  | import ix.IxPredicate; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||||
| import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||||
| @ -54,6 +54,7 @@ import static org.junit.Assert.assertNull; | |||||||
| import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||||
| import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||||
| import static org.mockito.ArgumentMatchers.eq; | import static org.mockito.ArgumentMatchers.eq; | ||||||
|  | import static org.mockito.Mockito.RETURNS_MOCKS; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
| import static org.mockito.Mockito.times; | import static org.mockito.Mockito.times; | ||||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||||
| @ -300,4 +301,45 @@ public class CorePluginTest { | |||||||
| 
 | 
 | ||||||
|         verify(textView, times(0)).setMovementMethod(any(MovementMethod.class)); |         verify(textView, times(0)).setMovementMethod(any(MovementMethod.class)); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void code_block_info_prop() { | ||||||
|  |         final CorePlugin plugin = CorePlugin.create(); | ||||||
|  |         final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); | ||||||
|  |         plugin.configureVisitor(builder); | ||||||
|  | 
 | ||||||
|  |         final ArgumentCaptor<MarkwonVisitor.NodeVisitor> fencedCaptor = | ||||||
|  |                 ArgumentCaptor.forClass(MarkwonVisitor.NodeVisitor.class); | ||||||
|  |         final ArgumentCaptor<MarkwonVisitor.NodeVisitor> indendedCaptor = | ||||||
|  |                 ArgumentCaptor.forClass(MarkwonVisitor.NodeVisitor.class); | ||||||
|  | 
 | ||||||
|  |         //noinspection unchecked | ||||||
|  |         verify(builder, times(1)).on(eq(FencedCodeBlock.class), fencedCaptor.capture()); | ||||||
|  |         //noinspection unchecked | ||||||
|  |         verify(builder, times(1)).on(eq(IndentedCodeBlock.class), indendedCaptor.capture()); | ||||||
|  | 
 | ||||||
|  |         final RenderProps renderProps = mock(RenderProps.class); | ||||||
|  |         final MarkwonVisitor visitor = mock(MarkwonVisitor.class, RETURNS_MOCKS); | ||||||
|  | 
 | ||||||
|  |         when(visitor.renderProps()).thenReturn(renderProps); | ||||||
|  | 
 | ||||||
|  |         // fenced | ||||||
|  |         { | ||||||
|  |             final FencedCodeBlock block = new FencedCodeBlock(); | ||||||
|  |             block.setInfo("testing-fenced"); | ||||||
|  |             //noinspection unchecked | ||||||
|  |             fencedCaptor.getValue().visit(visitor, block); | ||||||
|  | 
 | ||||||
|  |             verify(renderProps, times(1)).set(eq(CoreProps.CODE_BLOCK_INFO), eq("testing-fenced")); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // indended | ||||||
|  |         { | ||||||
|  |             final IndentedCodeBlock block = new IndentedCodeBlock(); | ||||||
|  |             //noinspection unchecked | ||||||
|  |             indendedCaptor.getValue().visit(visitor, block); | ||||||
|  | 
 | ||||||
|  |             verify(renderProps, times(1)).set(eq(CoreProps.CODE_BLOCK_INFO), eq((String) null)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -6,6 +6,7 @@ import android.widget.TextView; | |||||||
| 
 | 
 | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| 
 | 
 | ||||||
|  | import org.commonmark.ext.gfm.tables.TableBlock; | ||||||
| import org.commonmark.ext.gfm.tables.TableBody; | import org.commonmark.ext.gfm.tables.TableBody; | ||||||
| import org.commonmark.ext.gfm.tables.TableCell; | import org.commonmark.ext.gfm.tables.TableCell; | ||||||
| import org.commonmark.ext.gfm.tables.TableHead; | import org.commonmark.ext.gfm.tables.TableHead; | ||||||
| @ -115,19 +116,26 @@ public class TablePlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|         void configure(@NonNull MarkwonVisitor.Builder builder) { |         void configure(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|             builder |             builder | ||||||
|                     .on(TableBody.class, new MarkwonVisitor.NodeVisitor<TableBody>() { |                     // @since 4.1.1 we use TableBlock instead of TableBody to add new lines | ||||||
|  |                     .on(TableBlock.class, new MarkwonVisitor.NodeVisitor<TableBlock>() { | ||||||
|                         @Override |                         @Override | ||||||
|                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBody tableBody) { |                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBlock tableBlock) { | ||||||
| 
 | 
 | ||||||
|                             visitor.visitChildren(tableBody); |                             visitor.visitChildren(tableBlock); | ||||||
|                             tableRows = 0; |  | ||||||
| 
 | 
 | ||||||
|                             if (visitor.hasNext(tableBody)) { |                             if (visitor.hasNext(tableBlock)) { | ||||||
|                                 visitor.ensureNewLine(); |                                 visitor.ensureNewLine(); | ||||||
|                                 visitor.forceNewLine(); |                                 visitor.forceNewLine(); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     }) |                     }) | ||||||
|  |                     .on(TableBody.class, new MarkwonVisitor.NodeVisitor<TableBody>() { | ||||||
|  |                         @Override | ||||||
|  |                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBody tableBody) { | ||||||
|  |                             visitor.visitChildren(tableBody); | ||||||
|  |                             tableRows = 0; | ||||||
|  |                         } | ||||||
|  |                     }) | ||||||
|                     .on(TableRow.class, new MarkwonVisitor.NodeVisitor<TableRow>() { |                     .on(TableRow.class, new MarkwonVisitor.NodeVisitor<TableRow>() { | ||||||
|                         @Override |                         @Override | ||||||
|                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableRow tableRow) { |                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableRow tableRow) { | ||||||
|  | |||||||
| @ -11,7 +11,6 @@ import java.io.InputStream; | |||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| 
 | 
 | ||||||
| import io.noties.markwon.image.DrawableUtils; |  | ||||||
| import io.noties.markwon.image.MediaDecoder; | import io.noties.markwon.image.MediaDecoder; | ||||||
| import pl.droidsonroids.gif.GifDrawable; | import pl.droidsonroids.gif.GifDrawable; | ||||||
| 
 | 
 | ||||||
| @ -97,9 +96,7 @@ public class GifMediaDecoder extends MediaDecoder { | |||||||
| 
 | 
 | ||||||
|     private static void validate() { |     private static void validate() { | ||||||
|         if (!GifSupport.hasGifSupport()) { |         if (!GifSupport.hasGifSupport()) { | ||||||
|             throw new IllegalStateException("`pl.droidsonroids.gif:android-gif-drawable:*` " + |             throw new IllegalStateException(GifSupport.missingMessage()); | ||||||
|                     "dependency is missing, please add to your project explicitly if you " + |  | ||||||
|                     "wish to use GIF media decoder"); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,9 @@ | |||||||
| package io.noties.markwon.image.gif; | package io.noties.markwon.image.gif; | ||||||
| 
 | 
 | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @since 4.0.0 |  * @since 4.0.0 | ||||||
|  */ |  */ | ||||||
| @ -13,7 +17,9 @@ public abstract class GifSupport { | |||||||
|             pl.droidsonroids.gif.GifDrawable.class.getName(); |             pl.droidsonroids.gif.GifDrawable.class.getName(); | ||||||
|             result = true; |             result = true; | ||||||
|         } catch (Throwable t) { |         } catch (Throwable t) { | ||||||
|             t.printStackTrace(); |             // @since 4.1.1 instead of printing full stacktrace of the exception, | ||||||
|  |             // just print a warning to the console | ||||||
|  |             Log.w("MarkwonImagesPlugin", missingMessage()); | ||||||
|             result = false; |             result = false; | ||||||
|         } |         } | ||||||
|         HAS_GIF = result; |         HAS_GIF = result; | ||||||
| @ -23,6 +29,16 @@ public abstract class GifSupport { | |||||||
|         return HAS_GIF; |         return HAS_GIF; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 4.1.1 | ||||||
|  |      */ | ||||||
|  |     @NonNull | ||||||
|  |     static String missingMessage() { | ||||||
|  |         return "`pl.droidsonroids.gif:android-gif-drawable:*` " + | ||||||
|  |                 "dependency is missing, please add to your project explicitly if you " + | ||||||
|  |                 "wish to use GIF media-decoder"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private GifSupport() { |     private GifSupport() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -83,8 +83,7 @@ public class SvgMediaDecoder extends MediaDecoder { | |||||||
| 
 | 
 | ||||||
|     private static void validate() { |     private static void validate() { | ||||||
|         if (!SvgSupport.hasSvgSupport()) { |         if (!SvgSupport.hasSvgSupport()) { | ||||||
|             throw new IllegalStateException("`com.caverock:androidsvg:*` dependency is missing, " + |             throw new IllegalStateException(SvgSupport.missingMessage()); | ||||||
|                     "please add to your project explicitly if you wish to use SVG media decoder"); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,9 @@ | |||||||
| package io.noties.markwon.image.svg; | package io.noties.markwon.image.svg; | ||||||
| 
 | 
 | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @since 4.0.0 |  * @since 4.0.0 | ||||||
|  */ |  */ | ||||||
| @ -13,7 +17,9 @@ public abstract class SvgSupport { | |||||||
|             com.caverock.androidsvg.SVG.class.getName(); |             com.caverock.androidsvg.SVG.class.getName(); | ||||||
|             result = true; |             result = true; | ||||||
|         } catch (Throwable t) { |         } catch (Throwable t) { | ||||||
|             t.printStackTrace(); |             // @since 4.1.1 instead of printing full stacktrace of the exception, | ||||||
|  |             // just print a warning to the console | ||||||
|  |             Log.w("MarkwonImagesPlugin", missingMessage()); | ||||||
|             result = false; |             result = false; | ||||||
|         } |         } | ||||||
|         HAS_SVG = result; |         HAS_SVG = result; | ||||||
| @ -23,6 +29,15 @@ public abstract class SvgSupport { | |||||||
|         return HAS_SVG; |         return HAS_SVG; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 4.1.1 | ||||||
|  |      */ | ||||||
|  |     @NonNull | ||||||
|  |     static String missingMessage() { | ||||||
|  |         return "`com.caverock:androidsvg:*` dependency is missing, " + | ||||||
|  |                 "please add to your project explicitly if you wish to use SVG media-decoder"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private SvgSupport() { |     private SvgSupport() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry
						Dimitry