Browse Source

Merge branch 'master' into i/7956-reconversion-trigger-by

Maciej Gołaszewski 5 years ago
parent
commit
1013f0ee9e
28 changed files with 499 additions and 336 deletions
  1. 33 0
      .github/workflows/ckeditor5-integrations.yml
  2. 21 21
      CHANGELOG.md
  3. 22 13
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-custom-element-converter.js
  4. 3 1
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-extending-output.md
  5. 1 0
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-introduction.md
  6. 2 0
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-preserving-custom-content.md
  7. 49 44
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/custom-element-conversion.md
  8. 8 8
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/schema.md
  9. 6 0
      packages/ckeditor5-engine/src/conversion/downcastdispatcher.js
  10. 3 0
      packages/ckeditor5-engine/src/conversion/downcasthelpers.js
  11. 111 108
      packages/ckeditor5-engine/src/conversion/upcastdispatcher.js
  12. 1 1
      packages/ckeditor5-engine/src/model/schema.js
  13. 3 4
      packages/ckeditor5-engine/src/model/utils/insertcontent.js
  14. 29 31
      packages/ckeditor5-engine/src/view/downcastwriter.js
  15. 0 1
      packages/ckeditor5-image/src/image/imageediting.js
  16. 1 1
      packages/ckeditor5-image/src/imageupload/ui/imageuploadpanelview.js
  17. 10 12
      packages/ckeditor5-list/docs/_snippets/features/lists-basic.html
  18. 10 9
      packages/ckeditor5-list/docs/_snippets/features/lists-style.html
  19. 3 3
      packages/ckeditor5-list/docs/api/list.md
  20. 17 9
      packages/ckeditor5-list/docs/features/lists.md
  21. 2 2
      packages/ckeditor5-list/docs/features/todo-lists.md
  22. 48 41
      packages/ckeditor5-list/src/liststyleediting.js
  23. 92 4
      packages/ckeditor5-list/tests/liststyleediting.js
  24. 1 1
      packages/ckeditor5-table/src/converters/upcasttable.js
  25. 1 1
      packages/ckeditor5-table/src/tablewalker.js
  26. 14 14
      packages/ckeditor5-utils/src/collection.js
  27. 6 6
      scripts/docs/build-docs.js
  28. 2 1
      scripts/docs/buildapi.js

+ 33 - 0
.github/workflows/ckeditor5-integrations.yml

