Skip to content

Conversation

@joedolson
Copy link
Contributor

Uses the work from @siliconforks, and adds a fix for PHP editors.

Trac ticket: https://core.trac.wordpress.org/ticket/42822


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Uses the work from @siliconforks, and adds a fix for PHP editors.
@github-actions
Copy link

github-actions bot commented Jan 28, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props joedolson, westonruter, siliconforks.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@joedolson joedolson requested a review from westonruter January 28, 2026 04:07
@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

isAlphaKey && 'attribute' === token.type ||
'=' === token.string && token.state.htmlState && token.state.htmlState.tagName;
'=' === event.key &&
'=' === token.string && token.state.curState && token.state.curState.htmlState && token.state.curState.htmlState.tagName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to log out token.state.curState && token.state.curState.htmlState && token.state.curState.htmlState.tagName and it's always undefined. So this seems to have the effect of disabling the condition entirely.

Comment on lines 5 to 6
/* jshint esversion: 11 */
/* eslint-env es2020 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm opting-in this file for ES11/2020 to be able to use optional chaining operator. This is, in fact, being used in the core codebase elsewhere, specifically in built files coming from Gutenberg: https://github.com/search?q=repo%3AWordPress%2FWordPress+%2F%5Cw%2B%5C%3F%5C.%5Cw%2B%2F+language%3AJavaScript&type=code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also Core-64562

Comment on lines 320 to 326
'/' === event.key && 'tag' === token.type ||
isAlphaKey && 'tag' === token.type ||
isAlphaKey && 'attribute' === token.type ||
'=' === token.string && token.state.htmlState && token.state.htmlState.tagName;
( '/' === event.key && 'tag' === token.type ) ||
( isAlphaKey && 'tag' === token.type ) ||
( isAlphaKey && 'attribute' === token.type ) ||
( '=' === event.key && '=' === token.string && token.state.curState?.htmlState?.tagName )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added parentheses because the existing logic was confusing to read. I have no one to blame but myself, as this originated in the Better Code Editing feature plugin: WordPress/better-code-editing@f49a012

@siliconforks
Copy link

Just to summarize the situation ... there are two (actually three) separate fixes required here:

  1. The main problem in #63161 and #42822 is that codemirror.showHint() is getting called every time the up arrow or down arrow key is pressed. This means that the autocomplete list gets refreshed and the first item gets selected (again), so it is impossible to use the arrow keys to move to any other item - the first item just keeps getting re-selected. To fix this, a condition needs to be added checking which key was pressed. Note that this is already done for CSS and JavaScript autocompletion - the code checks whether isAlphaKey is true or event.key contains some other character which should trigger autocompletion. By adding the isAlphaKey condition to the PHP autocompletion code, we should fix the issue in #63161.

  2. The problem for HTML autocompletion in #42822 is almost exactly the same thing - the code is not checking which key was pressed. Actually, it is slightly more complicated because the code for HTML autocompletion really has five separate conditions all joined together with the || operator. The first four conditions appear to be fine - they already check which key was pressed. The fifth condition (which handles HTML attribute value autocompletion) is the problem - it apparently never had a check for this. By adding a check for event.key === '=', we should fix the HTML autocompletion.

  3. The final problem appears to be a separate bug not really related to the first two - it seems that CodeMirror has changed the structure of the token.state object. When this code was first written (in r41376), there was a token.state.htmlState property. At some point CodeMirror changed this so that this property no longer exists. Instead they added a token.state.curState.htmlState property and token.state.html.htmlState property, which appear to be the same thing.

@westonruter - are you testing with the old version of CodeMirror (the one that shipped with WordPress 6.9) or the newer version of CodeMirror (the one you're working on upgrading to in #48456)?

@westonruter
Copy link
Member

are you testing with the old version of CodeMirror (the one that shipped with WordPress 6.9) or the newer version of CodeMirror (the one you're working on upgrading to in #48456)?

@siliconforks I'm testing with the latest CodeMirror v5 as Core-48456 was committed (438132f) and is part of this PR's commit history.

@westonruter
Copy link
Member

westonruter commented Jan 28, 2026

For reference, in the latest CodeMirror v5, here's what a token contains when editing an HTML file at the moment that = is pressed when typing out <input type=:

{
    "start": 11,
    "end": 12,
    "string": "=",
    "type": null,
    "state": {
        "inTag": null,
        "localMode": null,
        "htmlState": {
            "indented": 0,
            "tagName": "input",
            "tagStart": 0,
            "context": null
        }
    }
}

@siliconforks
Copy link

On further investigation, it looks like I was wrong about CodeMirror changing the token structure in different versions - it's not actually a version issue, it's an issue with different modes:

  • If you edit a .html file, you get a token.state.htmlState.tagName property.
  • If you edit a .php file, you get a token.state.curState.htmlState.tagName property and a token.state.html.htmlState.tagName property.

So, the it seems that the code needs to handle both cases.

This should work:

					shouldAutocomplete = (
						'<' === event.key ||
						( '/' === event.key && 'tag' === token.type ) ||
						( isAlphaKey && 'tag' === token.type ) ||
						( isAlphaKey && 'attribute' === token.type ) ||
						/*
						 * In .html files the tag name is in token.state.htmlState.tagName;
						 * in .php files the tag name is in token.state.curState.htmlState.tagName.
						 */
						( '=' === event.key && '=' === token.string && ( token.state.htmlState?.tagName || token.state.curState?.htmlState?.tagName ) )
					);

