[
  {
    "path": ".github/workflows/release-please.yml",
    "content": "on:\n  push:\n    branches:\n      - main\nname: release-please\njobs:\n  release-please:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: google-github-actions/release-please-action@v3\n        with:\n          release-type: simple\n          package-name: release-please-action\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [3.0.1](https://github.com/nat-418/boole.nvim/compare/v3.0.0...v3.0.1) (2023-01-14)\n\n\n### Bug Fixes\n\n* misc bugfixes ([16836c4](https://github.com/nat-418/boole.nvim/commit/16836c444252295cc984fe831fc6ef4ec186d89b))\n\n## [3.0.0](https://github.com/nat-418/boole.nvim/compare/v2.1.2...v3.0.0) (2022-11-14)\n\n\n### ⚠ BREAKING CHANGES\n\n* jump to matches correctly\n\n### Features\n\n* add enable/disable ([3bc80ec](https://github.com/nat-418/boole.nvim/commit/3bc80ece8ea74f85665e0184d5853ee583dec534))\n* expose generate method ([0348b3e](https://github.com/nat-418/boole.nvim/commit/0348b3eaa5be364a3a8b4e896d81f35a66b5cd21))\n* support case insensitive pairs ([93617c4](https://github.com/nat-418/boole.nvim/commit/93617c4bc1f1826c76b17fc952c22ef48fe6d276))\n\n\n### Bug Fixes\n\n* hard stop at EOL ([cbb9221](https://github.com/nat-418/boole.nvim/commit/cbb9221256db9a76a479760e331294dcf1681264))\n* jump to matches correctly ([49a1354](https://github.com/nat-418/boole.nvim/commit/49a1354ef0fd3bc23350cbbf3f8d9e7d11cab077))\n* misc. bugs ([a21bef2](https://github.com/nat-418/boole.nvim/commit/a21bef208cf557f512606ba3deaef7bd0fe8bc4b))\n* misc. bugs ([9714f67](https://github.com/nat-418/boole.nvim/commit/9714f67c7ec3aea3ba2c9a483ef27153a6ba0e73))\n* misc. bugs ([c46279f](https://github.com/nat-418/boole.nvim/commit/c46279fec4f43257fbf54596122927786711d921))\n* off by one error / infinite loop ([5515ad9](https://github.com/nat-418/boole.nvim/commit/5515ad95bd751ca4bde10f54f9f01a5669122a54))\n* off by one error / infinite loop ([353e9e1](https://github.com/nat-418/boole.nvim/commit/353e9e1dbfe3ed3d5dc4bf1f40cf632188965f53))\n"
  },
  {
    "path": "LICENSE",
    "content": "Permission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "boole.nvim 🔛\n=============\n\nBoole is a simple Neovim plugin that extends the default increment and\ndecrement functionality of CTRL-A and CTRL-X to allow for toggling\nboolean values like `on`, `yes`, and `true` as well as cycling through:\n\n* Days of the week and their abbreviations (e.g., `Monday` → `Tuesday`)\n* Months of the year and their abbreviations (e.g., `Jan` → `Feb`)\n* X11 / Web color names (e.g., `Orange` → `OrangeRed`)\n* Canonical hours (e.g., `Compline` → `Vigil`)\n* Letter + number \"words\" (e.g., `F1` → `F2`)\n\nThis plugin ships one command:\n\n* `:Boole {increment|decrement}`\n\nThis command can be safely mapped to CTRL-A and CTRL-X. See the\nconfiguration section below for an example.\n\nInstallation\n------------\n\n```sh\n$ git clone --depth 1 https://github.com/nat-418/boole.nvim ~/.local/share/nvim/site/pack/boole/start/boole.nvim\n```\n\nConfiguration\n-------------\n\nBoole can be mapped to a key by passing a configuration table to the \n`setup` function. You can also add any cycle of words you would like.\nFeel free to submit an issue and pull request with additions you\nthink would make good defaults.\n\n```lua\nrequire('boole').setup({\n  mappings = {\n    increment = '<C-a>',\n    decrement = '<C-x>'\n  },\n  -- User defined loops\n  additions = {\n    {'Foo', 'Bar'},\n    {'tic', 'tac', 'toe'}\n  },\n  allow_caps_additions = {\n    {'enable', 'disable'}\n    -- enable → disable\n    -- Enable → Disable\n    -- ENABLE → DISABLE\n  }\n})\n```\n"
  },
  {
    "path": "doc/boole.txt",
    "content": " *boole.txt*  A Neovim plugin for toggling booleans, etc.\n\n==============================================================================\nCONTENTS                                          *boole-contents*\n\n    1. Introduction ......................... |boole-introduction|\n    2. Install .............................. |boole-install|\n    3. Commands ............................. |boole-commands|\n    4. Configuration ........................ |boole-config|\n\n==============================================================================\n1. INTRODUCTION                               *boole-introduction*\n\nBoole is a simple Neovim plugin that extends the default increment and\ndecrement functionality of CTRL-A and CTRL-X to allow for toggling\nboolean values like \"on\", \"yes\", and \"true\" as well as cycling through:\n\n* Days of the week and their abbreviations (e.g., `Monday` → `Tuesday`)\n* Months of the year and their abbreviations (e.g., `Jan` → `Feb`)\n* X11 / Web color names (e.g., `Orange` → `OrangeRed`)\n* Canonical hours (e.g., `Compline` → `Vigil`)\n\n==============================================================================\n2. INSTALL                                                     *boole-install*\n\nUsing Packer: >\n    use({\n      'https://github.com/nat-418/boole.nvim',\n      config = function()\n\trequire('boole').setup()\n      end\n    })\n\n==============================================================================\n3. COMMANDS                                                   *boole-commands*\n\n:Boole {increment|decrement}                                           *Boole*\n\nToggle or cycle up or down.\n\n==============================================================================\n4. CONFIGURATION                                                 *boole-config*\n\nBoole can be mapped to a key by passing a configuration table to the \n`setup` function. You can also add any cycle of words you would like.\nFeel free to submit an issue and pull request with additions you\nthink would make good defaults.\n\nHere is an example: >                             *boole.setup()*\n  require('boole').setup({\n    mappings = {\n      increment = '<C-a>',\n      decrement = '<C-x>'\n    },\n    -- User defined loops\n    additions = {\n      {'Foo', 'Bar'}\n      {'tic', 'tac', 'toe'}\n    },\n  })\n\n==============================================================================\nvim:tw=78:ts=8:ft=help:norl:noet:fen:noet:\n"
  },
  {
    "path": "lua/boole.lua",
    "content": "local M = {}\nlocal ori_v_count = 0\nlocal replace_map = {\n  increment = {},\n  decrement = {},\n}\n\nM.generate = function(loop_list, allow_caps)\n  for i = 1, #loop_list do\n    local current = loop_list[i]\n    local next    = loop_list[i + 1] or loop_list[1]\n\n    replace_map.increment[current] = next\n    replace_map.decrement[next]    = current\n\n    if allow_caps then\n      local capitalized_current = string.gsub(current, \"^%l\", string.upper)\n      local capitalized_next    = string.gsub(next,    \"^%l\", string.upper)\n      local all_caps_current    = string.upper(current)\n      local all_caps_next       = string.upper(next)\n\n      replace_map.increment[capitalized_current] = capitalized_next\n      replace_map.decrement[capitalized_next]    = capitalized_current\n      replace_map.increment[all_caps_current]    = all_caps_next\n      replace_map.decrement[all_caps_next]       = all_caps_current\n    end\n  end\nend\n\nlocal letters = {\n  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\n  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'\n}\n\nfor _, letter in ipairs(letters) do\n    M.generate(\n        {\n            letter .. 0,\n            letter .. 1,\n            letter .. 2,\n            letter .. 3,\n            letter .. 4,\n            letter .. 5,\n            letter .. 6,\n            letter .. 7,\n            letter .. 8,\n            letter .. 9\n        },\n        true\n    )\nend\n\n-- Booleans\nM.generate({'true',    'false'},    true)\nM.generate({'yes',     'no'},       true)\nM.generate({'on',      'off'},      true)\nM.generate({'enable',  'disable'},  true)\nM.generate({'enabled', 'disabled'}, true)\n\n-- Canonical hours\nM.generate(\n    {\n        'Matins',\n        'Lauds',\n        'Prime',\n        'Terce',\n        'Sext',\n        'Nones',\n        'Vespers',\n        'Compline',\n        'Vigil'\n    }\n)\n\n-- Days of the week\nM.generate(\n    {\n        'monday',\n        'tuesday',\n        'wednesday',\n        'thursday',\n        'friday',\n        'saturday',\n        'sunday'\n    },\n    true\n)\nM.generate(\n    {\n        'mon',\n        'tue',\n        'wed',\n        'thu',\n        'fri',\n        'sat',\n        'sun'\n    },\n    true\n)\n\n-- Months of the year\nM.generate(\n    {\n        'january',\n        'february',\n        'march',\n        'april',\n        'may',\n        'june',\n        'july',\n        'august',\n        'september',\n        'october',\n        'november',\n        'december'\n    },\n    true\n)\n\n-- Colors\nM.generate(\n    {\n        'red',\n        'orange',\n        'yellow',\n        'green',\n        'blue',\n        'indigo',\n        'violet'\n    })\n\nM.generate(\n    {\n        'White',\n        'Snow',\n        'Ivory',\n        'Linen',\n        'AntiqueWhite',\n        'Beige',\n        'WhiteSmoke',\n        'LavenderBlush',\n        'OldLace',\n        'AliceBlue',\n        'SeaShell',\n        'GhostWhite',\n        'Honeydew',\n        'FloralWhite',\n        'Azure',\n        'MintCream'\n    }\n)\n\nM.generate(\n    {\n        'Black',\n        'DarkSlateGray',\n        'DimGray',\n        'SlateGray',\n        'Gray',\n        'LightSlateGray',\n        'Silver',\n        'LightGray',\n        'Gainsboro'\n    }\n)\n\nM.generate(\n    {\n        'Pink',\n        'LightPink',\n        'HotPink',\n        'PaleVioletRed',\n        'DeepPink',\n        'MediumVioletRed',\n    }\n)\n\nM.generate(\n    {\n        'Indigo',\n        'Purple',\n        'DarkMagenta',\n        'DarkViolet',\n        'DarkSlateBlue',\n        'BlueViolet',\n        'DarkOrchid',\n        'Fuchsia',\n        'Magenta',\n        'SlateBlue',\n        'MediumSlateBlue',\n        'MediumOrchid',\n        'MediumPurple',\n        'Orchid',\n        'Violet',\n        'Plum',\n        'Thistle',\n        'Lavender'\n    }\n)\n\nM.generate(\n    {\n        'DarkRed',\n        'Red',\n        'Firebrick',\n        'Crimson',\n        'IndianRed',\n        'LightCoral',\n        'Salmon',\n        'DarkSalmon',\n        'LightSalmon'\n    }\n)\n\nM.generate(\n    {\n        'OrangeRed',\n        'Tomato',\n        'DarkOrange',\n        'Coral',\n        'Orange'\n    }\n)\n\nM.generate(\n    {\n        'DarkKhaki',\n        'Gold',\n        'Khaki',\n        'PeachPuff',\n        'Yellow',\n        'PaleGoldenRod',\n        'Moccasin',\n        'PapayaWhip',\n        'LightGoldenRodYellow',\n        'LemonChiffon',\n        'LightYellow'\n    }\n)\n\nM.generate(\n    {\n        'MidnightBlue',\n        'Navy',\n        'DarkBlue',\n        'MediumBlue',\n        'Blue',\n        'RoyalBlue',\n        'SteelBlue',\n        'DodgerBlue',\n        'DeepSkyBlue',\n        'CornflowerBlue',\n        'SkyBlue',\n        'LightSkyBlue',\n        'LightSteelBlue',\n        'LightBlue',\n        'PowderBlue',\n    }\n)\n\nM.generate(\n    {\n        'Maroon',\n        'Brown',\n        'SaddleBrown',\n        'Sienna',\n        'Chocolate',\n        'DarkGoldenRod',\n        'Peru',\n        'RosyBrown',\n        'GoldenRod',\n        'SandyBrown',\n        'Tan',\n        'BurlyWood',\n        'Wheat',\n        'NavajoWhite',\n        'Bisque',\n        'BlanchedAlmond',\n        'Cornsilk',\n    }\n)\n\nM.generate(\n    {\n        'Teal',\n        'DarkCyan',\n        'LightSeaGreen',\n        'CadetBlue',\n        'DarkTurquoise',\n        'MediumTurquoise',\n        'Turquoise',\n        'Aqua',\n        'Cyan',\n        'Aquamarine',\n        'PaleTurquoise',\n        'LightCyan',\n    }\n)\n\nM.generate(\n    {\n        'DarkGreen',\n        'Green',\n        'DarkOliveGreen',\n        'ForestGreen',\n        'SeaGreen',\n        'Olive',\n        'OliveDrab',\n        'MediumSeaGreen',\n        'LimeGreen',\n        'Lime',\n        'SpringGreen',\n        'MediumSpringGreen',\n        'DarkSeaGreen',\n        'MediumAquamarine',\n        'YellowGreen',\n        'LawnGreen',\n        'Chartreuse',\n        'LightGreen',\n        'GreenYellow',\n        'PaleGreen',\n    }\n)\n--Check cursor word match the cword\nfunction check_postion_word(words,target_position,target_word)\n    --In lua tab space is %s other is %S\n    --In vim cword won't contains \", different behavior with lua\n    --we have to know char in column[0] is word or %s\n    i,j = string.find(words,\"%S+\");\n    local position = 0;\n    if i==1 then \n        position = j\n        --Cursor in first word\n        if position > target_position then\n            return string.find(words:sub(i,j), target_word)\n        end\n    end\n    -- split word with (space\\tab)word\n    for word in string.gmatch(words,\"%s+%S+\") do\n        position = position + string.len(word)\n        local s_word = word:gsub(\"%s\",\"\")\n        -- means target_position is in %s location no need to compare\n        if position - string.len(s_word) >= target_position then\n            return false\n        end\n        if position > target_position then\n            return string.find(s_word, target_word)\n        end\n    end\n    return false\nend\n\nfunction number_exist_in_word(line,current_column)\n  local space_after_cursor = string.find(line:sub(current_column + 1, string.len(line)),\" \")\n  if space_after_cursor and space_after_cursor>1 then\n      local word_after_cursor = line:sub(current_column+1,current_column + space_after_cursor)\n      if string.match(word_after_cursor,\"%d\") then\n          return true\n      end\n  -- no space check it contains number or not\n  elseif space_after_cursor == nil then\n      local last_string = line:sub(current_column+1,string.len(line))\n      if string.match(last_string,\"%d\") then\n          return true\n      end\n  end\n  return false\nend\n\nM.run = function(direction)\n  local start_position = vim.api.nvim_win_get_cursor(0)\n\n  -- Tail-recursive function to match and replace.\n  local function tryMatch(last_position)\n    local line             = vim.api.nvim_get_current_line()\n    local cword            = vim.fn.expand('<cword>')\n    local current_position = vim.api.nvim_win_get_cursor(0)\n    local current_column   = current_position[2]\n    --Record the v.count vim.v.count will be reset after vim.cmd\n    --if we have any good idea please modify it\n    if(vim.v.count ~=0 ) then\n      ori_v_count = vim.v.count\n    end\n\n    -- C-a and C-x already handle numbers, no need to try and\n    -- match them to out added values.\n    -- after current_column contains number\n    if tonumber(cword) ~= nil or number_exist_in_word(line,current_column) then\n      return false\n    end\n\n    -- we only need check char in alpha and number\n    if string.find(line:sub(current_column+1, current_column+1),\"[^][a-zA-Z0-9]\") then\n      if (current_column + 1) == vim.fn.strlen(line) then\n        vim.api.nvim_win_set_cursor(0, start_position)\n        return false\n      end\n      vim.cmd('normal! w')\n      return tryMatch(current_position)\n    end\n\n    -- Limit matches to the original line.\n    if last_position[1] < current_position[1] then\n        -- After check number exist in word avoid number in front of cursor be increase or decrease\n        -- if we use wb 123_te*st (* as cursor) and use <C-a>\n        -- will be 124*_test not as aspect\n        vim.api.nvim_win_set_cursor(0, start_position)\n        return false\n    end\n\n    -- Do we have a match?\n    local match = direction == 'decrement'\n          and replace_map.decrement[cword]\n          or  replace_map.increment[cword]\n\n    if match then\n      -- Are we on the first character of the word? If not, move there.\n      -- If not first char compare current word is match or not\n      if cword:sub(1, 1) ~= line:sub(current_column + 1, current_column + 1) then\n        if check_postion_word(line,current_column, cword) then\n          vim.cmd('normal! b')\n        else\n          vim.cmd('normal! w')\n        end\n        return tryMatch(current_position)\n      -- Are we at the end of the line? If so, jump back.\n      -- Even in last word we will check match or not so move this to back\n      elseif (current_column + 1) == vim.fn.strlen(line) then\n        vim.api.nvim_win_set_cursor(0, start_position)\n        return false\n      end\n      --use ori_v_count to get correct match data\n      for i =0, ori_v_count-1 do\n          match = direction == 'decrement'\n          and replace_map.decrement[cword]\n          or  replace_map.increment[cword]\n          if match then\n              cword = match\n          else\n              return false\n          end\n      end\n      -- Replace the word and put the cursor on the beginning of replacement.\n      ori_v_count = 0\n      vim.cmd('normal! \"_ciw' .. match)\n      vim.cmd('normal! b')\n      return true\n    else\n      -- Are we at the end of the line? If so, give up.\n      if (current_column + 1) == vim.fn.strlen(line) then\n        vim.api.nvim_win_set_cursor(0, start_position)\n        return false\n      end\n      -- Try the next word to see if it matches.\n      vim.cmd('normal! w')\n      return tryMatch(current_position)\n    end\n  end\n\n  -- Fallback to original <C-a> and <C-x> functions for numbers.\n  if not tryMatch(start_position) then\n    local target_v_count = ori_v_count\n    ori_v_count = 0\n    if target_v_count ~= nil and target_v_count > 0 then\n        if direction == 'increment' then\n            return vim.cmd('normal!' .. target_v_count .. '\u0001')\n        end\n        if direction == 'decrement' then\n            return vim.cmd('normal!' .. target_v_count .. '\u0018')\n        end\n    else\n        if direction == 'increment' then return vim.cmd('normal!' .. '\u0001') end\n        if direction == 'decrement' then return vim.cmd('normal!' .. '\u0018') end\n    end\n  end\nend\n\nM.setup = function(options)\n    vim.api.nvim_create_user_command(\n        'Boole',\n        function(args) M.run(args.args) end,\n        {\n            nargs = 1,\n            complete = function() return { 'increment', 'decrement' } end\n        }\n    )\n\n    if options == nil then return false end\n\n    if options.allow_caps_additions ~= nil then\n       for _, val in pairs(options.allow_caps_additions) do\n           M.generate(val, true)\n       end\n    end\n\n    if options.additions ~= nil then\n        for _, val in pairs(options.additions) do\n            M.generate(val)\n        end\n    end\n\n    if options.mappings.increment ~= nil then\n        vim.keymap.set(\n            { 'n', 'v' },\n            options.mappings.increment,\n            '<Cmd>Boole increment<CR>'\n        )\n    end\n\n    if options.mappings.decrement ~= nil then\n        vim.keymap.set(\n            { 'n', 'v' },\n            options.mappings.decrement,\n            '<Cmd>Boole decrement<CR>'\n        )\n    end\n\n    return true\nend\n\nreturn M\n"
  }
]