diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc176f34..c8fd016b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,5 +65,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: results - path: /home/runner/work/AblFormatter/AblFormatter/resources/testResults + path: /home/runner/work/OpenedgeAblFormatter/OpenedgeAblFormatter/resources/testResults retention-days: 7 diff --git a/package.json b/package.json index 50c24056..7a47fd31 100644 --- a/package.json +++ b/package.json @@ -296,6 +296,12 @@ "default": "true", "description": "Enable STATEMENT formatting" }, + "AblFormatter.variableAssignmentFormatting": { + "order": 2000, + "type": "boolean", + "default": "true", + "description": "Enable STATEMENT formatting" + }, "AblFormatter.showTreeInfoOnHover": { "order": 10100, "type": "boolean", diff --git a/resources/functionalTests/arrayAccess/3-nested/input.p b/resources/functionalTests/arrayAccess/3-nested/input.p index 886f0dfb..457667dd 100644 --- a/resources/functionalTests/arrayAccess/3-nested/input.p +++ b/resources/functionalTests/arrayAccess/3-nested/input.p @@ -1,4 +1,4 @@ /* formatterSettingsOverride */ /* { "AblFormatter.arrayAccessFormatting": true}*/ -a [ b [ myFunc ( i ) ] ] = d [ e [ f [ i ] ] - g [ j ] ] +a [ b [ myFunc ( i ) ] ] = d [ e [ f [ i ] ] - g [ j ] ]. diff --git a/resources/functionalTests/arrayAccess/3-nested/target.p b/resources/functionalTests/arrayAccess/3-nested/target.p index ba15033f..5469379d 100644 --- a/resources/functionalTests/arrayAccess/3-nested/target.p +++ b/resources/functionalTests/arrayAccess/3-nested/target.p @@ -1,4 +1,4 @@ /* formatterSettingsOverride */ /* { "AblFormatter.arrayAccessFormatting": true}*/ -a[b[myFunc ( i )]] = d[e[f[i]] - g[j]] +a[b[myFunc ( i )]] = d[e[f[i]] - g[j]]. diff --git a/resources/functionalTests/arrayAccess/6-array-literal/input.p b/resources/functionalTests/arrayAccess/6-array-literal/input.p new file mode 100644 index 00000000..a7acea51 --- /dev/null +++ b/resources/functionalTests/arrayAccess/6-array-literal/input.p @@ -0,0 +1,4 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.arrayAccessFormatting": true}*/ + +VAR SparkleState [ 2 ] enchantedForest = [NEW SparkleState("Candyland"), NEW SparkleState("Bubbletopia")]. \ No newline at end of file diff --git a/resources/functionalTests/arrayAccess/6-array-literal/target.p b/resources/functionalTests/arrayAccess/6-array-literal/target.p new file mode 100644 index 00000000..cee8776d --- /dev/null +++ b/resources/functionalTests/arrayAccess/6-array-literal/target.p @@ -0,0 +1,4 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.arrayAccessFormatting": true}*/ + +VAR SparkleState[2] enchantedForest = [NEW SparkleState("Candyland"), NEW SparkleState("Bubbletopia")]. \ No newline at end of file diff --git a/resources/functionalTests/block/58-repeat-while/input.p b/resources/functionalTests/block/58-repeat-while/input.p new file mode 100644 index 00000000..02bceace --- /dev/null +++ b/resources/functionalTests/block/58-repeat-while/input.p @@ -0,0 +1,8 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +repeat while a < abc and true: + assign + l_dbnr = l_dbnr + 1 + . + end. diff --git a/resources/functionalTests/block/58-repeat-while/target.p b/resources/functionalTests/block/58-repeat-while/target.p new file mode 100644 index 00000000..cc2ce17b --- /dev/null +++ b/resources/functionalTests/block/58-repeat-while/target.p @@ -0,0 +1,8 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +repeat while a < abc and true: + assign + l_dbnr = l_dbnr + 1 + . +end. diff --git a/resources/functionalTests/block/59-do-while/input.p b/resources/functionalTests/block/59-do-while/input.p new file mode 100644 index 00000000..3e98c6cf --- /dev/null +++ b/resources/functionalTests/block/59-do-while/input.p @@ -0,0 +1,6 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +do while (iTotal < 50 and not bDone) or (iTotal > 100 and bDone): + iTotal = iTotal * 2. + END. \ No newline at end of file diff --git a/resources/functionalTests/block/59-do-while/target.p b/resources/functionalTests/block/59-do-while/target.p new file mode 100644 index 00000000..1f453a02 --- /dev/null +++ b/resources/functionalTests/block/59-do-while/target.p @@ -0,0 +1,6 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +do while (iTotal < 50 and not bDone) or (iTotal > 100 and bDone): + iTotal = iTotal * 2. +END. \ No newline at end of file diff --git a/resources/functionalTests/block/60-class/input.p b/resources/functionalTests/block/60-class/input.p new file mode 100644 index 00000000..3626ee92 --- /dev/null +++ b/resources/functionalTests/block/60-class/input.p @@ -0,0 +1,11 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +class WackyWorld.FunFactory.ServerCommand.SillyDanceCommand abstract inherits ProStuff: define static variable BananaFunction as char no-undo. + define protected override property GroovyCommandName as char no-undo + get(): + return "jazzytool". + end. + set. + +end class. diff --git a/resources/functionalTests/block/60-class/target.p b/resources/functionalTests/block/60-class/target.p new file mode 100644 index 00000000..34654137 --- /dev/null +++ b/resources/functionalTests/block/60-class/target.p @@ -0,0 +1,12 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.blockFormatting": true}*/ + +class WackyWorld.FunFactory.ServerCommand.SillyDanceCommand abstract inherits ProStuff: + define static variable BananaFunction as char no-undo. + define protected override property GroovyCommandName as char no-undo + get(): + return "jazzytool". + end. + set. + +end class. diff --git a/resources/functionalTests/for/8each-label/input.p b/resources/functionalTests/for/8each-label/input.p index 43c0aba5..04342b8b 100644 --- a/resources/functionalTests/for/8each-label/input.p +++ b/resources/functionalTests/for/8each-label/input.p @@ -5,6 +5,8 @@ define variable i as integer no-undo. Label: + + for each Customer: message Customer.Id. - end. \ No newline at end of file +end. \ No newline at end of file diff --git a/resources/functionalTests/for/9each-label/input.p b/resources/functionalTests/for/9each-label/input.p new file mode 100644 index 00000000..9165d839 --- /dev/null +++ b/resources/functionalTests/for/9each-label/input.p @@ -0,0 +1,13 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.forFormatting": true}*/ + + +chicFiles: FOR EACH stylishFile ON ERROR UNDO, THROW: + DO ON ERROR UNDO, THROW: + NEXT chicFiles. + END. + + IF fashionAnnotations:Count > 0 THEN DO: + NEXT chicFiles. + END. + END. \ No newline at end of file diff --git a/resources/functionalTests/for/9each-label/target.p b/resources/functionalTests/for/9each-label/target.p new file mode 100644 index 00000000..e538e28d --- /dev/null +++ b/resources/functionalTests/for/9each-label/target.p @@ -0,0 +1,14 @@ +/* formatterSettingsOverride */ +/* { "AblFormatter.forFormatting": true}*/ + + +chicFiles: +FOR EACH stylishFile ON ERROR UNDO, THROW: + DO ON ERROR UNDO, THROW: + NEXT chicFiles. + END. + + IF fashionAnnotations:Count > 0 THEN DO: + NEXT chicFiles. + END. +END. \ No newline at end of file diff --git a/resources/stabilityTests/_failures.txt b/resources/stabilityTests/_failures.txt index 8be4e527..01446b23 100644 --- a/resources/stabilityTests/_failures.txt +++ b/resources/stabilityTests/_failures.txt @@ -2,103 +2,36 @@ adecomm_aud-tts.i adecomm_dswid.i adecomm_peditor.i adecomm_xmlschema.p -adecomm_xmlwidg.i adecomm__dswidfunc.p adecomm__pweditor.i adecomm__reg2ini.p adedict_FLD__dfltgat.p adedict_FLD__fldgate.p -adedict_IDX__newidx.p -adeedit__winrsz.p -aderes_aderes.p -aderes_j-table2.p -aderes_l-verify.p -aderes_s-exp.p -aderesc__read.p -adeshar__cntrapi.p -adeshar__mchgi.p -adeuib_ide_request__requestmanager.cls +adeshar__mchgi.i adeuib_sookver.i -adeuib_ttobject.i -adeuib_vrfyimp.i -adeuib__cuelist.w adeuib__edtmenu.p -adeuib__genmult.p -adeuib__rdproc.p -adeuib__rsz_wp.p -adeuib__seresz.p -adeuib__ttyedit.p adeuib__ttyss.i adeuib__tview.w -adeweb_rdproc.i -adeweb__rddefs.p -adexml__xmlview.w -adm2_support_filterd.w -adm2_support_folderd.w -adm2_widgetprto.i -auditing_include__xmlsec.i auditing_sdo__audfieldpolicysdo.w auditing_sdo__audfilepolicysdo.w -auditing_src_adm2_widgetprto.i auditing_ttdefs__audeventpolicytt.i auditing_ttdefs__audeventtt.i auditing_ttdefs__audfieldpolicytt.i auditing_ttdefs__audfilepolicytt.i auditing_ttdefs__audpolicytt.i -corelib_OpenEdge_Core_Collections_AbstractTTCollection.cls netlib_OpenEdge_Web_DataObject_Writer_FieldElementWriter.cls -OpenEdge_DataAdmin_Binding_CreateContext.cls -OpenEdge_DataAdmin_Binding_Query_AreaExtentQuery.cls -OpenEdge_DataAdmin_Binding_Query_SchemaPartitionQuery.cls -OpenEdge_DataAdmin_Binding_SequenceValueContext.cls -OpenEdge_DataAdmin_Core_JSONLoader.cls -OpenEdge_DataAdmin_DataAccess_AllFileData.cls -OpenEdge_DataAdmin_DataFileList.cls OpenEdge_DataAdmin_DataSource_IndexDataSource.cls -OpenEdge_DataAdmin_DataSource_UserPermissionDataSource.cls -OpenEdge_DataAdmin_ExtentList.cls -OpenEdge_DataAdmin_IndexFieldList.cls -OpenEdge_DataAdmin_IPartitionCollection.cls -OpenEdge_DataAdmin_ITable.cls -OpenEdge_DataAdmin_IUserTablePermission.cls -OpenEdge_DataAdmin_Lang_QueryString.cls -OpenEdge_DataAdmin_Message_IFetchRequest.cls -OpenEdge_DataAdmin_Message_ISaveRequest.cls -OpenEdge_DataAdmin_Message_ITableRequest.cls -OpenEdge_DataAdmin_PartitionPolicyField.cls -OpenEdge_DataAdmin_Rest_RestRequest.cls -OpenEdge_DataAdmin_ServerCommand_ProstrctAddOnlineCommand.cls -OpenEdge_DataAdmin_TenantTypes.cls -OpenEdge_DataAdmin_Util_CDCTrackingHelper.cls OpenEdge_DataAdmin_Util_dbCanMigrate.p -OpenEdge_DataAdmin_Util_ITableImportUtility.cls -prodict_dictsplt.i prodict_dictsrch.i prodict_dump__dmpincr.p prodict_dump__dmpsec.p -prodict_dump__dmputil.p prodict_dump__lodsddl.p prodict_dump__lodsec.p prodict_gate_gatework.i prodict_gate__gat_cro.p -prodict_gate__gat_nam.p prodict_mss__mss_pul.p prodict_odb__odb_pul.p -prodict_ora__ora_typ.p -prodict_pro__pro_fld.p prodict_sec_sec-pol.i -prodict_user__usrtget.p -prohelp_keyword.p -prores_a-fast.p -prores_a-write.p -prores_c-edit.i -prores_l-guess.p -prores_l-verify.p -prores_l-write.p -prores_q-join.p -prores_r-ft.p -prores_r-main.p -prores_r-write.i prores_reslang_t-a-dan.p prores_reslang_t-a-fr7.p prores_reslang_t-a-fre.p @@ -124,20 +57,126 @@ prores_reslang_t-r-fre.p prores_reslang_t-s-eng.p prores_reslang_t-s-fr7.p prores_reslang_t-s-fre.p -prores_s-gen2.i -prores_s-info.p -prores_s-order.p prores_t-i-eng.p prores_t-s-eng.p -prores_u-export.p -prores_u-load.p -protools__dblist.w protools__scrcap.w -protools__session.w -protools__v89conv.w -web_examples_w-custdir.w webtools_util__dirlist.w workshop_dirnode.i -workshop_rdproc.i -workshop__methlib.w -workshop__rdproc.p +adexml__xmlview.w +//0046 issues --> +ablunit_OpenEdge_ABLUnit_Runner_ABLRunner.cls +adecomm__assign-wid.w +adecomm__auddat.p +adecomm__auddfilt.p +adecomm__auddtl.p +adecomm__cust-flt.p +adecomm__dbconng.w +adecomm__disconn.p +adecomm__getfdbs.p +adecomm__getwidobjs.w +adecomm__qtbldat.p +adedesk_menus.i +adedict_DB__getdbs.p +adedict_FLD_fldvar.i +adedict_gateproc.i +adedict_SEQ_seqvar.i +adedict_TBL_tblvar.i +aderes_c-header.p +aderes_j-both.p +aderes_j-field1.p +aderes_j-field2.p +aderes_j-table1.p +aderes_j-table2.p +aderes_r-header.p +aderes_y-total.p +aderes__aicdir.p +adeshar_qurydefs.i +adeshar__qenable.p +adeuib_brwscols.i +adeuib_layout.i +adeuib_uibmdefs.i +adeuib__attr-ed.w +adeuib__chkrlnk.i +adeuib__convertadm2dynamics.w +adeuib__cr_palette.p +adeuib__seprocs.i +adeuib__tabedit.w +adeuib__tempdb.w +adeuib__ttmaint.w +adeuib__widgetidassign.w +adm_samples_b-cuslkp.w +adm_samples_b-linord.w +adm_samples_itemsel.w +adm_support_filtedit.w +adm_support_folderd.w +adm_support_sortedit.w +adm_support__so-attr.w +adm_template_browser.w +adm_template_wbrowser.w +adm2_support_brouter.w +adm2_support_folderd.w +adm2_support__so-attr.w +adm2_template_browser.w +businesscomponents_OpenEdge_BusinessLogic_Filter_AblFilterParser.cls +corelib_OpenEdge_Core_DataTypeHelper.cls +corelib_OpenEdge_Core_Json_JsonConverter.cls +corelib_OpenEdge_Core_Util_leakobject.i +corelib_OpenEdge_Logging_Format_AnonymizedTokenFormat.cls +corelib_OpenEdge_Logging_Format_LogMessageTokenResolver.cls +netlib_OpenEdge_Net_HTTP_Filter_Payload_MultipartFormBodyWriter.cls +netlib_OpenEdge_Net_URI.cls +netlib_OpenEdge_Web_DataObject_OperationHandler.cls +netlib_OpenEdge_Web_DataObject_Writer_OpenAPI30ServiceWriter.cls +netlib_OpenEdge_Web_Logging_WebRequestTokenResolver.cls +OpenEdge_DataAdmin_Binding_ExtentContext.cls +OpenEdge_DataAdmin_Binding_TableContext.cls +OpenEdge_DataAdmin_ServerCommand_TableDataCommand.cls +prodict_aud__aud-pol.p +prodict_dump__dmpdefs.p +prodict_dump__lodaudp.p +prodict_gate__gat_cmp.p +prodict_gate__gat_ini.p +prodict_gate__gat_row.p +prodict_gate__snd_sql.i +prodict_gate__snd_sql.p +prodict_gui__guiuchg.p +prodict_misc__cdc-pol.p +prodict_misc__db-id-hst.p +prodict_misc__db-id-mnt.p +prodict_mss_key_check.i +prodict_mss_key_clust.i +prodict_mss_key_default.i +prodict_mss_key_modcheck.i +prodict_mss_key_modclus.i +prodict_mss_key_moddef.i +prodict_mss_key_moduni.i +prodict_mss_key_unique.i +prodict_mss__mssschg.p +prodict_mss__mss_md2.p +prodict_odb__odb_fld.p +prodict_odb__odb_md1.p +prodict_odb__odb_md2.p +prodict_ora_key_check.i +prodict_ora_key_modcheck.i +prodict_ora_key_moduni.i +prodict_ora_key_unique.i +prodict_ora__ora_lkg.p +prodict_ora__ora_lkz.p +prodict_ora__ora_md1.p +prodict_sec__sec-gen-key.p +prodict_user_usermenu.i +prodict_user__usrichg.p +prodict_user__usrschg.p +prodict_user__usrsdel.p +prodict__dctgues.p +prodict__dctsget.p +protools_as-partn.w +protools__procfg.w +protools__v89conv.w +seclib_OpenEdge_Security_Provider_UserTableAuthProvider.cls +serveradmin_OpenEdge_ApplicationServer_Util_convert_spring_properties.p +template_tty-dial.w +web_examples_w-cstinf.w +web_examples_w-cststa.w +workshop_objects.i +//0046 issues <-- diff --git a/resources/tree-sitter-abl.wasm b/resources/tree-sitter-abl.wasm index ddaac644..bac05f86 100644 Binary files a/resources/tree-sitter-abl.wasm and b/resources/tree-sitter-abl.wasm differ diff --git a/src/formatterFramework/enableFormatterDecorators.ts b/src/formatterFramework/enableFormatterDecorators.ts index d57e123a..a75af1e4 100644 --- a/src/formatterFramework/enableFormatterDecorators.ts +++ b/src/formatterFramework/enableFormatterDecorators.ts @@ -16,6 +16,7 @@ import { ProcedureParameterFormatter } from "../formatters/procedureParameter/Pr import { FunctionParameterFormatter } from "../formatters/functionParameter/FunctionParameterFormatter"; import { ArrayAccessFormatter } from "../formatters/arrayAccess/ArrayAccessFormatter"; import { StatementFormatter } from "../formatters/statement/StatementFormatter"; +import { VariableAssignmentFormatter } from "../formatters/variableAssignment/VariableAssignmentFormatter"; // needed just for enabling decorators. Decorators does not work if there is no usage of a class in the reachable code export function enableFormatterDecorators(): void { @@ -37,4 +38,5 @@ export function enableFormatterDecorators(): void { ArrayAccessFormatter; ExpressionFormatter; StatementFormatter; + VariableAssignmentFormatter; } diff --git a/src/formatters/arrayAccess/ArrayAccessFormatter.ts b/src/formatters/arrayAccess/ArrayAccessFormatter.ts index 84cd9e6b..29874d39 100644 --- a/src/formatters/arrayAccess/ArrayAccessFormatter.ts +++ b/src/formatters/arrayAccess/ArrayAccessFormatter.ts @@ -14,6 +14,7 @@ export class ArrayAccessFormatter extends AFormatter implements IFormatter { public static readonly formatterLabel = "arrayAccessFormatting"; private readonly settings: ArrayAccessSettings; private formattingArrayLiteral: boolean = false; + private addSpaceBeforeLeftBracket: boolean = false; public constructor(configurationManager: IConfigurationManager) { super(configurationManager); @@ -35,7 +36,16 @@ export class ArrayAccessFormatter extends AFormatter implements IFormatter { node: Readonly, fullText: Readonly ): CodeEdit | CodeEdit[] | undefined { - this.formattingArrayLiteral = node.type === SyntaxNodeType.ArrayLiteral; + if (node.type === SyntaxNodeType.ArrayLiteral) { + this.formattingArrayLiteral = true; + + if ( + node.previousSibling?.type !== SyntaxNodeType.Identifier && + node.previousNamedSibling?.type !== SyntaxNodeType.TypeTuning + ) { + this.addSpaceBeforeLeftBracket = true; + } + } const oldText = FormatterHelper.getCurrentText(node, fullText); const text = this.collectString(node, fullText); return this.getCodeEdit(node, oldText, text, fullText); @@ -56,7 +66,7 @@ export class ArrayAccessFormatter extends AFormatter implements IFormatter { let newString = ""; if (node.type === SyntaxNodeType.LeftBracket) { newString = FormatterHelper.getCurrentText(node, fullText).trim(); - if (this.formattingArrayLiteral) { + if (this.addSpaceBeforeLeftBracket) { newString = " " + newString; } } else if ( diff --git a/src/formatters/block/BlockFormatter.ts b/src/formatters/block/BlockFormatter.ts index 9bed4392..2ca6e840 100644 --- a/src/formatters/block/BlockFormatter.ts +++ b/src/formatters/block/BlockFormatter.ts @@ -61,12 +61,15 @@ export class BlockFormater extends AFormatter implements IFormatter { ); const indentationStep = this.settings.tabSize(); - let indexOfColon = -1; + let indexOfColon = node.startPosition.column; let deltaBetweenStartAndColon = 0; + let colonDelta = 0; let blockStatementsStartRows = node.children .filter((child) => { if (child.type === SyntaxNodeType.ColonKeyword) { - indexOfColon = child.startPosition.column; + // indexOfColon = child.startPosition.column; + colonDelta = + child.endPosition.column - child.startPosition.column; deltaBetweenStartAndColon = child.startPosition.row - parent.startPosition.row; return false; @@ -109,33 +112,49 @@ export class BlockFormater extends AFormatter implements IFormatter { codeLines.pop(); } - if (indexOfColon !== -1 && deltaBetweenStartAndColon === 0) { - // indexOfColon += parentIndentation; - indexOfColon -= parent.startPosition.column; - for (let i = indexOfColon + 1; i >= indexOfColon - 1; i--) { + if (indexOfColon !== -1) { + const columnIntervalStart = Math.max( + 0, + indexOfColon - parent.startPosition.column + ); + const columnIntervalEnd = Math.min( + firstLine.length - 1, + indexOfColon + parent.startPosition.column + colonDelta + ); + + let colonFound = false; + + for (let i = columnIntervalStart; i <= columnIntervalEnd; i++) { if (firstLine[i] === ":") { indexOfColon = i; + colonFound = true; break; } } - const partAfterColon = firstLine - .slice(indexOfColon + 1) - .trimStart(); - const statementWithColon = - firstLine.slice(0, indexOfColon).trimEnd() + ":"; - // If the part after the colon is not only whitespace, put it on the next line - if (partAfterColon.trim().length !== 0 && partAfterColon !== ":") { - codeLines.shift(); // pop from the start of the list - codeLines.unshift(statementWithColon, partAfterColon); - const firstBlockStatementRow = blockStatementsStartRows[0]; - blockStatementsStartRows.shift(); - blockStatementsStartRows.unshift( - firstBlockStatementRow - 1, - firstBlockStatementRow - ); - blockStatementsStartRows = blockStatementsStartRows.map( - (currentRow) => currentRow + 1 - ); + + if (colonFound) { + const partAfterColon = firstLine + .slice(indexOfColon + 1) + .trimStart(); + const statementWithColon = + firstLine.slice(0, indexOfColon).trimEnd() + ":"; + // If the part after the colon is not only whitespace, put it on the next line + if ( + partAfterColon.trim().length !== 0 && + partAfterColon !== ":" + ) { + codeLines.shift(); // pop from the start of the list + codeLines.unshift(statementWithColon, partAfterColon); + const firstBlockStatementRow = blockStatementsStartRows[0]; + blockStatementsStartRows.shift(); + blockStatementsStartRows.unshift( + firstBlockStatementRow - 1, + firstBlockStatementRow + ); + blockStatementsStartRows = blockStatementsStartRows.map( + (currentRow) => currentRow + 1 + ); + } } } diff --git a/src/formatters/expression/ExpressionFormatter.ts b/src/formatters/expression/ExpressionFormatter.ts index ed147325..394030cf 100644 --- a/src/formatters/expression/ExpressionFormatter.ts +++ b/src/formatters/expression/ExpressionFormatter.ts @@ -35,8 +35,7 @@ export class ExpressionFormatter extends AFormatter implements IFormatter { node.type === SyntaxNodeType.MultiplicativeExpression || node.type === SyntaxNodeType.UnaryExpression ) { - const parent = node.parent; - if (parent !== null && parent.type === SyntaxNodeType.WhilePhrase) { + if (this.hasWhilePhraseParent(node)) { return false; } return true; @@ -145,4 +144,13 @@ export class ExpressionFormatter extends AFormatter implements IFormatter { } return false; } + private hasWhilePhraseParent(node: Readonly): boolean { + if (node.parent === null) { + return false; + } + if (node.parent.type === SyntaxNodeType.WhilePhrase) { + return true; + } + return this.hasWhilePhraseParent(node.parent); + } } diff --git a/src/formatters/for/ForFormatter.ts b/src/formatters/for/ForFormatter.ts index 9551b253..892828e0 100644 --- a/src/formatters/for/ForFormatter.ts +++ b/src/formatters/for/ForFormatter.ts @@ -82,6 +82,11 @@ export class ForFormatter extends AFormatter implements IFormatter { let newString = ""; switch (node.type) { + case SyntaxNodeType.Label: + newString = + FormatterHelper.getCurrentText(node, fullText).trimStart() + + fullText.eolDelimiter; + break; case SyntaxNodeType.ForKeyword: newString = FormatterHelper.getCurrentText( node, diff --git a/src/formatters/variableAssignment/VariableAssignmentFormatter.ts b/src/formatters/variableAssignment/VariableAssignmentFormatter.ts new file mode 100644 index 00000000..68a8fa8b --- /dev/null +++ b/src/formatters/variableAssignment/VariableAssignmentFormatter.ts @@ -0,0 +1,75 @@ +import { SyntaxNode } from "web-tree-sitter"; +import { RegisterFormatter } from "../../formatterFramework/formatterDecorator"; +import { IFormatter } from "../../formatterFramework/IFormatter"; +import { CodeEdit } from "../../model/CodeEdit"; +import { FullText } from "../../model/FullText"; +import { AFormatter } from "../AFormatter"; +import { SyntaxNodeType } from "../../model/SyntaxNodeType"; +import { VariableDefinitionSettings } from "./VariableAssignmentSettings"; +import { IConfigurationManager } from "../../utils/IConfigurationManager"; +import { FormatterHelper } from "../../formatterFramework/FormatterHelper"; + +@RegisterFormatter +export class VariableAssignmentFormatter + extends AFormatter + implements IFormatter +{ + public static readonly formatterLabel = "variableAssignmentFormatting"; + private readonly settings: VariableDefinitionSettings; + private startOfAssignment = false; + + public constructor(configurationManager: IConfigurationManager) { + super(configurationManager); + this.settings = new VariableDefinitionSettings(configurationManager); + } + + match(node: Readonly): boolean { + if (node.type === SyntaxNodeType.Assignment) { + const parent = node.parent; + if ( + parent !== null && + parent.type === SyntaxNodeType.VariableAssignment + ) { + return true; + } + } + return false; + } + parse( + node: Readonly, + fullText: Readonly + ): CodeEdit | CodeEdit[] | undefined { + this.startOfAssignment = true; + const oldText = FormatterHelper.getCurrentText(node, fullText); + const newText = this.collectString(node, fullText); + return this.getCodeEdit(node, oldText, newText, fullText); + } + + private collectString(node: SyntaxNode, fullText: FullText): string { + let resultString = ""; + node.children.forEach((child) => { + resultString = resultString.concat(this.getString(child, fullText)); + }); + return resultString; + } + + private getString(node: SyntaxNode, fullText: FullText): string { + let newString = ""; + const text = FormatterHelper.getCurrentText(node, fullText).trim(); + switch (node.type) { + case SyntaxNodeType.Error: + newString = FormatterHelper.getCurrentText(node, fullText); + break; + default: + if (this.startOfAssignment) { + // No need for extra space at the start, simply keep the whitespace + this.startOfAssignment = false; + newString = FormatterHelper.getCurrentText(node, fullText); + } else { + newString = text.length === 0 ? "" : " " + text; + } + break; + } + return newString; + } +} diff --git a/src/formatters/variableAssignment/VariableAssignmentSettings.ts b/src/formatters/variableAssignment/VariableAssignmentSettings.ts new file mode 100644 index 00000000..17c31d63 --- /dev/null +++ b/src/formatters/variableAssignment/VariableAssignmentSettings.ts @@ -0,0 +1,8 @@ +import { ASettings } from "../ASettings"; + +export class VariableDefinitionSettings extends ASettings { + // variable definition settings + public variableDefinitionFormatting() { + return !!this.configurationManager.get("arrayAccessFormatting"); + } +} diff --git a/src/model/SyntaxNodeType.ts b/src/model/SyntaxNodeType.ts index 390938d2..40736684 100644 --- a/src/model/SyntaxNodeType.ts +++ b/src/model/SyntaxNodeType.ts @@ -5,7 +5,7 @@ export enum SyntaxNodeType { AvailableExpression = "available_expression", CaseStatement = "case_statement", - CaseCondition = "case_conditon", + CaseCondition = "case_condition", CaseBody = "case_body", CaseWhenBranch = "case_when_branch", CaseOtherwiseBranch = "case_otherwise_branch", @@ -74,6 +74,7 @@ export enum SyntaxNodeType { AccessTuning = "access_tuning", ArrayAccess = "array_access", ArrayLiteral = "array_literal", + ToPhrase = "to_phrase", Comment = "comment", Getter = "getter", Setter = "setter", diff --git a/src/utils/ConfigurationManager.ts b/src/utils/ConfigurationManager.ts index f0d86f54..b5696fab 100644 --- a/src/utils/ConfigurationManager.ts +++ b/src/utils/ConfigurationManager.ts @@ -18,6 +18,13 @@ export class ConfigurationManager implements IConfigurationManager { window.showInformationMessage( "ABL Formatter settings were changed!" ); + + const config = workspace.getConfiguration("AblFormatter"); + const expressionSetting = config.get("expressionFormatting"); + const arrayAccessSetting = config.get("arrayAccessFormatting"); + if (expressionSetting || arrayAccessSetting) { + config.update("variableAssignmentFormatting", true); + } } if (e.affectsConfiguration("abl.completion")) { this.reloadExternalConfig = true;