diff --git a/package.json b/package.json index d1af960..54cdf43 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,11 @@ "type": "boolean", "default": false, "description": "When creating a hanging indent, do not put the closing bracket on its own line." + }, + "pythonIndent.closeHangingIndent": { + "type": "boolean", + "default": false, + "description": "Close a hanging indent when pressing Enter right before the closing bracket." } } } diff --git a/src/indent.ts b/src/indent.ts index 89e4d28..5831b88 100644 --- a/src/indent.ts +++ b/src/indent.ts @@ -29,14 +29,20 @@ export function newlineAndIndent( let toInsert = '\n'; try { + // TODO: Is this if test necessary? The keybindings in package.json already check this. if (textEditor.document.languageId === 'python') { const lines = textEditor.document.getText( new vscode.Range(0, 0, position.line, position.character)).split("\n"); + const remainder = textEditor.document.getText( + new vscode.Range(position.line, position.character, position.line, currentLine.length)).trim(); + // Tell editsToMake to add a dedent if the curser is right before the closing bracket for a hanging indent. + const dedentIfHanging = "]})".includes(remainder[0]) && settings.closeHangingIndent; const edits = editsToMake( lines, currentLine, tabSize, position.line, position.character, settings.trimLinesWithOnlyWhitespace, - settings.keepHangingBracketOnLine); + settings.keepHangingBracketOnLine, + dedentIfHanging); toInsert = edits.insert; edits.deletes.forEach(range => { edit.delete(range); }); hanging = edits.hanging; @@ -64,9 +70,10 @@ export function editsToMake( lineNum: number, charNum: number, trimLinesWithOnlyWhitespace: boolean, - keepHangingBracketOnLine: boolean + keepHangingBracketOnLine: boolean, + dedentIfHanging?: boolean ): { insert: string; deletes: vscode.Range[]; hanging: Hanging } { - let { nextIndentationLevel: indent, parseOutput: parseOut } = indentationInfo(lines, tabSize); + let { nextIndentationLevel: indent, parseOutput: parseOut } = indentationInfo(lines, tabSize, dedentIfHanging); let deletes: vscode.Range[] = []; // If cursor has whitespace to the right, followed by non-whitespace, diff --git a/src/parser.ts b/src/parser.ts index 02c2227..7dababb 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -20,7 +20,7 @@ export function indentationLevel(line: string): number { return line.search(/\S|$/); } -function nextIndentationLevel(parseOutput: IParseOutput, lines: string[], tabSize: number): number { +function nextIndentationLevel(parseOutput: IParseOutput, lines: string[], tabSize: number, dedentIfHanging?: boolean): number { const row = lines.length - 1; // openBracketStack: A stack of [row, col] pairs describing where open brackets are // lastClosedRow: Either empty, or an array [rowOpen, rowClose] describing the rows @@ -85,7 +85,11 @@ function nextIndentationLevel(parseOutput: IParseOutput, lines: string[], tabSiz // Thus, nothing has happened that could have changed the // indentation level since the previous line, so // we should use whatever indent we are given. - return indentationLevel(lines[row]); + indentColumn = indentationLevel(lines[row]); + if (dedentIfHanging) { + // We're in a hanging indent, so we obey dedentIfHanging. + indentColumn -= tabSize; + } } else if (justClosedBracket && closedBracketOpenedAfterLineWithCurrentOpen) { // A bracket that was opened after the most recent open // bracket was closed on the line we just finished typing. @@ -108,6 +112,10 @@ function nextIndentationLevel(parseOutput: IParseOutput, lines: string[], tabSiz // before the "9", because it would try to match it up with the // open bracket instead of using the hanging indent. indentColumn = indentationLevel(lines[last_closed_info.open]); + if (dedentIfHanging) { + // We're in a hanging indent, so we obey dedentIfHanging. + indentColumn -= tabSize; + } } else { // lastOpenBracketLocation[1] is the column where the bracket was, // so need to bump up the indentation by one @@ -117,9 +125,9 @@ function nextIndentationLevel(parseOutput: IParseOutput, lines: string[], tabSiz return indentColumn; } -export function indentationInfo(lines: string[], tabSize: number): { nextIndentationLevel: number; parseOutput: IParseOutput } { +export function indentationInfo(lines: string[], tabSize: number, dedentIfHanging?: boolean): { nextIndentationLevel: number; parseOutput: IParseOutput } { const parseOutput = parse_lines(lines); - const nextIndent = nextIndentationLevel(parseOutput, lines, tabSize); + const nextIndent = nextIndentationLevel(parseOutput, lines, tabSize, dedentIfHanging); return { nextIndentationLevel: nextIndent, parseOutput }; } diff --git a/src/test/suite/indent.test.ts b/src/test/suite/indent.test.ts index cf95247..7235e09 100644 --- a/src/test/suite/indent.test.ts +++ b/src/test/suite/indent.test.ts @@ -382,6 +382,7 @@ def f(): ] ]; simpleCases.forEach((inputOutput, index) => { + // TODO: add tests for new closeHangingIndent option. Probbaly other tests need edits, too. let paramGrid: [boolean, boolean][] = [ [false, false], [false, true], [true, false], [true, true]]; test("simple case # " + (index + 1).toString(), async () => {