Skip to content

Commit

Permalink
Fix inline chord transposition
Browse files Browse the repository at this point in the history
  • Loading branch information
olvidalo committed Dec 13, 2024
1 parent 8700460 commit f88956e
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 46 deletions.
41 changes: 20 additions & 21 deletions src/chordsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,16 @@ function offsetIndex(indexArray: [number, number], offset: number): [number, num
return indexArray && [indexArray[0] + offset, indexArray[1] + offset];
}

function relativeIndex(indexArray: [number, number], matchIndex: number): [number, number] {
return indexArray && [indexArray[0] - matchIndex, indexArray[1] - matchIndex];
}

export function tokenizeLine(line: string, lineIndex: number, chordLineMarker: string, textLineMarker: string): TokenizedLine {
const chordLineMarkerPattern = escapeStringRegexp(chordLineMarker);
const textLineMarkerPattern = escapeStringRegexp(textLineMarker);

const tokenPattern = new RegExp(
`(?<header>(?<=^\\s*)(\\[)([^\\]]+)(])(?=\\s*$))|(?<marker>${textLineMarkerPattern}|${chordLineMarkerPattern})\\s*$|(?<inline_chord>(\\[)(\\S+)([^\\[()]*)(]))|(?<user_defined_chord>([A-Z][A-Za-z0-9#()+-°/]*)(\\[)([0-9]+\\|)?([0-9x_]+)(]))|(?<word>([[\\]/|%]+)|[^\\s\\[]+)|(?<ws>\\s+)`,
`(?<header>(?<=^\\s*)(\\[)([^\\]]+)(])(?=\\s*$))|(?<marker>${textLineMarkerPattern}|${chordLineMarkerPattern})\\s*$|(?<inline_chord>(\\[)(\\S+)([^\\[()]*)(]))|(?<user_defined_chord>([A-Z][A-Za-z0-9#()+-°/]*)\\[(([0-9]+)\\|)?([0-9x_]+)])|(?<word>([[\\]/|%]+)|[^\\s\\[]+)|(?<ws>\\s+)`,
"gd");

const tokens: Token[] = [];
Expand Down Expand Up @@ -141,29 +145,20 @@ export function tokenizeLine(line: string, lineIndex: number, chordLineMarker: s
possibleChordOrRhythmTokens.set(token, {
chord,
chordSymbol: groups.word,
chordSymbolIndex: index
chordSymbolIndex: relativeIndex(index, index[0])
});
}
}

