From f3d5036f6ba3bbc9c008b1c991ba6dfcc97531e8 Mon Sep 17 00:00:00 2001 From: Jan Dolejsi Date: Mon, 25 Jan 2021 00:47:02 +0100 Subject: [PATCH] PDDL formatter; v2.21.0 --- CHANGELOG.md | 36 +++++- package.json | 8 +- src/configuration/configuration.ts | 13 ++ src/extension.ts | 6 +- src/formatting/PddlFormatProvider.ts | 142 ++++++++++++++++++--- src/formatting/PddlOnTypeFormatter.ts | 10 +- src/init/OverviewPage.ts | 7 +- src/test/suite/PddlFormatProvider.test.ts | 148 +++++++++++++++++++++- views/overview/overview.css | 22 ++++ views/overview/overview.html | 21 +-- views/overview/overview.js | 69 ++++++---- 11 files changed, 417 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ef26b2d..ecbfff36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # PDDL support - What's new? +## 2.21.0 + +### PDDL Formatter + +Right-click on PDDL document and you will see two more options: _Format Document_ and _Format Selection_. +It re-formats the white space to enhance readability of the domain/problem.\ +The formatter does not modify most of your line breaks, but modifies most of the indentation. + +Even more helpful is the on-type formatter, which automatically suggests indentation when you press Enter in the document. + +![On-type formatter](https://raw.githubusercontent.com/wiki/jan-dolejsi/vscode-pddl/img/on-type-formatter.gif) + +The on-type formatter must however be first enabled using this setting: + +```json +"editor.formatOnType": true +``` + +You can selectively enable the on-type formatting just for PDDL documents: + +```json +"[pddl]": { + "editor.formatOnType": true +} +``` + +The above configuration can be now easily inserted from the _PDDL: Overview Page_. + +The Document and Document Selection formatter (along with the separately enabled on-type formatter) can be disabled (in case of clash with another extension) using the `pddl.formatter` setting. + +This was one of the oldest standing feature request is now addressed. +This work was started by our intern more than 2 years ago, but I only had time to finish it now - _thanks_ to C-19 isolation. + ## 2.20.4 - Overview alerts use codicons to look correct on MacOS @@ -1257,7 +1290,8 @@ Note for open source contributors: all notable changes to the "pddl" extension w Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. -[Unreleased]: https://github.com/jan-dolejsi/vscode-pddl/compare/v2.20.0...HEAD +[Unreleased]: https://github.com/jan-dolejsi/vscode-pddl/compare/v2.21.0...HEAD +[2.21.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.20.0...v2.21.0 [2.20.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.19.0...v2.20.0 [2.19.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.18.0...v2.19.0 [2.18.0]:https://github.com/jan-dolejsi/vscode-pddl/compare/v2.17.1...v2.18.0 diff --git a/package.json b/package.json index 6edd96db..dc93fb44 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Planning Domain Description Language support", "author": "Jan Dolejsi", "license": "MIT", - "version": "2.20.4", + "version": "2.21.0", "publisher": "jan-dolejsi", "engines": { "vscode": "^1.50.0", @@ -874,8 +874,8 @@ }, "pddl.formatter": { "type": "boolean", - "default": false, - "description": "Enable PDDL formatter (default is 'false'). Warning: this is an experimental feature - work in progress." + "default": true, + "description": "Enable/disable PDDL formatter (default is 'true')." }, "pddl.modelHierarchy": { "type": "boolean", @@ -1005,7 +1005,7 @@ "jsonc-parser": "^2.2.1", "open": "^7.0.2", "pddl-gantt": "^1.5.3", - "pddl-workspace": "^6.0.3", + "pddl-workspace": "^6.1.0", "request": "^2.88.2", "semver": "^7.1.3", "tree-kill": "^1.2.2", diff --git a/src/configuration/configuration.ts b/src/configuration/configuration.ts index b5cf52c2..125b0903 100644 --- a/src/configuration/configuration.ts +++ b/src/configuration/configuration.ts @@ -40,6 +40,7 @@ export const PLANNER_VAL_STEP_PATH = CONF_PDDL + "." + VAL_STEP_PATH; export const PLANNER_VALUE_SEQ_PATH = CONF_PDDL + "." + VALUE_SEQ_PATH; export const PDDL_CONFIGURE_COMMAND = CONF_PDDL + "." + "configure"; export const DEFAULT_EPSILON = 1e-3; +const EDITOR_FORMAT_ON_TYPE = "editor.formatOnType"; export class PddlConfiguration { @@ -69,6 +70,18 @@ export class PddlConfiguration { return workspace.getConfiguration().get(PLANNER_EPSILON_TIMESTEP, DEFAULT_EPSILON); } + getEditorFormatOnType(): boolean { + return workspace.getConfiguration('', { languageId: CONF_PDDL }).get(EDITOR_FORMAT_ON_TYPE, false); + } + + setEditorFormatOnType(newValue: boolean, options: { forPddlOnly: boolean }): void { + if (options.forPddlOnly) { + workspace.getConfiguration(undefined, { languageId: CONF_PDDL }).update(EDITOR_FORMAT_ON_TYPE, newValue, true, true); + } else { + workspace.getConfiguration().update(EDITOR_FORMAT_ON_TYPE, newValue, true); + } + } + getParserPath(workspaceFolder?: WorkspaceFolder): string | undefined { const configuredPath = workspace.getConfiguration(PDDL_PARSER, workspaceFolder).get(EXECUTABLE_OR_SERVICE); return ensureAbsoluteGlobalStoragePath(configuredPath, this.context); diff --git a/src/extension.ts b/src/extension.ts index 6c4d168e..05c2e88b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -296,8 +296,10 @@ function createAuthentication(pddlConfiguration: PddlConfiguration): Authenticat function registerDocumentFormattingProvider(context: ExtensionContext, pddlWorkspace: CodePddlWorkspace): boolean { if (workspace.getConfiguration("pddl").get("formatter") && !formattingProvider) { formattingProvider = new PddlFormatProvider(pddlWorkspace); - // const formattingProviderDisposable = languages.registerDocumentFormattingEditProvider(PDDL, formattingProvider); - // context.subscriptions.push(formattingProviderDisposable); + const formattingProviderDisposable = languages.registerDocumentFormattingEditProvider(PDDL, formattingProvider); + context.subscriptions.push(formattingProviderDisposable); + const rangeFormattingProviderDisposable = languages.registerDocumentRangeFormattingEditProvider(PDDL, formattingProvider); + context.subscriptions.push(rangeFormattingProviderDisposable); const onTypeFormattingProviderDisposable = languages.registerOnTypeFormattingEditProvider(PDDL, new PddlOnTypeFormatter(pddlWorkspace), '\n'); context.subscriptions.push(onTypeFormattingProviderDisposable); diff --git a/src/formatting/PddlFormatProvider.ts b/src/formatting/PddlFormatProvider.ts index 114f8996..d4cef627 100644 --- a/src/formatting/PddlFormatProvider.ts +++ b/src/formatting/PddlFormatProvider.ts @@ -4,17 +4,18 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; -import { DomainInfo, parser, PddlLanguage, ProblemInfo } from 'pddl-workspace'; -import {TextDocument, CancellationToken, DocumentFormattingEditProvider, FormattingOptions, TextEdit } from 'vscode'; +import { DomainInfo, FileInfo, parser, PddlLanguage, ProblemInfo } from 'pddl-workspace'; +import {TextDocument, CancellationToken, DocumentFormattingEditProvider, FormattingOptions, TextEdit, DocumentRangeFormattingEditProvider, Range, Position } from 'vscode'; import { nodeToRange } from '../utils'; import { CodePddlWorkspace } from '../workspace/CodePddlWorkspace'; +import { PddlOnTypeFormatter } from './PddlOnTypeFormatter'; -export class PddlFormatProvider implements DocumentFormattingEditProvider { +export class PddlFormatProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider { constructor(private pddlWorkspace?: CodePddlWorkspace) { } - async provideDocumentFormattingEdits(document: TextDocument, _options: FormattingOptions, token: CancellationToken): Promise { + async provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): Promise { const fileInfo = await this.pddlWorkspace?.upsertAndParseFile(document); if (token.isCancellationRequested) { return undefined; } @@ -22,6 +23,27 @@ export class PddlFormatProvider implements DocumentFormattingEditProvider { return undefined; } + const tree: parser.PddlSyntaxTree = this.getSyntaxTree(fileInfo, document); + + return new PddlFormatter(document, range, options, token).format(tree.getRootNode()); + } + + async provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): Promise { + const fileInfo = await this.pddlWorkspace?.upsertAndParseFile(document); + if (token.isCancellationRequested) { return undefined; } + + if (fileInfo && (fileInfo.getLanguage() !== PddlLanguage.PDDL)) { + return undefined; + } + + const tree: parser.PddlSyntaxTree = this.getSyntaxTree(fileInfo, document); + + const fullRange = document.validateRange(new Range(new Position(0, 0), new Position(document.lineCount, Number.MAX_VALUE))); + + return new PddlFormatter(document, fullRange, options, token).format(tree.getRootNode()); + } + + private getSyntaxTree(fileInfo: FileInfo | undefined, document: TextDocument): parser.PddlSyntaxTree { let tree: parser.PddlSyntaxTree; if (fileInfo && (fileInfo instanceof DomainInfo)) { tree = (fileInfo as DomainInfo).syntaxTree; @@ -31,19 +53,107 @@ export class PddlFormatProvider implements DocumentFormattingEditProvider { } else { tree = new parser.PddlSyntaxTreeBuilder(document.getText()).getTree(); - } - - const edits: TextEdit[] = []; - this.format(tree.getRootNode(), edits, document); + } + return tree; + } +} - return edits; - } +class PddlFormatter { - format(node: parser.PddlSyntaxNode, edits: TextEdit[], document: TextDocument): void { - if (node.getToken().type === parser.PddlTokenType.Whitespace) { - edits.push(TextEdit.replace(nodeToRange(document, node), ' ')); - } + private readonly edits: TextEdit[] = []; + private readonly firstOffset: number; + private readonly lastOffset: number; - node.getChildren().forEach(child => this.format(child, edits, document)); - } + constructor(private readonly document: TextDocument, range: Range, private readonly options: FormattingOptions, private token: CancellationToken) { + this.firstOffset = document.offsetAt(range.start); + this.lastOffset = document.offsetAt(range.end); + } + + format(node: parser.PddlSyntaxNode): TextEdit[] { + if (node.getStart() > this.lastOffset || this.token.isCancellationRequested) { + return this.edits; + } + + if (node.getStart() >= this.firstOffset || node.includesIndex(this.firstOffset)) { + if (node.getToken().type === parser.PddlTokenType.Whitespace) { + + const nextSibling = node.getFollowingSibling() + ?? node.getParent()?.getFollowingSibling(undefined, node); + + if (node.getParent() && ['(increase', '(decrease', '(assign'].includes(node.getParent()!.getToken().tokenText)) { + if ((node.getParent()?.length ?? 0) > 50) { + this.breakAndIndent(node); + } else { + this.replace(node, ' '); + } + } else if (node.getParent() && ['(:types', '(:objects'].includes(node.getParent()!.getToken().tokenText)) { + // todo: format type inheritance + } else if (nextSibling === undefined) { + this.replace(node, ''); + } else if (nextSibling.isType(parser.PddlTokenType.CloseBracket)) { + if (node.getText().includes('\n')) { + this.breakAndIndent(node, -1); + } else { + this.replace(node, ''); + } + } else if (nextSibling.isType(parser.PddlTokenType.Comment)) { + if (node.getText().includes('\n')) { + this.breakAndIndent(node); + } else { + this.replace(node, ' '); + } + } else if (nextSibling.isAnyOf([parser.PddlTokenType.Dash, parser.PddlTokenType.Other, parser.PddlTokenType.Parameter])) { + this.replace(node, ' '); + } else if (nextSibling.isAnyOf([parser.PddlTokenType.OpenBracket, parser.PddlTokenType.OpenBracketOperator, parser.PddlTokenType.Keyword])) { + if (nextSibling.isType(parser.PddlTokenType.Keyword)) { + if (node.getParent() + && ['(:requirements'].includes(node.getParent()!.getToken().tokenText)) { + this.replace(node, ' '); + } else { + this.breakAndIndent(node); + } + } else if (node.getParent()?.isNumericExpression() || node.getParent()?.isLogicalExpression() || node.getParent()?.isTemporalExpression()) { + if (node.getText().includes('\n')) { + this.breakAndIndent(node); + } else { + this.replace(node, ' '); + } + } else if (['(domain', '(problem'].includes(nextSibling.getToken().tokenText)) { + this.replace(node, ' '); + } else { + if (node.getParent() + && [':parameters', ':duration', ':precondition', ':condition', ':effect'].includes(node.getParent()!.getToken().tokenText)) { + this.replace(node, ' '); + } else { + this.breakAndIndent(node); + } + } + } + } + } + + node.getChildren().forEach(child => this.format(child)); + + return this.edits; + } + + breakAndIndent(node: parser.PddlSyntaxNode, offset = 0): void { + const level = node.getAncestors([parser.PddlTokenType.OpenBracket, parser.PddlTokenType.OpenBracketOperator]).length; + this.replace(node, this.ends(node.getText(), 1) + PddlOnTypeFormatter.createIndent('', level + offset, this.options)); + } + + replace(node: parser.PddlSyntaxNode, newText: string): void { + this.edits.push(TextEdit.replace(nodeToRange(this.document, node), newText)); + } + + /** @returns the endline characters only, but at least the `min` count */ + ends(text: string, min: number): string { + const endls = text.replace(/[^\n\r]/g, ''); + const endlCount = (endls.match(/\n/g) || []).length; + if (endlCount < min) { + return endls + '\n'.repeat(min - endlCount); + } else { + return endls; + } + } } \ No newline at end of file diff --git a/src/formatting/PddlOnTypeFormatter.ts b/src/formatting/PddlOnTypeFormatter.ts index b48df85a..03663f58 100644 --- a/src/formatting/PddlOnTypeFormatter.ts +++ b/src/formatting/PddlOnTypeFormatter.ts @@ -47,9 +47,9 @@ export class PddlOnTypeFormatter implements OnTypeFormattingEditProvider { if (ch === '\n' && currentNode.isType(parser.PddlTokenType.Whitespace)) { const insertBeforeText = previousIndent !== null ? - this.createIndent(previousIndent, 0, options) : - this.createIndent(parentIndent, +1, options); - const insertAfterText = '\n' + this.createIndent(previousIndent ?? parentIndent, 0, options); + PddlOnTypeFormatter.createIndent(previousIndent, 0, options) : + PddlOnTypeFormatter.createIndent(parentIndent, +1, options); + const insertAfterText = '\n' + PddlOnTypeFormatter.createIndent(previousIndent ?? parentIndent, 0, options); // todo: use createEolString(document) const trailingText = document.getText(rangeAfter).trim(); if (trailingText && !trailingText.match(/\w/)) { @@ -69,7 +69,7 @@ export class PddlOnTypeFormatter implements OnTypeFormattingEditProvider { else if (ch === '(-disabled') { // disabled, because it cancels out the auto-completion pop-up const leadingText = document.getText(rangeBefore); if (leadingText.trim() === '(') { - const insertBeforeText = this.createIndent(parentIndent, +1, options) + '('; + const insertBeforeText = PddlOnTypeFormatter.createIndent(parentIndent, +1, options) + '('; return [ TextEdit.replace(rangeBefore, insertBeforeText) ]; @@ -120,7 +120,7 @@ export class PddlOnTypeFormatter implements OnTypeFormattingEditProvider { return lineOfPrevious.text.substr(0, firstNonWhitespaceCharacter); } - createIndent(parentIndentText: string, levelIncrement: number, options: FormattingOptions): string { + static createIndent(parentIndentText: string, levelIncrement: number, options: FormattingOptions): string { const singleIndent = options.insertSpaces ? " ".repeat(options.tabSize) : "\t"; return parentIndentText + singleIndent.repeat(levelIncrement); diff --git a/src/init/OverviewPage.ts b/src/init/OverviewPage.ts index 73fea89e..e7d4685f 100644 --- a/src/init/OverviewPage.ts +++ b/src/init/OverviewPage.ts @@ -147,6 +147,9 @@ export class OverviewPage { const options: ValDownloadOptions = { bypassConsent: message.informedDecision }; await commands.executeCommand(VAL_DOWNLOAD_COMMAND, options); break; + case 'enableFormatOnType': + this.pddlConfiguration.setEditorFormatOnType(true, { forPddlOnly: message.forPddlOnly as boolean}); + break; default: if (message.command?.startsWith('command:')) { const command = message.command as string; @@ -358,7 +361,8 @@ export class OverviewPage { showInstallIconsAlert: !this.iconsInstalled, showEnableIconsAlert: this.iconsInstalled && workspace.getConfiguration().get("workbench.iconTheme") !== "vscode-icons", downloadValAlert: !this.pddlConfiguration.getValidatorPath(this.workspaceFolder) || !(await this.val.isInstalled()), - updateValAlert: await this.val.isNewValVersionAvailable() + updateValAlert: await this.val.isNewValVersionAvailable(), + showEnableFormatterAlert: !this.pddlConfiguration.getEditorFormatOnType() // todo: workbench.editor.revealIfOpen }; return this?.webViewPanel?.webview?.postMessage(message) ?? false; @@ -389,6 +393,7 @@ interface OverviewConfiguration { showEnableIconsAlert: boolean; downloadValAlert: boolean; updateValAlert: boolean; + showEnableFormatterAlert: boolean; } interface WireWorkspaceFolder { diff --git a/src/test/suite/PddlFormatProvider.test.ts b/src/test/suite/PddlFormatProvider.test.ts index 6cf21172..d5cadaf9 100644 --- a/src/test/suite/PddlFormatProvider.test.ts +++ b/src/test/suite/PddlFormatProvider.test.ts @@ -1,5 +1,6 @@ import * as assert from 'assert'; import { before } from 'mocha'; +import { EOL } from 'os'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it @@ -19,13 +20,22 @@ suite('Domain formatter Test Suite', () => { test('Does not modify formatted text', async () => { // GIVEN - const inputText = `(define )`; + const inputText = `(define)`; const expectedText = inputText; await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); }); + test('Removes white space before closing bracket', async () => { + // GIVEN + const inputText = `(define )`; + + const expectedText = `(define)`; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + test('Removes extra white-space', async () => { // GIVEN const inputText = `(define (domain domain_name))`; @@ -35,7 +45,7 @@ suite('Domain formatter Test Suite', () => { await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); }); - test.skip('Indents requirements', async () => { + test('Indents requirements', async () => { // GIVEN const inputText = `(define (domain domain_name) (:requirements :strips) @@ -48,6 +58,15 @@ suite('Domain formatter Test Suite', () => { await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); }); + test('Does not indent individual requirements', async () => { + // GIVEN + const inputText = `(:requirements :strips)`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + test.skip('Formats types', async () => { // GIVEN const inputText = `(define (domain domain_name)(:types child11 child12))`; @@ -75,8 +94,129 @@ suite('Domain formatter Test Suite', () => { await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); }); - test.skip('Removes trailing whitespace', async () => { - assert.fail('Not implemented yet'); + test('Removes trailing whitespace (last line)', async () => { + // GIVEN + const inputText = `(define) `; + + const expectedText = `(define)`; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Removes trailing whitespace', async () => { + // GIVEN + const inputText = [`(define (domain)\t\t`, + `)`].join('\n'); + + const expectedText = [`(define (domain)`, + `)`].join('\n'); + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Does not break line in numeric expressions', async () => { + // GIVEN + const inputText = `(= (f1) (f2))`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Formats numeric expression that is already broken to multiple lines', async () => { + // GIVEN + const inputText = `(= \n(f1)\n (f2))`; + + const expectedText = `(=\n\t(f1)\n\t(f2))`; + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); + }); + + test('Does not break line in logical expressions (not)', async () => { + // GIVEN + const inputText = `(not (p1))`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Does not break line in logical expressions (and)', async () => { + // GIVEN + const inputText = `(and (p1) (p2))`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Does not break line in temporal (at start)', async () => { + // GIVEN + const inputText = `(at start (p1))`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: true, tabSize: 4 }); + }); + + test('Does not break action keywords', async () => { + // GIVEN + const inputText = [`(:action a`, + '\t:parameters (?p1 - param1)', + ')' + ].join('\n'); + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); + }); + + test('Does breaks line before action keywords', async () => { + // GIVEN + const inputText = [`(:action a`, + '\t:parameters ()', + '\t:precondition (and)', + ')' + ].join('\n'); + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); + }); + + test('keeps line break before comment line', async () => { + // GIVEN + const inputText = [`(:functions`, + '\t(f1)', + '\t; (f2)', + ')' + ].join('\n'); + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); + }); + + test('keeps short effects on one line', async () => { + // GIVEN + const inputText = `(assign (f1) 10)`; + + const expectedText = inputText; + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); + }); + + test('splits long effect', async () => { + // GIVEN + const inputText = `(increase (ffffffffffffffffffffffffff1) (+ (gggggggggggggggggggg) 1))`; + + const expectedText = [ + `(increase`, + `\t(ffffffffffffffffffffffffff1)`, + `\t(+ (gggggggggggggggggggg) 1))` + ].join(EOL); + + await testFormatter(inputText, expectedText, { insertSpaces: false, tabSize: 4 }); }); }); diff --git a/views/overview/overview.css b/views/overview/overview.css index 2535aa38..657b9e05 100644 --- a/views/overview/overview.css +++ b/views/overview/overview.css @@ -75,3 +75,25 @@ tr.alert > td:nth-child(1) { /* text-indent: 0em; */ margin: 5px; } +button.alertButton { + text-align: center; +} +snap.code { + font-family: monospace; +} + + +/* Rotating the 'loading' Codicon to indicate _progress_. +Same implementation as what VS Code is using in the user interface. +https://github.com/microsoft/vscode/blob/master/src/vs/base/browser/ui/codicons/codicon/codicon-animations.css +*/ +@keyframes codicon-spin { + 100% { + transform:rotate(360deg); + } +} + +.codicon-animation-spin { + /* Use steps to throttle FPS to reduce CPU usage */ + animation: codicon-spin 1.5s steps(30) infinite; +} \ No newline at end of file diff --git a/views/overview/overview.html b/views/overview/overview.html index 76f5451a..458bde3b 100644 --- a/views/overview/overview.html +++ b/views/overview/overview.html @@ -43,7 +43,7 @@