@@ -0,0 +1,33 @@
+# This script triggers Travis that verifies whether projects that depend on CKEditor 5 build correctly.
+#
+# In order to integrate the action in a new repository, you need add a few secrets in the new repository.
+#   - INTEGRATION_CI_ORGANIZATION - a name of the organization that keeps the repository where the build should be triggered
+#   - INTEGRATION_CI_REPOSITORY - a name of the repository where the build should be triggered
+#   - INTEGRATION_CI_TRAVIS_TOKEN - an authorization token generated by Travis CLI: `travis --pro token`
+name: CKEditor 5 Integrations
+
+on:
+  push:
+    branches: [ master ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Trigger CI
+        env:
+          WORKFLOW_TRIGGER_REPOSITORY: ${{ github.repository }}
+          WORKFLOW_TRIGGER_COMMIT: ${{ github.sha }}
+          WORKFLOW_ORGANIZATION: ${{ secrets.INTEGRATION_CI_ORGANIZATION }}
+          WORKFLOW_REPOSITORY: ${{ secrets.INTEGRATION_CI_REPOSITORY }}
+          WORKFLOW_TRAVIS_TOKEN: ${{ secrets.INTEGRATION_CI_TRAVIS_TOKEN }}
+        run: |
+          export BUILD_MESSAGE="Repository: $WORKFLOW_TRIGGER_REPOSITORY\n\nCommit: https://github.com/$WORKFLOW_TRIGGER_REPOSITORY/commit/$WORKFLOW_TRIGGER_COMMIT."
+          export REQUEST_BODY="{\"request\": { \"branch\": \"master\", \"message\": \"$BUILD_MESSAGE\" } }"
+          curl -s -X POST \
+            -H "Content-Type: application/json" \
+            -H "Accept: application/json" \
+            -H "Travis-API-Version: 3" \
+            -H "Authorization: token $WORKFLOW_TRAVIS_TOKEN" \
+            -d "$REQUEST_BODY" \
+            "https://api.travis-ci.com/repo/$WORKFLOW_ORGANIZATION%2F$WORKFLOW_REPOSITORY/requests"

+ 21 - 21
CHANGELOG.md

@@ -9,24 +9,24 @@ We are happy to announce the release of CKEditor 5 v22.0.0.
 
 This release brings a few new features:
 
-* [List styles plugin](https://github.com/ckeditor/ckeditor5/issues/7801).
-* [Makrdown plugin](https://github.com/ckeditor/ckeditor5/issues/6007).
+* The [list style plugin](https://github.com/ckeditor/ckeditor5/issues/7801).
+* The [Markdown plugin](https://github.com/ckeditor/ckeditor5/issues/6007).
 * [Inserting image with URL](https://github.com/ckeditor/ckeditor5/issues/7794).
-* [New event-based conversion api](https://github.com/ckeditor/ckeditor5/issues/7336).
+* [A new event-based conversion API](https://github.com/ckeditor/ckeditor5/issues/7336).
 
 Please note that there are some **major breaking changes**. Be sure to review them before upgrading.
 
-<!-- TODO: Add a link to the blog post. -->
+Read more in the blog post: https://ckeditor.com/blog/ckeditor-5-v22.0.0-with-inserting-images-via-url-list-styles-and-markdown-plugin/
 
 ### Collaboration features
 
-The CKEditor 5 Collaboration features changelog can be found here: https://ckeditor.com/collaboration/changelog.
+The CKEditor 5 Collaboration Features changelog can be found here: https://ckeditor.com/collaboration/changelog.
 
 ### MAJOR BREAKING CHANGES [ℹ️](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes)
 
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The `config.view` callback of `DowncastHelpers` takes a `DowncastConversionApi` instance instead of a `DowncastWriter`. An example migration snippet can be found in a [GitHub comment](https://github.com/ckeditor/ckeditor5/issues/7334#issuecomment-670450941). See [#7334](https://github.com/ckeditor/ckeditor5/issues/7334).
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The `config.model` callback of `UpcastHelpers` takes a `UpcastConversionApi` instance instead of a `ModelWriter`. An example migration snippet can be found in a [GitHub comment](https://github.com/ckeditor/ckeditor5/issues/7334#issuecomment-670450941). See [#7334](https://github.com/ckeditor/ckeditor5/issues/7334).
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The `config.view` parameter for upcast element-to-element conversion helpers configurations is now mandatory. You can retain previous "catch-all" behavior for upcast converter using `config.view = /[\s\S]+/` value.
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The `view` and `model` callbacks of all one-way converter helpers (such as `editor.conversion.for( 'upcast' ).elementToElement()`, `editor.conversion.for( 'downcast' ).attributeToElement()`) now take the `conversionApi` as their second parameter. Previously, the second parameter was the downcast or upcast writer instance. Now, the writer needs to be retrieved from `conversionApi.writer`.<br><br>
+An example migration snippet can be found in a [GitHub comment](https://github.com/ckeditor/ckeditor5/issues/7334#issuecomment-670450941).
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The `config.view` parameter for upcast element-to-element conversion helper configurations is now mandatory. You can retain the previous "catch-all" behavior for the upcast converter using the `config.view = /[\s\S]+/` value.
 
 ### MINOR BREAKING CHANGES [ℹ️](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html#major-and-minor-breaking-changes)
 
@@ -36,35 +36,35 @@ The CKEditor 5 Collaboration features changelog can be found here: https://ckedi
 ### Features
 
 * **[clipboard](https://www.npmjs.com/package/@ckeditor/ckeditor5-clipboard)**: Pasting a plain text will inherit selection attributes. Closes [#1006](https://github.com/ckeditor/ckeditor5/issues/1006). ([commit](https://github.com/ckeditor/ckeditor5/commit/2a163e389a6b22b1e5590fe6a2ed8204387d4350))
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Options passed to `Editor#getData()` and `DataController#get()` are now available in downcast conversion under `conversionApi.options` object. Closes [#7628](https://github.com/ckeditor/ckeditor5/issues/7628). ([commit](https://github.com/ckeditor/ckeditor5/commit/0a5d07e3c9a5cef51ebfb4a5819b5118ad9ae115))
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Added conversion API to upcast and downcast helpers. Closes [#7334](https://github.com/ckeditor/ckeditor5/issues/7334). ([commit](https://github.com/ckeditor/ckeditor5/commit/16c971198971b770d4e7aff4ea8eec7a88a6fcdb))
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Introduced `SchemaItemDefinition#isSelectable` and `SchemaItemDefinition#isContent` properties. Closes [#6432](https://github.com/ckeditor/ckeditor5/issues/6432). ([commit](https://github.com/ckeditor/ckeditor5/commit/579c1c851ca33c78de60c98777684f8ee5ceb26e))
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Introduced new upcast `ConversionApi` helper methods - `conversionApi.safeInsert()` and `conversionApi.updateConversionResult()`. New methods are intended to simplify writing event based element-to-element converters. Closes [#7336](https://github.com/ckeditor/ckeditor5/issues/7336). ([commit](https://github.com/ckeditor/ckeditor5/commit/8d84af1610089ea7916401ecf6f636c9d330b459))
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Options passed to `Editor#getData()` and `DataController#get()` are now available in downcast conversion under the `conversionApi.options` object. Closes [#7628](https://github.com/ckeditor/ckeditor5/issues/7628). ([commit](https://github.com/ckeditor/ckeditor5/commit/0a5d07e3c9a5cef51ebfb4a5819b5118ad9ae115))
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Added the conversion API to upcast and downcast helpers. Closes [#7334](https://github.com/ckeditor/ckeditor5/issues/7334). ([commit](https://github.com/ckeditor/ckeditor5/commit/16c971198971b770d4e7aff4ea8eec7a88a6fcdb))
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Introduced the `SchemaItemDefinition#isSelectable` and `SchemaItemDefinition#isContent` properties. Closes [#6432](https://github.com/ckeditor/ckeditor5/issues/6432). ([commit](https://github.com/ckeditor/ckeditor5/commit/579c1c851ca33c78de60c98777684f8ee5ceb26e))
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Introduced new upcast `ConversionApi` helper methods: `conversionApi.safeInsert()` and `conversionApi.updateConversionResult()`. The new methods are intended to simplify writing event-based element-to-element converters. Closes [#7336](https://github.com/ckeditor/ckeditor5/issues/7336). ([commit](https://github.com/ckeditor/ckeditor5/commit/8d84af1610089ea7916401ecf6f636c9d330b459))
 * **[image](https://www.npmjs.com/package/@ckeditor/ckeditor5-image)**: Introduced the insert image via URL feature. Closes [#7794](https://github.com/ckeditor/ckeditor5/issues/7794). ([commit](https://github.com/ckeditor/ckeditor5/commit/bb00c23f6234751666e859e6e5d7e909f194e375))
 * **[indent](https://www.npmjs.com/package/@ckeditor/ckeditor5-indent)**: Block indentation is now recognized as a formatting attribute. Closes [#2358](https://github.com/ckeditor/ckeditor5/issues/2358). ([commit](https://github.com/ckeditor/ckeditor5/commit/6b2cc25dd717eb22caf7189d8cf33511397179c0))
-* **[list](https://www.npmjs.com/package/@ckeditor/ckeditor5-list)**: Introduced the list styles feature that allows customizing the list marker. Closes [#7801](https://github.com/ckeditor/ckeditor5/issues/7801). ([commit](https://github.com/ckeditor/ckeditor5/commit/137dd2856aecaa8f9c023e6ca9d01592707137a0))
+* **[list](https://www.npmjs.com/package/@ckeditor/ckeditor5-list)**: Introduced the list style feature that allows customizing the list marker. Closes [#7801](https://github.com/ckeditor/ckeditor5/issues/7801). ([commit](https://github.com/ckeditor/ckeditor5/commit/137dd2856aecaa8f9c023e6ca9d01592707137a0))
 * **[markdown-gfm](https://www.npmjs.com/package/@ckeditor/ckeditor5-markdown-gfm)**: Introduced the `Markdown` plugin. Closes [#6007](https://github.com/ckeditor/ckeditor5/issues/6007). ([commit](https://github.com/ckeditor/ckeditor5/commit/7cd5fc198e1977ecefbf0e455f4b514b467e7775))
-* **[markdown-gfm](https://www.npmjs.com/package/@ckeditor/ckeditor5-markdown-gfm)**: The markdown data processor has been revamped and got the dependencies updated. Closes [#5988](https://github.com/ckeditor/ckeditor5/issues/5988). ([commit](https://github.com/ckeditor/ckeditor5/commit/3881349eae0c9a862e76487f8eb117d6ca3e38b0))
-* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-utils)**: Introduced the `Rect#getBoundingRect()` method that returns a `Rect` instance containing all the rectangles passed as argument. Closes [#7858](https://github.com/ckeditor/ckeditor5/issues/7858). ([commit](https://github.com/ckeditor/ckeditor5/commit/ccfaf5e54854cc8a62ebbc005e35676f77be37c4))
+* **[markdown-gfm](https://www.npmjs.com/package/@ckeditor/ckeditor5-markdown-gfm)**: The Markdown data processor was revamped and got the dependencies updated. Closes [#5988](https://github.com/ckeditor/ckeditor5/issues/5988). ([commit](https://github.com/ckeditor/ckeditor5/commit/3881349eae0c9a862e76487f8eb117d6ca3e38b0))
+* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-utils)**: Introduced the `Rect#getBoundingRect()` method that returns a `Rect` instance containing all the rectangles passed as an argument. Closes [#7858](https://github.com/ckeditor/ckeditor5/issues/7858). ([commit](https://github.com/ckeditor/ckeditor5/commit/ccfaf5e54854cc8a62ebbc005e35676f77be37c4))
 * **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-utils)**: Introduced the `passive` option support in the `DomEmitterMixin#listenTo()` method. Closes [#7828](https://github.com/ckeditor/ckeditor5/issues/7828). ([commit](https://github.com/ckeditor/ckeditor5/commit/a7ef65c8246b9591a9a2081cfb19266de0c6194b))
-* **[widget](https://www.npmjs.com/package/@ckeditor/ckeditor5-widget)**: Keyboard vertical navigation in the text lines next to objects should move the caret to the position closest to the object. Closes [#7630](https://github.com/ckeditor/ckeditor5/issues/7630). ([commit](https://github.com/ckeditor/ckeditor5/commit/7984a14a411416634d64d405da2d6d18a314e947))
+* **[widget](https://www.npmjs.com/package/@ckeditor/ckeditor5-widget)**: Keyboard vertical navigation in text lines next to objects should move the caret to the position closest to the object. Closes [#7630](https://github.com/ckeditor/ckeditor5/issues/7630). ([commit](https://github.com/ckeditor/ckeditor5/commit/7984a14a411416634d64d405da2d6d18a314e947))
 
 ### Bug fixes
 
-* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Upcast conversion will now try to wrap text or inline elements in a paragraph in a place where is not allowed but a paragraph is allowed. Closes [#7753](https://github.com/ckeditor/ckeditor5/issues/7753), [#6698](https://github.com/ckeditor/ckeditor5/issues/6698). ([commit](https://github.com/ckeditor/ckeditor5/commit/5e857fd0ec6f4dc9e86dec0bf9c5b87289eedf8b))
+* **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: Upcast conversion will now try to wrap text or inline elements in a paragraph in a place where they are not allowed but a paragraph is allowed. Closes [#7753](https://github.com/ckeditor/ckeditor5/issues/7753), [#6698](https://github.com/ckeditor/ckeditor5/issues/6698). ([commit](https://github.com/ckeditor/ckeditor5/commit/5e857fd0ec6f4dc9e86dec0bf9c5b87289eedf8b))
 * **[engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)**: The selection will no longer inherit attributes from an empty inline element. Closes [#7459](https://github.com/ckeditor/ckeditor5/issues/7459). ([commit](https://github.com/ckeditor/ckeditor5/commit/1ddb955cc667ad16b41521762f77b95382f467da))
-* **[link](https://www.npmjs.com/package/@ckeditor/ckeditor5-link)**: Fixed a case where the link balloon would point invalid place after browser scroll or resize. Closes [#7705](https://github.com/ckeditor/ckeditor5/issues/7705). ([commit](https://github.com/ckeditor/ckeditor5/commit/5158209e2a39884a3015e317f17f33a340e2502d))
+* **[link](https://www.npmjs.com/package/@ckeditor/ckeditor5-link)**: Fixed a case where the link balloon would point to an invalid place after the browser scroll or resize. Closes [#7705](https://github.com/ckeditor/ckeditor5/issues/7705). ([commit](https://github.com/ckeditor/ckeditor5/commit/5158209e2a39884a3015e317f17f33a340e2502d))
 * **[ui](https://www.npmjs.com/package/@ckeditor/ckeditor5-ui)**: Dropdown panels from the editor's main toolbar should always float above the contextual balloons from the editor's content. Closes [#7874](https://github.com/ckeditor/ckeditor5/issues/7874). ([commit](https://github.com/ckeditor/ckeditor5/commit/57d3f02958ad32b8c774dbdc38e1a1210e75af1f))
 * **[ui](https://www.npmjs.com/package/@ckeditor/ckeditor5-ui)**: Balloon toolbar should reposition and ungroup items correctly when the window resizes. Closes [#6444](https://github.com/ckeditor/ckeditor5/issues/6444). ([commit](https://github.com/ckeditor/ckeditor5/commit/32523780fa27146d4f74b538af4831d8b9683bd9))
-* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-utils)**: `Rect` utility returns wrong sizes in case of a sequenced range. Closes [#7838](https://github.com/ckeditor/ckeditor5/issues/7838). ([commit](https://github.com/ckeditor/ckeditor5/commit/ccfaf5e54854cc8a62ebbc005e35676f77be37c4))
+* **[utils](https://www.npmjs.com/package/@ckeditor/ckeditor5-utils)**: The `Rect` utility returns wrong sizes in case of a sequenced range. Closes [#7838](https://github.com/ckeditor/ckeditor5/issues/7838). ([commit](https://github.com/ckeditor/ckeditor5/commit/ccfaf5e54854cc8a62ebbc005e35676f77be37c4))
 
 ### Other changes
 
 * **[markdown-gfm](https://www.npmjs.com/package/@ckeditor/ckeditor5-markdown-gfm)**: Upgraded to Marked v1.1.1. Closes [#7850](https://github.com/ckeditor/ckeditor5/issues/7850). ([commit](https://github.com/ckeditor/ckeditor5/commit/d6c8731a33f3402b8bd71b987b762116efd3898a))
-* **[mention](https://www.npmjs.com/package/@ckeditor/ckeditor5-mention)**: The <kbd>space</kbd> key will not confirm a mention selection from the list. Closes [#6394](https://github.com/ckeditor/ckeditor5/issues/6394). ([commit](https://github.com/ckeditor/ckeditor5/commit/a8d41ecbbeb5d36694ba74d0391805cfaa5214e7))
+* **[mention](https://www.npmjs.com/package/@ckeditor/ckeditor5-mention)**: The <kbd>Space</kbd> key will not confirm a mention selection from the list. Closes [#6394](https://github.com/ckeditor/ckeditor5/issues/6394). ([commit](https://github.com/ckeditor/ckeditor5/commit/a8d41ecbbeb5d36694ba74d0391805cfaa5214e7))
 * **[remove-format](https://www.npmjs.com/package/@ckeditor/ckeditor5-remove-format)**: Block formatting should be removed if the selection is inside that block. ([commit](https://github.com/ckeditor/ckeditor5/commit/6b2cc25dd717eb22caf7189d8cf33511397179c0))
 * **[table](https://www.npmjs.com/package/@ckeditor/ckeditor5-table)**: The `tableCell` model element brought by the `TableEditing` plugin is no longer an object (`SchemaItemDefinition#isObject`) in the `Schema` but a selectable (`SchemaItemDefinition#isSelectable`) (see [#6432](https://github.com/ckeditor/ckeditor5/issues/6432)). ([commit](https://github.com/ckeditor/ckeditor5/commit/579c1c851ca33c78de60c98777684f8ee5ceb26e))
-* **[table](https://www.npmjs.com/package/@ckeditor/ckeditor5-table)**: Pressing <kbd>Shift</kbd>+<kbd>Tab</kbd> in the first table cell now selects entire table. Closes [#7535](https://github.com/ckeditor/ckeditor5/issues/7535). ([commit](https://github.com/ckeditor/ckeditor5/commit/3064c64733145b40290480f3299e168b74380d04))
+* **[table](https://www.npmjs.com/package/@ckeditor/ckeditor5-table)**: Pressing <kbd>Shift</kbd>+<kbd>Tab</kbd> in the first table cell now selects the entire table. Closes [#7535](https://github.com/ckeditor/ckeditor5/issues/7535). ([commit](https://github.com/ckeditor/ckeditor5/commit/3064c64733145b40290480f3299e168b74380d04))
 * **[ui](https://www.npmjs.com/package/@ckeditor/ckeditor5-ui)**: The `clickOutsideHandler()` function will take into consideration that the editor can be placed in a shadow root while detecting a click. Closes [#7743](https://github.com/ckeditor/ckeditor5/issues/7743). ([commit](https://github.com/ckeditor/ckeditor5/commit/2dc026409051828618c274ae62ce331fe05681fe))
 
   Thanks to [@ywsang](https://github.com/ywsang).

+ 22 - 13
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-custom-element-converter.js

@@ -9,7 +9,7 @@ import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud
 
 class InfoBox {
 	constructor( editor ) {
-		// Schema definition
+		// Schema definition.
 		editor.model.schema.register( 'infoBox', {
 			allowWhere: '$block',
 			allowContentOf: '$root',
@@ -21,7 +21,7 @@ class InfoBox {
 		editor.conversion.for( 'upcast' )
 			.add( dispatcher => dispatcher.on( 'element:div', upcastConverter ) );
 
-		// The downcast conversion must be split as we need a widget in the editing pipeline.
+		// The downcast conversion must be split as you need a widget in the editing pipeline.
 		editor.conversion.for( 'editingDowncast' )
 			.add( dispatcher => dispatcher.on( 'insert:infoBox', editingDowncastConverter ) );
 		editor.conversion.for( 'dataDowncast' )
@@ -32,46 +32,51 @@ class InfoBox {
 function upcastConverter( event, data, conversionApi ) {
 	const viewInfoBox = data.viewItem;
 
-	// Detect that view element is an info-box div.
+	// Detect that a view element is an info box <div>.
 	// Otherwise, it should be handled by another converter.
 	if ( !viewInfoBox.hasClass( 'info-box' ) ) {
 		return;
 	}
 
-	// Create a model structure.
+	// Create the model structure.
 	const modelElement = conversionApi.writer.createElement( 'infoBox', {
 		infoBoxType: getTypeFromViewElement( viewInfoBox )
 	} );
 
-	// Try to safely insert element - if it returns false the element can't be safely inserted
-	// into the content, and the conversion process must stop.
+	// Try to safely insert the element into the model structure.
+	// If `safeInsert()` returns `false`, the element cannot be safely inserted
+	// into the content and the conversion process must stop.
+	// This may happen if the data that you are converting has an incorrect structure
+	// (e.g. it was copied from an external website).
 	if ( !conversionApi.safeInsert( modelElement, data.modelCursor ) ) {
 		return;
 	}
 
-	// Mark info-box div as handled by this converter.
+	// Mark the info box <div> as handled by this converter.
 	conversionApi.consumable.consume( viewInfoBox, { name: true } );
 
-	// Let's assume that the HTML structure is always the same.
+	// Let us assume that the HTML structure is always the same.
+	// Note: For full bulletproofing this converter, you should also check
+	// whether these elements are the right ones.
 	const viewInfoBoxTitle = viewInfoBox.getChild( 0 );
 	const viewInfoBoxContent = viewInfoBox.getChild( 1 );
 
-	// Mark info-box inner elements as handled by this converter.
+	// Mark info box inner elements (title and content <div>s) as handled by this converter.
 	conversionApi.consumable.consume( viewInfoBoxTitle, { name: true } );
 	conversionApi.consumable.consume( viewInfoBoxContent, { name: true } );
 
-	// Let the editor handle children of the info-box content conversion.
+	// Let the editor handle the children of <div class="info-box-content">.
 	conversionApi.convertChildren( viewInfoBoxContent, modelElement );
 
-	// Conversion requires updating result data structure properly.
+	// Finally, update the conversion's modelRange and modelCursor.
 	conversionApi.updateConversionResult( modelElement, data );
 }
 
 function editingDowncastConverter( event, data, conversionApi ) {
 	let { infoBox, infoBoxContent, infoBoxTitle } = createViewElements( data, conversionApi );
 
-	// Decorate view items as widgets.
-	infoBox = toWidget( infoBox, conversionApi.writer, { label: 'simple box widget' } );
+	// Decorate view items as a widget and widget editable area.
+	infoBox = toWidget( infoBox, conversionApi.writer, { label: 'info box widget' } );
 	infoBoxContent = toWidgetEditable( infoBoxContent, conversionApi.writer );
 
 	insertViewElements( data, conversionApi, infoBox, infoBoxTitle, infoBoxContent );
@@ -118,7 +123,11 @@ function insertViewElements( data, conversionApi, infoBox, infoBoxTitle, infoBox
 		infoBoxContent
 	);
 
+	// The default mapping between the model <infoBox> and its view representation.
 	conversionApi.mapper.bindElements( data.item, infoBox );
+	// However, since the model <infoBox> content needs to end up in the inner
+	// <div class="info-box-content">, you need to bind one with another overriding
+	// a part of the default binding.
 	conversionApi.mapper.bindElements( data.item, infoBoxContent );
 
 	conversionApi.writer.insert(

+ 3 - 1
packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-extending-output.md

@@ -355,4 +355,6 @@ Add some CSS styles for `.my-heading` to see the customization in action:
 
 ## What's next?
 
-If you would like to read more about how to make CKEditor 5 accept more content, refer to the {@link framework/guides/deep-dive/conversion-preserving-custom-content Preserving custom content} guide.
+If you would like to read more about how to make CKEditor 5 accept more content, refer to the {@link framework/guides/deep-dive/conversion-preserving-custom-content Preserving custom content} guide.
+
+If you want to learn how to create complex view structures or how to move from {@link module:engine/conversion/conversion~Conversion two-way} or {@link module:engine/conversion/conversion~Conversion#for one-way} converters to event-based ones, refer to the {@link framework/guides/deep-dive/custom-element-conversion Custom element conversion} guide.

+ 1 - 0
packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-introduction.md

@@ -126,3 +126,4 @@ Once you understand more about the conversion of model attributes, you can check
 
 * {@link framework/guides/deep-dive/conversion-extending-output Extending the editor output} &mdash; How to extend the output of existing CKEditor 5 features.
 * {@link framework/guides/deep-dive/conversion-preserving-custom-content Preserving custom content} &mdash; How to make CKEditor 5 accept more content.
+* {@link framework/guides/deep-dive/custom-element-conversion Custom element conversion} &mdash; How to deal with complex view structures during the model-to-view conversion.

+ 2 - 0
packages/ckeditor5-engine/docs/framework/guides/deep-dive/conversion-preserving-custom-content.md

@@ -487,3 +487,5 @@ ClassicEditor
 ## What's next?
 
 If you would like to read more about how to extend the output of existing CKEditor 5 features, refer to the {@link framework/guides/deep-dive/conversion-extending-output Extending the editor output} guide.
+
+If you want to learn how to create complex view structures or how to move from {@link module:engine/conversion/conversion~Conversion two-way} or {@link module:engine/conversion/conversion~Conversion#for one-way} converters to event-based ones, refer to the {@link framework/guides/deep-dive/custom-element-conversion Custom element conversion} guide.

+ 49 - 44
packages/ckeditor5-engine/docs/framework/guides/deep-dive/custom-element-conversion.md

@@ -8,28 +8,31 @@ order: 40
 
 There are three levels on which elements can be converted:
 
-* By using the two-way converter: {@link module:engine/conversion/conversion~Conversion#elementToElement `conversion.elementToElement()`}. It is a fully declarative API. It is the least powerful option but it is the easiest one to use.
-* By using one-way converters: for example {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `conversion.for( 'downcast' ).elementToElement()`} and {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToElement `conversion.for( 'upcast' ).elementToElement()`}. In this case, you need to define at least two converters (for upcast and downcast), but the "how" part becomes a callback, and hence you gain more control over it.
-* Finally, by using event-based converters. In this case, you need to listen to events fired by {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} and {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}. This method has the full access to every bit of logic that a converter needs to implement and therefore it can be used to write the most complex conversion methods.
+* By using the two-way converter: {@link module:engine/conversion/conversion~Conversion#elementToElement `conversion.elementToElement()`}.
+  This is a fully declarative API. It is the least powerful option but it is the easiest one to use.
+* By using one-way converters: for example {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `conversion.for( 'downcast' ).elementToElement()`} and {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToElement `conversion.for( 'upcast' ).elementToElement()`}.
+  In this case, you need to define at least two converters (for upcast and downcast), but the "how" part becomes a callback, and hence you gain more control over it.
+* Finally, by using event-based converters.
+  In this case, you need to listen to events fired by {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} and {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}. This method has full access to every bit of logic that a converter needs to implement and therefore it can be used to write the most complex conversion methods.
 
-In this guide, we will show you how to migrate from a simple two-way converter to an event-based converters as the requirements regarding the feature get more complex.
+This guide explains how to migrate from a simple two-way converter to an event-based converter as the requirements regarding the feature get more complex.
 
 ## Introduction
 
-Let's assume that content in your application contains "info boxes". As for now, it was only required to wrap part of a content in a `<div>` element that would look in the data and editing views like this:
+Let us assume that the content in your application contains "info boxes". As for now, it was only required to wrap a part of the content in a `<div>` element that would look like this in the data and editing views:
 
 ```html
 <div class="info-box">
-	<!-- any editable content -->
+	<!-- Any editable content. -->
 	<p>This is <strong>important!</strong></p>
 </div>
 ```
 
-This data is represented in the model as the following structure:
+The data is represented in the model as the following structure:
 
 ```html
 <infoBox>
-	<!-- any $block content: -->
+	<!-- Any $block content. -->
 	<paragraph><$text>This is </$text><$text bold="true">important!</$text></paragraph>
 </infoBox>
 ```
@@ -39,14 +42,14 @@ This can be easily done with the below schema and converters in a simple `InfoBo
 ```js
 class InfoBox {
 	constructor( editor ) {
-		// 1. Define infoBox as an object that can be contained any other content.
+		// 1. Define infoBox as an object that can contain any other content.
 		editor.model.schema.register( 'infoBox', {
 			allowWhere: '$block',
 			allowContentOf: '$root',
 			isObject: true
 		} );
 
-		// 2. Conversion is straight forward:
+		// 2. The conversion is straightforward:
 		editor.conversion.elementToElement( {
 			model: 'infoBox',
 			view: {
@@ -60,7 +63,7 @@ class InfoBox {
 
 ## Migrating to an event-based converter
 
-Let's now assume that the requirements have changed and there is a need for adding an additional element in the data and editing views that will display the type of the info box (warning, error, info, etc.).
+Let us now assume that the requirements have changed and there is a need for adding an additional element in the data and editing views that will display the type of the info box (warning, error, info, etc.).
 
 The new info box structure:
 
@@ -68,56 +71,56 @@ The new info box structure:
 <div class="info-box info-box-warning">
 	<div class="info-box-title">Warning</div>
 	<div class="info-box-content">
-		<!-- any editable content -->
+		<!-- Any editable content. -->
 		<p>This is <strong>important!</strong></p>
 	</div>
 </div>
 ```
 
-The "Warning" part should not be editable. It defines a type of the info box so we can store this  bit of information as an attribute of the `<infoBox>` element:
+The "Warning" part should not be editable. It defines the type of the info box so you can store this bit of information as an attribute of the `<infoBox>` element:
 
 ```html
 <infoBox infoBoxType="warning">
-	<!-- any $block content: -->
+	<!-- Any $block content. -->
 	<paragraph><$text>This is </$text><$text bold="true">important!</$text></paragraph>
 </infoBox>
 ```
 
-Let's see how to update our basic implementation to cover these requirements.
+Let us see how to update the basic implementation to cover these requirements.
 
 ### Demo
 
-Below is a demo of the editor with the example info box.
+Below is a demo of the editor with a sample info box.
 
 {@snippet framework/extending-content-custom-element-converter}
 
 ### Schema
 
-The type of the box is defined by the additional class on the main `<div>` but it is also represented as text in `<div class="info-box-title">`. All the info box content must be now placed inside `<div class="info-box-content">` instead of the main wrapper.
+The type of the box is defined by an additional class on the main `<div>` but it is also represented as text in `<div class="info-box-title">`. All the info box content must now be placed inside `<div class="info-box-content">` instead of the main wrapper.
 
-For the above requirements we can see that the model structure of the `infoBox` does not need to change much. We can still use a single element in the model. The only addition to the model is an attribute that will hold information about the info box type:
+For the above requirements you can see that the model structure of the `infoBox` does not need to change much. You can still use a single element in the model. The only addition to the model is an attribute that will store information about the info box type:
 
 ```js
 editor.model.schema.register( 'infoBox', {
 	allowWhere: '$block',
 	allowContentOf: '$root',
 	isObject: true,
-	allowAttributes: [ 'infoBoxType' ] // Added
+	allowAttributes: [ 'infoBoxType' ] // Added.
 } );
 ```
 
 ### Event-based upcast converter
 
-The conversion of the type of the box itself could be achieved by using {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `attributeToAttribute()`} (`info-box-*` CSS classes to the `infoBoxType` model attribute). However, two more changes were made to the data format that we need to handle:
+The conversion of the type of the box itself can be achieved by using {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `attributeToAttribute()`} (`info-box-*` CSS classes to the `infoBoxType` model attribute). However, two more changes were made to the data format that you need to handle:
 
-* There is the new `<div class="info-box-title">` element that should be ignored during upcast conversion as it duplicates the information conveyed by the main element's CSS class.
-* The content of the info box is now located inside another element (previously it was located directly in the main wrapper).
+* There is a new `<div class="info-box-title">` element that should be ignored during the upcast conversion as it duplicates the information conveyed by the main element's CSS class.
+* The content of the info box is now located inside another element. Previously it was located directly in the main wrapper.
 
-Neither two-way nor one-way converters can handle such conversion. Therefore, we need to use an event-based converter with the following behavior:
+Neither two-way nor one-way converters can handle such conversion. Therefore, you need to use an event-based converter with the following behavior:
 
-1. Create model `<infoBox>` element with `infoBoxType` attribute.
-1. Skip conversion of `<div class="info-box-title">` as the information about type can be obtained from the wrapper's CSS classes.
-1. Convert children of `<div class="info-box-content">` and insert them directly into `<infoBox>`.
+1. Create a model `<infoBox>` element with the `infoBoxType` attribute.
+1. Skip the conversion of `<div class="info-box-title">` as the information about type can be obtained from the wrapper's CSS classes.
+1. Convert the children of `<div class="info-box-content">` and insert them directly into `<infoBox>`.
 
 ```js
 function upcastConverter( event, data, conversionApi ) {
@@ -129,16 +132,16 @@ function upcastConverter( event, data, conversionApi ) {
 		return;
 	}
 
-	// Create a model structure.
+	// Create the model structure.
 	const modelElement = conversionApi.writer.createElement( 'infoBox', {
 		infoBoxType: getTypeFromViewElement( viewInfoBox )
 	} );
 
 	// Try to safely insert the element into the model structure.
-	// If `safeInsert()` returns `false` the element cannot be safely inserted
-	// into the content, and the conversion process must stop.
-	// This may happen if the data that we are converting has incorrect structure
-	// (e.g. was copied from an external website).
+	// If `safeInsert()` returns `false`, the element cannot be safely inserted
+	// into the content and the conversion process must stop.
+	// This may happen if the data that you are converting has an incorrect structure
+	// (e.g. it was copied from an external website).
 	if ( !conversionApi.safeInsert( modelElement, data.modelCursor ) ) {
 		return;
 	}
@@ -146,8 +149,8 @@ function upcastConverter( event, data, conversionApi ) {
 	// Mark the info box <div> as handled by this converter.
 	conversionApi.consumable.consume( viewInfoBox, { name: true } );
 
-	// Let's assume that the HTML structure is always the same.
-	// Note: for full bulletproofing this converter we should also check
+	// Let us assume that the HTML structure is always the same.
+	// Note: For full bulletproofing this converter, you should also check
 	// whether these elements are the right ones.
 	const viewInfoBoxTitle = viewInfoBox.getChild( 0 );
 	const viewInfoBoxContent = viewInfoBox.getChild( 1 );
@@ -156,14 +159,14 @@ function upcastConverter( event, data, conversionApi ) {
 	conversionApi.consumable.consume( viewInfoBoxTitle, { name: true } );
 	conversionApi.consumable.consume( viewInfoBoxContent, { name: true } );
 
-	// Let the editor handle children of <div class="info-box-content">.
+	// Let the editor handle the children of <div class="info-box-content">.
 	conversionApi.convertChildren( viewInfoBoxContent, modelElement );
 
 	// Finally, update the conversion's modelRange and modelCursor.
 	conversionApi.updateConversionResult( modelElement, data );
 }
 
-// Helper function to read the type from the view classes.
+// A helper function to read the type from the view classes.
 function getTypeFromViewElement( viewElement ) {
 	if ( viewElement.hasClass( 'info-box-info' ) ) {
 		return 'Info';
@@ -177,7 +180,7 @@ function getTypeFromViewElement( viewElement ) {
 }
 ```
 
-This upcast converter callback can now be plugged by adding a listener to the {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#element `UpcastDispatcher#element` event}. We will listen to `element:div` to ensure that the callback is called only for `<div>` elements.
+This upcast converter callback can now be plugged by adding a listener to the {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#element `UpcastDispatcher#element` event}. You will listen to `element:div` to ensure that the callback is called only for `<div>` elements.
 
 ```js
 editor.conversion.for( 'upcast' )
@@ -186,10 +189,12 @@ editor.conversion.for( 'upcast' )
 
 ### Event-based downcast converter
 
-The missing bit are the downcast converters for the editing and data pipelines. We want to use the widget system to make the info box behave like an "object". The other aspect that we need to take care of is the fact that the view structure has more elements than the model structure. In this case, we could actually use one-way converters. However, we will showcase how an event-based converter would look.
+The missing bits are the downcast converters for the editing and data pipelines.
+
+You will want to use the widget system to make the info box behave like an "object". Another aspect that you need to take care of is the fact that the view structure has more elements than the model structure. In this case, you could actually use one-way converters. However, this tutorial will showcase how an event-based converter would look.
 
 <info-box>
-	See the {@link framework/guides/tutorials/implementing-a-block-widget Implementing a block widget} to learn about the widget system.
+	See the {@link framework/guides/tutorials/implementing-a-block-widget Implementing a block widget} guide to learn about the widget system.
 </info-box>
 
 The remaining downcast converters:
@@ -248,9 +253,9 @@ function insertViewElements( data, conversionApi, infoBox, infoBoxTitle, infoBox
 
 	// The default mapping between the model <infoBox> and its view representation.
 	conversionApi.mapper.bindElements( data.item, infoBox );
-	// However, since the model <infoBox> content need to end up in the inner
-	// <div class="info-box-content"> we need to bind one with another overriding
-	// part of the default binding.
+	// However, since the model <infoBox> content needs to end up in the inner
+	// <div class="info-box-content">, you need to bind one with another overriding
+	// a part of the default binding.
 	conversionApi.mapper.bindElements( data.item, infoBoxContent );
 
 	conversionApi.writer.insert(
@@ -271,12 +276,12 @@ editor.conversion.for( 'dataDowncast' )
 
 ### Updated plugin code
 
-The updated `InfoBox` plugin that glues all this together:
+The updated `InfoBox` plugin that glues the event-based converters together:
 
 ```js
 class InfoBox {
 	constructor( editor ) {
-		// Schema definition
+		// Schema definition.
 		editor.model.schema.register( 'infoBox', {
 			allowWhere: '$block',
 			allowContentOf: '$root',
@@ -288,7 +293,7 @@ class InfoBox {
 		editor.conversion.for( 'upcast' )
 			.add( dispatcher => dispatcher.on( 'element:div', upcastConverter ) );
 
-		// The downcast conversion must be split as we need a widget in the editing pipeline.
+		// The downcast conversion must be split as you need a widget in the editing pipeline.
 		editor.conversion.for( 'editingDowncast' )
 			.add( dispatcher => dispatcher.on( 'insert:infoBox', editingDowncastConverter ) );
 		editor.conversion.for( 'dataDowncast' )

+ 8 - 8
packages/ckeditor5-engine/docs/framework/guides/deep-dive/schema.md

@@ -260,8 +260,8 @@ Here is a table listing various model elements and their properties registered i
 </table>
 
 <info-box>
-	* <span id="inherited1">[1]</span> The value of `isLimit` is `true` for this element because all [objects](#object-elements) are automatically [limit elements](#limit-elements),
-	* <span id="inherited2">[2]</span> The value of `isSelectable` is `true` for this element because all [objects](#object-elements) are automatically [selectable elements](#selectable-elements),
+	* <span id="inherited1">[1]</span> The value of `isLimit` is `true` for this element because all [objects](#object-elements) are automatically [limit elements](#limit-elements).
+	* <span id="inherited2">[2]</span> The value of `isSelectable` is `true` for this element because all [objects](#object-elements) are automatically [selectable elements](#selectable-elements).
 	* <span id="inherited3">[3]</span> The value of `isContent` is `true` for this element because all [objects](#object-elements) are automatically [content elements](#content-elements).
 </info-box>
 
@@ -303,11 +303,11 @@ schema.register( 'myImage', {
 The {@link module:engine/model/schema~Schema#isObject `Schema#isObject()`} can later be used to check this property.
 
 <info-box>
-	Every "object" is automatically also:
+	Every object is automatically also:
 
-	* a [limit element](#limit-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isLimit `Schema#isLimit( element )`} will always return `true`.
-	* a [selectable element](#selectable-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isSelectable `Schema#isSelectable( element )`} will always return `true`.
-	* a [content element](#content-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isContent `Schema#isContent( element )`} will always return `true`.
+	* A [limit element](#limit-elements) &ndash; For every element with `isObject` set to `true`, {@link module:engine/model/schema~Schema#isLimit `Schema#isLimit( element )`} will always return `true`.
+	* A [selectable element](#selectable-elements) &ndash; For every element with `isObject` set to `true`, {@link module:engine/model/schema~Schema#isSelectable `Schema#isSelectable( element )`} will always return `true`.
+	* A [content element](#content-elements) &ndash; For every element with `isObject` set to `true`, {@link module:engine/model/schema~Schema#isContent `Schema#isContent( element )`} will always return `true`.
 </info-box>
 
 ### Block elements
@@ -341,14 +341,14 @@ schema.register( 'mySelectable', {
 The {@link module:engine/model/schema~Schema#isSelectable `Schema#isSelectable()`} method can later be used to check this property.
 
 <info-box>
-	All [object elements](#object-elements) are selectable by default. There are other selectable elements registered in the editor, though. For instance, there is also the `tableCell` model element (rendered as a `<td>` in the editing view) that is selectable while **not** registered as an object. The {@link features/table#table-selection table selection} plugin takes advantage of this fact and allows users create rectangular selections made of multiple table cells.
+	All [object elements](#object-elements) are selectable by default. There are other selectable elements registered in the editor, though. For instance, there is also the `tableCell` model element (rendered as a `<td>` in the editing view) that is selectable while **not** registered as an object. The {@link features/table#table-selection table selection} plugin takes advantage of this fact and allows users to create rectangular selections made of multiple table cells.
 </info-box>
 
 ### Content elements
 
 You can tell content model elements from other elements by looking at their representation in the editor data (you can use {@link module:editor-classic/classiceditor~ClassicEditor#getData `editor.getData()`} or {@link module:engine/model/model~Model#hasContent Model#hasContent()} to check this out).
 
-Elements such as images or media will **always** find their way into editor data and this is what makes them content elements. They are marked with the {@link module:engine/model/schema~SchemaItemDefinition#isContent `isContent`} property in the schema:
+Elements such as images or media will **always** find their way into the editor data and this is what makes them content elements. They are marked with the {@link module:engine/model/schema~SchemaItemDefinition#isContent `isContent`} property in the schema:
 
 ```js
 schema.register( 'myImage', {

+ 6 - 0
packages/ckeditor5-engine/src/conversion/downcastdispatcher.js

@@ -75,6 +75,12 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  * When providing custom listeners for downcast dispatcher, remember to use the provided
  * {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer} to apply changes to the view document.
  *
+ * You can read more about conversion in the following guides:
+ *
+ * * {@glink framework/guides/deep-dive/conversion/conversion-introduction Advanced conversion concepts &mdash; attributes}
+ * * {@glink framework/guides/deep-dive/conversion/conversion-extending-output Extending the editor output }
+ * * {@glink framework/guides/deep-dive/conversion/custom-element-conversion Custom element conversion}
+ *
  * An example of a custom converter for the downcast dispatcher:
  *
  *		// You will convert inserting a "paragraph" model element into the model.

+ 3 - 0
packages/ckeditor5-engine/src/conversion/downcasthelpers.js

@@ -62,6 +62,9 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
 	 * to the conversion process.
 	 *
+	 * You can read more about element-to-element conversion in the
+	 * {@glink framework/guides/deep-dive/conversion/custom-element-conversion Custom element conversion} guide.
+	 *
 	 * @method #elementToElement
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.model The name of the model element to convert.

+ 111 - 108
packages/ckeditor5-engine/src/conversion/upcastdispatcher.js

@@ -18,24 +18,24 @@ import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 
 /**
- * `UpcastDispatcher` is a central point of the view to model conversion, which is a process of
- * converting given {@link module:engine/view/documentfragment~DocumentFragment view document fragment} or
+ * Upcast dispatcher is a central point of the view-to-model conversion, which is a process of
+ * converting a given {@link module:engine/view/documentfragment~DocumentFragment view document fragment} or
  * {@link module:engine/view/element~Element view element} into a correct model structure.
  *
  * During the conversion process, the dispatcher fires events for all {@link module:engine/view/node~Node view nodes}
  * from the converted view document fragment.
- * Special callbacks called "converters" should listen to these events in order to convert these view nodes.
+ * Special callbacks called "converters" should listen to these events in order to convert the view nodes.
  *
  * The second parameter of the callback is the `data` object with the following properties:
  *
- * * `data.viewItem` contains {@link module:engine/view/node~Node view node} or
+ * * `data.viewItem` contains a {@link module:engine/view/node~Node view node} or a
  * {@link module:engine/view/documentfragment~DocumentFragment view document fragment}
  * that is converted at the moment and might be handled by the callback.
  * * `data.modelRange` is used to point to the result
  * of the current conversion (e.g. the element that is being inserted)
- * and is always a {@link module:engine/model/range~Range} when the succeeds.
+ * and is always a {@link module:engine/model/range~Range} when the conversion succeeds.
  * * `data.modelCursor` is a {@link module:engine/model/position~Position position} on which the converter should insert
- * newly created items.
+ * the newly created items.
  *
  * The third parameter of the callback is an instance of {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi}
  * which provides additional tools for converters.
@@ -47,11 +47,11 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *
  * Examples of event-based converters:
  *
- *		// Converter for links (<a>).
+ *		// A converter for links (<a>).
  *		editor.data.upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
  *			if ( conversionApi.consumable.consume( data.viewItem, { name: true, attributes: [ 'href' ] } ) ) {
- *				// <a> element is inline and is represented by an attribute in the model.
- *				// This is why we need to convert only children.
+ *				// The <a> element is inline and is represented by an attribute in the model.
+ *				// This is why you need to convert only children.
  *				const { modelRange } = conversionApi.convertChildren( data.viewItem, data.modelCursor );
  *
  *				for ( let item of modelRange.getItems() ) {
@@ -62,9 +62,9 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *			}
  *		} );
  *
- *		// Convert <p>'s font-size style.
+ *		// Convert <p> element's font-size style.
  *		// Note: You should use a low-priority observer in order to ensure that
- *		// it's executed after the element-to-element converter.
+ *		// it is executed after the element-to-element converter.
  *		editor.data.upcastDispatcher.on( 'element:p', ( evt, data, conversionApi ) => {
  *			const { consumable, schema, writer } = conversionApi;
  *
@@ -74,7 +74,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *
  *			const fontSize = data.viewItem.getStyle( 'font-size' );
  *
- *			// Don't go for the model element after data.modelCursor because it might happen
+ *			// Do not go for the model element after data.modelCursor because it might happen
  *			// that a single view element was converted to multiple model elements. Get all of them.
  *			for ( const item of data.modelRange.getItems( { shallow: true } ) ) {
  *				if ( schema.checkAttribute( item, 'fontSize' ) ) {
@@ -85,27 +85,27 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *
  *		// Convert all elements which have no custom converter into a paragraph (autoparagraphing).
  *		editor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
- *			// Check if element can be converted.
+ *			// Check if an element can be converted.
  *			if ( !conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
- *				// When element is already consumed by higher priority converters then do nothing.
+ *				// When an element is already consumed by higher priority converters, do nothing.
  *				return;
  *			}
  *
  *			const paragraph = conversionApi.writer.createElement( 'paragraph' );
  *
- *			// Try to safely insert paragraph at model cursor - it will find an allowed parent for a current element.
+ *			// Try to safely insert a paragraph at the model cursor - it will find an allowed parent for the current element.
  *			if ( !conversionApi.safeInsert( paragraph, data.modelCursor ) ) {
- *				// When element was not inserted it means that we can't insert paragraph at this position.
+ *				// When an element was not inserted, it means that you cannot insert a paragraph at this position.
  *				return;
  *			}
  *
  *			// Consume the inserted element.
  *			conversionApi.consumable.consume( data.viewItem, { name: data.viewItem.name } ) );
  *
- *			// Convert children to paragraph.
+ *			// Convert the children to a paragraph.
  *			const { modelRange } = conversionApi.convertChildren( data.viewItem,  paragraph ) );
  *
- *			// Update `modelRange` and `modelCursor` in a `data` as a conversion result.
+ *			// Update `modelRange` and `modelCursor` in the `data` as a conversion result.
  *			conversionApi.updateConversionResult( paragraph, data );
  *		}, { priority: 'low' } );
  *
@@ -117,17 +117,17 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  */
 export default class UpcastDispatcher {
 	/**
-	 * Creates a `UpcastDispatcher` that operates using passed API.
+	 * Creates an upcast dispatcher that operates using the passed API.
 	 *
 	 * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi
-	 * @param {Object} [conversionApi] Additional properties for interface that will be passed to events fired
-	 * by `UpcastDispatcher`.
+	 * @param {Object} [conversionApi] Additional properties for an interface that will be passed to events fired
+	 * by the upcast dispatcher.
 	 */
 	constructor( conversionApi = {} ) {
 		/**
-		 * List of the elements that were created during splitting.
+		 * The list of elements that were created during splitting.
 		 *
-		 * After conversion process the list is cleared.
+		 * After the conversion process the list is cleared.
 		 *
 		 * @private
 		 * @type {Map.<module:engine/model/element~Element,Array.<module:engine/model/element~Element>>}
@@ -135,9 +135,9 @@ export default class UpcastDispatcher {
 		this._splitParts = new Map();
 
 		/**
-		 * List of cursor parent elements that were created during splitting.
+		 * The list of cursor parent elements that were created during splitting.
 		 *
-		 * After conversion process the list is cleared.
+		 * After the conversion process the list is cleared.
 		 *
 		 * @private
 		 * @type {Map.<module:engine/model/element~Element,Array.<module:engine/model/element~Element>>}
@@ -145,8 +145,8 @@ export default class UpcastDispatcher {
 		this._cursorParents = new Map();
 
 		/**
-		 * Position in the temporary structure where the converted content is inserted. The structure reflect the context of
-		 * the target position where the content will be inserted. This property is build based on the context parameter of the
+		 * The position in the temporary structure where the converted content is inserted. The structure reflects the context of
+		 * the target position where the content will be inserted. This property is built based on the context parameter of the
 		 * convert method.
 		 *
 		 * @private
@@ -155,7 +155,7 @@ export default class UpcastDispatcher {
 		this._modelCursor = null;
 
 		/**
-		 * Interface passed by dispatcher to the events callbacks.
+		 * An interface passed by the dispatcher to the event callbacks.
 		 *
 		 * @member {module:engine/conversion/upcastdispatcher~UpcastConversionApi}
 		 */
@@ -179,11 +179,11 @@ export default class UpcastDispatcher {
 	 * @fires text
 	 * @fires documentFragment
 	 * @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/element~Element} viewItem
-	 * Part of the view to be converted.
-	 * @param {module:engine/model/writer~Writer} writer Instance of model writer.
+	 * The part of the view to be converted.
+	 * @param {module:engine/model/writer~Writer} writer An instance of the model writer.
 	 * @param {module:engine/model/schema~SchemaContextDefinition} [context=['$root']] Elements will be converted according to this context.
-	 * @returns {module:engine/model/documentfragment~DocumentFragment} Model data that is a result of the conversion process
-	 * wrapped in `DocumentFragment`. Converted marker elements will be set as that document fragment's
+	 * @returns {module:engine/model/documentfragment~DocumentFragment} Model data that is the result of the conversion process
+	 * wrapped in `DocumentFragment`. Converted marker elements will be set as the document fragment's
 	 * {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.
 	 */
 	convert( viewItem, writer, context = [ '$root' ] ) {
@@ -419,9 +419,9 @@ export default class UpcastDispatcher {
 	}
 
 	/**
-	 * Registers that `splitPart` element is a split part of the `originalPart` element.
+	 * Registers that a `splitPart` element is a split part of the `originalPart` element.
 	 *
-	 * Data set by this method is used by {@link #_getSplitParts} and {@link #_removeEmptyElements}.
+	 * The data set by this method is used by {@link #_getSplitParts} and {@link #_removeEmptyElements}.
 	 *
 	 * @private
 	 * @param {module:engine/model/element~Element} originalPart
@@ -480,36 +480,37 @@ export default class UpcastDispatcher {
 	}
 
 	/**
-	 * Fired before the first conversion event, at the beginning of upcast (view to model conversion) process.
+	 * Fired before the first conversion event, at the beginning of the upcast (view-to-model conversion) process.
 	 *
 	 * @event viewCleanup
 	 * @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/element~Element}
-	 * viewItem Part of the view to be converted.
+	 * viewItem A part of the view to be converted.
 	 */
 
 	/**
-	 * Fired when {@link module:engine/view/element~Element} is converted.
+	 * Fired when an {@link module:engine/view/element~Element} is converted.
 	 *
-	 * `element` is a namespace event for a class of events. Names of actually called events follow this pattern:
-	 * `element:<elementName>` where `elementName` is the name of converted element. This way listeners may listen to
-	 * all elements conversion or to conversion of specific elements.
+	 * `element` is a namespace event for a class of events. Names of actually called events follow the pattern of
+	 * `element:<elementName>` where `elementName` is the name of the converted element. This way listeners may listen to
+	 * a conversion of all or just specific elements.
 	 *
 	 * @event element
-	 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data Conversion data. Keep in mind that this object is shared
-	 * by reference between all callbacks that will be called. This means that callbacks can override values if needed, and those values
-	 * will be available in other callbacks.
-	 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by callback.
+	 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data The conversion data. Keep in mind that this object is
+	 * shared by reference between all callbacks that will be called. This means that callbacks can override values if needed, and these
+	 * values will be available in other callbacks.
+	 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by the
+	 * callback.
 	 */
 
 	/**
-	 * Fired when {@link module:engine/view/text~Text} is converted.
+	 * Fired when a {@link module:engine/view/text~Text} is converted.
 	 *
 	 * @event text
 	 * @see #event:element
 	 */
 
 	/**
-	 * Fired when {@link module:engine/view/documentfragment~DocumentFragment} is converted.
+	 * Fired when a {@link module:engine/view/documentfragment~DocumentFragment} is converted.
 	 *
 	 * @event documentFragment
 	 * @see #event:element
@@ -582,54 +583,55 @@ function createContextTree( contextDefinition, writer ) {
 }
 
 /**
- * A set of conversion utils available as the third parameter of
+ * A set of conversion utilities available as the third parameter of the
  * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher upcast dispatcher}'s events.
  *
  * @interface module:engine/conversion/upcastdispatcher~UpcastConversionApi
  */
 
 /**
- * Starts conversion of given item by firing an appropriate event.
+ * Starts the conversion of a given item by firing an appropriate event.
  *
- * Every fired event is passed (as first parameter) an object with `modelRange` property. Every event may set and/or
- * modify that property. When all callbacks are done, the final value of `modelRange` property is returned by this method.
- * The `modelRange` must be {@link module:engine/model/range~Range model range} or `null` (as set by default).
+ * Every fired event is passed (as the first parameter) an object with the `modelRange` property. Every event may set and/or
+ * modify that property. When all callbacks are done, the final value of the `modelRange` property is returned by this method.
+ * The `modelRange` must be a {@link module:engine/model/range~Range model range} or `null` (as set by default).
  *
  * @method #convertItem
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:text
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:documentFragment
  * @param {module:engine/view/item~Item} viewItem Item to convert.
- * @param {module:engine/model/position~Position} modelCursor Position of conversion.
- * @returns {Object} result Conversion result.
- * @returns {module:engine/model/range~Range|null} result.modelRange Model range containing result of item conversion,
- * created and modified by callbacks attached to fired event, or `null` if the conversion result was incorrect.
- * @returns {module:engine/model/position~Position} result.modelCursor Position where conversion should be continued.
+ * @param {module:engine/model/position~Position} modelCursor The conversion position.
+ * @returns {Object} result The conversion result.
+ * @returns {module:engine/model/range~Range|null} result.modelRange The model range containing the result of the item conversion,
+ * created and modified by callbacks attached to the fired event, or `null` if the conversion result was incorrect.
+ * @returns {module:engine/model/position~Position} result.modelCursor The position where the conversion should be continued.
  */
 
 /**
- * Starts conversion of all children of given item by firing appropriate events for all those children.
+ * Starts the conversion of all children of a given item by firing appropriate events for all the children.
  *
  * @method #convertChildren
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:text
  * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:documentFragment
- * @param {module:engine/view/item~Item} viewItem Element which children should be converted.
- * @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrElement Position or element of conversion.
- * @returns {Object} result Conversion result.
- * @returns {module:engine/model/range~Range} result.modelRange Model range containing results of conversion of all children of given item.
- * When no children was converted then range is collapsed.
- * @returns {module:engine/model/position~Position} result.modelCursor Position where conversion should be continued.
+ * @param {module:engine/view/item~Item} viewItem An element whose children should be converted.
+ * @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrElement A position or an element of
+ * the conversion.
+ * @returns {Object} result The conversion result.
+ * @returns {module:engine/model/range~Range} result.modelRange The model range containing the results of the conversion of all children
+ * of the given item. When no child was converted, the range is collapsed.
+ * @returns {module:engine/model/position~Position} result.modelCursor The position where the conversion should be continued.
  */
 
 /**
- * Safely inserts an element to the document checking {@link module:engine/model/schema~Schema schema} to find allowed parent for
- * an element that we are going to insert starting from given position. If current parent does not allow to insert element
- * but one of the ancestors does then split nodes to allowed parent.
+ * Safely inserts an element to the document, checking the {@link module:engine/model/schema~Schema schema} to find an allowed parent for
+ * an element that you are going to insert, starting from the given position. If the current parent does not allow to insert the element
+ * but one of the ancestors does, then splits the nodes to allowed parent.
  *
- * If schema allows to insert node in given position, nothing is split.
+ * If the schema allows to insert the node in a given position, nothing is split.
  *
- * If it was not possible to find allowed parent, `false` is returned, nothing is split.
+ * If it was not possible to find an allowed parent, `false` is returned and nothing is split.
  *
  * Otherwise, ancestors are split.
  *
@@ -649,22 +651,22 @@ function createContextTree( contextDefinition, writer ) {
  *			return;
  *		}
  *
- * The split result is saved and {@link #updateConversionResult} should be used to update
+ * The split result is saved and {@link #updateConversionResult} should be used to update the
  * {@link module:engine/conversion/upcastdispatcher~UpcastConversionData conversion data}.
  *
  * @method #safeInsert
- * @param {module:engine/model/node~Node} node Node to insert.
- * @param {module:engine/model/position~Position} position Position on which element is going to be inserted.
- * @returns {Boolean} Split result. If it was not possible to find allowed position `false` is returned.
+ * @param {module:engine/model/node~Node} node The node to insert.
+ * @param {module:engine/model/position~Position} position The position where an element is going to be inserted.
+ * @returns {Boolean} The split result. If it was not possible to find an allowed position, `false` is returned.
  */
 
 /**
- * Updates the conversion result and sets proper {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelRange} and
- * next {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelCursor} after the conversion.
- * Used together with {@link #safeInsert} enables you to easily convert elements without worrying if the node was split
- * during its children conversion.
+ * Updates the conversion result and sets a proper {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelRange} and
+ * the next {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelCursor} after the conversion.
+ * Used together with {@link #safeInsert}, it enables you to easily convert elements without worrying if the node was split
+ * during the conversion of its children.
  *
- * Example of a usage in a converter code:
+ * A usage example in converter code:
  *
  *		const myElement = conversionApi.writer.createElement( 'myElement' );
  *
@@ -680,19 +682,19 @@ function createContextTree( contextDefinition, writer ) {
  * @method #updateConversionResult
  * @param {module:engine/model/element~Element} element
  * @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data Conversion data.
- * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by callback.
+ * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by the callback.
  */
 
 /**
- * Checks {@link module:engine/model/schema~Schema schema} to find allowed parent for element that we are going to insert
- * starting from given position. If current parent does not allow to insert element but one of the ancestors does then
- * split nodes to allowed parent.
+ * Checks the {@link module:engine/model/schema~Schema schema} to find an allowed parent for an element that is going to be inserted
+ * starting from the given position. If the current parent does not allow inserting an element but one of the ancestors does, the method
+ * splits nodes to allowed parent.
  *
- * If schema allows to insert node in given position, nothing is split and object with that position is returned.
+ * If the schema allows inserting the node in the given position, nothing is split and an object with that position is returned.
  *
- * If it was not possible to find allowed parent, `null` is returned, nothing is split.
+ * If it was not possible to find an allowed parent, `null` is returned and nothing is split.
  *
- * Otherwise, ancestors are split and object with position and the copy of the split element is returned.
+ * Otherwise, ancestors are split and an object with a position and the copy of the split element is returned.
  *
  * For instance, if `<image>` is not allowed in `<paragraph>` but is allowed in `$root`:
  *
@@ -702,23 +704,23 @@ function createContextTree( contextDefinition, writer ) {
  *
  *		<paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
  *
- * In the sample above position between `<paragraph>` elements will be returned as `position` and the second `paragraph`
+ * In the example above, the position between `<paragraph>` elements will be returned as `position` and the second `paragraph`
  * as `cursorParent`.
  *
  * **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.
  *
  * @method #splitToAllowedParent
- * @param {module:engine/model/position~Position} position Position on which element is going to be inserted.
- * @param {module:engine/model/node~Node} node Node to insert.
- * @returns {Object|null} Split result. If it was not possible to find allowed position `null` is returned.
- * @returns {module:engine/model/position~Position} position between split elements.
- * @returns {module:engine/model/element~Element} [cursorParent] Element inside which cursor should be placed to
- * continue conversion. When element is not defined it means that there was no split.
+ * @param {module:engine/model/position~Position} position The position where the element is going to be inserted.
+ * @param {module:engine/model/node~Node} node The node to insert.
+ * @returns {Object|null} The split result. If it was not possible to find an allowed position, `null` is returned.
+ * @returns {module:engine/model/position~Position} The position between split elements.
+ * @returns {module:engine/model/element~Element} [cursorParent] The element inside which the cursor should be placed to
+ * continue the conversion. When the element is not defined it means that there was no split.
  */
 
 /**
- * Returns all the split parts of given `element` that were created during upcasting through using {@link #splitToAllowedParent}.
- * It enables you to easily track those elements and continue processing them after they are split during their children conversion.
+ * Returns all the split parts of the given `element` that were created during upcasting through using {@link #splitToAllowedParent}.
+ * It enables you to easily track these elements and continue processing them after they are split during the conversion of their children.
  *
  *		<paragraph>Foo<image />bar<image />baz</paragraph> ->
  *		<paragraph>Foo</paragraph><image /><paragraph>bar</paragraph><image /><paragraph>baz</paragraph>
@@ -726,9 +728,9 @@ function createContextTree( contextDefinition, writer ) {
  * For a reference to any of above paragraphs, the function will return all three paragraphs (the original element included),
  * sorted in the order of their creation (the original element is the first one).
  *
- * If given `element` was not split, an array with single element is returned.
+ * If the given `element` was not split, an array with a single element is returned.
  *
- * Example of a usage in a converter code:
+ * A usage example in the converter code:
  *
  *		const myElement = conversionApi.writer.createElement( 'myElement' );
  *
@@ -747,9 +749,9 @@ function createContextTree( contextDefinition, writer ) {
  *		// Setting `data.modelCursor` to continue after the last split element:
  *		data.modelCursor = conversionApi.writer.createPositionAfter( lastSplitPart );
  *
- * **Tip:** if you are unable to get a reference to the original element (for example because the code is split into multiple converters
- * or even classes) but it was already converted, you might want to check first element in `data.modelRange`. This is a common situation
- * if an attribute converter is separated from an element converter.
+ * **Tip:** If you are unable to get a reference to the original element (for example because the code is split into multiple converters
+ * or even classes) but it has already been converted, you may want to check the first element in `data.modelRange`. This is a common
+ * situation if an attribute converter is separated from an element converter.
  *
  * **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.
  *
@@ -759,18 +761,19 @@ function createContextTree( contextDefinition, writer ) {
  */
 
 /**
- * Stores information about what parts of processed view item are still waiting to be handled. After a piece of view item
- * was converted, appropriate consumable value should be {@link module:engine/conversion/viewconsumable~ViewConsumable#consume consumed}.
+ * Stores information about what parts of the processed view item are still waiting to be handled. After a piece of view item
+ * was converted, an appropriate consumable value should be
+ * {@link module:engine/conversion/viewconsumable~ViewConsumable#consume consumed}.
  *
  * @member {module:engine/conversion/viewconsumable~ViewConsumable} #consumable
  */
 
 /**
- * Custom data stored by converters for conversion process. Custom properties of this object can be defined and use to
+ * Custom data stored by converters for the conversion process. Custom properties of this object can be defined and use to
  * pass parameters between converters.
  *
- * The difference between this property and `data` parameter of
- * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element} is that `data` parameters allows you
+ * The difference between this property and the `data` parameter of
+ * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element} is that the `data` parameters allow you
  * to pass parameters within a single event and `store` within the whole conversion.
  *
  * @member {Object} #store
@@ -783,7 +786,7 @@ function createContextTree( contextDefinition, writer ) {
  */
 
 /**
- * The {@link module:engine/model/writer~Writer} instance used to manipulate data during conversion.
+ * The {@link module:engine/model/writer~Writer} instance used to manipulate the data during conversion.
  *
  * @member {module:engine/model/writer~Writer} #writer
  */
@@ -792,13 +795,13 @@ function createContextTree( contextDefinition, writer ) {
  * Conversion data.
  *
  * **Note:** Keep in mind that this object is shared by reference between all conversion callbacks that will be called.
- * This means that callbacks can override values if needed, and those values will be available in other callbacks.
+ * This means that callbacks can override values if needed, and these values will be available in other callbacks.
  *
  * @typedef {Object} module:engine/conversion/upcastdispatcher~UpcastConversionData
  *
- * @property {module:engine/view/item~Item} viewItem Converted item.
- * @property {module:engine/model/position~Position} modelCursor Position where a converter should start changes.
+ * @property {module:engine/view/item~Item} viewItem The converted item.
+ * @property {module:engine/model/position~Position} modelCursor The position where the converter should start changes.
  * Change this value for the next converter to tell where the conversion should continue.
  * @property {module:engine/model/range~Range} [modelRange] The current state of conversion result. Every change to
- * converted element should be reflected by setting or modifying this property.
+ * the converted element should be reflected by setting or modifying this property.
  */

+ 1 - 1
packages/ckeditor5-engine/src/model/schema.js

@@ -1256,7 +1256,7 @@ mix( Schema, ObservableMixin );
  * {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements} section of the Schema deep dive} guide.
  *
  * @property {Boolean} isContent
- * An item is a content when it always finds its way to editor data output regardless of the number and type of its descendants.
+ * An item is a content when it always finds its way to the editor data output regardless of the number and type of its descendants.
  * Examples of content elements: `$text`, `image`, `table`, etc. (but not `paragraph`, `heading1` or `tableCell`).
  *
  * **Note:** An object is also a content element, so

+ 3 - 4
packages/ckeditor5-engine/src/model/utils/insertcontent.js

@@ -443,12 +443,11 @@ class Insertion {
 				// At this point the insertion position should be after the node we'll merge. If it isn't,
 				// it should need to be secured as in the left merge case.
 				/**
-				 * An internal error occurred during merging inserted content with its siblings.
-				 * The insertion position should equal to the merge position.
+				 * An internal error occurred when merging inserted content with its siblings.
+				 * The insertion position should equal the merge position.
 				 *
 				 * If you encountered this error, report it back to the CKEditor 5 team
-				 * with as many details regarding the content being inserted and the insertion position
-				 * as possible.
+				 * with as many details as possible regarding the content being inserted and the insertion position.
 				 *
 				 * @error insertcontent-invalid-insertion-position
 				 */

+ 29 - 31
packages/ckeditor5-engine/src/view/downcastwriter.js

@@ -427,7 +427,7 @@ export default class DowncastWriter {
 	}
 
 	/**
-	 * Breaks attribute elements at provided position or at boundaries of a provided range. It breaks attribute elements
+	 * Breaks attribute elements at the provided position or at the boundaries of a provided range. It breaks attribute elements
 	 * up to their first ancestor that is a container element.
 	 *
 	 * In following examples `<p>` is a container, `<b>` and `<u>` are attribute elements:
@@ -439,31 +439,29 @@ export default class DowncastWriter {
 	 *
 	 * **Note:** {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.
 	 *
-	 * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes} and
-	 * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer} is that `breakAttributes` breaks all
-	 * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of given `position`,
+	 * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and
+	 * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all
+	 * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,
 	 * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.
-	 * `breakContainer` assumes that given `position` is directly in container element and breaks that container element.
+	 * `breakContainer()` assumes that a given `position` is directly in the container element and breaks that container element.
 	 *
-	 * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container`
-	 * when {@link module:engine/view/range~Range#start start}
+	 * Throws the `view-writer-invalid-range-container` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
+	 * when the {@link module:engine/view/range~Range#start start}
 	 * and {@link module:engine/view/range~Range#end end} positions of a passed range are not placed inside same parent container.
 	 *
-	 * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-empty-element`
-	 * when trying to break attributes
-	 * inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
+	 * Throws the `view-writer-cannot-break-empty-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
+	 * when trying to break attributes inside an {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
 	 *
-	 * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-ui-element`
-	 * when trying to break attributes
-	 * inside {@link module:engine/view/uielement~UIElement UIElement}.
+	 * Throws the `view-writer-cannot-break-ui-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
+	 * when trying to break attributes inside a {@link module:engine/view/uielement~UIElement UIElement}.
 	 *
 	 * @see module:engine/view/attributeelement~AttributeElement
 	 * @see module:engine/view/containerelement~ContainerElement
 	 * @see module:engine/view/downcastwriter~DowncastWriter#breakContainer
-	 * @param {module:engine/view/position~Position|module:engine/view/range~Range} positionOrRange Position where
+	 * @param {module:engine/view/position~Position|module:engine/view/range~Range} positionOrRange The position where
 	 * to break attribute elements.
-	 * @returns {module:engine/view/position~Position|module:engine/view/range~Range} New position or range, after breaking the attribute
-	 * elements.
+	 * @returns {module:engine/view/position~Position|module:engine/view/range~Range} The new position or range, after breaking the
+	 * attribute elements.
 	 */
 	breakAttributes( positionOrRange ) {
 		if ( positionOrRange instanceof Position ) {
@@ -474,27 +472,27 @@ export default class DowncastWriter {
 	}
 
 	/**
-	 * Breaks {@link module:engine/view/containerelement~ContainerElement container view element} into two, at the given position. Position
-	 * has to be directly inside container element and cannot be in root. Does not break if position is at the beginning
-	 * or at the end of it's parent element.
+	 * Breaks a {@link module:engine/view/containerelement~ContainerElement container view element} into two, at the given position.
+	 * The position has to be directly inside the container element and cannot be in the root. It does not break the conrainer view element
+	 * if the position is at the beginning or at the end of its parent element.
 	 *
 	 *		<p>foo^bar</p> -> <p>foo</p><p>bar</p>
 	 *		<div><p>foo</p>^<p>bar</p></div> -> <div><p>foo</p></div><div><p>bar</p></div>
 	 *		<p>^foobar</p> -> ^<p>foobar</p>
 	 *		<p>foobar^</p> -> <p>foobar</p>^
 	 *
-	 * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes} and
-	 * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer} is that `breakAttributes` breaks all
-	 * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of given `position`,
+	 * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and
+	 * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all
+	 * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,
 	 * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.
-	 * `breakContainer` assumes that given `position` is directly in container element and breaks that container element.
+	 * `breakContainer()` assumes that the given `position` is directly in the container element and breaks that container element.
 	 *
 	 * @see module:engine/view/attributeelement~AttributeElement
 	 * @see module:engine/view/containerelement~ContainerElement
 	 * @see module:engine/view/downcastwriter~DowncastWriter#breakAttributes
-	 * @param {module:engine/view/position~Position} position Position where to break element.
-	 * @returns {module:engine/view/position~Position} Position between broken elements. If element has not been broken,
-	 * the returned position is placed either before it or after it.
+	 * @param {module:engine/view/position~Position} position The position where to break the element.
+	 * @returns {module:engine/view/position~Position} The position between broken elements. If an element has not been broken,
+	 * the returned position is placed either before or after it.
 	 */
 	breakContainer( position ) {
 		const element = position.parent;
@@ -517,7 +515,7 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-break-root
 			 */
-			throw new CKEditorError( 'view-writer-break-root: Trying to break root element.', this.document );
+			throw new CKEditorError( 'view-writer-break-root: Trying to break a root element.', this.document );
 		}
 
 		if ( position.isAtStart ) {
@@ -653,7 +651,7 @@ export default class DowncastWriter {
 	}
 
 	/**
-	 * Insert node or nodes at specified position. Takes care about breaking attributes before insertion
+	 * Inserts a node or nodes at specified position. Takes care about breaking attributes before insertion
 	 * and merging them afterwards.
 	 *
 	 * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert
@@ -1927,7 +1925,7 @@ function validateNodesToInsert( nodes, errorContext ) {
 	for ( const node of nodes ) {
 		if ( !validNodesToInsert.some( ( validNode => node instanceof validNode ) ) ) { // eslint-disable-line no-use-before-define
 			/**
-			 * One of the nodes to be inserted is of invalid type.
+			 * One of the nodes to be inserted is of an invalid type.
 			 *
 			 * Nodes to be inserted with {@link module:engine/view/downcastwriter~DowncastWriter#insert `DowncastWriter#insert()`} should be
 			 * of the following types:
@@ -1942,7 +1940,7 @@ function validateNodesToInsert( nodes, errorContext ) {
 			 * @error view-writer-insert-invalid-node-type
 			 */
 			throw new CKEditorError(
-				'view-writer-insert-invalid-node-type: One of the nodes to be inserted is of invalid type.',
+				'view-writer-insert-invalid-node-type: One of the nodes to be inserted is of an invalid type.',
 				errorContext
 			);
 		}
@@ -1985,7 +1983,7 @@ function validateRangeContainer( range, errorContext ) {
 		 * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#clean()`},
 		 * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#wrap()`},
 		 * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#unwrap()`} need to be called
-		 * on a range that have its start and end positions located in the same container element. Both positions can be
+		 * on a range that has its start and end positions located in the same container element. Both positions can be
 		 * nested within other elements (e.g. an attribute element) but the closest container ancestor must be the same.
 		 *
 		 * @error view-writer-invalid-range-container

+ 0 - 1
packages/ckeditor5-image/src/image/imageediting.js

@@ -113,7 +113,6 @@ export default class ImageEditing extends Plugin {
 			} )
 			.add( viewFigureToModel() );
 
-		// Register imageUpload command.
 		editor.commands.add( 'imageInsert', new ImageInsertCommand( editor ) );
 	}
 }

+ 1 - 1
packages/ckeditor5-image/src/imageupload/ui/imageuploadpanelview.js

@@ -218,7 +218,7 @@ export default class ImageUploadPanelView extends View {
 	/**
 	 * Returns a view of the integration.
 	 *
-	 * @param {string} name The name of the integration.
+	 * @param {String} name The name of the integration.
 	 * @returns {module:ui/view~View}
 	 */
 	getIntegration( name ) {

+ 10 - 12
packages/ckeditor5-list/docs/_snippets/features/lists-basic.html

@@ -1,20 +1,18 @@
 <div id="snippet-lists-basic">
-    <h2>Unordered list</h2>
-    <p>Such a list can represent items where the order is not important, e.g. a list of ingredients required for preparing a dish or a drink.</p>
+    <h2>Ingredients</h2>
     <p>Ingredients required for making a coffee:</p>
     <ul>
-        <li>15g coffee ground</li>
-        <li>a cup</li>
+        <li>15g ground coffee</li>
         <li>water</li>
+        <li>milk <i>(optional)</i></li>
     </ul>
-    <h2>Ordered list</h2>
-    <p>The ordered list can be used if the order of the items matters, e.g. when describing an instruction. The order of steps that must be done is important.</p>
-    <p>How to prepare the coffee?</p>
+    <h2>Instructions</h2>
+    <p>How to prepare a cup of coffee:</p>
     <ol>
-        <li>Gather the ingredients</li>
-        <li>Put 15g of the coffee ground into the cup</li>
-        <li>Boil 200ml of water</li>
-        <li>Pour water into a cup</li>
-        <li>Optional: add milk as you wish</li>
+        <li>Gather the ingredients.</li>
+        <li>Put 15g of ground coffee in the cup.</li>
+        <li>Boil 200ml of water.</li>
+        <li>Pour the water into the cup.</li>
+        <li>Optional: Add milk.</li>
     </ol>
 </div>

+ 10 - 9
packages/ckeditor5-list/docs/_snippets/features/lists-style.html

@@ -1,17 +1,18 @@
 <div id="snippet-lists-styles">
-    <p>Let's use the coffee recipe from the demo in section <a href="#demo">Ordered and unordered lists</a> and use the list styles feature.</p>
+    <h2>Ingredients</h2>
     <p>Ingredients required for making a coffee:</p>
     <ul style="list-style-type:circle;">
-        <li>15g coffee ground</li>
-        <li>a cup</li>
+        <li>15g ground coffee</li>
         <li>water</li>
+        <li>milk <i>(optional)</i></li>
     </ul>
-    <p>How to prepare the coffee?</p>
+    <h2>Instructions</h2>
+    <p>How to prepare a cup of coffee:</p>
     <ol style="list-style-type:decimal-leading-zero;">
-        <li>Gather the ingredients</li>
-        <li>Put 15g of the coffee ground into the cup</li>
-        <li>Boil 200ml of water</li>
-        <li>Pour water into a cup</li>
-        <li>Optional: add milk as you wish</li>
+        <li>Gather the ingredients.</li>
+        <li>Put 15g of ground coffee in the cup.</li>
+        <li>Boil 200ml of water.</li>
+        <li>Pour the water into the cup.</li>
+        <li>Optional: Add milk.</li>
     </ol>
 </div>

+ 3 - 3
packages/ckeditor5-list/docs/api/list.md

@@ -6,15 +6,15 @@ category: api-reference
 
 [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-list.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-list)
 
-This package implements bulleted, numbered and to-do list feature for CKEditor 5.
+This package implements bulleted, numbered and to-do list features for CKEditor 5.
 
 ## Documentation
 
-See the {@link module:list/list~List} plugin documentation and {@link features/todo-lists to-do list feature} guide.
+See the {@link module:list/list~List} plugin documentation as well as the {@link features/lists lists} and {@link features/todo-lists to-do list} feature guides.
 
 ## Installation
 
-```bash
+```
 npm install --save @ckeditor/ckeditor5-list
 ```
 

+ 17 - 9
packages/ckeditor5-list/docs/features/lists.md

@@ -4,7 +4,7 @@ category: features
 
 # Lists
 
-The {@link module:list/list~List list} feature allows creating ordered and unordered lists in the editor.
+The {@link module:list/list~List list} feature allows creating ordered (numbered) and unordered (bulleted) lists in the editor.
 
 <info-box info>
 	The feature is enabled by default in all CKEditor 5 WYSIWYG editor builds.
@@ -14,6 +14,10 @@ The {@link module:list/list~List list} feature allows creating ordered and unord
 
 ## Ordered and unordered lists
 
+An unordered (bulleted) list can represent items where the order is not important, for example, a list of ingredients required for preparing a dish or a drink.
+
+An ordered (numbered) list can be used if the order of the items matters, for example, when creating an instruction. Here, the sequence of steps that must be done is important.
+
 Use the editor below to see the list feature plugin in action.
 
 ### Demo
@@ -22,11 +26,11 @@ Use the editor below to see the list feature plugin in action.
 
 ## List styles
 
-The {@link module:list/liststyle~ListStyle list styles} feature allows customizing the list's marker.
+The {@link module:list/liststyle~ListStyle list style} feature allows customizing the list item markers. When enabled, it adds 3 styles for unordered lists and 6 styles for ordered lists to choose from. The styles can be changed via the dropdown that opens when you click the arrow next to the appropriate list button in the toolbar.
 
 ### Demo
 
-Use the editor below to see the list styles feature plugin in action.
+Use the editor below to see the list style plugin in action.
 
 {@snippet features/lists-style}
 
@@ -34,7 +38,7 @@ Use the editor below to see the list styles feature plugin in action.
 
 To add this feature to your editor, install the [`@ckeditor/ckeditor5-list`](https://www.npmjs.com/package/@ckeditor/ckeditor5-list) package:
 
-```bash
+```
 npm install --save @ckeditor/ckeditor5-list
 ```
 
@@ -60,6 +64,10 @@ ClassicEditor
 	The {@link module:list/liststyle~ListStyle} feature overrides UI button implementations from the {@link module:list/listui~ListUI}.
 </info-box>
 
+## List indentation
+
+Refer to the {@link features/indent Indenting lists} section of the Block indentation feature guide.
+
 ## To-do list
 
 You can read more about the feature in the {@link features/todo-lists To-do lists} feature guide.
@@ -72,12 +80,12 @@ The {@link module:list/list~List} plugin registers:
 * The {@link module:list/listcommand~ListCommand `'bulletedList'`} command.
 * The {@link module:list/indentcommand~IndentCommand `'indentList'`} command.
 * The {@link module:list/indentcommand~IndentCommand `'outdentList'`} command.
-* The `'numberedList'` ui button.
-* The `'bulletedList'` ui button.
+* The `'numberedList'` UI button.
+* The `'bulletedList'` UI button.
 
 The {@link module:list/liststyle~ListStyle} plugin registers:
 
-* The {@link module:list/liststylecommand~ListStyleCommand `'listStyle'`} command that accepts a `type` of a list style to set.
+* The {@link module:list/liststylecommand~ListStyleCommand `'listStyle'`} command that accepts a `type` of the list style to set.
     ```js
     editor.execute( 'listStyle', { type: 'decimal' } );
     ```
@@ -85,8 +93,8 @@ The {@link module:list/liststyle~ListStyle} plugin registers:
 
     * For bulleted lists: `'disc'`, `'circle'`, and `'square'`.
     * For numbered lists: `'decimal'`, `'decimal-leading-zero'`, `'lower-roman'`, `'upper-roman'`, `'lower-latin'`, and `'upper-latin'`.
-* The `'numberedList'` ui split button (it overrides UI button registered by the `List` plguin.
-* The `'bulletedList'` ui split button (it overrides UI button registered by the `List` plguin.
+* The `'numberedList'` UI split button (it overrides the UI button registered by the `List` plguin.
+* The `'bulletedList'` UI split button (it overrides the UI button registered by the `List` plguin.
 
 ## Contribute
 

+ 2 - 2
packages/ckeditor5-list/docs/features/todo-lists.md

@@ -4,7 +4,7 @@ category: features
 
 # To-do lists
 
-The {@link module:list/todolist~TodoList to-do list} feature lets you create a list of interactive checkboxes with labels. It supports all features of regular lists so you can nest a to-do list together with bulleted and numbered lists in any combination.
+The {@link module:list/todolist~TodoList to-do list} feature lets you create a list of interactive checkboxes with labels. It supports all features of regular lists so you can nest a to-do list together with {@link features/lists bulleted and numbered lists} in any combination.
 
 ## Demo
 
@@ -90,7 +90,7 @@ From the technical point of view, to-do lists are built on top of the {@link mod
 
 ## Ordered and unordered lists
 
-You can read more about those features in the {@link features/lists Lists} feature guide.
+You can read more about these features in the {@link features/lists Lists} feature guide.
 
 ## Contribute
 

+ 48 - 41
packages/ckeditor5-list/src/liststyleediting.js

@@ -189,7 +189,6 @@ export default class ListStyleEditing extends Plugin {
 // Returns a converter that consumes the `style` attribute and search for `list-style-type` definition.
 // If not found, the `"default"` value will be used.
 //
-// @private
 // @returns {Function}
 function upcastListItemStyle() {
 	return dispatcher => {
@@ -206,7 +205,6 @@ function upcastListItemStyle() {
 // Returns a converter that adds the `list-style-type` definition as a value for the `style` attribute.
 // The `"default"` value is removed and not present in the view/data.
 //
-// @private
 // @returns {Function}
 function downcastListStyleAttribute() {
 	return dispatcher => {
@@ -271,7 +269,6 @@ function downcastListStyleAttribute() {
 //     ○ List item 2.[]
 // ■ List item 3.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListAfterIndentListCommand( editor ) {
@@ -323,7 +320,6 @@ function fixListAfterIndentListCommand( editor ) {
 // ■ List item 2.[]
 // ■ List item 3.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListAfterOutdentListCommand( editor ) {
@@ -423,22 +419,17 @@ function fixListAfterOutdentListCommand( editor ) {
 // ■ List item 2.  // ...
 // ■ List item 3.  // ...
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListStyleAttributeOnListItemElements( editor ) {
 	return writer => {
 		let wasFixed = false;
-		let insertedListItems = [];
 
-		for ( const change of editor.model.document.differ.getChanges() ) {
-			if ( change.type == 'insert' && change.name == 'listItem' ) {
-				insertedListItems.push( change.position.nodeAfter );
-			}
-		}
-
-		// Don't touch todo lists.
-		insertedListItems = insertedListItems.filter( item => item.getAttribute( 'listType' ) !== 'todo' );
+		const insertedListItems = getChangedListItems( editor.model.document.differ.getChanges() )
+			.filter( item => {
+				// Don't touch todo lists. They are handled in another post-fixer.
+				return item.getAttribute( 'listType' ) !== 'todo';
+			} );
 
 		if ( !insertedListItems.length ) {
 			return wasFixed;
@@ -474,7 +465,7 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 
 		for ( const item of insertedListItems ) {
 			if ( !item.hasAttribute( 'listStyle' ) ) {
-				if ( shouldInheritListType( existingListItem ) ) {
+				if ( shouldInheritListType( existingListItem, item ) ) {
 					writer.setAttribute( 'listStyle', existingListItem.getAttribute( 'listStyle' ), item );
 				} else {
 					writer.setAttribute( 'listStyle', DEFAULT_LIST_TYPE, item );
@@ -493,8 +484,9 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 	// the value for the element is other than default in the base element.
 	//
 	// @param {module:engine/model/element~Element|null} baseItem
+	// @param {module:engine/model/element~Element} itemToChange
 	// @returns {Boolean}
-	function shouldInheritListType( baseItem ) {
+	function shouldInheritListType( baseItem, itemToChange ) {
 		if ( !baseItem ) {
 			return false;
 		}
@@ -509,28 +501,25 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 			return false;
 		}
 
+		if ( baseItem.getAttribute( 'listType' ) !== itemToChange.getAttribute( 'listType' ) ) {
+			return false;
+		}
+
 		return true;
 	}
 }
 
 // Removes the `listStyle` attribute from "todo" list items.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function removeListStyleAttributeFromTodoList( editor ) {
 	return writer => {
-		let todoListItems = [];
-
-		for ( const change of editor.model.document.differ.getChanges() ) {
-			const item = getItemFromChange( change );
-
-			if ( item && item.is( 'element', 'listItem' ) && item.getAttribute( 'listType' ) === 'todo' ) {
-				todoListItems.push( item );
-			}
-		}
-
-		todoListItems = todoListItems.filter( item => item.hasAttribute( 'listStyle' ) );
+		const todoListItems = getChangedListItems( editor.model.document.differ.getChanges() )
+			.filter( item => {
+				// Handle the todo lists only. The rest is handled in another post-fixer.
+				return item.getAttribute( 'listType' ) === 'todo' && item.hasAttribute( 'listStyle' );
+			} );
 
 		if ( !todoListItems.length ) {
 			return false;
@@ -542,23 +531,10 @@ function removeListStyleAttributeFromTodoList( editor ) {
 
 		return true;
 	};
-
-	function getItemFromChange( change ) {
-		if ( change.type === 'attribute' ) {
-			return change.range.start.nodeAfter;
-		}
-
-		if ( change.type === 'insert' ) {
-			return change.position.nodeAfter;
-		}
-
-		return null;
-	}
 }
 
 // Restores the `listStyle` attribute after changing the list type.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function restoreDefaultListStyle( editor ) {
@@ -573,3 +549,34 @@ function restoreDefaultListStyle( editor ) {
 		} );
 	};
 }
+
+// Returns `listItem` that were inserted or changed.
+//
+// @param {Array.<Object>} changes The changes list returned by the differ.
+// @returns {Array.<module:engine/model/element~Element>}
+function getChangedListItems( changes ) {
+	const items = [];
+
+	for ( const change of changes ) {
+		const item = getItemFromChange( change );
+
+		if ( item && item.is( 'element', 'listItem' ) ) {
+			items.push( item );
+		}
+	}
+
+	return items;
+}
+
+function getItemFromChange( change ) {
+	if ( change.type === 'attribute' ) {
+		return change.range.start.nodeAfter;
+	}
+
+	if ( change.type === 'insert' ) {
+		return change.position.nodeAfter;
+	}
+
+	return null;
+}
+

+ 92 - 4
packages/ckeditor5-list/tests/liststyleediting.js

@@ -452,19 +452,38 @@ describe( 'ListStyleEditing', () => {
 			it( 'should not inherit the list style attribute when merging different kind of lists (from top, merge a single item)', () => {
 				setModelData( model,
 					'<paragraph>Foo Bar.[]</paragraph>' +
-					'<listItem listIndent="0" listStyle="default"  listType="numbered">Foo</listItem>' +
-					'<listItem listIndent="0" listStyle="default"  listType="numbered">Bar</listItem>'
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
 				);
 
 				editor.execute( 'bulletedList' );
 
 				expect( getModelData( model ) ).to.equal(
 					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
-					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo</listItem>' +
-					'<listItem listIndent="0" listStyle="default" listType="numbered">Bar</listItem>'
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
 				);
 			} );
 
+			it(
+				'should not inherit the list style attribute when merging different kind of lists (from bottom, merge a single item)',
+				() => {
+					setModelData( model,
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
+						'<paragraph>Foo Bar.[]</paragraph>'
+					);
+
+					editor.execute( 'bulletedList' );
+
+					expect( getModelData( model ) ).to.equal(
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+				}
+			);
+
 			it( 'should inherit the list style attribute when merging the same kind of lists (from bottom, merge a single item)', () => {
 				setModelData( model,
 					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
@@ -503,6 +522,75 @@ describe( 'ListStyleEditing', () => {
 			);
 		} );
 
+		describe( 'modifying "listType" attribute', () => {
+			it( 'should inherit the list style attribute when the modified list is the same kind of the list as next sibling', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				editor.execute( 'bulletedList' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should inherit the list style attribute when the modified list is the same kind of the list as previous sibling', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>'
+				);
+
+				editor.execute( 'bulletedList' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'
+				);
+			} );
+
+			it( 'should not inherit the list style attribute when the modified list already has defined it (next sibling check)', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				editor.execute( 'listStyle', { type: 'disc' } );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it(
+				'should not inherit the list style attribute when the modified list already has defined it (previous sibling check)',
+				() => {
+					setModelData( model,
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+
+					editor.execute( 'listStyle', { type: 'disc' } );
+
+					expect( getModelData( model ) ).to.equal(
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+				}
+			);
+		} );
+
 		describe( 'indenting lists', () => {
 			it( 'should restore the default value for the list style attribute when indenting a single item', () => {
 				setModelData( model,

+ 1 - 1
packages/ckeditor5-table/src/converters/upcasttable.js

@@ -85,7 +85,7 @@ export function skipEmptyTableRow() {
 }
 
 /**
- * Converter that ensures empty paragraph is inserted in a table cell if no other content was converted.
+ * A converter that ensures an empty paragraph is inserted in a table cell if no other content was converted.
  *
  * @returns {Function} Conversion helper.
  */

+ 1 - 1
packages/ckeditor5-table/src/tablewalker.js

@@ -532,7 +532,7 @@ class TableSlot {
  */
 
 // @if CK_DEBUG // function throwMissingGetterError( getterName ) {
-// @if CK_DEBUG //		throw new CKEditorError( 'tableslot-getter-removed: This TableSlot getter does not exist any more.', this, {
+// @if CK_DEBUG //		throw new CKEditorError( 'tableslot-getter-removed: This TableSlot getter does not exist anymore.', this, {
 // @if CK_DEBUG //			getterName
 // @if CK_DEBUG //		} );
 // @if CK_DEBUG // }

+ 14 - 14
packages/ckeditor5-utils/src/collection.js

@@ -202,7 +202,7 @@ export default class Collection {
 		} else if ( index > this._items.length || index < 0 ) {
 			/**
 			 * The `index` passed to {@link module:utils/collection~Collection#addMany `Collection#addMany()`}
-			 * is invalid. It must be a number between 0 and the the collection's length.
+			 * is invalid. It must be a number between 0 and the collection's length.
 			 *
 			 * @error collection-add-item-bad-index
 			 */
@@ -230,9 +230,9 @@ export default class Collection {
 	}
 
 	/**
-	 * Gets item by its id or index.
+	 * Gets an item by its ID or index.
 	 *
-	 * @param {String|Number} idOrIndex The item id or index in the collection.
+	 * @param {String|Number} idOrIndex The item ID or index in the collection.
 	 * @returns {Object|null} The requested item or `null` if such item does not exist.
 	 */
 	get( idOrIndex ) {
@@ -244,20 +244,20 @@ export default class Collection {
 			item = this._items[ idOrIndex ];
 		} else {
 			/**
-			 * Index or id must be given.
+			 * An index or ID must be given.
 			 *
 			 * @error collection-get-invalid-arg
 			 */
-			throw new CKEditorError( 'collection-get-invalid-arg: Index or id must be given.', this );
+			throw new CKEditorError( 'collection-get-invalid-arg: An index or ID must be given.', this );
 		}
 
 		return item || null;
 	}
 
 	/**
-	 * Returns a boolean indicating whether the collection contains an item.
+	 * Returns a Boolean indicating whether the collection contains an item.
 	 *
-	 * @param {Object|String} itemOrId The item or its id in the collection.
+	 * @param {Object|String} itemOrId The item or its ID in the collection.
 	 * @returns {Boolean} `true` if the collection contains the item, `false` otherwise.
 	 */
 	has( itemOrId ) {
@@ -272,11 +272,11 @@ export default class Collection {
 	}
 
 	/**
-	 * Gets index of item in the collection.
-	 * When item is not defined in the collection then index will be equal -1.
+	 * Gets an index of an item in the collection.
+	 * When an item is not defined in the collection, the index will equal -1.
 	 *
-	 * @param {Object|String} itemOrId The item or its id in the collection.
-	 * @returns {Number} Index of given item.
+	 * @param {Object|String} itemOrId The item or its ID in the collection.
+	 * @returns {Number} The index of a given item.
 	 */
 	getIndex( itemOrId ) {
 		let item;
@@ -293,7 +293,7 @@ export default class Collection {
 	/**
 	 * Removes an item from the collection.
 	 *
-	 * @param {Object|Number|String} subject The item to remove, its id or index in the collection.
+	 * @param {Object|Number|String} subject The item to remove, its ID or index in the collection.
 	 * @returns {Object} The removed item.
 	 * @fires remove
 	 * @fires change
@@ -642,11 +642,11 @@ export default class Collection {
 
 			if ( typeof itemId != 'string' ) {
 				/**
-				 * This item's id should be a string.
+				 * This item's ID should be a string.
 				 *
 				 * @error collection-add-invalid-id
 				 */
-				throw new CKEditorError( 'collection-add-invalid-id: This item\'s id should be a string.', this );
+				throw new CKEditorError( 'collection-add-invalid-id: This item\'s ID should be a string.', this );
 			}
 
 			if ( this.get( itemId ) ) {

+ 6 - 6
scripts/docs/build-docs.js

@@ -27,12 +27,7 @@ function buildDocs() {
 	if ( skipApi ) {
 		promise = Promise.resolve();
 	} else {
-		promise = buildApiDocs()
-			.catch( err => {
-				console.error( err );
-
-				process.exitCode = 1;
-			} );
+		promise = buildApiDocs();
 	}
 
 	promise
@@ -45,6 +40,11 @@ function buildDocs() {
 				watch,
 				verbose
 			} );
+		} )
+		.catch( err => {
+			console.error( err );
+
+			process.exitCode = 1;
 		} );
 }
 

+ 2 - 1
scripts/docs/buildapi.js

@@ -23,6 +23,7 @@ module.exports = function buildApiDocs() {
 				'!' + process.cwd() + '/external/*/packages/@(ckeditor|ckeditor5)-*/src/lib/**/*.js',
 				'!' + process.cwd() + '/external/*/packages/ckeditor5-build-*/src/**/*.js'
 			],
-			validateOnly: process.argv.includes( '--validate-only' )
+			validateOnly: process.argv.includes( '--validate-only' ),
+			strict: process.argv.includes( '--strict' )
 		} );
 };