X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=mod_query%2Fmod_query.lua;h=bd3414bd95764cd5c6d62147d9181e4af0e44d18;hb=HEAD;hp=0f51738edceb6c7dde1058f5354a01cdf09cb18a;hpb=8366314611bf30a0f31d25bf5f5023186fa87692;p=ion3.git diff --git a/mod_query/mod_query.lua b/mod_query/mod_query.lua index 0f51738..bd3414b 100644 --- a/mod_query/mod_query.lua +++ b/mod_query/mod_query.lua @@ -1,12 +1,9 @@ -- -- ion/query/mod_query.lua -- Some common queries for Ion -- --- Copyright (c) Tuomo Valkonen 2004-2006. +-- Copyright (c) Tuomo Valkonen 2004-2009. -- --- Ion is free software; you can redistribute it and/or modify it under --- the terms of the GNU Lesser General Public License as published by --- the Free Software Foundation; either version 2.1 of the License, or --- (at your option) any later version. +-- See the included file LICENSE for details. -- @@ -31,11 +28,17 @@ local DIE_TIMEOUT_NO_ERRORCODE=2 -- 2 seconds -- Generic helper functions {{{ -function mod_query.make_completor(completefn) - local function completor(cp, str, point) - cp:set_completions(completefn(str, point)) - end - return completor +--DOC +-- Display an error message box in the multiplexer \var{mplex}. +function mod_query.warn(mplex, str) + ioncore.unsqueeze(mod_query.do_warn(mplex, str)) +end + + +--DOC +-- Display a message in \var{mplex}. +function mod_query.message(mplex, str) + ioncore.unsqueeze(mod_query.do_message(mplex, str)) end @@ -58,18 +61,28 @@ function mod_query.query(mplex, prompt, initvalue, handler, completor, local function cycle(wedln) wedln:complete('next', 'normal') end + local function bcycle(wedln) + wedln:complete('prev', 'normal') + end - -- Check that no other queries are open in the mplex. - local l=mplex:managed_list() - for i, r in pairs(l) do - if obj_is(r, "WEdln") then - return - end + -- Check that no other queries or message boxes are open in the mplex. + local ok=mplex:managed_i(function(r) + return not (obj_is(r, "WEdln") or + obj_is(r, "WMessage")) + end) + if not ok then + return end + local wedln=mod_query.do_query(mplex, prompt, initvalue, - handle_it, completor, cycle) - if context then - wedln:set_context(context) + handle_it, completor, cycle, bcycle) + + if wedln then + ioncore.unsqueeze(wedln) + + if context then + wedln:set_context(context) + end end return wedln @@ -175,6 +188,20 @@ function mod_query.file_completor(wedln, str) end +function mod_query.get_initdir(mplex) + --if mod_query.last_dir then + -- return mod_query.last_dir + --end + local wd=(ioncore.get_dir_for(mplex) or os.getenv("PWD")) + if wd==nil then + wd="/" + elseif string.sub(wd, -1)~="/" then + wd=wd .. "/" + end + return wd +end + + function mod_query.query_execfile(mplex, prompt, prog) assert(prog~=nil) local function handle_execwith(mplex, str) @@ -200,39 +227,10 @@ function mod_query.query_execwith(mplex, prompt, dflt, prog, completor, end -function mod_query.get_initdir(mplex) - --if mod_query.last_dir then - -- return mod_query.last_dir - --end - local wd=(ioncore.get_dir_for(mplex) or os.getenv("PWD")) - if wd==nil then - wd="/" - elseif string.sub(wd, -1)~="/" then - wd=wd .. "/" - end - return wd -end - +-- }}} -local MAXDEPTH=10 - - -function mod_query.complete_from_list(list, str) - local results={} - local len=string.len(str) - if len==0 then - results=list - else - for _, m in pairs(list) do - if string.sub(m, 1, len)==str then - table.insert(results, m) - end - end - end - - return results -end +-- Completion helpers {{{ local pipes={} @@ -241,13 +239,14 @@ mod_query.COLLECT_THRESHOLD=2000 --DOC -- This function can be used to read completions from an external source. -- The parameter \var{cp} is the completion proxy to be used, --- and the string \var{cmd} the shell command to be executed. To its stdout, --- the command should on the first line write the \var{common_beg} +-- and the string \var{cmd} the shell command to be executed, in the directory +-- \var{wd}. +-- To its stdout, the command should on the first line write the \var{common_beg} -- parameter of \fnref{WComplProxy.set_completions} (which \var{fn} maybe used -- to override) and a single actual completion on each of the successive lines. -- The function \var{reshnd} may be used to override a result table -- building routine. -function mod_query.popen_completions(cp, cmd, fn, reshnd) +function mod_query.popen_completions(cp, cmd, fn, reshnd, wd) local pst={cp=cp, maybe_stalled=0} @@ -320,8 +319,90 @@ function mod_query.popen_completions(cp, cmd, fn, reshnd) if not found_clean then pipes[rcv]=pst - ioncore.popen_bgread(cmd, coroutine.wrap(rcv)) + ioncore.popen_bgread(cmd, coroutine.wrap(rcv), nil, wd) + end +end + + +local function mk_completion_test(str, sub_ok, casei_ok) + local settings=mod_query.get() + + if not str then + return function(s) return true end + end + + local function mk(str, sub_ok) + if sub_ok then + return function(s) return string.find(s, str, 1, true) end + else + local len=string.len(str) + return function(s) return string.sub(s, 1, len)==str end + end + end + + casei_ok=(casei_ok and settings.caseicompl) + sub_ok=(sub_ok and settings.substrcompl) + + if not casei_ok then + return mk(str, sub_ok) + else + local fn=mk(string.lower(str), sub_ok) + return function(s) return fn(string.lower(s)) end + end +end + + +local function mk_completion_add(entries, str, sub_ok, casei_ok) + local tst=mk_completion_test(str, sub_ok, casei_ok) + + return function(s) + if s and tst(s) then + table.insert(entries, s) + end + end +end + + +function mod_query.complete_keys(list, str, sub_ok, casei_ok) + local results={} + local test_add=mk_completion_add(results, str, sub_ok, casei_ok) + + for m, _ in pairs(list) do + test_add(m) + end + + return results +end + + +function mod_query.complete_name(str, iter) + local sub_ok_first=true + local casei_ok=true + local entries={} + local tst_add=mk_completion_add(entries, str, sub_ok_first, casei_ok) + + iter(function(reg) + tst_add(reg:name()) + return true + end) + + if #entries==0 and not sub_ok_first then + local tst_add2=mk_completion_add(entries, str, true, casei_ok) + iter(function(reg) + tst_add2(reg:name()) + return true + end) + end + + return entries +end + + +function mod_query.make_completor(completefn) + local function completor(cp, str, point) + cp:set_completions(completefn(str, point)) end + return completor end @@ -340,36 +421,24 @@ function mod_query.call_warn(mplex, fn) end -function mod_query.complete_name(str, list) - local entries={} - local l=string.len(str) - for i, reg in pairs(list) do - local nm=reg:name() - if nm and string.sub(nm, 1, l)==str then - table.insert(entries, nm) - end - end - if #entries==0 then - for i, reg in pairs(list) do - local nm=reg:name() - if nm and string.find(nm, str, 1, true) then - table.insert(entries, nm) - end - end - end - return entries -end - function mod_query.complete_clientwin(str) - return mod_query.complete_name(str, ioncore.clientwin_list()) + return mod_query.complete_name(str, ioncore.clientwin_i) end + function mod_query.complete_workspace(str) - return mod_query.complete_name(str, ioncore.region_list("WGroupWS")) + local function iter(fn) + return ioncore.region_i(function(obj) + return (not obj_is(obj, "WGroupWS") + or fn(obj)) + end) + end + return mod_query.complete_name(str, iter) end + function mod_query.complete_region(str) - return mod_query.complete_name(str, ioncore.region_list()) + return mod_query.complete_name(str, ioncore.region_i) end @@ -383,6 +452,7 @@ function mod_query.gotoclient_handler(frame, str) end end + function mod_query.attachclient_handler(frame, str) local cwin=ioncore.lookup_clientwin(str) @@ -391,24 +461,12 @@ function mod_query.attachclient_handler(frame, str) return end - local reg=cwin:manager() - local attach + local reg=cwin:groupleader_of() - if not obj_is(reg, "WGroupCW") then - reg = cwin - attach = function() - frame:attach_new { - type = "WGroupCW", - switchto = true, - managed = {{ reg = cwin, bottom = true }} - } - end - else - attach = function() - frame:attach(reg, { switchto = true }) - end + local function attach() + frame:attach(reg, { switchto = true }) end - + if frame:rootwin_of()~=reg:rootwin_of() then mod_query.warn(frame, TR("Cannot attach: different root windows.")) elseif reg:manager()==frame then @@ -423,25 +481,47 @@ function mod_query.workspace_handler(mplex, name) local ws=ioncore.lookup_region(name, "WGroupWS") if ws then ws:goto() - return - end + else + local function create_handler(mplex_, layout) + if not layout or layout=="" then + layout="default" + end + + if not ioncore.getlayout(layout) then + mod_query.warn(mplex_, TR("Unknown layout")) + else + local scr=mplex:screen_of() + + local function mkws() + local tmpl={ + name=(name~="" and name), + switchto=true + } + if not ioncore.create_ws(scr, tmpl, layout) then + error(TR("Unknown error")) + end + end - local scr=mplex:screen_of() - - local function mkws() - if not ioncore.create_ws(scr, {name=name}) then - error(TR("Unknown error")) + mod_query.call_warn(mplex, mkws) + end end - end - mod_query.call_warn(mplex, mkws) + local function compl_layout(str) + local los=ioncore.getlayout(nil, true) + return mod_query.complete_keys(los, str, true, true) + end + + mod_query.query(mplex, TR("New workspace layout (default):"), nil, + create_handler, mod_query.make_completor(compl_layout), + "workspacelayout") + end end --DOC --- This query asks for the name of a client window and attaches --- it to the frame the query was opened in. It uses the completion --- function \fnref{ioncore.complete_clientwin}. +-- This query asks for the name of a client window and switches +-- focus to the one entered. It uses the completion function +-- \fnref{ioncore.complete_clientwin}. function mod_query.query_gotoclient(mplex) mod_query.query(mplex, TR("Go to window:"), nil, mod_query.gotoclient_handler, @@ -450,9 +530,9 @@ function mod_query.query_gotoclient(mplex) end --DOC --- This query asks for the name of a client window and switches --- focus to the one entered. It uses the completion function --- \fnref{ioncore.complete_clientwin}. +-- This query asks for the name of a client window and attaches +-- it to the frame the query was opened in. It uses the completion +-- function \fnref{ioncore.complete_clientwin}. function mod_query.query_attachclient(mplex) mod_query.query(mplex, TR("Attach window:"), nil, mod_query.attachclient_handler, @@ -504,10 +584,20 @@ end --DOC --- This function asks for a name new for the workspace on which the --- query resides. -function mod_query.query_renameworkspace(mplex) - local ws=ioncore.find_manager(mplex, "WGroupWS") +-- This function asks for a name new for the workspace \var{ws}, +-- or the one on which \var{mplex} resides, if it is not set. +-- If \var{mplex} is not set, one is looked for. +function mod_query.query_renameworkspace(mplex, ws) + if not mplex then + assert(ws) + mplex=ioncore.find_manager(ws, "WMPlex") + elseif not ws then + assert(mplex) + ws=ioncore.find_manager(mplex, "WGroupWS") + end + + assert(mplex and ws) + mod_query.query(mplex, TR("Workspace name:"), ws:name(), function(mplex, str) ws:set_name(str) end, nil, "framename") @@ -661,7 +751,7 @@ local function find_point(strs, point) end -function mod_query.exec_completor(wedln, str, point) +function mod_query.exec_completor_(wd, wedln, str, point) local parts=break_cmdline(str) local complidx=find_point(parts, point+1) @@ -719,11 +809,16 @@ function mod_query.exec_completor(wedln, str, point) if ic then mod_query.popen_completions(wedln, ic..wp..string.shell_safe(s_compl), - set_fn, filter_fn) + set_fn, filter_fn, wd) end end +function mod_query.exec_completor(...) + mod_query.exec_completor_(nil, ...) +end + + local cmd_overrides={} @@ -753,9 +848,12 @@ end -- \file{ion-runinxterm}. Two colons ('::') will ask you to press -- enter after the command has finished. function mod_query.query_exec(mplex) - mod_query.query(mplex, TR("Run:"), nil, mod_query.exec_handler, - mod_query.exec_completor, - "run") + local function compl(...) + local wd=ioncore.get_dir_for(mplex) + mod_query.exec_completor_(wd, ...) + end + mod_query.query(mplex, TR("Run:"), nil, mod_query.exec_handler, + compl, "run") end @@ -766,6 +864,8 @@ end mod_query.known_hosts={} +mod_query.hostnicks={} +mod_query.ssh_completions={} function mod_query.get_known_hosts(mplex) @@ -789,8 +889,6 @@ function mod_query.get_known_hosts(mplex) end -mod_query.hostnicks={} - function mod_query.get_hostnicks(mplex) mod_query.hostnicks={} local f @@ -800,7 +898,7 @@ function mod_query.get_hostnicks(mplex) if h then f=io.open(h.."/.ssh/config") end - if not f then + if not f then warn(TR("Failed to open ~/.ssh/config")) return end @@ -813,7 +911,7 @@ function mod_query.get_hostnicks(mplex) patterns=pat elseif string.find(substr, "^[nN][aA][mM][eE]") and patterns then - for s in string.gfind(patterns, "%S+") do + for s in string.gmatch(patterns, "%S+") do if not string.find(s, "[*?]") then table.insert(mod_query.hostnicks, s) end @@ -837,21 +935,10 @@ function mod_query.complete_ssh(str) end local res = {} - - if string.len(host)==0 then - if string.len(user)==0 then - return mod_query.ssh_completions - end - - for _, v in ipairs(mod_query.ssh_completions) do - table.insert(res, user .. v) - end - return res - end + local tst = mk_completion_test(host, true, true) for _, v in ipairs(mod_query.ssh_completions) do - local s, e=string.find(v, host, 1, true) - if s==1 and e>=1 then + if tst(v) then table.insert(res, user .. v) end end @@ -859,7 +946,6 @@ function mod_query.complete_ssh(str) return res end -mod_query.ssh_completions={} --DOC -- This query asks for a host to connect to with SSH. @@ -898,8 +984,10 @@ end function mod_query.man_completor(wedln, str) local mc=ioncore.lookup_script("ion-completeman") + local icase=(mod_query.get().caseicompl and " -icase" or "") + local mid="" if mc then - mod_query.popen_completions(wedln, (mc.." -complete " + mod_query.popen_completions(wedln, (mc..icase..mid.." -complete " ..string.shell_safe(str))) end end @@ -940,6 +1028,7 @@ function mod_query.do_handle_lua(mplex, env, code) local print_res local function collect_print(...) local tmp="" + local arg={...} local l=#arg for i=1,l do tmp=tmp..tostring(arg[i])..(i==l and "\n" or "\t") @@ -981,7 +1070,7 @@ function mod_query.do_complete_lua(env, str) -- Descend into tables if tocomp and string.len(tocomp)>=1 then - for t in string.gfind(tocomp, "([^.:]*)[.:]") do + for t in string.gmatch(tocomp, "([^.:]*)[.:]") do metas=false if string.len(t)==0 then comptab=env; @@ -1069,9 +1158,15 @@ end --DOC -- This query can be used to create a query of a defined menu. -function mod_query.query_menu(mplex, themenu, prompt) - local _sub=mplex:current() - local menu=ioncore.evalmenu(themenu, {mplex, _sub}) +function mod_query.query_menu(mplex, sub, themenu, prompt) + if type(sub)=="string" then + -- Backwards compat. shift + prompt=themenu + themenu=sub + sub=nil + end + + local menu=ioncore.evalmenu(themenu, mplex, sub) local menuname=(type(themenu)=="string" and themenu or "?") if not menu then @@ -1086,19 +1181,25 @@ function mod_query.query_menu(mplex, themenu, prompt) end local function xform_name(n, is_submenu) - return (string.lower(string.gsub(n, "[-%s]+", "-")) - ..(is_submenu and "/" or "")) + return string.lower(string.gsub(n, "[-/%s]+", "-")) end local function xform_menu(t, m, p) for _, v in ipairs(m) do if v.name then local is_submenu=v.submenu_fn - local n=p..xform_name(v.name, is_submenu) - while t[n] do + local n=p..xform_name(v.name) + + while t[n] or t[n..'/'] do n=n.."'" end + + if is_submenu then + n=n..'/' + end + t[n]=v + if is_submenu and not v.noautoexpand then local sm=v.submenu_fn() if sm then @@ -1116,13 +1217,7 @@ function mod_query.query_menu(mplex, themenu, prompt) local ntab=xform_menu({}, menu, "") local function complete(str) - local results={} - for s, e in pairs(ntab) do - if string.find(s, str, 1, true) then - table.insert(results, s) - end - end - return results + return mod_query.complete_keys(ntab, str, true, true) end local function handle(mplex, str) @@ -1188,14 +1283,16 @@ function mod_query.show_tree(mplex, reg, max_depth) indent, reg:xid()) end - if (not max_depth or max_depth > d) and reg.managed_list then - local mgd=reg:managed_list() - if #mgd > 0 then - s=s .. "\n" .. indent .. "---" - for k, v in pairs(mgd) do - s=s .. "\n" .. get_info(v, indent, d+1) - end - end + if (not max_depth or max_depth > d) and reg.managed_i then + local first=true + reg:managed_i(function(sub) + if first then + s=s .. "\n" .. indent .. "---" + first=false + end + s=s .. "\n" .. get_info(sub, indent, d+1) + return true + end) end return s