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

Feat/supports insert mode #12

Closed

Conversation

masa0x80
Copy link

I wanted to map MDListItemBelow to in insert mode, so I modified it to work in insert mode as well.

Demo
demo

@mmirus
Copy link

mmirus commented Jun 6, 2024

👍 supporting adding a new line (or just falling back to the default vim mapping) when the commands for adding a list item above/below are used outside of a list would be a nice addition, as would supporting insert mode.

The ideal usage for this feature for me is to be able to set mappings to make it work seamlessly during normal text editing. For that, I would map o, O, and <cr>, as I believe @masa0x80 has done, so that:

  • in normal mode, pressing o or O on a list item adds a new list item beneath/above the current item and leaves me in insert mode
  • in normal mode, pressing o or O when not on a list item works as usual (insert a blank line beneath/above the current one and leave me in insert mode)
  • in insert mode, pressing <cr> when on a list item adds a new list item beneath the current one and leaves me in insert mode
  • in insert mode, pressing <cr> when not on a list item works as usual (insert a blank line beneath the current one and leave me in insert mode)

Another way to support the "what do do when not in a list" scenario without modifying the existing logic for the commands might be to expose a helper is_list_item() api, so that users could easily do the desired logic in their mappings.

@mmirus
Copy link

mmirus commented Jun 6, 2024

This gets pretty darned close, based on what I found in the plugin code and this PR.

local function is_list_item()
  local md_ts = require("markdown.treesitter")
  local curr_row = vim.api.nvim_win_get_cursor(0)[1] - 1
  local curr_eol = vim.fn.col("$") - 1

  return md_ts.find_node(function(node)
    return node:type() == "list_item"
  end, { pos = { curr_row, curr_eol } })
end

return {
  "tadmccorkle/markdown.nvim",
  ft = "markdown",
  opts = {
    on_attach = function(bufnr)
      -- New list item below on `o` if in a list
      vim.keymap.set("n", "o", function()
        if is_list_item() then
          vim.cmd("MDListItemBelow")
          return
        end

        vim.api.nvim_feedkeys("o", "n", true)
      end, { buffer = bufnr, desc = "Insert list item below" })

      -- New list item above on `O` if in a list
      vim.keymap.set("n", "O", function()
        if is_list_item() then
          vim.cmd("MDListItemAbove")
          return
        end

        vim.api.nvim_feedkeys("O", "n", true)
      end, { buffer = bufnr, desc = "Insert list item above" })

      -- New list item below on `<cr>` if in a list and at the end of the line
      vim.keymap.set("i", "<cr>", function()
        local is_eol = vim.fn.col(".") == vim.fn.col("$")
        local in_list = is_list_item()

        if is_eol and in_list then
          vim.cmd("MDListItemBelow")
          return
        end

        local key = vim.api.nvim_replace_termcodes("<cr>", true, false, true)
        vim.api.nvim_feedkeys(key, "n", false)
      end, { buffer = bufnr, desc = "Insert list item below" })
    end,
  },
}

masa0x80 added a commit to masa0x80/dotfiles that referenced this pull request Jun 7, 2024
masa0x80 added a commit to masa0x80/dotfiles that referenced this pull request Jun 7, 2024
@masa0x80
Copy link
Author

masa0x80 commented Jun 8, 2024

@mmirus Thank you for your advice.

Your code is very simple and needless to fix plugin code, so I close this PR.

@masa0x80 masa0x80 closed this Jun 8, 2024
masa0x80 added a commit to masa0x80/dotfiles that referenced this pull request Jun 8, 2024
@mmirus
Copy link

mmirus commented Jun 8, 2024

Glad that's helpful for you! Still, if there is some reasonable way for the plugin to either directly support this workflow or offer APIs that would make it easier for users to achieve it through mappings, that would be helpful. All up to @tadmccorkle, of course, based on his goals and philosophy for the plugin and how much he wants to support.

@tadmccorkle
Copy link
Owner

@masa0x80 Sorry for the radio silence on this PR. And thanks @mmirus for the suggestions.

Because there are potentially various desired behaviors, the intended use case for the list item feature was to support unique mappings to :MDInsertListItem[Above|Below] and to allow users to tune any "complex" behaviors (e.g., inserting list items on <CR>) to their liking with the on_attach callback (just like @mmirus showed above).

