aboutsummaryrefslogtreecommitdiff
path: root/data
diff options
context:
space:
mode:
authorTakase <20792268+takase1121@users.noreply.github.com>2021-11-15 21:59:04 +0800
committerGitHub <noreply@github.com>2021-11-15 21:59:04 +0800
commit285ea4f495d8992a73de75ff8497d8e3d48181c2 (patch)
tree64e216dbd6f9ce4362f5fd2c9a30d362b0eb5548 /data
parent5cc23348a1a7d106aaee36c0eb88b42ed2c9b725 (diff)
parentca448c5fdbb6cb2d5f87d5955eabb9a3dfdd0e0b (diff)
downloadpragtical-285ea4f495d8992a73de75ff8497d8e3d48181c2.tar.gz
pragtical-285ea4f495d8992a73de75ff8497d8e3d48181c2.zip
Merge branch 'master' into keymap-improvements
Diffstat (limited to 'data')
-rw-r--r--data/core/commands/core.lua8
-rw-r--r--data/core/commands/doc.lua10
-rw-r--r--data/core/commands/findreplace.lua74
-rw-r--r--data/core/commandview.lua41
-rw-r--r--data/core/common.lua9
-rw-r--r--data/core/config.lua3
-rw-r--r--data/core/doc/highlighter.lua17
-rw-r--r--data/core/doc/init.lua10
-rw-r--r--data/core/docview.lua55
-rw-r--r--data/core/init.lua49
-rw-r--r--data/core/keymap-macos.lua5
-rw-r--r--data/core/keymap.lua12
-rw-r--r--data/core/regex.lua2
-rw-r--r--data/core/rootview.lua377
-rw-r--r--data/core/start.lua9
-rw-r--r--data/core/statusview.lua3
-rw-r--r--data/core/style.lua12
-rw-r--r--data/plugins/autocomplete.lua7
-rw-r--r--data/plugins/drawwhitespace.lua32
-rw-r--r--data/plugins/language_md.lua45
-rw-r--r--data/plugins/language_python.lua2
-rw-r--r--data/plugins/lineguide.lua3
-rw-r--r--data/plugins/scale.lua23
-rw-r--r--data/plugins/treeview.lua3
24 files changed, 611 insertions, 200 deletions
diff --git a/data/core/commands/core.lua b/data/core/commands/core.lua
index 7ac36976..432ded89 100644
--- a/data/core/commands/core.lua
+++ b/data/core/commands/core.lua
@@ -148,7 +148,9 @@ command.add(nil, {
["core:change-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
- core.command_view:set_text(common.home_encode(dirname) .. PATHSEP)
+ if dirname then
+ core.command_view:set_text(common.home_encode(dirname) .. PATHSEP)
+ end
core.command_view:enter("Change Project Folder", function(text, item)
text = system.absolute_path(common.home_expand(item and item.text or text))
if text == core.project_dir then return end
@@ -162,6 +164,10 @@ command.add(nil, {
end,
["core:open-project-folder"] = function()
+ local dirname = common.dirname(core.project_dir)
+ if dirname then
+ core.command_view:set_text(common.home_encode(dirname) .. PATHSEP)
+ end
core.command_view:enter("Open Project", function(text, item)
text = common.home_expand(item and item.text or text)
local path_stat = system.get_file_info(text)
diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua
index fb17e674..b8ce2cb5 100644
--- a/data/core/commands/doc.lua
+++ b/data/core/commands/doc.lua
@@ -168,16 +168,6 @@ local commands = {
doc():set_selection(line, col)
end,
-
- ["doc:indent"] = function()
- for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
- local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2)
- if l1 then
- doc():set_selections(idx, l1, c1, l2, c2)
- end
- end
- end,
-
["doc:select-lines"] = function()
for idx, line1, _, line2 in doc():get_selections(true) do
append_line_if_last_line(line2)
diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua
index 48cbecaf..f8e8e45a 100644
--- a/data/core/commands/findreplace.lua
+++ b/data/core/commands/findreplace.lua
@@ -12,6 +12,7 @@ local last_finds, last_view, last_fn, last_text, last_sel
local case_sensitive = config.find_case_sensitive or false
local find_regex = config.find_regex or false
+local found_expression
local function doc()
return core.active_view:is(DocView) and core.active_view.doc or last_view.doc
@@ -34,24 +35,37 @@ local function update_preview(sel, search_fn, text)
if ok and line1 and text ~= "" then
last_view.doc:set_selection(line2, col2, line1, col1)
last_view:scroll_to_line(line2, true)
- return true
+ found_expression = true
else
last_view.doc:set_selection(unpack(sel))
- return false
+ found_expression = false
end
end
+
+local function insert_unique(t, v)
+ local n = #t
+ for i = 1, n do
+ if t[i] == v then return end
+ end
+ t[n + 1] = v
+end
+
+
local function find(label, search_fn)
last_view, last_sel, last_finds = core.active_view,
{ core.active_view.doc:get_selection() }, {}
- local text, found = last_view.doc:get_text(unpack(last_sel)), false
+ local text = last_view.doc:get_text(unpack(last_sel))
+ found_expression = false
core.command_view:set_text(text, true)
core.status_view:show_tooltip(get_find_tooltip())
- core.command_view:enter(label, function(text)
+ core.command_view:set_hidden_suggestions()
+ core.command_view:enter(label, function(text, item)
+ insert_unique(core.previous_find, text)
core.status_view:remove_tooltip()
- if found then
+ if found_expression then
last_fn, last_text = search_fn, text
else
core.error("Couldn't find %q", text)
@@ -59,8 +73,9 @@ local function find(label, search_fn)
last_view:scroll_to_make_visible(unpack(last_sel))
end
end, function(text)
- found = update_preview(last_sel, search_fn, text)
+ update_preview(last_sel, search_fn, text)
last_fn, last_text = search_fn, text
+ return core.previous_find
end, function(explicit)
core.status_view:remove_tooltip()
if explicit then
@@ -75,19 +90,24 @@ local function replace(kind, default, fn)
core.command_view:set_text(default, true)
core.status_view:show_tooltip(get_find_tooltip())
+ core.command_view:set_hidden_suggestions()
core.command_view:enter("Find To Replace " .. kind, function(old)
+ insert_unique(core.previous_find, old)
core.command_view:set_text(old, true)
local s = string.format("Replace %s %q With", kind, old)
+ core.command_view:set_hidden_suggestions()
core.command_view:enter(s, function(new)
+ core.status_view:remove_tooltip()
+ insert_unique(core.previous_replace, new)
local n = doc():replace(function(text)
return fn(text, old, new)
end)
core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new)
- end, function() end, function()
+ end, function() return core.previous_replace end, function()
core.status_view:remove_tooltip()
end)
- end, function() end, function()
+ end, function() return core.previous_find end, function()
core.status_view:remove_tooltip()
end)
end
@@ -108,6 +128,20 @@ local function has_unique_selection()
return text ~= nil
end
+local function is_in_selection(line, col, l1, c1, l2, c2)
+ if line < l1 or line > l2 then return false end
+ if line == l1 and col <= c1 then return false end
+ if line == l2 and col > c2 then return false end
+ return true
+end
+
+local function is_in_any_selection(line, col)
+ for idx, l1, c1, l2, c2 in doc():get_selections(true, false) do
+ if is_in_selection(line, col, l1, c1, l2, c2) then return true end
+ end
+ return false
+end
+
local function select_next(all)
local il1, ic1 = doc():get_selection(true)
for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do
@@ -115,15 +149,27 @@ local function select_next(all)
repeat
l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l1 == il1 and c1 == ic1 then break end
- if l2 then doc():add_selection(l2, c2, l1, c1) end
+ if l2 and (all or not is_in_any_selection(l2, c2)) then
+ doc():add_selection(l2, c2, l1, c1)
+ if not all then
+ core.active_view:scroll_to_make_visible(l2, c2)
+ return
+ end
+ end
until not all or not l2
- break
+ if all then break end
end
end
command.add(has_unique_selection, {
- ["find-replace:select-next"] = function() select_next(false) end,
- ["find-replace:select-all"] = function() select_next(true) end
+ ["find-replace:select-next"] = function()
+ local l1, c1, l2, c2 = doc():get_selection(true)
+ local text = doc():get_text(l1, c1, l2, c2)
+ l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
+ if l2 then doc():set_selection(l2, c2, l1, c1) end
+ end,
+ ["find-replace:select-add-next"] = function() select_next(false) end,
+ ["find-replace:select-add-all"] = function() select_next(true) end
})
command.add("core.docview", {
@@ -135,7 +181,9 @@ command.add("core.docview", {
end,
["find-replace:replace"] = function()
- replace("Text", doc():get_text(doc():get_selection(true)), function(text, old, new)
+ local l1, c1, l2, c2 = doc():get_selection()
+ local selected_text = doc():get_text(l1, c1, l2, c2)
+ replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
if not find_regex then
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end
diff --git a/data/core/commandview.lua b/data/core/commandview.lua
index eb7febc7..b91f1394 100644
--- a/data/core/commandview.lua
+++ b/data/core/commandview.lua
@@ -34,6 +34,7 @@ function CommandView:new()
self.suggestion_idx = 1
self.suggestions = {}
self.suggestions_height = 0
+ self.show_suggestions = true
self.last_change_id = 0
self.gutter_width = 0
self.gutter_text_brightness = 0
@@ -45,6 +46,11 @@ function CommandView:new()
end
+function CommandView:set_hidden_suggestions()
+ self.show_suggestions = false
+end
+
+
function CommandView:get_name()
return View.get_name(self)
end
@@ -83,10 +89,29 @@ 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()
+ if self.show_suggestions then
+ 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()
+ else
+ local current_suggestion = #self.suggestions > 0 and self.suggestions[self.suggestion_idx].text
+ local text = self:get_text()
+ if text == current_suggestion then
+ local n = self.suggestion_idx + dir
+ if n == 0 and self.save_suggestion then
+ self:set_text(self.save_suggestion)
+ else
+ self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
+ self:complete()
+ end
+ else
+ self.save_suggestion = text
+ self:complete()
+ end
+ self.last_change_id = self.doc:get_change_id()
+ self.state.suggest(self:get_text())
+ end
end
@@ -134,6 +159,8 @@ function CommandView:exit(submitted, inexplicit)
self.doc:reset()
self.suggestions = {}
if not submitted then cancel(not inexplicit) end
+ self.show_suggestions = true
+ self.save_suggestion = nil
end
@@ -187,7 +214,7 @@ function CommandView:update()
-- update suggestions box height
local lh = self:get_suggestion_line_height()
- local dest = math.min(#self.suggestions, max_suggestions) * lh
+ local dest = self.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0
self:move_towards("suggestions_height", dest)
-- update suggestion cursor offset
@@ -256,7 +283,9 @@ end
function CommandView:draw()
CommandView.super.draw(self)
- core.root_view:defer_draw(draw_suggestions_box, self)
+ if self.show_suggestions then
+ core.root_view:defer_draw(draw_suggestions_box, self)
+ end
end
diff --git a/data/core/common.lua b/data/core/common.lua
index 3093a36d..1a1b22cd 100644
--- a/data/core/common.lua
+++ b/data/core/common.lua
@@ -41,6 +41,11 @@ function common.lerp(a, b, t)
end
+function common.distance(x1, y1, x2, y2)
+ return math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2))
+end
+
+
function common.color(str)
local r, g, b, a = str:match("#(%x%x)(%x%x)(%x%x)")
if r then
@@ -276,6 +281,7 @@ end
function common.normalize_path(filename)
+ if not filename then return end
if PATHSEP == '\\' then
filename = filename:gsub('[/\\]', '\\')
local drive, rem = filename:match('^([a-zA-Z])(:.*)')
@@ -290,7 +296,8 @@ function common.normalize_path(filename)
table.insert(accu, part)
end
end
- return table.concat(accu, PATHSEP)
+ local npath = table.concat(accu, PATHSEP)
+ return npath == "" and PATHSEP or npath
end
diff --git a/data/core/config.lua b/data/core/config.lua
index caecdfcd..7cf23925 100644
--- a/data/core/config.lua
+++ b/data/core/config.lua
@@ -5,6 +5,7 @@ config.fps = 60
config.max_log_items = 80
config.message_timeout = 5
config.mouse_wheel_scroll = 50 * SCALE
+config.scroll_past_end = true
config.file_size_limit = 10
config.ignore_files = "^%."
config.symbol_pattern = "[%a_][%w_]*"
@@ -23,6 +24,7 @@ config.max_project_files = 2000
config.transitions = true
config.animation_rate = 1.0
config.blink_period = 0.8
+config.disable_blink = false
config.draw_whitespace = false
config.borderless = false
config.tab_close_button = true
@@ -33,5 +35,6 @@ config.plugins = {}
config.plugins.trimwhitespace = false
config.plugins.lineguide = false
+config.plugins.drawwhitespace = false
return config
diff --git a/data/core/doc/highlighter.lua b/data/core/doc/highlighter.lua
index e7650d01..4cb703da 100644
--- a/data/core/doc/highlighter.lua
+++ b/data/core/doc/highlighter.lua
@@ -24,7 +24,7 @@ function Highlighter:new(doc)
for i = self.first_invalid_line, max do
local state = (i > 1) and self.lines[i - 1].state
local line = self.lines[i]
- if not (line and line.init_state == state) then
+ if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
self.lines[i] = self:tokenize_line(i, state)
end
end
@@ -44,12 +44,25 @@ function Highlighter:reset()
self.max_wanted_line = 0
end
-
function Highlighter:invalidate(idx)
self.first_invalid_line = math.min(self.first_invalid_line, idx)
self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines)
end
+function Highlighter:insert_notify(line, n)
+ self:invalidate(line)
+ for i = 1, n do
+ table.insert(self.lines, line, nil)
+ end
+end
+
+function Highlighter:remove_notify(line, n)
+ self:invalidate(line)
+ for i = 1, n do
+ table.remove(self.lines, line)
+ end
+end
+
function Highlighter:tokenize_line(idx, state)
local res = {}
diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua
index aff31e94..920e2669 100644
--- a/data/core/doc/init.lua
+++ b/data/core/doc/init.lua
@@ -102,7 +102,11 @@ end
function Doc:is_dirty()
- return self.clean_change_id ~= self:get_change_id() or self.new_file
+ if self.new_file then
+ return #self.lines > 1 or #self.lines[1] > 1
+ else
+ return self.clean_change_id ~= self:get_change_id()
+ end
end
@@ -348,7 +352,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
push_undo(undo_stack, time, "remove", line, col, line2, col2)
-- update highlighter and assure selection is in bounds
- self.highlighter:invalidate(line)
+ self.highlighter:insert_notify(line, #lines - 1)
self:sanitize_selection()
end
@@ -375,7 +379,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
end
-- update highlighter and assure selection is in bounds
- self.highlighter:invalidate(line1)
+ self.highlighter:remove_notify(line1, line2 - line1)
self:sanitize_selection()
end
diff --git a/data/core/docview.lua b/data/core/docview.lua
index 161eac47..6621ef72 100644
--- a/data/core/docview.lua
+++ b/data/core/docview.lua
@@ -98,6 +98,9 @@ end
function DocView:get_scrollable_size()
+ if not config.scroll_past_end then
+ return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2
+ end
return self:get_line_height() * (#self.doc.lines - 1) + self.size.y
end
@@ -150,14 +153,14 @@ function DocView:get_col_x_offset(line, col)
local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do
if column == col then
- return xoffset / font:subpixel_scale()
+ return xoffset
end
- xoffset = xoffset + font:get_width_subpixel(char)
+ xoffset = xoffset + font:get_width(char)
column = column + #char
end
end
- return xoffset / default_font:subpixel_scale()
+ return xoffset
end
@@ -166,14 +169,12 @@ function DocView:get_x_offset_col(line, x)
local xoffset, last_i, i = 0, 1, 1
local default_font = self:get_font()
- local subpixel_scale = default_font:subpixel_scale()
- local x_subpixel = subpixel_scale * x + subpixel_scale / 2
for _, type, text in self.doc.highlighter:each_token(line) do
local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do
- local w = font:get_width_subpixel(char)
- if xoffset >= subpixel_scale * x then
- return (xoffset - x_subpixel > w / 2) and last_i or i
+ local w = font:get_width(char)
+ if xoffset >= x then
+ return (xoffset - x > w / 2) and last_i or i
end
xoffset = xoffset + w
last_i = i
@@ -339,16 +340,11 @@ end
function DocView:draw_line_text(idx, x, y)
local default_font = self:get_font()
- local subpixel_scale = default_font:subpixel_scale()
- local tx, ty = subpixel_scale * x, y + self:get_line_text_y_offset()
+ local tx, ty = x, y + self:get_line_text_y_offset()
for _, type, text in self.doc.highlighter:each_token(idx) do
local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font
- if config.draw_whitespace then
- tx = renderer.draw_text_subpixel(font, text, tx, ty, color, core.replacements, style.syntax.comment)
- else
- tx = renderer.draw_text_subpixel(font, text, tx, ty, color)
- end
+ tx = renderer.draw_text(font, text, tx, ty, color)
end
end
@@ -358,6 +354,18 @@ function DocView:draw_caret(x, y)
end
function DocView:draw_line_body(idx, x, y)
+ -- draw highlight if any selection ends on this line
+ local draw_highlight = false
+ for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
+ if line1 == idx then
+ draw_highlight = true
+ break
+ end
+ end
+ if draw_highlight and config.highlight_current_line and core.active_view == self then
+ self:draw_line_highlight(x + self.scroll.x, y)
+ end
+
-- draw selection if it overlaps this line
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if idx >= line1 and idx <= line2 then
@@ -372,15 +380,6 @@ function DocView:draw_line_body(idx, x, y)
end
end
end
- local draw_highlight = nil
- for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
- -- draw line highlight if caret is on this line
- if draw_highlight ~= false and config.highlight_current_line
- and line1 == idx and core.active_view == self then
- draw_highlight = (line1 == line2 and col1 == col2)
- end
- end
- if draw_highlight then self:draw_line_highlight(x + self.scroll.x, y) end
-- draw line's text
self:draw_line_text(idx, x, y)
@@ -408,10 +407,12 @@ function DocView:draw_overlay()
local T = config.blink_period
for _, line, col in self.doc:get_selections() do
if line >= minline and line <= maxline
- and (core.blink_timer - core.blink_start) % T < T / 2
and system.window_has_focus() then
- local x, y = self:get_line_screen_position(line)
- self:draw_caret(x + self:get_col_x_offset(line, col), y)
+ if config.disable_blink
+ or (core.blink_timer - core.blink_start) % T < T / 2 then
+ local x, y = self:get_line_screen_position(line)
+ self:draw_caret(x + self:get_col_x_offset(line, col), y)
+ end
end
end
end
diff --git a/data/core/init.lua b/data/core/init.lua
index 6b770fd6..13d0907b 100644
--- a/data/core/init.lua
+++ b/data/core/init.lua
@@ -17,10 +17,7 @@ local core = {}
local function load_session()
local ok, t = pcall(dofile, USERDIR .. "/session.lua")
- if ok and t then
- return t.recents, t.window, t.window_mode
- end
- return {}
+ return ok and t or {}
end
@@ -30,6 +27,8 @@ local function save_session()
fp:write("return {recents=", common.serialize(core.recent_projects),
", window=", common.serialize(table.pack(system.get_window_size())),
", window_mode=", common.serialize(system.get_window_mode()),
+ ", previous_find=", common.serialize(core.previous_find),
+ ", previous_replace=", common.serialize(core.previous_replace),
"}\n")
fp:close()
end
@@ -80,6 +79,9 @@ function core.open_folder_project(dir_path_abs)
if core.set_project_dir(dir_path_abs, core.on_quit_project) then
core.root_view:close_all_docviews()
update_recents_project("add", dir_path_abs)
+ if not core.load_project_module() then
+ command.perform("core:open-log")
+ end
core.on_enter_project(dir_path_abs)
end
end
@@ -320,8 +322,8 @@ local style = require "core.style"
------------------------------- Fonts ----------------------------------------
-- customize fonts:
--- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE)
--- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
+-- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * SCALE)
+-- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE)
--
-- font names used by lite:
-- style.font : user interface
@@ -394,15 +396,6 @@ function core.remove_project_directory(path)
return false
end
-
-local function whitespace_replacements()
- local r = renderer.replacements.new()
- r:add(" ", "·")
- r:add("\t", "»")
- return r
-end
-
-
local function reload_on_user_module_save()
-- auto-realod style when user's module is saved by overriding Doc:Save()
local doc_save = Doc.save
@@ -435,13 +428,15 @@ function core.init()
end
do
- local recent_projects, window_position, window_mode = load_session()
- if window_mode == "normal" then
- system.set_window_size(table.unpack(window_position))
- elseif window_mode == "maximized" then
+ local session = load_session()
+ if session.window_mode == "normal" then
+ system.set_window_size(table.unpack(session.window))
+ elseif session.window_mode == "maximized" then
system.set_window_mode("maximized")
end
- core.recent_projects = recent_projects or {}
+ core.recent_projects = session.recents or {}
+ core.previous_find = session.previous_find or {}
+ core.previous_replace = session.previous_replace or {}
end
local project_dir = core.recent_projects[1] or "."
@@ -461,7 +456,10 @@ function core.init()
project_dir = arg_filename
project_dir_explicit = true
else
- delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
+ -- on macOS we can get an argument like "-psn_0_52353" that we just ignore.
+ if not ARGS[i]:match("^-psn") then
+ delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
+ end
end
end
@@ -495,7 +493,6 @@ function core.init()
core.visited_files = {}
core.restart_request = false
core.quit_request = false
- core.replacements = whitespace_replacements()
core.root_view = RootView()
core.command_view = CommandView()
@@ -678,16 +675,18 @@ function core.load_plugins()
userdir = {dir = USERDIR, plugins = {}},
datadir = {dir = DATADIR, plugins = {}},
}
- local files = {}
+ local files, ordered = {}, {}
for _, root_dir in ipairs {DATADIR, USERDIR} do
local plugin_dir = root_dir .. "/plugins"
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
+ if not files[filename] then table.insert(ordered, filename) end
files[filename] = plugin_dir -- user plugins will always replace system plugins
end
end
+ table.sort(ordered)
- for filename, plugin_dir in pairs(files) do
- local basename = filename:match("(.-)%.lua$") or filename
+ for _, filename in ipairs(ordered) do
+ local plugin_dir, basename = files[filename], filename:match("(.-)%.lua$") or filename
local is_lua_file, version_match = check_plugin_version(plugin_dir .. '/' .. filename)
if is_lua_file then
if not version_match then
diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua
index 7f54ddbb..53a20468 100644
--- a/data/core/keymap-macos.lua
+++ b/data/core/keymap-macos.lua
@@ -66,9 +66,10 @@ local function keymap_macos(keymap)
["cmd+shift+return"] = "doc:newline-above",
["cmd+j"] = "doc:join-lines",
["cmd+a"] = "doc:select-all",
- ["cmd+d"] = { "find-replace:select-next", "doc:select-word" },
+ ["cmd+d"] = { "find-replace:select-add-next", "doc:select-word" },
+ ["cmd+f3"] = "find-replace:select-next",
["cmd+l"] = "doc:select-lines",
- ["cmd+shift+l"] = { "find-replace:select-all", "doc:select-word" },
+ ["cmd+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["cmd+/"] = "doc:toggle-line-comments",
["option+up"] = "doc:move-lines-up",
["option+down"] = "doc:move-lines-down",
diff --git a/data/core/keymap.lua b/data/core/keymap.lua
index 8fd2b05b..308499c7 100644
--- a/data/core/keymap.lua
+++ b/data/core/keymap.lua
@@ -5,9 +5,10 @@ keymap.modkeys = {}
keymap.map = {}
keymap.reverse_map = {}
+local macos = PLATFORM == "Mac OS X"
-- Thanks to mathewmariani, taken from his lite-macos github repository.
-local modkeys_os = require("core.modkeys-" .. (MACOS and "macos" or "generic"))
+local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys
@@ -37,7 +38,7 @@ end
function keymap.add(map, overwrite)
for stroke, commands in pairs(map) do
- if MACOS then
+ if macos then
stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd")
end
if type(commands) == "string" then
@@ -119,7 +120,7 @@ function keymap.on_key_released(k)
end
-if MACOS then
+if macos then
local keymap_macos = require("core.keymap-macos")
keymap_macos(keymap)
return keymap
@@ -193,9 +194,10 @@ keymap.add_direct {
["ctrl+shift+return"] = "doc:newline-above",
["ctrl+j"] = "doc:join-lines",
["ctrl+a"] = "doc:select-all",
- ["ctrl+d"] = { "find-replace:select-next", "doc:select-word" },
+ ["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" },
+ ["ctrl+f3"] = "find-replace:select-next",
["ctrl+l"] = "doc:select-lines",
- ["ctrl+shift+l"] = { "find-replace:select-all", "doc:select-word" },
+ ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["ctrl+/"] = "doc:toggle-line-comments",
["ctrl+up"] = "doc:move-lines-up",
["ctrl+down"] = "doc:move-lines-down",
diff --git a/data/core/regex.lua b/data/core/regex.lua
index 19306e04..69203cbd 100644
--- a/data/core/regex.lua
+++ b/data/core/regex.lua
@@ -23,7 +23,7 @@ end
-- Moves to the end of the identified character.
local function end_character(str, index)
local byte = string.byte(str, index + 1)
- while byte >= 128 and byte < 192 do
+ while byte and byte >= 128 and byte < 192 do
index = index + 1
byte = string.byte(str, index + 1)
end
diff --git a/data/core/rootview.lua b/data/core/rootview.lua
index 9d017268..0d219474 100644
--- a/data/core/rootview.lua
+++ b/data/core/rootview.lua
@@ -142,7 +142,14 @@ function Node:remove_view(root, view)
local parent = self:get_parent_node(root)
local is_a = (parent.a == self)
local other = parent[is_a and "b" or "a"]
- if other:get_locked_size() then
+ local locked_size_x, locked_size_y = other:get_locked_size()
+ local locked_size
+ if parent.type == "hsplit" then
+ locked_size = locked_size_x
+ else
+ locked_size = locked_size_y
+ end
+ if self.is_primary_node or locked_size then
self.views = {}
self:add_view(EmptyView())
else
@@ -273,7 +280,10 @@ end
function Node:should_show_tabs()
if self.locked then return false end
- if #self.views > 1 then return true
+ local dn = core.root_view.dragged_node
+ if #self.views > 1
+ or (dn and dn.dragging) then -- show tabs while dragging
+ return true
elseif config.always_show_tabs then
return not self.views[1]:is(EmptyView)
end
@@ -506,6 +516,53 @@ function Node:update()
end
end
+function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
+ local ds = style.divider_size
+ local dots_width = style.font:get_width("…")
+ local color = style.dim
+ local padding_y = style.padding.y
+ renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
+ if standalone then
+ renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
+ end
+ if is_active then
+ color = style.text
+ renderer.draw_rect(x, y, w, h, style.background)
+ renderer.draw_rect(x + w, y, ds, h, style.divider)
+ renderer.draw_rect(x - ds, y, ds, h, style.divider)
+ end
+ local cx, cw, cspace = close_button_location(x, w)
+ local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
+ if show_close_button then
+ local close_style = is_close_hovered and style.text or style.dim
+ common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
+ end
+ if is_hovered then
+ color = style.text
+ end
+ local padx = style.padding.x
+ -- Normally we should substract "cspace" from text_avail_width and from the
+ -- clipping width. It is the padding space we give to the left and right of the
+ -- close button. However, since we are using dots to terminate filenames, we
+ -- choose to ignore "cspace" accepting that the text can possibly "touch" the
+ -- close button.
+ local text_avail_width = cx - x - padx
+ core.push_clip_rect(x, y, cx - x, h)
+ x, w = x + padx, w - padx * 2
+ local align = "center"
+ if style.font:get_width(text) > text_avail_width then
+ align = "left"
+ for i = 1, #text do
+ local reduced_text = text:sub(1, #text - i)
+ if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
+ text = reduced_text .. "…"
+ break
+ end
+ end
+ end
+ common.draw_text(style.font, color, text, align, x, y, w, h)
+ core.pop_clip_rect()
+end
function Node:draw_tabs()
local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
@@ -530,47 +587,9 @@ function Node:draw_tabs()
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
local view = self.views[i]
local x, y, w, h = self:get_tab_rect(i)
- local text = view:get_name()
- local color = style.dim
- local padding_y = style.padding.y
- renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
- if view == self.active_view then
- color = style.text
- renderer.draw_rect(x, y, w, h, style.background)
- renderer.draw_rect(x + w, y, ds, h, style.divider)
- renderer.draw_rect(x - ds, y, ds, h, style.divider)
- end
- local cx, cw, cspace = close_button_location(x, w)
- local show_close_button = ((view == self.active_view or i == self.hovered_tab) and config.tab_close_button)
- if show_close_button then
- local close_style = self.hovered_close == i and style.text or style.dim
- common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
- end
- if i == self.hovered_tab then
- color = style.text
- end
- local padx = style.padding.x
- -- Normally we should substract "cspace" from text_avail_width and from the
- -- clipping width. It is the padding space we give to the left and right of the
- -- close button. However, since we are using dots to terminate filenames, we
- -- choose to ignore "cspace" accepting that the text can possibly "touch" the
- -- close button.
- local text_avail_width = cx - x - padx
- core.push_clip_rect(x, y, cx - x, h)
- x, w = x + padx, w - padx * 2
- local align = "center"
- if style.font:get_width(text) > text_avail_width then
- align = "left"
- for i = 1, #text do
- local reduced_text = text:sub(1, #text - i)
- if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
- text = reduced_text .. "…"
- break
- end
- end
- end
- common.draw_text(style.font, color, text, align, x, y, w, h)
- core.pop_clip_rect()
+ self:draw_tab(view:get_name(), view == self.active_view,
+ i == self.hovered_tab, i == self.hovered_close,
+ x, y, w, h)
end
core.pop_clip_rect()
@@ -696,6 +715,62 @@ function Node:resize(axis, value)
end
+function Node:get_split_type(mouse_x, mouse_y)
+ local x, y = self.position.x, self.position.y
+ local w, h = self.size.x, self.size.y
+ local _, _, _, tab_h = self:get_scroll_button_rect(1)
+ y = y + tab_h
+ h = h - tab_h
+
+ local local_mouse_x = mouse_x - x
+ local local_mouse_y = mouse_y - y
+
+ if local_mouse_y < 0 then
+ return "tab"
+ else
+ local left_pct = local_mouse_x * 100 / w
+ local top_pct = local_mouse_y * 100 / h
+ if left_pct <= 30 then
+ return "left"
+ elseif left_pct >= 70 then
+ return "right"
+ elseif top_pct <= 30 then
+ return "up"
+ elseif top_pct >= 70 then
+ return "down"
+ end
+ return "middle"
+ end
+end
+
+
+function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
+ local tab_index = self:get_tab_overlapping_point(x, y)
+ if not tab_index then
+ local first_tab_x = self:get_tab_rect(1)
+ if x < first_tab_x then
+ -- mouse before first visible tab
+ tab_index = self.tab_offset or 1
+ else
+ -- mouse after last visible tab
+ tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
+ end
+ end
+ local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
+ if x > tab_x + tab_w / 2 and tab_index <= #self.views then
+ -- use next tab
+ tab_x = tab_x + tab_w
+ tab_index = tab_index + 1
+ end
+ if self == dragged_node and dragged_index and tab_index > dragged_index then
+ -- the tab we are moving is counted in tab_index
+ tab_index = tab_index - 1
+ tab_x = tab_x - tab_w
+ end
+ return tab_index, tab_x, tab_y, tab_w, tab_h
+end
+
+
local RootView = View:extend()
function RootView:new()
@@ -703,6 +778,14 @@ function RootView:new()
self.root_node = Node()
self.deferred_draws = {}
self.mouse = { x = 0, y = 0 }
+ self.drag_overlay = { x = 0, y = 0, w = 0, h = 0, visible = false, opacity = 0,
+ base_color = style.drag_overlay,
+ color = { table.unpack(style.drag_overlay) } }
+ self.drag_overlay.to = { x = 0, y = 0, w = 0, h = 0 }
+ self.drag_overlay_tab = { x = 0, y = 0, w = 0, h = 0, visible = false, opacity = 0,
+ base_color = style.drag_overlay_tab,
+ color = { table.unpack(style.drag_overlay_tab) } }
+ self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
end
@@ -786,10 +869,12 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
if button == "middle" or node.hovered_close == idx then
node:close_view(self.root_node, node.views[idx])
else
- self.dragged_node = { node, idx }
+ if button == "left" then
+ self.dragged_node = { node = node, idx = idx, dragging = false, drag_start_x = x, drag_start_y = y}
+ end
node:set_active_view(node.views[idx])
end
- else
+ elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
core.set_active_view(node.active_view)
if not self.on_view_mouse_pressed(button, x, y, clicks) then
node.active_view:on_mouse_pressed(button, x, y, clicks)
@@ -798,14 +883,73 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
end
-function RootView:on_mouse_released(...)
+function RootView:get_overlay_base_color(overlay)
+ if overlay == self.drag_overlay then
+ return style.drag_overlay
+ else
+ return style.drag_overlay_tab
+ end
+end
+
+
+function RootView:set_show_overlay(overlay, status)
+ overlay.visible = status
+ if status then -- reset colors
+ -- reload base_color
+ overlay.base_color = self:get_overlay_base_color(overlay)
+ overlay.color[1] = overlay.base_color[1]
+ overlay.color[2] = overlay.base_color[2]
+ overlay.color[3] = overlay.base_color[3]
+ overlay.color[4] = overlay.base_color[4]
+ overlay.opacity = 0
+ end
+end
+
+
+function RootView:on_mouse_released(button, x, y, ...)
if self.dragged_divider then
self.dragged_divider = nil
end
if self.dragged_node then
- self.dragged_node = nil
+ if button == "left" then
+ if self.dragged_node.dragging then
+ local node = self.root_node:get_child_overlapping_point(self.mouse.x, self.mouse.y)
+ local dragged_node = self.dragged_node.node
+
+ if node and not node.locked
+ -- don't do anything if dragging onto own node, with only one view
+ and (node ~= dragged_node or #node.views > 1) then
+ local split_type = node:get_split_type(self.mouse.x, self.mouse.y)
+ local view = dragged_node.views[self.dragged_node.idx]
+
+ if split_type ~= "middle" and split_type ~= "tab" then -- needs splitting
+ local new_node = node:split(split_type)
+ self.root_node:get_node_for_view(view):remove_view(self.root_node, view)
+ new_node:add_view(view)
+ elseif split_type == "middle" and node ~= dragged_node then -- move to other node
+ dragged_node:remove_view(self.root_node, view)
+ node:add_view(view)
+ self.root_node:get_node_for_view(view):set_active_view(view)
+ elseif split_type == "tab" then -- move besides other tabs
+ local tab_index = node:get_drag_overlay_tab_position(self.mouse.x, self.mouse.y, dragged_node, self.dragged_node.idx)
+ dragged_node:remove_view(self.root_node, view)
+ node:add_view(view, tab_index)
+ self.root_node:get_node_for_view(view):set_active_view(view)
+ end
+ self.root_node:update_layout()
+ core.redraw = true
+ end
+ end
+ self:set_show_overlay(self.drag_overlay, false)
+ self:set_show_overlay(self.drag_overlay_tab, false)
+ if self.dragged_node and self.dragged_node.dragging then
+ core.request_cursor("arrow")
+ end
+ self.dragged_node = nil
+ end
+ else -- avoid sending on_mouse_released events when dragging tabs
+ self.root_node:on_mouse_released(button, x, y, ...)
end
- self.root_node:on_mouse_released(...)
end
@@ -841,6 +985,19 @@ function RootView:on_mouse_moved(x, y, dx, dy)
end
self.mouse.x, self.mouse.y = x, y
+
+ local dn = self.dragged_node
+ if dn and not dn.dragging then
+ -- start dragging only after enough movement
+ dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
+ if dn.dragging then
+ core.request_cursor("hand")
+ end
+ end
+
+ -- avoid sending on_mouse_moved events when dragging tabs
+ if dn then return end
+
self.root_node:on_mouse_moved(x, y, dx, dy)
local node = self.root_node:get_child_overlapping_point(x, y)
@@ -855,24 +1012,6 @@ function RootView:on_mouse_moved(x, y, dx, dy)
elseif node then
core.request_cursor(node.active_view.cursor)
end
- if node and self.dragged_node and (self.dragged_node[1] ~= node or (tab_index and self.dragged_node[2] ~= tab_index))
- and node.type == "leaf" and #node.views > 0 and node.views[1]:is(DocView) then
- local tab = self.dragged_node[1].views[self.dragged_node[2]]
- if self.dragged_node[1] ~= node then
- for i, v in ipairs(node.views) do if v.doc == tab.doc then tab = nil break end end
- if tab then
- self.dragged_node[1]:remove_view(self.root_node, tab)
- node:add_view(tab, tab_index)
- self.root_node:update_layout()
- self.dragged_node = { node, tab_index or #node.views }
- core.redraw = true
- end
- else
- table.remove(self.dragged_node[1].views, self.dragged_node[2])
- table.insert(node.views, tab_index, tab)
- self.dragged_node = { node, tab_index }
- end
- end
end
@@ -893,10 +1032,110 @@ function RootView:on_focus_lost(...)
core.redraw = true
end
+
+function RootView:interpolate_drag_overlay(overlay)
+ self:move_towards(overlay, "x", overlay.to.x)
+ self:move_towards(overlay, "y", overlay.to.y)
+ self:move_towards(overlay, "w", overlay.to.w)
+ self:move_towards(overlay, "h", overlay.to.h)
+
+ self:move_towards(overlay, "opacity", overlay.visible and 100 or 0)
+ overlay.color[4] = overlay.base_color[4] * overlay.opacity / 100
+end
+
+
function RootView:update()
copy_position_and_size(self.root_node, self)
self.root_node:update()
self.root_node:update_layout()
+
+ self:update_drag_overlay()
+ self:interpolate_drag_overlay(self.drag_overlay)
+ self:interpolate_drag_overlay(self.drag_overlay_tab)
+end
+
+
+function RootView:set_drag_overlay(overlay, x, y, w, h, immediate)
+ overlay.to.x = x
+ overlay.to.y = y
+ overlay.to.w = w
+ overlay.to.h = h
+ if immediate then
+ overlay.x = x
+ overlay.y = y
+ overlay.w = w
+ overlay.h = h
+ end
+ if not overlay.visible then
+ self:set_show_overlay(overlay, true)
+ end
+end
+
+
+local function get_split_sizes(split_type, x, y, w, h)
+ if split_type == "left" then
+ w = w * .5
+ elseif split_type == "right" then
+ x = x + w * .5
+ w = w * .5
+ elseif split_type == "up" then
+ h = h * .5
+ elseif split_type == "down" then
+ y = y + h * .5
+ h = h * .5
+ end
+ return x, y, w, h
+end
+
+
+function RootView:update_drag_overlay()
+ if not (self.dragged_node and self.dragged_node.dragging) then return end
+ local over = self.root_node:get_child_overlapping_point(self.mouse.x, self.mouse.y)
+ if over and not over.locked then
+ local _, _, _, tab_h = over:get_scroll_button_rect(1)
+ local x, y = over.position.x, over.position.y
+ local w, h = over.size.x, over.size.y
+ local split_type = over:get_split_type(self.mouse.x, self.mouse.y)
+
+ if split_type == "tab" and (over ~= self.dragged_node.node or #over.views > 1) then
+ local tab_index, tab_x, tab_y, tab_w, tab_h = over:get_drag_overlay_tab_position(self.mouse.x, self.mouse.y)
+ self:set_drag_overlay(self.drag_overlay_tab,
+ tab_x + (tab_index and 0 or tab_w), tab_y,
+ style.caret_width, tab_h,
+ -- avoid showing tab overlay moving between nodes
+ over ~= self.drag_overlay_tab.last_over)
+ self:set_show_overlay(self.drag_overlay, false)
+ self.drag_overlay_tab.last_over = over
+ else
+ if (over ~= self.dragged_node.node or #over.views > 1) then
+ y = y + tab_h
+ h = h - tab_h
+ x, y, w, h = get_split_sizes(split_type, x, y, w, h)
+ end
+ self:set_drag_overlay(self.drag_overlay, x, y, w, h)
+ self:set_show_overlay(self.drag_overlay_tab, false)
+ end
+ else
+ self:set_show_overlay(self.drag_overlay, false)
+ self:set_show_overlay(self.drag_overlay_tab, false)
+ end
+end
+
+
+function RootView:draw_grabbed_tab()
+ local dn = self.dragged_node
+ local _,_, w, h = dn.node:get_tab_rect(dn.idx)
+ local x = self.mouse.x - w / 2
+ local y = self.mouse.y - h / 2
+ local text = dn.node.views[dn.idx] and dn.node.views[dn.idx]:get_name() or ""
+ self.root_node:draw_tab(text, true, true, false, x, y, w, h, true)
+end
+
+
+function RootView:draw_drag_overlay(ov)
+ if ov.opacity > 0 then
+ renderer.draw_rect(ov.x, ov.y, ov.w, ov.h, ov.color)
+ end
end
@@ -906,6 +1145,12 @@ function RootView:draw()
local t = table.remove(self.deferred_draws)
t.fn(table.unpack(t))
end
+
+ self:draw_drag_overlay(self.drag_overlay)
+ self:draw_drag_overlay(self.drag_overlay_tab)
+ if self.dragged_node and self.dragged_node.dragging then
+ self:draw_grabbed_tab()
+ end
if core.cursor_change_req then
system.set_cursor(core.cursor_change_req)
core.cursor_change_req = nil
diff --git a/data/core/start.lua b/data/core/start.lua
index 71050057..330c42f5 100644
--- a/data/core/start.lua
+++ b/data/core/start.lua
@@ -19,3 +19,12 @@ package.path = DATADIR .. '/?.lua;' .. package.path
package.path = DATADIR .. '/?/init.lua;' .. package.path
package.path = USERDIR .. '/?.lua;' .. package.path
package.path = USERDIR .. '/?/init.lua;' .. package.path
+
+local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
+package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix
+package.native_plugins = {}
+package.searchers = { package.searchers[1], package.searchers[2], function(modname)
+ local path = package.searchpath(modname, package.cpath)
+ if not path then return nil end
+ return system.load_native_plugin, path
+end }
diff --git a/data/core/statusview.lua b/data/core/statusview.lua
index 58421c31..3342bdb9 100644
--- a/data/core/statusview.lua
+++ b/data/core/statusview.lua
@@ -6,6 +6,7 @@ local style = require "core.style"
local DocView = require "core.docview"
local LogView = require "core.logview"
local View = require "core.view"
+local Object = require "core.object"
local StatusView = View:extend()
@@ -70,7 +71,7 @@ local function draw_items(self, items, x, y, draw_fn)
local color = style.text
for _, item in ipairs(items) do
- if type(item) == "userdata" then
+ if Object.is(item, renderer.font) then
font = item
elseif type(item) == "table" then
color = item
diff --git a/data/core/style.lua b/data/core/style.lua
index 7cf16eb1..3b0d9e35 100644
--- a/data/core/style.lua
+++ b/data/core/style.lua
@@ -21,11 +21,11 @@ style.tab_width = common.round(170 * SCALE)
--
-- On High DPI monitor or non RGB monitor you may consider using antialiasing grayscale instead.
-- The antialiasing grayscale with full hinting is interesting for crisp font rendering.
-style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE)
-style.big_font = style.font:copy(40 * SCALE)
-style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 14 * SCALE, {antialiasing="grayscale", hinting="full"})
-style.icon_big_font = style.icon_font:copy(20 * SCALE)
-style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
+style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 15 * SCALE)
+style.big_font = style.font:copy(46 * SCALE)
+style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE, {antialiasing="grayscale", hinting="full"})
+style.icon_big_font = style.icon_font:copy(23 * SCALE)
+style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 15 * SCALE)
style.background = { common.color "#2e2e32" }
style.background2 = { common.color "#252529" }
@@ -44,6 +44,8 @@ style.scrollbar2 = { common.color "#4b4b52" }
style.nagbar = { common.color "#FF0000" }
style.nagbar_text = { common.color "#FFFFFF" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" }
+style.drag_overlay = { common.color "rgba(255,255,255,0.1)" }
+style.drag_overlay_tab = { common.color "#93DDFA" }
style.syntax = {}
style.syntax["normal"] = { common.color "#e1e1e6" }
diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua
index 484199a9..fde9487e 100644
--- a/data/plugins/autocomplete.lua
+++ b/data/plugins/autocomplete.lua
@@ -10,7 +10,7 @@ local RootView = require "core.rootview"
local DocView = require "core.docview"
local Doc = require "core.doc"
-config.plugins.autocomplete = {
+config.plugins.autocomplete = {
-- Amount of characters that need to be written for autocomplete
min_len = 3,
-- The max amount of visible items
@@ -502,6 +502,11 @@ command.add(predicate, {
suggestions_idx = math.min(suggestions_idx + 1, #suggestions)
end,
+ ["autocomplete:cycle"] = function()
+ local newidx = suggestions_idx + 1
+ suggestions_idx = newidx > #suggestions and 1 or newidx
+ end,
+
["autocomplete:cancel"] = function()
reset_suggestions()
end,
diff --git a/data/plugins/drawwhitespace.lua b/data/plugins/drawwhitespace.lua
new file mode 100644
index 00000000..da9d1b12
--- /dev/null
+++ b/data/plugins/drawwhitespace.lua
@@ -0,0 +1,32 @@
+-- mod-version:2 -- lite-xl 2.0
+
+local style = require "core.style"
+local DocView = require "core.docview"
+local common = require "core.common"
+
+local draw_line_text = DocView.draw_line_text
+
+function DocView:draw_line_text(idx, x, y)
+ local font = (self:get_font() or style.syntax_fonts["comment"])
+ local color = style.syntax.comment
+ local ty, tx = y + self:get_line_text_y_offset()
+ local text, offset, s, e = self.doc.lines[idx], 1
+ while true do
+ s, e = text:find(" +", offset)
+ if not s then break end
+ tx = self:get_col_x_offset(idx, s) + x
+ renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color)
+ offset = e + 1
+ end
+ offset = 1
+ while true do
+ s, e = text:find("\t", offset)
+ if not s then break end
+ tx = self:get_col_x_offset(idx, s) + x
+ renderer.draw_text(font, "»", tx, ty, color)
+ offset = e + 1
+ end
+ draw_line_text(self, idx, x, y)
+end
+
+
diff --git a/data/plugins/language_md.lua b/data/plugins/language_md.lua
index 6e6e4255..3c1c329a 100644
--- a/data/plugins/language_md.lua
+++ b/data/plugins/language_md.lua
@@ -1,22 +1,41 @@
-- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax"
+
+
syntax.add {
files = { "%.md$", "%.markdown$" },
patterns = {
- { pattern = "\\.", type = "normal" },
- { pattern = { "<!%-%-", "%-%->" }, type = "comment" },
- { pattern = { "```", "```" }, type = "string" },
- { pattern = { "``", "``", "\\" }, type = "string" },
- { pattern = { "`", "`", "\\" }, type = "string" },
- { pattern = { "~~", "~~", "\\" }, type = "keyword2" },
- { pattern = "%-%-%-+", type = "comment" },
- { pattern = "%*%s+", type = "operator" },
- { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
- { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
- { pattern = "#.-\n", type = "keyword" },
- { pattern = "!?%[.-%]%(.-%)", type = "function" },
- { pattern = "https?://%S+", type = "function" },
+ { pattern = "\\.", type = "normal" },
+ { pattern = { "<!%-%-", "%-%->" }, type = "comment" },
+ { pattern = { "```c", "```" }, type = "string", syntax = ".c" },
+ { pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" },
+ { pattern = { "```python", "```" }, type = "string", syntax = ".py" },
+ { pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" },
+ { pattern = { "```perl", "```" }, type = "string", syntax = ".pl" },
+ { pattern = { "```php", "```" }, type = "string", syntax = ".php" },
+ { pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },
+ { pattern = { "```html", "```" }, type = "string", syntax = ".html" },
+ { pattern = { "```xml", "```" }, type = "string", syntax = ".xml" },
+ { pattern = { "```css", "```" }, type = "string", syntax = ".css" },
+ { pattern = { "```lua", "```" }, type = "string", syntax = ".lua" },
+ { pattern = { "```bash", "```" }, type = "string", syntax = ".sh" },
+ { pattern = { "```java", "```" }, type = "string", syntax = ".java" },
+ { pattern = { "```c#", "```" }, type = "string", syntax = ".cs" },
+ { pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" },
+ { pattern = { "```d", "```" }, type = "string", syntax = ".d" },
+ { pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" },
+ { pattern = { "```", "```" }, type = "string" },
+ { pattern = { "``", "``", "\\" }, type = "string" },
+ { pattern = { "`", "`", "\\" }, type = "string" },
+ { pattern = { "~~", "~~", "\\" }, type = "keyword2" },
+ { pattern = "%-%-%-+", type = "comment" },
+ { pattern = "%*%s+", type = "operator" },
+ { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
+ { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
+ { pattern = "#.-\n", type = "keyword" },
+ { pattern = "!?%[.-%]%(.-%)", type = "function" },
+ { pattern = "https?://%S+", type = "function" },
},
symbols = { },
}
diff --git a/data/plugins/language_python.lua b/data/plugins/language_python.lua
index e19caa63..252a0d14 100644
--- a/data/plugins/language_python.lua
+++ b/data/plugins/language_python.lua
@@ -2,7 +2,7 @@
local syntax = require "core.syntax"
syntax.add {
- files = { "%.py$", "%.pyw$" },
+ files = { "%.py$", "%.pyw$", "%.rpy$" },
headers = "^#!.*[ /]python",
comment = "#",
patterns = {
diff --git a/data/plugins/lineguide.lua b/data/plugins/lineguide.lua
index 61debbff..5a17c8ca 100644
--- a/data/plugins/lineguide.lua
+++ b/data/plugins/lineguide.lua
@@ -7,8 +7,7 @@ local draw_overlay = DocView.draw_overlay
function DocView:draw_overlay(...)
local ns = ("n"):rep(config.line_limit)
- local ss = self:get_font():subpixel_scale()
- local offset = self:get_font():get_width_subpixel(ns) / ss
+ local offset = self:get_font():get_width(ns)
local x = self:get_line_screen_position(1) + offset
local y = self.position.y
local w = math.ceil(SCALE * 1)
diff --git a/data/plugins/scale.lua b/data/plugins/scale.lua
index 8d16304b..b8384609 100644
--- a/data/plugins/scale.lua
+++ b/data/plugins/scale.lua
@@ -13,9 +13,6 @@ config.plugins.scale = {
use_mousewheel = true
}
-local MINIMUM_SCALE = 0.25;
-
-local scale_level = 0
local scale_steps = 0.05
local current_scale = SCALE
@@ -36,9 +33,6 @@ local function set_scale(scale)
local s = scale / current_scale
current_scale = scale
- -- we set scale_level in case this was called by user
- scale_level = (scale - default_scale) / scale_steps
-
if config.plugins.scale.mode == "ui" then
SCALE = scale
@@ -50,10 +44,14 @@ local function set_scale(scale)
style.tab_width = style.tab_width * s
for _, name in ipairs {"font", "big_font", "icon_font", "icon_big_font", "code_font"} do
- renderer.font.set_size(style[name], s * style[name]:get_size())
+ style[name] = renderer.font.copy(style[name], s * style[name]:get_size())
end
else
- renderer.font.set_size(style.code_font, s * style.code_font:get_size())
+ style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size())
+ end
+
+ for _, font in pairs(style.syntax_fonts) do
+ renderer.font.set_size(font, s * font:get_size())
end
-- restore scroll positions
@@ -81,18 +79,15 @@ function RootView:on_mouse_wheel(d, ...)
end
local function res_scale()
- scale_level = 0
set_scale(default_scale)
end
local function inc_scale()
- scale_level = scale_level + 1
- set_scale(default_scale + scale_level * scale_steps)
+ set_scale(current_scale + scale_steps)
end
-local function dec_scale()
- scale_level = scale_level - 1
- set_scale(math.max(default_scale + scale_level * scale_steps), MINIMUM_SCALE)
+local function dec_scale()
+ set_scale(current_scale - scale_steps)
end
diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua
index 84d5dd28..fa3ab53a 100644
--- a/data/plugins/treeview.lua
+++ b/data/plugins/treeview.lua
@@ -243,7 +243,7 @@ function TreeView:on_mouse_pressed(button, x, y, clicks)
end
else
core.try(function()
- local doc_filename = common.relative_path(core.project_dir, hovered_item.abs_filename)
+ local doc_filename = core.normalize_to_project_dir(hovered_item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
end
@@ -453,6 +453,7 @@ command.add(function() return view.hovered_item ~= nil end, {
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
+ doc:reset_syntax()
break -- only first needed
end
end