Merge branch 'project-manager'

This commit is contained in:
Francesco Abbate 2020-12-12 20:27:07 +01:00
commit 1fc0409163
13 changed files with 334 additions and 66 deletions

View File

@ -62,6 +62,7 @@ lite_build_package_windows () {
local bindir="$pdir"
local datadir="$pdir/data"
else
echo "WARNING: using non portable option on unix-like system"
local bindir="$pdir/bin"
local datadir="$pdir/share/lite-xl"
fi
@ -70,6 +71,9 @@ lite_build_package_windows () {
for module_name in core plugins colors fonts; do
copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp "$build/src/lite.exe" "$bindir"
strip --strip-all "$bindir/lite.exe"
pushd ".package-build"
@ -100,6 +104,9 @@ lite_build_package_macosx () {
for module_name in core plugins colors fonts; do
copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp "$build/src/lite" "$bindir"
strip "$bindir/lite"
pushd ".package-build"
@ -119,6 +126,7 @@ lite_build_package_linux () {
local os="linux"
local pdir=".package-build/lite-xl"
if [ $portable == "true" ]; then
echo "WARNING: using portable option on unix-like system"
local bindir="$pdir"
local datadir="$pdir/data"
else
@ -130,6 +138,9 @@ lite_build_package_linux () {
for module_name in core plugins colors fonts; do
copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp "$build/src/lite" "$bindir"
strip "$bindir/lite"
pushd ".package-build"
@ -154,6 +165,15 @@ lite_build_package () {
fi
}
lite_copy_third_party_modules () {
local build="$1"
curl --insecure -L "https://github.com/rxi/lite-colors/archive/master.zip" -o "$build/rxi-lite-colors.zip"
mkdir -p "$build/third/data/colors" "$build/third/data/plugins"
unzip "$build/rxi-lite-colors.zip" -d "$build"
mv "$build/lite-colors-master/colors" "$build/third/data"
rm -fr "$build/lite-colors-master"
}
if [[ -z "$1" || -z "$2" ]]; then
echo "usage: $0 [options] <version> <arch>"
exit 1
@ -184,5 +204,6 @@ if [ -z ${pgo+set} ]; then
else
lite_build_pgo "$portable" "$build_dir"
fi
lite_copy_third_party_modules "$build_dir"
lite_build_package "$portable" "$build_dir" "$version" "$arch"

View File

@ -12,6 +12,10 @@ command.add(nil, {
core.quit()
end,
["core:restart"] = function()
core.restart()
end,
["core:force-quit"] = function()
core.quit(true)
end,
@ -98,7 +102,15 @@ command.add(nil, {
end,
["core:open-user-module"] = function()
core.root_view:open_doc(core.open_doc(USERDIR .. "/init.lua"))
local user_module_doc = core.open_doc(USERDIR .. "/init.lua")
if not user_module_doc then return end
local doc_save = user_module_doc.save
user_module_doc.save = function(self)
doc_save(self)
core.reload_module("core.style")
core.load_user_directory()
end
core.root_view:open_doc(user_module_doc)
end,
["core:open-project-module"] = function()
@ -111,4 +123,32 @@ command.add(nil, {
doc:save(filename)
end
end,
["core:change-project-folder"] = function()
core.command_view:enter("Change Project Folder", function(text)
local path_stat = system.get_file_info(text)
if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open folder %q", text)
return
end
if core.confirm_close_all() then
core.open_folder_project(text)
end
end, function(text)
return text == "" and core.recent_projects or common.dir_path_suggest(text)
end)
end,
["core:open-project-folder"] = function()
core.command_view:enter("Open Project", function(text)
local path_stat = system.get_file_info(text)
if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open folder %q", text)
return
end
system.exec(string.format("%q %q", EXEFILE, text))
end, function(text)
return text == "" and core.recent_projects or common.dir_path_suggest(text)
end)
end,
})

View File

@ -102,6 +102,21 @@ function common.path_suggest(text)
end
function common.dir_path_suggest(text)
local path, name = text:match("^(.-)([^/\\]*)$")
local files = system.list_dir(path == "" and "." or path) or {}
local res = {}
for _, file in ipairs(files) do
file = path .. file
local info = system.get_file_info(file)
if info and info.type == "dir" and file:lower():find(text:lower(), nil, true) == 1 then
table.insert(res, file)
end
end
return res
end
function common.match_pattern(text, pattern, ...)
if type(pattern) == "string" then
return text:find(pattern, ...)

View File

@ -187,8 +187,14 @@ function DocView:scroll_to_make_visible(line, col)
self.scroll.to.y = math.max(self.scroll.to.y, max)
local gw = self:get_gutter_width()
local xoffset = self:get_col_x_offset(line, col)
local max = xoffset - self.size.x + gw + self.size.x / 5
self.scroll.to.x = math.max(0, max)
local xmargin = 3 * self:get_font():get_width(' ')
local xsup = xoffset + gw + xmargin
local xinf = xoffset - xmargin
if xsup > self.scroll.x + self.size.x then
self.scroll.to.x = xsup - self.size.x
elseif xinf < self.scroll.x then
self.scroll.to.x = math.max(0, xinf)
end
end

View File

@ -11,8 +11,53 @@ local Doc
local core = {}
local function table_serialize(t)
local ls = {"{"}
for i = 1, #t do
ls[#ls + 1] = string.format(" %q,", t[i])
end
ls[#ls + 1] = "}"
return table.concat(ls, "\n")
end
local function load_projects()
local ok, t = pcall(dofile, USERDIR .. "/recent_projects.lua")
core.recent_projects = (ok and t or {})
end
local function add_project_to_recents(dirname)
dirname = system.absolute_path(dirname)
if not dirname then return end
local recents = core.recent_projects
local n = #recents
for i = 1, n do
if dirname == recents[i] then
table.remove(recents, i)
break
end
end
table.insert(recents, 1, dirname)
end
local function save_projects()
local fp = io.open(USERDIR .. "/recent_projects.lua", "w")
if fp then
fp:write("return ", table_serialize(core.recent_projects), "\n")
fp:close()
end
end
function core.open_folder_project(dirname)
core.root_view:close_all_docviews()
add_project_to_recents(dirname)
save_projects()
core.switch_project = dirname
core.threads[core.project_scan_thread_id].wake = 0
end
local function project_scan_thread()
local priority_run = true
local function diff_files(a, b)
if #a ~= #b then return true end
for i, v in ipairs(a) do
@ -28,7 +73,7 @@ local function project_scan_thread()
end
local function get_files(path, t)
coroutine.yield()
if not priority_run then coroutine.yield() end
t = t or {}
local size_limit = config.file_size_limit * 10e5
local all = system.list_dir(path) or {}
@ -78,11 +123,18 @@ local function project_scan_thread()
end
core.project_files = t
core.redraw = true
priority_run = false
end
-- wait for next scan
if core.switch_project then
system.chdir(core.switch_project)
priority_run = true
core.switch_project = nil
else
coroutine.yield(config.project_scan_rate)
end
end
end
@ -121,30 +173,54 @@ local function write_user_init_file(init_filename)
init_file:write([[
-- put user settings here
-- this module will be loaded after everything else when the application starts
-- it will be automatically reloaded when saved
local keymap = require "core.keymap"
local config = require "core.config"
local style = require "core.style"
-- light theme:
-- require "colors.summer"
-- style.load("colors.summer")
-- key binding:
-- keymap.add { ["ctrl+escape"] = "core:quit" }
-- customize fonts:
-- style.font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 14 * SCALE)
-- style.code_font = renderer.font.load(DATADIR .. "/fonts/monospace.ttf", 13.5 * SCALE)
--
-- fonts used by the editor:
-- style.font : user interface
-- style.big_font : big text in welcome screen
-- style.icon_font : icons
-- style.code_font : code
--
-- the function to load the font accept a 3rd optional argument like:
--
-- {antialiasing="grayscale", hinting="full"}
--
-- possible values are:
-- antialiasing: grayscale, subpixel
-- hinting: none, slight, full
]])
init_file:close()
end
local function load_user_directory()
local init_filename = USERDIR .. "/init.lua"
local info = system.get_file_info(USERDIR)
if not info then
function core.load_user_directory()
return core.try(function()
local stat_dir = system.get_file_info(USERDIR)
if not stat_dir then
create_user_directory()
end
local init_filename = USERDIR .. "/init.lua"
local stat_file = system.get_file_info(init_filename)
if not stat_file then
write_user_init_file(init_filename)
end
return dofile(init_filename)
dofile(init_filename)
end)
end
@ -156,7 +232,9 @@ function core.init()
CommandView = require "core.commandview"
Doc = require "core.doc"
local project_dir = "."
load_projects()
local project_dir = core.recent_projects[1] or "."
local files = {}
for i = 2, #ARGS do
local info = system.get_file_info(ARGS[i]) or {}
@ -164,6 +242,8 @@ function core.init()
table.insert(files, system.absolute_path(ARGS[i]))
elseif info.type == "dir" then
project_dir = ARGS[i]
add_project_to_recents(project_dir)
save_projects()
end
end
@ -177,18 +257,25 @@ function core.init()
core.project_files = {}
core.redraw = true
core.visited_files = {}
core.restart_request = false
core.root_view = RootView()
core.command_view = CommandView()
core.status_view = StatusView()
core.root_view.root_node.is_primary_view = true
core.root_view.root_node:split("down", core.command_view, true)
core.root_view.root_node.b:split("down", core.status_view, true)
core.add_thread(project_scan_thread)
core.project_scan_thread_id = core.add_thread(project_scan_thread)
command.add_defaults()
local got_plugin_error = not core.load_plugins()
local got_user_error = not core.try(load_user_directory)
local got_user_error = not core.load_user_directory()
do
local pdir, pname = system.absolute_path(project_dir):match("(.*)[/\\\\](.*)")
core.log("Opening project %q from directory %q", pname, pdir)
end
local got_project_error = not core.load_project_module()
for _, filename in ipairs(files) do
@ -201,6 +288,28 @@ function core.init()
end
function core.confirm_close_all()
local dirty_count = 0
local dirty_name
for _, doc in ipairs(core.docs) do
if doc:is_dirty() then
dirty_count = dirty_count + 1
dirty_name = doc:get_name()
end
end
if dirty_count > 0 then
local text
if dirty_count == 1 then
text = string.format("\"%s\" has unsaved changes. Quit anyway?", dirty_name)
else
text = string.format("%d docs have unsaved changes. Quit anyway?", dirty_count)
end
local confirm = system.show_confirm_dialog("Unsaved Changes", text)
if not confirm then return false end
end
return true
end
local temp_uid = (system.get_time() * 1000) % 0xffffffff
local temp_file_prefix = string.format(".lite_temp_%08x", temp_uid)
local temp_file_counter = 0
@ -225,25 +334,16 @@ function core.quit(force)
delete_temp_files()
os.exit()
end
local dirty_count = 0
local dirty_name
for _, doc in ipairs(core.docs) do
if doc:is_dirty() then
dirty_count = dirty_count + 1
dirty_name = doc:get_name()
end
end
if dirty_count > 0 then
local text
if dirty_count == 1 then
text = string.format("\"%s\" has unsaved changes. Quit anyway?", dirty_name)
else
text = string.format("%d docs have unsaved changes. Quit anyway?", dirty_count)
end
local confirm = system.show_confirm_dialog("Unsaved Changes", text)
if not confirm then return end
end
if core.confirm_close_all() then
core.quit(true)
end
end
function core.restart()
if core.confirm_close_all() then
core.restart_request = true
end
end
@ -317,6 +417,7 @@ function core.add_thread(f, weak_ref)
local key = weak_ref or #core.threads + 1
local fn = function() return core.try(f) end
core.threads[key] = { cr = coroutine.create(fn), wake = 0 }
return key
end
@ -544,10 +645,12 @@ end)
function core.run()
local idle_iterations = 0
local frame_duration = 1 / config.fps
while true do
core.frame_start = system.get_time()
local did_redraw = core.step()
local need_more_work = run_threads()
if core.restart_request then break end
if not did_redraw and not need_more_work then
idle_iterations = idle_iterations + 1
-- do not wait of events at idle_iterations = 1 to give a chance at core.step to run
@ -555,16 +658,16 @@ function core.run()
if idle_iterations > 1 then
if system.window_has_focus() then
-- keep running even with no events to make the cursor blinks
system.wait_event(1 / config.fps)
system.wait_event(frame_duration)
else
system.wait_event()
end
end
else
idle_iterations = 0
end
local elapsed = system.get_time() - core.frame_start
system.sleep(math.max(0, 1 / config.fps - elapsed))
system.sleep(math.max(0, frame_duration - elapsed))
end
end
end

View File

@ -88,6 +88,8 @@ keymap.add {
["ctrl+p"] = "core:find-file",
["ctrl+o"] = "core:open-file",
["ctrl+n"] = "core:new-doc",
["ctrl+shift+c"] = "core:change-project-folder",
["ctrl+shift+o"] = "core:open-project-folder",
["alt+return"] = "core:toggle-fullscreen",
["alt+shift+j"] = "root:split-left",

View File

@ -4,6 +4,7 @@ local style = require "core.style"
local keymap = require "core.keymap"
local Object = require "core.object"
local View = require "core.view"
local CommandView = require "core.commandview"
local DocView = require "core.docview"
@ -18,9 +19,11 @@ local function draw_text(x, y, color)
local lines = {
{ fmt = "%s to run a command", cmd = "core:find-command" },
{ fmt = "%s to open a file from the project", cmd = "core:find-file" },
{ fmt = "%s to change project folder", cmd = "core:change-project-folder" },
{ fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
}
th = style.font:get_height()
y = y + (dh - th * 2 - style.padding.y) / 2
y = y + (dh - (th + style.padding.y) * #lines) / 2
local w = 0
for _, line in ipairs(lines) do
local text = string.format(line.fmt, keymap.get_binding(line.cmd))
@ -376,6 +379,41 @@ function Node:draw()
end
function Node:is_empty()
if self.type == "leaf" then
return #self.views == 0
else
return self.a:is_empty() and self.b:is_empty()
end
end
function Node:close_all_docviews()
if self.type == "leaf" then
local i = 1
while i <= #self.views do
local view = self.views[i]
if view:is(DocView) and not view:is(CommandView) then
table.remove(self.views, i)
else
i = i + 1
end
end
if #self.views == 0 and self.is_primary_view then
self:add_view(EmptyView())
end
else
self.a:close_all_docviews()
self.b:close_all_docviews()
if self.a:is_empty() then
self:consume(self.b)
elseif self.b:is_empty() then
self:consume(self.a)
end
end
end
local RootView = View:extend()
@ -396,20 +434,17 @@ function RootView:get_active_node()
return self.root_node:get_node_for_view(core.active_view)
end
-- Get un unlocked node with at least one view.
local function get_node_unlocked(node)
if not node.locked and #node.views > 0 then
local function get_primary_view(node)
if node.is_primary_view then
return node
end
if node.type ~= "leaf" then
local a = get_node_unlocked(node.a)
if a then return a end
return get_node_unlocked(node.b)
return get_primary_view(node.a) or get_primary_view(node.b)
end
end
function RootView:get_document_view()
local node = get_node_unlocked(self.root_node)
function RootView:get_primary_view()
local node = get_primary_view(self.root_node)
if node then
return node.views[1]
end
@ -418,8 +453,8 @@ end
function RootView:open_doc(doc)
local node = self:get_active_node()
if node.locked then
local default_view = self:get_document_view()
assert(default_view, "Cannot find an unlocked node to open the document.")
local default_view = self:get_primary_view()
assert(default_view, "internal error: cannot find original document node.")
core.set_active_view(default_view)
node = self:get_active_node()
end
@ -437,6 +472,11 @@ function RootView:open_doc(doc)
end
function RootView:close_all_docviews()
self.root_node:close_all_docviews()
end
function RootView:on_mouse_pressed(button, x, y, clicks)
local div = self.root_node:get_divider_overlapping_point(x, y)
if div then

View File

@ -53,4 +53,10 @@ style.syntax["string"] = { common.color "#f7c95c" }
style.syntax["operator"] = { common.color "#93DDFA" }
style.syntax["function"] = { common.color "#93DDFA" }
style.load = function(module_name)
package.loaded[module_name] = nil
require(module_name)
end
return style

View File

@ -1,13 +1,31 @@
-- put user settings here
-- this module will be loaded after everything else when the application starts
-- it will be automatically reloaded when saved
local keymap = require "core.keymap"
local config = require "core.config"
local style = require "core.style"
-- light theme:
-- require "colors.summer"
-- style.load("colors.summer")
-- key binding:
-- keymap.add { ["ctrl+escape"] = "core:quit" }
-- customize fonts:
-- style.font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 14 * SCALE)
-- style.code_font = renderer.font.load(DATADIR .. "/fonts/monospace.ttf", 13.5 * SCALE)
--
-- fonts used by the editor:
-- style.font : user interface
-- style.big_font : big text in welcome screen
-- style.icon_font : icons
-- style.code_font : code
--
-- the function to load the font accept a 3rd optional argument like:
--
-- {antialiasing="grayscale", hinting="full"}
--
-- possible values are:
-- antialiasing: grayscale, subpixel
-- hinting: none, slight, full

View File

@ -140,6 +140,13 @@ then be loaded manually as needed by using the `require` function.
Plugins can be downloaded from the [plugins repository](https://github.com/rxi/lite-plugins).
## Restarting the editor
If you modifies the user configuration file or some of the Lua implementation files you may
restart the editor using the command "Core: Restart".
All the application will be restarting by keeping the window that is already used.
## Color Themes
Colors themes in lite are lua modules which overwrite the color fields of lite's
`core.style` module.

View File

@ -81,20 +81,17 @@ public:
void set_font_height(double height) {
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
m_feng.height(height);
if (m_subpixel) {
const int subpixel_scale = 3;
m_feng.width(height * scale_x * subpixel_scale);
} else {
m_feng.width(height * scale_x);
}
}
template<class Rasterizer, class Scanline, class RenSolid>
void draw_codepoint(Rasterizer& ras, Scanline& sl,
RenSolid& ren_solid, const color_type color,
int codepoint, double& x, double& y, int subpixel_scale)
int codepoint, double& x, double& y, const int subpixel_scale)
{
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
// Coefficient to scale back the glyph to the final scale.
const double cx_inv_scale = subpixel_scale / scale_x;
// Represent the delta in x scaled by scale_x.
double x_delta = 0;
@ -114,15 +111,15 @@ public:
double ty = m_hinting ? floor(y + 0.5) : y;
ras.reset();
m_mtx.reset();
m_mtx *= agg::trans_affine_scaling(1.0 / scale_x, 1);
m_mtx *= agg::trans_affine_translation(start_x + x_delta / scale_x, ty);
m_mtx *= agg::trans_affine_scaling(cx_inv_scale, 1);
m_mtx *= agg::trans_affine_translation(start_x + cx_inv_scale * x_delta, ty);
ras.add_path(m_trans);
ren_solid.color(color);
agg::render_scanlines(ras, sl, ren_solid);
}
y += glyph->advance_y;
x += (x_delta + glyph->advance_x) / scale_x;
x += cx_inv_scale * (x_delta + glyph->advance_x);
}
}
@ -135,7 +132,7 @@ public:
void render_codepoint(agg::rendering_buffer& ren_buf,
const color_type text_color,
double& x, double& y,
int codepoint, int subpixel_scale)
int codepoint, const int subpixel_scale)
{
if (!m_font_loaded) {
return;

View File

@ -9,6 +9,8 @@ else
datadir="$rundir/share/lite-xl"
fi
userdir="$(realpath "$rundir")"
if [ "$#" -lt 1 ]; then
echo "usage: $0 <build-dir>"
exit 1
@ -16,7 +18,7 @@ fi
builddir="$1"
rm -fr "$rundir"
mkdir -p "$bindir" "$datadir"
mkdir -p "$bindir" "$datadir" "$userdir"
if [ -f "$builddir/src/lite" ]; then
cp "$builddir/src/lite" "$bindir"
elif [ -f "$builddir/src/lite.exe" ]; then
@ -28,4 +30,4 @@ fi
for module_name in core plugins colors fonts; do
cp -r "data/$module_name" "$datadir"
done
exec "$bindir/lite"
HOME="$userdir" USERPROFILE="$userdir" exec "$bindir/lite" "${@:2}"

View File

@ -89,8 +89,9 @@ int main(int argc, char **argv) {
init_window_icon();
ren_init(window);
lua_State *L = luaL_newstate();
lua_State *L;
init_lua:
L = luaL_newstate();
luaL_openlibs(L);
api_load_libs(L);
@ -117,7 +118,7 @@ int main(int argc, char **argv) {
lua_setglobal(L, "EXEFILE");
(void) luaL_dostring(L,
const char *init_lite_code = \
"local core\n"
"xpcall(function()\n"
" SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n"
@ -153,8 +154,18 @@ int main(int argc, char **argv) {
" pcall(core.on_error, err)\n"
" end\n"
" os.exit(1)\n"
"end)");
"end)\n"
"return core and core.restart_request\n";
if (luaL_loadstring(L, init_lite_code)) {
fprintf(stderr, "internal error when starting the application\n");
exit(1);
}
lua_pcall(L, 0, 1, 0);
if (lua_toboolean(L, -1)) {
lua_close(L);
goto init_lua;
}
lua_close(L);
SDL_DestroyWindow(window);