pbek / QOwnNotes

QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.
https://www.qownnotes.org/
GNU General Public License v2.0
4.69k stars 414 forks source link

Parse Tags from the Note itself #530

Closed cspann closed 6 years ago

cspann commented 7 years ago

Expected

Wouldn't it be better to indicate Tags by e.g "@tag" in the notes itself to make notes really self contained. Otherwise all tags will be lost when migrating to another Tool.

Actual behaviour

Tags ar not contained in the documents but in a separate sqlite db

Steps to reproduce

none required

Output from the debug settings

Relevant log output (you have to enable enable the log widget)

pbek commented 7 years ago

Thank you for your suggestion, but this is about the 4th or 5th time such a request was made and every time an other "format" to store tags was suggested.

Problems are:

cspann commented 7 years ago

there is no standard to include tags in a markdown file

Good point, but like gitlab/github where they have enhanced markdown I see no special problem here.

    how to handle nested tags?

What are nested tags?

it would require to that QON parses and modifies notes by itself when a tag is added to a note (which isn't a very good idea in my opinion and might lead to a lot of troubles)

I wouldn't handle it that way. To add a Tag the user has to write "@tag" somewhere in the document and you then index the file for this tag in your database

it needs to work for several 1000 notes in multiple note subfolders

Parsing is done only on save or when you import a new set of notes. The problem is not that you store the notes in a separate database, but that you store them only there.

Does this make my request more clear?

pbek commented 7 years ago

Good point, but like gitlab/github where they have enhanced markdown I see no special problem here.

there still is no standard and every feature request wanted something else

What are nested tags?

tags in tags

I wouldn't handle it that way. To add a Tag the user has to write "@tag" somewhere in the document and you then index the file for this tag in your database

In the current interface user can add tags with a button, or tag multiple notes, or move tags to other tags, or remove tags from multiple notes and you can rename or remove tags (remove a tag from a note and removing a tag altogether)... The "new way" to handle tags would be needed to be a 2nd, alternate way with a different user interface.

Parsing is done only on save or when you import a new set of notes. The problem is not that you store the notes in a separate database, but that you store them only there.

Parsing would be done every time the current note gets saves (there are people who do that every second) while typing and when all notes need to get reloaded (for example when external changes in a note folder happen).

cspann commented 7 years ago

there still is no standard and every feature request wanted something else

I see no problem in the lack of a standard as long as the tag escape char does not interfere with markdown. When I would have to migrate I could easily replace your tagging symbol with the new one with a simple sed call on all files. The marker should just not be part of markdown. Laverna has this problem because it does exactly what I suggest but it uses the # as tag marker and instantly creates a tag. This is not good as # also prefixes h3 and higher indexed headers.

What are nested tags?

tags in tags

I would say nesting is against the very purpos of tags and would not support it. I would even think about not allowing spaces in Tags

Maboroshy commented 7 years ago

I've used the app that does something very close to request - Wikidpad. It's made around parsing notes for meta-data. I really love the concept, but I ditched it in favor of QOwnNotes without any regret. It's simply not worth the hustle.

Want to save tags outside of SQL? There's an in-app script to put it to a file name.

Want to make it portable? Well, it's not really portable, since there's no standard, and no other apps parse in-text tags. To get tags for Epsilon notes, which I use on my phone, I've ended up getting them from SQL and making an index note with links to all notes sorted by tags.

Getting tags and putting them as the first line of every note in "@" format is not a big thing. But what to do with that next?

Losiara commented 7 years ago

Markdown is an html. Tags my be placed in \ tag as attrs or values. This will be ok in exporting to html/pdf and easy for parsing.

Maboroshy commented 7 years ago

Could you clarify? The app renders <tag>Test</tag> as text. I personally prefer [tag]: (Test) for hidden text, but it has a problem with spaces inside brackets.

Maboroshy commented 7 years ago

I've put some script prototype:

import QtQml 2.0
import com.qownnotes.noteapi 1.0

QtObject {
        function handleNoteTextFileNameHook(note) { 
        if (note.noteText.substring(0,6) == "[tag]:") {
            note.noteText = "[tag]: (" + note.tagNames() + ")\n" + substring(note.noteText.indexOf("\n") + 1)
            script.log( "[tag]: (" + note.tagNames() + ")\n" + note.noteText.split("\n").slice(1).join("\n") )
        } 
        else {
            note.noteText = "[tag]: (" + note.tagNames() + ")\n" + note.noteText
            script.log( "[tag]: (" + note.tagNames() + ")\n" + note.noteText )
        }
    }
}

But it won't work because note.noteText is a one-way thing, I can't write to it from script. You can see how it is supposed to work in the log, try use smaller notes. Tags syntax can be altered.

As far as I understand, it can be implemented only:

Also It would be good to be able get nested tags like nested/tag.

Losiara commented 7 years ago

For example <t tag="a, b, c"><tag> or <tag>a, b, c</tag> and so on. I thought that it should been rendered as invisible in preview without any additional efforts. Because of unknown tags.

IIANM there is a function convertHtmlToText. It should omit this html in rendering.

Maboroshy commented 7 years ago

I use markdown because it's portable and "future-proof", so I'm trying not to put anything tied up to specific app. So I don't think using convertHtmlToText is a convenient solution.

<t tag="a, b, c"><tag> is hidden in QOwnNote, <tag>a, b, c</tag> isn't. But both are visible in Epsilon Notes I use on my phone.

Anyway, if there'd be a script function to edit text, it will be easy to change format.

Losiara commented 7 years ago

But both are visible in Epsilon Notes

Thats because Epsilon notes is out of standart. Usually Markdown - is "wrapped and rendered as html". So "unknown tags" should be ok. But I realised that webengine is not an option for this project. So your option seems right.

fairplay commented 7 years ago

Any inline tagging will be easy to implement using scripting, but API lacks "removeTag" method, is it possible to add it?

pbek commented 7 years ago

@fairplay can you please explain in more detail what you are trying to do?

fairplay commented 7 years ago

I have a little script which parse third line for tags in org-mode format (":tag1:tag2:tag3:")

import QtQml 2.0

QtObject {
    function onNoteStored(note) {
        var lines = note.noteText.split(/\n/);
        var tags = lines[2].split(':');
        for (var i = 0; i < tags.length; i++) {
            var tag = tags[i];
            if (tag.length == 0) {
                continue;
            }
            script.tagCurrentNote(tag);
        }
    }
}

But I need to clean up existing tags before setting new ones. Did I miss smth?

pbek commented 7 years ago

Does that script already work? It's a bit dangerous, because script.tagCurrentNote tags the current note, and not the note that is provided with onNoteStored. script.tagCurrentNote also takes care of updating the user interface of the current note.

And your script only would take care of the current note (when you modify it in QOwnNotes), it doesn't take care of all other notes.

fairplay commented 7 years ago

Ye, thanks, it was mistake, but usually it works because it seems that note stored is current note usually. Nevertheless, it causes the error "UNIQUE constraint failed" on the second call, obviously. Can I manipulate note's tags directly somehow, without "tagCurrentNote" usage?

pbek commented 7 years ago

The links between tags and notes are stored in notes.sqlite in your note folder, if that is what you mean.

fairplay commented 7 years ago

I understood that, I mean "directly" using scripting API.

pbek commented 7 years ago

No there isn't. If you would want to play around with the code, https://github.com/pbek/QOwnNotes/blob/develop/src/api/noteapi.cpp is a good point to start. You would need Tag::removeLinkToNote to remove the tag from a note. See: https://github.com/pbek/QOwnNotes/blob/develop/src/entities/tag.cpp#L539

pbek commented 7 years ago

@fairplay, @Maboroshy something to play around with

17.09.5

pbek commented 7 years ago

And I'm working on a QQmlListProperty<NoteApi> NoteApi::fetchAll(int limit, int offset) to fetch all notes, but I haven't tested it yet.

pbek commented 7 years ago

There now is a new release, could you please test it and report if it works for you?

Maboroshy commented 7 years ago

Good stuff. I'll start looking into tags "parser". But it would still require note text modification, that can be done only externally now. When user drops tag by UI the tag should also disappear from the note text. So more Python job again. Not that I'm having anything against it though.

fetchAll thing is interesting. I'm still hoping to resolve the issues I had with externally creating and tagging note in a single script run.

pbek commented 7 years ago

Yes, the new methods were only meant for "one way". Parsing the note text and adding/removing tags. You really also want to modify the note text?

Maboroshy commented 7 years ago

I think it's a must. User would still be able to modify tags by UI, and he will expect the script reacts to it accordingly.

Also, I think that important part of in-text tagging is that such tags have a position. So selecting a tag should bring cursor to it. But I leave it to some second or third version if one ever will be. It would be quite painful.

pbek commented 7 years ago

There are so many operations with tags... Tags can also be removed altogether (I guess than all notes have to be checked and modified) and notes can be tagged in bulk. There must be a hook for everything.

The script would have to decide what to do if a tag should be added to or removed from a note.

pbek commented 7 years ago

And all notes would need to be scanned for tags constantly (every time when a note tree reload would be requested).

pbek commented 7 years ago

Reference - external tagging feature requests

pbek commented 7 years ago

@Maboroshy I started the implementation of such a tagging system. One could then implement a script like this to handle tagging: https://github.com/pbek/QOwnNotes/blob/ccec0bb61b764405c7a07a098410063c817e8900/doc/scripting/note-tagging.qml

Maboroshy commented 7 years ago

Don't you want to talk it through?

With what I know I would do the following script:

Main tag container is note text. Tag db is a secondary container.

Init: 1) Poll tag db for tags of all notes; 2) Parse all note's text for tag words; 3) If there are differences alter tag db based on note text.

