Repository: carbon-language/carbon-lang
File: utils/vscode/src/extension.ts (TypeScript)

Code before:
          inSingleQuotes = !inSingleQuotes;
          continue;
        }
        break;
      case '"':
        if (!inSingleQuotes) {
          inDoubleQuotes = !inDoubleQuotes;
          continue;
        }
        break;
      case ' ':
        if (!inSingleQuotes && !inDoubleQuotes) {
          args.push(arg);
          arg = '';
        }
        break;
    }
    arg += char;
  }

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

  return args;
}

/**
 * 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');

  const serverOptions: ServerOptions = {
    // The Carbon server can be configured, but we try to use bazel output as a
    // fallback.
    command: settings.get(
      'carbonPath',
      context.asAbsolutePath('./bazel-bin/toolchain/carbon')

Review comment (security):
```suggestion
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.

Code after:
      inEscape = false;
      continue;
    }
    switch (char) {
      case '\\':
        // First character of an escape.
        inEscape = true;
        continue;
      case "'":
        if (!inDoubleQuotes) {
          // Single-quoted section.
          inSingleQuotes = !inSingleQuotes;
          continue;
        }
        break;
      case '"':
        if (!inSingleQuotes) {
          // Double-quoted section.
          inDoubleQuotes = !inDoubleQuotes;
          continue;
        }
        break;
      case ' ':
        if (!inSingleQuotes && !inDoubleQuotes) {
          // Space between arguments (but possibly multiple spaces).
          if (hasArg) {
            args.push(arg);
            arg = '';
            hasArg = false;
          }
          continue;
        }
        break;
    }
    arg += char;
  }

  // Finish any pending argument.
  if (hasArg) {
    args.push(arg);
  }

  return args;
}

/**
 * Combines the `language-server` command with args from settings.
 */
function buildServerArgs(settings: WorkspaceConfiguration): string[] {
  const result: string[] = [];
  result.push(