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
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions utils/vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions utils/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "carbon-vscode",
"displayName": "Carbon Language",
"version": "0.0.5",
"version": "0.0.6",
"publisher": "carbon-lang",
"description": "Carbon language support for Visual Studio Code.",
"repository": {
Expand Down Expand Up @@ -42,8 +42,18 @@
"properties": {
"carbon.carbonPath": {
"type": "string",
"description": "The path to the 'carbon' binary.",
"description": "The path to the `carbon` binary.",
"default": "./bazel-bin/toolchain/carbon"
},
"carbon.carbonServerCommandArgs": {
"type": "string",
"description": "Extra flags to pass to `carbon` before the `language-server` subcommand, such as `-v` for debugging.",
"default": ""
},
"carbon.carbonServerSubcommandArgs": {
"type": "string",
"description": "Extra flags to pass to the `language-server` subcommand.",
"default": ""
}
}
},
Expand Down
42 changes: 40 additions & 2 deletions utils/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
* This is the main launcher for the LSP extension.
*/

import { workspace, ExtensionContext, commands } from 'vscode';
import {
workspace,
ExtensionContext,
commands,
WorkspaceConfiguration,
} from 'vscode';

import {
LanguageClient,
Expand All @@ -18,6 +23,39 @@ import {

let client: LanguageClient;

/**
* Splits a CLI-style quoted string.
*/
function splitQuotedString(carbonArgs: string): string[] {
const regex = /"([^"]*)"|'([^']*)'|(\S+)/g;
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.

if (match[1]) {
result.push(match[1]);
} else if (match[2]) {
result.push(match[2]);
} else if (match[3]) {
result.push(match[3]);
}
}
return result;
}

/**
* Combines the `language-server` command with args from settings.
*/
function buildServerArgs(settings: WorkspaceConfiguration): string[] {
const result: string[] = [];
result.push(...splitQuotedString(settings.get('carbonServerCommandArgs', '')));
result.push('language-server');
result.push(
...splitQuotedString(settings.get('carbonServerSubcommandArgs', ''))
);
return result;
}

export function activate(context: ExtensionContext) {
const settings = workspace.getConfiguration('carbon');

Expand All @@ -28,7 +66,7 @@ export function activate(context: ExtensionContext) {
'carbonPath',
context.asAbsolutePath('./bazel-bin/toolchain/carbon')
),
args: ['language-server'],
args: buildServerArgs(settings),
};

const clientOptions: LanguageClientOptions = {
Expand Down