Open brokosz opened 3 years ago
This triggers an architecture alert for me. Namely: is this particular request just one example of a wider need to be able to include or trigger these sorts of "extension function calls" (to coin a phrase)?
We currently have two forms of substitution:
{{function({params})}}
currently available in templates*|TITLE|*
, *|START|*
etc. set available in Event lists (BTW, I'm still really not keen on either syntax; the first is difficult to read, and the second makes documentation difficult. I really wish nmn had borrowed from Obsidian's syntax, as I recommended at the time.)
I've added ability to have pre-defined @mentions and #tags to the quick capture commands already (#57). It wouldn't take long to add another helper function to process extension function calls in any passed text, and use that to satisfy @brokosz's request.
But I wonder if there's a wider picture to consider first? For example I can imagine these new use cases:
The latter is takes us close to advanced functionality in Obsidian that allows users to effectively make database-like queries, with the results embedded in the note. This could be a different way of addressing event lists, or lists of all notes with open tasks etc. etc. etc.
What do we think, @weyert @mikeerickson @dwertheimer @EduardMe ?
Spot on, @jgclark - I made an assumption about function transclusion and I think it's the right way to go long term; seems like it would add to the flexibility of plugins but my concern would be up and downstream dependencies. I would think each plugin should be as atomic and idempotic as possible. So either there needs to be a base library of certain plugin functions as part of the NotePlan app bundle or each plugin developer would need to implement duplicative functions. I definitely don't know the right answer here.
Thanks, @brokosz. I quite agree about keeping things as separate as possible. I see it was early June I raised #11 worrying about the linkage I was finding particularly as we add more "extension functions". We didn't resolve that then, nor at the first plugin-dev-meetup with Eduard.
hey gang,
i have some thoughts on this and will be preparing a more verbose reply in the coming days. I have some household items to take care of this morning, but i will be back on computer this afternoon and i will provide with my thoughts š
DISCLAIMER: This may go deeper than expected š“
@jgclark Do you happen to have any reference material you can point me to on how Obsidian handles this type of situation? I am keen to implementing what they have done (based on your desires, I am sure it is a logical approach). I will give more context after I have reviewed this information (but again, I trust that your recommendation is a solid direction)
@brokosz I agree that it should be idempotent as it already a standard across the industry, thus no need to deviate from that whatsoever. With regards to a common set of libraries, this is something that I have thought about quite a bit. If we think in terms of writing C++ type code, we could have standard libraries (stdlib) that we would import in ALL plugin calls (no need to worry about bloated plugins, as they will be tree shaken accordingly at build time). This way, we do have to replicate code, just import the library and boom (we are already doing this in large part with the common helpers
so this would not be a big mind shift.
In general, I think we should create a standard library stdlib
which would be an isolated JavaScript library (or libraries if need be) so that it can evolve as we realize more and more what types of functions should be available (this is a similar approach used by VSCode and their extension development, see below for an example).
I do think however, that we need to be a bit more explicit with versioning and a single entry point or default
export so that we keep a proper CHANGELOG
for all the commands and reference to specific version when addressing an issue.
import np from ā./lib/noteplanā
ā¦
const config = np.getConfig(ā¦)
Or using standard destructuing
import { getConfig } from ā./lib/noteplanā
ā¦
const = getConfig(ā¦)
This is the standard extension.js
header which in this case imports an npm
package (another angle we could consider and would make versioning and maintaining that much easier (but that can come at a later time after our library API stabilizes).
// const values = config.get('configurations');
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');
OK, that is enough to nibble on for the time being, and more discussions will occur, but I think we are headed in the right direction, and the consensus seems we all want some āstandardsā, and this will evolve. I will give my strong vote to having a standard library (something like /lib/noteplan.js
or an npm
module down the road.
I am wide open to suggestions, just sharing some of the approaches I have done in my āday jobā as we face these types of situations on a regular basis with all of our development groups (we have an average of 40+ developers). We maintain a dozen or so libraries, which are used across all of our applications, so some of it may be overkill for what we are trying to accomplish.
I've enjoyed munching on that, @mikeerickson, and happy to head this way.
On the specifics of Obsidian, I admit that I've not used it extensively, but in particular it's the Templater extension/plugin that I was hoping we could learn from.
Here's a simple case, cribbed from their getting started:
The following template file, that is using Templater template syntax:
---
creation date: <% tp.file.creation_date() %>
modification date: <% tp.file.last_modified_date("dddd Do MMMM YYYY HH:mm:ss") %>
---
<< [[<% tp.date.now("YYYY-MM-DD", -1) %>]] | [[<% tp.date.now("YYYY-MM-DD", 1) %>]] >>
# <% tp.file.title %>
<% tp.web.daily_quote() %>
Will produce the following result when inserted:
--- creation date: 2021-01-07 17:20 modification date: Thursday 7th January 2021 17:20:43 ---
<< [[2021-04-08]] | [[2021-04-10]] >>
Do the best you can until you know better. Then when you know better, do better. — Maya Angelou
You can immediately see the similarities. I liked that it: - has a fairly readable syntax - has namespacing for functions, to help manage growth - already includes the ability to embed JavaScript directly (though naturally that's an advanced concept that would need great care)
I've not attempted to build plugins in this system, so I don't in detail what its standards are, other than seeing they hold to CommonJS which we're not.
NB: This is related to #11.
@jgclark Yea, this is definitely a nice plugin and I think we could provide something very similar for NotePlan, while maintaining the current variable syntax already in use ( or we could move to the same templating engine used by Templater eta if we want maintain consistency with those who may come from Obsidian
It is really just a matter of using {{ }}
versus <% %>
From reading the example from Templater plugin, we could implement the same sort of templates using GitHub - janl/mustache.js: Minimal templating with {{mustaches}} in JavaScript
If I would take the example Templater template you supplied, it could be formatted as such using Mustache (some of this is pseudo code)
const Mustache = require('mustache')
const mustacheTemplate = new Mustache()
const { format } = require('date-fns')
const dailyQuote = (/* whereever we are getting quotes */}
// format dateTime using optional user defined format
// uses `format` from `date-fns`
const formatDateTime = (formatStr = "yyyy-mm-dd HH:ss") => {
return format(formatStr)
}
const mergeData = {
file: {
title: 'Hello World',
creation_date: function(formatStr = null) {
return formatDateTime(formatStr)
},
last_modified_date: function(formatStr = null) {
return formatDateTime(formatStr)
}
},
date: {
now: function(formatStr = null) {
return formatDateTime(formatStr)
}
}
web: {
daily_quote: function() {
return dailyQuote()
}
}
}
const templateData = /* read from NotePlan template */
const result = mustacheTemplate.render(templateData, mergeData)
{{ file.title }}
{{ file.last_modified_date() }}
The following example could use the standard NotePlan Template (using the same object syntax as existing templates)
ļæ¼
creation date: {{ file.creation_date() }}
modification date: {{ file.last_modified_date("dddd Do MMMM YYYY HH:mm:ss") }}
ļæ¼
<< [[ {{ date.now("YYYY-MM-DD", -1) }} ]] | [[ {{ date.now("YYYY-MM-DD", 1) }} ]] >>
{{ file.title }}
{{ web.daily_quote() }}
@jgclark Sorry this is multiple replies, writing this on iPad is sometimes difficult :-)
OK, so we could then provide a series of namespaces objects which users could then use in their templates (objects with or without namespace as necessary)
title (variable)
daily_quote (function)
date.today (function)
file.create_date (function)
file.modified_date (function)
Note: My example was CommonJS format, but could just as easily been in ES6 format
Sorry for not participating too much in the plugin channels these days. Iām again deep into NotePlan coding.
However, I like the suggested templating ālanguageā.
Some thoughts from my side:
Eduard, this is interesting. I shipped a plugin over the weekend called /qtn which allows you to create a quick note from a template with {{templateTags}} in the title. It would not be hard to create a plugin even now to do much of this. Something like:
BTW, I don't want to derail the discussion of the templating engine, which would be amazing.
On Tue, Sep 07, 2021 at 7:35 AM, Eduard Metzger @.***> wrote:
Sorry for not participating too much in the plugin channels these days. Iām again deep into NotePlan coding.
However, I like the suggested templating ālanguageā.
Some thoughts from my side:
- I could add a standard library to NotePlan shipped with the bundle, if that helps (maybe one to process templates). Only problem is that updating it would be a pain.
- For the Meeting Notes feature, I wanted to get right away into templating. Means we got an event in the right sidebar and now we want to create a meeting note for that. Instead of taking one fixed version, I thought of using templates directly, so you can change it as you wish. For example, if you donāt want to have the attendees listed, then remove the template text. So, whatever we arrive at, I would like to use it later and integrate also in the main code. Or meeting notes could be also possibly a plugin in itself, just called from a context menu.
ā You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/NotePlan/plugins/issues/72#issuecomment-914359113, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACEI6VAFSOTCBABGU6CYJETUAYPKPANCNFSM5DL577JA .
Very cool idea, that would be almost all we need for that feature! Iāll add later some proper ālinkageā to it. Plan is that NotePlan check for an x-callback-url in the notes field of the event and then displays a note icon, so the user can directly jump into the note from the event. The plugin could add this as well.
Just a quick update, @brokosz, to say that I haven't forgotten about your original feature request here. Since the architectural conversation it spawned here, in #11 and in our first GH 'discussion', @mikeerickson is now underway with #89 which will implement a richer templating language for plugins.
My hope is that this will mean it's a simple configuration change to add dates in the way you want, without requiring specific plugin coding. So please hold on, and I hope to use it within a week or so as a useful test case.
@jgclark what's your thinking about this one? can it be closed?
Describe the bug Not sure if this could be parsed appended text or needs to be a new configurable feature - I tried to append the date using
{{date({locale: 'sv-SE', dateStyle: 'short'})}}
but it didn't parse. Goal would be to have the added inbox task look like- [ ] buy milk - 2021-09-03 09:04