Skip to content

Commit 67df493

Browse files
committed
chore: add granular config file parsing for each module
Different modules require different data, so having single function to parse all seems to overhead. Moreover we need to keep track of custom typedefs provided in configuration for formatter. So now this logic is splitted - we have 2 parsing function: for variables and formatter. Each function run before this module starts: parse variables before debug session starts and parse formatter part when formatting is requested. This can add some overhead, but the only thing that vscode API gives is to track *all* file changes events and of course such thing will affect everyone even if we just add some kind of 'dirty' flag.
1 parent b067f46 commit 67df493

File tree

2 files changed

+94
-147
lines changed

2 files changed

+94
-147
lines changed

src/extension.ts

Lines changed: 73 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,28 @@ class ConfigFile {
421421
formatter?: PgindentConfiguration;
422422
}
423423

424-
function parseConfigurationFile(configFile: any): ConfigFile | undefined {
424+
export function parseFormatterConfiguration(configFile: any): PgindentConfiguration | undefined {
425+
const parseTypedefs = (obj: any): string[] | undefined => {
426+
if (!obj) {
427+
return;
428+
}
429+
430+
if (typeof obj === 'string') {
431+
return [obj.trim()];
432+
} else if (Array.isArray(obj)) {
433+
return obj.map(x => x.toString());
434+
}
435+
}
436+
const typedefs = parseTypedefs(configFile.typedefs);
437+
438+
if (typedefs?.length) {
439+
return {
440+
typedefs,
441+
}
442+
}
443+
}
444+
445+
function parseVariablesConfiguration(configFile: any): VariablesConfiguration | undefined {
425446
const parseArrayMember = (obj: any): vars.ArraySpecialMemberInfo | undefined => {
426447
if (!(obj && typeof obj === 'object' && obj !== null)) {
427448
return;
@@ -519,18 +540,6 @@ function parseConfigurationFile(configFile: any): ConfigFile | undefined {
519540
}
520541
}
521542

522-
const parseTypedefs = (obj: any): string[] | undefined => {
523-
if (!obj) {
524-
return;
525-
}
526-
527-
if (typeof obj === 'string') {
528-
return [obj.trim()];
529-
} else if (Array.isArray(obj)) {
530-
return obj.map(x => x.toString());
531-
}
532-
}
533-
534543
const normalizeFuncName = (name: string) => {
535544
/*
536545
* Earlier extension versions used solib prefix in function name
@@ -832,16 +841,14 @@ function parseConfigurationFile(configFile: any): ConfigFile | undefined {
832841
const htabTypes = parseHtabTypes(configFile.htab);
833842
const simpleHashTableTypes = parseSimplehashTypes(configFile.simplehash);
834843
const bitmaskEnumMembers = parseEnumBitmasks(configFile.enums);
835-
const typedefs = parseTypedefs(configFile.typedefs);
836844

837-
let variables = undefined;
838845
if ( arrayInfos?.length
839846
|| aliasInfos?.length
840847
|| customListTypes?.length
841848
|| htabTypes?.length
842849
|| simpleHashTableTypes?.length
843850
|| bitmaskEnumMembers?.length) {
844-
variables = {
851+
return {
845852
arrayInfos,
846853
aliasInfos,
847854
customListTypes,
@@ -850,17 +857,6 @@ function parseConfigurationFile(configFile: any): ConfigFile | undefined {
850857
bitmaskEnumMembers,
851858
}
852859
}
853-
let formatter = undefined;
854-
if (typedefs?.length) {
855-
formatter = {
856-
typedefs,
857-
}
858-
}
859-
860-
return {
861-
variables,
862-
formatter,
863-
}
864860
}
865861

866862
async function promptWorkspace() {
@@ -1129,6 +1125,41 @@ async function bootstrapExtensionCommand() {
11291125
await vscode.window.showTextDocument(td);
11301126
}
11311127

1128+
export async function readConfigFile(workspace: vscode.WorkspaceFolder,
1129+
logger: utils.ILogger) {
1130+
const path = Configuration.getConfigFile(workspace.uri);
1131+
let document;
1132+
try {
1133+
document = await vscode.workspace.openTextDocument(path);
1134+
} catch (err: any) {
1135+
/* the file might not exist, this is ok */
1136+
return;
1137+
}
1138+
1139+
let text;
1140+
try {
1141+
text = document.getText();
1142+
} catch (err: any) {
1143+
logger.error('could not read settings file %s', document.uri.fsPath, err);
1144+
return;
1145+
}
1146+
1147+
if (text.length === 0) {
1148+
/* JSON file can be used as activation event */
1149+
return;
1150+
}
1151+
1152+
let data;
1153+
try {
1154+
data = JSON.parse(text);
1155+
} catch (err: any) {
1156+
logger.error('could not parse JSON settings file %s', document.uri.fsPath, err);
1157+
return;
1158+
}
1159+
1160+
return data;
1161+
}
1162+
11321163
export function setupExtension(context: vscode.ExtensionContext,
11331164
nodeVars: vars.NodeVarRegistry, logger: utils.ILogger,
11341165
nodesView: NodePreviewTreeViewProvider) {
@@ -1138,61 +1169,22 @@ export function setupExtension(context: vscode.ExtensionContext,
11381169
context.subscriptions.push(disposable);
11391170
}
11401171

1141-
const processSingleConfigFile = async (pathToFile: vscode.Uri) => {
1142-
let doc = undefined;
1143-
try {
1144-
doc = await vscode.workspace.openTextDocument(pathToFile);
1145-
} catch (err: any) {
1146-
logger.error('failed to read settings file %s', pathToFile, err);
1172+
const refreshWorkspaceConfiguration = async (workspace: vscode.WorkspaceFolder) => {
1173+
const config = await readConfigFile(workspace, logger);
1174+
if (!config) {
11471175
return;
11481176
}
11491177

1150-
let text;
1151-
try {
1152-
text = doc.getText();
1153-
} catch (err: any) {
1154-
logger.error('failed to read settings file %s', doc.uri.fsPath, err);
1155-
return;
1156-
}
1157-
1158-
if (text.length === 0) {
1159-
/* JSON file can be used as activation event */
1160-
logger.debug('JSON settings file %s is empty', doc.uri.fsPath);
1161-
return;
1162-
}
1163-
1164-
let data;
1165-
try {
1166-
data = JSON.parse(text);
1167-
} catch (err: any) {
1168-
logger.error('failed to parse JSON settings file %s', doc.uri.fsPath, err);
1169-
return;
1170-
}
1171-
1172-
let parsedConfigFile: ConfigFile | undefined;
1173-
try {
1174-
parsedConfigFile = parseConfigurationFile(data);
1175-
} catch (err: any) {
1176-
logger.error('failed to parse JSON settings file %s', doc.uri.fsPath, err);
1177-
return;
1178-
}
1179-
1180-
if (parsedConfigFile?.variables) {
1181-
nodesView.configFile = parsedConfigFile.variables;
1182-
}
1183-
1184-
if (parsedConfigFile?.formatter) {
1185-
FormatterConfiguration.typedefs = parsedConfigFile.formatter.typedefs;
1186-
}
1178+
let parsedConfigFile = parseVariablesConfiguration(config);
1179+
nodesView.configFile = parsedConfigFile;
11871180
}
1188-
1181+
11891182
const refreshConfigurationFromFolders = async (folders: readonly vscode.WorkspaceFolder[]) => {
11901183
for (const folder of folders) {
1191-
const pathToFile = utils.joinPath(
1192-
folder.uri, '.vscode', Configuration.ExtensionSettingsFileName);
1193-
1194-
if (await utils.fileExists(pathToFile)) {
1195-
await processSingleConfigFile(pathToFile);
1184+
try {
1185+
await refreshWorkspaceConfiguration(folder);
1186+
} catch (err) {
1187+
logger.error('could not refresh configuration in workspace %s', folder.uri.fsPath);
11961188
}
11971189
}
11981190
}
@@ -1335,29 +1327,7 @@ export function setupExtension(context: vscode.ExtensionContext,
13351327
}
13361328

13371329
logger.info('refreshing config file due to command execution');
1338-
for (const folder of vscode.workspace.workspaceFolders) {
1339-
const configFilePath = utils.joinPath(
1340-
folder.uri,
1341-
'.vscode',
1342-
Configuration.ExtensionSettingsFileName);
1343-
if (!await utils.fileExists(configFilePath)) {
1344-
const answer = await vscode.window.showWarningMessage(
1345-
'Config file does not exist. Create?',
1346-
'Yes', 'No');
1347-
if (answer !== 'Yes') {
1348-
return;
1349-
}
1350-
1351-
await vscode.commands.executeCommand(Configuration.Commands.OpenConfigFile);
1352-
return;
1353-
}
1354-
1355-
try {
1356-
await processSingleConfigFile(configFilePath);
1357-
} catch (err: any) {
1358-
logger.error('failed to update config file', err);
1359-
}
1360-
}
1330+
await refreshConfigurationFromFolders(vscode.workspace.workspaceFolders);
13611331
};
13621332

13631333
const refreshVariablesCmd = () => {
@@ -1544,6 +1514,10 @@ export class Configuration {
15441514
NodePreviewTreeView: `${this.ExtensionName}.node-tree-view`,
15451515
};
15461516
static ExtensionSettingsFileName = 'pgsql_hacker_helper.json';
1517+
1518+
static getConfigFile(workspace: vscode.Uri) {
1519+
return vscode.Uri.joinPath(workspace, '.vscode', this.ExtensionSettingsFileName);
1520+
}
15471521

15481522
static getLogLevel() {
15491523
return this.getConfig<string>(this.ConfigSections.LogLevel);

src/formatter.ts

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as vscode from 'vscode';
22
import {languages} from 'vscode';
33
import * as utils from './utils';
4-
import { Configuration } from './extension';
4+
import { Configuration, parseFormatterConfiguration, readConfigFile } from './extension';
55
import { PghhError } from './error';
66
import * as path from 'path';
77
import * as os from 'os';
@@ -260,21 +260,31 @@ class PgindentDocumentFormatterProvider implements vscode.DocumentFormattingEdit
260260
await utils.writeFile(typedefsFile, content);
261261
}
262262

263-
private async getListOfTypedefs(workspace: vscode.WorkspaceFolder) {
264-
const files = FormatterConfiguration.typedefs;
265-
if (!files?.length) {
266-
return '';
263+
private async getCustomTypedefs(workspace: vscode.WorkspaceFolder) {
264+
const configObj = await readConfigFile(workspace, this.logger);
265+
if (!configObj) {
266+
return [];
267+
}
268+
269+
const config = parseFormatterConfiguration(configObj);
270+
if (!config?.typedefs?.length) {
271+
return [];
267272
}
268273

274+
/*
275+
* pgindent accepts multiple --typedefs=s arguments. There is
276+
* another argument `--list-of-typedefs` - this is content of
277+
* a file itself and we do not use it.
278+
*/
269279
const typedefs = [];
270-
for (const f of files) {
280+
for (const t of config.typedefs) {
271281
let typedefFile;
272-
if (path.isAbsolute(f)) {
273-
typedefFile = vscode.Uri.file(f);
282+
if (path.isAbsolute(t)) {
283+
typedefFile = vscode.Uri.file(t);
274284
} else {
275-
typedefFile = utils.getWorkspacePgSrcFile(workspace.uri, f);
285+
typedefFile = utils.getWorkspacePgSrcFile(workspace.uri, t);
276286
}
277-
287+
278288
if (!await utils.fileExists(typedefFile)) {
279289
this.logger.warn('could not find file %s', typedefFile);
280290
continue;
@@ -325,7 +335,7 @@ class PgindentDocumentFormatterProvider implements vscode.DocumentFormattingEdit
325335
pg_bsd_indent: vscode.Uri,
326336
pgindent: vscode.Uri,
327337
workspace: vscode.WorkspaceFolder) {
328-
const typedefs = await this.getListOfTypedefs(workspace);
338+
const typedefs = await this.getCustomTypedefs(workspace);
329339

330340
/* Work in pgindent dir, so it can find default typedefs.list */
331341
const cwd = path.resolve(pgindent.fsPath, '..');
@@ -425,43 +435,6 @@ class PgindentDocumentFormatterProvider implements vscode.DocumentFormattingEdit
425435
}
426436
}
427437

428-
private async getDefaultTypedefs(workspace: vscode.WorkspaceFolder) {
429-
/*
430-
* Newer pg versions have 'src/tools/pgindent/typedefs.list' already
431-
* present in repository, so just read it. But older versions does not
432-
* have it installed, so we must download it manually.
433-
*
434-
* NOTE: extension's supported pg versions start from 9.6 which have
435-
* it installed - logic for downloading it is not tested, so I
436-
* expect that it is working (tested only once, manually on my PC)
437-
*/
438-
const typedefsFile = utils.getWorkspacePgSrcFile(
439-
workspace.uri, 'src', 'tools', 'pgindent', 'typedefs.list');
440-
if (await utils.fileExists(typedefsFile)) {
441-
this.logger.info('found default typedefs.list in %s', typedefsFile.fsPath);
442-
return await utils.readFile(typedefsFile);
443-
}
444-
445-
/* Version is not known, so just download latest */
446-
const url = 'https://buildfarm.postgresql.org/cgi-bin/typedefs.pl';
447-
this.logger.info('downloading typedefs file from %s', url);
448-
let content;
449-
try {
450-
content = await utils.downloadFile(url);
451-
} catch (err) {
452-
throw new Error(`failed to download typedefs: ${err}`);
453-
}
454-
455-
this.logger.info('saving typedefs.list to file %s', typedefsFile.fsPath);
456-
try {
457-
await utils.writeFile(typedefsFile, content);
458-
} catch (err) {
459-
throw new Error(`could not save typedef file: ${err}`);
460-
}
461-
462-
return content;
463-
}
464-
465438
private getWholeDocumentRange(document: vscode.TextDocument) {
466439
const start = new vscode.Position(0, 0);
467440
const lastLine = document.lineAt(document.lineCount - 1);

0 commit comments

Comments
 (0)