Documents
Un tampon est du texte brut ; un document est un tampon plus tout ce qui en fait un fichier : un historique d'annulation, un indicateur de modifications non enregistrées, un chemin et un encodage. Les fenêtres de l'éditeur travaillent toujours avec des documents.
Nouveaux fichiers
⌘N ouvre un tampon sans titre en texte brut ; File → New with Format liste tous les langages connus, si bien qu'un nouveau fichier peut être coloré avant d'avoir un nom (le langage reste jusqu'à ce que le premier enregistrement nomme le fichier, qui redétecte par l'extension comme d'habitude). Enregistrer sous, pour un document sans titre, démarre dans le dossier du fichier qui était au premier plan à sa création — un nouveau fichier appartient généralement au projet où l'on se trouvait — et suggère l'extension du langage choisi.
Frappe
Entrée indente automatiquement : la nouvelle ligne hérite du blanc de
tête de la ligne courante, et descend d'un niveau quand la ligne se
termine (avant le curseur) par un ouvrant — {, [, ( ou :. Le
niveau supplémentaire parle le dialecte du document lui-même : des
tabulations dans un fichier indenté aux tabulations, des espaces à la
largeur configurée sinon. Une ligne sans rien à hériter reçoit un saut
de ligne ordinaire — la fonction est invisible jusqu'à ce qu'elle serve.
Annuler et rétablir
L'historique d'annulation vit dans le noyau, pas dans le NSUndoManager
d'AppKit. Chaque édition est enregistrée comme une opération inversible ;
annuler dépile l'enregistrement le plus récent, applique son inverse et
signale le changement résultant à la fenêtre, qui le rejoue à l'écran.
Puisque l'historique se trouve derrière la même interface que tout le reste,
aucune édition ne peut lui échapper — il n'existe pas de second chemin vers
le texte.
Les enregistrements fusionnent pour qu'annuler avance à pas humains :
- Les séquences de frappe fusionnent : des insertions consécutives, chacune commençant exactement là où la précédente s'est arrêtée, deviennent un seul pas d'annulation.
- Les séquences de suppression fusionnent de la même façon, au retour arrière comme à la suppression avant.
- Un saut de ligne termine la séquence de part et d'autre ; annuler s'arrête donc à la granularité de la ligne.
- Déplacer le curseur (clic, flèches) termine la séquence en cours ; la frappe suivante ouvre un nouveau pas.
Les opérations composées s'enregistrent comme des groupes explicites : Tout remplacer réécrit chaque occurrence mais s'annule en un seul pas, et un rechargement depuis le disque (ci-dessous) est lui aussi un seul pas.
Un renommage ou une action de code d'un serveur de langage est un seul pas dans chaque fichier qu'il a modifié. Quand il en a modifié plusieurs, Annuler dans n'importe lequel le retire en entier : dans chaque document ouvert dont le pas le plus récent est encore celui-là, et dans les fichiers qui n'étaient pas ouverts, que l'éditeur a écrits lui-même en en gardant une copie. Rétablir le refait partout. Un fichier modifié depuis — à la main ou par un autre programme — est laissé tel quel, et la zone de notification dit lequel, avec le nombre de fichiers annulés. Cela vaut pour la session : après un redémarrage, l'historique de chaque document repart de zéro.
Plusieurs curseurs
Ajouter l'occurrence suivante (⌃G, Alt+J sous Linux) sélectionne le
mot sous le curseur ; appuyé de nouveau, il ajoute la correspondance
suivante de ce qui est sélectionné comme une sélection à part entière,
en repartant du début du fichier — mots entiers seulement quand la
sélection est un mot entier. Ajouter un curseur au-dessus et
au-dessous (⌃⇧↑ et ⌃⇧↓, Alt+Maj+Haut et Bas sous Linux) ajoutent un
curseur sur la ligne voisine, dans la colonne du premier curseur ou en
fin de ligne quand elle est plus courte. Les trois sont dans le menu Edit
et réassignables comme addNextOccurrence, addCaretAbove et
addCaretBelow.
Avec plusieurs curseurs, taper, coller, Retour arrière, Suppr, Entrée et Tab agissent à chacun, en une seule étape d'annulation ; les flèches, Début et Fin déplacent tous les curseurs, et avec Maj étendent toutes les sélections. Les curseurs qui se rejoignent n'en font plus qu'un. Échap, quand il n'y a plus rien d'autre à fermer, ne laisse que le curseur le plus récent ; un clic aussi, ou toute autre commande, qui ne concerne qu'un curseur.
Pas encore là : une colonne de curseurs se fait ligne par ligne, pas en faisant glisser ; Entrée et Tab avec plusieurs curseurs insèrent un saut de ligne et une tabulation simples, sans l'indentation qu'obtient un seul curseur ; les déplacements par mot et les autres commandes ne laissent que le curseur le plus récent ; et sous Linux le texte composé par une méthode de saisie ne va qu'au curseur le plus récent.
Repliement
Fold (Ctrl+[) replie le bloc qu'ouvre la ligne du curseur ; la ligne qui dit ce qu'est le bloc reste, et tout ce qui suit jusqu'à sa fin est caché. Fold All (Ctrl+Alt+[) le fait pour tous les blocs, du plus extérieur au plus intérieur, et Unfold All (Ctrl+]) les rend. Les blocs viennent de l'arbre qui sert déjà à la coloration.
Un repli par ligne d'ouverture, et le plus large quand plusieurs blocs
y commencent : impl Item { ouvre l'impl et son corps, et replier
l'impl est ce qu'on voulait. Un bloc qui cacherait une seule ligne
n'est pas proposé : dans un langage à accolades cette ligne est
l'accolade fermante, et ailleurs cela gagne une ligne contre un
ornement sur toutes les autres.
Replier change ce qu'on voit et rien d'autre. Le texte n'est pas touché : ce qui est enregistré est ce qui était là.
Sous macOS ce n'est pas encore disponible. TextKit 2 dispose une ligne par élément de texte et n'a aucun attribut disant d'en sauter un : un élément rendu vide occupe toujours une ligne, et un élément retiré de l'énumération est bien disposé par un layout manager nu mais ne se dessine pas à travers le contrôleur de viewport de la vue. Les deux ont été mesurés, non supposés.
Transformations
Edit ▸ Transform agit sur la sélection, ou sur le document entier quand rien n'est sélectionné — ce dont il est question quand aucune partie n'a été désignée :
- Majuscules, minuscules, casse de titre et casse inversée.
- Trier les lignes, les trier à l'envers, ôter les lignes en double.
- Joindre les lignes, ôter les espaces de fin.
- Convertir les fins de ligne en LF ou en CRLF.
Une opération sur les lignes reçoit des lignes entières : une sélection qui commence au milieu d'une ligne s'étend jusqu'aux limites qui l'entourent, car trier une demi-ligne n'a été demandé par personne. Le texte arrivé en CRLF repart en CRLF, sauf si la conversion est justement le but. L'étendue transformée reste sélectionnée, une deuxième opération peut donc suivre sans la resélectionner, et le tout ne fait qu'une étape d'annulation.
La casse de titre commence chaque mot et abaisse le reste, l'apostrophe
comptant comme partie du mot : don't be well-known devient Don't Be
Well-Known. Ôter les doublons garde le premier de chacun et laisse
l'ordre tranquille. Joindre abandonne l'indentation propre à chaque
ligne, qui était là pour se placer sous la ligne du dessus.
Rechercher et remplacer
⌘F ouvre la barre de recherche native (⌥⌘F avec le champ de remplacement, ⌘G / ⇧⌘G occurrence suivante et précédente, ⌘E recherche la sélection). Le menu d'options de la barre propose la correspondance par sous-chaîne, mot entier et expression régulière. Les remplacements sont des éditions ordinaires : ils passent par le noyau, entrent dans l'historique d'annulation — Tout remplacer en un seul pas — et marquent le document comme modifié, comme la frappe.
Modifications externes
Textchum surveille le fichier de chaque document ouvert. Si un autre programme le modifie :
- un document propre suit le disque en silence — la fenêtre montre simplement le nouveau contenu ;
- un document modifié pose la question : garder vos changements non enregistrés, ou recharger depuis le disque. Recharger abandonne le tampon au profit du fichier, mais le rechargement est lui-même un pas d'annulation : ⌘Z ramène votre version (et marque de nouveau le document comme modifié, puisqu'il diffère alors du disque).
Un fichier qui disparaît du disque est laissé tranquille : le tampon reste, et enregistrer recrée le fichier.
Revenir à l'enregistré (menu File, ⌥⌘R, réassignable comme
revertToSaved) est la version manuelle du même rechargement : jeter
le tampon et croire le disque, avec une confirmation quand il y a des
modifications non enregistrées (et une Annulation pour les retrouver).
Il existe pour le rare changement externe que le veilleur manque — les
flux supprimer-et-remplacer comme un git checkout.
Historique local
Chaque enregistrement garde la version qu'il a écrite, à part du
fichier, et le premier enregistrement d'un fichier garde aussi la
version qu'il a remplacée — on peut ainsi retrouver un enregistrement
qui n'aurait pas dû avoir lieu, un formateur allé trop loin ou un
travail perdu dans un checkout. File → Historique local…
(réassignable comme showLocalHistory) liste les versions gardées du
fichier au premier plan, de la plus récente à la plus ancienne, chacune
avec les lignes que la restaurer ajouterait et retirerait ; celle qui
est sélectionnée s'affiche à côté de la liste comme la différence avec
le texte actuel. En choisir une la met dans l'éditeur en une seule
modification : Annuler la retire, et enregistrer la garde, comme une
nouvelle version.
Les versions vivent dans un dossier history parmi les données de
l'éditeur (~/Library/Application Support/Textchum/history sous
macOS, ~/.local/share/textchum/history sous Linux, ou sous
--data-dir), un dossier par fichier. Une version identique à la plus
récente n'est pas gardée deux fois. Jusqu'à 100 versions sont gardées
par fichier, pendant 30 jours ; un fichier de plus de 4 Mo n'est pas
gardé.
Pas encore là : seuls les enregistrements sont gardés, pas le texte entre eux ; les versions d'un fichier supprimé ne peuvent pas être ouvertes depuis l'éditeur, même si elles restent dans le dossier jusqu'à leurs 30 jours ; et leur durée de conservation n'est pas un réglage.
Modifications non enregistrées
Un document connaît le point exact de son historique où il a été enregistré pour la dernière fois ; modifié signifie donc « l'état courant diffère de l'état enregistré » — et non « une édition a eu lieu à un moment donné ». Éditer puis annuler jusqu'au point d'enregistrement laisse un document propre, et le bouton de fermeture de la fenêtre perd son point en conséquence. Si de nouvelles éditions rendent l'état enregistré inatteignable (annulé au-delà, puis autre chose tapé), le document compte comme modifié jusqu'au prochain enregistrement, comme il se doit.
Fermer une fenêtre modifiée, ou quitter avec des fenêtres modifiées ouvertes, pose la question habituelle : enregistrer, ne pas enregistrer ou annuler.
Fichiers et encodages
Textchum décode à l'ouverture et ré-encode à l'enregistrement :
- L'UTF-8 valide se charge en UTF-8. Un BOM en tête est retiré en mémoire, mémorisé, et réécrit à l'enregistrement.
- Tout le reste est décodé en ISO-8859-1 (Latin-1), qui associe un caractère à chaque octet et ne peut donc pas échouer. L'enregistrement ré-encode en Latin-1 ; si une édition a introduit des caractères que le Latin-1 ne peut pas contenir, l'enregistrement promeut silencieusement le fichier en UTF-8 — rien ne peut se perdre dans ce sens — et le sous-titre de la fenêtre reflète le nouvel encodage.
Les fins de ligne ne sont jamais normalisées : ce qui a été lu est ce qui
est écrit, que ce soit \n ou \r\n.
L'encodage courant est toujours visible dans le sous-titre de la fenêtre, à côté de la taille du document.
Les enregistrements sont atomiques
Un enregistrement écrit le document entier dans un fichier temporaire du répertoire cible, le synchronise sur disque, puis le renomme par-dessus la cible. Un plantage en plein enregistrement ne peut jamais laisser un fichier tronqué, et les autres programmes qui observent le fichier voient l'ancien contenu ou le nouveau — jamais un mélange.
Restauration de session
Relancer Textchum rouvre les fichiers qui étaient ouverts, chacun avec sa
position de curseur et de défilement, en ramenant au premier plan celui
qui était utilisé. L'état est un simple fichier JSON (session.json, à
côté de la configuration), écrit en continu — pas seulement à la
fermeture — de sorte qu'un plantage perd au plus un instant de position,
jamais la liste des fichiers. Les fichiers disparus sont ignorés.
Pour démarrer sans mémoire (utile en chassant un bogue) : lancer avec
--fresh, maintenir ⇧ pendant le démarrage, ou supprimer
session.json — chacune des trois options est une remise à zéro
complète.
Fermer un onglet n'est pas définitif non plus : Rouvrir l'onglet fermé (⇧⌘T sur macOS, Ctrl+Shift+T sous Linux) ramène le dernier, curseur compris, et remonte ensuite les précédents. Seuls les documents enregistrés sont mémorisés : un tampon sans nom n'a rien d'où revenir, et le rouvrir vide serait un mensonge.
Pas encore là
- Les encodages au-delà d'UTF-8 et de Latin-1.