That said, I've played around with different ways of supporting this workflow based on your suggestions. Here's what I've settled on for now:

  • As of d6dd17d, insert_list_item_above and insert_list_item_below now return to indicate if a list item was successfully added. So the on_attach mappings above can be further simplified to:

    local map = vim.keymap.set
    local opts = { buffer = bufnr, silent = true }
    map("n", "o", function()
      if not require("markdown.list").insert_list_item_below() then
        vim.api.nvim_feedkeys("o", "n", false)
      end
    end, opts)
    
    map("n", "O", function()
      if not require("markdown.list").insert_list_item_above() then
        vim.api.nvim_feedkeys("O", "n", false)
      end
    end, opts)
    
    map("i", "<CR>", function()
      local is_eol = vim.fn.col(".") == vim.fn.col("$")
    
      if not (is_eol and require("markdown.list").insert_list_item_below()) then
        local key = vim.api.nvim_replace_termcodes("<CR>", true, false, true)
        vim.api.nvim_feedkeys(key, "n", false)
      end
    end, opts)

    Note that those mappings probably don't have the desired behavior in some scenarios; for example, they can insert new list items when the cursor is not on the last line of a multi-line list item.

  • See Add helpers module for common behaviors #15 for where easy-to-use support can be added for the <CR> workflow without interfering with other plugin code.

  • Regarding additional helpers like is_list_item for use in mappings, I would prefer use of the helper require("markdown.treesitter").find_node as you did above. I wouldn't really know where to draw the line on what deserves its own helper function, and it's trivial to use it in your own functions like is_list_item. I can definitely add better documentation for this function to make it more discoverable though.

Any feedback on what I've outlined above is welcome!

masa0x80 added a commit to masa0x80/dotfiles that referenced this pull request Jun 10, 2024
@mmirus
Copy link

mmirus commented Jun 10, 2024

Thank you, @tadmccorkle! That's great.

In case it's helpful for anyone else, in my own config, I extracted the insert_list_item_*() function calls into variables to make it extra obvious that it's trying to insert a list item and branching the logic off that. Just a personal preference for more obvious code over shorter code. Example:

    on_attach = function(bufnr)
      local function buffer_map(mode, lhs, rhs, desc)
        vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc, silent = true })
      end

      local md_list = require("markdown.list")

      -- New list item below on `o` if in a list
      buffer_map("n", "o", function()
        local list_item_inserted = md_list.insert_list_item_below()
        if not list_item_inserted then
          vim.api.nvim_feedkeys("o", "n", false)
        end
      end, "Insert list item below")

       -- ...
    end,

Note that those mappings probably don't have the desired behavior in some scenarios; for example, they can insert new list items when the cursor is not on the last line of a multi-line list item.

Yeah, that's what I was obliquely referring to when I said "This gets pretty darned close" in my comment above. 😁 I didn't find a solution for it, but so far it doesn't seem like a huge annoyance.

Thank you for the plugin update and your comment! This solution feels pretty good. Might take users slightly out of their existing knowledge (feedkeys), which isn't bad, but doesn't require them to figure out how to find list item nodes with treesitter (even using the helper from the plugin).

@mmirus
Copy link

mmirus commented Jun 12, 2024

Note that those mappings probably don't have the desired behavior in some scenarios; for example, they can insert new list items when the cursor is not on the last line of a multi-line list item.

Yeah, that's what I was obliquely referring to when I said "This gets pretty darned close" in my comment above. 😁 I didn't find a solution for it, but so far it doesn't seem like a huge annoyance.

It turned out to be pretty annoying. 😂 This seems like it might be an okay workaround? Haven't used it much yet.

    on_attach = function(bufnr)
      local function buffer_map(mode, lhs, rhs, desc)
        vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc, silent = true })
      end

      -- ⭐⭐⭐ This is the new bit:
      -- Attempt to insert a list item if the current line is not empty
      local md_list = require("markdown.list")
      local function insert_list_item(direction)
        local line_contents = vim.trim(vim.fn.getline("."))
        if vim.fn.empty(line_contents) == 1 then
          return false
        end

        return direction == "above" and md_list.insert_list_item_above() or md_list.insert_list_item_below()
      end

      -- New list item below on `o` if in a list
      buffer_map("n", "o", function()
        -- ⭐⭐⭐ Example usage:
        local list_item_inserted = insert_list_item()
        if not list_item_inserted then
          vim.api.nvim_feedkeys("o", "n", false)
        end
      end, "Insert list item below")

      -- New list item above on `O` if in a list
      buffer_map("n", "O", function()
        local list_item_inserted = insert_list_item("above")
        if not list_item_inserted then
          vim.api.nvim_feedkeys("O", "n", false)
        end
      end, "Insert list item above")

      -- New list item below on `<cr>` if in a list and at the end of the line
      buffer_map("i", "<cr>", function()
        local is_eol = vim.fn.col(".") == vim.fn.col("$")
        local list_item_inserted = is_eol and insert_list_item()
        if not list_item_inserted then
          local key = vim.api.nvim_replace_termcodes("<cr>", true, false, true)
          vim.api.nvim_feedkeys(key, "n", false)
        end
      end, "Insert list item below")

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

Successfully merging this pull request may close these issues.

None yet

3 participants