Merge pull request #2 from oeN/feature/dates-filters

New dates filters
This commit is contained in:
Diomede 2021-05-26 19:06:30 +02:00 committed by GitHub
commit 6c2b50047e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 294 additions and 50 deletions

View file

@ -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"**<br/>
`{{ "now" | date: "yyyy-MM-dd" }}`<br/>
Use the date from `new Date` and formatted with the given format<br/>
**"today"**<br/>
`{{ "today" | date: "yyyy-MM-dd" }}`<br/>
same as `now`<br/>
**"yesterday"**<br/>
`{{ "yesterday" | date: "yyyy-MM-dd" }}`<br/>
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<br/>
**"tomorrow"**<br/>
`{{ "tomorrow" | date: "yyyy-MM-dd" }}`<br/>
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<br/>
### `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: <code>{{ "now" &#124; date: default_date_format }}</code> |
| default_time_format | The time format that you have specified in the plugin settings and can be used like this: <code>{{ "now" &#124; date: default_time_format }}</code> |
@ -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)
- [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)

View file

@ -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",

View file

@ -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"
}

View file

@ -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(),
]
};
};

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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);
}

View file

@ -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);
}

48
src/engine/index.ts Normal file
View file

@ -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;
}

View file

@ -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<void> {
this.settings = Object.assign({ ...DEFAULT_SETTINGS }, await this.loadData());
this.settings = this.settingsWithDefault(await this.loadData());
}
async saveSettings(): Promise<void> {
// 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);
}
}

View file

@ -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)

View file

@ -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<ITemplateCompleti
super(app, plugin.settings.autocompleteTrigger);
this.plugin = plugin;
// TODO: move me in a separate class
this.engine = new Liquid({
fs: {
readFileSync: (file) => {
// 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<ITemplateCompleti
}
async generateContext() {
const {
dateFormat,
timeFormat
} = this.plugin.settings;
return {
// TODO: improve me
date: await this.engine.parseAndRender(`{{ "now" | date: "${this.plugin.settings.dateFormat}"}}`),
time: await this.engine.parseAndRender(`{{ "now" | date: "${this.plugin.settings.timeFormat}"}}`),
date: format(Date.now(), dateFormat),
time: format(Date.now(), timeFormat),
title: await this.app.workspace.getActiveFile().basename,
default_date_format: this.plugin.settings.dateFormat,
default_time_format: this.plugin.settings.timeFormat
default_date_format: dateFormat,
default_time_format: timeFormat
}
}

View file

@ -1,3 +1,3 @@
{
"0.1.5": "0.12.3"
"0.2.0": "0.12.3"
}