Skip to content

Integration request #11

Description

@FrancescoDerme

I'm trying to create an integration with Flash but have found little success due to my inexperience in programming with lua, could you offer some guidance or implement the integration?

Activity

  1. mawkler commented on Apr 15, 2025

    @mawkler
    Owner

    @FrancescoDerme Take a look at the implementations of the other integrations in lua/demicolon/integrations/. Basically you want to call require('demicolon.jump').repeatably_do() with a function that takes in a { forward: boolean }, and based on the value of forward you want to make a call to flash.

    Based on their examples section I'm guessing that you should be able to do something like this (I haven't actually tried this code out, so I don't know if it actually works though):

    ---@param options { forward?: boolean }
    function flash_jump(options)
      return function()
        require("demicolon.jump").repeatably_do(function(opts)
          require("flash").jump({ search = { forward = opts.forward } })
        end, options)
      end
    end
    
    vim.keymap.set({ "n", "x", "o" }, "s", flash_jump({ forward = true }), { desc = "Flash" })
  2. FrancescoDerme commented on Apr 16, 2025

    @FrancescoDerme
    Author

    @mawkler thanks for the help! I'm working my way through lengthy trial and error sessions, and again I kindly ask for some guidance. This is what I have thus far. I am trying to imitate what Flash does upon pressing the 'f' key, to do so I copy the relevant internal Flash settings and pass them to the jump function, this for once doesn't seem to work, but that's not even the worst part.

    When I press 'f' I get the "prompt" for a flash jump as expected (although not with the right settings as I mentioned) asking for the character to look for in the buffer, but when I press ";" I get the prompt again instead of "repeating the last motion" i.e. "going to the next match for the previous search" which is how Flash usually behaves if there are no conflicts with the keys for treesiteer-textobjects. I fear this is a little more nasty of an integration than the already present ones.

    return {
    	"mawkler/demicolon.nvim",
    	-- keys = { ';', ',', 't', 'f', 'T', 'F', ']', '[', ']d', '[d' }, -- Uncomment this to lazy load
    	-- ft = 'tex',                                                    -- ...and this if you use LaTeX
    	dependencies = {
    		"nvim-treesitter/nvim-treesitter",
    		"nvim-treesitter/nvim-treesitter-textobjects",
    	},
    	opts = {},
    
    	config = function()
    		require("demicolon").setup({
    			keymaps = {
    				horizontal_motions = false,
    			},
    		})
    
    		---@param options { forward?: boolean }
    		function flash_jump(options)
    			return function()
    				require("demicolon.jump").repeatably_do(function(opts)
    					local config = require("flash.config")
    					local settings = vim.deepcopy(config.get().modes.char)
    					require("flash").jump(settings)
    				end, options)
    			end
    		end
    
    		vim.keymap.set({ "n", "x", "o" }, "f", flash_jump({ forward = true }), { desc = "Flash jump" })
    	end,
    }

    Notice that I also disabled Flash's char search (the one corresponding to 'f') in order to be able to manage everything from demicolon without conflicts.

    return {
    	"folke/flash.nvim",
    	event = "VeryLazy",
    	---@type Flash.Config
    	opts = {
    		modes = {
    			search = {
    				enabled = false,
    			},
    			char = {
    				enabled = false,
    				jump_labels = false,
    			},
    		},
    	},
    	keys = {
    		{
    			"s",
    			mode = { "n", "x", "o" },
    			function()
    				require("flash").jump()
    			end,
    			desc = "Flash jump",
    		},
    		{
    			"S",
    			mode = { "n", "x", "o" },
    			function()
    				require("flash").treesitter()
    			end,
    			desc = "Flash textobject jump",
    		},
    	},
    }
  3. mawkler commented on Apr 16, 2025

    @mawkler
    Owner

    Hmm yes this does indeed seem more complex to implement.

    Looking at their docs it says this:

    You can also go to the next match with ; or previous match with ,

    Isn't the feature that you want already implemented in flash.nvim then?

  4. FrancescoDerme commented on Apr 16, 2025

    @FrancescoDerme
    Author

    It is except that when I also add treesitter-textobjects and I try to make motions like “]f” repeatable with “;”, either with the following snippet of code or by using demicolon, this flash functionality breaks. Essentially the behavior I’m looking for is one where “;” can repeat motions from both treesitter-textobjects and flash based on what was the last thing I did.

    local ts_repeat_move = require "nvim-treesitter.textobjects.repeatable_move"
    
    -- Repeat movement with ; and ,
    -- ensure ; goes forward and , goes backward regardless of the last direction
    vim.keymap.set({ "n", "x", "o" }, ";", ts_repeat_move.repeat_last_move_next)
    vim.keymap.set({ "n", "x", "o" }, ",", ts_repeat_move.repeat_last_move_previous)

    I want to emphasize that this snippet of code is not present in my current setup, it works to make treesitter-textobjects motions repeatable but I discarded it and started to use demicolon instead hoping it would be easier to do what I want to with it.

  5. mawkler commented on Apr 17, 2025

    @mawkler
    Owner

    I think I finally manage to create the behaviour you want:

    ---@param options { key: string, fowrard: boolean }
    local function flash_jump(options)
      return function()
        require("demicolon.jump").repeatably_do(function(o)
          local flash_char = require("flash.plugins.char")
    
          local key = o.forward and o.key:lower() or o.key:upper()
    
          -- Copied from here
          -- https://github.com/folke/flash.nvim/blob/3c942666f115e2811e959eabbdd361a025db8b63/lua/flash/plugins/char.lua#L118-L136
          flash_char.jumping = true
          local autohide = require("flash.config").get("char").autohide
          if require("flash.repeat").is_repeat then
            flash_char.jump_labels = false
            flash_char.state:jump({ count = vim.v.count1 })
            flash_char.state:show()
          else
            flash_char.jump(key)
          end
          vim.schedule(function()
            flash_char.jumping = false
            if flash_char.state and autohide then
              flash_char.state:hide()
            end
          end)
        end, options)
      end
    end
    
    vim.keymap.set({ "n", "x", "o" }, "f", flash_jump({ key = "f", forward = true }), { desc = "Flash forward" })
    vim.keymap.set({ "n", "x", "o" }, "F", flash_jump({ key = "F", forward = false }), { desc = "Flash backward" })
  6. FrancescoDerme commented on Apr 18, 2025

    @FrancescoDerme
    Author

    @mawkler I've tried playing with your code a bit and we're almost there, thanks for the support. The slight problem with it is that after having entered Flash's jump mode I can no longer exit from it in the way I usually do (esc doesn't work, entering insert mode doesn't work) and I am forced to quit neovim. To solve this I've added the following snippet of code and it works fine.

    The only thing that's missing now is to have the different behavior of the flash jump depending on whether it was called by "f" or by ";" (right now ";" doesn't jump to the next match for the previous search as it should but only enters flash's char search mode), isn't there a way inside require('demicolon.jump').repeatably_do() to understand wether it was called by "f" or by ";"?

    local flash_char = require("flash.plugins.char")
    vim.api.nvim_create_autocmd({ "BufLeave", "CursorMoved", "InsertEnter" }, {
    	group = vim.api.nvim_create_augroup("flash_char", { clear = true }),
    	callback = function(event)
    		local hide = event.event == "InsertEnter" or not flash_char.jumping
    		if hide and flash_char.state then
    			flash_char.state:hide()
    		end
    	end,
    })
    
    vim.on_key(function(key)
    	if
    		flash_char.state
    		and key == require("flash.util").ESC
    		and (vim.fn.mode() == "n" or vim.fn.mode() == "v")
    	then
    		flash_char.state:hide()
    	end
    end)
    
  7. mawkler commented on Apr 22, 2025

    @mawkler
    Owner

    @FrancescoDerme I pushed a feature to the branch repeated which should let you determine whether it was repeated with ;/, or not. In my code above that would be the value o.repeated.

    Let me know if that's of any help!

  8. FrancescoDerme commented on Apr 22, 2025

    @FrancescoDerme
    Author

    @mawkler we did it! Well, you did it, thanks! The following code seems to be working perfectly well. Will you consider merging this feature into the main branch or is it too niche?

    return {
    	"mawkler/demicolon.nvim",
    	branch = "repeated",
    	-- keys = { ';', ',', 't', 'f', 'T', 'F', ']', '[', ']d', '[d' }, -- Uncomment this to lazy load
    	-- ft = 'tex',                                                    -- ...and this if you use LaTeX
    	dependencies = {
    		"nvim-treesitter/nvim-treesitter",
    		"nvim-treesitter/nvim-treesitter-textobjects",
    	},
    	opts = {},
    
    	config = function()
    		require("demicolon").setup({
    			keymaps = {
    				horizontal_motions = false,
    			},
    		})
    
    		local flash_char = require("flash.plugins.char")
    		---@param options { key: string, fowrard: boolean }
    		local function flash_jump(options)
    			return function()
    				require("demicolon.jump").repeatably_do(function(o)
    					local key = o.forward and o.key:lower() or o.key:upper()
    
    					flash_char.jumping = true
    					local autohide = require("flash.config").get("char").autohide
    
    					-- Originally was
    					-- if require("flash.repeat").is_repeat then
    					if o.repeated then
    						flash_char.jump_labels = false
    
    						-- Originally was
    						-- flash_char.state:jump({ count = vim.v.count1 })
    						if o.forward then
    							flash_char.right()
    						else
    							flash_char.left()
    						end
    
    						flash_char.state:show()
    					else
    						flash_char.jump(key)
    					end
    
    					vim.schedule(function()
    						flash_char.jumping = false
    						if flash_char.state and autohide then
    							flash_char.state:hide()
    						end
    					end)
    				end, options)
    			end
    		end
    
    		vim.api.nvim_create_autocmd({ "BufLeave", "CursorMoved", "InsertEnter" }, {
    			group = vim.api.nvim_create_augroup("flash_char", { clear = true }),
    			callback = function(event)
    				local hide = event.event == "InsertEnter" or not flash_char.jumping
    				if hide and flash_char.state then
    					flash_char.state:hide()
    				end
    			end,
    		})
    
    		vim.on_key(function(key)
    			if
    				flash_char.state
    				and key == require("flash.util").ESC
    				and (vim.fn.mode() == "n" or vim.fn.mode() == "v")
    			then
    				flash_char.state:hide()
    			end
    		end)
    
    		vim.keymap.set({ "n", "x", "o" }, "f", flash_jump({ key = "f", forward = true }), { desc = "Flash f" })
    		vim.keymap.set({ "n", "x", "o" }, "F", flash_jump({ key = "F", forward = false }), { desc = "Flash F" })
    		vim.keymap.set({ "n", "x", "o" }, "t", flash_jump({ key = "t", forward = true }), { desc = "Flash t" })
    		vim.keymap.set({ "n", "x", "o" }, "T", flash_jump({ key = "T", forward = false }), { desc = "Flash T" })
    	end,
    }
  9. mawkler commented on Apr 23, 2025

    @mawkler
    Owner

    @FrancescoDerme I'm refactoring the implementation to simplify the configuration for the user, but I'll consider merging this when I've completed the refactoring 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions