Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/cmdline/render.lua

Raw
local format = require 'bugabinga.cmdline.format'
local position = require 'bugabinga.cmdline.position'
local cursor = require 'bugabinga.cmdline.cursor'
local highlight = require 'bugabinga.cmdline.highlight'

local ns = vim.api.nvim_create_namespace 'bugabinga.cmdline'
local hl_ns = vim.api.nvim_create_namespace 'bugabinga.cmdline.hl'
local prompt_ns = vim.api.nvim_create_namespace 'bugabinga.cmdline.prompt'

local state = {
	buffer = -1,
	window = -1,
	current_fmt = nil,
	text = '',
	firstc = '',
	prompt = '',
}

local function get_buf()
	if vim.api.nvim_buf_is_valid( state.buffer ) then
		return state.buffer
	end
	state.buffer = vim.api.nvim_create_buf( false, true )
	vim.bo[state.buffer].buflisted = false
	vim.bo[state.buffer].filetype = 'bugabinga_cmdline'
	return state.buffer
end

local function get_win( buf, width, fmt )
	if vim.api.nvim_win_is_valid( state.window ) then
		return state.window
	end
	local screen_pos = position.at_cursor( width or vim.o.columns )
	state.window = vim.api.nvim_open_win( buf, false, {
		relative = 'editor',
		width = width or vim.o.columns,
		height = 1,
		row = screen_pos.row,
		col = 0,
		style = 'minimal',
		border = vim.g.border_style,
		focusable = false,
		zindex = 50,
	} )
	vim.wo[state.window].cursorline = false
	vim.wo[state.window].winbar = ''
	vim.wo[state.window].foldenable = false
	vim.wo[state.window].winhighlight = format.winhighlight( fmt.type, 'NormalFloat' )
	return state.window
end

local function apply_icon( buf, fmt )
	vim.api.nvim_buf_clear_namespace( buf, ns, 0, -1 )
	local icon_hl = format.icon_highlight( fmt.type )
	vim.api.nvim_buf_set_extmark( buf, ns, 0, 0, {
		virt_text = { { fmt.icon, icon_hl } },
		virt_text_pos = 'overlay',
	} )
end

local function show( cmdline_state )
	local text = cmdline_state:text()
	local firstc = cmdline_state.firstc
	local prompt = cmdline_state.prompt
	state.text = text
	state.firstc = firstc
	state.prompt = prompt
	local fmt = format.detect( firstc, text, prompt )
	local fmt_changed = not state.current_fmt or fmt.type ~= state.current_fmt.type
	state.current_fmt = fmt
	local display_text = '  ' .. text .. ' '
	local width = vim.o.columns
	local buf = get_buf()
	local win = get_win( buf, width, fmt )
	local is_new = not position.get_cmdline_anchor()
	if is_new then
		local screen_pos = position.at_cursor( width )
		vim.api.nvim_win_set_config( win, {
			relative = 'editor',
			width = width,
			height = 1,
			row = screen_pos.row,
			col = 0,
		} )
		local anchor = {
			screenpos = { row = screen_pos.row, col = 0 },
			cursor = 0,
			buf = buf,
			win = win,
			bufpos = { row = 0, col = 0 },
		}
		position.set_cmdline_anchor( anchor )
		vim.g.ui_cmdline_pos = { anchor.screenpos.row, anchor.screenpos.col }
	end
	if fmt_changed then
		vim.wo[win].winhighlight = format.winhighlight( fmt.type, 'NormalFloat' )
	end
	vim.api.nvim_buf_set_lines( buf, 0, -1, false, { display_text } )
	apply_icon( buf, fmt )
	highlight.apply( buf, hl_ns, text, fmt.lang, 0, 2 )

	if fmt.type == 'input' and prompt and prompt ~= '' then
		vim.api.nvim_buf_clear_namespace( buf, prompt_ns, 0, -1 )
		local max_prompt_width = math.floor( vim.o.columns * format.PROMPT_MAX_RATIO )
		local prompt_is_single_line = not prompt:match '\n'
		local prompt_fits = vim.fn.strdisplaywidth( prompt ) <= max_prompt_width
		if prompt_is_single_line and prompt_fits then
			local title_text = ' ' .. prompt .. ' '
			vim.api.nvim_win_set_config( win, {
				border = vim.g.border_style,
				title = { { title_text, 'CmdlineBorderInput' } },
				title_pos = 'left',
			} )
		else
			local prompt_chunks = format.prompt_lines( prompt, max_prompt_width )
			local virt_line_tuples = {}
			for _, chunk in ipairs( prompt_chunks ) do
				vim.notify( 'adding prompt chunk', chunk )
				table.insert( virt_line_tuples, { { chunk, 'NormalFloat' } } )
			end
			vim.api.nvim_buf_set_extmark( buf, prompt_ns, 0, 0, {
				virt_lines = virt_line_tuples,
				virt_lines_above = true,
			} )
			vim.api.nvim_win_set_config( win, {
				border = vim.g.border_style,
				title = { { ' ', 'Normal' } },
				title_pos = 'left',
				height = #prompt_chunks + 1,
			} )
		end
	end

	local cursor_byte = cmdline_state:cursor_byte()
	local cursor_col = 2 + cursor_byte
	local anchor = position.get_cmdline_anchor()
	if anchor then
		anchor.cursor = cursor_col
		anchor.bufpos.col = cursor_col
		position.set_cmdline_anchor( anchor )
	end
	cursor.fix( win, cursor_col )
end

local function hide()
	if vim.api.nvim_win_is_valid( state.window ) then
		vim.api.nvim_buf_clear_namespace( state.buffer, prompt_ns, 0, -1 )
		vim.api.nvim_win_hide( state.window )
	end
	state.window = -1
	state.current_fmt = nil
	state.text = ''
	state.firstc = ''
	state.prompt = ''
	position.set_cmdline_anchor( nil )
	vim.g.ui_cmdline_pos = nil
end

local function update_cursor( cmdline_state )
	if not vim.api.nvim_win_is_valid( state.window ) then
		return
	end
	local cursor_byte = cmdline_state:cursor_byte()
	local cursor_col = 2 + cursor_byte
	local anchor = position.get_cmdline_anchor()
	if anchor then
		anchor.cursor = cursor_col
		position.set_cmdline_anchor( anchor )
	end
	cursor.fix( state.window, cursor_col )
end

local function get_format_type()
	return state.current_fmt and state.current_fmt.type or 'cmdline'
end

local function get_text()
	return state.text
end

return {
	show = show,
	hide = hide,
	update_cursor = update_cursor,
	get_format_type = get_format_type,
	get_text = get_text,
}