tokens.push(token);
wordTokenCount++;
} else if (groups.user_defined_chord) {
const {0: chordSymbol} = match;
const {0: chordSymbolIndex} = indices;
const baseToken = {value: groups.user_defined_chord, index: indexGroups.user_defined_chord};
const chord_name: string = chordSymbol.substring(0, chordSymbol.indexOf('['));
let frets: string = chordSymbol.substring(chordSymbol.indexOf('[') + 1, chordSymbol.indexOf(']'));
let position: string = "0";

if (frets.includes('|')) {
position = frets.substring(0, frets.indexOf('|'));
frets = frets.substring(frets.lastIndexOf('|') + 1);
}
const {12: chordSymbol, 14: position, 15: frets} = match;
const {12: chordSymbolIndex} = indices;

const chordToken: ChordToken = {
value: baseToken.value,
index: baseToken.index,
value: groups.user_defined_chord,
index: indexGroups.user_defined_chord,
type: "chord",
chord: {
tonic: "",
Expand All @@ -172,18 +167,20 @@ export function tokenizeLine(line: string, lineIndex: number, chordLineMarker: s
bass: "",
userDefinedChord: {
frets,
position: parseInt(position),
position: position ? parseInt(position) : 0,
}
},
chordSymbol: chord_name,
chordSymbolIndex: [chordSymbolIndex[0], chordSymbolIndex[0] + chord_name.length],
chordSymbol,
chordSymbolIndex: relativeIndex(chordSymbolIndex, indexGroups.user_defined_chord[0]),
};
tokens.push(chordToken);
hasUserDefinedChord = true;

} else if (groups.inline_chord) {
const {7: startTag, 8: chordSymbol, 9: auxText, 10: endTag} = match;
const {7: startTagIndex, 8: chordSymbolIndex, 9: auxTextIndex, 10: endTagIndex} = indices;
const {7: startTagIndex, 8: chordSymbolIndex, 9: auxTextIndex, 10: endTagIndex} = indices.map(
index => relativeIndex(index, indexGroups.inline_chord[0])
);

const tonalJsChord = Chord.get(chordSymbol);
const {tonic, type, aliases: typeAliases} = tonalJsChord;
Expand Down Expand Up @@ -215,7 +212,9 @@ export function tokenizeLine(line: string, lineIndex: number, chordLineMarker: s

} else if (groups.header) {
const { 2: startTag, 3: headerName, 4: endTag } = match;
const { 2: startTagIndex, 3: headerNameIndex, 4: endTagIndex } = indices;
const { 2: startTagIndex, 3: headerNameIndex, 4: endTagIndex } = indices.map(
index => relativeIndex(index, indexGroups.header[0])
);

headerToken = {
type: 'header',
Expand Down Expand Up @@ -248,7 +247,7 @@ export function tokenizeLine(line: string, lineIndex: number, chordLineMarker: s
return {tokens, isChordLine};
}

export function transposeTonic(chordTonic: string, direction: "up" | "down") {
export function transposeNote(chordTonic: string, direction: "up" | "down") {
const transposedTonic = Note.transpose(chordTonic, direction === "up" ? "2m" : "-2m");
return direction === "up" ? Note.enharmonic(transposedTonic) : Note.simplify(transposedTonic);
}
Expand Down
20 changes: 12 additions & 8 deletions src/editor-extension/chordBlocksStateField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
isChordToken,
isHeaderToken,
isMarkerToken,
isRhythmToken,
isRhythmToken, Token,
tokenizeLine
} from "../chordsUtils";
import {Decoration, DecorationSet, EditorView, ViewUpdate} from "@codemirror/view";
Expand Down Expand Up @@ -557,6 +557,10 @@ function parseChordBlocks(state: EditorState, from: number, to: number, parseCho
return result;
}

function resolveIndex(indexTuple: [number, number], token: Token): [from: number, to: number] {
const position = token.index[0];
return indexTuple && [position + indexTuple[0], position + indexTuple[1]];
}

function chordDecosForLine(line: Line, {
chordLineMarker,
Expand Down Expand Up @@ -589,29 +593,29 @@ function chordDecosForLine(line: Line, {
chordDecos.push(
Decoration
.mark({ class: `chord-sheet-inline-chord-tag` })
.range(...token.startTag.index)
.range(...resolveIndex(token.startTag.index, token))
);
}

chordDecos.push(
Decoration
.mark({ class:`chord-sheet-chord-name${highlightChords ? " chord-sheet-chord-highlight" : ""}` })
.range(...token.chordSymbolIndex)
.range(...resolveIndex(token.chordSymbolIndex, token))
);

if (token.auxText) {
chordDecos.push(
Decoration
.mark({ class: `chord-sheet-inline-chord-aux-text`})
.range(...token.auxText.index)
.range(...resolveIndex(token.auxText.index, token))
);
}

if (token.endTag) {
chordDecos.push(
Decoration
.mark({ class: `chord-sheet-inline-chord-tag` })
.range(...token.endTag.index)
.range(...resolveIndex(token.endTag.index, token))
);
}

Expand All @@ -633,9 +637,9 @@ function chordDecosForLine(line: Line, {

} else if (highlightSectionHeaders && isHeaderToken(token)) {
const [headerStart, headerEnd] = token.index;
const [startTagStart, startTagEnd] = token.startTagIndex;
const [headerNameStart, headerNameEnd] = token.headerNameIndex;
const endTagStart = token.endTagIndex[0];
const [startTagStart, startTagEnd] = resolveIndex(token.startTagIndex, token);
const [headerNameStart, headerNameEnd] = resolveIndex(token.headerNameIndex, token);
const endTagStart = resolveIndex(token.endTagIndex, token)[0];

chordDecos.push(
Decoration
Expand Down
20 changes: 13 additions & 7 deletions src/editor-extension/chordSheetsViewPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export interface TransposeEventDetail {
}
}

export interface ChordRange {
export interface ChordSymbolRange {
from: number,
to: number,
value: string,
chord: SheetChord
chord: SheetChord,
chordSymbol: string
}

export const chordSheetEditorPlugin = () => ViewPlugin.fromClass(ChordSheetsViewPlugin, {
Expand Down Expand Up @@ -165,8 +165,8 @@ export class ChordSheetsViewPlugin implements PluginValue {
return null;
}

async getChordRangesForBlock(blockDef: { from: number, to: number, value: IChordBlockRangeValue }): Promise<ChordRange[]> {
const chordRanges: ChordRange[] = [];
async getChordSymbolRangesForBlock(blockDef: { from: number, to: number, value: IChordBlockRangeValue }): Promise<ChordSymbolRange[]> {
const chordRanges: ChordSymbolRange[] = [];

let chordBlocksState: ChordBlocksState;
let chordBlockEnd: number;
Expand All @@ -184,9 +184,15 @@ export class ChordSheetsViewPlugin implements PluginValue {
chordBlockEnd = blockDef.to;
}

chordBlocksState.chordDecos.between(blockDef.from, chordBlockEnd, (from, to, value) => {
chordBlocksState.chordDecos.between(blockDef.from, chordBlockEnd, (from, _to, value) => {
if (value.spec.type === "chord") {
chordRanges.push({from, to, chord: value.spec.token.chord, value: value.spec.token.chordSymbol});
const chordToken = value.spec.token as ChordToken;
chordRanges.push({
from: from + chordToken.chordSymbolIndex[0],
to: from + chordToken.chordSymbolIndex[1],
chordSymbol: chordToken.chordSymbol,
chord: chordToken.chord
});
}
});

Expand Down
21 changes: 11 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import {debounce, Editor, MarkdownFileInfo, MarkdownView, Plugin, TFile, View} from 'obsidian';
import {EditorView, ViewPlugin} from "@codemirror/view";
import {Instrument, transposeTonic} from "./chordsUtils";
import {Instrument, transposeNote} from "./chordsUtils";
import {ChordBlockPostProcessorView} from "./chordBlockPostProcessorView";
import {ChordSheetsSettings, DEFAULT_SETTINGS} from "./chordSheetsSettings";
import {ChangeSpec, Extension} from "@codemirror/state";
import {
ChordRange,
ChordSymbolRange,
chordSheetEditorPlugin,
ChordSheetsViewPlugin,
TransposeEventDetail
Expand Down Expand Up @@ -88,7 +88,7 @@ export default class ChordSheetsPlugin extends Plugin implements IChordSheetsPlu
const editorView = editor.cm as EditorView;
const chordPlugin = editorView?.plugin(this.editorPlugin);
if (chordPlugin) {
const chordTokens = await chordPlugin.getChordRangesForBlock(blockDef);
const chordTokens = await chordPlugin.getChordSymbolRangesForBlock(blockDef);
this.transpose(chordTokens, editorView, direction);
}
}
Expand Down Expand Up @@ -239,7 +239,7 @@ export default class ChordSheetsPlugin extends Plugin implements IChordSheetsPlu
}

if (!checking) {
chordPlugin.getChordRangesForBlock(chordSheetBlockAtCursor).then(
chordPlugin.getChordSymbolRangesForBlock(chordSheetBlockAtCursor).then(
chordTokens => this.transpose(chordTokens, editorView, direction)
);
}
Expand Down Expand Up @@ -287,22 +287,23 @@ export default class ChordSheetsPlugin extends Plugin implements IChordSheetsPlu
}
}

private transpose(chordRanges: ChordRange[], editor: EditorView, direction: "up" | "down") {
private transpose(chordRanges: ChordSymbolRange[], editor: EditorView, direction: "up" | "down") {
const changes: ChangeSpec[] = [];
for (const chordRange of chordRanges) {
if (chordRange.chord.userDefinedChord === undefined) {
const {from, to, value} = chordRange;
const [chordTonic, chordType, bassNote] = Chord.tokenize(value);
const simplifiedTonic = transposeTonic(chordTonic, direction);
const {from, to, chordSymbol} = chordRange;
const [chordTonic, chordType, bassNote] = Chord.tokenize(chordSymbol);

const simplifiedTonic = transposeNote(chordTonic, direction);

let transposedChord;
if (bassNote) {
transposedChord = simplifiedTonic + chordType + "/" + transposeTonic(bassNote, direction);
transposedChord = simplifiedTonic + chordType + "/" + transposeNote(bassNote, direction);
} else {
transposedChord = simplifiedTonic + (chordType ?? "");
}

changes.push({from, to, insert: transposedChord});
changes.push({from: from, to: to, insert: transposedChord});
}
}
editor.plugin(this.editorPlugin)?.applyChanges(changes);
Expand Down

0 comments on commit f88956e

Please sign in to comment.