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

Fix 46 #54

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ Sven Slootweg <[email protected]> (https://github.com/joepie91/)
yy0931 (https://github.com/yy0931)
Rene Saarsoo <[email protected]> (https://github.com/nene/)
Lionel Rowe (https://github.com/lionel-rowe/)
Joe Andaverde (https://github.com/dynajoe)
Michael Klaus <[email protected]> (https://github.com/qades78)
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ of all of the rules in the Outline view.

![Outline](/images/outline.png)

## Live Preview

Live edit and test your Grammars, optionally starting at the rule under cursor.

### Tips

Name your grammar like this for optimal experience: grammar_name.language_extension.peggy. Where language_extension is the extension of the language you're parsing. This will provide syntax highlighting if you have a matching language server installed.

## Contributing

Feel free to contribute to this extension [here](https://github.com/peggyjs/code-peggy-language).
Expand Down
10 changes: 10 additions & 0 deletions client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { activate as activateClient, deactivate } from "./client";
import { ExtensionContext } from "vscode";
import { activate as activateLivePreview } from "./preview";

export function activate(context: ExtensionContext): void {
activateLivePreview(context);
activateClient(context);
}

export { deactivate };
176 changes: 176 additions & 0 deletions client/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import * as path from "path";
import * as peggy from "peggy";
import * as util from "node-inspect-extracted";
import {
ExtensionContext,
OutputChannel,
Uri,
ViewColumn,
commands,
window,
workspace,
} from "vscode";
import { MemFS } from "../vendor/vscode-extension-samples/fileSystemProvider";
import { debounce } from "../common/debounce";

const PEGGY_INPUT_SCHEME = "peggyjsin";

interface GrammarConfig {
name: string;
key: string;
start_rule: string | undefined;
grammar_uri: Uri;
input_uri: Uri;
timeout?: NodeJS.Timeout;
grammar_text?: string;
parser?: any;
}

async function executeAndDisplayResults(
output: OutputChannel,
config: GrammarConfig
): Promise<void> {
output.show(true);
let out = `// ${config.name} ${config.start_rule ? `(${config.start_rule})` : ""}\n`;

try {
const [grammar_document, input_document] = [
await workspace.openTextDocument(config.grammar_uri),
await workspace.openTextDocument(config.input_uri),
];

// Never leave it dirty; it's saved in memory anyway.
// Don't bother to wait for the promise.
input_document.save();
const grammar_text = grammar_document.getText();

if (grammar_text !== config.grammar_text) {
config.parser = peggy.generate(
grammar_text,
config.start_rule
? {
allowedStartRules: [config.start_rule],
}
: undefined
);
config.grammar_text = grammar_text;
}

const input = input_document.getText();
const result = config.parser.parse(
input,
config.start_rule ? { startRule: config.start_rule } : undefined
);

out += util.inspect(result, {
depth: Infinity,
colors: false,
maxArrayLength: Infinity,
maxStringLength: Infinity,
breakLength: 40,
sorted: true,
});
out += "\n";
} catch (error) {
out += error.toString();
out += "\n";
}
// Replace once, since addLine causes issues with trailing spaces.
output.replace(out);
}

const debounceExecution = debounce(executeAndDisplayResults, 300);

export function activate(context: ExtensionContext): void {
const peggy_output = window.createOutputChannel("Peggy Live", "javascript");
const memory_fs = new MemFS();
const grammars = new Map<string, GrammarConfig>();

function grammarNameFromUri(uri: Uri): string {
return path
.basename(uri.fsPath)
.replace(/\.(pegjs|peggy)$/, "")
.replace(/^[(][^)]+[)]__/, "");
}

function trackGrammar(
grammar_document_uri: Uri,
start_rule?: string
): GrammarConfig {
const grammar_name = grammarNameFromUri(grammar_document_uri);
const key = `${grammar_name}:${start_rule || "*"}`;

const input_document_uri = start_rule
? Uri.parse(`${PEGGY_INPUT_SCHEME}:/(${start_rule})__${grammar_name}`)
: Uri.parse(`${PEGGY_INPUT_SCHEME}:/${grammar_name}`);

const is_input_document_open = workspace.textDocuments.find(
d => d.uri === input_document_uri
);

if (!is_input_document_open) {
workspace.fs.writeFile(input_document_uri, Buffer.from("")).then(() => {
window.showTextDocument(input_document_uri, {
viewColumn: ViewColumn.Beside,
preserveFocus: true,
});
});
}
const config = {
name: grammar_name,
key,
start_rule,
grammar_uri: grammar_document_uri,
input_uri: input_document_uri,
};
grammars.set(key, config);
return config;
}

const documents_changed = workspace.onDidChangeTextDocument(async e => {
const document_uri_string = e.document.uri.toString();

for (const config of grammars.values()) {
if (
config.grammar_uri.toString() === document_uri_string
|| config.input_uri.toString() === document_uri_string
) {
await executeAndDisplayResults(peggy_output, config);
}
}
});

const documents_closed = workspace.onDidCloseTextDocument(e => {
const to_remove = [...grammars.values()].filter(
config => config.grammar_uri === e.uri || config.input_uri === e.uri
);

to_remove.forEach(config => {
grammars.delete(config.key);
});
});

context.subscriptions.push(
documents_changed,
documents_closed,
peggy_output,
commands.registerTextEditorCommand("editor.peggyLive", editor => {
const grammar_config = trackGrammar(editor.document.uri);
debounceExecution(peggy_output, grammar_config);
}),
commands.registerTextEditorCommand("editor.peggyLiveFromRule", editor => {
const word_range = editor.document.getWordRangeAtPosition(
editor.selection.start,
/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*/
);

if (word_range !== null) {
const rule_name = editor.document.getText(word_range);
const grammar_config = trackGrammar(editor.document.uri, rule_name);

debounceExecution(peggy_output, grammar_config);
}
})
);
workspace.registerFileSystemProvider(PEGGY_INPUT_SCHEME, memory_fs);
}
File renamed without changes.
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default [
{
ignores: [
"out/**",
"vendor/**",
],
},
...commonjs,
Expand Down
28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"homepage": "https://github.com/peggyjs/code-peggy-language",
"categories": [
"Debuggers",
"Programming Languages",
"Snippets"
],
Expand Down Expand Up @@ -91,6 +92,32 @@
"language": "peggy",
"path": "./snippets/snippets.json"
}
],
"menus": {
"editor/context": [
{
"command": "editor.peggyLive",
"group": "3_preview",
"when": "editorLangId == peggy"
},
{
"command": "editor.peggyLiveFromRule",
"group": "3_preview",
"when": "editorLangId == peggy"
}
]
},
"commands": [
{
"command": "editor.peggyLive",
"title": "Peggy Live Preview",
"category": "preview"
},
{
"command": "editor.peggyLiveFromRule",
"title": "Peggy Live from rule under cursor",
"category": "preview"
}
]
},
"scripts": {
Expand Down Expand Up @@ -120,6 +147,7 @@
"webpack-cli": "^5.1.4"
},
"dependencies": {
"node-inspect-extracted": "3.0.2",
"peggy": "^4.1.1",
"source-map-generator": "0.8.0",
"vscode-languageclient": "^9.0.1",
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading