]> git.decadent.org.uk Git - ion3.git/blobdiff - mod_query/mod_query.lua
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / mod_query / mod_query.lua
index 0f51738edceb6c7dde1058f5354a01cdf09cb18a..b5bdd71ada47e108a6ae080ca9469bb281fd1301 100644 (file)
@@ -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-2007.
 -- 
--- 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,27 @@ 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
+    local ok=mplex:managed_i(function(r) 
+                                 return not obj_is(r, "WEdln") 
+                             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 +187,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 +226,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={}
 
@@ -325,6 +322,85 @@ function mod_query.popen_completions(cp, cmd, fn, reshnd)
 end
 
 
+local function mk_completion_test(str, sub_ok, casei_ok)
+    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
+    
+    local casei=(casei_ok and mod_query.get().caseicompl)
+    
+    if not casei 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 +416,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 +447,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 +456,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 +476,44 @@ 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, 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 +522,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 +576,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")
@@ -766,6 +848,8 @@ end
 
 
 mod_query.known_hosts={}
+mod_query.hostnicks={}
+mod_query.ssh_completions={}
 
 
 function mod_query.get_known_hosts(mplex)
@@ -789,8 +873,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 +882,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
@@ -837,21 +919,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, false)
     
     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 +930,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 +968,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
@@ -1069,9 +1141,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 +1164,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 +1200,8 @@ 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
+        -- casei_ok false, because everything is already in lower case
+        return mod_query.complete_keys(ntab, str, true, false)
     end
     
     local function handle(mplex, str)
@@ -1188,14 +1267,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