Pular para o conteúdo
nandomoreira.dev

espanso: a substituição de texto do iPhone, agora no meu Linux

#linux#dicas#produtividade#wayland

Ajustes de leitura
Fundo
Tamanho do texto
Entrelinha
Largura

No iPhone eu digito vc e sai “você”, digito .email e sai meu e-mail inteiro. Usei o macOS por alguns anos com a mesma “Substituição de texto” configurada, e a lista cresceu junto: endereço, CNPJ, link do WhatsApp, (╯°□°)╯︵ ┻━┻ para os dias difíceis. No CachyOS, isso não existia, e fazia falta.

Minha primeira ideia foi escrever uma ferramenta para isso. Não precisou: o espanso existe desde 2019, é escrito em Rust, roda em Linux, macOS e Windows, e estava com release nova (2.4.1) quando instalei. O que a documentação não deixa claro é o que muda quando o seu desktop é Wayland, com um compositor que não é GNOME nem KDE. Aqui é CachyOS com Niri, e foram três ajustes até ficar redondo.

O que o espanso faz

Ele fica escutando o teclado e, quando você termina de digitar um gatilho, apaga o gatilho e coloca o texto no lugar. Funciona em qualquer campo de texto: navegador, terminal, chat, editor. Os atalhos ficam em arquivos YAML dentro de ~/.config/espanso/match/, e ele recarrega sozinho quando você salva.

matches:
  - trigger: ".email"
    replace: "voce@exemplo.com"

Além de texto fixo, ele aceita variáveis (data, hora, saída de comando, clipboard), formulários e regex. Além das datas, uso pouco disso; a graça para mim é a lista de atalhos curtos que já mora na minha memória muscular.

Instalando em cada distro

Primeiro descubra se a sua sessão é X11 ou Wayland, porque o espanso tem um build para cada:

echo $XDG_SESSION_TYPE

Terminal no CachyOS com o comando echo $XDG_SESSION_TYPE respondendo wayland

No Wayland ele lê o teclado direto do kernel (evdev), então o binário precisa de uma capability extra. Esse setcap aparece em todas as instalações Wayland abaixo, e esquecer dele dá o pior sintoma possível: o espanso roda, mas não vê nenhuma tecla.

Arch, CachyOS e Manjaro

Vem do AUR, que compila Rust, então leva alguns minutos:

paru -S espanso-wayland   # ou espanso-x11
espanso service register
espanso start

O pacote do AUR já aplica o setcap sozinho na instalação.

Ubuntu e Debian

Para Wayland:

wget https://github.com/espanso/espanso/releases/latest/download/espanso-debian-wayland-amd64.deb
sudo apt install ./espanso-debian-wayland-amd64.deb
sudo setcap "cap_dac_override+p" $(which espanso)
espanso service register
espanso start

Para X11, troque o .deb por espanso-debian-x11-amd64.deb e pule o setcap.

Fedora

Depois de habilitar o repositório Terra:

sudo dnf install espanso-wayland   # ou espanso-x11
sudo setcap "cap_dac_override+p" $(which espanso)
espanso service register
espanso start

Outras distros

Com X11, há uma AppImage que roda em qualquer distro, e dá para compilar do código-fonte nos dois casos. Os comandos estão na página de instalação para Linux.

O primeiro start que dizia que tinha dado certo

Instalei, rodei espanso service register e espanso start. A saída foi esta:

unable to start service: timed out

E o código de saída foi 0. Meu script de setup leu o 0, imprimiu um check verde e seguiu em frente com o espanso parado (o tipo de verde que eu já aprendi a desconfiar). O espanso status contava a verdade: espanso is not running.

A causa estava numa janela. Na primeira execução, o launcher abre uma tela de boas-vindas e só sobe o daemon depois que alguém clica nela; o timeout era ele esperando o meu clique. Fui ao código-fonte e, no Linux, essa tela é a única página do assistente. Ela é pulada quando existe um arquivo com uma preferência gravada em JSON:

mkdir -p ~/.cache/espanso/kvs
printf 'true' > ~/.cache/espanso/kvs/has_completed_wizard

Clicar na janela resolve do mesmo jeito. O arquivo é para quando a instalação roda sozinha, num OS novo, sem ninguém olhando. E a regra que ficou: depois do start, quem diz se subiu é o espanso status, não o exit code.

Três ajustes no default.yml

Esta é a minha config/default.yml, com o porquê de cada linha:

keyboard_layout:
  layout: "us,br"
  variant: "intl,"

backend: Clipboard
preserve_clipboard: true

search_shortcut: off

