Skip to content
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

Support language-server arguments in the extension. #5056

Merged
merged 4 commits into from
Mar 4, 2025

Conversation

jonmeow
Copy link
Contributor

@jonmeow jonmeow commented Mar 3, 2025

I was on the fence about just having a string which was "language-server", but was thinking split options might be less error-prone (e.g., changing options to just "-v" and trying to figure out why nothing worked).

@github-actions github-actions bot added the utilities Utilities for working with Carbon code: syntax highlighting, editor plugins, etc. label Mar 3, 2025
@github-actions github-actions bot requested a review from zygoloid March 3, 2025 17:43
const result = [];
let match;

while ((match = regex.exec(carbonArgs)) !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this converts --foo="bar baz" to two arguments: --foo="bar and baz", because the regex will greedily pick the longest match starting with the -; that seems surprising to me. If we exclude ' and " from the \S, we'd get --foo= and bar baz, which still seems surprising to me but is closer to what I'd expect.

Maybe we could match "([^"]*)"|'([^']*)'|([^'"\s]+)|(\s+), and append to the current argument in the first three cases and start a new argument in the fourth case?

Copy link
Contributor Author

@jonmeow jonmeow Mar 3, 2025

Choose a reason for hiding this comment

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

Note for --foo="bar baz (no closing quote), the suggestion would fail [silently, discarding args]. In the current code, it falls under the \S case -- but if you keep the \S case, your concatenation approach won't work.

I understand it's not perfect, but I'm also not familiar enough with typescript to fix this quickly. Let me know if you'd prefer I close the PR, or if you can suggest code.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, maybe just add a TODO for now, then.

Is there any reasonable way to add a test for this?

Copy link
Contributor Author

@jonmeow jonmeow Mar 3, 2025

Choose a reason for hiding this comment

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

OK, maybe just add a TODO for now, then.

Ended up spending enough time on this that I just figured out a different approach which should work.

Is there any reasonable way to add a test for this?

Bazel doesn't seem to be prioritizing js/ts support, and I'm not sure what your "reasonable" threshold is.

There's https://github.com/aspect-build/rules_js and https://github.com/aspect-build/rules_ts, but they're not well-aimed at our use-case of having just a little ts buried in the repo here and there. I've considered investing time into figuring that out, have occasionally banged my head against bazel for a couple hours trying to figure out a good setup (now including today), but so far I've instead tried to keep the amount of JavaScript/TypeScript as niche as possible (now including today). My guess is that with a couple days I could probably figure out something to have a unit test for this code -- but it's not trivial, and I suspect it'd be fragile. I continue to hope that someone more fluent in ts and bazel will come along and clean up.

TBH if I could find an easy library to parse the args, I would. But quick searches were turning up advice on how to just write it myself.

Comment on lines 29 to 73
function splitQuotedString(argsString: string): string[] {
const args: string[] = [];
let arg = '';
let inSingleQuotes = false;
let inDoubleQuotes = false;
let escaped = false;

while ((match = regex.exec(carbonArgs)) !== null) {
if (match[1]) {
result.push(match[1]);
} else if (match[2]) {
result.push(match[2]);
} else if (match[3]) {
result.push(match[3]);
for (const char of argsString) {
if (escaped) {
arg += char;
escaped = false;
continue;
}
switch (char) {
case '\\':
escaped = true;
continue;
case "'":
if (!inDoubleQuotes) {
inSingleQuotes = !inSingleQuotes;
continue;
}
break;
case '"':
if (!inSingleQuotes) {
inDoubleQuotes = !inDoubleQuotes;
continue;
}
break;
case ' ':
if (!inSingleQuotes && !inDoubleQuotes) {
args.push(arg);
arg = '';
}
break;
}
arg += char;
}
return result;

if (arg.length > 0) {
args.push(arg);
}

return args;
}
Copy link
Contributor

@zygoloid zygoloid Mar 4, 2025

Choose a reason for hiding this comment

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

Suggested change
function splitQuotedString(argsString: string): string[] {
const args: string[] = [];
let arg = '';
let inSingleQuotes = false;
let inDoubleQuotes = false;
let escaped = false;
while ((match = regex.exec(carbonArgs)) !== null) {
if (match[1]) {
result.push(match[1]);
} else if (match[2]) {
result.push(match[2]);
} else if (match[3]) {
result.push(match[3]);
for (const char of argsString) {
if (escaped) {
arg += char;
escaped = false;
continue;
}
switch (char) {
case '\\':
escaped = true;
continue;
case "'":
if (!inDoubleQuotes) {
inSingleQuotes = !inSingleQuotes;
continue;
}
break;
case '"':
if (!inSingleQuotes) {
inDoubleQuotes = !inDoubleQuotes;
continue;
}
break;
case ' ':
if (!inSingleQuotes && !inDoubleQuotes) {
args.push(arg);
arg = '';
}
break;
}
arg += char;
}
return result;
if (arg.length > 0) {
args.push(arg);
}
return args;
}
function splitQuotedString(argsString: string): string[] {
const args: string[] = [];
let arg = '';
let inSingleQuotes = false;
let inDoubleQuotes = false;
let escaped = false;
let empty = true;
for (const char of argsString) {
const was_empty = empty;
empty = false;
if (escaped) {
arg += char;
escaped = false;
continue;
}
switch (char) {
case '\\':
escaped = true;
continue;
case "'":
if (!inDoubleQuotes) {
inSingleQuotes = !inSingleQuotes;
continue;
}
break;
case '"':
if (!inSingleQuotes) {
inDoubleQuotes = !inDoubleQuotes;
continue;
}
break;
case ' ':
if (!inSingleQuotes && !inDoubleQuotes) {
if (!was_empty) {
args.push(arg);
arg = '';
}
empty = true;
continue;
}
break;
}
arg += char;
}
if (!empty) {
args.push(arg);
}
return args;
}

Some tweaks to handle "" as an argument, multiple spaces between arguments, and an argument list ending in a space.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adopting this, but in the future I'd find smaller deltas more helpful. The delta is broken and it's difficult to actually understand the changes here without applying them.

Copy link
Contributor Author

@jonmeow jonmeow Mar 4, 2025

Choose a reason for hiding this comment

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

Note, part of the issue here I think is you're [manually] making a suggestion around code that I assume GitHub was not happy to take a suggestion for.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, github's suggestion feature is a bit broken, unfortunately. I'll try to split the suggestion up into chunks in future.

jonmeow and others added 2 commits March 4, 2025 09:26
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
@zygoloid zygoloid added this pull request to the merge queue Mar 4, 2025
Merged via the queue into carbon-language:trunk with commit 92b3e61 Mar 4, 2025
8 checks passed
@jonmeow jonmeow deleted the ext-args branch March 4, 2025 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
utilities Utilities for working with Carbon code: syntax highlighting, editor plugins, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants