Initial finish of refactor

This commit is contained in:
Mayuran Visakan 2023-06-25 12:06:12 +01:00
parent b475f3e954
commit d90d6d6e5b
6 changed files with 323 additions and 288 deletions

224
main.js

File diff suppressed because one or more lines are too long

View file

@ -1,115 +0,0 @@
import { StateField, StateEffect, RangeSetBuilder } from "@codemirror/state";
import { EditorView, Decoration } from "@codemirror/view";
import { searchString, isExcluded } from "./Utils";
function defaultFold(state: EditorState, settings: CodeblockCustomizerSettings) {
let CollapseStart = null;
let CollapseEnd = null;
let Fold = false;
let blockFound = false;
let bExclude = false;
const builder = new RangeSetBuilder<Decoration>();
for (let i = 1; i < state.doc.lines; i++) {
const lineText = state.doc.line(i).text.toString();
const line = state.doc.line(i);
bExclude = isExcluded(lineText, settings.ExcludeLangs);
if (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1) {
if (bExclude)
continue;
if (CollapseStart === null) {
Fold = searchString(lineText, "fold");
if (Fold)
CollapseStart = line.from;
} else {
blockFound = true;
CollapseEnd = line.to;
}
}
if (blockFound) {
if (CollapseStart != null && CollapseEnd != null ){
const decoration = Decoration.replace({ effect: Collapse.of([doFold.range(CollapseStart, CollapseEnd)]), block:true, side:-1 });
builder.add(CollapseStart, CollapseEnd, decoration );
CollapseStart = null;
CollapseEnd = null;
}
blockFound = false;
}
}// for
return builder.finish();
}// defaultFold
const Collapse = StateEffect.define(), UnCollapse = StateEffect.define()
export const collapseField = StateField.define({
create(state) {
return defaultFold(state, collapseField.pluginSettings);
//return Decoration.none
},
update(value, tr) {
value = value.map(tr.changes)
for (const effect of tr.effects) {
if (effect.is(Collapse))
value = value.update({add: effect.value, sort: true});
else if (effect.is(UnCollapse))
value = value.update({filter: effect.value});
}
return value;
},
provide: f => EditorView.decorations.from(f)
})
const doFold = Decoration.replace({block: true});
function handleClick(view: EditorView, target: HTMLElement){
const Pos = view.posAtDOM(target);
const effect = view.state.field(collapseField, false);
let isFolded = false;
effect.between(Pos, Pos, () => { isFolded = true});
let CollapseStart: number | null = null;
let CollapseEnd: number | null = null;
let WidgetStart: number | null = null;
// NOTE: Can't use for loop over view.visibleRanges, because that way the closing backticks wouldn't be found and collapse would not be possible
let blockFound = false;
for (let i = 1; i < view.state.doc.lines; i++) {
const lineText = view.state.doc.line(i).text.toString();
const line = view.state.doc.line(i);
if (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1) {
if (WidgetStart === null) {
WidgetStart = line.from;
if (Pos === line.from){
CollapseStart = line.from;
}
} else {
blockFound = true;
CollapseEnd = line.to;
}
}
if (blockFound) {
if (CollapseStart != null && CollapseEnd != null ){
if (isFolded){
view.dispatch({ effects: UnCollapse.of((from, to) => to <= CollapseStart || from >= CollapseEnd) });
}
else {
view.dispatch({ effects: Collapse.of([doFold.range(CollapseStart, CollapseEnd)]) });
}
view.requestMeasure();
CollapseStart = null;
CollapseEnd = null;
}//if (CollapseStart
WidgetStart = null;
blockFound = false;
}
}
}

View file