O layout precisa ser declarado. Fora do GNOME e do KDE, o espanso não consegue descobrir o layout ativo no Wayland (o log avisa com keyboard layout watcher couldn't determine active layout). Eu copiei o bloco xkb do meu config.kdl do Niri, que usa us internacional com br de segunda opção. Se você usa ABNT2 puro, é layout: "br".

O texto entra pela área de transferência. O padrão do espanso é simular as teclas, e no us-intl isso é armadilha: aspas simples e aspas duplas são teclas mortas. Um ' injetado seguido de c vira ç, e o texto chega estragado. Com backend: Clipboard, ele cola o texto e devolve o que estava no clipboard antes.

O atalho de busca saiu. O espanso abre uma busca de atalhos com Alt+Espaço, que no meu Niri é a tecla que alterna entre os layouts us e br. Desliguei a busca.

Por que os meus atalhos começam com ponto

A convenção da documentação é começar o gatilho com dois-pontos: :email. Durou pouco: em vários apps (Slack, Discord, GitHub) o : seguido de letras abre o seletor de emojis, e o seletor aparecia antes de o espanso trocar o texto.

Voltei para o ponto, que é como eu já fazia no iPhone. O ponto tem o problema contrário: ele aparece no meio de domínio, nome de arquivo e versão. Sem cuidado, nandomoreira.site viraria nandomoreirahttps://.... A opção left_word resolve, porque o gatilho só dispara no começo de uma palavra:

  - trigger: ".site"
    left_word: true
    replace: "https://seusite.dev"

Os atalhos sem ponto, como vc e tbm, pedem a palavra inteira (word: true), senão vcard viraria vocêard. E propagate_case mantém a maiúscula: Vc vira “Você”.

  - trigger: "vc"
    replace: "você"
    word: true
    propagate_case: true

O arquivo abbrev.yml aberto no VS Code, com os atalhos hj, tbm, tbom e td, cada um com word: true e propagate_case: true

Uma coisa eu descobri testando, e conto porque vai acontecer com quem vem do iPhone. Lá a troca acontece quando você digita o espaço depois do atalho. Testei o @@ depois de uma palavra e saiu contato @nandomoreira.dev, com um espaço sobrando. O espaço era meu: eu digitava achando que era ele que disparava a troca. No espanso, o atalho com ponto expande no instante em que você termina de digitar, sem esperar espaço nenhum. Só os de palavra inteira (word: true) esperam um separador, e esse separador fica no texto.

Como eu organizei os 41 atalhos

Tirei print da lista nos Ajustes do iPhone, passei tudo para YAML e separei em quatro arquivos dentro de match/:

Arquivo O que tem
base.yml Contato e links (e-mail, site, GitHub, WhatsApp) e datas
personal.yml Documentos e endereço
abbrev.yml Abreviações sem ponto: vc, vcs, tbm, hj, td, vdd
fun.yml Risadas, (╯°□°)╯︵ ┻━┻, ¯\_(ツ)_/¯ e as piadas internas

Os dados pessoais ficam num arquivo separado de propósito: se um dia essa pasta for parar num repositório público, sei exatamente o que tirar.

Dois recursos que valeram a pena. Um atalho pode ter mais de um gatilho, o que limpou as duplicatas que eu carregava do iPhone (.wa e .whats levavam ao mesmo link):

  - triggers: [".wa", ".whats"]
    left_word: true
    replace: "https://wa.me/5511900000000"

E as datas, que o iPhone não faz, saem no formato brasileiro:

  - trigger: ".hoje"
    left_word: true
    replace: "{{today}}"
    vars:
      - name: today
        type: date
        params:
          format: "%d/%m/%Y"

Quando um atalho não funciona depois de salvar, quase sempre é indentação do YAML. Dois comandos mostram onde:

espanso match list                     # o que ele carregou de fato
grep ERROR ~/.cache/espanso/espanso.log

Num OS novo, sem lembrar de nada

A pasta ~/.config/espanso aqui é um link simbólico para os meus dotfiles, então os atalhos viajam com o resto da configuração. Num OS novo, o setup instala o espanso-wayland, confere a capability, marca a tela de boas-vindas como vista, registra o serviço e só dá verde quando o espanso status confirma. Cada passo só roda se a verificação dele falhar, e um --check repete as verificações sem mudar nada.

Fiquei com o espanso e não escrevi ferramenta nenhuma. E, pra ser sincero, gostei muito mais de como ele resolve esse problema do que da Substituição de texto do iPhone e do macOS. Lá é uma lista de atalho e texto, e só. Aqui eu tenho data no formato que eu quiser, mais de um gatilho para o mesmo texto, controle de quando o atalho dispara (começo da palavra, palavra inteira, maiúscula mantida) e tudo num arquivo que eu versiono junto com o resto da minha configuração. Eu decido o que é escrito e como é escrito.

Os três ajustes acima agora estão no script, e um OS novo já nasce com os meus atalhos.

{{end}}