-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added new option allowing for multiline cards with empty lines #1012
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @alberti42 thanks for this, many people have been after this for a long time, and will be very happy when it is released.
A few requests:
- Currently some of the existing unit test cases fail. Please update your enhancement so that the existing tests all pass. See https://www.stephenmwangi.com/obsidian-spaced-repetition/contributing/#code for details
- Update the user facing documentation, see https://www.stephenmwangi.com/obsidian-spaced-repetition/contributing/#documentation
- Let me know and I'll review at that time
- We will need some new unit test cases. You may prefer to wait till I complete step 3. and provide feedback about functionality.
Thanks once again!
Cheers
Hi @alberti42, I've just noticed that you have rearchitected the parser from the ground up. You must have put in much time and thought into this! @4Source initiated the discussion Rework the flashcard seperator/detecting system #1018 noting that there are many outstanding requests that relate to the parser and that it's not very easy to enhance in its current form. So your rearchitect of this part of the plugin might be perfect! I don't have familiarity with https://github.com/pegjs/pegjs/wiki, one question that comes to mind is regarding tokens that you currently have hardcoded in @4Source and @st3v3nmw given the significance of the changes in this PR, would be great if you could both look at this as well. Cheers |
It required much more work than what I thought because I wanted to have a clean solution. I created a true parser. It is quick and easy to maintain for the future. The old parser was quite untransparent and could not be easily extended. The new parser makes use of Peggy. The rules are defined with If you modify The The only part that is not nice in For the If you leave the field empty in Obsidian configuration pane, then the old behavior will apply. PS: All tests are now passed. I also translated the label in the few languages.. although the translation is not great yet. |
Sorry, I did not see this message when I wrote the previous one just a moment ago. Yes, we have a new parser, which should be much easier to understand and maintain. The rules can still be simplified a tiny bit to make the grammar more streamlined.. but let's first see what you think of it.
It now works with the user-based keywords. For this, I required what I called a HACK in the previous message. The HACK is pretty straightforward and does not compromise efficiency. At least, it does not make it slower. However, cosmetically, it does not look nice if you are a purist of good coding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can' say much about the parser it self haven't investigated in it jet. But what I have seen/understand looks great to me for future adjustments and planed features from the discussion
I think this is a bad Idea we had already issues with the
If I understand this correctly I think how this works should be well documented somewhere in dev guide |
Sure. I did not say that this needs to be the default behavior. This is just what I find nice for me. From the preference pane, the user can choose
Yes. We should add one line to explain this step. |
I just meant this should not be the default setting. So user who try this plugin for first time or user there not investigate in the settings have a normally functional experience |
I agree 100%. Actually, I would have left the field as empty as the default setting. When empty, the old behavior applies, which is what probably people in the community expect. |
That's great! @alberti42 , there may be an alternative to the HACK. Have you come across this? Does this help? Cheers |
Hey Ronny, Thanks! Yes, I had read the page before. You can indeed use predicates I have used such predicates already for evaluating dynamically the flags deciding what sybols define a close. They work nicely. The reason why I did not use it for the other symbols such as To try this, we would need to define our own grammar of what are the sequences of characters the user is allowed to type. In principle, we could decide any characters except newlines. However, my guess is that the predication function will be evaluated a lot of times to decide if there is a match or not. There are also other options if you do not like the HACK (I don't like it either). We could generate the parser.mjs anew directly in Obsidian every time the user changes the configuration in the setting pnae. This happens only rarely anyway, and peggyjs is quite fast to generate from the grammar.pegjs a new .mjs file. The downside is that we need to ship with the app the entire transpiler of peggy.js. So, I would avoid it if we find other and better solutions. The other possibility is that we use in the grammar.pegjs different PLACEHOLDERSTRINGS123 for each of the strings to be then replaced by the user. We then write a little .js program of a few lines introducing a second-step transpiler. The purpos is to process the parser.mjs generated by peggyjs. This step can be combined with Such second-step transpiler replaces the static placeholders with javascript code importing the values for each placeholder from the variable This last approach is possibly the most elegant, but not without caveats. If we use static placeholders, peggyjs optimizes the parsing by detecting their string length. Our transpiler would have to replace in the .mjs code the static length with one dynamically optained from the user variables. This is quite efficient in terms of execution speed. However, the transpiler is a bit fragile because it needs to do a surgery on the .mjs file about the length of the PLACEHOLDERS. If something changes in future versions of peggyjs, these changes may break the compatibility with our transpiler, and some small fix will be needed. Other possibilities. I know it is possible to create plugins for peggyjs, but I do not know if these allow to have dynamical keywords. And the last possibility, we improve peggyjs and make a pull request to introduce the feature of dynamical keywords. 😅 Bottom line: If you are happy with the parser, I would focus on testing all features, removing any possible small bugs, and streamlining the grammar file. It can be cleaned up a little. We can then release with the community. We could think how to replace the HACK in the future. In my assessment, the HACK has no impact on the execution speed or anything else. It is not elegant, and it is not easy to understand someone just opens the code for the first time, and was not given any explanation. Best, |
Hi @alberti42 thanks for such a comprehensive post! If I understand correctly, the best way would be for the parser to be generated whenever any of the relevant user options are changed. The downside is needing to include the peggy package (and it's dependencies) increasing memory requirements. Out of curiosity, I tried the following code which dynamically generates the parser: test("hello", () => {
var peg = require("peggy");
var inlineMark = '"::" / "--"';
var grammar = `
start
= q:question inline_mark a:answer { return "Q: " + q + ", A: " + a}
question = text
answer = text
text = t:[a-zA-Z ]+ { return t.join(""); }
inline_mark = ${inlineMark}
`
var parser = peg.generate(grammar);
var actual = parser.parse("What should I eat for dinner::chocolate cake");
expect(actual).toEqual("Q: What should I eat for dinner, A: chocolate cake");
actual = parser.parse("What should I eat for dinner--chocolate cake");
expect(actual).toEqual("Q: What should I eat for dinner, A: chocolate cake");
}); I think this is a very clean solution. In terms of package size, I came across this web site: To me, the benefit outweighs the additional memory usage. I'm wondering if you have had a chance to look at some of the other related PRs and issues: With some of those, there will be more user options, and so whatever we come up with needs to handle the future requirements. Cheers |
I tested your example and worked very nicely. Great that it is so smooth to generate the parser from the grammar.
I agree, speed and memory consumption should not be a problem. Even if this operation takes a bunch of ms, it is only executed rarely, when the user changes the keywords in the configuration pane. I would only add a debouncing solution, so the function is not called to many times, each time that the user press a key in the editing field; see for example https://github.com/alberti42/obsidian-plugins-annotations/blob/main/src/main.ts#L566
Not really. Is there anything in particular you think it is important to consider? I think the parser is very powerful and I cannot imagine a situation that cannot be handled. I was surprised that currently all parsed information is not really stored. At the end, you only store the beginning and end position of the card. In principle, one can make more use of the parser and store the different parts of a flash card in different fields, with annotated the position in the text. But, it seems you did not need this extra structuring of parsed information... so maybe we do not need to put extra work on something like this that is not really needed. |
Any idea when this PR will be merged ? |
That all sounds great. Are you planning to update your code to use this technique?
I'm sure you're right, so let's not worry about future changes right now. Thanks! |
I have implemented it. It worked for my usage cases. There are some tests that did not pass yet. I will have to look at them in the next days to understand what goes wrong. Hopefully, it will be a very quick fix. I tested the time to generate the parser. It is already in the debouncing function, which avoids generating the parser too many times if the user changes the options very quickly. In any case, it takes on my laptop 20 ms. Which is superfast, given the fact that it is generated only once when the plugin is loaded and every time the user changes one of the relevant settings, requiring producing a new parser. So very seldom. |
The other day I was in a hurry and had to leave quickly. I checked the messages generated in the test again. It seems to me that all tests are passed. There is some red warning because of some part of code that are never reached. This concerns code when using So I think it is not so important, and it is very time consuming to write test modules that verify all try and catch constructs. Did you do so before? Or simply avoided using try/catch constructs? I show you a screenshot of what the result of the tests is: Will you handle the integration of the pull request into the main branch? Is there anything else you want me to do? |
Ok. I fetched the upstream changes, and made a single commit with all changes for the new parser. |
Ok. I understood that I need to do some more changes on the test routines. Also my latest commit does not include some changes I did before which I need to reintroduce. I need some more time to finish it. It should not last long. |
I made one more commit. I believe everything works. However, many tests are still not passing because the test routines need to be updated to integrate with the new syntax of the parser. I am afraid I don't have anymore time to invest in this project. Regarding the test modules, it is often very difficult for me to understand the reasons of certain rules that seem to be inserted ad hoc. For example, I had to debug a test in
It was not clear to me at all why if I give:
then it expects something without the space after Also, ideally you would like to parse Obsidian tags with the proper parser. I already made a rule in the grammar. And the frontmatter could be parsed as well with peggy rules without creating any problem. It does not need to be treated separately. I need your support. We should reduce the test cases and streamline the code. I myself cannot invest any more time in this plugin. I am sorry. In the worst case, I will create a new plugin for spaced repetitions from scratch with a shorter and cleaner code. |
I try to help you with writing and fixing some of the test cases I hope I can push to this pr, if not I will create one on your fork |
@ronzulu Not sure this helps you but you can revert a commit. Alternatively you can reset branch without losing the changes not sure how good this works for already pushed commits |
Hi @alberti42 I think I've found a bug whilst writing the user notes. Here is a multiline card with a table in the answer...
This displays correctly during review... But after clicking on Cheers |
Hi Ronny, Good that you found the bug. I will have a look at it. PS: I can reproduce the bug. No need more examples from your side. |
To my understanding it has nothing to do with the tables.
The problem seems to be when you have multicards with empty lines and there is an empty line after |
Hi Ronny, All tests now pass (commit: f700346). |
Hi @alberti42 I've completed changes to the user guide at: Feel free to merge and make any changes. The end is in sight! |
Wonderful. I accepted all your changes. All tests are green!! |
Thanks @alberti42, @ronzulu & @4Source for the work put in! |
I think if this was IRL we would be going to the pub for a drink to celebrate :-) |
So do I just need to reinstall the plugin to get the latest version of the spaced repetition plugin |
🎉🎉🎉 Absolutely 😀 |
I don't think you need to uninstall it. However, you need to wait for the upstream changes to be part of the new release. I am not sure when this will happen, but I celebrate that the changes are now part of the upstream and the release will happen at some point. |
wondering when will this be available. Seems like the updated the plugin was never released. The github page shows the last release to be 1.12.5 which was about a month ago |
This is now part of the latest beta release: |
I started using it. Thanks. It’s working great. Didn’t notice any issue . |
* Updated to upstream. Included all changes. * Forgot to include peggy in the dependencies * Automatically generate parser * Trying to improve the structure. * Trying to improve the structure. * Trying to understand the structure of tests * Updated rules * Fixing testing units * Testing a different approach * Finished with the dynamic parser * All tests now pass * Remove json * Parser error case test * Add PaserDebugSetting + test case for messages * - Minor changes to the language translations * - Added a few test cases for testing `multilineCardEndMarker` * - Added more comments to the code - Improve parser debug infos * - Added tests for multiline clozes with `multilineCardEndMarker` * Minor cleanup of the parser grammar * Squashed commit of the following: commit b2d6d18 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Sat Aug 17 15:35:40 2024 +0200 Minor cleanup of the parser grammar commit 9514c97 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Sat Aug 17 15:30:47 2024 +0200 - Added tests for multiline clozes with `multilineCardEndMarker` commit ca0f980 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Sat Aug 17 15:16:15 2024 +0200 - Added more comments to the code - Improve parser debug infos commit 2be3444 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Sat Aug 17 15:15:59 2024 +0200 - Added a few test cases for testing `multilineCardEndMarker` commit 6976005 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Sat Aug 17 15:15:47 2024 +0200 - Minor changes to the language translations commit 8a63ed2 Author: 4Source <38220764+4Source@users.noreply.github.com> Date: Fri Aug 16 19:27:50 2024 +0200 Add PaserDebugSetting + test case for messages commit 6297fe5 Author: 4Source <38220764+4Source@users.noreply.github.com> Date: Fri Aug 16 18:25:47 2024 +0200 Parser error case test commit 1465e9d Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 17:47:26 2024 +0200 Remove json commit 460d71b Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 17:46:47 2024 +0200 All tests now pass commit f9e3303 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 17:23:04 2024 +0200 Finished with the dynamic parser commit 84523bc Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 17:14:10 2024 +0200 Testing a different approach commit 7b77d23 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 16:05:25 2024 +0200 Fixing testing units commit 1940e7b Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 13:03:52 2024 +0200 Updated rules commit b9098ee Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 10:21:47 2024 +0200 Trying to understand the structure of tests commit 7e5529b Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 09:47:50 2024 +0200 Trying to improve the structure. commit f33cfeb Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 09:40:55 2024 +0200 Trying to improve the structure. commit 0e35b5b Author: Andrea Alberti <a.alberti82@gmail.com> Date: Fri Aug 16 00:04:46 2024 +0200 Automatically generate parser commit 41f90c1 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Thu Aug 15 17:57:11 2024 +0200 Forgot to include peggy in the dependencies commit e2f6750 Author: Andrea Alberti <a.alberti82@gmail.com> Date: Thu Aug 15 17:35:55 2024 +0200 Updated to upstream. Included all changes. * Added some comments, minor stylistic changes to the parser grammar, updated a test case * Tiny change. * Added some comments, minor stylistic changes to the parser grammar, updated a test case * Trying to fix but not there yet * Seems to work again * One more fix and added one more test case. All tests pass. * Partial update of the user documentation * Completed changes to user documentation --------- Co-authored-by: 4Source <38220764+4Source@users.noreply.github.com> Co-authored-by: ronzulu <75528127+ronzulu@users.noreply.github.com>
Added
multilineCardEndMarker
setting storing a delimiting string. The user can choose for example '---'. This allows handling flashcards with empty lines, without resorting to expedients such as hidden characters to mimic the empty line.