-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce new CodeMirror block for editing code. #665
Conversation
7d0defe
to
32275e5
Compare
I think this would be an awesome thing to have in the editor. This could also potentially become a good test case for building a plugin that registers a block. I doubt the vast majority of WordPress users will need/want this kind of content block, but any programmer would probably really like this! |
thanks @BE-Webdesign - your comment makes me realize that I should create this at some point outside of the project as a new-editor plugin!~ closing for now then |
19674f4
to
06893cb
Compare
086e7b4
to
d75a2fc
Compare
blocks/library/code-mirror/index.js
Outdated
const { html } = query; | ||
|
||
const toEditor = s => he.decode( s.split( '<br>' ).join( '\n' ) ); | ||
const fromEditor = s => he.encode( s, { useNamedReferences: true } ).split( '\n' ).join( '<br>' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use text
to query for the attribute, and <pre><code>
, this is all not needed? React will do it by itself? That's what I did with the plain code block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…will investigate, thanks!
have you not had any problems with special characters or whitespace? can I get rid of he
altogether?
(note also that he
has a few cool points, like being able to choose how and when characters get encoded)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove it entirely, yes. It will convert to named entities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(note also that
he
has a few cool points, like being able to choose how and when characters get encoded)
It's also a behemoth of a library, at least 24kb minified + gzipped. Might be a point toward dynamically enqueueing block scripts separately based on presence in content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! I pulled it in initially because it was a prototype. I'm very glad you were aware of how <pre><code>
changed the semantics. much better now!
blocks/library/code-mirror/index.js
Outdated
edit( { attributes, setAttributes } ) { | ||
return ( | ||
<div> | ||
<select onBlur={ ( { target: { value } } ) => setAttributes( { language: value } ) }> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't onChange
be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
had onChange
to begin with but eslint
yapped at me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used onChange
- please see my new comment asking about this…
blocks/library/code-mirror/index.js
Outdated
{ [ 'css', 'diff', 'javascript', 'markdown', 'pegjs', 'php' ].map( language => ( | ||
<option | ||
key={ language } | ||
selected={ language === attributes.language } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting a warning:
Warning: Use the
defaultValue
orvalue
props on<select>
instead of settingselected
on<option>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks. I honestly didn't pay any attention to this (obviously). it seems like I need to make it a setting in the page inspector too but at the time I didn't know about that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used value
post-content.js
Outdated
@@ -163,6 +163,10 @@ window._wpGutenbergPost = { | |||
'https://make.wordpress.org/core/2017/01/17/editor-technical-overview/', | |||
'<!-- /wp:core/embed -->', | |||
|
|||
'<!-- wp:core/code-mirror language="php" -->', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JS? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…will change to Elm 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to Elm 😄
d75a2fc
to
a331c56
Compare
I've got some difficulty understanding the CSS errors. They are coming in via CodeMirror. Honestly I would prefer to be able to dynamically load the CSS but I'm not sure how that would happen. if someone would be willing to help me through these errors/async issues I'll buy you an 🍦 |
blocks/library/code-mirror/index.js
Outdated
require( 'codemirror/mode/markdown/markdown' ); | ||
require( 'codemirror/mode/pegjs/pegjs' ); | ||
require( 'codemirror/mode/php/php' ); | ||
require( 'codemirror/lib/codemirror.css' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are more of these. if we had a way to async load them I would do so on-demand and provide the full suite with a better language selector
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try using import
instead of require
here? It should teach Webpack to async load all those files. I guess we would still have to wrap CodeMirror
into its own component to make sure those deps are loaded only when it mounts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried using import()
and it didn't seem to recognize it. In d957054 I added the use of require.ensure()
which seems to mostly work. There is still an issue that it's bundling the entirety of the modes directory into one chunk instead of many.
f1f4488
to
b88aa08
Compare
FYI: @georgestephanis has a feature plugin for introducing CodeMirror into other areas of WP. The block perhaps could be made part of that plugin and re-use the copy of CodeMirror that it includes. |
Just for a separation of concerns, and to keep Gutenberg a little bit lighter, it might be wise for the Gutenberg version of a code editor block to just be a plain textarea, and have the CodeMirror-WP plugin handle turning it more interactive and engaging? I'm happy to add anyone to the cm repo, just ask. |
thanks @georgestephanis. the block editor is a very good place, in my opinion, to pull in code mirror. since it is implementation specific I would imagine a WordPress plugin that both registers the editor block and includes the script into the browser. this PR isn't going to be considered ready regardless until we can asynchronously load in CodeMirror and its modes. it would keep Gutenberg lightweight and make code editing first-class. actually I would prefer to generalize it as something like "syntax editor" or "code editor" in its name so we could swap out CodeMirror if we wanted. (it started out this way as "code" until @iseulde took that name) |
f42a1e3
to
5660dd1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have async loading of the library yet?
Is this the only remaining blocker here? Do we want to have it in core or rather as a plugin? I'm happy to help with this one.
I'm not surprised :) Using CodeMirror was my first thought when I saw the existing code block 😄 Thanks for opening this PR! |
@gziolo WordPress 4.9 will ship with it, so we can enqueue it from there, which would make things easier. It'd be nice to still async load it, if that's something you want to explore. |
If core adds it, let's add it to the default code block, but also keep a "plain" type (no language). Also note we'll need highlighting on the front-end. Prism is quite nice. |
We used it for the docs: http://gutenberg-devdoc.surge.sh/block-api/ With a tweaked stylesheet. |
And agreed that if it lands in core we should make it part of the main block. |
5660dd1
to
272c6e6
Compare
This has been updated to lazy-load CodeMirror and also the language modes. There are some problems indicated inside the code itself on those. Notably:
At first I tried manually inserting a new The tests are still failing and I didn't really bother to look at why. The PR has known issues to address before fixing the tests.
|
c344cb1
to
fab4ec9
Compare
// @see node_modules/codemirror/modes | ||
// when supported we can probably replace with standard `import()` syntax | ||
const modes = { | ||
apl: { label: 'APL', req: () => System.import( 'codemirror/mode/apl/apl' ) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System.import
is deprecated. You should use dynamic import
instead`. See webpack 3 announcement post: https://medium.com/webpack/webpack-3-official-release-15fd2dd8f07b:
import(/* webpackChunkName: "my-chunk-name" */ 'module');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed we are on Webpack 2.7, but I think it still supports import
, but without this magic comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the comment. import()
was throwing errors as not supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can double check though too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we miss something in Babel config ¯_(ツ)_/¯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to that ^^^ it seems like it's still experimental in babel so even though we have webpack support for it we're not going to get it unless we pull in a new dependency. I'm happy leaving this PR without additional dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/tc39/proposal-dynamic-import - it's stage 3. It learned yesterday that Gutenberg uses only features that are accepted by ECMA TC39. So this one needs to wait. It's good as it is :)
d85bc27
to
6b222b5
Compare
// when this CSS was imported dynamically | ||
// it was causing major visual glitches to | ||
// the editor which completely broke it | ||
require( 'codemirror/lib/codemirror.css' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All styles are loaded using the following syntax:
import './style.scss';
Webpack uses the following pattern test: /style\.s?css$/,
to match styles. So it should work here, too. Unless this is the reason why it breaks :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I think it had to do with race conditions on the mounted components. I was comparing to async loading as the dynamic bits though vs. import
statement.
mainly I meant to convey that this stylesheet seemed to need to load before the editor and I wasn't sure how best to do that
Yes, it's enforced to add fixtures for all blocks in here: https://github.com/WordPress/gutenberg/blob/master/blocks/test/full-content.js#L198. |
6b222b5
to
0bec9a2
Compare
While demonstrating Gutenberg to my team the question was asked about editing code blocks: "it's so terribly difficult in the WordPress editor." Wanting to demonstrate some of the flexibility that the block system provides I created a new block type which turns a normal block into an instance of the CodeMirror editor. This should eventually replace the standard `code` block once it's ready for production. At the moment it lazy-loads the `CodeMirror` editor _and_ the mode files, so thankfully this keeps the initial builds small.
a714d0d
to
94abcdc
Compare
As talked about in this week's chat, closing this as now Codemirror is in for one block it needs iterating. |
https://github.com/WordPress/gutenberg/blob/master/components/code-editor/index.js - we have CodeMirror integrated here, we should try to use a similar approach or even better extend the existing one to make it more flexible. |
😢 this was my favorite block |
While demonstrating Gutenberg to my team the question was asked about
editing code blocks: "it's so terribly difficult in the WordPress editor."
Wanting to demonstrate some of the flexibility that the block system
provides I created a new block type which turns a normal block into an
instance of the CodeMirror editor.
Issues:
Also shown here with a degraded network connection to show the async code loading in when requested
