Skip to content
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

Support editing #6

Closed
chaorace opened this issue Jun 7, 2020 · 22 comments
Closed

Support editing #6

chaorace opened this issue Jun 7, 2020 · 22 comments
Labels
enhancement New feature or request

Comments

@chaorace
Copy link

chaorace commented Jun 7, 2020

Currently, it seems the app only allows for viewing of org files. Are there any plans to provide write functionality in the app? If so, perhaps we should figure out a roadmap of desirable editing features to provide?

@son1112
Copy link

son1112 commented Jun 7, 2020

I'm curious about this as well. Would love to be able to create and edit org files using this application. It's the best org viewer for mobile yet, IMO!

@amake amake changed the title Plans for writeable functionality? Support editing Jun 8, 2020
@amake amake added the enhancement New feature or request label Jun 8, 2020
@amake
Copy link
Owner

amake commented Jun 8, 2020

Thanks for the request. Here's what I wrote about editing previously:

Editing would be a very big stretch goal. File access I don’t think is the main hurdle. Off the top of my head the main barriers are:

  • Medium difficultly: Ensuring faithful round-trip from text to parsed representations and back to text. I wouldn’t want to mangle files or make spurious changes, but I didn’t design the parser with round-tripping in mind, so that might be hard.
  • Very hard difficulty: Making a good interface for editing. The display is not one big text buffer with styling like in Emacs, it’s a bunch of separate UI components (“widgets” in Flutter parlance). So editing can’t be as simple as placing a cursor and typing, which is probably not very desirable on mobile anyway. It will be hard just to design a usable interface; implementing it may be even harder—it could require completely reimplementing the current UI.

That said, I may get the itch to do something a bit smaller in scope, such as allowing checking checkboxes or toggling todo status.

@amake
Copy link
Owner

amake commented Jun 8, 2020

Markor has been suggested as inspiration for implementing editing.

@shombando
Copy link

shombando commented Jun 8, 2020

Markor has been suggested as inspiration for implementing editing.

Also suggesting Organice org-parser as parsing tool.

@amake
Copy link
Owner

amake commented Jun 8, 2020

I made my own parser for Orgro in Dart. Organice's org_parser is written in Clojure, and I can't see any path to making that run on iOS, so it's a non-starter as a replacement. It could be useful as inspiration for e.g. AST representation.

@macdonaldster
Copy link

My attraction to this software is I can quickly view org files on my mobile device. I don't necassarily want to edit. I am liking the fact that a simple "git pull" (e.g. using Mgit) gets my latest files without messing with conflict resolution because of something Orgzly did to my files (NOTHING against Orgzly - love it, but it can complicate workflow). I can see something like "org capture" that appends a headline with some other data (e.g. default TODO state, scheduled "today", etc). This would make it perfectly usable for me. N = 1, of course. I was thinking a separate app that only did that formatted append might be useful (I am guessing I could gear up something with Automate or Tasker to do this pretty easily).

@amake
Copy link
Owner

amake commented Jun 8, 2020

I can see something like "org capture" that appends a headline with some other data (e.g. default TODO state, scheduled "today", etc).

Sure, "just support appending new sections" is a useful reduction in scope that would be easier than supporting general editing.

@shombando
Copy link

I can see something like "org capture" that appends a headline with some other data (e.g. default TODO state, scheduled "today", etc). This would make it perfectly usable for me. N = 1, of course.

N=2, that is a great idea and goes along with @amake's comment on each node being a flutter widget. All the existing nodes can be read-only and allow a "capture" of a new node in plain text as an append to the file. Once the file is saved and rendered then any org formatting that was typed in will be displayed as intended!

@chaorace
Copy link
Author

chaorace commented Jun 8, 2020

No problem from me regarding targeting simple section appending. That covers my use-case for a desktop GTD + mobile companion workflow.

If we're going with appending new sections as the main editing functionality though... I would like to chip in a request to be able to also cycle TODO states.

@snuffop
Copy link

snuffop commented Jun 9, 2020

What about passing the file to an editor? and re reading on save or change. thus allowing the use of next|own cloud dropbox or other to handle the sync editor of choice to edit and orgro to view.

@bradyt
Copy link

bradyt commented Jan 30, 2021

Maybe you can toggle the mode, so that wherever "point" is, you toggle to edit mode which takes you to a plain text version of the file, no parsing, and you're positioned to edit the same text where you were at previously, up to glitches. Then edit, save, and toggle back to parsed view. I've also wondered if there's a way to pass "point" to open the file in an external editor at the right position.

Sort of like toggling between org-viewer-mode and text-editing-mode or something.

@amake
Copy link
Owner

amake commented Feb 1, 2021

The main issue I see with doing a bimodal editing scheme like that is that for large documents parsing is already very slow. So either the UX would become quite poor for large documents, or I have to create the machinery to allow editing partial subtrees of the document. The latter could work, but right now the parsed representation doesn't remember its exact text representation, so the round trip would have to be lossy.

@riccardovarenna
Copy link

maybe there can be certain forms of editing that are "easier".

So rather than allowing free text editing, have some predefined edit actions and only go for the ones that give most value with least effort.

