Skip to content

Commit feaf934

Browse files
committed
chore: return all GUCs for pgconf completion
I just realized, that VS Code will perform search and ranking parameters itself, so we do not need to add extra logic. Code with binary search, prefix match, etc... is deleted and now we just return all known GUCs.
1 parent 9d7ed48 commit feaf934

File tree

2 files changed

+4
-187
lines changed

2 files changed

+4
-187
lines changed

src/pgconf.ts

Lines changed: 4 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,129 +2,17 @@ import * as vscode from 'vscode';
22

33
import * as constants from './constants';
44

5-
function binarySearchPrefix(parameters: string[], input: string) {
6-
let start = 0;
7-
let end = parameters.length;
8-
9-
while (start < end) {
10-
const mid = Math.floor((start + end) / 2);
11-
const cur = parameters[mid];
12-
const cmp = cur.localeCompare(input);
13-
if (cmp < 0) {
14-
start = mid + 1;
15-
} else if (cmp > 0) {
16-
// end = mid - 1;
17-
end = mid;
18-
} else {
19-
/* There are must be no duplicates */
20-
return mid;
21-
}
22-
}
23-
24-
if (start === parameters.length) {
25-
return -1;
26-
}
27-
28-
/*
29-
* As an input we are searching a prefix and not exact match,
30-
* so 'start' can point to any element: 1) with such prefix
31-
* or 2) previous element of 1.
32-
* i.e. with input 'enabl' binary search can result in 'start'
33-
* pointing to either 'effective_io_concurrency' (previous) or
34-
* 'enable_async_append' (target), so we should check both cases.
35-
*/
36-
if (parameters[start].startsWith(input)) {
37-
return start;
38-
}
39-
40-
if ( start < parameters.length - 1
41-
&& parameters[start + 1].startsWith(input)) {
42-
return start + 1;
43-
}
44-
45-
return -1;
46-
}
47-
48-
export function getParamsByPrefix(parameters: string[], input: string): [number, number] | number | undefined {
49-
const start = binarySearchPrefix(parameters, input);
50-
51-
if (start === -1) {
52-
/* No arguments match */
53-
return;
54-
}
55-
56-
/* Last argument is the only */
57-
if (start === parameters.length - 1) {
58-
return start;
59-
}
60-
61-
/*
62-
* Linearly search all prefixes. This is ok, since we know that there
63-
* are not so many parameters
64-
*/
65-
let i = start;
66-
while (i < parameters.length - 1 && parameters[i + 1].startsWith(input)) {
67-
++i;
68-
}
69-
70-
if (start !== i) {
71-
/* Return 'i + 1', so Array.slice can be used by simple array expansion */
72-
return [start, i + 1];
73-
} else {
74-
return start;
75-
}
76-
}
77-
785
class PgConfCompletionProvider implements vscode.CompletionItemProvider {
6+
items?: vscode.CompletionItem[];;
797
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) {
808
const range = document.getWordRangeAtPosition(position);
819
if (!range) {
10+
/* Check this is a word (parameter) */
8211
return;
8312
}
8413

85-
/*
86-
* For now use simple prefix match for autocompletion.
87-
* In future versions we should add fuzzy string match,
88-
* i.e. based on Jaccard or Levenshtein.
89-
*
90-
* XXX: When implementing we should keep in mind, that
91-
* TS/JS is slow and likes to allocate huge memory, so
92-
* prefer simple, but faster algorithms.
93-
*/
94-
const param = document.getText(range);
95-
const parameters = this.getGUCs();
96-
const prefixRange = getParamsByPrefix(parameters, param);
97-
if (prefixRange === undefined) {
98-
return;
99-
}
100-
101-
if (typeof prefixRange === 'number') {
102-
return [new vscode.CompletionItem(parameters[prefixRange])];
103-
}
104-
105-
if (Array.isArray(prefixRange)) {
106-
return parameters.slice(...prefixRange).map(p => new vscode.CompletionItem(p));
107-
}
108-
}
109-
110-
private getGUCs() {
111-
/*
112-
* Use only builtin GUCs, because tracking contribs is hard task.
113-
* We of course can traverse 'contrib/' directory and find all *.c
114-
* files - that what I did just now.
115-
*
116-
* But this approach gives another pain - resources consumption, IO,
117-
* CPU and memory. When I tested such logic on medium sized contrib dir
118-
* I faced high CPU consumption (even fans started making noise) and
119-
* memory (extension profiler showed up to 1GB heap size with sawtooth
120-
* shaped graph).
121-
*
122-
* Another approach is to keep track of all contribs in code, i.e. have
123-
* another 'contribGucs' in 'constants.ts' with Map contrib -> it's GUCs,
124-
* but it will require to constant monitoring for changes, which is now
125-
* doesn't sound appealing yet.
126-
*/
127-
return constants.getWellKnownConfigurationParameters();
14+
return this.items ??= constants.getWellKnownConfigurationParameters()
15+
.map(x => new vscode.CompletionItem(x));
12816
}
12917
}
13018

