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

Implement getState method #613

Closed
wants to merge 3 commits into from
Closed

Implement getState method #613

wants to merge 3 commits into from

Conversation

parisk
Copy link
Contributor

@parisk parisk commented Mar 17, 2017

This is a PoC for a getState terminal method that will return the serialized current state of the terminal.

Things to include in state

  • Geometry
  • Mode
  • Buffer
  • Alt buffer
  • Cursor position
  • Cursor style
  • Options
  • Version

To do

  • Add all members in state
  • Implement tests for getState

State size

It seems that the serialized state of the terminal can get kinda big, relatively easily. This happens because of the (verbose) way we are storing the buffers.

Maybe we should entertain the idea of keeping less information in memory and computing some things on the fly instead, considering buffers.

Example

  1. Open the xterm.js demo
  2. Run less typings.json in the terminal
  3. Then use term.getState() in the browser's dev tools to retrieve the state
  4. The resulting state is almost 250KBs and 25k+ lines of prettified JSON data

Screenshot

image

This PR should eventually close #595.

@parisk parisk added the work-in-progress Do not merge label Mar 17, 2017
@parisk parisk added this to the 2.5.0 milestone Mar 17, 2017
@parisk parisk self-assigned this Mar 17, 2017
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.2%) to 67.661% when pulling 45aa7eb on issue-#595-terminal-get-state into ef1b1c8 on master.

