@@ -191,6 +191,29 @@ function Attach:get_node(file, cursor)
191191  return  AttachNode .at_cursor (file , cursor )
192192end 
193193
194+ --- Get attachment node pointed at in a window
195+ --- 
196+ --- @param  window ?  integer  |  string window-ID ,  window number or any argument 
197+ ---                                 accepted by `winnr()`; if 0 or nil, use the
198+ ---                                 current window
199+ --- @return  OrgAttachNode 
200+ function  Attach :get_node_by_window (window )
201+   local  winid 
202+   if  not  window  or  window  ==  0  then 
203+     winid  =  vim .api .nvim_get_current_win ()
204+   elseif  type (window ) ==  ' string'  then 
205+     winid  =  vim .fn .win_getid (vim .fn .winnr (window ))
206+   elseif  vim .fn .win_id2win (window ) ~=  0  then 
207+     winid  =  window 
208+   else 
209+     winid  =  vim .fn .win_getid (window )
210+   end 
211+   if  winid  ==  0  then 
212+     error ((' invalid window: %s'  ):format (window ))
213+   end 
214+   return  self .core :get_node_by_winid (winid )
215+ end 
216+ 
194217--- Return the directory associated with the current outline node.
195218--- 
196219--- First check for DIR property, then ID property.
@@ -559,6 +582,141 @@ function Attach:attach_lns(node)
559582  return  self :attach (nil , { method  =  ' lns'  , node  =  node  })
560583end 
561584
585+ --- @class  orgmode.attach.attach_to_other_buffer.Options 
586+ --- @inlinedoc
587+ --- @field  window ? integer  |  string if passed ,  attach to the node pointed at in 
588+ ---                the given window; you can pass a window-ID, window number, or
589+ ---                `winnr()`-style strings, e.g. `#` to use the previously
590+ ---                active window. Pass 0 for the current window. It's an error
591+ ---                if the window doesn't display an org file.
592+ --- @field  ask ? ' always' | ' multiple'  determines what to do if  ` window`  is nil ;
593+ ---             if 'always', collect all nodes displayed in a window and ask the
594+ ---             user to select one. If 'multiple', only ask if more than one
595+ ---             node is displayed. If false or nil, never ask the user; accept
596+ ---             the unambiguous choice or abort.
597+ --- @field  prefer_recent ? ' ask' | ' buffer' | ' window' | boolean if not nil but 
598+ ---                       `window` is nil, and more than one node is displayed,
599+ ---                       and one of them is more preferable than the others,
600+ ---                       this one is used without asking the user.
601+ ---                       Preferred nodes are those displayed in the current
602+ ---                       window's current buffer and alternate buffer, as well
603+ ---                       as the previous window's current buffer. Pass 'buffer'
604+ ---                       to prefer the alternate buffer over the previous
605+ ---                       window. Pass 'window' for the same vice versa. Pass
606+ ---                       'ask' to ask the user in case of conflict. Pass 'true'
607+ ---                       to prefer only an unambiguous recent node over
608+ ---                       non-recent ones.
609+ --- @field  include_hidden ? boolean If not nil ,  include not only displayed nodes , 
610+ ---                        but also those in hidden buffers; for those, the node
611+ ---                        pointed at by the `"` mark (position when last
612+ ---                        exiting the buffer) is chosen.
613+ --- @field  visit_dir ? boolean if not nil ,  open the relevant attachment directory 
614+ ---                           after attaching the file.
615+ --- @field  method ? ' cp'  |  ' mv'  |  ' ln'  |  ' lns'  The attachment method ,  same values 
616+ ---                as in `org_attach_method`.
617+ 
618+ --- @param  file_or_files  string  |  string[] 
619+ --- @param  opts ?  orgmode.attach.attach_to_other_buffer.Options 
620+ --- @return  string | nil attachment_name 
621+ function  Attach :attach_to_other_buffer (file_or_files , opts )
622+   local  files  =  utils .ensure_array (file_or_files ) --- @type  string[] 
623+   return  self 
624+     :find_other_node (opts )
625+     :next (function (node )
626+       if  not  node  then 
627+         return  nil 
628+       end 
629+       return  self :attach_many (files , {
630+         node  =  node ,
631+         method  =  opts  and  opts .method ,
632+         visit_dir  =  opts  and  opts .visit_dir ,
633+       })
634+     end )
635+     :wait (MAX_TIMEOUT )
636+ end 
637+ 
638+ --- Helper to `Attach:attach_to_other_buffer`, unfortunately really complicated.
639+ --- @param  opts ?  orgmode.attach.attach_to_other_buffer.Options 
640+ --- @return  OrgPromise<OrgAttachNode  |  nil> 
641+ function  Attach :find_other_node (opts )
642+   local  window  =  opts  and  opts .window 
643+   local  ask  =  opts  and  opts .ask 
644+   local  prefer_recent  =  opts  and  opts .prefer_recent 
645+   local  include_hidden  =  opts  and  opts .include_hidden  or  false 
646+   if  window  then 
647+     return  Promise .resolve (self :get_node_by_window (window ))
648+   end 
649+   if  prefer_recent  then 
650+     local  ok , node  =  pcall (self .core .get_current_node , self .core )
651+     if  ok  then 
652+       return  Promise .resolve (node )
653+     end 
654+     local  altbuf_nodes , altwin_node 
655+     if  prefer_recent  ==  ' buffer'  then 
656+       altbuf_nodes  =  self .core :get_single_node_by_buffer (vim .fn .bufnr (' #'  ))
657+       if  altbuf_nodes  then 
658+         return  Promise .resolve (altbuf_nodes )
659+       end 
660+       ok , altwin_node  =  pcall (self .get_node_by_window , self , ' #'  )
661+       if  ok  then 
662+         return  Promise .resolve (altwin_node )
663+       end 
664+     elseif  prefer_recent  ==  ' window'  then 
665+       ok , altwin_node  =  pcall (self .get_node_by_window , self , ' #'  )
666+       if  ok  then 
667+         return  Promise .resolve (altwin_node )
668+       end 
669+       altbuf_nodes  =  self .core :get_single_node_by_buffer (vim .fn .bufnr (' #'  ))
670+       if  altbuf_nodes  then 
671+         return  Promise .resolve (altbuf_nodes )
672+       end 
673+     else 
674+       local  altbuf  =  vim .fn .bufnr (' #'  )
675+       local  altwin  =  vim .fn .win_getid (vim .fn .winnr (' #'  ))
676+       --  altwin falls back to current window if previous window doesn't exist;
677+       --  that's fine, we've handled it earlier.
678+       ok , altwin_node  =  pcall (self .core .get_node_by_winid , self .core , altwin )
679+       altwin_node  =  ok  and  altwin_node  or  nil 
680+       altbuf_nodes  =  self .core :get_nodes_by_buffer (altbuf )
681+       if  altwin_node  and  (# altbuf_nodes  ==  0  or  vim .api .nvim_win_get_buf (altwin ) ==  altbuf ) then 
682+         return  Promise .resolve (altwin_node )
683+       end 
684+       if  # altbuf_nodes  ==  1  and  not  altwin_node  then 
685+         return  Promise .resolve (altbuf_nodes [1 ])
686+       end 
687+       if  prefer_recent  ==  ' ask'  then 
688+         local  candidates  =  altbuf_nodes 
689+         if  altwin_node  then 
690+           table.insert (candidates , 1 , altwin_node )
691+         end 
692+         return  ui .select_node (candidates )
693+       end 
694+       --  More than one possible attachment location and not asking; fall back
695+       --  to regular behavior.
696+     end 
697+   end 
698+   local  candidates  =  self .core :list_current_nodes ({ include_hidden  =  include_hidden  })
699+   if  # candidates  ==  0  then 
700+     return  Promise .reject (' nowhere to attach to'  )
701+   end 
702+   if  ask  ==  ' always'  then 
703+     return  ui .select_node (candidates )
704+   end 
705+   if  ask  ==  ' multiple'  then 
706+     if  # candidates  ==  1  then 
707+       return  Promise .resolve (candidates [1 ])
708+     end 
709+     return  ui .select_node (candidates )
710+   end 
711+   if  ask  then 
712+     return  Promise .reject ((' invalid value for ask: %s'  ):format (ask ))
713+   end 
714+   if  # candidates  ==  1  then 
715+     return  Promise .resolve (candidates [1 ])
716+   end 
717+   return  Promise .reject (' more than one possible attachment location'  )
718+ end 
719+ 
562720--- Open the attachments directory via `vim.ui.open()`.
563721--- 
564722--- @param  attach_dir ?  string the directory to open 
0 commit comments