Changes in tag db: 1) Detect modification by watching db file file system events; 2) On file change - repoll tag db; 3) Modify note texts accordingly.

Changes in some notes: 1) Detect modification by watching note folder file system events; 2) Reparse note files that were changed; 3) Modify tag db accordingly.

To make such script I don't need many hooks and stuff. I need cross-platform tag db interface that works with all note files, rather than only visible ones, and has no issues with sub-folders or nested tags. I'd rather not use direct SQL queries because they will break on db structure changes. Python can do the rest.

Maboroshy commented 7 years ago

From example script I don't understand how would it work with nested tags. Like two "subtags" with the same name. Also there should be "rename" action.

pbek commented 7 years ago

From example script I don't understand how would it work with nested tags. Like two "subtags" with the same name.

there will be no subtags, the first tag with the name will match

Also there should be "rename" action.

don't know yet, maybe remove and add, maybe an other action in the script...

pbek commented 7 years ago

My current plan is that as soon as such a script is activated the tag db will be "ignored" and tagging information will come from the notes only (synced from the notes into the db).

pbek commented 7 years ago

The current tags will stay in the note db, but the linking to notes will be synced from the notes itself, new tags will be created in this process. They can then be moved around into a parent tag the usual way.

Maboroshy commented 7 years ago

Rename is needed to save position of tag in text. If you remove then add, tag will appear in default position rather than the one user put the tag to.

