Compare commits
6 commits
0743dd1dbc
...
a351eeadfa
Author | SHA1 | Date | |
---|---|---|---|
|
a351eeadfa | ||
|
27eb55b154 | ||
|
f1e282d713 | ||
|
a4ce781544 | ||
|
d598ec1e46 | ||
|
cf79016484 |
7 changed files with 122 additions and 486 deletions
414
elisp/ox-rss.el
414
elisp/ox-rss.el
|
@ -1,414 +0,0 @@
|
||||||
;;; ox-rss.el --- RSS 2.0 Back-End for Org Export Engine
|
|
||||||
|
|
||||||
;; Copyright (C) 2013-2015 Bastien Guerry
|
|
||||||
|
|
||||||
;; Author: Bastien Guerry <bzg@gnu.org>
|
|
||||||
;; Keywords: org, wp, blog, feed, rss
|
|
||||||
|
|
||||||
;; This file is not yet part of GNU Emacs.
|
|
||||||
|
|
||||||
;; This program 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.
|
|
||||||
|
|
||||||
;; This program 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This library implements a RSS 2.0 back-end for Org exporter, based on
|
|
||||||
;; the `html' back-end.
|
|
||||||
;;
|
|
||||||
;; It requires Emacs 24.1 at least.
|
|
||||||
;;
|
|
||||||
;; It provides two commands for export, depending on the desired output:
|
|
||||||
;; `org-rss-export-as-rss' (temporary buffer) and `org-rss-export-to-rss'
|
|
||||||
;; (as a ".xml" file).
|
|
||||||
;;
|
|
||||||
;; This backend understands two new option keywords:
|
|
||||||
;;
|
|
||||||
;; #+RSS_EXTENSION: xml
|
|
||||||
;; #+RSS_IMAGE_URL: http://myblog.org/mypicture.jpg
|
|
||||||
;;
|
|
||||||
;; It uses #+HTML_LINK_HOME: to set the base url of the feed.
|
|
||||||
;;
|
|
||||||
;; Exporting an Org file to RSS modifies each top-level entry by adding a
|
|
||||||
;; PUBDATE property. If `org-rss-use-entry-url-as-guid', it will also add
|
|
||||||
;; an ID property, later used as the guid for the feed's item.
|
|
||||||
;;
|
|
||||||
;; The top-level headline is used as the title of each RSS item unless
|
|
||||||
;; an RSS_TITLE property is set on the headline.
|
|
||||||
;;
|
|
||||||
;; You typically want to use it within a publishing project like this:
|
|
||||||
;;
|
|
||||||
;; (add-to-list
|
|
||||||
;; 'org-publish-project-alist
|
|
||||||
;; '("homepage_rss"
|
|
||||||
;; :base-directory "~/myhomepage/"
|
|
||||||
;; :base-extension "org"
|
|
||||||
;; :rss-image-url "http://lumiere.ens.fr/~guerry/images/faces/15.png"
|
|
||||||
;; :html-link-home "http://lumiere.ens.fr/~guerry/"
|
|
||||||
;; :html-link-use-abs-url t
|
|
||||||
;; :rss-extension "xml"
|
|
||||||
;; :publishing-directory "/home/guerry/public_html/"
|
|
||||||
;; :publishing-function (org-rss-publish-to-rss)
|
|
||||||
;; :section-numbers nil
|
|
||||||
;; :exclude ".*" ;; To exclude all files...
|
|
||||||
;; :include ("index.org") ;; ... except index.org.
|
|
||||||
;; :table-of-contents nil))
|
|
||||||
;;
|
|
||||||
;; ... then rsync /home/guerry/public_html/ with your server.
|
|
||||||
;;
|
|
||||||
;; By default, the permalink for a blog entry points to the headline.
|
|
||||||
;; You can specify a different one by using the :RSS_PERMALINK:
|
|
||||||
;; property within an entry.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'ox-html)
|
|
||||||
(declare-function url-encode-url "url-util" (url))
|
|
||||||
|
|
||||||
;;; Variables and options
|
|
||||||
|
|
||||||
(defgroup org-export-rss nil
|
|
||||||
"Options specific to RSS export back-end."
|
|
||||||
:tag "Org RSS"
|
|
||||||
:group 'org-export
|
|
||||||
:version "24.4"
|
|
||||||
:package-version '(Org . "8.0"))
|
|
||||||
|
|
||||||
(defcustom org-rss-image-url "http://orgmode.org/img/org-mode-unicorn-logo.png"
|
|
||||||
"The URL of the an image for the RSS feed."
|
|
||||||
:group 'org-export-rss
|
|
||||||
:type 'string)
|
|
||||||
|
|
||||||
(defcustom org-rss-extension "xml"
|
|
||||||
"File extension for the RSS 2.0 feed."
|
|
||||||
:group 'org-export-rss
|
|
||||||
:type 'string)
|
|
||||||
|
|
||||||
(defcustom org-rss-categories 'from-tags
|
|
||||||
"Where to extract items category information from.
|
|
||||||
The default is to extract categories from the tags of the
|
|
||||||
headlines. When set to another value, extract the category
|
|
||||||
from the :CATEGORY: property of the entry."
|
|
||||||
:group 'org-export-rss
|
|
||||||
:type '(choice
|
|
||||||
(const :tag "From tags" from-tags)
|
|
||||||
(const :tag "From the category property" from-category)))
|
|
||||||
|
|
||||||
(defcustom org-rss-use-entry-url-as-guid t
|
|
||||||
"Use the URL for the <guid> metatag?
|
|
||||||
When nil, Org will create ids using `org-icalendar-create-uid'."
|
|
||||||
:group 'org-export-rss
|
|
||||||
:type 'boolean)
|
|
||||||
|
|
||||||
;;; Define backend
|
|
||||||
|
|
||||||
(org-export-define-derived-backend 'rss 'html
|
|
||||||
:menu-entry
|
|
||||||
'(?r "Export to RSS"
|
|
||||||
((?R "As RSS buffer"
|
|
||||||
(lambda (a s v b) (org-rss-export-as-rss a s v)))
|
|
||||||
(?r "As RSS file" (lambda (a s v b) (org-rss-export-to-rss a s v)))
|
|
||||||
(?o "As RSS file and open"
|
|
||||||
(lambda (a s v b)
|
|
||||||
(if a (org-rss-export-to-rss t s v)
|
|
||||||
(org-open-file (org-rss-export-to-rss nil s v)))))))
|
|
||||||
:options-alist
|
|
||||||
'((:description "DESCRIPTION" nil nil newline)
|
|
||||||
(:keywords "KEYWORDS" nil nil space)
|
|
||||||
(:with-toc nil nil nil) ;; Never include HTML's toc
|
|
||||||
(:rss-extension "RSS_EXTENSION" nil org-rss-extension)
|
|
||||||
(:rss-image-url "RSS_IMAGE_URL" nil org-rss-image-url)
|
|
||||||
(:rss-categories nil nil org-rss-categories))
|
|
||||||
:filters-alist '((:filter-final-output . org-rss-final-function))
|
|
||||||
:translate-alist '((headline . org-rss-headline)
|
|
||||||
(comment . (lambda (&rest args) ""))
|
|
||||||
(comment-block . (lambda (&rest args) ""))
|
|
||||||
(timestamp . (lambda (&rest args) ""))
|
|
||||||
(plain-text . org-rss-plain-text)
|
|
||||||
(section . org-rss-section)
|
|
||||||
(template . org-rss-template)))
|
|
||||||
|
|
||||||
;;; Export functions
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun org-rss-export-as-rss (&optional async subtreep visible-only)
|
|
||||||
"Export current buffer to a RSS buffer.
|
|
||||||
|
|
||||||
If narrowing is active in the current buffer, only export its
|
|
||||||
narrowed part.
|
|
||||||
|
|
||||||
If a region is active, export that region.
|
|
||||||
|
|
||||||
A non-nil optional argument ASYNC means the process should happen
|
|
||||||
asynchronously. The resulting buffer should be accessible
|
|
||||||
through the `org-export-stack' interface.
|
|
||||||
|
|
||||||
When optional argument SUBTREEP is non-nil, export the sub-tree
|
|
||||||
at point, extracting information from the headline properties
|
|
||||||
first.
|
|
||||||
|
|
||||||
When optional argument VISIBLE-ONLY is non-nil, don't export
|
|
||||||
contents of hidden elements.
|
|
||||||
|
|
||||||
Export is done in a buffer named \"*Org RSS Export*\", which will
|
|
||||||
be displayed when `org-export-show-temporary-export-buffer' is
|
|
||||||
non-nil."
|
|
||||||
(interactive)
|
|
||||||
(let ((file (buffer-file-name (buffer-base-buffer))))
|
|
||||||
(org-icalendar-create-uid file 'warn-user)
|
|
||||||
(org-rss-add-pubdate-property))
|
|
||||||
(org-export-to-buffer 'rss "*Org RSS Export*"
|
|
||||||
async subtreep visible-only nil nil (lambda () (text-mode))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun org-rss-export-to-rss (&optional async subtreep visible-only)
|
|
||||||
"Export current buffer to a RSS file.
|
|
||||||
|
|
||||||
If narrowing is active in the current buffer, only export its
|
|
||||||
narrowed part.
|
|
||||||
|
|
||||||
If a region is active, export that region.
|
|
||||||
|
|
||||||
A non-nil optional argument ASYNC means the process should happen
|
|
||||||
asynchronously. The resulting file should be accessible through
|
|
||||||
the `org-export-stack' interface.
|
|
||||||
|
|
||||||
When optional argument SUBTREEP is non-nil, export the sub-tree
|
|
||||||
at point, extracting information from the headline properties
|
|
||||||
first.
|
|
||||||
|
|
||||||
When optional argument VISIBLE-ONLY is non-nil, don't export
|
|
||||||
contents of hidden elements.
|
|
||||||
|
|
||||||
Return output file's name."
|
|
||||||
(interactive)
|
|
||||||
(let ((file (buffer-file-name (buffer-base-buffer))))
|
|
||||||
(org-icalendar-create-uid file 'warn-user)
|
|
||||||
(org-rss-add-pubdate-property))
|
|
||||||
(let ((outfile (org-export-output-file-name
|
|
||||||
(concat "." org-rss-extension) subtreep)))
|
|
||||||
(org-export-to-file 'rss outfile async subtreep visible-only)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun org-rss-publish-to-rss (plist filename pub-dir)
|
|
||||||
"Publish an org file to RSS.
|
|
||||||
|
|
||||||
FILENAME is the filename of the Org file to be published. PLIST
|
|
||||||
is the property list for the given project. PUB-DIR is the
|
|
||||||
publishing directory.
|
|
||||||
|
|
||||||
Return output file name."
|
|
||||||
(let ((bf (get-file-buffer filename)))
|
|
||||||
(if bf
|
|
||||||
(with-current-buffer bf
|
|
||||||
(org-icalendar-create-uid filename 'warn-user)
|
|
||||||
(org-rss-add-pubdate-property)
|
|
||||||
(write-file filename))
|
|
||||||
(find-file filename)
|
|
||||||
(org-icalendar-create-uid filename 'warn-user)
|
|
||||||
(org-rss-add-pubdate-property)
|
|
||||||
(write-file filename) (kill-buffer)))
|
|
||||||
(org-publish-org-to
|
|
||||||
'rss filename (concat "." org-rss-extension) plist pub-dir))
|
|
||||||
|
|
||||||
;;; Main transcoding functions
|
|
||||||
|
|
||||||
(defun org-rss-headline (headline contents info)
|
|
||||||
"Transcode HEADLINE element into RSS format.
|
|
||||||
CONTENTS is the headline contents. INFO is a plist used as a
|
|
||||||
communication channel."
|
|
||||||
(unless (or (org-element-property :footnote-section-p headline)
|
|
||||||
;; Only consider first-level headlines
|
|
||||||
(> (org-export-get-relative-level headline info) 1))
|
|
||||||
(let* ((author (and (plist-get info :with-author)
|
|
||||||
(let ((auth (plist-get info :author)))
|
|
||||||
(and auth (org-export-data auth info)))))
|
|
||||||
(htmlext (plist-get info :html-extension))
|
|
||||||
(hl-number (org-export-get-headline-number headline info))
|
|
||||||
(hl-home (file-name-as-directory (plist-get info :html-link-home)))
|
|
||||||
(hl-pdir (plist-get info :publishing-directory))
|
|
||||||
(hl-perm (org-element-property :RSS_PERMALINK headline))
|
|
||||||
(anchor (org-export-get-reference headline info))
|
|
||||||
(category (org-rss-plain-text
|
|
||||||
(or (org-element-property :CATEGORY headline) "") info))
|
|
||||||
(pubdate0 (org-element-property :PUBDATE headline))
|
|
||||||
(pubdate (let ((system-time-locale "C"))
|
|
||||||
(if pubdate0
|
|
||||||
(format-time-string
|
|
||||||
"%a, %d %b %Y %H:%M:%S %z"
|
|
||||||
(org-time-string-to-time pubdate0)))))
|
|
||||||
(title (or (org-element-property :RSS_TITLE headline)
|
|
||||||
(replace-regexp-in-string
|
|
||||||
org-bracket-link-regexp
|
|
||||||
(lambda (m) (or (match-string 3 m)
|
|
||||||
(match-string 1 m)))
|
|
||||||
(org-element-property :raw-value headline))))
|
|
||||||
(publink
|
|
||||||
(or (and hl-perm (concat (or hl-home hl-pdir) hl-perm))
|
|
||||||
(concat
|
|
||||||
(or hl-home hl-pdir)
|
|
||||||
(file-name-nondirectory
|
|
||||||
(file-name-sans-extension
|
|
||||||
(plist-get info :input-file))) "." htmlext "#" anchor)))
|
|
||||||
(guid (if org-rss-use-entry-url-as-guid
|
|
||||||
publink
|
|
||||||
(org-rss-plain-text
|
|
||||||
(or (org-element-property :ID headline)
|
|
||||||
(org-element-property :CUSTOM_ID headline)
|
|
||||||
publink)
|
|
||||||
info))))
|
|
||||||
(if (not pubdate0) "" ;; Skip entries with no PUBDATE prop
|
|
||||||
(format
|
|
||||||
(concat
|
|
||||||
"<item>\n"
|
|
||||||
"<title>%s</title>\n"
|
|
||||||
"<link>%s</link>\n"
|
|
||||||
"<author>%s</author>\n"
|
|
||||||
"<guid isPermaLink=\"false\">%s</guid>\n"
|
|
||||||
"<pubDate>%s</pubDate>\n"
|
|
||||||
(org-rss-build-categories headline info) "\n"
|
|
||||||
"<description><![CDATA[%s]]></description>\n"
|
|
||||||
"</item>\n")
|
|
||||||
title publink author guid pubdate contents)))))
|
|
||||||
|
|
||||||
(defun org-rss-build-categories (headline info)
|
|
||||||
"Build categories for the RSS item."
|
|
||||||
(if (eq (plist-get info :rss-categories) 'from-tags)
|
|
||||||
(mapconcat
|
|
||||||
(lambda (c) (format "<category><![CDATA[%s]]></category>" c))
|
|
||||||
(org-element-property :tags headline)
|
|
||||||
"\n")
|
|
||||||
(let ((c (org-element-property :CATEGORY headline)))
|
|
||||||
(format "<category><![CDATA[%s]]></category>" c))))
|
|
||||||
|
|
||||||
(defun org-rss-template (contents info)
|
|
||||||
"Return complete document string after RSS conversion.
|
|
||||||
CONTENTS is the transcoded contents string. INFO is a plist used
|
|
||||||
as a communication channel."
|
|
||||||
(concat
|
|
||||||
(format "<?xml version=\"1.0\" encoding=\"%s\"?>"
|
|
||||||
(symbol-name org-html-coding-system))
|
|
||||||
"\n<rss version=\"2.0\"
|
|
||||||
xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"
|
|
||||||
xmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"
|
|
||||||
xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
|
|
||||||
xmlns:atom=\"http://www.w3.org/2005/Atom\"
|
|
||||||
xmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"
|
|
||||||
xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"
|
|
||||||
xmlns:georss=\"http://www.georss.org/georss\"
|
|
||||||
xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"
|
|
||||||
xmlns:media=\"http://search.yahoo.com/mrss/\">"
|
|
||||||
"<channel>"
|
|
||||||
(org-rss-build-channel-info info) "\n"
|
|
||||||
contents
|
|
||||||
"</channel>\n"
|
|
||||||
"</rss>"))
|
|
||||||
|
|
||||||
(defun org-rss-build-channel-info (info)
|
|
||||||
"Build the RSS channel information."
|
|
||||||
(let* ((system-time-locale "C")
|
|
||||||
(title (plist-get info :title))
|
|
||||||
(email (org-export-data (plist-get info :email) info))
|
|
||||||
(author (and (plist-get info :with-author)
|
|
||||||
(let ((auth (plist-get info :author)))
|
|
||||||
(and auth (org-export-data auth info)))))
|
|
||||||
(date (format-time-string "%a, %d %b %Y %H:%M:%S %z")) ;; RFC 882
|
|
||||||
(description (org-export-data (plist-get info :description) info))
|
|
||||||
(lang (plist-get info :language))
|
|
||||||
(keywords (plist-get info :keywords))
|
|
||||||
(rssext (plist-get info :rss-extension))
|
|
||||||
(blogurl (or (plist-get info :html-link-home)
|
|
||||||
(plist-get info :publishing-directory)))
|
|
||||||
(image (url-encode-url (plist-get info :rss-image-url)))
|
|
||||||
(ifile (plist-get info :input-file))
|
|
||||||
(publink
|
|
||||||
(concat (file-name-as-directory blogurl)
|
|
||||||
(file-name-nondirectory
|
|
||||||
(file-name-sans-extension ifile))
|
|
||||||
"." rssext)))
|
|
||||||
(format
|
|
||||||
"\n<title>%s</title>
|
|
||||||
<atom:link href=\"%s\" rel=\"self\" type=\"application/rss+xml\" />
|
|
||||||
<link>%s</link>
|
|
||||||
<description><![CDATA[%s]]></description>
|
|
||||||
<language>%s</language>
|
|
||||||
<pubDate>%s</pubDate>
|
|
||||||
<lastBuildDate>%s</lastBuildDate>
|
|
||||||
<generator>%s</generator>
|
|
||||||
<webMaster>%s (%s)</webMaster>
|
|
||||||
<image>
|
|
||||||
<url>%s</url>
|
|
||||||
<title>%s</title>
|
|
||||||
<link>%s</link>
|
|
||||||
</image>
|
|
||||||
"
|
|
||||||
title publink blogurl description lang date date
|
|
||||||
(concat (format "Emacs %d.%d"
|
|
||||||
emacs-major-version
|
|
||||||
emacs-minor-version)
|
|
||||||
" Org-mode " (org-version))
|
|
||||||
email author image title blogurl)))
|
|
||||||
|
|
||||||
(defun org-rss-section (section contents info)
|
|
||||||
"Transcode SECTION element into RSS format.
|
|
||||||
CONTENTS is the section contents. INFO is a plist used as
|
|
||||||
a communication channel."
|
|
||||||
contents)
|
|
||||||
|
|
||||||
(defun org-rss-timestamp (timestamp contents info)
|
|
||||||
"Transcode a TIMESTAMP object from Org to RSS.
|
|
||||||
CONTENTS is nil. INFO is a plist holding contextual
|
|
||||||
information."
|
|
||||||
(org-html-encode-plain-text
|
|
||||||
(org-timestamp-translate timestamp)))
|
|
||||||
|
|
||||||
(defun org-rss-plain-text (contents info)
|
|
||||||
"Convert plain text into RSS encoded text."
|
|
||||||
(let (output)
|
|
||||||
(setq output (org-html-encode-plain-text contents)
|
|
||||||
output (org-export-activate-smart-quotes
|
|
||||||
output :html info))))
|
|
||||||
|
|
||||||
;;; Filters
|
|
||||||
|
|
||||||
(defun org-rss-final-function (contents backend info)
|
|
||||||
"Prettify the RSS output."
|
|
||||||
(with-temp-buffer
|
|
||||||
(xml-mode)
|
|
||||||
(insert contents)
|
|
||||||
(indent-region (point-min) (point-max))
|
|
||||||
(buffer-substring-no-properties (point-min) (point-max))))
|
|
||||||
|
|
||||||
;;; Miscellaneous
|
|
||||||
|
|
||||||
(defun org-rss-add-pubdate-property ()
|
|
||||||
"Set the PUBDATE property for top-level headlines."
|
|
||||||
(let (msg)
|
|
||||||
(org-map-entries
|
|
||||||
(lambda ()
|
|
||||||
(let* ((entry (org-element-at-point))
|
|
||||||
(level (org-element-property :level entry)))
|
|
||||||
(when (= level 1)
|
|
||||||
(unless (org-entry-get (point) "PUBDATE")
|
|
||||||
(setq msg t)
|
|
||||||
(org-set-property
|
|
||||||
"PUBDATE" (format-time-string
|
|
||||||
(cdr org-time-stamp-formats)))))))
|
|
||||||
nil nil 'comment 'archive)
|
|
||||||
(when msg
|
|
||||||
(message "Property PUBDATE added to top-level entries in %s"
|
|
||||||
(buffer-file-name))
|
|
||||||
(sit-for 2))))
|
|
||||||
|
|
||||||
(provide 'ox-rss)
|
|
||||||
|
|
||||||
;;; ox-rss.el ends here
|
|
|
@ -446,7 +446,11 @@ These interactive functions scroll the “notes” in the other window in anothe
|
||||||
(call-interactively 'org-find-file))
|
(call-interactively 'org-find-file))
|
||||||
(setq ha-slide-presentation (buffer-name))
|
(setq ha-slide-presentation (buffer-name))
|
||||||
(when initial-heading
|
(when initial-heading
|
||||||
(imenu initial-heading))
|
(goto-char (point-min))
|
||||||
|
(re-search-forward (rx bol
|
||||||
|
(one-or-more "*")
|
||||||
|
(one-or-more space)
|
||||||
|
(literal initial-heading))))
|
||||||
(cond
|
(cond
|
||||||
((fboundp #'dslide-deck-forward) (call-interactively 'dslide-deck-start))
|
((fboundp #'dslide-deck-forward) (call-interactively 'dslide-deck-start))
|
||||||
((fboundp #'org-present-next) (call-interactively 'org-present))
|
((fboundp #'org-present-next) (call-interactively 'org-present))
|
||||||
|
@ -510,7 +514,7 @@ To make the contents of the expression easier to write, the =define-ha-demo= as
|
||||||
|
|
||||||
Probably best to explain this in an example:
|
Probably best to explain this in an example:
|
||||||
|
|
||||||
\(define-demo demo1
|
\(define-ha-demo demo1
|
||||||
\(:buffer \"demonstrations.py\") \(message \"In a buffer\"\)
|
\(:buffer \"demonstrations.py\") \(message \"In a buffer\"\)
|
||||||
\(:mode 'dired-mode\) \(message \"In a dired\"\)
|
\(:mode 'dired-mode\) \(message \"In a dired\"\)
|
||||||
\(:heading \"Raven Civilizations\"\) \(message \"In an org file\"\)\)
|
\(:heading \"Raven Civilizations\"\) \(message \"In an org file\"\)\)
|
||||||
|
|
|
@ -27,13 +27,26 @@ A literate programming file for publishing my website using org.
|
||||||
* Introduction
|
* Introduction
|
||||||
While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and use the standard =org-publish= feature.
|
While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and use the standard =org-publish= feature.
|
||||||
|
|
||||||
|
The RSS needs UUIDs:
|
||||||
|
|
||||||
|
#+BEGIN_SRC emacs-lisp results silent
|
||||||
|
(use-package uuidgen
|
||||||
|
:straight (:host github :repo "emacsmirror/uuidgen"))
|
||||||
|
|
||||||
|
(defun org-icalendar-create-uid (&rest ignored)
|
||||||
|
"Returns a UUID."
|
||||||
|
(uuidgen-1))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
While the following packages come with Emacs, they aren't necessarily loaded:
|
While the following packages come with Emacs, they aren't necessarily loaded:
|
||||||
|
|
||||||
#+begin_src emacs-lisp :results silent
|
#+begin_src emacs-lisp :results silent
|
||||||
|
(use-package ox-rss
|
||||||
|
:straight (:host github :repo "emacsmirror/ox-rss"))
|
||||||
|
|
||||||
(use-package org
|
(use-package org
|
||||||
:config
|
:config
|
||||||
(require 'ox-html)
|
(require 'ox-html)
|
||||||
(require 'ox-rss)
|
|
||||||
(require 'ox-publish))
|
(require 'ox-publish))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
|
30
ha-org.org
30
ha-org.org
|
@ -3,7 +3,7 @@
|
||||||
#+date: 2020-09-18
|
#+date: 2020-09-18
|
||||||
#+tags: emacs org
|
#+tags: emacs org
|
||||||
#+startup: inlineimages
|
#+startup: inlineimages
|
||||||
#+lastmod: [2025-03-11 Tue]
|
#+lastmod: [2025-04-17 Thu]
|
||||||
|
|
||||||
A literate programming file for configuring org-mode and those files.
|
A literate programming file for configuring org-mode and those files.
|
||||||
|
|
||||||
|
@ -771,11 +771,14 @@ Splitting out HTML snippets is often a way that I can transfer org-formatted con
|
||||||
(:link (@ :rel "stylesheet"
|
(:link (@ :rel "stylesheet"
|
||||||
:type "text/css"
|
:type "text/css"
|
||||||
:href "https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,300;0,600;1,300;1,600&display=swap"))
|
:href "https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,300;0,600;1,300;1,600&display=swap"))
|
||||||
|
(:link (@ :rel "stylesheet"
|
||||||
|
:type "text/css"
|
||||||
|
:href "https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap"))
|
||||||
(:style ,(string-join '(
|
(:style ,(string-join '(
|
||||||
"body { font-family: 'Literata', sans-serif; color: #333; }"
|
"body { font-family: 'Literata', sans-serif; color: #333; }"
|
||||||
"h1,h2,h3,h4,h5 { font-family: 'Overpass', sans-serif; color: #333; }"
|
"h1,h2,h3,h4,h5 { font-family: 'Overpass', sans-serif; color: #333; }"
|
||||||
"code { color: steelblue }"
|
"code { font-family: 'Source Code Pro'; color: steelblue }"
|
||||||
"pre { background-color: #eee; border-color: #aaa; }"
|
"pre { font-family: 'Source Code Pro'; background-color: #eee; border-color: #aaa; }"
|
||||||
"a { text-decoration-style: dotted }"
|
"a { text-decoration-style: dotted }"
|
||||||
"@media (prefers-color-scheme: dark) {"
|
"@media (prefers-color-scheme: dark) {"
|
||||||
" body { background-color: #1d1f21; color: white; }"
|
" body { background-color: #1d1f21; color: white; }"
|
||||||
|
@ -871,7 +874,6 @@ Since this auto-correction needs to happen in /insert/ mode, I have bound a few
|
||||||
Of course I need a thesaurus, and I'm installing [[https://github.com/SavchenkoValeriy/emacs-powerthesaurus][powerthesaurus]]:
|
Of course I need a thesaurus, and I'm installing [[https://github.com/SavchenkoValeriy/emacs-powerthesaurus][powerthesaurus]]:
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package powerthesaurus
|
(use-package powerthesaurus
|
||||||
:bind ("s-t" . powerthesaurus-lookup-dwim)
|
|
||||||
:config
|
:config
|
||||||
(ha-leader
|
(ha-leader
|
||||||
"s t" '(:ignore t :which-key "thesaurus")
|
"s t" '(:ignore t :which-key "thesaurus")
|
||||||
|
@ -882,14 +884,19 @@ Of course I need a thesaurus, and I'm installing [[https://github.com/SavchenkoV
|
||||||
"s t u" '("usages" . powerthesaurus-lookup-sentences-dwim)))
|
"s t u" '("usages" . powerthesaurus-lookup-sentences-dwim)))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
The key-bindings, keystrokes, and key-connections work well with ~M-T~ (notice the Shift), but to jump to specifics, we use a leader.
|
The key-bindings, keystrokes, and key-connections work well with a hyper-command, but to jump to specifics, I use a leader.
|
||||||
|
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(use-package powerthesaurus
|
||||||
|
:bind ("s-t" . powerthesaurus-lookup-dwim))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
*** Definitions
|
*** Definitions
|
||||||
Since the /definitions/ do not work, so let's use the [[https://github.com/abo-abo/define-word][define-word]] project:
|
Since the /definitions/ do not work, so let's use the [[https://github.com/abo-abo/define-word][define-word]] project:
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package define-word
|
(use-package define-word
|
||||||
:bind ("s-d" . define-word-at-point)
|
|
||||||
:config
|
:config
|
||||||
(ha-leader :keymaps 'text-mode-map
|
(ha-leader :keymaps 'text-mode-map
|
||||||
"s d" '(:ignore t :which-key "dictionary")
|
"s d" '(:ignore t :which-key "dictionary")
|
||||||
|
@ -897,6 +904,13 @@ Since the /definitions/ do not work, so let's use the [[https://github.com/abo-a
|
||||||
"s d a" '("define any word" . define-word)))
|
"s d a" '("define any word" . define-word)))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
And what about a binding when I’m in insert mode:
|
||||||
|
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(use-package define-word
|
||||||
|
:bind ("s-d" . define-word-at-point))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
After my enamoring of Noah Webster’s 1913 dictionary (originally due to reading [[https://janusworx.com/blog/thank-god-for-noah/][this essay]] by Mario Jason Braganza who referred to James Somers’ original [[https://jsomers.net/blog/dictionary][2014 blog entry]]), I easily followed the instructions from [[https://github.com/ponychicken/WebsterParser][WebsterParser]], a Github project, with the dictionary:
|
After my enamoring of Noah Webster’s 1913 dictionary (originally due to reading [[https://janusworx.com/blog/thank-god-for-noah/][this essay]] by Mario Jason Braganza who referred to James Somers’ original [[https://jsomers.net/blog/dictionary][2014 blog entry]]), I easily followed the instructions from [[https://github.com/ponychicken/WebsterParser][WebsterParser]], a Github project, with the dictionary:
|
||||||
1. Download [[https://github.com/ponychicken/WebsterParser/releases/latest/download/websters-1913.dictionary.zip][the dictionary]] file.
|
1. Download [[https://github.com/ponychicken/WebsterParser/releases/latest/download/websters-1913.dictionary.zip][the dictionary]] file.
|
||||||
2. Unzip the archive … have a *Finder* window open to the =.dictionary= file.
|
2. Unzip the archive … have a *Finder* window open to the =.dictionary= file.
|
||||||
|
@ -935,9 +949,7 @@ The [[https://github.com/bnbeckwith/writegood-mode][writegood-mode]] is effectiv
|
||||||
#+end_src
|
#+end_src
|
||||||
And it reports obnoxious messages.
|
And it reports obnoxious messages.
|
||||||
|
|
||||||
Hrm::hook ((org-mode . writegood-mode)
|
Note: Instead of hooking the =writegood-mode= to Org files, I will hook it to =flycheck= instead.
|
||||||
(gfm-mode . writegood-mode)
|
|
||||||
(markdown-mode) . writegood-mode)
|
|
||||||
|
|
||||||
We install the =write-good= NPM:
|
We install the =write-good= NPM:
|
||||||
#+begin_src shell
|
#+begin_src shell
|
||||||
|
|
|
@ -347,8 +347,7 @@ Now that the [[file:ha-programming.org::*Language Server Protocol (LSP) Integrat
|
||||||
("Server"
|
("Server"
|
||||||
(("l" python-lsp/body "LSP..."))
|
(("l" python-lsp/body "LSP..."))
|
||||||
"Edit"
|
"Edit"
|
||||||
(("r" lsp-rename "Rename")
|
(("=" lsp-format-region "Format"))
|
||||||
("=" lsp-format-region "Format"))
|
|
||||||
"Navigate"
|
"Navigate"
|
||||||
(("A" lsp-workspace-folders-add "Add Folder")
|
(("A" lsp-workspace-folders-add "Add Folder")
|
||||||
("R" lsp-workspace-folders-remove "Remove Folder"))
|
("R" lsp-workspace-folders-remove "Remove Folder"))
|
||||||
|
|
119
pud.org
119
pud.org
|
@ -2,7 +2,7 @@
|
||||||
#+author: Howard X. Abrams
|
#+author: Howard X. Abrams
|
||||||
#+date: 2025-01-18
|
#+date: 2025-01-18
|
||||||
#+filetags: emacs hamacs
|
#+filetags: emacs hamacs
|
||||||
#+lastmod: [2025-03-21 Fri]
|
#+lastmod: [2025-06-08 Sun]
|
||||||
|
|
||||||
A literate programming file for a Comint-based MUD client.
|
A literate programming file for a Comint-based MUD client.
|
||||||
|
|
||||||
|
@ -30,17 +30,31 @@ A literate programming file for a Comint-based MUD client.
|
||||||
|
|
||||||
This project is a simple MUD client for Emacs, based on COM-INT MUD client I learn about on Mickey Petersen’s [[https://www.masteringemacs.org/article/comint-writing-command-interpreter][essay on Comint]].
|
This project is a simple MUD client for Emacs, based on COM-INT MUD client I learn about on Mickey Petersen’s [[https://www.masteringemacs.org/article/comint-writing-command-interpreter][essay on Comint]].
|
||||||
|
|
||||||
This uses =telnet= (at the moment) for the connection, so you will need to install that first. On Mac, this would be:
|
This uses eithr =ssh= or good ol’ =telnet= for the connection. Surprised that one can still install in on a Mac, like:
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
brew install telent
|
brew install telnet
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
And use a similar command on Linux.
|
Use your favorite way to install Emacs’ packages, for instance, with Emacs 30, once can install it, and customize some settings in one go with =use-package=:
|
||||||
|
|
||||||
|
#+BEGIN_SRC emacs-lisp :tangle no :eval no
|
||||||
|
(use-package pud
|
||||||
|
:vc (:url "https://howardabrams.com/git/howard/pud")
|
||||||
|
:custom
|
||||||
|
(pud-worlds
|
||||||
|
'(["Remote Moss-n-Puddles" 'ssh "howardabrams.com" 4000 "george"]
|
||||||
|
; ↑ No password? Should be in .authinfo.gpg
|
||||||
|
|
||||||
|
["Local Root" 'telnet "localhost" 4000 "darol" "some-pass"]
|
||||||
|
; ↑ This has the password in your custom settings.
|
||||||
|
|
||||||
|
; ↓ Password from authinfo, special connection string:
|
||||||
|
["Local User" 'telnet "localhost" 4000 "rick" nil "login %s %s"])))
|
||||||
|
#+END_SRC
|
||||||
** Customization
|
** Customization
|
||||||
|
|
||||||
You may want to customize your connections to more worlds.
|
You will want to customize your connections to the connections, as this program defaults to *Moss ‘n Puddles*, my own MUD which I invite you to join.
|
||||||
The default connects to *Moss ‘n Puddles*, my own MUD which I invite you to join.
|
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defgroup pud nil
|
(defgroup pud nil
|
||||||
|
@ -88,27 +102,12 @@ The default connects to *Moss ‘n Puddles*, my own MUD which I invite you to jo
|
||||||
:group 'pud)
|
:group 'pud)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
For instance:
|
For instance, you could set up a call to =setopt= or customize the =pud-worlds= variable, as in:
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp :tangle no :eval no
|
#+BEGIN_SRC emacs-lisp :tangle no :eval no
|
||||||
(use-package pud
|
(setopt pud-worlds
|
||||||
:custom
|
'(["Moss-n-Puddles" ssh "howardabrams.com" 4004 "howard" "" "connect %s %s"]
|
||||||
(pud-worlds
|
["Moss-n-Puddles" ssh "howardabrams.com" 4004 "rick" "" "connect %s %s"]
|
||||||
'(["Remote Moss-n-Puddles" 'ssh "howardabrams.com" 4000 "bobby"]
|
|
||||||
; ↑ No password? Should be in .authinfo.gpg
|
|
||||||
|
|
||||||
["Local Root" 'telnet "localhost" 4000 "suzy" "some-pass"]
|
|
||||||
; ↑ This has the password in your custom settings.
|
|
||||||
|
|
||||||
; ↓ Password from authinfo, special connection string:
|
|
||||||
["Local User" 'telnet "localhost" 4000 "rick" nil "login %s %s"])))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
Hidden:
|
|
||||||
#+BEGIN_SRC emacs-lisp :tangle no :eval no
|
|
||||||
(setq pud-worlds
|
|
||||||
'(["Moss-n-Puddles" ssh "howardabrams.com" 4004 "howard" "" "\\nconnect %s %s\\n"]
|
|
||||||
["Moss-n-Puddles" ssh "howardabrams.com" 4004 "rick" "" "\\nconnect %s %s\\n"]
|
|
||||||
["Local-Moss" telnet "localhost" 4000 "howard" "" ""]
|
["Local-Moss" telnet "localhost" 4000 "howard" "" ""]
|
||||||
["Local-Moss" telnet "localhost" 4000 "rick" "" ""]))
|
["Local-Moss" telnet "localhost" 4000 "rick" "" ""]))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -129,7 +128,7 @@ Next, open [[file:~/.authinfo.gpg][your authinfo file]], and insert the followin
|
||||||
machine howardabrams.com login [name] port 4000 password [pass]
|
machine howardabrams.com login [name] port 4000 password [pass]
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
Now, let’s play! Type =run-pud=, and optionally select a world. If you get disconnected, re-run it, or even =pud-reconnect=.
|
Now, let’s play! Type =pud-=run= and optionally select a world. If you get disconnected, re-run it, or even =pud-reconnect=.
|
||||||
|
|
||||||
The rest of this file describes the code implementing this project.
|
The rest of this file describes the code implementing this project.
|
||||||
* Code
|
* Code
|
||||||
|
@ -163,7 +162,6 @@ Choosing a world… er, connection using a =completing-read= allowing you to cho
|
||||||
(t (customize-option 'pud-worlds)))))
|
(t (customize-option 'pud-worlds)))))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
The following functions are accessibility functions to the world entry.
|
The following functions are accessibility functions to the world entry.
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
@ -182,7 +180,7 @@ The following functions are accessibility functions to the world entry.
|
||||||
(defun pud-world-creds (world)
|
(defun pud-world-creds (world)
|
||||||
"Return the username and password from WORLD.
|
"Return the username and password from WORLD.
|
||||||
Multiple search queries for the .authinfo file."
|
Multiple search queries for the .authinfo file."
|
||||||
(seq-let (label host port user) world
|
(seq-let (label conn-type host port user) world
|
||||||
(if-let ((auth-results (first (auth-source-search
|
(if-let ((auth-results (first (auth-source-search
|
||||||
:host host
|
:host host
|
||||||
:port port
|
:port port
|
||||||
|
@ -200,8 +198,8 @@ And some basic functions I should expand.
|
||||||
(ert-deftest pud-world-name-test ()
|
(ert-deftest pud-world-name-test ()
|
||||||
(should (string-equal (pud-world-name "foobar") "foobar"))
|
(should (string-equal (pud-world-name "foobar") "foobar"))
|
||||||
(should (string-equal (pud-world-name ["foobar" "localhost" "4000"]) "foobar"))
|
(should (string-equal (pud-world-name ["foobar" "localhost" "4000"]) "foobar"))
|
||||||
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" nil]) "foobar"))
|
|
||||||
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" ""]) "foobar"))
|
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" ""]) "foobar"))
|
||||||
|
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" nil]) "foobar"))
|
||||||
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" "guest" "guest"]) "guest@foobar")))
|
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" "guest" "guest"]) "guest@foobar")))
|
||||||
|
|
||||||
(ert-deftest pud-world-network-test ()
|
(ert-deftest pud-world-network-test ()
|
||||||
|
@ -221,9 +219,6 @@ And some basic functions I should expand.
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
* Basics
|
* Basics
|
||||||
:LOGBOOK:
|
|
||||||
CLOCK: [2025-03-03 Mon 11:57]--[2025-03-03 Mon 12:10] => 0:13
|
|
||||||
:END:
|
|
||||||
Using Comint, and hoping to have the ANSI colors displayed.
|
Using Comint, and hoping to have the ANSI colors displayed.
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
@ -235,12 +230,12 @@ I’m going to use good ‘ol fashion =telnet= for the connection:
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defcustom pud-telnet-path "telnet"
|
(defcustom pud-telnet-path "telnet"
|
||||||
"Path to the program used by `run-pud' to connect using telnet."
|
"Path to the program used by `pud-run' to connect using telnet."
|
||||||
:type '(string)
|
:type '(string)
|
||||||
:group 'pud)
|
:group 'pud)
|
||||||
|
|
||||||
(defcustom pud-ssh-path "ssh"
|
(defcustom pud-ssh-path "ssh"
|
||||||
"Path to the program used by `run-pud' to connect using ssh."
|
"Path to the program used by `pud-run' to connect using ssh."
|
||||||
:type '(string)
|
:type '(string)
|
||||||
:group 'pud)
|
:group 'pud)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -259,7 +254,6 @@ Command string to use, given a =world= with a connection type:
|
||||||
"Return a command string to pass to the shell.
|
"Return a command string to pass to the shell.
|
||||||
The WORLD is a vector with the hostname, see `pud-worlds'."
|
The WORLD is a vector with the hostname, see `pud-worlds'."
|
||||||
(seq-let (host port) (pud-world-network world)
|
(seq-let (host port) (pud-world-network world)
|
||||||
(message "Dealing with: %s %s %s" host port (aref world 1))
|
|
||||||
(cl-case (aref world 1)
|
(cl-case (aref world 1)
|
||||||
(telnet (append (cons pud-telnet-path pud-cli-arguments)
|
(telnet (append (cons pud-telnet-path pud-cli-arguments)
|
||||||
(list host port)))
|
(list host port)))
|
||||||
|
@ -286,14 +280,14 @@ The empty and currently disused mode map for storing our custom keybindings inhe
|
||||||
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
|
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
|
||||||
(define-key map "\t" 'completion-at-point)
|
(define-key map "\t" 'completion-at-point)
|
||||||
map)
|
map)
|
||||||
"Basic mode map for `run-pud'.")
|
"Basic mode map for `pud-run'.")
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
This holds a regular expression that matches the prompt style for the MUD. Not sure if this is going to work, since MUDs typically don’t have prompts.
|
This holds a regular expression that matches the prompt style for the MUD. Not sure if this is going to work, since MUDs typically don’t have prompts.
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defvar pud-prompt-regexp "" ; "^\\(?:\\[[^@]+@[^@]+\\]\\)"
|
(defvar pud-prompt-regexp "" ; "^\\(?:\\[[^@]+@[^@]+\\]\\)"
|
||||||
"Prompt for `run-pud'.")
|
"Prompt for `pud-run'.")
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
The name of the buffer:
|
The name of the buffer:
|
||||||
|
@ -305,13 +299,14 @@ The name of the buffer:
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
** Run and Connect
|
** Run and Connect
|
||||||
The main entry point to the program is the =run-pud= function:
|
The main entry point to the program is the =pud-run= function:
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defun run-pud (world)
|
(defun pud-run (world)
|
||||||
"Run an inferior instance of `pud-cli' inside Emacs.
|
"Run an inferior instance of `pud-cli' inside Emacs.
|
||||||
The WORLD should be vector containing the following:
|
The WORLD should be vector containing the following:
|
||||||
- label for the world
|
- label for the world
|
||||||
|
- server type, 'ssh or 'telnet
|
||||||
- server hostname
|
- server hostname
|
||||||
- server port
|
- server port
|
||||||
- username (can be overridden)
|
- username (can be overridden)
|
||||||
|
@ -328,7 +323,7 @@ The main entry point to the program is the =run-pud= function:
|
||||||
(apply 'make-comint-in-buffer "Pud" buffer (car pud-cli) nil (cdr pud-cli))
|
(apply 'make-comint-in-buffer "Pud" buffer (car pud-cli) nil (cdr pud-cli))
|
||||||
(pud-mode)
|
(pud-mode)
|
||||||
(visual-line-mode 1)
|
(visual-line-mode 1)
|
||||||
(pud-reconnect world)))
|
(pud-login world)))
|
||||||
;; Regardless, provided we have a valid buffer, we pop to it.
|
;; Regardless, provided we have a valid buffer, we pop to it.
|
||||||
(when buffer
|
(when buffer
|
||||||
(pop-to-buffer buffer))))
|
(pop-to-buffer buffer))))
|
||||||
|
@ -337,29 +332,57 @@ The main entry point to the program is the =run-pud= function:
|
||||||
Connection and/or re-connection:
|
Connection and/or re-connection:
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defun pud-reconnect (world)
|
(defun pud-login (world)
|
||||||
"Collect and send a `connect' sequence to WORLD.
|
"Collect and send a `connect' sequence to WORLD.
|
||||||
Where WORLD is a vector of world information. NOP if the buffer has no
|
|
||||||
connection or no password could be found."
|
Where WORLD is a vector of world information. NOP if the buffer
|
||||||
|
has no connection or no password could be found."
|
||||||
(interactive (list (pud-get-world)))
|
(interactive (list (pud-get-world)))
|
||||||
(when (called-interactively-p)
|
(when (called-interactively-p)
|
||||||
(pop-to-buffer (pud-buffer-name world)))
|
(pop-to-buffer (pud-buffer-name world)))
|
||||||
(sit-for 1)
|
(sit-for 1)
|
||||||
|
|
||||||
(message "Attempting to log in...")
|
(length world)
|
||||||
|
(message "Attempting to log in to %s..." (pud-world-name world))
|
||||||
(seq-let (username password) (pud-world-creds world)
|
(seq-let (username password) (pud-world-creds world)
|
||||||
(let* ((conn-str (if (length> world 5)
|
(let* ((local-conn (when (length> world 6)
|
||||||
(aref world 5)
|
(aref world 6 )))
|
||||||
|
(conn-str (if (and local-conn
|
||||||
|
(not (string-blank-p local-conn)))
|
||||||
|
local-conn
|
||||||
pud-default-connection-string))
|
pud-default-connection-string))
|
||||||
(conn-full (format conn-str username password))
|
(conn-full (format conn-str username password))
|
||||||
(process (get-buffer-process (current-buffer))))
|
(process (get-buffer-process (current-buffer))))
|
||||||
|
|
||||||
(message "proc: %s str: '%s'" process conn-full)
|
|
||||||
(goto-char (point-max))
|
(goto-char (point-max))
|
||||||
(if process
|
(if process
|
||||||
(comint-send-string process conn-full)
|
(comint-send-string process conn-full)
|
||||||
(insert conn-full)))))
|
(insert conn-full)))))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
(setq world (pud-get-world))
|
||||||
|
** Reconnect
|
||||||
|
Force a kill process, and restart.
|
||||||
|
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(defun pud-reconnect (world)
|
||||||
|
"Force stop an inferior instance of `pud-cli'.
|
||||||
|
The WORLD should be vector containing the following:
|
||||||
|
- label for the world
|
||||||
|
- server hostname
|
||||||
|
- server port
|
||||||
|
- username (can be overridden)
|
||||||
|
- password (should be overridden)"
|
||||||
|
(interactive (list (pud-get-world)))
|
||||||
|
(let* ((pud-cli (pud-cli-command world))
|
||||||
|
(buffer (get-buffer-create (pud-buffer-name world)))
|
||||||
|
(proc-alive (comint-check-proc buffer))
|
||||||
|
(process (get-buffer-process buffer)))
|
||||||
|
(when (processp process)
|
||||||
|
(kill-process process))
|
||||||
|
(pud-run world)))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
* Pud Mode
|
* Pud Mode
|
||||||
Note that =comint-process-echoes=, depending on the mode and the circumstances, may result in prompts appearing twice. Setting =comint-process-echoes= to =t= helps with that.
|
Note that =comint-process-echoes=, depending on the mode and the circumstances, may result in prompts appearing twice. Setting =comint-process-echoes= to =t= helps with that.
|
||||||
|
|
||||||
|
@ -370,7 +393,7 @@ Note that =comint-process-echoes=, depending on the mode and the circumstances,
|
||||||
(setq comint-use-prompt-regexp nil))
|
(setq comint-use-prompt-regexp nil))
|
||||||
|
|
||||||
(define-derived-mode pud-mode comint-mode "Pud"
|
(define-derived-mode pud-mode comint-mode "Pud"
|
||||||
"Major mode for `run-pud'.
|
"Major mode for `pud-run'.
|
||||||
|
|
||||||
\\<pud-mode-map>"
|
\\<pud-mode-map>"
|
||||||
;; this sets up the prompt so it matches things like: [foo@bar]
|
;; this sets up the prompt so it matches things like: [foo@bar]
|
||||||
|
|
19
zshell.org
19
zshell.org
|
@ -18,7 +18,7 @@ This creates the following files:
|
||||||
#!/usr/bin/env zsh
|
#!/usr/bin/env zsh
|
||||||
#
|
#
|
||||||
# My complete Zshell configuration. Don't edit this file.
|
# My complete Zshell configuration. Don't edit this file.
|
||||||
# Instead edit: ~/technical/zshell.org and tangle it.
|
# Instead edit: zshell.org and tangle it.
|
||||||
#
|
#
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ This creates the following files:
|
||||||
#!/usr/bin/env zsh
|
#!/usr/bin/env zsh
|
||||||
#
|
#
|
||||||
# Non-interactive, mostly easily settable environment variables. Don't
|
# Non-interactive, mostly easily settable environment variables. Don't
|
||||||
# edit this file. # Instead edit: ~/technical/zshell.org and tangle.
|
# edit this file. Instead edit: zshell.org and tangle.
|
||||||
#
|
#
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
@ -133,9 +133,9 @@ If you type something wrong, Zshell, by default, prompts to see if you wanted to
|
||||||
ENABLE_CORRECTION="true"
|
ENABLE_CORRECTION="true"
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
What about just /fixing it/? For this, we update the [[https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html][ZShell line editor]]:
|
What about just /fixing it/? For this, I thought to update the [[https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html][ZShell line editor]] with something like:
|
||||||
|
|
||||||
#+BEGIN_SRC zsh
|
#+BEGIN_SRC zsh :tangle no
|
||||||
autocorrect() {
|
autocorrect() {
|
||||||
zle .spell-word
|
zle .spell-word
|
||||||
zle .$WIDGET
|
zle .$WIDGET
|
||||||
|
@ -145,12 +145,7 @@ What about just /fixing it/? For this, we update the [[https://zsh.sourceforge.i
|
||||||
zle -N magic-space autocorrect
|
zle -N magic-space autocorrect
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
Bind the ability to auto-correct the word to the left with =Space= or =Enter=:
|
Now you can’t insert a space as it attempts to correct it. Not worth the space savings.
|
||||||
|
|
||||||
#+BEGIN_SRC zsh
|
|
||||||
bindkey ' ' magic-space
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
** Waiting Indication
|
** Waiting Indication
|
||||||
Display red dots whilst waiting for commands to complete.
|
Display red dots whilst waiting for commands to complete.
|
||||||
|
|
||||||
|
@ -374,3 +369,7 @@ To let us know we read the =~/.zshrc= file:
|
||||||
#+options: num:nil toc:t todo:nil tasks:nil tags:nil date:nil
|
#+options: num:nil toc:t todo:nil tasks:nil tags:nil date:nil
|
||||||
#+options: skip:nil author:nil email:nil creator:nil timestamp:nil
|
#+options: skip:nil author:nil email:nil creator:nil timestamp:nil
|
||||||
#+infojs_opt: view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|
#+infojs_opt: view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# eval: (add-hook 'after-save-hook #'org-babel-tangle t t)
|
||||||
|
# End:
|
||||||
|
|
Loading…
Reference in a new issue