Skip to content

Commit

Permalink
feat: open picker without a color under cursor (#8)
Browse files Browse the repository at this point in the history
Closes: #6
  • Loading branch information
eero-lehtinen authored Feb 9, 2025
1 parent 695b978 commit fd096f9
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 17 deletions.
42 changes: 37 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,52 @@ opts = {

## API & Commands

### Picking

```lua
-- Launch color picker for color under cursor:
-- Launch color picker for color under cursor.
require('oklch-color-picker').pick_under_cursor()
-- Force input format, useful for raw color types that can't be auto detected:
require('oklch-color-picker').pick_under_cursor("raw_oklch")
-- Force input format, useful for raw color types that can't be auto detected.
require('oklch-color-picker').pick_under_cursor({ force_format = "raw_oklch" })
-- Call `open_picker` as a fallback with default options if there was no color under the cursor.
require('oklch-color-picker').pick_under_cursor({ fallback_open = {} })

-- Open the picker ignoring whatever is under the cursor. Useful for inserting new colors.
require("oklch-color-picker").open_picker()
```

The command `:ColorPickOklch` can be used instead of `pick_under_cursor()`.

#### Definitions

```lua
---@class picker.PickUnderCursorOpts
---@field force_format? string auto detect by default
---@field fallback_open? picker.OpenPickerOpts open the picker anyway if no color under the cursor is found

--- @param opts? picker.PickUnderCursorOpts
--- @return boolean success true if a color was found and picker opened
function pick_under_cursor(opts)

---@class picker.OpenPickerOpts
---@field initial_color? string any color that the picker can parse, e.g. "#fff" (uses a random hex color by default)
---@field force_format? string auto detect by default

--- @param opts? picker.OpenPickerOpts
--- @return boolean success true if the picker was able to open
function open_picker(opts)

```

### Highlighting

```lua
-- Highlighting can be controlled at runtime:
require("oklch-color-picker").highlight.disable()
require("oklch-color-picker").highlight.enable()
require("oklch-color-picker").highlight.toggle()
```

The command `:ColorPickOklch` can be used instead of `pick_under_cursor()`.

### Color Formats

The picker application supports the following formats: (`hex`, `rgb`, `oklch`, `hsl`, `hex_literal`, `raw_rgb`, `raw_rgb_float`, `raw_rgb_linear`, `raw_oklch`).
Expand Down
1 change: 1 addition & 0 deletions lua/oklch-color-picker/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ function M.components_to_number(r, g, b)
end

M.pick_under_cursor = picker.pick_under_cursor
M.open_picker = picker.open_picker

M.highlight = {
enable = highlight.enable,
Expand Down
80 changes: 68 additions & 12 deletions lua/oklch-color-picker/picker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ function M.setup(final_patterns_)
final_patterns = final_patterns_
end

--- @alias oklch.picker.PendingEdit { bufnr: number, changedtick: number, line_number: number, start: number, finish: number, color: string, color_format: string|nil }|nil

--- @type oklch.picker.PendingEdit
--- @class oklch.picker.PendingEdit
--- @field bufnr number
--- @field changedtick number
--- @field line_number number
--- @field start number
--- @field finish number
--- @field color? string
--- @field color_format? string

--- @type oklch.picker.PendingEdit|nil
local pending_edit = nil

---@param color string
Expand All @@ -34,7 +41,7 @@ local function apply_new_color(color)
pending_edit.line_number - 1,
pending_edit.start - 1,
pending_edit.line_number - 1,
pending_edit.finish,
pending_edit.finish - 1,
{ color }
)
pending_edit = nil
Expand All @@ -46,7 +53,7 @@ end
local function start_app()
if not pending_edit then
utils.log("Can't start app, no pending edit", vim.log.levels.WARN)
return
return false
end

local stdout = function(err, data)
Expand Down Expand Up @@ -78,7 +85,7 @@ local function start_app()
local exec = utils.executable_full_path()
if exec == nil then
utils.log("Picker executable not found", vim.log.levels.ERROR)
return
return false
end

local cmd = { exec, pending_edit.color }
Expand All @@ -93,6 +100,8 @@ local function start_app()
end
utils.log("App exited successfully " .. vim.inspect(res), vim.log.levels.DEBUG)
end)

return true
end

--- @param line string
Expand Down Expand Up @@ -120,7 +129,7 @@ local function find_color(line, cursor_col, ft)

if color then
return {
pos = { replace_start, replace_end - 1 },
pos = { replace_start, replace_end },
color = color,
color_format = format,
}
Expand All @@ -136,36 +145,83 @@ local function find_color(line, cursor_col, ft)
return nil
end

--- @param force_color_format string|nil
function M.pick_under_cursor(force_color_format)
local function cursor_info()
local cursor_pos = vim.api.nvim_win_get_cursor(0)
local row = cursor_pos[1]
local col = cursor_pos[2] + 1

local bufnr = vim.api.nvim_get_current_buf()

return row, col, bufnr
end

---@class picker.PickUnderCursorOpts
---@field force_format? string auto detect by default
---@field fallback_open? picker.OpenPickerOpts open the picker anyway if no color under the cursor is found

--- @param opts? picker.PickUnderCursorOpts|string opts (or `force_color_format` for backwards compatibility)
--- @return boolean success true if a color was found and picker opened
function M.pick_under_cursor(opts)
local row, col, bufnr = cursor_info()

local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
local ft = vim.api.nvim_get_option_value("filetype", { buf = 0 })

local res = find_color(line, col, ft)

if not res then
if type(opts) == "table" and opts.fallback_open then
return M.open_picker(opts.fallback_open)
end
utils.log("No color under cursor", vim.log.levels.INFO)
return
return false
end

utils.log(string.format("Found color '%s' at position %s", res.color, vim.inspect(res.pos)), vim.log.levels.DEBUG)

local color_format = nil
if type(opts) == "string" then
color_format = opts
elseif type(opts) == "table" then
color_format = opts.force_format
end
color_format = color_format or res.color_format

pending_edit = {
bufnr = bufnr,
changedtick = vim.api.nvim_buf_get_changedtick(bufnr),
line_number = row,
start = res.pos[1],
finish = res.pos[2],
color = res.color,
color_format = force_color_format or res.color_format,
color_format = color_format,
}

return start_app()
end

---@class picker.OpenPickerOpts
---@field initial_color? string any color that the picker can parse, e.g. "#fff" (uses a random hex color by default)
---@field force_format? string auto detect by default

--- Open the picker and insert the new color at the cursor position. Ignores whatever is happening under the cursor.
---
--- @param opts? picker.OpenPickerOpts
--- @return boolean success true if the picker was able to open
function M.open_picker(opts)
local row, col, bufnr = cursor_info()

pending_edit = {
bufnr = bufnr,
changedtick = vim.api.nvim_buf_get_changedtick(bufnr),
line_number = row,
start = col,
finish = col,
color = opts and opts.initial_color,
color_format = opts and opts.force_format,
}

start_app()
return start_app()
end

return M

0 comments on commit fd096f9

Please sign in to comment.