Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

engine/model reorganization. #1203

Merged
merged 54 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c7a0fc5
Introduce model class.
Dec 1, 2017
b45e815
Use model instead of the document in controllers.
Dec 1, 2017
0a7e6e2
Update usage of the new API in data controller. Make batch a optional…
Dec 1, 2017
8edd0f3
Splited Batch to Writer and Batch.
oskarwrobel Dec 1, 2017
11d01a1
Batch should be created only through change block.
Dec 1, 2017
60b679a
Change block now creates writer.
Dec 1, 2017
d72cddd
Added tests for change and enqueueChange methods. Change writer.batch…
Dec 1, 2017
1a7e060
Added model instance to the Documentselection.
oskarwrobel Dec 1, 2017
b750c67
Fixed Writer failing tests.
oskarwrobel Dec 1, 2017
f914510
Refactored conversion to use writer instead of batch.
oskarwrobel Dec 4, 2017
f46b04c
Aligned delete content with the engine changes.
oskarwrobel Dec 4, 2017
61e0e8f
Aligned code with the engine changes.
oskarwrobel Dec 4, 2017
b646542
Changed DataController to use model.change.
oskarwrobel Dec 5, 2017
c5145d1
Changed enqueueChange to change.
oskarwrobel Dec 5, 2017
b7a5b3d
Added changesDone event to document - backward compatibility.
oskarwrobel Dec 5, 2017
0b8c6d8
Aligned model dev-utils with engine changes.
oskarwrobel Dec 7, 2017
8dc3030
Fixed conversion tests.
oskarwrobel Dec 7, 2017
cf2c9a8
Fixed failing DataController tests.
oskarwrobel Dec 7, 2017
92cdaab
Fixed failing EditingController tests.
oskarwrobel Dec 8, 2017
1a439f2
Fixed failing deleteContent util tests.
oskarwrobel Dec 8, 2017
b38d8c1
Fixed failing getSelectedContent util tests.
oskarwrobel Dec 8, 2017
0122183
Fixed failing deleteContent util tests.
oskarwrobel Dec 8, 2017
e1f1a44
Improved DataController utils test.
oskarwrobel Dec 8, 2017
76c5852
Aligned DeltaReplayer with engine changes.
oskarwrobel Dec 8, 2017
0e13e7d
Aligned Deltas with engine changes.
oskarwrobel Dec 9, 2017
ded76f6
Typo.
oskarwrobel Dec 10, 2017
4468388
Aligned Document tests with engine changes.
oskarwrobel Dec 10, 2017
98d6506
Aligned view tests with engine changes.
oskarwrobel Dec 10, 2017
849fa3a
Aligned operation tests with engine changes.
oskarwrobel Dec 10, 2017
b60affb
Added destroy method to Model class.
oskarwrobel Dec 10, 2017
f20145d
Aligned DocumentSelection tests with engine changes.
oskarwrobel Dec 10, 2017
4b67d12
Fixed failing Model test.
oskarwrobel Dec 11, 2017
04caf1c
Aligned schema tests with engine changes.
oskarwrobel Dec 11, 2017
847153a
Aligned tests with engine changes.
oskarwrobel Dec 11, 2017
68f3609
Aligned tests with engine changes.
oskarwrobel Dec 11, 2017
caba101
Increased model CC.
oskarwrobel Dec 11, 2017
f099942
Merge branch 'master' into t/1186
oskarwrobel Dec 11, 2017
75c0db5
Fixed failing ticket test.
oskarwrobel Dec 11, 2017
ba42189
Temporarily skipped EngineDebug test.
oskarwrobel Dec 11, 2017
6d60c71
Docs for model, document, writer and batch.
Dec 12, 2017
cde21de
Fix: Don't register LiveRange as listeningTo in `DocumentSelection._p…
jodator Nov 9, 2017
ef23b8c
Added a 50ms timeout after focus before rendering.
ma2ciek Dec 11, 2017
3a7a820
Imporved docs.
oskarwrobel Dec 12, 2017
d663a13
Fixed incorrect document reference.
oskarwrobel Dec 12, 2017
b19b1fe
Changed incorrect markers collection reference.
oskarwrobel Dec 12, 2017
cf9e084
Fixed typo in Model tests.
oskarwrobel Dec 12, 2017
2ae88be
Increased Model class CC.
oskarwrobel Dec 12, 2017
85b8de5
Improved docs.
oskarwrobel Dec 12, 2017
f41f4ef
Aligned manual tests with engine chamges.
oskarwrobel Dec 12, 2017
e966841
Fixed invalid docs.
oskarwrobel Dec 12, 2017
8dc4bbc
Merge branch 'master' into t/1186
oskarwrobel Dec 13, 2017
8bc19cc
Aligned EngineDebug with engine changes.
oskarwrobel Dec 13, 2017
cffce8a
Removed model from DocumentSelection params.
oskarwrobel Dec 14, 2017
9bc974a
Docs for applyOperation event.
Dec 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 21 additions & 30 deletions src/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ export default class DataController {
/**
* Creates data controller instance.
*
* @param {module:engine/model/document~Document} model Document model.
* @param {module:engine/model/model~Model} model Data model.
* @param {module:engine/dataprocessor/dataprocessor~DataProcessor} [dataProcessor] Data processor which should used by the controller.
*/
constructor( model, dataProcessor ) {
/**
* Document model.
* Data model.
*
* @readonly
* @member {module:engine/model/document~Document}
* @member {module:engine/model/model~Model}
*/
this.model = model;

Expand Down Expand Up @@ -104,7 +104,7 @@ export default class DataController {
* @readonly
* @member {module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher}
*/
this.viewToModel = new ViewConversionDispatcher( {
this.viewToModel = new ViewConversionDispatcher( this.model, {
schema: model.schema
} );

Expand All @@ -130,7 +130,7 @@ export default class DataController {
*/
get( rootName = 'main' ) {
// Get model range.
return this.stringify( this.model.getRoot( rootName ) );
return this.stringify( this.model.document.getRoot( rootName ) );
}

/**
Expand Down Expand Up @@ -186,19 +186,16 @@ export default class DataController {
*/
set( data, rootName = 'main' ) {
// Save to model.
const modelRoot = this.model.getRoot( rootName );
const modelRoot = this.model.document.getRoot( rootName );

this.model.enqueueChanges( () => {
this.model.enqueueChange( 'transparent', writer => {
// Clearing selection is a workaround for ticket #569 (LiveRange loses position after removing data from document).
// After fixing it this code should be removed.
this.model.selection.removeAllRanges();
this.model.selection.clearAttributes();
this.model.document.selection.removeAllRanges();
this.model.document.selection.clearAttributes();

// Initial batch should be ignored by features like undo, etc.
const batch = this.model.batch( 'transparent' );

batch.remove( ModelRange.createIn( modelRoot ) );
batch.insert( this.parse( data, batch ), modelRoot );
writer.remove( ModelRange.createIn( modelRoot ) );
writer.insert( this.parse( data ), modelRoot );
} );
}

Expand All @@ -208,17 +205,16 @@ export default class DataController {
*
* @see #set
* @param {String} data Data to parse.
* @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added.
* @param {String} [context='$root'] Base context in which the view will be converted to the model. See:
* {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#convert}.
* @returns {module:engine/model/documentfragment~DocumentFragment} Parsed data.
*/
parse( data, batch, context = '$root' ) {
parse( data, context = '$root' ) {
// data -> view
const viewDocumentFragment = this.processor.toView( data );

// view -> model
return this.toModel( viewDocumentFragment, batch, context );
return this.toModel( viewDocumentFragment, context );
}

/**
Expand All @@ -232,13 +228,12 @@ export default class DataController {
*
* @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElementOrFragment
* Element or document fragment which content will be converted.
* @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added.
* @param {String} [context='$root'] Base context in which the view will be converted to the model. See:
* {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#convert}.
* @returns {module:engine/model/documentfragment~DocumentFragment} Output document fragment.
*/
toModel( viewElementOrFragment, batch, context = '$root' ) {
return this.viewToModel.convert( viewElementOrFragment, batch, { context: [ context ] } );
toModel( viewElementOrFragment, context = '$root' ) {
return this.viewToModel.convert( viewElementOrFragment, { context: [ context ] } );
}

/**
Expand All @@ -252,11 +247,9 @@ export default class DataController {
* @fires insertContent
* @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
* @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted.
* @param {module:engine/model/batch~Batch} [batch] Batch to which deltas will be added. If not specified, then
* changes will be added to a new batch.
*/
insertContent( content, selection, batch ) {
insertContent( this, content, selection, batch );
insertContent( content, selection ) {
insertContent( this, content, selection );
}

/**
Expand All @@ -272,11 +265,10 @@ export default class DataController {
*
* @fires deleteContent
* @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted.
* @param {module:engine/model/batch~Batch} batch Batch to which deltas will be added.
* @param {Object} options See {@link module:engine/controller/deletecontent~deleteContent}'s options.
*/
deleteContent( selection, batch, options ) {
deleteContent( selection, batch, options );
deleteContent( selection, options ) {
deleteContent( this, selection, options );
}

/**
Expand All @@ -295,11 +287,10 @@ export default class DataController {
*
* @fires module:engine/controller/datacontroller~DataController#getSelectedContent
* @param {module:engine/model/selection~Selection} selection The selection of which content will be retrieved.
* @param {module:engine/model/batch~Batch} batch Batch to which deltas will be added.
* @returns {module:engine/model/documentfragment~DocumentFragment} Document fragment holding the clone of the selected content.
*/
getSelectedContent( selection, batch ) {
return getSelectedContent( selection, batch );
getSelectedContent( selection ) {
return getSelectedContent( this, selection );
}

/**
Expand Down
112 changes: 57 additions & 55 deletions src/controller/deletecontent.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import LivePosition from '../model/liveposition';
import Position from '../model/position';
import Range from '../model/range';
import Element from '../model/element';

/**
* Deletes content of the selection and merge siblings. The resulting selection is always collapsed.
*
* @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which the insertion
* should be performed.
* @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted.
* @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added.
* @param {Object} [options]
Expand All @@ -36,63 +37,65 @@ import Element from '../model/element';
* * `<paragraph>^</paragraph>` with the option disabled (`doNotResetEntireContent == false`)
* * `<heading>^</heading>` with enabled (`doNotResetEntireContent == true`).
*/
export default function deleteContent( selection, batch, options = {} ) {
export default function deleteContent( dataController, selection, options = {} ) {
if ( selection.isCollapsed ) {
return;
}

const schema = batch.document.schema;
const schema = dataController.model.schema;

// 1. Replace the entire content with paragraph.
// See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594.
if ( !options.doNotResetEntireContent && shouldEntireContentBeReplacedWithParagraph( schema, selection ) ) {
replaceEntireContentWithParagraph( batch, selection );
dataController.model.change( writer => {
// 1. Replace the entire content with paragraph.
// See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594.
if ( !options.doNotResetEntireContent && shouldEntireContentBeReplacedWithParagraph( schema, selection ) ) {
replaceEntireContentWithParagraph( writer, selection, schema );

return;
}
return;
}

const selRange = selection.getFirstRange();
const startPos = selRange.start;
const endPos = LivePosition.createFromPosition( selRange.end );
const selRange = selection.getFirstRange();
const startPos = selRange.start;
const endPos = LivePosition.createFromPosition( selRange.end );

// 2. Remove the content if there is any.
if ( !selRange.start.isTouching( selRange.end ) ) {
batch.remove( selRange );
}
// 2. Remove the content if there is any.
if ( !selRange.start.isTouching( selRange.end ) ) {
writer.remove( selRange );
}

// 3. Merge elements in the right branch to the elements in the left branch.
// The only reasonable (in terms of data and selection correctness) case in which we need to do that is:
//
// <heading type=1>Fo[</heading><paragraph>]ar</paragraph> => <heading type=1>Fo^ar</heading>
//
// However, the algorithm supports also merging deeper structures (up to the depth of the shallower branch),
// as it's hard to imagine what should actually be the default behavior. Usually, specific features will
// want to override that behavior anyway.
if ( !options.leaveUnmerged ) {
mergeBranches( batch, startPos, endPos );

// We need to check and strip disallowed attributes in all nested nodes because after merge
// some attributes could end up in a path where are disallowed.
// 3. Merge elements in the right branch to the elements in the left branch.
// The only reasonable (in terms of data and selection correctness) case in which we need to do that is:
//
// e.g. bold is disallowed for <H1>
// <h1>Fo{o</h1><p>b}a<b>r</b><p> -> <h1>Fo{}a<b>r</b><h1> -> <h1>Fo{}ar<h1>.
schema.removeDisallowedAttributes( startPos.parent.getChildren(), startPos, batch );
}
// <heading type=1>Fo[</heading><paragraph>]ar</paragraph> => <heading type=1>Fo^ar</heading>
//
// However, the algorithm supports also merging deeper structures (up to the depth of the shallower branch),
// as it's hard to imagine what should actually be the default behavior. Usually, specific features will
// want to override that behavior anyway.
if ( !options.leaveUnmerged ) {
mergeBranches( writer, startPos, endPos );

// We need to check and strip disallowed attributes in all nested nodes because after merge
// some attributes could end up in a path where are disallowed.
//
// e.g. bold is disallowed for <H1>
// <h1>Fo{o</h1><p>b}a<b>r</b><p> -> <h1>Fo{}a<b>r</b><h1> -> <h1>Fo{}ar<h1>.
schema.removeDisallowedAttributes( startPos.parent.getChildren(), startPos, writer );
}

selection.setCollapsedAt( startPos );
selection.setCollapsedAt( startPos );

// 4. Autoparagraphing.
// Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here).
if ( shouldAutoparagraph( schema, startPos ) ) {
insertParagraph( batch, startPos, selection );
}
// 4. Autoparagraphing.
// Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here).
if ( shouldAutoparagraph( schema, startPos ) ) {
insertParagraph( writer, startPos, selection );
}

endPos.detach();
endPos.detach();
} );
}

// This function is a result of reaching the Ballmer's peak for just the right amount of time.
// Even I had troubles documenting it after a while and after reading it again I couldn't believe that it really works.
function mergeBranches( batch, startPos, endPos ) {
function mergeBranches( writer, startPos, endPos ) {
const startParent = startPos.parent;
const endParent = endPos.parent;

Expand All @@ -112,7 +115,7 @@ function mergeBranches( batch, startPos, endPos ) {
// Check if operations we'll need to do won't need to cross object or limit boundaries.
// E.g., we can't merge endParent into startParent in this case:
// <limit><startParent>x[]</startParent></limit><endParent>{}</endParent>
if ( !checkCanBeMerged( startPos, endPos ) ) {
if ( !checkCanBeMerged( startPos, endPos, writer.model.schema ) ) {
return;
}

Expand All @@ -128,13 +131,13 @@ function mergeBranches( batch, startPos, endPos ) {
// <a><b>x[]</b></a><c><d>{}y</d></c>
// becomes:
// <a><b>x</b>[]<d>y</d></a><c>{}</c>
batch.insert( endParent, startPos );
writer.insert( endParent, startPos );
}

// Merge two siblings:
// <a>x</a>[]<b>y</b> -> <a>xy</a> (the usual case)
// <a><b>x</b>[]<d>y</d></a><c></c> -> <a><b>xy</b>[]</a><c></c> (this is the "move parent" case shown above)
batch.merge( startPos );
writer.merge( startPos );

// Remove empty end ancestors:
// <a>fo[o</a><b><a><c>bar]</c></a></b>
Expand All @@ -146,11 +149,11 @@ function mergeBranches( batch, startPos, endPos ) {

endPos = Position.createBefore( parentToRemove );

batch.remove( parentToRemove );
writer.remove( parentToRemove );
}

// Continue merging next level.
mergeBranches( batch, startPos, endPos );
mergeBranches( writer, startPos, endPos );
}

function shouldAutoparagraph( schema, position ) {
Expand All @@ -166,8 +169,7 @@ function shouldAutoparagraph( schema, position ) {
// E.g. in <bQ><p>x[]</p></bQ><widget><caption>{}</caption></widget>
// we'll check <p>, <bQ>, <widget> and <caption>.
// Usually, widget and caption are marked as objects/limits in the schema, so in this case merging will be blocked.
function checkCanBeMerged( leftPos, rightPos ) {
const schema = leftPos.root.document.schema;
function checkCanBeMerged( leftPos, rightPos, schema ) {
const rangeToCheck = new Range( leftPos, rightPos );

for ( const value of rangeToCheck.getWalker() ) {
Expand All @@ -179,18 +181,18 @@ function checkCanBeMerged( leftPos, rightPos ) {
return true;
}

function insertParagraph( batch, position, selection ) {
const paragraph = new Element( 'paragraph' );
batch.insert( paragraph, position );
function insertParagraph( writer, position, selection ) {
const paragraph = writer.createElement( 'paragraph' );

writer.insert( paragraph, position );
selection.setCollapsedAt( paragraph );
}

function replaceEntireContentWithParagraph( batch, selection ) {
const limitElement = batch.document.schema.getLimitElement( selection );
function replaceEntireContentWithParagraph( writer, selection ) {
const limitElement = writer.model.schema.getLimitElement( selection );

batch.remove( Range.createIn( limitElement ) );
insertParagraph( batch, Position.createAt( limitElement ), selection );
writer.remove( Range.createIn( limitElement ) );
insertParagraph( writer, Position.createAt( limitElement ), selection );
}

// We want to replace the entire content with a paragraph when:
Expand Down
14 changes: 7 additions & 7 deletions src/controller/editingcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ export default class EditingController {
/**
* Creates editing controller instance.
*
* @param {module:engine/model/document~Document} model Document model.
* @param {module:engine/model/model~Model} model Editing model.
*/
constructor( model ) {
/**
* Document model.
* Editing model.
*
* @readonly
* @member {module:engine/model/document~Document}
* @member {module:engine/model/model~Model}
*/
this.model = model;

Expand Down Expand Up @@ -84,13 +84,13 @@ export default class EditingController {
} );

// Convert changes in model to view.
this.listenTo( this.model, 'change', ( evt, type, changes ) => {
this.listenTo( this.model.document, 'change', ( evt, type, changes ) => {
this.modelToView.convertChange( type, changes );
}, { priority: 'low' } );

// Convert model selection to view.
this.listenTo( this.model, 'changesDone', () => {
const selection = this.model.selection;
this.listenTo( this.model.document, 'changesDone', () => {
const selection = this.model.document.selection;

this.modelToView.convertSelection( selection );
this.view.render();
Expand Down Expand Up @@ -139,7 +139,7 @@ export default class EditingController {
*/
createRoot( domRoot, name = 'main' ) {
const viewRoot = this.view.createRoot( domRoot, name );
const modelRoot = this.model.getRoot( name );
const modelRoot = this.model.document.getRoot( name );

this.mapper.bindElements( modelRoot, viewRoot );

Expand Down
Loading