Skip to content

Commit

Permalink
New config query to inspect flowr configuration (#1230)
Browse files Browse the repository at this point in the history
* feat(config-query): new config query

* doc: document new `@conifg` query
  • Loading branch information
EagleoutIce authored Jan 6, 2025
1 parent cd859c2 commit 8f9ef04
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 6 deletions.
14 changes: 12 additions & 2 deletions src/cli/repl/commands/repl-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ function printHelp(output: ReplOutput) {
output.stdout('The query is an array of query objects to represent multiple queries. Each query object may have the following properties:');
output.stdout(describeSchema(AnyQuerySchema(), output.formatter));
output.stdout(`\n\nThe example ${italic(':query "[{\\"type\\": \\"call-context\\", \\"callName\\": \\"mean\\" }]" mean(1:10)', output.formatter)} would return the call context of the mean function.`);
output.stdout('As a convenience, we interpret any (non-help) string not starting with \'[\' as a regex for the simple call-context query.');
output.stdout('As a convenience, we interpret any (non-help, non-@) string not starting with \'[\' as a regex for the simple call-context query.');
output.stdout(`Hence, ${italic(':query "mean" mean(1:10)', output.formatter)} is equivalent to the above example.`);
output.stdout(`Similarly, '@<type>' is interpreted as a query of the given type.`);
output.stdout(`With this, ${italic(':query @config', output.formatter)} prints the result of the config query.`);
}

async function processQueryArgs(line: string, shell: RShell, output: ReplOutput): Promise<undefined | { query: QueryResults<SupportedQueryTypes>, processed: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE> }> {
Expand All @@ -44,7 +46,15 @@ async function processQueryArgs(line: string, shell: RShell, output: ReplOutput)
}

let parsedQuery: Query[] = [];
if(query.startsWith('[')) {
if(query.startsWith('@')) {
parsedQuery = [{ type: query.slice(1) as SupportedQueryTypes } as Query];
const validationResult = QueriesSchema().validate(parsedQuery);
if(validationResult.error) {
output.stderr(`Invalid query: ${validationResult.error.message}`);
printHelp(output);
return;
}
} else if(query.startsWith('[')) {
parsedQuery = JSON.parse(query) as Query[];
const validationResult = QueriesSchema().validate(parsedQuery);
if(validationResult.error) {
Expand Down
9 changes: 7 additions & 2 deletions src/documentation/print-interface-wiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ function explainConfigFile(): string {
When running _flowR_, you may want to specify some behaviors with a dedicated configuration file.
By default, flowR looks for a file named \`${defaultConfigFile}\` in the current working directory (or any higher directory).
You can also specify a different file with ${getCliLongOptionOf('flowr', 'config-file')} or pass the configuration inline using ${getCliLongOptionOf('flowr', 'config-json')}.
To inspect the current configuration, you can run flowr with the ${getCliLongOptionOf('flowr', 'verbose')} flag, or use the \`config\` [Query](${FlowrWikiBaseRef}/Query%20API).
Within the REPL this works by running the following:
${codeBlock('shell', ':query @config')}
The following summarizes the configuration options:
Expand Down Expand Up @@ -220,8 +225,8 @@ ${codeBlock('json', JSON.stringify(
- \`loadDefaults\` (boolean, initially \`true\`): If set to \`true\`, the default built-in definitions are loaded before applying the custom definitions. Setting this flag to \`false\` explicitly disables the loading of the default definitions.
- \`definitions\` (array, initially empty): Allows to overwrite or define new built-in elements. Each object within must have a \`type\` which is one of the below. Furthermore, they may define a string array of \`names\` which specifies the identifiers to bind the definitions to. You may use \`assumePrimitive\` to specify whether _flowR_ should assume that this is a primitive non-library definition (so you probably just do not want to specify the key).
| Type | Description | Example |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Type | Description | Example |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| \`constant\` | Additionally allows for a \`value\` this should resolve to. | \`{ type: 'constant', names: ['NULL', 'NA'], value: null }\` |
| \`function\` | Is a rather flexible way to define and bind built-in functions. For the time, we do not have extensive documentation to cover all the cases, so please either consult the sources with the \`default-builtin-config.ts\` or open a [new issue](${NewIssueUrl}). | \`{ type: 'function', names: ['next'], processor: 'builtin:default', config: { cfg: ExitPointType.Next } }\` |
| \`replacement\` | A comfortable way to specify replacement functions like \`$<-\` or \`names<-\`. \`suffixes\` describes the... suffixes to attach automatically. | \`{ type: 'replacement', suffixes: ['<-', '<<-'], names: ['[', '[['] }\` |
Expand Down
14 changes: 14 additions & 0 deletions src/documentation/print-query-wiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getReplCommand } from './doc-util/doc-cli-option';
import { NewIssueUrl } from './doc-util/doc-issue';
import { executeLocationMapQuery } from '../queries/catalog/location-map-query/location-map-query-executor';
import { CallTargets } from '../queries/catalog/call-context-query/identify-link-to-last-call-relation';
import { executeConfigQuery } from '../queries/catalog/config-query/config-query-executor';


registerQueryDocumentation('call-context', {
Expand Down Expand Up @@ -238,6 +239,19 @@ ${
}
});

registerQueryDocumentation('config', {
name: 'Config Query',
type: 'active',
shortDescription: 'Returns the current configuration of flowR.',
functionName: executeConfigQuery.name,
functionFile: '../queries/catalog/config-query/config-query-format.ts',
// eslint-disable-next-line @typescript-eslint/require-await -- no need for async here
buildExplanation: async() => {
return `
This query provides access to the current configuration of the flowR instance. See the [Interface](${FlowrWikiBaseRef}/Interface) wiki page for more information on what the configuration represents.`;
}
});

registerQueryDocumentation('compound', {
name: 'Compound Query',
type: 'virtual',
Expand Down
22 changes: 22 additions & 0 deletions src/queries/catalog/config-query/config-query-executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { log } from '../../../util/log';
import type {
ConfigQuery,
ConfigQueryResult
} from './config-query-format';
import type { BasicQueryData } from '../../base-query-format';
import { getConfig } from '../../../config';


export function executeConfigQuery(_: BasicQueryData, queries: readonly ConfigQuery[]): ConfigQueryResult {
if(queries.length !== 1) {
log.warn('Config query expects only up to one query, but got', queries.length);
}

return {
'.meta': {
/* there is no sense in measuring a get */
timing: 0
},
config: getConfig()
};
}
29 changes: 29 additions & 0 deletions src/queries/catalog/config-query/config-query-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
import { executeConfigQuery } from './config-query-executor';
import { bold, type OutputFormatter } from '../../../util/ansi';
import { printAsMs } from '../../../util/time';
import Joi from 'joi';
import type { FlowrConfigOptions } from '../../../config';
import { jsonReplacer } from '../../../util/json';

export interface ConfigQuery extends BaseQueryFormat {
readonly type: 'config';
}

export interface ConfigQueryResult extends BaseQueryResult {
readonly config: FlowrConfigOptions;
}


export const ConfigQueryDefinition = {
executor: executeConfigQuery,
asciiSummarizer: (formatter: OutputFormatter, _processed: unknown, queryResults: BaseQueryResult, result: string[]) => {
const out = queryResults as ConfigQueryResult;
result.push(`Query: ${bold('config', formatter)} (${printAsMs(out['.meta'].timing, 0)})`);
result.push(` ╰ Config:\n${JSON.stringify(out.config, jsonReplacer, 4)}`);
return true;
},
schema: Joi.object({
type: Joi.string().valid('config').required().description('The type of the query.'),
}).description('The config query retrieves the current configuration of the flowR instance.')
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { executeLocationMapQuery } from './location-map-query-executor';
import { bold, type OutputFormatter } from '../../../util/ansi';
import { printAsMs } from '../../../util/time';
import Joi from 'joi';
import { summarizeIdsIfTooLong } from '../../query-print';
import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
import type { SourceRange } from '../../../util/range';
import { summarizeIdsIfTooLong } from '../../query-print';

export interface LocationMapQuery extends BaseQueryFormat {
readonly type: 'location-map';
Expand All @@ -25,5 +25,5 @@ export const LocationMapQueryDefinition = {
},
schema: Joi.object({
type: Joi.string().valid('location-map').required().description('The type of the query.'),
}).description('The id map query retrieves the location of every id in the ast.')
}).description('The location map query retrieves the location of every id in the ast.')
} as const;
4 changes: 4 additions & 0 deletions src/queries/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ import type { DEFAULT_DATAFLOW_PIPELINE } from '../core/steps/pipeline/default-p
import Joi from 'joi';
import type { LocationMapQuery } from './catalog/location-map-query/location-map-query-format';
import { LocationMapQueryDefinition } from './catalog/location-map-query/location-map-query-format';
import type { ConfigQuery } from './catalog/config-query/config-query-format';
import { ConfigQueryDefinition } from './catalog/config-query/config-query-format';

export type Query = CallContextQuery
| ConfigQuery
| DataflowQuery
| NormalizedAstQuery
| IdMapQuery
Expand All @@ -54,6 +57,7 @@ export interface SupportedQuery<QueryType extends BaseQueryFormat['type']> {

export const SupportedQueries = {
'call-context': CallContextQueryDefinition,
'config': ConfigQueryDefinition,
'dataflow': DataflowQueryDefinition,
'id-map': IdMapQueryDefinition,
'normalized-ast': NormalizedAstQueryDefinition,
Expand Down

0 comments on commit 8f9ef04

Please sign in to comment.