Skip to content

Add support for client-side regex validators in interactive forms - #6108

Merged
DanTup merged 6 commits into
masterfrom
interactive-forms-regex-validation
Jul 14, 2026
Merged

DanTup merged 6 commits into
masterfrom
interactive-forms-regex-validation

Conversation

@DanTup

@DanTup DanTup commented Jun 30, 2026 •

Copy link
Copy Markdown
Member

Fixes #6092

@DanTup DanTup added this to the v3.140.0 milestone Jun 30, 2026
@DanTup DanTup added in editor Relates to code editing or language features relies on sdk changes Something that requires changes in the Dart/Flutter SDK to ship before it will become available labels Jun 30, 2026
gemini-code-assist[bot]

This comment was marked as resolved.

@codecov

This comment was marked as resolved.

@DanTup
DanTup force-pushed the interactive-forms-regex-validation branch 2 times, most recently from 6230a53 to bc72a9e Compare June 30, 2026 16:58
@DanTup DanTup added the is enhancement An enhancement or improvement that should be listed in release notes but is not a bug fix. label Jun 30, 2026
@DanTup
DanTup requested a review from Copilot June 30, 2026 17:07

This comment was marked as resolved.

@DanTup
DanTup force-pushed the interactive-forms-regex-validation branch from bc72a9e to 06126cd Compare June 30, 2026 17:16
@DanTup

DanTup commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

@bwilkerson @h9jiang here is my initial proposal for client-side regex validation. Rather than just put TS into an issue, I made the changes in the code, so I could also verify what I'm proposing works. I'm happy to change as necessary to ensure we're all happy with it though.

With these changes, we can show errors and warnings for string fields (and also lists or strings), and only errors block submission.

Here's a video showing how it works (the server changes aren't done, I just cobbled together enough to test/demo). Sorry about the quality, Windows snippet recorder usually does better, but there's weird artifacts here.

Extension.Development.Host.addconstructorname.dart.-.dart_sample.-.Visual.Studio.Code.2026-06-30.16-59-49.mp4

