Skip to content

Commit f5b197f

Browse files
committed
#432
1 parent b96578d commit f5b197f

4 files changed

Lines changed: 116 additions & 79 deletions

File tree

src/client/typeFormatters/blockFormatProvider.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,65 @@
11
import { OnTypeFormattingEditProvider, FormattingOptions, TextEdit, CancellationToken, TextDocument } from 'vscode';
22
import { Position } from 'vscode';
3-
import { CodeBlockFormatProvider } from './contracts';
4-
import { ElseFormatProvider } from './elseFormatProvider';
3+
import { CodeBlockFormatProvider } from './codeBlockFormatProvider';
4+
import { IF_REGEX, ELIF_REGEX, ELSE_REGEX, FOR_IN_REGEX, ASYNC_FOR_IN_REGEX, WHILE_REGEX } from './contracts';
5+
import { TRY_REGEX, FINALLY_REGEX, EXCEPT_REGEX } from './contracts';
6+
import { DEF_REGEX, ASYNC_DEF_REGEX, CLASS_REGEX } from './contracts';
57

68
export class BlockFormatProviders implements OnTypeFormattingEditProvider {
79
private providers: CodeBlockFormatProvider[];
810
constructor() {
911
this.providers = [];
10-
this.providers.push(new ElseFormatProvider());
12+
const boundaryBlocks = [
13+
DEF_REGEX,
14+
ASYNC_DEF_REGEX,
15+
CLASS_REGEX
16+
];
17+
18+
const elseParentBlocks = [
19+
IF_REGEX,
20+
ELIF_REGEX,
21+
FOR_IN_REGEX,
22+
ASYNC_FOR_IN_REGEX,
23+
WHILE_REGEX,
24+
TRY_REGEX,
25+
EXCEPT_REGEX
26+
];
27+
this.providers.push(new CodeBlockFormatProvider(ELSE_REGEX, elseParentBlocks, boundaryBlocks));
28+
29+
const elifParentBlocks = [
30+
IF_REGEX,
31+
ELIF_REGEX
32+
];
33+
this.providers.push(new CodeBlockFormatProvider(ELIF_REGEX, elifParentBlocks, boundaryBlocks));
34+
35+
const exceptParentBlocks = [
36+
TRY_REGEX,
37+
EXCEPT_REGEX
38+
];
39+
this.providers.push(new CodeBlockFormatProvider(EXCEPT_REGEX, exceptParentBlocks, boundaryBlocks));
40+
41+
const finallyParentBlocks = [
42+
TRY_REGEX,
43+
EXCEPT_REGEX
44+
];
45+
this.providers.push(new CodeBlockFormatProvider(FINALLY_REGEX, finallyParentBlocks, boundaryBlocks));
1146
}
1247
provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): TextEdit[] {
1348
if (position.line === 0) {
1449
return [];
1550
}
1651

17-
const currentLine = document.lineAt(position.line).text;
18-
const provider = this.providers.find(provider => provider.canProvideEdits(currentLine));
52+
const currentLine = document.lineAt(position.line);
53+
const prevousLine = document.lineAt(position.line - 1);
54+
55+
// We're only interested in cases where the current block is at the same indentation level as the previous line
56+
// E.g. if we have an if..else block, generally the else statement would be at the same level as the code in the if...
57+
if (currentLine.firstNonWhitespaceCharacterIndex !== prevousLine.firstNonWhitespaceCharacterIndex) {
58+
return [];
59+
}
60+
61+
const currentLineText = currentLine.text;
62+
const provider = this.providers.find(provider => provider.canProvideEdits(currentLineText));
1963
if (provider) {
2064
return provider.provideEdits(document, position, ch, options, currentLine);
2165
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { FormattingOptions, TextEdit, TextDocument } from 'vscode';
2+
import { Position, Range, TextLine } from 'vscode';
3+
import { BlockRegEx } from './contracts';
4+
5+
export class CodeBlockFormatProvider {
6+
constructor(private blockRegExp: BlockRegEx, private previousBlockRegExps: BlockRegEx[], private boundaryRegExps: BlockRegEx[]) {
7+
}
8+
canProvideEdits(line: string): boolean {
9+
return this.blockRegExp.test(line);
10+
}
11+
12+
provideEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, line: TextLine): TextEdit[] {
13+
// We can have else for the following blocks:
14+
// if:
15+
// elif x:
16+
// for x in y:
17+
// while x:
18+
19+
// We need to find a block statement that is less than or equal to this statement block (but not greater)
20+
for (let lineNumber = position.line - 1; lineNumber > 0; lineNumber--) {
21+
const prevLine = document.lineAt(lineNumber);
22+
const prevLineText = prevLine.text;
23+
24+
// Oops, we've reached a boundary (like the function or class definition)
25+
// Get out of here
26+
if (this.boundaryRegExps.some(value => value.test(prevLineText))) {
27+
return [];
28+
}
29+
30+
const blockRegEx = this.previousBlockRegExps.find(value => value.test(prevLineText));
31+
if (!blockRegEx) {
32+
continue;
33+
}
34+
35+
const startOfBlockInLine = prevLine.firstNonWhitespaceCharacterIndex;
36+
if (startOfBlockInLine > line.firstNonWhitespaceCharacterIndex) {
37+
continue;
38+
}
39+
40+
const startPosition = new Position(position.line, 0);
41+
const endPosition = new Position(position.line, line.firstNonWhitespaceCharacterIndex - startOfBlockInLine);
42+
43+
if (startPosition === endPosition) {
44+
return [];
45+
}
46+
if (options.insertSpaces) {
47+
return [
48+
TextEdit.delete(new Range(startPosition, endPosition))
49+
];
50+
}
51+
else {
52+
// Delete everything before the block and insert the same characters we have in the previous block
53+
const prefixOfPreviousBlock = prevLineText.substring(0, startOfBlockInLine);
54+
55+
const startDeletePosition = new Position(position.line, 0);
56+
const endDeletePosition = new Position(position.line, line.firstNonWhitespaceCharacterIndex);
57+
58+
return [
59+
TextEdit.delete(new Range(startDeletePosition, endDeletePosition)),
60+
TextEdit.insert(startDeletePosition, prefixOfPreviousBlock)
61+
];
62+
}
63+
}
64+
65+
return [];
66+
}
67+
}

src/client/typeFormatters/contracts.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import { FormattingOptions, TextEdit, TextDocument } from 'vscode';
2-
import { Position } from 'vscode';
3-
41
export class BlockRegEx {
52
constructor(private regEx: RegExp, public startWord) {
63

@@ -24,8 +21,3 @@ export const EXCEPT_REGEX = new BlockRegEx(/^( |\t)*except *\w* *(as)? *\w* *: *
2421
export const DEF_REGEX = new BlockRegEx(/^( |\t)*def \w *\(.*$/g, 'def');
2522
export const ASYNC_DEF_REGEX = new BlockRegEx(/^( |\t)*async *def \w *\(.*$/g, 'async');
2623
export const CLASS_REGEX = new BlockRegEx(/^( |\t)*class *\w* *.*: *$/g, 'class');
27-
28-
export interface CodeBlockFormatProvider {
29-
canProvideEdits(line: string): boolean;
30-
provideEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, line: string): TextEdit[];
31-
}

src/client/typeFormatters/elseFormatProvider.ts

Lines changed: 0 additions & 66 deletions
This file was deleted.

0 commit comments

Comments
 (0)