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

Ignore modified state with buftype prompt #14116

Open
mfussenegger opened this issue Mar 12, 2021 · 12 comments
Open

Ignore modified state with buftype prompt #14116

mfussenegger opened this issue Mar 12, 2021 · 12 comments
Labels
enhancement feature request

Comments

@mfussenegger
Copy link
Member

nvim-dap uses buftype=prompt for the debug REPL.

It looks as if by default any input to a prompt buffer sets the modified flag. This is a bit annoying for the REPL case, because users usually don't care whether the REPL is modified or not and they shouldn't get a "Did not save [dap-repl]" message. So I added something like this:

api.nvim_buf_attach(buf, false, {
  on_lines = function(b)
    api.nvim_buf_set_option(b, 'modified', false)
  end;
  on_changedtick = function(b)
    api.nvim_buf_set_option(b, 'modified', false)
  end;
  ...

But this workaround had the side effect of breaking the backspace key in that after you type something you can no longer remove the characters with backspace.

Is there any other good way to that would allow to treat a prompt buffer like a terminal or nofile where modifications don't matter?

@mfussenegger mfussenegger added the enhancement feature request label Mar 12, 2021
@bfredl
Copy link
Member

bfredl commented Mar 12, 2021

probably buftype=prompt,nofile should be allowed. Perhaps it can be suggested upstream?

@cogig
Copy link

cogig commented Jul 24, 2021

Are there any updates?

@mfussenegger
Copy link
Member Author

Afaik no one has reported it upstream to vim yet. I myself didn't get to it - because it would first be necessary to verify that the behavior isn't already different and the problem also occurs within vim.

If you could take care of that, that would be great.

@cogig
Copy link

cogig commented Jul 25, 2021

@bfredl @mfussenegger I have tried the latest version of neovim.

NVIM v0.6.0-dev+65-g46009499a
Build type: Release
LuaJIT 2.1.0-beta3

The problem still exists in nvim-dap repl.

@cogig
Copy link

cogig commented Jul 25, 2021

@mfussenegger @bfredl How to comfirm that the problem also occurs within vim? I have no idea. Can you give me any advise?

@pittcat
Copy link

pittcat commented Jul 26, 2021

@mfussenegger @bfredl I think the bug has large degree of influence on usage of repl and watch window. Many key can't work well.

@gpanders
Copy link
Member

I ran into this myself a little while ago while working on a Fennel REPL plugin. Vim modified the behavior of 'modified' to respect buftype=prompt in vim/vim@4551c0a, solely for the termdebug plugin.

We could consider breaking with upstream on this and making buftype=prompt always ignore 'modified' as it did before that commit (which I would argue is the less surprising behavior).

@pfeiferj
Copy link
Contributor

It seems it's pretty common for plugins to use the workaround mentioned in the original post leading to the broken backspace issue also mentioned in the original post. I ran into it using a couple different plugins and am currently using the following workaround. I decided to just go ahead and set the remap for all prompts since it's a common issue but the logic should be able to be pulled down into individual plugins. That being said, I do think this is a case where we should break from vim in the name of usability and change the default behavior in neovim so these workarounds aren't needed.

function PromptBackspace() --Expects to have entered normal mode before entering function, will return to insert mode
  local currentLineNumber = vim.fn["line"](".")
  local currentColumnNumber = vim.fn["col"](".")
  local currentLineLength = vim.fn["col"]("$")
  local currentLine = vim.api.nvim_buf_get_lines(0, currentLineNumber-1, currentLineNumber, false)[1]
  local _, endPrompt = string.find(currentLine, "> ")

  if endPrompt == nil then
    endPrompt = 0
  end

  if currentColumnNumber ~= endPrompt then --Not beginning of the prompt
    if currentColumnNumber == currentLineLength - 1 then --Not beginning of prompt and is the end of the line
      vim.cmd([[normal x]])
      vim.cmd([[startinsert!]])
    else --Not beginning of prompt and is not the end of the line
      vim.cmd([[normal x]])
      vim.cmd([[startinsert]])
    end
  else --Beginning of prompt
    vim.cmd([[startinsert]])
  end
end

vim.cmd([[
fun! PromptBackspaceSetup()
    if v:option_new == 'prompt'
      inoremap <buffer> <backspace> <esc>:lua PromptBackspace()<cr>
    endif
endfun

autocmd OptionSet * call PromptBackspaceSetup() "not using a lua function here because of error when accessing :help
]])

@pfeiferj
Copy link
Contributor

pfeiferj commented Nov 24, 2021

An improvement to the previous backspace workaround that doesn't switch between normal and insert modes:

function PromptBackspace()
  -- Allows backspacing through previously set text when in a prompt.
  --
  -- Note 1: Insert mode cursor is after (+1) the column as opposed to in normal mode it would be on (+0) the column.
  -- Note 2: nvim_win_[get|set]_cursor is (1, 0) indexed for (line, column) while nvim_buf_[get|set]_[lines|text] is 0 indexed for both.

  local currentCursor = vim.api.nvim_win_get_cursor(0)
  local currentLineNumber = currentCursor[1]
  local currentColumnNumber = currentCursor[2]
  local promptLength = vim.str_utfindex(vim.fn['prompt_getprompt']('%'));

  if (currentColumnNumber) ~= promptLength then
    vim.api.nvim_buf_set_text(0, currentLineNumber-1, currentColumnNumber-1, currentLineNumber-1, currentColumnNumber, {""})
    vim.api.nvim_win_set_cursor(0, {currentLineNumber, currentColumnNumber-1})
  end
end

vim.cmd([[
fun! PromptBackspaceSetup()
    if v:option_new == 'prompt'
      inoremap <buffer> <backspace> <Cmd>lua PromptBackspace()<cr>
    endif
endfun

autocmd OptionSet * call PromptBackspaceSetup() "not using a lua function here because of error when accessing :help
]])

Edit: updated to get actual prompt text to determine prompt length
Edit2: updated prompt length detection as per @rcarriga's suggestion

@rcarriga
Copy link
Sponsor

Just FYI @pfeiferj (or anyone using the workaround) string.len doesn't work with unicode characters in the prompt, you can use vim.str_utfindex instead 😄

@pfeiferj
Copy link
Contributor

whoops, updated the post. Dang unicode messing everything up, clearly the world should have adapted to 256 characters not programmers to languages/emojis lol.

@beardedsakimonkey
Copy link
Contributor

beardedsakimonkey commented Sep 9, 2022

We could consider breaking with upstream on this and making buftype=prompt always ignore 'modified' as it did before that commit (which I would argue is the less surprising behavior).

Does this sound good to other folks? If so, I'd be happy to take a shot at implementing it if contributions are welcome.

Edit: Ah, this appears to just be a one line change in undo.c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement feature request
Projects
None yet
Development

No branches or pull requests

8 participants