如何在 Emacs 缓冲区中同时打开多个 URL?

Posted

技术标签:

【中文标题】如何在 Emacs 缓冲区中同时打开多个 URL?【英文标题】:How to open multiple URLs at the same time in an Emacs buffer? 【发布时间】:2020-05-05 19:39:04 【问题描述】:

我将 Emacs 编辑器与 org-mode 和 evil-mode 一起用于文本处理和文档。通常有几个不同的网站 URL 属于一个主题。

示例:我有一个关于如何安装 Emacs 的文本 sn-p:

*** install emacs

emacs - I want to try org-mode. What's the shortest path from zero to typing? - Stack Overflow
https://***.com/questions/4940680/i-want-to-try-org-mode-whats-the-shortest-path-from-zero-to-typing

Index of /gnu/emacs/windows/emacs-26
http://ftp.gnu.org/gnu/emacs/windows/emacs-26/emacs-26.3-x86_64.zip

Installation target:
file://C:\Lupo_Pensuite\MyApps\emacs

How to
file://C:\Lupo_Pensuite\MyDocs\howto.txt

是否可以选择区域并在我的默认网络浏览器中打开所有 URL? Windows资源管理器正在打开文件链接?文本文件是用关联的编辑器打开的吗?

甚至更好:emacs 知道上午文本 sn-p 实际上是一个 org-mode 章节。而且无论光标在该章中的哪个位置,诸如 M-x open-all-links-in-chapter 之类的东西都是...打开当前章节中提到的所有链接。

Prio 1:emacs/org-mode/evil-mode 中是否已经存在类似的东西?

Prio 2:有没有你知道的 elisp 函数可以实现这个用例?

环境:Windows 10下的Cygwin,emacs 26.3,org-mode 9.1.9

【问题讨论】:

这对我来说听起来是个相当糟糕的主意:如果在这些 URL 中有一些视频(比如每个 1GB)会发生什么 - 你有足够的内存来处理它们吗?您的浏览器和/或您的网络能否在猛攻中幸存下来?您的机器会开始交换并变得如此缓慢以至于唯一要做的就是循环电源吗?一些值得深思的东西......如果你仍然想这样做,我认为没有任何内置的东西;有人可能已经在某个地方编写了一个函数来做到这一点(我不知道);编写一个函数来做到这一点相当容易。但你真的想要吗? 嗨@NickD,首先:非常感谢您的调查!现在回答你的问题。我当然想拥有这个。在 99% 的情况下,这些链接不是视频,而是指向网页的链接。和本地链接。只是文档。 是的,我担心的是最后 1% :-) 我会发布部分答案(获取链接列表,但我会让你担心实际发送到浏览器应该很简单)今天晚些时候 - 没有时间ATM。 这实际上是一个完整的答案(但仅适用于 http[s] 类型的链接)。 【参考方案1】:

警告:不加思索地使用,以下可能让您的机器瘫痪。我会在最后添加一些更具体的警告,但要小心!

下面代码的基本思想是解析Org模式文件的缓冲区,以得到缓冲区的解析树:这是由org-element-parse-buffer完成的。然后,我们可以使用org-element-map 遍历解析树并选择only 类型为link 的节点,同时对每个节点应用一个函数。我们应用的函数get-link 遍历链接节点的内容,提取类型和路径并返回这两者的列表。到目前为止,它是这样的:

(defun get-link (x)
  (let* ((link (cadr x))
         (type (plist-get link :type))
         (path (plist-get link :path)))
   (if (or (string= type "http") (string= type "https"))
     (list type path))))

(defun visit-all-http-links ()
  (interactive)
  (let* ((parse-tree (org-element-parse-buffer))
         (links (org-element-map parse-tree 'link #'get-link)))
    links))

请注意,我只保留 httphttps 链接:您可能需要添加额外的类型。

这对于获得您想要的东西已经大有帮助。其实如果你用上面两个函数加载文件,你可以在下面的示例Org模式文件上试一试:

