phases 4-6: article system, stem note fields, edit-state cleanup
Phase 4 — remove edit-state flags from domain objects: - ast-parser.js: drop isDirty from buildInstrument, buildBar, buildStemNote - store.js: add resetEditState() - PaneCP.js: use resetEditState() on import; drop flags arg from patchScore - PaneFO.js: isDirty -> _modified for linked instruments; no per-node dirty tracking - PaneSubObjects.js: _spliceNode helper; deletion via splice; _isNew on new bars - exporter.js: on-demand traversal; absence from model means deleted Phase 5 — article parser and exporter: - ast-parser.js: _buildArticleEntry replaces _mergeArticleEntry; stage.article and voice.article dispatch; article properties carry scope and overwritten flag - exporter.js: _buildArticlesBlock emits -important: list for overwrites scope Phase 6 — stem note dead fields: - ast-parser.js: parse weight and articulatory on stem notes; flatten article.definite properties onto stem note model sibling to pitch/chain
This commit is contained in:
@@ -133,23 +133,30 @@ function _serializeArticleValue(v) {
|
||||
|
||||
function _buildArticlesBlock(articles) {
|
||||
const body = articles.flatMap(art => {
|
||||
const props = (art.properties ?? []).filter(p => p.name);
|
||||
if (!props.length) return [];
|
||||
return [` ${art.name}:`, ...props.map(p => ` ${p.name}: ${_serializeArticleValue(p.value)}`)];
|
||||
const defaults = (art.properties ?? []).filter(p => p.scope !== 'overwrites' && p.name);
|
||||
const overwrites = (art.properties ?? []).filter(p => p.scope === 'overwrites' && p.name);
|
||||
if (!defaults.length && !overwrites.length) return [];
|
||||
const lines = [` ${art.name}:`];
|
||||
defaults.forEach(p => lines.push(` ${p.name}: ${_serializeArticleValue(p.value)}`));
|
||||
if (overwrites.length) {
|
||||
lines.push(` -important:`);
|
||||
overwrites.forEach(p => lines.push(` - ${p.name}`));
|
||||
}
|
||||
return lines;
|
||||
});
|
||||
return body.length ? ['articles:', ...body].join('\n') : null;
|
||||
}
|
||||
|
||||
function _patchArticles(text, articles) {
|
||||
const newBlock = _buildArticlesBlock(articles);
|
||||
if (!newBlock) return text;
|
||||
const lines = text.split('\n');
|
||||
const artIdx = lines.findIndex(l => /^articles\s*:/.test(l));
|
||||
if (artIdx !== -1) {
|
||||
let end = artIdx + 1;
|
||||
while (end < lines.length && (lines[end] === '' || lines[end].startsWith(' ') || lines[end].startsWith('\t'))) end++;
|
||||
lines.splice(artIdx, end - artIdx, newBlock);
|
||||
} else {
|
||||
if (newBlock) lines.splice(artIdx, end - artIdx, newBlock);
|
||||
else lines.splice(artIdx, end - artIdx);
|
||||
} else if (newBlock) {
|
||||
const instrIdx = lines.findIndex(l => /^instrument\s/.test(l));
|
||||
const insertAt = instrIdx !== -1 ? instrIdx : lines.length;
|
||||
lines.splice(insertAt, 0, newBlock, '');
|
||||
@@ -196,17 +203,18 @@ function _patchInstrumentHeader(text, instruments) {
|
||||
if (m) {
|
||||
const rawName = m[1].replace(/^'|'$/g, '');
|
||||
const instr = instrMap[rawName];
|
||||
if (instr && instr.deleted) {
|
||||
if (!instr) {
|
||||
// not in model → deleted; skip block
|
||||
i++;
|
||||
while (i < lines.length && (lines[i].startsWith(' ') || lines[i] === '')) i++;
|
||||
} else if (instr && instr.isDirty) {
|
||||
} else if (instr.isLinked && !instr._modified) {
|
||||
result.push(line);
|
||||
i++;
|
||||
} else {
|
||||
i++;
|
||||
while (i < lines.length && (lines[i].startsWith(' ') || lines[i] === '')) i++;
|
||||
result.push(exportInstrument(instr));
|
||||
result.push('');
|
||||
} else {
|
||||
result.push(line);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result.push(line);
|
||||
@@ -263,42 +271,37 @@ function _buildNewBarDoc(bar) {
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export function patchScore(rawScoreText, instruments, bars = [], info = null, articles = [], flags = {}) {
|
||||
export function patchScore(rawScoreText, instruments, bars = [], info = null, articles = []) {
|
||||
const log = [];
|
||||
const SEP = '\n---\n';
|
||||
const [header, ...barDocs] = rawScoreText.split(SEP);
|
||||
|
||||
let patchedHeader = _patchMetadata(header, info);
|
||||
|
||||
const dirtyArticles = articles.filter(a => a.isDirty);
|
||||
if (dirtyArticles.length || flags.articlesModified) {
|
||||
if (articles.length) {
|
||||
patchedHeader = _patchArticles(patchedHeader, articles);
|
||||
dirtyArticles.forEach(a => log.push({ level: 'changed', path: `articles / ${a.name}` }));
|
||||
articles.forEach(a => log.push({ level: 'changed', path: `articles / ${a.name}` }));
|
||||
}
|
||||
|
||||
patchedHeader = _patchInstrumentHeader(patchedHeader, instruments);
|
||||
instruments.forEach(i => {
|
||||
if (i.deleted) log.push({ level: 'changed', path: `instrument / ${i.name} (deleted)` });
|
||||
else if (i.isDirty) log.push({ level: 'changed', path: `instrument / ${i.name}` });
|
||||
if (!(i.isLinked && !i._modified)) log.push({ level: 'changed', path: `instrument / ${i.name}` });
|
||||
});
|
||||
|
||||
const barMap = Object.fromEntries(bars.map(b => [b.id, b]));
|
||||
const newBars = bars.filter(b => b.isNew && !b.deleted);
|
||||
const newBars = bars.filter(b => b._isNew);
|
||||
|
||||
if (!barDocs.length) {
|
||||
newBars.forEach(b => log.push({ level: 'changed', path: `bar / ${b.id}` }));
|
||||
return { text: [patchedHeader, ...newBars.map(_buildNewBarDoc)].join(SEP), log };
|
||||
}
|
||||
|
||||
let passedThrough = 0;
|
||||
const patchedBarDocs = [];
|
||||
barDocs.forEach(doc => {
|
||||
const m = doc.match(/^_id:\s*(\S+)/m);
|
||||
if (!m) { passedThrough++; patchedBarDocs.push(doc); return; }
|
||||
if (!m) { patchedBarDocs.push(doc); return; }
|
||||
const bar = barMap[m[1]];
|
||||
if (!bar) { passedThrough++; patchedBarDocs.push(doc); return; }
|
||||
if (bar.deleted) return;
|
||||
if (!bar.isDirty) { passedThrough++; patchedBarDocs.push(doc); return; }
|
||||
if (!bar) return; // not in model → deleted
|
||||
log.push({ level: 'changed', path: `bar / ${bar.id}` });
|
||||
patchedBarDocs.push(_patchBarMeta(doc, bar));
|
||||
});
|
||||
@@ -308,9 +311,5 @@ export function patchScore(rawScoreText, instruments, bars = [], info = null, ar
|
||||
log.push({ level: 'changed', path: `bar / ${bar.id}` });
|
||||
});
|
||||
|
||||
if (passedThrough > 0) {
|
||||
log.push({ level: 'info', message: `${passedThrough} bar document${passedThrough !== 1 ? 's' : ''} passed through unchanged` });
|
||||
}
|
||||
|
||||
return { text: [patchedHeader, ...patchedBarDocs].join(SEP), log };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user