Skip to content

Commit b96578d

Browse files
committed
#432
1 parent 73d6677 commit b96578d

4 files changed

Lines changed: 124 additions & 0 deletions

File tree

src/client/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import * as jup from './jupyter/main';
2222
import { HelpProvider } from './helpProvider';
2323
import { activateFormatOnSaveProvider } from './providers/formatOnSaveProvider';
2424
import { WorkspaceSymbols } from './workspaceSymbols/main';
25+
import { BlockFormatProviders } from './typeFormatters/blockFormatProvider';
2526
import * as os from 'os';
2627

2728
const PYTHON: vscode.DocumentFilter = { language: 'python', scheme: 'file' };
@@ -95,6 +96,7 @@ export function activate(context: vscode.ExtensionContext) {
9596

9697
context.subscriptions.push(new WorkspaceSymbols(lintingOutChannel));
9798

99+
context.subscriptions.push(vscode.languages.registerOnTypeFormattingEditProvider(PYTHON, new BlockFormatProviders(), ':'));
98100
// In case we have CR LF
99101
const triggerCharacters: string[] = os.EOL.split('');
100102
triggerCharacters.shift();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { OnTypeFormattingEditProvider, FormattingOptions, TextEdit, CancellationToken, TextDocument } from 'vscode';
2+
import { Position } from 'vscode';
3+
import { CodeBlockFormatProvider } from './contracts';
4+
import { ElseFormatProvider } from './elseFormatProvider';
5+
6+
export class BlockFormatProviders implements OnTypeFormattingEditProvider {
7+
private providers: CodeBlockFormatProvider[];
8+
constructor() {
9+
this.providers = [];
10+
this.providers.push(new ElseFormatProvider());
11+
}
12+
provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): TextEdit[] {
13+
if (position.line === 0) {
14+
return [];
15+
}
16+
17+
const currentLine = document.lineAt(position.line).text;
18+
const provider = this.providers.find(provider => provider.canProvideEdits(currentLine));
19+
if (provider) {
20+
return provider.provideEdits(document, position, ch, options, currentLine);
21+
}
22+
23+
return [];
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { FormattingOptions, TextEdit, TextDocument } from 'vscode';
2+
import { Position } from 'vscode';
3+
4+
export class BlockRegEx {
5+
constructor(private regEx: RegExp, public startWord) {
6+
7+
}
8+
public test(value: string): boolean {
9+
// Clear the cache
10+
this.regEx.lastIndex = -1;
11+
return this.regEx.test(value);
12+
}
13+
}
14+
15+
export const IF_REGEX = new BlockRegEx(/^( |\t)*if +.*: *$/g, 'if');
16+
export const ELIF_REGEX = new BlockRegEx(/^( |\t)*elif +.*: *$/g, 'elif');
17+
export const ELSE_REGEX = new BlockRegEx(/^( |\t)*else *: *$/g, 'else');
18+
export const FOR_IN_REGEX = new BlockRegEx(/^( |\t)*for \w in .*: *$/g, 'for');
19+
export const ASYNC_FOR_IN_REGEX = new BlockRegEx(/^( |\t)*async *for \w in .*: *$/g, 'for');
20+
export const WHILE_REGEX = new BlockRegEx(/^( |\t)*while .*: *$/g, 'while');
21+
export const TRY_REGEX = new BlockRegEx(/^( |\t)*try *: *$/g, 'try');
22+
export const FINALLY_REGEX = new BlockRegEx(/^( |\t)*finally *: *$/g, 'finally');
23+
export const EXCEPT_REGEX = new BlockRegEx(/^( |\t)*except *\w* *(as)? *\w* *: *$/g, 'except');
24+
export const DEF_REGEX = new BlockRegEx(/^( |\t)*def \w *\(.*$/g, 'def');
25+
export const ASYNC_DEF_REGEX = new BlockRegEx(/^( |\t)*async *def \w *\(.*$/g, 'async');
26+
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+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { FormattingOptions, TextEdit, TextDocument } from 'vscode';
2+
import { Position, Range } from 'vscode';
3+
import { CodeBlockFormatProvider, BlockRegEx } from './contracts';
4+
import { IF_REGEX, ELIF_REGEX, ELSE_REGEX, FOR_IN_REGEX, ASYNC_FOR_IN_REGEX, WHILE_REGEX } from './contracts';
5+
import { DEF_REGEX, ASYNC_DEF_REGEX, CLASS_REGEX } from './contracts';
6+
7+
export class ElseFormatProvider implements CodeBlockFormatProvider {
8+
private regExps: BlockRegEx[];
9+
private boundaryRegExps: BlockRegEx[];
10+
constructor() {
11+
this.regExps = [
12+
IF_REGEX,
13+
ELIF_REGEX,
14+
FOR_IN_REGEX,
15+
ASYNC_FOR_IN_REGEX,
16+
WHILE_REGEX
17+
];
18+
this.boundaryRegExps = [
19+
DEF_REGEX,
20+
ASYNC_DEF_REGEX,
21+
CLASS_REGEX
22+
];
23+
}
24+
canProvideEdits(line: string): boolean {
25+
return ELSE_REGEX.test(line);
26+
}
27+
28+
provideEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, line: string): TextEdit[] {
29+
// We can have else for the following blocks:
30+
// if:
31+
// elif x:
32+
// for x in y:
33+
// while x:
34+
35+
const indexOfElse = line.indexOf(ELSE_REGEX.startWord);
36+
37+
// We need to find a block statement that is less than or equal to this statement block (but not greater)
38+
for (let lineNumber = position.line - 1; lineNumber > 0; lineNumber--) {
39+
const line = document.lineAt(lineNumber).text;
40+
41+
// Oops, we've reached a boundary (like the function or class definition)
42+
// Get out of here
43+
if (this.boundaryRegExps.some(value => value.test(line))) {
44+
return [];
45+
}
46+
47+
const blockRegEx = this.regExps.find(value => value.test(line));
48+
if (!blockRegEx) {
49+
continue;
50+
}
51+
52+
const startOfBlockInLine = line.indexOf(blockRegEx.startWord);
53+
if (startOfBlockInLine > indexOfElse) {
54+
continue;
55+
}
56+
57+
const startPosition = new Position(position.line, 0);
58+
const endPosition = new Position(position.line, indexOfElse - startOfBlockInLine);
59+
return [
60+
TextEdit.delete(new Range(startPosition, endPosition))
61+
];
62+
}
63+
64+
return [];
65+
}
66+
}

0 commit comments

Comments
 (0)