Merge branch master into dev

This commit is contained in:
Francesco Abbate 2021-05-22 15:12:05 +02:00
commit fbbe6d5dfb
25 changed files with 528 additions and 163 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2020 rxi Copyright (c) 2020-2021 Francesco Abbate
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View File

@ -108,7 +108,7 @@ is meant for people using a unix-like shell and the command line.
Please note that there aren't any hard-coded directories in the executable, so that the Please note that there aren't any hard-coded directories in the executable, so that the
package can be extracted and used in any directory. package can be extracted and used in any directory.
Mac OS X is fully supported and a notarized app disk image is provided in the release page. Mac OS X is fully supported and a notarized app disk image is provided in the [release page](https://github.com/franko/lite-xl/releases).
In addition the application can be compiled using the generic instructions given above. In addition the application can be compiled using the generic instructions given above.
## Contributing ## Contributing

View File

@ -14,6 +14,24 @@ local style = require "core.style"
local italic = renderer.font.load("italic.ttf", 14) local italic = renderer.font.load("italic.ttf", 14)
style.syntax_fonts["comment"] = italic style.syntax_fonts["comment"] = italic
``` ```
### 1.16.10
Improved syntax highlight system thanks to @liquidev and @adamharrison.
Thanks to the new system we provide more a accurate syntax highlighting for Lua, C and C++.
Other syntax improvements contributed by @vincens2005.
Move to JetBrains Mono and Fira Sans fonts for code and UI respectively.
Thet are provided under the SIL Open Font License, Version 1.1.
See `doc/licenses.md` for license details.
Fixed bug with fonts and rencache module.
Under very specific situations the application was crashing due to invalid memory access.
Add documentation for keymap binding, thanks to @Janis-Leuenberger.
Added a contibutors page in `doc/contributors.md`.
### 1.16.9 ### 1.16.9
Fix a bug related to nested panes resizing. Fix a bug related to nested panes resizing.

View File

@ -46,21 +46,14 @@ local function insert_at_start_of_selected_lines(text, skip_empty)
end end
local function remove_from_start_of_selected_lines(text, skip_empty)
local function remove_from_start_of_selected_lines(text, skip_empty, remove_partial)
local line1, col1, line2, col2, swap = doc_multiline_selection(true) local line1, col1, line2, col2, swap = doc_multiline_selection(true)
for line = line1, line2 do for line = line1, line2 do
local line_text = doc().lines[line] local line_text = doc().lines[line]
for i = #text, 1, -1 do if line_text:sub(1, #text) == text
if line_text:sub(1, i) == text:sub(1, i) and (not skip_empty or line_text:find("%S"))
and (not skip_empty or line_text:find("%S")) then
then doc():remove(line, 1, line, #text + 1)
doc():remove(line, 1, line, i + 1)
break
end
if not remove_partial then
break
end
end end
end end
doc():set_selection(line1, col1 - #text, line2, col2 - #text, swap) doc():set_selection(line1, col1 - #text, line2, col2 - #text, swap)
@ -86,6 +79,56 @@ local function save(filename)
core.log("Saved \"%s\"", saved_filename) core.log("Saved \"%s\"", saved_filename)
end end
-- returns the size of the original indent, and the indent
-- in your config format, rounded either up or down
local function get_line_indent(line, rnd_up)
local _, e = line:find("^[ \t]+")
local soft_tab = string.rep(" ", config.indent_size)
if config.tab_type == "hard" then
local indent = e and line:sub(1, e):gsub(soft_tab, "\t") or ""
return e, indent:gsub(" +", rnd_up and "\t" or "")
else
local indent = e and line:sub(1, e):gsub("\t", soft_tab) or ""
local number = #indent / #soft_tab
return e, indent:sub(1,
(rnd_up and math.ceil(number) or math.floor(number))*#soft_tab)
end
end
-- un/indents text; behaviour varies based on selection and un/indent.
-- * if there's a selection, it will stay static around the
-- text for both indenting and unindenting.
-- * if you are in the beginning whitespace of a line, and are indenting, the
-- cursor will insert the exactly appropriate amount of spaces, and jump the
-- cursor to the beginning of first non whitespace characters
-- * if you are not in the beginning whitespace of a line, and you indent, it
-- inserts the appropriate whitespace, as if you typed them normally.
-- * if you are unindenting, the cursor will jump to the start of the line,
-- and remove the appropriate amount of spaces (or a tab).
local function indent_text(unindent)
local text = get_indent_string()
local line1, col1, line2, col2, swap = doc():get_selection(true)
local _, se = doc().lines[line1]:find("^[ \t]+")
local in_beginning_whitespace = col1 == 1 or (se and col1 <= se + 1)
if unindent or doc():has_selection() or in_beginning_whitespace then
local l1d, l2d = #doc().lines[line1], #doc().lines[line2]
for line = line1, line2 do
local e, rnded = get_line_indent(doc().lines[line], unindent)
doc():remove(line, 1, line, (e or 0) + 1)
doc():insert(line, 1,
unindent and rnded:sub(1, #rnded - #text) or rnded .. text)
end
l1d, l2d = #doc().lines[line1] - l1d, #doc().lines[line2] - l2d
if (unindent or in_beginning_whitespace) and not doc():has_selection() then
local start_cursor = (se and se + 1 or 1) + l1d or #(doc().lines[line1])
doc():set_selection(line1, start_cursor, line2, start_cursor, swap)
else
doc():set_selection(line1, col1 + l1d, line2, col2 + l2d, swap)
end
else
doc():text_input(text)
end
end
local commands = { local commands = {
["doc:undo"] = function() ["doc:undo"] = function()
@ -195,17 +238,11 @@ local commands = {
end, end,
["doc:indent"] = function() ["doc:indent"] = function()
local text = get_indent_string() indent_text()
if doc():has_selection() then
insert_at_start_of_selected_lines(text)
else
doc():text_input(text)
end
end, end,
["doc:unindent"] = function() ["doc:unindent"] = function()
local text = get_indent_string() indent_text(true)
remove_from_start_of_selected_lines(text, false, true)
end, end,
["doc:duplicate-lines"] = function() ["doc:duplicate-lines"] = function()

View File

@ -269,8 +269,8 @@ local style = require "core.style"
------------------------------- Fonts ---------------------------------------- ------------------------------- Fonts ----------------------------------------
-- customize fonts: -- customize fonts:
-- style.font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 13 * SCALE) -- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Medium.ttf", 13 * SCALE)
-- style.code_font = renderer.font.load(DATADIR .. "/fonts/monospace.ttf", 12 * SCALE) -- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
-- --
-- font names used by lite: -- font names used by lite:
-- style.font : user interface -- style.font : user interface

View File

@ -1,6 +1,6 @@
-- this file is used by lite-xl to setup the Lua environment -- this file is used by lite-xl to setup the Lua environment
-- when starting -- when starting
VERSION = "1.16.9" VERSION = "1.16.10"
MOD_VERSION = "1" MOD_VERSION = "1"
SCALE = tonumber(os.getenv("LITE_SCALE")) or SCALE SCALE = tonumber(os.getenv("LITE_SCALE")) or SCALE

View File

@ -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. -- 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. -- The antialiasing grayscale with full hinting is interesting for crisp font rendering.
style.font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 13 * SCALE) style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Medium.ttf", 13 * SCALE)
style.big_font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 34 * SCALE) style.big_font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Medium.ttf", 40 * SCALE)
style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 14 * SCALE, {antialiasing="grayscale", hinting="full"}) style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 14 * SCALE, {antialiasing="grayscale", hinting="full"})
style.icon_big_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 20 * SCALE, {antialiasing="grayscale", hinting="full"}) style.icon_big_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 20 * SCALE, {antialiasing="grayscale", hinting="full"})
style.code_font = renderer.font.load(DATADIR .. "/fonts/monospace.ttf", 12 * SCALE) style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
style.background = { common.color "#2e2e32" } style.background = { common.color "#2e2e32" }
style.background2 = { common.color "#252529" } style.background2 = { common.color "#252529" }

View File

@ -75,8 +75,19 @@ end
-- differnet delimiters we have open, and which subsyntaxes we have active. -- differnet delimiters we have open, and which subsyntaxes we have active.
-- At most, there are 3 subsyntaxes active at the same time. Beyond that, -- At most, there are 3 subsyntaxes active at the same time. Beyond that,
-- does not support further highlighting. -- does not support further highlighting.
-- You can think of it as a maximum 4 integer (0-255) stack. It always has
-- 1 integer in it. Calling `push_subsyntax` increases the stack depth. Calling
-- `pop_subsyntax` decreases it. The integers represent the index of a pattern
-- that we're following in the syntax. The top of the stack can be any valid
-- pattern index, any integer lower in the stack must represent a pattern that
-- specifies a subsyntax.
-- If you do not have subsyntaxes in your syntax, the three most
-- singificant numbers will always be 0, the stack will only ever be length 1
-- and the state variable will only ever range from 0-255.
local function retrieve_syntax_state(incoming_syntax, state) local function retrieve_syntax_state(incoming_syntax, state)
local current_syntax, subsyntax_info, current_state, current_level = local current_syntax, subsyntax_info, current_pattern_idx, current_level =
incoming_syntax, nil, state, 0 incoming_syntax, nil, state, 0
if state > 0 and (state > 255 or current_syntax.patterns[state].syntax) then if state > 0 and (state > 255 or current_syntax.patterns[state].syntax) then
-- If we have higher bits, then decode them one at a time, and find which -- If we have higher bits, then decode them one at a time, and find which
@ -89,10 +100,10 @@ local function retrieve_syntax_state(incoming_syntax, state)
subsyntax_info = current_syntax.patterns[target] subsyntax_info = current_syntax.patterns[target]
current_syntax = type(subsyntax_info.syntax) == "table" and current_syntax = type(subsyntax_info.syntax) == "table" and
subsyntax_info.syntax or syntax.get(subsyntax_info.syntax) subsyntax_info.syntax or syntax.get(subsyntax_info.syntax)
current_state = 0 current_pattern_idx = 0
current_level = i+1 current_level = i+1
else else
current_state = target current_pattern_idx = target
break break
end end
else else
@ -100,7 +111,7 @@ local function retrieve_syntax_state(incoming_syntax, state)
end end
end end
end end
return current_syntax, subsyntax_info, current_state, current_level return current_syntax, subsyntax_info, current_pattern_idx, current_level
end end
function tokenizer.tokenize(incoming_syntax, text, state) function tokenizer.tokenize(incoming_syntax, text, state)
@ -112,17 +123,51 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end end
state = state or 0 state = state or 0
local current_syntax, subsyntax_info, current_state, current_level = -- incoming_syntax : the parent syntax of the file.
-- state : a 32-bit number representing syntax state (see above)
-- current_syntax : the syntax we're currently in.
-- subsyntax_info : info about the delimiters of this subsyntax.
-- current_pattern_idx: the index of the pattern we're on for this syntax.
-- current_level : how many subsyntaxes deep we are.
local current_syntax, subsyntax_info, current_pattern_idx, current_level =
retrieve_syntax_state(incoming_syntax, state) retrieve_syntax_state(incoming_syntax, state)
-- Should be used to set the state variable. Don't modify it directly.
local function set_subsyntax_pattern_idx(pattern_idx)
current_pattern_idx = pattern_idx
state = bit32.replace(state, pattern_idx, current_level*8, 8)
end
local function push_subsyntax(entering_syntax, pattern_idx)
set_subsyntax_pattern_idx(pattern_idx)
current_level = current_level + 1
subsyntax_info = entering_syntax
current_syntax = type(entering_syntax.syntax) == "table" and
entering_syntax.syntax or syntax.get(entering_syntax.syntax)
current_pattern_idx = 0
end
local function pop_subsyntax()
set_subsyntax_pattern_idx(0)
current_level = current_level - 1
set_subsyntax_pattern_idx(0)
current_syntax, subsyntax_info, current_pattern_idx, current_level =
retrieve_syntax_state(incoming_syntax, state)
end
while i <= #text do while i <= #text do
-- continue trying to match the end pattern of a pair if we have a state set -- continue trying to match the end pattern of a pair if we have a state set
if current_state > 0 then if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_state] local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_non_escaped(text, p.pattern[2], i, p.pattern[3]) local s, e = find_non_escaped(text, p.pattern[2], i, p.pattern[3])
local cont = true local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax -- If we're in subsyntax mode, always check to see if we end our syntax
-- first. -- first, before the found delimeter, as ending the subsyntax takes
-- precedence over ending the delimiter in the subsyntax.
if subsyntax_info then if subsyntax_info then
local ss, se = find_non_escaped( local ss, se = find_non_escaped(
text, text,
@ -130,17 +175,22 @@ function tokenizer.tokenize(incoming_syntax, text, state)
i, i,
subsyntax_info.pattern[3] subsyntax_info.pattern[3]
) )
-- If we find that we end the subsyntax before the
-- delimiter, push the token, and signal we shouldn't
-- treat the bit after as a token to be normally parsed
-- (as it's the syntax delimiter).
if ss and (s == nil or ss < s) then if ss and (s == nil or ss < s) then
push_token(res, p.type, text:sub(i, ss - 1)) push_token(res, p.type, text:sub(i, ss - 1))
i = ss i = ss
cont = false cont = false
end end
end end
-- If we don't have any concerns about syntax delimiters,
-- continue on as normal.
if cont then if cont then
if s then if s then
push_token(res, p.type, text:sub(i, e)) push_token(res, p.type, text:sub(i, e))
current_state = 0 set_subsyntax_pattern_idx(0)
state = bit32.replace(state, 0, current_level*8, 8)
i = e + 1 i = e + 1
else else
push_token(res, p.type, text:sub(i)) push_token(res, p.type, text:sub(i))
@ -148,7 +198,9 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end end
end end
end end
-- Check for end of syntax. -- General end of syntax check. Applies in the case where
-- we're ending early in the middle of a delimiter, or
-- just normally, upon finding a token.
if subsyntax_info then if subsyntax_info then
local s, e = find_non_escaped( local s, e = find_non_escaped(
text, text,
@ -158,11 +210,8 @@ function tokenizer.tokenize(incoming_syntax, text, state)
) )
if s then if s then
push_token(res, subsyntax_info.type, text:sub(i, e)) push_token(res, subsyntax_info.type, text:sub(i, e))
current_level = current_level - 1 -- On finding unescaped delimiter, pop it.
-- Zero out the state above us, as well as our new current state. pop_subsyntax()
state = bit32.replace(state, 0, current_level*8, 16)
current_syntax, subsyntax_info, current_state, current_level =
retrieve_syntax_state(incoming_syntax, state)
i = e + 1 i = e + 1
end end
end end
@ -180,17 +229,11 @@ function tokenizer.tokenize(incoming_syntax, text, state)
-- update state if this was a start|end pattern pair -- update state if this was a start|end pattern pair
if type(p.pattern) == "table" then if type(p.pattern) == "table" then
state = bit32.replace(state, n, current_level*8, 8) -- If we have a subsyntax, push that onto the subsyntax stack.
-- If we've found a new subsyntax, bump our level, and set the
-- appropriate variables.
if p.syntax then if p.syntax then
current_level = current_level + 1 push_subsyntax(p, n)
subsyntax_info = p
current_syntax = type(p.syntax) == "table" and
p.syntax or syntax.get(p.syntax)
current_state = 0
else else
current_state = n set_subsyntax_pattern_idx(n)
end end
end end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,21 +6,28 @@ syntax.add {
headers = "^#!.*[ /]lua", headers = "^#!.*[ /]lua",
comment = "--", comment = "--",
patterns = { patterns = {
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = { "%[%[", "%]%]" }, type = "string" }, { pattern = { "%[%[", "%]%]" }, type = "string" },
{ pattern = { "%-%-%[%[", "%]%]"}, type = "comment" }, { pattern = { "%-%-%[%[", "%]%]"}, type = "comment" },
{ pattern = "%-%-.-\n", type = "comment" }, { pattern = "%-%-.-\n", type = "comment" },
{ pattern = "-?0x%x+", type = "number" }, { pattern = "0x%x+%.%x*[pP][-+]?%d+", type = "number" },
{ pattern = "-?%d+[%d%.eE]*", type = "number" }, { pattern = "0x%x+%.%x*", type = "number" },
{ pattern = "-?%.?%d+", type = "number" }, { pattern = "0x%.%x+[pP][-+]?%d+", type = "number" },
{ pattern = "<%a+>", type = "keyword2" }, { pattern = "0x%.%x+", type = "number" },
{ pattern = "%.%.%.?", type = "operator" }, { pattern = "0x%x+[pP][-+]?%d+", type = "number" },
{ pattern = "[<>~=]=", type = "operator" }, { pattern = "0x%x+", type = "number" },
{ pattern = "[%+%-=/%*%^%%#<>]", type = "operator" }, { pattern = "%d%.%d*[eE][-+]?%d+", type = "number" },
{ pattern = "[%a_][%w_]*%s*%f[(\"{]", type = "function" }, { pattern = "%d%.%d*", type = "number" },
{ pattern = "[%a_][%w_]*", type = "symbol" }, { pattern = "%.?%d*[eE][-+]?%d+", type = "number" },
{ pattern = "::[%a_][%w_]*::", type = "function" }, { pattern = "%.?%d+", type = "number" },
{ pattern = "<%a+>", type = "keyword2" },
{ pattern = "%.%.%.?", type = "operator" },
{ pattern = "[<>~=]=", type = "operator" },
{ pattern = "[%+%-=/%*%^%%#<>]", type = "operator" },
{ pattern = "[%a_][%w_]*()%s*%f[(\"'{]", type = {"function", "normal"} },
{ pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "::[%a_][%w_]*::", type = "function" },
}, },
symbols = { symbols = {
["if"] = "keyword", ["if"] = "keyword",

View File

@ -1,50 +0,0 @@
-- put user settings here
-- this module will be loaded after everything else when the application starts
-- it will be automatically reloaded when saved
local core = require "core"
local keymap = require "core.keymap"
local config = require "core.config"
local style = require "core.style"
------------------------------ Themes ----------------------------------------
-- light theme:
-- core.reload_module("colors.summer")
--------------------------- Key bindings -------------------------------------
-- key binding:
-- keymap.add { ["ctrl+escape"] = "core:quit" }
------------------------------- Fonts ----------------------------------------
-- customize fonts:
-- style.font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 13 * SCALE)
-- style.code_font = renderer.font.load(DATADIR .. "/fonts/monospace.ttf", 12 * SCALE)
--
-- font names used by lite:
-- style.font : user interface
-- style.big_font : big text in welcome screen
-- style.icon_font : icons
-- style.icon_big_font : toolbar 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
------------------------------ Plugins ----------------------------------------
-- enable or disable plugin loading setting config entries:
-- enable trimwhitespace, otherwise it is disable by default:
-- config.trimwhitespace = true
--
-- disable detectindent, otherwise it is enabled by default
-- config.detectindent = false

43
doc/contributors.md Normal file
View File

@ -0,0 +1,43 @@
## rxi
Original development of lite editor.
## Francesco Abbate (franko)
Creator of lite-xl fork from rxi/lite.
## Takase (takase1121)
NagView and X Window database resource query for Xft.dpi setting.
## Nils Kvist (budRich)
Popup window replacement with CommandView dialog.
## liquidev
Tab style and animations improvements.
## adamharrison
Multi-language syntax highlighting and many other improvements.
## vincens2005
Syntax highlighting improvements.
## Janis-Leuenberger
Add keymap bindings help file and macOS testing.
## Mat Mariani (mathewmariani)
Help for Mac OS port. Some resources taken from mathewmariani/lite-macos.
## daubaris
Initial implementation of Xft.dpi query using xrdb command.
## Robert Štojs (netrobert)
Continuos integration configuration

113
doc/default-keymap.md Normal file
View File

@ -0,0 +1,113 @@
# Default keymap
*Note: When using macOS `ctrl` refers to the command key.*
| Key combination | Actions |
| --------------------- | ----------------------------------- |
| ctrl+shift+p | core:find-command |
| 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 |
| alt+shift+l | root:split-right |
| alt+shift+i | root:split-up |
| alt+shift+k | root:split-down |
| alt+j | root:switch-to-left |
| alt+l | root:switch-to-right |
| alt+i | root:switch-to-up |
| alt+k | root:switch-to-down |
| ctrl+w | root:close |
| ctrl+tab | root:switch-to-next-tab |
| ctrl+shift+tab | root:switch-to-previous-tab |
| ctrl+pageup | root:move-tab-left |
| ctrl+pagedown | root:move-tab-right |
| alt+1 | root:switch-to-tab-1 |
| alt+2 | root:switch-to-tab-2 |
| alt+3 | root:switch-to-tab-3 |
| alt+4 | root:switch-to-tab-4 |
| alt+5 | root:switch-to-tab-5 |
| alt+6 | root:switch-to-tab-6 |
| alt+7 | root:switch-to-tab-7 |
| alt+8 | root:switch-to-tab-8 |
| alt+9 | root:switch-to-tab-9 |
| ctrl+f | find-replace:find |
| ctrl+r | find-replace:replace |
| f3 | find-replace:repeat-find |
| shift+f3 | find-replace:previous-find |
| ctrl+g | doc:go-to-line |
| ctrl+s | doc:save |
| ctrl+shift+s | doc:save-as |
| ctrl+z | doc:undo |
| ctrl+y | doc:redo |
| ctrl+x | doc:cut |
| ctrl+c | doc:copy |
| ctrl+v | doc:paste |
| ctrl+insert | doc:copy |
| shift+insert | doc:paste |
| escape | command:escape |
| escape | doc:select-none |
| escape | dialog:select-no |
| tab | command:complete |
| tab | doc:indent |
| shift+tab | doc:unindent |
| backspace | doc:backspace |
| shift+backspace | doc:backspace |
| ctrl+backspace | doc:delete-to-previous-word-start |
| ctrl+shift+backspace | doc:delete-to-previous-word-start |
| delete | doc:delete |
| shift+delete | doc:delete |
| ctrl+delete | doc:delete-to-next-word-end |
| ctrl+shift+delete | doc:delete-to-next-word-end |
| return | command:submit |
| return | doc:newline |
| return | dialog:select |
| keypad enter | command:submit |
| keypad enter | doc:newline |
| keypad enter | dialog:select |
| ctrl+return | doc:newline-below |
| ctrl+shift+return | doc:newline-above |
| ctrl+j | doc:join-lines |
| ctrl+a | doc:select-all |
| ctrl+d | find-replace:select-next |
| ctrl+d | doc:select-word |
| ctrl+l | doc:select-lines |
| ctrl+/ | doc:toggle-line-comments |
| ctrl+up | doc:move-lines-up |
| ctrl+down | doc:move-lines-down |
| ctrl+shift+d | doc:duplicate-lines |
| ctrl+shift+k | doc:delete-lines |
| left | doc:move-to-previous-char |
| left | dialog:previous-entry |
| right | doc:move-to-next-char |
| right | dialog:next-entry |
| up | command:select-previous |
| up | doc:move-to-previous-line |
| down | command:select-next |
| down | doc:move-to-next-line |
| ctrl+left | doc:move-to-previous-word-start |
| ctrl+right | doc:move-to-next-word-end |
| ctrl+[ | doc:move-to-previous-block-start |
| ctrl+] | doc:move-to-next-block-end |
| home | doc:move-to-start-of-line |
| end | doc:move-to-end-of-line |
| ctrl+home | doc:move-to-start-of-doc |
| ctrl+end | doc:move-to-end-of-doc |
| pageup | doc:move-to-previous-page |
| pagedown | doc:move-to-next-page |
| shift+left | doc:select-to-previous-char |
| shift+right | doc:select-to-next-char |
| shift+up | doc:select-to-previous-line |
| shift+down | doc:select-to-next-line |
| ctrl+shift+left | doc:select-to-previous-word-start |
| ctrl+shift+right | doc:select-to-next-word-end |
| ctrl+shift+[ | doc:select-to-previous-block-start |
| ctrl+shift+] | doc:select-to-next-block-end |
| shift+home | doc:select-to-start-of-line |
| shift+end | doc:select-to-end-of-line |
| ctrl+shift+home | doc:select-to-start-of-doc |
| ctrl+shift+end | doc:select-to-end-of-doc |
| shift+pageup | doc:select-to-previous-page |
| shift+pagedown | doc:select-to-next-page |

130
doc/licenses.md Normal file
View File

@ -0,0 +1,130 @@
# Licenses
## rxi/lite
Copyright (c) 2020 rxi
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## Fira Sans
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
## Fira Code
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
## JetBrains Mono
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
# SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -149,6 +149,8 @@ local keymap = require "core.keymap"
keymap.add { ["ctrl+q"] = "core:quit" } keymap.add { ["ctrl+q"] = "core:quit" }
``` ```
A list of default mappings can be viewed [here](./default-keymap.md).
## Plugins ## Plugins
Plugins in lite are normal lua modules and are treated as such — no Plugins in lite are normal lua modules and are treated as such — no

View File

@ -29,8 +29,10 @@ endif
lite_cargs = [] lite_cargs = []
if get_option('portable') if get_option('portable')
lite_docdir = 'doc'
lite_datadir = 'data' lite_datadir = 'data'
else else
lite_docdir = 'share/doc'
lite_datadir = 'share/lite-xl' lite_datadir = 'share/lite-xl'
endif endif
@ -39,10 +41,17 @@ foreach data_module : ['core', 'fonts', 'plugins', 'colors']
install_subdir('data' / data_module , install_dir : lite_datadir) install_subdir('data' / data_module , install_dir : lite_datadir)
endforeach endforeach
foreach file : ['usage.md', 'licenses.md', 'contributors.md', 'default-keymap.md']
install_data('doc' / file, install_dir : lite_docdir)
endforeach
lite_link_args = [] lite_link_args = []
if cc.get_id() == 'gcc' and get_option('buildtype') == 'release' if cc.get_id() == 'gcc' and get_option('buildtype') == 'release'
lite_link_args += ['-static-libgcc', '-static-libstdc++'] lite_link_args += ['-static-libgcc', '-static-libstdc++']
endif endif
if host_machine.system() == 'darwin'
lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation']
endif
lite_rc = [] lite_rc = []
if host_machine.system() == 'windows' if host_machine.system() == 'windows'

View File

@ -38,13 +38,13 @@ static int f_get_size(lua_State *L) {
static int f_begin_frame(lua_State *L) { static int f_begin_frame(lua_State *L) {
rencache_begin_frame(); rencache_begin_frame(L);
return 0; return 0;
} }
static int f_end_frame(lua_State *L) { static int f_end_frame(lua_State *L) {
rencache_end_frame(); rencache_end_frame(L);
return 0; return 0;
} }
@ -90,7 +90,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
replace_color = (RenColor) {0}; replace_color = (RenColor) {0};
} }
x_subpixel = rencache_draw_text(font_desc, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); x_subpixel = rencache_draw_text(L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color);
lua_pushnumber(L, x_subpixel); lua_pushnumber(L, x_subpixel);
return 1; return 1;
} }

