--
-- 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.
--
-- 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
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
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)
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={}
--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}
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
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
end
end
+
function mod_query.attachclient_handler(frame, str)
local cwin=ioncore.lookup_clientwin(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
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,
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,
--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")
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)
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={}
-- \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
mod_query.known_hosts={}
+mod_query.hostnicks={}
+mod_query.ssh_completions={}
function mod_query.get_known_hosts(mplex)
end
-mod_query.hostnicks={}
-
function mod_query.get_hostnicks(mplex)
mod_query.hostnicks={}
local f
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
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
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
return res
end
-mod_query.ssh_completions={}
--DOC
-- This query asks for a host to connect to with SSH.
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
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")
-- 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;
--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
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
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)
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