feat: ID-Kurzform wird beim Doppelpunkt aufgelöst (D55-Nachtrag)
`#.kc` wird jetzt aufgelöst, sobald ein Doppelpunkt die ID abschließt — beim Tippen des Doppelpunkts ebenso wie beim nächsten Tastendruck in einer Zeile, die ihn schon trägt. Ohne Doppelpunkt bleibt es beim Verlassen der Zeile. Der Cursor allein löst weiterhin nichts aus: Angefasst wird nur die Zeile, in der auch getippt wurde (D55/D27). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a60c466d82
commit
4900eb3428
+34
-10
@@ -1,5 +1,5 @@
|
||||
import './style.css';
|
||||
import { parse, setFoldMark, expandShortIds } from './parser.js';
|
||||
import { parse, setFoldMark, expandShortIds, shortIdClosed } from './parser.js';
|
||||
import { computeCheapPlan, freshProdSet, initialCollapsed, nodeKeys, effectiveStatus, atMostM } from './model.js';
|
||||
import { esc, renderTreeHtml, TIP_RULE } from './render.js';
|
||||
import { formatWarning, warningText } from './warnings.js';
|
||||
@@ -1427,10 +1427,14 @@ function syncCaret(){
|
||||
}
|
||||
for(const ev of ['click','keyup','input','focus']) src.addEventListener(ev, syncCaret);
|
||||
|
||||
/* Kurzschreibweise der ID auflösen (D55): `#.kc` wird beim **Verlassen der
|
||||
Zeile** zu `#prod-stage.kc`. Eingabehilfe, keine Notation — in der Datei
|
||||
steht danach die volle ID, sie bleibt also durchsuchbar und überlebt das
|
||||
Umsortieren.
|
||||
/* Kurzschreibweise der ID auflösen (D55): `#.kc` wird zu `#prod-stage.kc`.
|
||||
Eingabehilfe, keine Notation — in der Datei steht danach die volle ID, sie
|
||||
bleibt also durchsuchbar und überlebt das Umsortieren.
|
||||
|
||||
**Wann:** sobald die ID abgeschlossen ist, also sobald ihr ein Doppelpunkt
|
||||
folgt (`#.kc:`) — beim Tippen des Doppelpunkts ebenso wie beim nächsten
|
||||
Tastendruck in einer Zeile, die ihn schon trägt. Sonst beim **Verlassen der
|
||||
Zeile**, spätestens dann ist sie fertig (D55-Nachtrag).
|
||||
|
||||
Angefasst wird nur die **eine** Zeile, in der auch getippt wurde. Beides ist
|
||||
nötig: `#.foo` ist schon heute eine gültige ID, und wer ein fremdes Dokument
|
||||
@@ -1438,13 +1442,33 @@ for(const ev of ['click','keyup','input','focus']) src.addEventListener(ev, sync
|
||||
Nachziehen mitgelieferter Fassungen fallen, D27). Geschrieben wird
|
||||
undo-fähig — ein Griff daneben kostet ein Strg+Z (D53). */
|
||||
let touchedLine = null;
|
||||
src.addEventListener('input', () => { touchedLine = caretLineOf(); });
|
||||
src.addEventListener('input', () => {
|
||||
touchedLine = caretLineOf();
|
||||
/* Der Vorfilter fragt nur die Zeile unter der Schreibmarke — ob wirklich
|
||||
etwas aufzulösen ist, entscheidet `expandShortIds()`. Scheitert es (noch
|
||||
kein Vorfahr mit ID), bleibt `touchedLine` stehen: Der nächste Tastendruck
|
||||
versucht es erneut, und das Verlassen der Zeile fängt es ohnehin auf. */
|
||||
if(shortIdClosed(caretLineText())) writeShortId(touchedLine);
|
||||
});
|
||||
/* Die Zeile unter der Schreibmarke, ohne den ganzen Text zu zerlegen — das
|
||||
liefe bei jedem Tastendruck über alle Zeilen. */
|
||||
function caretLineText(){
|
||||
const v = src.value, p = src.selectionStart;
|
||||
const s = v.lastIndexOf('\n', p - 1) + 1;
|
||||
const e = v.indexOf('\n', p);
|
||||
return e === -1 ? v.slice(s) : v.slice(s, e);
|
||||
}
|
||||
function resolveShortId(line){
|
||||
if(src.readOnly || line == null || line !== touchedLine) return;
|
||||
if(line == null || line !== touchedLine) return;
|
||||
touchedLine = null;
|
||||
/* **Nicht** sofort schreiben: Der Zeilenwechsel kommt oft aus dem
|
||||
`input`-Ereignis der Enter-Taste, und `execCommand` verweigert den Dienst,
|
||||
wenn es re-entrant darin aufgerufen wird. `replaceTextUndoable` fiele dann
|
||||
writeShortId(line);
|
||||
}
|
||||
function writeShortId(line){
|
||||
if(src.readOnly || line == null) return;
|
||||
/* **Nicht** sofort schreiben: Beide Wege hierher hängen am
|
||||
`input`-Ereignis — der Doppelpunkt unmittelbar, der Zeilenwechsel über die
|
||||
Enter-Taste —, und `execCommand` verweigert den Dienst, wenn es
|
||||
re-entrant darin aufgerufen wird. `replaceTextUndoable` fiele dann
|
||||
auf `src.value =` zurück — und das löscht die Undo-Historie (D38-Nachtrag
|
||||
2). Gemessen: erstes Rückgängig ohne Wirkung, jedes weitere `false`.
|
||||
Deshalb ein Zug später, wenn das Ereignis zugestellt ist.
|
||||
|
||||
@@ -131,6 +131,24 @@ export function expandShortIds(text){
|
||||
return changed ? lines.join('\n') : text;
|
||||
}
|
||||
|
||||
/* Ist die Kurzform in dieser Zeile schon **abgeschlossen**? Also folgt ihr
|
||||
unmittelbar ein Doppelpunkt — der übliche Trenner vor dem Titel (§1/D36) und
|
||||
zugleich der Anfang einer Abhängigkeitsliste (`#.kc:#db`). Dann steht fest,
|
||||
wie die ID heißt, und der Editor löst sofort auf, statt das Verlassen der
|
||||
Zeile abzuwarten (D55-Nachtrag).
|
||||
|
||||
Bewusst nur ein **Vorfilter**: Ob die Zeile überhaupt einen Knoten trägt und
|
||||
ob es einen Vorfahren mit ID gibt, weiß allein `expandShortIds()` — es bleibt
|
||||
die eine Stelle, die die Regel kennt. */
|
||||
export function shortIdClosed(line){
|
||||
const k = line.indexOf('%%');
|
||||
const head = k === -1 ? line : line.slice(0, k);
|
||||
const m = head.match(RE_ID_TOKEN);
|
||||
/* `.kc` ja, `..kc` nein — dieselbe Prüfung wie oben. */
|
||||
if(!m || !/^\.[^.]/.test(m[2])) return false;
|
||||
return head[m.index + m[0].length] === ':';
|
||||
}
|
||||
|
||||
/* Status, die als „realisiert" zählen (XOR-Regel, SPEC §3/D35): Kosten sind
|
||||
investiert oder mehr. Absicht (`[?]`, `[ ]`, `[!]`), Ablehnung (`[-]`) und
|
||||
neutrale Knoten zählen nicht. */
|
||||
|
||||
Reference in New Issue
Block a user