From 08fcf7cf159233753ce3abe5f0159db36d7af657 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Fri, 17 Sep 2021 17:31:37 +0200 Subject: [PATCH] Global/local settings implementation --- src/commands/Article.ts | 12 ++-- src/commands/Dashboard.ts | 8 ++- src/commands/Folders.ts | 6 +- src/commands/Preview.ts | 4 +- src/commands/Settings.ts | 4 +- src/commands/StatusListener.ts | 4 +- src/commands/Template.ts | 8 +-- src/explorerView/ExplorerView.ts | 25 ++++--- src/extension.ts | 2 +- src/helpers/ArticleHelper.ts | 12 ++-- src/helpers/Extension.ts | 4 +- src/helpers/SettingsHelper.ts | 83 +++++++++++++++++++++++- src/helpers/TomlEngine.ts | 4 +- src/providers/MarkdownFoldingProvider.ts | 5 +- 14 files changed, 135 insertions(+), 46 deletions(-) diff --git a/src/commands/Article.ts b/src/commands/Article.ts index f915ca1e..c9acb4de 100644 --- a/src/commands/Article.ts +++ b/src/commands/Article.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode'; import { TaxonomyType } from "../models"; import { CONFIG_KEY, SETTING_DATE_FORMAT, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX } from "../constants/settings"; import { format } from "date-fns"; -import { ArticleHelper, SettingsHelper, SlugHelper } from '../helpers'; +import { ArticleHelper, Settings, SlugHelper } from '../helpers'; import matter = require('gray-matter'); import { Notifications } from '../helpers/Notifications'; import { extname, basename } from 'path'; @@ -45,7 +45,7 @@ export class Article { } // Add all the known options to the selection list - const crntOptions = SettingsHelper.getTaxonomy(type); + const crntOptions = Settings.getTaxonomy(type); if (crntOptions && crntOptions.length > 0) { for (const crntOpt of crntOptions) { if (!options.find(o => o.label === crntOpt)) { @@ -109,7 +109,7 @@ export class Article { * Sets the article lastmod date */ public static async setLastModifiedDate() { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const editor = vscode.window.activeTextEditor; if (!editor) { return; @@ -135,7 +135,7 @@ export class Article { * Generate the slug based on the article title */ public static async generateSlug() { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const prefix = config.get(SETTING_SLUG_PREFIX) as string; const suffix = config.get(SETTING_SLUG_SUFFIX) as string; const updateFileName = config.get(SETTING_SLUG_UPDATE_FILE_NAME) as string; @@ -217,7 +217,7 @@ export class Article { const editor = vscode.window.activeTextEditor; if (txtChanges.length > 0 && editor && ArticleHelper.isMarkdownFile()) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const autoUpdate = config.get(SETTING_AUTO_UPDATE_DATE); if (autoUpdate) { @@ -241,7 +241,7 @@ export class Article { * Format the date to the defined format */ public static formatDate(dateValue: Date) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const dateFormat = config.get(SETTING_DATE_FORMAT) as string; if (dateFormat && typeof dateFormat === "string") { diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index 33ca1c8d..21bc4898 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -3,7 +3,7 @@ import { ArticleHelper } from './../helpers/ArticleHelper'; import { basename, dirname, extname, join } from "path"; import { existsSync, statSync, unlinkSync, writeFileSync } from "fs"; import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env } from "vscode"; -import { SettingsHelper } from '../helpers'; +import { Settings as SettingsHelper } from '../helpers'; import { TaxonomyType } from '../models'; import { Folders } from './Folders'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; @@ -120,6 +120,12 @@ export class Dashboard { Dashboard.getSettings(); }); + workspace.onDidChangeTextDocument((e) => { + if (e.document.fileName === "frontmatter.json") { + console.log("frontmatter.json changed"); + } + }); + Dashboard.webview.webview.onDidReceiveMessage(async (msg) => { switch(msg.command) { case DashboardMessage.getViewType: diff --git a/src/commands/Folders.ts b/src/commands/Folders.ts index b068b63b..e89f394a 100644 --- a/src/commands/Folders.ts +++ b/src/commands/Folders.ts @@ -6,7 +6,7 @@ import uniqBy = require("lodash.uniqby"); import { Template } from "./Template"; import { Notifications } from "../helpers/Notifications"; import { CONTEXT } from "../constants/context"; -import { SettingsHelper } from "../helpers"; +import { Settings } from "../helpers"; export const WORKSPACE_PLACEHOLDER = `[[workspace]]`; @@ -197,7 +197,7 @@ export class Folders { * @returns */ public static get(): ContentFolder[] { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const wsFolder = Folders.getWorkspaceFolder(); const folders: ContentFolder[] = config.get(SETTINGS_CONTENT_PAGE_FOLDERS) as ContentFolder[]; @@ -212,7 +212,7 @@ export class Folders { * @param folders */ private static async update(folders: ContentFolder[]) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const wsFolder = Folders.getWorkspaceFolder(); await config.update(SETTINGS_CONTENT_PAGE_FOLDERS, folders.map(folder => ({ title: folder.title, path: Folders.relWsFolder(folder, wsFolder) }))); } diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index dad21306..36a7d3d3 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -2,7 +2,7 @@ import { SETTING_PREVIEW_HOST, SETTING_PREVIEW_PATHNAME } from './../constants/s import { ArticleHelper } from './../helpers/ArticleHelper'; import { join } from "path"; import { commands, env, Uri, ViewColumn, window } from "vscode"; -import { SettingsHelper } from '../helpers'; +import { Settings } from '../helpers'; import { PreviewSettings } from '../models'; import { format } from 'date-fns'; import { CONTEXT } from '../constants/context'; @@ -125,7 +125,7 @@ export class Preview { */ public static getSettings(): PreviewSettings { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const host = config.get(SETTING_PREVIEW_HOST); const pathname = config.get(SETTING_PREVIEW_PATHNAME); diff --git a/src/commands/Settings.ts b/src/commands/Settings.ts index 0a10e754..5995267e 100644 --- a/src/commands/Settings.ts +++ b/src/commands/Settings.ts @@ -2,8 +2,8 @@ import * as vscode from 'vscode'; import * as matter from 'gray-matter'; import * as fs from 'fs'; import { TaxonomyType } from "../models"; -import { CONFIG_KEY, SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, EXTENSION_NAME } from '../constants'; -import { ArticleHelper, SettingsHelper, FilesHelper } from '../helpers'; +import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, EXTENSION_NAME } from '../constants'; +import { ArticleHelper, Settings as SettingsHelper, FilesHelper } from '../helpers'; import { TomlEngine, getFmLanguage, getFormatOpts } from '../helpers/TomlEngine'; import { DumpOptions } from 'js-yaml'; import { Notifications } from '../helpers/Notifications'; diff --git a/src/commands/StatusListener.ts b/src/commands/StatusListener.ts index e6171242..aaa1e670 100644 --- a/src/commands/StatusListener.ts +++ b/src/commands/StatusListener.ts @@ -1,6 +1,6 @@ import { SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH } from './../constants/settings'; import * as vscode from 'vscode'; -import { ArticleHelper, SeoHelper, SettingsHelper } from '../helpers'; +import { ArticleHelper, SeoHelper, Settings } from '../helpers'; import { ExplorerView } from '../explorerView/ExplorerView'; import { DefaultFields } from '../constants'; @@ -37,7 +37,7 @@ export class StatusListener { collection.clear(); // Retrieve the SEO config properties - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const titleLength = config.get(SETTING_SEO_TITLE_LENGTH) as number || -1; const descLength = config.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1; const fieldName = config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description; diff --git a/src/commands/Template.ts b/src/commands/Template.ts index 5a3baa75..a0597c69 100644 --- a/src/commands/Template.ts +++ b/src/commands/Template.ts @@ -1,10 +1,10 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; -import { CONFIG_KEY, SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants'; +import { SETTING_TEMPLATES_FOLDER, SETTING_TEMPLATES_PREFIX } from '../constants'; import { format } from 'date-fns'; import sanitize from '../helpers/Sanitize'; -import { ArticleHelper, SettingsHelper } from '../helpers'; +import { ArticleHelper, Settings } from '../helpers'; import { Article } from '.'; import { Notifications } from '../helpers/Notifications'; import { CONTEXT } from '../constants/context'; @@ -93,7 +93,7 @@ export class Template { * Create from a template */ public static async create(folderPath: string) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const folder = config.get(SETTING_TEMPLATES_FOLDER); const prefix = config.get(SETTING_TEMPLATES_PREFIX); @@ -188,7 +188,7 @@ export class Template { * Get the folder settings */ public static getSettings() { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const folder = config.get(SETTING_TEMPLATES_FOLDER); return folder; } diff --git a/src/explorerView/ExplorerView.ts b/src/explorerView/ExplorerView.ts index 03d8b026..796ffab2 100644 --- a/src/explorerView/ExplorerView.ts +++ b/src/explorerView/ExplorerView.ts @@ -4,7 +4,7 @@ import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPD import * as os from 'os'; import { PanelSettings, CustomScript } from '../models/PanelSettings'; import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode"; -import { ArticleHelper, SettingsHelper } from "../helpers"; +import { ArticleHelper, Settings } from "../helpers"; import { Command } from "../panelWebView/Command"; import { CommandToCode } from '../panelWebView/CommandToCode'; import { Article } from '../commands'; @@ -207,6 +207,13 @@ export class ExplorerView implements WebviewViewProvider, Disposable { workspace.onDidChangeConfiguration(() => { this.getSettings(); }); + + workspace.onDidChangeTextDocument((e) => { + console.log(e.document.fileName); + if (e.document.fileName === "frontmatter.json") { + console.log("frontmatter.json changed"); + } + }); } /** @@ -216,7 +223,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { public pushMetadata(metadata: any) { const wsFolder = Folders.getWorkspaceFolder(); const filePath = window.activeTextEditor?.document.uri.fsPath; - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const commaSeparated = config.get(SETTING_COMMA_SEPARATED_FIELDS); const staticFolder = config.get(SETTINGS_CONTENT_STATIC_FOLDERS); const contentTypes = config.get(SETTING_TAXONOMY_CONTENT_TYPES); @@ -331,7 +338,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * @param msg */ private runCustomScript(msg: { command: string, data: any}) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const scripts: CustomScript[] | undefined = config.get(SETTING_CUSTOM_SCRIPTS); if (msg?.data?.title && msg?.data?.script && scripts) { @@ -382,7 +389,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * Retrieve the extension settings */ public async getSettings() { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); this.postWebviewMessage({ command: Command.settings, @@ -466,7 +473,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { */ private async addTags(tagType: TagType, value: string) { if (value) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); let options = tagType === TagType.tags ? config.get(SETTING_TAXONOMY_TAGS) : config.get(SETTING_TAXONOMY_CATEGORIES); if (!options) { @@ -475,7 +482,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { options.push(value); const taxType = tagType === TagType.tags ? TaxonomyType.Tag : TaxonomyType.Category; - await SettingsHelper.update(taxType, options); + await Settings.update(taxType, options); } } @@ -574,7 +581,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * Update the preview URL */ private async updatePreviewUrl(previewUrl: string) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); await config.update(SETTING_PREVIEW_HOST, previewUrl); this.getSettings(); } @@ -583,7 +590,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * Toggle the Front Matter highlighting */ private async updateFmHighlight(autoUpdate: boolean) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); await config.update(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, autoUpdate); this.getSettings(); } @@ -592,7 +599,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { * Toggle the modified auto-update setting */ private async updateModifiedUpdating(autoUpdate: boolean) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); await config.update(SETTING_AUTO_UPDATE_DATE, autoUpdate); this.getSettings(); } diff --git a/src/extension.ts b/src/extension.ts index b94f3094..1ade4a62 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -124,7 +124,7 @@ export async function activate(context: vscode.ExtensionContext) { Template.init(); Preview.init(); - const exView = ExplorerView.getInstance(); + const exView = ExplorerView.getInstance(); exView.getSettings(); exView.getFoldersAndFiles(); MarkdownFoldingProvider.triggerHighlighting(); diff --git a/src/helpers/ArticleHelper.ts b/src/helpers/ArticleHelper.ts index 7c974423..eb8e9dcf 100644 --- a/src/helpers/ArticleHelper.ts +++ b/src/helpers/ArticleHelper.ts @@ -6,7 +6,7 @@ import * as fs from "fs"; import { DefaultFields, SETTING_COMMA_SEPARATED_FIELDS, SETTING_DATE_FIELD, SETTING_DATE_FORMAT, SETTING_INDENT_ARRAY, SETTING_REMOVE_QUOTES, SETTING_TAXONOMY_CONTENT_TYPES } from '../constants'; import { DumpOptions } from 'js-yaml'; import { TomlEngine, getFmLanguage, getFormatOpts } from './TomlEngine'; -import { SettingsHelper } from '.'; +import { Settings } from '.'; import { parse } from 'date-fns'; import { Notifications } from './Notifications'; import { Article } from '../commands'; @@ -39,7 +39,7 @@ export class ArticleHelper { * @param article */ public static async update(editor: vscode.TextEditor, article: matter.GrayMatterFile) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const removeQuotes = config.get(SETTING_REMOVE_QUOTES) as string[]; const commaSeparated = config.get(SETTING_COMMA_SEPARATED_FIELDS); @@ -72,7 +72,7 @@ export class ArticleHelper { * @param data */ public static stringifyFrontMatter(content: string, data: any) { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const indentArray = config.get(SETTING_INDENT_ARRAY) as boolean; const commaSeparated = config.get(SETTING_COMMA_SEPARATED_FIELDS); @@ -114,7 +114,7 @@ export class ArticleHelper { return; } - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const dateFormat = config.get(SETTING_DATE_FORMAT) as string; const dateField = config.get(SETTING_DATE_FIELD) as string || DefaultFields.PublishingDate; @@ -135,7 +135,7 @@ export class ArticleHelper { * @param updatedMetadata */ public static getContentType(metadata: { [field: string]: string; }): ContentType { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const contentTypes = config.get(SETTING_TAXONOMY_CONTENT_TYPES); if (!contentTypes || !metadata) { @@ -173,7 +173,7 @@ export class ArticleHelper { */ private static parseFile(fileContents: string): matter.GrayMatterFile | null { try { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const commaSeparated = config.get(SETTING_COMMA_SEPARATED_FIELDS); if (fileContents) { diff --git a/src/helpers/Extension.ts b/src/helpers/Extension.ts index cf63ebb7..1c6837d7 100644 --- a/src/helpers/Extension.ts +++ b/src/helpers/Extension.ts @@ -6,7 +6,7 @@ import { DEFAULT_CONTENT_TYPE_NAME } from "../constants/ContentType"; import { EXTENSION_BETA_ID, EXTENSION_ID, EXTENSION_STATE_VERSION } from "../constants/Extension"; import { ContentType } from "../models"; import { Notifications } from "./Notifications"; -import { SettingsHelper } from "./SettingsHelper"; +import { Settings } from "./SettingsHelper"; export class Extension { @@ -66,7 +66,7 @@ export class Extension { * Migrate old settings to new settings */ public async migrateSettings(): Promise { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); // Migration to version 3.1.0 const folders = config.get(SETTINGS_CONTENT_FOLDERS); diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 20dee8fb..4a55336c 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -1,13 +1,90 @@ +import { workspace } from 'vscode'; import * as vscode from 'vscode'; import { TaxonomyType } from '../models'; import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY } from '../constants'; +import { Folders } from '../commands/Folders'; +import { join } from 'path'; +import { existsSync, readFileSync } from 'fs'; -export class SettingsHelper { +export class Settings { + private static config: vscode.WorkspaceConfiguration; + private static globalConfig: any; - public static getConfig(): vscode.WorkspaceConfiguration { - return vscode.workspace.getConfiguration(CONFIG_KEY); + public static init() { + const wsFolder = Folders.getWorkspaceFolder(); + if (wsFolder) { + const fmConfig = join(wsFolder.fsPath, 'frontmatter.json'); + if (existsSync(fmConfig)) { + const localConfig = readFileSync(fmConfig, 'utf8'); + this.globalConfig = JSON.parse(localConfig); + } + } + + this.config = vscode.workspace.getConfiguration(CONFIG_KEY); + + Settings.onConfigChange((global?: any) => { + if (global) { + this.globalConfig = Object.assign({}, global); + } else { + this.config = vscode.workspace.getConfiguration(CONFIG_KEY); + } + }); } + /** + * Check for config changes on global and local settings + * @param callback + */ + public static onConfigChange(callback: (global?: any) => void) { + workspace.onDidChangeConfiguration(() => { + callback(); + }); + + workspace.onDidChangeTextDocument((e) => { + if (e.document.fileName === "frontmatter.json") { + if (e && e.document.fileName === "frontmatter.json") { + const fileContents = e.document.getText(); + const json = JSON.parse(fileContents); + callback(json); + } + } + }); + } + + /** + * Retrieve a setting from global and local config + */ + public static get(name: string): T | undefined{ + const configInpection = this.config.inspect(name); + + let setting = undefined; + const settingKey = `${CONFIG_KEY}.${name}`; + + if (typeof this.globalConfig[settingKey] !== "undefined") { + setting = this.globalConfig[settingKey]; + } + + // Local overrides global + if (configInpection && typeof configInpection.workspaceValue !== undefined) { + setting = configInpection.workspaceValue; + } + + return setting; + } + + /** + * Retrieves the config + * @returns + */ + public static getConfig(): vscode.WorkspaceConfiguration { + return this.config; + } + + /** + * Update a setting + * @param name + * @param value + */ public static async updateSetting(name: string, value: any) { const config = vscode.workspace.getConfiguration(CONFIG_KEY); await config.update(name, value); diff --git a/src/helpers/TomlEngine.ts b/src/helpers/TomlEngine.ts index f0bcc240..fdeda4c3 100644 --- a/src/helpers/TomlEngine.ts +++ b/src/helpers/TomlEngine.ts @@ -1,10 +1,10 @@ import * as vscode from 'vscode'; import * as toml from '@iarna/toml'; import { SETTING_FRONTMATTER_TYPE } from '../constants'; -import { SettingsHelper } from '.'; +import { Settings } from '.'; export const getFmLanguage = (): string => { - const config = SettingsHelper.getConfig(); + const config = Settings.getConfig(); const language = config.get(SETTING_FRONTMATTER_TYPE) as string || "YAML"; return language.toLowerCase(); }; diff --git a/src/providers/MarkdownFoldingProvider.ts b/src/providers/MarkdownFoldingProvider.ts index 7e69beac..17d0fb1f 100644 --- a/src/providers/MarkdownFoldingProvider.ts +++ b/src/providers/MarkdownFoldingProvider.ts @@ -1,7 +1,7 @@ import { TextEditorDecorationType } from 'vscode'; import { CancellationToken, FoldingContext, FoldingRange, FoldingRangeKind, FoldingRangeProvider, Range, TextDocument, window, Position } from 'vscode'; import { SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT } from '../constants'; -import { SettingsHelper } from '../helpers'; +import { Settings } from '../helpers'; import { FrontMatterDecorationProvider } from './FrontMatterDecorationProvider'; export class MarkdownFoldingProvider implements FoldingRangeProvider { @@ -44,8 +44,7 @@ export class MarkdownFoldingProvider implements FoldingRangeProvider { } public static triggerHighlighting() { - const config = SettingsHelper.getConfig(); - const fmHighlight = config.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT); + const fmHighlight = Settings.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT); if (MarkdownFoldingProvider.start !== null && MarkdownFoldingProvider.end !== null && MarkdownFoldingProvider.endLine !== null) { const range = new Range(new Position(MarkdownFoldingProvider.start, 0), new Position(MarkdownFoldingProvider.end, MarkdownFoldingProvider.endLine));