* foo
** foo 1
http://www.google.com
https://redhat.com
* bar
** bar 2
[[https://gnome.org][Gnome]] is a FLOSS project. So is Fedora: https://fedoraproject.org.


* Code
#+begin_src emacs-lisp :results value verbatim :wrap example
(visit-all-http-links)
#+end_src

#+RESULTS:
#+begin_example
(("http" "//www.google.com") ("https" "//redhat.com") ("https" "//gnome.org") ("https" "//fedoraproject.com"))
#+end_example

并使用C-c C-c 评估源块,您会得到显示的结果。

现在我们需要做的就是将结果列表中的每个 (TYPE PATH) 对转换为真实的 URL,然后访问它 - 这是代码的最终版本:


(defun get-link (x)
  "Assuming x is a LINK node in an Org mode parse tree,
   return a list consisting of its type (e.g. \"http\")
   and its path."
  (let* ((link (cadr x))
         (type (plist-get link :type))
         (path (plist-get link :path)))
   (if (or (string= type "http") (string= type "https"))
     (list type path))))

(defun format-url (x)
  "Take a (TYPE PATH) list and return a proper URL. Note
   the following works for http- and https-type links, but
   might need modification for other types."
  (format "%s:%s" (nth 0 x) (nth 1 x)))

(defun visit-all-http-links ()
  (interactive)
  (let* ((parse-tree (org-element-parse-buffer))
         (links (org-element-map parse-tree 'link #'get-link)))
    (mapcar #'browse-url (mapcar #'format-url links))))

我们添加一个函数format-url 来执行此操作:("http" "//example.com") --> "http://example.com" 并将其映射到链接列表,生成一个新的 URL 列表。然后我们将函数browse-url(由emacs提供)映射到结果列表中,然后我们观察浏览器将它们全部打开。

警告:

如果文件中有成百上千个链接,那么您将在浏览器中创建成百上千个选项卡。你确定你的机器可以承受吗?

如果您的链接指向大对象,则会给您的系统带来另一种内存压力。你确定你的机器可以承受吗?

如果您的 Org 模式缓冲区很大,那么 org-element-parse-buffer 可能需要 LONG 时间来处理它。而且,虽然有缓存机制,但是因为bug,默认是没有开启的,所以每次执行函数都会从头开始解析缓冲区AGAIN

每次执行函数时,都会在浏览器中打开选项卡。

针对 cme​​ts 中的问题进行编辑:

Q1:“visit-all-http-links 打开文件中的所有 URL。我最初的问题是,是否可以只打开当前 org-mode 章节中找到的 URL。”

A1:如果您保证该区域在语法上是正确的 Org 模式(例如标题及其内容的集合),则只做一个区域有点困难,但可能。您只需将该区域写入临时缓冲区,然后执行我在临时缓冲区而不是原始缓冲区上所做的操作。 这是使用问题 2 中的visit-url 函数修改后的代码:

(defun visit-all-http-links-in-region (beg end)
  (interactive "r")
  (let ((s (buffer-substring beg end)))
    (with-temp-buffer
      (set-buffer (current-buffer))
      (insert s)
      (let* ((parse-tree (org-element-parse-buffer))
             (links (org-element-map parse-tree 'link #'get-link)))
        (mapcar #'visit-url (mapcar #'format-url links))))))

(defun visit-all-http-links ()
  (interactive)
  (visit-all-http-links-in-region (point-min) (point-max)))

非常轻微的测试

Q2:“每次我使用您的示例 URL 执行您的函数时,都会以不同的顺序打开 URL - 是否可以按照在 org 文件中找到的顺序打开 URL?”

A2:链接是按照它们在文件中出现的顺序确定性地找到的。但是当你调用browse-url 的那一刻,所有的赌注都没有了,因为 URL 现在属于浏览器,它会尝试在单独的选项卡中打开它接收到的每个 URL,并使用单独的线程 - 在换句话说,异步。您可以尝试在通话之间引入延迟,但不能保证:

(defun visit-url(url)
   (browse-url)
   (sit-for 1 t))

然后在visit-all-urls 中使用visit-url 而不是browse-url

【讨论】:

哇!这是工作!我通过添加类型 file:// 稍微增强了您的代码 - 现在即使这样也可以正常工作。我已阅读您的警告。感谢那!问题: (1) visit-all-http-links 打开文件中的all URL。我最初的问题是,是否可以打开 current org-mode chapter 中找到的 URL。当只是简单地打开文件中的 all URL 时,不需要解析 org-mode 树。 第二个问题:每次我使用您的示例 URL 执行您的函数时,都会以不同的顺序打开这些 URL。有时 google 是第一个,有时是 fedora 等等 - 是否可以按照 org 文件中的顺序打开 URL?在您的示例中,这将是:首先是 google,然后是 redhat,然后是 gnome,最后是 fedora。 获取所有链接的最简单方法是解析树(假设有人 else 编写了解析器:幸运的是,情况就是这样这里 - org-element 是官方的 Org 模式解析器)。否则,您必须自己进行(通常是半途而废)解析(通常最终会尝试编写正则表达式以匹配感兴趣的事物,然后将其无限复杂化以尝试摆脱误报/负数): 尝试像我上面所做的那样在两行代码中做到这一点。当然,org-element 是 6K 行代码,但它已经编写和调试过了。【参考方案2】:

事实证明,org-mode 已经内置了这个!

今天我在浏览 org-mode 的文档,想知道 究竟 C-c C-o 是如何工作的。该组合键调用 emacs org-mode 函数“org-open-at-point”。 org-open-at-point 正在打开光标所在的 URL(在 emacs 中为:point)。

现在,如果在标题上按下C-c C-o,则该标题下的所有 URL都会打开!这正是我从一开始就要求的。非常感谢 NickD,您的建设性贡献!

这里是原始帮助文本:

When point is on a headline, display a list of every link in the entry, so it is possible to pick one, or all, of them.

【讨论】:

以上是关于如何在 Emacs 缓冲区中同时打开多个 URL?的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止 emacs dired 模式打开这么多缓冲区?

在 git 中切换分支时,如何让 Emacs 恢复所有未更改的缓冲区?

如何在emacs中进行缓冲区拆分后切换焦点?

Emacs shell 模式在缓冲区中打开文件

如何在 emacs 中创建一个空文件?

在 Emacs 中 C-x 2/3 时如何立即跳转到另一个窗口