Skip to content

Commit 5321959

Browse files
committed
Format all tables in the document
Issue #7
1 parent 6670259 commit 5321959

14 files changed

Lines changed: 490 additions & 233 deletions

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ All notable changes to the `markdowntableprettify` extension will be documented
55
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased]
8-
### 2.2.0 - 2018-09-26
8+
### Added
9+
- Issue #7: Support formatting all tables in the document.
10+
11+
## 2.2.0 - 2018-09-26
12+
### Added
913
- Issue #22: Allow formatting without a filename
1014

1115
## 2.1.0 - 2018-09-15

src/extension/extension.ts

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,16 @@
11
'use strict';
22
import * as vscode from 'vscode';
3-
import { TableRangePrettyfier } from "./tableRangePrettyfier";
4-
import { ConsoleLogger } from '../diagnostics/consoleLogger';
5-
import { VsWindowLogger } from '../diagnostics/vsWindowLogger';
6-
import { TableFactory } from "../modelFactory/tableFactory";
7-
import { AlignmentFactory } from "../modelFactory/alignmentFactory";
8-
import { TableValidator } from "../modelFactory/tableValidator";
9-
import { TableStringWriter } from "../writers/tableStringWriter";
10-
import { ContentPadCalculator } from '../padCalculation/contentPadCalculator';
11-
import { TableViewModelFactory } from '../viewModelFactories/tableViewModelFactory';
12-
import { RowViewModelFactory } from '../viewModelFactories/rowViewModelFactory';
13-
import { TrimmerTransformer } from '../modelFactory/transformers/trimmerTransformer';
14-
import { BorderTransformer } from '../modelFactory/transformers/borderTransformer';
15-
import { SelectionInterpreter } from '../modelFactory/selectionInterpreter';
16-
import { PadCalculatorSelector } from '../padCalculation/padCalculatorSelector';
17-
import { AlignmentMarkerStrategy } from '../viewModelFactories/alignmentMarking';
3+
import { getDocumentRangePrettyfier, getDocumentPrettyfier } from './prettyfierFactory';
184

