diff --git a/changelog.md b/changelog.md index 71170dda..57ab9646 100644 --- a/changelog.md +++ b/changelog.md @@ -2,8 +2,47 @@ This files document the changes done in Lite XL for each release. ### 2.0.2 -Change behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. -The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+F3`. +Fix problem project directory when starting the application from Launcher on macOS. + +Improved LogView. Entries can now be expanded and there is a context menu to copy the item's content. + +Change the behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. +The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`. + +Added a command to create a multi-cursor with all the occurrences of the current selection. +Activated with the shortcut `ctrl+shift+l`. + +Fix problem when trying to close an unsaved new document. + +No longer shows an error for the `-psn` argument passed to the application on macOS. + +Fix `treeview:open-in-system` command on Windows. + +Fix rename command to update name of document if opened. + +Improve the find and replace dialog so that previously used expressions can be recalled +using "up" and "down" keys. + +Build package script rewrite with many improvements. + +Use bigger fonts by default. + +Other minor improvements and fixes. + +With many thanks to the contributors: @adamharrison, @takase1121, @Guldoman, @redtide, @Timofffee, @boppyt, @Jan200101. + +### 2.0.1 + +Fix a few bugs and we mandate the mod-version 2 for plugins. +This means that users should ensure they have up-to-date plugins for Lite XL 2.0. + +Here some details about the bug fixes: + +- fix a bug that created a fatal error when using the command to change project folder or when closing all the active documents +- add a limit to avoid scaling fonts too much and fix a related invalid memory access for very small fonts +- fix focus problem with NagView when switching project directory +- fix error that prevented the verification of plugins versions +- fix error on X11 that caused a bug window event on exit ### 2.0 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/findreplace.lua b/data/core/commands/findreplace.lua index 8ef97365..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 @@ -161,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..9f3102bb 100644 --- a/data/core/common.lua +++ b/data/core/common.lua @@ -276,6 +276,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 +291,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/init.lua b/data/core/init.lua index 6b770fd6..af291767 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 @@ -320,8 +319,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 @@ -435,13 +434,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 +462,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 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/style.lua b/data/core/style.lua index 7cf16eb1..faca166e 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.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * 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.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE, {antialiasing="grayscale", hinting="full"}) +style.icon_big_font = style.icon_font:copy(24 * SCALE) +style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE) style.background = { common.color "#2e2e32" } style.background2 = { common.color "#252529" } diff --git a/meson.build b/meson.build index ab192e7e..79c07195 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('lite-xl', ['c', 'cpp'], - version : '2.0.1', + version : '2.0.2', license : 'MIT', meson_version : '>= 0.54', default_options : ['c_std=gnu11', 'cpp_std=c++03'] diff --git a/scripts/repackage.sh b/scripts/repackage.sh index 99368582..f8da579f 100644 --- a/scripts/repackage.sh +++ b/scripts/repackage.sh @@ -10,7 +10,7 @@ copy_directory_from_repo () { fi local dirname="$1" local destdir="$2" - git archive master "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}" + git archive "$lite_branch" "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}" } lite_copy_third_party_modules () { @@ -23,12 +23,17 @@ lite_copy_third_party_modules () { rm "$build/rxi-lite-colors.zip" } +lite_branch=master while [ ! -z ${1+x} ]; do case "$1" in -dir) use_dir="$(realpath $2)" shift 2 ;; + -branch) + lite_branch="$2" + shift 2 + ;; *) echo "unknown option: $1" exit 1 @@ -73,6 +78,8 @@ for filename in $(ls -1 *.zip *.tar.*); do fi rm "$filename" find lite-xl -name lite -exec chmod a+x '{}' \; + start_file=$(find lite-xl -name start.lua) + lite_version=$(cat "$start_file" | awk 'match($0, /^\s*VERSION\s*=\s*"(.+)"/, a) { print(a[1]) }') xcoredir="$(find lite-xl -type d -name 'core')" coredir="$(dirname $xcoredir)" echo "coredir: $coredir" @@ -81,6 +88,7 @@ for filename in $(ls -1 *.zip *.tar.*); do rm -fr "$coredir/$module_name" (cd .. && copy_directory_from_repo --strip-components=1 "data/$module_name" "$workdir/$coredir") done + sed -i "s/@PROJECT_VERSION@/$lite_version/g" "$start_file" for module_name in plugins colors; do cp -r "third/data/$module_name" "$coredir" done