From fab3b8944ccf61376683c08f6bb413b3731a4882 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Fri, 3 Jan 2025 21:25:38 +0000 Subject: [PATCH 1/9] initial commit for issue 77 --- src/Formatter.spec.ts | 2 + src/Formatter.ts | 1 + src/constants.ts | 8 ++- src/formatters/IndentFormatter.spec.ts | 71 ++++++++++++++++++++++-- src/formatters/IndentFormatter.ts | 75 +++++++++++++++++++++++++- 5 files changed, 149 insertions(+), 8 deletions(-) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 6d6ed60..2dc7cdb 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -7,6 +7,8 @@ import { SourceMapConsumer } from 'source-map'; import { undent } from 'undent'; describe('Formatter', () => { + + console.log(`Formatter tests`); let formatter: Formatter; beforeEach(() => { diff --git a/src/Formatter.ts b/src/Formatter.ts index eec4a0b..cf597de 100644 --- a/src/Formatter.ts +++ b/src/Formatter.ts @@ -62,6 +62,7 @@ export class Formatter { * @returns an object with property `code` holding the formatted code, and `map` holding the source map. */ public formatWithSourceMap(inputText: string, sourcePath: string, formattingOptions?: FormattingOptions) { + console.log('formatWithSourceMap', inputText, sourcePath, formattingOptions); let tokens = this.getFormattedTokens(inputText, formattingOptions); let chunks = [] as Array; for (let token of tokens) { diff --git a/src/constants.ts b/src/constants.ts index ad7f36a..6de56bf 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -103,7 +103,9 @@ export let IndentSpacerTokenKinds = [ export let IgnoreIndentSpacerByParentTokenKind = new Map([ [TokenKind.Interface, [ TokenKind.Sub, - TokenKind.Function + TokenKind.Function, + TokenKind.Enum, + TokenKind.Class ]] ]); @@ -124,7 +126,9 @@ export let OutdentSpacerTokenKinds = [ TokenKind.EndInterface, TokenKind.EndNamespace, TokenKind.EndTry, - TokenKind.EndEnum + TokenKind.EndEnum, + TokenKind.Class, + TokenKind.Enum ]; /** diff --git a/src/formatters/IndentFormatter.spec.ts b/src/formatters/IndentFormatter.spec.ts index c145f91..ab32541 100644 --- a/src/formatters/IndentFormatter.spec.ts +++ b/src/formatters/IndentFormatter.spec.ts @@ -1,14 +1,76 @@ import { expect } from 'chai'; -import { expectTokens, lex } from '../testHelpers.spec'; -import { IndentFormatter } from './IndentFormatter'; +// import { expectTokens, lex } from '../testHelpers.spec'; +//import { IndentFormatter } from './IndentFormatter'; describe('IndentFormatter', () => { - let formatter: IndentFormatter; + //let formatter: IndentFormatter; beforeEach(() => { - formatter = new IndentFormatter(); + //formatter = new IndentFormatter(); }); + describe('nullTest', () => { + it('null test', () => { + expect(null).to.eql(null); + }); + }); + + /* + describe('ensureTokenIndentation', () => { + it('does nothing for empty or invalid tokens', () => { + expect( + formatter['ensureTokenIndentation'](null as any, 0) + ).to.eql(null); + expect( + formatter['ensureTokenIndentation']([], 0) + ).to.eql([]); + }); + + it('handles negative tab size', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(`\tspeak()`), -2), + ['', 'speak', '(', ')'] + ); + }); + + it('does not add whitespace token if no indentation is needed', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(`speak()`), 0), + ['speak', '(', ')'] + ); + }); + + it('dedupes side-by-side whitespace tokens into one', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(` \t speak()`), 1), + [' ', 'speak', '(', ')'] + ); + }); + + it('adds whitespace when missing', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(`speak()`), 1), + [' ', 'speak', '(', ')'] + ); + }); + + it('adds correct indentation when missing', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(`speak()`), 3), + [' ', 'speak', '(', ')'] + ); + }); + + it('uses supplied indentation char when provided', () => { + expectTokens( + formatter['ensureTokenIndentation'](lex(`speak()`), 3, '\t'), + ['\t\t\t', 'speak', '(', ')'] + ); + }); + }); + + + /* describe('ensureTokenIndentation', () => { it('does nothing for empty or invalid tokens', () => { expect( @@ -77,4 +139,5 @@ describe('IndentFormatter', () => { ); }); }); + */ }); diff --git a/src/formatters/IndentFormatter.ts b/src/formatters/IndentFormatter.ts index cc54164..103f8bd 100644 --- a/src/formatters/IndentFormatter.ts +++ b/src/formatters/IndentFormatter.ts @@ -5,11 +5,17 @@ import { OutdentSpacerTokenKinds, IndentSpacerTokenKinds, CallableKeywordTokenKi import type { FormattingOptions } from '../FormattingOptions'; import { util } from '../util'; +console.log('IndentFormatter.ts'); + export class IndentFormatter { + /** * Handle indentation for an array of tokens */ public format(tokens: Token[], options: FormattingOptions, parser: Parser) { + + console.log('IndentFormatter.format'); + // The text used for each tab let tabText = options.indentStyle === 'tabs' ? '\t' : ' '.repeat(options.indentSpaceCount ?? DEFAULT_INDENT_SPACE_COUNT); @@ -29,20 +35,24 @@ export class IndentFormatter { const { currentLineOffset, nextLineOffset } = this.processLine(lineTokens, tokens, ifStatements, parentIndentTokenKinds); //uncomment the next line to debug indent/outdent issues - // console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); + console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); //compute the current line's tab count (default to 0 if we somehow went negative) let currentLineTabCount = Math.max(globalTabCount + currentLineOffset, 0); + console.log('currentLineTabCount', currentLineTabCount); //update the offset for the next line (default to 0 if we somehow went negative) globalTabCount = Math.max(globalTabCount + nextLineOffset, 0); + console.log('globalTabCount', globalTabCount); this.ensureTokenIndentation(lineTokens, currentLineTabCount, tabText); this.trimWhitespaceOnlyLines(lineTokens); //push these tokens to the result list + console.log('lineTokens', lineTokens.map(x => x.text).join('')); result.push(...lineTokens); } + console.log('result', result.map(x => x.text).join('')); return result; } @@ -52,14 +62,29 @@ export class IndentFormatter { ifStatements: Map, parentIndentTokenKinds: TokenKind[] ): { currentLineOffset: number; nextLineOffset: number } { + + console.log(`\n\nIndentFormatter.processLine`); + + console.log('parentIndentTokenKinds at start of line processing ', parentIndentTokenKinds.map(x => TokenKind[x])); + const getParentIndentTokenKind = () => { - return parentIndentTokenKinds.length > 0 ? parentIndentTokenKinds[parentIndentTokenKinds.length - 1] : undefined; + const parentIndentTokenKind = parentIndentTokenKinds.length > 0 ? parentIndentTokenKinds[parentIndentTokenKinds.length - 1] : undefined; + console.log('parentIndentTokenKinds at end of line processing', parentIndentTokenKinds.map(x => TokenKind[x])); + console.log('parentIndentTokenKind', parentIndentTokenKind); + return parentIndentTokenKind; }; let currentLineOffset = 0; let nextLineOffset = 0; let foundIndentorThisLine = false; + console.log(`\nlineTokens`, lineTokens.map(x => x.text).join('')); + console.log(`lineTokens.length: ${lineTokens.length}`); + lineTokens.forEach((token, index) => { + console.log(`token `, index, TokenKind[token.kind]); + }); + console.log('parentIndentTokenKinds', parentIndentTokenKinds.map(x => TokenKind[x])); + for (let i = 0; i < lineTokens.length; i++) { let token = lineTokens[i]; let previousNonWhitespaceToken = util.getPreviousNonWhitespaceToken(lineTokens, i); @@ -111,8 +136,31 @@ export class IndentFormatter { } // check for specifically mentioned tokens to NOT indent + console.log(`checking for items not to indent`); const parentIndentTokenKind = getParentIndentTokenKind(); + console.log('parentIndentTokenKind at point of decision is ', parentIndentTokenKind); + console.log('token.kind', TokenKind[token.kind]); + + const parentIndentTokenKindsContainsSubOrFunction = parentIndentTokenKinds.includes(TokenKind.Sub) || parentIndentTokenKinds.includes(TokenKind.Function); + + const tokenKindIsClass = token.kind === TokenKind.Class; + const tokenKindIsEnum = token.kind === TokenKind.Enum; + const tokenKindIsInterface = token.kind === TokenKind.Interface; + const tokenKindIsNamespace = token.kind === TokenKind.Namespace; + const tokenKindIsTry = token.kind === TokenKind.Try; + + // don't indent class or enum if parent is sub or function + const preventIndent = parentIndentTokenKindsContainsSubOrFunction && (tokenKindIsClass || tokenKindIsEnum || tokenKindIsInterface || tokenKindIsNamespace || tokenKindIsTry); + + if (preventIndent) { + console.log('parentIndentTokenKindsContainsSubOrFunction && tokenKindIsClass'); + // don't indent class if parent is sub or function + continue; + } + if (parentIndentTokenKind && IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)?.includes(token.kind)) { + console.log('IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)'); + console.log(IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)?.includes(token.kind)); // This particular token should not be indented because it is listed in the ignore group for its parent continue; } @@ -189,6 +237,8 @@ export class IndentFormatter { // tabCount--; // } } + console.log('currentLineOffset', currentLineOffset); + console.log('nextLineOffset', nextLineOffset); return { currentLineOffset: currentLineOffset, nextLineOffset: nextLineOffset @@ -202,6 +252,9 @@ export class IndentFormatter { * @param tabText the string to use for each tab. For tabs, this is "\t", for spaces it would be something like " " or " " */ private ensureTokenIndentation(tokens: Token[], tabCount: number, tabText = ' '): Token[] { + + console.log('IndentFormatter.ensureTokenIndentation'); + //do nothing if we have an empty tokens list if (!tokens || tokens.length === 0) { return tokens; @@ -232,6 +285,9 @@ export class IndentFormatter { * This should only be called once the line has been whitespace-deduped */ private trimWhitespaceOnlyLines(tokens: Token[]) { + + console.log('IndentFormatter.trimWhitespaceOnlyLines'); + //if the first token is whitespace, and the next is EOL or EOF if ( tokens[0].kind === TokenKind.Whitespace && @@ -248,6 +304,8 @@ export class IndentFormatter { */ private getAllIfStatements(parser: Parser) { + console.log('IndentFormatter.getAllIfStatements'); + const ifStatements = new Map(); parser.ast.walk(createVisitor({ @@ -264,6 +322,9 @@ export class IndentFormatter { * Split the tokens by newline (including the newline or EOF as the last token in that array) */ private splitTokensByLine(tokens: Token[]) { + + console.log('IndentFormatter.splitTokensByLine'); + const result: Array = []; let line: Token[] = []; for (let token of tokens) { @@ -284,6 +345,9 @@ export class IndentFormatter { * Then return the endIf token if it exists */ private getEndIfToken(ifStatement: IfStatement | undefined) { + + console.log('IndentFormatter.getEndIfToken'); + if (isIfStatement(ifStatement)) { while (true) { if (isIfStatement(ifStatement.elseBranch)) { @@ -300,6 +364,9 @@ export class IndentFormatter { * Given a kind like `}` or `]`, walk backwards until we find its match */ public getOpeningToken(tokens: Token[], currentIndex: number, openKind: TokenKind, closeKind: TokenKind) { + + console.log('IndentFormatter.getOpeningToken'); + let openCount = 0; for (let i = currentIndex; i >= 0; i--) { let token = tokens[i]; @@ -318,6 +385,10 @@ export class IndentFormatter { * Determines if this is an outdent token */ public isOutdentToken(token: Token, nextNonWhitespaceToken?: Token) { + + console.log('IndentFormatter.isOutdentToken'); + + //this is a temporary fix for broken indentation for brighterscript ternary operations. const isSymbol = [TokenKind.RightCurlyBrace, TokenKind.RightSquareBracket].includes(token.kind); if ( From 64bcfe9ce9ec1edb0188be77d1ada2f04c245ba9 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Fri, 3 Jan 2025 21:27:17 +0000 Subject: [PATCH 2/9] Remove unused token kinds from OutdentSpacerTokenKinds --- src/constants.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 6de56bf..d11575b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -126,9 +126,7 @@ export let OutdentSpacerTokenKinds = [ TokenKind.EndInterface, TokenKind.EndNamespace, TokenKind.EndTry, - TokenKind.EndEnum, - TokenKind.Class, - TokenKind.Enum + TokenKind.EndEnum ]; /** From 4a5704c3143feae63db2a457cc14c1fc99daf878 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Fri, 3 Jan 2025 21:28:09 +0000 Subject: [PATCH 3/9] Refactor IndentFormatter tests to uncomment and enable relevant code --- src/formatters/IndentFormatter.spec.ts | 71 ++------------------------ 1 file changed, 4 insertions(+), 67 deletions(-) diff --git a/src/formatters/IndentFormatter.spec.ts b/src/formatters/IndentFormatter.spec.ts index ab32541..c145f91 100644 --- a/src/formatters/IndentFormatter.spec.ts +++ b/src/formatters/IndentFormatter.spec.ts @@ -1,76 +1,14 @@ import { expect } from 'chai'; -// import { expectTokens, lex } from '../testHelpers.spec'; -//import { IndentFormatter } from './IndentFormatter'; +import { expectTokens, lex } from '../testHelpers.spec'; +import { IndentFormatter } from './IndentFormatter'; describe('IndentFormatter', () => { - //let formatter: IndentFormatter; + let formatter: IndentFormatter; beforeEach(() => { - //formatter = new IndentFormatter(); + formatter = new IndentFormatter(); }); - describe('nullTest', () => { - it('null test', () => { - expect(null).to.eql(null); - }); - }); - - /* - describe('ensureTokenIndentation', () => { - it('does nothing for empty or invalid tokens', () => { - expect( - formatter['ensureTokenIndentation'](null as any, 0) - ).to.eql(null); - expect( - formatter['ensureTokenIndentation']([], 0) - ).to.eql([]); - }); - - it('handles negative tab size', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(`\tspeak()`), -2), - ['', 'speak', '(', ')'] - ); - }); - - it('does not add whitespace token if no indentation is needed', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(`speak()`), 0), - ['speak', '(', ')'] - ); - }); - - it('dedupes side-by-side whitespace tokens into one', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(` \t speak()`), 1), - [' ', 'speak', '(', ')'] - ); - }); - - it('adds whitespace when missing', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(`speak()`), 1), - [' ', 'speak', '(', ')'] - ); - }); - - it('adds correct indentation when missing', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(`speak()`), 3), - [' ', 'speak', '(', ')'] - ); - }); - - it('uses supplied indentation char when provided', () => { - expectTokens( - formatter['ensureTokenIndentation'](lex(`speak()`), 3, '\t'), - ['\t\t\t', 'speak', '(', ')'] - ); - }); - }); - - - /* describe('ensureTokenIndentation', () => { it('does nothing for empty or invalid tokens', () => { expect( @@ -139,5 +77,4 @@ describe('IndentFormatter', () => { ); }); }); - */ }); From 88272b23d564b30435f98c62d9be78e556bde632 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Sun, 5 Jan 2025 22:16:51 +0000 Subject: [PATCH 4/9] remove console.logs and tidy up code --- src/formatters/IndentFormatter.ts | 57 ++----------------------------- 1 file changed, 2 insertions(+), 55 deletions(-) diff --git a/src/formatters/IndentFormatter.ts b/src/formatters/IndentFormatter.ts index 103f8bd..62cf9d9 100644 --- a/src/formatters/IndentFormatter.ts +++ b/src/formatters/IndentFormatter.ts @@ -5,17 +5,12 @@ import { OutdentSpacerTokenKinds, IndentSpacerTokenKinds, CallableKeywordTokenKi import type { FormattingOptions } from '../FormattingOptions'; import { util } from '../util'; -console.log('IndentFormatter.ts'); - export class IndentFormatter { - /** * Handle indentation for an array of tokens */ public format(tokens: Token[], options: FormattingOptions, parser: Parser) { - console.log('IndentFormatter.format'); - // The text used for each tab let tabText = options.indentStyle === 'tabs' ? '\t' : ' '.repeat(options.indentSpaceCount ?? DEFAULT_INDENT_SPACE_COUNT); @@ -35,24 +30,20 @@ export class IndentFormatter { const { currentLineOffset, nextLineOffset } = this.processLine(lineTokens, tokens, ifStatements, parentIndentTokenKinds); //uncomment the next line to debug indent/outdent issues - console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); + //console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); //compute the current line's tab count (default to 0 if we somehow went negative) let currentLineTabCount = Math.max(globalTabCount + currentLineOffset, 0); - console.log('currentLineTabCount', currentLineTabCount); //update the offset for the next line (default to 0 if we somehow went negative) globalTabCount = Math.max(globalTabCount + nextLineOffset, 0); - console.log('globalTabCount', globalTabCount); this.ensureTokenIndentation(lineTokens, currentLineTabCount, tabText); this.trimWhitespaceOnlyLines(lineTokens); //push these tokens to the result list - console.log('lineTokens', lineTokens.map(x => x.text).join('')); result.push(...lineTokens); } - console.log('result', result.map(x => x.text).join('')); return result; } @@ -63,14 +54,8 @@ export class IndentFormatter { parentIndentTokenKinds: TokenKind[] ): { currentLineOffset: number; nextLineOffset: number } { - console.log(`\n\nIndentFormatter.processLine`); - - console.log('parentIndentTokenKinds at start of line processing ', parentIndentTokenKinds.map(x => TokenKind[x])); - const getParentIndentTokenKind = () => { const parentIndentTokenKind = parentIndentTokenKinds.length > 0 ? parentIndentTokenKinds[parentIndentTokenKinds.length - 1] : undefined; - console.log('parentIndentTokenKinds at end of line processing', parentIndentTokenKinds.map(x => TokenKind[x])); - console.log('parentIndentTokenKind', parentIndentTokenKind); return parentIndentTokenKind; }; @@ -78,13 +63,6 @@ export class IndentFormatter { let nextLineOffset = 0; let foundIndentorThisLine = false; - console.log(`\nlineTokens`, lineTokens.map(x => x.text).join('')); - console.log(`lineTokens.length: ${lineTokens.length}`); - lineTokens.forEach((token, index) => { - console.log(`token `, index, TokenKind[token.kind]); - }); - console.log('parentIndentTokenKinds', parentIndentTokenKinds.map(x => TokenKind[x])); - for (let i = 0; i < lineTokens.length; i++) { let token = lineTokens[i]; let previousNonWhitespaceToken = util.getPreviousNonWhitespaceToken(lineTokens, i); @@ -136,11 +114,7 @@ export class IndentFormatter { } // check for specifically mentioned tokens to NOT indent - console.log(`checking for items not to indent`); const parentIndentTokenKind = getParentIndentTokenKind(); - console.log('parentIndentTokenKind at point of decision is ', parentIndentTokenKind); - console.log('token.kind', TokenKind[token.kind]); - const parentIndentTokenKindsContainsSubOrFunction = parentIndentTokenKinds.includes(TokenKind.Sub) || parentIndentTokenKinds.includes(TokenKind.Function); const tokenKindIsClass = token.kind === TokenKind.Class; @@ -149,18 +123,13 @@ export class IndentFormatter { const tokenKindIsNamespace = token.kind === TokenKind.Namespace; const tokenKindIsTry = token.kind === TokenKind.Try; - // don't indent class or enum if parent is sub or function + // dont indent if parent is sub or function and this is a class, enum, interface, namespace, or try const preventIndent = parentIndentTokenKindsContainsSubOrFunction && (tokenKindIsClass || tokenKindIsEnum || tokenKindIsInterface || tokenKindIsNamespace || tokenKindIsTry); - if (preventIndent) { - console.log('parentIndentTokenKindsContainsSubOrFunction && tokenKindIsClass'); - // don't indent class if parent is sub or function continue; } if (parentIndentTokenKind && IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)?.includes(token.kind)) { - console.log('IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)'); - console.log(IgnoreIndentSpacerByParentTokenKind.get(parentIndentTokenKind)?.includes(token.kind)); // This particular token should not be indented because it is listed in the ignore group for its parent continue; } @@ -169,7 +138,6 @@ export class IndentFormatter { foundIndentorThisLine = true; parentIndentTokenKinds.push(token.kind); - //don't double indent if this is `[[...\n...]]` or `[{...\n...}]` if ( //is open square @@ -237,8 +205,6 @@ export class IndentFormatter { // tabCount--; // } } - console.log('currentLineOffset', currentLineOffset); - console.log('nextLineOffset', nextLineOffset); return { currentLineOffset: currentLineOffset, nextLineOffset: nextLineOffset @@ -253,8 +219,6 @@ export class IndentFormatter { */ private ensureTokenIndentation(tokens: Token[], tabCount: number, tabText = ' '): Token[] { - console.log('IndentFormatter.ensureTokenIndentation'); - //do nothing if we have an empty tokens list if (!tokens || tokens.length === 0) { return tokens; @@ -286,8 +250,6 @@ export class IndentFormatter { */ private trimWhitespaceOnlyLines(tokens: Token[]) { - console.log('IndentFormatter.trimWhitespaceOnlyLines'); - //if the first token is whitespace, and the next is EOL or EOF if ( tokens[0].kind === TokenKind.Whitespace && @@ -304,8 +266,6 @@ export class IndentFormatter { */ private getAllIfStatements(parser: Parser) { - console.log('IndentFormatter.getAllIfStatements'); - const ifStatements = new Map(); parser.ast.walk(createVisitor({ @@ -322,9 +282,6 @@ export class IndentFormatter { * Split the tokens by newline (including the newline or EOF as the last token in that array) */ private splitTokensByLine(tokens: Token[]) { - - console.log('IndentFormatter.splitTokensByLine'); - const result: Array = []; let line: Token[] = []; for (let token of tokens) { @@ -345,9 +302,6 @@ export class IndentFormatter { * Then return the endIf token if it exists */ private getEndIfToken(ifStatement: IfStatement | undefined) { - - console.log('IndentFormatter.getEndIfToken'); - if (isIfStatement(ifStatement)) { while (true) { if (isIfStatement(ifStatement.elseBranch)) { @@ -364,9 +318,6 @@ export class IndentFormatter { * Given a kind like `}` or `]`, walk backwards until we find its match */ public getOpeningToken(tokens: Token[], currentIndex: number, openKind: TokenKind, closeKind: TokenKind) { - - console.log('IndentFormatter.getOpeningToken'); - let openCount = 0; for (let i = currentIndex; i >= 0; i--) { let token = tokens[i]; @@ -385,10 +336,6 @@ export class IndentFormatter { * Determines if this is an outdent token */ public isOutdentToken(token: Token, nextNonWhitespaceToken?: Token) { - - console.log('IndentFormatter.isOutdentToken'); - - //this is a temporary fix for broken indentation for brighterscript ternary operations. const isSymbol = [TokenKind.RightCurlyBrace, TokenKind.RightSquareBracket].includes(token.kind); if ( From c86ed13d533052083ac63a976c65c8510a337a70 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Sun, 5 Jan 2025 22:22:54 +0000 Subject: [PATCH 5/9] remove spacing --- src/Formatter.spec.ts | 2 -- src/Formatter.ts | 1 - src/formatters/IndentFormatter.ts | 6 +----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 2dc7cdb..6d6ed60 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -7,8 +7,6 @@ import { SourceMapConsumer } from 'source-map'; import { undent } from 'undent'; describe('Formatter', () => { - - console.log(`Formatter tests`); let formatter: Formatter; beforeEach(() => { diff --git a/src/Formatter.ts b/src/Formatter.ts index cf597de..eec4a0b 100644 --- a/src/Formatter.ts +++ b/src/Formatter.ts @@ -62,7 +62,6 @@ export class Formatter { * @returns an object with property `code` holding the formatted code, and `map` holding the source map. */ public formatWithSourceMap(inputText: string, sourcePath: string, formattingOptions?: FormattingOptions) { - console.log('formatWithSourceMap', inputText, sourcePath, formattingOptions); let tokens = this.getFormattedTokens(inputText, formattingOptions); let chunks = [] as Array; for (let token of tokens) { diff --git a/src/formatters/IndentFormatter.ts b/src/formatters/IndentFormatter.ts index 62cf9d9..dc005aa 100644 --- a/src/formatters/IndentFormatter.ts +++ b/src/formatters/IndentFormatter.ts @@ -10,7 +10,6 @@ export class IndentFormatter { * Handle indentation for an array of tokens */ public format(tokens: Token[], options: FormattingOptions, parser: Parser) { - // The text used for each tab let tabText = options.indentStyle === 'tabs' ? '\t' : ' '.repeat(options.indentSpaceCount ?? DEFAULT_INDENT_SPACE_COUNT); @@ -30,7 +29,7 @@ export class IndentFormatter { const { currentLineOffset, nextLineOffset } = this.processLine(lineTokens, tokens, ifStatements, parentIndentTokenKinds); //uncomment the next line to debug indent/outdent issues - //console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); + // console.log(currentLineOffset.toString().padStart(3, ' '), nextLineOffset.toString().padStart(3, ' '), lineTokens.map(x => x.text).join('').replace(/\r?\n/, '').replace(/^\s*/, '')); //compute the current line's tab count (default to 0 if we somehow went negative) let currentLineTabCount = Math.max(globalTabCount + currentLineOffset, 0); @@ -53,7 +52,6 @@ export class IndentFormatter { ifStatements: Map, parentIndentTokenKinds: TokenKind[] ): { currentLineOffset: number; nextLineOffset: number } { - const getParentIndentTokenKind = () => { const parentIndentTokenKind = parentIndentTokenKinds.length > 0 ? parentIndentTokenKinds[parentIndentTokenKinds.length - 1] : undefined; return parentIndentTokenKind; @@ -218,7 +216,6 @@ export class IndentFormatter { * @param tabText the string to use for each tab. For tabs, this is "\t", for spaces it would be something like " " or " " */ private ensureTokenIndentation(tokens: Token[], tabCount: number, tabText = ' '): Token[] { - //do nothing if we have an empty tokens list if (!tokens || tokens.length === 0) { return tokens; @@ -249,7 +246,6 @@ export class IndentFormatter { * This should only be called once the line has been whitespace-deduped */ private trimWhitespaceOnlyLines(tokens: Token[]) { - //if the first token is whitespace, and the next is EOL or EOF if ( tokens[0].kind === TokenKind.Whitespace && From 7e8ae0ce0bdcdc0b5ff2d3933ad75707f6583872 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Fri, 10 Jan 2025 09:20:42 +0000 Subject: [PATCH 6/9] add tests for function class and function enum --- src/Formatter.spec.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 6d6ed60..6d12c37 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -935,6 +935,38 @@ end sub`; ); }); + it('correctly indents function class modifiers', () => { + expect(formatter.format( + 'function class()\nend function' + )).to.equal( + 'function class()\nend function' + ); + }); + + it('correctly indents function class modifiers with print statement', () => { + expect(formatter.format( + 'function class()\nprint "hello"\nend function' + )).to.equal( + 'function class()\n print "hello"\nend function' + ); + }); + + it('correctly indents function enum modifiers', () => { + expect(formatter.format( + 'function enum()\nend function' + )).to.equal( + 'function enum()\nend function' + ); + }); + + it('correctly indents function enum modifiers with print statement', () => { + expect(formatter.format( + 'function enum()\nprint "hello"\nend function' + )).to.equal( + 'function enum()\n print "hello"\nend function' + ); + }); + it('trims empty lines', () => { expect(formatter.format(`sub a()\n \nend sub`)).to.equal(`sub a()\n\nend sub`); }); From 4794157beeb6a446e8c2c4bbb303740e4c5abfa7 Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Wed, 22 Jan 2025 14:09:24 -0500 Subject: [PATCH 7/9] Add failing test --- src/Formatter.spec.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 6d12c37..051a035 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -967,6 +967,28 @@ end sub`; ); }); + it('correctly indents function with try having contents', () => { + expect(formatter.format(` + function try + try + print "hello" + catch e + print "world" + end try + print "done" + end function + `)).to.equal(undent` + function try + try + print "hello" + catch e + print "world" + end try + print "done" + end function + `); + }); + it('trims empty lines', () => { expect(formatter.format(`sub a()\n \nend sub`)).to.equal(`sub a()\n\nend sub`); }); From 9d78b1781ad5addc7b5fe71bebaf397b363d4260 Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Tue, 28 Jan 2025 10:33:09 +0000 Subject: [PATCH 8/9] add tests and passing code for Try ... Catch blocks --- src/Formatter.spec.ts | 21 +++++++++++++++++---- src/formatters/IndentFormatter.ts | 5 ++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 051a035..78c65fc 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -74,7 +74,7 @@ describe('Formatter', () => { end try `); }); - + it('properly indents foreach loops', () => { formatEqual( `for each item in collection\n name = true\nend for` @@ -966,9 +966,21 @@ end sub`; 'function enum()\n print "hello"\nend function' ); }); + it('property indents try catch statements', () => { + formatEqualTrim(` + function try() + try + print "hello" + catch e + print "caught" + end try + print "hello" + end function + `); + }); it('correctly indents function with try having contents', () => { - expect(formatter.format(` + expect(formatter.format(undent` function try try print "hello" @@ -976,7 +988,7 @@ end sub`; print "world" end try print "done" - end function + end function `)).to.equal(undent` function try try @@ -985,7 +997,7 @@ end sub`; print "world" end try print "done" - end function + end function `); }); @@ -1605,6 +1617,7 @@ end sub`; expect(format(['exitwhile', TokenKind.ExitWhile])).to.equal('exit while'); expect(format(['exitfor', TokenKind.ExitFor])).to.equal('exit for'); expect(format(['endfor', TokenKind.EndFor])).to.equal('end for'); + expect(format(['endtry', TokenKind.EndTry])).to.equal('end try'); expect(formatter.format( `sub add()\n if true then\n a=1\n elseif true then\n a=1\n endif\nendsub`, diff --git a/src/formatters/IndentFormatter.ts b/src/formatters/IndentFormatter.ts index dc005aa..5980cdb 100644 --- a/src/formatters/IndentFormatter.ts +++ b/src/formatters/IndentFormatter.ts @@ -124,6 +124,9 @@ export class IndentFormatter { // dont indent if parent is sub or function and this is a class, enum, interface, namespace, or try const preventIndent = parentIndentTokenKindsContainsSubOrFunction && (tokenKindIsClass || tokenKindIsEnum || tokenKindIsInterface || tokenKindIsNamespace || tokenKindIsTry); if (preventIndent) { + if (tokenKindIsTry && (lineTokens.length === 2 || lineTokens.length === 3)) { + nextLineOffset++; + } continue; } @@ -158,7 +161,7 @@ export class IndentFormatter { } else if (this.isOutdentToken(token, nextNonWhitespaceToken)) { //do not un-indent if this is a `next` or `endclass` token preceeded by a period if ( - [TokenKind.Next, TokenKind.EndClass, TokenKind.Namespace, TokenKind.EndNamespace].includes(token.kind) && + [TokenKind.Next, TokenKind.EndClass, TokenKind.Namespace, TokenKind.EndNamespace, TokenKind.Catch, TokenKind.EndTry].includes(token.kind) && previousNonWhitespaceToken && previousNonWhitespaceToken.kind === TokenKind.Dot ) { continue; From 5c87e0f6bf8a7ddee09c681d08140ac1e3b9db8d Mon Sep 17 00:00:00 2001 From: philanderson888 Date: Tue, 28 Jan 2025 10:36:49 +0000 Subject: [PATCH 9/9] remove trailing space --- src/Formatter.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formatter.spec.ts b/src/Formatter.spec.ts index 78c65fc..9f33cd6 100644 --- a/src/Formatter.spec.ts +++ b/src/Formatter.spec.ts @@ -74,7 +74,7 @@ describe('Formatter', () => { end try `); }); - + it('properly indents foreach loops', () => { formatEqual( `for each item in collection\n name = true\nend for`