diff --git a/.eslintrc.js b/.eslintrc.js index cd3cbd4a..3bab8359 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,6 +12,7 @@ module.exports = { 'parserOptions': { 'ecmaVersion': 12, 'sourceType': 'module', + 'project': ['./tsconfig.json', './packages/*/tsconfig.json'], }, 'plugins': [ '@typescript-eslint', @@ -29,6 +30,7 @@ module.exports = { 'require-jsdoc': 'off', 'unicorn/template-indent': 'error', 'no-unused-vars': 'off', + '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { diff --git a/__integration__/custom-commands.test.ts b/__integration__/custom-commands.test.ts index 3e405961..0135080e 100644 --- a/__integration__/custom-commands.test.ts +++ b/__integration__/custom-commands.test.ts @@ -1,7 +1,5 @@ -import dedent from 'ts-dedent'; import TestLinterPlugin, {IntegrationTestCase} from './main.test'; import {Editor} from 'obsidian'; -import expect from 'expect'; export const customCommandTestCases: IntegrationTestCase[] = [ { @@ -17,33 +15,5 @@ export const customCommandTestCases: IntegrationTestCase[] = [ 'name': 'Format all tables in this file', }]; }, - async assertions(editor: Editor) { - expect(editor.getValue()).toBe(dedent` - | one | two | three | four | - | ----- | --- | ----- | ------------- | - | 1 | ✓ | | | - | 2 | | ✓ | | - | 3 | | ✓ | ✓ | - | 4 | … | | someting else | - | Total | 100 | 20 | 300000000 | - ${''} - | one | two | three | four | - | ----- | --- | ----- | ------------- | - | 1 | ✓ | | | - | 2 | | ✓ | | - | 3 | | ✓ | ✓ | - | 4 | … | | someting else | - | Total | 100 | 20 | 300000000 | - ${''} - | one | two | three | four | - | ----- | --- | ----- | ------------- | - | 1 | ✓ | | | - | 2 | | ✓ | | - | 3 | | ✓ | ✓ | - | 4 | … | | someting else | - | Total | 100 | 20 | 300000000 | - ${''} - `); - }, }, ]; diff --git a/__integration__/main.test.ts b/__integration__/main.test.ts index 177974c6..05befcbe 100644 --- a/__integration__/main.test.ts +++ b/__integration__/main.test.ts @@ -1,28 +1,61 @@ -import {Editor, MarkdownView, Plugin, TFile, normalizePath} from 'obsidian'; +import {Editor, MarkdownView, Notice, Plugin, TFile, normalizePath} from 'obsidian'; import LinterPlugin from 'src/main'; import {obsidianModeTestCases} from './obsidian-mode.test'; import {setWorkspaceItemMode} from './utils.test'; import {customCommandTestCases} from './custom-commands.test'; +import {obsidianYAMLRuleTestCases} from './yaml-rule.test'; +import expect from 'expect'; export type IntegrationTestCase = { name: string, filePath: string, - setup?: (plugin: TestLinterPlugin, editor: Editor) => void, - assertions: (editor: Editor) => void, + setup?: (plugin: TestLinterPlugin, editor: Editor) => Promise, + assertions?: (editor: Editor) => void, + modifyExpected?: (expectedText: string, file: TFile) => string, } +type testStatus = { + name: string, + succeeded: boolean, +} + +const testTimeout = 15000; + export default class TestLinterPlugin extends Plugin { - regularTests: Array = [...obsidianModeTestCases]; + regularTests: Array = [...obsidianModeTestCases, ...obsidianYAMLRuleTestCases]; afterCacheUpdateTests: Array = [...customCommandTestCases]; plugin: LinterPlugin; + private timeoutId: any = undefined; + private testRunNotice: Notice; async onload() { this.addCommand({ id: 'run-linter-tests', name: 'Run Linter Tests', callback: async () => { + if (this.timeoutId != undefined) { + clearTimeout(this.timeoutId); + } + await this.setup(); - await this.runTests(); + + const testStatuses = [] as testStatus[]; + const expectedTestCount = this.regularTests.length + this.afterCacheUpdateTests.length; + this.timeoutId = setTimeout(() => { + console.log(testStatuses); + if (testStatuses.length != expectedTestCount) { + if (this.testRunNotice) { + this.testRunNotice.setMessage(`❌: Tests took too long to run with only ${testStatuses.length} of ${expectedTestCount} tests running in ${testTimeout/1000}s.`); + } else { + console.log('❌', `Tests took too long to run with only ${testStatuses.length} of ${expectedTestCount} tests running in ${testTimeout/1000}s.`); + } + } else { + this.handleTestFinalization(testStatuses); + console.log(`✅ all ${expectedTestCount} tests have completed in the alloted time.`); + } + }, testTimeout); + + await this.runTests(testStatuses, expectedTestCount); }, }); } @@ -37,7 +70,9 @@ export default class TestLinterPlugin extends Plugin { } } - async runTests() { + async runTests(testStatuses: testStatus[], totalTestCount: number) { + this.testRunNotice = new Notice('Starting the Linter\'s Integration Tests', 0); + const activeLeaf = this.getActiveLeaf(); if (!activeLeaf) { console.error('failed to get active leaf'); @@ -48,6 +83,8 @@ export default class TestLinterPlugin extends Plugin { const file = this.getFileFromPath(t.filePath); if (!file) { console.error('failed to get file: ' + t.filePath); + + this.handleTestCompletion(t.name, false, testStatuses, totalTestCount); continue; } @@ -61,23 +98,35 @@ export default class TestLinterPlugin extends Plugin { } await this.plugin.runLinterEditor(activeLeaf.editor); - await t.assertions(activeLeaf.editor); + await this.handleAssertions(t, activeLeaf, file); console.log('✅', t.name); + this.handleTestCompletion(t.name, true, testStatuses, totalTestCount); } catch (e) { console.log('❌', t.name); console.error(e); + + this.handleTestCompletion(t.name, false, testStatuses, totalTestCount); } await this.resetFileContents(activeLeaf, originalText); } - await this.runMetadataTests(this.afterCacheUpdateTests, activeLeaf); + if (testStatuses.length != this.regularTests.length) { + if (this.testRunNotice) { + this.testRunNotice.setMessage(`❌ failed to run all ${this.regularTests.length} regular tests before attempting to start the metadata tests.`); + } else { + console.log(`❌ failed to run all ${this.regularTests.length} regular tests before attempting to start the metadata tests.`); + } + return; + } + + await this.runMetadataTests(this.afterCacheUpdateTests, activeLeaf, testStatuses, totalTestCount); } - async runMetadataTests(tests: IntegrationTestCase[], activeLeaf: MarkdownView) { + async runMetadataTests(tests: IntegrationTestCase[], activeLeaf: MarkdownView, testStatuses: testStatus[], totalTestCount: number) { let index = 0; - let originalText = await this.setupMetadataTest(this, tests[index], activeLeaf); + let originalText = await this.setupMetadataTest(this, tests[index], activeLeaf, testStatuses, totalTestCount); if (originalText == null) { return; } @@ -95,29 +144,29 @@ export default class TestLinterPlugin extends Plugin { const t = tests[index]; try { - await t.assertions(activeLeaf.editor); + await this.handleAssertions(t, activeLeaf, file); console.log('✅', t.name); + this.handleTestCompletion(t.name, true, testStatuses, totalTestCount); } catch (e) { console.log('❌', t.name); console.error(e); + + this.handleTestCompletion(t.name, false, testStatuses, totalTestCount); } await that.resetFileContents(activeLeaf, originalText); originalText = null; - while (index+1 < tests.length && originalText == null) { - originalText = await that.setupMetadataTest(that, tests[++index], activeLeaf); - } - - // remove the custom commands callback once all tests have run - if (index >= tests.length && originalText == null) { + if (index+1 < tests.length) { + originalText = await that.setupMetadataTest(that, tests[++index], activeLeaf, testStatuses, totalTestCount); + } else { // remove the custom commands callback once all tests have run that.plugin.setCustomCommandCallback(null); } }); } - async setupMetadataTest(testPlugin: TestLinterPlugin, t: IntegrationTestCase, activeLeaf: MarkdownView): Promise { + async setupMetadataTest(testPlugin: TestLinterPlugin, t: IntegrationTestCase, activeLeaf: MarkdownView, testStatuses: testStatus[], totalTestCount: number): Promise { const file = this.getFileFromPath(t.filePath); if (!file) { console.error('failed to get file: ' + t.filePath); @@ -135,6 +184,8 @@ export default class TestLinterPlugin extends Plugin { await testPlugin.plugin.runLinterEditor(activeLeaf.editor); } catch (e) { + this.handleTestCompletion(t.name, false, testStatuses, totalTestCount); + console.log('❌', t.name); console.error(e); await testPlugin.resetFileContents(activeLeaf, originalText); @@ -145,10 +196,26 @@ export default class TestLinterPlugin extends Plugin { return originalText; } - onunload(): void { + async onunload(): Promise { if (this.plugin) { - this.plugin.onunload(); + await this.plugin.onunload(); + } + } + + private async handleAssertions(t: IntegrationTestCase, activeLeaf: MarkdownView, file: TFile) { + let expectedText = await this.getExpectedContents(t.filePath.replace('.md', '.linted.md')); + if (t.modifyExpected) { + expectedText = t.modifyExpected(expectedText, file); } + + expect(activeLeaf.editor.getValue()).toBe(expectedText); + if (t.assertions) { + t.assertions(activeLeaf.editor); + } + + console.log('assertions complete for ' + t.filePath); + + return; } private async resetFileContents(activeLeaf: MarkdownView, originalText: string) { @@ -164,6 +231,16 @@ export default class TestLinterPlugin extends Plugin { return activeLeaf; } + private async getExpectedContents(filePath: string): Promise { + const file = this.getFileFromPath(filePath); + if (!file) { + console.error('failed to get file: ' + filePath); + return; + } + + return await this.app.vault.cachedRead(file); + } + private getFileFromPath(filePath: string): TFile { const file = this.app.vault.getAbstractFileByPath(normalizePath(filePath)); if (file instanceof TFile) { @@ -176,4 +253,80 @@ export default class TestLinterPlugin extends Plugin { private async resetSettings() { await this.plugin.loadSettings(); } + + private handleTestCompletion(testName: string, succeeded: boolean, testStatuses: testStatus[], totalTestCount: number) { + testStatuses.push( + { + name: testName, + succeeded: succeeded, + }); + + let numberOfSuccesses = 0; + let numberOfFailures = 0; + for (const testResult of testStatuses) { + if (testResult.succeeded) { + numberOfSuccesses++; + } else { + numberOfFailures++; + } + } + + if (this.testRunNotice) { + let message = `Running the Linter's Integration Tests (${testStatuses.length}/${totalTestCount})`; + message += '\nSo far there '; + + if (numberOfFailures == 1) { + message += 'has been 1 failure'; + } else { + message += `have been ${numberOfFailures} failures`; + } + + message += ' and there '; + + if (numberOfSuccesses == 1) { + message += 'has been 1 success'; + } else { + message += `have been ${numberOfSuccesses} successes`; + } + + message += '.'; + + this.testRunNotice.setMessage(message); + } + } + + private handleTestFinalization(testStatuses: testStatus[]) { + if (this.testRunNotice) { + let message = `Finished running the Linter's Integration Tests.`; + message += '\nThere '; + + let numberOfSuccesses = 0; + let numberOfFailures = 0; + for (const testResult of testStatuses) { + if (testResult.succeeded) { + numberOfSuccesses++; + } else { + numberOfFailures++; + } + } + + if (numberOfFailures == 1) { + message += 'was 1 failure'; + } else { + message += `have been ${numberOfFailures} failures`; + } + + message += ' and there '; + + if (numberOfSuccesses == 1) { + message += 'has been 1 success'; + } else { + message += `have been ${numberOfSuccesses} successes`; + } + + message += '. See the console for more details.'; + + this.testRunNotice.setMessage(message); + } + } } diff --git a/__integration__/obsidian-mode.test.ts b/__integration__/obsidian-mode.test.ts index dff89907..8e80d3a9 100644 --- a/__integration__/obsidian-mode.test.ts +++ b/__integration__/obsidian-mode.test.ts @@ -1,13 +1,12 @@ -import dedent from 'ts-dedent'; import TestLinterPlugin, {IntegrationTestCase} from './main.test'; -import {Editor} from 'obsidian'; +import {Editor, TFile} from 'obsidian'; import expect from 'expect'; import {setWorkspaceItemMode} from './utils.test'; -import {moment} from 'obsidian'; +import moment from 'moment'; const cursorStart = 319; -function modeSetup(plugin: TestLinterPlugin, editor: Editor) { +function modeSetup(plugin: TestLinterPlugin, editor: Editor): Promise { plugin.plugin.settings.ruleConfigs['yaml-key-sort'] = { 'enabled': true, 'yaml-key-priority-sort-order': '', @@ -16,46 +15,24 @@ function modeSetup(plugin: TestLinterPlugin, editor: Editor) { }; editor.setCursor(editor.offsetToPos(cursorStart)); + + return; } function modeAssertions(editor: Editor) { - expect(editor.getValue()).toBe(dedent` - --- - author: - - Somebody - citation: unknown - cover: https:github.com - datePub: 1993 - device: - - dsi - format: - - epub - priority: 2 - publisher: Pokemon Publisher - readingStatus: easy - related: - researchArea: - - scifi - status: - summary: - tags: - - pokemon - - monsters - title: Pokemon - total: 481 - withProject: - --- - # Heading here... - ${''} - Some text here... - ${''} - `); - // one character was added before the cursor expect(editor.posToOffset(editor.getCursor())).toBe(cursorStart+1); } -function edgeCaseSetup(plugin: TestLinterPlugin, _: Editor) { +function edgeCaseExpectedTextModifications(text: string, file: TFile):string { + text = text.replace('{{created_date}}', moment(file.stat.ctime ?? '').format('YYYY-MM-DD')); + text = text.replace('{{modified_date}}', moment().format('YYYY-MM-DD')); + + return text; +} + + +function edgeCaseSetup(plugin: TestLinterPlugin, _: Editor): Promise { plugin.plugin.settings.ruleConfigs['yaml-timestamp'] = { 'enabled': true, 'date-created': true, @@ -103,66 +80,8 @@ function edgeCaseSetup(plugin: TestLinterPlugin, _: Editor) { 'enabled': true, 'style': 'asterisk', }; -} -function edgeCaseAssertions(editor: Editor) { - expect(editor.getValue()).toBe(dedent` - --- - aliases: - - test - tags: - - test1 - - test2 - related: - - "[[test]]" - - "[[test 2]]" - date: 2024-05-22 - class: "[[test]]" - instructor: "[[test]]" - readings: - - "[[test]]" - - "[[test 2#1.1 test chapter]]" - ${''} - created: ${moment().format('YYYY-MM-DD')} - last_modified: ${moment().format('YYYY-MM-DD')} - --- - ${''} - - Focus on XYZ. - ${''} - # I. Test: - ${''} - ## (Document A, paras [para 2] – [para 8], [para 13] – [para 24]; Document B, para. [3]) - ${''} - ### a. test (*Document A, para. [para 2])*? - ${''} - Lorem ipsum dolor: - ${''} - - **test**: **X** v. **Y** *(the allies)* with support from **Y** - ${''} - \t- Lorem ipsum dolor sit amet + consectetur adipiscing elit. Morbi vel ipsum ipsum - - More info ([test](https://www.example.org)): - ${''} - \t- „quote […] quote.” - ${''} - \t- “quote.” - ${''} - \t- “quote.” - ${''} - - **test**: X v Y - ${''} - - **test:** X v Y - ${''} - - (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vel ipsum ipsum.) - ${''} - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vel ipsum ipsum. (Document A, para. [2])? - ${''} - \t- Document A [para 2]: “Lorem [ipsum] dolor sit amet, consectetur adipiscing elit.’,” - \t- *Ut purus est, laoreet non massa id*, *placerat mollis elit*. - ${''} - \t\t- test - ${''} - \t\t- More on this - `); + return; } export const obsidianModeTestCases: IntegrationTestCase[] = [ @@ -170,7 +89,7 @@ export const obsidianModeTestCases: IntegrationTestCase[] = [ name: 'Updating YAML in live preview mode does not break YAML and keeps cursor at the expected location', filePath: 'obsidian-mode/mode-yaml.md', async setup(plugin: TestLinterPlugin, editor: Editor) { - modeSetup(plugin, editor), + await modeSetup(plugin, editor), await setWorkspaceItemMode(plugin.app, false); }, assertions: modeAssertions, @@ -185,15 +104,15 @@ export const obsidianModeTestCases: IntegrationTestCase[] = [ name: 'Updating YAML in live preview mode does not break YAML when an update is being made to the end of the frontmatter', filePath: 'obsidian-mode/edge-case-yaml.md', async setup(plugin: TestLinterPlugin, editor: Editor) { - edgeCaseSetup(plugin, editor), + await edgeCaseSetup(plugin, editor), await setWorkspaceItemMode(plugin.app, false); }, - assertions: edgeCaseAssertions, + modifyExpected: edgeCaseExpectedTextModifications, }, { name: 'Updating YAML in source mode does not break YAML when an update is being made to the end of the frontmatter', filePath: 'obsidian-mode/edge-case-yaml.md', setup: edgeCaseSetup, - assertions: edgeCaseAssertions, + modifyExpected: edgeCaseExpectedTextModifications, }, ]; diff --git a/__integration__/yaml-rule.test.ts b/__integration__/yaml-rule.test.ts new file mode 100644 index 00000000..bb5ce586 --- /dev/null +++ b/__integration__/yaml-rule.test.ts @@ -0,0 +1,23 @@ +import TestLinterPlugin, {IntegrationTestCase} from './main.test'; +import {Editor} from 'obsidian'; + +function addBlankLineAfterSetup(plugin: TestLinterPlugin, _: Editor): Promise { + plugin.plugin.settings.ruleConfigs['add-blank-line-after-yaml'] = { + 'enabled': true, + }; + + return; +} + +export const obsidianYAMLRuleTestCases: IntegrationTestCase[] = [ + { + name: 'Updating a file with no yaml for adding blank lines after yaml should do nothing', + filePath: 'yaml-rules/add-blank-line-after-yaml/no-yaml.md', + setup: addBlankLineAfterSetup, + }, + { + name: 'Updating a file with yaml and no blanks for adding blank lines after yaml should get updated', + filePath: 'yaml-rules/add-blank-line-after-yaml/yaml.md', + setup: addBlankLineAfterSetup, + }, +]; diff --git a/package-lock.json b/package-lock.json index a6d53a96..de4eac5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-linter", - "version": "1.26.0-rc-2", + "version": "1.26.0-rc-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-linter", - "version": "1.26.0-rc-2", + "version": "1.26.0-rc-3", "license": "MIT", "dependencies": { "@popperjs/core": "^2.11.6", @@ -52,6 +52,7 @@ "eslint": "^8.57.0", "eslint-config-google": "^0.14.0", "eslint-plugin-jest": "^27.9.0", + "eslint-plugin-no-floating-promise": "^2.0.0", "eslint-plugin-unicorn": "^51.0.1", "jest": "^29.3.1", "moment": "^2.30.1", @@ -5282,6 +5283,18 @@ } } }, + "node_modules/eslint-plugin-no-floating-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-floating-promise/-/eslint-plugin-no-floating-promise-2.0.0.tgz", + "integrity": "sha512-XVAk+a1Qq3fsfY8tLT64Ky4sdJjZSjx0BsDxjdWbyceU0yWwg1ZKHiWgaOZ0hDYNzrl3qx5MEGrRlTceGzd0ow==", + "dev": true, + "dependencies": { + "requireindex": "1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-plugin-unicorn": { "version": "51.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", @@ -9433,6 +9446,15 @@ "node": ">=0.10.0" } }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -14185,6 +14207,15 @@ "@typescript-eslint/utils": "^5.10.0" } }, + "eslint-plugin-no-floating-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-floating-promise/-/eslint-plugin-no-floating-promise-2.0.0.tgz", + "integrity": "sha512-XVAk+a1Qq3fsfY8tLT64Ky4sdJjZSjx0BsDxjdWbyceU0yWwg1ZKHiWgaOZ0hDYNzrl3qx5MEGrRlTceGzd0ow==", + "dev": true, + "requires": { + "requireindex": "1.2.0" + } + }, "eslint-plugin-unicorn": { "version": "51.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", @@ -17031,6 +17062,12 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", diff --git a/package.json b/package.json index 7faf72fd..1bd9cc7f 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@babel/core": "^7.24.0", "@babel/plugin-proposal-decorators": "^7.24.0", "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", "@babel/preset-env": "^7.24.0", "@babel/preset-typescript": "^7.18.6", "@jest/types": "^29.5.0", @@ -28,6 +29,7 @@ "@types/diff": "^5.0.9", "@types/diff-match-patch": "^1.0.32", "@types/jest": "^29.5.12", + "@types/js-yaml": "^4.0.5", "@types/node": "^20.11.28", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", @@ -47,13 +49,11 @@ "ts-node": "^10.9.2", "tslib": "^2.4.1", "typescript": "^5.4.2", - "unified-lint-rule": "^2.1.1", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@types/js-yaml": "^4.0.5" + "unified-lint-rule": "^2.1.1" }, "dependencies": { - "async-lock": "^1.4.1", "@popperjs/core": "^2.11.6", + "async-lock": "^1.4.1", "diff-match-patch": "^1.0.5", "js-yaml": "^4.1.0", "loglevel": "^1.9.1", diff --git a/src/main.ts b/src/main.ts index 07c2a558..3a118824 100644 --- a/src/main.ts +++ b/src/main.ts @@ -115,7 +115,7 @@ export default class LinterPlugin extends Plugin { } setLogLevel(this.settings.logLevel); - this.setOrUpdateMomentInstance(); + await this.setOrUpdateMomentInstance(); if (!this.settings.settingsConvertedToConfigKeyValues) { this.moveConfigValuesToKeyBasedFormat(); @@ -160,7 +160,7 @@ export default class LinterPlugin extends Plugin { return that.isMarkdownFile(ctx.file); } - that.runLinterEditor(editor); + void that.runLinterEditor(editor); }, icon: iconInfo.file.id, hotkeys: [ @@ -180,7 +180,7 @@ export default class LinterPlugin extends Plugin { } if (!that.shouldIgnoreFile(ctx.file)) { - that.runLinterEditor(editor); + void that.runLinterEditor(editor); } }, icon: iconInfo.file.id, @@ -221,7 +221,7 @@ export default class LinterPlugin extends Plugin { return this.overridePaste; } - this.pasteAsPlainText(editor); + void this.pasteAsPlainText(editor); }, }); } @@ -235,7 +235,7 @@ export default class LinterPlugin extends Plugin { return; } - this.modifyPasteEvent(clipboardEv); + void this.modifyPasteEvent(clipboardEv); }); this.registerEvent(eventRef); this.eventRefs.push(eventRef); @@ -270,7 +270,7 @@ export default class LinterPlugin extends Plugin { if (editor) { const file = this.app.workspace.getActiveFile(); if (!this.shouldIgnoreFile(file) && this.isMarkdownFile(file)) { - this.runLinterEditor(editor); + void this.runLinterEditor(editor); } } } @@ -289,11 +289,11 @@ export default class LinterPlugin extends Plugin { if (this.editorLintFiles.includes(file)) { this.editorLintFiles.remove(file); - this.runCustomCommands(file); + void this.runCustomCommands(file); } else if (this.fileLintFiles.has(file)) { this.fileLintFiles.delete(file); - this.runCustomCommandsInSidebar(file); + void this.runCustomCommandsInSidebar(file); } } @@ -306,9 +306,9 @@ export default class LinterPlugin extends Plugin { const activeFile = this.app.workspace.getActiveFile(); const editor = this.getEditor(); if (activeFile === file && editor) { - this.runLinterEditor(editor); + void this.runLinterEditor(editor); } else { - this.runLinterFile(file); + void this.runLinterFile(file); } }); }); @@ -545,7 +545,7 @@ export default class LinterPlugin extends Plugin { // run custom commands now since no change was made if (!charsAdded && !charsRemoved) { - this.runCustomCommands(file); + void this.runCustomCommands(file); } else { this.editorLintFiles.push(file); } @@ -620,12 +620,9 @@ export default class LinterPlugin extends Plugin { // so we will too. const text = plainClipboard.trim(); if (urlRegex.test(text)) { - // debugger; - console.log('failed: "' + text + '"'); logWarn(getTextInLanguage('logs.paste-link-warning')); return; } - console.log('passed: "' + text + '"'); // prevent default pasting & abort when not successful clipboardEv.stopPropagation(); @@ -837,7 +834,7 @@ export default class LinterPlugin extends Plugin { } this.settings.settingsConvertedToConfigKeyValues = true; - this.saveSettings(); + void this.saveSettings(); setLanguage(window.localStorage.getItem('language')); } diff --git a/src/option.ts b/src/option.ts index 9065a31b..de8dbfae 100644 --- a/src/option.ts +++ b/src/option.ts @@ -61,7 +61,7 @@ export class BooleanOption extends Option { toggle.onChange((value) => { this.setOption(value, settings); plugin.settings = settings; - plugin.saveSettings(); + void plugin.saveSettings(); }); }); @@ -79,7 +79,7 @@ export class TextOption extends Option { textbox.onChange((value) => { this.setOption(value, settings); plugin.settings = settings; - plugin.saveSettings(); + void plugin.saveSettings(); }); }); @@ -97,7 +97,7 @@ export class TextAreaOption extends Option { textbox.onChange((value) => { this.setOption(value, settings); plugin.settings = settings; - plugin.saveSettings(); + void plugin.saveSettings(); }); }); @@ -116,7 +116,7 @@ export class MomentFormatOption extends Option { format.onChange((value) => { this.setOption(value, settings); plugin.settings = settings; - plugin.saveSettings(); + void plugin.saveSettings(); }); }); @@ -165,7 +165,7 @@ export class DropdownOption extends Option { dropdown.onChange((value) => { this.setOption(value, settings); plugin.settings = settings; - plugin.saveSettings(); + void plugin.saveSettings(); }); }); @@ -183,7 +183,7 @@ export class MdFilePickerOption extends Option { settings.ruleConfigs[this.ruleAlias][this.configKey] = settings.ruleConfigs[this.ruleAlias][this.configKey] ?? []; new AutoCorrectFilesPickerOption(containerEl, plugin.settingsTab.component, settings.ruleConfigs[this.ruleAlias][this.configKey], plugin.app, () => { - plugin.saveSettings(); + void plugin.saveSettings(); }, this.nameKey, this.descriptionKey); } } diff --git a/src/ui/components/dropdown-setting.ts b/src/ui/components/dropdown-setting.ts index 80a3a550..40c44f3b 100644 --- a/src/ui/components/dropdown-setting.ts +++ b/src/ui/components/dropdown-setting.ts @@ -26,7 +26,7 @@ export class DropdownSetting extends BaseSetting { dropdown.setValue(this.getString()); dropdown.onChange(async (value) => { - this.saveValue(value); + void this.saveValue(value); }); }); } diff --git a/src/ui/components/number-input-setting.ts b/src/ui/components/number-input-setting.ts index 79b96b71..267815eb 100644 --- a/src/ui/components/number-input-setting.ts +++ b/src/ui/components/number-input-setting.ts @@ -20,7 +20,7 @@ export class NumberInputSetting extends BaseSetting { textbox .setValue(this.getNumber().toString()) .onChange(async (value) => { - this.saveValue(parseInt(value)); + void this.saveValue(parseInt(value)); }); }); } diff --git a/src/ui/components/toggle-setting.ts b/src/ui/components/toggle-setting.ts index 4673ad2e..a2e3b438 100644 --- a/src/ui/components/toggle-setting.ts +++ b/src/ui/components/toggle-setting.ts @@ -16,7 +16,7 @@ export class ToggleSetting extends BaseSetting { toggle .setValue(this.getBoolean()) .onChange(async (value) => { - this.saveValue(value); + void this.saveValue(value); }); }); diff --git a/src/ui/helpers.ts b/src/ui/helpers.ts index cd27e960..b95f34eb 100644 --- a/src/ui/helpers.ts +++ b/src/ui/helpers.ts @@ -1,7 +1,7 @@ import {Component, MarkdownRenderer} from 'obsidian'; export function parseTextToHTMLWithoutOuterParagraph(text: string, containerEl: HTMLElement, component: Component) { - MarkdownRenderer.renderMarkdown(text, containerEl, '', component); + void MarkdownRenderer.renderMarkdown(text, containerEl, '', component); let htmlString = containerEl.innerHTML.trim(); if (htmlString.startsWith('

')) { diff --git a/src/ui/linter-components/tab-components/custom-tab.ts b/src/ui/linter-components/tab-components/custom-tab.ts index bb760e62..8569c5d8 100644 --- a/src/ui/linter-components/tab-components/custom-tab.ts +++ b/src/ui/linter-components/tab-components/custom-tab.ts @@ -13,13 +13,13 @@ export class CustomTab extends Tab { display(): void { const customCommandEl = this.contentEl.createDiv(); const customCommands = new CustomCommandOption(customCommandEl, this.plugin.settingsTab.component, this.plugin.settings.lintCommands, this.app, () => { - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); this.addSettingSearchInfo(customCommandEl, customCommands.name, customCommands.description.replaceAll('\n', ' ') + customCommands.warning.replaceAll('\n', ' ')); const customReplaceEl = this.contentEl.createDiv(); const customRegexes = new CustomReplaceOption(customReplaceEl, this.plugin.settingsTab.component, this.plugin.settings.customRegexes, () => { - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); this.addSettingSearchInfo(customReplaceEl, customRegexes.name, customRegexes.description.replaceAll('\n', ' ') + customRegexes.warning.replaceAll('\n', ' ')); } diff --git a/src/ui/linter-components/tab-components/general-tab.ts b/src/ui/linter-components/tab-components/general-tab.ts index 679702f7..0e80e0db 100644 --- a/src/ui/linter-components/tab-components/general-tab.ts +++ b/src/ui/linter-components/tab-components/general-tab.ts @@ -98,14 +98,14 @@ export class GeneralTab extends Tab { const folderIgnoreEl = this.contentEl.createDiv(); const folderIgnore = new FolderIgnoreOption(folderIgnoreEl, this.plugin.settingsTab.component, this.plugin.settings.foldersToIgnore, this.app, () => { - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); this.addSettingSearchInfo(folderIgnoreEl, folderIgnore.name, folderIgnore.description.replaceAll('\n', ' ')); const filesToIgnoreEl = this.contentEl.createDiv(); const filesToIgnore = new FilesToIgnoreOption(filesToIgnoreEl, this.plugin.settingsTab.component, this.plugin.settings.filesToIgnore, () => { - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); this.addSettingSearchInfo(filesToIgnoreEl, filesToIgnore.name, filesToIgnore.description.replaceAll('\n', ' ')); diff --git a/src/ui/suggesters/suggest.ts b/src/ui/suggesters/suggest.ts index 9d7e500f..b625294b 100644 --- a/src/ui/suggesters/suggest.ts +++ b/src/ui/suggesters/suggest.ts @@ -179,7 +179,7 @@ export abstract class TextInputSuggest implements ISuggestOwner { return; } state.styles.popper.width = targetWidth; - instance.update(); + void instance.update(); }, phase: 'beforeWrite', requires: ['computeStyles'], diff --git a/test-vault/custom-commands/table-format.linted.md b/test-vault/custom-commands/table-format.linted.md new file mode 100644 index 00000000..0efa9e06 --- /dev/null +++ b/test-vault/custom-commands/table-format.linted.md @@ -0,0 +1,23 @@ +| one | two | three | four | +| ----- | --- | ----- | ------------- | +| 1 | ✓ | | | +| 2 | | ✓ | | +| 3 | | ✓ | ✓ | +| 4 | … | | someting else | +| Total | 100 | 20 | 300000000 | + +| one | two | three | four | +| ----- | --- | ----- | ------------- | +| 1 | ✓ | | | +| 2 | | ✓ | | +| 3 | | ✓ | ✓ | +| 4 | … | | someting else | +| Total | 100 | 20 | 300000000 | + +| one | two | three | four | +| ----- | --- | ----- | ------------- | +| 1 | ✓ | | | +| 2 | | ✓ | | +| 3 | | ✓ | ✓ | +| 4 | … | | someting else | +| Total | 100 | 20 | 300000000 | diff --git a/test-vault/obsidian-mode/edge-case-yaml.linted.md b/test-vault/obsidian-mode/edge-case-yaml.linted.md new file mode 100644 index 00000000..7570d965 --- /dev/null +++ b/test-vault/obsidian-mode/edge-case-yaml.linted.md @@ -0,0 +1,55 @@ +--- +aliases: + - test +tags: + - test1 + - test2 +related: + - "[[test]]" + - "[[test 2]]" +date: 2024-05-22 +class: "[[test]]" +instructor: "[[test]]" +readings: + - "[[test]]" + - "[[test 2#1.1 test chapter]]" + +created: {{created_date}} +last_modified: {{modified_date}} +--- + +- Focus on XYZ. + +# I. Test: + +## (Document A, paras [para 2] – [para 8], [para 13] – [para 24]; Document B, para. [3]) + +### a. test (*Document A, para. [para 2])*? + +Lorem ipsum dolor: + +- **test**: **X** v. **Y** *(the allies)* with support from **Y** + + - Lorem ipsum dolor sit amet + consectetur adipiscing elit. Morbi vel ipsum ipsum +- More info ([test](https://www.example.org)): + + - „quote […] quote.” + + - “quote.” + + - “quote.” + +- **test**: X v Y + +- **test:** X v Y + +- (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vel ipsum ipsum.) + +- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vel ipsum ipsum. (Document A, para. [2])? + + - Document A [para 2]: “Lorem [ipsum] dolor sit amet, consectetur adipiscing elit.’,” + - *Ut purus est, laoreet non massa id*, *placerat mollis elit*. + + - test + + - More on this \ No newline at end of file diff --git a/test-vault/obsidian-mode/mode-yaml.linted.md b/test-vault/obsidian-mode/mode-yaml.linted.md new file mode 100644 index 00000000..7262ae1f --- /dev/null +++ b/test-vault/obsidian-mode/mode-yaml.linted.md @@ -0,0 +1,28 @@ +--- +author: + - Somebody +citation: unknown +cover: https:github.com +datePub: 1993 +device: + - dsi +format: + - epub +priority: 2 +publisher: Pokemon Publisher +readingStatus: easy +related: +researchArea: + - scifi +status: +summary: +tags: + - pokemon + - monsters +title: Pokemon +total: 481 +withProject: +--- +# Heading here... + +Some text here... diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.linted.md b/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.linted.md new file mode 100644 index 00000000..8ae80a3a --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.linted.md @@ -0,0 +1,2 @@ +This is a file with no YAML in it. +It remains the same, right? diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.md b/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.md new file mode 100644 index 00000000..8ae80a3a --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/no-yaml.md @@ -0,0 +1,2 @@ +This is a file with no YAML in it. +It remains the same, right? diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.linted.md b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.linted.md new file mode 100644 index 00000000..4e59e263 --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.linted.md @@ -0,0 +1,7 @@ +--- +key: value +--- + + + +No blank \ No newline at end of file diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.md b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.md new file mode 100644 index 00000000..4e59e263 --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml-blanks.md @@ -0,0 +1,7 @@ +--- +key: value +--- + + + +No blank \ No newline at end of file diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.linted.md b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.linted.md new file mode 100644 index 00000000..91de99ec --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.linted.md @@ -0,0 +1,5 @@ +--- +key: value +--- + +No blank diff --git a/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.md b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.md new file mode 100644 index 00000000..2aed0838 --- /dev/null +++ b/test-vault/yaml-rules/add-blank-line-after-yaml/yaml.md @@ -0,0 +1,4 @@ +--- +key: value +--- +No blank