I think there better be an initial dialog to put all db tags to text, since the db data will be lost.

pbek commented 7 years ago

Good idea!

pbek commented 7 years ago

I think there better be an initial dialog to put all db tags to text, since the db data will be lost.

Hm, but I'm not sure yet how to prevent that the tag-links are removed from the database as soon as the tagging-script is activated...

pbek commented 7 years ago

17.09.6

pbek commented 7 years ago

There now is a new release, could you please test it and report if it works for you with the script note-tagging.qml?

Maboroshy commented 7 years ago

Testing. It works, but sometimes makes surprising stuff. I'll start posting what I find.

If there're two notes named 1, and only one of them is tagged, filtering by the tag will show both 1 notes. Maybe that is also true for normal tagging.

Maboroshy commented 7 years ago

Unable to see tagged items from other sub-folders hurts. I know it's not related to the issue, but I did not touched tagging for a few months, and it hurts me again like the first time.

Maboroshy commented 7 years ago

Looks like tags are parsed on note list reload. So, when I put tag to note, I don't see update until I wonder around note structure a little. That's confusing. With no sub-folder structure it can be even more confusing, since you get updates only manually or creating-deleting notes.

I think the best way would be reparse current note on each save. Don't know if it's appropriate for the app's architecture.

Maboroshy commented 7 years ago

Sometimes app crashes silently on manual folder reload after some in-text tag modification. I'll investigate further. Maybe something is racing.

Maboroshy commented 7 years ago

When I tag a note by UI, I won't see update in text until update. It's worse with removing by UI - if I continue editing unupdated note text, the tag will remain.

Maboroshy commented 7 years ago

Non-latin tags are broken. When I put @幻 to text or tag that by UI, it just won't sync.

pbek commented 7 years ago

I think the best way would be reparse current note on each save. Don't know if it's appropriate for the app's architecture.

the problem is that the note is stored to disk in the background every X seconds, I'll see if I can parse the note before that...

Maboroshy commented 7 years ago

I was thinking about parsing right after that.

pbek commented 7 years ago

When I tag a note by UI, I won't see update in text until update. It's worse with removing by UI - if I continue editing unupdated note text, the tag will remain.

yes, I'm still trying to get around that somehow...

pbek commented 7 years ago

Non-latin tags are broken. When I put @幻 or tag that by UI, it just won't sync.

interesting, so the tag doesn't get into the note text? does the log panel say anything?

pbek commented 7 years ago

I was thinking about parsing right after that.

the background mechanism doesn't know anything about tags... I'll try to do it when the note gets saved to RAM