src/xterm.js Outdated
@@ -251,6 +251,52 @@ function Terminal(options) {
inherits(Terminal, EventEmitter);

/**
* Returns the current mode of the terminal. Can be one of the following:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If only 1 of these modes can be active at once we should turn that into an enum.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I was thinking about that, but I am not exactly sure how this works right now.

I took a look at the following links and I was not able to find any reference to the origin mode, so I ditched it. Also I cannot understand if insert mode can co-exist with application mode:

continue;
}

if (typeof value.getState == 'function') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 when Terminal is in TS I think we can do if value instanceof ISerializable which is even nicer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move forward with this and implement an interface ISerializable. For serialization tests this can be done at the level of the component as well (tests for the Buffer go in Buffer.test.ts).

@parisk parisk force-pushed the issue-#595-terminal-get-state branch from 5d38551 to ff5baee Compare March 20, 2017 10:39
@parisk
Copy link
Contributor Author

parisk commented Mar 20, 2017

@Tyriar I am thinking about whether I should include the buffers as "plain text to be rendered" or as an array of lines (each line can either be a string, or an array of characters).

I have not decided yet, but my gut tells me that having an array of lines (each line should be a string) should do the work pretty well.

Any opinions on this?

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.3%) to 67.677% when pulling ff5baee on issue-#595-terminal-get-state into 045f361 on master.

@mofux
Copy link
Contributor

mofux commented Mar 20, 2017

@parisk My use cases for this would be to:

  1. store the state of the terminal when the application quits, and restore the state including the scrollback buffer etc. when the user reopens the app. Additionally, If I can't get hold of the old tty session, I would spawn a new tty in the background, and write a one-line notice to the terminal that indicates that the session was re-created.

  2. provide a viewfinder, similar to what code editors like Sublime Text have on the right hand side of the code pane, where you can see a zoomed out version of the code, and allow you to scroll through it using a view finder. To get this working, It would massively help if I would get a line representation that can be easily transformed into html, e.g.

lines: [
  // one line, saying "hello world", 
  // "hello" has green foreground and white background (ascii color)
  [
    { text: 'hello', fg: 'green', bg: 'white', bold: false, underline: false },
    { text: ' world': fg: 'fg', bg: 'bg', bold: false, underline: false }
  ]
]

In general, having some sort of parser that can be used to transform raw tty data into something that can be easily rendered by a browser would be very useful.

@Tyriar
Copy link
Member

Tyriar commented Mar 20, 2017

@mofux

  1. Thanks for sharing your use case
  2. xterm.js has enough performance challenges without considering a minimap imo, the fact that the output changes so frequently and so much means that the minimap would have to be re-rendered a lot. Sure it's possible but there are better areas of opportunity to focus on

@Tyriar Tyriar modified the milestones: 2.6.0, 2.5.0 Apr 4, 2017
@parisk parisk removed this from the 2.6.0 milestone May 5, 2017
@parisk parisk force-pushed the issue-#595-terminal-get-state branch from ff5baee to 7f8f8ff Compare May 6, 2017 08:44
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.3%) to 68.416% when pulling 7f8f8ff on issue-#595-terminal-get-state into 2221f70 on master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.3%) to 68.416% when pulling e6eaca4 on issue-#595-terminal-get-state into 2221f70 on master.

@vincentwoo
Copy link
Contributor

I'm excited to see this PR progress along!

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.09%) to 68.621% when pulling c74a49c on issue-#595-terminal-get-state into 2221f70 on master.

@parisk parisk force-pushed the issue-#595-terminal-get-state branch from c74a49c to 42d287c Compare May 27, 2017 12:02
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 68.621% when pulling 42d287c on issue-#595-terminal-get-state into 35b32b7 on master.

@parisk
Copy link
Contributor Author

parisk commented May 27, 2017

OK, I just pushed an update here, that makes getState() fully-functional for a v1 IMO.

My only thought here is that the buffers can get really really big, really quickly. This implies that:

  1. The serialized state of the terminal can get king-size
  2. This will make testing (and debugging) of serialized buffers cumbersome (thinking of using pre-baked .json files with already serialized states

Any ideas on how we could work around better with this issue?

If nothing comes, I will move on with implementing tests utilizing static files for the buffer data.

Copy link
Member

@Tyriar Tyriar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we hold off on merging this until a Terminal.setState or a new constructor that takes a state is created? Otherwise we would be shipping code that isn't used potentially for a few versions?

Also some basic tests would be good 😃


let state = {
buffers: {
normal: normalBuffer,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should create a CircularList.getState function which returns data from indexes 0 to CircularList.length. That's all the data that's meaningful:

screen shot 2017-05-27 at 3 14 09 pm

With the 24 lines from the CircularList it should be easy to create a new CircularList constructor or setState method which sets the length to 24 and puts those 24 lines into the list.

});

// Iterate through all terminal properties to embed their state as well.
for (let i in properties) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this retrieve any data yet? Maybe we should comment it out if it's not used yet?

@parisk parisk force-pushed the issue-#595-terminal-get-state branch from 42d287c to 31a7e51 Compare June 18, 2017 11:58
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.3%) to 69.094% when pulling 31a7e51 on issue-#595-terminal-get-state into 663d3b2 on master.

@vincentwoo
Copy link
Contributor

@parisk I imagine there's pretty straightforward compression you can do if many entries in the buffer share the same mode?

@Tyriar
Copy link
Member

Tyriar commented Jun 21, 2017

@vincentwoo if size matters I was thinking consumers could always gzip it or something. Seems like the better way to go to avoid additional complexity in the codebase imo.

@Tyriar
Copy link
Member

Tyriar commented Jun 21, 2017

Maybe this should just go in Buffer.getState/setState post #717

If the serialize functions are nicely separated it should be pretty simple.

@vincentwoo
Copy link
Contributor

vincentwoo commented Jun 21, 2017 via email

@Tyriar
Copy link
Member

Tyriar commented Jun 22, 2017

@vincentwoo I'd wait until at least this and #717 are merged.

The buffer format may indeed change in the future as it's still using the original character format from term.js which is a little strange ([number, string, number]), we may also want to store more metadata some day as well. Certainly don't want to lock ourselves in to anything.

@parisk
Copy link
Contributor Author

parisk commented Jun 26, 2017

@vincentwoo let's hold off until we merge the new buffer classes into master and the first implementation of getState and setState, both of which turned out to need significantly more work than I initially thought 😅 .

After that, I think it will be pretty much easier to iterate over performance improving, benchmarking etc.

@parisk parisk mentioned this pull request Aug 3, 2017
13 tasks
@parisk parisk changed the base branch from master to v3 August 3, 2017 18:51
@parisk parisk added this to the 3.0.0 milestone Aug 3, 2017
@ioquatix
Copy link
Contributor

I would find this useful in my Atom package script-runner as it needs to save and restore window state.

@parisk
Copy link
Contributor Author

parisk commented Aug 10, 2017

@ioquatix this will hopefully land in 3.0 🙂.

@Tyriar
Copy link
Member

Tyriar commented Aug 10, 2017

I think I mentioned it before but I think we should only move forward with this if we allow breakages across versions. Otherwise this will severely impact our ability to improve the buffer and other components in the future.

We could do this in a way that minimizes breakages for users though, the state could have a version property which declares the breakage. So when consumers try to use the state, maybe it would return false if the version is not what is expected?

@vincentwoo
Copy link
Contributor

vincentwoo commented Aug 10, 2017

I am perfectly fine with that, I don't want to constrain xterm.js development speed at all. I think anyone who uses this feature is liable to be a savvier user and probably can deal with the consequences.

@ioquatix
Copy link
Contributor

In the case of script-runner, it's really not that important, it's more like, nice to have it working correctly - if the version is updated and some old panels fail to reload correctly, it's not a big deal.

@ioquatix
Copy link
Contributor

I can't tell you how much I'm looking forward to this PR :)

ioquatix added a commit to ioquatix/script-runner that referenced this pull request Aug 11, 2017
@parisk
Copy link
Contributor Author

parisk commented Aug 19, 2017

@Tyriar I think it's OK to introduce breaking changes when we have a new major version out (e.g. 4.0).

Considering versioning the serialized state, I think it's a great idea, so I'll put it in a todo.

Copy link
Member

@Tyriar Tyriar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opps forgot to submit the review

@@ -68,7 +68,7 @@ function createTerminal() {
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';

term.open(terminalContainer);
term.open(terminalContainer, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the , true as this arg was removed

* Returns the current position and style of the terminal cursor.
* It returns an object in the following form: {position: [x, y], style: "cursorStyle"}
*/
Terminal.prototype._getCursor = function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be deleted since we're on v3 now.

continue;
}

