123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- local core = require "core"
- local config = require "core.config"
- local common = require "core.common"
- local style = require "core.style"
- local Doc = require "core.doc"
- local DocView = require "core.docview"
- local View = require "core.view"
- local SingleLineDoc = Doc:extend()
- function SingleLineDoc:insert(line, col, text)
- SingleLineDoc.super.insert(self, line, col, text:gsub("\n", ""))
- end
- local CommandView = DocView:extend()
- local max_suggestions = 10
- local noop = function() end
- local default_state = {
- submit = noop,
- suggest = noop,
- cancel = noop,
- }
- function CommandView:new()
- CommandView.super.new(self, SingleLineDoc())
- self.suggestion_idx = 1
- self.suggestions = {}
- self.suggestions_height = 0
- self.last_change_id = 0
- self.gutter_width = 0
- self.gutter_text_brightness = 0
- self.selection_offset = 0
- self.state = default_state
- self.font = "font"
- self.size.y = 0
- self.label = ""
- end
- function CommandView:get_name()
- return View.get_name(self)
- end
- function CommandView:get_line_screen_position()
- local x = CommandView.super.get_line_screen_position(self, 1)
- local _, y = self:get_content_offset()
- local lh = self:get_line_height()
- return x, y + (self.size.y - lh) / 2
- end
- function CommandView:get_scrollable_size()
- return 0
- end
- function CommandView:scroll_to_make_visible()
- -- no-op function to disable this functionality
- end
- function CommandView:get_text()
- return self.doc:get_text(1, 1, 1, math.huge)
- end
- function CommandView:set_text(text)
- self.doc:remove(1, 1, math.huge, math.huge)
- self.doc:text_input(text)
- end
- function CommandView:move_suggestion_idx(dir)
- local n = self.suggestion_idx + dir
- self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
- self:complete()
- self.last_change_id = self.doc:get_change_id()
- end
- function CommandView:complete()
- if #self.suggestions > 0 then
- self:set_text(self.suggestions[self.suggestion_idx].text)
- end
- end
- function CommandView:submit()
- local suggestion = self.suggestions[self.suggestion_idx]
- local text = self:get_text()
- local submit = self.state.submit
- self:exit(true)
- submit(text, suggestion)
- end
- function CommandView:enter(text, submit, suggest, cancel)
- if self.state ~= default_state then
- return
- end
- self.state = {
- submit = submit or noop,
- suggest = suggest or noop,
- cancel = cancel or noop,
- view = core.active_view
- }
- core.active_view = self
- self:update_suggestions()
- self.gutter_text_brightness = 100
- self.label = text .. ": "
- end
- function CommandView:exit(submitted, inexplicit)
- if core.active_view == self then
- core.active_view = self.state.view
- end
- local cancel = self.state.cancel
- self.state = default_state
- self.doc:reset()
- self.suggestions = {}
- if not submitted then cancel(not inexplicit) end
- end
- function CommandView:get_gutter_width()
- return self.gutter_width
- end
- function CommandView:get_suggestion_line_height()
- return self:get_font():get_height() + style.padding.y
- end
- function CommandView:update_suggestions()
- local t = self.state.suggest(self:get_text()) or {}
- local res = {}
- for i, item in ipairs(t) do
- if i == max_suggestions then
- break
- end
- if type(item) == "string" then
- item = { text = item }
- end
- res[i] = item
- end
- self.suggestions = res
- self.suggestion_idx = 1
- end
- function CommandView:update()
- CommandView.super.update(self)
- if core.active_view ~= self and self.state ~= default_state then
- self:exit(false, true)
- end
- -- update suggestions if text has changed
- if self.last_change_id ~= self.doc:get_change_id() then
- self:update_suggestions()
- self.last_change_id = self.doc:get_change_id()
- end
- -- update gutter text color brightness
- self:move_towards("gutter_text_brightness", 0, 0.1)
- -- update gutter width
- local dest = self:get_font():get_width(self.label) + style.padding.x
- if self.size.y <= 0 then
- self.gutter_width = dest
- else
- self:move_towards("gutter_width", dest)
- end
- -- update suggestions box height
- local lh = self:get_suggestion_line_height()
- local dest = #self.suggestions * lh
- self:move_towards("suggestions_height", dest)
- -- update suggestion cursor offset
- local dest = self.suggestion_idx * self:get_suggestion_line_height()
- self:move_towards("selection_offset", dest)
- -- update size based on whether this is the active_view
- local dest = 0
- if self == core.active_view then
- dest = style.font:get_height() + style.padding.y * 2
- end
- self:move_towards(self.size, "y", dest)
- end
- function CommandView:draw_line_highlight()
- -- no-op function to disable this functionality
- end
- function CommandView:draw_gutter_text(idx, x, y)
- local yoffset = self:get_line_text_y_offset()
- local pos = self.position
- local color = common.lerp(style.text, style.accent, self.gutter_text_brightness / 100)
- core.push_clip_rect(pos.x, pos.y, self:get_gutter_width(), self.size.y)
- renderer.draw_text(self:get_font(), self.label, x, y + yoffset, color)
- core.pop_clip_rect()
- end
- local function draw_suggestions_box(self)
- local lh = self:get_suggestion_line_height()
- local dh = style.divider_size
- local offsety = self:get_line_text_y_offset()
- local x, _ = self:get_line_screen_position()
- local h = self.suggestions_height
- local rx, ry, rw, rh = self.position.x, self.position.y - h - dh, self.size.x, h
- -- draw suggestions background
- if #self.suggestions > 0 then
- renderer.draw_rect(rx, ry, rw, rh, style.background3)
- renderer.draw_rect(rx, ry - dh, rw, dh, style.divider)
- local y = self.position.y - self.selection_offset - dh
- renderer.draw_rect(rx, y, rw, lh, style.line_highlight)
- end
- -- draw suggestion text
- core.push_clip_rect(rx, ry, rw, rh)
- for i, item in ipairs(self.suggestions) do
- local color = (i == self.suggestion_idx) and style.accent or style.text
- local y = self.position.y - i * lh - dh
- common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh)
- if item.info then
- local w = self.size.x - x - style.padding.x
- common.draw_text(self:get_font(), style.dim, item.info, "right", x, y, w, lh)
- end
- end
- core.pop_clip_rect()
- end
- function CommandView:draw()
- CommandView.super.draw(self)
- core.root_view:defer_draw(draw_suggestions_box, self)
- end
- return CommandView
|