@ -71,10 +71,10 @@ function styleThemeSettings (themeSettings: CodeblockCustomizerThemeSettings): s
`}
}
body.codeblock-customizer {
--border-radius: ${themeSettings.codeblock.curvature}px;
--language-icon-size: ${themeSettings.advanced.iconSize}px;
--gradient-highlights-color-stop: ${themeSettings.advanced.gradientHighlights?themeSettings.advanced.gradientHighlightsColorStop:'100%'};
--language-border-width: ${themeSettings.advanced.languageBorderColor?themeSettings.advanced.languageBorderWidth:0}px;
--border-radius: ${themeSettings.codeblock.curvature}px !important;
--language-icon-size: ${themeSettings.advanced.iconSize}px !important;
--gradient-highlights-color-stop: ${themeSettings.advanced.gradientHighlights?themeSettings.advanced.gradientHighlightsColorStop:'100%'} !important;
--language-border-width: ${themeSettings.advanced.languageBorderColor?themeSettings.advanced.languageBorderWidth:0}px !important;
}
${themeSettings.header.languageIcon.displayColor?'':`
.codeblock-customizer-icon {

View file

@ -1,5 +1,5 @@
import { ViewPlugin, EditorView, ViewUpdate, Decoration, DecorationSet, WidgetType } from "@codemirror/view";
import { Extension, StateField, StateEffect, Range, RangeSet, RangeSetBuilder, Transaction } from "@codemirror/state";
import { Extension, EditorState, StateField, StateEffect, StateEffectType, Range, RangeSet, RangeSetBuilder, Transaction, Line } from "@codemirror/state";
import { syntaxTree } from "@codemirror/language";
import { SyntaxNodeRef } from "@lezer/common";
@ -7,8 +7,8 @@ import { CodeblockCustomizerSettings, CodeblockCustomizerThemeSettings } from ".
import { CodeblockParameters, parseCodeblockParameters, isLanguageExcluded } from "./CodeblockParsing";
import { createHeader, getLineClass } from "./CodeblockDecorating";
export function codeblockLines(settings: CodeblockCustomizerSettings) {
return ViewPlugin.fromClass(
export function createCodeMirrorExtensions(settings: CodeblockCustomizerSettings) {
const codeblockLines = ViewPlugin.fromClass(
class CodeblockLines {
settings: CodeblockCustomizerSettings;
currentSettings: {
@ -109,7 +109,7 @@ export function codeblockLines(settings: CodeblockCustomizerSettings) {
return RangeSet.of(decorations,true);
}
destroy() {
this.mutationObserver.disconnect();
@ -117,14 +117,11 @@ export function codeblockLines(settings: CodeblockCustomizerSettings) {
},
{decorations: (value) => value.decorations}
)
}
export function codeblockHeader(settings: CodeblockCustomizerSettings) {
return StateField.define<DecorationSet>({
create(state): DecorationSet {
const codeblockHeader = StateField.define<DecorationSet>({
create(state: EditorState): DecorationSet {
return Decoration.none;
},
update(oldState: DecorationSet, transaction: Transaction): DecorationSet {
update(value: DecorationSet, transaction: Transaction): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
let codeblockParameters: CodeblockParameters;
let startLine: boolean = true;
@ -149,82 +146,161 @@ export function codeblockHeader(settings: CodeblockCustomizerSettings) {
return EditorView.decorations.from(field);
}
})
}
const codeblockCollapse = StateField.define({
create(state: EditorState): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
let codeblockParameters: CodeblockParameters;
let collapseStart: Line | null = null;
let collapseEnd: Line | null = null;
let startLine: boolean = true;
for (let i = 1; i < state.doc.lines; i++) {
const line = state.doc.line(i);
const lineText = line.text.toString();
const codeblockDelimiterLine = (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1);
if (codeblockDelimiterLine) {
if (startLine) {
startLine = false;
codeblockParameters = parseCodeblockParameters(lineText,settings.currentTheme);
if (!isLanguageExcluded(codeblockParameters.language,settings.excludedLanguages) && codeblockParameters.fold.enabled)
collapseStart = line;
} else {
startLine = true;
if (collapseStart)
collapseEnd = line;
}
}
if (collapseStart && collapseEnd) {
builder.add(collapseStart.from,collapseEnd.to,Decoration.replace({effect: collapse.of([Decoration.replace({block: true}).range(collapseStart.from,collapseEnd.to)]), block: true, side: -1}))
collapseStart = null;
collapseEnd = null;
}
}
return builder.finish();
},
update(value: DecorationSet, transaction: Transaction): DecorationSet {
value = value.map(transaction.changes);
for (const effect of transaction.effects) {
if (effect.is(collapse))
value = value.update({add: effect.value, sort: true});
else if (effect.is(uncollapse))
value = value.update({filter: effect.value});
}
return value;
},
provide(field: StateField<DecorationSet>): Extension {
return EditorView.decorations.from(field);
}
})
export function codeblockCollapse(settings: CodeblockCustomizerSettings) {
}
class LineNumberWidget extends WidgetType {
lineNumber: number;
codeblockParameters: CodeblockParameters;
empty: boolean;
constructor(lineNumber: number, codeblockParameters: CodeblockParameters, empty: boolean) {
super();
this.lineNumber = lineNumber;
this.codeblockParameters = codeblockParameters;
this.empty = empty;
}
eq(other: LineNumberWidget) {
return this.lineNumber === other.lineNumber && this.codeblockParameters === other.codeblockParameters;
}
toDOM(view: EditorView): HTMLElement {
let lineNumberDisplay = '';
if (!this.codeblockParameters.lineNumbers.alwaysEnabled && this.codeblockParameters.lineNumbers.alwaysDisabled)
lineNumberDisplay = '-hide'
else if (this.codeblockParameters.lineNumbers.alwaysEnabled && !this.codeblockParameters.lineNumbers.alwaysDisabled)
lineNumberDisplay = '-specific'
return createSpan({cls: `codeblock-customizer-line-number${lineNumberDisplay}`, text: this.empty?'':(this.lineNumber + this.codeblockParameters.lineNumbers.offset).toString()});
}
}
class HeaderWidget extends WidgetType {
codeblockParameters: CodeblockParameters;
themeSettings: CodeblockCustomizerThemeSettings;
view: EditorView;
mutationObserver: MutationObserver;
constructor(codeblockParameters: CodeblockParameters, themeSettings: CodeblockCustomizerThemeSettings) {
super();
this.codeblockParameters = codeblockParameters;
this.themeSettings = themeSettings;
this.mutationObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if ((mutation.target as HTMLElement).hasAttribute('data-clicked'))
console.log('func')
//handleClick(this.view, mutation.target);
})
});
}
eq(other: HeaderWidget) {
return this.codeblockParameters == other.codeblockParameters
}
toDOM(view: EditorView): HTMLElement {
this.view = view;
const headerContainer = createHeader(this.codeblockParameters, this.themeSettings);
headerContainer.addEventListener("mousedown", event => {
headerContainer.setAttribute("data-clicked", "true");
});
this.mutationObserver.observe(headerContainer,{
attributes: true,
});
return headerContainer;
class LineNumberWidget extends WidgetType {
lineNumber: number;
codeblockParameters: CodeblockParameters;
empty: boolean;
constructor(lineNumber: number, codeblockParameters: CodeblockParameters, empty: boolean) {
super();
this.lineNumber = lineNumber;
this.codeblockParameters = codeblockParameters;
this.empty = empty;
}
eq(other: LineNumberWidget) {
return this.lineNumber === other.lineNumber && this.codeblockParameters === other.codeblockParameters;
}
toDOM(view: EditorView): HTMLElement {
let lineNumberDisplay = '';
if (!this.codeblockParameters.lineNumbers.alwaysEnabled && this.codeblockParameters.lineNumbers.alwaysDisabled)
lineNumberDisplay = '-hide'
else if (this.codeblockParameters.lineNumbers.alwaysEnabled && !this.codeblockParameters.lineNumbers.alwaysDisabled)
lineNumberDisplay = '-specific'
return createSpan({cls: `codeblock-customizer-line-number${lineNumberDisplay}`, text: this.empty?'':(this.lineNumber + this.codeblockParameters.lineNumbers.offset).toString()});
}
}
class HeaderWidget extends WidgetType {
codeblockParameters: CodeblockParameters;
themeSettings: CodeblockCustomizerThemeSettings;
view: EditorView;
mutationObserver: MutationObserver;
constructor(codeblockParameters: CodeblockParameters, themeSettings: CodeblockCustomizerThemeSettings) {
super();
this.codeblockParameters = codeblockParameters;
this.themeSettings = themeSettings;
this.mutationObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if ((mutation.target as HTMLElement).hasAttribute('data-clicked'))
collapseOnClick(this.view,(mutation.target as HTMLElement))
})
});
}
destroy(dom: HTMLElement) {
dom.removeAttribute("data-clicked");
//dom.removeEventListener("mousedown",handleClick);
this.mutationObserver.disconnect();
eq(other: HeaderWidget) {
return this.codeblockParameters == other.codeblockParameters
}
toDOM(view: EditorView): HTMLElement {
this.view = view;
const headerContainer = createHeader(this.codeblockParameters, this.themeSettings);
headerContainer.addEventListener("mousedown", (event: MouseEvent) => {
headerContainer.setAttribute("data-clicked", "true");
});
this.mutationObserver.observe(headerContainer,{
attributes: true,
});
return headerContainer;
}
destroy(dom: HTMLElement) {
dom.removeAttribute("data-clicked");
dom.removeEventListener("mousedown",collapseOnClick);
this.mutationObserver.disconnect();
}
ignoreEvent() {
return false;
}
}
function collapseOnClick(view: EditorView, target: HTMLElement) {
const position = view.posAtDOM(target);
let folded = false;
view.state.field(codeblockCollapse,false)?.between(position,position,()=>{
folded = true;
})
let collapseStart: Line | null = null;
let collapseEnd: Line | null = null;
let startLine: boolean = true;
for (let i = 1; i < view.state.doc.lines; i++) {
const line = view.state.doc.line(i);
const lineText = line.text.toString();
const codeblockDelimiterLine = (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1);
if (codeblockDelimiterLine) {
if (startLine) {
startLine = false;
if (position === line.from)
collapseStart = line;
} else {
startLine = true;
if (collapseStart)
collapseEnd = line;
}
}
if (collapseStart && collapseEnd) {
if (folded)
view.dispatch({effects: uncollapse.of((from,to) => to<=collapseStart.from||from>=collapseEnd.to)});
else
view.dispatch({effects: collapse.of([Decoration.replace({block: true}).range(collapseStart.from,collapseEnd.to)])})
view.requestMeasure();
collapseStart = null;
collapseEnd = null;
}
}
}
ignoreEvent() {
return false;
}
return [codeblockLines,codeblockHeader,codeblockCollapse]
}
function findUnduplicatedCodeblocks(view: EditorView): Array<SyntaxNodeRef> {
@ -242,7 +318,7 @@ function findVisibleCodeblocks(view: EditorView): Array<SyntaxNodeRef> {
}
function findCodeblocks(view: EditorView): Array<SyntaxNodeRef> {
const codeblocks: Array<SyntaxNodeRef> = [];
syntaxTree(view.state).iterate({from: view.state.doc.from, to: view.state.doc.to,
syntaxTree(view.state).iterate({
enter: (node) => {
if (
node.type.name.includes("HyperMD-codeblock-begin") ||
@ -253,9 +329,13 @@ function findCodeblocks(view: EditorView): Array<SyntaxNodeRef> {
}
}
})
console.log(codeblocks)
return codeblocks;
}
function arraysEqual(array1: Array<any>,array2: Array<any>): boolean {
return array1.length === array2.length && array1.every((el) => array2.includes(el));
}
const collapse: StateEffectType<Array<Range<Decoration>>> = StateEffect.define();
const uncollapse: StateEffectType<(from: any, to: any) => boolean> = StateEffect.define();

View file

@ -3,7 +3,7 @@ import { Plugin } from "obsidian";
import { DEFAULT_SETTINGS, LANGUAGE_ICONS, CodeblockCustomizerSettings } from './Settings';
import { SettingsTab } from "./SettingsTab";
import { updateStyling } from "./ApplyStyling";
import { codeblockLines, codeblockHeader } from "./EditingView";
import { createCodeMirrorExtensions } from "./EditingView";
import { readingViewPostProcessor } from "./ReadingView";
export default class CodeblockCustomizerPlugin extends Plugin {
@ -17,16 +17,8 @@ export default class CodeblockCustomizerPlugin extends Plugin {
const settingsTab = new SettingsTab(this.app, this);
this.addSettingTab(settingsTab);
this.registerEditorExtension([
codeblockLines(this.settings),
codeblockHeader(this.settings),
// collapseField,
]);
this.registerMarkdownPostProcessor(async (el, ctx) => {
await readingViewPostProcessor(el, ctx, this)
})
this.registerEditorExtension(createCodeMirrorExtensions(this.settings));
this.registerMarkdownPostProcessor(async (el, ctx) => {await readingViewPostProcessor(el, ctx, this)})
console.log("Loaded plugin: CodeBlock Customizer");
}

View file

@ -6,9 +6,9 @@ body.codeblock-customizer {
--header-padding: 0px;
--header-spacing: 15px;
--language-border-color: var(--codeblock-customizer-codeblock-background-color);
/* --border-radius: 10px; */
/* --language-icon-size: 28px; */
/* --gradient-highlights-color-stop: 100%; */
--border-radius: 10px;
--language-icon-size: 28px;
--gradient-highlights-color-stop: 100%;
}
/* Codeblock background colours */