AI Planning and PDDL support in VS Code

When processing files using command-line tools, it is simpler to enable file auto-saving. - +
It is also recommended to go run the Git: Initialize Repository command to enjoy safety of version control and never miss a working version. @@ -54,7 +54,7 @@

AI Planning and PDDL support in VS Code

build of VAL tools to get a PDDL parser, to be able to validate and evaluate plans. - plan validation tools @@ -64,15 +64,19 @@

AI Planning and PDDL support in VS Code

New version of VAL tools is available. Update to the latest version. - - + + + Enable on-type formatter to save time and make your models readable without wasting time formatting whitespace. + + +
+ You can switch it off again using the editor.formatOnType setting. + + @@ -91,6 +95,7 @@

Getting started

Configuration +

diff --git a/views/overview/overview.js b/views/overview/overview.js index 8ab861cd..54b36f2a 100644 --- a/views/overview/overview.js +++ b/views/overview/overview.js @@ -53,6 +53,7 @@ function initialize() { * @property {boolean} showEnableIconsAlert * @property {boolean} downloadValAlert * @property {boolean} updateValAlert + * @property {boolean} showEnableFormatterAlert */ /** @@ -66,19 +67,28 @@ function initialize() { * @param {OverviewConfiguration} message configuration */ function updateConfiguration(message) { - updatePlanners(message.planners, message.selectedPlanner, message.imagesPath); - updatePlannersError(message.plannersConfigError); - updateWorkspaceFolders(message.workspaceFolders, message.selectedWorkspaceFolder); - document.getElementById('parser').value = message.parser; - document.getElementById('validator').value = message.validator; - setStyleDisplay('installIconsAlert', message.showInstallIconsAlert, "list-item"); - setStyleDisplay('enableIconsAlert', message.showEnableIconsAlert, "list-item"); - setStyleDisplay('enableAutoSaveAlert', message.autoSave === "off", "list-item"); - setStyleDisplay('downloadValAlert', message.downloadValAlert, "list-item"); - setStyleDisplay('updateValAlert', message.updateValAlert, "list-item"); - setStyleDisplay('alertList', hasAnyChildrenToDisplay('alertList'), "block"); - updatePlannerOutputTarget(message.plannerOutputTarget); - updateShowOverviewChanged(message.shouldShow); + try { + updatePlanners(message.planners, message.selectedPlanner, message.imagesPath); + updatePlannersError(message.plannersConfigError); + updateWorkspaceFolders(message.workspaceFolders, message.selectedWorkspaceFolder); + document.getElementById('parser').value = message.parser; + document.getElementById('validator').value = message.validator; + setStyleDisplay('installIconsAlert', message.showInstallIconsAlert, "table-row"); + setStyleDisplay('enableIconsAlert', message.showEnableIconsAlert, "table-row"); + setStyleDisplay('enableAutoSaveAlert', message.autoSave === "off", "table-row"); + setStyleDisplay('downloadValAlert', message.downloadValAlert, "table-row"); + setStyleDisplay('updateValAlert', message.updateValAlert, "table-row"); + setStyleDisplay('enableFormatterAlert', message.showEnableFormatterAlert, "table-row"); + setStyleDisplay('alertList', hasAnyChildrenToDisplay(), "block"); + updatePlannerOutputTarget(message.plannerOutputTarget); + updateShowOverviewChanged(message.shouldShow); + } finally { + const settingsProgress = document.getElementById('settingsProgress'); + if (settingsProgress) { + settingsProgress.classList.remove('codicon-animation-spin'); + settingsProgress.style.visibility = "hidden"; + } + } } /** @@ -279,6 +289,20 @@ function selectPlanner(selectedPlanner) { }); } +function enableFormatOnTypeForPddlOnly() { + postMessage({ + command: 'enableFormatOnType', + forPddlOnly: true + }); +} + +function enableFormatOnType() { + postMessage({ + command: 'enableFormatOnType', + forPddlOnly: false + }); +} + /** * Converts a boolean to a display style * @param {string} elementId element ID @@ -293,16 +317,11 @@ function setStyleDisplay(elementId, shouldDisplay, displayStyle) { /** * Returns true if at least one element has not-"none" display style - * @param {string} elementId html element ID */ -function hasAnyChildrenToDisplay(elementId) { - const parent = document.getElementById(elementId); - for (let index = 0; index < parent.childElementCount; index++) { - const child = parent.children.item(index); - if (child.nodeType !== Node.ELEMENT_NODE) { continue; } - if (child.tagName === 'TBODY') { continue; } - - if (child.style.display !== "none") { +function hasAnyChildrenToDisplay() { + const alerts = document.querySelectorAll('table.alertList > tbody > tr.alert'); + for (const tr of alerts) { + if (tr.style.display !== "none"){ return true; } } @@ -448,7 +467,8 @@ function populateWithTestData() { showInstallIconsAlert: true, showEnableIconsAlert: true, downloadValAlert: true, - updateValAlert: true + updateValAlert: true, + showEnableFormatterAlert: true }); } @@ -462,6 +482,7 @@ function clearData() { showInstallIconsAlert: false, showEnableIconsAlert: false, downloadValAlert: false, - updateValAlert: false + updateValAlert: false, + enableFormatterAlert: false }); } \ No newline at end of file