{"action":"created","issue":{"url":"https://api.github.com/repos/sindresorhus/pure/issues/143","labels_url":"https://api.github.com/repos/sindresorhus/pure/issues/143/labels{/name}","comments_url":"https://api.github.com/repos/sindresorhus/pure/issues/143/comments","events_url":"https://api.github.com/repos/sindresorhus/pure/issues/143/events","html_url":"https://github.com/sindresorhus/pure/issues/143","id":94671200,"number":143,"title":"Multiline ZLE buffer overwritten by preprompt update","user":{"login":"zmwangx","id":4149852,"avatar_url":"https://avatars.githubusercontent.com/u/4149852?v=3","gravatar_id":"","url":"https://api.github.com/users/zmwangx","html_url":"https://github.com/zmwangx","followers_url":"https://api.github.com/users/zmwangx/followers","following_url":"https://api.github.com/users/zmwangx/following{/other_user}","gists_url":"https://api.github.com/users/zmwangx/gists{/gist_id}","starred_url":"https://api.github.com/users/zmwangx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zmwangx/subscriptions","organizations_url":"https://api.github.com/users/zmwangx/orgs","repos_url":"https://api.github.com/users/zmwangx/repos","events_url":"https://api.github.com/users/zmwangx/events{/privacy}","received_events_url":"https://api.github.com/users/zmwangx/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":18,"created_at":"2015-07-13T08:55:37Z","updated_at":"2015-07-18T17:17:41Z","closed_at":null,"body":"A screencast should suffice to demonstrate this problem:\r\n\r\n[![asciicast](https://asciinema.org/a/645061r08iwz94hdxxtnfkmpz.png)](https://asciinema.org/a/645061r08iwz94hdxxtnfkmpz)\r\n\r\nThe problem is that the [`lines` variable in `prompt_pure_preprompt_render`](https://github.com/sindresorhus/pure/blob/5f5b990e2b1739ba359f08a33727c8bf107bf4f8/pure.zsh#L108) doesn't take into account current ZLE buffer lines (it only records the number of lines spanned by the preprompt), so if the buffer spans multiple lines, then the preprompt is updated in the wrong place.\r\n\r\n## Proposed fix\r\n\r\nA naive fix: ZLE has a read-only parameter `BUFFERLINES` available to ZLE widgets. We can thus register a ZLE widget, say `prompt_pure_record_buffer_lines`, that records `BUFFERLINES` in a global variable, say `prompt_pure_buffer_lines`. Then we can simply do `(( lines += prompt_pure_buffer_lines - 1 ))` to account for the buffer.\r\n\r\nHowever, there are two other technical challenges:\r\n\r\n1. The naive fix doesn't capture the case where the cursor is not at the end of the buffer. But this is easy to deal with. We can determine the absolute position of the cursor (from the beginning of the line) by summing the length of `PS1/PS2` (depending on `CONTEXT`) and the `CURSOR` parameter. (We already have a function `prompt_pure_string_length` to calculated the prompt-expanded length of a string.) Then we can determine the line where cursor is on by dividing by `COLUMNS`.\r\n\r\n2. The naive fix only works when the ZLE `CONTEXT` is `start`; if the `CONTEXT` is, say, `cont` (continuation of a command line), then the previous lines would have already been flushed from the ZLE buffer, so `BUFFERLINES` doesn't count those lines. As a concrete example, consider\r\n\r\n  ```\r\n> echo \"hello\r\ndquote> world\"_\r\n```\r\n\r\n  where `_` represents the cursor position. At that point, `BUFFERLINES` is 1, but we should count both lines in order to go back to the preprompt.\r\n\r\n  To deal with this scenario, the solution I came up with is to use an accumulator, say `prompt_pure_accumulated_buffer_lines`, which is cleared precmd and incremented by `BUFFERLINES` right before each `accept-line`. This would require replacing the default `accept-line` with a custom widget.\r\n\r\nI have an implementation of these ideas in my own prompt (the async model of which is largely based on pure). The relevant change set, which might be helpful, is here: https://github.com/zmwangx/prezto/compare/991dfe8...zmwangx:2ef0cfd."},"comment":{"url":"https://api.github.com/repos/sindresorhus/pure/issues/comments/122568003","html_url":"https://github.com/sindresorhus/pure/issues/143#issuecomment-122568003","issue_url":"https://api.github.com/repos/sindresorhus/pure/issues/143","id":122568003,"user":{"login":"zmwangx","id":4149852,"avatar_url":"https://avatars.githubusercontent.com/u/4149852?v=3","gravatar_id":"","url":"https://api.github.com/users/zmwangx","html_url":"https://github.com/zmwangx","followers_url":"https://api.github.com/users/zmwangx/followers","following_url":"https://api.github.com/users/zmwangx/following{/other_user}","gists_url":"https://api.github.com/users/zmwangx/gists{/gist_id}","starred_url":"https://api.github.com/users/zmwangx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zmwangx/subscriptions","organizations_url":"https://api.github.com/users/zmwangx/orgs","repos_url":"https://api.github.com/users/zmwangx/repos","events_url":"https://api.github.com/users/zmwangx/events{/privacy}","received_events_url":"https://api.github.com/users/zmwangx/received_events","type":"User","site_admin":false},"created_at":"2015-07-18T17:17:41Z","updated_at":"2015-07-18T17:17:41Z","body":"After quite extensive tests, I concluded that the only way we can get the current execution context on the terminal is to expand within a ZLE widget that is invoked interactively (i.e., with a keybinding). `accept-line` is such an example, which is invoked by `^J`. Therefore, it seems to me that the only way we can expand it right is to hack directly into `self-insert` (still doesn't work right after a `^J`, but that's a pretty minor concern, with the assumption that `PS2` is not going to occupy more than a line). That's just too hacky and probably have performance ramifications, so my suggestion is to just leave it like this (expand to the last `PS2`) and hope for the best... An average Zsh user is unlikely to hit the edge case in a lifetime, after all.\r\n\r\n> the only way to get the correct prompt length would be to define PS2 manually, e.g. PS2=\"> \"\r\n\r\nNot good. We shouldn't discard a nice functionality just to guard against a highly improbable edge case."}}