Here are in my opinion the most valuable edit actions aside from free flow typing text:

  • refiling a heading to a different heading
  • marking a todo as done / changing the todo state of a heading
  • adding a heading underneath another heading / at the bottom of the file

Maybe those can be achieved without having to deal with difficult UI where you need to free render text but rather only change one flutter element per edit action the user can take

amake added a commit that referenced this issue Oct 28, 2022
```
══╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during a scheduler callback:
Layer OffsetEngineLayer was previously used as oldLayer.
Once a layer is used as oldLayer, it may not be used again. Instead, after calling one of the
SceneBuilder.push* methods and passing an oldLayer to it, use the layer returned by the method as
oldLayer in subsequent frames.
'dart:ui/compositing.dart':
Failed assertion: line 110 pos 9: '<optimized out>'

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

When the exception was thrown, this was the stack:
#2      _EngineLayerWrapper._debugCheckNotUsedAsOldLayer (dart:ui/compositing.dart:110:9)
#3      SceneBuilder.addRetained.<anonymous closure>.recursivelyCheckChildrenUsedOnce (dart:ui/compositing.dart:695:21)
#4      List.forEach (dart:core-patch/growable_array.dart:416:8)
#5      SceneBuilder.addRetained.<anonymous closure>.recursivelyCheckChildrenUsedOnce (dart:ui/compositing.dart:701:18)
#6      SceneBuilder.addRetained.<anonymous closure> (dart:ui/compositing.dart:704:7)
#7      SceneBuilder.addRetained (dart:ui/compositing.dart:707:6)
#8      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:671:15)
#9      ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#10     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#11     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#12     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#13     ClipRectLayer.addToScene (package:flutter/src/rendering/layer.dart:1590:5)
#14     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#15     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#16     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#17     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#18     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#19     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#20     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#21     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#22     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#23     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#24     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#25     TransformLayer.addToScene (package:flutter/src/rendering/layer.dart:1914:5)
#26     ContainerLayer.buildScene (package:flutter/src/rendering/layer.dart:1097:5)
#27     RenderView.compositeFrame (package:flutter/src/rendering/view.dart:231:37)
#28     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:514:18)
#29     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:869:13)
#30     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:375:5)
#31     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1271:15)
#32     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1200:9)
#33     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1058:5)
#34     _invoke (dart:ui/hooks.dart:145:13)
#35     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5)
#36     _drawFrame (dart:ui/hooks.dart:112:31)
(elided 2 frames from class _AssertionError)
════════════════════════════════════════════════════════════════════════════════════════════════════
```
@amake
Copy link
Owner

amake commented Jan 16, 2023

  • Medium difficultly: Ensuring faithful round-trip from text to parsed representations and back to text. I wouldn’t want to mangle files or make spurious changes, but I didn’t design the parser with round-tripping in mind, so that might be hard.

I've been playing with this a bit and I find that it's quite cumbersome to adjust org_parser to retain all whitespace.

What is the best practice for parsing with an eye to reproducing the original document verbatim? If there was a clear pattern to adopt, that would probably help.

Another thought is that there should really be a tree-parser grammar for Org syntax; tree-sitter seems more suited to this application and the parser produced could be used pretty much everywhere.

@amake
Copy link
Owner

amake commented Jan 18, 2023

Relevant discussion: petitparser/dart-petitparser#142

@amake
Copy link
Owner

amake commented Aug 31, 2023

Update: I've gotten org_parser to the point where it can successfully round-trip verbatim some well-formed Org documents.

The next hurdle is how to implement programmatic editing of the AST. Currently the AST is immutable. The easy thing would be to allow mutation, but I'm also looking into zippers.

@amake
Copy link
Owner

amake commented Sep 7, 2023

Zippers have proven to be fruitful. If they have a problem it's performance, as they inherently cause a lot of object allocations. So far it seems OK though.

Locally I have implemented:

  • Tap on a list item with checkbox to toggle it ([ ] ↔︎ [X])
  • The updated document is written back to the original file if possible (when opened with appropriate permissions, etc.)

I'm worried about mangling user files, but unfortunately I can't easily back up a file before saving (I would have to prompt the user for permissions on the backup file). I will have to think about how to do this.

Edit: I may be able to back up the file if I have directory permissions.

@amake
Copy link
Owner

amake commented Sep 11, 2023

Initial support for editing will be in v1.32.0. Look for it soon:

@amake
Copy link
Owner

amake commented Sep 12, 2023

v1.32.0 is now available for beta testing at the above links.

@amake
Copy link
Owner

amake commented Sep 15, 2023

v1.32.0 is now released on the App Store and Google Play. It will be on F-Droid whenever the next build cycle completes.

@amake
Copy link
Owner

amake commented Sep 15, 2023

v1.33.0 will soon be available for beta testing:

It adds undo/redo, and a prompt to share unsaved changes if they can't be saved back to the original file.

@amake
Copy link
Owner

amake commented Sep 27, 2023

v1.33.3 is now available everywhere. I'm going to close this ticket. For improvements to editing please open new tickets or discussion threads.

@amake amake closed this as completed Sep 27, 2023
@amake amake mentioned this issue Jan 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants