Subject: bug#69098: [PATCH] Add new command 'browse-url-here'

From: Philip Kaludercic <[email-masked]>
Date: Wed, 14 Feb 2024 17:11:35 +0000

Felician Nemeth <[email-masked]> writes:

> Philip Kaludercic <[email-masked]> writes:
>
>> The idea is to have a command that can be used to open a file or
>> directory using something outside Emacs.
>>
>> I find this useful, but it might be that there are edge-cases that I am
>> not considering that don't pop up on my system.
>
> Shouldn't it do something when the buffer is modified?

A warning is generated by `browse-url-of-file'.

>> +(defun browse-url-here (arg)
>
> Since browse-url-of-buffer does something similar, the name could be
> more descriptive.   Maybe browse-url-of-buffer-external?  

I like that name, used it.

>> +  "Open current file or directory with external tools.
>> +With prefix argument ARG, open the current `default-directory' instead
>> +of the buffer of the current file."
>
> "the file of the current buffer."

Done, thanks.

>> +  (interactive "P")
>> +  (let ((browse-url-default-handlers '()))
>
> This is strange.  There are other ways to use an external browser-kind.
> Maybe you could use browse-url-secondary-browser-function, which is
> "usually an external browser".

The thing is if `browse-url-default-handlers' includes

  (browse-url--non-html-file-url-p . browse-url-emacs)

then this catches all the requests and the file is always opened in
Emacs.  In this patch I try to only filter out these entries that would
open the file in Emacs:

>From 58cc4a8a3fc8d5ae87c1a443d02715d65c776675 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <[email-masked]>
Date: Tue, 13 Feb 2024 11:08:09 +0100
Subject: [PATCH] Add new command 'browse-url-of-buffer-external'

* lisp/net/browse-url.el (browse-url-of-buffer-external): Add it.
* etc/NEWS: Document it.
---
 etc/NEWS                           |   4 +
 etc/package-autosuggest.eld        |  11 +++
 lisp/net/browse-url.el             |  20 +++++
 lisp/textmodes/markdown-ts-mode.el | 135 +++++++++++++++++++++++++++++
 4 files changed, 170 insertions(+)
 create mode 100644 etc/package-autosuggest.eld
 create mode 100644 lisp/textmodes/markdown-ts-mode.el

diff --git a/etc/NEWS b/etc/NEWS
index a2b5e0d1a9a..b7c9fc9aa81 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -324,6 +324,10 @@ Previously, it was set to t but this broke remote file name detection.
 ** Multi-character key echo now ends with a suggestion to use Help.
 Customize 'echo-keystrokes-help' to nil to prevent that.
 
+** New command 'browse-url-of-buffer-external'.
+This command opens the current file or directory using an external tool,
+as defined by 'browse-url-secondary-browser-function'.
+
 
 * Editing Changes in Emacs 30.1
 
diff --git a/etc/package-autosuggest.eld b/etc/package-autosuggest.eld
new file mode 100644
index 00000000000..87c7e382026
--- /dev/null
+++ b/etc/package-autosuggest.eld
@@ -0,0 +1,11 @@
+((sml-mode auto-mode-alist "\\.sml\\'")
+ (lua-mode auto-mode-alist "\\.lua\\'" )
+ (ada-mode auto-mode-alist "\\.ada\\'")
+ ;; ...
+ ;;
+ ;; this is just for testing, in practice I think this data should
+ ;; be generated and bundled with Emacs, and would ideally be
+ ;; loaded in at compile-time.  When the "archive-contents" file
+ ;; format is updated, we can include more information in there
+ ;; that would be added to this database.
+ )
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index bc2a7db9a8b..e017d679d84 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -943,6 +943,26 @@ browse-url-at-point
 			  browse-url-new-window-flag))
       (error "No URL found"))))
 
+(defun browse-url-of-buffer-external (arg)
+  "Open current file or directory with external tools.
+With prefix argument ARG, open the current `default-directory' instead
+of the file of the current buffer."
+  (interactive "P")
+  (let ((browse-url-browser-function
+         browse-url-secondary-browser-function)
+        (browse-url-default-handlers
+         (seq-filter
+          (lambda (ent) (eq (cdr ent) 'browse-url-emacs))
+          browse-url-default-handlers)))
+    (browse-url-of-file
+     (expand-file-name
+      (cond
+       (arg ".")
+       ((derived-mode-p 'dired-mode)
+        (dired-get-filename))
+       (buffer-file-name)
+       ((user-error "No associated file found")))))))
+
 ;;;###autoload
 (defun browse-url-with-browser-kind (kind url &optional arg)
   "Browse URL with a browser of the given browser KIND.
diff --git a/lisp/textmodes/markdown-ts-mode.el b/lisp/textmodes/markdown-ts-mode.el
new file mode 100644
index 00000000000..60321d789eb
--- /dev/null
+++ b/lisp/textmodes/markdown-ts-mode.el
@@ -0,0 +1,135 @@
+;;; markdown-ts-mode.el --- Markdown editing mode -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <[email-masked]>
+;; Keywords: languages
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Major mode for Markdown.  The syntax highlighting has been
+;; implemented using Tree Sitter.
+
+;;; Code:
+
+(eval-when-compile (require 'pcase))
+(require 'treesit)
+
+(defgroup markdown-ts-mode '()
+  "Markdown support."
+  :prefix "markdown-ts-"
+  :group 'text)
+
+(defcustom markdown-ts-mode-executable
+  (seq-find #'executable-find '("cmark" "pandoc" "markdown"))
+  "Name of the executable used to render Markdown.
+It should read the Markdown text via stdin, and render HTML via
+the standard output."
+  :type 'string)
+
+(defconst markdown-ts-mode-font-lock-rules
+  (treesit-font-lock-rules
+   :language 'markdown
+   :feature 'bracket
+   '((["[" "]" "{" "}"]) @font-lock-bracket-face)
+   ;; TODO
+   )
+  "Markdown settings suitable for `treesit-font-lock-settings'.")
+
+(defun markdown-ts-mode-insert-markup (left right)
+  "Generate a new command for inserting markup.
+Insert LEFT and RIGHT into the current buffer.
+Both arguments are strings that are inserted LEFT and RIGHT of
+the active region, or if not present the current point.  This
+function is meant to be invoked by other commands that specialise
+on a specific kind of formatting."
+  (lambda ()
+    (interactive)
+    (if (use-region-p)
+        (save-mark-and-excursion
+          (goto-char (region-end))
+          (insert right)
+          (goto-char (region-beginning))
+          (insert left))
+      (insert left)
+      (save-excursion (insert right)))))
+
+(defalias 'markdown-ts-mode-insert-bold
+  (markdown-ts-mode-insert-markup "**" "**")
+  "Insert bold markup.")
+
+(defalias 'markdown-ts-mode-insert-italics
+  (markdown-ts-mode-insert-markup "_" "_")
+  "Insert italic markup.")
+
+;; (defalias 'markdown-ts-mode-insert-link (uri)
+;;   (markdown-ts-mode-insert-markup
+;;    "[" (format "](%s)" uri))
+;;   "Insert a link to a URI."
+;;   (interactive "MURI: ")
+;;   )
+
+(defun markdown-ts-mode-render ()
+  "Render the current buffer using EWW."
+  (interactive)
+  (unless markdown-ts-mode-executable
+    (user-error "No markdown available"))
+  (let ((bounds (bounds-of-thing-at-point (if (use-region-p)
+                                              'region
+                                            'buffer)))
+        (file (make-temp-file "markdown" nil ".html")))
+    (call-process-region (car bounds) (cdr bounds)
+                         markdown-ts-mode-executable
+                         nil (list :file file))
+    (eww-open-file file)))
+
+(defvar-keymap markdown-ts-mode-map
+  "C-c C-f C-b" #'markdown-ts-mode-insert-bold
+  "C-c C-f C-i" #'markdown-ts-mode-insert-italics
+  ;; "C-c C-l" #'markdown-ts-mode-insert-link
+  "C-c C-c" #'markdown-ts-mode-render)
+
+;;;###autoload
+(define-derived-mode markdown-ts-mode text-mode "MD"
+  "Major mode for editing Markdown files.
+This implementation makes use of Tree Sitter (see the Info
+Node `(elisp) Tree-sitter Major Modes'), and relies on external
+support for rendering.  Using `treesit-install-language-grammar'
+you can install a grammar."
+  (unless (treesit-ready-p 'markdown)
+    (user-error "Tree Sitter support is not available"))
+  (treesit-parser-create 'markdown)
+  (setq-local treesit-font-lock-settings
+              markdown-ts-mode-font-lock-rules
+              treesit-font-lock-feature-list
+              '((constant escape-sequence number property)
+                (bracket delimiter error misc-punctuation)
+                (string type)
+                (comment))
+              comment-start "# ")
+  (treesit-major-mode-setup))
+
+(when (treesit-ready-p 'markdown)
+  (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode)))
+
+(add-to-list
+ 'treesit-language-source-alist
+ '(markdown "https://github.com/ikatyang/tree-sitter-markdown" "v0.7.1"))
+
+;;; markdown-ts-mode.el ends here
+(provide 'markdown-ts-mode)
-- 
2.43.0