xi-editor / xi-mac

The xi-editor mac frontend.
Apache License 2.0
3.02k stars 148 forks source link

Implement split views #290

Open cmyr opened 5 years ago

cmyr commented 5 years ago

A medium-large project for somebody, which will require some exploration and some design discussion. A PR implementing this feature is unlikely to get merged if there has not been a preliminary discussion and a loosely agreed upon design.

Eventually we would like to have split views in xi-mac, so let's at least get an issue up:

My vision for this is to work like iTerm2; that is, any view can be arbitrarily split vertically or horizontally. If you would like your editor to look like a Mondrian that's up to you.

For now, splits should open a new untitled document.

I'm not totally sure how this should work with tabs. I think that a tab is probably a collection of splits; in this case we might also need to reimplement tabs? Which means we might need to drop the NSDocument stuff?

terhechte commented 5 years ago

I'm a huge fan of split views. My emacs always looks like a Mondrian, so this excites me. I don't think that it is necessary to remove NSDocument support. There's a lot to leverage from NSDocument and even though it is a tad complicated NSWindow and NSDocument can be made to work in a many-to-many scenario (one document in multiple windows, one window hosting multiple documents). There's a rough overview here: https://cutecoder.org/programming/window-multiple-documents/

Tabs would probably need to be reimplemented, which is not a bad thing imho. The AppKit tab support is great for simple usecases but quickly reaches limits for slightly more complex scenarios.

There're several alternative open source tabbar implementations such as (https://github.com/robin/LYTabView) that can be used (though most are in Objective-C). What's more, custom tabs would also allow Xi to gain its own distinctive style.

Replacing the tabbar might also be the most difficult topic in this issue if we'd keep NSDocument support.

Another open question would be how to do the split views. There's NSSplitView and NSSplitViewController. NSSplitView is a bit verbose to use, NSSplitViewController is easier to use but also a bit more constrained in whats possible.

I guess we'd also want nice keyboard shortcuts to modify the splits, and in the future maybe saved layouts.

jansol commented 5 years ago

Mm, if that works out it seems like it'd make it possible to completely avoid the sync and memory issues of #232, which would be awesome!

cmyr commented 5 years ago

NSDocument:

NSDocument does provide a bunch of nice stuff, but the reality is that we already implement most of this functionality ourselves, in core. This makes our use of NSDocument necessarily pretty hacky; for instance since core handles all file persistence, all of the NSDocument methods around opening and saving end up being either noops or wrappers on core functions.

Another more subtle concern I have with leaning heavily on NSDocument is that it lets xi-mac ignore or avoid problems that other frontends will have to deal with. One example of this is the automatic window restoration features; the ability to store session state is something that we would like to have available to all clients, and by leaning on NSDocument for this on xi-mac, we force other clients to come up with their own solutions.

The main thing we're really taking advantage of NSDocument for, today, is handling of tabs.

custom tab implementations

I've looked into various solutions briefly, and I'm happy to lean on third-party code. (We were less eager to do this when the project was inside goog, because we had to be careful about the licensing of any third-party dependencies we brought in). To date we've avoided carthage/cocoapods etc, but that isn't set in stone. I probably prefer the lightest weight option possible.

Re: distinctive style: an explicit goal for xi-mac is to look (by default) as much like a native application as possible. When you open the app, you should believe that it could have shipped with the system. The user can explicitly choose to theme and otherwise customize it, but by default we just want to keep up with current Cocoa conventions.

splitview implementation:

When imagining how I'd implement splits I've often figured I would end up avoiding the provided cocoa classes and just writing a new controller type from scratch, but I tend to want to do that and I have no strong reason to believe that it's necessary. I think the flexibility is important. I've looked in to the iTerm2 implementation and I believe they're just using NSSplitView.

If you're interested in taking this up, it will involve digging into most of the existing corners of the mac client. There are some warty bits, but I'd be happy to try and provide some background on how exactly things got to their current state. ☝️👴

@jansol makes a good point, in that we would also want to think in this work about a few other features, all of which would be fully optional

terhechte commented 5 years ago

I see the point about moving everything that NSDocument offers into xi-editor. What should be considered is that there may be some things that become much harder to achieve without NSDocument; the best example I can think of is the integrated version browser in Cocoa apps:

Manage multiple versions of documents. Autosave creates versions at regular intervals, and users can manually save a version whenever they wish. Users can browse versions and revert the document’s contents to a chosen version using a Time Machine–like interface. The version browser is also used to resolve version conflicts from simultaneous iCloud updates.

(https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/Designing/Designing.html)

There's a WWDC where this feature is demoed in a nice way: They browse back to an older version of a Pages document in a split screen view (i.e. current document left side, past version right side) and then are able to copy data from the old version into the new version. I couldn't find that WWDC/Video though. However, I have to admit I've never used that feature in any of Apple's apps so far. Also, I guess for a developer's text editor, Git solves this use case in a much better way anyway. Nevertheless, this would probably be possible somehow with a lot of hacking, but realistically, without NSDocument, this is kinda tricky to pull of properly. Other possible issues could arise with iCloud containers, handoff, etc.

