diff --git a/esbuild.config.mjs b/esbuild.config.mjs
index 4697ae9f..bb77bbf5 100644
--- a/esbuild.config.mjs
+++ b/esbuild.config.mjs
@@ -25,7 +25,7 @@ const mockedPlugins = [replace({
// update usage of moment from obsidian to the node implementation of moment we have
'import {moment} from \'obsidian\';': 'import moment from \'moment\';',
// remove the use of obsidian in the options to allow for docs.js to run
- 'import {Setting} from \'obsidian\';': '',
+ 'import {App, Setting, ToggleComponent} from \'obsidian\';': '',
// remove the use of obsidian in settings helper to allow for docs.js to run
'import {App, MarkdownRenderer} from \'obsidian\';': '',
// remove the use of obsidian in the auto-correct files picker to allow for docs.js to run
@@ -38,6 +38,8 @@ const mockedPlugins = [replace({
'import {App, TFile} from \'obsidian\';': '',
// remove the use of obsidian in parse results modal to allow for docs.js to run
'import {Modal, App} from \'obsidian\';': 'class Modal {}',
+ // remove the use of app from a couple of settings for docs.js to run
+ 'import {App} from \'obsidian\';': '',
},
delimiters: ['', ''],
})];
diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts
index 2d2fd113..75b9ecd2 100644
--- a/src/lang/locale/en.ts
+++ b/src/lang/locale/en.ts
@@ -111,6 +111,12 @@ export default {
'copy-aria-label': 'Copy',
+ 'disabled-other-rule-notice': 'If you enable {NAME_1}
, it will disable {NAME_2}
. Would you like to proceed?',
+ 'disabled-conflicting-rule-notice': '{NAME_1}, conflicts with {NAME_2}, so it has been turned off. You can switch which setting is off in the settings tab.',
+
+ // confirm-rule-disable-modal.ts
+ 'ok': 'Ok',
+
// parse-results-modal.ts
'parse-results-heading-text': 'Custom Parse Values',
'file-parse-description-text': 'The following is the list of custom replacements found in {FILE}.',
diff --git a/src/main.ts b/src/main.ts
index 78877fe5..0e5fe9da 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -129,69 +129,13 @@ export default class LinterPlugin extends Plugin {
setLogLevel(this.settings.logLevel);
await this.setOrUpdateMomentInstance();
- let updateMade = false;
- if (!this.settings.settingsConvertedToConfigKeyValues) {
- updateMade = await this.moveConfigValuesToKeyBasedFormat();
- }
-
- if ('lintOnFileContentChangeDelay' in this.settings) {
- this.settings.ruleConfigs['yaml-timestamp']['update-on-file-contents-updated'] = this.settings['lintOnFileContentChangeDelay'];
-
- delete this.settings['lintOnFileContentChangeDelay'];
- updateMade = true;
- }
-
- // make sure to load the defaults of any missing rules to make sure they do not cause issues on the settings page
- for (const rule of rules) {
- if (!this.settings.ruleConfigs[rule.alias]) {
- this.settings.ruleConfigs[rule.alias] = rule.getDefaultOptions();
- }
-
- // remove this after a reasonable amount of time
- if (rule.alias == 'space-between-chinese-japanese-or-korean-and-english-or-numbers') {
- const defaults = rule.getDefaultOptions();
- if (!('english-symbols-punctuation-before' in this.settings.ruleConfigs[rule.alias])) {
- this.settings.ruleConfigs[rule.alias]['english-symbols-punctuation-before'] = defaults['english-symbols-punctuation-before'];
- updateMade = true;
- }
-
- if (!('english-symbols-punctuation-after' in this.settings.ruleConfigs[rule.alias])) {
- this.settings.ruleConfigs[rule.alias]['english-symbols-punctuation-after'] = defaults['english-symbols-punctuation-after'];
- updateMade = true;
- }
- } else if (rule.alias == 'yaml-timestamp') {
- const defaults = rule.getDefaultOptions();
- if ('force-retention-of-create-value' in this.settings.ruleConfigs[rule.alias]) {
- if (!('date-created-source-of-truth' in this.settings.ruleConfigs[rule.alias])) {
- if (this.settings.ruleConfigs[rule.alias]['force-retention-of-create-value']) {
- this.settings.ruleConfigs[rule.alias]['date-created-source-of-truth'] = 'frontmatter';
- } else {
- this.settings.ruleConfigs[rule.alias]['date-created-source-of-truth'] = defaults['date-created-source-of-truth'];
- }
- }
-
- delete this.settings.ruleConfigs[rule.alias]['force-retention-of-create-value'];
- updateMade = true;
- }
-
- if (!('date-modified-source-of-truth' in this.settings.ruleConfigs[rule.alias])) {
- this.settings.ruleConfigs[rule.alias]['date-modified-source-of-truth'] = defaults['date-modified-source-of-truth'];
- updateMade = true;
- }
- }
- }
-
this.updatePasteOverrideStatus();
this.updateHasCustomCommandStatus();
-
- if (updateMade) {
- await this.saveSettings();
- }
}
async saveSettings() {
if (!this.hasLoadedMisspellingFiles) {
- await this.loadAutoCorrectFiles();
+ await this.loadAutoCorrectFiles(false);
}
await this.saveData(this.settings);
@@ -328,7 +272,8 @@ export default class LinterPlugin extends Plugin {
this.eventRefs.push(eventRef);
this.app.workspace.onLayoutReady(async () => {
- await this.loadAutoCorrectFiles();
+ await this.makeSureSettingsFilledInAndCleanupSettings();
+ await this.loadAutoCorrectFiles(true);
});
// Source for save setting
@@ -375,13 +320,17 @@ export default class LinterPlugin extends Plugin {
}
}
- async loadAutoCorrectFiles() {
+ async loadAutoCorrectFiles(isOnload: boolean) {
const customAutoCorrectSettings = this.settings.ruleConfigs['auto-correct-common-misspellings'];
if (!customAutoCorrectSettings || !customAutoCorrectSettings.enabled) {
return;
}
await downloadMisspellings(this, async (message: string) => {
+ if (isOnload) {
+ message = 'Obsidian Linter:\n' + message;
+ }
+
new Notice(message);
this.settings.ruleConfigs['auto-correct-common-misspellings'].enabled = false;
@@ -619,6 +568,109 @@ export default class LinterPlugin extends Plugin {
moment.locale(oldLocale);
}
+ private async makeSureSettingsFilledInAndCleanupSettings() {
+ let updateMade = false;
+
+ // migrate settings over to the new format if they are using the now deprecated format that uses
+ // actual settings names for the key in the json
+ if (!this.settings.settingsConvertedToConfigKeyValues) {
+ updateMade = await this.moveConfigValuesToKeyBasedFormat();
+ }
+
+ // move a recently moved setting to its new location
+ if ('lintOnFileContentChangeDelay' in this.settings) {
+ this.settings.ruleConfigs['yaml-timestamp']['update-on-file-contents-updated'] = this.settings['lintOnFileContentChangeDelay'];
+
+ delete this.settings['lintOnFileContentChangeDelay'];
+ updateMade = true;
+ }
+
+ // check for and fix invalid settings
+ let noticeText = 'Obsidian Linter:';
+ let conflictingRulePresent = false;
+ if (this.settings.ruleConfigs['header-increment'] && this.settings.ruleConfigs['header-increment'].enabled && this.settings.ruleConfigs['header-increment']['start-at-h2'] &&
+ this.settings.ruleConfigs['file-name-heading'] && this.settings.ruleConfigs['file-name-heading'].enabled
+ ) {
+ this.settings.ruleConfigs['header-increment']['start-at-h2'] = false;
+ updateMade = true;
+ conflictingRulePresent = true;
+
+ noticeText += '\n' + getTextInLanguage('disabled-conflicting-rule-notice').replace('{NAME_1}', getTextInLanguage('rules.header-increment.start-at-h2.name')).replace('{NAME_2}', getTextInLanguage('rules.file-name-heading.name'));
+ }
+
+ if (this.settings.ruleConfigs['paragraph-blank-lines'] && this.settings.ruleConfigs['paragraph-blank-lines'].enabled &&
+ this.settings.ruleConfigs['two-spaces-between-lines-with-content'] && this.settings.ruleConfigs['two-spaces-between-lines-with-content'].enabled
+ ) {
+ this.settings.ruleConfigs['paragraph-blank-lines'].enabled = false;
+ updateMade = true;
+
+ if (conflictingRulePresent) {
+ noticeText += '\n';
+ }
+ conflictingRulePresent = true;
+
+ noticeText += '\n' + getTextInLanguage('disabled-conflicting-rule-notice').replace('{NAME_1}', getTextInLanguage('rules.paragraph-blank-lines.name')).replace('{NAME_2}', getTextInLanguage('rules.two-spaces-between-lines-with-content.name'));
+ }
+
+ if (conflictingRulePresent) {
+ new Notice(noticeText, userClickTimeout);
+ }
+
+ // make sure to load the defaults of any missing rules to make sure they do not cause issues on the settings page
+ for (const rule of rules) {
+ const ruleDefaults = rule.getDefaultOptions();
+ if (!this.settings.ruleConfigs[rule.alias]) {
+ this.settings.ruleConfigs[rule.alias] = ruleDefaults;
+ updateMade = true;
+ continue;
+ }
+
+ // remove this after a reasonable amount of time
+ if (rule.alias == 'space-between-chinese-japanese-or-korean-and-english-or-numbers') {
+ if (!('english-symbols-punctuation-before' in this.settings.ruleConfigs[rule.alias])) {
+ this.settings.ruleConfigs[rule.alias]['english-symbols-punctuation-before'] = ruleDefaults['english-symbols-punctuation-before'];
+ updateMade = true;
+ }
+
+ if (!('english-symbols-punctuation-after' in this.settings.ruleConfigs[rule.alias])) {
+ this.settings.ruleConfigs[rule.alias]['english-symbols-punctuation-after'] = ruleDefaults['english-symbols-punctuation-after'];
+ updateMade = true;
+ }
+ } else if (rule.alias == 'yaml-timestamp') {
+ const defaults = rule.getDefaultOptions();
+ if ('force-retention-of-create-value' in this.settings.ruleConfigs[rule.alias]) {
+ if (!('date-created-source-of-truth' in this.settings.ruleConfigs[rule.alias])) {
+ if (this.settings.ruleConfigs[rule.alias]['force-retention-of-create-value']) {
+ this.settings.ruleConfigs[rule.alias]['date-created-source-of-truth'] = 'frontmatter';
+ } else {
+ this.settings.ruleConfigs[rule.alias]['date-created-source-of-truth'] = defaults['date-created-source-of-truth'];
+ }
+ }
+
+ delete this.settings.ruleConfigs[rule.alias]['force-retention-of-create-value'];
+ updateMade = true;
+ }
+
+ if (!('date-modified-source-of-truth' in this.settings.ruleConfigs[rule.alias])) {
+ this.settings.ruleConfigs[rule.alias]['date-modified-source-of-truth'] = defaults['date-modified-source-of-truth'];
+ updateMade = true;
+ }
+ }
+
+ // make sure new/empty settings on a rule that exists get filled in with their default value as well
+ for (const key of Object.keys(ruleDefaults)) {
+ if (!Object.hasOwn(this.settings.ruleConfigs[rule.alias], key)) {
+ this.settings.ruleConfigs[rule.alias][key] = ruleDefaults[key];
+ updateMade = true;
+ }
+ }
+ }
+
+ if (updateMade) {
+ await this.saveSettings();
+ }
+ }
+
private createDebouncedFileUpdate(): Debouncer<[TFile, Editor], Promise> {
let delay = 5000;
switch (this.settings.ruleConfigs['yaml-timestamp']['update-on-file-contents-updated'] ?? AfterFileChangeLintTimes.Never) {
diff --git a/src/option.ts b/src/option.ts
index b02e018b..2d49c0d3 100644
--- a/src/option.ts
+++ b/src/option.ts
@@ -1,4 +1,4 @@
-import {Setting} from 'obsidian';
+import {App, Setting, ToggleComponent} from 'obsidian';
import {getTextInLanguage, LanguageStringKey} from './lang/helpers';
import LinterPlugin from './main';
import {hideEl, unhideEl, setElContent} from './ui/helpers';
@@ -63,21 +63,24 @@ export abstract class Option {
export class BooleanOption extends Option {
public defaultValue: boolean;
+ private toggleComponent: ToggleComponent;
- constructor(configKey: string, nameKey: LanguageStringKey, descriptionKey: LanguageStringKey, defaultValue: any, ruleAlias?: string | null, private onChange?: (value: boolean) => void) {
+ constructor(configKey: string, nameKey: LanguageStringKey, descriptionKey: LanguageStringKey, defaultValue: any, ruleAlias?: string | null, private onChange?: (value: boolean, app: App) => void) {
super(configKey, nameKey, descriptionKey, defaultValue, ruleAlias);
}
public display(containerEl: HTMLElement, settings: LinterSettings, plugin: LinterPlugin): void {
this.setting = new Setting(containerEl)
.addToggle((toggle) => {
+ this.toggleComponent = toggle;
+
toggle.setValue(settings.ruleConfigs[this.ruleAlias][this.configKey]);
toggle.onChange((value) => {
this.setOption(value, settings);
plugin.settings = settings;
if (this.onChange) {
- this.onChange(value);
+ this.onChange(value, plugin.app);
}
void plugin.saveSettings();
@@ -86,6 +89,14 @@ export class BooleanOption extends Option {
this.parseNameAndDescriptionAndRemoveSettingBorder();
}
+
+ getValue(): boolean {
+ return this.toggleComponent.getValue();
+ }
+
+ setValue(value: boolean) {
+ this.toggleComponent.setValue(value);
+ }
}
export class TextOption extends Option {
diff --git a/src/rules.ts b/src/rules.ts
index 87c70dd9..754d89fc 100644
--- a/src/rules.ts
+++ b/src/rules.ts
@@ -11,6 +11,7 @@ import {LinterError} from './linter-error';
import {getTextInLanguage, LanguageStringKey} from './lang/helpers';
import {ignoreListOfTypes, IgnoreType} from './utils/ignore-types';
import {LinterSettings} from './settings-data';
+import {App} from 'obsidian';
export type Options = { [optionName: string]: any};
@@ -41,6 +42,7 @@ export class Rule {
* @param {Array