From 2c1dc93d9d201f06bb5311eb3b4a981a10c7ed0e Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 6 Mar 2024 04:56:01 +0100 Subject: [PATCH] Improve `CommandView` and `autocomplete` scroll behavior (#1732) * Make command palette item scrolling more natural Also add a config option for the maximum number of visible entries in the command palette. * Make `autocomplete` item scrolling more natural --- data/core/commandview.lua | 38 ++++++++++++++++++++++++++--------- data/core/config.lua | 6 ++++++ data/plugins/autocomplete.lua | 23 ++++++++++++++++++--- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/data/core/commandview.lua b/data/core/commandview.lua index dd2c5e42..cb6a2e84 100644 --- a/data/core/commandview.lua +++ b/data/core/commandview.lua @@ -1,5 +1,6 @@ local core = require "core" local common = require "core.common" +local config = require "core.config" local style = require "core.style" local Doc = require "core.doc" local DocView = require "core.docview" @@ -20,8 +21,6 @@ local CommandView = DocView:extend() CommandView.context = "application" -local max_suggestions = 10 - local noop = function() end ---@class core.commandview.state @@ -50,6 +49,7 @@ local default_state = { function CommandView:new() CommandView.super.new(self, SingleLineDoc()) self.suggestion_idx = 1 + self.suggestions_offset = 1 self.suggestions = {} self.suggestions_height = 0 self.last_change_id = 0 @@ -128,6 +128,24 @@ function CommandView:move_suggestion_idx(dir) end end + local function get_suggestions_offset() + local max_visible = math.min(config.max_visible_commands, #self.suggestions) + if dir > 0 then + if self.suggestions_offset + max_visible < self.suggestion_idx + 1 then + return self.suggestion_idx - max_visible + 1 + elseif self.suggestions_offset > self.suggestion_idx then + return self.suggestion_idx + end + else + if self.suggestions_offset > self.suggestion_idx then + return self.suggestion_idx + elseif self.suggestions_offset + max_visible < self.suggestion_idx + 1 then + return self.suggestion_idx - max_visible + 1 + end + end + return self.suggestions_offset + end + if self.state.show_suggestions then local n = self.suggestion_idx + dir self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions) @@ -151,6 +169,8 @@ function CommandView:move_suggestion_idx(dir) self.last_change_id = self.doc:get_change_id() self.state.suggest(self:get_text()) end + + self.suggestions_offset = get_suggestions_offset() end @@ -261,6 +281,7 @@ function CommandView:update_suggestions() end self.suggestions = res self.suggestion_idx = 1 + self.suggestions_offset = 1 end @@ -300,11 +321,11 @@ function CommandView:update() -- update suggestions box height local lh = self:get_suggestion_line_height() - local dest = self.state.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0 + local dest = self.state.show_suggestions and math.min(#self.suggestions, config.max_visible_commands) * lh or 0 self:move_towards("suggestions_height", dest, nil, "commandview") -- update suggestion cursor offset - local dest = math.min(self.suggestion_idx, max_suggestions) * self:get_suggestion_line_height() + local dest = (self.suggestion_idx - self.suggestions_offset + 1) * self:get_suggestion_line_height() self:move_towards("selection_offset", dest, nil, "commandview") -- update size based on whether this is the active_view @@ -340,6 +361,7 @@ local function draw_suggestions_box(self) local h = math.ceil(self.suggestions_height) local rx, ry, rw, rh = self.position.x, self.position.y - h - dh, self.size.x, h + core.push_clip_rect(rx, ry, rw, rh) -- draw suggestions background if #self.suggestions > 0 then renderer.draw_rect(rx, ry, rw, rh, style.background3) @@ -349,14 +371,12 @@ local function draw_suggestions_box(self) end -- draw suggestion text - local offset = math.max(self.suggestion_idx - max_suggestions, 0) - local last = math.min(offset + max_suggestions, #self.suggestions) - core.push_clip_rect(rx, ry, rw, rh) - local first = 1 + offset + local first = math.max(self.suggestions_offset, 1) + local last = math.min(self.suggestions_offset + config.max_visible_commands, #self.suggestions) for i=first, last do local item = self.suggestions[i] local color = (i == self.suggestion_idx) and style.accent or style.text - local y = self.position.y - (i - offset) * lh - dh + local y = self.position.y - (i - first + 1) * lh - dh common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh) if item.info then diff --git a/data/core/config.lua b/data/core/config.lua index 88f8d96d..8e7df316 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -112,6 +112,12 @@ config.max_undos = 10000 ---@type number config.max_tabs = 8 +---The maximum number of entries shown at a time in the command palette. +--- +---The default is 10. +---@type integer +config.max_visible_commands = 10 + ---Shows/hides the tab bar when there is only one tab open. --- ---The tab bar is always shown by default. diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua index 0c09f335..5f4c2cdf 100644 --- a/data/plugins/autocomplete.lua +++ b/data/plugins/autocomplete.lua @@ -282,12 +282,14 @@ end) local partial = "" +local suggestions_offset = 1 local suggestions_idx = 1 local suggestions = {} local last_line, last_col local function reset_suggestions() + suggestions_offset = 1 suggestions_idx = 1 suggestions = {} @@ -369,6 +371,7 @@ local function update_suggestions() end end suggestions_idx = 1 + suggestions_offset = 1 end local function get_partial_symbol() @@ -581,8 +584,8 @@ local function draw_suggestions_box(av) local font = av:get_font() local lh = font:get_height() + style.padding.y local y = ry + style.padding.y / 2 - local show_count = #suggestions <= ah and #suggestions or ah - local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1 + local show_count = math.min(#suggestions, ah) + local start_index = suggestions_offset local hide_info = config.plugins.autocomplete.hide_info for i=start_index, start_index+show_count-1, 1 do @@ -848,7 +851,7 @@ command.add(predicate, { local current_partial = get_partial_symbol() local sz = #current_partial - for idx, line1, col1, line2, col2 in doc:get_selections(true) do + for _, line1, col1, line2, _ in doc:get_selections(true) do local n = col1 - 1 local line = doc.lines[line1] for i = 1, sz + 1 do @@ -869,10 +872,24 @@ command.add(predicate, { ["autocomplete:previous"] = function() suggestions_idx = (suggestions_idx - 2) % #suggestions + 1 + + local ah = math.min(config.plugins.autocomplete.max_height, #suggestions) + if suggestions_offset > suggestions_idx then + suggestions_offset = suggestions_idx + elseif suggestions_offset + ah < suggestions_idx + 1 then + suggestions_offset = suggestions_idx - ah + 1 + end end, ["autocomplete:next"] = function() suggestions_idx = (suggestions_idx % #suggestions) + 1 + + local ah = math.min(config.plugins.autocomplete.max_height, #suggestions) + if suggestions_offset + ah < suggestions_idx + 1 then + suggestions_offset = suggestions_idx - ah + 1 + elseif suggestions_offset > suggestions_idx then + suggestions_offset = suggestions_idx + end end, ["autocomplete:cycle"] = function()