That being said, it looks like quite a bit of what NSDocument / AppKit State Restoration offers would still need to be implemented in xi-editor, right? I.e. remembering the last open documents, their positions, their undo history, the state of their UI (i.e. for each window, where is the split indicator), recent entries in individual search fields, selected theme (per document), selected language mode (per document), is there a files sidebar, which folder does it show, what's selected in there, etc.

Given that this would need to be implemented in xi-editor in order to move away from NSDocument, I wondered if a stop gap solution might be to implement these calls for now only in the xi-mac frontend and do the actual implementation as a prototype on top of NSUserDefaults. That way, one could already start throwing out NSDocument and also asses and iterate over the actual protocol additions that are required to pull this off. Once there is a solid implementation it would be easy to get rid of the NSUserDefaults layer and get / set the information via xi-editor.

This way, work on this topic could start right away, without having to also coordinate a proper API for it.

cmyr commented 5 years ago

This is a good point, that I probably don't know exactly what features NSDocument actually provides.

The general idea of state restoration is one of the main things I've been thinking about when I talk about 'workspaces' (https://github.com/xi-editor/xi-editor/issues/865). It's possible that this is confusing to people.

In any case, I think we can divide this state into two categories: the first is state that is general to all clients, and the second is state that is client specific. So things like the open documents and views, selections, undo state, find state, and theme all naturally live in core. Things like split state, position on the monitor(s), the presence of a side view (and it's state) are all client specific; I imagine the client passing this information to the core as an opaque blob of data that it will be handed back at restoration time.

I think that the approach of a stopgap makes sense. We should figure out exactly what NSDocument is currently providing us, and figure out how to ween ourselves off of that, using NSUserDefaults or similar. That will put us in a good position to evaluate what we really need in a core API.

Thinking about this in these terms, this does feel quite related to adding the state restoration stuff in core. So perhaps this is project to be undertaken before splits?

terhechte commented 5 years ago

(I totally haven't had the time to do a deep dive into this project, so neither had I read the workspaces discussion yet, nor did I even read much from the original Rope design documents. So I may be wrong about many things ;) )

I feel like a possible approach could be to do state restoration on macOS w/ NSUserDefaults to figure out whats needed, etc. Splits info storage could be done as part of this process and also end up temporarily in NSUserDefaults (as many macOS apps do it anyway). However the actual implementation of splits, as I see it, is dependent upon a possible removal of NSDocument which would probably affect a good chunk of the codebase. It would be wasteful to implement splits with NSDocument (i.e. see the link I pasted above) only to remove all of the multi-nsdocument-window logic afterwards when getting rid of NSDocument. So:

  1. NSUserDefault Storage (+)
  2. Remove NSDocument (++)
  3. Add splits
cmyr commented 5 years ago

This approach makes overall sense to me. I think a (temporary) regression in functionality here is okay if it's a point on a known roadmap. My personal suspicion is that it actually wouldn't be too much work to remove NSDocument right now; I really don't think we're using that much of it. For instance, we can get rid of NSDocument without having to give up window restoration (as far as I understand).

In any case, firm answers will probably only come from poking at things a little bit. If it's helpful, I would be happy to have a voice call or screenshare at some point if you'd like to talk this through; I might be able to also provide some background on how things got to be in their current state. 😬

cmyr commented 5 years ago

@terhechte wanted to bump this and check in; @akxcv has been starting to sketch out work on state restoration in core, so it feels like a good time to start thinking more concretely about this? Would be very happy to have you involved (at whatever pace works for you), your contributions so far have been much appreciated.

terhechte commented 5 years ago

Hey! I'd love to work on this, but I realistically won't have time until beginning of December as I'm almost non-stop travelling. However, from December on I'd love to look into this or help out if somebody already started by then.

cmyr commented 5 years ago

@terhechte sounds good! I don't expect anyone will get to this before you, but we'll hopefully have made some progress on the state restoration stuff in core so there will be something for this to build on. Happy travels!

lukakerr commented 5 years ago

I was just having a look at this (albeit only for an hour or so during a study break) and its relatively easy to implement the actual split views, the hard part comes with the separation of the window from the document. With NSDocument and native macos tabs, since each tab is essentially a document, when there are multiple split views in one tab (or window) things like saving don't work properly.

I think to properly implement split views we would definitely have to get rid of the native macos tabs. This would also fix the memory/sync issues as @jansol mentioned for the sidebar.

I've added an image below just to give an idea of how it looks just using NSSplitView's. I think using the native split views would work fine, although am not sure how things like drag-and-drop for split views would work (if we even want it). If we were to move away from the native macos tabs, then this would be easier, as ideally you'd have a tab per split pane.

splits
cmyr commented 5 years ago

@lukakerr cool, thanks for the screenshot, that at least looks very reasonable.

cmyr commented 5 years ago

@terhechte is this still something you'd be interested in working on? I think it's definitely a good time for some renewed focus on xi-mac, and this would be great to have.

terhechte commented 5 years ago

@cmyr Hey! Yeah, still interested. I'm mostly done travelling and my one-month Christmas vacation starts in two days, I'll then have a look at the code again and see what I can do :)

cmyr commented 5 years ago

@terhechte great! If you'd like to chat about this we've set up a zulip instance at http://xi.zulipchat.com.