src/test/suite/unit.test.ts

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -76,75 +76,4 @@ suite('Unit', async function () {
7676
assert.equal(actual, expected, `${type}: ${count}`);
7777
}
7878
});
79-
80-
test('getParamsByPrefix', function() {
81-
type functionResult = [number, number] | number | undefined;
82-
type testData = [string[], string, functionResult];
83-
const _ = (arr: string[], input: string, idx: functionResult): testData => [arr, input, idx];
84-
const data: testData[] = [
85-
/* Simple binary search tests */
86-
_(['a', 'b', 'c'], 'a', 0),
87-
_(['a', 'b', 'c'], 'b', 1),
88-
_(['a', 'b', 'c'], 'c', 2),
89-
_(['a', 'b', 'c'], 'e', undefined),
90-
_(['b', 'c', 'd'], 'a', undefined),
91-
_(['a', 'aa', 'aaa'], 'a', [0, 3]),
92-
93-
/* Prefix search */
94-
_(['abc', 'def', 'ghi'], 'ab', 0),
95-
_(['abc', 'def', 'ghi'], 'de', 1),
96-
_(['abc', 'def', 'ghi'], 'gh', 2),
97-
_(['abcd', 'abce', 'abcf'], 'ab', [0, 3]),
98-
_(['abcd', 'abce', 'abcf'], 'xx', undefined),
99-
100-
/* Realistic input */
101-
...[
102-
['enable', undefined],
103-
['jo', 1],
104-
['lc_m', [4, 6]],
105-
['lc_n', 6],
106-
].map(([input, r]) => _([
107-
'jit_tuple_deforming',
108-
'join_collapse_limit',
109-
'krb_caseins_users',
110-
'krb_server_keyfile',
111-
'lc_messages',
112-
'lc_monetary',
113-
'lc_numeric',
114-
], input as string, r as functionResult)),
115-
116-
...[
117-
['b', [0, 6]],
118-
['bg', [0, 3]],
119-
['bon', [3, 5]],
120-
['bonjour', [3, 5]],
121-
['bonjour_', 4],
122-
].map(([input, r]) => _([
123-
'bgwriter_flush_after',
124-
'bgwriter_lru_maxpages',
125-
'bgwriter_lru_multiplier',
126-
'bonjour',
127-
'bonjour_name',
128-
'bytea_output',
129-
], input as string, r as functionResult)),
130-
];
131-
for (const [array, prefix, expected] of data) {
132-
const actual = configfile.getParamsByPrefix(array, prefix);
133-
assert.deepStrictEqual(actual, expected, `${prefix} - ${array}`);
134-
}
135-
136-
/* Some integration testing */
137-
const parameters = constants.getWellKnownConfigurationParameters();
138-
const check = (input: string) => {
139-
const range = configfile.getParamsByPrefix(parameters, input);
140-
assert.ok(Array.isArray(range));
141-
/* Use set, so we can add elements anytime */
142-
const expected = parameters.filter(p => p.startsWith(input));
143-
assert.deepStrictEqual(new Set(parameters.slice(...range)), new Set(expected));
144-
};
145-
146-
check('pg_stat_sta');
147-
check('enabl');
148-
check('max_paral');
149-
});
15079
});

0 commit comments

Comments
 (0)