Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code-lens to run pipeline #606

Merged
merged 8 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions apps/interpreter/src/current-dir.ts

This file was deleted.

24 changes: 4 additions & 20 deletions apps/interpreter/src/examples-smoke-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: AGPL-3.0-only

import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { processExitMockImplementation } from '@jvalue/jayvee-execution/test';
import {
Expand Down Expand Up @@ -40,21 +39,6 @@ vi.mock('sqlite3', () => {
};
});

// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(
path.dirname(fileURLToPath(import.meta.url)), // relative to this test file
'..',
'..',
'..',
'example',
);
return {
getCurrentDir: currentDirMock,
};
});

describe('jv example smoke tests', () => {
const defaultOptions: RunOptions = {
pipeline: '.*',
Expand Down Expand Up @@ -105,7 +89,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction('cars.jv', {
await runAction('example/cars.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -137,7 +121,7 @@ describe('jv example smoke tests', () => {
postgresLoaderMock.setup();
sqliteLoaderMock.setup();

await runAction('electric-vehicles.jv', {
await runAction('example/electric-vehicles.jv', {
...defaultOptions,
env: new Map<string, string>([
['DB_HOST', 'mock'],
Expand Down Expand Up @@ -200,7 +184,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction('gtfs-rt.jv', {
await runAction('example/gtfs-rt.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -228,7 +212,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction('gtfs-static.jv', {
await runAction('example/gtfs-static.jv', {
...defaultOptions,
});

Expand Down
24 changes: 7 additions & 17 deletions apps/interpreter/src/parse-only.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,11 @@ const interpreterMock: JayveeInterpreter = {
vi.stubGlobal('DefaultJayveeInterpreter', interpreterMock);

const dirPathOfThisTest = path.dirname(fileURLToPath(import.meta.url));
const pathExamplesRelativeToThisTest = path.join('..', '..', '..', 'example');

// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(dirPathOfThisTest, pathExamplesRelativeToThisTest);
return {
getCurrentDir: currentDirMock,
};
});
const pathProjectRootRelativeToThisTest = path.join('..', '..', '..');

describe('Parse Only', () => {
const pathToValidModelFromExamplesDir = 'cars.jv';
const pathToInvalidModelFromExamplesDir = path.join(
'..',
const pathToValidModelFromProjectRoot = path.join('example', 'cars.jv');
const pathToInvalidModelFromProjectRoot = path.join(
'apps',
'interpreter',
'test',
Expand Down Expand Up @@ -68,7 +58,7 @@ describe('Parse Only', () => {

it('should exit with 0 on a valid option', async () => {
await expect(
runAction(pathToValidModelFromExamplesDir, {
runAction(pathToValidModelFromProjectRoot, {
...defaultOptions,
parseOnly: true,
}),
Expand All @@ -81,13 +71,13 @@ describe('Parse Only', () => {
it('should exit with 1 on error', async () => {
const modelPathRelativeToThisTest = path.join(
dirPathOfThisTest,
pathExamplesRelativeToThisTest,
pathToInvalidModelFromExamplesDir,
pathProjectRootRelativeToThisTest,
pathToInvalidModelFromProjectRoot,
);
expect(fs.existsSync(modelPathRelativeToThisTest)).toBe(true);

await expect(
runAction(pathToInvalidModelFromExamplesDir, {
runAction(pathToInvalidModelFromProjectRoot, {
...defaultOptions,
parseOnly: true,
}),
Expand Down
11 changes: 6 additions & 5 deletions apps/interpreter/src/run-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
type JayveeServices,
} from '@jvalue/jayvee-language-server';

import { getCurrentDir } from './current-dir';
import { parsePipelineMatcherRegExp, parseRunOptions } from './run-options';

export async function runAction(
Expand All @@ -35,9 +34,9 @@ export async function runAction(
return process.exit(ExitCode.FAILURE);
}

const currentDir = getCurrentDir();
const currentDir = process.cwd();
const workingDir = currentDir;
const absoluteFilePath = path.join(currentDir, filePath);
const filePathRelativeToCurrentDir = path.relative(currentDir, filePath);
georg-schwarz marked this conversation as resolved.
Show resolved Hide resolved

const interpreter = new DefaultJayveeInterpreter({
pipelineMatcher: (pipelineDefinition) =>
Expand All @@ -49,10 +48,12 @@ export async function runAction(
}).addWorkspace(workingDir);

if (options.parseOnly === true) {
return await runParseOnly(absoluteFilePath, interpreter);
return await runParseOnly(filePathRelativeToCurrentDir, interpreter);
}

const exitCode = await interpreter.interpretFile(absoluteFilePath);
const exitCode = await interpreter.interpretFile(
filePathRelativeToCurrentDir,
);
process.exit(exitCode);
}

Expand Down
14 changes: 12 additions & 2 deletions apps/vs-code-extension/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "jayvee",
"version": "0.0.0",
"displayName": "Jayvee",
"description": "Support for the Jayvee language",
"engines": {
Expand Down Expand Up @@ -28,6 +29,16 @@
"scopeName": "source.jayvee",
"path": "jayvee.tmLanguage.json"
}
],
"commands": [
{
"command": "jayvee.pipeline.run",
"title": "Run a Jayvee pipeline"
},
{
"command": "jayvee.pipeline.debug",
"title": "Run a Jayvee pipeline with debug output"
}
]
},
"activationEvents": [
Expand All @@ -41,6 +52,5 @@
"type": "git",
"url": "https://github.com/jvalue/jayvee.git"
},
"homepage": "https://github.com/jvalue/jayvee",
"version": "0.0.0"
"homepage": "https://github.com/jvalue/jayvee"
}
83 changes: 83 additions & 0 deletions apps/vs-code-extension/src/commands/run-jv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg
//
// SPDX-License-Identifier: AGPL-3.0-only

// eslint-disable-next-line unicorn/prefer-node-protocol
import { strict as assert } from 'assert';
import { exec } from 'node:child_process';
import { readFileSync } from 'node:fs';
import path from 'node:path';

import { isRunJayveeCodeLensPayload } from '@jvalue/jayvee-language-server';
import { type ExtensionContext, window } from 'vscode';

export async function runJayveeCommand(
payload: unknown,
enableDebugOutput: boolean,
context: ExtensionContext,
) {
try {
await throwOnMismatchingJayveeVersion(context);
} catch (e: unknown) {
const errorMsg = e instanceof Error ? e.message : 'unknown error';
await window.showErrorMessage(`Error executing jv: ${errorMsg}`);
}

assert(
isRunJayveeCodeLensPayload(payload),
`Payload ${JSON.stringify(
payload,
)} is not a valid RunJayveeCodeLensPayload`,
);
const filePath = payload.filePath;

const shell = window.createTerminal('Run Jayvee');
shell.sendText(
`jv ${filePath} --pipeline ${payload.pipelineName}${
enableDebugOutput ? ' --debug --debug-granularity peek' : ''
}`,
);
shell.show();
}

function throwOnMismatchingJayveeVersion(
context: ExtensionContext,
): Promise<void> {
const command = 'jv --version';
const requiredVersion = getExtensionVersion(context);

return new Promise((resolve, reject) => {
exec(command, (error, stdout) => {
if (error) {
return reject(error);
}

const installedVersion = stdout.trim();
if (installedVersion !== requiredVersion) {
return reject(
new Error(
`No matching jv versions: expected version ${requiredVersion} but found version ${installedVersion}`,
),
);
}
return resolve();
});
});
}

function getExtensionVersion(context: ExtensionContext): string | undefined {
georg-schwarz marked this conversation as resolved.
Show resolved Hide resolved
const packageJsonPath = path.join(context.extensionPath, 'package.json');
const packageJsonString = readFileSync(packageJsonPath, 'utf8');
const packageJson = JSON.parse(packageJsonString) as unknown;

const hasVersionField =
typeof packageJson === 'object' &&
packageJson != null &&
'version' in packageJson &&
typeof packageJson.version === 'string';

if (!hasVersionField) {
return undefined;
}
return packageJson.version as string;
}
16 changes: 15 additions & 1 deletion apps/vs-code-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

import path from 'node:path';

import { type ExtensionContext, workspace } from 'vscode';
import { type ExtensionContext, commands, workspace } from 'vscode';
import {
LanguageClient,
type LanguageClientOptions,
type ServerOptions,
TransportKind,
} from 'vscode-languageclient/node';

import { runJayveeCommand } from './commands/run-jv';
import { StandardLibraryFileSystemProvider } from './standard-library-file-system-provider';

let client: LanguageClient;
Expand Down Expand Up @@ -64,6 +65,19 @@ function startLanguageClient(context: ExtensionContext): LanguageClient {
},
};

// Commands
const commandRunJayvee = commands.registerCommand(
'jayvee.pipeline.run',
(...args: unknown[]) => runJayveeCommand(args[0], false, context),
);
const commandDebugJayvee = commands.registerCommand(
'jayvee.pipeline.debug',
(...args: unknown[]) => runJayveeCommand(args[0], true, context),
);

context.subscriptions.push(commandRunJayvee);
context.subscriptions.push(commandDebugJayvee);

// Create the language client and start the client.
const client = new LanguageClient(
'jayvee',
Expand Down
2 changes: 2 additions & 0 deletions libs/language-server/src/lib/jayvee-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { WrapperFactoryProvider } from './ast/wrappers/wrapper-factory-provider'
import { JayveeValueConverter } from './jayvee-value-converter';
import {
JayveeCodeActionProvider,
JayveeCodeLensProvider,
JayveeCompletionProvider,
JayveeDefinitionProvider,
JayveeFormatter,
Expand Down Expand Up @@ -88,6 +89,7 @@ export const JayveeModule: Module<
Formatter: () => new JayveeFormatter(),
DefinitionProvider: (services) => new JayveeDefinitionProvider(services),
CodeActionProvider: (services) => new JayveeCodeActionProvider(services),
CodeLensProvider: () => new JayveeCodeLensProvider(),
},
references: {
ScopeProvider: (services) => new JayveeScopeProvider(services),
Expand Down
1 change: 1 addition & 0 deletions libs/language-server/src/lib/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './jayvee-hover-provider';
export * from './jayvee-scope-provider';
export * from './jayvee-definition-provider';
export * from './jayvee-code-action-provider';
export * from './jayvee-code-lens-provider';
Loading
Loading