Skip to content

Commit

Permalink
feat: migrate to new formatting provider
Browse files Browse the repository at this point in the history
  • Loading branch information
1nVitr0 committed Dec 13, 2021
1 parent edb05a5 commit ae6254b
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 17 deletions.
33 changes: 16 additions & 17 deletions src/commands/blockSort.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
import { commands, InputBoxOptions, Selection, TextEditor, TextEditorEdit, window } from 'vscode';
import BlockSortProvider from '../providers/BlockSortProvider';
import ConfigurationProvider from '../providers/ConfigurationProvider';
import { commands, InputBoxOptions, Selection, Range, TextEditor, TextEditorEdit, window } from "vscode";
import BlockSortProvider from "../providers/BlockSortProvider";
import ConfigurationProvider from "../providers/ConfigurationProvider";
import FormattingProvider from "../providers/FormattingProvider";

export function blockSort(
editor: TextEditor,
editor: TextEditor | undefined,
editBuilder: TextEditorEdit,
sortFunction: (a: string, b: string) => number,
sortChildren = 0
) {
if (!window.activeTextEditor) return;
const { document, selection } = window.activeTextEditor;
if (!editor) editor = window.activeTextEditor;
if (!editor) return;

const blockSort = new BlockSortProvider(document);
const range = blockSort.expandSelection(selection);
const blocks = blockSort.getBlocks(range);
const sorted = blockSort.sortBlocks(blocks, sortFunction, sortChildren);
const { document, selection } = editor;

editor.selection = new Selection(range.start, range.end);
editBuilder.replace(range, sorted.join('\n'));
const edit = FormattingProvider.getBlockSortEdit(document, selection, { sortFunction, sortChildren });
editBuilder.replace(edit.range, edit.newText);
editor.selection = new Selection(edit.range.start, edit.range.end);
}

function blockSortMultilevel(sortFunction: (a: string, b: string) => number) {
const defaultDepth = ConfigurationProvider.getDefaultMultilevelDepth();
if (!ConfigurationProvider.getAskForMultilevelDepth())
return commands.executeCommand('blocksort._sortBlocks', sortFunction, defaultDepth);
return commands.executeCommand("blocksort._sortBlocks", sortFunction, defaultDepth);

let options: InputBoxOptions = {
prompt: 'Indentation Depth: ',
placeHolder: '(number)',
prompt: "Indentation Depth: ",
placeHolder: "(number)",
value: defaultDepth.toString(),
validateInput: (value) => (/\-?\d+/.test(value) ? null : 'Only integer values allowed. Use -1 for infinite depth'),
validateInput: (value) => (/\-?\d+/.test(value) ? null : "Only integer values allowed. Use -1 for infinite depth"),
};

window.showInputBox(options).then((value) => {
if (value === undefined) return;
commands.executeCommand('blocksort._sortBlocks', sortFunction, parseInt(value));
commands.executeCommand("blocksort._sortBlocks", sortFunction, parseInt(value));
});
}

Expand Down
113 changes: 113 additions & 0 deletions src/providers/FormattingProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
CancellationToken,
DocumentFormattingEditProvider,
Position,
ProviderResult,
Range,
TextDocument,
TextEdit,
TextLine,
FormattingOptions,
DocumentRangeFormattingEditProvider,
} from "vscode";
import { commentMarkers } from "../constants/comments";
import BlockSortProvider from "./BlockSortProvider";

type BlockSortOptions = {
sortFunction: (a: string, b: string) => number;
sortChildren: number;
};

export default class FormattingProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider {
public static blockSortMarker = "@blocksort";

public static getBlockSortMarkers(document: TextDocument, range?: Range, token?: CancellationToken): TextLine[] {
const markers: TextLine[] = [];
const comments = commentMarkers[document.languageId] ?? commentMarkers.default;
const markerPrefixes = comments.map((comment) => `${comment.start} ${FormattingProvider.blockSortMarker}`);

for (let i = range?.start.line ?? 0; i < (range?.end.line ?? document.lineCount); i++) {
if (token && token.isCancellationRequested) return markers;

const line = document.lineAt(i);
if (markerPrefixes.some((prefix) => line.text.trim().startsWith(prefix))) markers.push(line);
}

return markers;
}

public static getBlockSortMarkerOptions(document: TextDocument, position: Position): BlockSortOptions {
const line = document.lineAt(position.line).text;
const matches = line.match(/@blocksort ?(asc|desc)? ?(\d+|infinite)?/) ?? [];
const [_, direction = "asc", depth = "0"] = matches;

return {
sortFunction: direction === "asc" ? BlockSortProvider.sort.asc : BlockSortProvider.sort.desc,
sortChildren: parseInt(depth, 10),
};
}

public static getNextBlockPosition(document: TextDocument, position: Position): Position {
let line = document.lineAt(position.line);
const initialIndent = line.firstNonWhitespaceCharacterIndex;

while (line.firstNonWhitespaceCharacterIndex <= initialIndent) {
line = document.lineAt(line.lineNumber + 1);
}

return line.range.end;
}

public static getBlockSortEdit(
document: TextDocument,
position: Position | Range,
options: BlockSortOptions
): TextEdit {
const blockSort = new BlockSortProvider(document);
const initialRange = "start" in position ? position : new Range(position, position);
const range = blockSort.expandRange(initialRange);
const blocks = blockSort.getBlocks(range);
const sorted = blockSort.sortBlocks(blocks, options.sortFunction, options.sortChildren);

return TextEdit.replace(range, sorted.join("\n"));
}

public static mapBlockSortHeaders<T>(
document: TextDocument,
headers: (TextLine | Position)[],
callback: (position: Position, options: BlockSortOptions) => T
): T[] {
return headers.map((line) => {
const position = "range" in line ? line.range.start : line;
const nextBlockPosition = FormattingProvider.getNextBlockPosition(document, position);
const options = FormattingProvider.getBlockSortMarkerOptions(document, position);
return callback(nextBlockPosition, options);
});
}

public provideDocumentFormattingEdits(
document: TextDocument,
options?: FormattingOptions,
token?: CancellationToken
): ProviderResult<TextEdit[]> {
return this.provideDocumentRangeFormattingEdits(document, undefined, options, token);
}

public provideDocumentRangeFormattingEdits(
document: TextDocument,
range: Range | undefined,
options?: FormattingOptions,
token?: CancellationToken
): ProviderResult<TextEdit[]> {
const markers = FormattingProvider.getBlockSortMarkers(document, range, token);
return this.provideBlockMarkerFormattingEdits(document, ...markers);
}

public provideBlockMarkerFormattingEdits(document: TextDocument, ...positions: (Position | TextLine)[]): TextEdit[] {
return FormattingProvider.mapBlockSortHeaders(
document,
positions,
FormattingProvider.getBlockSortEdit.bind(this, document)
);
}
}

0 comments on commit ae6254b

Please sign in to comment.