@westonruter
Copy link
Member

@siliconforks And what purpose does ( token.state.htmlState?.tagName || token.state.curState?.htmlState?.tagName ) serve? Just to verify that the token is in the context of a tag?

@siliconforks
Copy link

@siliconforks And what purpose does ( token.state.htmlState?.tagName || token.state.curState?.htmlState?.tagName ) serve? Just to verify that the token is in the context of a tag?

Yes, I'm assuming the original code (token.state.htmlState && token.state.htmlState.tagName) was there simply to avoid the possibility of autocompletion being triggered by an equals sign outside a tag. But it was also having the effect of preventing autocompletion inside a tag in .php files (although it seemed to be working fine for .html files). That's why I'm suggesting changing it to ( token.state.htmlState?.tagName || token.state.curState?.htmlState?.tagName ).

@westonruter
Copy link
Member

That's why I'm suggesting changing it to ( token.state.htmlState?.tagName || token.state.curState?.htmlState?.tagName ).

OK, I applied that change in 8cd4589. Please test.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR applies autocompletion fixes for the WordPress code editor, specifically addressing issues with PHP autocompletion logic and improving condition clarity across HTML, CSS, and JavaScript modes.

Changes:

  • Added ES2020 environment configuration for eslint
  • Fixed PHP autocompletion to only trigger on alpha keys when token type is keyword or variable
  • Improved condition clarity by adding parentheses around compound boolean expressions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

( '/' === event.key && 'tag' === token.type ) ||
( isAlphaKey && 'tag' === token.type ) ||
( isAlphaKey && 'attribute' === token.type ) ||
( '=' === event.key && '=' === token.string && (
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checks both event.key and token.string for '=', which may be redundant or indicate a logical error. If event.key is '=' and token.string is also '=', this suggests the same data is being checked twice. Verify if both checks are necessary or if one should be checking a different property.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@siliconforks right, is '=' === event.key not redundant?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe '=' === token.string is actually redundant.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly the event.key check is not redundant here - it is necessary to check event.key to make sure the user didn't just press one of the arrow keys.

Possibly the token.string check is redundant now that event.key is being checked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does seem redundant. This is the token is the token immediately preceding the cursor, so it will always be = in this case if event.key is =. It works well without it:

Screen.Recording.2026-02-02.at.11.27.26.mov

Removed in b5997a8

pento pushed a commit that referenced this pull request Feb 2, 2026
…tribute values and PHP keywords/variables.

Developed in #10807

Follow-up to [41376].

Props siliconforks, joedolson, westonruter, afercia, jorbin.
Fixes #42822.


git-svn-id: https://develop.svn.wordpress.org/trunk@61579 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Feb 2, 2026
…tribute values and PHP keywords/variables.

Developed in WordPress/wordpress-develop#10807

Follow-up to [41376].

Props siliconforks, joedolson, westonruter, afercia, jorbin.
Fixes #42822.

Built from https://develop.svn.wordpress.org/trunk@61579


git-svn-id: http://core.svn.wordpress.org/trunk@60890 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@westonruter
Copy link
Member

Committed in r61579 (7965dec)

@westonruter westonruter closed this Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants