Skip to content

Commit

Permalink
Implement readChunksFromFile
Browse files Browse the repository at this point in the history
See README.md for description and examples.
  • Loading branch information
dylanbeattie committed Mar 23, 2024
1 parent c77dd9f commit e9df232
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 46 deletions.
5 changes: 3 additions & 2 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"eg2.tslint"
"dbaeumer.vscode-eslint",
"ms-vscode.extension-test-runner"
]
}
}
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ This extension contributes the following settings:

- `presentation-buddy.delay`: Delay (in ms) between keys entered. Defaults to 100ms.
- `presentation-buddy.randomness`: Randomness (in ms) between keys entered. Defaults to 25ms.
- `presentation-buddy.waitInsteadOfTyping`: Default array of strings which indicate a pause when typing chunks from a file.
- `presentation-buddy.waitAfterTyping`: Default array of strings which indicate a pause when typing chunks from a file.
- `presentation-buddy.skipLinesContaining`: Default array of strings indicating lines to skip when typing chunks from a file.
- `presentation-buddy.waitAfterNewLine`: controls whether to pause after each new line when typing chunks from a file.

## Instructions

Expand Down Expand Up @@ -63,6 +67,45 @@ Example:
}
```

### TypeChunksFromFile

Type text at the current cursor position of the currently open file. Text is read from the file indicated by the path, relative to the `.presentation-buddy` folder. The input file will be split into chunks based on the supplied settings.

By default, the input will be split on newlines (`\n`). Windows-style `\r\n` line endings will be converted to `\n` before the file is processed. To disable this, set `waitAfterNewLines` to `false`.

Presentation Buddy will:

* `wait` after typing any string matching a supplied `waitAfter` argument
* `wait` **instead of** typing any string matching a supplied `waitInsteadOf` argument
* Skip any line containing any string matching a supplied `skipLinesContaining` argument

Example:

```json
{
"type":"typeChunksFromFile",
"path": "chunks-example.js",
"waitAfterTyping": [ "{", ".", " => ", " = " ],
"waitInsteadOfTyping": [ "/*WAIT*/" ],
"skipLinesContaining": [ "//skip" ]
}
```

The input file `chunks-example.js`:

```js
import { Color } from './color.js'; //skip
//skip
export class Scene {
constructor(camera, background, shapes) {
this.camera = camera;
this.background = background /*WAIT*/ ?? Color.Black;
this.shapes = shapes ?? [];
}
trace = (x, y) => this.camera.trace(this, x, y);
}
```
### OpenFile
Opens the file specified in the editor. Note that the file needs to exist.
Expand Down
40 changes: 34 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@
"color": "#193549",
"theme": "dark"
},
"activationEvents": [
"onCommand:presentationBuddy.init",
"onCommand:presentationBuddy.start",
"onCommand:presentationBuddy.continue"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
Expand All @@ -50,7 +45,7 @@
}
],
"configuration": {
"title": "Presentation Buddy configuration",
"title": "Presentation Buddy",
"properties": {
"presentation-buddy.delay": {
"type": "integer",
Expand All @@ -63,6 +58,39 @@
"default": 25,
"description": "Randomness (in ms) between keys entered",
"scope": "user"
},
"presentation-buddy.waitInsteadOfTyping": {
"type": "array",
"items": {
"type": "string"
},
"default": [ "/*WAIT*/" ],
"description": "When using typeChunksFromFile, Presentation Buddy will pause when it finds any matching string. Matches are NOT copied to the output.",
"scope": "user"
},
"presentation-buddy.waitAfterNewLine": {
"type": "boolean",
"default": true,
"description": "When using typeChunksFromFile, controls whether Presentation Buddy should pause at the start of each new line.",
"scope": "user"
},
"presentation-buddy.waitAfterTyping": {
"type": "array",
"items": {
"type": "string"
},
"default": [ "(", "=" ],
"description": "When using typeChunksFromFile, Presentation Buddy will pause after typing any series of matching strings.",
"scope": "user"
},
"presentation-buddy.skipLinesContaining": {
"type": "array",
"items": {
"type": "string"
},
"default": [ "/*SKIP*/" ],
"description": "When using typeChunksFromFile, Presentation Buddy will skip any line containing any matching string.",
"scope": "user"
}
}
}
Expand Down
48 changes: 30 additions & 18 deletions src/instruction-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
CreateFile,
Wait,
TypeTextFromFile,
TypeChunksFromFile
TypeChunksFromFile,
IHaveAFilePath
} from './instructions';
import {
mkdirIfNotExists,
Expand All @@ -27,21 +28,31 @@ import {
getDelay,
readFileAsync,
getRandomness,
readChunks
readChunks,
getWaitAfterTyping,
getWaitInsteadOfTyping,
getSkipLinesContaining,
getWaitAfterNewLine
} from './utils';
import { setAwaiter } from './wait-for-input';

export const typeTextFromFile = async (
instruction: TypeTextFromFile
): Promise<void> => {
const readFileContents = async (
instruction: IHaveAFilePath
) : Promise<string> => {
if (!workspace.workspaceFolders) {
return;
return "";
}

const workspaceFolder = workspace.workspaceFolders[0].uri.fsPath;
const path = join(workspaceFolder, '.presentation-buddy', instruction.path);
return await readFileAsync(path);
};

export const typeTextFromFile = async (
instruction: TypeTextFromFile
): Promise<void> => {

const text = await readFileAsync(path);
const text = await readFileContents(instruction);
if (text === '') { return; }
const data = Array.from(text.split('\r\n').join('\n'));

await typeTextIntoActiveTextEditor(data, instruction.delay);
Expand All @@ -50,17 +61,18 @@ export const typeTextFromFile = async (
export const typeChunksFromFile = async (
instruction: TypeChunksFromFile
): Promise<void> => {
if (!workspace.workspaceFolders) {
return;
}

const workspaceFolder = workspace.workspaceFolders[0].uri.fsPath;
const path = join(workspaceFolder, '.presentation-buddy', instruction.path);
const text = await readFileAsync(path);
const consumeTokens = instruction.waitInsteadOf || ["/*WAIT*/"];
const preserveTokens = instruction.waitAfter || ["\n"];
const skipTokens = instruction.skipLinesContaining || ["/*SKIP*/"];
var chunks = readChunks(text, consumeTokens, preserveTokens, skipTokens);
const text = await readFileContents(instruction);
if (text === '') { return; }

const waitInsteadOf = instruction.waitInsteadOfTyping || getWaitInsteadOfTyping();
const waitAfter = instruction.waitAfterTyping || getWaitAfterTyping();
const skipLinesContaining = instruction.skipLinesContaining || getSkipLinesContaining();
const waitAfterNewLine = instruction.waitAfterNewLine ?? getWaitAfterNewLine() ?? true;

if (waitAfterNewLine) { waitAfter.push('\n'); }

var chunks = readChunks(text, waitInsteadOf, waitAfter, skipLinesContaining);
for (const chunk of chunks) {
await typeTextIntoActiveTextEditor(Array.from(chunk), instruction.delay);
if (chunk.endsWith('\n')) {
Expand Down
9 changes: 7 additions & 2 deletions src/instructions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
type Skipable = { skip?: boolean };

export interface IHaveAFilePath {
path: string;
}

export type Command = {
type: "command";
command: string;
Expand All @@ -21,8 +25,9 @@ export type TypeChunksFromFile = {
type: "typeChunksFromFile";
path: string;
delay?: number;
waitInsteadOf: string[];
waitAfter: string[];
waitInsteadOfTyping: string[];
waitAfterTyping: string[];
waitAfterNewLine: boolean | null,
skipLinesContaining: string[];
};

Expand Down
22 changes: 13 additions & 9 deletions src/presentation-buddy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { jsonc } from 'jsonc';

import { Instruction, InstructionHandler } from './instructions';
import * as instructionHandlers from './instruction-handlers';
import { mkdirIfNotExists } from './utils';
import { existsAsync, mkdirIfNotExists } from './utils';

export const init = async () => {
if (!workspace.workspaceFolders) {
Expand All @@ -18,17 +18,21 @@ export const init = async () => {

const dir = join(workspaceFolder, '.presentation-buddy');
const fileName = join(dir, 'instructions.json');

await mkdirIfNotExists(dir);

await jsonc.write(fileName, json, { space: 2 });
if (await existsAsync(fileName)) {
window.showWarningMessage(
`File ${fileName} exists: overwrite it?`, "Yes", "No"
).then(async answer => {
if (answer === "Yes") {
await jsonc.write(fileName, json, { space: 2 });
};
});
} else {
await mkdirIfNotExists(dir);
await jsonc.write(fileName, json, { space: 2 });
}
};

export const start = async () => {
// const editor = window.activeTextEditor;
// if (!editor) {
// return;
// }
if (!workspace.workspaceFolders) {
return;
}
Expand Down
38 changes: 34 additions & 4 deletions src/test/extension.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as assert from 'assert';
import { splat, readChunks } from '../utils';
import { crunch, readChunks } from '../utils';

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
Expand Down Expand Up @@ -59,7 +59,7 @@ h,i,j
preserveTokens: [ "\n" ],
consumeTokens: [","],
skipTokens: ["SKIP"],
expected: ["a", "b\n", "c\n", "h", "i", "j\n", "\n"]
expected: ["a", "b\n", "c\n", "h", "i", "j\n\n"]
},
{
...empty,
Expand All @@ -78,11 +78,27 @@ var z = 25;
"Console.ReadLine(", ");\n",
"Console.WriteLine(", "x + y);\n",
"var z = ",
"25;\n",
"\n"
"25;\n\n"
]
},
{
...empty,
text: "foo\n\n\nbar",
preserveTokens: [ "\n" ],
expected: [ "foo\n\n\n", "bar" ]
},
{
...empty,
text: `main() {
printf('hello!');
}`,
preserveTokens: [ "{", "\n" ],
expected: [ "main() {\n\n", " printf('hello!');\n\n", "}" ]
}
];

suite('Chunk parsing tests', () => {
inputs.forEach((input) => {
test(`${input.text} / ${input.consumeTokens} / ${input.preserveTokens} / ${input.skipTokens}`, () => {
Expand All @@ -91,3 +107,17 @@ suite('Chunk parsing tests', () => {
});
});
});

let crunchTestCases = [
[ ["a", "a", "a" ], ["a"], [ "aaa"] ],
[ [ "b", "a", "e", "n", "a", "e", "n", "a", "e" ], ["a", "e"], ["bae", "nae", "nae"] ],
[ [ "b", "a", " ", "e", "n", "a", " ", "e", "n", "\t", "a", "\t", "e" ], ["a", "e"], ["ba e", "na e", "n\ta\te"] ]
];
suite('Crunch tests', () => {
crunchTestCases.forEach(([chunks,tokens,expected]) => {
test(`crunch(${chunks} / ${tokens}) == ${expected}`, () => {
var result = [...crunch(chunks,tokens)];
assert.deepEqual(result, expected);
});
});
});
Loading

0 comments on commit e9df232

Please sign in to comment.