diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index 18bfbe0d..3abe56da 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -58,6 +58,11 @@ jobs:
submodules: "recursive"
fetch-depth: 0
fetch-tags: true
+ - name: install just
+ uses: extractions/setup-just@v2
+ - name: Add versioninfo
+ run: |
+ just version
- name: Build bare love package
uses: love-actions/love-actions-core@v1
with:
@@ -242,7 +247,7 @@ jobs:
VER: ${{ github.ref_name }}
run: echo VER_CODE="$(echo $VERSION | sed -e 's/^v//' -e 's/\.//g')" >> $GITHUB_ENV
- name: Package for android
- uses: compy-toys/love-actions-android@v0.2.5
+ uses: compy-toys/love-actions-android@v0.2.6
with:
love-ref: "compy"
no-soft-keyboard: "enabled"
diff --git a/.luarc.json b/.luarc.json
index ecedad0a..ccc8cc99 100644
--- a/.luarc.json
+++ b/.luarc.json
@@ -17,6 +17,7 @@
"write_to_input",
"validated_input",
"stop",
+ "pause",
"continue",
// luautils
"prequire",
diff --git a/README.md b/README.md
index 08e56e21..f8cdf686 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,7 @@ a project must be selected first.
| Search definitions | Ctrl+F |
| Exit search | Esc |
| Jump to selected definition | Enter ⏎ |
+| Edit required file / return to previous | Ctrl+O |
## Projects
@@ -140,10 +141,6 @@ contents.
Write to _file_ the text supplied as the _content_ parameter.
This can be either a string, or an array of strings.
-- `runfile(file)`
-
- Run _file_ if it's a lua script.
-
## Editor
If a project is open, the files inside can be edited or new ones
@@ -164,7 +161,7 @@ To modify an existing line, navigate there with

Happy with the modifications now, we can quit by pressing
-Ctrl-Shift-Q
+Ctrl-Shift-S

