diff --git a/LICENSE b/LICENSE index 39ddd05c..20ca7d69 100644 --- a/LICENSE +++ b/LICENSE @@ -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 this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index ca9558f2..a5e0badc 100644 --- a/README.md +++ b/README.md @@ -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 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. ## Contributing diff --git a/changelog.md b/changelog.md index 6f95fd60..e9566a59 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,24 @@ local style = require "core.style" local italic = renderer.font.load("italic.ttf", 14) 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 Fix a bug related to nested panes resizing. diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index 033055c5..aecc80be 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -45,22 +45,15 @@ local function insert_at_start_of_selected_lines(text, skip_empty) doc():set_selection(line1, col1 + #text, line2, col2 + #text, swap) end - -local function remove_from_start_of_selected_lines(text, skip_empty, remove_partial) +local function remove_from_start_of_selected_lines(text, skip_empty) local line1, col1, line2, col2, swap = doc_multiline_selection(true) for line = line1, line2 do local line_text = doc().lines[line] - for i = #text, 1, -1 do - if line_text:sub(1, i) == text:sub(1, i) - and (not skip_empty or line_text:find("%S")) - then - doc():remove(line, 1, line, i + 1) - break - end - if not remove_partial then - break - end + if line_text:sub(1, #text) == text + and (not skip_empty or line_text:find("%S")) + then + doc():remove(line, 1, line, #text + 1) end end doc():set_selection(line1, col1 - #text, line2, col2 - #text, swap) @@ -86,6 +79,56 @@ local function save(filename) core.log("Saved \"%s\"", saved_filename) 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 = { ["doc:undo"] = function() @@ -195,17 +238,11 @@ local commands = { end, ["doc:indent"] = function() - local text = get_indent_string() - if doc():has_selection() then - insert_at_start_of_selected_lines(text) - else - doc():text_input(text) - end + indent_text() end, ["doc:unindent"] = function() - local text = get_indent_string() - remove_from_start_of_selected_lines(text, false, true) + indent_text(true) end, ["doc:duplicate-lines"] = function() diff --git a/data/core/init.lua b/data/core/init.lua index caab6f13..f1cd9367 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -269,8 +269,8 @@ local style = require "core.style" ------------------------------- 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) +-- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Medium.ttf", 13 * SCALE) +-- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE) -- -- font names used by lite: -- style.font : user interface diff --git a/data/core/start.lua b/data/core/start.lua index 7d4ad9a8..17e12bde 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -1,6 +1,6 @@ -- this file is used by lite-xl to setup the Lua environment -- when starting -VERSION = "1.16.9" +VERSION = "1.16.10" MOD_VERSION = "1" SCALE = tonumber(os.getenv("LITE_SCALE")) or SCALE diff --git a/data/core/style.lua b/data/core/style.lua index c6859dd4..7001cdc1 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/font.ttf", 13 * SCALE) -style.big_font = renderer.font.load(DATADIR .. "/fonts/font.ttf", 34 * SCALE) +style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Medium.ttf", 13 * 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_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.background2 = { common.color "#252529" } diff --git a/data/core/tokenizer.lua b/data/core/tokenizer.lua index 6d51928c..83e0e665 100644 --- a/data/core/tokenizer.lua +++ b/data/core/tokenizer.lua @@ -74,9 +74,20 @@ end -- State is a 32-bit number that is four separate bytes, illustrating how many -- differnet delimiters we have open, and which subsyntaxes we have active. -- 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 current_syntax, subsyntax_info, current_state, current_level = + local current_syntax, subsyntax_info, current_pattern_idx, current_level = incoming_syntax, nil, state, 0 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 @@ -89,10 +100,10 @@ local function retrieve_syntax_state(incoming_syntax, state) subsyntax_info = current_syntax.patterns[target] current_syntax = type(subsyntax_info.syntax) == "table" and subsyntax_info.syntax or syntax.get(subsyntax_info.syntax) - current_state = 0 + current_pattern_idx = 0 current_level = i+1 else - current_state = target + current_pattern_idx = target break end else @@ -100,7 +111,7 @@ local function retrieve_syntax_state(incoming_syntax, state) end end end - return current_syntax, subsyntax_info, current_state, current_level + return current_syntax, subsyntax_info, current_pattern_idx, current_level end function tokenizer.tokenize(incoming_syntax, text, state) @@ -112,17 +123,51 @@ function tokenizer.tokenize(incoming_syntax, text, state) end 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) + + -- 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 -- continue trying to match the end pattern of a pair if we have a state set - if current_state > 0 then - local p = current_syntax.patterns[current_state] + if current_pattern_idx > 0 then + local p = current_syntax.patterns[current_pattern_idx] local s, e = find_non_escaped(text, p.pattern[2], i, p.pattern[3]) local cont = true -- 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 local ss, se = find_non_escaped( text, @@ -130,17 +175,22 @@ function tokenizer.tokenize(incoming_syntax, text, state) i, 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 push_token(res, p.type, text:sub(i, ss - 1)) i = ss cont = false end end + -- If we don't have any concerns about syntax delimiters, + -- continue on as normal. if cont then if s then push_token(res, p.type, text:sub(i, e)) - current_state = 0 - state = bit32.replace(state, 0, current_level*8, 8) + set_subsyntax_pattern_idx(0) i = e + 1 else push_token(res, p.type, text:sub(i)) @@ -148,7 +198,9 @@ function tokenizer.tokenize(incoming_syntax, text, state) 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 local s, e = find_non_escaped( text, @@ -158,11 +210,8 @@ function tokenizer.tokenize(incoming_syntax, text, state) ) if s then push_token(res, subsyntax_info.type, text:sub(i, e)) - current_level = current_level - 1 - -- Zero out the state above us, as well as our new current state. - state = bit32.replace(state, 0, current_level*8, 16) - current_syntax, subsyntax_info, current_state, current_level = - retrieve_syntax_state(incoming_syntax, state) + -- On finding unescaped delimiter, pop it. + pop_subsyntax() i = e + 1 end end @@ -180,20 +229,14 @@ function tokenizer.tokenize(incoming_syntax, text, state) -- update state if this was a start|end pattern pair if type(p.pattern) == "table" then - state = bit32.replace(state, n, current_level*8, 8) - -- If we've found a new subsyntax, bump our level, and set the - -- appropriate variables. + -- If we have a subsyntax, push that onto the subsyntax stack. if p.syntax then - current_level = current_level + 1 - subsyntax_info = p - current_syntax = type(p.syntax) == "table" and - p.syntax or syntax.get(p.syntax) - current_state = 0 - else - current_state = n + push_subsyntax(p, n) + else + set_subsyntax_pattern_idx(n) end end - + -- move cursor past this token i = fin + 1 matched = true diff --git a/data/fonts/FiraSans-Medium.ttf b/data/fonts/FiraSans-Medium.ttf new file mode 100644 index 00000000..fb9c257c Binary files /dev/null and b/data/fonts/FiraSans-Medium.ttf differ diff --git a/data/fonts/FiraSans-Regular.ttf b/data/fonts/FiraSans-Regular.ttf new file mode 100644 index 00000000..6b288649 Binary files /dev/null and b/data/fonts/FiraSans-Regular.ttf differ diff --git a/data/fonts/JetBrainsMono-Regular.ttf b/data/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 00000000..5e3aa31d Binary files /dev/null and b/data/fonts/JetBrainsMono-Regular.ttf differ diff --git a/data/fonts/font.ttf b/data/fonts/font.ttf deleted file mode 100644 index 2b6392ff..00000000 Binary files a/data/fonts/font.ttf and /dev/null differ diff --git a/data/fonts/monospace.ttf b/data/fonts/monospace.ttf deleted file mode 100644 index 5919b5d1..00000000 Binary files a/data/fonts/monospace.ttf and /dev/null differ diff --git a/data/plugins/language_lua.lua b/data/plugins/language_lua.lua index 168ea0ce..5df3d29f 100644 --- a/data/plugins/language_lua.lua +++ b/data/plugins/language_lua.lua @@ -6,21 +6,28 @@ syntax.add { headers = "^#!.*[ /]lua", comment = "--", patterns = { - { pattern = { '"', '"', '\\' }, type = "string" }, - { pattern = { "'", "'", '\\' }, type = "string" }, - { pattern = { "%[%[", "%]%]" }, type = "string" }, - { pattern = { "%-%-%[%[", "%]%]"}, type = "comment" }, - { pattern = "%-%-.-\n", type = "comment" }, - { pattern = "-?0x%x+", type = "number" }, - { pattern = "-?%d+[%d%.eE]*", type = "number" }, - { pattern = "-?%.?%d+", type = "number" }, - { pattern = "<%a+>", type = "keyword2" }, - { pattern = "%.%.%.?", type = "operator" }, - { pattern = "[<>~=]=", type = "operator" }, - { pattern = "[%+%-=/%*%^%%#<>]", type = "operator" }, - { pattern = "[%a_][%w_]*%s*%f[(\"{]", type = "function" }, - { pattern = "[%a_][%w_]*", type = "symbol" }, - { pattern = "::[%a_][%w_]*::", type = "function" }, + { pattern = { '"', '"', '\\' }, type = "string" }, + { pattern = { "'", "'", '\\' }, type = "string" }, + { pattern = { "%[%[", "%]%]" }, type = "string" }, + { pattern = { "%-%-%[%[", "%]%]"}, type = "comment" }, + { pattern = "%-%-.-\n", type = "comment" }, + { pattern = "0x%x+%.%x*[pP][-+]?%d+", type = "number" }, + { pattern = "0x%x+%.%x*", type = "number" }, + { pattern = "0x%.%x+[pP][-+]?%d+", type = "number" }, + { pattern = "0x%.%x+", type = "number" }, + { pattern = "0x%x+[pP][-+]?%d+", type = "number" }, + { pattern = "0x%x+", type = "number" }, + { pattern = "%d%.%d*[eE][-+]?%d+", type = "number" }, + { pattern = "%d%.%d*", type = "number" }, + { pattern = "%.?%d*[eE][-+]?%d+", type = "number" }, + { 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 = { ["if"] = "keyword", diff --git a/data/user/init.lua b/data/user/init.lua deleted file mode 100644 index a1c6ddd3..00000000 --- a/data/user/init.lua +++ /dev/null @@ -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 diff --git a/doc/contributors.md b/doc/contributors.md new file mode 100644 index 00000000..4313e465 --- /dev/null +++ b/doc/contributors.md @@ -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 diff --git a/doc/default-keymap.md b/doc/default-keymap.md new file mode 100644 index 00000000..72bca33c --- /dev/null +++ b/doc/default-keymap.md @@ -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 | diff --git a/doc/licenses.md b/doc/licenses.md new file mode 100644 index 00000000..8005c4a7 --- /dev/null +++ b/doc/licenses.md @@ -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. diff --git a/doc/usage.md b/doc/usage.md index b42917d8..d92ea707 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -149,6 +149,8 @@ local keymap = require "core.keymap" keymap.add { ["ctrl+q"] = "core:quit" } ``` +A list of default mappings can be viewed [here](./default-keymap.md). + ## Plugins Plugins in lite are normal lua modules and are treated as such — no diff --git a/meson.build b/meson.build index f0404c5d..36fc8fe5 100644 --- a/meson.build +++ b/meson.build @@ -29,8 +29,10 @@ endif lite_cargs = [] if get_option('portable') + lite_docdir = 'doc' lite_datadir = 'data' else + lite_docdir = 'share/doc' lite_datadir = 'share/lite-xl' endif @@ -39,10 +41,17 @@ foreach data_module : ['core', 'fonts', 'plugins', 'colors'] install_subdir('data' / data_module , install_dir : lite_datadir) endforeach +foreach file : ['usage.md', 'licenses.md', 'contributors.md', 'default-keymap.md'] + install_data('doc' / file, install_dir : lite_docdir) +endforeach + lite_link_args = [] if cc.get_id() == 'gcc' and get_option('buildtype') == 'release' lite_link_args += ['-static-libgcc', '-static-libstdc++'] endif +if host_machine.system() == 'darwin' + lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation'] +endif lite_rc = [] if host_machine.system() == 'windows' diff --git a/src/api/renderer.c b/src/api/renderer.c index c6883a95..8dc13ada 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -38,13 +38,13 @@ static int f_get_size(lua_State *L) { static int f_begin_frame(lua_State *L) { - rencache_begin_frame(); + rencache_begin_frame(L); return 0; } static int f_end_frame(lua_State *L) { - rencache_end_frame(); + rencache_end_frame(L); return 0; } @@ -90,7 +90,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { 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); return 1; } diff --git a/src/api/renderer_font.c b/src/api/renderer_font.c index 3d85597b..47ea97a3 100644 --- a/src/api/renderer_font.c +++ b/src/api/renderer_font.c @@ -1,3 +1,6 @@ +#include +#include + #include "api.h" #include "fontdesc.h" #include "renderer.h" @@ -62,7 +65,7 @@ static int f_set_tab_size(lua_State *L) { static int f_gc(lua_State *L) { FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - rencache_free_font(self); + font_desc_free(self); return 0; } diff --git a/src/main.c b/src/main.c index ca332dd6..b0eb50d6 100644 --- a/src/main.c +++ b/src/main.c @@ -159,11 +159,6 @@ init_lua: enable_momentum_scroll(); #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 = \ "local core\n" "xpcall(function()\n" diff --git a/src/rencache.c b/src/rencache.c index 1f3e42c2..31165e90 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -1,6 +1,8 @@ #include #include #include + +#include #include "rencache.h" /* a cache over the software renderer -- all drawing operations are stored as @@ -14,7 +16,7 @@ #define COMMAND_BUF_SIZE (1024 * 512) #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 { int8_t type; @@ -30,6 +32,15 @@ typedef struct { char text[0]; } 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_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 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 */ #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) { Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE); 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, 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.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; Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); 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 */ int w, h; ren_get_size(&w, &h); @@ -182,6 +214,7 @@ void rencache_begin_frame(void) { screen_rect.height = h; 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 */ Command *cmd = NULL; RenRect cr = screen_rect; @@ -253,7 +286,6 @@ void rencache_end_frame(void) { } /* redraw updated regions */ - bool has_free_commands = false; for (int i = 0; i < rect_count; i++) { /* draw */ RenRect r = rect_buf[i]; @@ -262,9 +294,6 @@ void rencache_end_frame(void) { cmd = NULL; while (next_command(&cmd)) { switch (cmd->type) { - case FREE_FONT: - has_free_commands = true; - break; case SET_CLIP: ren_set_clip_rect(intersect_rects(cmd->rect, r)); break; @@ -296,16 +325,6 @@ void rencache_end_frame(void) { 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 */ unsigned *tmp = cells; cells = cells_prev; @@ -313,6 +332,3 @@ void rencache_end_frame(void) { command_buf_idx = 0; } -void rencache_clear() { - command_buf_idx = 0; -} diff --git a/src/rencache.h b/src/rencache.h index 987b6855..1d0f45a6 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -2,17 +2,16 @@ #define RENCACHE_H #include +#include #include "renderer.h" void rencache_show_debug(bool enable); -void rencache_free_font(FontDesc *font_desc); void rencache_set_clip_rect(RenRect rect); 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); void rencache_invalidate(void); -void rencache_begin_frame(void); -void rencache_end_frame(void); -void rencache_clear(); +void rencache_begin_frame(lua_State *L); +void rencache_end_frame(lua_State *L); #endif