Skip to content

Commit 0119bad

Browse files
GregSavinbhufmann
authored andcommitted
Enhance traceserver argument parsing scheme
1 parent 779fdc7 commit 0119bad

File tree

4 files changed

+134
-2
lines changed

4 files changed

+134
-2
lines changed

src/argparse.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
class ArgExtractor {
2+
argsStr: string;
3+
index: number;
4+
5+
constructor(argsStr: string) {
6+
this.argsStr = argsStr;
7+
this.index = 0;
8+
this.skipWhitespace();
9+
}
10+
11+
isEos(): boolean {
12+
const result = this.index >= this.argsStr.length;
13+
return result;
14+
}
15+
16+
private isWhitespace(ch: string) {
17+
const result = /\s/g.test(ch);
18+
return result;
19+
}
20+
21+
private consumeChar(): void {
22+
this.index++;
23+
}
24+
25+
private peekChar(): string {
26+
return this.argsStr[this.index];
27+
}
28+
29+
private nextChar(): string {
30+
return this.argsStr[this.index++];
31+
}
32+
33+
private skipWhitespace(): void {
34+
let char = this.peekChar();
35+
while (this.index < this.argsStr.length && this.isWhitespace(char)) {
36+
this.consumeChar();
37+
char = this.peekChar();
38+
}
39+
}
40+
41+
next(): string {
42+
let arg = '';
43+
let inQuotedString = false;
44+
45+
const handleWhitespace = (char: string): boolean => {
46+
if (inQuotedString) {
47+
arg = arg.concat(char);
48+
return false;
49+
} else {
50+
this.skipWhitespace();
51+
return true;
52+
}
53+
};
54+
55+
const handleDoubleQuote = () => {
56+
if (inQuotedString) {
57+
const peek = this.peekChar();
58+
if (peek == '"') {
59+
// literal quote
60+
this.consumeChar();
61+
arg = arg.concat(peek);
62+
} else {
63+
// end of quotation; just consume character without adding literally to arg
64+
inQuotedString = false;
65+
}
66+
} else {
67+
// open double quotation
68+
inQuotedString = true;
69+
}
70+
};
71+
72+
while (!this.isEos()) {
73+
const char = this.nextChar();
74+
if (this.isWhitespace(char)) {
75+
if (handleWhitespace(char)) {
76+
break;
77+
}
78+
} else if (char == '"') {
79+
handleDoubleQuote();
80+
} else {
81+
// some other character other than whitespace or quotes or escape character; include in arg and continue
82+
arg = arg.concat(char);
83+
}
84+
}
85+
return arg;
86+
}
87+
}
88+
89+
export function parseArgs(argsStr: string): string[] {
90+
const extractor = new ArgExtractor(argsStr);
91+
const result = [];
92+
while (!extractor.isEos()) {
93+
const arg = extractor.next();
94+
result.push(arg);
95+
}
96+
return result;
97+
}

src/test/suite/argparse.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as assert from 'assert';
2+
import * as vscode from 'vscode';
3+
import * as argparse from '../../argparse';
4+
5+
suite('Argument Parser Test Suite', () => {
6+
vscode.window.showInformationMessage('Start argument parser tests.');
7+
8+
const prefix = 'Argument parser should be able to handle argument strings ';
9+
10+
test(prefix + 'that do not use quoting', () => {
11+
const args = argparse.parseArgs('-vmargs -Dsome.property=value_without_spaces');
12+
assert.deepEqual(args, ['-vmargs', '-Dsome.property=value_without_spaces']);
13+
});
14+
15+
test(prefix + 'that have extra whitespace between arguments', () => {
16+
const args = argparse.parseArgs(' -vmargs -Dsome.property=value_without_spaces \t ');
17+
assert.deepEqual(args, ['-vmargs', '-Dsome.property=value_without_spaces']);
18+
});
19+
20+
test(prefix + 'that use quoting', () => {
21+
const args = argparse.parseArgs(' -vmargs -Dsome.property="value with spaces" \t ');
22+
assert.deepEqual(args, ['-vmargs', '-Dsome.property=value with spaces']);
23+
});
24+
25+
test(prefix + 'that use quotes with embedded literal quotes (using a sequence of 2 consecutive quotes)', () => {
26+
const args = argparse.parseArgs(' -vmargs -Dsome.property="""in literal quotes""" \t ');
27+
assert.deepEqual(args, ['-vmargs', '-Dsome.property="in literal quotes"']);
28+
});
29+
30+
test(prefix + 'recognize non-nested consecutive quotes as an empty string', () => {
31+
const parsed = argparse.parseArgs('empty_value=""');
32+
assert.deepEqual(parsed, ['empty_value=']);
33+
});
34+
});

src/test/suite/trace-server.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ suite('TraceServer Test Suite', () => {
1616

1717
test(prefix + 'arguments', () => {
1818
const args = server.getArgs_test(from);
19-
assert.deepEqual(args, ['']);
19+
assert.deepEqual(args, []);
2020
});
2121

2222
test(prefix + 'url', () => {

src/trace-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ChildProcess, spawn } from 'child_process';
22
import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
33
import treeKill from 'tree-kill';
44
import * as vscode from 'vscode';
5+
import { parseArgs } from './argparse';
56

67
// Based on github.com/eclipse-cdt-cloud/vscode-trace-extension/blob/master/vscode-trace-extension/package.json
78
// -for naming consistency purposes across sibling extensions/settings:
@@ -122,7 +123,7 @@ export class TraceServer {
122123
if (!args) {
123124
args = '';
124125
}
125-
return args.split(' ');
126+
return parseArgs(args);
126127
}
127128
getArgs_test = this.getArgs;
128129

0 commit comments

Comments
 (0)