Skip to content

Commit 426af4c

Browse files
author
Jan Schulte
authored
Merge pull request #54 from schultyy/feature/better-header-errors
Feature/better header errors
2 parents 8ffd455 + adc00c6 commit 426af4c

File tree

5 files changed

+107
-19
lines changed

5 files changed

+107
-19
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"type": "git",
1414
"url": "https://github.com/schultyy/better-seo"
1515
},
16-
"version": "1.6.0",
16+
"version": "1.7.0",
1717
"icon": "resources/better_seo.png",
1818
"engines": {
1919
"vscode": "^1.54.0"

src/analyzer.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ export class ParagraphError extends AnalyzerError {
3636
}
3737
}
3838

39+
export class HeaderError extends AnalyzerError {
40+
constructor(
41+
public title: string,
42+
public loc: Location,
43+
public message: string,
44+
public resultType: ResultType) {
45+
super(title, message, resultType);
46+
}
47+
}
48+
3949
interface AstChild {
4050
type: string;
4151
value: string;
@@ -93,10 +103,15 @@ export class FileAnalyzer {
93103
}
94104

95105
private validateHeaderStructure() : Array<AnalyzerResult> {
96-
if(this.children.filter(child => child.type === 'Header' && child.depth === 1).length > 1) {
97-
return [
98-
new AnalyzerError('Header', 'Inconsistent Header Structure. Only one first level Header allowed.', ResultType.body)
99-
];
106+
const firstLevelHeadlines = this.children.filter(child => child.type === 'Header' && child.depth === 1);
107+
if(firstLevelHeadlines.length > 1) {
108+
return firstLevelHeadlines.map(firstLevelHeadline => {
109+
return new HeaderError(
110+
'Header',
111+
firstLevelHeadline.loc,
112+
'Inconsistent Header Structure. Only one first level Header allowed.',
113+
ResultType.body);
114+
});
100115
}
101116
return [];
102117
}

src/extension.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
11
// The module 'vscode' contains the VS Code extensibility API
22
// Import the module and reference it with the alias vscode in your code below
33
import * as vscode from 'vscode';
4-
import TreeProvider from './treeProvider';
4+
import TreeProvider, { FindingWithPosition } from './treeProvider';
5+
6+
function moveCursor(firstSelection: FindingWithPosition) {
7+
const editor = vscode.window.activeTextEditor;
8+
if(!editor) {
9+
return;
10+
}
11+
const position = editor.selection.active;
12+
13+
const startLine = firstSelection.location.start.line === 0 ? 0 : firstSelection.location.start.line - 1;
14+
const newPosition = position.with(startLine, firstSelection.location.start.column);
15+
const newSelection = new vscode.Selection(newPosition, newPosition);
16+
editor.selection = newSelection;
17+
}
518

619
// this method is called when your extension is activated
720
// your extension is activated the very first time the command is executed
821
export function activate(context: vscode.ExtensionContext) {
922

10-
// Use the console to output diagnostic information (console.log) and errors (console.error)
11-
// This line of code will only be executed once when your extension is activated
12-
const seoResultTreeProvider = new TreeProvider();
13-
vscode.window.registerTreeDataProvider('seo-results', seoResultTreeProvider);
23+
// Use the console to output diagnostic information (console.log) and errors (console.error)
24+
// This line of code will only be executed once when your extension is activated
25+
const seoResultTreeProvider = new TreeProvider();
26+
const treeView = vscode.window.createTreeView('seo-results', {
27+
treeDataProvider: seoResultTreeProvider
28+
});
29+
30+
treeView.onDidChangeSelection((event) => {
31+
console.log(event instanceof FindingWithPosition);
32+
if(event.selection.length === 0) {
33+
return;
34+
}
35+
const firstSelection = event.selection[0];
36+
if(firstSelection instanceof FindingWithPosition) {
37+
moveCursor(firstSelection);
38+
}
39+
});
1440

15-
let disposable = vscode.commands.registerCommand('better-seo.refresh', () => {
16-
seoResultTreeProvider.refresh();
17-
});
18-
context.subscriptions.push(disposable);
41+
let disposable = vscode.commands.registerCommand('better-seo.refresh', () => {
42+
seoResultTreeProvider.refresh();
43+
});
44+
context.subscriptions.push(disposable);
1945
}
2046

2147
// this method is called when your extension is deactivated

src/test/suite/extension.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ suite('Extension Test Suite', () => {
310310
assert.ok(headerError === null || headerError === undefined);
311311
});
312312

313-
test('Only returns one Header error even for multiple keywords', () => {
313+
test('Returns one Header error for each first-level headline', () => {
314314
const results = runAnalysis(withIncorrectHeadersMultipleKeywords, frontmatterConfiguration);
315-
const headerError = results.filter(result => result.title === 'Header');
316-
assert.strictEqual(headerError.length, 1);
315+
const headerError = results.filter(result => result.title === 'Header' && result.message.startsWith('Inconsistent Header Structure.'));
316+
assert.strictEqual(headerError.length, 2);
317317
});
318318
});
319319

src/treeProvider.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Event, EventEmitter, Position, ProviderResult, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window, workspace } from "vscode";
2-
import { AnalyzerError, AnalyzerResult, Location, ParagraphError, ResultType, runAnalysis } from "./analyzer";
2+
import { AnalyzerError, AnalyzerResult, HeaderError, Location, ParagraphError, ResultType, runAnalysis } from "./analyzer";
33
import * as path from 'path';
44

55
export default class TreeProvider implements TreeDataProvider<ResultsTreeItem> {
@@ -41,11 +41,42 @@ export default class TreeProvider implements TreeDataProvider<ResultsTreeItem> {
4141
this.generateParagraphErrors(<ParagraphFinding> element)
4242
);
4343
}
44+
else if(element.label === 'Header') {
45+
return Promise.resolve(
46+
this.generateHeaderErrors(<HeaderFinding> element)
47+
)
48+
}
4449
else {
4550
return Promise.resolve([]);
4651
}
4752
}
4853