View File

@ -1,3 +1,6 @@
#include <lua.h>
#include <lauxlib.h>
#include "api.h" #include "api.h"
#include "fontdesc.h" #include "fontdesc.h"
#include "renderer.h" #include "renderer.h"
@ -62,7 +65,7 @@ static int f_set_tab_size(lua_State *L) {
static int f_gc(lua_State *L) { static int f_gc(lua_State *L) {
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
rencache_free_font(self); font_desc_free(self);
return 0; return 0;
} }

View File

@ -159,11 +159,6 @@ init_lua:
enable_momentum_scroll(); enable_momentum_scroll();
#endif #endif
/* We need to clear the rencache commands because we may restarting the application
and it could be non-empty. It is important to clear the command buffer because it
stores pointers to font_desc objects and these are Lua managed. */
rencache_clear();
const char *init_lite_code = \ const char *init_lite_code = \
"local core\n" "local core\n"
"xpcall(function()\n" "xpcall(function()\n"

View File

@ -1,6 +1,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <lauxlib.h>
#include "rencache.h" #include "rencache.h"
/* a cache over the software renderer -- all drawing operations are stored as /* a cache over the software renderer -- all drawing operations are stored as
@ -14,7 +16,7 @@
#define COMMAND_BUF_SIZE (1024 * 512) #define COMMAND_BUF_SIZE (1024 * 512)
#define COMMAND_BARE_SIZE offsetof(Command, text) #define COMMAND_BARE_SIZE offsetof(Command, text)
enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL };
typedef struct { typedef struct {
int8_t type; int8_t type;
@ -30,6 +32,15 @@ typedef struct {
char text[0]; char text[0];
} Command; } Command;
#define FONT_REFS_MAX 12
struct FontRef {
FontDesc *font_desc;
int index;
};
typedef struct FontRef FontRef;
FontRef font_refs[FONT_REFS_MAX];
int font_refs_len = 0;
static unsigned cells_buf1[CELLS_X * CELLS_Y]; static unsigned cells_buf1[CELLS_X * CELLS_Y];
static unsigned cells_buf2[CELLS_X * CELLS_Y]; static unsigned cells_buf2[CELLS_X * CELLS_Y];
@ -45,6 +56,33 @@ static bool show_debug;
static inline int min(int a, int b) { return a < b ? a : b; } static inline int min(int a, int b) { return a < b ? a : b; }
static inline int max(int a, int b) { return a > b ? a : b; } static inline int max(int a, int b) { return a > b ? a : b; }
static int font_refs_add(lua_State *L, FontDesc *font_desc, int index) {
for (int i = 0; i < font_refs_len; i++) {
if (font_refs[i].font_desc == font_desc) {
return font_refs[i].index;
}
}
if (font_refs_len >= FONT_REFS_MAX) {
fprintf(stderr, "Warning: (" __FILE__ "): exhausted font reference buffer\n");
return LUA_NOREF;
}
lua_pushvalue(L, index);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
font_refs[font_refs_len++] = (FontRef) { font_desc, ref };
return ref;
}
static void font_refs_clear(lua_State *L) {
for (int i = 0; i < font_refs_len; i++) {
luaL_unref(L, LUA_REGISTRYINDEX, font_refs[i].index);
}
font_refs_len = 0;
}
/* 32bit fnv-1a hash */ /* 32bit fnv-1a hash */
#define HASH_INITIAL 2166136261 #define HASH_INITIAL 2166136261
@ -115,12 +153,6 @@ void rencache_show_debug(bool enable) {
} }
void rencache_free_font(FontDesc *font_desc) {
Command *cmd = push_command(FREE_FONT, COMMAND_BARE_SIZE);
if (cmd) { cmd->font_desc = font_desc; }
}
void rencache_set_clip_rect(RenRect rect) { void rencache_set_clip_rect(RenRect rect) {
Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE); Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE);
if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); } if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); }
@ -136,7 +168,7 @@ void rencache_draw_rect(RenRect rect, RenColor color) {
} }
} }
int rencache_draw_text(FontDesc *font_desc, int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index,
const char *text, int x, int y, RenColor color, bool draw_subpixel, const char *text, int x, int y, RenColor color, bool draw_subpixel,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
@ -148,7 +180,7 @@ int rencache_draw_text(FontDesc *font_desc,
rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0);
rect.height = ren_get_font_height(font_desc); rect.height = ren_get_font_height(font_desc);
if (rects_overlap(screen_rect, rect)) { if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) {
int sz = strlen(text) + 1; int sz = strlen(text) + 1;
Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz);
if (cmd) { if (cmd) {
@ -173,7 +205,7 @@ void rencache_invalidate(void) {
} }
void rencache_begin_frame(void) { void rencache_begin_frame(lua_State *L) {
/* reset all cells if the screen width/height has changed */ /* reset all cells if the screen width/height has changed */
int w, h; int w, h;
ren_get_size(&w, &h); ren_get_size(&w, &h);
@ -182,6 +214,7 @@ void rencache_begin_frame(void) {
screen_rect.height = h; screen_rect.height = h;
rencache_invalidate(); rencache_invalidate();
} }
font_refs_clear(L);
} }
@ -214,7 +247,7 @@ static void push_rect(RenRect r, int *count) {
} }
void rencache_end_frame(void) { void rencache_end_frame(lua_State *L) {
/* update cells from commands */ /* update cells from commands */
Command *cmd = NULL; Command *cmd = NULL;
RenRect cr = screen_rect; RenRect cr = screen_rect;
@ -253,7 +286,6 @@ void rencache_end_frame(void) {
} }
/* redraw updated regions */ /* redraw updated regions */
bool has_free_commands = false;
for (int i = 0; i < rect_count; i++) { for (int i = 0; i < rect_count; i++) {
/* draw */ /* draw */
RenRect r = rect_buf[i]; RenRect r = rect_buf[i];
@ -262,9 +294,6 @@ void rencache_end_frame(void) {
cmd = NULL; cmd = NULL;
while (next_command(&cmd)) { while (next_command(&cmd)) {
switch (cmd->type) { switch (cmd->type) {
case FREE_FONT:
has_free_commands = true;
break;
case SET_CLIP: case SET_CLIP:
ren_set_clip_rect(intersect_rects(cmd->rect, r)); ren_set_clip_rect(intersect_rects(cmd->rect, r));
break; break;
@ -296,16 +325,6 @@ void rencache_end_frame(void) {
ren_update_rects(rect_buf, rect_count); ren_update_rects(rect_buf, rect_count);
} }
/* free fonts */
if (has_free_commands) {
cmd = NULL;
while (next_command(&cmd)) {
if (cmd->type == FREE_FONT) {
font_desc_free(cmd->font_desc);
}
}
}
/* swap cell buffer and reset */ /* swap cell buffer and reset */
unsigned *tmp = cells; unsigned *tmp = cells;
cells = cells_prev; cells = cells_prev;
@ -313,6 +332,3 @@ void rencache_end_frame(void) {
command_buf_idx = 0; command_buf_idx = 0;
} }
void rencache_clear() {
command_buf_idx = 0;
}

View File

@ -2,17 +2,16 @@
#define RENCACHE_H #define RENCACHE_H
#include <stdbool.h> #include <stdbool.h>
#include <lua.h>
#include "renderer.h" #include "renderer.h"
void rencache_show_debug(bool enable); void rencache_show_debug(bool enable);
void rencache_free_font(FontDesc *font_desc);
void rencache_set_clip_rect(RenRect rect); void rencache_set_clip_rect(RenRect rect);
void rencache_draw_rect(RenRect rect, RenColor color); void rencache_draw_rect(RenRect rect, RenColor color);
int rencache_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color,
bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color);
void rencache_invalidate(void); void rencache_invalidate(void);
void rencache_begin_frame(void); void rencache_begin_frame(lua_State *L);
void rencache_end_frame(void); void rencache_end_frame(lua_State *L);
void rencache_clear();
#endif #endif