195
// This method is called when the extension is activated.
206
// The extension is activated the very first time the command is executed.
217
export function activate(context: vscode.ExtensionContext): void {
228
const MD_MODE: vscode.DocumentFilter = { language: "markdown" };
239

24-
let disposable = vscode.languages.registerDocumentRangeFormattingEditProvider(
25-
MD_MODE, new TableRangePrettyfier(
26-
new TableFactory(
27-
new AlignmentFactory(),
28-
new SelectionInterpreter(),
29-
new TrimmerTransformer(new BorderTransformer(null)),
30-
),
31-
new TableValidator(new SelectionInterpreter()),
32-
new TableViewModelFactory(
33-
new RowViewModelFactory(
34-
new ContentPadCalculator(new PadCalculatorSelector(), " "),
35-
new AlignmentMarkerStrategy(":")
36-
)
37-
),
38-
new TableStringWriter(),
39-
[ new VsWindowLogger(), new ConsoleLogger() ])
10+
context.subscriptions.push(
11+
vscode.languages.registerDocumentRangeFormattingEditProvider(MD_MODE, getDocumentRangePrettyfier()),
12+
vscode.languages.registerDocumentFormattingEditProvider(MD_MODE, getDocumentPrettyfier())
4013
);
41-
42-
context.subscriptions.push(disposable);
4314
}
4415

45-
export function deactivate() {
46-
}
16+
export function deactivate() { }

src/extension/prettyfierFactory.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as vscode from 'vscode';
2+
import { TableDocumentPrettyfier } from './tableDocumentPrettyfier';
3+
import { TableFinder } from '../tableFinding/tableFinder';
4+
import { TableDocumentRangePrettyfier } from "./tableDocumentRangePrettyfier";
5+
import { ConsoleLogger } from '../diagnostics/consoleLogger';
6+
import { VsWindowLogger } from '../diagnostics/vsWindowLogger';
7+
import { TableFactory } from "../modelFactory/tableFactory";
8+
import { AlignmentFactory } from "../modelFactory/alignmentFactory";
9+
import { TableValidator } from "../modelFactory/tableValidator";
10+
import { TableStringWriter } from "../writers/tableStringWriter";
11+
import { ContentPadCalculator } from '../padCalculation/contentPadCalculator';
12+
import { TableViewModelFactory } from '../viewModelFactories/tableViewModelFactory';
13+
import { RowViewModelFactory } from '../viewModelFactories/rowViewModelFactory';
14+
import { TrimmerTransformer } from '../modelFactory/transformers/trimmerTransformer';
15+
import { BorderTransformer } from '../modelFactory/transformers/borderTransformer';
16+
import { SelectionInterpreter } from '../modelFactory/selectionInterpreter';
17+
import { PadCalculatorSelector } from '../padCalculation/padCalculatorSelector';
18+
import { AlignmentMarkerStrategy } from '../viewModelFactories/alignmentMarking';
19+
20+
export function getDocumentRangePrettyfier(strict: boolean = false) {
21+
return new TableDocumentRangePrettyfier(
22+
new TableFactory(
23+
new AlignmentFactory(), new SelectionInterpreter(strict),
24+
new TrimmerTransformer(new BorderTransformer(null))
25+
),
26+
new TableValidator(new SelectionInterpreter(strict)),
27+
new TableViewModelFactory(
28+
new RowViewModelFactory(
29+
new ContentPadCalculator(new PadCalculatorSelector(), " "),
30+
new AlignmentMarkerStrategy(":")
31+
)
32+
),
33+
new TableStringWriter(),
34+
[new VsWindowLogger(), new ConsoleLogger()]
35+
);
36+
}
37+
38+
export function getDocumentPrettyfier(strict: boolean = true): vscode.DocumentFormattingEditProvider {
39+
return new TableDocumentPrettyfier(
40+
new TableFinder(new TableValidator(new SelectionInterpreter(strict))),
41+
getDocumentRangePrettyfier(strict)
42+
);
43+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as vscode from "vscode";
2+
import { TableDocumentRangePrettyfier } from "./tableDocumentRangePrettyfier";
3+
import { TableFinder } from "../tableFinding/tableFinder";
4+
5+
export class TableDocumentPrettyfier implements vscode.DocumentFormattingEditProvider {
6+
7+
constructor(
8+
private readonly _tableFinder: TableFinder,
9+
private readonly _tableDocumentRangePrettyfier: TableDocumentRangePrettyfier
10+
) { }
11+
12+
public provideDocumentFormattingEdits(document: vscode.TextDocument,
13+
options: vscode.FormattingOptions, token: vscode.CancellationToken): vscode.TextEdit[]
14+
{
15+
let result: vscode.TextEdit[] = [];
16+
let tables: vscode.Range[] = this._tableFinder.getTables(document);
17+
for (let tableRange of tables) {
18+
let edits = this._tableDocumentRangePrettyfier.provideDocumentRangeFormattingEdits(document, tableRange, options, token);
19+
result = result.concat(edits);
20+
}
21+
return result;
22+
}
23+
}
Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,54 @@
1-
import * as vscode from "vscode";
2-
import { ILogger } from "../diagnostics/logger";
3-
import { SelectionBasedLogToogler } from "../diagnostics/selectionBasedLogToogler";
4-
import { Table } from "../models/table";
5-
import { TableFactory } from "../modelFactory/tableFactory";
6-
import { TableValidator } from "../modelFactory/tableValidator";
7-
import { TableViewModel } from "../viewModels/tableViewModel";
8-
import { TableViewModelFactory } from "../viewModelFactories/tableViewModelFactory";
9-
import { TableStringWriter } from "../writers/tableStringWriter";
10-
11-
export class TableRangePrettyfier implements vscode.DocumentRangeFormattingEditProvider {
12-
13-
constructor(
14-
private _tableFactory: TableFactory,
15-
private _tableValidator: TableValidator,
16-
private _viewModelFactory: TableViewModelFactory,
17-
private _writer: TableStringWriter,
18-
private _loggers: ILogger[]
19-
) { }
20-
21-
public provideDocumentRangeFormattingEdits(
22-
document: vscode.TextDocument, range: vscode.Range,
23-
options: vscode.FormattingOptions, token: vscode.CancellationToken) : vscode.TextEdit[]
24-
{
25-
const result: vscode.TextEdit[] = [];
26-
const selection = document.getText(range);
27-
28-
this.toogleLogging(document, range);
29-
let message: string = null;
30-
31-
try {
32-
if (this._tableValidator.isValid(selection)) {
33-
const table: Table = this._tableFactory.getModel(selection);
34-
const tableVm: TableViewModel = this._viewModelFactory.build(table);
35-
const formattedTable: string = this._writer.writeTable(tableVm);
36-
result.push(new vscode.TextEdit(range, formattedTable));
37-
} else {
38-
message = "Can't parse table from invalid text."
39-
}
40-
} catch (ex) {
41-
this._loggers.forEach(_ => _.logError(ex));
42-
}
43-
44-
if (!!message)
45-
this._loggers.forEach(_ => _.logInfo(message));
46-
47-
return result;
48-
}
49-
50-
private toogleLogging(document: vscode.TextDocument, range: vscode.Range) {
51-
const toogler = new SelectionBasedLogToogler(document, range);
52-
toogler.toogleLoggers(this._loggers);
53-
}
1+
import * as vscode from "vscode";
2+
import { ILogger } from "../diagnostics/logger";
3+
import { SelectionBasedLogToogler } from "../diagnostics/selectionBasedLogToogler";
4+
import { Table } from "../models/table";
5+
import { TableFactory } from "../modelFactory/tableFactory";
6+
import { TableValidator } from "../modelFactory/tableValidator";
7+
import { TableViewModel } from "../viewModels/tableViewModel";
8+
import { TableViewModelFactory } from "../viewModelFactories/tableViewModelFactory";
9+
import { TableStringWriter } from "../writers/tableStringWriter";
10+
11+
export class TableDocumentRangePrettyfier implements vscode.DocumentRangeFormattingEditProvider {
12+
13+
constructor(
14+
private _tableFactory: TableFactory,
15+
private _tableValidator: TableValidator,
16+
private _viewModelFactory: TableViewModelFactory,
17+
private _writer: TableStringWriter,
18+
private _loggers: ILogger[]
19+
) { }
20+
21+
public provideDocumentRangeFormattingEdits(
22+
document: vscode.TextDocument, range: vscode.Range,
23+
options: vscode.FormattingOptions, token: vscode.CancellationToken) : vscode.TextEdit[]
24+
{
25+
const result: vscode.TextEdit[] = [];
26+
const selection = document.getText(range);
27+
28+
this.toogleLogging(document, range);
29+
let message: string = null;
30+
31+
try {
32+
if (this._tableValidator.isValid(selection)) {
33+
const table: Table = this._tableFactory.getModel(selection);
34+
const tableVm: TableViewModel = this._viewModelFactory.build(table);
35+
const formattedTable: string = this._writer.writeTable(tableVm);
36+
result.push(new vscode.TextEdit(range, formattedTable));
37+
} else {
38+
message = "Can't parse table from invalid text."
39+
}
40+
} catch (ex) {
41+
this._loggers.forEach(_ => _.logError(ex));
42+
}
43+
44+
if (!!message)
45+
this._loggers.forEach(_ => _.logInfo(message));
46+
47+
return result;
48+
}
49+
50+
private toogleLogging(document: vscode.TextDocument, range: vscode.Range) {
51+
const toogler = new SelectionBasedLogToogler(document, range);
52+
toogler.toogleLoggers(this._loggers);
53+
}
5454
}

src/modelFactory/selectionInterpreter.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
export class SelectionInterpreter {
2+
constructor(
3+
private readonly _strict: boolean
4+
) { }
5+
26
public allRows(selection: string): string[][] {
3-
return selection.split(/\r\n|\r|\n/)
4-
.map(this.splitLine, this)
5-
.filter(arr => arr.length > 0 && !(arr.length == 1 && /^\s*$/.test(arr[0])));
7+
let split = selection.split(/\r\n|\r|\n/).map(this.splitLine, this);
8+
return this._strict
9+
? split
10+
: split.filter(arr => arr.length > 0 && !(arr.length == 1 && /^\s*$/.test(arr[0])));
611
}
712

813
public separator(selection: string): string[] {

src/tableFinding/tableFinder.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as vscode from "vscode";
2+
import { EOL } from "os";
3+
import { TableValidator } from "../modelFactory/tableValidator";
4+
5+
export class TableFinder {
6+
private readonly _separatorMarker = "-|-";
7+
constructor(
8+
private readonly _tableValidator: TableValidator
9+
) { }
10+
11+
public getTables(document: vscode.TextDocument): vscode.Range[] {
12+
let rows: string[] = document.getText().split(/\r\n|\r|\n/);
13+
14+
let result = [];
15+
let previousRowIndex = 0;
16+
while(true) {
17+
let { tableStartRow, tableEndRow } = this.getNextResult(rows, previousRowIndex);
18+
if (tableStartRow == null || tableEndRow == null)
19+
break;
20+
result.push(this.getRangeForLines(rows, tableStartRow, tableEndRow));
21+
previousRowIndex = tableEndRow;
22+
}
23+
24+
return result;
25+
}
26+
27+
private getNextResult(rows: string[], startAtRow: number) {
28+
// look for the separator row, assume table starts 1 row before & ends when invalid
29+
let rowIndex = startAtRow;
30+
while (rowIndex < rows.length) {
31+
let table = rows[rowIndex].indexOf(this._separatorMarker) >= 0
32+
? this.getTableFromSeparatorIndex(rows, rowIndex)
33+
: null;
34+
if (table != null) return table;
35+
rowIndex++;
36+
}
37+
38+
return {
39+
tableStartRow: null,
40+
tableEndRow: null
41+
};
42+
}
43+
44+
private getTableFromSeparatorIndex(rows: string[], separatorRowIndex: number) {
45+
let tableValid = true;
46+
let tableStartRow = separatorRowIndex - 1;
47+
let tableEndRow = separatorRowIndex;
48+
49+
while (tableValid && tableEndRow < rows.length) {
50+
tableEndRow++;
51+
let selection = this.concatRows(rows, tableStartRow, tableEndRow)
52+
tableValid = this._tableValidator.isValid(selection);
53+
}
54+
55+
// make sure there is at least 1 row after the separator
56+
return tableEndRow > separatorRowIndex + 1
57+
? {
58+
tableStartRow: tableStartRow ,
59+
tableEndRow: tableEndRow - 1
60+
}
61+
: null;
62+
}
63+
64+
private concatRows(rows: string[], from: number, to: number) {
65+
let relevantRows = rows.slice(from, to + 1);
66+
return relevantRows.join(EOL);
67+
}
68+
69+
private getRangeForLines(rows: string[], startLine: number, endLine: number): vscode.Range {
70+
return new vscode.Range(
71+
new vscode.Position(startLine, 0),
72+
new vscode.Position(endLine, rows[endLine - 1].length)
73+
);
74+
}
75+
}

test/stubs/markdownTextDocumentStub.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class MarkdownTextDocumentStub implements vscode.TextDocument {
4343
}
4444

4545
getText(range?: vscode.Range): string {
46+
range = range == null ? this.getFullRange() : range;
4647
let buffer: string = "";
4748
for (let row = range.start.line; row < range.end.line; row++) {
4849
const isFirstRow = row == range.start.line;
@@ -74,12 +75,12 @@ export class MarkdownTextDocumentStub implements vscode.TextDocument {
7475
return new vscode.Range[0];
7576
}
7677
validateRange(range: vscode.Range): vscode.Range {
77-
return new vscode.Range[0];
78+
return range;
7879
}
7980
positionAt(offset: number): vscode.Position {
8081
return new vscode.Position[0];
8182
}
8283
validatePosition(position: vscode.Position): vscode.Position {
83-
return new vscode.Position[0];
84+
return position;
8485
}
8586
}

test/systemTests/tableRangePrettyfierFactory.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as assert from 'assert';
22
import * as vscode from "vscode";
33
import * as fs from 'fs';
44
import * as path from 'path';
5-
import { TableRangePrettyfier } from "../../src/extension/tableRangePrettyfier";
5+
import { TableDocumentRangePrettyfier } from "../../src/extension/tableDocumentRangePrettyfier";
66
import { TableFactory } from "../../src/modelFactory/tableFactory";
77
import { AlignmentFactory } from "../../src/modelFactory/alignmentFactory";
88
import { TableValidator } from "../../src/modelFactory/tableValidator";
@@ -51,14 +51,14 @@ export class PrettyfierFromFile {
5151
return fs.readFileSync(path.resolve(__dirname, fileName), 'utf-8');
5252
}
5353

54-
private createPrettyfier(): TableRangePrettyfier {
55-
return new TableRangePrettyfier(
54+
private createPrettyfier(): TableDocumentRangePrettyfier {
55+
return new TableDocumentRangePrettyfier(
5656
new TableFactory(
5757
new AlignmentFactory(),
58-
new SelectionInterpreter(),
58+
new SelectionInterpreter(false),
5959
new TrimmerTransformer(new BorderTransformer(null))
6060
),
61-
new TableValidator(new SelectionInterpreter()),
61+
new TableValidator(new SelectionInterpreter(false)),
6262
new TableViewModelFactory(new RowViewModelFactory(
6363
new ContentPadCalculator(new PadCalculatorSelector(), " "),
6464
new AlignmentMarkerStrategy(":")

0 commit comments

Comments
 (0)