Vexil — Create flags of countries and subdivisions (Emacs package)
Below you find the latest version of (1) the package's README and (2) its main source file.
For the git repository and issue tracker, see the project's page on sr.ht.
For more packages, see Software.
Overview
Vexil can convert:
- from two-letter territory code to territory flag
- from territory flag to two-letter territory code
- from subdivision code to subdivision flag
- from subdivision flag to subdivision code
A territory code can be anything from ISO 3166-1 alpha-2.
Subdivision code can be anything from ISO 3166-2.
A territory is typically a "country", with some exceptions (e.g. EU → 🇪🇺).
Strings return strings; symbols return symbols.
Your system needs a font that can display flag emojis. Then:
- Flags of all territories are usually available.
- As for subdivisions:
- England (🏴), Scotland (🏴), and Wales (🏴) are usually available.
- Beyond that, it'll depend on the font.
- England (🏴), Scotland (🏴), and Wales (🏴) are usually available.
There're no lookup tables: conversion depends solely on the input.
Examples
A few examples:
;;; To flag (vexil-flag "AQ") => "🇦🇶" (vexil-flag 'GL) => '🇬🇱 (vexil-flag 'GB-WLS) => '🏴 ;;; To code (vexil-code '🇨🇭) => 'CH (vexil-code "🇪🇺") => "EU" (vexil-code "🏴") => "GB-ENG"
Here's another — the Org table below is initially half-empty:
Annual revenue from frobnicated flanges exports (in 🪐$, 2042) | | | 🪐$ | Frobnicated flanges exports | |---+----+-----+-----------------------------| | | AQ | 170 | | | | CC | 481 | | | | CH | 293 | | | | GL | 353 | | | | NP | 142 | | | | TK | 218 | | #+tblfm: $1='(vexil-flag $2) #+tblfm: $4='(orgtbl-uc-draw-grid $3 0 500 50)
After a C-c
C-c
on each of the #+tblfm
lines, we get:
🪐$ | Frobnicated flanges exports | ||
---|---|---|---|
🇦🇶 | AQ | 170 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ |
🇨🇨 | CC | 481 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▏ |
🇨🇭 | CH | 293 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▎ |
🇬🇱 | GL | 353 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▎ |
🇳🇵 | NP | 142 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▏ |
🇹🇰 | TK | 218 | ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▊ |
Installation
See my page Software for the most up-to-date instructions on how to download and install any of my Emacs packages.
Having downloaded and installed the package and its dependencies, adapt the configurations below to your init.el
file.
(use-package vexil :demand t)
Alternatively, if you don't have use-package
:
(require 'vexil)
Summary of callables
Here's an overview of this package's callables:
Function | Summary |
---|---|
vexil-flag | Convert OBJ to flag. |
vexil-code | Convert OBJ to code. |
vexil-toggle | Convert OBJ: code ⬄ flag. |
vexil-dwim | Convert code ⬄ flag. Do what I mean: use selection, or ask. |
vexil-region | Convert code ⬄ flag. Use selected region, replacing it. |
vexil-ask | Convert code ⬄ flag. Ask for input and show the result. |
vexil-see-readme | Open vexil's README.org file. |
vexil-see-news | See the News in vexil's README.org file. |
They're described in more detail below.
Functions
Conversions
These functions convert an object OBJ that represents either a territory
or a subdivision.
- If OBJ is a string, return a string.
- If OBJ is a symbol, return a symbol.
OBJ type is auto-detected: it must match a regular expression for either
code or flag, for either territory or subdivision — or it throws an error.
Subdivision codes may be passed in either ISO 3166-2 or CLDR formats.
So, for example, either of these:
- 'GB-SCT
- 'gbsct
would equally return '🏴 when passed to vexil-flag.
vexil-flag (obj)
Convert OBJ to flag.
;;;; Territories ;;;;; From code ;;;;;; Actual (vexil-flag "AQ") => "🇦🇶" (vexil-flag 'GL) => '🇬🇱 (vexil-flag 'EU) => '🇪🇺 (mapcar #'vexil-flag '(AQ CC CH GL NP TK)) => '(🇦🇶 🇨🇨 🇨🇭 🇬🇱 🇳🇵 🇹🇰) ;;;;;; Nonexistent (vexil-flag "FU") => "🇫🇺" (vexil-flag 'FU) => '🇫🇺 ;;;;; From flag ;; Return the flag unmodified if it matches territory flag regexp. ;;;;;; Actual (vexil-flag "🇦🇶") => "🇦🇶" (vexil-flag '🇬🇱) => '🇬🇱 (vexil-flag '🇪🇺) => '🇪🇺 ;;;;;; Nonexistent (vexil-flag "🇫🇺") => "🇫🇺" (vexil-flag '🇫🇺) => '🇫🇺 ;;;;; Error ;; For territories, input must be: ;; - symbol or string ;; - either territory flag or upcased two-letter code (vexil-flag) !!> wrong-number-of-arguments (vexil-flag "") !!> error (vexil-flag 'D) !!> error (vexil-flag "D") !!> error (vexil-flag 'gl) !!> error (vexil-flag "gl") !!> error (vexil-flag "304") !!> error ;;;; Subdivisions ;; ;; For subdivisions, input must be: ;; - symbol or string, and ;; - either: ;; - subdivision flag, or ;; - 2 letters + optional hyphen + 1–3 letter and/or numbers ;; ;; Note that: ;; - England, Scotland, and Wales are available in most emoji fonts. ;; - Availability of any other territory subdivision depends on the font. ;;;;; From code ;;;;;; Canonical ISO 3166-2 format (vexil-flag "GB-ENG") => "🏴" (vexil-flag "GB-SCT") => "🏴" (vexil-flag "GB-WLS") => "🏴" (vexil-flag 'GB-ENG) => '🏴 (vexil-flag 'GB-SCT) => '🏴 (vexil-flag 'GB-WLS) => '🏴 ;;;;;; Canonical CLDR format (vexil-flag "gbeng") => "🏴" (vexil-flag "gbsct") => "🏴" (vexil-flag "gbwls") => "🏴" (vexil-flag 'gbeng) => '🏴 (vexil-flag 'gbsct) => '🏴 (vexil-flag 'gbwls) => '🏴 ;;;;;; Availability of these depends on system fonts ;; (unavailable ones might show up as a waving black flag: 🏴) (vexil-flag "US-CO") => "🏴" (vexil-flag "US-DC") => "🏴" (vexil-flag 'US-NM) => '🏴 (vexil-flag 'US-SC) => '🏴 ;;;;;;; 🇺🇸 subdivisions — flags (--map (->> it symbol-name (concat "US-") vexil-flag read) '( AK AL AR AZ CA CO CT DC DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY)) => '( 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴) ;; (plus those that have their own two-letter territory code, of course) (mapcar #'vexil-flag '(AS GU MP PR UM VI)) => '(🇦🇸 🇬🇺 🇲🇵 🇵🇷 🇺🇲 🇻🇮) ;;;;;;; 🇨🇦 subdivisions — flags (--map (->> it symbol-name (concat "CA-") vexil-flag read) '(AB BC MB NB NL NS NT NU ON PE QC SK YT)) => '(🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴 🏴) ;;;;; From flag ;; Return the flag unmodified if it matches subdivision flag regexp. (vexil-flag '🏴) => '🏴 (vexil-flag "🏴") => "🏴" (vexil-flag '🏴) => '🏴 (vexil-flag "🏴") => "🏴" ;;;;; Error ;; Unmatched regular expressions throw errors (vexil-flag "GB/ENG") !!> error ; slash divisor (vexil-flag "US.NY") !!> error ; dot divisor (vexil-flag "US-A@") !!> error ; non-alphanumeric (vexil-flag "US-QUUX") !!> error ; too large (vexil-flag 'usquux) !!> error ; too large ;; Note that no checks are made as to whether it actually exists, ;; so this nonexistent subdivision of nonexistent country will pass: (vexil-flag "QX-FU") => "🏴"
vexil-code (obj)
Convert OBJ to code.
;;;; Territories ;;;;; From flag ;;;;;; Actual (vexil-code "🇦🇶") => "AQ" (vexil-code '🇬🇱) => 'GL (vexil-code '🇪🇺) => 'EU (mapcar #'vexil-code '(🇦🇶 🇨🇨 🇨🇭 🇬🇱 🇳🇵 🇹🇰)) => '(AQ CC CH GL NP TK) ;;;;;; Nonexistent (vexil-code "🇫🇺") => "FU" (vexil-code '🇫🇺) => 'FU ;;;;; From code ;; Return the code unmodified if it matches territory code regexp. ;;;;;; Actual (vexil-code "AQ") => "AQ" (vexil-code 'GL) => 'GL (vexil-code 'EU) => 'EU ;;;;;; Nonexistent (vexil-code "FU") => "FU" (vexil-code 'FU) => 'FU ;;;;; Error ;; For territories, input must be: ;; - symbol or string ;; - either territory flag or upcased two-letter code (vexil-code) !!> wrong-number-of-arguments (vexil-code "") !!> error (vexil-code 'D) !!> error (vexil-code "D") !!> error (vexil-code 'gl) !!> error (vexil-code "gl") !!> error (vexil-code "304") !!> error ;;;; Subdivisions ;; ;; For subdivisions, input must be: ;; - symbol or string, and ;; - either: ;; - subdivision flag, or ;; - 2 letters + optional hyphen + 1–3 letter and/or numbers ;; ;; Note that: ;; - England, Scotland, and Wales are available in most emoji fonts. ;; - Availability of any other territory subdivision depends on the font. ;;;;; From flag (vexil-code '🏴) => 'GB-ENG (vexil-code "🏴") => "GB-SCT" (vexil-code '🏴) => 'GB-WLS (vexil-code "🏴") => "US-SC" ;;;;; From code ;; Return the code unmodified if it matches subdivision code regexp. ;;;;;; Canonical ISO 3166-2 format (vexil-code "GB-ENG") => "GB-ENG" (vexil-code "GB-SCT") => "GB-SCT" (vexil-code "GB-WLS") => "GB-WLS" (vexil-code 'GB-ENG) => 'GB-ENG (vexil-code 'GB-SCT) => 'GB-SCT (vexil-code 'GB-WLS) => 'GB-WLS (vexil-code "US-CO") => "US-CO" (vexil-code "US-DC") => "US-DC" (vexil-code 'US-NM) => 'US-NM (vexil-code 'US-SC) => 'US-SC ;;;;;; Canonical CLDR format (vexil-code "gbeng") => "gbeng" (vexil-code "gbsct") => "gbsct" (vexil-code "gbwls") => "gbwls" (vexil-code 'gbeng) => 'gbeng (vexil-code 'gbsct) => 'gbsct (vexil-code 'gbwls) => 'gbwls (vexil-code "usco") => "usco" (vexil-code "usdc") => "usdc" (vexil-code 'usnm) => 'usnm (vexil-code 'ussc) => 'ussc ;;;;; Error ;; Unmatched regular expressions throw errors (vexil-code "GB/ENG") !!> error ; slash divisor (vexil-code "US.NY") !!> error ; dot divisor (vexil-code "US-A@") !!> error ; non-alphanumeric (vexil-code "US-QUUX") !!> error ; too large (vexil-code 'usquux) !!> error ; too large ;; Note that no checks are made as to whether it actually exists, ;; so this nonexistent subdivision of nonexistent country will pass: (vexil-code "QX-FU") => "QX-FU"
vexil-toggle (obj)
Convert OBJ: code ⬄ flag.
;;;; Territories ;;;;; From code ;;;;;; Actual (vexil-toggle "AQ") => "🇦🇶" (vexil-toggle 'GL) => '🇬🇱 (vexil-toggle 'EU) => '🇪🇺 (mapcar #'vexil-toggle '(AQ CC CH GL NP TK)) => '(🇦🇶 🇨🇨 🇨🇭 🇬🇱 🇳🇵 🇹🇰) ;;;;;; Nonexistent (vexil-toggle "FU") => "🇫🇺" (vexil-toggle 'FU) => '🇫🇺 ;;;;; From flag ;;;;;; Actual (vexil-toggle "🇦🇶") => "AQ" (vexil-toggle '🇬🇱) => 'GL (vexil-toggle '🇪🇺) => 'EU (mapcar #'vexil-toggle '(🇦🇶 🇨🇨 🇨🇭 🇬🇱 🇳🇵 🇹🇰)) => '(AQ CC CH GL NP TK) ;;;;;; Nonexistent (vexil-toggle "🇫🇺") => "FU" (vexil-toggle '🇫🇺) => 'FU ;;;;; Error ;; For territories, input must be: ;; - symbol or string ;; - either territory flag or upcased two-letter code (vexil-toggle) !!> wrong-number-of-arguments (vexil-toggle "") !!> error (vexil-toggle 'D) !!> error (vexil-toggle "D") !!> error (vexil-toggle 'gl) !!> error (vexil-toggle "gl") !!> error (vexil-toggle "304") !!> error ;;;; Subdivisions ;; ;; For subdivisions, input must be: ;; - symbol or string, and ;; - either: ;; - subdivision flag, or ;; - 2 letters + optional hyphen + 1–3 letter and/or numbers ;; ;; Note that: ;; - England, Scotland, and Wales are available in most emoji fonts. ;; - Availability of any other territory subdivision depends on the font. ;;;;; From code ;;;;;; Canonical ISO 3166-2 format (vexil-toggle "GB-ENG") => "🏴" (vexil-toggle "GB-SCT") => "🏴" (vexil-toggle "GB-WLS") => "🏴" (vexil-toggle 'GB-ENG) => '🏴 (vexil-toggle 'GB-SCT) => '🏴 (vexil-toggle 'GB-WLS) => '🏴 ;;;;;; Canonical CLDR format (vexil-toggle "gbeng") => "🏴" (vexil-toggle "gbsct") => "🏴" (vexil-toggle "gbwls") => "🏴" (vexil-toggle 'gbeng) => '🏴 (vexil-toggle 'gbsct) => '🏴 (vexil-toggle 'gbwls) => '🏴 ;;;;;; Availability of these depends on system fonts ;; (unavailable ones might show up as a waving black flag: 🏴) (vexil-toggle "US-CO") => "🏴" (vexil-toggle "US-DC") => "🏴" (vexil-toggle 'US-NM) => '🏴 (vexil-toggle 'US-SC) => '🏴 ;;;;; From flag (vexil-toggle '🏴) => 'GB-ENG (vexil-toggle "🏴") => "GB-SCT" (vexil-toggle '🏴) => 'GB-WLS (vexil-toggle "🏴") => "US-SC" ;;;;; Error ;; Unmatched regular expressions throw errors (vexil-toggle "GB/ENG") !!> error ; slash divisor (vexil-toggle "US.NY") !!> error ; dot divisor (vexil-toggle "US-A@") !!> error ; non-alphanumeric (vexil-toggle "US-QUUX") !!> error ; too large (vexil-toggle 'usquux) !!> error ; too large ;; Note that no checks are made as to whether it actually exists, ;; so this nonexistent subdivision of nonexistent country will pass: (vexil-toggle "QX-FU") => "🏴"
Commands
Conversions
These work like vexil-toggle, but are interactive.
Input will be either the contents of a selected region, or whatever you
entered in the minibuffer. In the former case, the region will be replaced.
See README
Commands to open vexil's README.org. Optionally, find things in it.
vexil-see-readme (&optional heading narrow)
Open vexil's README.org file.
Search for the file in vexil.el's directory.
If found, open it read-only.
If optional argument HEADING is passed, try to navigate to the
heading after opening it. HEADING should be a string.
If optional argument NARROW is non-nil, narrow to that heading.
This argument has no effect if HEADING is nil or not found.
Disclaimer
This package doesn't take or endorse any position on territorial claims, nor on flag designs, nor on any such disputes.
All this package does is:
- take your input query;
- apply functions to it;
- and serve you the converted output.
The applied functions simply follow the standard ways to produce emoji flags: it takes a string that matches a regular expression for a potentially valid territory code or subdivision code, and then produce another string that could be rendered as a flag by an emoji-capable font.
Flag designs themselves are supplied by your emoji font. So if a flag you wanted is missing or seems inaccurate, and yet you'd like to see that corrected, then consider sending a bug report to the font designers.
FAQ
What's the deal with these 🇿🇿 and 🏴 that I see?
The function vexil-flag doesn't look things up on a table; rather, it calculates the flag.
If the input code doesn't match an existing place, or if you have no fonts to display an existing place's flag, then the output will look like one of these.
There are two cases: territory code and subdivision code.
Territory code
In the first case, the input is a potentially valid two-letter territory code, such as FR
. Then function vexil-flag maps that pair of capital letters to the corresponding pair of regional indicator symbols:
FR → 🇫🇷 FU → 🇫🇺
When the combination produces a pair that corresponds to a valid two-letter emoji flag sequence, and you have an emoji flag–capable font, then the two regional indicators disappear and an emoji flag pops up.
- The first example corresponds to France. If you have the font, you'll see a flag.
- The second example corresponds to a nonexistent territory:
FR → 🇫🇷 FU → 🇫🇺
What you'll see when it's invalid depends on your system and on its available fonts.
Some fonts will leave the two regional indicator symbols as they are; other fonts (notably fonts-noto-color-emoji
) might render any of them as a flag with a question mark.
The former will take the space of four characters; the latter will take only two, as would any other flag.
Subdivision code
In the second case, the input is a potentially valid subdivision code, such as US-SC
. The function vexil-flag
downcases these letters, strips the hyphen, and produces a corresponding emoji tag sequence:
GB-SCT → 🏴 US-SC → 🏴 FU-QUX → 🏴
If that sequence is valid and you have an emoji flag–capable font that supplies flags for the subdivisions of that specific territory, then the sequence disappear and an emoji flag pops up. Above:
- The first example corresponds to Scotland, United Kingdom. If you have an emoji font, you'll probably see this flag, since it's one of only three subdivision flags that are universally supported (the other two being the flags of England and Wales).
- The second example corresponds to South Carolina, United States. Emoji fonts often include flags of US states, so there's good chance you'll see this one.
- The third example corresponds to a nonexistent subdivision code. You're likely to see either a flag with a question mark; or a black waving flag (which is followed by a few thin invisible characters). That's also what you might see when your font doesn't support a flag for an existing subdivision.
Contributing
See my page Software for information about how to contribute to any of my Emacs packages.
See also
Other packages
You can integrate vexil
's examples into Help or Helpful buffers with Democratize, which also offers examples from xht
, dash
, s
, f
, and native Emacs libraries.
You may also be interested in Mondo, with which you can translate into several languages the names of territories and their subdivisions. These translations happen locally — in your machine.
License
This project follows the REUSE Specification (FAQ), which in turn is built upon SPDX.
Therefore, license and copyright information can be found in:
- each file's comment header, or
- an adjacent file of the same name with the additional extension
.license
, or - the
.reuse/dep5
file
The full text of the licenses can be found in the LICENSES
subdirectory.
vexil.el
Structure
;;; vexil.el --- Create flags of countries and subdivisions -*- lexical-binding: t -*- ;;; Commentary: ;;;; For all the details, please do see the README ;;; Code: ;;;; Libraries ;;;; Package metadata ;;;; Customizable variables ;;;; Other variables ;;;;; Regular expressions ;;;; Functions ;;;;; Description macro ;;;;; Conversions ;;;;;; Internal ;;;;;;; Territories ;;;;;;; Subdivisions ;;;; Commands ;;;;; Conversions ;;;;;; Internal ;;;;; See README ;;;;;; Internal ;;;; Wrapping up ;;; vexil.el ends here
Contents
;;; vexil.el --- Create flags of countries and subdivisions -*- lexical-binding: t -*- ;; SPDX-FileCopyrightText: © flandrew <https://flandrew.srht.site/listful> ;;--------------------------------------------------------------------------- ;; Author: flandrew ;; Created: 2025-09-05 ;; Updated: 2025-09-08 ;; Keywords: extensions, lisp ;; Homepage: <https://flandrew.srht.site/listful/software.html> ;;--------------------------------------------------------------------------- ;; Package-Version: 0.2.0 ;; Package-Requires: ((emacs "25.1")) ;;--------------------------------------------------------------------------- ;; SPDX-License-Identifier: GPL-3.0-or-later ;; This file 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 file 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 this file. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; ;; Vexil can convert: ;; - from two-letter territory code to territory flag ;; - from territory flag to two-letter territory code ;; - from subdivision code to subdivision flag ;; - from subdivision flag to subdivision code ;; ;; A territory code can be anything from ISO 3166-1 alpha-2. ;; Subdivision code can be anything from ISO 3166-2. ;; ;; A territory is typically a "country", with some exceptions (e.g. EU → 🇪🇺). ;; ;; Strings return strings; symbols return symbols. ;; ;; Examples: ;; (vexil-flag "AQ") => "🇦🇶" ;; (vexil-flag 'GL) => '🇬🇱 ;; (vexil-flag 'GB-WLS) => '🏴 ;; (vexil-code '🇨🇭) => 'CH ;; (vexil-code "🇪🇺") => "EU" ;; (vexil-code "🏴") => "GB-ENG" ;; ;; Interactive functions are also available. ;; ;; Your system needs a font that can display flag emojis. Then: ;; - Flags of all territories are usually available. ;; - As for subdivisions: ;; - England (🏴), Scotland (🏴), and Wales (🏴) are usually available. ;; - Beyond that, it'll depend on the font. ;; ;; There're no lookup tables: conversion depends solely on the input. ;; ;;;; For all the details, please do see the README ;; ;; Open it easily with: ;; (find-file-read-only "README.org") <--- C-x C-e here¹ ;; ;; or from any buffer: ;; M-x vexil-see-readme ;; ;; or read it online: ;; <https://flandrew.srht.site/listful/sw-emacs-vexil.html> ;; ;; ¹ or the key that ‘eval-last-sexp’ is bound to, if not C-x C-e. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Code: ;;;; Libraries (require 'rx) (require 'pcase) (require 'lisp-mnt) ; ‘lm-summary’, ‘lm-homepage’, ‘lm-version’, ‘lm-header’ ;;;; Package metadata (defvar vexil--name "Vexil") (defvar vexil--dot-el (format "%s.el" (file-name-sans-extension (eval-and-compile (or load-file-name buffer-file-name))))) (defvar vexil--readme-org (expand-file-name "README.org" (file-name-directory vexil--dot-el))) (defvar vexil--summary (lm-summary vexil--dot-el)) (defvar vexil--homepage (lm-homepage vexil--dot-el)) (defvar vexil--version (lm-with-file vexil--dot-el (or (lm-header "package-version") (lm-version)))) ;;;; Customizable variables (defgroup vexil nil (format "%s." vexil--summary) :group 'extensions :group 'lisp :link '(emacs-library-link :tag "Lisp file" "vexil.el") :link `(file-link :tag "README.org" ,vexil--readme-org) :link `(url-link :tag "Homepage" ,vexil--homepage)) ;;;; Other variables ;;;;; Regular expressions (defvar vexil--territ-code-re (rx bos (= 2 (in "A-Z")) eos) "Regular expression matching two-letter territory code.") (defvar vexil--territ-flag-re (rx bos (= 2 (in "🇦-🇿")) eos) "Regular expression matching territory flag.") (defvar vexil--subdiv-code-re (rx bos (or (: (= 2 (in "A-Z")) "-" (** 1 3 (in "0-9A-Z"))) (: (= 2 (in "a-z")) (** 1 3 (in "0-9a-z")))) eos) "Regular expression matching subdivision code.") (defvar vexil--subdiv-flag-re (let* ((bos "\\`") (eos "\\'") (two "\\{2\\}") (1-3 "\\{1,3\\}") (dlt (- #xe0061 ?a)) (wbf (string #x1f3f4)) ; WAVING BLACK FLAG (ctg (string #xe007f)) ; CANCEL TAG (tga (string (+ ?a dlt))) ; TAG a (tgz (string (+ ?z dlt))) ; TAG z (tg0 (string (+ ?0 dlt))) ; TAG 0 (tg9 (string (+ ?9 dlt))) ; TAG 9 (alp (format "[%s-%s]" tga tgz)) (aln (format "[%s-%s%s-%s]" tg0 tg9 tga tgz))) (concat bos wbf alp two aln 1-3 ctg eos)) "Regular expression matching subdivision flag.") ;;;; Functions ;;;;; Description macro (defmacro vexil--describe (str) "Describe with string STR the contents under this heading. Think of it as a docstring for the headings of your elisp files. For the full docstring, look for ‘orgreadme-fy-describe’ in the package ‘orgreadme-fy’." (declare (indent 0)) (unless (stringp str) (user-error "‘vexil--describe’ must receive a string"))) ;;;;; Conversions (vexil--describe "These functions convert an object OBJ that represents either a territory or a subdivision. - If OBJ is a string, return a string. - If OBJ is a symbol, return a symbol. OBJ type is auto-detected: it must match a regular expression for either code or flag, for either territory or subdivision — or it throws an error. Subdivision codes may be passed in either ISO 3166-2 or CLDR formats. So, for example, either of these: - 'GB-SCT - 'gbsct would equally return '🏴 when passed to ‘vexil-flag’.") ;;;###autoload (defun vexil-flag (obj) "Convert OBJ to flag." (vexil--convert-1 obj 'flag)) ;;;###autoload (defun vexil-code (obj) "Convert OBJ to code." (vexil--convert-1 obj 'code)) ;;;###autoload (defun vexil-toggle (obj) "Convert OBJ: code ⬄ flag." (vexil--convert-1 obj)) ;;;;;; Internal (defun vexil--convert-1 (obj &optional type) "Convert place OBJ to format TYPE. For valid options of type, see ‘vexil--convert-str’." (cond ((stringp obj) (vexil--convert-str obj type)) ((symbolp obj) (vexil--convert-sym obj type)) (:otherwise (error "%s must be a string or symbol" obj)))) (defun vexil--convert-sym (obj &optional type) "Convert place OBJ symbol to format TYPE. For valid options of type, see ‘vexil--convert-str’." (intern (vexil--convert-str (format "%s" obj) type))) (defun vexil--convert-str (obj &optional type) "Convert place OBJ string to format TYPE. TYPE can be either: - \\='code, in which case output code. - \\='flag, in which case output flag. - \\='nil, in which case output code if flag, flag if code." (let ((from (vexil--type obj))) (pcase-exhaustive type ('flag (pcase-exhaustive from ('tc (vexil--convert-str-territ-code->flag obj)) ('sc (vexil--convert-str-subdiv-code->flag obj)) ('tf obj) ('sf obj))) ('code (pcase-exhaustive from ('tc obj) ('sc obj) ('tf (vexil--convert-str-territ-flag->code obj)) ('sf (vexil--convert-str-subdiv-flag->code obj)))) ('nil (pcase-exhaustive from ('tc (vexil--convert-str-territ-code->flag obj)) ('sc (vexil--convert-str-subdiv-code->flag obj)) ('tf (vexil--convert-str-territ-flag->code obj)) ('sf (vexil--convert-str-subdiv-flag->code obj))))))) (defun vexil--type (obj) "Guess type of OBJ. Territory or subdivision? Code or flag?" (let ((case-fold-search nil)) (cond ((string-match-p vexil--territ-code-re obj) 'tc) ((string-match-p vexil--subdiv-code-re obj) 'sc) ((string-match-p vexil--territ-flag-re obj) 'tf) ((string-match-p vexil--subdiv-flag-re obj) 'sf) (:otherwise (error "Invalid place type: %s" obj))))) ;;;;;;; Territories (defun vexil--convert-str-territ-code->flag (code) "Convert territory two-letter CODE to flag." (let ((Δ (- ?🇦 ?A))) (with-output-to-string (dotimes (i 2) (princ (string (+ (aref code i) Δ))))))) (defun vexil--convert-str-territ-flag->code (flag) "Convert territory FLAG to two-letter code." (let ((Δ (- ?🇦 ?A))) (with-output-to-string (dotimes (i 2) (princ (string (- (aref flag i) Δ))))))) ;;;;;;; Subdivisions (defun vexil--convert-str-subdiv-code->flag (code) "Convert subdivision CODE to flag." (let ((wbf #x1f3f4) ; WAVING BLACK FLAG (stl (vexil--subdiv-str->tags code)) ; TAG CHARS (lowercase, list) (ctg #xe007f)) ; CANCEL TAG (concat `(,wbf ,@stl ,ctg)))) (defun vexil--convert-str-subdiv-flag->code (flag &optional cldr) "Convert subdivision FLAG to code. If CLDR is non-nil, keep it in CLDR format (all lowercase, no hyphen). Otherwise, convert it to ISO 3166-2 format (all uppercase, hyphen)." (let ((it (mapcar #'vexil--subdiv-tag->string (vexil--subdiv-flag->vector flag)))) (if cldr (apply #'concat it) (upcase (apply #'concat (car it) (cadr it) "-" (cddr it)))))) (defun vexil--subdiv-str->tags (str) "From string STR to list of their corresponding lowercase tag chars." (mapcar #'vexil--subdiv-char->tag (replace-regexp-in-string "-" "" str))) (defun vexil--subdiv-char->tag (char) "From character CHAR to its lowercase tag equivalent." (+ (downcase char) (- #xe0061 ?a))) (defun vexil--subdiv-tag->string (tag) "From TAG to string." (string (- tag (- #xe0061 ?a)))) (defun vexil--subdiv-flag->vector (flag) "From FLAG to vector of tags." (vconcat (replace-regexp-in-string (string #x1f3f4) "" (replace-regexp-in-string (string #xe007f) "" flag)))) ;;;; Commands ;;;;; Conversions (vexil--describe "These work like ‘vexil-toggle’, but are interactive. Input will be either the contents of a selected region, or whatever you entered in the minibuffer. In the former case, the region will be replaced.") ;;;###autoload (defun vexil-dwim () "Convert code ⬄ flag. Do what I mean: use selection, or ask." (interactive) (vexil--convert-dwim)) ;;;###autoload (defun vexil-region () "Convert code ⬄ flag. Use selected region, replacing it." (interactive) (vexil--convert-region)) ;;;###autoload (defun vexil-ask () "Convert code ⬄ flag. Ask for input and show the result." (interactive) (vexil--convert-ask)) ;;;;;; Internal (defun vexil--convert-dwim () "Convert selected region. If none, ask." (if mark-active (vexil--convert-region) (vexil--convert-ask))) (defun vexil--convert-region () "Convert selected region." (unless mark-active (error "You must first select a region")) (save-excursion (let* ((p (point)) (m (mark)) (obj (buffer-substring-no-properties p m)) (mod (vexil--convert-1 obj))) (delete-region p m) (insert mod)))) (defun vexil--convert-ask () "Ask what to convert and show the result." (let ((obj (read-string "Code or flag: "))) (message "%s" (vexil--convert-1 obj)))) ;;;;; See README (vexil--describe "Commands to open vexil's README.org. Optionally, find things in it.") ;;;###autoload (defun vexil-see-readme (&optional heading narrow) "Open vexil's README.org file. Search for the file in vexil.el's directory. If found, open it read-only. If optional argument HEADING is passed, try to navigate to the heading after opening it. HEADING should be a string. If optional argument NARROW is non-nil, narrow to that heading. This argument has no effect if HEADING is nil or not found." (interactive) (let ((readme vexil--readme-org)) (if (file-exists-p readme) (let ((pr (make-progress-reporter (format "Opening %s ... " (abbreviate-file-name readme))))) (find-file-read-only readme) (when heading (vexil--goto-org-heading heading narrow)) (progress-reporter-done pr)) (message "Couldn't find %s's README.org" vexil--name)))) ;;;###autoload (defun vexil-see-news () "See the News in vexil's README.org file." (interactive) (vexil-see-readme "News" 'narrow) (vexil--display-org-subtree)) ;;;;;; Internal (defun vexil--display-org-subtree () "Selectively display org subtree." (let ((cmds '( outline-hide-subtree outline-show-children outline-next-heading outline-show-branches))) (and (equal (mapcar #'fboundp cmds) '(t t t t)) (mapc #'funcall cmds)))) (defun vexil--goto-org-heading (heading &optional narrow) "Navigate to org HEADING and optionally NARROW to it." (let* ((hrx (format "^[*]+ %s" heading)) (pos (save-match-data (save-excursion (save-restriction (widen) (goto-char (point-max)) (re-search-backward hrx nil t 1)))))) (when pos (widen) (goto-char pos) (if (and narrow (fboundp 'org-narrow-to-subtree)) (org-narrow-to-subtree) (recenter-top-bottom 1)) (when (fboundp 'outline-show-subtree) (outline-show-subtree)) (when (fboundp 'org-flag-drawer) (save-excursion (forward-line 1) (org-flag-drawer t)))))) ;;;; Wrapping up (provide 'vexil) ;; Local Variables: ;; coding: utf-8 ;; indent-tabs-mode: nil ;; sentence-end-double-space: nil ;; outline-regexp: ";;;;* " ;; End: ;;; vexil.el ends here
📆 2025-09-08