if (typeof value.getState == 'function') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move forward with this and implement an interface ISerializable. For serialization tests this can be done at the level of the component as well (tests for the Buffer go in Buffer.test.ts).

@Tyriar
Copy link
Member

Tyriar commented Aug 19, 2017

@parisk I think we should allow "breaking changes" to the getState data even between minor versions, not doing so could be overly restricting. Basically defer the breakage to a separate schema version contained in the getState json which may change a between versions.

@parisk
Copy link
Contributor Author

parisk commented Aug 20, 2017

@Tyriar do you have any components in might that might need to break their serialized form more than once in (let's say) a year?

@Tyriar
Copy link
Member

Tyriar commented Aug 21, 2017

@parisk buffer improvements #791 and true color #484 are the main ones that come to mind immediately. Not sure if these will be done by November yet.

Additional styles would change the format, perhaps in a backwards friendly way though #580

@parisk
Copy link
Contributor Author

parisk commented Oct 20, 2017

Closing this, as I will open a fresh one for 3.0.0.

@parisk parisk closed this Oct 20, 2017
@ioquatix
Copy link
Contributor

Looking forward to this, can you add a link here to the new issue?

@ioquatix
Copy link
Contributor

ioquatix commented May 4, 2018

Was this ever implemented?

@parisk parisk deleted the issue-#595-terminal-get-state branch May 4, 2018 12:36
@parisk
Copy link
Contributor Author

parisk commented May 4, 2018

@ioquatix, nope. This was not implemented at all. PRs are welcome though. I would love to review.

@ioquatix
Copy link
Contributor

ioquatix commented May 4, 2018

Okay. I don't really know enough about it nor do I have the time :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
work-in-progress Do not merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants