diff --git a/README.md b/README.md
index ddd9322..1690237 100644
--- a/README.md
+++ b/README.md
@@ -58,12 +58,83 @@ I'm just a footer
But other than that, you can use all the basic [tags](https://liquidjs.com/tags/overview.html) and [filters](https://liquidjs.com/filters/overview.html)
+## Custom Filters
+
+For now I'll keep the documentation of the filters here, when they become more I'll move it somewhere more convenient
+
+## `date` filter
+
+The LiquidJS [built-in `date` filter](https://liquidjs.com/filters/date.html) has been replaced with a custom one.
+For now, if there is a `%` character in the date format the old filter is used, otherwise the new one will take place.
+
+The new `date` filter uses [date-fns](https://date-fns.org/) as library to handle dates, and the format strings to use are showed here: [date-fns formats](https://date-fns.org/v2.21.3/docs/format)
+
+There are also some special words that can be used with this filter:
+
+**"now"**
+`{{ "now" | date: "yyyy-MM-dd" }}`
+Use the date from `new Date` and formatted with the given format
+
+
+**"today"**
+`{{ "today" | date: "yyyy-MM-dd" }}`
+same as `now`
+
+
+**"yesterday"**
+`{{ "yesterday" | date: "yyyy-MM-dd" }}`
+use the [`subDays`](https://date-fns.org/v2.21.3/docs/subDays) function to subtract `1` from `Date.now()` and formatted with the given format
+
+
+**"tomorrow"**
+`{{ "tomorrow" | date: "yyyy-MM-dd" }}`
+use the [`addDays`](https://date-fns.org/v2.21.3/docs/addDays) function to add `1` to `Date.now()` and formatted with the given format
+
+
+### `date` Default format
+
+Now you can use the "Date Format" you defined inside the plugin settings in a easier way:
+
+**Before**: `{{ "now" | date: default_date_format }}`
+
+**Now**: `{{ "now" | date }}` the format you specified in the settings is now used as default
+
+## `days_ago` filter
+
+A simple filter that uses the [`subDays`](https://date-fns.org/v2.21.3/docs/subDays) to subtract days from the current date (`Date.now()`)
+
+Keep in mind that this filter returns a date that needs to be formatted, so needs to be used with the `date` filter, like this:
+
+- `{{ 1 | days_ago | date }}` same as `{{ "yesterday" | date }}`
+- `{{ 2 | days_ago | date }}` return the date from two days ago
+
+This can be used for a "Weekly Review" template, something like:
+
+```
+- [[{{ 7 | days_ago | date }}]]
+- [[{{ 6 | days_ago | date }}]]
+- [[{{ 5 | days_ago | date }}]]
+- [[{{ 4 | days_ago | date }}]]
+- [[{{ 3 | days_ago | date }}]]
+- [[{{ 2 | days_ago | date }}]]
+- [[{{ 1 | days_ago | date }}]]
+```
+
+## `days_after` filter
+
+A simple filter that uses the [`addDays`](https://date-fns.org/v2.21.3/docs/addDays) to add days to the current date (`Date.now()`)
+
+Keep in mind that this filter returns a date that needs to be formatted, so needs to be used with the `date` filter, like this:
+
+- `{{ 1 | days_after | date }}` same as `{{ "tomorrow" | date }}`
+- `{{ 2 | days_after | date }}` return the date from two days from now
+
## Template context
Other than the classic, `date`, `time` and `title` variable you also have:
-| Name | Description |
-| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| Name | Description |
+| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| default_date_format | The date format that you have specified in the plugin settings and can be used like this: {{ "now" | date: default_date_format }} |
| default_time_format | The time format that you have specified in the plugin settings and can be used like this: {{ "now" | date: default_time_format }} |
@@ -73,5 +144,5 @@ For now, this plugin just includes the basic version of LiquidJS, but I want to
- [ ] Add autocomplete to the template folder, excluded folder settings
- [ ] Implement a fuzzy finder for the autocomplete
-- [ ] Implement/install a filter that allows you to write `{{ 1 | days_ago | date: default_date_format }}`
-- [ ] Parse a selected template string, something like you select `{{ "dQw4w9WgXcQ" | youtube_iframe }}` run a command and end up with the parsed result, in this case, the youtube iframe. (the `youtube_iframe` tag does not exist yet)
\ No newline at end of file
+- [x] Implement/install a filter that allows you to write `{{ 1 | days_ago | date: default_date_format }}`
+- [ ] Parse a selected template string, something like you select `{{ "dQw4w9WgXcQ" | youtube_iframe }}` run a command and end up with the parsed result, in this case, the youtube iframe. (the `youtube_iframe` tag does not exist yet)
diff --git a/manifest.json b/manifest.json
index 94a3076..cfb757d 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "liquid-templates",
"name": "Liquid Templates",
- "version": "0.1.5",
+ "version": "0.2.0",
"minAppVersion": "0.12.3",
"description": "Empower your template with LiquidJS tags",
"author": "diomedet",
diff --git a/package.json b/package.json
index c8d1123..dea2a3d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "liquid-templates",
- "version": "0.1.5",
+ "version": "0.2.0",
"description": "Empower your template with LiquidJS tags",
"main": "main.js",
"scripts": {
@@ -22,6 +22,7 @@
"typescript": "^4.2.4"
},
"dependencies": {
+ "date-fns": "^2.21.3",
"liquidjs": "^9.25.0",
"lodash": "^4.17.21"
}
diff --git a/rollup.config.js b/rollup.config.js
index dd4d041..80651fd 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -4,7 +4,7 @@ import commonjs from '@rollup/plugin-commonjs';
const isProd = (process.env.BUILD === 'production');
-const banner =
+const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
@@ -12,7 +12,7 @@ if you want to view the source visit the plugins github repository
`;
export default {
- input: 'main.ts',
+ input: 'src/main.ts',
output: {
dir: '.',
sourcemap: 'inline',
@@ -27,4 +27,4 @@ export default {
nodeResolve({browser: true}),
commonjs(),
]
-};
\ No newline at end of file
+};
diff --git a/src/engine/filters/base_filter.ts b/src/engine/filters/base_filter.ts
new file mode 100644
index 0000000..ac0c4aa
--- /dev/null
+++ b/src/engine/filters/base_filter.ts
@@ -0,0 +1,26 @@
+import { Liquid, FilterImplOptions } from 'liquidjs';
+
+import LiquidTemplates from '../../main';
+
+export interface BaseFilterProps {
+ engine: Liquid,
+ plugin: LiquidTemplates,
+ filterName?: string;
+}
+
+export class BaseFilter {
+ readonly plugin: LiquidTemplates;
+ readonly engine: Liquid;
+ readonly filterName: string;
+ handler: FilterImplOptions;
+
+ constructor({ plugin, engine, filterName }: BaseFilterProps) {
+ this.plugin = plugin;
+ this.engine = engine;
+ this.filterName = filterName;
+ }
+
+ register(): void {
+ this.engine.registerFilter(this.filterName, this.handler);
+ }
+}
diff --git a/src/engine/filters/date.ts b/src/engine/filters/date.ts
new file mode 100644
index 0000000..a5f1312
--- /dev/null
+++ b/src/engine/filters/date.ts
@@ -0,0 +1,41 @@
+import { addDays, format, subDays } from 'date-fns';
+
+import { BaseFilter, BaseFilterProps } from './base_filter';
+
+export default class DateFiler extends BaseFilter {
+ originalFilter: Function;
+
+ constructor(props: BaseFilterProps) {
+ super({ ...props, filterName: 'date' });
+ this.originalFilter = this.engine.filters.get('date');
+ }
+
+ handler = (givenValue: number | string, dateFormat?: string): string | number => {
+ const dateToFormat = (typeof givenValue === 'number')
+ ? givenValue
+ : this.parseDate(givenValue)
+ const formatToUse = dateFormat || this.plugin.settings.dateFormat;
+
+ // TODO: improve or remove me: keep backwards compatibility with the current 0.1.5 version
+ if (formatToUse.includes('%')) {
+ // TODO: send a notification when using this function
+ return this.originalFilter(givenValue, dateFormat);
+ }
+
+ try {
+ return format(dateToFormat, formatToUse)
+ } catch (e) {
+ if (!(e instanceof RangeError)) throw e;
+ }
+
+ return givenValue;
+ }
+
+ parseDate = (stringToParse: string): Date => {
+ if(['now', 'today'].includes(stringToParse)) return new Date;
+ if(stringToParse === 'yesterday') return subDays(Date.now(), 1);
+ if(stringToParse === 'tomorrow') return addDays(Date.now(), 1);
+ // try to parse the given string
+ return new Date(Date.parse(stringToParse));
+ }
+}
diff --git a/src/engine/filters/days_after.ts b/src/engine/filters/days_after.ts
new file mode 100644
index 0000000..56e3321
--- /dev/null
+++ b/src/engine/filters/days_after.ts
@@ -0,0 +1,11 @@
+import { addDays } from 'date-fns';
+
+import { BaseFilter, BaseFilterProps } from './base_filter';
+
+export default class DaysAfter extends BaseFilter {
+ constructor(props: BaseFilterProps) {
+ super({ ...props, filterName: 'days_after' });
+ }
+
+ handler = (daysToSub: number): Date => addDays(Date.now(), daysToSub);
+}
diff --git a/src/engine/filters/days_ago.ts b/src/engine/filters/days_ago.ts
new file mode 100644
index 0000000..758e8a7
--- /dev/null
+++ b/src/engine/filters/days_ago.ts
@@ -0,0 +1,11 @@
+import { subDays } from 'date-fns';
+
+import { BaseFilter, BaseFilterProps } from './base_filter';
+
+export default class DaysAgo extends BaseFilter {
+ constructor(props: BaseFilterProps) {
+ super({ ...props, filterName: 'days_ago' });
+ }
+
+ handler = (daysToSub: number): Date => subDays(Date.now(), daysToSub);
+}
diff --git a/src/engine/index.ts b/src/engine/index.ts
new file mode 100644
index 0000000..46cb557
--- /dev/null
+++ b/src/engine/index.ts
@@ -0,0 +1,48 @@
+import { App } from 'obsidian';
+import { Liquid } from 'liquidjs';
+
+import LiquidTemplates from '../main';
+import { getTFilesFromFolder } from 'src/utils';
+
+import DateFilter from './filters/date';
+import DaysAgo from './filters/days_ago';
+import DaysAfter from './filters/days_after';
+
+export function applyCustomFilters(engine: Liquid, plugin: LiquidTemplates) {
+ [
+ DateFilter,
+ DaysAgo,
+ DaysAfter
+ ].forEach(filter => new filter({ plugin, engine }).register())
+}
+
+export function initEngine(app: App, plugin: LiquidTemplates): Liquid {
+ const engine = new Liquid({
+ fs: {
+ readFileSync: (file) => {
+ // TODO: implement me
+ return "";
+ },
+ readFile: async (file) => {
+ const [fileName, ...folder] = file.split('/').reverse()
+ const folderToCheck = [plugin.settings.templatesFolder, ...folder.reverse()].join('/')
+ // TODO: find a better way to do this
+ const fileObj = getTFilesFromFolder(app, folderToCheck).find(f => f.basename === fileName);
+ return app.vault.read(fileObj);
+ },
+ existsSync: () => {
+ return true
+ },
+ exists: async () => {
+ return true
+ },
+ resolve: (_root, file, _ext) => {
+ return file
+ }
+ },
+ extname: '.md',
+ greedy: true,
+ });
+ applyCustomFilters(engine, plugin);
+ return engine;
+}
diff --git a/main.ts b/src/main.ts
similarity index 56%
rename from main.ts
rename to src/main.ts
index 2dbfada..f909dbd 100644
--- a/main.ts
+++ b/src/main.ts
@@ -1,3 +1,4 @@
+import { get, isEmpty } from "lodash";
import { Plugin } from "obsidian";
import { LiquidTemplatesSettings, DEFAULT_SETTINGS, LiquidTemplatesSettingsTab } from "./settings";
@@ -40,11 +41,29 @@ export default class LiquidTemplates extends Plugin {
};
async loadSettings(): Promise {
- this.settings = Object.assign({ ...DEFAULT_SETTINGS }, await this.loadData());
+ this.settings = this.settingsWithDefault(await this.loadData());
}
async saveSettings(): Promise {
+ // the templates folder can change
this.setupAutosuggest();
await this.saveData(this.settings);
}
+
+ settingsWithDefault(savedSettings: LiquidTemplatesSettings): LiquidTemplatesSettings {
+ // TODO: improve me
+ return {
+ templatesFolder: this.getSettingOrDefault(savedSettings, 'templatesFolder'),
+ excludeFolders: this.getSettingOrDefault(savedSettings, 'excludeFolders'),
+ dateFormat: this.getSettingOrDefault(savedSettings, 'dateFormat'),
+ timeFormat: this.getSettingOrDefault(savedSettings, 'timeFormat'),
+ autocompleteTrigger: this.getSettingOrDefault(savedSettings, 'autocompleteTrigger'),
+ }
+ }
+
+ getSettingOrDefault(savedSettings: LiquidTemplatesSettings, settingKey: string, defaultValue?: any): any {
+ let currentValue = get(savedSettings, settingKey)
+ if (!isEmpty(currentValue)) return currentValue;
+ return get(DEFAULT_SETTINGS, settingKey, defaultValue);
+ }
}
diff --git a/settings.ts b/src/settings.ts
similarity index 72%
rename from settings.ts
rename to src/settings.ts
index 24a641b..5a0a2d5 100644
--- a/settings.ts
+++ b/src/settings.ts
@@ -1,6 +1,6 @@
-import { get, isEmpty } from "lodash";
import { App, PluginSettingTab, Setting } from "obsidian";
import LiquidTemplates from "./main";
+import { format } from 'date-fns';
export interface LiquidTemplatesSettings {
templatesFolder: string;
@@ -13,8 +13,8 @@ export interface LiquidTemplatesSettings {
export const DEFAULT_SETTINGS: LiquidTemplatesSettings = {
templatesFolder: 'templates',
excludeFolders: "",
- dateFormat: '%Y-%m-%d',
- timeFormat: '%H:%M',
+ dateFormat: 'yyyy-MM-dd',
+ timeFormat: 'k:m',
autocompleteTrigger: ';;'
};
@@ -60,9 +60,26 @@ export class LiquidTemplatesSettingsTab extends PluginSettingTab {
})
);
+ const dateFormatDesc = document.createDocumentFragment();
+ dateFormatDesc.append(
+ 'Output format for render dates.',
+ dateFormatDesc.createEl('br'),
+ 'For more syntax, refer to ',
+ dateFormatDesc.createEl("a", {
+ href: "https://date-fns.org/v2.21.3/docs/format",
+ text: "format reference",
+ }),
+ dateFormatDesc.createEl('br'),
+ 'current format: ',
+ dateFormatDesc.createEl('b', {
+ cls: 'u-pop',
+ text: format(new Date, this.plugin.settings.dateFormat)
+ })
+ );
+
new Setting(containerEl)
.setName('Date format')
- .setDesc('Output format for render dates')
+ .setDesc(dateFormatDesc)
.addText((text) =>
text
.setPlaceholder(DEFAULT_SETTINGS.dateFormat)
@@ -73,9 +90,27 @@ export class LiquidTemplatesSettingsTab extends PluginSettingTab {
})
);
+
+ const timeFormatDesc = document.createDocumentFragment();
+ timeFormatDesc.append(
+ 'Output format for render time.',
+ timeFormatDesc.createEl('br'),
+ 'For more syntax, refer to ',
+ timeFormatDesc.createEl("a", {
+ href: "https://date-fns.org/v2.21.3/docs/format",
+ text: "format reference",
+ }),
+ timeFormatDesc.createEl('br'),
+ 'current format: ',
+ timeFormatDesc.createEl('b', {
+ cls: 'u-pop',
+ text: format(new Date, this.plugin.settings.timeFormat)
+ })
+ );
+
new Setting(containerEl)
.setName('Time format')
- .setDesc('Output format for render time')
+ .setDesc(timeFormatDesc)
.addText((text) =>
text
.setPlaceholder(DEFAULT_SETTINGS.timeFormat)
diff --git a/suggest/codemirror-suggest.ts b/src/suggest/codemirror-suggest.ts
similarity index 100%
rename from suggest/codemirror-suggest.ts
rename to src/suggest/codemirror-suggest.ts
diff --git a/suggest/suggest.ts b/src/suggest/suggest.ts
similarity index 100%
rename from suggest/suggest.ts
rename to src/suggest/suggest.ts
diff --git a/suggest/template-suggest.ts b/src/suggest/template-suggest.ts
similarity index 64%
rename from suggest/template-suggest.ts
rename to src/suggest/template-suggest.ts
index ff9a0c1..423638d 100644
--- a/suggest/template-suggest.ts
+++ b/src/suggest/template-suggest.ts
@@ -1,8 +1,11 @@
import { App, TFile } from "obsidian";
import { Liquid } from "liquidjs";
-import type PoweredTemplates from "../main";
+import { format } from "date-fns";
+
import CodeMirrorSuggest from "./codemirror-suggest";
-import { getTFilesFromFolder } from "utils";
+import type PoweredTemplates from "../main";
+import { getTFilesFromFolder } from "../utils";
+import { initEngine } from "src/engine";
interface ITemplateCompletion {
label: string;
@@ -20,34 +23,7 @@ export default class TemplateSuggest extends CodeMirrorSuggest {
- // TODO: implement me
- return "";
- },
- readFile: async (file) => {
- const [fileName, ...folder] = file.split('/').reverse()
- const { templatesFolder } = this.plugin.settings;
- const folderToCheck = [templatesFolder, ...folder.reverse()].join('/')
- // TODO: find a better way to do this
- const fileObj = getTFilesFromFolder(app, folderToCheck).find(f => f.basename === fileName);
- return app.vault.read(fileObj);
- },
- existsSync: () => {
- return true
- },
- exists: async () => {
- return true
- },
- resolve: (_root, file, _ext) => {
- return file
- }
- },
- extname: '.md',
- greedy: true,
- });
+ this.engine = initEngine(app, plugin);
}
open(): void {
@@ -83,13 +59,18 @@ export default class TemplateSuggest extends CodeMirrorSuggest