diff --git a/doc/development/OOP.md b/doc/development/OOP.md
index 24cc3a92..f7e4400b 100644
--- a/doc/development/OOP.md
+++ b/doc/development/OOP.md
@@ -1,14 +1,14 @@
-### OOP
+## OOP
-Even though lua is not an object oriented language per se, it can approximate
-some OO behaviors with clever use of metatables.
+Even though lua is not an object oriented language per se, it
+can approximate some OO behaviors with clever use of metatables.
See:
- [http://lua-users.org/wiki/ObjectOrientedProgramming][oo1]
- [http://lua-users.org/wiki/ObjectOrientationTutorial][oo2]
-#### Class factory
+### Class factory
To automate this, a class factory utility was added.
@@ -20,26 +20,73 @@ local class = require('util.class')
Then it can be used in the following ways:
-- passing a constructor (record/dataclass pattern)
+#### passing a constructor (record/dataclass pattern)
```lua
A = class.create(function()
return { a = 'a' }
end
)
-local a = A() --- results in an instance with the preset values, not very useful
+--- results in an instance with the preset values
+local a = A()
B = class.create(function(x, y)
return { x = x, y = y }
end)
-local b = B(1, 2) --- results in a B instance where x = 1 and y = 2
+--- results in a B instance where x = 1 and y = 2
+local b = B(1, 2)
+```
+
+Note: in the codebase, the function is often not defined inline,
+and very likely is named `new`, _however_ be careful not to
+confuse it with the [`new` method][n]. If the function is not a
+member of the class, it's this pattern.
+
+##### late init
+
+Sometimes it's useful to extract some behavior that's required
+both on init but also later on-demand. An instance method is a
+fine solution for this, but in order to invoke it, a full-blown
+properly initialized instance is necessary, so it can't be done
+in the constructor. Also, it would be nice to not have to do it
+on each call site where an instance is created. Hence,
+`lateinit`.
+
+Let's say we have Text objects, storing text line-by-line, and
+we want to know the average line length. This of course needs to
+be calculated on first creation, and any time the text changes.
+
+```lua
+local function new(text)
+ return {
+ text = string.lines(text or '')
+ }
+end
+local function lateinit(self)
+ local n = #(self.text)
+ if n ~= 0 then
+ local lens = table.map(self.text, string.ulen)
+ local l = 0
+ for _, v in ipairs(lens) do
+ l = l + v
+ end
+ self.avg_len = l / n
+ else
+ self.avg_len = 0
+ end
+end
+--- @class Text
+--- @field text string[]
+--- @field avg_len number
+Text = class.create(new, lateinit)
```
-For more advanced use cases, it will probably be necessary to manually control the
-metatable setup, this is achieved with the
+#### `new` method
-- `new()` method
+For more advanced use cases, it will probably be necessary to
+manually control the metatable setup, this is achieved by
+defining the `new()` method on the class.
```lua
N = class.create()
@@ -61,3 +108,4 @@ local n = N({width = 80, height = 25})
[oo1]: https://archive.vn/B3buW
[oo2]: https://archive.vn/muhJx
+[n]: #new-method
diff --git a/doc/development/editor/visible.md b/doc/development/editor/visible.md
index 6bf41649..14d2ed32 100644
--- a/doc/development/editor/visible.md
+++ b/doc/development/editor/visible.md
@@ -45,6 +45,9 @@ loin█ bresaola veni | 5
son. |
```
We arrive at 'visible coords'.
+
+#### convert
+
What is `visible(3, 3)` in the others?
* `wrapped(11, 3)`
* `normal(4, 41)`
diff --git a/doc/development/keyboard.md b/doc/development/keyboard.md
new file mode 100644
index 00000000..ce67fa6c
--- /dev/null
+++ b/doc/development/keyboard.md
@@ -0,0 +1,38 @@
+
+| Label | KeyConstant | ok? |
+| :------------------- | :---------- | :--- |
+| Esc | escape | ✓ |
+| F1 | f1 | ✗ |
+| F2 | f2 | ✗ |
+| F3 | f3 | ✗ |
+| F4 | f4 | ✗ |
+| F5 | f5 | ✗ |
+| F6 | f6 | ✗ |
+| F7 | f7 | ✗ |
+| F8 | f8 | ✗ |
+| F9 | f9 | ✗ |
+| F10 | f10 | ✓ |
+| F11 | f11 | - |
+| F12 | f12 | - |
+| numlk | code 143 | ✗ |
+| Delete | delete | ✓ |
+| Tab | tab | ✓ |
+| Enter | return | ✓ |
+| Caps Lock | capslock | ✓ |
+| ⇧ Shift | lshift | ✓ |
+| Shift | rshift | ✓ |
+| Ctrl | | ✗ |
+| Alt | lalt | ✓ |
+| PauseBreak | pause | ✓ |
+| Menu | menu | ✓ |
+| Insert (Fn+F12) | insert | ✓ |
+| Scroll Lock (Fn+F10) | scrolllock | ✓ |
+| Home (Fn+Left) | home | ✓ |
+| End (Fn+Right) | end | ✓ |
+| Mute (Fn+F5) | code 164 | ✗ |
+| PgUp (Fn+Up) | pageup | ✓ |
+| PgDn (Fn+Down) | pagedown | ✓ |
+| Next (Fn+F6) | audioplay | ✓ |
+| Prev (Fn+F7) | audioprev | ✓ |
+| Next (Fn+F8) | audionext | ✓ |
+| | | |
diff --git a/doc/intro.md b/doc/intro.md
new file mode 100644
index 00000000..5dc5d240
--- /dev/null
+++ b/doc/intro.md
@@ -0,0 +1,53 @@
+# Introduction to Compy
+
+Compy is a 7'' portable educational computer with an interactive development
+environment for the [love2d](https://love2d.org) framework. It runs on top of
+Android operating system and uses the Lua programming language for the command
+line, scripting, configuration and programming. The primary interface is the
+command console, with the standard input displayed at the bottom of the screen,
+with real-time syntax highlingting (when appropriate). The standard output is
+displayed on a terminal occupying the rest of the screen, which also doubles as
+a graphical canvas. Thus, commands with both text-based or graphical output can
+be entered. Below are some invariant principles of operations:
+
+* Pointing devices (such as mice, touch pad or touch screen) might be used, but
+ are not necessary for the successful operation of Compy.
+* Apart from syntax highlighting, anything entered in the console
+ input will only take effect upon pressing the **Enter** key.
+* If the input is syntactically invalid at the time of pressing **Enter**, the
+ user can continue editing, correcting the mistake.
+* Users cannot render compy unusable from the command line. Compy can always
+ be restored to its initial state by inserting a clean _SD card_ or formatting
+ the currently inserted one. Example projects are written onto the SD card by
+ entering `example_projects()` on the command line.
+
+## Persistence
+
+All data persisted by the user is saved on the included _SD card_. It is
+organized into named _projects_, with no file system hieararchy. Technically,
+each project is a directory under `Documents/compy/projects/` on the SD card.
+Inside these directories, there is no further directory structure.
+
+Projects can be selected or, in the absence of one, created by entering
+`project("...")` in the console, with the project's name between the double
+quotes. Running the project means executing a file called "`main.lua`" in its
+directory. It can be done by entering `run()` in the console for the currently
+selected project or `run("...")` for a different project, with the name of the
+project between the double quotes.
+
+Text files, including Lua sources can be edited using the built-in text editor
+that can be started by entering `edit("...")` in the console, with the name of
+the file between the quotes. In its absence, `main.lua` will be edited.
+
+No changes to the edited file will occur unless the user presses **Enter** _and_
+the text in the edit area at the bottom of the screen is syntactically correct.
+Pressing **Enter** takes immediate effect on the SD card, there is no need to
+"save" the edited file separately.
+
+If there is no highlighted section in the edited file, the text inputed in the
+console will be appended to the end of the file. If there is a bright white
+highlight, the entered text is inserted before it. If there is a bright yellow
+highlight, the entered text replaces it.
+
+For more information, please see the documentation of the [editor](EDITOR.md)
+and the [console](../README.md).
diff --git a/doc/mermaid/editor.md b/doc/mermaid/editor.md
index 3552900b..f80d117c 100644
--- a/doc/mermaid/editor.md
+++ b/doc/mermaid/editor.md
@@ -73,7 +73,7 @@ class VisibleContent {
class VisibleStructuredContent {
text: string[]
- blocks: VisibleBlock[]
+ v_blocks: VisibleBlock[]
reverse_map: ReverseMap
range: Range?
diff --git a/src/controller/consoleController.lua b/src/controller/consoleController.lua
index 8cb2f51c..0ab31176 100644
--- a/src/controller/consoleController.lua
+++ b/src/controller/consoleController.lua
@@ -35,13 +35,11 @@ function ConsoleController.new(M, main_ctrl)
local config = M.cfg
pre_env.font = config.view.font
local IC = UserInputController(M.input)
- local EC = EditorController(M.editor)
local self = setmetatable({
time = 0,
model = M,
main_ctrl = main_ctrl,
input = IC,
- editor = EC,
-- console runner env
main_env = env,
-- copy of the application's env before the prep
@@ -58,6 +56,9 @@ function ConsoleController.new(M, main_ctrl)
cfg = config
}, ConsoleController)
+ --- the editor has to know about us
+ local EC = EditorController(M.editor, self)
+ self.editor = EC
-- initialize the stub env tables
ConsoleController.prepare_env(self)
ConsoleController.prepare_project_env(self)
@@ -88,11 +89,11 @@ end
--- @return boolean success
--- @return string? errmsg
local function run_user_code(f, cc, project_path)
- local G = love.graphics
+ local gfx = love.graphics
local output = cc.model.output
local env = cc:get_base_env()
- G.setCanvas(cc:get_canvas())
+ gfx.setCanvas(cc:get_canvas())
local ok, call_err
if project_path then
env = cc:get_project_env()
@@ -102,7 +103,7 @@ local function run_user_code(f, cc, project_path)
cc.main_ctrl.set_user_handlers(env['love'])
end
output:restore_main()
- G.setCanvas()
+ gfx.setCanvas()
if not ok then
local msg = LANG.get_call_error(call_err)
return false, msg
@@ -194,10 +195,11 @@ local function project_require(cc, name)
local pr_env = cc:get_project_env()
if chunk then
setfenv(chunk, pr_env)
- chunk()
+ return chunk()
else
--- hack around love.js not having the bit lib
if name == 'bit' and _G.web then
+ ---@diagnostic disable-next-line: inject-field
pr_env.bit = o_require('util.luabit')
end
end
@@ -209,7 +211,7 @@ end
function ConsoleController.prepare_env(cc)
local prepared = cc.main_env
- prepared.G = love.graphics
+ prepared.gfx = love.graphics
local P = cc.model.projects
@@ -310,18 +312,6 @@ function ConsoleController.prepare_env(cc)
end)
end
- --- @param name string
- --- @return any
- prepared.runfile = function(name)
- local code = check_open_pr(cc._readfile, cc, name)
- local chunk, err = load(code, '', 't')
- if chunk then
- chunk()
- else
- print(err)
- end
- end
-
--- @param name string
prepared.edit = function(name)
return check_open_pr(cc.edit, cc, name)
@@ -352,24 +342,24 @@ function ConsoleController.prepare_project_env(cc)
require("controller.userInputController")
require("model.input.userInputModel")
require("view.input.userInputView")
- local cfg = cc.model.cfg
+ local cfg = cc.model.cfg
---@type table
- local project_env = cc:get_pre_env_c()
- project_env.G = love.graphics
+ local project_env = cc:get_pre_env_c()
+ project_env.gfx = love.graphics
- project_env.require = function(name)
+ project_env.require = function(name)
return project_require(cc, name)
end
--- @param msg string?
- project_env.pause = function(msg)
+ project_env.pause = function(msg)
cc:suspend_run(msg)
end
- project_env.stop = function()
+ project_env.stop = function()
cc:stop_project_run()
end
- project_env.continue = function()
+ project_env.continue = function()
if love.state.app_state == 'inspect' then
-- resume
love.state.app_state = 'running'
@@ -399,7 +389,7 @@ function ConsoleController.prepare_project_env(cc)
if not input_ref then return end
ui_model = UserInputModel(cfg, eval, true, prompt)
ui_model:set_text(init)
- local inp_con = UserInputController(ui_model, input_ref)
+ local inp_con = UserInputController(ui_model, input_ref, true)
local view = UserInputView(cfg.view, inp_con)
love.state.user_input = {
M = ui_model, C = inp_con, V = view
@@ -600,6 +590,7 @@ function ConsoleController:open_project(name, play)
then
table.insert(package.loaders, 1, project_loader)
end
+ love.state.app_state = 'project_open'
end
if open then
print('Project ' .. name .. ' opened')
@@ -667,8 +658,11 @@ function ConsoleController:edit(name, state)
if ex then
text = self:_readfile(filename)
end
- love.state.prev_state = love.state.app_state
- love.state.app_state = 'editor'
+
+ if love.state.app_state ~= 'editor' then
+ love.state.prev_state = love.state.app_state
+ love.state.app_state = 'editor'
+ end
local save = function(newcontent)
return self:_writefile(filename, newcontent)
end
@@ -677,6 +671,11 @@ function ConsoleController:edit(name, state)
self.editor:restore_state(state)
end
+--- @return EditorState?
+function ConsoleController:close_buffer()
+ self.editor:close_buffer()
+end
+
--- @return EditorState?
function ConsoleController:finish_edit()
self.editor:save_state()
@@ -685,6 +684,7 @@ function ConsoleController:finish_edit()
if ok then
love.state.app_state = love.state.prev_state
love.state.prev_state = nil
+ --- TODO clear bufferlist
return self.editor:get_state()
else
print(err)
diff --git a/src/controller/controller.lua b/src/controller/controller.lua
index b90cb14e..01283d12 100644
--- a/src/controller/controller.lua
+++ b/src/controller/controller.lua
@@ -8,6 +8,9 @@ local messages = {
user_break = "BREAK into program",
exit_anykey = "Press any key to exit.",
exec_error = function(err)
+ Log.error((debug.traceback(
+ "Error: " .. tostring(err), 1):gsub("\n[^\n]+$", "")
+ ))
return 'Execution error at ' .. err
end
}
@@ -376,7 +379,11 @@ Controller = {
end
local user_input = get_user_input()
if user_input then
- user_input.V:draw(user_input.C:get_input(), C.time)
+ if love.DEBUG then
+ user_input.V:draw(user_input.C:get_input(), C.time)
+ else
+ user_input.V:draw(user_input.C:get_input())
+ end
end
end
View.prev_draw = draw
@@ -416,11 +423,11 @@ Controller = {
View.prev_draw = love.draw
View.main_draw = love.draw
View.end_draw = function()
- local w, h = G.getDimensions()
- G.setColor(Color[Color.white])
- G.setFont(C.cfg.view.font)
- G.clear()
- G.printf(messages.exit_anykey, 0, h / 3, w, "center")
+ local w, h = gfx.getDimensions()
+ gfx.setColor(Color[Color.white])
+ gfx.setFont(C.cfg.view.font)
+ gfx.clear()
+ gfx.printf(messages.exit_anykey, 0, h / 3, w, "center")
end
end,
@@ -521,7 +528,7 @@ Controller = {
handlers.keypressed = function(k)
local function quickswitch()
- if k == 'f8' then
+ if Key.ctrl() and k == 't' then
if love.state.app_state == 'running'
or love.state.app_state == 'inspect'
or love.state.app_state == 'project_open'
@@ -556,7 +563,7 @@ Controller = {
if love.state.app_state == 'running' then
C:stop_project_run()
elseif love.state.app_state == 'editor' then
- C:finish_edit()
+ C:close_buffer()
end
end
if k == "r" then
diff --git a/src/controller/editorController.lua b/src/controller/editorController.lua
index b79d89c1..58d8ba72 100644
--- a/src/controller/editorController.lua
+++ b/src/controller/editorController.lua
@@ -6,7 +6,8 @@ require("view.input.customStatus")
local class = require('util.class')
--- @param M EditorModel
-local function new(M)
+--- @oaram CC ConsoleController
+local function new(M, CC)
return {
input = UserInputController(M.input, nil, true),
model = M,
@@ -14,6 +15,7 @@ local function new(M)
M.search,
UserInputController(M.search.input, nil, true)
),
+ console = CC,
view = nil,
mode = 'edit',
}
@@ -28,36 +30,21 @@ end
--- @field model EditorModel
--- @field input UserInputController
--- @field search SearchController
+--- @field console ConsoleController
--- @field view EditorView?
--- @field state EditorState?
--- @field mode EditorMode
----
---- @field open function
---- @field get_state function
---- @field set_state function
---- @field save_state function
---- @field restore_state function
---- @field get_clipboard function
---- @field set_clipboard function
---- @field set_mode function
---- @field get_mode function
---- @field close function
---- @field get_active_buffer function
---- @field get_input function
---- @field update_status function
---- @field textinput function
---- @field keypressed function
EditorController = class.create(new)
--- @param name string
---- @param content string?
+--- @param content str?
--- @param save function
function EditorController:open(name, content, save)
local w = self.model.cfg.view.drawableChars
local is_lua = string.match(name, '.lua$')
local is_md = string.match(name, '.md$')
- local ch, hl, pp
+ local ch, hl, pp, tr
if is_lua then
self.input:set_eval(LuaEditorEval)
@@ -73,6 +60,9 @@ function EditorController:open(name, content, save)
pp = function(t)
return parser.pprint(t, w)
end
+ tr = function(code)
+ return parser.trunc(code, self.model.cfg.view.fold_lines)
+ end
elseif is_md then
local mdEval = MdEval(name)
hl = mdEval.highlighter
@@ -81,13 +71,59 @@ function EditorController:open(name, content, save)
self.input:set_eval(TextEval)
end
- local b = BufferModel(name, content, save, ch, hl, pp)
- self.model.buffer = b
- self.view.buffer:open(b)
+ local b = BufferModel(name, content, save, ch, hl, pp, tr)
+ self.model.buffers:push_front(b)
+ self.view:open(b)
self:update_status()
self:set_state()
end
+--- @private
+function EditorController:_dump_bufferlist()
+ for i, v in ipairs(self.model.buffers) do
+ Log.debug(i, v.name)
+ end
+ orig_print()
+end
+
+function EditorController:follow_require()
+ local buf = self:get_active_buffer()
+ if not buf.semantic then return end
+ local bn = buf:get_selection()
+ local reqs = buf.semantic.requires
+ local reqsel = table.find_by_v(reqs, function(r)
+ return r.block == bn
+ end)
+
+ if reqsel then
+ local name = reqsel.name
+ self.console:edit(name .. '.lua')
+ else
+ self:pop_buffer()
+ end
+end
+
+function EditorController:pop_buffer()
+ local bs = self.model.buffers
+ local n_buffers = bs:length()
+ if n_buffers < 2 then return end
+ bs:pop_front()
+ local b = bs:first()
+ self.view:get_current_buffer():open(b)
+end
+
+function EditorController:close_buffer()
+ local bs = self.model.buffers
+ local n_buffers = bs:length()
+ if n_buffers < 2 then
+ -- Log.debug('fin', n_buffers)
+ self.console:finish_edit()
+ else
+ -- Log.debug(':bd', n_buffers)
+ self:pop_buffer()
+ end
+end
+
--- @param m EditorMode
--- @return boolean
local function is_normal(m)
@@ -101,10 +137,12 @@ function EditorController:set_mode(mode)
self:save_state()
end
local init_search = function()
- self:save_state()
local db = buf.semantic
- local ds = db.definitions
- self.search:load(ds)
+ if db then
+ self:save_state()
+ local ds = db.definitions
+ self.search:load(ds)
+ end
end
local current = self.mode
@@ -113,7 +151,6 @@ function EditorController:set_mode(mode)
set_reorg()
end
if mode == 'search' then
- if not buf.semantic then return end
init_search()
end
self.mode = mode
@@ -149,8 +186,10 @@ end
--- @param clipboard string?
function EditorController:set_state(clipboard)
- local buf_view_state = self.view.buffer:get_state()
+ --- TODO: multibuffer support
local buf = self:get_active_buffer()
+ local bid = buf:get_id()
+ local buf_view_state = self.view:get_buffer(bid):get_state()
if self.state then
self.state.buffer = buf_view_state
self.state.moved = buf:get_selection()
@@ -170,17 +209,19 @@ function EditorController:get_state()
end
function EditorController:save_state()
+ --- TODO: multibuffer support
self:set_state(love.system.getClipboardText())
end
--- @param state EditorState?
function EditorController:restore_state(state)
+ --- TODO: multibuffer support
if state then
local buf = self:get_active_buffer()
local sel = state.buffer.selection
local off = state.buffer.offset
buf:set_selection(sel)
- self.view.buffer:scroll_to(off)
+ self.view:get_current_buffer():scroll_to(off)
local clip = state.clipboard
if string.is_non_empty_string(clip) then
love.system.setClipboardText(clip or '')
@@ -199,7 +240,13 @@ end
--- @return BufferModel
function EditorController:get_active_buffer()
- return self.model.buffer
+ return self.model.buffers:first()
+end
+
+--- @return Id
+function EditorController:get_active_buffer_id()
+ local buf = self:get_active_buffer()
+ return buf:get_id()
end
--- @private
@@ -209,7 +256,7 @@ function EditorController:_generate_status(sel)
--- @type BufferModel
local buffer = self:get_active_buffer()
local len = buffer:get_content_length() + 1
- local bufview = self.view.buffer
+ local bufview = self.view:get_buffer(buffer:get_id())
local more = bufview.content:get_more()
local cs
local m = self.mode
@@ -319,7 +366,7 @@ function EditorController:_move_sel(dir, by, warp, moved)
local m = buf:move_selection(dir, by, warp, mv)
if m then
if mv then self.view:refresh(moved) end
- self.view.buffer:follow_selection()
+ self.view:get_current_buffer():follow_selection()
self:update_status()
end
end
@@ -329,7 +376,7 @@ end
--- @param warp boolean?
--- @param by integer?
function EditorController:_scroll(dir, warp, by)
- self.view.buffer:scroll(dir, by, warp)
+ self.view:get_current_buffer():scroll(dir, by, warp)
self:update_status()
end
@@ -414,7 +461,7 @@ function EditorController:_search_mode_keys(k)
local bn = jump.block
local ln = jump.line - 1
buf:set_selection(bn)
- self.view.buffer:scroll_to_line(ln)
+ self.view:get_current_buffer():scroll_to_line(ln)
self:set_mode('edit')
self.search:clear()
end
@@ -519,7 +566,7 @@ function EditorController:_normal_mode_keys(k)
--- handlers
local function submit()
if not Key.ctrl() and not Key.shift() and Key.is_enter(k) then
- local bufv = self.view.buffer
+ local bufv = self.view:get_current_buffer()
local function go(newtext)
if bufv:is_selection_visible() then
if buf:loaded_is_sel(true) then
@@ -609,6 +656,13 @@ function EditorController:_normal_mode_keys(k)
and k == "pagedown" then
self:_scroll('down', false, 1)
end
+
+ -- step into
+ if love.DEBUG and Key.ctrl() then
+ if k == "o" then
+ self:follow_require()
+ end
+ end
end
local function clear()
if Key.ctrl() and k == "w" then
@@ -651,7 +705,7 @@ function EditorController:keypressed(k)
if love.debug then
local buf = self:get_active_buffer()
- local bufview = self.view.buffer
+ local bufview = self.view:get_buffer(buf:get_id())
if k == 'f5' then
if Key.ctrl() then buf:rechunk() end
bufview:refresh()
diff --git a/src/examples/clock/README.md b/src/examples/clock/README.md
index 0ab14f54..667f5b74 100644
--- a/src/examples/clock/README.md
+++ b/src/examples/clock/README.md
@@ -6,19 +6,19 @@ In this example, we explore how to properly create a program with it's own drawi
We will be taking over the screen drawing for this simple game.
Similar to `update()`, we can override the `love.draw()` function and have the LOVE2D framework handle displaying the content we wish.
-Drawing generally follows a simple procedure: set up some values, such as what foreground and background color to use, then build up our desired image using basic elements. These are called graphics "primitives", and we can access them from the `love.graphics` table (aliased here as `G`).
+Drawing generally follows a simple procedure: set up some values, such as what foreground and background color to use, then build up our desired image using basic elements. These are called graphics "primitives", and we can access them from the `love.graphics` table (aliased here as `gfx`).
So, our example clock:
```lua
function love.draw()
- G.setColor(Color[color + Color.bright])
- G.setBackgroundColor(Color[bg_color])
- G.setFont(font)
+ gfx.setColor(Color[color + Color.bright])
+ gfx.setBackgroundColor(Color[bg_color])
+ gfx.setFont(font)
local text = getTimestamp()
local off_x = font:getWidth(text) / 2
local off_y = font:getHeight() / 2
- G.print(text, midx - off_x, midy - off_y)
+ gfx.print(text, midx - off_x, midy - off_y)
end
```
Let's see this step-by-step.
@@ -28,13 +28,13 @@ The way the `print` helper, and most graphics helpers work, is by starting drawi
So, we need to determine the half-width and half-height of our text object to correctly draw it at the center. To do this, we can use the `getWidth()` and `getHeight()` helpers.
We determined the midpoint of the screen earlier:
```lua
-width, height = G.getDimensions()
+width, height = gfx.getDimensions()
midx = width / 2
midy = height / 2
```
Armed with this, we can draw the time dead center:
```lua
-G.print(text, midx - off_x, midy - off_y)
+gfx.print(text, midx - off_x, midy - off_y)
```
#### Getting the timestamp
@@ -60,7 +60,7 @@ function setTime()
t = s + M * m + H * h
end
```
-Reading the current time is achieved by using `os.date()`, which unlike `os.time()`, allows us to specify the format of the resulting string. We want to achieve the end result of "hour:minute"second", which we could get with the format string "%H:%M:%S", but we also need these intermediate values separately, to keep time. Instead, if the special format string "*t" is passed to the function, it will return a table with the parts of the timestamp instead of a string.
+Reading the current time is achieved by using `os.date()`, which unlike `os.time()`, allows us to specify the format of the resulting stringfx. We want to achieve the end result of "hour:minute"second", which we could get with the format string "%H:%M:%S", but we also need these intermediate values separately, to keep time. Instead, if the special format string "*t" is passed to the function, it will return a table with the parts of the timestamp instead of a stringfx.
#### Timekeeping
@@ -83,11 +83,11 @@ function getTimestamp()
return string.format("%s:%s:%s", hours, minutes, seconds)
end
```
-Quick aside on format strings: for a digital clock, we usually want to always have the numbers displayed as two digits, with colons in between them. To achieve this, we `pad` all our results, then stitch them together with `string.format()`.
+Quick aside on format strings: for a digital clock, we usually want to have the numbers displayed as two digits, with colons in between them. To achieve this, we `pad` all our results, then stitch them together with `string.format()`.
In the above code, we are doing two different kinds of division.First, an integer division (`/`), going from seconds to minutes and hours, in this case we are only interested in the whole numbers, for example: 143 seconds is two full minutes and then some, but what the clock will be displaying is '02', so the remaining 23 seconds is not interesing for the minutes part.
However, for 143 minutes, the display _should_ say 23, disregarding the two full hours, we are only interested in the remainder part. We can get this value by using `math.fmod()`, in this example, `math.fmod(143, 60)`.
#### User documentation
This program displays the current time in a randomly selected color over a randomly selected background. These colors can be changed by pressing [Space] and [Shift-Space], respectively.
-Should the clock deviate from the correct time (for example, because the program run was paused), it can be reset with the [R] key.
\ No newline at end of file
+Should the clock deviate from the correct time (for example, because the program run was paused), it can be reset with the [R] key.
diff --git a/src/examples/clock/main.lua b/src/examples/clock/main.lua
index 2ec69534..b940aa84 100644
--- a/src/examples/clock/main.lua
+++ b/src/examples/clock/main.lua
@@ -1,6 +1,6 @@
-local G = love.graphics
+local gfx = love.graphics
-width, height = G.getDimensions()
+width, height = gfx.getDimensions()
midx = width / 2
midy = height / 2
@@ -22,7 +22,7 @@ setTime()
math.randomseed(os.time())
color = math.random(7)
bg_color = math.random(7)
-font = G.newFont(144)
+font = gfx.newFont(144)
local function pad(i)
return string.format("%02d", i)
@@ -36,13 +36,13 @@ function getTimestamp()
end
function love.draw()
- G.setColor(Color[color + Color.bright])
- G.setBackgroundColor(Color[bg_color])
- G.setFont(font)
+ gfx.setColor(Color[color + Color.bright])
+ gfx.setBackgroundColor(Color[bg_color])
+ gfx.setFont(font)
local text = getTimestamp()
local off_x = font:getWidth(text) / 2
local off_y = font:getHeight() / 2
- G.print(text, midx - off_x, midy - off_y)
+ gfx.print(text, midx - off_x, midy - off_y)
end
function love.update(dt)
diff --git a/src/examples/life/README.md b/src/examples/life/README.md
index 17e43eec..5e2479ed 100644
--- a/src/examples/life/README.md
+++ b/src/examples/life/README.md
@@ -10,7 +10,7 @@ We have used the screen size before, but only in a very limited capacity, to det
```lua
cell_size = 10
-screen_w, screen_h = G.getDimensions()
+screen_w, screen_h = gfx.getDimensions()
grid_w = screen_w / cell_size
grid_h = screen_h / cell_size
```
@@ -250,10 +250,10 @@ function drawHelp()
local right_edge = screen_w - margin
local reset_msg = "Reset: [r] key or long press"
local speed_msg = "Set speed: [+]/[-] key or drag up/down"
- G.print(reset_msg, margin, (bottom - fh) - fh)
- G.print(speed_msg, margin, bottom - fh)
+ gfx.print(reset_msg, margin, (bottom - fh) - fh)
+ gfx.print(speed_msg, margin, bottom - fh)
local speed_label = string.format("Speed: %02d", speed)
local label_w = font:getWidth(speed_label)
- G.print(speed_label, right_edge - label_w, bottom - fh)
+ gfx.print(speed_label, right_edge - label_w, bottom - fh)
end
```
diff --git a/src/examples/life/main.lua b/src/examples/life/main.lua
index 74b42f7a..c3412a62 100644
--- a/src/examples/life/main.lua
+++ b/src/examples/life/main.lua
@@ -1,12 +1,12 @@
--- original from https://github.com/Aethelios/Conway-s-Game-of-Life-in-Lua-and-Love2D
-G = love.graphics
-G.setFont(font)
+gfx = love.graphics
+gfx.setFont(font)
fh = font:getHeight()
cell_size = 10
margin = 5
-screen_w, screen_h = G.getDimensions()
+screen_w, screen_h = gfx.getDimensions()
grid_w = screen_w / cell_size
grid_h = screen_h / cell_size
grid = {}
@@ -146,22 +146,22 @@ function drawHelp()
local right_edge = screen_w - margin
local reset_msg = "Reset: [r] key or long press"
local speed_msg = "Set speed: [+]/[-] key or drag up/down"
- G.print(reset_msg, margin, (bottom - fh) - fh)
- G.print(speed_msg, margin, bottom - fh)
+ gfx.print(reset_msg, margin, (bottom - fh) - fh)
+ gfx.print(speed_msg, margin, bottom - fh)
local speed_label = string.format("Speed: %02d", speed)
local label_w = font:getWidth(speed_label)
- G.print(speed_label, right_edge - label_w, bottom - fh)
+ gfx.print(speed_label, right_edge - label_w, bottom - fh)
end
function drawCell(x, y)
- G.setColor(.9, .9, .9)
- G.rectangle('fill',
+ gfx.setColor(.9, .9, .9)
+ gfx.rectangle('fill',
(x - 1) * cell_size,
(y - 1) * cell_size,
cell_size, cell_size)
- G.setColor(.3, .3, .3)
+ gfx.setColor(.3, .3, .3)
- G.rectangle('line',
+ gfx.rectangle('line',
(x - 1) * cell_size,
(y - 1) * cell_size,
cell_size, cell_size)
@@ -176,7 +176,7 @@ function love.draw()
end
end
- G.setColor(1, 1, 1, 0.5)
+ gfx.setColor(1, 1, 1, 0.5)
drawHelp()
end
diff --git a/src/examples/paint/README.md b/src/examples/paint/README.md
index acd44128..bfde8aa5 100644
--- a/src/examples/paint/README.md
+++ b/src/examples/paint/README.md
@@ -42,7 +42,7 @@ Building on the 16-color theme, we will divide the screen into 10 columns (8 col
Then halve these columns to get the row height. Display the selected background on a double block, with the foreground color in the middle.
```lua
-width, height = G.getDimensions()
+width, height = gfx.getDimensions()
--- color palette
block_w = width / 10
block_h = block_w / 2
@@ -142,13 +142,13 @@ In our case, we are only interested in the whole number to navigate our grid, an
local y = height - block_h
for c = 0, 7 do
local x = block_w * (c + 2)
- G.setColor(Color[c])
- G.rectangle("fill", x, y, width, block_h)
- G.setColor(Color[c + 8])
- G.rectangle("fill", x, y - block_h, width, block_h)
- G.setColor(Color[Color.white])
- G.rectangle("line", x, y, width, block_h)
- G.rectangle("line", x, y - block_h, width, block_h)
+ gfx.setColor(Color[c])
+ gfx.rectangle("fill", x, y, width, block_h)
+ gfx.setColor(Color[c + 8])
+ gfx.rectangle("fill", x, y - block_h, width, block_h)
+ gfx.setColor(Color[Color.white])
+ gfx.rectangle("line", x, y, width, block_h)
+ gfx.rectangle("line", x, y - block_h, width, block_h)
end
```
@@ -163,7 +163,7 @@ Let's see how this works. First, we set up the canvas:
```lua
can_w = width - box_w
can_h = height - pal_h - 1
-canvas = G.newCanvas(can_w, can_h)
+canvas = gfx.newCanvas(can_w, can_h)
```
The default size of a canvas would be equivalent to the screen, but we have some UI elements here, so a bit smaller makes more sense. However, this does mean we need to calculate the offsets properly when detecting clicks and displaying it.
@@ -177,12 +177,12 @@ function useCanvas(x, y, btn)
local aw = getWeight()
canvas:renderTo(function()
-- ...
- G.circle("fill", x - box_w, y, aw)
+ gfx.circle("fill", x - box_w, y, aw)
end)
end
```
-Note the _x_ coordinate, which is offset by `box_w` (the width of the side panel). When drawing, we go the opposite direction: `G.draw(canvas, box_w)`.
+Note the _x_ coordinate, which is offset by `box_w` (the width of the side panel). When drawing, we go the opposite direction: `gfx.draw(canvas, box_w)`.
### Click detection
diff --git a/src/examples/paint/main.lua b/src/examples/paint/main.lua
index dc2f19b2..01e4ef7d 100644
--- a/src/examples/paint/main.lua
+++ b/src/examples/paint/main.lua
@@ -1,4 +1,4 @@
-width, height = G.getDimensions()
+width, height = gfx.getDimensions()
--- color palette
block_w = width / 10
block_h = block_w / 2
@@ -29,7 +29,7 @@ weights = { 1, 2, 4, 5, 6, 9, 11, 13 }
--- canvas
can_w = width - box_w
can_h = height - pal_h - 1
-canvas = G.newCanvas(can_w, can_h)
+canvas = gfx.newCanvas(can_w, can_h)
--- selected
color = 0 -- black
@@ -55,42 +55,42 @@ function inWeightRange(x, y)
end
function drawBackground()
- G.setColor(Color[Color.black])
- G.rectangle("fill", 0, 0, width, height)
+ gfx.setColor(Color[Color.black])
+ gfx.rectangle("fill", 0, 0, width, height)
end
function drawPaletteOutline(y)
- G.setColor(Color[bg_color])
- G.rectangle("fill", 0, y - block_h, block_w * 2, block_h * 2)
- G.setColor(Color[Color.white])
- G.rectangle("line", 0, y - block_h, sel_w, pal_h)
- G.rectangle("line", sel_w, y - block_h, width, pal_h)
+ gfx.setColor(Color[bg_color])
+ gfx.rectangle("fill", 0, y - block_h, block_w * 2, block_h * 2)
+ gfx.setColor(Color[Color.white])
+ gfx.rectangle("line", 0, y - block_h, sel_w, pal_h)
+ gfx.rectangle("line", sel_w, y - block_h, width, pal_h)
end
function drawSelectedColor(y)
- G.setColor(Color[color])
- G.rectangle("fill", block_w / 2, y - (block_h / 2),
+ gfx.setColor(Color[color])
+ gfx.rectangle("fill", block_w / 2, y - (block_h / 2),
block_w, block_h)
-- outline
local line_color = Color.white + Color.bright
if color == line_color then
line_color = Color.black
end
- G.setColor(Color[line_color])
- G.rectangle("line", block_w / 2, y - (block_h / 2),
+ gfx.setColor(Color[line_color])
+ gfx.rectangle("line", block_w / 2, y - (block_h / 2),
block_w, block_h)
end
function drawColorBoxes(y)
for c = 0, 7 do
local x = block_w * (c + 2)
- G.setColor(Color[c])
- G.rectangle("fill", x, y, width, block_h)
- G.setColor(Color[c + 8])
- G.rectangle("fill", x, y - block_h, width, block_h)
- G.setColor(Color[Color.white])
- G.rectangle("line", x, y, width, block_h)
- G.rectangle("line", x, y - block_h, width, block_h)
+ gfx.setColor(Color[c])
+ gfx.rectangle("fill", x, y, width, block_h)
+ gfx.setColor(Color[c + 8])
+ gfx.rectangle("fill", x, y - block_h, width, block_h)
+ gfx.setColor(Color[Color.white])
+ gfx.rectangle("line", x, y, width, block_h)
+ gfx.rectangle("line", x, y - block_h, width, block_h)
end
end
@@ -102,31 +102,31 @@ function drawColorPalette()
end
function drawBrush(cx, cy)
- G.push()
- G.translate(cx, cy)
+ gfx.push()
+ gfx.translate(cx, cy)
local s = icon_d / 100 * .8
- G.scale(s, s)
- G.rotate(math.pi / 4) -- 45 degree rotation
+ gfx.scale(s, s)
+ gfx.rotate(math.pi / 4) -- 45 degree rotation
-- Draw the brush handle (wooden brown color)
- G.setColor(0.6, 0.4, 0.2)
- G.rectangle("fill", -8, -80, 16, 60)
+ gfx.setColor(0.6, 0.4, 0.2)
+ gfx.rectangle("fill", -8, -80, 16, 60)
-- Handle highlight
- G.setColor(0.8, 0.6, 0.4)
- G.rectangle("fill", -6, -75, 3, 50)
+ gfx.setColor(0.8, 0.6, 0.4)
+ gfx.rectangle("fill", -6, -75, 3, 50)
-- Metal ferrule
- G.setColor(0.7, 0.7, 0.8)
- G.rectangle("fill", -10, -25, 20, 12)
+ gfx.setColor(0.7, 0.7, 0.8)
+ gfx.rectangle("fill", -10, -25, 20, 12)
-- Ferrule shine
- G.setColor(0.9, 0.9, 1.0)
- G.rectangle("fill", -8, -24, 3, 10)
+ gfx.setColor(0.9, 0.9, 1.0)
+ gfx.rectangle("fill", -8, -24, 3, 10)
-- Bristles with smooth flame-shaped tip
- G.setColor(0.2, 0.2, 0.2)
- G.rectangle("fill", -12, -13, 24, 25)
+ gfx.setColor(0.2, 0.2, 0.2)
+ gfx.rectangle("fill", -12, -13, 24, 25)
-- Create flame tip using bezier curve
local curve = love.math.newBezierCurve(
@@ -140,38 +140,38 @@ function drawBrush(cx, cy)
)
local points = curve:render()
- G.polygon("fill", points)
+ gfx.polygon("fill", points)
- G.pop()
+ gfx.pop()
end
function drawEraser(cx, cy)
- G.push()
- G.translate(cx, cy)
+ gfx.push()
+ gfx.translate(cx, cy)
local s = icon_d / 100
- G.scale(s, s)
- G.rotate(math.pi / 4) -- 45 degree rotation
+ gfx.scale(s, s)
+ gfx.rotate(math.pi / 4) -- 45 degree rotation
-- Main eraser body (light blue)
- G.setColor(Color[Color.white])
- G.rectangle("fill", -12, -40, 24, 60)
+ gfx.setColor(Color[Color.white])
+ gfx.rectangle("fill", -12, -40, 24, 60)
-- Blue stripes running lengthwise (darker blue)
- G.setColor(Color[Color.blue])
- G.rectangle("fill", -12, -40, 6, 60)
- G.rectangle("fill", 6, -40, 6, 60)
+ gfx.setColor(Color[Color.blue])
+ gfx.rectangle("fill", -12, -40, 6, 60)
+ gfx.rectangle("fill", 6, -40, 6, 60)
-- Worn eraser tip (slightly darker)
- G.setColor(Color[Color.white + Color.bright])
- G.rectangle("fill", -12, 15, 24, 8)
+ gfx.setColor(Color[Color.white + Color.bright])
+ gfx.rectangle("fill", -12, 15, 24, 8)
-- Eraser crumbs
- G.setColor(Color[Color.white])
- G.circle("fill", 18, 25, 2)
- G.circle("fill", 22, 30, 1.5)
- G.circle("fill", 15, 32, 1)
+ gfx.setColor(Color[Color.white])
+ gfx.circle("fill", 18, 25, 2)
+ gfx.circle("fill", 22, 30, 1.5)
+ gfx.circle("fill", 15, 32, 1)
- G.pop()
+ gfx.pop()
end
-- this is a color
@@ -187,14 +187,14 @@ function drawTools()
local x = tool_midx - tb_half
local y = (i - 1) * (m_2 + tb)
if i == tool then
- G.setColor(Color[Color.black])
+ gfx.setColor(Color[Color.black])
else
- G.setColor(Color[Color.white + Color.bright])
+ gfx.setColor(Color[Color.white + Color.bright])
end
- G.rectangle("fill", x, y + m_2, tb, tb)
+ gfx.rectangle("fill", x, y + m_2, tb, tb)
- G.setColor(Color[Color.black])
- G.rectangle("line", x, y + m_2, tb, tb)
+ gfx.setColor(Color[Color.black])
+ gfx.rectangle("line", x, y + m_2, tb, tb)
local draw = tools[i]
draw(tool_midx - m_2, y + tb_half + m_4)
@@ -202,20 +202,20 @@ function drawTools()
end
function drawWeightSelector()
- G.setColor(Color[Color.white + Color.bright])
- G.rectangle("line", 0, box_h - weight_h, box_w - 1, weight_h)
+ gfx.setColor(Color[Color.white + Color.bright])
+ gfx.rectangle("line", 0, box_h - weight_h, box_w - 1, weight_h)
local h = (weight_h - (2 * margin)) / 8
local w = marg_l
for i = 0, 7 do
local y = wb_y + margin + (i * h)
local lw = i + 1
local mid = y + (h / 2)
- G.setColor(Color[Color.white + Color.bright])
- G.rectangle("fill", margin, y, w, h)
+ gfx.setColor(Color[Color.white + Color.bright])
+ gfx.rectangle("fill", margin, y, w, h)
if lw == weight then
- -- G.setColor(Color[Color.white])
- -- G.rectangle("fill", margin, y, w, h)
- G.setColor(goose)
+ -- gfx.setColor(Color[Color.white])
+ -- gfx.rectangle("fill", margin, y, w, h)
+ gfx.setColor(goose)
local rx1 = 3 * margin
local rx2 = 5 * margin
local ry1 = mid - margin
@@ -224,7 +224,7 @@ function drawWeightSelector()
local x2 = 7 * margin
local y1 = mid - m_2
local y2 = mid + m_2
- G.polygon("fill",
+ gfx.polygon("fill",
-- body
rx2, ry1,
rx1, ry1,
@@ -235,9 +235,9 @@ function drawWeightSelector()
x2, mid,
x1, y1
)
- G.setColor(Color[Color.black])
- G.setLineWidth(2)
- G.polygon("line",
+ gfx.setColor(Color[Color.black])
+ gfx.setLineWidth(2)
+ gfx.polygon("line",
-- body
rx2, ry1,
rx1, ry1,
@@ -248,22 +248,22 @@ function drawWeightSelector()
x2, mid,
x1, y1
)
- G.setLineWidth(1)
+ gfx.setLineWidth(1)
else
end
- G.setColor(Color[Color.black])
+ gfx.setColor(Color[Color.black])
local aw = weights[lw]
- G.rectangle("fill", box_w / 3, mid - (aw / 2),
+ gfx.rectangle("fill", box_w / 3, mid - (aw / 2),
box_w / 2, aw)
end
end
function drawToolbox()
--- outline
- G.setColor(Color[Color.white])
- G.rectangle("fill", 0, 0, box_w - 1, height - pal_h)
- G.setColor(Color[Color.white + Color.bright])
- G.rectangle("line", 0, 0, box_w - 1, box_h)
+ gfx.setColor(Color[Color.white])
+ gfx.rectangle("fill", 0, 0, box_w - 1, height - pal_h)
+ gfx.setColor(Color[Color.white + Color.bright])
+ gfx.rectangle("line", 0, 0, box_w - 1, box_h)
drawTools()
drawWeightSelector()
end
@@ -282,8 +282,8 @@ function drawTarget()
local x, y = love.mouse.getPosition()
if inCanvasRange(x, y) then
local aw = getWeight()
- G.setColor(Color[Color.white])
- G.circle("line", x, y, aw)
+ gfx.setColor(Color[Color.white])
+ gfx.circle("line", x, y, aw)
end
end
@@ -291,7 +291,7 @@ function love.draw()
drawBackground()
drawToolbox()
drawColorPalette()
- G.draw(canvas, box_w)
+ gfx.draw(canvas, box_w)
drawTarget()
end
@@ -327,14 +327,14 @@ function useCanvas(x, y, btn)
canvas:renderTo(function()
if btn == 1 then
if tool == 1 then
- G.setColor(Color[color])
+ gfx.setColor(Color[color])
elseif tool == 2 then
- G.setColor(Color[bg_color])
+ gfx.setColor(Color[bg_color])
end
elseif btn == 2 then
- G.setColor(Color[bg_color])
+ gfx.setColor(Color[bg_color])
end
- G.circle("fill", x - box_w, y, aw)
+ gfx.circle("fill", x - box_w, y, aw)
end)
end
diff --git a/src/examples/sine/main.lua b/src/examples/sine/main.lua
index 8348f3a1..bcfcf7f7 100644
--- a/src/examples/sine/main.lua
+++ b/src/examples/sine/main.lua
@@ -1,20 +1,20 @@
-local G = love.graphics
+local gfx = love.graphics
local x0 = 0
-local xe = G.getWidth()
+local xe = gfx.getWidth()
local y0 = 0
-local ye = G.getHeight()
+local ye = gfx.getHeight()
local xh = xe / 2
local yh = ye / 2
-G.setColor(1, 1, 1, 0.5)
-G.setLineWidth(1)
-G.line(xh, y0, xh, ye)
-G.line(x0, yh, xe, yh)
+gfx.setColor(1, 1, 1, 0.5)
+gfx.setLineWidth(1)
+gfx.line(xh, y0, xh, ye)
+gfx.line(x0, yh, xe, yh)
-G.setColor(1, 0, 0)
-G.setPointSize(2)
+gfx.setColor(1, 0, 0)
+gfx.setPointSize(2)
local amp = 100
local times = 2
@@ -27,4 +27,4 @@ for x = 0, xe do
table.insert(points, y)
end
-G.points(points)
+gfx.points(points)
diff --git a/src/examples/tixy/README.md b/src/examples/tixy/README.md
index d5ac52bd..77dca387 100644
--- a/src/examples/tixy/README.md
+++ b/src/examples/tixy/README.md
@@ -132,14 +132,14 @@ end
```lua
function drawCircle(color, radius, x, y)
- G.setColor(color)
- G.circle(
+ gfx.setColor(color)
+ gfx.circle(
"fill",
x * (size + spacing) + offset,
y * (size + spacing) + offset,
radius
)
- G.circle(
+ gfx.circle(
"line",
x * (size + spacing) + offset,
y * (size + spacing) + offset,
diff --git a/src/examples/tixy/examples.lua b/src/examples/tixy/examples.lua
index 963ffdf2..618eb7f5 100644
--- a/src/examples/tixy/examples.lua
+++ b/src/examples/tixy/examples.lua
@@ -1,4 +1,4 @@
-examples = {}
+local examples = {}
function example(c, l)
table.insert(examples, {
@@ -98,3 +98,5 @@ example(
"return (x-5)^2 + (y-5)^2 - 99*sin(t)",
"create your own!"
)
+
+return examples
diff --git a/src/examples/tixy/main.lua b/src/examples/tixy/main.lua
index b89da9b8..da687455 100644
--- a/src/examples/tixy/main.lua
+++ b/src/examples/tixy/main.lua
@@ -1,10 +1,10 @@
-local G = love.graphics
+local gfx = love.graphics
math.randomseed(os.time())
-cw, ch = G.getDimensions()
+cw, ch = gfx.getDimensions()
midx = cw / 2
require("math")
-require("examples")
+examples = require("examples")
size = 28
spacing = 3
@@ -44,7 +44,6 @@ function advance()
load_example(e)
if ex_idx < #examples then
ex_idx = ex_idx + 1
- time = 0
end
end
@@ -53,7 +52,6 @@ function retreat()
local e = examples[ex_idx]
load_example(e)
ex_idx = ex_idx - 1
- time = 0
end
end
@@ -96,24 +94,25 @@ function setupTixy()
local f = loadstring(code)
if f then
setfenv(f, _G)
+ time = 0
tixy = f()
end
end
function drawBackground()
- G.setColor(colors.bg)
- G.rectangle("fill", 0, 0, cw, ch)
+ gfx.setColor(colors.bg)
+ gfx.rectangle("fill", 0, 0, cw, ch)
end
function drawCircle(color, radius, x, y)
- G.setColor(color)
- G.circle(
+ gfx.setColor(color)
+ gfx.circle(
"fill",
x * (size + spacing) + offset,
y * (size + spacing) + offset,
radius
)
- G.circle(
+ gfx.circle(
"line",
x * (size + spacing) + offset,
y * (size + spacing) + offset,
@@ -148,14 +147,14 @@ function drawOutput()
end
function drawText()
- G.setColor(colors.text)
+ gfx.setColor(colors.text)
local sof = (size / 2) + offset
local hof = sof / 2
- G.printf(legend, midx + hof, sof, midx - sof)
+ gfx.printf(legend, midx + hof, sof, midx - sof)
if showHelp then
- G.setColor(colors.help)
- G.setFont(font)
- G.printf(help, midx + hof, ch - (5 * sof), midx - sof)
+ gfx.setColor(colors.help)
+ gfx.setFont(font)
+ gfx.printf(help, midx + hof, ch - (5 * sof), midx - sof)
end
end
diff --git a/src/examples/turtle/README.md b/src/examples/turtle/README.md
index bc114c43..8704ece6 100644
--- a/src/examples/turtle/README.md
+++ b/src/examples/turtle/README.md
@@ -29,11 +29,11 @@ For the most simple example of this, let's represent the turtle with only an ell
local x_r = 15
local y_r = 20
function turtleA(x, y)
- G.ellipse("fill", x, y, x_r, y_r, 100)
+ gfx.ellipse("fill", x, y, x_r, y_r, 100)
end
function turtleB(x, y)
- G.translate(x, y)
- G.ellipse("fill", 0, 0, x_r, y_r, 100)
+ gfx.translate(x, y)
+ gfx.ellipse("fill", 0, 0, x_r, y_r, 100)
end
```
We can draw it at (x, y) either by drawing the shape to (x, y), or first translating the whole drawing to (x, y), and drawing at (0, 0). This might not seem that big of a deal in this simple case, but when the number of transformations and shapes go up, things cat get hard to track very quickly.
@@ -45,7 +45,7 @@ The way we translate this for LOVE is an "x radius" and a "y radius".
In o
Next, we are adding the turtle's head, which is in some sort of relation to it's body, but also the location where the whole drawing is.
```lua
-G.circle("fill", 0, ((0 - y_r) - head_r) + neck, head_r, 100)
+gfx.circle("fill", 0, ((0 - y_r) - head_r) + neck, head_r, 100)
```
Using the second method, we are able to provide the head position in "turtle coordinates".
So far, there's nothing about this we couldn't have done the other route, but let's proceed to the legs, which we want to draw at an angle. LOVE doesn't provide us any way to do this with only the ellipse function, we do need to `rotate` first.
@@ -53,15 +53,15 @@ So far, there's nothing about this we couldn't have done the other route, but le
See this condensed example:
```lua
function frontLeftLeg(x, y, x_r, y_r, leg_xr, leg_yr)
- G.setColor(Color[Color.green + Color.bright])
+ gfx.setColor(Color[Color.green + Color.bright])
--- move to the turtle's position
- G.translate(x, y)
+ gfx.translate(x, y)
--- move to where the leg attaches to the body
- G.translate(-x_r, -y_r / 2 - leg_xr)
+ gfx.translate(-x_r, -y_r / 2 - leg_xr)
--- rotate
- G.rotate(-math.pi / 4)
+ gfx.rotate(-math.pi / 4)
--- draw the leg
- G.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
+ gfx.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
end
```
@@ -77,17 +77,17 @@ You will notice that the actual code does not look like that. For one, in the le
Another, more interesting difference is the `push()` - `pop()` pairs around each leg.
```lua
--- left front leg
-G.push("all")
-G.translate(-x_r, -y_r / 2 - leg_xr)
-G.rotate(-math.pi / 4)
-G.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
-G.pop()
+gfx.push("all")
+gfx.translate(-x_r, -y_r / 2 - leg_xr)
+gfx.rotate(-math.pi / 4)
+gfx.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
+gfx.pop()
--- right front leg
-G.push("all")
-G.translate(x_r, -y_r / 2 - leg_xr)
-G.rotate(math.pi / 4)
-G.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
-G.pop()
+gfx.push("all")
+gfx.translate(x_r, -y_r / 2 - leg_xr)
+gfx.rotate(math.pi / 4)
+gfx.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
+gfx.pop()
```
Say we are done drawing the left leg, and now we want to proceed to drawing the other one. We could do the opposite transformations to go back to "zero", or transform from our current state to the desired one, but that leads to more complicated math and less readable code.
Instead, we work in stages. When done with the first leg, we can "reset" to our previous state (the "turtle coordinates"), and set up our next one again relative to the center.
diff --git a/src/examples/turtle/action.lua b/src/examples/turtle/action.lua
index e02c700d..1d62f00f 100644
--- a/src/examples/turtle/action.lua
+++ b/src/examples/turtle/action.lua
@@ -14,11 +14,11 @@ function moveRight(d)
tx = tx + (d or (2 * incr))
end
-function pause(msg)
+function pause_game(msg)
pause(msg or "user paused the game")
end
-actions = {
+local actions = {
forward = moveForward,
fd = moveForward,
back = moveBack,
@@ -27,5 +27,7 @@ actions = {
l = moveLeft,
right = moveRight,
r = moveRight,
- pause = pause
+ pause = pause_game
}
+
+return actions
diff --git a/src/examples/turtle/drawing.lua b/src/examples/turtle/drawing.lua
index 2327153f..5c5a2077 100644
--- a/src/examples/turtle/drawing.lua
+++ b/src/examples/turtle/drawing.lua
@@ -1,6 +1,6 @@
-local G = love.graphics
+gfx = love.graphics
-font = G.newFont()
+font = gfx.newFont()
bg_color = Color.black
body_color = Color.green
limb_color = body_color + Color.bright
@@ -14,45 +14,45 @@ function drawBackground(color)
if color_valid then
c = color
end
- G.setColor(Color[c])
- G.rectangle("fill", 0, 0, width, height)
+ gfx.setColor(Color[c])
+ gfx.rectangle("fill", 0, 0, width, height)
end
function drawFrontLegs(x_r, y_r, leg_xr, leg_yr)
- G.setColor(Color[limb_color])
- G.push("all")
- G.translate(-x_r, -y_r / 2 - leg_xr)
- G.rotate(-math.pi / 4)
- G.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
- G.pop()
- G.push("all")
- G.translate(x_r, -y_r / 2 - leg_xr)
- G.rotate(math.pi / 4)
- G.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
- G.pop()
+ gfx.setColor(Color[limb_color])
+ gfx.push("all")
+ gfx.translate(-x_r, -y_r / 2 - leg_xr)
+ gfx.rotate(-math.pi / 4)
+ gfx.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
+ gfx.pop()
+ gfx.push("all")
+ gfx.translate(x_r, -y_r / 2 - leg_xr)
+ gfx.rotate(math.pi / 4)
+ gfx.ellipse("fill", 0, 0, leg_xr, leg_yr, 100)
+ gfx.pop()
end
function drawHindLegs(x_r, y_r, leg_r, leg_yr)
- G.setColor(Color[limb_color])
- G.push("all")
- G.translate(-x_r, y_r / 2 + leg_r)
- G.rotate(math.pi / 4)
- G.ellipse("fill", 0, 0, leg_r, leg_yr, 100)
- G.pop()
- G.push("all")
- G.translate(x_r, y_r / 2 + leg_r)
- G.rotate(-math.pi / 4)
- G.ellipse("fill", 0, 0, leg_r, leg_yr, 100)
- G.pop()
+ gfx.setColor(Color[limb_color])
+ gfx.push("all")
+ gfx.translate(-x_r, y_r / 2 + leg_r)
+ gfx.rotate(math.pi / 4)
+ gfx.ellipse("fill", 0, 0, leg_r, leg_yr, 100)
+ gfx.pop()
+ gfx.push("all")
+ gfx.translate(x_r, y_r / 2 + leg_r)
+ gfx.rotate(-math.pi / 4)
+ gfx.ellipse("fill", 0, 0, leg_r, leg_yr, 100)
+ gfx.pop()
end
function drawBody(x_r, y_r, head_r)
--- body
- G.setColor(Color[body_color])
- G.ellipse("fill", 0, 0, x_r, y_r, 100)
+ gfx.setColor(Color[body_color])
+ gfx.ellipse("fill", 0, 0, x_r, y_r, 100)
--- head
local neck = 5
- G.circle("fill", 0, ((0 - y_r) - head_r) + neck, head_r, 100)
+ gfx.circle("fill", 0, ((0 - y_r) - head_r) + neck, head_r, 100)
--- end
end
@@ -62,24 +62,24 @@ function drawTurtle(x, y)
local leg_yr = 10
local x_r = 15
local y_r = 20
- G.push("all")
- G.translate(x, y)
+ gfx.push("all")
+ gfx.translate(x, y)
drawFrontLegs(x_r, y_r, leg_xr, leg_yr)
drawHindLegs(x_r, y_r, leg_xr, leg_yr)
drawBody(x_r, y_r, head_r)
- G.pop()
+ gfx.pop()
end
function drawHelp()
- G.setColor(Color[Color.white])
- G.print("Press [I] to open console", 20, 20)
+ gfx.setColor(Color[Color.white])
+ gfx.print("Press [I] to open console", 20, 20)
local help = "Enter 'forward', 'back', 'left', or 'right'" ..
"to move the turtle!"
- G.print(help, 20, 50)
+ gfx.print(help, 20, 50)
end
function drawDebuginfo()
- G.setColor(Color[debug_color])
+ gfx.setColor(Color[debug_color])
local dt = string.format("Turtle position: (%d, %d)", tx, ty)
- G.print(dt, width - 200, 20)
+ gfx.print(dt, width - 200, 20)
end
diff --git a/src/examples/turtle/main.lua b/src/examples/turtle/main.lua
index 1833a4c9..7e570807 100644
--- a/src/examples/turtle/main.lua
+++ b/src/examples/turtle/main.lua
@@ -1,7 +1,7 @@
-require("action")
+actions = require("action")
require("drawing")
-width, height = love.graphics.getDimensions()
+width, height = gfx.getDimensions()
midx = width / 2
midy = height / 2
incr = 10
@@ -19,7 +19,7 @@ function eval(input)
end
function love.draw()
- G.setFont(font)
+ gfx.setFont(font)
drawBackground()
drawHelp()
drawTurtle(tx, ty)
diff --git a/src/harmony/init.lua b/src/harmony/init.lua
index 111aff9f..07518df0 100644
--- a/src/harmony/init.lua
+++ b/src/harmony/init.lua
@@ -83,10 +83,10 @@ local function new(_lock)
if love.update then love.update(dt) end
if love.graphics and love.graphics.isActive() then
- local G = love.graphics
- G.origin()
- G.clear(
- G.getBackgroundColor()
+ local gfx = love.graphics
+ gfx.origin()
+ gfx.clear(
+ gfx.getBackgroundColor()
)
if love.draw then love.draw() end
@@ -150,7 +150,7 @@ local function utils()
if not love.harmony then return end
if love.harmony.utils then return end
- G = love.graphics
+ local gfx = love.graphics
--- @param name love.Event
local love_event = function(name, ...)
@@ -182,6 +182,9 @@ local function utils()
lgui = false,
rgui = false,
}
+ local shortcuts = {
+ toggle = 'C-t'
+ }
--- @param tag string
@@ -203,7 +206,7 @@ local function utils()
FS.mkdirp(dir)
--- @param img_data love.ImageData
- G.captureScreenshot(function(img_data)
+ gfx.captureScreenshot(function(img_data)
if img_data then
local from = FS.join_path(
love.filesystem.getSaveDirectory(), fn
@@ -234,6 +237,7 @@ local function utils()
--- @field love_text function
--- @field screenshot function
--- @field release_keys function
+ --- @field shortcuts table
return {
patch_isDown = function()
local down = love.keyboard.isDown
@@ -291,6 +295,8 @@ local function utils()
release_keys = release_keys,
+ shortcuts = shortcuts,
+
screenshot = function(tag)
timer:script(function(wait)
wait(frame_time)
@@ -362,6 +368,7 @@ local function runner()
end)
scrun = coroutine.create(function()
+ -- timer = Timer.new()
for _, v in ipairs(scenarios) do
local tag = v.id
local sc = v.sc
diff --git a/src/harmony/scenarios/editor.lua b/src/harmony/scenarios/editor.lua
index c175b393..b9d1e64a 100644
--- a/src/harmony/scenarios/editor.lua
+++ b/src/harmony/scenarios/editor.lua
@@ -131,14 +131,14 @@ local function editor()
wait(.1)
h.screenshot('open')
wait(.2)
- h.love_key('f8')
+ h.love_key(h.shortcuts.toggle)
wait(2)
wait(.1)
h.screenshot('before')
wait(.2)
- h.love_key('f8')
+ h.love_key(h.shortcuts.toggle)
wait(.2)
h.love_key('C-home')
wait(.1)
@@ -199,12 +199,12 @@ local function editor()
wait(.1)
h.screenshot('edited')
wait(.2)
- h.love_key('f8')
+ h.love_key(h.shortcuts.toggle)
wait(.1)
h.screenshot('after')
wait(.4)
- h.love_key('f8')
+ h.love_key(h.shortcuts.toggle)
wait(.1)
h.love_key('C-f')
wait(.01)
@@ -232,7 +232,7 @@ local function editor()
wait(.1)
h.screenshot('edited-2')
wait(.2)
- h.love_key('f8')
+ h.love_key(h.shortcuts.toggle)
wait(.1)
h.screenshot('after-2')
diff --git a/src/lib/djot/djot.lua b/src/lib/djot/djot.lua
index f9a84eb3..3dfbc267 100644
--- a/src/lib/djot/djot.lua
+++ b/src/lib/djot/djot.lua
@@ -60,7 +60,7 @@ end
--- @param sourcepos (boolean?) if true, source positions are included in the AST
--- @param warn (function?) function that processes a warning, accepting a warning
--- object with `pos` and `message` fields.
---- @return (AST)
+--- @return (djotAST)
local function parse(input, sourcepos, warn)
local parser = Parser:new(input, warn)
return ast.to_ast(parser, sourcepos or false)
@@ -82,7 +82,7 @@ local function parse_events(input, warn)
end
--- Render a document's AST in human-readable form.
---- @param doc (AST) the AST
+--- @param doc (djotAST) the AST
--- @return (string) rendered AST
local function render_ast_pretty(doc)
local handle = StringHandle:new()
@@ -91,14 +91,14 @@ local function render_ast_pretty(doc)
end
--- Render a document's AST in JSON.
---- @param doc (AST) the AST
+--- @param doc (djotAST) the AST
--- @return (string) rendered AST (JSON string)
local function render_ast_json(doc)
return json.encode(doc) .. "\n"
end
--- Render a document as HTML.
---- @param doc (AST) the AST
+--- @param doc (djotAST) the AST
--- @return (string) rendered document (HTML string)
local function render_html(doc)
local handle = StringHandle:new()
diff --git a/src/lib/djot/djot/ast.lua b/src/lib/djot/djot/ast.lua
index cd875b5a..85cd2a2d 100644
--- a/src/lib/djot/djot/ast.lua
+++ b/src/lib/djot/djot/ast.lua
@@ -5,10 +5,10 @@
--- @field class? string
--- @field id? string
---- @class AST
+--- @class djotAST
--- @field t string tag for the node
--- @field s? string text for the node
---- @field c AST[] child node
+--- @field c djotAST[] child node
--- @field alias string
--- @field level integer
--- @field startidx integer
@@ -236,7 +236,7 @@ end, function(k) return displaykeys[k] or k end)
--- Create a new AST node.
--- @param tag (string) tag for the node
---- @return (AST) node (table)
+--- @return (djotAST) node (table)
local function new_node(tag)
local node = { t = tag, c = nil }
setmetatable(node, mt)
@@ -244,8 +244,8 @@ local function new_node(tag)
end
--- Add `child` as a child of `node`.
---- @param node (AST) node parent node
---- @param child (AST) node child node
+--- @param node (djotAST) node parent node
+--- @param child (djotAST) node child node
local function add_child(node, child)
if (not node.c) then
node.c = { child }
@@ -255,7 +255,7 @@ local function add_child(node, child)
end
--- Returns true if `node` has children.
---- @param node (AST) node to check
+--- @param node (djotAST) node to check
--- @return (boolean) true if node has children
local function has_children(node)
return (node.c and #node.c > 0)
@@ -303,8 +303,8 @@ local function copy_attributes(target, source)
end
end
---- @param targetnode (AST)
---- @param cs (AST)
+--- @param targetnode (djotAST)
+--- @param cs (djotAST)
local function insert_attributes_from_nodes(targetnode, cs)
targetnode.attr = targetnode.attr or new_attributes()
local i = 1
@@ -325,7 +325,7 @@ local function insert_attributes_from_nodes(targetnode, cs)
end
end
---- @param node (AST)
+--- @param node (djotAST)
local function make_definition_list_item(node)
node.t = "definition_list_item"
if not has_children(node) then
@@ -416,7 +416,7 @@ local function to_ast(parser, sourcepos)
local subject = parser.subject
local warn = parser.warn
if not warn then
- warn = function() end
+ warn = function(o) end
end
local sourceposmap
if sourcepos then
@@ -669,7 +669,7 @@ local function to_ast(parser, sourcepos)
elseif node.t == "attributes" then
-- parse attributes, add to last node
local tip = containers[#containers]
- --- @type AST|false
+ --- @type djotAST|false
local prevnode = has_children(tip) and tip.c[#tip.c]
if prevnode then
local endswithspace = false
@@ -987,7 +987,7 @@ end
--- Render an AST in human-readable form, with indentation
--- showing the hierarchy.
---- @param doc (AST) djot AST
+--- @param doc (djotAST) djot AST
--- @param handle (StringHandle) handle to which to write content
local function render(doc, handle)
render_node(doc, handle, 0)
diff --git a/src/lib/djot/djot/filter.lua b/src/lib/djot/djot/filter.lua
index bd28fc2b..f9fd4fd3 100644
--- a/src/lib/djot/djot/filter.lua
+++ b/src/lib/djot/djot/filter.lua
@@ -104,7 +104,7 @@ local function traverse(node, filterpart)
end
--- Apply a filter to a document.
---- @param node (AST)
+--- @param node (djotAST)
--- @param filter table the filter to apply
local function apply_filter(node, filter)
for _, filterpart in ipairs(filter) do
diff --git a/src/lib/hump/timer.lua b/src/lib/hump/timer.lua
index cd425bb8..f7f9933a 100644
--- a/src/lib/hump/timer.lua
+++ b/src/lib/hump/timer.lua
@@ -22,7 +22,7 @@ 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.
-]]--
+]] --
local Timer = {}
Timer.__index = Timer
@@ -31,219 +31,220 @@ local function _nothing_() end
local unpack = unpack or table.unpack
local function updateTimerHandle(handle, dt)
- -- handle: {
- -- time = ,
- -- after = ,
- -- during = ,
- -- limit = ,
- -- count = ,
- -- }
- handle.time = handle.time + dt
- handle.during(dt, math.max(handle.limit - handle.time, 0))
-
- while handle.time >= handle.limit and handle.count > 0 do
- if handle.after(handle.after) == false then
- handle.count = 0
- break
- end
- handle.time = handle.time - handle.limit
- handle.count = handle.count - 1
- end
+ -- handle: {
+ -- time = ,
+ -- after = ,
+ -- during = ,
+ -- limit = ,
+ -- count = ,
+ -- }
+ handle.time = handle.time + dt
+ handle.during(dt, math.max(handle.limit - handle.time, 0))
+
+ while handle.time >= handle.limit and handle.count > 0 do
+ if handle.after(handle.after) == false then
+ handle.count = 0
+ break
+ end
+ handle.time = handle.time - handle.limit
+ handle.count = handle.count - 1
+ end
end
function Timer:update(dt)
- -- timers may create new timers, which leads to undefined behavior
- -- in pairs() - so we need to put them in a different table first
- local to_update = {}
- for handle in pairs(self.functions) do
- to_update[handle] = handle
- end
-
- for handle in pairs(to_update) do
- if self.functions[handle] then
- updateTimerHandle(handle, dt)
- if handle.count == 0 then
- self.functions[handle] = nil
- end
- end
- end
+ -- timers may create new timers, which leads to undefined behavior
+ -- in pairs() - so we need to put them in a different table first
+ local to_update = {}
+ for handle in pairs(self.functions) do
+ to_update[handle] = handle
+ end
+
+ for handle in pairs(to_update) do
+ if self.functions[handle] then
+ updateTimerHandle(handle, dt)
+ if handle.count == 0 then
+ self.functions[handle] = nil
+ end
+ end
+ end
end
function Timer:during(delay, during, after)
- local handle = { time = 0, during = during, after = after or _nothing_, limit = delay, count = 1 }
- self.functions[handle] = true
- return handle
+ local handle = { time = 0, during = during, after = after or _nothing_, limit = delay, count = 1 }
+ self.functions[handle] = true
+ return handle
end
function Timer:after(delay, func)
- return self:during(delay, _nothing_, func)
+ return self:during(delay, _nothing_, func)
end
function Timer:every(delay, after, count)
- local count = count or math.huge -- exploit below: math.huge - 1 = math.huge
- local handle = { time = 0, during = _nothing_, after = after, limit = delay, count = count }
- self.functions[handle] = true
- return handle
+ local count = count or math.huge -- exploit below: math.huge - 1 = math.huge
+ local handle = { time = 0, during = _nothing_, after = after, limit = delay, count = count }
+ self.functions[handle] = true
+ return handle
end
function Timer:cancel(handle)
- self.functions[handle] = nil
+ self.functions[handle] = nil
end
function Timer:clear()
- self.functions = {}
+ self.functions = {}
end
function Timer:script(f)
- local co = coroutine.wrap(f)
- co(function(t)
- self:after(t, co)
- coroutine.yield()
- end)
+ local co = coroutine.wrap(f)
+ co(function(t)
+ self:after(t, co)
+ coroutine.yield()
+ end)
end
local function func_tween(tween, self, len, subject, target, method, after,
- setters_and_getters, ...)
- -- recursively collects fields that are defined in both subject and target into a flat list
- -- re-use of ref is confusing
- local to_func_tween = {}
- local function set_and_get(subject, k, v)
- setters_and_getters = setters_and_getters or {}
-
- local setter, getter
- if setters_and_getters[k] then
- setter, getter = unpack(setters_and_getters[k])
- else
- setter = subject['set'..k]
- getter = subject['get'..k]
+ setters_and_getters, ...)
+ -- recursively collects fields that are defined in both subject and target into a flat list
+ -- re-use of ref is confusing
+ local to_func_tween = {}
+ local function set_and_get(subject, k, v)
+ setters_and_getters = setters_and_getters or {}
+
+ local setter, getter
+ if setters_and_getters[k] then
+ setter, getter = unpack(setters_and_getters[k])
+ else
+ setter = subject['set' .. k]
+ getter = subject['get' .. k]
+ end
+ assert(setter and getter,
+ "key's value in subject is nil with no set/getter")
+
+ if to_func_tween[subject] == nil then
+ to_func_tween[subject] = {}
+ end
+
+ local ref = { getter(subject) }
+ to_func_tween[subject][k] = { ref, setter }
+ if type(v) == 'number' or #ref == 1 then
+ v = { v }
+ end
+ return ref, v
+ end
+
+ local function tween_collect_payload(subject, target, out)
+ for k, v in pairs(target) do
+ -- this might not be the smoothest way to do this
+ local ref = subject[k]
+ if ref == nil then
+ ref, v = set_and_get(subject, k, v)
end
- assert(setter and getter,
- "key's value in subject is nil with no set/getter")
-
- if to_func_tween[subject] == nil then
- to_func_tween[subject] = {}
- end
-
- ref = {getter(subject)}
- to_func_tween[subject][k] = {ref, setter}
- if type(v) == 'number' or #ref == 1 then
- v = {v}
- end
- return ref, v
- end
-
- local function tween_collect_payload(subject, target, out)
- for k,v in pairs(target) do
-
- -- this might not be the smoothest way to do this
- local ref = subject[k]
- if ref == nil then
- ref, v = set_and_get(subject, k, v)
- end
- assert(type(v) == type(ref), 'Type mismatch in field "'..k..'". '
- ..type(v)..' vs '.. type(ref))
- if type(v) == 'table' then
- tween_collect_payload(ref, v, out)
- else
- local ok, delta = pcall(function() return (v-ref)*1 end)
- assert(ok, 'Field "'..k..'" does not support arithmetic operations')
- out[#out+1] = {subject, k, delta}
- end
- end
- return out
- end
-
- method = tween[method or 'linear'] -- see __index
- local payload, t, args = tween_collect_payload(subject, target, {}), 0, {...}
-
- local last_s = 0
- return self:during(len, function(dt)
- t = t + dt
- local s = method(math.min(1, t/len), unpack(args))
- local ds = s - last_s
- last_s = s
- for _, info in ipairs(payload) do
- local ref, key, delta = unpack(info)
- ref[key] = ref[key] + delta * ds
+ assert(type(v) == type(ref), 'Type mismatch in field "' .. k .. '". '
+ .. type(v) .. ' vs ' .. type(ref))
+ if type(v) == 'table' then
+ tween_collect_payload(ref, v, out)
+ else
+ local ok, delta = pcall(function() return (v - ref) * 1 end)
+ assert(ok, 'Field "' .. k .. '" does not support arithmetic operations')
+ out[#out + 1] = { subject, k, delta }
end
- for ref, t in pairs(to_func_tween) do
- for key, value in pairs(t) do
- local setter_args, setter = unpack(value)
- if not pcall(function() setter(ref, unpack(setter_args)) end) then
- setter(unpack(setter_args))
- end
- end
+ end
+ return out
+ end
+
+ method = tween[method or 'linear'] -- see __index
+ local payload, t, args = tween_collect_payload(subject, target, {}), 0, { ... }
+
+ local last_s = 0
+ return self:during(len, function(dt)
+ t = t + dt
+ local s = method(math.min(1, t / len), unpack(args))
+ local ds = s - last_s
+ last_s = s
+ for _, info in ipairs(payload) do
+ local ref, key, delta = unpack(info)
+ ref[key] = ref[key] + delta * ds
+ end
+ for ref, t in pairs(to_func_tween) do
+ for key, value in pairs(t) do
+ local setter_args, setter = unpack(value)
+ if not pcall(function() setter(ref, unpack(setter_args)) end) then
+ setter(unpack(setter_args))
+ end
end
- end, after)
+ end
+ end, after)
end
local function plain_tween(tween, self, len, subject, target, method, after, ...)
- return func_tween(tween, self, len, subject, target, method, after, nil, ...)
+ return func_tween(tween, self, len, subject, target, method, after, nil, ...)
end
local function def_tween(func)
- return setmetatable(
- {
- -- helper functions
- out = function(f) -- 'rotates' a function
- return function(s, ...) return 1 - f(1-s, ...) end
- end,
- chain = function(f1, f2) -- concatenates two functions
- return function(s, ...) return (s < .5 and f1(2*s, ...) or 1 + f2(2*s-1, ...)) * .5 end
- end,
-
- -- useful tweening functions
- linear = function(s) return s end,
- quad = function(s) return s*s end,
- cubic = function(s) return s*s*s end,
- quart = function(s) return s*s*s*s end,
- quint = function(s) return s*s*s*s*s end,
- sine = function(s) return 1-math.cos(s*math.pi/2) end,
- expo = function(s) return 2^(10*(s-1)) end,
- circ = function(s) return 1 - math.sqrt(1-s*s) end,
-
- back = function(s,bounciness)
- bounciness = bounciness or 1.70158
- return s*s*((bounciness+1)*s - bounciness)
- end,
-
- bounce = function(s) -- magic numbers ahead
- local a,b = 7.5625, 1/2.75
- return math.min(a*s^2, a*(s-1.5*b)^2 + .75, a*(s-2.25*b)^2 + .9375, a*(s-2.625*b)^2 + .984375)
- end,
-
- elastic = function(s, amp, period)
- amp, period = amp and math.max(1, amp) or 1, period or .3
- return (-amp * math.sin(2*math.pi/period * (s-1) - math.asin(1/amp))) * 2^(10*(s-1))
- end,
-
-
- }, {
-
- -- register new tween
- __call = func,
-
- -- fetches function and generated compositions for method `key`
- __index = function(tweens, key)
- if type(key) == 'function' then return key end
-
- assert(type(key) == 'string', 'Method must be function or string.')
- if rawget(tweens, key) then return rawget(tweens, key) end
-
- local function construct(pattern, f)
- local method = rawget(tweens, key:match(pattern))
- if method then return f(method) end
- return nil
- end
-
- local out, chain = rawget(tweens,'out'), rawget(tweens,'chain')
- return construct('^in%-([^-]+)$', function(...) return ... end)
- or construct('^out%-([^-]+)$', out)
- or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
- or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
- or error('Unknown interpolation method: ' .. key)
- end})
+ return setmetatable(
+ {
+ -- helper functions
+ out = function(f) -- 'rotates' a function
+ return function(s, ...) return 1 - f(1 - s, ...) end
+ end,
+ chain = function(f1, f2) -- concatenates two functions
+ return function(s, ...) return (s < .5 and f1(2 * s, ...) or 1 + f2(2 * s - 1, ...)) * .5 end
+ end,
+
+ -- useful tweening functions
+ linear = function(s) return s end,
+ quad = function(s) return s * s end,
+ cubic = function(s) return s * s * s end,
+ quart = function(s) return s * s * s * s end,
+ quint = function(s) return s * s * s * s * s end,
+ sine = function(s) return 1 - math.cos(s * math.pi / 2) end,
+ expo = function(s) return 2 ^ (10 * (s - 1)) end,
+ circ = function(s) return 1 - math.sqrt(1 - s * s) end,
+
+ back = function(s, bounciness)
+ bounciness = bounciness or 1.70158
+ return s * s * ((bounciness + 1) * s - bounciness)
+ end,
+
+ bounce = function(s) -- magic numbers ahead
+ local a, b = 7.5625, 1 / 2.75
+ return math.min(a * s ^ 2, a * (s - 1.5 * b) ^ 2 + .75, a * (s - 2.25 * b) ^ 2 + .9375, a * (s - 2.625 * b) ^ 2 +
+ .984375)
+ end,
+
+ elastic = function(s, amp, period)
+ amp, period = amp and math.max(1, amp) or 1, period or .3
+ return (-amp * math.sin(2 * math.pi / period * (s - 1) - math.asin(1 / amp))) * 2 ^ (10 * (s - 1))
+ end,
+
+
+ }, {
+
+ -- register new tween
+ __call = func,
+
+ -- fetches function and generated compositions for method `key`
+ __index = function(tweens, key)
+ if type(key) == 'function' then return key end
+
+ assert(type(key) == 'string', 'Method must be function or string.')
+ if rawget(tweens, key) then return rawget(tweens, key) end
+
+ local function construct(pattern, f)
+ local method = rawget(tweens, key:match(pattern))
+ if method then return f(method) end
+ return nil
+ end
+
+ local out, chain = rawget(tweens, 'out'), rawget(tweens, 'chain')
+ return construct('^in%-([^-]+)$', function(...) return ... end)
+ or construct('^out%-([^-]+)$', out)
+ or construct('^in%-out%-([^-]+)$', function(f) return chain(f, out(f)) end)
+ or construct('^out%-in%-([^-]+)$', function(f) return chain(out(f), f) end)
+ or error('Unknown interpolation method: ' .. key)
+ end
+ })
end
@@ -252,7 +253,7 @@ Timer.func_tween = def_tween(func_tween)
-- Timer instancing
function Timer.new()
- return setmetatable({functions = {}, tween = Timer.tween}, Timer)
+ return setmetatable({ functions = {}, tween = Timer.tween }, Timer)
end
-- default instance
@@ -261,14 +262,14 @@ local default = Timer.new()
-- module forwards calls to default instance
local module = {}
for k in pairs(Timer) do
- if k ~= "__index" then
- module[k] = function(...) return default[k](default, ...) end
- end
+ if k ~= "__index" then
+ module[k] = function(...) return default[k](default, ...) end
+ end
end
module.tween = setmetatable({}, {
- __index = Timer.tween,
- __newindex = function(k,v) Timer.tween[k] = v end,
- __call = function(t, ...) return default:tween(...) end,
+ __index = Timer.tween,
+ __newindex = function(k, v) Timer.tween[k] = v end,
+ __call = function(t, ...) return default:tween(...) end,
})
-return setmetatable(module, {__call = Timer.new})
+return setmetatable(module, { __call = Timer.new })
diff --git a/src/main.lua b/src/main.lua
index 5ccc3c05..8c24ad5a 100644
--- a/src/main.lua
+++ b/src/main.lua
@@ -1,5 +1,6 @@
local redirect_to = require("model.io.redirect")
local OS = require("util.os")
+
require("model.consoleModel")
require("controller.controller")
require("controller.consoleController")
@@ -15,7 +16,7 @@ local FS = require("util.filesystem")
require("lib.error_explorer")
-G = love.graphics
+local gfx = love.graphics
local messages = {
how_to_exit = 'Press Ctrl-Esc to exit',
@@ -45,11 +46,11 @@ local config_view = function(flags)
local font_dir = "assets/fonts/"
local mf = "ubuntu_mono_bold_nerd.ttf"
- local font_main = G.newFont(
+ local font_main = gfx.newFont(
font_dir .. mf, font_size)
- local font_icon = G.newFont(
+ local font_icon = gfx.newFont(
font_dir .. "SFMonoNerdFontMono-Regular.otf", font_size)
- local font_cjk = G.newFont(
+ local font_cjk = gfx.newFont(
font_dir .. "SarasaGothicJ-Bold.ttf", font_size * (2 / 3))
font_main:setFallbacks(font_icon, font_cjk)
@@ -64,9 +65,9 @@ local config_view = function(flags)
local lines = 16
local input_max = 14
- local font_labels = G.newFont(font_dir .. mf, 12)
- local w = love.fixWidth or G.getWidth()
- local h = love.fixHeight or G.getHeight()
+ local font_labels = gfx.newFont(font_dir .. mf, 12)
+ local w = love.fixWidth or gfx.getWidth()
+ local h = love.fixHeight or gfx.getHeight()
local eh = h - 2 * fh
local debugheight = math.floor(eh / (love.test_grid_y * fh))
local debugwidth = math.floor(w / love.test_grid_x) / fw
@@ -104,6 +105,7 @@ local config_view = function(flags)
drawableWidth = drawableWidth,
drawableChars = drawableChars,
+ fold_lines = 1,
drawtest = tf.draw,
sizedebug = tf.size,
}
diff --git a/src/model/canvasModel.lua b/src/model/canvasModel.lua
index 767c99c8..e9449f01 100644
--- a/src/model/canvasModel.lua
+++ b/src/model/canvasModel.lua
@@ -4,7 +4,7 @@ require("util.view")
local class = require('util.class')
local Terminal = require("lib.terminal")
-local G = love.graphics
+local gfx = love.graphics
--- @class CanvasModel
--- @field terminal table
@@ -31,7 +31,7 @@ function CanvasModel.new(cfg)
-- h = ViewUtils.get_drawable_height(cfg.view)
h = cfg.view.h
end
- local canvas = G.newCanvas(w, h)
+ local canvas = gfx.newCanvas(w, h)
local custom_height = cfg.view.fh * cfg.view.lh
local term = Terminal(w, h, cfg.view.font,
nil, custom_height)
@@ -85,14 +85,14 @@ end
function CanvasModel:clear_canvas()
return self.canvas:renderTo(function()
- G.clear(0, 0, 0, 0)
+ gfx.clear(0, 0, 0, 0)
end)
end
function CanvasModel:draw_to()
- G.setCanvas(self.canvas)
+ gfx.setCanvas(self.canvas)
end
function CanvasModel:restore_main()
- G.setCanvas()
+ gfx.setCanvas()
end
diff --git a/src/model/editor/bufferModel.lua b/src/model/editor/bufferModel.lua
index cac3a5c9..75e51a07 100644
--- a/src/model/editor/bufferModel.lua
+++ b/src/model/editor/bufferModel.lua
@@ -28,16 +28,22 @@ end
--- @alias Content Dequeue|Dequeue
--- @param name string
---- @param content string
+--- @param content str
--- @param save function
--- @param chunker Chunker?
--- @param highlighter Highlighter?
---- @param printer function?
+--- @param printer Printer?
+--- @param truncer function?
--- @return BufferModel?
-local function new(name, content, save,
- chunker, highlighter, printer)
+local function new(
+ name,
+ content,
+ save,
+ chunker,
+ highlighter,
+ printer,
+ truncer)
local _content, sel, ct, semantic
- local revmap = {}
local readonly = false
local lines = string.lines(content or '')
@@ -51,21 +57,10 @@ local function new(name, content, save,
--- @param chk function
local function luacontent(chk)
ct = 'lua'
- local ok, blocks, ast = chk(lines)
+ local ok, blocks = chk(lines)
if ok then
local len = #blocks
sel = len + 1
- local anaok, ana = pcall(analyzer.analyze, ast)
- if anaok then
- for bi, v in ipairs(blocks) do
- if (v.pos) then
- for _, l in ipairs(v.pos:enumerate()) do
- revmap[l] = bi
- end
- end
- end
- semantic = bsi.convert(ana, revmap)
- end
else
readonly = true
sel = 1
@@ -82,7 +77,7 @@ local function new(name, content, save,
plaintext()
end
- return {
+ local self = {
name = name or 'untitled',
content = _content,
content_type = ct,
@@ -90,13 +85,23 @@ local function new(name, content, save,
chunker = chunker,
highlighter = highlighter,
printer = printer,
+ truncer = truncer,
+ revmap = {},
semantic = semantic,
selection = sel,
readonly = readonly
}
+ local id = tostring(self):gsub('table: ', '')
+ self.id = id
+ return self
+end
+
+--- @param self BufferModel
+local function lateinit(self)
+ self:analyze()
end
---- @class BufferModel
+--- @class BufferModel : Object
--- @field name string
--- @field content Dequeue -- Content
--- @field content_type ContentType
@@ -105,17 +110,40 @@ end
--- @field loaded integer?
--- @field readonly boolean
--- @field semantic BufferSemanticInfo?
+--- @field revmap table?
---
--- @field chunker Chunker
--- @field highlighter Highlighter
--- @field printer Printer
+--- @field truncer function
--- @field move_selection function
--- @field get_selection function
--- @field get_selected_text function
--- @field delete_selected_text function
--- @field replace_selected_text function
--- @field get_text_content function
-BufferModel = class.create(new)
+BufferModel = class.create(new, lateinit)
+
+function BufferModel:get_id()
+ return self.id
+end
+
+function BufferModel:analyze()
+ if self.content_type ~= 'lua' then return end
+ local lines = string.lines(self:get_text_content())
+ local ok, blocks, ast = self.chunker(lines)
+ if not ok then return end
+ local anaok, ana = pcall(analyzer.analyze, ast)
+ if not anaok then return end
+ for bi, v in ipairs(blocks) do
+ if (v.pos) then
+ for _, l in ipairs(v.pos:enumerate()) do
+ self.revmap[l] = bi
+ end
+ end
+ end
+ self.semantic = bsi.convert(ana, self.revmap)
+end
function BufferModel:rechunk()
if self.content_type ~= 'lua' then return end
@@ -127,6 +155,7 @@ end
function BufferModel:save()
self:highlight()
local text = self:get_text_content()
+ self:analyze()
return self.save_file(text)
end
@@ -215,7 +244,7 @@ function BufferModel:set_selection(sel)
end
--- Get index of selected line/block
---- @return integer
+--- @return integer blocknum
function BufferModel:get_selection()
return self.selection
end
diff --git a/src/model/editor/bufferSemanticInfo.lua b/src/model/editor/bufferSemanticInfo.lua
index 76f7e500..4d854ec8 100644
--- a/src/model/editor/bufferSemanticInfo.lua
+++ b/src/model/editor/bufferSemanticInfo.lua
@@ -5,21 +5,30 @@ require('util.table')
--- @class Definition: Assignment
--- @field block blocknum
+--- @class RequireCall: Require
+--- @field block blocknum
+
--- @class BufferSemanticInfo
--- @field definitions Definition[]
+--- @field requires RequireCall[]
--- @param si SemanticInfo
--- @param rev table
--- @return BufferSemanticInfo
local function convert(si, rev)
- local as = si.assignments
- local defs = table.map(as, function(a)
+ local blockmap = function(a)
local r = table.clone(a)
r.block = rev[a.line]
return r
- end)
+ end
+ local as = si.assignments
+ local defs = table.map(as, blockmap)
+ local rs = si.requires
+ local reqs = table.map(rs, blockmap)
+
return {
definitions = defs,
+ requires = reqs,
}
end
diff --git a/src/model/editor/editorModel.lua b/src/model/editor/editorModel.lua
index 75bda4ba..c9fb4cbe 100644
--- a/src/model/editor/editorModel.lua
+++ b/src/model/editor/editorModel.lua
@@ -6,13 +6,13 @@ local class = require('util.class')
--- @class EditorModel
--- @field input UserInputModel
---- @field buffer BufferModel?
+--- @field buffers Dequeue
--- @field search Search
--- @field cfg Config
EditorModel = class.create(function(cfg)
return {
input = UserInputModel(cfg, LuaEval()),
- buffer = nil,
+ buffers = Dequeue.new({}, 'BufferModel'),
search = Search(cfg),
cfg = cfg,
}
diff --git a/src/model/interpreter/eval/evaluator.lua b/src/model/interpreter/eval/evaluator.lua
index 12161efb..642870be 100644
--- a/src/model/interpreter/eval/evaluator.lua
+++ b/src/model/interpreter/eval/evaluator.lua
@@ -48,7 +48,7 @@ end
--- @param s string[]
--- @return boolean ok
--- @return str content|errpr
---- @return AST? ast
+--- @return luaAST? ast
local function default_apply(self, s)
local valid, errors = validate(self, s)
local parser = self.parser
diff --git a/src/model/lang/lua/analyze.lua b/src/model/lang/lua/analyze.lua
index f4c47ed7..1cd29ff7 100644
--- a/src/model/lang/lua/analyze.lua
+++ b/src/model/lang/lua/analyze.lua
@@ -1,19 +1,7 @@
require('util.tree')
+require('model.lang.lua.semantic_info')
---- @alias AssignmentType
---- | 'function'
---- | 'method'
---- | 'local'
---- | 'global'
---- | 'field'
-
---- @class Assignment
---- @field name string
---- @field line integer
---- @field type AssignmentType
-
---- @class SemanticInfo
---- @field assignments Assignment[]
+--- utils
local keywords_list = {
"and",
@@ -43,7 +31,17 @@ for _, kw in pairs(keywords_list) do
keywords[kw] = true
end
---- @param ast AST
+--- @param n luaAST
+--- @return number?
+local function get_line_number(n)
+ local li = n.lineinfo
+ local li_f = type(li) == 'table' and li.first
+ return type(li_f) == 'table' and li_f.line or nil
+end
+
+--- assignments
+
+--- @param ast luaAST
--- @return string?
local function get_idx_stack(ast)
--- @return string?
@@ -61,7 +59,7 @@ local function get_idx_stack(ast)
return go(ast)
end
---- @param node AST
+--- @param node luaAST
--- @return boolean
local function is_idx_stack(node)
local st = get_idx_stack(node)
@@ -77,20 +75,11 @@ local function is_ident(id)
return string["match"](id, "^[%a_][%w_]*$") and not keywords[id]
end
---- @param node AST
+--- @param node luaAST
--- @return table?
local function definition_extractor(node)
local deftags = { 'Local', 'Localrec', 'Set' }
- local function get_line_number(n)
- local li = n.lineinfo
- local li_f = type(li) == 'table' and li.first
- return type(li_f) == 'table' and li_f.line
- end
- local function get_lhs_name(n)
- return n[1]
- end
-
if type(node) == 'table' and node.tag then
local tag = node.tag
if table.is_member(deftags, tag) then
@@ -170,7 +159,7 @@ local function definition_extractor(node)
at = 'global'
end
for i, w in ipairs(lhs) do
- local n = get_lhs_name(w)
+ local n = w[1]
if is_local and not rhs[i] then
dec_only = true
end
@@ -202,9 +191,9 @@ local function defmatch(name)
end
end
---- @param ast AST
---- @return SemanticInfo
-local function analyze(ast)
+--- @param ast luaAST
+--- @return Assignment[]
+local function get_assignments(ast)
local sets = table.flatten(
Tree.preorder(ast, definition_extractor)
)
@@ -231,7 +220,52 @@ local function analyze(ast)
table.insert(candidates, v)
end
end
- return { assignments = assignments }
+ return assignments
+end
+
+--- requires
+
+--- @param node luaAST
+--- @return Require[]
+local function req_extractor(node)
+ local calltags = { 'Call' }
+ if type(node) == 'table' and node.tag then
+ local tag = node.tag
+ if table.is_member(calltags, tag) then
+ local lhs = node[1]
+ local rhs = node[2]
+
+ if tag == 'Call'
+ and lhs.tag == 'Id' and lhs[1] == 'require'
+ then
+ local val = rhs[1]
+ local li = get_line_number(rhs)
+ return { { line = li, name = val } }
+ end
+ end
+ end
+ return {}
+end
+
+--- @param ast luaAST
+--- @return Require[]
+local function get_requires(ast)
+ local candidates = table.flatten(
+ Tree.preorder(ast, req_extractor)
+ )
+ local reqs = {}
+ for _, v in ipairs(candidates or {}) do
+ table.insert(reqs, v)
+ end
+ return reqs
+end
+
+--- @param ast luaAST
+--- @return string[]
+local function analyze(ast)
+ local assignments = get_assignments(ast)
+ local reqs = get_requires(ast)
+ return SemanticInfo(assignments, reqs)
end
return {
diff --git a/src/model/lang/lua/parser.lua b/src/model/lang/lua/parser.lua
index b5c66baa..ad3cf448 100644
--- a/src/model/lang/lua/parser.lua
+++ b/src/model/lang/lua/parser.lua
@@ -5,7 +5,7 @@ require("util.debug")
require("util.string.string")
require("util.dequeue")
---- @class luaAST : token[]
+--- @class luaAST : token
--- @alias CPos 'first'|'last'
@@ -288,6 +288,7 @@ return function(lib)
local w = wrap or 80
local ok, r = parse(code)
if ok then
+ --- @diagnostic disable-next-line: param-type-mismatch
local src = ast_to_src(r, {}, w)
return string.lines(src)
end
@@ -419,11 +420,24 @@ return function(lib)
end
end
+ --- @param code str
+ --- @param n integer?
+ --- @return string[]?
+ local function trunc(code, n)
+ local lines = n or 1
+ if lines == 1 then
+ local txt = string.lines(code)
+ local line1 = txt[1]:sub(1, -1) .. '…'
+ return { line1 }
+ end
+ end
+
return {
parse = parse,
pprint = pprint,
highlighter = highlighter,
ast_to_src = ast_to_src,
chunker = chunker,
+ trunc = trunc,
}
end
diff --git a/src/model/lang/lua/semantic_info.lua b/src/model/lang/lua/semantic_info.lua
new file mode 100644
index 00000000..908c8d15
--- /dev/null
+++ b/src/model/lang/lua/semantic_info.lua
@@ -0,0 +1,27 @@
+--- @class SemanticInfoBase
+--- @field name string
+--- @field line integer
+
+--- @alias AssignmentType
+--- | 'function'
+--- | 'method'
+--- | 'local'
+--- | 'global'
+--- | 'field'
+
+--- @class Assignment : SemanticInfoBase
+--- @field type AssignmentType
+
+--- @class Require : SemanticInfoBase
+
+local class = require('util.class')
+
+--- @class SemanticInfo
+--- @field assignments Assignment[]
+--- @field requires Require[]
+SemanticInfo = class.create(function(asn, reqs)
+ return {
+ assignments = asn or {},
+ requires = reqs or {},
+ }
+end)
diff --git a/src/model/lang/md/parser.lua b/src/model/lang/md/parser.lua
index 6d724b93..d5fccf89 100644
--- a/src/model/lang/md/parser.lua
+++ b/src/model/lang/md/parser.lua
@@ -69,7 +69,7 @@ end
--- @param input str
--- @param skip_posinfo boolean?
---- @return AST -- djot AST, distinct from metalua
+--- @return djotAST
local function parse(input, skip_posinfo)
local text = string.unlines(input)
local posinfo = not (skip_posinfo == true)
diff --git a/src/types.lua b/src/types.lua
index 1a644972..25c60141 100644
--- a/src/types.lua
+++ b/src/types.lua
@@ -57,6 +57,7 @@
--- @field debugwidth integer
--- @field drawableWidth number
--- @field drawableChars integer
+--- @field fold_lines integer
--- @field drawtest boolean
--- @field sizedebug boolean
diff --git a/src/util/class.lua b/src/util/class.lua
index a33b2818..ac72966d 100644
--- a/src/util/class.lua
+++ b/src/util/class.lua
@@ -1,7 +1,12 @@
+--- @alias Id string
+--- @class Object
+--- @field id Id
+
return {
--- Simple factory, to spare boilerplate
--- @param constructor function?
- create = function(constructor)
+ --- @param lateinit function?
+ create = function(constructor, lateinit)
local ret = {}
ret.__index = ret
local function new(...)
@@ -16,8 +21,10 @@ return {
if type(cls.new) == "function" then
return cls.new(...)
else
- local instance = new(...)
- setmetatable(instance, cls)
+ local instance = setmetatable(new(...), cls)
+ if type(lateinit) == "function" then
+ lateinit(instance)
+ end
return instance
end
end,
diff --git a/src/util/debug.lua b/src/util/debug.lua
index 352af995..5656a121 100644
--- a/src/util/debug.lua
+++ b/src/util/debug.lua
@@ -122,6 +122,10 @@ local function terse_hash(t, level, prev_seen, jsonify)
return res
end
+local function nontable(t)
+ return '(not a table) ' .. tostring(t)
+end
+
--- @param a table?
--- @param skip integer?
local function terse_array(a, skip)
@@ -142,7 +146,7 @@ local function terse_array(a, skip)
return res
else
- return ''
+ return nontable(a)
end
end
@@ -154,7 +158,9 @@ end
--- @param style dumpstyle?
--- @return string
local function terse_ast(ast, skip_lineinfo, style)
- if type(ast) ~= 'table' then return '' end
+ if type(ast) ~= 'table' then
+ return nontable(ast)
+ end
local style = style or 'json5'
--- @param t table?
@@ -516,7 +522,7 @@ Log = {
once = once,
fire_once = function()
- if not love.DEBUG then return end
+ if not love or not love.DEBUG then return end
love.debug.once = love.debug.once + 1
end,
--- @param color integer
diff --git a/src/util/dequeue.lua b/src/util/dequeue.lua
index 99955256..fb5c4917 100644
--- a/src/util/dequeue.lua
+++ b/src/util/dequeue.lua
@@ -172,7 +172,7 @@ function Dequeue:_checked(i, add, f)
return ok, err
end
---- Insert element at index
+--- Pop element at index
--- @param i integer
--- @return boolean
--- @return string|any err_or_result
diff --git a/src/util/scrollableContent.lua b/src/util/scrollableContent.lua
index b2e59e95..a5d4b98e 100644
--- a/src/util/scrollableContent.lua
+++ b/src/util/scrollableContent.lua
@@ -2,7 +2,7 @@ require("util.wrapped_text")
require("util.scrollable")
require("util.range")
---- @class ScrollableContent
+--- @class ScrollableContent: WrappedText
--- @field range Range?
--- @field size integer
--- @field size_max integer
diff --git a/src/util/table.lua b/src/util/table.lua
index d3d91ecc..dbf93fb3 100644
--- a/src/util/table.lua
+++ b/src/util/table.lua
@@ -281,6 +281,17 @@ function table.find_by(self, pred)
end
end
+--- Find first element that the predicate holds for
+--- @param self table[]
+--- @param pred function
+--- @return any?
+function table.find_by_v(self, pred)
+ if not self or not pred then return end
+ for _, v in pairs(self) do
+ if pred(v) then return v end
+ end
+end
+
--- Filter elements that satisfy the predicate
--- enumerates sequentially
--- @param self table[]
@@ -352,3 +363,27 @@ function table.map(self, f)
end
return ret
end
+
+--- Tabulate array values with index (returns new table)
+--- @param self table
+--- @param f function
+--- @return table
+function table.imap(self, f)
+ local ret = {}
+ for i, v in ipairs(self) do
+ ret[i] = f(v, i)
+ end
+ return ret
+end
+
+--- Create a table of `n` elements by running `f`
+--- @param n integer
+--- @param f function
+--- @return table
+function table.fill(n, f)
+ local ret = {}
+ for i = 1, n do
+ ret[i] = f()
+ end
+ return ret
+end
diff --git a/src/util/view.lua b/src/util/view.lua
index 385ab219..2dfec3d6 100644
--- a/src/util/view.lua
+++ b/src/util/view.lua
@@ -21,8 +21,8 @@ end
--- @param cfg ViewConfig
local write_line = function(l, str, y, breaks, cfg)
local dy = y - (-l + 1 + breaks) * cfg.fh
- G.setFont(cfg.font)
- G.print(str, 0, dy)
+ gfx.setFont(cfg.font)
+ gfx.print(str, 0, dy)
end
--- Write a token to output
@@ -34,17 +34,17 @@ end
--- @param selected boolean
local write_token = function(dy, dx, token,
color, bgcolor, selected)
- G.push('all')
+ gfx.push('all')
if selected then
- G.setColor(color)
+ gfx.setColor(color)
local back = string.rep('█', string.ulen(token))
- G.print(back, dx, dy)
- G.setColor(bgcolor)
+ gfx.print(back, dx, dy)
+ gfx.setColor(bgcolor)
else
- G.setColor(color)
+ gfx.setColor(color)
end
- G.print(token, dx, dy)
- G.pop()
+ gfx.print(token, dx, dy)
+ gfx.pop()
end
--- Hide elements for debugging
@@ -73,61 +73,61 @@ BlendMode = Alpha AlphaMode
local blendModes = {
{ -- 1
name = 'Alpha AlphaM',
- blend = function() G.setBlendMode('alpha', "alphamultiply") end
+ blend = function() gfx.setBlendMode('alpha', "alphamultiply") end
},
{ -- 2
name = 'Alpha PreM',
- blend = function() G.setBlendMode('alpha', "premultiplied") end
+ blend = function() gfx.setBlendMode('alpha', "premultiplied") end
},
-- add
{
name = 'Add AlphaM',
- blend = function() G.setBlendMode('add', "alphamultiply") end
+ blend = function() gfx.setBlendMode('add', "alphamultiply") end
},
{
name = 'Add PreM',
- blend = function() G.setBlendMode('add', "premultiplied") end
+ blend = function() gfx.setBlendMode('add', "premultiplied") end
},
-- subtract
{
name = 'Subtract AlphaM',
- blend = function() G.setBlendMode('subtract', "alphamultiply") end
+ blend = function() gfx.setBlendMode('subtract', "alphamultiply") end
},
{
name = 'Subtract PreM',
- blend = function() G.setBlendMode('subtract', "premultiplied") end
+ blend = function() gfx.setBlendMode('subtract', "premultiplied") end
},
-- replace
{
name = 'Replace AlphaM',
- blend = function() G.setBlendMode('replace', "alphamultiply") end
+ blend = function() gfx.setBlendMode('replace', "alphamultiply") end
},
{
name = 'Replace PreM',
- blend = function() G.setBlendMode('replace', "premultiplied") end
+ blend = function() gfx.setBlendMode('replace', "premultiplied") end
},
-- pre only
{
name = 'Multiply PreM',
- blend = function() G.setBlendMode('multiply', "premultiplied") end
+ blend = function() gfx.setBlendMode('multiply', "premultiplied") end
},
{
name = 'Darken PreM',
- blend = function() G.setBlendMode('darken', "premultiplied") end
+ blend = function() gfx.setBlendMode('darken', "premultiplied") end
},
{
name = 'Lighten PreM',
- blend = function() G.setBlendMode('lighten', "premultiplied") end
+ blend = function() gfx.setBlendMode('lighten', "premultiplied") end
},
-- screen
{
name = 'Screen AlphaM',
- blend = function() G.setBlendMode('screen', "alphamultiply") end
+ blend = function() gfx.setBlendMode('screen', "alphamultiply") end
},
{
name = 'Screen PreM',
- blend = function() G.setBlendMode('screen', "premultiplied") end
+ blend = function() gfx.setBlendMode('screen', "premultiplied") end
},
}
@@ -188,7 +188,7 @@ local function draw_hl_text(text, highlight, cfg, options)
end
end
end
- G.setColor(color)
+ gfx.setColor(color)
local dy = (t_l - 1) * fh
local dx = (c - 1) * fw
write_token(dy, dx, char, color, bg, false)
diff --git a/src/util/wrapped_text.lua b/src/util/wrapped_text.lua
index 28b1aab1..e375eede 100644
--- a/src/util/wrapped_text.lua
+++ b/src/util/wrapped_text.lua
@@ -37,7 +37,7 @@ require("util.lua")
--- @field n_breaks integer
---
--- @field wrap function
---- @field get_text function
+--- @field get_text fun(self): Dequeue
--- @field get_line function
--- @field get_text_length function
WrappedText = class.create()
diff --git a/src/view/canvas/bgView.lua b/src/view/canvas/bgView.lua
index 512137d8..ec660a14 100644
--- a/src/view/canvas/bgView.lua
+++ b/src/view/canvas/bgView.lua
@@ -10,14 +10,14 @@ function BGView:draw(drawable_height)
local w = cfg.w
local fh = cfg.fh
- G.push('all')
+ gfx.push('all')
-- background in case input is not visible
- G.rectangle("fill",
+ gfx.rectangle("fill",
0,
drawable_height - 2,
w,
fh * 2 + 2
)
- G.pop()
+ gfx.pop()
end
diff --git a/src/view/canvas/canvasView.lua b/src/view/canvas/canvasView.lua
index 9cf794da..d12f5197 100644
--- a/src/view/canvas/canvasView.lua
+++ b/src/view/canvas/canvasView.lua
@@ -4,7 +4,7 @@ require("view.canvas.terminalView")
local class = require("util.class")
require("util.view")
-local G = love.graphics
+local gfx = love.graphics
--- @class CanvasView : ViewBase
--- @field bg BGView
@@ -26,60 +26,60 @@ function CanvasView:draw(
local cfg = self.cfg
local test = cfg.drawtest
- G.reset()
- G.push('all')
- G.setBlendMode('alpha', 'alphamultiply') -- default
+ gfx.reset()
+ gfx.push('all')
+ gfx.setBlendMode('alpha', 'alphamultiply') -- default
if ViewUtils.conditional_draw('show_snapshot') then
if snapshot then
- G.draw(snapshot)
+ gfx.draw(snapshot)
end
self.bg:draw(drawable_height)
end
if not test then
if ViewUtils.conditional_draw('show_terminal') then
- -- G.setBlendMode('multiply', "premultiplied")
+ -- gfx.setBlendMode('multiply', "premultiplied")
TerminalView.draw(terminal, term_canvas, snapshot)
end
if ViewUtils.conditional_draw('show_canvas') then
- G.draw(canvas)
+ gfx.draw(canvas)
end
- G.setBlendMode('alpha', 'alphamultiply') -- default
+ gfx.setBlendMode('alpha', 'alphamultiply') -- default
else
- G.setBlendMode('alpha', 'alphamultiply') -- default
+ gfx.setBlendMode('alpha', 'alphamultiply') -- default
for i = 0, love.test_grid_y - 1 do
for j = 0, love.test_grid_x - 1 do
local off_x = cfg.debugwidth * cfg.fw
local off_y = cfg.debugheight * cfg.fh
local dx = j * off_x
local dy = i * off_y
- G.reset()
- G.translate(dx, dy)
+ gfx.reset()
+ gfx.translate(dx, dy)
local index = (i * love.test_grid_x) + j + 1
local b = ViewUtils.blendModes[index]
if b then
- -- G.setBlendMode('alpha') -- default
+ -- gfx.setBlendMode('alpha') -- default
if ViewUtils.conditional_draw('show_terminal') then
b.blend()
TerminalView.draw(terminal, term_canvas, snapshot)
end
- G.setBlendMode('alpha') -- default
+ gfx.setBlendMode('alpha') -- default
if ViewUtils.conditional_draw('show_canvas') then
- G.draw(canvas)
+ gfx.draw(canvas)
end
- G.setBlendMode('alpha') -- default
- G.setColor(1, 1, 1, 1)
- G.setFont(cfg.labelfont)
+ gfx.setBlendMode('alpha') -- default
+ gfx.setColor(1, 1, 1, 1)
+ gfx.setFont(cfg.labelfont)
- -- G.print(index .. ' ' .. b.name)
- G.print(b.name)
+ -- gfx.print(index .. ' ' .. b.name)
+ gfx.print(b.name)
end
end
end
end
- G.pop()
+ gfx.pop()
end
diff --git a/src/view/canvas/terminalView.lua b/src/view/canvas/terminalView.lua
index 50c56bf6..12a44759 100644
--- a/src/view/canvas/terminalView.lua
+++ b/src/view/canvas/terminalView.lua
@@ -1,4 +1,4 @@
-local G = love.graphics
+local gfx = love.graphics
--- @class TerminalView
TerminalView = {}
@@ -10,11 +10,11 @@ local function terminal_draw(terminal, canvas, overlay)
terminal.char_width, terminal.char_height
-- if terminal.dirty or overlay then
- G.push('all')
+ gfx.push('all')
- G.setCanvas(canvas)
- G.setFont(terminal.font)
- G.clear(terminal.clear_color_alpha)
+ gfx.setCanvas(canvas)
+ gfx.setFont(terminal.font)
+ gfx.clear(terminal.clear_color_alpha)
local font_height = terminal.font:getHeight()
for y, row in ipairs(terminal.buffer) do
@@ -41,33 +41,33 @@ local function terminal_draw(terminal, canvas, overlay)
-- Character background
if not overlay then
- G.setColor(unpack(bg))
- G.rectangle("fill",
+ gfx.setColor(unpack(bg))
+ gfx.rectangle("fill",
left, top + (font_height - char_height),
char_width, char_height)
end
- local bm, am = G.getBlendMode()
- G.setBlendMode('alpha', "alphamultiply")
+ local bm, am = gfx.getBlendMode()
+ gfx.setBlendMode('alpha', "alphamultiply")
-- Character
- G.setColor(unpack(fg))
- G.print(char, left, top)
+ gfx.setColor(unpack(fg))
+ gfx.print(char, left, top)
- G.setBlendMode(bm, am)
+ gfx.setBlendMode(bm, am)
state.dirty = false
-- end
end
end
terminal.dirty = false
- G.pop()
+ gfx.pop()
-- end
if terminal.show_cursor then
- G.setFont(terminal.font)
+ gfx.setFont(terminal.font)
if love.timer.getTime() % 1 > 0.5 then
- G.print("_",
+ gfx.print("_",
(terminal.cursor_x - 1) * char_width,
(terminal.cursor_y - 1) * char_height)
end
@@ -76,15 +76,15 @@ end
--- @param terminal table
function TerminalView.draw(terminal, canvas, snapshot)
- G.setCanvas()
- G.push('all')
+ gfx.setCanvas()
+ gfx.push('all')
if snapshot then
terminal_draw(terminal, canvas, true)
else
terminal_draw(terminal, canvas)
end
- G.draw(canvas)
- G.setBlendMode('alpha') -- default
- G.pop()
+ gfx.draw(canvas)
+ gfx.setBlendMode('alpha') -- default
+ gfx.pop()
end
diff --git a/src/view/consoleView.lua b/src/view/consoleView.lua
index a46fa024..2f8be205 100644
--- a/src/view/consoleView.lua
+++ b/src/view/consoleView.lua
@@ -8,7 +8,7 @@ require("util.color")
require("util.view")
require("util.debug")
-local G = love.graphics
+local gfx = love.graphics
--- @param cfg Config
--- @param ctrl ConsoleController
@@ -73,15 +73,15 @@ function ConsoleView:draw_placeholder()
local band = self.cfg.view.fh
local w = self.cfg.view.w
local h = self.cfg.view.h
- G.push('all')
- G.setColor(Color[Color.yellow])
+ gfx.push('all')
+ gfx.setColor(Color[Color.yellow])
for o = -h, w, 2 * band do
- G.polygon("fill"
+ gfx.polygon("fill"
, o + 0, h
, o + h, 0
, o + h + band, 0
, o + band, h
)
end
- G.pop()
+ gfx.pop()
end
diff --git a/src/view/editor/bufferView.lua b/src/view/editor/bufferView.lua
index b4a8aded..4f43eb64 100644
--- a/src/view/editor/bufferView.lua
+++ b/src/view/editor/bufferView.lua
@@ -15,12 +15,12 @@ local function new(cfg)
cfg = cfg,
LINES = l,
SCROLL_BY = math.floor(l / 2),
- w = cfg.drawableChars,
+ wrap_w = cfg.drawableChars,
content = nil,
content_type = nil,
more = { up = false, down = false },
- offset = 0,
+
buffer = nil
}
end
@@ -28,12 +28,12 @@ end
--- @class BufferView : ViewBase
--- @field content VisibleContent|VisibleStructuredContent
--- @field content_type ContentType
---- @field buffer BufferModel
+--- @field buffers Dequeue
---
+--- @field cfg ViewConfig
--- @field LINES integer
--- @field SCROLL_BY integer
---- @field w integer
---- @field offset integer
+--- @field wrap_w integer
--- @field more More
---
--- @field open function
@@ -53,32 +53,31 @@ function BufferView:open(buffer)
if not self.buffer then
error('no buffer')
end
- local cont = buffer.content_type
- self.content_type = cont
+ local ct = buffer.content_type
+ self.content_type = ct
- if cont == 'plain' or cont == 'md' then
+ if ct == 'plain' or ct == 'md' then
local bufcon = buffer:get_text_content()
self.buffer:highlight()
self.content = VisibleContent(
- self.w, bufcon, self.SCROLL_BY, L)
+ self.wrap_w, bufcon, self.SCROLL_BY, L)
self.hl = self.buffer:get_highlight()
- elseif cont == 'lua' then
+ elseif ct == 'lua' then
local bufcon = buffer:get_content()
self.content =
- VisibleStructuredContent(
- self.w,
+ VisibleStructuredContent({
+ wrap_w = self.wrap_w,
+ overscroll_max = self.SCROLL_BY,
+ size_max = L,
+ cfg = self.cfg,
+ },
bufcon,
- buffer.highlighter,
- self.SCROLL_BY,
- L)
+ buffer.highlighter)
else
error 'unknown filetype'
end
- -- TODO clean this up
- local clen = self.content:get_text_length()
- self.offset = math.max(clen - L, 0)
- local off = self.offset
+ local off = self.content.offset
if off > 0 then
self.more.up = true
end
@@ -118,7 +117,7 @@ function BufferView:get_state()
return {
filename = buf.name,
selection = buf.selection,
- offset = self.offset,
+ offset = self.content.offset,
}
end
@@ -139,7 +138,7 @@ function BufferView:refresh(moved)
local sel = self.buffer:get_selection()
if self.content_type == 'lua' then
local vsc = self.content
- local blocks = vsc.blocks
+ local blocks = vsc.v_blocks
blocks:move(moved, sel)
vsc:recalc_range()
else
@@ -150,7 +149,7 @@ function BufferView:refresh(moved)
end
local clen = self.content:get_content_length()
- local off = self.offset
+ local off = self.content.offset
local si = 1 + off
local ei = math.min(self.LINES, clen + 1) + off
self:_update_visible(Range(si, ei))
@@ -160,6 +159,11 @@ end
--- scrolling ---
-------------------
+--- @return integer
+function BufferView:get_offset()
+ return self.content.offset
+end
+
--- @private
--- @return Range
function BufferView:_get_end_range()
@@ -190,8 +194,7 @@ function BufferView:scroll(dir, by, warp)
end
end
end)()
- local o = self.content:move_range(n)
- self.offset = self.offset + o
+ self.content:move_range(n)
end
--- @param off integer
@@ -253,9 +256,13 @@ function BufferView:follow_selection()
end
end
+--------------
+--- draw ---
+--------------
+
--- @param special boolean
function BufferView:draw(special)
- local G = love.graphics
+ local gfx = love.graphics
local cf_colors = self.cfg.colors
local colors = cf_colors.editor
local font = self.cfg.font
@@ -265,17 +272,17 @@ function BufferView:draw(special)
--- @type VisibleContent|VisibleStructuredContent
local content_text = vc:get_visible()
local last_line_n = #content_text
- local width, height = G.getDimensions()
+ local width, height = gfx.getDimensions()
local draw_background = function()
- G.push('all')
- G.setColor(colors.bg)
- G.rectangle("fill", 0, 0, width, height)
- G.setColor(Color.with_alpha(colors.fg, .0625))
+ gfx.push('all')
+ gfx.setColor(colors.bg)
+ gfx.rectangle("fill", 0, 0, width, height)
+ gfx.setColor(Color.with_alpha(colors.fg, .0625))
local bh = math.min(last_line_n, self.cfg.lines) * fh
- G.rectangle("fill", 0, 0, width, bh)
- G.pop()
+ gfx.rectangle("fill", 0, 0, width, bh)
+ gfx.pop()
end
local draw_highlight = function()
@@ -284,19 +291,19 @@ function BufferView:draw(special)
local highlight_line = function(ln)
if not ln then return end
if special then
- G.setColor(colors.highlight_special)
+ gfx.setColor(colors.highlight_special)
else
if ls then
- G.setColor(colors.highlight_loaded)
+ gfx.setColor(colors.highlight_loaded)
else
- G.setColor(colors.highlight)
+ gfx.setColor(colors.highlight)
end
end
local l_y = (ln - 1) * fh
- G.rectangle('fill', 0, l_y, width, fh)
+ gfx.rectangle('fill', 0, l_y, width, fh)
end
- local off = self.offset
+ local off = self.content.offset
for _, w in ipairs(ws) do
for _, v in ipairs(w) do
if self.content.range:inc(v) then
@@ -313,7 +320,7 @@ function BufferView:draw(special)
end
local draw_text = function()
- G.setFont(font)
+ gfx.setFont(font)
if self.content_type == 'lua' then
local vbl = vc:get_visible_blocks()
for _, block in ipairs(vbl) do
@@ -323,7 +330,7 @@ function BufferView:draw(special)
local text = wt:get_text()
local highlight = { hl = block.highlight }
local ltf = function(l)
- return l + rs - 1 - self.offset
+ return l + rs - 1 - self.content.offset
end
local ctf = function(a) return a end
local limit = self.cfg.lines
@@ -337,9 +344,9 @@ function BufferView:draw(special)
if love.DEBUG then
--- phantom text
- G.setColor(Color.with_alpha(colors.fg, 0.3))
+ gfx.setColor(Color.with_alpha(colors.fg, 0.3))
local text = string.unlines(content_text)
- G.print(text)
+ gfx.print(text)
end
elseif self.content_type == 'md' then
local text = vc:get_visible()
@@ -357,10 +364,10 @@ function BufferView:draw(special)
ltf = ltf, ctf = ctf, limit = limit,
})
elseif self.content_type == 'plain' then
- G.setColor(colors.fg)
+ gfx.setColor(colors.fg)
local text = string.unlines(content_text)
- G.print(text)
+ gfx.print(text)
end
end
@@ -370,24 +377,24 @@ function BufferView:draw(special)
local lnc = colors.fg
local x = self.cfg.w - font:getWidth(' ') - 3
local lnvc = Color.with_alpha(lnc, 0.2)
- G.setColor(lnvc)
- G.rectangle("fill", x, 0, 2, self.cfg.h)
+ gfx.setColor(lnvc)
+ gfx.rectangle("fill", x, 0, 2, self.cfg.h)
local seen = {}
for ln = 1, self.LINES do
local l_y = (ln - 1) * fh
- local vln = ln + self.offset
+ local vln = ln + self.content.offset
local ln_w = self.content.wrap_reverse[vln]
if ln_w then
local l = string.format('%3d', ln_w)
local l_x = self.cfg.w - font:getWidth(l)
local l_xv = l_x - font:getWidth(l) - 3.5
if showap then
- G.setColor(lnvc)
- G.print(string.format('%3d', vln), l_xv, l_y)
+ gfx.setColor(lnvc)
+ gfx.print(string.format('%3d', vln), l_xv, l_y)
end
if not seen[ln_w] then
- G.setColor(lnc)
- G.print(l, l_x, l_y)
+ gfx.setColor(lnc)
+ gfx.print(l, l_x, l_y)
seen[ln_w] = true
end
end
diff --git a/src/view/editor/editorView.lua b/src/view/editor/editorView.lua
index e86b68e0..f6cd20a4 100644
--- a/src/view/editor/editorView.lua
+++ b/src/view/editor/editorView.lua
@@ -12,7 +12,7 @@ local function new(cfg, ctrl)
cfg = cfg,
controller = ctrl,
input = UserInputView(cfg, ctrl.input),
- buffer = BufferView(cfg),
+ buffers = {},
search = SearchView(cfg, ctrl.search),
}
--- hook the view in the controller
@@ -23,7 +23,7 @@ end
--- @class EditorView : ViewBase
--- @field controller EditorController
--- @field input UserInputView
---- @field buffer BufferView
+--- @field buffers { [string]: BufferView }
--- @field search SearchView
EditorView = class.create(new)
@@ -34,7 +34,8 @@ function EditorView:draw()
self.search:draw(ctrl.search:get_input())
else
local spec = mode == 'reorder'
- self.buffer:draw(spec)
+ local bv = self:get_current_buffer()
+ bv:draw(spec)
if ViewUtils.conditional_draw('show_input') then
local input = ctrl:get_input()
self.input:draw(input)
@@ -42,7 +43,35 @@ function EditorView:draw()
end
end
+--- @param buffer BufferModel
+--- @return BufferView
+function EditorView:open(buffer)
+ local bid = buffer:get_id()
+ local opn = self.buffers[bid]
+ if not opn then
+ local v = BufferView(self.cfg)
+ self.buffers[bid] = v
+ v:open(buffer)
+ return v
+ end
+ return opn
+end
+
+--- @return BufferView
+function EditorView:get_current_buffer()
+ local ctrl = self.controller
+ local bm = ctrl:get_active_buffer()
+ local bid = bm:get_id()
+ return self.buffers[bid]
+end
+
+--- @param bid string
+--- @return BufferView
+function EditorView:get_buffer(bid)
+ return self.buffers[bid]
+end
+
--- @param moved integer?
function EditorView:refresh(moved)
- self.buffer:refresh(moved)
+ self:get_current_buffer():refresh(moved)
end
diff --git a/src/view/editor/search/resultsView.lua b/src/view/editor/search/resultsView.lua
index 880e8ee1..c24015f5 100644
--- a/src/view/editor/search/resultsView.lua
+++ b/src/view/editor/search/resultsView.lua
@@ -16,14 +16,14 @@ ResultsView = class.create(new)
function ResultsView:draw(results)
local colors = self.cfg.colors.editor
local fh = self.cfg.fh * 1.032 -- magic constant
- local width, height = G.getDimensions()
+ local width, height = gfx.getDimensions()
local has_results = (results.results and #(results.results) > 0)
local draw_background = function()
- G.push('all')
- G.setColor(colors.results.bg)
- G.rectangle("fill", 0, 0, width, height)
- G.pop()
+ gfx.push('all')
+ gfx.setColor(colors.results.bg)
+ gfx.rectangle("fill", 0, 0, width, height)
+ gfx.pop()
end
local draw_results = function()
@@ -40,33 +40,33 @@ function ResultsView:draw(results)
return ""
end
end
- G.push('all')
- G.setFont(self.cfg.font)
+ gfx.push('all')
+ gfx.setFont(self.cfg.font)
if not has_results then
- G.setColor(Color.with_alpha(colors.results.fg, 0.5))
- G.print("No results", 25, 0)
+ gfx.setColor(Color.with_alpha(colors.results.fg, 0.5))
+ gfx.print("No results", 25, 0)
else
for i, v in ipairs(results.results) do
local ln = i
local lh = (ln - 1) * fh
local t = v.r.type
local label = getLabel(t)
- G.setColor(Color.with_alpha(colors.results.fg, 0.5))
- G.print(label, 2, lh + 2)
- G.setColor(colors.results.fg)
- G.print(v.r.name, 25, lh)
+ gfx.setColor(Color.with_alpha(colors.results.fg, 0.5))
+ gfx.print(label, 2, lh + 2)
+ gfx.setColor(colors.results.fg)
+ gfx.print(v.r.name, 25, lh)
end
end
- G.pop()
+ gfx.pop()
end
local draw_selection = function()
local highlight_line = function(ln)
if not ln then return end
- G.setColor(colors.highlight)
+ gfx.setColor(colors.highlight)
local l_y = (ln - 1) * fh
- G.rectangle('fill', 0, l_y, width, fh)
+ gfx.rectangle('fill', 0, l_y, width, fh)
end
local v = results.selection
highlight_line(v)
diff --git a/src/view/editor/visibleContent.lua b/src/view/editor/visibleContent.lua
index 68223f78..9ab4d7f6 100644
--- a/src/view/editor/visibleContent.lua
+++ b/src/view/editor/visibleContent.lua
@@ -134,6 +134,7 @@ function VisibleContent:move_range(by)
if r then
local nr, n = r:translate_limit(by, 1, upper)
self:set_range(nr)
+ self.offset = nr.start - 1
return n
end
end
diff --git a/src/view/editor/visibleStructuredContent.lua b/src/view/editor/visibleStructuredContent.lua
index 4c63c50d..d0cddb37 100644
--- a/src/view/editor/visibleStructuredContent.lua
+++ b/src/view/editor/visibleStructuredContent.lua
@@ -1,16 +1,24 @@
require("view.editor.visibleBlock")
require("util.wrapped_text")
+require("util.scrollable")
require("util.range")
+--- @class VSCOpts
+--- @field wrap_w integer
+--- @field size_max integer
+--- @field overscroll_max integer
+--- @field cfg ViewConfig
--- @alias ReverseMap Dequeue
--- Inverse mapping from line number to block index
--- @class VisibleStructuredContent: WrappedText
---- @field overscroll_max integer
+--- @field offset integer
+--- @field overscroll integer
+--- @field highlighter fun(c: string[]): SyntaxColoring
--- @field size_max integer
--- @field range Range?
---- @field blocks Dequeue
+--- @field v_blocks Dequeue
--- @field reverse_map ReverseMap
---
--- @field set_range fun(self, Range)
@@ -36,19 +44,19 @@ setmetatable(VisibleStructuredContent, {
end,
})
---- @param w integer
+--- @param opts VSCOpts
--- @param blocks Block[]
--- @param highlighter fun(c: string[]): SyntaxColoring
---- @param overscroll integer
---- @param size_max integer
--- @return VisibleStructuredContent
-function VisibleStructuredContent.new(w, blocks, highlighter,
- overscroll, size_max)
+function VisibleStructuredContent.new(
+ opts,
+ blocks,
+ highlighter)
local self = setmetatable({
+ overscroll = opts.overscroll_max,
+ opts = opts,
highlighter = highlighter,
- size_max = size_max,
- overscroll_max = overscroll,
- w = w,
+ offset = 0,
}, VisibleStructuredContent)
self:load_blocks(blocks)
self:to_end()
@@ -59,7 +67,7 @@ end
--- Set the visible range so that last of the content is visible
function VisibleStructuredContent:to_end()
self.range = Scrollable.to_end(
- self.size_max, self:get_text_length())
+ self.opts.size_max, self:get_text_length())
self.offset = self.range.start - 1
end
@@ -70,16 +78,17 @@ function VisibleStructuredContent:load_blocks(blocks)
local revmap = Dequeue.typed('integer')
local visible_blocks = Dequeue()
local off = 0
+ local w = self.opts.wrap_w
for bi, v in ipairs(blocks) do
if v:is_empty() then
fulltext:append('')
local npos = v.pos:translate(off)
visible_blocks:append(
- VisibleBlock(self.w, { '' }, {}, v.pos, npos))
+ VisibleBlock(w, { '' }, {}, v.pos, npos))
else
fulltext:append_all(v.lines)
local hl = self.highlighter(v.lines)
- local vblock = VisibleBlock(self.w, v.lines, hl,
+ local vblock = VisibleBlock(w, v.lines, hl,
v.pos, v.pos:translate(off))
off = off + vblock.wrapped.n_breaks
visible_blocks:append(vblock)
@@ -90,15 +99,15 @@ function VisibleStructuredContent:load_blocks(blocks)
end
end
end
- WrappedText._init(self, self.w, fulltext)
+ WrappedText._init(self, self.opts.wrap_w, fulltext)
self:_init()
self.reverse_map = revmap
- self.blocks = visible_blocks
+ self.v_blocks = visible_blocks
end
function VisibleStructuredContent:recalc_range()
local ln, aln = 1, 1
- for _, v in ipairs(self.blocks) do
+ for _, v in ipairs(self.v_blocks) do
local l = #(v.wrapped.orig)
local al = #(v.wrapped.text)
v.pos = Range(ln, ln + l - 1)
@@ -121,7 +130,7 @@ end
--- @protected
function VisibleStructuredContent:_update_overscroll()
local len = WrappedText.get_text_length(self)
- local over = math.min(self.overscroll_max, len)
+ local over = math.min(self.opts.overscroll_max, len)
self.overscroll = over
end
@@ -154,6 +163,7 @@ function VisibleStructuredContent:move_range(by)
local upper = self:get_text_length() + self.overscroll
local nr, n = r:translate_limit(by, 1, upper)
self:set_range(nr)
+ self.offset = nr.start - 1
return n
end
return 0
@@ -168,7 +178,7 @@ function VisibleStructuredContent:get_visible_blocks()
local si = self.wrap_reverse[self.range.start]
local ei = self.wrap_reverse[self.range.fin]
local sbi, sei = self.reverse_map[si], self.reverse_map[ei]
- return table.slice(self.blocks, sbi, sei)
+ return table.slice(self.v_blocks, sbi, sei)
end
--- @return integer
@@ -179,22 +189,22 @@ end
--- @param bn integer
--- @return Range?
function VisibleStructuredContent:get_block_pos(bn)
- local cl = #(self.blocks)
+ local cl = #(self.v_blocks)
if bn > 0 and bn <= cl then
- return self.blocks[bn].pos
+ return self.v_blocks[bn].pos
elseif cl == 0 then --- empty/new file
Range.singleton(1)
elseif bn == cl + 1 then
- return Range.singleton(self.blocks[cl].pos.fin + 1)
+ return Range.singleton(self.v_blocks[cl].pos.fin + 1)
end
end
--- @param bn integer
--- @return Range?
function VisibleStructuredContent:get_block_app_pos(bn)
- local cl = #(self.blocks)
+ local cl = #(self.v_blocks)
if bn > 0 and bn <= cl then
- return self.blocks[bn].app_pos
+ return self.v_blocks[bn].app_pos
elseif bn == cl + 1 then
local wr = self.wrap_reverse
return Range.singleton(#wr)
diff --git a/src/view/input/statusline.lua b/src/view/input/statusline.lua
index ad2f5fa9..e1cf7669 100644
--- a/src/view/input/statusline.lua
+++ b/src/view/input/statusline.lua
@@ -10,7 +10,7 @@ end)
--- @param nLines integer
--- @param time number?
function Statusline:draw(status, nLines, time)
- local G = love.graphics
+ local gfx = love.graphics
local cf = self.cfg
local colors = (function()
if love.state.app_state == 'inspect' then
@@ -34,10 +34,10 @@ function Statusline:draw(status, nLines, time)
local midX = (start_box.x + w) / 2
local function drawBackground()
- G.setColor(colors.bg)
- G.setFont(font)
+ gfx.setColor(colors.bg)
+ gfx.setFont(font)
local corr = 2 -- correct for fractional slit left under the terminal
- G.rectangle("fill", start_box.x, start_box.y - corr, w, fh + corr)
+ gfx.rectangle("fill", start_box.x, start_box.y - corr, w, fh + corr)
end
--- @param m More?
@@ -63,21 +63,22 @@ function Statusline:draw(status, nLines, time)
y = start_box.y - 2,
}
- G.setColor(colors.fg)
+ gfx.setColor(colors.fg)
local label = status.label
if label then
- G.print(label, start_text.x, start_text.y)
+ gfx.print(label, start_text.x, start_text.y)
end
if love.DEBUG then
- G.setColor(cf.colors.debug)
+ gfx.setColor(cf.colors.debug)
if love.state.testing then
- G.print('testing', midX - (8 * cf.fw), start_text.y)
+ gfx.print('testing', midX - (8 * cf.fw), start_text.y)
end
- G.print(love.state.app_state, midX - (13 * cf.fw), start_text.y)
+ gfx.print((love.state.app_state or '???'),
+ midX - (13 * cf.fw), start_text.y)
if time then
- G.print(tostring(time), midX, start_text.y)
+ gfx.print(tostring(time), midX, start_text.y)
end
- G.setColor(colors.fg)
+ gfx.setColor(colors.fg)
end
local c = status.cursor
@@ -98,50 +99,50 @@ function Statusline:draw(status, nLines, time)
local more_b = morelabel(custom.buffer_more) .. ' '
local more_i = morelabel(status.input_more) .. ' '
- G.setColor(colors.fg)
- local w_il = G.getFont():getWidth(" 999:9999")
- local w_br = G.getFont():getWidth("B999 L999-999(99)")
- local w_mb = G.getFont():getWidth(" ↕↕ ")
- local w_mi = G.getFont():getWidth(" ↕↕ ")
+ gfx.setColor(colors.fg)
+ local w_il = gfx.getFont():getWidth(" 999:9999")
+ local w_br = gfx.getFont():getWidth("B999 L999-999(99)")
+ local w_mb = gfx.getFont():getWidth(" ↕↕ ")
+ local w_mi = gfx.getFont():getWidth(" ↕↕ ")
local s_mb = endTextX - w_br - w_il - w_mi - w_mb
- local cw_p = G.getFont():getWidth(t_blp)
- local cw_il = G.getFont():getWidth(t_ic)
+ local cw_p = gfx.getFont():getWidth(t_blp)
+ local cw_il = gfx.getFont():getWidth(t_ic)
local sxl = endTextX - (cw_p + w_il + w_mi)
local s_mi = endTextX - w_il
- G.setFont(self.cfg.font)
- G.setColor(colors.fg)
- if colors.fg2 then G.setColor(colors.fg2) end
+ gfx.setFont(self.cfg.font)
+ gfx.setColor(colors.fg)
+ if colors.fg2 then gfx.setColor(colors.fg2) end
--- cursor pos
- G.print(t_ic, endTextX - cw_il, start_text.y)
+ gfx.print(t_ic, endTextX - cw_il, start_text.y)
--- input more
- G.print(more_i, s_mi, start_text.y - 3)
+ gfx.print(more_i, s_mi, start_text.y - 3)
- G.setColor(colors.fg)
+ gfx.setColor(colors.fg)
if custom.mode == 'reorder'
and custom.content_type == 'plain' then
- G.setColor(colors.special)
+ gfx.setColor(colors.special)
end
--- block line range / line
- G.print(t_blp, sxl, start_text.y)
- G.setColor(colors.fg)
+ gfx.print(t_blp, sxl, start_text.y)
+ gfx.setColor(colors.fg)
--- block number
if custom.content_type == 'lua' then
- local bpw = G.getFont():getWidth(t_bbp)
+ local bpw = gfx.getFont():getWidth(t_bbp)
local sxb = sxl - bpw
if sel == lim then
- G.setColor(colors.indicator)
+ gfx.setColor(colors.indicator)
end
if custom.mode == 'reorder' then
- G.setColor(colors.special)
+ gfx.setColor(colors.special)
end
- G.print(t_bbp, sxb, start_text.y)
+ gfx.print(t_bbp, sxb, start_text.y)
end
--- buffer more
- G.setColor(colors.fg)
- G.print(more_b, s_mb, start_text.y)
+ gfx.setColor(colors.fg)
+ gfx.print(more_b, s_mb, start_text.y)
else
--- normal statusline
local pos_c = ':' .. c.c
@@ -154,22 +155,22 @@ function Statusline:draw(status, nLines, time)
l_lim = status.n_lines
end
if ln == l_lim then
- G.setColor(colors.indicator)
+ gfx.setColor(colors.indicator)
end
local pos_l = 'L' .. ln
- local lw = G.getFont():getWidth(pos_l)
- local cw = G.getFont():getWidth(pos_c)
+ local lw = gfx.getFont():getWidth(pos_l)
+ local cw = gfx.getFont():getWidth(pos_c)
local sx = endTextX - (lw + cw)
- G.print(pos_l, sx, start_text.y)
- G.setColor(colors.fg)
- G.print(pos_c, sx + lw, start_text.y)
+ gfx.print(pos_l, sx, start_text.y)
+ gfx.setColor(colors.fg)
+ gfx.print(pos_c, sx + lw, start_text.y)
end
end
end
- G.push('all')
+ gfx.push('all')
drawBackground()
drawStatus()
- G.pop()
+ gfx.pop()
end
diff --git a/src/view/input/userInputView.lua b/src/view/input/userInputView.lua
index ad63190d..625ec029 100644
--- a/src/view/input/userInputView.lua
+++ b/src/view/input/userInputView.lua
@@ -25,7 +25,7 @@ UserInputView = class.create(new)
--- @param input InputDTO
--- @param time number?
function UserInputView:draw_input(input, time)
- local G = love.graphics
+ local gfx = love.graphics
local cfg = self.cfg
local status = self.controller:get_status()
@@ -46,7 +46,7 @@ function UserInputView:draw_input(input, time)
local drawableWidth = cfg.drawableWidth
local w = cfg.drawableChars
-- drawtest hack
- if drawableWidth < G.getWidth() / 3 then
+ if drawableWidth < gfx.getWidth() / 3 then
w = w * 2
end
@@ -103,15 +103,15 @@ function UserInputView:draw_input(input, time)
local ch = start_y + (vcl - 1) * fh
local x_offset = math.fmod(acc, w)
- G.push('all')
- G.setColor(cf_colors.input.cursor)
- G.print('|', (x_offset - .5) * fw, ch)
- G.pop()
+ gfx.push('all')
+ gfx.setColor(cf_colors.input.cursor)
+ gfx.print('|', (x_offset - .5) * fw, ch)
+ gfx.pop()
end
local drawBackground = function()
- G.setColor(colors.bg)
- G.rectangle("fill",
+ gfx.setColor(colors.bg)
+ gfx.rectangle("fill",
0,
start_y,
drawableWidth,
@@ -120,7 +120,7 @@ function UserInputView:draw_input(input, time)
local highlight = input.highlight
local visible = vc:get_visible()
- G.setFont(self.cfg.font)
+ gfx.setFont(self.cfg.font)
drawBackground()
self.statusline:draw(status, apparentLines, time)
@@ -255,7 +255,7 @@ function UserInputView:draw_input(input, time)
end
else
for l, str in ipairs(visible) do
- G.setColor(colors.fg)
+ gfx.setColor(colors.fg)
ViewUtils.write_line(l, str, start_y, 0, self.cfg)
end
end
@@ -279,8 +279,8 @@ function UserInputView:draw(input, time)
local apparentHeight = #err_text
local start_y = h - inHeight
local drawBackground = function()
- G.setColor(colors.input.error_bg)
- G.rectangle("fill",
+ gfx.setColor(colors.input.error_bg)
+ gfx.rectangle("fill",
0,
start_y,
drawableWidth,
@@ -288,7 +288,7 @@ function UserInputView:draw(input, time)
end
drawBackground()
- G.setColor(colors.input.error)
+ gfx.setColor(colors.input.error)
for l, str in ipairs(err_text) do
local breaks = 0 -- starting height is already calculated
ViewUtils.write_line(l, str, start_y, breaks, self.cfg)
diff --git a/src/view/titleView.lua b/src/view/titleView.lua
index 5a4c8f42..27e2dd13 100644
--- a/src/view/titleView.lua
+++ b/src/view/titleView.lua
@@ -1,20 +1,20 @@
TitleView = {
draw = function(title, x, y, w, custom_font)
title = title or "LÖVEputer"
- local prev_font = G.getFont()
+ local prev_font = gfx.getFont()
local font = custom_font or prev_font
local fh = font:getHeight()
x = x or 0
- y = y or G.getHeight() - 2 * fh
- w = w or G.getWidth()
- G.setColor(Color[0])
- G.rectangle("fill", x, y, w, fh)
+ y = y or gfx.getHeight() - 2 * fh
+ w = w or gfx.getWidth()
+ gfx.setColor(Color[0])
+ gfx.rectangle("fill", x, y, w, fh)
local i = 1
local c = { 13, 12, 14, 10 }
for lx = w - fh, w - 4 * fh, -fh do
- G.setColor(Color[c[i]])
+ gfx.setColor(Color[c[i]])
i = i + 1
- G.polygon("fill",
+ gfx.polygon("fill",
lx,
y,
lx - fh,
@@ -24,14 +24,14 @@ TitleView = {
lx - fh,
y + fh)
end
- G.setColor(Color[15])
+ gfx.setColor(Color[15])
if custom_font then
- G.setFont(font)
+ gfx.setFont(font)
end
- G.print(title, x + fh, y)
+ gfx.print(title, x + fh, y)
if custom_font then
- G.setFont(prev_font)
+ gfx.setFont(prev_font)
end
end
}
diff --git a/src/view/view.lua b/src/view/view.lua
index 4cc29370..b56302ad 100644
--- a/src/view/view.lua
+++ b/src/view/view.lua
@@ -8,22 +8,22 @@ View = {
--- @param C ConsoleController
--- @param CV ConsoleView
draw = function(C, CV)
- G.push('all')
+ gfx.push('all')
local terminal = C:get_terminal()
local canvas = C:get_canvas()
local input = C.input:get_input()
CV:draw(terminal, canvas, input, canvas_snapshot)
- G.pop()
+ gfx.pop()
end,
snap_canvas = function()
- -- G.captureScreenshot(os.time() .. ".png")
+ -- gfx.captureScreenshot(os.time() .. ".png")
if canvas_snapshot then
View.clear_snapshot()
collectgarbage()
end
- G.captureScreenshot(function(img)
- canvas_snapshot = G.newImage(img)
+ gfx.captureScreenshot(function(img)
+ canvas_snapshot = gfx.newImage(img)
end)
end,
diff --git a/tests/editor/buffer_spec.lua b/tests/editor/buffer_spec.lua
index 68e40c57..a61ece0d 100644
--- a/tests/editor/buffer_spec.lua
+++ b/tests/editor/buffer_spec.lua
@@ -107,7 +107,7 @@ print(sierpinski(4))]])
describe('lua', function()
local turtle = {
'--- @diagnostic disable',
- 'width, height = G.getDimensions()',
+ 'width, height = gfx.getDimensions()',
'midx = width / 2',
'midy = height / 2',
'incr = 5',
@@ -119,15 +119,15 @@ print(sierpinski(4))]])
'bg_color = Color.black',
'',
'local function drawHelp()',
- ' G.setColor(Color[Color.white])',
- ' G.print("Press [I] to open console", 20, 20)',
- ' G.print("Enter \'forward\', \'back\', \'left\', or \'right\' to move the turtle!", 20, 40)',
+ ' gfx.setColor(Color[Color.white])',
+ ' gfx.print("Press [I] to open console", 20, 20)',
+ ' gfx.print("Enter \'forward\', \'back\', \'left\', or \'right\' to move the turtle!", 20, 40)',
'end',
'',
'local function drawDebuginfo()',
- ' G.setColor(Color[debugColor])',
+ ' gfx.setColor(Color[debugColor])',
' local label = string.format("Turtle position: (%d, %d)", tx, ty)',
- ' G.print(label, width - 200, 20)',
+ ' gfx.print(label, width - 200, 20)',
'end',
'',
'function love.draw()',
@@ -212,13 +212,13 @@ print(sierpinski(4))]])
assert.same('', embuf:get_text_content()[1])
embuf:move_selection('down', 2)
assert.same(3, embuf:get_selection())
- assert.same({ 'width, height = G.getDimensions()' }, embuf:get_selected_text())
+ assert.same({ 'width, height = gfx.getDimensions()' }, embuf:get_selected_text())
local res = {
'',
'--- @diagnostic disable',
'',
- 'width, height = G.getDimensions()',
+ 'width, height = gfx.getDimensions()',
'midx = width / 2',
}
assert.same(3, embuf:get_selection())
@@ -229,7 +229,7 @@ print(sierpinski(4))]])
embuf:insert_newline()
assert.same(res, table.take(embuf:get_text_content(), 5))
embuf:move_selection('down')
- assert.same({ 'width, height = G.getDimensions()' }, embuf:get_selected_text())
+ assert.same({ 'width, height = gfx.getDimensions()' }, embuf:get_selected_text())
embuf:insert_newline()
assert.same(res, table.take(embuf:get_text_content(), 5))
end)
diff --git a/tests/editor/editor_spec.lua b/tests/editor/editor_spec.lua
index 4e052c41..f0953a10 100644
--- a/tests/editor/editor_spec.lua
+++ b/tests/editor/editor_spec.lua
@@ -130,7 +130,7 @@ describe('Editor #editor', function()
assert.same(start_sel - 1, buffer:get_selection())
mock.keystroke('up', press)
assert.same(start_sel - 2, buffer:get_selection())
- assert.same(turtle_doc[2], model.buffer:get_selected_text())
+ assert.same(turtle_doc[2], buffer:get_selected_text())
--- load it
local input = function()
return controller.input:get_text():items()
@@ -190,10 +190,11 @@ describe('Editor #editor', function()
local save = TU.get_save_function(sierpinski)
--- use it as plaintext for this test
controller:open('sierpinski.txt', sierpinski, save)
- view.buffer:open(model.buffer)
+ local buf = controller:get_active_buffer()
+ local bv = view:open(buf)
- local visible = view.buffer.content
- local scroll = view.buffer.SCROLL_BY
+ local visible = bv.content
+ local scroll = bv.SCROLL_BY
local off = #sierpinski - l + 1
local start_range = Range(off + 1, #sierpinski + 1)
@@ -201,7 +202,7 @@ describe('Editor #editor', function()
it('loads', function()
--- inital scroll is at EOF, meaning last l lines are visible
--- plus the phantom line
- assert.same(off, view.buffer.offset)
+ assert.same(off, bv:get_offset())
assert.same(start_range, visible.range)
end)
local base = Range(1, l)
@@ -238,7 +239,6 @@ describe('Editor #editor', function()
local l = 6
local controller, _, view = wire(getMockConf(27, l))
- local model = controller.model
local save = TU.get_save_function(sierpinski)
controller:open('sierpinski.txt', sierpinski, save)
@@ -249,11 +249,11 @@ describe('Editor #editor', function()
local buffer = controller:get_active_buffer()
--- @type BufferView
- local bv = view.buffer
- bv:open(model.buffer)
+ local bv = view:open(buffer)
+ -- bv:open(buffer)
- local visible = view.buffer.content
- local scroll = view.buffer.SCROLL_BY
+ local visible = bv.content
+ local scroll = bv.SCROLL_BY
local clen = visible:get_content_length()
local off = clen - l + 1
@@ -261,7 +261,7 @@ describe('Editor #editor', function()
it('loads', function()
--- inital scroll is at EOF, meaning last l lines are visible
--- plus the phantom line
- assert.same(off, view.buffer.offset)
+ assert.same(off, bv:get_offset())
assert.same(start_range, visible.range)
end)
local base = Range(1, l)
diff --git a/tests/editor/visible_content_spec.lua b/tests/editor/visible_content_spec.lua
index 9ad20188..d6445c01 100644
--- a/tests/editor/visible_content_spec.lua
+++ b/tests/editor/visible_content_spec.lua
@@ -1,14 +1,69 @@
require("view.editor.visibleContent")
+require("model.input.cursor")
require("util.string.string")
-describe('VisibleContent #wrap', function()
+local md_ex = [[### Input validation
+
+As an extension to the user input functionality, `validated_input()` allows arbitrary user-specified filters.
+A "filter" is a function, which takes a string as input and returns a boolean value of whether it is valid and an optional `Error`.
+The `Error` is structure which contains the error message (`msg`), and the location the error comes from, with line and character fields (`l` and `c`).
+
+#### Helper functions
+
+* `string.ulen(s)` - as opposed to the builtin `len()`, this works for unicode strings
+* `string.usub(s, from, to)` - unicode substrings
+* `Char.is_alpha(c)` - is `c` a letter
+* `Char.is_alnum(c)` - is `c` a letter or a number (alphanumeric)
+]]
+
+describe('VisibleContent #visible', function()
+ local md_text = string.lines(md_ex)
+ local w = 64
local turtle_doc = {
'',
'Turtle graphics game inspired the LOGO family of languages.',
'',
}
+ it('translates', function()
+ local visible = VisibleContent(w, md_text, 1, 8)
+ --- scroll to the top
+ visible:move_range(- #md_text)
+ local cur11 = Cursor()
+ local cur33 = Cursor(3, 3)
+ local cur3w = Cursor(3, w)
+ local cur3wp1 = Cursor(3, w + 1)
+ local cur44 = Cursor(4, 4)
+ assert.same(cur11, visible:translate_to_wrapped(cur11))
+ assert.same(cur33, visible:translate_to_wrapped(cur33))
+ assert.same(cur3w, visible:translate_to_wrapped(cur3w))
+ assert.same(Cursor(4, 1), visible:translate_to_wrapped(cur3wp1))
+
+ assert.same(cur33, visible:translate_from_visible(cur33))
+ local cur3_67 = Cursor(3, 3 + w)
+ local exp3_67 = Cursor(4, 3)
+ assert.same(exp3_67, visible:translate_to_wrapped(cur3_67))
+
+ --- scroll to bottom
+ visible:to_end()
+ -- #01: ''
+ -- #02: '* `string.ulen(s)` - as opposed to the builtin `len()`, this wor'
+ -- #03: 'ks for unicode strings'
+ -- #04: '* `string.usub(s, from, to)` - unicode substrings'
+ -- #05: '* `Char.is_alpha(c)` - is `c` a letter'
+ -- #06: '* `Char.is_alnum(c)` - is `c` a letter or a number (alphanumeric'
+ -- #07: ')'
+ -- #08: ''
+ assert.same(Cursor(9, 3 + w),
+ visible:translate_from_visible(cur33))
+ assert.same(Cursor(10, 4),
+ visible:translate_from_visible(cur44))
+ assert.is_nil(visible:translate_from_visible(Cursor(5, 40)))
+ local cur71 = Cursor(7, 1)
+ assert.same(Cursor(12, 65),
+ visible:translate_from_visible(cur71))
+ end)
local os_max = 8
local input_max = 16
diff --git a/tests/interpreter/analyzer_inputs.lua b/tests/interpreter/analyzer_inputs.lua
index 4987c615..bf55daab 100644
--- a/tests/interpreter/analyzer_inputs.lua
+++ b/tests/interpreter/analyzer_inputs.lua
@@ -1,7 +1,7 @@
--- @param s str
---- @param defs Assignment[]
+--- @param semi SemanticInfo
--- @return table {string[], string[]}
-local prep = function(s, defs)
+local prep = function(s, semi)
local orig = (function()
if type(s) == 'string' then
return string.lines(s)
@@ -14,7 +14,7 @@ local prep = function(s, defs)
end
end)()
- return { orig, defs }
+ return { orig, semi }
end
local table1 = prep({
@@ -30,7 +30,7 @@ local table1 = prep({
' z = 2,',
' 3,',
'}',
-}, {
+}, SemanticInfo({
{ name = 't', line = 1, type = 'local' },
{ name = 't.ty', line = 2, type = 'field' },
{ name = 't2', line = 4, type = 'global' },
@@ -38,13 +38,13 @@ local table1 = prep({
{ name = 't2.w2', line = 6, type = 'field' },
{ name = 'a', line = 8, type = 'global' },
{ name = 'a.z', line = 10, type = 'field' },
-})
+}, {}))
local table2 = prep({
'tmp = {}',
'tmp[1] = 2',
-}, {
+}, SemanticInfo({
{ name = 'tmp', line = 1, type = 'global' }
-})
+}, {}))
local simple = {
--- sets
@@ -53,21 +53,22 @@ local simple = {
'y = 3',
'x = 3',
'w, ww = 10, 11',
- }, {
+ }, SemanticInfo({
{ line = 1, name = 'x', type = 'global', },
{ line = 2, name = 'y', type = 'global', },
{ line = 3, name = 'x', type = 'global', },
{ line = 4, name = 'w', type = 'global', },
{ line = 4, name = 'ww', type = 'global', },
- }),
+ }, {})),
+
prep({
'local l = 1',
'local x, y = 2, 3',
- }, {
+ }, SemanticInfo({
{ line = 1, name = 'l', type = 'local', },
{ line = 2, name = 'x', type = 'local', },
{ line = 2, name = 'y', type = 'local', },
- }),
+ }, {})),
--- tables
table1,
-- table2,
@@ -75,35 +76,35 @@ local simple = {
prep({
'function drawBackground()',
'end',
- }, {
+ }, SemanticInfo({
{ line = 1, name = 'drawBackground', type = 'function', },
- }),
+ }, {})),
prep({
'function love.draw()',
' draw()',
'end',
- }, {
+ }, SemanticInfo({
{ line = 1, name = 'love.draw', type = 'function', },
- }),
+ }, {})),
prep({
'function love.handlers.keypressed()',
'end',
- }, {
+ }, SemanticInfo({
{ line = 1, name = 'love.handlers.keypressed', type = 'function', },
- }),
+ }, {})),
prep({
'local function drawBody()',
'end',
- }, {
+ }, SemanticInfo({
{ name = 'drawBody', line = 1, type = 'function' }
- }),
+ }, {})),
--- methods
prep({
'function M:draw()',
'end',
- }, {
+ }, SemanticInfo({
{ name = 'M:draw', line = 1, type = 'method' }
- }),
+ }, {})),
}
local sierpinski = [[function sierpinski(depth)
@@ -210,7 +211,7 @@ end
local fullclock = [[
--- @diagnostic disable: duplicate-set-field,lowercase-global
-width, height = G.getDimensions()
+width, height = gfx.getDimensions()
midx = width / 2
midy = height / 2
@@ -232,7 +233,7 @@ s = 0
math.randomseed(os.time())
color = math.random(7)
bg_color = math.random(7)
-font = G.newFont(72)
+font = gfx.newFont(72)
local function pad(i)
return string.format("%02d", i)
@@ -249,15 +250,15 @@ function getTimestamp()
end
function love.draw()
- G.setColor(Color[color + Color.bright])
- G.setBackgroundColor(Color[bg_color])
- G.setFont(font)
+ gfx.setColor(Color[color + Color.bright])
+ gfx.setBackgroundColor(Color[bg_color])
+ gfx.setFont(font)
local text = getTimestamp()
local l = string.len(text)
local off_x = l * font:getWidth(' ')
local off_y = font:getHeight() / 2
- G.print(text, midx - off_x, midy - off_y, 0, 1, 1)
+ gfx.print(text, midx - off_x, midy - off_y, 0, 1, 1)
end
function love.update(dt)
@@ -295,14 +296,14 @@ end
]]
local full = {
- prep(sierpinski, {
+ prep(sierpinski, SemanticInfo({
{ line = 1, name = 'sierpinski', type = 'function', },
{ line = 2, name = 'lines', type = 'global', },
{ line = 4, name = 'sp', type = 'global', },
{ line = 5, name = 'tmp', type = 'global', },
{ line = 10, name = 'lines', type = 'global', },
- }),
- prep(clock, {
+ }, {})),
+ prep(clock, SemanticInfo({
{ line = 1, name = 'love.draw', type = 'function', },
{ line = 5, name = 'love.update', type = 'function', },
{ line = 6, name = 't', type = 'global', },
@@ -312,8 +313,8 @@ local full = {
{ line = 16, name = 'love.keyreleased', type = 'function', },
{ line = 19, name = 'bg_color', type = 'global', },
{ line = 21, name = 'color', type = 'global', },
- }),
- prep(meta, {
+ }, {})),
+ prep(meta, SemanticInfo({
{ line = 3, name = 'M:extract_comments', type = 'method', },
{ line = 4, name = 'lfi', type = 'local', },
{ line = 5, name = 'lla', type = 'local', },
@@ -346,8 +347,8 @@ local full = {
{ line = 32, name = 'li.multiline', type = 'field', },
{ line = 33, name = 'li.position', type = 'field', },
{ line = 34, name = 'li.prepend_newline', type = 'field', },
- }),
- prep(fullclock, {
+ }, {})),
+ prep(fullclock, SemanticInfo({
{ line = 2, name = 'width', type = 'global', },
{ line = 2, name = 'height', type = 'global', },
{ line = 3, name = 'midx', type = 'global', },
@@ -386,10 +387,31 @@ local full = {
{ line = 69, name = 'bg_color', type = 'global', },
{ line = 71, name = 'color', type = 'global', },
{ line = 75, name = 'love.keyreleased', type = 'function', },
- }),
+ }, {})),
+}
+
+local req = {
+ prep({
+ 'require ("math")',
+ 'x = sin(pi)',
+ }, SemanticInfo({
+ { name = 'x', line = 2, type = 'global' }
+ }, {
+ { name = 'math', line = 1 }
+ })),
+ prep({
+ 'require("action")',
+ 'print "req test"',
+ 'require("drawing")',
+ }, SemanticInfo({
+ }, {
+ { name = 'action', line = 1 },
+ { name = 'drawing', line = 3 },
+ })),
}
return {
- { 'simple', simple },
- { 'full', full },
+ { 'simple', simple },
+ { 'full', full },
+ { 'require', req },
}
diff --git a/tests/interpreter/analyzer_spec.lua b/tests/interpreter/analyzer_spec.lua
index 11d16417..37d62aec 100644
--- a/tests/interpreter/analyzer_spec.lua
+++ b/tests/interpreter/analyzer_spec.lua
@@ -96,7 +96,7 @@ describe('analyzer #analyzer', function()
---@diagnostic disable-next-line: param-type-mismatch
local semDB = analyzer.analyze(r)
it('matches ' .. i, function()
- assert.same(output, semDB.assignments)
+ assert.same(output, semDB)
end)
else
Log.warn('syntax error in input #' .. i)
diff --git a/tests/testutil.lua b/tests/testutil.lua
index 7c1e4c1b..8b324f3e 100644
--- a/tests/testutil.lua
+++ b/tests/testutil.lua
@@ -1,6 +1,9 @@
require('util.table')
require('util.string.string')
+local w = 64
+
+local noop = function() end
--- @param init str
--- @return fun(str): boolean, string?
--- @return reftable handle
@@ -18,5 +21,16 @@ local get_save_function = function(init)
end
return {
- get_save_function = get_save_function
+ get_save_function = get_save_function,
+ noop = noop,
+ LINES = 16,
+ SCROLL_BY = 8,
+ w = w,
+ mock_view_cfg = {
+ view = {
+ drawableChars = w,
+ lines = 16,
+ input_max = 14
+ },
+ }
}
diff --git a/tests/util/visible_spec.lua b/tests/util/visible_spec.lua
deleted file mode 100644
index 97fa932f..00000000
--- a/tests/util/visible_spec.lua
+++ /dev/null
@@ -1,63 +0,0 @@
-require("view.editor.visibleContent")
-require("model.input.cursor")
-require("util.string.string")
-require("util.debug")
-
-local md_ex = [[### Input validation
-
-As an extension to the user input functionality, `validated_input()` allows arbitrary user-specified filters.
-A "filter" is a function, which takes a string as input and returns a boolean value of whether it is valid and an optional `Error`.
-The `Error` is structure which contains the error message (`msg`), and the location the error comes from, with line and character fields (`l` and `c`).
-
-#### Helper functions
-
-* `string.ulen(s)` - as opposed to the builtin `len()`, this works for unicode strings
-* `string.usub(s, from, to)` - unicode substrings
-* `Char.is_alpha(c)` - is `c` a letter
-* `Char.is_alnum(c)` - is `c` a letter or a number (alphanumeric)
-]]
-local text = string.lines(md_ex)
-local w = 64
-
-describe('VisibleContent #visible', function()
- local visible = VisibleContent(w, text, 1, 8)
-
- -- Log.debug(Debug.terse_ast(visible, true, 'lua'))
- it('translates', function()
- --- scroll to the top
- visible:move_range(- #text)
- local cur11 = Cursor()
- local cur33 = Cursor(3, 3)
- local cur3w = Cursor(3, w)
- local cur3wp1 = Cursor(3, w + 1)
- local cur44 = Cursor(4, 4)
- assert.same(cur11, visible:translate_to_wrapped(cur11))
- assert.same(cur33, visible:translate_to_wrapped(cur33))
- assert.same(cur3w, visible:translate_to_wrapped(cur3w))
- assert.same(Cursor(4, 1), visible:translate_to_wrapped(cur3wp1))
-
- assert.same(cur33, visible:translate_from_visible(cur33))
- local cur3_67 = Cursor(3, 3 + w)
- local exp3_67 = Cursor(4, 3)
- assert.same(exp3_67, visible:translate_to_wrapped(cur3_67))
-
- --- scroll to bottom
- visible:to_end()
- -- #01: ''
- -- #02: '* `string.ulen(s)` - as opposed to the builtin `len()`, this wor'
- -- #03: 'ks for unicode strings'
- -- #04: '* `string.usub(s, from, to)` - unicode substrings'
- -- #05: '* `Char.is_alpha(c)` - is `c` a letter'
- -- #06: '* `Char.is_alnum(c)` - is `c` a letter or a number (alphanumeric'
- -- #07: ')'
- -- #08: ''
- assert.same(Cursor(9, 3 + w),
- visible:translate_from_visible(cur33))
- assert.same(Cursor(10, 4),
- visible:translate_from_visible(cur44))
- assert.is_nil(visible:translate_from_visible(Cursor(5, 40)))
- local cur71 = Cursor(7, 1)
- assert.same(Cursor(12, 65),
- visible:translate_from_visible(cur71))
- end)
-end)