Browse Source

Changed the configuration format (CodeBlockLanguageDefinition). CSS classes are now generated from #language or customized using #class.

Aleksander Nowodzinski 6 years ago
parent
commit
82482d7a43

+ 54 - 28
packages/ckeditor5-code-block/src/codeblock.js

@@ -64,57 +64,83 @@ export default class CodeBlock extends Plugin {
  * The code block language descriptor. See {@link module:code-block/codeblock~CodeBlockConfig#languages} to learn more.
  *
  *		{
- *			 class: 'language-javascript',
+ *			 language: 'javascript',
  *			 label: 'JavaScript'
  *		}
  *
  * @typedef {Object} module:code-block/codeblock~CodeBlockLanguageDefinition
- * @property {String} label The human–readable label associated with the language displayed in the UI.
- * @property {String} class The CSS class associated with the language.
+ * @property {String} language The name of the language that will be stored in the model attribute. Also, when `class`
+ * is not specified, it will also be used to create the CSS class associated with the language (prefixed by "language-").
+ * @property {String} label The human–readable label associated with the language and displayed in the UI.
+ * @property {String} [class] The CSS class associated with the language. When not specified the `language`
+ * property is used to create a class prefixed by "language-".
  */
 
 /**
  * The list of code languages available in the user interface to choose for a particular code block.
- * The language of the code block is represented as a CSS class set on the `<code>` element, both
- * when editing and in the editor data:
  *
- *		<pre><code class="javascript">window.alert( 'Hello world!' )</code></pre>
+ * The language of the code block is represented as a CSS class (by default prefixed by "language-") set on the
+ * `<code>` element, both when editing and in the editor data. The CSS class associated with the language
+ * can be used by third–party code syntax highlighters to detect and apply the correct highlighting.
  *
- * The CSS class associated with the language can be then used by third–party code syntax
- * highlighters to detect and apply the correct highlighting. Use this configuration
- * to match requirements of your integration:
+ * For instance, this language configuration:
  *
  *		ClassicEditor
  *			.create( editorElement, {
  *				codeBlock: {
  *					languages: [
- * 						{ class: 'language-plaintext', label: 'Plain text' }, // The default language.
- *						{ class: 'language-php', label: 'PHP' },
- *						{ class: 'language-java', label: 'Java' },
- *						{ class: 'language-javascript', label: 'JavaScript' },
- *						{ class: 'language-python', label: 'Python' }
+ *						// ...
+ *						{ language: 'javascript', label: 'JavaScript' },
+ *						// ...
  *					]
  *				}
  *		} )
  *		.then( ... )
  *		.catch( ... );
  *
- * The default value is as follows:
+ * will result in the following structure of JavaScript code blocks in the editor editing and data:
+ *
+ *		<pre><code class="language-javascript">window.alert( 'Hello world!' )</code></pre>
+ *
+ * You can customize the CSS class by specifying an optional `class` property in a language definition:
+ *
+ *		ClassicEditor
+ *			.create( editorElement, {
+ *				codeBlock: {
+ *					languages: [
+ *						// Do not render CSS class for the plain text code blocks.
+ * 						{ language: 'plaintext', label: 'Plain text', class: '' },
+ *
+ *						// Use the "php-code" class for PHP code blocks.
+ *						{ language: 'php', label: 'PHP', class: 'php-code' },
+ *
+ *						// Use the "js" class for JavaScript code blocks.
+ *						{ language: 'javascript', label: 'JavaScript', class: 'js' },
+ *
+ *						// Python code blocks will have the default "language-python" CSS class.
+ *						{ language: 'python', label: 'Python' }
+ *					]
+ *				}
+ *		} )
+ *		.then( ... )
+ *		.catch( ... );
+ *
+ * The default value of the language configuration is as follows:
  *
  *		languages: [
- *			{ class: 'language-plaintext', label: 'Plain text' }, // The default language.
- *			{ class: 'language-c', label: 'C' },
- *			{ class: 'language-cs', label: 'C#' },
- *			{ class: 'language-cpp', label: 'C++' },
- *			{ class: 'language-css', label: 'CSS' },
- *			{ class: 'language-diff', label: 'Diff' },
- *			{ class: 'language-xml', label: 'HTML/XML' },
- *			{ class: 'language-java', label: 'Java' },
- *			{ class: 'language-javascript', label: 'JavaScript' },
- *			{ class: 'language-php', label: 'PHP' },
- *			{ class: 'language-python', label: 'Python' },
- *			{ class: 'language-ruby', label: 'Ruby' },
- *			{ class: 'language-typescript', label: 'TypeScript' },
+ *			{ language: 'plaintext', label: 'Plain text' }, // The default language.
+ *			{ language: 'c', label: 'C' },
+ *			{ language: 'cs', label: 'C#' },
+ *			{ language: 'cpp', label: 'C++' },
+ *			{ language: 'css', label: 'CSS' },
+ *			{ language: 'diff', label: 'Diff' },
+ *			{ language: 'xml', label: 'HTML/XML' },
+ *			{ language: 'java', label: 'Java' },
+ *			{ language: 'javascript', label: 'JavaScript' },
+ *			{ language: 'php', label: 'PHP' },
+ *			{ language: 'python', label: 'Python' },
+ *			{ language: 'ruby', label: 'Ruby' },
+ *			{ language: 'typescript', label: 'TypeScript' },
  *		]
  *
  * **Note**: The first language defined in the configuration is considered the default one. This means it will be

+ 4 - 2
packages/ckeditor5-code-block/src/codeblockcommand.js

@@ -9,6 +9,7 @@
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import first from '@ckeditor/ckeditor5-utils/src/first';
+import { getNormalizedAndLocalizedLanguageDefinitions } from './utils';
 
 /**
  * The code block command plugin.
@@ -46,11 +47,12 @@ export default class CodeBlockCommand extends Command {
 		const editor = this.editor;
 		const model = editor.model;
 		const selection = model.document.selection;
-		const firstLanguageInConfig = editor.config.get( 'codeBlock.languages' ).shift();
+		const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions( editor );
+		const firstLanguageInConfig = normalizedLanguagesDefs[ 0 ];
 
 		const blocks = Array.from( selection.getSelectedBlocks() );
 		const value = ( options.forceValue === undefined ) ? !this.value : options.forceValue;
-		const language = options.language || firstLanguageInConfig.class;
+		const language = options.language || firstLanguageInConfig.language;
 
 		model.change( writer => {
 			if ( value ) {

+ 18 - 20
packages/ckeditor5-code-block/src/codeblockediting.js

@@ -13,7 +13,7 @@ import CodeBlockCommand from './codeblockcommand';
 import IndentCodeBlockCommand from './indentcodeblockcommand';
 import OutdentCodeBlockCommand from './outdentcodeblockcommand';
 import {
-	getLocalizedLanguageDefinitions,
+	getNormalizedAndLocalizedLanguageDefinitions,
 	getLeadingWhiteSpaces,
 	rawSnippetTextToModelDocumentFragment
 } from './utils';
@@ -55,19 +55,19 @@ export default class CodeBlockEditing extends Plugin {
 
 		editor.config.define( 'codeBlock', {
 			languages: [
-				{ class: 'language-plaintext', label: 'Plain text' },
-				{ class: 'language-c', label: 'C' },
-				{ class: 'language-cs', label: 'C#' },
-				{ class: 'language-cpp', label: 'C++' },
-				{ class: 'language-css', label: 'CSS' },
-				{ class: 'language-diff', label: 'Diff' },
-				{ class: 'language-xml', label: 'HTML/XML' },
-				{ class: 'language-java', label: 'Java' },
-				{ class: 'language-javascript', label: 'JavaScript' },
-				{ class: 'language-php', label: 'PHP' },
-				{ class: 'language-python', label: 'Python' },
-				{ class: 'language-ruby', label: 'Ruby' },
-				{ class: 'language-typescript', label: 'TypeScript' },
+				{ language: 'plaintext', label: 'Plain text' },
+				{ language: 'c', label: 'C' },
+				{ language: 'cs', label: 'C#' },
+				{ language: 'cpp', label: 'C++' },
+				{ language: 'css', label: 'CSS' },
+				{ language: 'diff', label: 'Diff' },
+				{ language: 'xml', label: 'HTML/XML' },
+				{ language: 'java', label: 'Java' },
+				{ language: 'javascript', label: 'JavaScript' },
+				{ language: 'php', label: 'PHP' },
+				{ language: 'python', label: 'Python' },
+				{ language: 'ruby', label: 'Ruby' },
+				{ language: 'typescript', label: 'TypeScript' },
 			],
 
 			// A single tab.
@@ -83,9 +83,7 @@ export default class CodeBlockEditing extends Plugin {
 		const schema = editor.model.schema;
 		const model = editor.model;
 
-		const localizedLanguageDefinitions = getLocalizedLanguageDefinitions( editor );
-		const languageClasses = localizedLanguageDefinitions.map( def => def.class );
-		const languageLabels = Object.assign( {}, ...localizedLanguageDefinitions.map( def => ( { [ def.class ]: def.label } ) ) );
+		const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions( editor );
 
 		// The main command.
 		editor.commands.add( 'codeBlock', new CodeBlockCommand( editor ) );
@@ -126,10 +124,10 @@ export default class CodeBlockEditing extends Plugin {
 		} );
 
 		// Conversion.
-		editor.editing.downcastDispatcher.on( 'insert:codeBlock', modelToViewCodeBlockInsertion( model, languageLabels ) );
-		editor.data.downcastDispatcher.on( 'insert:codeBlock', modelToViewCodeBlockInsertion( model ) );
+		editor.editing.downcastDispatcher.on( 'insert:codeBlock', modelToViewCodeBlockInsertion( model, normalizedLanguagesDefs, true ) );
+		editor.data.downcastDispatcher.on( 'insert:codeBlock', modelToViewCodeBlockInsertion( model, normalizedLanguagesDefs ) );
 		editor.data.downcastDispatcher.on( 'insert:softBreak', modelToDataViewSoftBreakInsertion( model ), { priority: 'high' } );
-		editor.data.upcastDispatcher.on( 'element:pre', dataViewToModelCodeBlockInsertion( editor.data, languageClasses ) );
+		editor.data.upcastDispatcher.on( 'element:pre', dataViewToModelCodeBlockInsertion( editor.data, normalizedLanguagesDefs ) );
 
 		// Intercept the clipboard input (paste) when the selection is anchored in the code block and force the clipboard
 		// data to be pasted as a single plain text. Otherwise, the code lines will split the code block and

+ 11 - 12
packages/ckeditor5-code-block/src/codeblockui.js

@@ -12,7 +12,7 @@ import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import Model from '@ckeditor/ckeditor5-ui/src/model';
 import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
 import { createDropdown, addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
-import { getLocalizedLanguageDefinitions } from './utils';
+import { getNormalizedAndLocalizedLanguageDefinitions } from './utils';
 
 import codeBlockIcon from '../theme/icons/codeblock.svg';
 import '../theme/codeblock.css';
@@ -32,7 +32,8 @@ export default class CodeBlockUI extends Plugin {
 		const editor = this.editor;
 		const t = editor.t;
 		const componentFactory = editor.ui.componentFactory;
-		const localizedLanguageDefinitions = getLocalizedLanguageDefinitions( editor );
+		const normalizedLanguageDefs = getNormalizedAndLocalizedLanguageDefinitions( editor );
+		const defaultLanguageDefinition = normalizedLanguageDefs[ 0 ];
 
 		componentFactory.add( 'codeBlock', locale => {
 			const command = editor.commands.get( 'codeBlock' );
@@ -49,10 +50,8 @@ export default class CodeBlockUI extends Plugin {
 			splitButtonView.bind( 'isOn' ).to( command, 'value', value => !!value );
 
 			splitButtonView.on( 'execute', () => {
-				const defaultLanguageDefinition = localizedLanguageDefinitions[ 0 ];
-
 				editor.execute( 'codeBlock', {
-					language: defaultLanguageDefinition.class,
+					language: defaultLanguageDefinition.language,
 				} );
 
 				editor.editing.view.focus();
@@ -60,7 +59,7 @@ export default class CodeBlockUI extends Plugin {
 
 			dropdownView.on( 'execute', evt => {
 				editor.execute( 'codeBlock', {
-					language: evt.source._codeBlockLanguageClass,
+					language: evt.source._codeBlockLanguage,
 					forceValue: true
 				} );
 
@@ -70,7 +69,7 @@ export default class CodeBlockUI extends Plugin {
 			dropdownView.class = 'ck-code-block-dropdown';
 			dropdownView.bind( 'isEnabled' ).to( command );
 
-			addListToDropdown( dropdownView, this._getLanguageListItemDefinitions( localizedLanguageDefinitions ) );
+			addListToDropdown( dropdownView, this._getLanguageListItemDefinitions( normalizedLanguageDefs ) );
 
 			return dropdownView;
 		} );
@@ -81,26 +80,26 @@ export default class CodeBlockUI extends Plugin {
 	 * available for the user to choose from.
 	 *
 	 * @private
-	 * @param {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>} localizedLanguageDefinitions
+	 * @param {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>} normalizedLanguageDefs
 	 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>}
 	 */
-	_getLanguageListItemDefinitions( localizedLanguageDefinitions ) {
+	_getLanguageListItemDefinitions( normalizedLanguageDefs ) {
 		const editor = this.editor;
 		const command = editor.commands.get( 'codeBlock' );
 		const itemDefinitions = new Collection();
 
-		for ( const languageDef of localizedLanguageDefinitions ) {
+		for ( const languageDef of normalizedLanguageDefs ) {
 			const definition = {
 				type: 'button',
 				model: new Model( {
-					_codeBlockLanguageClass: languageDef.class,
+					_codeBlockLanguage: languageDef.language,
 					label: languageDef.label,
 					withText: true
 				} )
 			};
 
 			definition.model.bind( 'isOn' ).to( command, 'value', value => {
-				return value === definition.model._codeBlockLanguageClass;
+				return value === definition.model._codeBlockLanguage;
 			} );
 
 			itemDefinitions.add( definition );

+ 69 - 20
packages/ckeditor5-code-block/src/converters.js

@@ -7,7 +7,10 @@
  * @module code-block/converters
  */
 
-import { rawSnippetTextToModelDocumentFragment } from './utils';
+import {
+	rawSnippetTextToModelDocumentFragment,
+	getPropertyAssociation
+} from './utils';
 
 /**
  * A model → view (both editing and data) converter for the `codeBlock` element.
@@ -18,19 +21,40 @@ import { rawSnippetTextToModelDocumentFragment } from './utils';
  *
  * Sample output (editing):
  *
- *		<pre data-language="JavaScript"><code class="javascript">foo();<br />bar();</code></pre>
+ *		<pre data-language="JavaScript"><code class="language-javascript">foo();<br />bar();</code></pre>
  *
  * Sample output (data, see {@link module:code-block/converters~modelToDataViewSoftBreakInsertion}):
  *
- *		<pre><code class="javascript">foo();\nbar();</code></pre>
+ *		<pre><code class="language-javascript">foo();\nbar();</code></pre>
  *
  * @param {module:engine/model/model~Model} model
- * @param {Object.<String,String>} [languageLabels={}] An object associating a programming language
- * classes with human–readable labels (as in the editor config). Labels are used in the editing
- * view only.
+ * @param {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>} languageDefs The normalized language
+ * configuration passed to the feature.
+ * @param {Boolean} [useLabels=false] When `true`, the `<pre>` will get a `data-language` attribute with a
+ * human–readable label of the language. Used only in the editing.
  * @returns {Function} Returns a conversion callback.
  */
-export function modelToViewCodeBlockInsertion( model, languageLabels = {} ) {
+export function modelToViewCodeBlockInsertion( model, languageDefs, useLabels = false ) {
+	// Language CSS classes:
+	//
+	//		{
+	//			php: 'language-php',
+	//			python: 'language-python',
+	//			javascript: 'js',
+	//			...
+	//		}
+	const languagesToClasses = getPropertyAssociation( languageDefs, 'language', 'class' );
+
+	// Language labels:
+	//
+	//		{
+	//			php: 'PHP',
+	//			python: 'Python',
+	//			javascript: 'JavaScript',
+	//			...
+	//		}
+	const languagesToLabels = getPropertyAssociation( languageDefs, 'language', 'label' );
+
 	return ( evt, data, conversionApi ) => {
 		const { writer, mapper, consumable } = conversionApi;
 
@@ -40,12 +64,16 @@ export function modelToViewCodeBlockInsertion( model, languageLabels = {} ) {
 
 		const codeBlockLanguage = data.item.getAttribute( 'language' );
 		const targetViewPosition = mapper.toViewPosition( model.createPositionBefore( data.item ) );
-		const pre = writer.createContainerElement( 'pre', {
-			// This attribute is only in the editing view.
-			'data-language': languageLabels[ codeBlockLanguage ] || null
-		} );
+		const preAttributes = {};
+
+		// The attribute added only in the editing view.
+		if ( useLabels ) {
+			preAttributes[ 'data-language' ] = languagesToLabels[ codeBlockLanguage ];
+		}
+
+		const pre = writer.createContainerElement( 'pre', preAttributes );
 		const code = writer.createContainerElement( 'code', {
-			class: codeBlockLanguage
+			class: languagesToClasses[ codeBlockLanguage ] || null
 		} );
 
 		writer.insert( writer.createPositionAt( pre, 0 ), code );
@@ -91,18 +119,29 @@ export function modelToDataViewSoftBreakInsertion( model ) {
  *
  * Sample input:
  *
- *		<pre><code class="javascript">foo();\nbar();</code></pre>
+ *		<pre><code class="language-javascript">foo();\nbar();</code></pre>
  *
  * Sample output:
  *
  *		<codeBlock language="javascript">foo();<softBreak></softBreak>bar();</codeBlock>
  *
  * @param {module:engine/controller/datacontroller~DataController} dataController
- * @param {Array.<String>} languageClasses An array of valid (as in the editor config) CSS classes
- * associated with code languages.
+ * @param {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>} languageDefs The normalized language
+ * configuration passed to the feature.
  * @returns {Function} Returns a conversion callback.
  */
-export function dataViewToModelCodeBlockInsertion( dataController, languageClasses ) {
+export function dataViewToModelCodeBlockInsertion( dataController, languageDefs ) {
+	// Language names associated with CSS classes:
+	//
+	//		{
+	//			'language-php': 'php',
+	//			'language-python': 'python',
+	//			js: 'javascript',
+	//			...
+	//		}
+	const classesToLanguages = getPropertyAssociation( languageDefs, 'class', 'language' );
+	const defaultLanguageName = languageDefs[ 0 ].language;
+
 	return ( evt, data, conversionApi ) => {
 		const viewItem = data.viewItem;
 		const viewChild = viewItem.getChild( 0 );
@@ -118,19 +157,29 @@ export function dataViewToModelCodeBlockInsertion( dataController, languageClass
 		}
 
 		const codeBlock = writer.createElement( 'codeBlock' );
+		const viewChildClasses = [ ...viewChild.getClassNames() ];
+
+		// As we're to associate each class with a model language, a lack of class (empty class) can be
+		// also associated with a language if the language definition was configured so. Pushing an empty
+		// string to make sure the association will work.
+		if ( !viewChildClasses.length ) {
+			viewChildClasses.push( '' );
+		}
 
 		// Figure out if any of the <code> element's class names is a valid programming
 		// language class. If so, use it on the model element (becomes the language of the entire block).
-		for ( const className of viewChild.getClassNames() ) {
-			if ( languageClasses.includes( className ) ) {
-				writer.setAttribute( 'language', className, codeBlock );
+		for ( const className of viewChildClasses ) {
+			const language = classesToLanguages[ className ];
+
+			if ( language ) {
+				writer.setAttribute( 'language', language, codeBlock );
 				break;
 			}
 		}
 
 		// If no language value was set, use the default language from the config.
 		if ( !codeBlock.hasAttribute( 'language' ) ) {
-			writer.setAttribute( 'language', languageClasses[ 0 ], codeBlock );
+			writer.setAttribute( 'language', defaultLanguageName, codeBlock );
 		}
 
 		const stringifiedElement = dataController.processor.toData( viewChild );

+ 53 - 10
packages/ckeditor5-code-block/src/utils.js

@@ -10,28 +10,71 @@
 import first from '@ckeditor/ckeditor5-utils/src/first';
 
 /**
- * Returns code block languages as defined in `config.codeBlock.languages` but processed to consider
- * the editor localization, i.e. to display {@link module:code-block/codeblock~CodeBlockLanguageDefinition}
- * in the correct language.
+ * Returns code block languages as defined in `config.codeBlock.languages` but processed:
  *
- * Note: The reason behind this method is that there is no way to use {@link module:utils/locale~Locale#t}
- * when the user configuration is defined because the editor does not exist yet.
+ * * to consider the editor localization, i.e. to display {@link module:code-block/codeblock~CodeBlockLanguageDefinition}
+ * in the correct language — there is no way to use {@link module:utils/locale~Locale#t} when the user
+ * configuration is defined because the editor does not exist yet.
+ * * to make sure each definition has a CSS class associated with it even if not specified
+ * in the original config.
  *
  * @param {module:core/editor/editor~Editor} editor
  * @returns {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>}.
  */
-export function getLocalizedLanguageDefinitions( editor ) {
+export function getNormalizedAndLocalizedLanguageDefinitions( editor ) {
 	const t = editor.t;
-	const languagesDefs = editor.config.get( 'codeBlock.languages' );
+	const languageDefs = editor.config.get( 'codeBlock.languages' );
 
-	for ( const def of languagesDefs ) {
+	for ( const def of languageDefs ) {
 		if ( def.label === 'Plain text' ) {
 			def.label = t( 'Plain text' );
-			break;
+		}
+
+		if ( def.class === undefined ) {
+			def.class = `language-${ def.language }`;
 		}
 	}
 
-	return languagesDefs;
+	return languageDefs;
+}
+
+/**
+ * Returns an object associating certain language definition properties with another. For instance:
+ *
+ * For:
+ *
+ *		const definitions = {
+ *			{ language: 'php', class: 'language-php', label: 'PHP' },
+ *			{ language: 'javascript', class: 'js', label: 'JavaScript' },
+ *		};
+ *
+ *		getPropertyAssociation( definitions, 'class', 'language' );
+ *
+ * returns:
+ *
+ *		{
+ *			'language-php': 'php'
+ *			'js': 'javascript'
+ *		}
+ *
+ * and
+ *
+ *		getPropertyAssociation( definitions, 'language', 'label' );
+ *
+ * returns:
+ *
+ *		{
+ *			'php': 'PHP'
+ *			'javascript': 'JavaScript'
+ *		}
+ *
+ * @param {Array.<module:code-block/codeblock~CodeBlockLanguageDefinition>}
+ * @param {String} key
+ * @param {String} value
+ * @param {Object.<String,String>}
+ */
+export function getPropertyAssociation( languageDefs, key, value ) {
+	return Object.assign( {}, ...languageDefs.map( def => ( { [ def[ key ] ]: def[ value ] } ) ) );
 }
 
 /**

+ 21 - 21
packages/ckeditor5-code-block/tests/codeblockcommand.js

@@ -144,7 +144,7 @@ describe( 'CodeBlockCommand', () => {
 
 			command.execute();
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">[]</codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">[]</codeBlock>' );
 		} );
 
 		it( 'should change selected block to codeBlock', () => {
@@ -152,7 +152,7 @@ describe( 'CodeBlockCommand', () => {
 
 			command.execute();
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">fo[]o</codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">fo[]o</codeBlock>' );
 		} );
 
 		it( 'should change multiple selected block to codeBlock', () => {
@@ -161,36 +161,36 @@ describe( 'CodeBlockCommand', () => {
 			command.execute();
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">f[oo<softBreak></softBreak>ba]r</codeBlock>' );
+				'<codeBlock language="plaintext">f[oo<softBreak></softBreak>ba]r</codeBlock>' );
 		} );
 
 		it( 'should merge selected blocks with selected codeBlocks', () => {
-			setModelData( model, '<paragraph>f[oo</paragraph><codeBlock language="language-plaintext">ba]r</codeBlock>' );
+			setModelData( model, '<paragraph>f[oo</paragraph><codeBlock language="plaintext">ba]r</codeBlock>' );
 
 			command.execute();
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">f[oo<softBreak></softBreak>ba]r</codeBlock>' );
+				'<codeBlock language="plaintext">f[oo<softBreak></softBreak>ba]r</codeBlock>' );
 		} );
 
 		it( 'should not merge codeBlock with siblings when siblings are not selected', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">foo</codeBlock>' +
+				'<codeBlock language="plaintext">foo</codeBlock>' +
 				'<paragraph>b[a]r</paragraph>' +
-				'<codeBlock language="language-plaintext">biz</codeBlock>'
+				'<codeBlock language="plaintext">biz</codeBlock>'
 			);
 
 			command.execute();
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">foo</codeBlock>' +
-				'<codeBlock language="language-plaintext">b[a]r</codeBlock>' +
-				'<codeBlock language="language-plaintext">biz</codeBlock>'
+				'<codeBlock language="plaintext">foo</codeBlock>' +
+				'<codeBlock language="plaintext">b[a]r</codeBlock>' +
+				'<codeBlock language="plaintext">biz</codeBlock>'
 			);
 		} );
 
 		it( 'should change selected empty codeBlock to paragraph', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">[]</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">[]</codeBlock>' );
 
 			command.execute();
 
@@ -198,7 +198,7 @@ describe( 'CodeBlockCommand', () => {
 		} );
 
 		it( 'should change selected codeBlock to paragraph', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">f[o]o</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">f[o]o</codeBlock>' );
 
 			command.execute();
 
@@ -207,7 +207,7 @@ describe( 'CodeBlockCommand', () => {
 
 		it( 'should change selected multi-line codeBlock to paragraphs', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">foo<softBreak></softBreak>b[]ar<softBreak></softBreak>biz</codeBlock>'
+				'<codeBlock language="plaintext">foo<softBreak></softBreak>b[]ar<softBreak></softBreak>biz</codeBlock>'
 			);
 
 			command.execute();
@@ -224,15 +224,15 @@ describe( 'CodeBlockCommand', () => {
 
 			command.execute();
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">f[o]o</codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">f[o]o</codeBlock>' );
 		} );
 
 		it( 'should use forceValue parameter', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">f[o]o</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">f[o]o</codeBlock>' );
 
 			command.execute( { forceValue: true } );
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">f[o]o</codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">f[o]o</codeBlock>' );
 		} );
 
 		it( 'should allow setting the language of the new block', () => {
@@ -244,7 +244,7 @@ describe( 'CodeBlockCommand', () => {
 		} );
 
 		it( 'should allow changing the language of the existing block', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">f[o]o</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">f[o]o</codeBlock>' );
 
 			command.execute( { language: 'css', forceValue: true } );
 
@@ -259,7 +259,7 @@ describe( 'CodeBlockCommand', () => {
 			command.execute();
 
 			expect( getModelData( model ) ).to.equal(
-				'<blockQuote><codeBlock language="language-plaintext">f[o]o</codeBlock></blockQuote>' );
+				'<blockQuote><codeBlock language="plaintext">f[o]o</codeBlock></blockQuote>' );
 		} );
 
 		it( 'should change a paragraph inside a blockQuote to codeBlock when blockQuote is selected with siblings', () => {
@@ -272,9 +272,9 @@ describe( 'CodeBlockCommand', () => {
 			command.execute();
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">f[oo</codeBlock>' +
-				'<blockQuote><codeBlock language="language-plaintext">bar</codeBlock></blockQuote>' +
-				'<codeBlock language="language-plaintext">bi]z</codeBlock>'
+				'<codeBlock language="plaintext">f[oo</codeBlock>' +
+				'<blockQuote><codeBlock language="plaintext">bar</codeBlock></blockQuote>' +
+				'<codeBlock language="plaintext">bi]z</codeBlock>'
 			);
 		} );
 	} );

+ 270 - 180
packages/ckeditor5-code-block/tests/codeblockediting.js

@@ -77,87 +77,22 @@ describe( 'CodeBlockEditing', () => {
 			describe( 'default value', () => {
 				it( 'should be set', () => {
 					expect( editor.config.get( 'codeBlock.languages' ) ).to.deep.equal( [
-						{ class: 'language-plaintext', label: 'Plain text' },
-						{ class: 'language-c', label: 'C' },
-						{ class: 'language-cs', label: 'C#' },
-						{ class: 'language-cpp', label: 'C++' },
-						{ class: 'language-css', label: 'CSS' },
-						{ class: 'language-diff', label: 'Diff' },
-						{ class: 'language-xml', label: 'HTML/XML' },
-						{ class: 'language-java', label: 'Java' },
-						{ class: 'language-javascript', label: 'JavaScript' },
-						{ class: 'language-php', label: 'PHP' },
-						{ class: 'language-python', label: 'Python' },
-						{ class: 'language-ruby', label: 'Ruby' },
-						{ class: 'language-typescript', label: 'TypeScript' }
+						{ language: 'plaintext', label: 'Plain text' },
+						{ language: 'c', label: 'C' },
+						{ language: 'cs', label: 'C#' },
+						{ language: 'cpp', label: 'C++' },
+						{ language: 'css', label: 'CSS' },
+						{ language: 'diff', label: 'Diff' },
+						{ language: 'xml', label: 'HTML/XML' },
+						{ language: 'java', label: 'Java' },
+						{ language: 'javascript', label: 'JavaScript' },
+						{ language: 'php', label: 'PHP' },
+						{ language: 'python', label: 'Python' },
+						{ language: 'ruby', label: 'Ruby' },
+						{ language: 'typescript', label: 'TypeScript' }
 					] );
 				} );
 			} );
-
-			it( 'should be recognized when loading data', () => {
-				return ClassicTestEditor.create(
-					'<pre><code class="language-foo">bar</code></pre>' +
-					'<pre><code class="language-bar">baz</code></pre>',
-					{
-						plugins: [ CodeBlockEditing ],
-						codeBlock: {
-							languages: [
-								{ class: 'language-foo', label: 'Foo' },
-								{ class: 'language-bar', label: 'Bar' },
-							]
-						}
-					} )
-					.then( editor => {
-						model = editor.model;
-
-						expect( getModelData( model ) ).to.equal(
-							'<codeBlock language="language-foo">[]bar</codeBlock>' +
-							'<codeBlock language="language-bar">baz</codeBlock>'
-						);
-
-						return editor.destroy();
-					} );
-			} );
-
-			it( 'should use the first if the code in data has no language', () => {
-				return ClassicTestEditor
-					.create( '<pre><code>bar</code></pre>', {
-						plugins: [ CodeBlockEditing ],
-						codeBlock: {
-							languages: [
-								{ class: 'language-foo', label: 'Foo' },
-								{ class: 'language-bar', label: 'Bar' }
-							]
-						}
-					} )
-					.then( editor => {
-						model = editor.model;
-
-						expect( getModelData( model ) ).to.equal( '<codeBlock language="language-foo">[]bar</codeBlock>' );
-
-						return editor.destroy();
-					} );
-			} );
-
-			it( 'should use the first if the code in data has an invalid language', () => {
-				return ClassicTestEditor
-					.create( '<pre><code class="baz">bar</code></pre>', {
-						plugins: [ CodeBlockEditing ],
-						codeBlock: {
-							languages: [
-								{ class: 'language-foo', label: 'Foo' },
-								{ class: 'language-bar', label: 'Bar' }
-							]
-						}
-					} )
-					.then( editor => {
-						model = editor.model;
-
-						expect( getModelData( model ) ).to.equal( '<codeBlock language="language-foo">[]bar</codeBlock>' );
-
-						return editor.destroy();
-					} );
-			} );
 		} );
 
 		describe( 'indentSequence', () => {
@@ -196,12 +131,12 @@ describe( 'CodeBlockEditing', () => {
 	} );
 
 	it( 'disallows all attributes (except "language") for codeBlock', () => {
-		setModelData( model, '<codeBlock language="language-css">f[o]o</codeBlock>' );
+		setModelData( model, '<codeBlock language="css">f[o]o</codeBlock>' );
 
 		editor.execute( 'alignment', { value: 'right' } );
 		editor.execute( 'bold' );
 
-		expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">f[o]o</codeBlock>' );
+		expect( getModelData( model ) ).to.equal( '<codeBlock language="css">f[o]o</codeBlock>' );
 	} );
 
 	describe( 'tab key handling', () => {
@@ -222,7 +157,7 @@ describe( 'CodeBlockEditing', () => {
 		} );
 
 		it( 'should execute indentCodeBlock command on tab key', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">[]foo</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">[]foo</codeBlock>' );
 
 			editor.editing.view.document.fire( 'keydown', domEvtDataStub );
 
@@ -235,9 +170,9 @@ describe( 'CodeBlockEditing', () => {
 		it( 'should execute outdentCodeBlock command on Shift+Tab keystroke', () => {
 			domEvtDataStub.keyCode += getCode( 'Shift' );
 
-			setModelData( model, '<codeBlock language="language-plaintext">[]foo</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">[]foo</codeBlock>' );
 
-			// '<codeBlock language="language-plaintext">	[]foo</codeBlock>
+			// '<codeBlock language="plaintext">	[]foo</codeBlock>
 			model.change( writer => {
 				writer.insertText( '	', model.document.getRoot().getChild( 0 ) );
 			} );
@@ -263,7 +198,7 @@ describe( 'CodeBlockEditing', () => {
 		it( 'should not indent or outdent if alt+tab is pressed', () => {
 			domEvtDataStub.keyCode += getCode( 'alt' );
 
-			setModelData( model, '<codeBlock language="language-plaintext">[]foo</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">[]foo</codeBlock>' );
 
 			editor.editing.view.document.fire( 'keydown', domEvtDataStub );
 
@@ -308,46 +243,46 @@ describe( 'CodeBlockEditing', () => {
 
 		describe( 'indentation retention', () => {
 			it( 'should work when indentation is with spaces', () => {
-				setModelData( model, '<codeBlock language="language-css">foo[]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo[]</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">  foo[]</codeBlock>
+					// <codeBlock language="css">  foo[]</codeBlock>
 					writer.insertText( '  ', model.document.getRoot().getChild( 0 ), 0 );
 				} );
 
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">  foo<softBreak></softBreak>  []</codeBlock>' );
+					'<codeBlock language="css">  foo<softBreak></softBreak>  []</codeBlock>' );
 
 				editor.execute( 'undo' );
 
-				expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">  foo[]</codeBlock>' );
+				expect( getModelData( model ) ).to.equal( '<codeBlock language="css">  foo[]</codeBlock>' );
 			} );
 
 			it( 'should work when indentation is with tabs', () => {
-				setModelData( model, '<codeBlock language="language-css">foo[]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo[]</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">	foo[]</codeBlock>
+					// <codeBlock language="css">	foo[]</codeBlock>
 					writer.insertText( '	', model.document.getRoot().getChild( 0 ), 0 );
 				} );
 
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">	foo<softBreak></softBreak>	[]</codeBlock>' );
+					'<codeBlock language="css">	foo<softBreak></softBreak>	[]</codeBlock>' );
 
 				editor.execute( 'undo' );
 
-				expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">	foo[]</codeBlock>' );
+				expect( getModelData( model ) ).to.equal( '<codeBlock language="css">	foo[]</codeBlock>' );
 			} );
 
 			it( 'should retain only the last line', () => {
-				setModelData( model, '<codeBlock language="language-css">foo<softBreak></softBreak>bar[]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>bar[]</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">  foo<softBreak></softBreak>	bar[]</codeBlock>
+					// <codeBlock language="css">  foo<softBreak></softBreak>	bar[]</codeBlock>
 					writer.insertText( '	', model.document.getRoot().getChild( 0 ), 4 );
 					writer.insertText( '  ', model.document.getRoot().getChild( 0 ), 0 );
 				} );
@@ -355,37 +290,37 @@ describe( 'CodeBlockEditing', () => {
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">  foo<softBreak></softBreak>	bar<softBreak></softBreak>	[]</codeBlock>' );
+					'<codeBlock language="css">  foo<softBreak></softBreak>	bar<softBreak></softBreak>	[]</codeBlock>' );
 
 				editor.execute( 'undo' );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">  foo<softBreak></softBreak>	bar[]</codeBlock>' );
+					'<codeBlock language="css">  foo<softBreak></softBreak>	bar[]</codeBlock>' );
 			} );
 
 			it( 'should retain when the selection is non–collapsed', () => {
-				setModelData( model, '<codeBlock language="language-css">f[o]o</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">f[o]o</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">    f[o]o</codeBlock>
+					// <codeBlock language="css">    f[o]o</codeBlock>
 					writer.insertText( '    ', model.document.getRoot().getChild( 0 ), 0 );
 				} );
 
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">    f<softBreak></softBreak>    []o</codeBlock>' );
+					'<codeBlock language="css">    f<softBreak></softBreak>    []o</codeBlock>' );
 
 				editor.execute( 'undo' );
 
-				expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">    f[o]o</codeBlock>' );
+				expect( getModelData( model ) ).to.equal( '<codeBlock language="css">    f[o]o</codeBlock>' );
 			} );
 
 			it( 'should consider only leading white-spaces', () => {
-				setModelData( model, '<codeBlock language="language-css">foo[]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo[]</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">  foo []</codeBlock>
+					// <codeBlock language="css">  foo []</codeBlock>
 					writer.insertText( ' ', model.document.getRoot().getChild( 0 ), 3 );
 					writer.insertText( '  ', model.document.getRoot().getChild( 0 ), 0 );
 				} );
@@ -393,25 +328,25 @@ describe( 'CodeBlockEditing', () => {
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">  foo <softBreak></softBreak>  []</codeBlock>' );
+					'<codeBlock language="css">  foo <softBreak></softBreak>  []</codeBlock>' );
 
 				editor.execute( 'undo' );
 
-				expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">  foo []</codeBlock>' );
+				expect( getModelData( model ) ).to.equal( '<codeBlock language="css">  foo []</codeBlock>' );
 			} );
 
 			it( 'should not work when there is some non-whitespace character', () => {
-				setModelData( model, '<codeBlock language="language-css">foo[]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo[]</codeBlock>' );
 
 				model.change( writer => {
-					// <codeBlock language="language-css">foo   []</codeBlock>
+					// <codeBlock language="css">foo   []</codeBlock>
 					writer.insertText( '   ', model.document.getRoot().getChild( 0 ), 3 );
 				} );
 
 				viewDoc.fire( 'enter', getEvent() );
 
 				expect( getModelData( model ) ).to.equal(
-					'<codeBlock language="language-css">foo   <softBreak></softBreak>[]</codeBlock>' );
+					'<codeBlock language="css">foo   <softBreak></softBreak>[]</codeBlock>' );
 			} );
 		} );
 
@@ -420,17 +355,17 @@ describe( 'CodeBlockEditing', () => {
 				it( 'should leave the block when pressed twice at the end', () => {
 					const spy = sinon.spy( editor.editing.view, 'scrollToTheSelection' );
 
-					setModelData( model, '<codeBlock language="language-css">foo[]</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">foo[]</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">foo<softBreak></softBreak>[]</codeBlock>' );
+						'<codeBlock language="css">foo<softBreak></softBreak>[]</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">foo</codeBlock>' +
+						'<codeBlock language="css">foo</codeBlock>' +
 						'<paragraph>[]</paragraph>'
 					);
 
@@ -438,70 +373,70 @@ describe( 'CodeBlockEditing', () => {
 
 					editor.execute( 'undo' );
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">foo<softBreak></softBreak>[]</codeBlock>' );
+						'<codeBlock language="css">foo<softBreak></softBreak>[]</codeBlock>' );
 
 					editor.execute( 'undo' );
-					expect( getModelData( model ) ).to.equal( '<codeBlock language="language-css">foo[]</codeBlock>' );
+					expect( getModelData( model ) ).to.equal( '<codeBlock language="css">foo[]</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when the selection is not collapsed', () => {
-					setModelData( model, '<codeBlock language="language-css">f[oo<softBreak></softBreak>]</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">f[oo<softBreak></softBreak>]</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">f<softBreak></softBreak>[]</codeBlock>' );
+						'<codeBlock language="css">f<softBreak></softBreak>[]</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when pressed twice when in the middle of the code', () => {
-					setModelData( model, '<codeBlock language="language-css">fo[]o</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">fo[]o</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">fo<softBreak></softBreak>[]o</codeBlock>' );
+						'<codeBlock language="css">fo<softBreak></softBreak>[]o</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">fo<softBreak></softBreak><softBreak></softBreak>[]o</codeBlock>' );
+						'<codeBlock language="css">fo<softBreak></softBreak><softBreak></softBreak>[]o</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when pressed twice at the beginning of the code', () => {
-					setModelData( model, '<codeBlock language="language-css">[]foo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[]foo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak>[]foo</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak>[]foo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak><softBreak></softBreak>[]foo</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak><softBreak></softBreak>[]foo</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when pressed shift+enter twice at the end of the code', () => {
-					setModelData( model, '<codeBlock language="language-css">foo<softBreak></softBreak>[]</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>[]</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent( { isSoft: true } ) );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">foo<softBreak></softBreak><softBreak></softBreak>[]</codeBlock>' );
+						'<codeBlock language="css">foo<softBreak></softBreak><softBreak></softBreak>[]</codeBlock>' );
 				} );
 
 				it( 'should clean up the last line if has white–space characters only', () => {
-					setModelData( model, '<codeBlock language="language-css">foo<softBreak></softBreak>[]</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>[]</codeBlock>' );
 
 					model.change( writer => {
-						// <codeBlock language="language-css">foo<softBreak></softBreak>  []</codeBlock>
+						// <codeBlock language="css">foo<softBreak></softBreak>  []</codeBlock>
 						writer.insertText( '  ', model.document.getRoot().getChild( 0 ), 4 );
 					} );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">foo</codeBlock><paragraph>[]</paragraph>' );
+						'<codeBlock language="css">foo</codeBlock><paragraph>[]</paragraph>' );
 				} );
 			} );
 
@@ -509,62 +444,62 @@ describe( 'CodeBlockEditing', () => {
 				it( 'should leave the block when pressed at the beginning in a new line', () => {
 					const spy = sinon.spy( editor.editing.view, 'scrollToTheSelection' );
 
-					setModelData( model, '<codeBlock language="language-css">[]<softBreak></softBreak>foo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[]<softBreak></softBreak>foo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
 						'<paragraph>[]</paragraph>' +
-						'<codeBlock language="language-css">foo</codeBlock>'
+						'<codeBlock language="css">foo</codeBlock>'
 					);
 
 					sinon.assert.calledOnce( spy );
 
 					editor.execute( 'undo' );
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">[]<softBreak></softBreak>foo</codeBlock>' );
+						'<codeBlock language="css">[]<softBreak></softBreak>foo</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when the selection is not collapsed (#1)', () => {
-					setModelData( model, '<codeBlock language="language-css">[f]<softBreak></softBreak>oo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[f]<softBreak></softBreak>oo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak>[]<softBreak></softBreak>oo</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak>[]<softBreak></softBreak>oo</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when the selection is not collapsed (#2)', () => {
-					setModelData( model, '<codeBlock language="language-css">[<softBreak></softBreak>oo]</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[<softBreak></softBreak>oo]</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak>[]</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak>[]</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when pressed shift+enter at the beginning of the code', () => {
-					setModelData( model, '<codeBlock language="language-css">[]<softBreak></softBreak>foo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[]<softBreak></softBreak>foo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent( { isSoft: true } ) );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak>[]<softBreak></softBreak>foo</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak>[]<softBreak></softBreak>foo</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when there is some text after the selection', () => {
-					setModelData( model, '<codeBlock language="language-css">[]foo<softBreak></softBreak>foo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[]foo<softBreak></softBreak>foo</codeBlock>' );
 
 					viewDoc.fire( 'enter', getEvent() );
 
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css"><softBreak></softBreak>[]foo<softBreak></softBreak>foo</codeBlock>' );
+						'<codeBlock language="css"><softBreak></softBreak>[]foo<softBreak></softBreak>foo</codeBlock>' );
 				} );
 
 				it( 'should not leave the block when there is some text before the selection', () => {
-					setModelData( model, '<codeBlock language="language-css">[]<softBreak></softBreak>foo</codeBlock>' );
+					setModelData( model, '<codeBlock language="css">[]<softBreak></softBreak>foo</codeBlock>' );
 
-					// <codeBlock language="language-css">    []<softBreak></softBreak>foo</codeBlock>
+					// <codeBlock language="css">    []<softBreak></softBreak>foo</codeBlock>
 					model.change( writer => {
 						writer.insertText( '    ', model.document.getRoot().getChild( 0 ), 0 );
 					} );
@@ -573,7 +508,7 @@ describe( 'CodeBlockEditing', () => {
 
 					// Extra spaces before "[]" come from the indentation retention mechanism.
 					expect( getModelData( model ) ).to.equal(
-						'<codeBlock language="language-css">    <softBreak></softBreak>    []<softBreak></softBreak>foo</codeBlock>' );
+						'<codeBlock language="css">    <softBreak></softBreak>    []<softBreak></softBreak>foo</codeBlock>' );
 				} );
 			} );
 		} );
@@ -638,20 +573,20 @@ describe( 'CodeBlockEditing', () => {
 
 	describe( 'editing pipeline m -> v', () => {
 		it( 'should convert empty codeBlock to empty pre tag', () => {
-			setModelData( model, '<codeBlock language="language-plaintext"></codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext"></codeBlock>' );
 
 			expect( getViewData( view ) ).to.equal( '<pre data-language="Plain text"><code class="language-plaintext">[]</code></pre>' );
 		} );
 
 		it( 'should convert non-empty codeBlock to pre tag', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">Foo</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">Foo</codeBlock>' );
 
 			expect( getViewData( view ) ).to.equal( '<pre data-language="Plain text"><code class="language-plaintext">{}Foo</code></pre>' );
 		} );
 
 		it( 'should convert codeBlock with softBreaks to pre tag #1', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">' +
+				'<codeBlock language="plaintext">' +
 					'Foo<softBreak></softBreak>' +
 					'Bar<softBreak></softBreak>' +
 					'Biz' +
@@ -666,7 +601,7 @@ describe( 'CodeBlockEditing', () => {
 
 		it( 'should convert codeBlock with softBreaks to pre tag #2', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">' +
+				'<codeBlock language="plaintext">' +
 					'<softBreak></softBreak>' +
 					'<softBreak></softBreak>' +
 					'Foo' +
@@ -696,7 +631,7 @@ describe( 'CodeBlockEditing', () => {
 					const view = editor.editing.view;
 
 					setModelData( model,
-						'<codeBlock language="language-plaintext">foo</codeBlock>'
+						'<codeBlock language="plaintext">foo</codeBlock>'
 					);
 
 					expect( getViewData( view ) ).to.equal(
@@ -709,24 +644,55 @@ describe( 'CodeBlockEditing', () => {
 					return editor.destroy();
 				} );
 		} );
+
+		it( 'should not set the class on the <code> if it was configured so', () => {
+			const element = document.createElement( 'div' );
+			document.body.appendChild( element );
+
+			return ClassicTestEditor
+				.create( element, {
+					plugins: [ CodeBlockEditing, AlignmentEditing, BoldEditing, Enter, Paragraph ],
+					codeBlock: {
+						languages: [
+							{ language: 'cpp', label: 'C++', class: '' },
+						]
+					}
+				} )
+				.then( newEditor => {
+					const editor = newEditor;
+					const model = editor.model;
+					const view = editor.editing.view;
+
+					setModelData( model, '<codeBlock language="cpp">foo</codeBlock>' );
+
+					expect( getViewData( view ) ).to.equal(
+						'<pre data-language="C++">' +
+							'<code>{}foo</code>' +
+						'</pre>' );
+
+					element.remove();
+
+					return editor.destroy();
+				} );
+		} );
 	} );
 
 	describe( 'data pipeline m -> v conversion ', () => {
 		it( 'should convert empty codeBlock to empty pre tag', () => {
-			setModelData( model, '<codeBlock language="language-plaintext"></codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext"></codeBlock>' );
 
 			expect( editor.getData( { trim: 'none' } ) ).to.equal( '<pre><code class="language-plaintext">&nbsp;</code></pre>' );
 		} );
 
 		it( 'should convert non-empty codeBlock to pre tag', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">Foo</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">Foo</codeBlock>' );
 
 			expect( editor.getData() ).to.equal( '<pre><code class="language-plaintext">Foo</code></pre>' );
 		} );
 
 		it( 'should convert codeBlock with softBreaks to pre tag #1', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">' +
+				'<codeBlock language="plaintext">' +
 					'Foo<softBreak></softBreak>' +
 					'Bar<softBreak></softBreak>' +
 					'Biz' +
@@ -739,7 +705,7 @@ describe( 'CodeBlockEditing', () => {
 
 		it( 'should convert codeBlock with softBreaks to pre tag #2', () => {
 			setModelData( model,
-				'<codeBlock language="language-plaintext">' +
+				'<codeBlock language="plaintext">' +
 					'<softBreak></softBreak>' +
 					'<softBreak></softBreak>' +
 					'Foo' +
@@ -752,7 +718,7 @@ describe( 'CodeBlockEditing', () => {
 		} );
 
 		it( 'should convert codeBlock with html content', () => {
-			setModelData( model, '<codeBlock language="language-plaintext">[]</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">[]</codeBlock>' );
 
 			model.change( writer => writer.insertText( '<div><p>Foo</p></div>', model.document.selection.getFirstPosition() ) );
 
@@ -779,10 +745,36 @@ describe( 'CodeBlockEditing', () => {
 				api.writer.insert( position, api.writer.createText( '\n' ) );
 			}, { priority: 'highest' } );
 
-			setModelData( model, '<codeBlock language="language-plaintext">Foo<softBreak></softBreak>Bar</codeBlock>' );
+			setModelData( model, '<codeBlock language="plaintext">Foo<softBreak></softBreak>Bar</codeBlock>' );
 
 			expect( editor.getData() ).to.equal( '<code>Foo\nBar</code>' );
 		} );
+
+		it( 'should not set the class on the <code> if it was configured so', () => {
+			const element = document.createElement( 'div' );
+			document.body.appendChild( element );
+
+			return ClassicTestEditor
+				.create( element, {
+					plugins: [ CodeBlockEditing, AlignmentEditing, BoldEditing, Enter, Paragraph ],
+					codeBlock: {
+						languages: [
+							{ language: 'cpp', label: 'C++', class: '' },
+						]
+					}
+				} )
+				.then( newEditor => {
+					const editor = newEditor;
+					const model = editor.model;
+
+					setModelData( model, '<codeBlock language="cpp">foo</codeBlock>' );
+					expect( editor.getData() ).to.equal( '<pre><code>foo</code></pre>' );
+
+					element.remove();
+
+					return editor.destroy();
+				} );
+		} );
 	} );
 
 	describe( 'data pipeline v -> m conversion ', () => {
@@ -801,14 +793,14 @@ describe( 'CodeBlockEditing', () => {
 		it( 'should convert pre > code to code block', () => {
 			editor.setData( '<pre><code></code></pre>' );
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">[]</codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">[]</codeBlock>' );
 		} );
 
 		it( 'should convert pre > code with multi-line text to code block #1', () => {
 			editor.setData( '<pre><code>foo\nbar</code></pre>' );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]' +
+				'<codeBlock language="plaintext">[]' +
 					'foo' +
 					'<softBreak></softBreak>' +
 					'bar' +
@@ -820,7 +812,7 @@ describe( 'CodeBlockEditing', () => {
 			editor.setData( '<pre><code>\n\nfoo\n\n</code></pre>' );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]' +
+				'<codeBlock language="plaintext">[]' +
 					'<softBreak></softBreak>' +
 					'<softBreak></softBreak>' +
 					'foo' +
@@ -834,7 +826,7 @@ describe( 'CodeBlockEditing', () => {
 			editor.setData( '<pre><code><p>Foo</p>\n<p>Bar</p></code></pre>' );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]' +
+				'<codeBlock language="plaintext">[]' +
 					'<p>Foo</p>' +
 					'<softBreak></softBreak>' +
 					'<p>Bar</p>' +
@@ -846,13 +838,13 @@ describe( 'CodeBlockEditing', () => {
 			editor.setData( '<pre><code><p>Foo</p><pre>Bar</pre><p>Biz</p></code></pre>' );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]<p>Foo</p><pre>Bar</pre><p>Biz</p></codeBlock>' );
+				'<codeBlock language="plaintext">[]<p>Foo</p><pre>Bar</pre><p>Biz</p></codeBlock>' );
 		} );
 
 		it( 'should convert pre > code tag with escaped html content', () => {
 			editor.setData( '<pre><code>&lt;div&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/div&gt;</code></pre>' );
 
-			expect( getModelData( model ) ).to.equal( '<codeBlock language="language-plaintext">[]<div><p>Foo</p></div></codeBlock>' );
+			expect( getModelData( model ) ).to.equal( '<codeBlock language="plaintext">[]<div><p>Foo</p></div></codeBlock>' );
 		} );
 
 		it( 'should be overridable', () => {
@@ -878,7 +870,7 @@ describe( 'CodeBlockEditing', () => {
 			// Note: The empty <paragraph> should not be here. It's a conversion/auto–paragraphing bug.
 			expect( getModelData( model ) ).to.equal(
 				'<paragraph>[]foo</paragraph>' +
-				'<codeBlock language="language-plaintext">x</codeBlock>' +
+				'<codeBlock language="plaintext">x</codeBlock>' +
 				'<paragraph>bar</paragraph>' +
 				'<paragraph></paragraph>' );
 		} );
@@ -887,7 +879,7 @@ describe( 'CodeBlockEditing', () => {
 			editor.setData( '<pre><code>foo</code></pre><pre><code>bar</code></pre>' );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]foo</codeBlock><codeBlock language="language-plaintext">bar</codeBlock>' );
+				'<codeBlock language="plaintext">[]foo</codeBlock><codeBlock language="plaintext">bar</codeBlock>' );
 		} );
 
 		it( 'should upcast two code blocks in a row (#2)', () => {
@@ -896,9 +888,9 @@ describe( 'CodeBlockEditing', () => {
 
 			// Note: The empty <paragraph> in between should not be here. It's a conversion/auto–paragraphing bug.
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-plaintext">[]foo</codeBlock>' +
+				'<codeBlock language="plaintext">[]foo</codeBlock>' +
 				'<paragraph> </paragraph>' +
-				'<codeBlock language="language-plaintext">bar</codeBlock>' );
+				'<codeBlock language="plaintext">bar</codeBlock>' );
 		} );
 
 		it( 'should not convert when modelCursor and its ancestors disallow to insert codeBlock', () => {
@@ -913,6 +905,104 @@ describe( 'CodeBlockEditing', () => {
 
 			expect( getModelData( model, { rootName: 'title', withoutSelection: true } ) ).to.equal( '' );
 		} );
+
+		describe( 'config.codeBlock.languages', () => {
+			it( 'should be respected when upcasting', () => {
+				return ClassicTestEditor.create(
+					'<pre><code class="language-foo">bar</code></pre>' +
+					'<pre><code class="language-bar">baz</code></pre>' +
+					'<pre><code class="qux">qux</code></pre>',
+					{
+						plugins: [ CodeBlockEditing ],
+						codeBlock: {
+							languages: [
+								{ language: 'foo', label: 'Foo' },
+								{ language: 'bar', label: 'Bar' },
+								{ language: 'qux', label: 'Qux', class: 'qux' },
+							]
+						}
+					} )
+					.then( editor => {
+						model = editor.model;
+
+						expect( getModelData( model ) ).to.equal(
+							'<codeBlock language="foo">[]bar</codeBlock>' +
+							'<codeBlock language="bar">baz</codeBlock>' +
+							'<codeBlock language="qux">qux</codeBlock>'
+						);
+
+						return editor.destroy();
+					} );
+			} );
+
+			it( 'should be respected when upcasting and a language has an empty class configured', () => {
+				return ClassicTestEditor.create(
+					'<pre><code class="language-foo">bar</code></pre>' +
+					'<pre><code class="language-bar">baz</code></pre>' +
+					'<pre><code>qux</code></pre>',
+					{
+						plugins: [ CodeBlockEditing ],
+						codeBlock: {
+							languages: [
+								{ language: 'foo', label: 'Foo' },
+								{ language: 'bar', label: 'Bar' },
+								{ language: 'qux', label: 'Qux', class: '' },
+							]
+						}
+					} )
+					.then( editor => {
+						model = editor.model;
+
+						expect( getModelData( model ) ).to.equal(
+							'<codeBlock language="foo">[]bar</codeBlock>' +
+							'<codeBlock language="bar">baz</codeBlock>' +
+							'<codeBlock language="qux">qux</codeBlock>'
+						);
+
+						return editor.destroy();
+					} );
+			} );
+
+			it( 'should upcast using the first language if the code in data has no language', () => {
+				return ClassicTestEditor
+					.create( '<pre><code>bar</code></pre>', {
+						plugins: [ CodeBlockEditing ],
+						codeBlock: {
+							languages: [
+								{ language: 'foo', label: 'Foo' },
+								{ language: 'bar', label: 'Bar' }
+							]
+						}
+					} )
+					.then( editor => {
+						model = editor.model;
+
+						expect( getModelData( model ) ).to.equal( '<codeBlock language="foo">[]bar</codeBlock>' );
+
+						return editor.destroy();
+					} );
+			} );
+
+			it( 'should upast using the first language if the code in data has an invalid language', () => {
+				return ClassicTestEditor
+					.create( '<pre><code class="baz">bar</code></pre>', {
+						plugins: [ CodeBlockEditing ],
+						codeBlock: {
+							languages: [
+								{ language: 'foo', label: 'Foo' },
+								{ language: 'bar', label: 'Bar' }
+							]
+						}
+					} )
+					.then( editor => {
+						model = editor.model;
+
+						expect( getModelData( model ) ).to.equal( '<codeBlock language="foo">[]bar</codeBlock>' );
+
+						return editor.destroy();
+					} );
+			} );
+		} );
 	} );
 
 	describe( 'clipboard integration', () => {
@@ -933,7 +1023,7 @@ describe( 'CodeBlockEditing', () => {
 		} );
 
 		it( 'should intercept input when selection anchored in the code block', () => {
-			setModelData( model, '<codeBlock language="language-css">f[o]o</codeBlock>' );
+			setModelData( model, '<codeBlock language="css">f[o]o</codeBlock>' );
 
 			const dataTransferMock = {
 				getData: sinon.stub().withArgs( 'text/plain' ).returns( 'bar\nbaz\n' )
@@ -945,7 +1035,7 @@ describe( 'CodeBlockEditing', () => {
 			} );
 
 			expect( getModelData( model ) ).to.equal(
-				'<codeBlock language="language-css">' +
+				'<codeBlock language="css">' +
 					'fbar' +
 					'<softBreak></softBreak>' +
 					'baz' +
@@ -958,40 +1048,40 @@ describe( 'CodeBlockEditing', () => {
 
 		describe( 'getSelectedContent()', () => {
 			it( 'should not engage when there is nothing selected', () => {
-				setModelData( model, '<codeBlock language="language-css">fo[]o<softBreak></softBreak>bar</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">fo[]o<softBreak></softBreak>bar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal( '' );
 			} );
 
 			it( 'should wrap a partial multi-line selection into a code block (#1)', () => {
-				setModelData( model, '<codeBlock language="language-css">fo[o<softBreak></softBreak>b]ar</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">fo[o<softBreak></softBreak>b]ar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<codeBlock language="language-css">o<softBreak></softBreak>b</codeBlock>'
+					'<codeBlock language="css">o<softBreak></softBreak>b</codeBlock>'
 				);
 			} );
 
 			it( 'should wrap a partial multi-line selection into a code block (#2)', () => {
-				setModelData( model, '<codeBlock language="language-css">fo[o<softBreak></softBreak>]bar</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">fo[o<softBreak></softBreak>]bar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<codeBlock language="language-css">o<softBreak></softBreak></codeBlock>'
+					'<codeBlock language="css">o<softBreak></softBreak></codeBlock>'
 				);
 			} );
 
 			it( 'should wrap a partial multi-line selection into a code block (#3)', () => {
-				setModelData( model, '<codeBlock language="language-css">[foo<softBreak></softBreak>bar]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">[foo<softBreak></softBreak>bar]</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<codeBlock language="language-css">foo<softBreak></softBreak>bar</codeBlock>'
+					'<codeBlock language="css">foo<softBreak></softBreak>bar</codeBlock>'
 				);
 			} );
 
 			it( 'should wrap a complete single-line selection into a code block', () => {
-				setModelData( model, '<codeBlock language="language-css">[foo]</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">[foo]</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<codeBlock language="language-css">foo</codeBlock>'
+					'<codeBlock language="css">foo</codeBlock>'
 				);
 			} );
 
@@ -1000,7 +1090,7 @@ describe( 'CodeBlockEditing', () => {
 					allowAttributes: 'code'
 				} );
 
-				setModelData( model, '<codeBlock language="language-css">[fo]o<softBreak></softBreak>bar</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">[fo]o<softBreak></softBreak>bar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
 					'<$text code="true">fo</$text>'
@@ -1012,7 +1102,7 @@ describe( 'CodeBlockEditing', () => {
 					allowAttributes: 'code'
 				} );
 
-				setModelData( model, '<codeBlock language="language-css">foo<softBreak></softBreak>b[a]r</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>b[a]r</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
 					'<$text code="true">a</$text>'
@@ -1020,35 +1110,35 @@ describe( 'CodeBlockEditing', () => {
 			} );
 
 			it( 'should now wrap a partial single-line selection into an inline code when the attribute is disallowed', () => {
-				setModelData( model, '<codeBlock language="language-css">foo<softBreak></softBreak>b[a]r</codeBlock>' );
+				setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>b[a]r</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal( 'a' );
 			} );
 
 			it( 'should preserve a code block in a cross-selection (#1)', () => {
 				setModelData( model,
-					'<paragraph>[x</paragraph><codeBlock language="language-css">fo]o<softBreak></softBreak>bar</codeBlock>' );
+					'<paragraph>[x</paragraph><codeBlock language="css">fo]o<softBreak></softBreak>bar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<paragraph>x</paragraph><codeBlock language="language-css">fo</codeBlock>'
+					'<paragraph>x</paragraph><codeBlock language="css">fo</codeBlock>'
 				);
 			} );
 
 			it( 'should preserve a code block in a cross-selection (#2)', () => {
 				setModelData( model,
-					'<paragraph>[x</paragraph><codeBlock language="language-css">foo<softBreak></softBreak>b]ar</codeBlock>' );
+					'<paragraph>[x</paragraph><codeBlock language="css">foo<softBreak></softBreak>b]ar</codeBlock>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<paragraph>x</paragraph><codeBlock language="language-css">foo<softBreak></softBreak>b</codeBlock>'
+					'<paragraph>x</paragraph><codeBlock language="css">foo<softBreak></softBreak>b</codeBlock>'
 				);
 			} );
 
 			it( 'should preserve a code block in a cross-selection (#3)', () => {
 				setModelData( model,
-					'<codeBlock language="language-css">foo<softBreak></softBreak>b[ar</codeBlock><paragraph>x]</paragraph>' );
+					'<codeBlock language="css">foo<softBreak></softBreak>b[ar</codeBlock><paragraph>x]</paragraph>' );
 
 				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
-					'<codeBlock language="language-css">ar</codeBlock><paragraph>x</paragraph>'
+					'<codeBlock language="css">ar</codeBlock><paragraph>x</paragraph>'
 				);
 			} );
 		} );

+ 3 - 3
packages/ckeditor5-code-block/tests/codeblockui.js

@@ -79,7 +79,7 @@ describe( 'CodeBlockUI', () => {
 			sinon.assert.calledOnce( executeSpy );
 			sinon.assert.calledOnce( focusSpy );
 			sinon.assert.calledWithExactly( executeSpy.firstCall, 'codeBlock', {
-				language: 'language-cs',
+				language: 'cs',
 				forceValue: true
 			} );
 		} );
@@ -157,7 +157,7 @@ describe( 'CodeBlockUI', () => {
 
 				expect( listView.items.get( 2 ).children.first.isOn ).to.be.false;
 
-				command.value = 'language-cs';
+				command.value = 'cs';
 				expect( listView.items.get( 2 ).children.first.isOn ).to.be.true;
 			} );
 
@@ -216,7 +216,7 @@ describe( 'CodeBlockUI', () => {
 				sinon.assert.calledOnce( executeSpy );
 				sinon.assert.calledOnce( focusSpy );
 				sinon.assert.calledWithExactly( executeSpy.firstCall, 'codeBlock', {
-					language: 'language-plaintext'
+					language: 'plaintext'
 				} );
 			} );
 		} );

+ 4 - 4
packages/ckeditor5-code-block/tests/manual/codeblock.html

@@ -1,8 +1,8 @@
 <div id="editor">
-<pre><code class="plaintext">Plain text</code></pre>
+<pre><code class="language-plaintext">Plain text</code></pre>
 	<p>Getting used to an entirely different culture can be challenging. While it’s also nice to learn about cultures online or from books, nothing comes close to experiencing cultural diversity in person.</p>
 	<p>You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>
-<pre><code class="javascript">function foo() {
+<pre><code class="language-javascript">function foo() {
 	console.log( 'indented using 1 tab' );
 }
 
@@ -10,9 +10,9 @@ function bar() {
     console.log( 'indented using spaces' );
 }</code></pre>
 	<p>Getting used to an entirely different culture can be challenging. While it’s also nice to learn about cultures online or from books, nothing comes close to experiencing cultural diversity in person. You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>
-<pre><code class="css">body { color: red }</code></pre>
+<pre><code class="language-css">body { color: red }</code></pre>
 <p>You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>
-<pre><code class="css">p { font-size: 10px; }</code></pre>
+<pre><code class="language-css">p { font-size: 10px; }</code></pre>
 <p>Some ASCII art:</p>
 <pre><code> ________________
 /                \

File diff suppressed because it is too large
+ 3 - 3
packages/ckeditor5-code-block/tests/manual/rtl.html