manbytesgnu_site

Source files for manbytesgnu.org
git clone git://holbrook.no/manbytesgnu_site.git
Info | Log | Files | Refs

20240517_localmd.rst (9725B)


      1 Support Your Local Viewer
      2 #########################
      3 
      4 :date: 2024-07-07 21:03:40
      5 :category: Offlining
      6 :author: Louis Holbrook
      7 :tags: bash,markdown,pandoc,vimb,w3m,lynx,xdg
      8 :slug: local-markdown
      9 :summary: Bash script to render and spawn a viewer for markdown files
     10 :lang: en
     11 :status: published
     12 
     13 
     14 Markdown is the fast-food of document formats.
     15 
     16 That doesn't change the fact that it's everywhere.
     17 
     18 So much everywhere, in fact, that it's kind of puzzling there is not a dedicated tool around to view it.
     19 
     20 
     21 Pinning down markdown
     22 =====================
     23 
     24 There is no shortage of applications that *can* render markdown. Among the alternatives are free code editors like Atom_ or Geany_, the browser plugin `Markdown Viewer`_ and even a dedicated markdown editor like Marktext_.
     25 
     26 And of course, there exist SaaS offerings such as hackmd_. But seeing as those are not alternatives for offline use, we don't concern ourselves with those here.
     27 
     28 But what is the equivalent of sxiv_ or feh_ for Markdown?
     29 
     30 Honestly, I couldn't find any such thing. If there is, I'd `love to know`_.
     31 
     32 Fortunately, there is a perfectly reasonable workaround.
     33 
     34 
     35 Step by step
     36 ============
     37 
     38 After all, it is in the spirit of \*nixes to use a choice of tools who *does one thing and does it well*.
     39 
     40 So, no matter how bizarre it feels, it may make sense that a lurid format like *Markdown* should be treated in a separate step to produce a more well-established - and less ambiguous - format.
     41 
     42 I asked a related question on Stackexchange_ long ago, and there the ``pandoc`` tool came up as a solution.
     43 
     44 And it turns out it works beautifully in this case aswell.
     45 
     46 Consider the following script:
     47 
     48 .. code-block:: bash
     49 
     50         t=$(mktemp --suffix=.html)
     51         2>&1 echo $t
     52         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
     53         w3m $t
     54 
     55 
     56 Quite simply, we generate a standalone html file in ``tmpfs``, which in turn is read and renderered by a web browser.
     57 
     58 
     59 Browsing browsers
     60 =================
     61 
     62 No appreciation for w3m_, eh? Instead want that fuzzy feel of comforting colors, smooth scroll and fancy fonts?
     63 
     64 I can understand. I used to suffer that affliction, too.
     65 
     66 But nonetheless; it's an important point. For example, what if I wanted to use lynx_ or vimb_ instead? Choosing the browser to view with should definitely be the caller's call.
     67 
     68 Is there a canonical way of doing that in Linux.
     69 
     70 Kind of.
     71 
     72 Let's review a couple of the options.
     73 
     74 
     75 The environmental solution
     76 --------------------------
     77 
     78 Some applications honor the ``$BROWSER`` environment variable. So let's cover for that:
     79 
     80 .. code-block:: bash
     81 
     82         browser=${BROWSER:-w3m}
     83         t=$(mktemp --suffix=.html)
     84         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
     85         $browser $t
     86 
     87 Now, viewing the markdown file ``README.md`` with ``lynx`` is as easy as:
     88 
     89 .. code-block:: console 
     90 
     91         $ BROWSER=$(which lynx) bash wmd.sh README.md
     92 
     93 
     94 The cross solution
     95 ------------------
     96 
     97 So, have you heard about the Cross Desktop Group? [1]_ These are the guys you should be sending a thought of gratitude every time you intuitively look in your ``~/.config`` or ``~/.local`` folder for application data. And if you're a **HUIf** [2]_ coder, chances are you have seen the string ``xdg`` in some function call somewhere.
     98 
     99 These days they go by the name `Free Desktop Group`_, and among other things they have negotiated something particularly useful to us in this particular case.
    100 
    101 To launch programs in a desktop environment in Linux, a `Desktop Entry Specification`_ file format [3]_ has been defined - appropriately suffixed ``.desktop``.
    102 
    103 Take a look at a random ``*.desktop`` file in ``/usr/share/applications`` (your most likely default ``$XDG_DATA_DIRS`` location, where xdg searches for desktop files). Every one will contain an top-level ``Exec=`` entry.
    104 
    105 For example, my ``feh.desktop`` entry has ``Exec=feh --start-at %u`` which means find ``feh`` in ``$PATH`` and execute it with the ``--start-at`` switch and one single *url* as argument:
    106 
    107 .. code-block:: ini
    108 
    109         [Desktop Entry]
    110         Name=Feh
    111         Name[en_US]=feh
    112         GenericName=Image viewer
    113         GenericName[en_US]=Image viewer
    114         Comment=Image viewer and cataloguer
    115         Exec=feh --start-at %u
    116         Terminal=false
    117         Type=Application
    118         Icon=feh
    119         Categories=Graphics;2DGraphics;Viewer;
    120         MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/webp;image/x-bmp;image/x-pcx;image/x-png;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-tga;image/x-xbitmap;image/heic;
    121         NoDisplay=true
    122 
    123 Covering this case in our script:
    124 
    125 .. code-block:: bash
    126 
    127         fallbackbrowsercmd=w3m
    128         browsercmd=
    129         # if browser env exists, then
    130         # try handle it as a desktop entry
    131         if [ ! -z "$BROWSER" ]; then
    132                 # find the xdg paths
    133                 XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share}
    134                 if [ ! -z "$XDG_DATA_HOME" ]; then
    135                         XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS
    136                 fi
    137                 # split on ":" and try dirs one by one
    138                 # use first matching browser desktop entry
    139                 _ifs=$IFS
    140                 IFS=:
    141                 dirs=("$XDG_DATA_DIRS")
    142                 for d in $dirs; do
    143                         s=$BROWSER.desktop
    144                         a=$d/applications/$BROWSER.desktop 
    145                         if [ -f "$a" ]; then
    146                                 browsercmd="gtk-launch $s"
    147                         fi
    148                 done
    149                 IFS=
    150         fi
    151         # if no browser set or could not be found in xdg,
    152         # then try the browser env var as command, or
    153         # ultimately the static fallback
    154         if [ -z "$browsercmd" ]; then
    155                 browsercmd=${BROWSER:-$fallbackbrowsercmd}
    156         fi
    157         t=$(mktemp --suffix=.html)
    158         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
    159         $browsercmd $t
    160 
    161 
    162 The default solution
    163 --------------------
    164 
    165 Yes, there is such a thing as "default web browser" in ``XDG``, too.
    166 
    167 Have a look at ``xdg-settings --list``. On my system, it shows a rather modest output:
    168 
    169 .. code-block:: console
    170 
    171         $ xdg-settings --list
    172         Known properties:
    173                 default-url-scheme-handler    Default handler for URL scheme
    174                 default-web-browser           Default web browser
    175 
    176 And the default web browser is:
    177 
    178 .. code-block:: console
    179 
    180         $ xdg-settings get default-web-browser
    181         brave-browser.desktop 
    182 
    183 Yeah, yeah, yeah. Busted. I use graphical browsers, too.
    184 
    185 Now let's add this to the mix, then.
    186 
    187 .. code-block:: bash
    188 
    189         fallbackbrowsercmd=w3m
    190         browsercmd=
    191         # if browser env var not set, set it with default browser
    192         if [ -z "$BROWSER" ]; then
    193                 BROWSER=$(xdg-settings get default-web-browser)
    194                 BROWSER=${BROWSER%%.*}
    195         fi
    196         if [ ! -z "$BROWSER" ]; then
    197                 XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share}
    198                 if [ ! -z "$XDG_DATA_HOME" ]; then
    199                         XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS
    200                 fi
    201                 _ifs=$IFS
    202                 IFS=:
    203                 dirs=("$XDG_DATA_DIRS")
    204                 for d in $dirs; do
    205                         s=$BROWSER.desktop
    206                         a=$d/applications/$BROWSER.desktop 
    207                         if [ -f "$a" ]; then
    208                                 browsercmd="gtk-launch $s"
    209                         fi
    210                 done
    211                 IFS=
    212         fi
    213         if [ -z "$browsercmd" ]; then
    214                 browsercmd=${BROWSER:-$fallbackbrowsercmd}
    215         fi
    216         t=$(mktemp --suffix=.html)
    217         pandoc -f gfm -t html -M document-css=false --standalone $1 > $t 2> /dev/null
    218         $browsercmd $t
    219 
    220 
    221 Naming the executioner
    222 ======================
    223 
    224 So now we have a markdown viewer. And it can be as lean or as heavy as you want it to be. It's all to the browser you choose.
    225 
    226 To make it accessible, map the script as a command alias in your ``.profile`` or ``.bashrc`` and you have the viewer at your fingertips.
    227 
    228 I call mine ``wmd``:
    229 
    230 
    231 .. code-block:: bash
    232 
    233         alias wmd="bash $HOME/scripts/markdown.sh"
    234 
    235 And voilá:
    236 
    237 .. code-block:: console
    238 
    239         $ export BROWSER=lynx
    240         $ wmd README.md
    241 
    242 ..
    243 
    244                 .. [1] Or X Desktop Group, as they were originally called.
    245 
    246 
    247 ..
    248 
    249                 .. [2] Human User Interface. Don't bother looking - I made it up. As the cause of AI, robots and machines inevitably will become appropriated by the woke intersectionality complex, the terminology is probably going to need such distictions.
    250 
    251 
    252 ..
    253 
    254                 .. [3] Actually the format is ini_. The standard is rather in the file naming, really.
    255 
    256 .. _Free Desktop Group: https://www.freedesktop.org/
    257 
    258 .. _Desktop Entry Specification: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
    259 
    260 .. _Atom: https://atom-editor.cc/
    261 
    262 .. _Geany: https://plugins.geany.org/markdown.html
    263 
    264 .. _Markdown Viewer: https://chromewebstore.google.com/detail/markdown-viewer/ckkdlimhmcjmikdlpkmbgfkaikojcbjk
    265 
    266 .. _Marktext: https://github.com/marktext/marktext
    267 
    268 .. _Stackexchange: https://tex.stackexchange.com/questions/341899/latex-to-markdown-converter
    269 
    270 .. _w3m: https://w3m.sourceforge.net
    271 
    272 .. _lynx: https://lynx.browser.org
    273 
    274 .. _vimb: https://fanglingsu.github.io/vimb
    275 
    276 .. _sxiv: https://github.com/muennich/sxiv
    277 
    278 .. _feh: https://feh.finalrewind.org
    279 
    280 .. _hackmd: https://hackmd.io
    281 
    282 .. _love to know: https://holbrook.no/msg
    283 
    284 .. _ini: https://en.wikipedia.org/wiki/INI_file