diff --git a/CHANGELOG.md b/CHANGELOG.md index f609fd57..c5f162f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 2.6.2 - 2021-01-30 + +- Fix formatting after ternary operator ([#18](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/18)) +- Fix formatting after multiple close braces on one line ([#26](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/26)) + ## 2.6.1 - 2021-01-23 - Fix hover provider ([#16](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/16)) diff --git a/demos/README.md b/demos/README.md new file mode 100644 index 00000000..bc833203 --- /dev/null +++ b/demos/README.md @@ -0,0 +1,3 @@ +# Demos + +This folder contains AutoHotkey scripts formatted with this extension. diff --git a/demos/demo_for_ahk_v1.ahk b/demos/demo_for_ahk_v1.ahk index 6335f3d3..f4c89cc8 100644 --- a/demos/demo_for_ahk_v1.ahk +++ b/demos/demo_for_ahk_v1.ahk @@ -36,15 +36,13 @@ function() obj := { str: str, int: int, float: float } objobj := { str: str, obj: obj } - ; Known bug: the entire body of a function should be indented - ; https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/issues/26 -objobjobj := { str: str, int: int, obj: { str: str, obj: obj } } + objobjobj := { str: str, int: int, obj: { str: str, obj: obj } } -circular := {} -circular.circular := circular -instance := new Cls() + circular := {} + circular.circular := circular + instance := new Cls() -enum := obj._NewEnum() + enum := obj._NewEnum() } class Cls { @@ -103,4 +101,8 @@ ExitApp f1:: MsgBox, You hit F1 ; The F2 hotkey will not work because it was not part of the selection -f2:: MsgBox, You hit F2 \ No newline at end of file +f2:: MsgBox, You hit F2 + +; Formatting line below ternary with third operand a string value +true ? 1 : "string" +foo() diff --git a/docs/Development.md b/docs/Development.md index a28cb9dc..d289ba71 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -10,11 +10,24 @@ This document covers the development process, from writing code to publishing a 1. Once the `dev` branch has all the features for a new release, create a new release branch named `v..` (e.g. `v2.5.10`). 1. Update package version 1. Update changelog - 1. Save final changes in commit. The message of the commit should be the same name as the branch. + 1. Save final changes in commit. The message of the commit should be the name of the release branch. 1. Push the changes, open a PR, review the changes, and merge to `master`. 1. Pull the new master branch 1. Package the new release using `vsce package`. -1. Final round of tests +1. Install new release +1. Perform final round of tests + 1. If tests fail, there are two choices: + 1. Delay the release until the tests pass (preferred choice) + > Changes can be made on the same release branch, same package version + 1. Create issues for the newly-introduced failures before releasing, then publish the release anyway +1. Create and push the version tag + 1. Delete the release branch + 1. `git tag v..` + 1. `git push` + 1. Update the metadata in the [Releases Entry](https://github.com/mark-wiemer/vscode-autohotkey-plus-plus/releases) + 1. Release title: Same as in [CHANGELOG.md](../CHANGELOG.md) + 1. Description: Same as in changelog + 1. Attach binary 1. Publish the release through [Visual Studio Marketplace](https://marketplace.visualstudio.com/manage/publishers/mark-wiemer) 1. Select the ellipsis `Actions` icon and select `Update`. diff --git a/package.json b/package.json index 742e053e..d4d75eb0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-autohotkey-plus-plus", "displayName": "AutoHotkey Plus Plus", "description": "AutoHotkey IntelliSense, debug, and language support for VS Code, forked from AutoHotkey Plus by cweijan", - "version": "2.6.1", + "version": "2.6.2", "publisher": "mark-wiemer", "engines": { "vscode": "^1.30.0" diff --git a/src/common/codeUtil.ts b/src/common/codeUtil.ts index a166edbf..a0cbc373 100644 --- a/src/common/codeUtil.ts +++ b/src/common/codeUtil.ts @@ -1,21 +1,26 @@ export class CodeUtil { /** - * trim unfoucs code. - * @param origin any string + * Trim non-formatted chars out of original lines of code + * @param original Original line of code */ - public static purity(origin: string): string { - if (!origin) return ''; - // TODO: untest - return origin + public static purify(original: string): string { + if (!original) return ''; + return original .replace(/;.+/, '') - .replace(/".*?"/g, '') - .replace(/\{.*?\}/g, '') + .replace(/".*?"/g, '""') // replace string literals with empty string literal + .replace(/{.*}/g, '') // remove matching braces .replace(/ +/g, ' ') .replace(/\bgui\b.*/gi, '') .replace(/\b(msgbox)\b.+?%/gi, '$1'); } - public static join(array: any[], items: any | any[]) { + /** + * Concats an array and an item or array of items. Impure, @see array is modified + * @param array The initial array + * @param items Either an item to add to the end of the array, + * or another array to concat to the end of @see array + */ + public static join(array: unknown[], items: unknown) { if (array == undefined || items == undefined) { return; } diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 628f83b2..c754a291 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -197,7 +197,7 @@ export class Parser { } private static getLabelByLine(document: vscode.TextDocument, line: number) { - const text = CodeUtil.purity(document.lineAt(line).text); + const text = CodeUtil.purify(document.lineAt(line).text); const label = /^[ \t]*([\u4e00-\u9fa5_a-zA-Z0-9]+) *:{1}(?!(:|=))/.exec( text, ); @@ -219,7 +219,7 @@ export class Parser { document: vscode.TextDocument, line: number, ): Variable | Variable[] { - const lineText = CodeUtil.purity(document.lineAt(line).text); + const lineText = CodeUtil.purify(document.lineAt(line).text); const defMatch = lineText.match(Parser.varDefPattern); if (defMatch) { @@ -270,7 +270,7 @@ export class Parser { origin?: string, ) { origin = origin != undefined ? origin : document.lineAt(line).text; - const text = CodeUtil.purity(origin); + const text = CodeUtil.purify(origin); const refPattern = /\s*(([\u4e00-\u9fa5_a-zA-Z0-9]+)(? { - let formatDocument = ''; - let deep = 0; - let tagDeep = 0; + let formattedDocument = ''; + let depth = 0; + let tagDepth = 0; let oneCommandCode = false; let blockComment = false; - - for (let line = 0; line < document.lineCount; line++) { - const originText = document.lineAt(line).text; - if (originText.match(/ *\/\*/)) { + let atTopLevel = true; + + for (let lineIndex = 0; lineIndex < document.lineCount; lineIndex++) { + const originalLine = document.lineAt(lineIndex).text; + const purifiedLine = CodeUtil.purify(originalLine.toLowerCase()); + /** The line comment. Empty string if no line comment exists */ + const comment = /;.+/.exec(originalLine)?.[0] ?? ''; + const formattedLine = originalLine + .replace(/^\s*/, '') + .replace(/;.+/, '') + .replace(/ {2,}/g, ' ') + .concat(comment); + + atTopLevel = true; + + // Match block comments + if (originalLine.match(/ *\/\*/)) { blockComment = true; } - if (originText.match(/ *\*\//)) { + if (originalLine.match(/ *\*\//)) { blockComment = false; } if (blockComment) { - formatDocument += originText; - if (line !== document.lineCount - 1) { - formatDocument += '\n'; + formattedDocument += originalLine; + if (lineIndex !== document.lineCount - 1) { + formattedDocument += '\n'; } continue; } - const purityText = CodeUtil.purity(originText.toLowerCase()); - let notDeep = true; if ( - purityText.match(/#ifwinactive$/) || - purityText.match(/#ifwinnotactive$/) + purifiedLine.match(/#ifwinactive$/) || + purifiedLine.match(/#ifwinnotactive$/) ) { - if (tagDeep > 0) { - deep -= tagDeep; + if (tagDepth > 0) { + depth -= tagDepth; } else { - deep--; + depth--; } - notDeep = false; + atTopLevel = false; } - if (purityText.match(/\b(return|ExitApp)\b/i) && tagDeep === deep) { - tagDeep == 0; - deep--; - notDeep = false; + if ( + purifiedLine.match(/\b(return|ExitApp)\b/i) && + tagDepth === depth + ) { + tagDepth == 0; + depth--; + atTopLevel = false; } - if (purityText.match(/^\s*case.+?:\s*$/)) { - tagDeep--; - deep--; - notDeep = false; - } else if (purityText.match(/:\s*$/)) { - if (tagDeep > 0 && tagDeep === deep) { - deep--; - notDeep = false; + if (purifiedLine.match(/^\s*case.+?:\s*$/)) { + tagDepth--; + depth--; + atTopLevel = false; + } else if (purifiedLine.match(/:\s*$/)) { + if (tagDepth > 0 && tagDepth === depth) { + depth--; + atTopLevel = false; } } - if (purityText.match(/}/) != null) { - let temp = purityText.match(/}/).length; - const t2 = purityText.match(/{[^{}]*}/); + // Check open and close braces + if (purifiedLine.match(/}/) != null) { + let temp = purifiedLine.match(/}/).length; + const t2 = purifiedLine.match(/{[^{}]*}/); if (t2) { temp = temp - t2.length; } - deep -= temp; + depth -= temp; if (temp > 0) { - notDeep = false; + atTopLevel = false; } } - - if (oneCommandCode && purityText.match(/{/) != null) { - let temp = purityText.match(/{/).length; - const t2 = purityText.match(/{[^{}]*}/); + if (oneCommandCode && purifiedLine.match(/{/) != null) { + let temp = purifiedLine.match(/{/).length; + const t2 = purifiedLine.match(/{[^{}]*}/); if (t2) { temp = temp - t2.length; } if (temp > 0) { oneCommandCode = false; - deep--; + depth--; } } - if (deep < 0) { - deep = 0; + if (depth < 0) { + depth = 0; } - let comment: any = /;.+/.exec(originText); - comment = comment ? comment[0] : ''; - - const formatedText = - originText - .replace(/^\s*/, '') - .replace(/;.+/, '') - .replace(/ {2,}/g, ' ') + comment; - formatDocument += - !formatedText || formatedText.trim() == '' - ? formatedText - : ' '.repeat(deep * options.tabSize) + formatedText; - if (line !== document.lineCount - 1) { - formatDocument += '\n'; + + formattedDocument += + !formattedLine || formattedLine.trim() == '' + ? formattedLine + : ' '.repeat(depth * options.tabSize) + formattedLine; + + // If not last line, add newline + if (lineIndex !== document.lineCount - 1) { + formattedDocument += '\n'; } if (oneCommandCode) { oneCommandCode = false; - deep--; + depth--; } if ( - purityText.match(/#ifwinactive.*?\s/) || - purityText.match(/#ifwinnotactive.*?\s/) + purifiedLine.match(/#ifwinactive.*?\s/) || + purifiedLine.match(/#ifwinnotactive.*?\s/) ) { - deep++; - notDeep = false; + depth++; + atTopLevel = false; } - if (purityText.match(/{/) != null) { - let temp = purityText.match(/{/).length; - const t2 = purityText.match(/{[^{}]*}/); + if (purifiedLine.match(/{/) != null) { + let temp = purifiedLine.match(/{/).length; + const t2 = purifiedLine.match(/{[^{}]*}/); if (t2) { temp = temp - t2.length; } - deep += temp; + depth += temp; if (temp > 0) { - notDeep = false; + atTopLevel = false; } } - if (purityText.match(/:\s*$/)) { - deep++; - tagDeep = deep; - notDeep = false; + if (purifiedLine.match(/:\s*$/)) { + depth++; + tagDepth = depth; + atTopLevel = false; } - if (notDeep) { + if (atTopLevel) { for (const oneCommand of FormatProvider.oneCommandList) { let temp: RegExpExecArray; if ( (temp = new RegExp('\\b' + oneCommand + '\\b(.*)').exec( - purityText, + purifiedLine, )) != null && !temp[1].includes('/') ) { oneCommandCode = true; - deep++; + depth++; break; } } } } - const result = []; - result.push( + + return [ new vscode.TextEdit( fullDocumentRange(document), - formatDocument.replace(/\n{2,}/g, '\n\n'), + formattedDocument.replace(/\n{2,}/g, '\n\n'), ), - ); - return result; + ]; } }