54+
generateHeaderErrors(element: HeaderFinding): FindingWithPosition[] {
55+
const startLocationToString = (loc: Location) => (
56+
`Line:Column ${element.header.loc.start.line}:${element.header.loc.start.column}`
57+
);
58+
const endLocationToString = (loc: Location) => (
59+
`Line:Column ${element.header.loc.end.line}:${element.header.loc.end.column}`
60+
);
61+
62+
return [
63+
new FindingWithPosition(
64+
"Start",
65+
startLocationToString(element.header.loc),
66+
element.header.loc,
67+
this,
68+
TreeItemCollapsibleState.None
69+
),
70+
new FindingWithPosition(
71+
"End",
72+
endLocationToString(element.header.loc),
73+
element.header.loc,
74+
this,
75+
TreeItemCollapsibleState.None
76+
)
77+
];
78+
}
79+
4980
generateParagraphErrors(element: ParagraphFinding): FindingWithPosition[] {
5081

5182
const startLocationToString = (loc: Location) => (
@@ -82,7 +113,11 @@ export default class TreeProvider implements TreeDataProvider<ResultsTreeItem> {
82113
private generateBodyError(result: AnalyzerResult): Finding {
83114
if (result instanceof ParagraphError) {
84115
return new ParagraphFinding(result.title, result.message, result, this, TreeItemCollapsibleState.Collapsed);
85-
} else {
116+
}
117+
else if (result instanceof HeaderError) {
118+
return new HeaderFinding(result.title, result.message, result, this, TreeItemCollapsibleState.Collapsed);
119+
}
120+
else {
86121
return new Finding(result.title, result.message, this, TreeItemCollapsibleState.None);
87122
}
88123
}
@@ -154,6 +189,18 @@ export class Finding extends ResultsTreeItem {
154189
};
155190
}
156191

192+
export class HeaderFinding extends Finding {
193+
constructor(
194+
public readonly label: string,
195+
public readonly description: string,
196+
public readonly header: HeaderError,
197+
public provider: TreeProvider,
198+
public readonly collapsibleState: TreeItemCollapsibleState
199+
) {
200+
super(label, description, provider, collapsibleState);
201+
}
202+
}
203+
157204
export class ParagraphFinding extends Finding {
158205
constructor(
159206
public readonly label: string,

0 commit comments

Comments
 (0)