fix CRLF
parent
b14c31b4e0
commit
b841064174
|
@ -1,182 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- This file license is CC0 (https://creativecommons.org/publicdomain/zero/1.0/). -->
|
||||
<!-- Original code by @SpartanJ https://github.com/SpartanJ/eepp/blob/8552941da19380d7a629c4da80a976aec5d39e5c/bin/emscripten-fs.html -->
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
|
||||
<!-- <title>Example</title> -->
|
||||
<style>
|
||||
body { margin: 0; background-color: black }
|
||||
.emscripten {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
margin: 0px;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
image-rendering: optimizeSpeed;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: optimize-contrast;
|
||||
image-rendering: crisp-edges;
|
||||
image-rendering: pixelated;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
.loader {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
border-top: 3px solid #3daee9;
|
||||
border-right: 3px solid transparent;
|
||||
box-sizing: border-box;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
.loader-cont {
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
}
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
<div id="cont" class="loader-cont">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
<script type='text/javascript'>
|
||||
var loaderCont;
|
||||
|
||||
function getDemoScript(name) {
|
||||
if (name)
|
||||
return name;
|
||||
return "index.js";
|
||||
}
|
||||
|
||||
function getParameter(name) {
|
||||
let url_string = window.location.href;
|
||||
let url = new URL(url_string);
|
||||
return url.searchParams.get(name);
|
||||
}
|
||||
|
||||
function loadScript(url, callback) {
|
||||
loaderCont = document.getElementById('cont');
|
||||
let head = document.head;
|
||||
let script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
script.onreadystatechange = callback;
|
||||
script.onload = callback;
|
||||
head.appendChild(script);
|
||||
}
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: (function() {
|
||||
return function(text) {
|
||||
text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
};
|
||||
})(),
|
||||
printErr: function(text) {
|
||||
text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
var dpi = window.devicePixelRatio;
|
||||
canvas.width = window.innerWidth*dpi;
|
||||
canvas.height = window.innerHeight*dpi;
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (canvas.canResize) {
|
||||
dpi = window.devicePixelRatio;
|
||||
canvas.width = window.innerWidth*dpi;
|
||||
canvas.height = window.innerHeight*dpi;
|
||||
}
|
||||
})
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
console.log("status: " + text);
|
||||
if (text == "Running...")
|
||||
loaderCont.style.display = 'none';
|
||||
},
|
||||
monitorRunDependencies: function(left) {
|
||||
// no run dependencies to log
|
||||
},
|
||||
arguments: window.location.search.substr(1).split('&')
|
||||
};
|
||||
window.onerror = function() {
|
||||
console.log("onerror: " + event);
|
||||
};
|
||||
|
||||
(function() {
|
||||
loadScript(getDemoScript(getParameter("run")));
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{{ SCRIPT }}}
|
||||
|
||||
<button onclick="openFullscreen();" style="position:relative; z-index: 1000; float: right;">⛶</button>
|
||||
<!-- <button id="btn-audio" onclick="toggleAudio();" style="position:relative; z-index: 1000; float: right;">M</button> -->
|
||||
<script>
|
||||
function openFullscreen() {
|
||||
var canvas = document.getElementById("canvas");
|
||||
if (canvas.requestFullscreen) {
|
||||
canvas.requestFullscreen();
|
||||
} else if (canvas.webkitRequestFullscreen) { /* Safari */
|
||||
canvas.webkitRequestFullscreen();
|
||||
} else if (canvas.msRequestFullscreen) { /* IE11 */
|
||||
canvas.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- <script type='text/javascript'>
|
||||
var audioBtn = document.querySelector('#btn-audio');
|
||||
|
||||
// An array of all contexts to resume on the page
|
||||
const audioContexList = [];
|
||||
(function() {
|
||||
// A proxy object to intercept AudioContexts and
|
||||
// add them to the array for tracking and resuming later
|
||||
self.AudioContext = new Proxy(self.AudioContext, {
|
||||
construct(target, args) {
|
||||
const result = new target(...args);
|
||||
audioContexList.push(result);
|
||||
if (result.state == "suspended") audioBtn.value = "RESUME";
|
||||
return result;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
function toggleAudio() {
|
||||
var resumed = false;
|
||||
audioContexList.forEach(ctx => {
|
||||
if (ctx.state == "suspended") { ctx.resume(); resumed = true; }
|
||||
else if (ctx.state == "running") ctx.suspend();
|
||||
});
|
||||
|
||||
if (resumed) audioBtn.value = "SUSPEND";
|
||||
else audioBtn.value = "RESUME";
|
||||
}
|
||||
</script> -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,284 +0,0 @@
|
|||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local command = require "core.command"
|
||||
local style = require "core.style"
|
||||
local keymap = require "core.keymap"
|
||||
local translate = require "core.doc.translate"
|
||||
local RootView = require "core.rootview"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.autocomplete_max_suggestions = 6
|
||||
|
||||
local autocomplete = {}
|
||||
autocomplete.map = {}
|
||||
|
||||
|
||||
local mt = { __tostring = function(t) return t.text end }
|
||||
|
||||
function autocomplete.add(t)
|
||||
local items = {}
|
||||
for text, info in pairs(t.items) do
|
||||
info = (type(info) == "string") and info
|
||||
table.insert(items, setmetatable({ text = text, info = info }, mt))
|
||||
end
|
||||
autocomplete.map[t.name] = { files = t.files or ".*", items = items }
|
||||
end
|
||||
|
||||
|
||||
core.add_thread(function()
|
||||
local cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function get_symbols(doc)
|
||||
local i = 1
|
||||
local s = {}
|
||||
while i < #doc.lines do
|
||||
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
|
||||
s[sym] = true
|
||||
end
|
||||
i = i + 1
|
||||
if i % 100 == 0 then coroutine.yield() end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function cache_is_valid(doc)
|
||||
local c = cache[doc]
|
||||
return c and c.last_change_id == doc:get_change_id()
|
||||
end
|
||||
|
||||
while true do
|
||||
local symbols = {}
|
||||
|
||||
-- lift all symbols from all docs
|
||||
for _, doc in ipairs(core.docs) do
|
||||
-- update the cache if the doc has changed since the last iteration
|
||||
if not cache_is_valid(doc) then
|
||||
cache[doc] = {
|
||||
last_change_id = doc:get_change_id(),
|
||||
symbols = get_symbols(doc)
|
||||
}
|
||||
end
|
||||
-- update symbol set with doc's symbol set
|
||||
for sym in pairs(cache[doc].symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
-- update symbols list
|
||||
autocomplete.add { name = "open-docs", items = symbols }
|
||||
|
||||
-- wait for next scan
|
||||
local valid = true
|
||||
while valid do
|
||||
coroutine.yield(1)
|
||||
for _, doc in ipairs(core.docs) do
|
||||
if not cache_is_valid(doc) then
|
||||
valid = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local partial = ""
|
||||
local suggestions_idx = 1
|
||||
local suggestions = {}
|
||||
local last_line, last_col
|
||||
|
||||
|
||||
local function reset_suggestions()
|
||||
suggestions_idx = 1
|
||||
suggestions = {}
|
||||
end
|
||||
|
||||
|
||||
local function update_suggestions()
|
||||
local doc = core.active_view.doc
|
||||
local filename = doc and doc.filename or ""
|
||||
|
||||
-- get all relevant suggestions for given filename
|
||||
local items = {}
|
||||
for _, v in pairs(autocomplete.map) do
|
||||
if common.match_pattern(filename, v.files) then
|
||||
for _, item in pairs(v.items) do
|
||||
table.insert(items, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fuzzy match, remove duplicates and store
|
||||
items = common.fuzzy_match(items, partial)
|
||||
local j = 1
|
||||
for i = 1, config.autocomplete_max_suggestions do
|
||||
suggestions[i] = items[j]
|
||||
while items[j] and items[i].text == items[j].text do
|
||||
items[i].info = items[i].info or items[j].info
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_partial_symbol()
|
||||
local doc = core.active_view.doc
|
||||
local line2, col2 = doc:get_selection()
|
||||
local line1, col1 = doc:position_offset(line2, col2, translate.start_of_word)
|
||||
return doc:get_text(line1, col1, line2, col2)
|
||||
end
|
||||
|
||||
|
||||
local function get_active_view()
|
||||
if getmetatable(core.active_view) == DocView then
|
||||
return core.active_view
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_suggestions_rect(av)
|
||||
if #suggestions == 0 then
|
||||
return 0, 0, 0, 0
|
||||
end
|
||||
|
||||
local line, col = av.doc:get_selection()
|
||||
local x, y = av:get_line_screen_position(line)
|
||||
x = x + av:get_col_x_offset(line, col - #partial)
|
||||
y = y + av:get_line_height() + style.padding.y
|
||||
local font = av:get_font()
|
||||
local th = font:get_height()
|
||||
|
||||
local max_width = 0
|
||||
for _, s in ipairs(suggestions) do
|
||||
local w = font:get_width(s.text)
|
||||
if s.info then
|
||||
w = w + style.font:get_width(s.info) + style.padding.x
|
||||
end
|
||||
max_width = math.max(max_width, w)
|
||||
end
|
||||
|
||||
return
|
||||
x - style.padding.x,
|
||||
y - style.padding.y,
|
||||
max_width + style.padding.x * 2,
|
||||
#suggestions * (th + style.padding.y) + style.padding.y
|
||||
end
|
||||
|
||||
|
||||
local function draw_suggestions_box(av)
|
||||
-- draw background rect
|
||||
local rx, ry, rw, rh = get_suggestions_rect(av)
|
||||
renderer.draw_rect(rx, ry, rw, rh, style.background3)
|
||||
|
||||
-- draw text
|
||||
local font = av:get_font()
|
||||
local lh = font:get_height() + style.padding.y
|
||||
local y = ry + style.padding.y / 2
|
||||
for i, s in ipairs(suggestions) do
|
||||
local color = (i == suggestions_idx) and style.accent or style.text
|
||||
common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
|
||||
if s.info then
|
||||
color = (i == suggestions_idx) and style.text or style.dim
|
||||
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
|
||||
end
|
||||
y = y + lh
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- patch event logic into RootView
|
||||
local on_text_input = RootView.on_text_input
|
||||
local update = RootView.update
|
||||
local draw = RootView.draw
|
||||
|
||||
|
||||
RootView.on_text_input = function(...)
|
||||
on_text_input(...)
|
||||
|
||||
local av = get_active_view()
|
||||
if av then
|
||||
-- update partial symbol and suggestions
|
||||
partial = get_partial_symbol()
|
||||
if #partial >= 3 then
|
||||
update_suggestions()
|
||||
last_line, last_col = av.doc:get_selection()
|
||||
else
|
||||
reset_suggestions()
|
||||
end
|
||||
|
||||
-- scroll if rect is out of bounds of view
|
||||
local _, y, _, h = get_suggestions_rect(av)
|
||||
local limit = av.position.y + av.size.y
|
||||
if y + h > limit then
|
||||
av.scroll.to.y = av.scroll.y + y + h - limit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
RootView.update = function(...)
|
||||
update(...)
|
||||
|
||||
local av = get_active_view()
|
||||
if av then
|
||||
-- reset suggestions if caret was moved
|
||||
local line, col = av.doc:get_selection()
|
||||
if line ~= last_line or col ~= last_col then
|
||||
reset_suggestions()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
RootView.draw = function(...)
|
||||
draw(...)
|
||||
|
||||
local av = get_active_view()
|
||||
if av then
|
||||
-- draw suggestions box after everything else
|
||||
core.root_view:defer_draw(draw_suggestions_box, av)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function predicate()
|
||||
return get_active_view() and #suggestions > 0
|
||||
end
|
||||
|
||||
|
||||
command.add(predicate, {
|
||||
["autocomplete:complete"] = function()
|
||||
local doc = core.active_view.doc
|
||||
local line, col = doc:get_selection()
|
||||
local text = suggestions[suggestions_idx].text
|
||||
doc:insert(line, col, text)
|
||||
doc:remove(line, col, line, col - #partial)
|
||||
doc:set_selection(line, col + #text - #partial)
|
||||
reset_suggestions()
|
||||
end,
|
||||
|
||||
["autocomplete:previous"] = function()
|
||||
suggestions_idx = math.max(suggestions_idx - 1, 1)
|
||||
end,
|
||||
|
||||
["autocomplete:next"] = function()
|
||||
suggestions_idx = math.min(suggestions_idx + 1, #suggestions)
|
||||
end,
|
||||
|
||||
["autocomplete:cancel"] = function()
|
||||
reset_suggestions()
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["tab"] = "autocomplete:complete",
|
||||
["up"] = "autocomplete:previous",
|
||||
["down"] = "autocomplete:next",
|
||||
["escape"] = "autocomplete:cancel",
|
||||
}
|
||||
|
||||
|
||||
return autocomplete
|
|
@ -1,114 +0,0 @@
|
|||
local core = require "core"
|
||||
local translate = require "core.doc.translate"
|
||||
local config = require "core.config"
|
||||
local DocView = require "core.docview"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
|
||||
config.autoinsert_map = {
|
||||
["["] = "]",
|
||||
["{"] = "}",
|
||||
["("] = ")",
|
||||
['"'] = '"',
|
||||
["'"] = "'",
|
||||
["`"] = "`",
|
||||
}
|
||||
|
||||
|
||||
local function is_closer(chr)
|
||||
for _, v in pairs(config.autoinsert_map) do
|
||||
if v == chr then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function count_char(text, chr)
|
||||
local count = 0
|
||||
for _ in text:gmatch(chr) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
local on_text_input = DocView.on_text_input
|
||||
|
||||
function DocView:on_text_input(text)
|
||||
local mapping = config.autoinsert_map[text]
|
||||
|
||||
-- prevents plugin from operating on `CommandView`
|
||||
if getmetatable(self) ~= DocView then
|
||||
return on_text_input(self, text)
|
||||
end
|
||||
|
||||
-- wrap selection if we have a selection
|
||||
if mapping and self.doc:has_selection() then
|
||||
local l1, c1, l2, c2, swap = self.doc:get_selection(true)
|
||||
self.doc:insert(l2, c2, mapping)
|
||||
self.doc:insert(l1, c1, text)
|
||||
self.doc:set_selection(l1, c1, l2, c2 + 2, swap)
|
||||
return
|
||||
end
|
||||
|
||||
-- skip inserting closing text
|
||||
local chr = self.doc:get_char(self.doc:get_selection())
|
||||
if text == chr and is_closer(chr) then
|
||||
self.doc:move_to(1)
|
||||
return
|
||||
end
|
||||
|
||||
-- don't insert closing quote if we have a non-even number on this line
|
||||
local line = self.doc:get_selection()
|
||||
if text == mapping and count_char(self.doc.lines[line], text) % 2 == 1 then
|
||||
return on_text_input(self, text)
|
||||
end
|
||||
|
||||
-- auto insert closing bracket
|
||||
if mapping and (chr:find("%s") or is_closer(chr) and chr ~= '"') then
|
||||
on_text_input(self, text)
|
||||
on_text_input(self, mapping)
|
||||
self.doc:move_to(-1)
|
||||
return
|
||||
end
|
||||
|
||||
on_text_input(self, text)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function predicate()
|
||||
return getmetatable(core.active_view) == DocView
|
||||
and not core.active_view.doc:has_selection()
|
||||
end
|
||||
|
||||
command.add(predicate, {
|
||||
["autoinsert:backspace"] = function()
|
||||
local doc = core.active_view.doc
|
||||
local l, c = doc:get_selection()
|
||||
local chr = doc:get_char(l, c)
|
||||
if config.autoinsert_map[doc:get_char(l, c - 1)] and is_closer(chr) then
|
||||
doc:delete_to(1)
|
||||
end
|
||||
command.perform "doc:backspace"
|
||||
end,
|
||||
|
||||
["autoinsert:delete-to-previous-word-start"] = function()
|
||||
local doc = core.active_view.doc
|
||||
local le, ce = translate.previous_word_start(doc, doc:get_selection())
|
||||
while true do
|
||||
local l, c = doc:get_selection()
|
||||
if l == le and c == ce then
|
||||
break
|
||||
end
|
||||
command.perform "autoinsert:backspace"
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["backspace"] = "autoinsert:backspace",
|
||||
["ctrl+backspace"] = "autoinsert:delete-to-previous-word-start",
|
||||
["ctrl+shift+backspace"] = "autoinsert:delete-to-previous-word-start",
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
local core = require "core"
|
||||
local config = require "core.config"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
|
||||
local times = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function update_time(doc)
|
||||
local info = system.get_file_info(doc.filename)
|
||||
times[doc] = info.modified
|
||||
end
|
||||
|
||||
|
||||
local function reload_doc(doc)
|
||||
local fp = io.open(doc.filename, "r")
|
||||
local text = fp:read("*a")
|
||||
fp:close()
|
||||
|
||||
local sel = { doc:get_selection() }
|
||||
doc:remove(1, 1, math.huge, math.huge)
|
||||
doc:insert(1, 1, text:gsub("\r", ""):gsub("\n$", ""))
|
||||
doc:set_selection(table.unpack(sel))
|
||||
|
||||
update_time(doc)
|
||||
doc:clean()
|
||||
core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename)
|
||||
end
|
||||
|
||||
|
||||
core.add_thread(function()
|
||||
while true do
|
||||
-- check all doc modified times
|
||||
for _, doc in ipairs(core.docs) do
|
||||
local info = system.get_file_info(doc.filename or "")
|
||||
if info and times[doc] ~= info.modified then
|
||||
reload_doc(doc)
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
-- wait for next scan
|
||||
coroutine.yield(config.project_scan_rate)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- patch `Doc.save|load` to store modified time
|
||||
local load = Doc.load
|
||||
local save = Doc.save
|
||||
|
||||
Doc.load = function(self, ...)
|
||||
local res = load(self, ...)
|
||||
update_time(self)
|
||||
return res
|
||||
end
|
||||
|
||||
Doc.save = function(self, ...)
|
||||
local res = save(self, ...)
|
||||
update_time(self)
|
||||
return res
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
require "plugins.reflow"
|
||||
local config = require "core.config"
|
||||
local command = require "core.command"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.autowrap_files = { "%.md$", "%.txt$" }
|
||||
|
||||
|
||||
local on_text_input = DocView.on_text_input
|
||||
|
||||
DocView.on_text_input = function(self, ...)
|
||||
on_text_input(self, ...)
|
||||
|
||||
-- early-exit if the filename does not match a file type pattern
|
||||
local filename = self.doc.filename or ""
|
||||
local matched = false
|
||||
for _, ptn in ipairs(config.autowrap_files) do
|
||||
if filename:match(ptn) then
|
||||
matched = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not matched then return end
|
||||
|
||||
-- do automatic reflow on line if we're typing at the end of the line and have
|
||||
-- reached the line limit
|
||||
local line, col = self.doc:get_selection()
|
||||
local text = self.doc:get_text(line, 1, line, math.huge)
|
||||
if #text >= config.line_limit and col > #text then
|
||||
command.perform("doc:select-lines")
|
||||
command.perform("reflow:reflow")
|
||||
command.perform("doc:move-to-next-char")
|
||||
command.perform("doc:move-to-previous-char")
|
||||
end
|
||||
end
|
|
@ -1,117 +0,0 @@
|
|||
local core = require "core"
|
||||
local style = require "core.style"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
local bracket_maps = {
|
||||
-- [ ] ( ) { }
|
||||
{ [91] = 93, [40] = 41, [123] = 125, step = 1 },
|
||||
-- ] [ ) ( } {
|
||||
{ [93] = 91, [41] = 40, [125] = 123, step = -1 },
|
||||
}
|
||||
|
||||
|
||||
local function get_matching_bracket(doc, line, col, line_limit, open_byte, close_byte, step)
|
||||
local end_line = line + line_limit * step
|
||||
local depth = 0
|
||||
|
||||
while line ~= end_line do
|
||||
local byte = doc.lines[line]:byte(col)
|
||||
if byte == open_byte then
|
||||
depth = depth + 1
|
||||
elseif byte == close_byte then
|
||||
depth = depth - 1
|
||||
if depth == 0 then return line, col end
|
||||
end
|
||||
|
||||
local prev_line, prev_col = line, col
|
||||
line, col = doc:position_offset(line, col, step)
|
||||
if line == prev_line and col == prev_col then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local state = {}
|
||||
|
||||
local function update_state(line_limit)
|
||||
line_limit = line_limit or math.huge
|
||||
|
||||
-- reset if we don't have a document (eg. DocView isn't focused)
|
||||
local doc = core.active_view.doc
|
||||
if not doc then
|
||||
state = {}
|
||||
return
|
||||
end
|
||||
|
||||
-- early exit if nothing has changed since the last call
|
||||
local line, col = doc:get_selection()
|
||||
local change_id = doc:get_change_id()
|
||||
if state.doc == doc and state.line == line and state.col == col
|
||||
and state.change_id == change_id and state.limit == line_limit then
|
||||
return
|
||||
end
|
||||
|
||||
-- find matching bracket if we're on a bracket
|
||||
local line2, col2
|
||||
for _, map in ipairs(bracket_maps) do
|
||||
for i = 0, -1, -1 do
|
||||
local line, col = doc:position_offset(line, col, i)
|
||||
local open = doc.lines[line]:byte(col)
|
||||
local close = map[open]
|
||||
if close then
|
||||
line2, col2 = get_matching_bracket(doc, line, col, line_limit, open, close, map.step)
|
||||
goto found
|
||||
end
|
||||
end
|
||||
end
|
||||
::found::
|
||||
|
||||
-- update
|
||||
state = {
|
||||
change_id = change_id,
|
||||
doc = doc,
|
||||
line = line,
|
||||
col = col,
|
||||
line2 = line2,
|
||||
col2 = col2,
|
||||
limit = line_limit,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local update = DocView.update
|
||||
|
||||
function DocView:update(...)
|
||||
update(self, ...)
|
||||
update_state(100)
|
||||
end
|
||||
|
||||
|
||||
local draw_line_text = DocView.draw_line_text
|
||||
|
||||
function DocView:draw_line_text(idx, x, y)
|
||||
draw_line_text(self, idx, x, y)
|
||||
|
||||
if self.doc == state.doc and idx == state.line2 then
|
||||
local color = style.bracketmatch_color or style.syntax["function"]
|
||||
local x1 = x + self:get_col_x_offset(idx, state.col2)
|
||||
local x2 = x + self:get_col_x_offset(idx, state.col2 + 1)
|
||||
local h = math.ceil(1 * SCALE)
|
||||
renderer.draw_rect(x1, y + self:get_line_height() - h, x2 - x1, h, color)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["bracket-match:move-to-matching"] = function()
|
||||
update_state()
|
||||
if state.line2 then
|
||||
core.active_view.doc:set_selection(state.line2, state.col2)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
keymap.add { ["ctrl+m"] = "bracket-match:move-to-matching" }
|
|
@ -1,53 +0,0 @@
|
|||
-- CloseConfirmX plugin for lite text editor
|
||||
-- implementation by chekoopa
|
||||
|
||||
local core = require "core"
|
||||
local config = require "core.config"
|
||||
|
||||
config.closeconfirmx_use_legacy = false
|
||||
config.closeconfirmx_use_short_name = true
|
||||
|
||||
local legacy_confirm = core.confirm_close_all
|
||||
|
||||
local function commandful_confirm()
|
||||
local dirty_count = 0
|
||||
local dirty_name
|
||||
for _, doc in ipairs(core.docs) do
|
||||
if doc:is_dirty() then
|
||||
dirty_count = dirty_count + 1
|
||||
dirty_name = doc:get_name()
|
||||
end
|
||||
end
|
||||
if dirty_count > 0 then
|
||||
local text
|
||||
if dirty_count == 1 then
|
||||
if config.closeconfirmx_use_short_name then
|
||||
dirty_name = dirty_name:match("[^/%\\]*$")
|
||||
end
|
||||
text = string.format("Unsaved changes in \"%s\"; Confirm Exit", dirty_name)
|
||||
else
|
||||
text = string.format("Unsaved changes in %d docs; Confirm Exit", dirty_count)
|
||||
end
|
||||
core.command_view:enter(text, function(_, item)
|
||||
if item.text:match("^[cC]") then
|
||||
core.quit(true)
|
||||
end
|
||||
end, function(text)
|
||||
local items = {}
|
||||
if not text:find("^[^sS]") then table.insert(items, "Stay here") end
|
||||
if not text:find("^[^cC]") then table.insert(items, "Close Without Saving") end
|
||||
return items
|
||||
end)
|
||||
-- as we delegate a choice inside the callback,
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function core.confirm_close_all()
|
||||
if config.closeconfirmx_use_legacy then
|
||||
return legacy_confirm()
|
||||
else
|
||||
return commandful_confirm()
|
||||
end
|
||||
end
|
|
@ -1,54 +0,0 @@
|
|||
local common = require "core.common"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
|
||||
local white = { common.color "#ffffff" }
|
||||
local black = { common.color "#000000" }
|
||||
local tmp = {}
|
||||
|
||||
|
||||
local function draw_color_previews(self, idx, x, y, ptn, base, nibbles)
|
||||
local text = self.doc.lines[idx]
|
||||
local s, e = 0, 0
|
||||
|
||||
while true do
|
||||
s, e = text:find(ptn, e + 1)
|
||||
if not s then break end
|
||||
|
||||
local str = text:sub(s, e)
|
||||
local r, g, b = str:match(ptn)
|
||||
r, g, b = tonumber(r, base), tonumber(g, base), tonumber(b, base)
|
||||
|
||||
-- #123 becomes #112233
|
||||
if nibbles then
|
||||
r = r * 16
|
||||
g = g * 16
|
||||
b = b * 16
|
||||
end
|
||||
|
||||
local x1 = x + self:get_col_x_offset(idx, s)
|
||||
local x2 = x + self:get_col_x_offset(idx, e + 1)
|
||||
local oy = self:get_line_text_y_offset()
|
||||
|
||||
local text_color = math.max(r, g, b) < 128 and white or black
|
||||
tmp[1], tmp[2], tmp[3] = r, g, b
|
||||
|
||||
local l1, _, l2, _ = self.doc:get_selection(true)
|
||||
|
||||
if not (self.doc:has_selection() and idx >= l1 and idx <= l2) then
|
||||
renderer.draw_rect(x1, y, x2 - x1, self:get_line_height(), tmp)
|
||||
renderer.draw_text(self:get_font(), str, x1, y + oy, text_color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local draw_line_text = DocView.draw_line_text
|
||||
|
||||
function DocView:draw_line_text(idx, x, y)
|
||||
draw_line_text(self, idx, x, y)
|
||||
draw_color_previews(self, idx, x, y, "#(%x%x)(%x%x)(%x%x)%f[%W]", 16)
|
||||
draw_color_previews(self, idx, x, y, "#(%x)(%x)(%x)%f[%W]", 16, true) -- support #fff css format
|
||||
draw_color_previews(self, idx, x, y, "rgb?%((%d+)%D+(%d+)%D+(%d+).-%)", 10)
|
||||
draw_color_previews(self, idx, x, y, "rgba?%((%d+)%D+(%d+)%D+(%d+).-%)", 10)
|
||||
end
|
|
@ -1,383 +0,0 @@
|
|||
local core = require "core"
|
||||
local keymap = require "core.keymap"
|
||||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local View = require "core.view"
|
||||
|
||||
config.console_size = 250 * SCALE
|
||||
config.max_console_lines = 200
|
||||
config.autoscroll_console = true
|
||||
|
||||
local files = {
|
||||
script = core.temp_filename(PLATFORM == "Windows" and ".bat"),
|
||||
script2 = core.temp_filename(PLATFORM == "Windows" and ".bat"),
|
||||
output = core.temp_filename(),
|
||||
complete = core.temp_filename(),
|
||||
}
|
||||
|
||||
local console = {}
|
||||
|
||||
local views = {}
|
||||
local pending_threads = {}
|
||||
local thread_active = false
|
||||
local output = nil
|
||||
local output_id = 0
|
||||
local visible = false
|
||||
|
||||
function console.clear()
|
||||
output = { { text = "", time = 0 } }
|
||||
end
|
||||
|
||||
|
||||
local function read_file(filename, offset)
|
||||
local fp = io.open(filename, "rb")
|
||||
fp:seek("set", offset or 0)
|
||||
local res = fp:read("*a")
|
||||
fp:close()
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function write_file(filename, text)
|
||||
local fp = io.open(filename, "w")
|
||||
fp:write(text)
|
||||
fp:close()
|
||||
end
|
||||
|
||||
|
||||
local function lines(text)
|
||||
return (text .. "\n"):gmatch("(.-)\n")
|
||||
end
|
||||
|
||||
|
||||
local function push_output(str, opt)
|
||||
local first = true
|
||||
for line in lines(str) do
|
||||
if first then
|
||||
line = table.remove(output).text .. line
|
||||
end
|
||||
line = line:gsub("\x1b%[[%d;]+m", "") -- strip ANSI colors
|
||||
table.insert(output, {
|
||||
text = line,
|
||||
time = os.time(),
|
||||
icon = line:find(opt.error_pattern) and "!"
|
||||
or line:find(opt.warning_pattern) and "i",
|
||||
file_pattern = opt.file_pattern,
|
||||
})
|
||||
if #output > config.max_console_lines then
|
||||
table.remove(output, 1)
|
||||
for view in pairs(views) do
|
||||
view:on_line_removed()
|
||||
end
|
||||
end
|
||||
first = false
|
||||
end
|
||||
output_id = output_id + 1
|
||||
core.redraw = true
|
||||
end
|
||||
|
||||
|
||||
local function init_opt(opt)
|
||||
local res = {
|
||||
command = "",
|
||||
file_pattern = "[^?:%s]+%.[^?:%s]+",
|
||||
error_pattern = "error",
|
||||
warning_pattern = "warning",
|
||||
on_complete = function() end,
|
||||
}
|
||||
for k, v in pairs(res) do
|
||||
res[k] = opt[k] or v
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
function console.run(opt)
|
||||
opt = init_opt(opt)
|
||||
|
||||
local function thread()
|
||||
-- init script file(s)
|
||||
if PLATFORM == "Windows" then
|
||||
write_file(files.script, opt.command .. "\n")
|
||||
write_file(files.script2, string.format([[
|
||||
@echo off
|
||||
call %q >%q 2>&1
|
||||
echo "" >%q
|
||||
exit
|
||||
]], files.script, files.output, files.complete))
|
||||
system.exec(string.format("call %q", files.script2))
|
||||
else
|
||||
write_file(files.script, string.format([[
|
||||
%s
|
||||
touch %q
|
||||
]], opt.command, files.complete))
|
||||
system.exec(string.format("bash %q >%q 2>&1", files.script, files.output))
|
||||
end
|
||||
|
||||
-- checks output file for change and reads
|
||||
local last_size = 0
|
||||
local function check_output_file()
|
||||
if PLATFORM == "Windows" then
|
||||
local fp = io.open(files.output)
|
||||
if fp then fp:close() end
|
||||
end
|
||||
local info = system.get_file_info(files.output)
|
||||
if info and info.size > last_size then
|
||||
local text = read_file(files.output, last_size)
|
||||
push_output(text, opt)
|
||||
last_size = info.size
|
||||
end
|
||||
end
|
||||
|
||||
-- read output file until we get a file indicating completion
|
||||
while not system.get_file_info(files.complete) do
|
||||
check_output_file()
|
||||
coroutine.yield(0.1)
|
||||
end
|
||||
check_output_file()
|
||||
if output[#output].text ~= "" then
|
||||
push_output("\n", opt)
|
||||
end
|
||||
push_output("!DIVIDER\n", opt)
|
||||
|
||||
-- clean up and finish
|
||||
for _, file in pairs(files) do
|
||||
os.remove(file)
|
||||
end
|
||||
opt.on_complete()
|
||||
|
||||
-- handle pending thread
|
||||
local pending = table.remove(pending_threads, 1)
|
||||
if pending then
|
||||
core.add_thread(pending)
|
||||
else
|
||||
thread_active = false
|
||||
end
|
||||
end
|
||||
|
||||
-- push/init thread
|
||||
if thread_active then
|
||||
table.insert(pending_threads, thread)
|
||||
else
|
||||
core.add_thread(thread)
|
||||
thread_active = true
|
||||
end
|
||||
|
||||
-- make sure static console is visible if it's the only ConsoleView
|
||||
local count = 0
|
||||
for _ in pairs(views) do count = count + 1 end
|
||||
if count == 1 then visible = true end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local ConsoleView = View:extend()
|
||||
|
||||
function ConsoleView:new()
|
||||
ConsoleView.super.new(self)
|
||||
self.scrollable = true
|
||||
self.hovered_idx = -1
|
||||
views[self] = true
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:try_close(...)
|
||||
ConsoleView.super.try_close(self, ...)
|
||||
views[self] = nil
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:get_name()
|
||||
return "Console"
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:get_line_height()
|
||||
return style.code_font:get_height() * config.line_height
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:get_line_count()
|
||||
return #output - (output[#output].text == "" and 1 or 0)
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:get_scrollable_size()
|
||||
return self:get_line_count() * self:get_line_height() + style.padding.y * 2
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:get_visible_line_range()
|
||||
local lh = self:get_line_height()
|
||||
local min = math.max(1, math.floor(self.scroll.y / lh))
|
||||
return min, min + math.floor(self.size.y / lh) + 1
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:on_mouse_moved(mx, my, ...)
|
||||
ConsoleView.super.on_mouse_moved(self, mx, my, ...)
|
||||
self.hovered_idx = 0
|
||||
for i, item, x,y,w,h in self:each_visible_line() do
|
||||
if mx >= x and my >= y and mx < x + w and my < y + h then
|
||||
if item.text:find(item.file_pattern) then
|
||||
self.hovered_idx = i
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function resolve_file(name)
|
||||
if system.get_file_info(name) then
|
||||
return name
|
||||
end
|
||||
local filenames = {}
|
||||
for _, f in ipairs(core.project_files) do
|
||||
table.insert(filenames, f.filename)
|
||||
end
|
||||
local t = common.fuzzy_match(filenames, name)
|
||||
return t[1]
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:on_line_removed()
|
||||
local diff = self:get_line_height()
|
||||
self.scroll.y = self.scroll.y - diff
|
||||
self.scroll.to.y = self.scroll.to.y - diff
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:on_mouse_pressed(...)
|
||||
local caught = ConsoleView.super.on_mouse_pressed(self, ...)
|
||||
if caught then
|
||||
return
|
||||
end
|
||||
local item = output[self.hovered_idx]
|
||||
if item then
|
||||
local file, line, col = item.text:match(item.file_pattern)
|
||||
local resolved_file = resolve_file(file)
|
||||
if not resolved_file then
|
||||
core.error("Couldn't resolve file \"%s\"", file)
|
||||
return
|
||||
end
|
||||
core.try(function()
|
||||
core.set_active_view(core.last_active_view)
|
||||
local dv = core.root_view:open_doc(core.open_doc(resolved_file))
|
||||
if line then
|
||||
dv.doc:set_selection(line, col or 0)
|
||||
dv:scroll_to_line(line, false, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:each_visible_line()
|
||||
return coroutine.wrap(function()
|
||||
local x, y = self:get_content_offset()
|
||||
local lh = self:get_line_height()
|
||||
local min, max = self:get_visible_line_range()
|
||||
y = y + lh * (min - 1) + style.padding.y
|
||||
max = math.min(max, self:get_line_count())
|
||||
|
||||
for i = min, max do
|
||||
local item = output[i]
|
||||
if not item then break end
|
||||
coroutine.yield(i, item, x, y, self.size.x, lh)
|
||||
y = y + lh
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:update(...)
|
||||
if self.last_output_id ~= output_id then
|
||||
if config.autoscroll_console then
|
||||
self.scroll.to.y = self:get_scrollable_size()
|
||||
end
|
||||
self.last_output_id = output_id
|
||||
end
|
||||
ConsoleView.super.update(self, ...)
|
||||
end
|
||||
|
||||
|
||||
function ConsoleView:draw()
|
||||
self:draw_background(style.background)
|
||||
local icon_w = style.icon_font:get_width("!")
|
||||
|
||||
for i, item, x, y, w, h in self:each_visible_line() do
|
||||
local tx = x + style.padding.x
|
||||
local time = os.date("%H:%M:%S", item.time)
|
||||
local color = style.text
|
||||
if self.hovered_idx == i then
|
||||
color = style.accent
|
||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
||||
end
|
||||
if item.text == "!DIVIDER" then
|
||||
local w = style.font:get_width(time)
|
||||
renderer.draw_rect(tx, y + h / 2, w, math.ceil(SCALE * 1), style.dim)
|
||||
else
|
||||
tx = common.draw_text(style.font, style.dim, time, "left", tx, y, w, h)
|
||||
tx = tx + style.padding.x
|
||||
if item.icon then
|
||||
common.draw_text(style.icon_font, color, item.icon, "left", tx, y, w, h)
|
||||
end
|
||||
tx = tx + icon_w + style.padding.x
|
||||
common.draw_text(style.code_font, color, item.text, "left", tx, y, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
self:draw_scrollbar(self)
|
||||
end
|
||||
|
||||
|
||||
-- init static bottom-of-screen console
|
||||
local view = ConsoleView()
|
||||
local node = core.root_view:get_active_node()
|
||||
node:split("down", view, true)
|
||||
|
||||
function view:update(...)
|
||||
local dest = visible and config.console_size or 0
|
||||
self:move_towards(self.size, "y", dest)
|
||||
ConsoleView.update(self, ...)
|
||||
end
|
||||
|
||||
|
||||
local last_command = ""
|
||||
|
||||
command.add(nil, {
|
||||
["console:reset-output"] = function()
|
||||
output = { { text = "", time = 0 } }
|
||||
end,
|
||||
|
||||
["console:open-console"] = function()
|
||||
local node = core.root_view:get_active_node()
|
||||
node:add_view(ConsoleView())
|
||||
end,
|
||||
|
||||
["console:toggle"] = function()
|
||||
visible = not visible
|
||||
end,
|
||||
|
||||
["console:run"] = function()
|
||||
core.command_view:set_text(last_command, true)
|
||||
core.command_view:enter("Run Console Command", function(cmd)
|
||||
console.run { command = cmd }
|
||||
last_command = cmd
|
||||
end)
|
||||
end
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["ctrl+."] = "console:toggle",
|
||||
["ctrl+shift+."] = "console:run",
|
||||
}
|
||||
|
||||
-- for `workspace` plugin:
|
||||
package.loaded["plugins.console.view"] = ConsoleView
|
||||
|
||||
console.clear()
|
||||
return console
|
|
@ -1,271 +0,0 @@
|
|||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
local style = require "core.style"
|
||||
local Object = require "core.object"
|
||||
local RootView = require "core.rootview"
|
||||
|
||||
local border_width = 1
|
||||
local divider_width = 1
|
||||
local DIVIDER = {}
|
||||
|
||||
local ContextMenu = Object:extend()
|
||||
|
||||
ContextMenu.DIVIDER = DIVIDER
|
||||
|
||||
function ContextMenu:new()
|
||||
self.itemset = {}
|
||||
self.show_context_menu = false
|
||||
self.selected = -1
|
||||
self.height = 0
|
||||
self.position = { x = 0, y = 0 }
|
||||
end
|
||||
|
||||
local function get_item_size(item)
|
||||
local lw, lh
|
||||
if item == DIVIDER then
|
||||
lw = 0
|
||||
lh = divider_width
|
||||
else
|
||||
lw = style.font:get_width(item.text)
|
||||
if item.info then
|
||||
lw = lw + style.padding.x + style.font:get_width(item.info)
|
||||
end
|
||||
lh = style.font:get_height() + style.padding.y
|
||||
end
|
||||
return lw, lh
|
||||
end
|
||||
|
||||
function ContextMenu:register(predicate, items)
|
||||
if type(predicate) == "string" then
|
||||
predicate = require(predicate)
|
||||
end
|
||||
if type(predicate) == "table" then
|
||||
local class = predicate
|
||||
predicate = function() return core.active_view:is(class) end
|
||||
end
|
||||
|
||||
local width, height = 0, 0 --precalculate the size of context menu
|
||||
for i, item in ipairs(items) do
|
||||
if item ~= DIVIDER then
|
||||
item.info = keymap.reverse_map[item.command]
|
||||
end
|
||||
local lw, lh = get_item_size(item)
|
||||
width = math.max(width, lw)
|
||||
height = height + lh
|
||||
end
|
||||
width = width + style.padding.x * 2
|
||||
items.width, items.height = width, height
|
||||
table.insert(self.itemset, { predicate = predicate, items = items })
|
||||
end
|
||||
|
||||
function ContextMenu:show(x, y)
|
||||
self.items = nil
|
||||
for _, items in ipairs(self.itemset) do
|
||||
if items.predicate(x, y) then
|
||||
self.items = items.items
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if self.items then
|
||||
local w, h = self.items.width, self.items.height
|
||||
|
||||
-- by default the box is opened on the right and below
|
||||
if x + w >= core.root_view.size.x then
|
||||
x = x - w
|
||||
end
|
||||
if y + h >= core.root_view.size.y then
|
||||
y = y - h
|
||||
end
|
||||
|
||||
self.position.x, self.position.y = x, y
|
||||
self.show_context_menu = true
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ContextMenu:hide()
|
||||
self.show_context_menu = false
|
||||
self.items = nil
|
||||
self.selected = -1
|
||||
self.height = 0
|
||||
end
|
||||
|
||||
function ContextMenu:each_item()
|
||||
local x, y, w = self.position.x, self.position.y, self.items.width
|
||||
local oy = y
|
||||
return coroutine.wrap(function()
|
||||
for i, item in ipairs(self.items) do
|
||||
local _, lh = get_item_size(item)
|
||||
if y - oy > self.height then break end
|
||||
coroutine.yield(i, item, x, y, w, lh)
|
||||
y = y + lh
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ContextMenu:on_mouse_moved(px, py)
|
||||
if not self.show_context_menu then return end
|
||||
|
||||
for i, item, x, y, w, h in self:each_item() do
|
||||
if px > x and px <= x + w and py > y and py <= y + h then
|
||||
system.set_cursor("arrow")
|
||||
self.selected = i
|
||||
return true
|
||||
end
|
||||
end
|
||||
self.selected = -1
|
||||
return true
|
||||
end
|
||||
|
||||
function ContextMenu:on_selected(item)
|
||||
if type(item.command) == "string" then
|
||||
command.perform(item.command)
|
||||
else
|
||||
item.command()
|
||||
end
|
||||
end
|
||||
|
||||
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
|
||||
local selected = (self.items or {})[self.selected]
|
||||
local caught = false
|
||||
|
||||
self:hide()
|
||||
if button == "left" then
|
||||
if selected then
|
||||
self:on_selected(selected)
|
||||
caught = true
|
||||
end
|
||||
end
|
||||
|
||||
if button == "right" then
|
||||
caught = self:show(x, y)
|
||||
end
|
||||
return caught
|
||||
end
|
||||
|
||||
-- copied from core.docview
|
||||
function ContextMenu:move_towards(t, k, dest, rate)
|
||||
if type(t) ~= "table" then
|
||||
return self:move_towards(self, t, k, dest, rate)
|
||||
end
|
||||
local val = t[k]
|
||||
if math.abs(val - dest) < 0.5 then
|
||||
t[k] = dest
|
||||
else
|
||||
t[k] = common.lerp(val, dest, rate or 0.5)
|
||||
end
|
||||
if val ~= dest then
|
||||
core.redraw = true
|
||||
end
|
||||
end
|
||||
|
||||
function ContextMenu:update()
|
||||
if self.show_context_menu then
|
||||
self:move_towards("height", self.items.height)
|
||||
end
|
||||
end
|
||||
|
||||
function ContextMenu:draw()
|
||||
if not self.show_context_menu then return end
|
||||
core.root_view:defer_draw(self.draw_context_menu, self)
|
||||
end
|
||||
|
||||
function ContextMenu:draw_context_menu()
|
||||
if not self.items then return end
|
||||
local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height
|
||||
|
||||
renderer.draw_rect(
|
||||
bx - border_width,
|
||||
by - border_width,
|
||||
bw + (border_width * 2),
|
||||
bh + (border_width * 2),
|
||||
style.divider
|
||||
)
|
||||
renderer.draw_rect(bx, by, bw, bh, style.background3)
|
||||
|
||||
for i, item, x, y, w, h in self:each_item() do
|
||||
if item == DIVIDER then
|
||||
renderer.draw_rect(x, y, w, h, style.caret)
|
||||
else
|
||||
if i == self.selected then
|
||||
renderer.draw_rect(x, y, w, h, style.selection)
|
||||
end
|
||||
|
||||
common.draw_text(style.font, style.text, item.text, "left", x + style.padding.x, y, w, h)
|
||||
if item.info then
|
||||
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local menu = ContextMenu()
|
||||
local root_view_on_mouse_pressed = RootView.on_mouse_pressed
|
||||
local root_view_on_mouse_moved = RootView.on_mouse_moved
|
||||
local root_view_update = RootView.update
|
||||
local root_view_draw = RootView.draw
|
||||
|
||||
function RootView:on_mouse_moved(...)
|
||||
if menu:on_mouse_moved(...) then return end
|
||||
root_view_on_mouse_moved(self, ...)
|
||||
end
|
||||
|
||||
-- copied from core.rootview
|
||||
function RootView:on_mouse_pressed(button, x,y, clicks)
|
||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||
if div then
|
||||
self.dragged_divider = div
|
||||
return
|
||||
end
|
||||
local node = self.root_node:get_child_overlapping_point(x, y)
|
||||
local idx = node:get_tab_overlapping_point(x, y)
|
||||
if idx then
|
||||
node:set_active_view(node.views[idx])
|
||||
if button == "right" then --< @r-lyeh middle>right
|
||||
node:close_active_view(self.root_node)
|
||||
end
|
||||
else
|
||||
core.set_active_view(node.active_view)
|
||||
-- send to context menu first
|
||||
if not menu:on_mouse_pressed(button, x, y, clicks) then
|
||||
node.active_view:on_mouse_pressed(button, x, y, clicks)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RootView:update(...)
|
||||
root_view_update(self, ...)
|
||||
menu:update()
|
||||
end
|
||||
|
||||
function RootView:draw(...)
|
||||
root_view_draw(self, ...)
|
||||
menu:draw()
|
||||
end
|
||||
|
||||
command.add(nil, {
|
||||
["context:show"] = function()
|
||||
menu:show(core.active_view.position.x, core.active_view.position.y)
|
||||
end
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["menu"] = "context:show"
|
||||
}
|
||||
|
||||
-- register some sensible defaults
|
||||
menu:register("core.docview", {
|
||||
{ text = "Cut", command = "doc:cut" },
|
||||
{ text = "Copy", command = "doc:copy" },
|
||||
{ text = "Paste", command = "doc:paste" },
|
||||
DIVIDER,
|
||||
{ text = "Command Palette...", command = "core:find-command" }
|
||||
})
|
||||
|
||||
return menu
|
|
@ -1,63 +0,0 @@
|
|||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local DocView = require "core.docview"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
local cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
|
||||
local function detect_indent(doc)
|
||||
for _, text in ipairs(doc.lines) do
|
||||
local str = text:match("^ +")
|
||||
if str then return "soft", #str end
|
||||
local str = text:match("^\t+")
|
||||
if str then return "hard" end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function update_cache(doc)
|
||||
local type, size = detect_indent(doc)
|
||||
if type then
|
||||
cache[doc] = { type = type, size = size }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local new = Doc.new
|
||||
function Doc:new(...)
|
||||
new(self, ...)
|
||||
update_cache(self)
|
||||
end
|
||||
|
||||
local clean = Doc.clean
|
||||
function Doc:clean(...)
|
||||
clean(self, ...)
|
||||
update_cache(self)
|
||||
end
|
||||
|
||||
|
||||
local function with_indent_override(doc, fn, ...)
|
||||
local c = cache[doc]
|
||||
if not c then
|
||||
return fn(...)
|
||||
end
|
||||
local type, size = config.tab_type, config.indent_size
|
||||
config.tab_type, config.indent_size = c.type, c.size or config.indent_size
|
||||
local r1, r2, r3 = fn(...)
|
||||
config.tab_type, config.indent_size = type, size
|
||||
return r1, r2, r3
|
||||
end
|
||||
|
||||
|
||||
local perform = command.perform
|
||||
function command.perform(...)
|
||||
return with_indent_override(core.active_view.doc, perform, ...)
|
||||
end
|
||||
|
||||
|
||||
local draw = DocView.draw
|
||||
function DocView:draw(...)
|
||||
return with_indent_override(self.doc, draw, self, ...)
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
local command = require "core.command"
|
||||
|
||||
-- originally written by luveti
|
||||
|
||||
config.whitespace_map = { [" "] = "·", ["\t"] = "»" }
|
||||
config.draw_whitespace = true
|
||||
|
||||
local draw_line_text = DocView.draw_line_text
|
||||
|
||||
function DocView:draw_line_text(idx, x, y)
|
||||
draw_line_text(self, idx, x, y)
|
||||
if not config.draw_whitespace then return end
|
||||
|
||||
local text = self.doc.lines[idx]
|
||||
local tx, ty = x, y + self:get_line_text_y_offset()
|
||||
local font = self:get_font()
|
||||
local color = style.whitespace or style.syntax.comment
|
||||
local map = config.whitespace_map
|
||||
|
||||
for chr in common.utf8_chars(text) do
|
||||
local rep = map[chr]
|
||||
if rep then
|
||||
renderer.draw_text(font, rep, tx, ty, color)
|
||||
end
|
||||
tx = tx + font:get_width(chr)
|
||||
end
|
||||
end
|
||||
|
||||
command.add("core.docview", {
|
||||
["draw-whitespace:toggle"] = function() config.draw_whitespace = not config.draw_whitespace end,
|
||||
["draw-whitespace:disable"] = function() config.draw_whitespace = false end,
|
||||
["draw-whitespace:enable"] = function() config.draw_whitespace = true end,
|
||||
})
|
|
@ -1,29 +0,0 @@
|
|||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
local function eof_newline(doc)
|
||||
local leof,neof = #doc.lines,#doc.lines
|
||||
for i = leof,1,-1 do
|
||||
if not string.match(doc.lines[i],"^%s*$") then break end
|
||||
neof = i
|
||||
end
|
||||
local eol,_ = string.find(doc.lines[neof],"\n")
|
||||
if eol then
|
||||
doc:remove(neof,eol,math.huge,math.huge)
|
||||
return
|
||||
end
|
||||
doc:insert(neof,math.huge,"\n")
|
||||
end
|
||||
|
||||
command.add("core.docview", {
|
||||
["eof-newline:eof-newline"] = function()
|
||||
eof_newline(core.active_view.doc)
|
||||
end,
|
||||
})
|
||||
|
||||
local save = Doc.save
|
||||
Doc.save = function(self, ...)
|
||||
eof_newline(self)
|
||||
save(self, ...)
|
||||
end
|
|
@ -1,167 +0,0 @@
|
|||
-- mod-version:1 -- lite-xl 1.16
|
||||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local command = require "core.command"
|
||||
|
||||
local fsutils = {}
|
||||
|
||||
function fsutils.iterdir(dir)
|
||||
local stack = { dir }
|
||||
return function()
|
||||
local path = table.remove(stack)
|
||||
if not path then return end
|
||||
|
||||
for _, file in ipairs(system.list_dir(path) or {}) do
|
||||
stack[#stack + 1] = path .. PATHSEP .. file
|
||||
end
|
||||
|
||||
return path, system.get_file_info(path)
|
||||
end
|
||||
end
|
||||
|
||||
function fsutils.delete(dir, yield)
|
||||
local dirs = {}
|
||||
local n = 0
|
||||
for filename, stat in fsutils.iterdir(dir) do
|
||||
if stat.type == "dir" then
|
||||
-- this will later allow us to delete the dirs in correct sequence
|
||||
table.insert(dirs, filename)
|
||||
else
|
||||
os.remove(filename)
|
||||
if yield then
|
||||
n = n + 1
|
||||
coroutine.yield(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
for i = #dirs, 1, -1 do
|
||||
os.remove(dirs[i])
|
||||
if yield then
|
||||
n = n + 1
|
||||
coroutine.yield(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function fsutils.move(oldname, newname)
|
||||
os.rename(oldname, newname)
|
||||
end
|
||||
|
||||
function fsutils.split(path)
|
||||
local segments = {}
|
||||
local pos = 1
|
||||
while true do
|
||||
local s, e = string.find(path, "[/\\]+", pos)
|
||||
if not s then break end
|
||||
table.insert(segments, string.sub(path, pos, s - 1))
|
||||
pos = e + 1
|
||||
end
|
||||
table.insert(list, string.sub(path, pos))
|
||||
|
||||
if segments[#segments] == '' then
|
||||
table.remove(segments)
|
||||
end
|
||||
|
||||
return segments
|
||||
end
|
||||
|
||||
function fsutils.normalize(path)
|
||||
return table.concat(fsutils.split(path), PATHSEP)
|
||||
end
|
||||
|
||||
function fsutils.normalize_posix(path)
|
||||
return table.concat(fsutils.split(path), '/')
|
||||
end
|
||||
|
||||
function fsutils.mkdir(path)
|
||||
local segments = fsutils.split(path)
|
||||
if system.mkdir then
|
||||
local p = ""
|
||||
for i = 1, #segments do
|
||||
p = table.concat(segments, PATHSEP, 1, i)
|
||||
end
|
||||
|
||||
if p == "" then
|
||||
return nil, "path empty", p
|
||||
end
|
||||
|
||||
local stat = system.get_file_info(p)
|
||||
if stat and stat.type == "file" then
|
||||
return nil, "path exists as a file", p
|
||||
end
|
||||
local ok, err = system.mkdir(p)
|
||||
if not ok then
|
||||
return nil, err, p
|
||||
end
|
||||
else
|
||||
-- just wing it lol
|
||||
system.exec(string.format(PLATFORM == "Windows" and "setlocal enableextensions & mkdir %q" or "mkdir -p %q", fsutils.normalize(path)))
|
||||
end
|
||||
end
|
||||
|
||||
local function async_exec(f, cb)
|
||||
cb = cb or function() end
|
||||
local co = coroutine.create(f)
|
||||
local function resolve(...)
|
||||
local ok, exec_body = coroutine.resume(co, ...)
|
||||
if not ok then
|
||||
error(debug.traceback(co, exec_body))
|
||||
end
|
||||
if coroutine.status(co) ~= "dead" then
|
||||
exec_body(resolve)
|
||||
end
|
||||
end
|
||||
resolve(cb)
|
||||
end
|
||||
|
||||
local function prompt(text, suggest)
|
||||
return coroutine.yield(function(resolve)
|
||||
core.command_view:enter(text, resolve, suggest)
|
||||
end)
|
||||
end
|
||||
|
||||
command.add(nil, {
|
||||
["files:delete"] = function()
|
||||
async_exec(function()
|
||||
local path = prompt("Delete", common.path_suggest)
|
||||
|
||||
core.add_thread(function()
|
||||
-- we use a wrapping coroutine to get status
|
||||
local function delete()
|
||||
return coroutine.wrap(function() fsutils.delete(path, true) end)
|
||||
end
|
||||
|
||||
for n in delete() do
|
||||
if n % 100 == 0 then
|
||||
core.log("Deleted %d items...", n)
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
core.log("%q deleted.", path)
|
||||
end)
|
||||
end)
|
||||
end,
|
||||
["files:move"] = function()
|
||||
async_exec(function()
|
||||
local oldname = prompt("Move", common.path_suggest)
|
||||
local newname = prompt("To", common.path_suggest)
|
||||
|
||||
fsutils.move(oldname, newname)
|
||||
core.log("Moved %q to %q", oldname, newname)
|
||||
end)
|
||||
end
|
||||
})
|
||||
|
||||
if not command.map["files:create-directory"] then
|
||||
command.add(nil, {
|
||||
["files:create-directory"] = function()
|
||||
async_exec(function()
|
||||
local path = prompt("Name", common.path_suggest)
|
||||
fsutils.mkdir(path)
|
||||
core.log("%q created.", path)
|
||||
end)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
return fsutils
|
|
@ -1,45 +0,0 @@
|
|||
local style = require "core.style"
|
||||
local config = require "core.config"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
|
||||
local function get_line_spaces(doc, idx, dir)
|
||||
local text = doc.lines[idx]
|
||||
if not text then
|
||||
return 0
|
||||
end
|
||||
local s, e = text:find("^%s*")
|
||||
if e == #text then
|
||||
return get_line_spaces(doc, idx + dir, dir)
|
||||
end
|
||||
local n = 0
|
||||
for i = s, e do
|
||||
n = n + (text:byte(i) == 9 and config.indent_size or 1)
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
|
||||
local function get_line_indent_guide_spaces(doc, idx)
|
||||
if doc.lines[idx]:find("^%s*\n") then
|
||||
return math.max(
|
||||
get_line_spaces(doc, idx - 1, -1),
|
||||
get_line_spaces(doc, idx + 1, 1))
|
||||
end
|
||||
return get_line_spaces(doc, idx)
|
||||
end
|
||||
|
||||
|
||||
local draw_line_text = DocView.draw_line_text
|
||||
|
||||
function DocView:draw_line_text(idx, x, y)
|
||||
local spaces = get_line_indent_guide_spaces(self.doc, idx)
|
||||
local sw = self:get_font():get_width(" ")
|
||||
local w = math.ceil(1 * SCALE)
|
||||
local h = self:get_line_height()
|
||||
for i = 0, spaces - 1, config.indent_size do
|
||||
local color = style.guide or style.selection
|
||||
renderer.draw_rect(x + sw * i, y, w, h, color)
|
||||
end
|
||||
draw_line_text(self, idx, x, y)
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
config.lfautoinsert_map = {
|
||||
["{%s*\n"] = "}",
|
||||
["%(%s*\n"] = ")",
|
||||
["%f[[]%[%s*\n"] = "]",
|
||||
["%[%[%s*\n"] = "]]",
|
||||
["=%s*\n"] = false,
|
||||
[":%s*\n"] = false,
|
||||
["^#if.*\n"] = "#endif",
|
||||
["^#else.*\n"] = "#endif",
|
||||
["%f[%w]do%s*\n"] = "end",
|
||||
["%f[%w]then%s*\n"] = "end",
|
||||
["%f[%w]else%s*\n"] = "end",
|
||||
["%f[%w]repeat%s*\n"] = "until",
|
||||
["%f[%w]function.*%)%s*\n"] = "end",
|
||||
["^%s*<([^/][^%s>]*)[^>]*>%s*\n"] = "</$TEXT>",
|
||||
}
|
||||
|
||||
|
||||
local function indent_size(doc, line)
|
||||
local text = doc.lines[line] or ""
|
||||
local s, e = text:find("^[\t ]*")
|
||||
return e - s
|
||||
end
|
||||
|
||||
command.add("core.docview", {
|
||||
["autoinsert:newline"] = function()
|
||||
command.perform("doc:newline")
|
||||
|
||||
local doc = core.active_view.doc
|
||||
local line, col = doc:get_selection()
|
||||
local text = doc.lines[line - 1]
|
||||
|
||||
for ptn, close in pairs(config.lfautoinsert_map) do
|
||||
local s, _, str = text:find(ptn)
|
||||
if s then
|
||||
if close
|
||||
and col == #doc.lines[line]
|
||||
and indent_size(doc, line + 1) <= indent_size(doc, line - 1)
|
||||
then
|
||||
close = str and close:gsub("$TEXT", str) or close
|
||||
command.perform("doc:newline")
|
||||
core.active_view:on_text_input(close)
|
||||
command.perform("doc:move-to-previous-line")
|
||||
if doc.lines[line+1] == doc.lines[line+2] then
|
||||
doc:remove(line+1, 1, line+2, 1)
|
||||
end
|
||||
elseif col < #doc.lines[line] then
|
||||
command.perform("doc:newline")
|
||||
command.perform("doc:move-to-previous-line")
|
||||
end
|
||||
command.perform("doc:indent")
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["return"] = { "command:submit", "autoinsert:newline" }
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
local draw = DocView.draw
|
||||
|
||||
function DocView:draw(...)
|
||||
draw(self, ...)
|
||||
|
||||
local offset = self:get_font():get_width("n") * config.line_limit
|
||||
local x = self:get_line_screen_position(1) + offset
|
||||
local y = self.position.y
|
||||
local w = math.ceil(SCALE * 1)
|
||||
local h = self.size.y
|
||||
|
||||
local color = style.guide or style.selection
|
||||
renderer.draw_rect(x, y, w, h, color)
|
||||
end
|
|
@ -1,70 +0,0 @@
|
|||
-- mod-version:3
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
local handled_events = {
|
||||
["keypressed"] = true,
|
||||
["keyreleased"] = true,
|
||||
["textinput"] = true,
|
||||
}
|
||||
|
||||
local state = "stopped"
|
||||
local event_buffer = {}
|
||||
local modkeys = {}
|
||||
|
||||
local on_event = core.on_event
|
||||
|
||||
core.on_event = function(type, ...)
|
||||
local res = on_event(type, ...)
|
||||
if state == "recording" and handled_events[type] then
|
||||
table.insert(event_buffer, { type, ... })
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function clone(t)
|
||||
local res = {}
|
||||
for k, v in pairs(t) do res[k] = v end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function predicate()
|
||||
return state ~= "playing"
|
||||
end
|
||||
|
||||
|
||||
command.add(predicate, {
|
||||
["macro:toggle-record"] = function()
|
||||
if state == "stopped" then
|
||||
state = "recording"
|
||||
event_buffer = {}
|
||||
modkeys = clone(keymap.modkeys)
|
||||
core.log("Recording macro...")
|
||||
else
|
||||
state = "stopped"
|
||||
core.log("Stopped recording macro (%d events)", #event_buffer)
|
||||
end
|
||||
end,
|
||||
|
||||
["macro:play"] = function()
|
||||
state = "playing"
|
||||
core.log("Playing macro... (%d events)", #event_buffer)
|
||||
local mk = keymap.modkeys
|
||||
keymap.modkeys = clone(modkeys)
|
||||
for _, ev in ipairs(event_buffer) do
|
||||
on_event(table.unpack(ev))
|
||||
core.root_view:update()
|
||||
end
|
||||
keymap.modkeys = mk
|
||||
state = "stopped"
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["ctrl+shift+;"] = "macro:toggle-record",
|
||||
["ctrl+;"] = "macro:play",
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
-- Markers plugin for lite text editor
|
||||
-- original implementation by Petri Häkkinen
|
||||
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
local cache = {} -- this table contains subtables for each document, each subtable is a set of line numbers
|
||||
setmetatable(cache, {
|
||||
__mode = "k",
|
||||
__index = function(t, k)
|
||||
t[k] = {}
|
||||
return t[k]
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local function shift_lines(doc, at, diff)
|
||||
if diff == 0 then return end
|
||||
local t = {}
|
||||
for line in pairs(cache[doc]) do
|
||||
line = line >= at and line + diff or line
|
||||
t[line] = true
|
||||
end
|
||||
cache[doc] = t
|
||||
end
|
||||
|
||||
|
||||
local raw_insert = Doc.raw_insert
|
||||
|
||||
function Doc:raw_insert(line, col, text, ...)
|
||||
raw_insert(self, line, col, text, ...)
|
||||
local line_count = 0
|
||||
for _ in text:gmatch("\n") do
|
||||
line_count = line_count + 1
|
||||
end
|
||||
shift_lines(self, line, line_count)
|
||||
end
|
||||
|
||||
|
||||
local raw_remove = Doc.raw_remove
|
||||
|
||||
function Doc:raw_remove(line1, col1, line2, col2, ...)
|
||||
raw_remove(self, line1, col1, line2, col2, ...)
|
||||
shift_lines(self, line2, line1 - line2)
|
||||
end
|
||||
|
||||
|
||||
local draw_line_gutter = DocView.draw_line_gutter
|
||||
|
||||
function DocView:draw_line_gutter(idx, x, y)
|
||||
if cache[self.doc] and cache[self.doc][idx] then
|
||||
local h = self:get_line_height()
|
||||
renderer.draw_rect(x, y, style.padding.x * 0.4, h, style.selection)
|
||||
end
|
||||
draw_line_gutter(self, idx, x, y)
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["markers:toggle-marker"] = function()
|
||||
local doc = core.active_view.doc
|
||||
local line = doc:get_selection()
|
||||
local markers = cache[doc]
|
||||
|
||||
if markers[line] then
|
||||
markers[line] = nil
|
||||
else
|
||||
markers[line] = true
|
||||
end
|
||||
end,
|
||||
|
||||
["markers:go-to-next-marker"] = function()
|
||||
local doc = core.active_view.doc
|
||||
local line = doc:get_selection()
|
||||
local markers = cache[doc]
|
||||
|
||||
local first_marker = math.huge
|
||||
local next_marker = math.huge
|
||||
for l, _ in pairs(markers) do
|
||||
if l > line and l < next_marker then
|
||||
next_marker = l
|
||||
end
|
||||
first_marker = math.min(first_marker, l)
|
||||
end
|
||||
if next_marker == math.huge then
|
||||
next_marker = first_marker
|
||||
end
|
||||
if next_marker ~= math.huge then
|
||||
doc:set_selection(next_marker, 1)
|
||||
core.active_view:scroll_to_line(next_marker, true)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["ctrl+f2"] = "markers:toggle-marker",
|
||||
["f2"] = "markers:go-to-next-marker",
|
||||
}
|
|
@ -1,295 +0,0 @@
|
|||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
-- General plugin settings
|
||||
config.minimap_enabled = true
|
||||
config.minimap_width = 100
|
||||
config.minimap_instant_scroll = false
|
||||
config.minimap_syntax_highlight = true
|
||||
config.minimap_scale = 1
|
||||
config.minimap_draw_background = true
|
||||
|
||||
-- Configure size for rendering each char in the minimap
|
||||
local char_height = 1 * SCALE * config.minimap_scale
|
||||
local char_spacing = 0.8 * SCALE * config.minimap_scale
|
||||
local line_spacing = 2 * SCALE * config.minimap_scale
|
||||
|
||||
-- Overloaded since the default implementation adds a extra x3 size of hotspot for the mouse to hit the scrollbar.
|
||||
local prev_scrollbar_overlaps_point = DocView.scrollbar_overlaps_point
|
||||
DocView.scrollbar_overlaps_point = function(self, x, y)
|
||||
if not config.minimap_enabled then return prev_scrollbar_overlaps_point(self, x, y) end
|
||||
|
||||
local sx, sy, sw, sh = self:get_scrollbar_rect()
|
||||
return x >= sx and x < sx + sw and y >= sy and y < sy + sh
|
||||
end
|
||||
|
||||
-- Helper function to determine if current file is too large to be shown fully inside the minimap area.
|
||||
local function is_file_too_large(self)
|
||||
local line_count = #self.doc.lines
|
||||
local _, _, _, sh = self:get_scrollbar_rect()
|
||||
|
||||
-- check if line count is too large to fit inside the minimap area
|
||||
local max_minmap_lines = math.floor(sh / line_spacing)
|
||||
return line_count > 1 and line_count > max_minmap_lines
|
||||
end
|
||||
|
||||
-- Overloaded with an extra check if the user clicked inside the minimap to automatically scroll to that line.
|
||||
local prev_on_mouse_pressed = DocView.on_mouse_pressed
|
||||
DocView.on_mouse_pressed = function(self, button, x, y, clicks)
|
||||
if not config.minimap_enabled then return prev_on_mouse_pressed(self, button, x, y, clicks) end
|
||||
|
||||
-- check if user clicked in the minimap area and jump directly to that line
|
||||
-- unless they are actually trying to perform a drag
|
||||
local minimap_hit = self:scrollbar_overlaps_point(x, y)
|
||||
if minimap_hit then
|
||||
local line_count = #self.doc.lines
|
||||
local minimap_height = line_count * line_spacing
|
||||
|
||||
-- check if line count is too large to fit inside the minimap area
|
||||
local is_too_large = is_file_too_large(self)
|
||||
if is_too_large then
|
||||
local _, _, _, sh = self:get_scrollbar_rect()
|
||||
minimap_height = sh
|
||||
end
|
||||
|
||||
-- calc which line to jump to
|
||||
local dy = y - self.position.y
|
||||
local jump_to_line = math.floor((dy / minimap_height) * line_count) + 1
|
||||
|
||||
local _, cy, _, cy2 = self:get_content_bounds()
|
||||
local lh = self:get_line_height()
|
||||
local visible_lines_count = math.max(1, (cy2 - cy) / lh)
|
||||
local visible_lines_start = math.max(1, math.floor(cy / lh))
|
||||
|
||||
-- calc if user hit the currently visible area
|
||||
local hit_visible_area = true
|
||||
if is_too_large then
|
||||
|
||||
local visible_height = visible_lines_count * line_spacing
|
||||
local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1)
|
||||
scroll_pos = math.min(1.0, scroll_pos) -- 0..1
|
||||
local visible_y = self.position.y + scroll_pos * (minimap_height - visible_height)
|
||||
|
||||
local t = (line_count - visible_lines_start) / visible_lines_count
|
||||
if t <= 1 then
|
||||
visible_y = visible_y + visible_height * (1.0 - t)
|
||||
end
|
||||
|
||||
if y < visible_y or y > visible_y + visible_height then
|
||||
hit_visible_area = false
|
||||
end
|
||||
else
|
||||
|
||||
-- If the click is on the currently visible line numbers,
|
||||
-- ignore it since then they probably want to initiate a drag instead.
|
||||
if jump_to_line < visible_lines_start or jump_to_line > visible_lines_start + visible_lines_count then
|
||||
hit_visible_area = false
|
||||
end
|
||||
end
|
||||
|
||||
-- if user didn't click on the visible area (ie not dragging), scroll accordingly
|
||||
if not hit_visible_area then
|
||||
self:scroll_to_line(jump_to_line, false, config.minimap_instant_scroll)
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return prev_on_mouse_pressed(self, button, x, y, clicks)
|
||||
end
|
||||
|
||||
-- Overloaded with pretty much the same logic as original DocView implementation,
|
||||
-- with the exception of the dragging scrollbar delta. We want it to behave a bit snappier
|
||||
-- since the "scrollbar" essentially represents the lines visible in the content view.
|
||||
local prev_on_mouse_moved = DocView.on_mouse_moved
|
||||
DocView.on_mouse_moved = function(self, x, y, dx, dy)
|
||||
if not config.minimap_enabled then return prev_on_mouse_moved(self, x, y, dx, dy) end
|
||||
|
||||
if self.dragging_scrollbar then
|
||||
local line_count = #self.doc.lines
|
||||
local lh = self:get_line_height()
|
||||
local delta = lh / line_spacing * dy
|
||||
|
||||
if is_file_too_large(self) then
|
||||
local _, sy, _, sh = self:get_scrollbar_rect()
|
||||
delta = (line_count * lh) / sh * dy
|
||||
end
|
||||
|
||||
self.scroll.to.y = self.scroll.to.y + delta
|
||||
end
|
||||
|
||||
-- we need to "hide" that the scrollbar is dragging so that View doesnt does its own scrolling logic
|
||||
local t = self.dragging_scrollbar
|
||||
self.dragging_scrollbar = false
|
||||
local r = prev_on_mouse_moved(self, x, y, dx, dy)
|
||||
self.dragging_scrollbar = t
|
||||
return r
|
||||
end
|
||||
|
||||
-- Overloaded since we want the mouse to interact with the full size of the minimap area,
|
||||
-- not juse the scrollbar.
|
||||
local prev_get_scrollbar_rect = DocView.get_scrollbar_rect
|
||||
DocView.get_scrollbar_rect = function (self)
|
||||
if not config.minimap_enabled then return prev_get_scrollbar_rect(self) end
|
||||
|
||||
return
|
||||
self.position.x + self.size.x - config.minimap_width * SCALE,
|
||||
self.position.y,
|
||||
config.minimap_width * SCALE,
|
||||
self.size.y
|
||||
end
|
||||
|
||||
-- Overloaded so we can render the minimap in the "scrollbar area".
|
||||
local prev_draw_scrollbar = DocView.draw_scrollbar
|
||||
DocView.draw_scrollbar = function (self)
|
||||
if not config.minimap_enabled then return prev_draw_scrollbar(self) end
|
||||
|
||||
local x, y, w, h = self:get_scrollbar_rect()
|
||||
|
||||
local highlight = self.hovered_scrollbar or self.dragging_scrollbar
|
||||
local visual_color = highlight and style.scrollbar2 or style.scrollbar
|
||||
|
||||
local _, cy, _, cy2 = self:get_content_bounds()
|
||||
local lh = self:get_line_height()
|
||||
local visible_lines_count = math.max(1, (cy2 - cy) / lh)
|
||||
local visible_lines_start = math.max(1, math.floor(cy / lh))
|
||||
local scroller_height = visible_lines_count * line_spacing
|
||||
local line_count = #self.doc.lines
|
||||
|
||||
local visible_y = self.position.y + (visible_lines_start-1) * line_spacing
|
||||
|
||||
|
||||
-- check if file is too large to fit inside the minimap area
|
||||
local max_minmap_lines = math.floor(h / line_spacing)
|
||||
local minimap_start_line = 1
|
||||
if is_file_too_large(self) then
|
||||
|
||||
local scroll_pos = (visible_lines_start-1) / (line_count - visible_lines_count - 1)
|
||||
scroll_pos = math.min(1.0, scroll_pos) -- 0..1, procent of visual area scrolled
|
||||
|
||||
local scroll_pos_pixels = scroll_pos * (h - scroller_height)
|
||||
visible_y = self.position.y + scroll_pos_pixels
|
||||
|
||||
-- offset visible area if user is scrolling past end
|
||||
local t = (line_count - visible_lines_start) / visible_lines_count
|
||||
if t <= 1 then
|
||||
visible_y = visible_y + scroller_height * (1.0 - t)
|
||||
end
|
||||
|
||||
minimap_start_line = visible_lines_start - math.floor(scroll_pos_pixels / line_spacing)
|
||||
minimap_start_line = math.max(1, math.min(minimap_start_line, line_count - max_minmap_lines))
|
||||
end
|
||||
|
||||
if config.minimap_draw_background then
|
||||
renderer.draw_rect(x, y, w, h, style.minimap_background or style.background)
|
||||
end
|
||||
-- draw visual rect
|
||||
renderer.draw_rect(x, visible_y, w, scroller_height, visual_color)
|
||||
|
||||
-- time to draw the actual code, setup some local vars that are used in both highlighted and plain renderind.
|
||||
local line_y = y
|
||||
|
||||
-- when not using syntax highlighted rendering, just use the normal color but dim it 50%.
|
||||
local color = style.syntax["normal"]
|
||||
color = { color[1],color[2],color[3],color[4] * 0.5 }
|
||||
|
||||
-- we try to "batch" characters so that they can be rendered as just one rectangle instead of one for each.
|
||||
local batch_width = 0
|
||||
local batch_start = x
|
||||
local minimap_cutoff_x = x + config.minimap_width * SCALE
|
||||
|
||||
-- render lines with syntax highlighting
|
||||
if config.minimap_syntax_highlight then
|
||||
|
||||
-- keep track of the highlight type, since this needs to break batches as well
|
||||
local batch_syntax_type = nil
|
||||
|
||||
local function flush_batch(type)
|
||||
if batch_width > 0 then
|
||||
-- fetch and dim colors
|
||||
color = style.syntax[batch_syntax_type]
|
||||
color = { color[1], color[2], color[3], color[4] * 0.5 }
|
||||
renderer.draw_rect(batch_start, line_y, batch_width, char_height, color)
|
||||
end
|
||||
batch_syntax_type = type
|
||||
batch_start = batch_start + batch_width
|
||||
batch_width = 0
|
||||
end
|
||||
|
||||
-- per line
|
||||
local endidx = minimap_start_line + max_minmap_lines
|
||||
endidx = math.min(endidx, line_count)
|
||||
for idx=minimap_start_line,endidx do
|
||||
batch_syntax_type = nil
|
||||
batch_start = x
|
||||
batch_width = 0
|
||||
|
||||
-- per token
|
||||
for _, type, text in self.doc.highlighter:each_token(idx) do
|
||||
-- flush prev batch
|
||||
if not batch_syntax_type then batch_syntax_type = type end
|
||||
if batch_syntax_type ~= type then
|
||||
flush_batch(type)
|
||||
end
|
||||
|
||||
-- per character
|
||||
for char in common.utf8_chars(text) do
|
||||
if char == " " or char == "\n" then
|
||||
flush_batch(type)
|
||||
batch_start = batch_start + char_spacing
|
||||
elseif batch_start + batch_width > minimap_cutoff_x then
|
||||
flush_batch(type)
|
||||
break
|
||||
else
|
||||
batch_width = batch_width + char_spacing
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
flush_batch(nil)
|
||||
line_y = line_y + line_spacing
|
||||
end
|
||||
|
||||
else -- render lines without syntax highlighting
|
||||
|
||||
local function flush_batch()
|
||||
if batch_width > 0 then
|
||||
renderer.draw_rect(batch_start, line_y, batch_width, char_height, color)
|
||||
end
|
||||
batch_start = batch_start + batch_width
|
||||
batch_width = 0
|
||||
end
|
||||
|
||||
for idx=1,line_count-1 do
|
||||
batch_start = x
|
||||
batch_width = 0
|
||||
|
||||
for char in common.utf8_chars(self.doc.lines[idx]) do
|
||||
if char == " " or char == "\n" then
|
||||
flush_batch()
|
||||
batch_start = batch_start + char_spacing
|
||||
elseif batch_start + batch_width > minimap_cutoff_x then
|
||||
flush_batch()
|
||||
else
|
||||
batch_width = batch_width + char_spacing
|
||||
end
|
||||
end
|
||||
flush_batch()
|
||||
line_y = line_y + line_spacing
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
command.add(nil, {
|
||||
["minimap:toggle-visibility"] = function()
|
||||
config.minimap_enabled = not config.minimap_enabled
|
||||
end,
|
||||
["minimap:toggle-syntax-highlighting"] = function()
|
||||
config.minimap_syntax_highlight = not config.minimap_syntax_highlight
|
||||
end,
|
||||
})
|
|
@ -1,56 +0,0 @@
|
|||
local core = require "core"
|
||||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local Doc = require "core.doc"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.motiontrail_steps = config.motiontrail_steps or 50
|
||||
|
||||
local function doc()
|
||||
return core.active_view.doc
|
||||
end
|
||||
|
||||
local function lerp(a, b, t)
|
||||
return a + (b - a) * t
|
||||
end
|
||||
|
||||
|
||||
local function get_caret_rect(dv, idx)
|
||||
local line1, col1, line2, col2 = doc():get_selection_idx(idx)
|
||||
local x1, y1 = dv:get_line_screen_position(line1)
|
||||
x1 = x1 + dv:get_col_x_offset(line1, col1)
|
||||
return x1, y1, style.caret_width, dv:get_line_height()
|
||||
end
|
||||
|
||||
|
||||
local last_x = {}
|
||||
local last_y = {}
|
||||
local last_view = {}
|
||||
|
||||
local draw = DocView.draw
|
||||
|
||||
function DocView:draw(...)
|
||||
draw(self, ...)
|
||||
if self ~= core.active_view then return end
|
||||
|
||||
for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do
|
||||
--if line1 == line2 and col1 == col2 then return false end
|
||||
|
||||
local x, y, w, h = get_caret_rect(self, idx)
|
||||
|
||||
if last_view[idx] == self and (x ~= last_x[idx] or y ~= last_y[idx]) then
|
||||
local lx = x
|
||||
for i = 0, 1, 1 / config.motiontrail_steps do
|
||||
local ix = lerp(x, last_x[idx], i)
|
||||
local iy = lerp(y, last_y[idx], i)
|
||||
local iw = math.max(w, math.ceil(math.abs(ix - lx)))
|
||||
renderer.draw_rect(ix, iy, iw, h, style.caret)
|
||||
lx = ix
|
||||
end
|
||||
core.redraw = true
|
||||
end
|
||||
|
||||
last_view[idx], last_x[idx], last_y[idx] = self, x, y
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
|
||||
|
||||
if PLATFORM == "Windows" then
|
||||
config.filemanager = "explorer"
|
||||
elseif PLATFORM == "Mac OS X" then
|
||||
config.filemanager = "open"
|
||||
else
|
||||
config.filemanager = "xdg-open"
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["open-file-location:open-file-location"] = function()
|
||||
local doc = core.active_view.doc
|
||||
if not doc.filename then
|
||||
core.error "Cannot open location of unsaved doc"
|
||||
return
|
||||
end
|
||||
local folder = doc.filename:match("^(.*)[/\\].*$") or "."
|
||||
core.log("Opening \"%s\"", folder)
|
||||
if PLATFORM == "Windows" then
|
||||
system.exec(string.format("%s %s", config.filemanager, folder))
|
||||
else
|
||||
system.exec(string.format("%s %q", config.filemanager, folder))
|
||||
end
|
||||
end
|
||||
})
|
|
@ -1,399 +0,0 @@
|
|||
-- mod-version:3
|
||||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local keymap = require "core.keymap"
|
||||
local command = require "core.command"
|
||||
local style = require "core.style"
|
||||
local View = require "core.view"
|
||||
|
||||
---@class plugins.projectsearch.resultsview : core.view
|
||||
local ResultsView = View:extend()
|
||||
|
||||
ResultsView.context = "session"
|
||||
|
||||
function ResultsView:new(path, text, fn)
|
||||
ResultsView.super.new(self)
|
||||
self.scrollable = true
|
||||
self.brightness = 0
|
||||
self:begin_search(path, text, fn)
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:get_name()
|
||||
return "Search Results"
|
||||
end
|
||||
|
||||
|
||||
local function find_all_matches_in_file(t, filename, fn)
|
||||
local fp = io.open(filename)
|
||||
if not fp then return t end
|
||||
local n = 1
|
||||
for line in fp:lines() do
|
||||
local s = fn(line)
|
||||
if s then
|
||||
-- Insert maximum 256 characters. If we insert more, for compiled files, which can have very long lines
|
||||
-- things tend to get sluggish. If our line is longer than 80 characters, begin to truncate the thing.
|
||||
local start_index = math.max(s - 80, 1)
|
||||
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
||||
core.redraw = true
|
||||
end
|
||||
if n % 100 == 0 then coroutine.yield(0) end
|
||||
n = n + 1
|
||||
core.redraw = true
|
||||
end
|
||||
fp:close()
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:begin_search(path, text, fn)
|
||||
self.search_args = { path, text, fn }
|
||||
self.results = {}
|
||||
self.last_file_idx = 1
|
||||
self.query = text
|
||||
self.searching = true
|
||||
self.selected_idx = 0
|
||||
|
||||
core.add_thread(function()
|
||||
local i = 1
|
||||
for dir_name, file in core.get_project_files() do
|
||||
if file.type == "file" and (not path or (dir_name .. "/" .. file.filename):find(path, 1, true) == 1) then
|
||||
local truncated_path = (dir_name == core.project_dir and "" or (dir_name .. PATHSEP))
|
||||
find_all_matches_in_file(self.results, truncated_path .. file.filename, fn)
|
||||
end
|
||||
self.last_file_idx = i
|
||||
i = i + 1
|
||||
end
|
||||
self.searching = false
|
||||
self.brightness = 100
|
||||
core.redraw = true
|
||||
end, self.results)
|
||||
|
||||
self.scroll.to.y = 0
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:refresh()
|
||||
self:begin_search(table.unpack(self.search_args))
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:on_mouse_moved(mx, my, ...)
|
||||
ResultsView.super.on_mouse_moved(self, mx, my, ...)
|
||||
self.selected_idx = 0
|
||||
for i, item, x,y,w,h in self:each_visible_result() do
|
||||
if mx >= x and my >= y and mx < x + w and my < y + h then
|
||||
self.selected_idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:on_mouse_pressed(...)
|
||||
local caught = ResultsView.super.on_mouse_pressed(self, ...)
|
||||
if not caught then
|
||||
return self:open_selected_result()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:open_selected_result()
|
||||
local res = self.results[self.selected_idx]
|
||||
if not res then
|
||||
return
|
||||
end
|
||||
core.try(function()
|
||||
local dv = core.root_view:open_doc(core.open_doc(res.file))
|
||||
core.root_view.root_node:update_layout()
|
||||
dv.doc:set_selection(res.line, res.col)
|
||||
dv:scroll_to_line(res.line, false, true)
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:update()
|
||||
self:move_towards("brightness", 0, 0.1)
|
||||
ResultsView.super.update(self)
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:get_results_yoffset()
|
||||
return style.font:get_height() + style.padding.y * 3
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:get_line_height()
|
||||
return style.padding.y + style.font:get_height()
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:get_scrollable_size()
|
||||
return self:get_results_yoffset() + #self.results * self:get_line_height()
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:get_visible_results_range()
|
||||
local lh = self:get_line_height()
|
||||
local oy = self:get_results_yoffset()
|
||||
local min = math.max(1, math.floor((self.scroll.y - oy) / lh))
|
||||
return min, min + math.floor(self.size.y / lh) + 1
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:each_visible_result()
|
||||
return coroutine.wrap(function()
|
||||
local lh = self:get_line_height()
|
||||
local x, y = self:get_content_offset()
|
||||
local min, max = self:get_visible_results_range()
|
||||
y = y + self:get_results_yoffset() + lh * (min - 1)
|
||||
for i = min, max do
|
||||
local item = self.results[i]
|
||||
if not item then break end
|
||||
coroutine.yield(i, item, x, y, self.size.x, lh)
|
||||
y = y + lh
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:scroll_to_make_selected_visible()
|
||||
local h = self:get_line_height()
|
||||
local y = self:get_results_yoffset() + h * (self.selected_idx - 1)
|
||||
self.scroll.to.y = math.min(self.scroll.to.y, y)
|
||||
self.scroll.to.y = math.max(self.scroll.to.y, y + h - self.size.y)
|
||||
end
|
||||
|
||||
|
||||
function ResultsView:draw()
|
||||
self:draw_background(style.background)
|
||||
|
||||
-- status
|
||||
local ox, oy = self:get_content_offset()
|
||||
local x, y = ox + style.padding.x, oy + style.padding.y
|
||||
local files_number = core.project_files_number()
|
||||
local per = common.clamp(files_number and self.last_file_idx / files_number or 1, 0, 1)
|
||||
local text
|
||||
if self.searching then
|
||||
if files_number then
|
||||
text = string.format("Searching %.f%% (%d of %d files, %d matches) for %q...",
|
||||
per * 100, self.last_file_idx, files_number,
|
||||
#self.results, self.query)
|
||||
else
|
||||
text = string.format("Searching (%d files, %d matches) for %q...",
|
||||
self.last_file_idx, #self.results, self.query)
|
||||
end
|
||||
else
|
||||
text = string.format("Found %d matches for %q",
|
||||
#self.results, self.query)
|
||||
end
|
||||
local color = common.lerp(style.text, style.accent, self.brightness / 100)
|
||||
renderer.draw_text(style.font, text, x, y, color)
|
||||
|
||||
-- horizontal line
|
||||
local yoffset = self:get_results_yoffset()
|
||||
local x = ox + style.padding.x
|
||||
local w = self.size.x - style.padding.x * 2
|
||||
local h = style.divider_size
|
||||
local color = common.lerp(style.dim, style.text, self.brightness / 100)
|
||||
renderer.draw_rect(x, oy + yoffset - style.padding.y, w, h, color)
|
||||
if self.searching then
|
||||
renderer.draw_rect(x, oy + yoffset - style.padding.y, w * per, h, style.text)
|
||||
end
|
||||
|
||||
-- results
|
||||
local y1, y2 = self.position.y, self.position.y + self.size.y
|
||||
for i, item, x,y,w,h in self:each_visible_result() do
|
||||
local color = style.text
|
||||
if i == self.selected_idx then
|
||||
color = style.accent
|
||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
||||
end
|
||||
x = x + style.padding.x
|
||||
local text = string.format("%s at line %d (col %d): ", item.file, item.line, item.col)
|
||||
x = common.draw_text(style.font, style.dim, text, "left", x, y, w, h)
|
||||
x = common.draw_text(style.code_font, color, item.text, "left", x, y, w, h)
|
||||
end
|
||||
|
||||
self:draw_scrollbar()
|
||||
end
|
||||
|
||||
|
||||
---@param path string
|
||||
---@param text string
|
||||
---@param fn fun(line_text:string):...
|
||||
---@return plugins.projectsearch.resultsview?
|
||||
local function begin_search(path, text, fn)
|
||||
if text == "" then
|
||||
core.error("Expected non-empty string")
|
||||
return
|
||||
end
|
||||
local rv = ResultsView(path, text, fn)
|
||||
core.root_view:get_active_node_default():add_view(rv)
|
||||
return rv
|
||||
end
|
||||
|
||||
|
||||
local function get_selected_text()
|
||||
local view = core.active_view
|
||||
local doc = (view and view.doc) and view.doc or nil
|
||||
if doc then
|
||||
return doc:get_text(table.unpack({ doc:get_selection() }))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function normalize_path(path)
|
||||
if not path then return nil end
|
||||
path = common.normalize_path(path)
|
||||
for i, project_dir in ipairs(core.project_directories) do
|
||||
if common.path_belongs_to(path, project_dir.name) then
|
||||
return project_dir.item.filename .. PATHSEP .. common.relative_path(project_dir.name, path)
|
||||
end
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
---@class plugins.projectsearch
|
||||
local projectsearch = {}
|
||||
|
||||
---@type plugins.projectsearch.resultsview
|
||||
projectsearch.ResultsView = ResultsView
|
||||
|
||||
---@param text string
|
||||
---@param path string
|
||||
---@param insensitive? boolean
|
||||
---@return plugins.projectsearch.resultsview?
|
||||
function projectsearch.search_plain(text, path, insensitive)
|
||||
if insensitive then text = text:lower() end
|
||||
return begin_search(path, text, function(line_text)
|
||||
if insensitive then
|
||||
return line_text:lower():find(text, nil, true)
|
||||
else
|
||||
return line_text:find(text, nil, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@param text string
|
||||
---@param path string
|
||||
---@param insensitive? boolean
|
||||
---@return plugins.projectsearch.resultsview?
|
||||
function projectsearch.search_regex(text, path, insensitive)
|
||||
local re, errmsg
|
||||
if insensitive then
|
||||
re, errmsg = regex.compile(text, "i")
|
||||
else
|
||||
re, errmsg = regex.compile(text)
|
||||
end
|
||||
if not re then core.log("%s", errmsg) return end
|
||||
return begin_search(path, text, function(line_text)
|
||||
return regex.cmatch(re, line_text)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param text string
|
||||
---@param path string
|
||||
---@param insensitive? boolean
|
||||
---@return plugins.projectsearch.resultsview?
|
||||
function projectsearch.search_fuzzy(text, path, insensitive)
|
||||
if insensitive then text = text:lower() end
|
||||
return begin_search(path, text, function(line_text)
|
||||
if insensitive then
|
||||
return common.fuzzy_match(line_text:lower(), text) and 1
|
||||
else
|
||||
return common.fuzzy_match(line_text, text) and 1
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
command.add(nil, {
|
||||
["project-search:find"] = function(path)
|
||||
core.command_view:enter("Find Text In " .. (normalize_path(path) or "Project"), {
|
||||
text = get_selected_text(),
|
||||
select_text = true,
|
||||
submit = function(text)
|
||||
projectsearch.search_plain(text, path, true)
|
||||
end
|
||||
})
|
||||
end,
|
||||
|
||||
["project-search:find-regex"] = function(path)
|
||||
core.command_view:enter("Find Regex In " .. (normalize_path(path) or "Project"), {
|
||||
submit = function(text)
|
||||
projectsearch.search_regex(text, path, true)
|
||||
end
|
||||
})
|
||||
end,
|
||||
|
||||
["project-search:fuzzy-find"] = function(path)
|
||||
core.command_view:enter("Fuzzy Find Text In " .. (normalize_path(path) or "Project"), {
|
||||
text = get_selected_text(),
|
||||
select_text = true,
|
||||
submit = function(text)
|
||||
projectsearch.search_fuzzy(text, path, true)
|
||||
end
|
||||
})
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
command.add(ResultsView, {
|
||||
["project-search:select-previous"] = function()
|
||||
local view = core.active_view
|
||||
view.selected_idx = math.max(view.selected_idx - 1, 1)
|
||||
view:scroll_to_make_selected_visible()
|
||||
end,
|
||||
|
||||
["project-search:select-next"] = function()
|
||||
local view = core.active_view
|
||||
view.selected_idx = math.min(view.selected_idx + 1, #view.results)
|
||||
view:scroll_to_make_selected_visible()
|
||||
end,
|
||||
|
||||
["project-search:open-selected"] = function()
|
||||
core.active_view:open_selected_result()
|
||||
end,
|
||||
|
||||
["project-search:refresh"] = function()
|
||||
core.active_view:refresh()
|
||||
end,
|
||||
|
||||
["project-search:move-to-previous-page"] = function()
|
||||
local view = core.active_view
|
||||
view.scroll.to.y = view.scroll.to.y - view.size.y
|
||||
end,
|
||||
|
||||
["project-search:move-to-next-page"] = function()
|
||||
local view = core.active_view
|
||||
view.scroll.to.y = view.scroll.to.y + view.size.y
|
||||
end,
|
||||
|
||||
["project-search:move-to-start-of-doc"] = function()
|
||||
local view = core.active_view
|
||||
view.scroll.to.y = 0
|
||||
end,
|
||||
|
||||
["project-search:move-to-end-of-doc"] = function()
|
||||
local view = core.active_view
|
||||
view.scroll.to.y = view:get_scrollable_size()
|
||||
end
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["f5"] = "project-search:refresh",
|
||||
["ctrl+shift+f"] = "project-search:find",
|
||||
["up"] = "project-search:select-previous",
|
||||
["down"] = "project-search:select-next",
|
||||
["return"] = "project-search:open-selected",
|
||||
["pageup"] = "project-search:move-to-previous-page",
|
||||
["pagedown"] = "project-search:move-to-next-page",
|
||||
["ctrl+home"] = "project-search:move-to-start-of-doc",
|
||||
["ctrl+end"] = "project-search:move-to-end-of-doc",
|
||||
["home"] = "project-search:move-to-start-of-doc",
|
||||
["end"] = "project-search:move-to-end-of-doc"
|
||||
}
|
||||
|
||||
|
||||
return projectsearch
|
|
@ -1,31 +0,0 @@
|
|||
-- mod-version:3
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
|
||||
local escapes = {
|
||||
["\\"] = "\\\\",
|
||||
["\""] = "\\\"",
|
||||
["\n"] = "\\n",
|
||||
["\r"] = "\\r",
|
||||
["\t"] = "\\t",
|
||||
["\b"] = "\\b",
|
||||
}
|
||||
|
||||
local function replace(chr)
|
||||
return escapes[chr] or string.format("\\x%02x", chr:byte())
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["quote:quote"] = function(dv)
|
||||
dv.doc:replace(function(text)
|
||||
return '"' .. text:gsub("[\0-\31\\\"]", replace) .. '"'
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["ctrl+'"] = "quote:quote",
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
local tokenizer = require "core.tokenizer"
|
||||
local style = require "core.style"
|
||||
local common = require "core.common"
|
||||
|
||||
local tokenize = tokenizer.tokenize
|
||||
local closers = {
|
||||
["("] = ")",
|
||||
["["] = "]",
|
||||
["{"] = "}"
|
||||
}
|
||||
local function parenstyle(parenstack)
|
||||
return "paren" .. ((#parenstack % 5) + 1)
|
||||
end
|
||||
function tokenizer.tokenize(syntax, text, state)
|
||||
state = state or {}
|
||||
local res, istate = tokenize(syntax, text, state.istate)
|
||||
local parenstack = state.parenstack or ""
|
||||
local newres = {}
|
||||
-- split parens out
|
||||
-- the stock tokenizer can't do this because it merges identical adjacent tokens
|
||||
for i, type, text in tokenizer.each_token(res) do
|
||||
if type == "normal" or type == "symbol" then
|
||||
for normtext1, paren, normtext2 in text:gmatch("([^%(%[{}%]%)]*)([%(%[{}%]%)]?)([^%(%[{}%]%)]*)") do
|
||||
if #normtext1 > 0 then
|
||||
table.insert(newres, type)
|
||||
table.insert(newres, normtext1)
|
||||
end
|
||||
if #paren > 0 then
|
||||
if paren == parenstack:sub(-1) then -- expected closer
|
||||
parenstack = parenstack:sub(1, -2)
|
||||
table.insert(newres, parenstyle(parenstack))
|
||||
elseif closers[paren] then -- opener
|
||||
table.insert(newres, parenstyle(parenstack))
|
||||
parenstack = parenstack .. closers[paren]
|
||||
else -- unexpected closer
|
||||
table.insert(newres, "paren_unbalanced")
|
||||
end
|
||||
table.insert(newres, paren)
|
||||
end
|
||||
if #normtext2 > 0 then
|
||||
table.insert(newres, type)
|
||||
table.insert(newres, normtext2)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(newres, type)
|
||||
table.insert(newres, text)
|
||||
end
|
||||
end
|
||||
return newres, { parenstack = parenstack, istate = istate }
|
||||
end
|
||||
|
||||
style.syntax.paren_unbalanced = style.syntax.paren_unbalanced or { common.color "#DC0408" }
|
||||
style.syntax.paren1 = style.syntax.paren1 or { common.color "#FC6F71"}
|
||||
style.syntax.paren2 = style.syntax.paren2 or { common.color "#fcb053"}
|
||||
style.syntax.paren3 = style.syntax.paren3 or { common.color "#fcd476"}
|
||||
style.syntax.paren4 = style.syntax.paren4 or { common.color "#52dab2"}
|
||||
style.syntax.paren5 = style.syntax.paren5 or { common.color "#5a98cf"}
|
|
@ -1,63 +0,0 @@
|
|||
local core = require "core"
|
||||
local config = require "core.config"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
|
||||
local function wordwrap_text(text, limit)
|
||||
local t = {}
|
||||
local n = 0
|
||||
|
||||
for word in text:gmatch("%S+") do
|
||||
if n + #word > limit then
|
||||
table.insert(t, "\n")
|
||||
n = 0
|
||||
elseif #t > 0 then
|
||||
table.insert(t, " ")
|
||||
end
|
||||
table.insert(t, word)
|
||||
n = n + #word + 1
|
||||
end
|
||||
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["reflow:reflow"] = function()
|
||||
local doc = core.active_view.doc
|
||||
doc:replace(function(text)
|
||||
local prefix_set = "[^%w\n%[%](){}`'\"]*"
|
||||
|
||||
-- get line prefix and trailing whitespace
|
||||
local prefix1 = text:match("^\n*" .. prefix_set)
|
||||
local prefix2 = text:match("\n(" .. prefix_set .. ")", #prefix1+1)
|
||||
local trailing = text:match("%s*$")
|
||||
if not prefix2 or prefix2 == "" then
|
||||
prefix2 = prefix1
|
||||
end
|
||||
|
||||
-- strip all line prefixes and trailing whitespace
|
||||
text = text:sub(#prefix1+1, -#trailing - 1):gsub("\n" .. prefix_set, "\n")
|
||||
|
||||
-- split into blocks, wordwrap and join
|
||||
local line_limit = config.line_limit - #prefix1
|
||||
local blocks = {}
|
||||
text = text:gsub("\n\n", "\0")
|
||||
for block in text:gmatch("%Z+") do
|
||||
table.insert(blocks, wordwrap_text(block, line_limit))
|
||||
end
|
||||
text = table.concat(blocks, "\n\n")
|
||||
|
||||
-- add prefix to start of lines
|
||||
text = prefix1 .. text:gsub("\n", "\n" .. prefix2) .. trailing
|
||||
|
||||
return text
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["ctrl+shift+q"] = "reflow:reflow"
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local keymap = require "core.keymap"
|
||||
local style = require "core.style"
|
||||
local RootView = require "core.rootview"
|
||||
local CommandView = require "core.commandview"
|
||||
|
||||
config.scale_mode = "code"
|
||||
config.scale_use_mousewheel = true
|
||||
|
||||
local font_cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
-- the following should be kept in sync with core.style's default font settings
|
||||
font_cache[style.font] = { DATADIR .. "/data/fonts/font.ttf", 14 * SCALE }
|
||||
font_cache[style.big_font] = { DATADIR .. "/data/fonts/font.ttf", 34 * SCALE }
|
||||
font_cache[style.icon_font] = { DATADIR .. "/data/fonts/icons.ttf", 14 * SCALE }
|
||||
font_cache[style.code_font] = { DATADIR .. "/data/fonts/monospace.ttf", 13.5 * SCALE }
|
||||
|
||||
|
||||
local load_font = renderer.font.load
|
||||
function renderer.font.load(...)
|
||||
local res = load_font(...)
|
||||
font_cache[res] = { ... }
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function scale_font(font, s)
|
||||
local fc = font_cache[font]
|
||||
return renderer.font.load(fc[1], fc[2] * s)
|
||||
end
|
||||
|
||||
|
||||
local current_scale = SCALE
|
||||
local default = current_scale
|
||||
|
||||
|
||||
local function get_scale() return current_scale end
|
||||
|
||||
|
||||
local function set_scale(scale)
|
||||
scale = common.clamp(scale, 0.2, 6)
|
||||
|
||||
-- save scroll positions
|
||||
local scrolls = {}
|
||||
for _, view in ipairs(core.root_view.root_node:get_children()) do
|
||||
local n = view:get_scrollable_size()
|
||||
if n ~= math.huge and not view:is(CommandView) then
|
||||
scrolls[view] = view.scroll.y / (n - view.size.y)
|
||||
end
|
||||
end
|
||||
|
||||
local s = scale / current_scale
|
||||
current_scale = scale
|
||||
|
||||
if config.scale_mode == "ui" then
|
||||
SCALE = current_scale
|
||||
|
||||
style.padding.x = style.padding.x * s
|
||||
style.padding.y = style.padding.y * s
|
||||
style.divider_size = style.divider_size * s
|
||||
style.scrollbar_size = style.scrollbar_size * s
|
||||
style.caret_width = style.caret_width * s
|
||||
style.tab_width = style.tab_width * s
|
||||
|
||||
style.big_font = scale_font(style.big_font, s)
|
||||
style.icon_font = scale_font(style.icon_font, s)
|
||||
style.font = scale_font(style.font, s)
|
||||
end
|
||||
|
||||
style.code_font = scale_font(style.code_font, s)
|
||||
|
||||
-- restore scroll positions
|
||||
for view, n in pairs(scrolls) do
|
||||
view.scroll.y = n * (view:get_scrollable_size() - view.size.y)
|
||||
view.scroll.to.y = view.scroll.y
|
||||
end
|
||||
|
||||
core.redraw = true
|
||||
end
|
||||
|
||||
|
||||
local on_mouse_wheel = RootView.on_mouse_wheel
|
||||
|
||||
function RootView:on_mouse_wheel(d, ...)
|
||||
if keymap.modkeys["ctrl"] and config.scale_use_mousewheel then
|
||||
if d < 0 then command.perform "scale:decrease" end
|
||||
if d > 0 then command.perform "scale:increase" end
|
||||
else
|
||||
return on_mouse_wheel(self, d, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
command.add(nil, {
|
||||
["scale:reset" ] = function() set_scale(default) end,
|
||||
["scale:decrease"] = function() set_scale(current_scale * 0.9) end,
|
||||
["scale:increase"] = function() set_scale(current_scale * 1.1) end,
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["ctrl+0"] = "scale:reset",
|
||||
["ctrl+-"] = "scale:decrease",
|
||||
["ctrl++"] = "scale:increase",
|
||||
}
|
||||
|
||||
return { get_scale = get_scale, set_scale = set_scale }
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
--[[
|
||||
scalestatus.lua
|
||||
displays current scale (zoom) in status view
|
||||
version: 20200628_155804
|
||||
originally by SwissalpS
|
||||
|
||||
Depends on plugin scale.lua version >= 20200628_154010
|
||||
--]]
|
||||
local scale = require "plugins.scale"
|
||||
-- make sure plugin is installed and has get_scale field
|
||||
if not scale.get_scale then
|
||||
local core = require "core"
|
||||
core.error("Plugin 'scale' needs to be updated, scalestatus inactive.")
|
||||
return false
|
||||
end
|
||||
|
||||
local config = require "core.config"
|
||||
local StatusView = require "core.statusview"
|
||||
|
||||
config.scalestatus_format = '%.0f%%'
|
||||
|
||||
local get_items = StatusView.get_items
|
||||
function StatusView:get_items()
|
||||
|
||||
local left, right = get_items(self)
|
||||
|
||||
local t = {
|
||||
self.separator,
|
||||
string.format(config.scalestatus_format, scale.get_scale() * 100),
|
||||
}
|
||||
|
||||
for _, item in ipairs(t) do
|
||||
table.insert(right, item)
|
||||
end
|
||||
|
||||
return left, right
|
||||
|
||||
end
|
||||
|
||||
return true
|
|
@ -1,37 +0,0 @@
|
|||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
-- originally written by luveti
|
||||
|
||||
local function draw_box(x, y, w, h, color)
|
||||
local r = renderer.draw_rect
|
||||
local s = math.ceil(SCALE)
|
||||
r(x, y, w, s, color)
|
||||
r(x, y + h - s, w, s, color)
|
||||
r(x, y + s, s, h - s * 2, color)
|
||||
r(x + w - s, y + s, s, h - s * 2, color)
|
||||
end
|
||||
|
||||
|
||||
local draw_line_body = DocView.draw_line_body
|
||||
|
||||
function DocView:draw_line_body(idx, x, y)
|
||||
local line1, col1, line2, col2 = self.doc:get_selection(true)
|
||||
if line1 == line2 and col1 ~= col2 then
|
||||
local lh = self:get_line_height()
|
||||
local selected_text = self.doc.lines[line1]:sub(col1, col2 - 1)
|
||||
local current_line_text = self.doc.lines[idx]
|
||||
local last_col = 1
|
||||
while true do
|
||||
local start_col, end_col = current_line_text:find(selected_text, last_col, true)
|
||||
if start_col == nil then break end
|
||||
local x1 = x + self:get_col_x_offset(idx, start_col)
|
||||
local x2 = x + self:get_col_x_offset(idx, end_col + 1)
|
||||
local color = style.selectionhighlight or style.syntax.comment
|
||||
draw_box(x1, y, x2 - x1, lh, color)
|
||||
last_col = end_col + 1
|
||||
end
|
||||
end
|
||||
draw_line_body(self, idx, x, y)
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
-- mod-version:2 -- lite-xl 2.0
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local translate = require "core.doc.translate"
|
||||
|
||||
local function split_lines(text)
|
||||
local res = {}
|
||||
for line in (text .. "\n"):gmatch("(.-)\n") do
|
||||
table.insert(res, line)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
command.add("core.docview", {
|
||||
["sort:sort"] = function()
|
||||
local doc = core.active_view.doc
|
||||
|
||||
local l1, c1, l2, c2, swap = doc:get_selection(true)
|
||||
l1, c1 = translate.start_of_line(doc, l1, c1)
|
||||
l2, c2 = translate.end_of_line(doc, l2, c2)
|
||||
doc:set_selection(l1, c1, l2, c2, swap)
|
||||
|
||||
doc:replace(function(text)
|
||||
local head, body, foot = text:match("(\n*)(.-)(\n*)$")
|
||||
local lines = split_lines(body)
|
||||
table.sort(lines, function(a, b) return a:lower() < b:lower() end)
|
||||
return head .. table.concat(lines, "\n") .. foot
|
||||
end)
|
||||
end,
|
||||
})
|
|
@ -1,63 +0,0 @@
|
|||
-- mod-version:3
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local translate = require "core.doc.translate"
|
||||
|
||||
|
||||
local function gmatch_to_array(text, ptn)
|
||||
local res = {}
|
||||
for x in text:gmatch(ptn) do
|
||||
table.insert(res, x)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function tabularize_lines(lines, delim)
|
||||
local rows = {}
|
||||
local cols = {}
|
||||
|
||||
-- split lines at delimiters and get maximum width of columns
|
||||
local ptn = "[^" .. delim:sub(1,1):gsub("%W", "%%%1") .. "]+"
|
||||
for i, line in ipairs(lines) do
|
||||
rows[i] = gmatch_to_array(line, ptn)
|
||||
for j, col in ipairs(rows[i]) do
|
||||
cols[j] = math.max(#col, cols[j] or 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- pad columns with space
|
||||
for _, row in ipairs(rows) do
|
||||
for i = 1, #row - 1 do
|
||||
row[i] = row[i] .. string.rep(" ", cols[i] - #row[i])
|
||||
end
|
||||
end
|
||||
|
||||
-- write columns back to lines array
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = table.concat(rows[i], delim)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["tabularize:tabularize"] = function(dv)
|
||||
core.command_view:enter("Tabularize On Delimiter", {
|
||||
submit = function(delim)
|
||||
if delim == "" then delim = " " end
|
||||
|
||||
local doc = dv.doc
|
||||
local line1, col1, line2, col2, swap = doc:get_selection(true)
|
||||
line1, col1 = doc:position_offset(line1, col1, translate.start_of_line)
|
||||
line2, col2 = doc:position_offset(line2, col2, translate.end_of_line)
|
||||
doc:set_selection(line1, col1, line2, col2, swap)
|
||||
|
||||
doc:replace(function(text)
|
||||
local lines = gmatch_to_array(text, "[^\n]*\n?")
|
||||
tabularize_lines(lines, delim)
|
||||
return table.concat(lines)
|
||||
end)
|
||||
end
|
||||
})
|
||||
end,
|
||||
})
|
|
@ -1,389 +0,0 @@
|
|||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local keymap = require "core.keymap"
|
||||
local style = require "core.style"
|
||||
local View = require "core.view"
|
||||
|
||||
|
||||
local TodoTreeView = View:extend()
|
||||
|
||||
config.todo_tags = { --"TODO", "BUG", "FIX", "FIXME", "IMPROVEMENT",
|
||||
"@todo", "@fixme", "@testme", "@leak" } --< @r-lyeh
|
||||
|
||||
-- Paths or files to be ignored
|
||||
config.todo_ignore_paths = {
|
||||
"tools/tcc", --< @r-lyeh
|
||||
"tools\\tcc", --< @r-lyeh
|
||||
"engine/fwk", --< @r-lyeh
|
||||
"engine\\fwk", --< @r-lyeh
|
||||
"engine/joint", --< @r-lyeh
|
||||
"engine\\joint", --< @r-lyeh
|
||||
}
|
||||
|
||||
-- 'tag' mode can be used to group the todos by tags
|
||||
-- 'file' mode can be used to group the todos by files
|
||||
config.todo_mode = "tag"
|
||||
|
||||
-- Tells if the plugin should start with the nodes expanded. default: true for tag mode
|
||||
config.todo_expanded = config.todo_mode == "tag"
|
||||
|
||||
-- list of allowed extensions: items must start and end with a dot character
|
||||
config.todo_allowed_extensions = '.h.c.m.hh.cc.hpp.cpp.cxx.lua.py.cs.vs.fs.bat.' --< @r-lyeh
|
||||
|
||||
-- whether the sidebar treeview is initially visible or not
|
||||
config.todo_visible = false
|
||||
|
||||
|
||||
function TodoTreeView:new()
|
||||
TodoTreeView.super.new(self)
|
||||
self.scrollable = true
|
||||
self.focusable = false
|
||||
self.visible = config.todo_visible
|
||||
self.times_cache = {}
|
||||
self.cache = {}
|
||||
self.cache_updated = false
|
||||
self.init_size = true
|
||||
|
||||
-- Items are generated from cache according to the mode
|
||||
self.items = {}
|
||||
end
|
||||
|
||||
local function is_file_ignored(filename)
|
||||
for _, path in ipairs(config.todo_ignore_paths) do
|
||||
local s, _ = filename:find(path)
|
||||
if s then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function TodoTreeView:refresh_cache()
|
||||
local items = {}
|
||||
if not next(self.items) then
|
||||
items = self.items
|
||||
end
|
||||
self.updating_cache = true
|
||||
|
||||
core.add_thread(function()
|
||||
for _, item in ipairs(core.project_files) do
|
||||
local ignored = is_file_ignored(item.filename)
|
||||
if not ignored and item.type == "file" then
|
||||
local cached = self:get_cached(item)
|
||||
|
||||
if config.todo_mode == "file" then
|
||||
items[cached.filename] = cached
|
||||
else
|
||||
for _, todo in ipairs(cached.todos) do
|
||||
local tag = todo.tag
|
||||
if not items[tag] then
|
||||
local t = {}
|
||||
t.expanded = config.todo_expanded
|
||||
t.type = "group"
|
||||
t.todos = {}
|
||||
t.tag = tag
|
||||
items[tag] = t
|
||||
end
|
||||
|
||||
table.insert(items[tag].todos, todo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Copy expanded from old items
|
||||
if config.todo_mode == "tag" and next(self.items) then
|
||||
for tag, data in pairs(self.items) do
|
||||
if items[tag] then
|
||||
items[tag].expanded = data.expanded
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.items = items
|
||||
core.redraw = true
|
||||
self.cache_updated = true
|
||||
self.updating_cache = false
|
||||
end, self)
|
||||
end
|
||||
|
||||
|
||||
local function find_file_todos(t, filename)
|
||||
--< @r-lyeh
|
||||
local ext = (filename:match "[^.]+$") .. '.'
|
||||
if not string.find(config.todo_allowed_extensions,ext) then
|
||||
return
|
||||
end
|
||||
--<
|
||||
|
||||
local fp = io.open(filename)
|
||||
if not fp then return t end
|
||||
|
||||
--< @r-lyeh: optimized loops: early exit if quicksearch fails
|
||||
local function lines(str)
|
||||
local result = {}
|
||||
for line in string.gmatch(str, "(.-)%c") do -- line in str:gmatch '[^\n]+' do
|
||||
-- Add spaces at the start and end of line so the pattern will pick
|
||||
-- tags at the start and at the end of lines
|
||||
table.insert(result, " "..line.." ")
|
||||
end
|
||||
return result
|
||||
end
|
||||
local before = #t
|
||||
local content = fp:read("*all")
|
||||
for _, todo_tag in ipairs(config.todo_tags) do
|
||||
if string.find(content, todo_tag) then
|
||||
local n = 0
|
||||
for _, line in ipairs(lines(content)) do
|
||||
n = n + 1
|
||||
local match_str = todo_tag[1] == '@' and todo_tag or "[^a-zA-Z_\"'`]"..todo_tag.."[^a-zA-Z_\"'`]+"
|
||||
local s, e = line:find(match_str)
|
||||
if s then
|
||||
local d = {}
|
||||
d.tag = string.sub(string.upper(todo_tag), todo_tag:byte(1) == 64 and 2 or 1) .. 's'
|
||||
d.filename = filename
|
||||
d.text = line:sub(e+1)
|
||||
if d.text == "" then
|
||||
d.text = config.todo_mode == "tag" and filename:match("^.+[/\\](.+)$") or "blank"
|
||||
end
|
||||
d.line = n
|
||||
d.col = s
|
||||
table.insert(t, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
fp:close()
|
||||
if #t ~= before then
|
||||
coroutine.yield()
|
||||
core.redraw = true
|
||||
end
|
||||
--<
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:get_cached(item)
|
||||
local t = self.cache[item.filename]
|
||||
if not t then
|
||||
t = {}
|
||||
t.expanded = config.todo_expanded
|
||||
t.filename = item.filename
|
||||
t.abs_filename = system.absolute_path(item.filename)
|
||||
t.type = item.type
|
||||
t.todos = {}
|
||||
find_file_todos(t.todos, t.filename)
|
||||
self.cache[t.filename] = t
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:get_name()
|
||||
return "Todo Tree"
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:get_item_height()
|
||||
return style.font:get_height() + style.padding.y
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:get_cached_time(doc)
|
||||
local t = self.times_cache[doc]
|
||||
if not t then
|
||||
local info = system.get_file_info(doc.filename)
|
||||
if not info then return nil end
|
||||
self.times_cache[doc] = info.modified
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:check_cache()
|
||||
for _, doc in ipairs(core.docs) do
|
||||
if doc.filename then
|
||||
local info = system.get_file_info(doc.filename)
|
||||
local cached = self:get_cached_time(doc)
|
||||
if not info and cached then
|
||||
-- document deleted
|
||||
self.times_cache[doc] = nil
|
||||
self.cache[doc.filename] = nil
|
||||
self.cache_updated = false
|
||||
elseif cached and cached ~= info.modified then
|
||||
-- document modified
|
||||
self.times_cache[doc] = info.modified
|
||||
self.cache[doc.filename] = nil
|
||||
self.cache_updated = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if core.project_files ~= self.last_project_files then
|
||||
self.last_project_files = core.project_files
|
||||
self.cache_updated = false
|
||||
end
|
||||
end
|
||||
|
||||
function TodoTreeView:each_item()
|
||||
self:check_cache()
|
||||
if not self.updating_cache and not self.cache_updated then
|
||||
self:refresh_cache()
|
||||
end
|
||||
|
||||
return coroutine.wrap(function()
|
||||
local ox, oy = self:get_content_offset()
|
||||
local y = oy + style.padding.y
|
||||
local w = self.size.x
|
||||
local h = self:get_item_height()
|
||||
|
||||
for _, item in pairs(self.items) do
|
||||
if #item.todos > 0 then
|
||||
coroutine.yield(item, ox, y, w, h)
|
||||
y = y + h
|
||||
|
||||
for _, todo in ipairs(item.todos) do
|
||||
if item.expanded then
|
||||
coroutine.yield(todo, ox, y, w, h)
|
||||
y = y + h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:on_mouse_moved(px, py)
|
||||
self.hovered_item = nil
|
||||
for item, x,y,w,h in self:each_item() do
|
||||
if px > x and py > y and px <= x + w and py <= y + h then
|
||||
self.hovered_item = item
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:on_mouse_pressed(button, x, y)
|
||||
if not self.hovered_item then
|
||||
return
|
||||
elseif self.hovered_item.type == "file"
|
||||
or self.hovered_item.type == "group" then
|
||||
self.hovered_item.expanded = not self.hovered_item.expanded
|
||||
else
|
||||
core.try(function()
|
||||
local i = self.hovered_item
|
||||
local dv = core.root_view:open_doc(core.open_doc(i.filename))
|
||||
core.root_view.root_node:update_layout()
|
||||
dv.doc:set_selection(i.line, i.col)
|
||||
dv:scroll_to_line(i.line, false, true)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:update()
|
||||
self.scroll.to.y = math.max(0, self.scroll.to.y)
|
||||
|
||||
-- update width
|
||||
local dest = self.visible and config.treeview_size or 0
|
||||
if self.init_size then
|
||||
self.size.x = dest
|
||||
self.init_size = false
|
||||
else
|
||||
self:move_towards(self.size, "x", dest)
|
||||
end
|
||||
|
||||
TodoTreeView.super.update(self)
|
||||
end
|
||||
|
||||
|
||||
function TodoTreeView:draw()
|
||||
self:draw_background(style.background2)
|
||||
|
||||
--local h = self:get_item_height()
|
||||
local icon_width = style.icon_font:get_width("D")
|
||||
local spacing = style.font:get_width(" ") * 2
|
||||
local root_depth = 0
|
||||
|
||||
for item, x,y,w,h in self:each_item() do
|
||||
local color = style.text
|
||||
|
||||
-- hovered item background
|
||||
if item == self.hovered_item then
|
||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
||||
color = style.accent
|
||||
end
|
||||
|
||||
-- icons
|
||||
local item_depth = 0
|
||||
x = x + (item_depth - root_depth) * style.padding.x + style.padding.x
|
||||
if item.type == "file" then
|
||||
local icon1 = item.expanded and "-" or "+"
|
||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
||||
x = x + style.padding.x
|
||||
common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
|
||||
x = x + icon_width
|
||||
elseif item.type == "group" then
|
||||
local icon1 = item.expanded and "-" or "+"
|
||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
||||
x = x + icon_width / 2
|
||||
else
|
||||
if config.todo_mode == "tag" then
|
||||
x = x + style.padding.x
|
||||
else
|
||||
x = x + style.padding.x * 1.5
|
||||
end
|
||||
common.draw_text(style.icon_font, color, "i", nil, x, y, 0, h)
|
||||
x = x + icon_width
|
||||
end
|
||||
|
||||
-- text
|
||||
x = x + spacing
|
||||
if item.type == "file" then
|
||||
common.draw_text(style.font, color, item.filename, nil, x, y, 0, h)
|
||||
elseif item.type == "group" then
|
||||
common.draw_text(style.font, color, item.tag, nil, x, y, 0, h)
|
||||
else
|
||||
if config.todo_mode == "file" then
|
||||
common.draw_text(style.font, color, item.tag.." - "..item.text, nil, x, y, 0, h)
|
||||
else
|
||||
common.draw_text(style.font, color, item.text, nil, x, y, 0, h)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- init
|
||||
local view = TodoTreeView()
|
||||
local node = core.root_view:get_active_node()
|
||||
view.size.x = config.treeview_size
|
||||
node:split("right", view, true)
|
||||
|
||||
-- register commands and keymap
|
||||
command.add(nil, {
|
||||
["todotreeview:toggle"] = function()
|
||||
view.visible = not view.visible
|
||||
end,
|
||||
|
||||
["todotreeview:expand-items"] = function()
|
||||
for _, item in pairs(view.items) do
|
||||
item.expanded = true
|
||||
end
|
||||
end,
|
||||
|
||||
["todotreeview:hide-items"] = function()
|
||||
for _, item in pairs(view.items) do
|
||||
item.expanded = false
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
keymap.add { ["ctrl+shift+t"] = "todotreeview:toggle" }
|
||||
keymap.add { ["ctrl+shift+e"] = "todotreeview:expand-items" }
|
||||
keymap.add { ["ctrl+shift+h"] = "todotreeview:hide-items" }
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
local core = require "core"
|
||||
local common = require "core.common"
|
||||
local command = require "core.command"
|
||||
local config = require "core.config"
|
||||
local keymap = require "core.keymap"
|
||||
local style = require "core.style"
|
||||
local View = require "core.view"
|
||||
|
||||
config.treeview_size = 200 * SCALE
|
||||
|
||||
local function get_depth(filename)
|
||||
local n = 0
|
||||
for sep in filename:gmatch("[\\/]") do
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
|
||||
local TreeView = View:extend()
|
||||
|
||||
function TreeView:new()
|
||||
TreeView.super.new(self)
|
||||
self.scrollable = true
|
||||
self.visible = false --< @r-lyeh true>false
|
||||
self.init_size = true
|
||||
self.cache = {}
|
||||
end
|
||||
|
||||
|
||||
function TreeView:get_cached(item)
|
||||
local t = self.cache[item.filename]
|
||||
if not t then
|
||||
t = {}
|
||||
t.filename = item.filename
|
||||
t.abs_filename = system.absolute_path(item.filename)
|
||||
t.name = t.filename:match("[^\\/]+$")
|
||||
t.depth = get_depth(t.filename)
|
||||
t.type = item.type
|
||||
self.cache[t.filename] = t
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function TreeView:get_name()
|
||||
return "Project"
|
||||
end
|
||||
|
||||
|
||||
function TreeView:get_item_height()
|
||||
return style.font:get_height() + style.padding.y
|
||||
end
|
||||
|
||||
|
||||
function TreeView:check_cache()
|
||||
-- invalidate cache's skip values if project_files has changed
|
||||
if core.project_files ~= self.last_project_files then
|
||||
for _, v in pairs(self.cache) do
|
||||
v.skip = nil
|
||||
end
|
||||
self.last_project_files = core.project_files
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TreeView:each_item()
|
||||
return coroutine.wrap(function()
|
||||
self:check_cache()
|
||||
local ox, oy = self:get_content_offset()
|
||||
local y = oy + style.padding.y
|
||||
local w = self.size.x
|
||||
local h = self:get_item_height()
|
||||
|
||||
local i = 1
|
||||
while i <= #core.project_files do
|
||||
local item = core.project_files[i]
|
||||
local cached = self:get_cached(item)
|
||||
|
||||
coroutine.yield(cached, ox, y, w, h)
|
||||
y = y + h
|
||||
i = i + 1
|
||||
|
||||
if not cached.expanded then
|
||||
if cached.skip then
|
||||
i = cached.skip
|
||||
else
|
||||
local depth = cached.depth
|
||||
while i <= #core.project_files do
|
||||
local filename = core.project_files[i].filename
|
||||
if get_depth(filename) <= depth then break end
|
||||
i = i + 1
|
||||
end
|
||||
cached.skip = i
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function TreeView:on_mouse_moved(px, py)
|
||||
self.hovered_item = nil
|
||||
for item, x,y,w,h in self:each_item() do
|
||||
if px > x and py > y and px <= x + w and py <= y + h then
|
||||
self.hovered_item = item
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TreeView:on_mouse_pressed(button, x, y)
|
||||
if not self.hovered_item then
|
||||
return
|
||||
elseif self.hovered_item.type == "dir" then
|
||||
self.hovered_item.expanded = not self.hovered_item.expanded
|
||||
else
|
||||
core.try(function()
|
||||
core.root_view:open_doc(core.open_doc(self.hovered_item.filename))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TreeView:update()
|
||||
-- update width
|
||||
local dest = self.visible and config.treeview_size or 0
|
||||
if self.init_size then
|
||||
self.size.x = dest
|
||||
self.init_size = false
|
||||
else
|
||||
self:move_towards(self.size, "x", dest)
|
||||
end
|
||||
|
||||
TreeView.super.update(self)
|
||||
end
|
||||
|
||||
|
||||
function TreeView:draw()
|
||||
self:draw_background(style.background2)
|
||||
|
||||
local icon_width = style.icon_font:get_width("D")
|
||||
local spacing = style.font:get_width(" ") * 2
|
||||
|
||||
local doc = core.active_view.doc
|
||||
local active_filename = doc and system.absolute_path(doc.filename or "")
|
||||
|
||||
for item, x,y,w,h in self:each_item() do
|
||||
local color = style.text
|
||||
|
||||
-- highlight active_view doc
|
||||
if item.abs_filename == active_filename then
|
||||
color = style.accent
|
||||
end
|
||||
|
||||
-- hovered item background
|
||||
if item == self.hovered_item then
|
||||
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
||||
color = style.accent
|
||||
end
|
||||
|
||||
-- icons
|
||||
x = x + item.depth * style.padding.x + style.padding.x
|
||||
if item.type == "dir" then
|
||||
local icon1 = item.expanded and "-" or "+"
|
||||
local icon2 = item.expanded and "D" or "d"
|
||||
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
|
||||
x = x + style.padding.x
|
||||
common.draw_text(style.icon_font, color, icon2, nil, x, y, 0, h)
|
||||
x = x + icon_width
|
||||
else
|
||||
x = x + style.padding.x
|
||||
common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
|
||||
x = x + icon_width
|
||||
end
|
||||
|
||||
-- text
|
||||
x = x + spacing
|
||||
x = common.draw_text(style.font, color, item.name, nil, x, y, 0, h)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- init
|
||||
local view = TreeView()
|
||||
local node = core.root_view:get_active_node()
|
||||
node:split("left", view, true)
|
||||
|
||||
-- register commands and keymap
|
||||
command.add(nil, {
|
||||
["treeview:toggle"] = function()
|
||||
view.visible = not view.visible
|
||||
end,
|
||||
})
|
||||
|
||||
keymap.add { ["ctrl+t"] = "treeview:toggle" } --< @r-lyeh ctrl+// > ctrl+t
|
||||
|
||||
-- register some context menu items, if available
|
||||
local has_menu, menu = core.try(require, "plugins.contextmenu")
|
||||
local has_fsutils, fsutils = core.try(require, "plugins.fsutils")
|
||||
|
||||
if has_menu and has_fsutils then
|
||||
local function new_file_f(path)
|
||||
command.perform "core:new-doc"
|
||||
end
|
||||
|
||||
local function new_file()
|
||||
new_file_f(view.hovered_item.abs_filename)
|
||||
end
|
||||
|
||||
local function new_dir_f(path)
|
||||
core.command_view:enter("New directory name", function(dir)
|
||||
fsutils.mkdir(dir)
|
||||
end)
|
||||
core.command_view:set_text(path .. PATHSEP .. "New folder")
|
||||
end
|
||||
|
||||
local function new_dir()
|
||||
new_dir_f(view.hovered_item.abs_filename)
|
||||
end
|
||||
|
||||
local function delete_f(path)
|
||||
core.add_thread(function()
|
||||
local function wrap()
|
||||
return coroutine.wrap(function() fsutils.delete(path, true) end)
|
||||
end
|
||||
|
||||
for n in wrap() do
|
||||
if n % 100 == 0 then
|
||||
core.log("Deleted %d items.", n)
|
||||
coroutine.yield(0)
|
||||
end
|
||||
end
|
||||
|
||||
core.log("%q deleted.", path)
|
||||
end)
|
||||
end
|
||||
|
||||
local function delete()
|
||||
local path = view.hovered_item.abs_filename
|
||||
if view.hovered_item.type == "dir"
|
||||
and system.show_confirm_dialog("Delete confirmation", string.format("Do you really want to delete %q ?", path)) then
|
||||
delete_f(path)
|
||||
else
|
||||
delete_f(path)
|
||||
end
|
||||
end
|
||||
|
||||
local function dirname(path)
|
||||
local p = fsutils.split(path)
|
||||
table.remove(p)
|
||||
return table.concat(p, PATHSEP)
|
||||
end
|
||||
|
||||
local function rename()
|
||||
local oldname = view.hovered_item.abs_filename
|
||||
core.command_view:enter("Rename to", function(newname)
|
||||
fsutils.move(oldname, newname)
|
||||
core.log("Moved %q to %q", oldname, newname)
|
||||
end, common.path_suggest)
|
||||
core.command_view:set_text(dirname(oldname))
|
||||
end
|
||||
|
||||
local function copy_path()
|
||||
system.set_clipboard(view.hovered_item.abs_filename)
|
||||
end
|
||||
|
||||
menu:register(function() return view.hovered_item and view.hovered_item.type == "dir" end, {
|
||||
{ text = "New file", command = new_file },
|
||||
{ text = "New folder", command = new_dir },
|
||||
menu.DIVIDER,
|
||||
{ text = "Rename", command = rename },
|
||||
{ text = "Delete", command = delete },
|
||||
menu.DIVIDER,
|
||||
{ text = "Copy directory name", command = copy_path }
|
||||
})
|
||||
menu:register(function() return view.hovered_item and view.hovered_item.type == "file" end, {
|
||||
{ text = "Rename", command = rename },
|
||||
{ text = "Delete", command = delete },
|
||||
menu.DIVIDER,
|
||||
{ text = "Copy filename", command = copy_path }
|
||||
})
|
||||
-- general region of the treeview
|
||||
menu:register(function(x, y)
|
||||
local x1, y1, x2, y2 = view:get_content_bounds()
|
||||
return not view.hovered_item and x > x1 and x <= x2 and y > y1 and y <= y2
|
||||
end, {
|
||||
{ text = "New file", command = function() new_file_f(system.absolute_path('.')) end },
|
||||
{ text = "New folder", command = function() new_dir_f(system.absolute_path('.')) end }
|
||||
})
|
||||
end
|
||||
|
||||
return view --< @r-lyeh
|
|
@ -1,36 +0,0 @@
|
|||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
|
||||
local function trim_trailing_whitespace(doc)
|
||||
local cline, ccol = doc:get_selection()
|
||||
for i = 1, #doc.lines do
|
||||
local old_text = doc:get_text(i, 1, i, math.huge)
|
||||
local new_text = old_text:gsub("%s*$", "")
|
||||
|
||||
-- don't remove whitespace which would cause the caret to reposition
|
||||
if cline == i and ccol > #new_text then
|
||||
new_text = old_text:sub(1, ccol - 1)
|
||||
end
|
||||
|
||||
if old_text ~= new_text then
|
||||
doc:insert(i, 1, new_text)
|
||||
doc:remove(i, #new_text + 1, i, math.huge)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
command.add("core.docview", {
|
||||
["trim-whitespace:trim-trailing-whitespace"] = function()
|
||||
trim_trailing_whitespace(core.active_view.doc)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local save = Doc.save
|
||||
Doc.save = function(self, ...)
|
||||
trim_trailing_whitespace(self)
|
||||
save(self, ...)
|
||||
end
|
|
@ -1,179 +0,0 @@
|
|||
local core = require "core"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
local workspace_filename = ".lite_workspace.lua"
|
||||
|
||||
|
||||
local function serialize(val)
|
||||
if type(val) == "string" then
|
||||
return string.format("%q", val)
|
||||
elseif type(val) == "table" then
|
||||
local t = {}
|
||||
for k, v in pairs(val) do
|
||||
table.insert(t, "[" .. serialize(k) .. "]=" .. serialize(v))
|
||||
end
|
||||
return "{" .. table.concat(t, ",") .. "}"
|
||||
end
|
||||
return tostring(val)
|
||||
end
|
||||
|
||||
|
||||
local function has_no_locked_children(node)
|
||||
if node.locked then return false end
|
||||
if node.type == "leaf" then return true end
|
||||
return has_no_locked_children(node.a) and has_no_locked_children(node.b)
|
||||
end
|
||||
|
||||
|
||||
local function get_unlocked_root(node)
|
||||
if node.type == "leaf" then
|
||||
return not node.locked and node
|
||||
end
|
||||
if has_no_locked_children(node) then
|
||||
return node
|
||||
end
|
||||
return get_unlocked_root(node.a) or get_unlocked_root(node.b)
|
||||
end
|
||||
|
||||
|
||||
local function save_view(view)
|
||||
local mt = getmetatable(view)
|
||||
if mt == DocView then
|
||||
return {
|
||||
type = "doc",
|
||||
active = (core.active_view == view),
|
||||
filename = view.doc.filename,
|
||||
selection = { view.doc:get_selection() },
|
||||
scroll = { x = view.scroll.to.x, y = view.scroll.to.y },
|
||||
text = not view.doc.filename and view.doc:get_text(1, 1, math.huge, math.huge)
|
||||
}
|
||||
end
|
||||
for name, mod in pairs(package.loaded) do
|
||||
if mod == mt then
|
||||
return {
|
||||
type = "view",
|
||||
active = (core.active_view == view),
|
||||
module = name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function load_view(t)
|
||||
if t.type == "doc" then
|
||||
local ok, doc = pcall(core.open_doc, t.filename)
|
||||
if not ok then
|
||||
return DocView(core.open_doc())
|
||||
end
|
||||
local dv = DocView(doc)
|
||||
if t.text then doc:insert(1, 1, t.text) end
|
||||
doc:set_selection(table.unpack(t.selection))
|
||||
dv.last_line, dv.last_col = doc:get_selection()
|
||||
dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x
|
||||
dv.scroll.y, dv.scroll.to.y = t.scroll.y, t.scroll.y
|
||||
return dv
|
||||
end
|
||||
return require(t.module)()
|
||||
end
|
||||
|
||||
|
||||
local function save_node(node)
|
||||
local res = {}
|
||||
res.type = node.type
|
||||
if node.type == "leaf" then
|
||||
res.views = {}
|
||||
for _, view in ipairs(node.views) do
|
||||
local t = save_view(view)
|
||||
if t then
|
||||
table.insert(res.views, t)
|
||||
if node.active_view == view then
|
||||
res.active_view = #res.views
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
res.divider = node.divider
|
||||
res.a = save_node(node.a)
|
||||
res.b = save_node(node.b)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function load_node(node, t)
|
||||
if t.type == "leaf" then
|
||||
local res
|
||||
for _, v in ipairs(t.views) do
|
||||
local view = load_view(v)
|
||||
if v.active then res = view end
|
||||
node:add_view(view)
|
||||
end
|
||||
if t.active_view then
|
||||
node:set_active_view(node.views[t.active_view])
|
||||
end
|
||||
return res
|
||||
else
|
||||
node:split(t.type == "hsplit" and "right" or "down")
|
||||
node.divider = t.divider
|
||||
local res1 = load_node(node.a, t.a)
|
||||
local res2 = load_node(node.b, t.b)
|
||||
return res1 or res2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function save_workspace()
|
||||
local root = get_unlocked_root(core.root_view.root_node)
|
||||
local fp = io.open(workspace_filename, "w")
|
||||
if fp then
|
||||
fp:write("return ", serialize(save_node(root)), "\n")
|
||||
fp:close()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function load_workspace()
|
||||
local ok, t = pcall(dofile, workspace_filename)
|
||||
os.remove(workspace_filename)
|
||||
if ok then
|
||||
local root = get_unlocked_root(core.root_view.root_node)
|
||||
local active_view = load_node(root, t)
|
||||
if active_view then
|
||||
core.set_active_view(active_view)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local run = core.run
|
||||
function core.run(...)
|
||||
if #core.docs == 0 then
|
||||
core.try(load_workspace)
|
||||
|
||||
local exit = os.exit
|
||||
function os.exit(...)
|
||||
save_workspace()
|
||||
exit(...)
|
||||
end
|
||||
end
|
||||
|
||||
core.run = run
|
||||
return core.run(...)
|
||||
end
|
||||
|
||||
local run1 = core.run1
|
||||
function core.run1(...)
|
||||
if #core.docs == 0 then
|
||||
core.try(load_workspace)
|
||||
|
||||
local exit = os.exit
|
||||
function os.exit(...)
|
||||
save_workspace()
|
||||
exit(...)
|
||||
end
|
||||
end
|
||||
|
||||
core.run1 = run1
|
||||
return core.run1(...)
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,714 +0,0 @@
|
|||
//Definitions of default DIALOG and CONTROL classes for the dialog editor.
|
||||
//This is a text file to enable simple customization and the addition of custom controls
|
||||
|
||||
[DIALOG]
|
||||
//default values for dialogs...
|
||||
width=300
|
||||
height=150
|
||||
fontname=Arial
|
||||
fontsize=9
|
||||
style=DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
language=LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//style flags and MASK values
|
||||
sf=DS_ABSALIGN
|
||||
sf=DS_SYSMODAL
|
||||
sf=DS_LOCALEDIT
|
||||
sf=DS_SETFONT
|
||||
sf=DS_MODALFRAME
|
||||
sf=DS_NOIDLEMSG
|
||||
sf=DS_SETFOREGROUND
|
||||
sf=DS_FIXEDSYS
|
||||
sf=DS_NOFAILCREATE
|
||||
sf=DS_CONTROL
|
||||
sf=DS_CENTER
|
||||
sf=DS_CENTERMOUSE
|
||||
sf=DS_CONTEXTHELP
|
||||
sf=WS_CHILD, (WS_POPUP | WS_CHILD) //ie: either WS_POPUP or WS_CHILD but not both!
|
||||
sf=WS_POPUP, (WS_CHILD | WS_POPUP)
|
||||
sf=WS_CLIPSIBLINGS
|
||||
sf=WS_CLIPCHILDREN
|
||||
sf=WS_DISABLED
|
||||
sf=WS_VISIBLE
|
||||
sf=WS_CAPTION
|
||||
sf=WS_BORDER, WS_CAPTION
|
||||
sf=WS_DLGFRAME, WS_CAPTION
|
||||
sf=WS_MINIMIZEBOX
|
||||
sf=WS_MAXIMIZEBOX
|
||||
sf=WS_SYSMENU
|
||||
sf=WS_THICKFRAME
|
||||
sf=WS_TILEDWINDOW
|
||||
sf=WS_POPUPWINDOW
|
||||
sf=WS_MINIMIZE
|
||||
sf=WS_MAXIMIZE
|
||||
sf=WS_VSCROLL
|
||||
sf=WS_HSCROLL
|
||||
//exstyle flags and MASK values
|
||||
esf=WS_EX_DLGMODALFRAME
|
||||
esf=WS_EX_NOPARENTNOTIFY
|
||||
esf=WS_EX_TOPMOST
|
||||
esf=WS_EX_ACCEPTFILES
|
||||
esf=WS_EX_TRANSPARENT
|
||||
esf=WS_EX_MDICHILD
|
||||
esf=WS_EX_TOOLWINDOW
|
||||
esf=WS_EX_WINDOWEDGE
|
||||
esf=WS_EX_CLIENTEDGE
|
||||
esf=WS_EX_CONTEXTHELP
|
||||
esf=WS_EX_RIGHT
|
||||
esf=WS_EX_LEFT, WS_EX_RIGHT
|
||||
esf=WS_EX_RTLREADING
|
||||
esf=WS_EX_LTRREADING, WS_EX_RTLREADING
|
||||
esf=WS_EX_LEFTSCROLLBAR
|
||||
esf=WS_EX_RIGHTSCROLLBAR, WS_EX_LEFTSCROLLBAR
|
||||
esf=WS_EX_CONTROLPARENT
|
||||
esf=WS_EX_STATICEDGE
|
||||
esf=WS_EX_APPWINDOW
|
||||
esf=WS_EX_OVERLAPPEDWINDOW
|
||||
esf=WS_EX_PALETTEWINDOW
|
||||
esf=WS_EX_LAYERED
|
||||
esf=WS_EX_NOINHERITLAYOUT
|
||||
esf=WS_EX_LAYOUTRTL
|
||||
esf=WS_EX_NOACTIVATE
|
||||
|
||||
|
||||
[BUTTON]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
style= WS_CHILD | WS_VISIBLE | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=BS_PUSHBUTTON, 0x1F
|
||||
sf=BS_DEFPUSHBUTTON, 0x1F
|
||||
sf=BS_CHECKBOX, 0x1F
|
||||
sf=BS_AUTOCHECKBOX, 0x1F
|
||||
sf=BS_3STATE, 0x1F
|
||||
sf=BS_AUTO3STATE, 0x1F
|
||||
sf=BS_RADIOBUTTON, 0x1F
|
||||
sf=BS_AUTORADIOBUTTON, 0x1F
|
||||
sf=BS_GROUPBOX, 0x1F
|
||||
sf=BS_USERBUTTON, 0x1F
|
||||
sf=BS_OWNERDRAW, 0x1F
|
||||
sf=BS_SPLITBUTTON, 0x1F
|
||||
sf=BS_DEFSPLITBUTTON, 0x1F
|
||||
sf=BS_COMMANDLINK, 0x1F
|
||||
sf=BS_DEFCOMMANDLINK, 0x1F
|
||||
sf=BS_LEFTTEXT
|
||||
sf=BS_ICON
|
||||
sf=BS_BITMAP
|
||||
sf=BS_LEFT, BS_CENTER
|
||||
sf=BS_RIGHT, BS_CENTER
|
||||
sf=BS_CENTER, BS_CENTER
|
||||
sf=BS_TOP, BS_VCENTER
|
||||
sf=BS_BOTTOM, BS_VCENTER
|
||||
sf=BS_VCENTER, BS_VCENTER
|
||||
sf=BS_PUSHLIKE
|
||||
sf=BS_MULTILINE
|
||||
sf=BS_NOTIFY
|
||||
sf=BS_FLAT
|
||||
|
||||
[CHECKBOX]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
style= BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP
|
||||
classname=BUTTON
|
||||
//style flags and MASK values
|
||||
sf=BS_CHECKBOX, 0x1F
|
||||
sf=BS_AUTOCHECKBOX, 0x1F
|
||||
sf=BS_3STATE, 0x1F
|
||||
sf=BS_AUTO3STATE, 0x1F
|
||||
sf=BS_LEFTTEXT
|
||||
sf=BS_LEFT, BS_CENTER
|
||||
sf=BS_RIGHT, BS_CENTER
|
||||
sf=BS_CENTER, BS_CENTER
|
||||
sf=BS_TOP, BS_VCENTER
|
||||
sf=BS_BOTTOM, BS_VCENTER
|
||||
sf=BS_VCENTER, BS_VCENTER
|
||||
sf=BS_PUSHLIKE
|
||||
sf=BS_MULTILINE
|
||||
sf=BS_NOTIFY
|
||||
sf=BS_FLAT
|
||||
|
||||
[RADIOBUTTON]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
style= BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP
|
||||
classname=BUTTON
|
||||
//style flags and MASK values
|
||||
sf=BS_RADIOBUTTON, 0x1F
|
||||
sf=BS_AUTORADIOBUTTON, 0x1F
|
||||
sf=BS_LEFTTEXT
|
||||
sf=BS_LEFT, BS_CENTER
|
||||
sf=BS_RIGHT, BS_CENTER
|
||||
sf=BS_CENTER, BS_CENTER
|
||||
sf=BS_TOP, BS_VCENTER
|
||||
sf=BS_BOTTOM, BS_VCENTER
|
||||
sf=BS_VCENTER, BS_VCENTER
|
||||
sf=BS_PUSHLIKE
|
||||
sf=BS_MULTILINE
|
||||
sf=BS_NOTIFY
|
||||
sf=BS_FLAT
|
||||
|
||||
[GROUPBOX]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= BS_GROUPBOX | WS_CHILD | WS_VISIBLE
|
||||
classname=BUTTON
|
||||
//style flags and MASK values
|
||||
sf=BS_GROUPBOX, 0x1F
|
||||
sf=BS_LEFT, BS_CENTER
|
||||
sf=BS_RIGHT, BS_CENTER
|
||||
sf=BS_CENTER, BS_CENTER
|
||||
sf=BS_NOTIFY
|
||||
sf=BS_FLAT
|
||||
|
||||
[COMBOBOX]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWN
|
||||
//style flags and MASK values
|
||||
sf=CBS_SIMPLE, CBS_DROPDOWNLIST
|
||||
sf=CBS_DROPDOWN, CBS_DROPDOWNLIST
|
||||
sf=CBS_DROPDOWNLIST, CBS_DROPDOWNLIST
|
||||
sf=CBS_OWNERDRAWFIXED
|
||||
sf=CBS_OWNERDRAWVARIABLE
|
||||
sf=CBS_AUTOHSCROLL
|
||||
sf=CBS_OEMCONVERT
|
||||
sf=CBS_SORT
|
||||
sf=CBS_HASSTRINGS
|
||||
sf=CBS_NOINTEGRALHEIGHT
|
||||
sf=CBS_DISABLENOSCROLL
|
||||
sf=CBS_UPPERCASE
|
||||
sf=CBS_LOWERCASE
|
||||
|
||||
[EDIT]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
style= WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=ES_LEFT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_CENTER, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_RIGHT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_MULTILINE
|
||||
sf=ES_UPPERCASE
|
||||
sf=ES_LOWERCASE
|
||||
sf=ES_PASSWORD
|
||||
sf=ES_AUTOVSCROLL
|
||||
sf=ES_AUTOHSCROLL
|
||||
sf=ES_NOHIDESEL
|
||||
sf=ES_OEMCONVERT
|
||||
sf=ES_READONLY
|
||||
sf=ES_WANTRETURN
|
||||
sf=ES_NUMBER
|
||||
|
||||
[MEMO]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
classname=EDIT
|
||||
style= ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL
|
||||
//style flags and MASK values
|
||||
sf=ES_LEFT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_CENTER, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_RIGHT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_MULTILINE
|
||||
sf=ES_UPPERCASE
|
||||
sf=ES_LOWERCASE
|
||||
sf=ES_PASSWORD
|
||||
sf=ES_AUTOVSCROLL
|
||||
sf=ES_AUTOHSCROLL
|
||||
sf=ES_NOHIDESEL
|
||||
sf=ES_OEMCONVERT
|
||||
sf=ES_READONLY
|
||||
sf=ES_WANTRETURN
|
||||
sf=ES_NUMBER
|
||||
|
||||
[LISTBOX]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | WS_TABSTOP | LBS_STANDARD
|
||||
//LBS_STANDARD = (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
|
||||
//style flags
|
||||
sf=LBS_NOTIFY
|
||||
sf=LBS_SORT
|
||||
sf=LBS_NOREDRAW
|
||||
sf=LBS_MULTIPLESEL
|
||||
sf=LBS_OWNERDRAWFIXED
|
||||
sf=LBS_OWNERDRAWVARIABLE
|
||||
sf=LBS_HASSTRINGS
|
||||
sf=LBS_USETABSTOPS
|
||||
sf=LBS_NOINTEGRALHEIGHT
|
||||
sf=LBS_MULTICOLUMN
|
||||
sf=LBS_WANTKEYBOARDINPUT
|
||||
sf=LBS_EXTENDEDSEL
|
||||
sf=LBS_DISABLENOSCROLL
|
||||
sf=LBS_NODATA
|
||||
sf=LBS_NOSEL
|
||||
|
||||
[SCROLLBAR]
|
||||
//default values ...
|
||||
width=30
|
||||
height=14
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags and MASK values
|
||||
sf=SBS_HORZ, SBS_VERT
|
||||
sf=SBS_VERT, SBS_VERT
|
||||
sf=SBS_TOPALIGN
|
||||
sf=SBS_LEFTALIGN
|
||||
sf=SBS_BOTTOMALIGN
|
||||
sf=SBS_RIGHTALIGN
|
||||
sf=SBS_SIZEBOXTOPLEFTALIGN
|
||||
sf=SBS_SIZEBOXBOTTOMRIGHTALIGN
|
||||
sf=SBS_SIZEBOX
|
||||
sf=SBS_SIZEGRIP
|
||||
|
||||
[STATIC]
|
||||
//default values ...
|
||||
width=32
|
||||
height=32
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP
|
||||
//style flags and MASK values
|
||||
sf=SS_LEFT, 0x1F
|
||||
sf=SS_CENTER, 0x1F
|
||||
sf=SS_RIGHT, 0x1F
|
||||
sf=SS_ICON, 0x1F
|
||||
sf=SS_BLACKRECT, 0x1F
|
||||
sf=SS_GRAYRECT, 0x1F
|
||||
sf=SS_WHITERECT, 0x1F
|
||||
sf=SS_BLACKFRAME, 0x1F
|
||||
sf=SS_GRAYFRAME, 0x1F
|
||||
sf=SS_WHITEFRAME, 0x1F
|
||||
sf=SS_USERITEM, 0x1F
|
||||
sf=SS_SIMPLE, 0x1F
|
||||
sf=SS_LEFTNOWORDWRAP, 0x1F
|
||||
sf=SS_BITMAP, 0x1F
|
||||
sf=SS_OWNERDRAW, 0x1F
|
||||
sf=SS_ENHMETAFILE, 0x1F
|
||||
sf=SS_ETCHEDHORZ, 0x1F
|
||||
sf=SS_ETCHEDVERT, 0x1F
|
||||
sf=SS_ETCHEDFRAME, 0x1F
|
||||
sf=SS_NOPREFIX
|
||||
sf=SS_NOTIFY
|
||||
sf=SS_CENTERIMAGE
|
||||
sf=SS_RIGHTJUST
|
||||
sf=SS_REALSIZEIMAGE
|
||||
sf=SS_SUNKEN
|
||||
sf=SS_ENDELLIPSIS, SS_ELLIPSISMASK
|
||||
sf=SS_PATHELLIPSIS, SS_ELLIPSISMASK
|
||||
sf=SS_WORDELLIPSIS, SS_ELLIPSISMASK
|
||||
|
||||
[LTEXT]
|
||||
//default values ...
|
||||
width=60
|
||||
height=18
|
||||
classname=STATIC
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP
|
||||
//style flags
|
||||
sf=SS_NOPREFIX
|
||||
sf=SS_NOTIFY
|
||||
sf=SS_CENTERIMAGE
|
||||
sf=SS_RIGHTJUST
|
||||
sf=SS_SUNKEN
|
||||
|
||||
[ICON]
|
||||
//default values ...
|
||||
width=20
|
||||
height=20
|
||||
classname=STATIC
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP | SS_ICON
|
||||
//style flags
|
||||
sf=SS_ICON, 0x1F
|
||||
sf=SS_NOPREFIX
|
||||
sf=SS_NOTIFY
|
||||
sf=SS_CENTERIMAGE
|
||||
sf=SS_RIGHTJUST
|
||||
sf=SS_REALSIZEIMAGE
|
||||
sf=SS_SUNKEN
|
||||
|
||||
[BITMAP]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
classname=STATIC
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP | SS_BITMAP
|
||||
//style flags
|
||||
sf=SS_BITMAP, 0x1F
|
||||
sf=SS_NOPREFIX
|
||||
sf=SS_NOTIFY
|
||||
sf=SS_CENTERIMAGE
|
||||
sf=SS_RIGHTJUST
|
||||
sf=SS_REALSIZEIMAGE
|
||||
sf=SS_SUNKEN
|
||||
|
||||
["RICHEDIT"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=ES_LEFT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_CENTER, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_RIGHT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_MULTILINE
|
||||
sf=ES_AUTOVSCROLL
|
||||
sf=ES_AUTOHSCROLL
|
||||
sf=ES_NOHIDESEL
|
||||
sf=ES_READONLY
|
||||
sf=ES_WANTRETURN
|
||||
sf=ES_PASSWORD
|
||||
sf=ES_SAVESEL
|
||||
sf=ES_SUNKEN
|
||||
sf=ES_DISABLENOSCROLL
|
||||
sf=ES_SELECTIONBAR
|
||||
sf=ES_NOOLEDRAGDROP
|
||||
sf=ES_VERTICAL
|
||||
sf=ES_NOIME
|
||||
sf=ES_SELFIME
|
||||
|
||||
esf=ES_EX_NOCALLOLEINIT
|
||||
|
||||
["SysAnimate32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
sf=ACS_CENTER
|
||||
sf=ACS_TRANSPARENT
|
||||
sf=ACS_AUTOPLAY
|
||||
sf=ACS_TIMER
|
||||
|
||||
["SysDateTimePick32"]
|
||||
//default values ...
|
||||
width=80
|
||||
height=14
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
sf=DTS_UPDOWN
|
||||
sf=DTS_SHOWNONE
|
||||
sf=DTS_SHORTDATEFORMAT, DTS_LONGDATEFORMAT
|
||||
sf=DTS_LONGDATEFORMAT, DTS_LONGDATEFORMAT
|
||||
sf=DTS_TIMEFORMAT //nb: must be DTS_UPDOWN (not calendar) with DTS_TIMEFORMAT
|
||||
sf=DTS_APPCANPARSE
|
||||
sf=DTS_RIGHTALIGN
|
||||
|
||||
["SysHeader32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=12
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags and MASK values
|
||||
sf=HDS_BUTTONS
|
||||
sf=HDS_HOTTRACK
|
||||
sf=HDS_HIDDEN
|
||||
sf=HDS_DRAGDROP
|
||||
sf=HDS_FULLDRAG
|
||||
sf=CCS_TOP, CCS_BOTTOM
|
||||
sf=CCS_NOMOVEY, CCS_BOTTOM
|
||||
sf=CCS_BOTTOM, CCS_BOTTOM
|
||||
sf=CCS_NORESIZE
|
||||
sf=CCS_NOPARENTALIGN
|
||||
sf=CCS_ADJUSTABLE
|
||||
sf=CCS_NODIVIDER
|
||||
sf=CCS_VERT
|
||||
sf=CCS_LEFT
|
||||
sf=CCS_RIGHT
|
||||
sf=CCS_NOMOVEX
|
||||
|
||||
["SysListView32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=LVS_ICON, LVS_TYPEMASK
|
||||
sf=LVS_REPORT, LVS_TYPEMASK
|
||||
sf=LVS_SMALLICON, LVS_TYPEMASK
|
||||
sf=LVS_LIST, LVS_TYPEMASK
|
||||
sf=LVS_SINGLESEL
|
||||
sf=LVS_SHOWSELALWAYS
|
||||
sf=LVS_SORTASCENDING
|
||||
sf=LVS_SORTDESCENDING
|
||||
sf=LVS_SHAREIMAGELISTS
|
||||
sf=LVS_NOLABELWRAP
|
||||
sf=LVS_AUTOARRANGE
|
||||
sf=LVS_EDITLABELS
|
||||
sf=LVS_OWNERDATA
|
||||
sf=LVS_NOSCROLL
|
||||
sf=LVS_ALIGNTOP, LVS_ALIGNLEFT
|
||||
sf=LVS_ALIGNLEFT, LVS_ALIGNLEFT
|
||||
sf=LVS_OWNERDRAWFIXED
|
||||
sf=LVS_NOCOLUMNHEADER
|
||||
sf=LVS_NOSORTHEADER
|
||||
|
||||
esf=LVS_EX_GRIDLINES
|
||||
esf=LVS_EX_SUBITEMIMAGES
|
||||
esf=LVS_EX_CHECKBOXES
|
||||
esf=LVS_EX_TRACKSELECT
|
||||
esf=LVS_EX_HEADERDRAGDROP
|
||||
esf=LVS_EX_FULLROWSELECT
|
||||
esf=LVS_EX_ONECLICKACTIVATE
|
||||
esf=LVS_EX_TWOCLICKACTIVATE
|
||||
|
||||
["SysMonthCal32"]
|
||||
//default values ...
|
||||
width=130
|
||||
height=100
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
sf=MCS_DAYSTATE
|
||||
sf=MCS_MULTISELECT
|
||||
sf=MCS_WEEKNUMBERS
|
||||
sf=MCS_NOTODAY
|
||||
|
||||
["SysPager"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=20
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags and MASK values
|
||||
sf=PGS_VERT, PGS_HORZ
|
||||
sf=PGS_HORZ, PGS_HORZ
|
||||
sf=PGS_AUTOSCROLL
|
||||
sf=PGS_DRAGNDROP
|
||||
|
||||
["SysTabControl32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | TCS_RAGGEDRIGHT
|
||||
sf=TCS_SCROLLOPPOSITE
|
||||
sf=TCS_BOTTOM
|
||||
sf=TCS_RIGHT
|
||||
sf=TCS_FORCEICONLEFT
|
||||
sf=TCS_FORCELABELLEFT
|
||||
sf=TCS_HOTTRACK
|
||||
sf=TCS_VERTICAL
|
||||
sf=TCS_TABS, TCS_BUTTONS
|
||||
sf=TCS_BUTTONS, TCS_BUTTONS
|
||||
sf=TCS_SINGLELINE, TCS_MULTILINE
|
||||
sf=TCS_MULTILINE, TCS_MULTILINE
|
||||
sf=TCS_RIGHTJUSTIFY, TCS_FIXEDWIDTH
|
||||
sf=TCS_FIXEDWIDTH, TCS_FIXEDWIDTH
|
||||
sf=TCS_RAGGEDRIGHT
|
||||
sf=TCS_FOCUSONBUTTONDOWN
|
||||
sf=TCS_OWNERDRAWFIXED
|
||||
sf=TCS_TOOLTIPS
|
||||
sf=TCS_FOCUSNEVER
|
||||
|
||||
esf=TCS_EX_FLATSEPARATORS
|
||||
esf=TCS_EX_REGISTERDROP
|
||||
|
||||
["SysTreeView32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
|
||||
//style flags
|
||||
sf=TVS_HASBUTTONS
|
||||
sf=TVS_HASLINES
|
||||
sf=TVS_LINESATROOT
|
||||
sf=TVS_EDITLABELS
|
||||
sf=TVS_DISABLEDRAGDROP
|
||||
sf=TVS_SHOWSELALWAYS
|
||||
|
||||
esf=TVS_EX_MULTISELECT
|
||||
esf=TVS_EX_DOUBLEBUFFER
|
||||
esf=TVS_EX_NOINDENTSTATE
|
||||
esf=TVS_EX_RICHTOOLTIP
|
||||
esf=TVS_EX_AUTOHSCROLL
|
||||
esf=TVS_EX_FADEINOUTEXPANDOS
|
||||
esf=TVS_EX_PARTIALCHECKBOXES
|
||||
esf=TVS_EX_EXCLUSIONCHECKBOXES
|
||||
esf=TVS_EX_DIMMEDCHECKBOXES
|
||||
esf=TVS_EX_DRAWIMAGEASYNC
|
||||
|
||||
["msctls_hotkey32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
|
||||
["msctls_progress32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=12
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
sf=PBS_SMOOTH
|
||||
sf=PBS_VERTICAL
|
||||
|
||||
["msctls_statusbar32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=12
|
||||
style= WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP
|
||||
sf=SBARS_SIZEGRIP
|
||||
sf=SBT_TOOLTIPS
|
||||
sf=CCS_TOP, CCS_BOTTOM
|
||||
sf=CCS_NOMOVEY, CCS_BOTTOM
|
||||
sf=CCS_BOTTOM, CCS_BOTTOM
|
||||
sf=CCS_NORESIZE
|
||||
sf=CCS_NOPARENTALIGN
|
||||
sf=CCS_ADJUSTABLE
|
||||
sf=CCS_NODIVIDER
|
||||
sf=CCS_VERT
|
||||
sf=CCS_LEFT
|
||||
sf=CCS_RIGHT
|
||||
sf=CCS_NOMOVEX
|
||||
|
||||
["msctls_trackbar32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=18
|
||||
style= WS_CHILD | WS_VISIBLE | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=TBS_HORZ, TBS_VERT
|
||||
sf=TBS_VERT, TBS_VERT
|
||||
sf=TBS_AUTOTICKS
|
||||
sf=TBS_BOTTOM, TBS_TOP
|
||||
sf=TBS_TOP, TBS_TOP
|
||||
sf=TBS_RIGHT, TBS_LEFT
|
||||
sf=TBS_LEFT, TBS_LEFT
|
||||
sf=TBS_BOTH
|
||||
sf=TBS_NOTICKS
|
||||
sf=TBS_ENABLESELRANGE
|
||||
sf=TBS_FIXEDLENGTH
|
||||
sf=TBS_NOTHUMB
|
||||
|
||||
["msctls_updown32"]
|
||||
//default values ...
|
||||
width=12
|
||||
height=20
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags
|
||||
sf=UDS_WRAP
|
||||
sf=UDS_SETBUDDYINT
|
||||
sf=UDS_ALIGNRIGHT
|
||||
sf=UDS_ALIGNLEFT
|
||||
sf=UDS_AUTOBUDDY
|
||||
sf=UDS_ARROWKEYS
|
||||
sf=UDS_HORZ
|
||||
sf=UDS_NOTHOUSANDS
|
||||
|
||||
["ComboBoxEx32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= WS_CHILD | WS_VISIBLE | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=CBS_SIMPLE, CBS_DROPDOWNLIST
|
||||
sf=CBS_DROPDOWN, CBS_DROPDOWNLIST
|
||||
sf=CBS_DROPDOWNLIST, CBS_DROPDOWNLIST
|
||||
sf=CBS_AUTOHSCROLL
|
||||
sf=CBS_OEMCONVERT
|
||||
sf=CBS_SORT
|
||||
sf=CBS_HASSTRINGS
|
||||
sf=CBS_NOINTEGRALHEIGHT
|
||||
sf=CBS_DISABLENOSCROLL
|
||||
sf=CBS_UPPERCASE
|
||||
sf=CBS_LOWERCASE
|
||||
|
||||
esf=CBES_EX_CASESENSITIVE
|
||||
esf=CBES_EX_NOEDITIMAGE
|
||||
esf=CBES_EX_NOEDITIMAGEINDENT
|
||||
esf=CBES_EX_NOSIZELIMIT
|
||||
esf=CBES_EX_PATHWORDBREAKPROC
|
||||
|
||||
["ReBarWindow32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=12
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags
|
||||
sf=RBS_TOOLTIPS
|
||||
sf=RBS_VARHEIGHT
|
||||
sf=RBS_BANDBORDERS
|
||||
sf=RBS_FIXEDORDER
|
||||
sf=RBS_REGISTERDROP
|
||||
sf=RBS_AUTOSIZE
|
||||
sf=RBS_VERTICALGRIPPER
|
||||
sf=RBS_DBLCLKTOGGLE
|
||||
sf=CCS_TOP, CCS_BOTTOM
|
||||
sf=CCS_NOMOVEY, CCS_BOTTOM
|
||||
sf=CCS_BOTTOM, CCS_BOTTOM
|
||||
sf=CCS_NORESIZE
|
||||
sf=CCS_NOPARENTALIGN
|
||||
sf=CCS_ADJUSTABLE
|
||||
sf=CCS_NODIVIDER
|
||||
sf=CCS_VERT
|
||||
sf=CCS_LEFT
|
||||
sf=CCS_RIGHT
|
||||
sf=CCS_NOMOVEX
|
||||
|
||||
["ToolbarWindow32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=12
|
||||
style= WS_CHILD | WS_VISIBLE
|
||||
//style flags and MASK value
|
||||
sf=TBSTYLE_TOOLTIPS
|
||||
sf=TBSTYLE_WRAPABLE
|
||||
sf=TBSTYLE_ALTDRAG
|
||||
sf=TBSTYLE_FLAT
|
||||
sf=TBSTYLE_LIST
|
||||
sf=TBSTYLE_CUSTOMERASE
|
||||
sf=TBSTYLE_REGISTERDROP
|
||||
sf=TBSTYLE_TRANSPARENT
|
||||
sf=CCS_TOP, CCS_BOTTOM
|
||||
sf=CCS_NOMOVEY, CCS_BOTTOM
|
||||
sf=CCS_BOTTOM, CCS_BOTTOM
|
||||
sf=CCS_NORESIZE
|
||||
sf=CCS_NOPARENTALIGN
|
||||
sf=CCS_ADJUSTABLE
|
||||
sf=CCS_NODIVIDER
|
||||
sf=CCS_VERT
|
||||
sf=CCS_LEFT
|
||||
sf=CCS_RIGHT
|
||||
sf=CCS_NOMOVEX
|
||||
|
||||
esf=TBSTYLE_EX_DRAWDDARROWS
|
||||
//esf=TBSTYLE_EX_HIDECLIPPEDBUTTONS
|
||||
//esf=TBSTYLE_EX_MIXEDBUTTONS
|
||||
|
||||
["RichEdit20W"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=60
|
||||
style= ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
|
||||
//style flags and MASK values
|
||||
sf=ES_LEFT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_CENTER, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_RIGHT, (ES_CENTER | ES_RIGHT)
|
||||
sf=ES_MULTILINE
|
||||
sf=ES_AUTOVSCROLL
|
||||
sf=ES_AUTOHSCROLL
|
||||
sf=ES_NOHIDESEL
|
||||
sf=ES_READONLY
|
||||
sf=ES_WANTRETURN
|
||||
sf=ES_PASSWORD
|
||||
sf=ES_SAVESEL
|
||||
sf=ES_SUNKEN
|
||||
sf=ES_DISABLENOSCROLL
|
||||
sf=ES_SELECTIONBAR
|
||||
sf=ES_NOOLEDRAGDROP
|
||||
sf=ES_VERTICAL
|
||||
sf=ES_NOIME
|
||||
sf=ES_SELFIME
|
||||
|
||||
esf=ES_EX_NOCALLOLEINIT
|
||||
|
||||
["SysLink"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=18
|
||||
classname="SysLink"
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP
|
||||
|
||||
|
||||
["SysIPAddress32"]
|
||||
//default values ...
|
||||
width=60
|
||||
height=14
|
||||
classname="SysIPAddress32"
|
||||
style= WS_CHILD | WS_VISIBLE | WS_GROUP
|
Binary file not shown.
Loading…
Reference in New Issue