diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5b2137..5c2420fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [1.2.0] - 2020-07-03 + +- Added SEO title warning when over 60 characters is used + ## [1.1.1] - 2020-04-07 - `TOML` delimiter fix diff --git a/src/commands/StatusBar.ts b/src/commands/StatusBar.ts deleted file mode 100644 index cc0e9d22..00000000 --- a/src/commands/StatusBar.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as vscode from 'vscode'; -import { ArticleHelper } from '../helpers'; - -export class StatusBar { - - /** - * Update the text of the status bar - * - * @param frontMatterStatusBar - */ - public static async showDraftStatus(frontMatterSB: vscode.StatusBarItem) { - const draftMsg = "in draft"; - const publishMsg = "to publish"; - - let editor = vscode.window.activeTextEditor; - if (editor && editor.document && editor.document.languageId.toLowerCase() === "markdown") { - try { - const article = ArticleHelper.getFrontMatter(editor); - if (article && typeof article.data["draft"] !== "undefined") { - console.log(`Draft status: ${article.data["draft"]}`); - if (article.data["draft"] === true) { - frontMatterSB.text = `$(book) ${draftMsg}`; - frontMatterSB.show(); - } else if (article.data["draft"] === false) { - frontMatterSB.text = `$(book) ${publishMsg}`; - frontMatterSB.show(); - } - return; - } - } catch (e) { - // Nothing to do - } - } - - frontMatterSB.hide(); - } -} \ No newline at end of file diff --git a/src/commands/StatusListener.ts b/src/commands/StatusListener.ts new file mode 100644 index 00000000..243f09e2 --- /dev/null +++ b/src/commands/StatusListener.ts @@ -0,0 +1,71 @@ +import * as vscode from 'vscode'; +import { ArticleHelper } from '../helpers'; + +export class StatusListener { + + /** + * Update the text of the status bar + * + * @param frontMatterSB + * @param collection + */ + public static async verify(frontMatterSB: vscode.StatusBarItem, collection: vscode.DiagnosticCollection) { + const draftMsg = "in draft"; + const publishMsg = "to publish"; + + let editor = vscode.window.activeTextEditor; + if (editor && editor.document && editor.document.languageId.toLowerCase() === "markdown") { + try { + const article = ArticleHelper.getFrontMatter(editor); + + // Update the StatusBar based on the article draft state + if (article && typeof article.data["draft"] !== "undefined") { + // console.log(`Draft status: ${article.data["draft"]}`); + if (article.data["draft"] === true) { + frontMatterSB.text = `$(book) ${draftMsg}`; + frontMatterSB.show(); + } else if (article.data["draft"] === false) { + frontMatterSB.text = `$(book) ${publishMsg}`; + frontMatterSB.show(); + } + } + + // Check SEO of the title + if (article && article.data && article.data.title) { + const title: string = article.data.title; + console.log(`Title length: ${title.length}`); + if (title.length >= 60) { + const text = editor.document.getText(); + + const markdown = ArticleHelper.stringifyFrontMatter("", article.data); + + const txtIdx = text.indexOf(title); + if (txtIdx !== -1 && txtIdx < markdown.length) { + collection.clear(); + const posStart = editor.document.positionAt(txtIdx); + const posEnd = editor.document.positionAt(txtIdx + 1 + title.length); + + collection.set(editor.document.uri, [{ + code: '', + message: `Article title is longer than 60 characters (current length: ${title.length}). For SEO reasons, it would be better to make it less than 60 characters.`, + range: new vscode.Range(posStart, posEnd), + severity: vscode.DiagnosticSeverity.Warning, + source: 'Front Matter' + }]); + } else { + collection.clear(); + } + } else { + collection.clear(); + } + } + + return; + } catch (e) { + // Nothing to do + } + } + + frontMatterSB.hide(); + } +} \ No newline at end of file diff --git a/src/commands/index.ts b/src/commands/index.ts index 7e11499b..d5aa6bdc 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,3 +1,3 @@ export * from './Article'; export * from './Settings'; -export * from './StatusBar'; +export * from './StatusListener'; diff --git a/src/extension.ts b/src/extension.ts index ab7a7e62..33323bd8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,11 +1,13 @@ import * as vscode from 'vscode'; -import { Article, Settings, StatusBar } from './commands'; +import { Article, Settings, StatusListener } from './commands'; import { TaxonomyType } from './models'; let frontMatterStatusBar: vscode.StatusBarItem; let debouncer: { (fnc: any, time: number): void; }; +let collection: vscode.DiagnosticCollection; export function activate({ subscriptions }: vscode.ExtensionContext) { + collection = vscode.languages.createDiagnosticCollection('frontMatter'); let insertTags = vscode.commands.registerCommand('frontMatter.insertTags', () => { Article.insert(TaxonomyType.Tag); @@ -50,7 +52,7 @@ export function activate({ subscriptions }: vscode.ExtensionContext) { frontMatterStatusBar.command = toggleDraftCommand; subscriptions.push(frontMatterStatusBar); debouncer = debounceShowDraftTrigger(); - // Register listeners that make sure the status bar + // Register listeners that make sure the status bar updates subscriptions.push(vscode.window.onDidChangeActiveTextEditor(triggerShowDraftStatus)); subscriptions.push(vscode.window.onDidChangeTextEditorSelection(triggerShowDraftStatus)); // Automatically run the command @@ -71,7 +73,7 @@ export function activate({ subscriptions }: vscode.ExtensionContext) { export function deactivate() {} const triggerShowDraftStatus = () => { - debouncer(() => { StatusBar.showDraftStatus(frontMatterStatusBar); }, 1000); + debouncer(() => { StatusListener.verify(frontMatterStatusBar, collection); }, 1000); }; const debounceShowDraftTrigger = () => { diff --git a/src/helpers/ArticleHelper.ts b/src/helpers/ArticleHelper.ts index 2914c2e3..77e7226d 100644 --- a/src/helpers/ArticleHelper.ts +++ b/src/helpers/ArticleHelper.ts @@ -35,16 +35,9 @@ export class ArticleHelper { */ public static async update(editor: vscode.TextEditor, article: matter.GrayMatterFile) { const config = vscode.workspace.getConfiguration(CONFIG_KEY); - const indentArray = config.get(SETTING_INDENT_ARRAY) as boolean; const removeQuotes = config.get(SETTING_REMOVE_QUOTES) as string[]; - const language = getFmLanguage(); - const langOpts = getFormatOpts(language); - let newMarkdown = matter.stringify(article.content, article.data, ({ - ...TomlEngine, - ...langOpts, - noArrayIndent: !indentArray - } as DumpOptions as any)); + let newMarkdown = this.stringifyFrontMatter(article.content, article.data); // Check for field where quotes need to be removed if (removeQuotes && removeQuotes.length) { @@ -60,12 +53,33 @@ export class ArticleHelper { await editor.edit(builder => builder.replace(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(nrOfLines, 0)), newMarkdown)); } + /** + * Stringify the front matter + * + * @param content + * @param data + */ + public static stringifyFrontMatter(content: string, data: any) { + const config = vscode.workspace.getConfiguration(CONFIG_KEY); + const indentArray = config.get(SETTING_INDENT_ARRAY) as boolean; + + const language = getFmLanguage(); + const langOpts = getFormatOpts(language); + + return matter.stringify(content, data, ({ + ...TomlEngine, + ...langOpts, + noArrayIndent: !indentArray, + lineWidth: 500 + } as DumpOptions as any)); + } + /** * Generate the slug * * @param articleTitle */ - static createSlug(articleTitle: string): string | null { + public static createSlug(articleTitle: string): string | null { if (!articleTitle) { return null; }