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