@DanTup
DanTup requested a review from h9jiang June 30, 2026 17:20
Comment thread src/extension/analysis/form.ts Outdated
* Validates a number, returning a user-facing error message if it's not valid.
*/
private validateNumber(text: string, required: boolean): string | null {
private validateNumber(text: string, { required, isList }: { required: boolean; isList: boolean; }): string | null {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Does the validation allow for both integer and floating point values? I'm guessing the answer is 'yes' and the server is responsible for rejecting kinds of numbers that it can't handle. (Which is fine, just checking my understanding.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The current implementation does allow either (this is the same before and after this PR, my changes in number validation here are mostly for consistency - although I see I didn't make it completely consistent, so I will tidy this up a bit more tomorrow).

We could add a validator kind for numbers for whole numbers similar to how we added a regex validator for strings here (we could also just add a flag on the number field, but maybe the validator would keep things a bit more general).

I would suggest we do add this if either of us start using numeric fields, because it's a nicer experience to have them validation client-side than only at the "end" of the form, even if it is just one input.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I agree that we don't need to add them until we have a use case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I updated the test in b593384 to include a decimal number so this behaviour is clearer.

I do wonder if this behaviour is obvious though - I suspect it just fell out of using JS/TS here - @h9jiang do you have an opinion on this? If you're not already using number (or you assumed it was integers), we could "fix" that now. Otherwise we can keep as-is and decide on how to restrict to whole numbers when someone first uses it.

@DanTup

DanTup commented Jul 1, 2026

Copy link
Copy Markdown
Member Author

I pushed some minor tweaks to make validateNumber() a bit more consistent with validateString() (always return the same structured types instead of bare strings).

I did consider rewriting the built-in validation parts (required-ness, and list handling) to just map on to validators the same way that custom ones do, but it was not that trivial (and also raises questions about the exposed types, since the validators would then include kinds that are not intended to be provided by servers), so I decided to abort that for now because I don't want to drag this out for what should be implementation details.

*
* Clients must ignore validators they do not support.
*/
validators?: StringValidator[];

@h9jiang h9jiang Jul 6, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this a & logic or a | logic if multiple validator exist. I assumed it is "and".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct, I've added this to the comment:

Answers must pass all (supported) validators to be considered valid.

Comment thread src/extension/analysis/form.ts Outdated
}
}

// The severity of a violation of a validator.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is that possible to move the block up so all the type decl are together on top of the file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done!

Comment thread src/extension/analysis/form.ts Outdated
export interface RegexValidator extends Validator {
kind: 'regex';

// The (JS) regex pattern to validate the input.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The LSP has a quite strict rule of regex engine

see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#regExp

I assume the regex pattern here will adhere to the client capability. It is not necessary to reference the regex client capabilities here (but it would be more clear if we can mention that).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, I was unaware of this capability and had assumed LSP was fixed. I've updated this comment to:

/**
 * The regex pattern to validate the input.
 *
 * The server must only provide regular expressions for the engine supported by the client
 * in the `general.regularExpressions` client capability. If the server cannot provide an
 * appropriate regular expression it should not provide the regex validator.
 */

Does this make sense to you?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also FYI, I didn't realise this capability was new in LSP 3.18, so this does mean you'll need to be on the latest LSP client for the server to be passed this. I think it's probably better to require this than to make servers assume the client knows ES regex if not provided. Dart-Code is already on the latest LSP client.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see, in that case, I need to also upgrade the client to lsp 3.18, but the gopls latest release v0.23.0 is using 3.17 so I will make sure to upgrade the vscode-go to 3.18 after the current vscode-go release so it can work with the next gopls release v0.24.0.

Does this make sense to you?

Thanks. Yes this make sense to me.

*
* Clients must ignore validators they do not support.
*/
validators?: StringValidator[];

@h9jiang h9jiang Jul 6, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

do we allow multiple validator of the same kind? I assumed yes...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, I've added:

Multiple validators of the same kind are allowed.

Comment thread src/extension/analysis/form.ts Outdated

// Whether the answer matching the pattern means it is valid (no message
// reported) or invalid (message reported).
matchIsValid: boolean;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I understand the purpose of the field but I do find it confusing in combination with the message below.

The message below says, if the input does not match the pattern. But if the matchIsValid == false, then message should be positive. if the matchIsValid == true, then message should be negative.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch - I wrote the comment on message before I added matchIsValid. I've updated the comment on message now to:

The message to show if the answer is not valid according to pattern and matchIsValid.

@h9jiang h9jiang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you so much for raising this PR, I think I agree on idea of introducing the regex validator but the message and matchIsValid is a little confusing to me.

In addition, I would like Alan to review the change to the protocol.

- Move interface declarations to top
- Explain regex is based on client engine
- Expand/improve comments
@DanTup

DanTup commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@h9jiang thanks for the review! I've addressed your comments, please let me know if you think these changes are sufficient.

In addition, I would like Alan to review the change to the protocol.

Of course - if you're otherwise happy, do you want to ping him here, or have him review it as a change in the Go repo? Just let me know if I need to change anything or if you're (collectively) happy with this so I can merge here. Thanks!

*/
export type StringValidator = RegexValidator /* | FooValidator */;

/**

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hongxiang asked me to take a look. This CL seems to be mostly a Dart reflection of the agreed protocol changes, so I'm not qualified to say anything intelligent about the code. About the protocol however:

Regular expressions vary between implementations. (Even RE2, used by Go, varies across languages since the original C++ implementation is defined to operate on a byte stream wheres the Go and Java ports operation on a sequence of Unicode code points, so the "any byte" operator has no meaning.)

The LSP of course acknowledges this in its RegularExpressionsClientCapabilities feature, but it's not clear to me how that feature can realistically work, as it expects both peers (client and server) to check the regular expression engine in use and either support it, emulate it, or (more likely) have a go at at it with the wrong engine, or just refuse to do anything. Given that most (client, server) pairs have a language barrier, it seems almost doomed not to work.

I don't think our protocol changes are really making the LSP regexp situation worse than it already is, but that's my one concern with our use of regexp validation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@adonovan thanks for taking a look!

The validation here is by design entirely optional - it's only for improving the user experience and if either the server or the client dropped the validation, nothing bad should happen (since the server would do the normal server-side validation when the whole form is submitted).

So, my intention was that a server should only provide regular expression validators if it can do so in the format supported by the client (in RegularExpressionsClientCapabilities). For the VS Code LSP client, that's ES2020. If the client uses a different regex engine, then the server should either provide the regex patterns in a compatible form, or not provide them at all. (if this isn't clear from the code, I should definitely make this clearer).

It'd be nice if there was a better story for this, but I think it will be difficult to solve more generally (without implemented some very specific rules in the spec, or some parallel to regex) - I'm definitely open to ideas though :-)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This CL seems to be mostly a Dart reflection of the agreed protocol changes, so I'm not qualified to say anything intelligent about the code.

btw, I believed the goal was to share the form.ts file, so I think all changes here are potentially relevant for Go. It's not intended that anything here is Dart-specific (although my motivation is to support this in the Dart server).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Agreed, I don't think there's much we can do; I just think it is wildly unrealistic for clients and servers to do anything other than execute their own dialect of regular expressions and hope that most server-provided expressions fall in the least-common denominator subdialect.

the goal was to share the form.ts file, so I think all changes here are potentially relevant for Go

Yeah, I understand that our tools will share and execute parts of the code, I just don't know enough TS to review them competently.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I just think it is wildly unrealistic for clients and servers to do anything other than execute their own dialect of regular expressions and hope that most server-provided expressions fall in the least-common denominator subdialect.

I'll just point out that it isn't a requirement that the client and server use the same regular expression, only that they use equivalent regular expressions. It isn't even a requirement that the server use a regular expression at all, though in most cases that will be the easiest way to keep the client and server consistent with each other.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I also think it's best for servers to not return these regex validators if the clients regex engine is not one that is likely to be compatible the the expressions provided. In the Dart server we're only going to return these optional validators if the client uses the ECMAScript regex engine, because that's what they're written/tested for. This means clients that use other regex engines will fall back to the original server-validation only.

If there are significant LSP clients that are not using ECMAScript, nothing prevents the server from returning a different set of regular expressions for that engine for those clients (or if the expressions happen to be compatible, the same one).

@bwilkerson perhaps it's worth capturing the regexp engine from client capabilities in analytics?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That makes sense to me. There are probably other capabilities that it would be useful to capture as well. We should talk off-line. Let me know when you're available.

@h9jiang h9jiang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi Danny, I think this looks good.

I will copy the code form.ts to vscode-go and do some debugging once you merge it. Thank you.

@DanTup
DanTup merged commit 3479195 into master Jul 14, 2026
25 checks passed
@DanTup
DanTup deleted the interactive-forms-regex-validation branch July 14, 2026 10:17
@DanTup

DanTup commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@h9jiang thanks! I've merged the client changes here and the Dart server changes are at https://dart-review.googlesource.com/c/sdk/+/524020.

gopherbot pushed a commit to golang/vscode-go that referenced this pull request Jul 14, 2026
This CL enables the client side validation in interactive
refactoring. This CL is a sync from:
Dart-Code/Dart-Code#6108

Change-Id: I074858014f9c5c3de91ed0c7669dd301cfaacb05
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/799902
LUCI-TryBot-Result: [email protected] <[email protected]>
Reviewed-by: Madeline Kalil <[email protected]>
@DanTup DanTup modified the milestones: v3.140.0, v3.142.0 Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in editor Relates to code editing or language features is enhancement An enhancement or improvement that should be listed in release notes but is not a bug fix. relies on sdk changes Something that requires changes in the Dart/Flutter SDK to ship before it will become available

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support "real-time" validation for Interactive Forms

5 participants