Compare commits
30 Commits
amiga2.1
...
os4-1.16.1
Author | SHA1 | Date |
---|---|---|
George Sokianos | 79f0ac4f80 | |
George Sokianos | ce62b9c8da | |
George Sokianos | fb3d36da43 | |
George Sokianos | 94807d505c | |
George Sokianos | 44cd036b7a | |
George Sokianos | 3c7da82ad2 | |
George Sokianos | d30a9622a5 | |
George Sokianos | 2fdf19ec49 | |
George Sokianos | 9f656f8ab4 | |
George Sokianos | 32a3d4b933 | |
George Sokianos | c5309e04d6 | |
George Sokianos | 1c3f766e6b | |
George Sokianos | de6c0fd575 | |
George Sokianos | 7bd164b17e | |
George Sokianos | de3e4815ee | |
George Sokianos | b5d4f3f0f8 | |
George Sokianos | a788ac871b | |
George Sokianos | 1f27d6f923 | |
George Sokianos | 5af782b884 | |
George Sokianos | 95ece12d74 | |
George Sokianos | 03f2818657 | |
George Sokianos | 689901daca | |
George Sokianos | 4499f1f111 | |
George Sokianos | 14d813cea1 | |
George Sokianos | 2c711138d7 | |
George Sokianos | ff535843e8 | |
George Sokianos | fdd2f3af33 | |
George Sokianos | 7c85530e92 | |
George Sokianos | e13efe91b4 | |
George Sokianos | c155f797cf |
|
@ -7,3 +7,5 @@ build*
|
|||
subprojects/lua
|
||||
subprojects/libagg
|
||||
sybprojects/lua
|
||||
lite
|
||||
.config/
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Project: Lite XL
|
||||
#
|
||||
# Created on: 26-12-2021
|
||||
#
|
||||
.PHONY: build release
|
||||
|
||||
default: build
|
||||
|
||||
build:
|
||||
@sh os4build.sh
|
||||
|
||||
release:
|
||||
mkdir -p release/LiteXL
|
||||
cp release_files/* release/LiteXL/ -r
|
||||
mv release/LiteXL/LiteXL.info release/
|
||||
cp data release/LiteXL/ -r
|
||||
cp doc release/LiteXL/ -r
|
||||
cp lite release/LiteXL/
|
||||
strip release/LiteXL/lite
|
||||
cp README.md release/LiteXL/
|
||||
cp README_OS4.md release/LiteXL/
|
||||
cp LICENSE release/LiteXL/
|
||||
lha -aeqr3 a LiteXL.lha release/
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
# Lite XL for AmigaOS 4.1 FE
|
||||
|
||||
Lite XL is coming to AmigaOS 4.1.
|
||||
|
||||
A few things are not quite working. Please have a look at the "Known issues"
|
||||
section below.
|
||||
|
||||
## Installation
|
||||
You can extract the Lite XL archive wherever you want and run the *lite*
|
||||
editor.
|
||||
|
||||
## Configuration folder
|
||||
This editor creates a `.config` folder where the configuration is saved, as
|
||||
well as plugins, themes etc.. By default this AmigaOS 4.1 FE version uses the
|
||||
executable folder, but if you want to ovveride it, create an ENV variable
|
||||
named `HOME` and set there your path.
|
||||
|
||||
You can check if there is one already set by executing the following command
|
||||
in a shell
|
||||
```
|
||||
GetEnv HOME
|
||||
```
|
||||
If there is one set, then you will see the path at the output.
|
||||
|
||||
Otherwise, you can set your home path be executing the following command.
|
||||
Change the path to the one of your preference.
|
||||
```
|
||||
SetEnv SAVE HOME "Sys:home/"
|
||||
```
|
||||
|
||||
|
||||
## Know issues
|
||||
You can find the known issues at
|
||||
https://git.walkero.gr/walkero/lite-xl/issues
|
||||
|
||||
|
||||
# Changelog
|
||||
|
||||
## [1.16.12.7] - future
|
||||
## Added
|
||||
- Added config.scroll_past_end that when its true lets the user scroll
|
||||
further than the end of the file. By default is set to true.
|
||||
- Added "SDL_RENDERER_ACCELERATED" and "SDL_RENDERER_PRESENTVSYNC" on
|
||||
SDL_CreateRenderer() since this reduces the CPU usage when the user
|
||||
scrolls and seems to work pretty good on my systems (X5000, A1222 and
|
||||
microAmigaOne). This is exeprimental. If this brings problems on your
|
||||
system, you can disable them using SDL ENV variable, like below:
|
||||
setenv SDL_RENDER_VSYNC 0
|
||||
setenv SDL_RENDER_DRIVER "software"
|
||||
|
||||
## [1.16.12.6] - 2022-01-04
|
||||
### Fixed
|
||||
- Fixed a problem introduced in previous version when LiteXL was executed
|
||||
from the root path of a partition or from ram disk (#13)
|
||||
|
||||
## [1.16.12.5] - 2022-01-03
|
||||
### Changed
|
||||
- Changed the Gfx memory leak solution to a fix that was applied by the
|
||||
editor development team on Lua scripts at a later version. Less custom
|
||||
code for AmigaOS 4 port.
|
||||
- Now, when return from fullscreen, there is no extra header visible
|
||||
at the top of the window content
|
||||
|
||||
### Fixed
|
||||
- Fixed the assertion error and crash when the window is resized (#2)
|
||||
- Fixed the resolution on fullscreen toggle to be like the workbench (#4)
|
||||
- Fixed loading the current folder from terminal using the dot, like
|
||||
`lite .` or without it (#3)
|
||||
|
||||
## [1.16.12.4] - 2021-12-31
|
||||
### Fixed
|
||||
- Fixed the Gfx memory leak. Now LiteXL frees the reserved memory from the
|
||||
gfx card.
|
||||
|
||||
## [1.16.12.3] - 2021-12-29
|
||||
### Changed
|
||||
- Compiled with an experimental version of the latest Anti-Grain Geometry
|
||||
library. This is might have issues and crash LiteXL
|
||||
|
||||
## [1.16.12.2] - 2021-12-26
|
||||
### Added
|
||||
- Added Amiga version. This version of LiteXL is based on v1.16.12 source code
|
||||
which will not change. I will use the fourth digit to distinguish different
|
||||
AmigaOS 4 releases, until a new port of the latest available source (v2.x)
|
||||
is made.
|
||||
|
||||
### Fixed
|
||||
- The keyboard shortcuts are now working
|
||||
|
||||
### Changed
|
||||
- Now the `HOME` ENV variable is optional. If this is not set, the LiteXL
|
||||
folder will be used to create user's `.config` folder
|
||||
|
Binary file not shown.
|
@ -6,6 +6,7 @@ local LogView = require "core.logview"
|
|||
|
||||
|
||||
local fullscreen = false
|
||||
local restore_title_view = false
|
||||
|
||||
local function suggest_directory(text)
|
||||
text = common.home_expand(text)
|
||||
|
@ -27,9 +28,12 @@ command.add(nil, {
|
|||
|
||||
["core:toggle-fullscreen"] = function()
|
||||
fullscreen = not fullscreen
|
||||
if fullscreen then
|
||||
restore_title_view = core.title_view.visible
|
||||
end
|
||||
system.set_window_mode(fullscreen and "fullscreen" or "normal")
|
||||
core.show_title_bar(not fullscreen)
|
||||
core.title_view:configure_hit_test(not fullscreen)
|
||||
core.show_title_bar(not fullscreen and restore_title_view)
|
||||
core.title_view:configure_hit_test(not fullscreen and restore_title_view)
|
||||
end,
|
||||
|
||||
["core:reload-module"] = function()
|
||||
|
|
|
@ -24,13 +24,16 @@ local function get_indent_string()
|
|||
end
|
||||
|
||||
|
||||
local function doc_multiline_selection(sort)
|
||||
local line1, col1, line2, col2, swap = doc():get_selection(sort)
|
||||
if line2 > line1 and col2 == 1 then
|
||||
line2 = line2 - 1
|
||||
col2 = #doc().lines[line2]
|
||||
local function doc_multiline_selections(sort)
|
||||
local iter, state, idx, line1, col1, line2, col2 = doc():get_selections(sort)
|
||||
return function()
|
||||
idx, line1, col1, line2, col2 = iter(state, idx)
|
||||
if idx and line2 > line1 and col2 == 1 then
|
||||
line2 = line2 - 1
|
||||
col2 = #doc().lines[line2]
|
||||
end
|
||||
return idx, line1, col1, line2, col2
|
||||
end
|
||||
return line1, col1, line2, col2, swap
|
||||
end
|
||||
|
||||
local function append_line_if_last_line(line)
|
||||
|
@ -39,7 +42,6 @@ local function append_line_if_last_line(line)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
local function save(filename)
|
||||
doc():save(filename and core.normalize_to_project_dir(filename))
|
||||
local saved_filename = doc().filename
|
||||
|
@ -47,55 +49,31 @@ 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)
|
||||
local function cut_or_copy(delete)
|
||||
local full_text = ""
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections() do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
local text = doc():get_text(line1, col1, line2, col2)
|
||||
if delete then
|
||||
doc():delete_to_cursor(idx, 0)
|
||||
end
|
||||
full_text = full_text == "" and text or (full_text .. "\n" .. text)
|
||||
doc().cursor_clipboard[idx] = text
|
||||
else
|
||||
doc().cursor_clipboard[idx] = ""
|
||||
end
|
||||
end
|
||||
system.set_clipboard(full_text)
|
||||
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_multiline_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)
|
||||
local function split_cursor(direction)
|
||||
local new_cursors = {}
|
||||
for _, line1, col1 in doc():get_selections() do
|
||||
if line1 + direction >= 1 and line1 + direction <= #doc().lines then
|
||||
table.insert(new_cursors, { line1 + direction, col1 })
|
||||
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
|
||||
for i,v in ipairs(new_cursors) do doc():add_selection(v[1], v[2]) end
|
||||
end
|
||||
|
||||
local commands = {
|
||||
|
@ -108,65 +86,66 @@ local commands = {
|
|||
end,
|
||||
|
||||
["doc:cut"] = function()
|
||||
if doc():has_selection() then
|
||||
local text = doc():get_text(doc():get_selection())
|
||||
system.set_clipboard(text)
|
||||
doc():delete_to(0)
|
||||
end
|
||||
cut_or_copy(true)
|
||||
end,
|
||||
|
||||
["doc:copy"] = function()
|
||||
if doc():has_selection() then
|
||||
local text = doc():get_text(doc():get_selection())
|
||||
system.set_clipboard(text)
|
||||
end
|
||||
cut_or_copy(false)
|
||||
end,
|
||||
|
||||
["doc:paste"] = function()
|
||||
doc():text_input(system.get_clipboard():gsub("\r", ""))
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections() do
|
||||
local value = doc().cursor_clipboard[idx] or system.get_clipboard()
|
||||
doc():text_input(value:gsub("\r", ""), idx)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:newline"] = function()
|
||||
local line, col = doc():get_selection()
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
if col <= #indent then
|
||||
indent = indent:sub(#indent + 2 - col)
|
||||
for idx, line, col in doc():get_selections(false, true) do
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
if col <= #indent then
|
||||
indent = indent:sub(#indent + 2 - col)
|
||||
end
|
||||
doc():text_input("\n" .. indent, idx)
|
||||
end
|
||||
doc():text_input("\n" .. indent)
|
||||
end,
|
||||
|
||||
["doc:newline-below"] = function()
|
||||
local line = doc():get_selection()
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
doc():insert(line, math.huge, "\n" .. indent)
|
||||
doc():set_selection(line + 1, math.huge)
|
||||
for idx, line in doc():get_selections(false, true) do
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
doc():insert(line, math.huge, "\n" .. indent)
|
||||
doc():set_selections(idx, line + 1, math.huge)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:newline-above"] = function()
|
||||
local line = doc():get_selection()
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
doc():insert(line, 1, indent .. "\n")
|
||||
doc():set_selection(line, math.huge)
|
||||
for idx, line in doc():get_selections(false, true) do
|
||||
local indent = doc().lines[line]:match("^[\t ]*")
|
||||
doc():insert(line, 1, indent .. "\n")
|
||||
doc():set_selections(idx, line, math.huge)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:delete"] = function()
|
||||
local line, col = doc():get_selection()
|
||||
if not doc():has_selection() and doc().lines[line]:find("^%s*$", col) then
|
||||
doc():remove(line, col, line, math.huge)
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections() do
|
||||
if line1 == line2 and col1 == col2 and doc().lines[line1]:find("^%s*$", col1) then
|
||||
doc():remove(line1, col1, line1, math.huge)
|
||||
end
|
||||
doc():delete_to_cursor(idx, translate.next_char)
|
||||
end
|
||||
doc():delete_to(translate.next_char)
|
||||
end,
|
||||
|
||||
["doc:backspace"] = function()
|
||||
local line, col = doc():get_selection()
|
||||
if not doc():has_selection() then
|
||||
local text = doc():get_text(line, 1, line, col)
|
||||
if #text >= config.indent_size and text:find("^ *$") then
|
||||
doc():delete_to(0, -config.indent_size)
|
||||
return
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections() do
|
||||
if line1 == line2 and col1 == col2 then
|
||||
local text = doc():get_text(line1, 1, line1, col1)
|
||||
if #text >= config.indent_size and text:find("^ *$") then
|
||||
doc():delete_to_cursor(idx, 0, -config.indent_size)
|
||||
return
|
||||
end
|
||||
end
|
||||
doc():delete_to_cursor(idx, translate.previous_char)
|
||||
end
|
||||
doc():delete_to(translate.previous_char)
|
||||
end,
|
||||
|
||||
["doc:select-all"] = function()
|
||||
|
@ -178,76 +157,103 @@ local commands = {
|
|||
doc():set_selection(line, col)
|
||||
end,
|
||||
|
||||
|
||||
["doc:indent"] = function()
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2)
|
||||
if l1 then
|
||||
doc():set_selections(idx, l1, c1, l2, c2)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:select-lines"] = function()
|
||||
local line1, _, line2, _, swap = doc():get_selection(true)
|
||||
append_line_if_last_line(line2)
|
||||
doc():set_selection(line1, 1, line2 + 1, 1, swap)
|
||||
for idx, line1, _, line2 in doc():get_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
doc():set_selections(idx, line1, 1, line2 + 1, 1, swap)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:select-word"] = function()
|
||||
local line1, col1 = doc():get_selection(true)
|
||||
local line1, col1 = translate.start_of_word(doc(), line1, col1)
|
||||
local line2, col2 = translate.end_of_word(doc(), line1, col1)
|
||||
doc():set_selection(line2, col2, line1, col1)
|
||||
for idx, line1, col1 in doc():get_selections(true) do
|
||||
local line1, col1 = translate.start_of_word(doc(), line1, col1)
|
||||
local line2, col2 = translate.end_of_word(doc(), line1, col1)
|
||||
doc():set_selections(idx, line2, col2, line1, col1)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:join-lines"] = function()
|
||||
local line1, _, line2 = doc():get_selection(true)
|
||||
if line1 == line2 then line2 = line2 + 1 end
|
||||
local text = doc():get_text(line1, 1, line2, math.huge)
|
||||
text = text:gsub("(.-)\n[\t ]*", function(x)
|
||||
return x:find("^%s*$") and x or x .. " "
|
||||
end)
|
||||
doc():insert(line1, 1, text)
|
||||
doc():remove(line1, #text + 1, line2, math.huge)
|
||||
if doc():has_selection() then
|
||||
doc():set_selection(line1, math.huge)
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
|
||||
if line1 == line2 then line2 = line2 + 1 end
|
||||
local text = doc():get_text(line1, 1, line2, math.huge)
|
||||
text = text:gsub("(.-)\n[\t ]*", function(x)
|
||||
return x:find("^%s*$") and x or x .. " "
|
||||
end)
|
||||
doc():insert(line1, 1, text)
|
||||
doc():remove(line1, #text + 1, line2, math.huge)
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
doc():set_selections(idx, line1, math.huge)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:indent"] = function()
|
||||
indent_text()
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2)
|
||||
if l1 then
|
||||
doc():set_selections(idx, l1, c1, l2, c2)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:unindent"] = function()
|
||||
indent_text(true)
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
local l1, c1, l2, c2 = doc():indent_text(true, line1, col1, line2, col2)
|
||||
if l1 then
|
||||
doc():set_selections(idx, l1, c1, l2, c2)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:duplicate-lines"] = function()
|
||||
local line1, col1, line2, col2, swap = doc_multiline_selection(true)
|
||||
append_line_if_last_line(line2)
|
||||
local text = doc():get_text(line1, 1, line2 + 1, 1)
|
||||
doc():insert(line2 + 1, 1, text)
|
||||
local n = line2 - line1 + 1
|
||||
doc():set_selection(line1 + n, col1, line2 + n, col2, swap)
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
local text = doc():get_text(line1, 1, line2 + 1, 1)
|
||||
doc():insert(line2 + 1, 1, text)
|
||||
local n = line2 - line1 + 1
|
||||
doc():set_selections(idx, line1 + n, col1, line2 + n, col2, swap)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:delete-lines"] = function()
|
||||
local line1, col1, line2 = doc_multiline_selection(true)
|
||||
append_line_if_last_line(line2)
|
||||
doc():remove(line1, 1, line2 + 1, 1)
|
||||
doc():set_selection(line1, col1)
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
doc():remove(line1, 1, line2 + 1, 1)
|
||||
doc():set_selections(idx, line1, col1)
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:move-lines-up"] = function()
|
||||
local line1, col1, line2, col2, swap = doc_multiline_selection(true)
|
||||
append_line_if_last_line(line2)
|
||||
if line1 > 1 then
|
||||
local text = doc().lines[line1 - 1]
|
||||
doc():insert(line2 + 1, 1, text)
|
||||
doc():remove(line1 - 1, 1, line1, 1)
|
||||
doc():set_selection(line1 - 1, col1, line2 - 1, col2, swap)
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
if line1 > 1 then
|
||||
local text = doc().lines[line1 - 1]
|
||||
doc():insert(line2 + 1, 1, text)
|
||||
doc():remove(line1 - 1, 1, line1, 1)
|
||||
doc():set_selections(idx, line1 - 1, col1, line2 - 1, col2)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["doc:move-lines-down"] = function()
|
||||
local line1, col1, line2, col2, swap = doc_multiline_selection(true)
|
||||
append_line_if_last_line(line2 + 1)
|
||||
if line2 < #doc().lines then
|
||||
local text = doc().lines[line2 + 1]
|
||||
doc():remove(line2 + 1, 1, line2 + 2, 1)
|
||||
doc():insert(line1, 1, text)
|
||||
doc():set_selection(line1 + 1, col1, line2 + 1, col2, swap)
|
||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||
append_line_if_last_line(line2 + 1)
|
||||
if line2 < #doc().lines then
|
||||
local text = doc().lines[line2 + 1]
|
||||
doc():remove(line2 + 1, 1, line2 + 2, 1)
|
||||
doc():insert(line1, 1, text)
|
||||
doc():set_selections(idx, line1 + 1, col1, line2 + 1, col2)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
@ -256,28 +262,29 @@ local commands = {
|
|||
if not comment then return end
|
||||
local indentation = get_indent_string()
|
||||
local comment_text = comment .. " "
|
||||
local line1, _, line2 = doc_multiline_selection(true)
|
||||
local uncomment = true
|
||||
local start_offset = math.huge
|
||||
for line = line1, line2 do
|
||||
local text = doc().lines[line]
|
||||
local s = text:find("%S")
|
||||
local cs, ce = text:find(comment_text, s, true)
|
||||
if s and cs ~= s then
|
||||
uncomment = false
|
||||
start_offset = math.min(start_offset, s)
|
||||
end
|
||||
end
|
||||
for line = line1, line2 do
|
||||
local text = doc().lines[line]
|
||||
local s = text:find("%S")
|
||||
if uncomment then
|
||||
for idx, line1, _, line2 in doc_multiline_selections(true) do
|
||||
local uncomment = true
|
||||
local start_offset = math.huge
|
||||
for line = line1, line2 do
|
||||
local text = doc().lines[line]
|
||||
local s = text:find("%S")
|
||||
local cs, ce = text:find(comment_text, s, true)
|
||||
if ce then
|
||||
doc():remove(line, cs, line, ce + 1)
|
||||
if s and cs ~= s then
|
||||
uncomment = false
|
||||
start_offset = math.min(start_offset, s)
|
||||
end
|
||||
end
|
||||
for line = line1, line2 do
|
||||
local text = doc().lines[line]
|
||||
local s = text:find("%S")
|
||||
if uncomment then
|
||||
local cs, ce = text:find(comment_text, s, true)
|
||||
if ce then
|
||||
doc():remove(line, cs, line, ce + 1)
|
||||
end
|
||||
elseif s then
|
||||
doc():insert(line, start_offset, comment_text)
|
||||
end
|
||||
elseif s then
|
||||
doc():insert(line, start_offset, comment_text)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -363,6 +370,32 @@ local commands = {
|
|||
end
|
||||
end, common.path_suggest)
|
||||
end,
|
||||
|
||||
|
||||
["file:delete"] = function()
|
||||
local filename = doc().abs_filename
|
||||
if not filename then
|
||||
core.error("Cannot remove unsaved doc")
|
||||
return
|
||||
end
|
||||
for i,docview in ipairs(core.get_views_referencing_doc(doc())) do
|
||||
local node = core.root_view.root_node:get_node_for_view(docview)
|
||||
node:close_view(core.root_view, docview)
|
||||
end
|
||||
os.remove(filename)
|
||||
core.log("Removed \"%s\"", filename)
|
||||
end,
|
||||
|
||||
["doc:create-cursor-previous-line"] = function()
|
||||
split_cursor(-1)
|
||||
doc():merge_cursors()
|
||||
end,
|
||||
|
||||
["doc:create-cursor-next-line"] = function()
|
||||
split_cursor(1)
|
||||
doc():merge_cursors()
|
||||
end
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -392,21 +425,21 @@ for name, fn in pairs(translations) do
|
|||
end
|
||||
|
||||
commands["doc:move-to-previous-char"] = function()
|
||||
if doc():has_selection() then
|
||||
local line, col = doc():get_selection(true)
|
||||
doc():set_selection(line, col)
|
||||
else
|
||||
doc():move_to(translate.previous_char)
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
doc():set_selections(idx, line1, col1)
|
||||
end
|
||||
end
|
||||
doc():move_to(translate.previous_char)
|
||||
end
|
||||
|
||||
commands["doc:move-to-next-char"] = function()
|
||||
if doc():has_selection() then
|
||||
local _, _, line, col = doc():get_selection(true)
|
||||
doc():set_selection(line, col)
|
||||
else
|
||||
doc():move_to(translate.next_char)
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
doc():set_selections(idx, line2, col2)
|
||||
end
|
||||
end
|
||||
doc():move_to(translate.next_char)
|
||||
end
|
||||
|
||||
command.add("core.docview", commands)
|
||||
|
|
|
@ -54,6 +54,26 @@ function common.color(str)
|
|||
end
|
||||
|
||||
|
||||
function common.splice(t, at, remove, insert)
|
||||
insert = insert or {}
|
||||
local offset = #insert - remove
|
||||
local old_len = #t
|
||||
if offset < 0 then
|
||||
for i = at - offset, old_len - offset do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
elseif offset > 0 then
|
||||
for i = old_len, at, -1 do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
end
|
||||
for i, item in ipairs(insert) do
|
||||
t[at + i - 1] = item
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function compare_score(a, b)
|
||||
return a.score > b.score
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ config.max_log_items = 80
|
|||
config.message_timeout = 5
|
||||
config.mouse_wheel_scroll = 50 * SCALE
|
||||
config.file_size_limit = 10
|
||||
config.scroll_past_end = true
|
||||
config.ignore_files = "^%."
|
||||
config.symbol_pattern = "[%a_][%w_]*"
|
||||
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
|
|
|
@ -16,26 +16,6 @@ local function split_lines(text)
|
|||
return res
|
||||
end
|
||||
|
||||
|
||||
local function splice(t, at, remove, insert)
|
||||
insert = insert or {}
|
||||
local offset = #insert - remove
|
||||
local old_len = #t
|
||||
if offset < 0 then
|
||||
for i = at - offset, old_len - offset do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
elseif offset > 0 then
|
||||
for i = old_len, at, -1 do
|
||||
t[i + offset] = t[i]
|
||||
end
|
||||
end
|
||||
for i, item in ipairs(insert) do
|
||||
t[at + i - 1] = item
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:new(filename)
|
||||
self:reset()
|
||||
if filename then
|
||||
|
@ -46,7 +26,8 @@ end
|
|||
|
||||
function Doc:reset()
|
||||
self.lines = { "\n" }
|
||||
self.selection = { a = { line=1, col=1 }, b = { line=1, col=1 } }
|
||||
self.selections = { 1, 1, 1, 1 }
|
||||
self.cursor_clipboard = {}
|
||||
self.undo_stack = { idx = 1 }
|
||||
self.redo_stack = { idx = 1 }
|
||||
self.clean_change_id = 1
|
||||
|
@ -126,45 +107,87 @@ function Doc:get_change_id()
|
|||
return self.undo_stack.idx
|
||||
end
|
||||
|
||||
-- Cursor section. Cursor indices are *only* valid during a get_selections() call.
|
||||
-- Cursors will always be iterated in order from top to bottom. Through normal operation
|
||||
-- curors can never swap positions; only merge or split, or change their position in cursor
|
||||
-- order.
|
||||
function Doc:get_selection(sort)
|
||||
local idx, line1, col1, line2, col2 = self:get_selections(sort)({ self.selections, sort }, 0)
|
||||
return line1, col1, line2, col2, sort
|
||||
end
|
||||
|
||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||
assert(not line2 == not col2, "expected 2 or 4 arguments")
|
||||
function Doc:has_selection()
|
||||
local line1, col1, line2, col2 = self:get_selection(false)
|
||||
return line1 ~= line2 or col1 ~= col2
|
||||
end
|
||||
|
||||
function Doc:sanitize_selection()
|
||||
for idx, line1, col1, line2, col2 in self:get_selections() do
|
||||
self:set_selections(idx, line1, col1, line2, col2)
|
||||
end
|
||||
end
|
||||
|
||||
local function sort_positions(line1, col1, line2, col2)
|
||||
if line1 > line2 or line1 == line2 and col1 > col2 then
|
||||
return line2, col2, line1, col1
|
||||
end
|
||||
return line1, col1, line2, col2
|
||||
end
|
||||
|
||||
function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
|
||||
assert(not line2 == not col2, "expected 3 or 5 arguments")
|
||||
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2 or line1, col2 or col1)
|
||||
self.selection.a.line, self.selection.a.col = line1, col1
|
||||
self.selection.b.line, self.selection.b.col = line2, col2
|
||||
common.splice(self.selections, (idx - 1)*4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
|
||||
end
|
||||
|
||||
|
||||
local function sort_positions(line1, col1, line2, col2)
|
||||
if line1 > line2
|
||||
or line1 == line2 and col1 > col2 then
|
||||
return line2, col2, line1, col1, true
|
||||
function Doc:add_selection(line1, col1, line2, col2, swap)
|
||||
local l1, c1 = sort_positions(line1, col1, line2 or line1, col2 or col1)
|
||||
local target = #self.selections / 4 + 1
|
||||
for idx, tl1, tc1 in self:get_selections(true) do
|
||||
if l1 < tl1 or l1 == tl1 and c1 < tc1 then
|
||||
target = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
return line1, col1, line2, col2, false
|
||||
self:set_selections(target, line1, col1, line2, col2, swap, 0)
|
||||
end
|
||||
|
||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||
self.selections, self.cursor_clipboard = {}, {}
|
||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||
end
|
||||
|
||||
function Doc:get_selection(sort)
|
||||
local a, b = self.selection.a, self.selection.b
|
||||
if sort then
|
||||
return sort_positions(a.line, a.col, b.line, b.col)
|
||||
function Doc:merge_cursors(idx)
|
||||
for i = (idx or (#self.selections - 3)), (idx or 5), -4 do
|
||||
for j = 1, i - 4, 4 do
|
||||
if self.selections[i] == self.selections[j] and
|
||||
self.selections[i+1] == self.selections[j+1] then
|
||||
common.splice(self.selections, i, 4)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return a.line, a.col, b.line, b.col
|
||||
end
|
||||
|
||||
|
||||
function Doc:has_selection()
|
||||
local a, b = self.selection.a, self.selection.b
|
||||
return not (a.line == b.line and a.col == b.col)
|
||||
local function selection_iterator(invariant, idx)
|
||||
local target = invariant[3] and (idx*4 - 7) or (idx*4 + 1)
|
||||
if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end
|
||||
if invariant[2] then
|
||||
return idx+(invariant[3] and -1 or 1), sort_positions(unpack(invariant[1], target, target+4))
|
||||
else
|
||||
return idx+(invariant[3] and -1 or 1), unpack(invariant[1], target, target+4)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:sanitize_selection()
|
||||
self:set_selection(self:get_selection())
|
||||
-- If idx_reverse is true, it'll reverse iterate. If nil, or false, regular iterate.
|
||||
-- If a number, runs for exactly that iteration.
|
||||
function Doc:get_selections(sort_intra, idx_reverse)
|
||||
return selection_iterator, { self.selections, sort_intra, idx_reverse },
|
||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
|
||||
end
|
||||
|
||||
-- End of cursor seciton.
|
||||
|
||||
function Doc:sanitize_position(line, col)
|
||||
line = common.clamp(line, 1, #self.lines)
|
||||
|
@ -251,14 +274,11 @@ local function pop_undo(self, undo_stack, redo_stack, modified)
|
|||
if cmd.type == "insert" then
|
||||
local line, col, text = table.unpack(cmd)
|
||||
self:raw_insert(line, col, text, redo_stack, cmd.time)
|
||||
|
||||
elseif cmd.type == "remove" then
|
||||
local line1, col1, line2, col2 = table.unpack(cmd)
|
||||
self:raw_remove(line1, col1, line2, col2, redo_stack, cmd.time)
|
||||
|
||||
elseif cmd.type == "selection" then
|
||||
self.selection.a.line, self.selection.a.col = cmd[1], cmd[2]
|
||||
self.selection.b.line, self.selection.b.col = cmd[3], cmd[4]
|
||||
self.selections = { unpack(cmd) }
|
||||
end
|
||||
|
||||
modified = modified or (cmd.type ~= "selection")
|
||||
|
@ -288,11 +308,11 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
lines[#lines] = lines[#lines] .. after
|
||||
|
||||
-- splice lines into line array
|
||||
splice(self.lines, line, 1, lines)
|
||||
common.splice(self.lines, line, 1, lines)
|
||||
|
||||
-- push undo
|
||||
local line2, col2 = self:position_offset(line, col, #text)
|
||||
push_undo(undo_stack, time, "selection", self:get_selection())
|
||||
push_undo(undo_stack, time, "selection", unpack(self.selections))
|
||||
push_undo(undo_stack, time, "remove", line, col, line2, col2)
|
||||
|
||||
-- update highlighter and assure selection is in bounds
|
||||
|
@ -304,7 +324,7 @@ end
|
|||
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||
-- push undo
|
||||
local text = self:get_text(line1, col1, line2, col2)
|
||||
push_undo(undo_stack, time, "selection", self:get_selection())
|
||||
push_undo(undo_stack, time, "selection", unpack(self.selections))
|
||||
push_undo(undo_stack, time, "insert", line1, col1, text)
|
||||
|
||||
-- get line content before/after removed text
|
||||
|
@ -312,7 +332,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
|||
local after = self.lines[line2]:sub(col2)
|
||||
|
||||
-- splice line into line array
|
||||
splice(self.lines, line1, line2 - line1 + 1, { before .. after })
|
||||
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
|
||||
|
||||
-- update highlighter and assure selection is in bounds
|
||||
self.highlighter:invalidate(line1)
|
||||
|
@ -348,22 +368,20 @@ function Doc:redo()
|
|||
end
|
||||
|
||||
|
||||
function Doc:text_input(text)
|
||||
if self:has_selection() then
|
||||
self:delete_to()
|
||||
function Doc:text_input(text, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
self:delete_to_cursor(sidx)
|
||||
end
|
||||
self:insert(line1, col1, text)
|
||||
self:move_to_cursor(sidx, #text)
|
||||
end
|
||||
local line, col = self:get_selection()
|
||||
self:insert(line, col, text)
|
||||
self:move_to(#text)
|
||||
end
|
||||
|
||||
|
||||
function Doc:replace(fn)
|
||||
local line1, col1, line2, col2, swap
|
||||
local had_selection = self:has_selection()
|
||||
if had_selection then
|
||||
line1, col1, line2, col2, swap = self:get_selection(true)
|
||||
else
|
||||
local line1, col1, line2, col2 = self:get_selection(true)
|
||||
if line1 == line2 and col1 == col2 then
|
||||
line1, col1, line2, col2 = 1, 1, #self.lines, #self.lines[#self.lines]
|
||||
end
|
||||
local old_text = self:get_text(line1, col1, line2, col2)
|
||||
|
@ -371,38 +389,104 @@ function Doc:replace(fn)
|
|||
if old_text ~= new_text then
|
||||
self:insert(line2, col2, new_text)
|
||||
self:remove(line1, col1, line2, col2)
|
||||
if had_selection then
|
||||
if line1 == line2 and col1 == col2 then
|
||||
line2, col2 = self:position_offset(line1, col1, #new_text)
|
||||
self:set_selection(line1, col1, line2, col2, swap)
|
||||
self:set_selection(line1, col1, line2, col2)
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
|
||||
function Doc:delete_to(...)
|
||||
local line, col = self:get_selection(true)
|
||||
if self:has_selection() then
|
||||
self:remove(self:get_selection())
|
||||
else
|
||||
local line2, col2 = self:position_offset(line, col, ...)
|
||||
self:remove(line, col, line2, col2)
|
||||
line, col = sort_positions(line, col, line2, col2)
|
||||
function Doc:delete_to_cursor(idx, ...)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
self:remove(line1, col1, line2, col2)
|
||||
else
|
||||
local l2, c2 = self:position_offset(line1, col1, ...)
|
||||
self:remove(line1, col1, l2, c2)
|
||||
line1, col1 = sort_positions(line1, col1, l2, c2)
|
||||
end
|
||||
self:set_selections(sidx, line1, col1)
|
||||
end
|
||||
self:set_selection(line, col)
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:move_to_cursor(idx, ...)
|
||||
for sidx, line, col in self:get_selections(false, idx) do
|
||||
self:set_selections(sidx, self:position_offset(line, col, ...))
|
||||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
function Doc:select_to_cursor(idx, ...)
|
||||
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
||||
line, col = self:position_offset(line, col, ...)
|
||||
self:set_selections(sidx, line, col, line2, col2)
|
||||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
local function get_indent_string()
|
||||
if config.tab_type == "hard" then
|
||||
return "\t"
|
||||
end
|
||||
return string.rep(" ", config.indent_size)
|
||||
end
|
||||
|
||||
|
||||
function Doc:move_to(...)
|
||||
local line, col = self:get_selection()
|
||||
self:set_selection(self:position_offset(line, col, ...))
|
||||
-- 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
|
||||
|
||||
|
||||
function Doc:select_to(...)
|
||||
local line, col, line2, col2 = self:get_selection()
|
||||
line, col = self:position_offset(line, col, ...)
|
||||
self:set_selection(line, col, line2, col2)
|
||||
-- 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).
|
||||
function Doc:indent_text(unindent, line1, col1, line2, col2)
|
||||
local text = get_indent_string()
|
||||
local _, se = self.lines[line1]:find("^[ \t]+")
|
||||
local in_beginning_whitespace = col1 == 1 or (se and col1 <= se + 1)
|
||||
local has_selection = line1 ~= line2 or col1 ~= col2
|
||||
if unindent or has_selection or in_beginning_whitespace then
|
||||
local l1d, l2d = #self.lines[line1], #self.lines[line2]
|
||||
for line = line1, line2 do
|
||||
local e, rnded = get_line_indent(self.lines[line], unindent)
|
||||
self:remove(line, 1, line, (e or 0) + 1)
|
||||
self:insert(line, 1,
|
||||
unindent and rnded:sub(1, #rnded - #text) or rnded .. text)
|
||||
end
|
||||
l1d, l2d = #self.lines[line1] - l1d, #self.lines[line2] - l2d
|
||||
if (unindent or in_beginning_whitespace) and not has_selection then
|
||||
local start_cursor = (se and se + 1 or 1) + l1d or #(self.lines[line1])
|
||||
return line1, start_cursor, line2, start_cursor
|
||||
end
|
||||
return line1, col1 + l1d, line2, col2 + l2d
|
||||
end
|
||||
self:insert(line1, col1, text)
|
||||
return line1, col1 + #text, line1, col1 + #text
|
||||
end
|
||||
|
||||
-- For plugins to add custom actions of document change
|
||||
|
|
|
@ -97,6 +97,9 @@ end
|
|||
|
||||
|
||||
function DocView:get_scrollable_size()
|
||||
if not config.scroll_past_end then
|
||||
return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2
|
||||
end
|
||||
return self:get_line_height() * (#self.doc.lines - 1) + self.size.y
|
||||
end
|
||||
|
||||
|
@ -256,7 +259,11 @@ function DocView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
else
|
||||
local line, col = self:resolve_screen_position(x, y)
|
||||
self.doc:set_selection(mouse_selection(self.doc, clicks, line, col, line, col))
|
||||
if keymap.modkeys["ctrl"] then
|
||||
self.doc:add_selection(mouse_selection(self.doc, clicks, line, col, line, col))
|
||||
else
|
||||
self.doc:set_selection(mouse_selection(self.doc, clicks, line, col, line, col))
|
||||
end
|
||||
self.mouse_selecting = { line, col, clicks = clicks }
|
||||
end
|
||||
core.blink_reset()
|
||||
|
@ -276,7 +283,15 @@ function DocView:on_mouse_moved(x, y, ...)
|
|||
local l1, c1 = self:resolve_screen_position(x, y)
|
||||
local l2, c2 = table.unpack(self.mouse_selecting)
|
||||
local clicks = self.mouse_selecting.clicks
|
||||
self.doc:set_selection(mouse_selection(self.doc, clicks, l1, c1, l2, c2))
|
||||
if keymap.modkeys["ctrl"] then
|
||||
if l1 > l2 then l1, l2 = l2, l1 end
|
||||
self.doc.selections = { }
|
||||
for i = l1, l2 do
|
||||
self.doc:set_selections(i - l1 + 1, i, math.min(c1, #self.doc.lines[i]), i, math.min(c2, #self.doc.lines[i]))
|
||||
end
|
||||
else
|
||||
self.doc:set_selection(mouse_selection(self.doc, clicks, l1, c1, l2, c2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -340,24 +355,24 @@ end
|
|||
|
||||
|
||||
function DocView:draw_line_body(idx, x, y)
|
||||
local line, col = self.doc:get_selection()
|
||||
|
||||
-- draw selection if it overlaps this line
|
||||
local line1, col1, line2, col2 = self.doc:get_selection(true)
|
||||
if idx >= line1 and idx <= line2 then
|
||||
local text = self.doc.lines[idx]
|
||||
if line1 ~= idx then col1 = 1 end
|
||||
if line2 ~= idx then col2 = #text + 1 end
|
||||
local x1 = x + self:get_col_x_offset(idx, col1)
|
||||
local x2 = x + self:get_col_x_offset(idx, col2)
|
||||
local lh = self:get_line_height()
|
||||
renderer.draw_rect(x1, y, x2 - x1, lh, style.selection)
|
||||
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
|
||||
if idx >= line1 and idx <= line2 then
|
||||
local text = self.doc.lines[idx]
|
||||
if line1 ~= idx then col1 = 1 end
|
||||
if line2 ~= idx then col2 = #text + 1 end
|
||||
local x1 = x + self:get_col_x_offset(idx, col1)
|
||||
local x2 = x + self:get_col_x_offset(idx, col2)
|
||||
local lh = self:get_line_height()
|
||||
renderer.draw_rect(x1, y, x2 - x1, lh, style.selection)
|
||||
end
|
||||
end
|
||||
|
||||
-- draw line highlight if caret is on this line
|
||||
if config.highlight_current_line and not self.doc:has_selection()
|
||||
and line == idx and core.active_view == self then
|
||||
self:draw_line_highlight(x + self.scroll.x, y)
|
||||
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
|
||||
-- draw line highlight if caret is on this line
|
||||
if config.highlight_current_line and (line1 == line2 and col1 == col2)
|
||||
and line1 == idx and core.active_view == self then
|
||||
self:draw_line_highlight(x + self.scroll.x, y)
|
||||
end
|
||||
end
|
||||
|
||||
-- draw line's text
|
||||
|
@ -365,21 +380,25 @@ function DocView:draw_line_body(idx, x, y)
|
|||
|
||||
-- draw caret if it overlaps this line
|
||||
local T = config.blink_period
|
||||
if line == idx and core.active_view == self
|
||||
and (core.blink_timer - core.blink_start) % T < T / 2
|
||||
and system.window_has_focus() then
|
||||
local lh = self:get_line_height()
|
||||
local x1 = x + self:get_col_x_offset(line, col)
|
||||
renderer.draw_rect(x1, y, style.caret_width, lh, style.caret)
|
||||
for _, line, col in self.doc:get_selections() do
|
||||
if line == idx and core.active_view == self
|
||||
and (core.blink_timer - core.blink_start) % T < T / 2
|
||||
and system.window_has_focus() then
|
||||
local lh = self:get_line_height()
|
||||
local x1 = x + self:get_col_x_offset(line, col)
|
||||
renderer.draw_rect(x1, y, style.caret_width, lh, style.caret)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function DocView:draw_line_gutter(idx, x, y)
|
||||
local color = style.line_number
|
||||
local line1, _, line2, _ = self.doc:get_selection(true)
|
||||
if idx >= line1 and idx <= line2 then
|
||||
color = style.line_number2
|
||||
for _, line1, _, line2 in self.doc:get_selections(true) do
|
||||
if idx >= line1 and idx <= line2 then
|
||||
color = style.line_number2
|
||||
break
|
||||
end
|
||||
end
|
||||
local yoffset = self:get_line_text_y_offset()
|
||||
x = x + style.padding.x
|
||||
|
|
|
@ -59,9 +59,9 @@ end
|
|||
|
||||
|
||||
function core.reschedule_project_scan()
|
||||
if core.project_scan_thread_id then
|
||||
core.threads[core.project_scan_thread_id].wake = 0
|
||||
end
|
||||
-- if core.project_scan_thread_id then
|
||||
-- core.threads[core.project_scan_thread_id].wake = 0
|
||||
-- end
|
||||
end
|
||||
|
||||
|
||||
|
@ -512,6 +512,7 @@ function core.init()
|
|||
core.redraw = true
|
||||
core.visited_files = {}
|
||||
core.restart_request = false
|
||||
core.quite_request = false
|
||||
core.replacements = whitespace_replacements()
|
||||
|
||||
core.root_view = RootView()
|
||||
|
@ -535,7 +536,7 @@ function core.init()
|
|||
local plugins_success, plugins_refuse_list = core.load_plugins()
|
||||
|
||||
do
|
||||
local pdir, pname = project_dir_abs:match("(.*)[/\\\\](.*)")
|
||||
local pdir, pname = project_dir_abs:match("(.*)[:/\\\\](.*)")
|
||||
core.log("Opening project %q from directory %s", pname, pdir)
|
||||
end
|
||||
local got_project_error = not core.load_project_module()
|
||||
|
@ -668,7 +669,8 @@ local function quit_with_function(quit_fn, force)
|
|||
end
|
||||
|
||||
function core.quit(force)
|
||||
quit_with_function(os.exit, force)
|
||||
-- quit_with_function(os.exit, force)
|
||||
quit_with_function(function() core.quit_request = true end, force)
|
||||
end
|
||||
|
||||
|
||||
|
@ -1044,7 +1046,7 @@ function core.run()
|
|||
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 core.restart_request or core.quit_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
|
||||
|
|
|
@ -101,6 +101,8 @@ local function keymap_macos(keymap)
|
|||
["cmd+shift+end"] = "doc:select-to-end-of-doc",
|
||||
["shift+pageup"] = "doc:select-to-previous-page",
|
||||
["shift+pagedown"] = "doc:select-to-next-page",
|
||||
["cmd+shift+up"] = "doc:create-cursor-previous-line",
|
||||
["cmd+shift+down"] = "doc:create-cursor-next-line"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -6,9 +6,12 @@ keymap.map = {}
|
|||
keymap.reverse_map = {}
|
||||
|
||||
local macos = rawget(_G, "MACOS_RESOURCES")
|
||||
|
||||
local os4 = false
|
||||
if PLATFORM == "AmigaOS 4" then
|
||||
os4 = true
|
||||
end
|
||||
-- Thanks to mathewmariani, taken from his lite-macos github repository.
|
||||
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
|
||||
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or "generic"))
|
||||
local modkey_map = modkeys_os.map
|
||||
local modkeys = modkeys_os.keys
|
||||
|
||||
|
@ -202,6 +205,8 @@ keymap.add_direct {
|
|||
["ctrl+shift+end"] = "doc:select-to-end-of-doc",
|
||||
["shift+pageup"] = "doc:select-to-previous-page",
|
||||
["shift+pagedown"] = "doc:select-to-next-page",
|
||||
["ctrl+shift+up"] = "doc:create-cursor-previous-line",
|
||||
["ctrl+shift+down"] = "doc:create-cursor-next-line"
|
||||
}
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
local modkeys = {}
|
||||
|
||||
modkeys.map = {
|
||||
["left amiga"] = "cmd",
|
||||
["right amiga"] = "cmd",
|
||||
["control"] = "ctrl",
|
||||
["left shift"] = "shift",
|
||||
["right shift"] = "shift",
|
||||
["left alt"] = "alt",
|
||||
["right alt"] = "altgr",
|
||||
}
|
||||
|
||||
modkeys.keys = { "cmd", "ctrl", "alt", "altgr", "shift" }
|
||||
|
||||
return modkeys
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
|
||||
cxxcompiler="g++"
|
||||
cxxflags="-Wall -O3 -g -std=c++03 -fno-exceptions -fno-rtti -Isrc -Ilib/font_renderer -DFONT_RENDERER_HEIGHT_HACK -lagg -lfreetype -I/sdk/local/common/include/agg"
|
||||
|
||||
echo "compiling font renderer library..."
|
||||
|
||||
g++ -c $cxxflags agg_font_freetype.cpp -o agg_font_freetype.o
|
||||
g++ -c $cxxflags font_renderer.cpp -o font_renderer.o
|
||||
|
||||
ar -rcs libfontrenderer.a *.o
|
||||
|
||||
rm *.o
|
||||
echo "font renderer library created"
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/bash
|
||||
|
||||
cflags="-D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR -DLITE_USE_SDL_RENDERER -Wall -O3 -std=gnu11 -fno-strict-aliasing -Isrc -Ilib/font_renderer -I/sdk/local/newlib/include/SDL2 -lSDL2 -llua -lauto"
|
||||
#cflags+=" $(pkg-config --cflags lua5.2) $(sdl2-config --cflags)"
|
||||
lflags="-mcrt=newlib -static-libgcc -static-libstdc++ -lSDL2 -lagg -lfreetype -llua -lm -lpthread -athread=native"
|
||||
#for package in libagg freetype2 lua5.2; do
|
||||
# lflags+=" $(pkg-config --libs $package)"
|
||||
#done
|
||||
#lflags+=" $(sdl2-config --libs) -lm"
|
||||
|
||||
#if [[ $* == *windows* ]]; then
|
||||
# echo "cross compiling for windows is not yet supported"
|
||||
# exit 1
|
||||
#else
|
||||
outfile="lite"
|
||||
compiler="gcc"
|
||||
cxxcompiler="g++"
|
||||
#fi
|
||||
|
||||
#lib/font_renderer/build.sh || exit 1
|
||||
#libs=libfontrenderer.a
|
||||
libs="-lfontrenderer"
|
||||
|
||||
echo "compiling lite..."
|
||||
|
||||
gcc -c $cflags src/fontdesc.c -o fontdesc.o
|
||||
gcc -c $cflags src/main.c -o main.o
|
||||
gcc -c $cflags src/rencache.c -o rencache.o
|
||||
gcc -c $cflags src/renderer.c -o renderer.o
|
||||
gcc -c $cflags src/renwindow.c -o renwindow.o
|
||||
|
||||
gcc -c $cflags src/api/api.c -o api.o
|
||||
gcc -c $cflags src/api/cp_replace.c -o cp_replace.o
|
||||
gcc -c $cflags src/api/renderer.c -o apirenderer.o
|
||||
gcc -c $cflags src/api/renderer_font.c -o renderer_font.o
|
||||
gcc -c $cflags src/api/system.c -o system.o
|
||||
|
||||
gcc -c $cflags src/platform/amigaos4.c -o amigaos4.o
|
||||
|
||||
echo "linking..."
|
||||
g++ -o $outfile *.o $libs $lflags
|
||||
|
||||
echo "cleaning up..."
|
||||
rm *.o
|
||||
echo "done"
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -12,6 +12,10 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __amigaos4__
|
||||
#include "platform/amigaos4.h"
|
||||
#endif
|
||||
|
||||
extern SDL_Window *window;
|
||||
|
||||
|
||||
|
@ -287,7 +291,11 @@ static int f_set_window_mode(lua_State *L) {
|
|||
int n = luaL_checkoption(L, 1, "normal", window_opts);
|
||||
SDL_SetWindowFullscreen(window,
|
||||
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
if (n == WIN_NORMAL) { SDL_RestoreWindow(window); }
|
||||
if (n == WIN_NORMAL)
|
||||
{
|
||||
ren_resize_window();
|
||||
SDL_RestoreWindow(window);
|
||||
}
|
||||
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
|
||||
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); }
|
||||
return 0;
|
||||
|
@ -423,6 +431,10 @@ static int f_list_dir(lua_State *L) {
|
|||
#define realpath(x, y) _fullpath(y, x, MAX_PATH)
|
||||
#endif
|
||||
|
||||
#ifdef __amigaos4__
|
||||
#define realpath(x, y) _fullpath(x)
|
||||
#endif
|
||||
|
||||
static int f_absolute_path(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
char *res = realpath(path, NULL);
|
||||
|
|
15
src/main.c
15
src/main.c
|
@ -13,6 +13,8 @@
|
|||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
#elif __amigaos4__
|
||||
#include "platform/amigaos4.h"
|
||||
#elif __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
@ -70,12 +72,13 @@ static void get_exe_filename(char *buf, int sz) {
|
|||
char exepath[size];
|
||||
_NSGetExecutablePath(exepath, &size);
|
||||
realpath(exepath, buf);
|
||||
#elif __amigaos4__
|
||||
strcpy(buf, _fullpath("./lite"));
|
||||
#else
|
||||
strcpy(buf, "./lite");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void init_window_icon(void) {
|
||||
#ifndef _WIN32
|
||||
#include "../icon.inl"
|
||||
|
@ -132,6 +135,8 @@ int main(int argc, char **argv) {
|
|||
window = SDL_CreateWindow(
|
||||
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||
SDL_SetWindowDisplayMode(window, &dm);
|
||||
|
||||
init_window_icon();
|
||||
ren_init(window);
|
||||
|
||||
|
@ -154,7 +159,6 @@ init_lua:
|
|||
|
||||
lua_pushnumber(L, get_scale());
|
||||
lua_setglobal(L, "SCALE");
|
||||
|
||||
char exename[2048];
|
||||
get_exe_filename(exename, sizeof(exename));
|
||||
lua_pushstring(L, exename);
|
||||
|
@ -164,13 +168,15 @@ init_lua:
|
|||
set_macos_bundle_resources(L);
|
||||
enable_momentum_scroll();
|
||||
#endif
|
||||
|
||||
const char *init_lite_code = \
|
||||
"local core\n"
|
||||
"xpcall(function()\n"
|
||||
" HOME = os.getenv('" LITE_OS_HOME "')\n"
|
||||
" local exedir = EXEFILE:match(\"^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$\")\n"
|
||||
" local prefix = exedir:match(\"^(.*)" LITE_PATHSEP_PATTERN "bin$\")\n"
|
||||
" if not HOME then\n"
|
||||
" HOME = exedir\n"
|
||||
" end\n"
|
||||
" dofile((MACOS_RESOURCES or (prefix and prefix .. '/share/lite-xl' or exedir .. '/data')) .. '/core/start.lua')\n"
|
||||
" core = require('core')\n"
|
||||
" core.init()\n"
|
||||
|
@ -203,11 +209,12 @@ init_lua:
|
|||
lua_pcall(L, 0, 1, 0);
|
||||
if (lua_toboolean(L, -1)) {
|
||||
lua_close(L);
|
||||
rencache_invalidate();
|
||||
goto init_lua;
|
||||
}
|
||||
|
||||
lua_close(L);
|
||||
ren_free_window_resources();
|
||||
SDL_Quit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "amigaos4.h"
|
||||
|
||||
static char *getFullPath(const char *path)
|
||||
{
|
||||
char *appPath = malloc(sizeof(char) * MAX_DOS_NAME);
|
||||
BPTR pathLock = Lock(path, SHARED_LOCK);
|
||||
if (pathLock)
|
||||
{
|
||||
NameFromLock(pathLock, appPath, sizeof(char) * MAX_DOS_NAME);
|
||||
UnLock(pathLock);
|
||||
|
||||
return appPath;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *getCurrentPath(void)
|
||||
{
|
||||
char *appPath = malloc(sizeof(char) * MAX_DOS_NAME);
|
||||
BPTR pathLock = GetCurrentDir();
|
||||
if (pathLock)
|
||||
{
|
||||
NameFromLock(pathLock, appPath, sizeof(char) * MAX_DOS_NAME);
|
||||
return appPath;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *_fullpath(const char *path)
|
||||
{
|
||||
static char prvPath[MAX_DOS_NAME];
|
||||
static char result[MAX_DOS_NAME];
|
||||
|
||||
if (!strcmp(path, prvPath))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
strcpy(prvPath, path);
|
||||
|
||||
if (!strcmp(path, "./lite"))
|
||||
{
|
||||
// TODO: Add code to get the name of the executable
|
||||
strcpy(result, getFullPath("PROGDIR:lite"));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!strcmp(path, "."))
|
||||
{
|
||||
strcpy(result, getCurrentPath());
|
||||
return result;
|
||||
}
|
||||
|
||||
strcpy(result, getFullPath(path));
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _AMIGAOS4_H
|
||||
#define _AMIGAOS4_H
|
||||
|
||||
#include <proto/dos.h>
|
||||
#include <proto/exec.h>
|
||||
|
||||
#define VSTRING "Lite XL 1.16.12.6 (04.01.2022)"
|
||||
#define VERSTAG "\0$VER: " VSTRING
|
||||
|
||||
static CONST_STRPTR stack USED = "$STACK:102400";
|
||||
static CONST_STRPTR version USED = VERSTAG;
|
||||
|
||||
char *_fullpath(const char *);
|
||||
|
||||
|
||||
#endif
|
|
@ -9,6 +9,15 @@ static int query_surface_scale(RenWindow *ren) {
|
|||
SDL_GetWindowSize(ren->window, &w_points, &h_points);
|
||||
/* We consider that the ratio pixel/point will always be an integer and
|
||||
it is the same along the x and the y axis. */
|
||||
|
||||
// This is a workaround when the w_pixels != w_points and h_pixels != h_points
|
||||
// because of redraw delays, especially when the "Resize with contents" is enabled
|
||||
if (w_pixels != w_points) {
|
||||
w_pixels = w_points;
|
||||
}
|
||||
if (h_pixels != h_points) {
|
||||
h_pixels = h_points;
|
||||
}
|
||||
assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points);
|
||||
return w_pixels / w_points;
|
||||
}
|
||||
|
@ -20,7 +29,7 @@ static void setup_renderer(RenWindow *ren, int w, int h) {
|
|||
SDL_DestroyTexture(ren->texture);
|
||||
SDL_DestroyRenderer(ren->renderer);
|
||||
}
|
||||
ren->renderer = SDL_CreateRenderer(ren->window, -1, 0);
|
||||
ren->renderer = SDL_CreateRenderer(ren->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
ren->texture = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h);
|
||||
ren->surface_scale = query_surface_scale(ren);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue