Update spacing

This commit is contained in:
Mayuran Visakan 2023-06-23 18:28:32 +01:00
parent b58362a937
commit c8ba7dbed7
17 changed files with 6282 additions and 6436 deletions

View file

@ -1,10 +1,10 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
tab_width = 4
# Top Level EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
tab_width = 4

View file

@ -1,23 +1,23 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

View file

@ -18,19 +18,19 @@ The plugin lets you customize the codeblocks in the following way:
- Exclude languages. You can define languages separated by a comma, to which the plugin will not apply.
- Set background color for codeblocks.
- Lets you highlight specific lines.
- Customize highlight color
- Customize highlight color
- Lets you define multiple highlight colors to highlight lines.
- Display filename
- If a filename is defined a header will be inserted, where it is possible to customize the text (color, bold, italic), and the header (color, header line) itself as well
- If a filename is defined a header will be inserted, where it is possible to customize the text (color, bold, italic), and the header (color, header line) itself as well
- Fold code
- If the header is displayed (either by defining filename or other way explained below), you can click on the header to fold the codeblock below it
- If the header is displayed (either by defining filename or other way explained below), you can click on the header to fold the codeblock below it
- Display codeblock language. This displays the language (if specified) of the codeblock in the header.
- Customize text color, background color, bold text, italic text for the language tag inside the header.
- By default the language tag is only displayed, if the header is displayed, and a if a language is defined for a codeblock. You can however force, to always display the codeblock language, even if the header would not be displayed.
- Customize text color, background color, bold text, italic text for the language tag inside the header.
- By default the language tag is only displayed, if the header is displayed, and a if a language is defined for a codeblock. You can however force, to always display the codeblock language, even if the header would not be displayed.
- Display codeblock language icon (if available for the specified language) in the header.
- Add line numbers to codeblocks
- Customize if the linenumber should also be highlighted, if a line is highlighted
- Customize background color, and color of the line numbers
- Customize if the linenumber should also be highlighted, if a line is highlighted
- Customize background color, and color of the line numbers
## Themes

6842
main.js

File diff suppressed because one or more lines are too long

View file

@ -5,260 +5,260 @@ import { syntaxTree } from "@codemirror/language";
import { splitAndTrimString, searchString, getHighlightedLines, getLanguageIcon, getLanguageName, isExcluded } from "./Utils";
export function codeblockHighlight(settings: CodeblockCustomizerSettings) {
const viewPlugin = ViewPlugin.fromClass(
class CodeblockHighlightPlugin {
decorations: DecorationSet;
settings: CodeblockCustomizerSettings;
prevAlternateColors: ColorSettings[];
view: EditorView;
mutationObserver: MutationObserver;
prevBGColor: string;
prevHLColor: string;
prevExcludeLangs: string;
prevTextColor: string;
prevBackgroundColor: string;
prevHighlightGutter: boolean;
prevLineNumbers: boolean;
const viewPlugin = ViewPlugin.fromClass(
class CodeblockHighlightPlugin {
decorations: DecorationSet;
settings: CodeblockCustomizerSettings;
prevAlternateColors: ColorSettings[];
view: EditorView;
mutationObserver: MutationObserver;
prevBGColor: string;
prevHLColor: string;
prevExcludeLangs: string;
prevTextColor: string;
prevBackgroundColor: string;
prevHighlightGutter: boolean;
prevLineNumbers: boolean;
constructor(view: EditorView) {
this.initialize(view, settings);
}
constructor(view: EditorView) {
this.initialize(view, settings);
}
initialize(view: EditorView, settings: CodeblockCustomizerSettings) {
this.view = view;
this.settings = settings;
this.decorations = this.buildDecorations(view);
this.prevAlternateColors = [];
this.mutationObserver = setupMutationObserver(view, this);
this.prevBGColor = '';
this.prevHLColor = '';
this.prevExcludeLangs = '';
this.prevTextColor = '';
this.prevBackgroundColor = '';
this.prevHighlightGutter = false;
this.prevLineNumbers = false;
}// initialize
initialize(view: EditorView, settings: CodeblockCustomizerSettings) {
this.view = view;
this.settings = settings;
this.decorations = this.buildDecorations(view);
this.prevAlternateColors = [];
this.mutationObserver = setupMutationObserver(view, this);
this.prevBGColor = '';
this.prevHLColor = '';
this.prevExcludeLangs = '';
this.prevTextColor = '';
this.prevBackgroundColor = '';
this.prevHighlightGutter = false;
this.prevLineNumbers = false;
}// initialize
forceUpdate(editorView: EditorView) {
this.view = editorView;
this.decorations = this.buildDecorations(this.view);
this.view.requestMeasure();
}// forceUpdate
forceUpdate(editorView: EditorView) {
this.view = editorView;
this.decorations = this.buildDecorations(this.view);
this.view.requestMeasure();
}// forceUpdate
shouldUpdate(update: ViewUpdate) {
return (update.docChanged || update.viewportChanged || !this.compareSettings());
}// shouldUpdate
shouldUpdate(update: ViewUpdate) {
return (update.docChanged || update.viewportChanged || !this.compareSettings());
}// shouldUpdate
compareSettings() {
return (
this.settings.backgroundColor === this.prevBGColor &&
this.settings.highlightColor === this.prevHLColor &&
this.settings.ExcludeLangs === this.prevExcludeLangs &&
compareArrays(this.settings.alternateColors, this.prevAlternateColors) &&
this.settings.gutterTextColor === this.prevTextColor &&
this.settings.gutterBackgroundColor === this.prevBackgroundColor &&
this.settings.bGutterHighlight === this.prevHighlightGutter &&
this.settings.bEnableLineNumbers === this.prevLineNumbers
);
}// compareSettings
updateSettings() {
this.prevBGColor = this.settings.backgroundColor;
this.prevHLColor = this.settings.highlightColor;
this.prevExcludeLangs = this.settings.ExcludeLangs;
this.prevAlternateColors = this.settings.alternateColors.map(({name}) => {
return {name};
});
this.prevTextColor = this.settings.gutterTextColor;
this.prevBackgroundColor = this.settings.gutterBackgroundColor;
this.prevHighlightGutter = this.settings.bGutterHighlight;
this.prevLineNumbers = this.settings.bEnableLineNumbers;
}// updateSettings
compareSettings() {
return (
this.settings.backgroundColor === this.prevBGColor &&
this.settings.highlightColor === this.prevHLColor &&
this.settings.ExcludeLangs === this.prevExcludeLangs &&
compareArrays(this.settings.alternateColors, this.prevAlternateColors) &&
this.settings.gutterTextColor === this.prevTextColor &&
this.settings.gutterBackgroundColor === this.prevBackgroundColor &&
this.settings.bGutterHighlight === this.prevHighlightGutter &&
this.settings.bEnableLineNumbers === this.prevLineNumbers
);
}// compareSettings
updateSettings() {
this.prevBGColor = this.settings.backgroundColor;
this.prevHLColor = this.settings.highlightColor;
this.prevExcludeLangs = this.settings.ExcludeLangs;
this.prevAlternateColors = this.settings.alternateColors.map(({name}) => {
return {name};
});
this.prevTextColor = this.settings.gutterTextColor;
this.prevBackgroundColor = this.settings.gutterBackgroundColor;
this.prevHighlightGutter = this.settings.bGutterHighlight;
this.prevLineNumbers = this.settings.bEnableLineNumbers;
}// updateSettings
update(update: ViewUpdate) {
if (this.shouldUpdate(update)) {
this.updateSettings();
this.decorations = this.buildDecorations(update.view);
}
}// update
update(update: ViewUpdate) {
if (this.shouldUpdate(update)) {
this.updateSettings();
this.decorations = this.buildDecorations(update.view);
}
}// update
destroy() {
this.mutationObserver.disconnect();
}// destroy
destroy() {
this.mutationObserver.disconnect();
}// destroy
filterVisibleCodeblocks(view: EditorView, codeblocks: Codeblock[]): Codeblock[] {
return codeblocks.filter((codeblock) => {
return view.visibleRanges.some((visibleRange) => {
return (codeblock.from < visibleRange.to && codeblock.to > visibleRange.from);
});
});
}// filterVisibleCodeblocks
filterVisibleCodeblocks(view: EditorView, codeblocks: Codeblock[]): Codeblock[] {
return codeblocks.filter((codeblock) => {
return view.visibleRanges.some((visibleRange) => {
return (codeblock.from < visibleRange.to && codeblock.to > visibleRange.from);
});
});
}// filterVisibleCodeblocks
deduplicateCodeblocks(codeblocks: Codeblock[]): Codeblock[] {
const deduplicatedCodeblocks = [];
for (let i = 0; i < codeblocks.length; i++) {
if (i === 0 || codeblocks[i].from !== codeblocks[i - 1].from) {
deduplicatedCodeblocks.push(codeblocks[i]);
}
}
return deduplicatedCodeblocks;
}// deduplicateCodeblocks
buildDecorations(view: EditorView): DecorationSet {
let lineNumber = 0;
let HL = [];
let altHL = [];
let lineNumberOffset = 0;
let showNumbers = true;
// const Exclude = this.settings.ExcludeLangs;
// const ExcludeLangs = splitAndTrimString(Exclude);
let bExclude = false;
const alternateColors = this.settings.alternateColors || [];
const decorations = [];
deduplicateCodeblocks(codeblocks: Codeblock[]): Codeblock[] {
const deduplicatedCodeblocks = [];
for (let i = 0; i < codeblocks.length; i++) {
if (i === 0 || codeblocks[i].from !== codeblocks[i - 1].from) {
deduplicatedCodeblocks.push(codeblocks[i]);
}
}
return deduplicatedCodeblocks;
}// deduplicateCodeblocks
buildDecorations(view: EditorView): DecorationSet {
let lineNumber = 0;
let HL = [];
let altHL = [];
let lineNumberOffset = 0;
let showNumbers = true;
// const Exclude = this.settings.ExcludeLangs;
// const ExcludeLangs = splitAndTrimString(Exclude);
let bExclude = false;
const alternateColors = this.settings.alternateColors || [];
const decorations = [];
if (!view.visibleRanges || view.visibleRanges.length === 0) {
return RangeSet.empty;
}
// Find all code blocks in the document
const codeblocks = findCodeblocks(view.state, view.state.doc.from, view.state.doc.to);
// Find code blocks that intersect with the visible range
const visibleCodeblocks = this.filterVisibleCodeblocks(view, codeblocks);
// remove duplicates
const deduplicatedCodeblocks = this.deduplicateCodeblocks(visibleCodeblocks);
if (!view.visibleRanges || view.visibleRanges.length === 0) {
return RangeSet.empty;
}
// Find all code blocks in the document
const codeblocks = findCodeblocks(view.state, view.state.doc.from, view.state.doc.to);
// Find code blocks that intersect with the visible range
const visibleCodeblocks = this.filterVisibleCodeblocks(view, codeblocks);
// remove duplicates
const deduplicatedCodeblocks = this.deduplicateCodeblocks(visibleCodeblocks);
for (const codeblock of deduplicatedCodeblocks) {
syntaxTree(view.state).iterate({ from: codeblock.from, to: codeblock.to,
enter(node) {
const line = view.state.doc.lineAt(node.from);
const lineText = view.state.sliceDoc(line.from, line.to);
const lang = searchString(lineText, "```");
const startLine = node.type.name.includes("HyperMD-codeblock-begin")
const endLine = node.type.name.includes("HyperMD-codeblock-end")
if (lang) {
bExclude = isExcluded(lineText, settings.ExcludeLangs);
}
if (bExclude) {
if (endLine) {
bExclude = false;
}
return;
}
if (startLine) {
lineNumber = 0;
lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const params = searchString(lineText, "HL:");
HL = getHighlightedLines(params);
altHL = [];
for (const { name } of alternateColors) {
const altParams = searchString(lineText, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
}
let lineClass = 'codeblock-customizer-line';
if (HL.includes(lineNumber)) {
lineClass = 'codeblock-customizer-line-highlighted';
} else {
const altHLMatch = altHL.filter((hl) => hl.lineNumber === lineNumber);
if (altHLMatch.length > 0) {
lineClass = `codeblock-customizer-line-highlighted-${altHLMatch[0].name.replace(/\s+/g, '-').toLowerCase()}`;
}
}
for (const codeblock of deduplicatedCodeblocks) {
syntaxTree(view.state).iterate({ from: codeblock.from, to: codeblock.to,
enter(node) {
const line = view.state.doc.lineAt(node.from);
const lineText = view.state.sliceDoc(line.from, line.to);
const lang = searchString(lineText, "```");
const startLine = node.type.name.includes("HyperMD-codeblock-begin")
const endLine = node.type.name.includes("HyperMD-codeblock-end")
if (lang) {
bExclude = isExcluded(lineText, settings.ExcludeLangs);
}
if (bExclude) {
if (endLine) {
bExclude = false;
}
return;
}
if (startLine) {
lineNumber = 0;
lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const params = searchString(lineText, "HL:");
HL = getHighlightedLines(params);
altHL = [];
for (const { name } of alternateColors) {
const altParams = searchString(lineText, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
}
let lineClass = 'codeblock-customizer-line';
if (HL.includes(lineNumber)) {
lineClass = 'codeblock-customizer-line-highlighted';
} else {
const altHLMatch = altHL.filter((hl) => hl.lineNumber === lineNumber);
if (altHLMatch.length > 0) {
lineClass = `codeblock-customizer-line-highlighted-${altHLMatch[0].name.replace(/\s+/g, '-').toLowerCase()}`;
}
}
if (node.type.name === "HyperMD-codeblock_HyperMD-codeblock-bg" || startLine || endLine) {
decorations.push(Decoration.line({attributes: {class: lineClass}}).range(node.from));
if (node.type.name === "HyperMD-codeblock_HyperMD-codeblock-bg" || startLine || endLine) {
decorations.push(Decoration.line({attributes: {class: lineClass}}).range(node.from));
decorations.push(Decoration.line({}).range(node.from));
decorations.push(Decoration.widget({ widget: new LineNumberWidget((startLine || endLine) ? " " : lineNumber+lineNumberOffset,showNumbers),}).range(node.from));
lineNumber++;
}
},
});
}
return RangeSet.of(decorations, true);
}
},// CodeblockHighlightPlugin
{
decorations: (value) => value.decorations,
}
);
decorations.push(Decoration.line({}).range(node.from));
decorations.push(Decoration.widget({ widget: new LineNumberWidget((startLine || endLine) ? " " : lineNumber+lineNumberOffset,showNumbers),}).range(node.from));
lineNumber++;
}
},
});
}
return RangeSet.of(decorations, true);
}
},// CodeblockHighlightPlugin
{
decorations: (value) => value.decorations,
}
);
viewPlugin.name = 'codeblockHighlight';
return viewPlugin;
viewPlugin.name = 'codeblockHighlight';
return viewPlugin;
}// codeblockHighlight
function compareArrays(array1, array2) {
if (array1.length !== array2.length) {
return false;
}
for (let i = 0; i < array1.length; i++) {
if ((array1[i].name !== array2[i].name) || (array1[i].currentColor !== array2[i].currentColor)) {
return false;
}
}
return true;
if (array1.length !== array2.length) {
return false;
}
for (let i = 0; i < array1.length; i++) {
if ((array1[i].name !== array2[i].name) || (array1[i].currentColor !== array2[i].currentColor)) {
return false;
}
}
return true;
}// compareArrays
class LineNumberWidget extends WidgetType {
constructor(private lineNumber: number, private showNumbers: boolean) {
super();
}
constructor(private lineNumber: number, private showNumbers: boolean) {
super();
}
eq(other: LineNumberWidget) {
return this.lineNumber === other.lineNumber && this.showNumbers === other.showNumbers;
}
eq(other: LineNumberWidget) {
return this.lineNumber === other.lineNumber && this.showNumbers === other.showNumbers;
}
toDOM(view: EditorView): HTMLElement {
const container = document.createElement("span");
container.classList.add(`codeblock-customizer-line-number${this.showNumbers?'':'-hide'}`);
container.innerText = `${this.lineNumber}`;
toDOM(view: EditorView): HTMLElement {
const container = document.createElement("span");
container.classList.add(`codeblock-customizer-line-number${this.showNumbers?'':'-hide'}`);
container.innerText = `${this.lineNumber}`;
return container;
}
return container;
}
}// LineNumberWidget
function findCodeblocks(doc: Text, from: number, to: number): SyntaxNode[] {
const tree = syntaxTree(doc);
const codeblocks: SyntaxNode[] = [];
const tree = syntaxTree(doc);
const codeblocks: SyntaxNode[] = [];
tree.iterate({ from, to,
enter: (node) => {
if (
node.type.name.includes("HyperMD-codeblock-begin") ||
node.type.name === "HyperMD-codeblock_HyperMD-codeblock-bg" ||
node.type.name.includes("HyperMD-codeblock-end")
) {
codeblocks.push(node);
}
},
});
tree.iterate({ from, to,
enter: (node) => {
if (
node.type.name.includes("HyperMD-codeblock-begin") ||
node.type.name === "HyperMD-codeblock_HyperMD-codeblock-bg" ||
node.type.name.includes("HyperMD-codeblock-end")
) {
codeblocks.push(node);
}
},
});
return codeblocks;
return codeblocks;
}// findCodeblocks
function setupMutationObserver(editorView: EditorView, pluginInstance: any) { //TODO (@mayurankv) What does this do? Work out
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (
mutation.type === "attributes" &&
mutation.attributeName === "class" &&
(mutation.target.classList.contains("HyperMD-codeblock-begin") ||
mutation.target.classList.contains("HyperMD-codeblock_HyperMD-codeblock-bg") ||
mutation.target.classList.contains("HyperMD-codeblock-end"))
) {
pluginInstance.forceUpdate(editorView);
}
}
});
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (
mutation.type === "attributes" &&
mutation.attributeName === "class" &&
(mutation.target.classList.contains("HyperMD-codeblock-begin") ||
mutation.target.classList.contains("HyperMD-codeblock_HyperMD-codeblock-bg") ||
mutation.target.classList.contains("HyperMD-codeblock-end"))
) {
pluginInstance.forceUpdate(editorView);
}
}
});
observer.observe(editorView.dom, {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['class'], // Only observe changes to the 'class' attribute
});
observer.observe(editorView.dom, {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['class'], // Only observe changes to the 'class' attribute
});
return observer;
} // setupMutationObserver
return observer;
} // setupMutationObserver

File diff suppressed because one or more lines are too long

0
src/EditingView.ts Normal file
View file

View file

@ -4,230 +4,230 @@ import { EditorView, Decoration, WidgetType } from "@codemirror/view";
import { searchString, getLanguageName, getLanguageIcon, isExcluded, createContainer, createWrapper, createCodeblockLang, createCodeblockIcon, createFileName } 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;
}
}
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();
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
let settings: CodeblockCustomizerSettings;
export const codeblockHeader = StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
},
update(oldState: DecorationSet, transaction: Transaction): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
let WidgetStart = null;
let Fold = false;
let fileName = null;
let bExclude = false;
let specificHeader = true;
for (let i = 1; i < transaction.state.doc.lines; i++) {
const lineText = transaction.state.doc.line(i).text.toString();
const line = transaction.state.doc.line(i);
const lang = searchString(lineText, "```");
bExclude = isExcluded(lineText, this.settings.ExcludeLangs);
specificHeader = true;
if (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1) {
if (WidgetStart === null) {
WidgetStart = line;
fileName = searchString(lineText, "file:");
Fold = searchString(lineText, "fold");
if (!bExclude) {
if (fileName === null || fileName === "") {
if (Fold) {
fileName = 'Collapsed Code';
} else {
fileName = '';
specificHeader = false;
}
}
builder.add(WidgetStart.from, WidgetStart.from, createDecorationWidget(fileName, getLanguageName(lang), specificHeader));
//EditorView.requestMeasure;
}
} else {
WidgetStart = null;
Fold = false;
fileName = null;
}
}
}
return builder.finish();
},
provide(field: StateField<DecorationSet>): Extension {
return EditorView.decorations.from(field);
},
create(state): DecorationSet {
return Decoration.none;
},
update(oldState: DecorationSet, transaction: Transaction): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
let WidgetStart = null;
let Fold = false;
let fileName = null;
let bExclude = false;
let specificHeader = true;
for (let i = 1; i < transaction.state.doc.lines; i++) {
const lineText = transaction.state.doc.line(i).text.toString();
const line = transaction.state.doc.line(i);
const lang = searchString(lineText, "```");
bExclude = isExcluded(lineText, this.settings.ExcludeLangs);
specificHeader = true;
if (lineText.startsWith('```') && lineText.indexOf('```', 3) === -1) {
if (WidgetStart === null) {
WidgetStart = line;
fileName = searchString(lineText, "file:");
Fold = searchString(lineText, "fold");
if (!bExclude) {
if (fileName === null || fileName === "") {
if (Fold) {
fileName = 'Collapsed Code';
} else {
fileName = '';
specificHeader = false;
}
}
builder.add(WidgetStart.from, WidgetStart.from, createDecorationWidget(fileName, getLanguageName(lang), specificHeader));
//EditorView.requestMeasure;
}
} else {
WidgetStart = null;
Fold = false;
fileName = null;
}
}
}
return builder.finish();
},
provide(field: StateField<DecorationSet>): Extension {
return EditorView.decorations.from(field);
},
});// codeblockHeader
function createDecorationWidget(textToDisplay: string, languageName: string, specificHeader: boolean) {
return Decoration.widget({
widget: new TextAboveCodeblockWidget(textToDisplay, languageName, specificHeader), block: true});
return Decoration.widget({
widget: new TextAboveCodeblockWidget(textToDisplay, languageName, specificHeader), block: true});
}// createDecorationWidget
const Collapse = StateEffect.define(), UnCollapse = StateEffect.define()
let pluginSettings: CodeblockCustomizerSettings;
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)
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});
class TextAboveCodeblockWidget extends WidgetType {
text: string;
observer: MutationObserver;
view: EditorView;
text: string;
observer: MutationObserver;
view: EditorView;
constructor(text: string, Lang: string, specificHeader: boolean) {
super();
this.text = text;
this.Lang = Lang;
this.specificHeader = specificHeader;
this.observer = new MutationObserver(this.handleMutation);
}
handleMutation = (mutations, view) => {
mutations.forEach(mutation => {
if (mutation.target.hasAttribute("data-clicked")){
handleClick(this.view, mutation.target);
//this.view.update([]);
//this.view.state.update();
//EditorView.requestMeasure;
}
});
//this.view.update([]);
//this.view.state.update();
//this.view.requestMeasure();
}
eq(other: TextAboveCodeblockWidget) {
return other.text == this.text && other.Lang == this.Lang && other.specificHeader == this.specificHeader
}
toDOM(view: EditorView): HTMLElement {
this.view = view;
const container = createContainer(this.specificHeader);
if (this.Lang){
const Icon = getLanguageIcon(this.Lang)
if (Icon) {
container.appendChild(createCodeblockIcon(this.Lang));
}
container.appendChild(createCodeblockLang(this.Lang));
}
constructor(text: string, Lang: string, specificHeader: boolean) {
super();
this.text = text;
this.Lang = Lang;
this.specificHeader = specificHeader;
this.observer = new MutationObserver(this.handleMutation);
}
handleMutation = (mutations, view) => {
mutations.forEach(mutation => {
if (mutation.target.hasAttribute("data-clicked")){
handleClick(this.view, mutation.target);
//this.view.update([]);
//this.view.state.update();
//EditorView.requestMeasure;
}
});
//this.view.update([]);
//this.view.state.update();
//this.view.requestMeasure();
}
eq(other: TextAboveCodeblockWidget) {
return other.text == this.text && other.Lang == this.Lang && other.specificHeader == this.specificHeader
}
toDOM(view: EditorView): HTMLElement {
this.view = view;
const container = createContainer(this.specificHeader);
if (this.Lang){
const Icon = getLanguageIcon(this.Lang)
if (Icon) {
container.appendChild(createCodeblockIcon(this.Lang));
}
container.appendChild(createCodeblockLang(this.Lang));
}
container.appendChild(createFileName(this.text));
this.observer.view = view;
this.observer.observe(container, { attributes: true });
container.addEventListener("mousedown", event => {
container.setAttribute("data-clicked", "true");
});
//EditorView.requestMeasure;
container.appendChild(createFileName(this.text));
this.observer.view = view;
this.observer.observe(container, { attributes: true });
container.addEventListener("mousedown", event => {
container.setAttribute("data-clicked", "true");
});
//EditorView.requestMeasure;
return container;
}
destroy(dom: HTMLElement) {
dom.removeAttribute("data-clicked");
dom.removeEventListener("mousedown", handleClick);
this.observer.disconnect();
}
return container;
}
destroy(dom: HTMLElement) {
dom.removeAttribute("data-clicked");
dom.removeEventListener("mousedown", handleClick);
this.observer.disconnect();
}
ignoreEvent() { return false; }
ignoreEvent() { return false; }
}// TextAboveCodeblockWidget
export function handleClick(view: EditorView, target: HTMLElement){
//view.state.update();
//view.update([]);
//view.requestMeasure({});
const Pos = view.posAtDOM(target);
//view.state.update();
//view.update([]);
//view.requestMeasure({});
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;
}
}// for
}// handleClick
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;
}
}// for
}// handleClick

View file

@ -3,228 +3,5 @@ import { MarkdownView, MarkdownPostProcessorContext, sanitizeHTMLToDom } from "o
import { searchString, getHighlightedLines, getLanguageName, isExcluded, getLanguageIcon, createContainer, createCodeblockLang, createCodeblockIcon, createFileName } from "./Utils";
export async function ReadingView(codeBlockElement: HTMLElement, context: MarkdownPostProcessorContext, plugin: CodeblockCustomizerPlugin) {
const codeElm: HTMLElement = codeBlockElement.querySelector('pre > code');
if (!codeElm)
return;
const classRegex = /^language-\S+/;
const match = Array.from(codeElm.classList).some(className => classRegex.test(className));
if (match)
while(!codeElm.classList.contains("is-loaded"))
await sleep(2);
const codeBlockSectionInfo = context.getSectionInfo(codeElm);
let codeBlockFirstLine = "";
if (codeBlockSectionInfo) {
const view = plugin.app.workspace.getActiveViewOfType(MarkdownView);
if (view && view.editor)
codeBlockFirstLine = view.editor.getLine(codeBlockSectionInfo.lineStart);
} else {
// PDF export
const file = plugin.app.vault.getAbstractFileByPath(context.sourcePath);
if (!file) {
//console.error(`File not found: ${context.sourcePath}`);
return;
}
const cache = plugin.app.metadataCache.getCache(context.sourcePath);
const fileContent = await plugin.app.vault.cachedRead(<TFile> file).catch((error) => {
//console.error(`Error reading file: ${error.message}`);
return '';
});
const fileContentLines = fileContent.split(/\n/g);
const codeBlockFirstLines: string[] = [];
if (cache.sections) {
for (const element of cache.sections) {
if (element.type === 'code') {
const lineStart = element.position.start.line;
const codeBlockFirstLine = fileContentLines[lineStart];
if (!isAdmonition(codeBlockFirstLine)) {
codeBlockFirstLines.push(codeBlockFirstLine);
}
}
}
} else {
//console.error(`Metadata cache not found for file: ${context.sourcePath}`);
return;
}
try {
await PDFExport(codeBlockElement, plugin, codeBlockFirstLines);
} catch (error) {
//console.error(`Error exporting to PDF: ${error.message}`);
return;
}
return;
}
const codeBlockLang = searchString(codeBlockFirstLine, "```");
const highlightedLinesParams = searchString(codeBlockFirstLine, "HL:");
const linesToHighlight = getHighlightedLines(highlightedLinesParams);
const FileName = searchString(codeBlockFirstLine, "file:");
const Fold = searchString(codeBlockFirstLine, "fold");
const lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
const showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const alternateColors = plugin.settings.alternateColors || [];
let altHL = [];
for (const { name } of alternateColors) {
const altParams = searchString(codeBlockFirstLine, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
let isCodeBlockExcluded = false;
isCodeBlockExcluded = isExcluded(codeBlockFirstLine, plugin.settings.ExcludeLangs);
const codeElements = codeBlockElement.getElementsByTagName("code");
const codeBlockPreElement: HTMLPreElement | null = codeBlockElement.querySelector("pre:not(.frontmatter)");
if (codeBlockPreElement === null) {
return;
}
if (!isCodeBlockExcluded) {
codeBlockPreElement.classList.add(`codeblock-customizer-pre`);
}
AddHeaderAndHighlight(isCodeBlockExcluded, FileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL );
}// ReadingView
function isAdmonition(lineText: string): boolean {
const adTypes = ["ad-note", "ad-seealso", "ad-abstract", "ad-summary", "ad-tldr", "ad-info", "ad-todo", "ad-tip", "ad-hint", "ad-important", "ad-success", "ad-check", "ad-done", "ad-question", "ad-help", "ad-faq", "ad-warning", "ad-caution", "ad-attention", "ad-failure", "ad-fail", "ad-missing", "ad-danger", "ad-error", "ad-bug", "ad-example", "ad-quote", "ad-cite"]; //TODO (@mayurankv) refactor to use regex
const codeBlockLang = searchString(lineText, "```");
return adTypes.some((adType) => codeBlockLang && codeBlockLang.startsWith(adType));
}// isAdmonition
function HeaderWidget(preElements, textToDisplay: string, specificHeader: boolean, codeblockLanguage: string, Collapse: boolean) {
const parent = preElements.parentNode;
const container = createContainer(specificHeader);
if (codeblockLanguage){
const Icon = getLanguageIcon(codeblockLanguage)
if (Icon) {
container.appendChild(createCodeblockIcon(codeblockLanguage));
}
container.appendChild(createCodeblockLang(codeblockLanguage));
}
container.appendChild(createFileName(textToDisplay));
parent.insertBefore(container, preElements);
// Add event listener to the widget element
container.addEventListener("click", function() {
// Toggle the "collapsed" class on the codeblock element
preElements.classList.toggle("codeblock-customizer-codeblock-collapsed");
});
if (Collapse) {
preElements.classList.add(`codeblock-customizer-codeblock-collapsed`);
}
}// HeaderWidget
function createLineNumberElement(lineNumber,showNumbers) {
const lineNumberWrapper = document.createElement("div");
lineNumberWrapper.classList.add(`codeblock-customizer-line-number${showNumbers?'':'-hide'}`);
lineNumberWrapper.setText(lineNumber);
return lineNumberWrapper;
}// createLineNumberElement
function createLineTextElement(line) {
const lineText = line !== "" ? line : "<br>";
const sanitizedText = sanitizeHTMLToDom(lineText);
const lineContentWrapper = createDiv({cls: `codeblock-customizer-line-text`, text: sanitizedText});
return lineContentWrapper;
}// createLineTextElement
function highlightLines(codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL) {
for (let i = 0; i < codeElements.length; i++) {
let lines = codeElements[i].innerHTML.split("\n");
const preElm = codeElements[i].parentNode;
if (preElm === null || preElm.nodeName !== "PRE") // only process pre > code elements
return;
if (lines.length == 1) {
lines = ['',''];
}
codeElements[i].innerHTML = "";
for (let j = 0; j < lines.length - 1; j++) {
const line = lines[j];
const lineNumber = j + 1;
const isHighlighted = linesToHighlight.includes(lineNumber);
const altHLMatch = altHL.filter((hl) => hl.lineNumber === lineNumber);
// create line element
const lineWrapper = document.createElement("div");
if (isHighlighted) {
lineWrapper.classList.add(`codeblock-customizer-line-highlighted`);
}
else if (altHLMatch.length > 0) {
lineWrapper.classList.add(`codeblock-customizer-line-highlighted-${altHLMatch[0].name.replace(/\s+/g, '-').toLowerCase()}`);
}
codeElements[i].appendChild(lineWrapper);
// create line number element
const lineNumberEl = createLineNumberElement(lineNumber+lineNumberOffset,showNumbers);
lineWrapper.appendChild(lineNumberEl);
// create line text element
const lineTextEl = createLineTextElement(line);
lineWrapper.appendChild(lineTextEl);
}
}
}// highlightLines
function AddHeaderAndHighlight(isCodeBlockExcluded, fileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL ){
if (!isCodeBlockExcluded) {
let specificHeader = true;
if (fileName === null || fileName === "") {
if (Fold) {
fileName = 'Collapsed Code';
} else {
fileName = '';
specificHeader = false;
}
}
HeaderWidget(codeBlockPreElement, fileName, specificHeader, getLanguageName(codeBlockLang), Fold);
highlightLines(codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL);
if (codeBlockPreElement.parentElement) {
codeBlockPreElement.parentElement.classList.add(`codeblock-customizer-pre-parent`);
}
}
}// AddHeaderAndHighlight
function PDFExport(codeBlockElement: HTMLElement, plugin: CodeblockCustomizerPlugin, codeBlockFirstLines: string[]) {
const codeBlocks = codeBlockElement.querySelectorAll('pre > code');
codeBlocks.forEach((codeElm, key) => {
const codeBlockFirstLine = codeBlockFirstLines[key];
const codeBlockLang = searchString(codeBlockFirstLine, "```");
const highlightedLinesParams = searchString(codeBlockFirstLine, "HL:");
const linesToHighlight = getHighlightedLines(highlightedLinesParams);
const fileName = searchString(codeBlockFirstLine, "file:");
const Fold = searchString(codeBlockFirstLine, "fold");
const lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
const showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const alternateColors = plugin.settings.alternateColors || [];
let altHL = [];
for (const { name, _ } of alternateColors) {
const altParams = searchString(codeBlockFirstLine, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
let isCodeBlockExcluded = false;
isCodeBlockExcluded = isExcluded(codeBlockFirstLine, plugin.settings.ExcludeLangs);
const codeBlockPreElement: HTMLPreElement | null = codeElm.parentElement;
if (codeBlockPreElement === null) {
return;
}
if (!isCodeBlockExcluded){
codeBlockPreElement.classList.add(`codeblock-customizer-pre`);
}
const codeElements = codeBlockPreElement.getElementsByTagName("code");
AddHeaderAndHighlight(isCodeBlockExcluded, fileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL );
})
}// PDFExport
}

230
src/ReadingViewOld.ts Normal file
View file

@ -0,0 +1,230 @@
import { MarkdownView, MarkdownPostProcessorContext, sanitizeHTMLToDom } from "obsidian";
import { searchString, getHighlightedLines, getLanguageName, isExcluded, getLanguageIcon, createContainer, createCodeblockLang, createCodeblockIcon, createFileName } from "./Utils";
export async function ReadingView(codeBlockElement: HTMLElement, context: MarkdownPostProcessorContext, plugin: CodeblockCustomizerPlugin) {
const codeElm: HTMLElement = codeBlockElement.querySelector('pre > code');
if (!codeElm)
return;
const classRegex = /^language-\S+/;
const match = Array.from(codeElm.classList).some(className => classRegex.test(className));
if (match)
while(!codeElm.classList.contains("is-loaded"))
await sleep(2);
const codeBlockSectionInfo = context.getSectionInfo(codeElm);
let codeBlockFirstLine = "";
if (codeBlockSectionInfo) {
const view = plugin.app.workspace.getActiveViewOfType(MarkdownView);
if (view && view.editor)
codeBlockFirstLine = view.editor.getLine(codeBlockSectionInfo.lineStart);
} else {
// PDF export
const file = plugin.app.vault.getAbstractFileByPath(context.sourcePath);
if (!file) {
//console.error(`File not found: ${context.sourcePath}`);
return;
}
const cache = plugin.app.metadataCache.getCache(context.sourcePath);
const fileContent = await plugin.app.vault.cachedRead(<TFile> file).catch((error) => {
//console.error(`Error reading file: ${error.message}`);
return '';
});
const fileContentLines = fileContent.split(/\n/g);
const codeBlockFirstLines: string[] = [];
if (cache.sections) {
for (const element of cache.sections) {
if (element.type === 'code') {
const lineStart = element.position.start.line;
const codeBlockFirstLine = fileContentLines[lineStart];
if (!isAdmonition(codeBlockFirstLine)) {
codeBlockFirstLines.push(codeBlockFirstLine);
}
}
}
} else {
//console.error(`Metadata cache not found for file: ${context.sourcePath}`);
return;
}
try {
await PDFExport(codeBlockElement, plugin, codeBlockFirstLines);
} catch (error) {
//console.error(`Error exporting to PDF: ${error.message}`);
return;
}
return;
}
const codeBlockLang = searchString(codeBlockFirstLine, "```");
const highlightedLinesParams = searchString(codeBlockFirstLine, "HL:");
const linesToHighlight = getHighlightedLines(highlightedLinesParams);
const FileName = searchString(codeBlockFirstLine, "file:");
const Fold = searchString(codeBlockFirstLine, "fold");
const lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
const showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const alternateColors = plugin.settings.alternateColors || [];
let altHL = [];
for (const { name } of alternateColors) {
const altParams = searchString(codeBlockFirstLine, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
let isCodeBlockExcluded = false;
isCodeBlockExcluded = isExcluded(codeBlockFirstLine, plugin.settings.ExcludeLangs);
const codeElements = codeBlockElement.getElementsByTagName("code");
const codeBlockPreElement: HTMLPreElement | null = codeBlockElement.querySelector("pre:not(.frontmatter)");
if (codeBlockPreElement === null) {
return;
}
if (!isCodeBlockExcluded) {
codeBlockPreElement.classList.add(`codeblock-customizer-pre`);
}
AddHeaderAndHighlight(isCodeBlockExcluded, FileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL );
}// ReadingView
function isAdmonition(lineText: string): boolean {
const adTypes = ["ad-note", "ad-seealso", "ad-abstract", "ad-summary", "ad-tldr", "ad-info", "ad-todo", "ad-tip", "ad-hint", "ad-important", "ad-success", "ad-check", "ad-done", "ad-question", "ad-help", "ad-faq", "ad-warning", "ad-caution", "ad-attention", "ad-failure", "ad-fail", "ad-missing", "ad-danger", "ad-error", "ad-bug", "ad-example", "ad-quote", "ad-cite"]; //TODO (@mayurankv) refactor to use regex
const codeBlockLang = searchString(lineText, "```");
return adTypes.some((adType) => codeBlockLang && codeBlockLang.startsWith(adType));
}// isAdmonition
function HeaderWidget(preElements, textToDisplay: string, specificHeader: boolean, codeblockLanguage: string, Collapse: boolean) {
const parent = preElements.parentNode;
const container = createContainer(specificHeader);
if (codeblockLanguage){
const Icon = getLanguageIcon(codeblockLanguage)
if (Icon) {
container.appendChild(createCodeblockIcon(codeblockLanguage));
}
container.appendChild(createCodeblockLang(codeblockLanguage));
}
container.appendChild(createFileName(textToDisplay));
parent.insertBefore(container, preElements);
// Add event listener to the widget element
container.addEventListener("click", function() {
// Toggle the "collapsed" class on the codeblock element
preElements.classList.toggle("codeblock-customizer-codeblock-collapsed");
});
if (Collapse) {
preElements.classList.add(`codeblock-customizer-codeblock-collapsed`);
}
}// HeaderWidget
function createLineNumberElement(lineNumber,showNumbers) {
const lineNumberWrapper = document.createElement("div");
lineNumberWrapper.classList.add(`codeblock-customizer-line-number${showNumbers?'':'-hide'}`);
lineNumberWrapper.setText(lineNumber);
return lineNumberWrapper;
}// createLineNumberElement
function createLineTextElement(line) {
const lineText = line !== "" ? line : "<br>";
const sanitizedText = sanitizeHTMLToDom(lineText);
const lineContentWrapper = createDiv({cls: `codeblock-customizer-line-text`, text: sanitizedText});
return lineContentWrapper;
}// createLineTextElement
function highlightLines(codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL) {
for (let i = 0; i < codeElements.length; i++) {
let lines = codeElements[i].innerHTML.split("\n");
const preElm = codeElements[i].parentNode;
if (preElm === null || preElm.nodeName !== "PRE") // only process pre > code elements
return;
if (lines.length == 1) {
lines = ['',''];
}
codeElements[i].innerHTML = "";
for (let j = 0; j < lines.length - 1; j++) {
const line = lines[j];
const lineNumber = j + 1;
const isHighlighted = linesToHighlight.includes(lineNumber);
const altHLMatch = altHL.filter((hl) => hl.lineNumber === lineNumber);
// create line element
const lineWrapper = document.createElement("div");
if (isHighlighted) {
lineWrapper.classList.add(`codeblock-customizer-line-highlighted`);
}
else if (altHLMatch.length > 0) {
lineWrapper.classList.add(`codeblock-customizer-line-highlighted-${altHLMatch[0].name.replace(/\s+/g, '-').toLowerCase()}`);
}
codeElements[i].appendChild(lineWrapper);
// create line number element
const lineNumberEl = createLineNumberElement(lineNumber+lineNumberOffset,showNumbers);
lineWrapper.appendChild(lineNumberEl);
// create line text element
const lineTextEl = createLineTextElement(line);
lineWrapper.appendChild(lineTextEl);
}
}
}// highlightLines
function AddHeaderAndHighlight(isCodeBlockExcluded, fileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL ){
if (!isCodeBlockExcluded) {
let specificHeader = true;
if (fileName === null || fileName === "") {
if (Fold) {
fileName = 'Collapsed Code';
} else {
fileName = '';
specificHeader = false;
}
}
HeaderWidget(codeBlockPreElement, fileName, specificHeader, getLanguageName(codeBlockLang), Fold);
highlightLines(codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL);
if (codeBlockPreElement.parentElement) {
codeBlockPreElement.parentElement.classList.add(`codeblock-customizer-pre-parent`);
}
}
}// AddHeaderAndHighlight
function PDFExport(codeBlockElement: HTMLElement, plugin: CodeblockCustomizerPlugin, codeBlockFirstLines: string[]) {
const codeBlocks = codeBlockElement.querySelectorAll('pre > code');
codeBlocks.forEach((codeElm, key) => {
const codeBlockFirstLine = codeBlockFirstLines[key];
const codeBlockLang = searchString(codeBlockFirstLine, "```");
const highlightedLinesParams = searchString(codeBlockFirstLine, "HL:");
const linesToHighlight = getHighlightedLines(highlightedLinesParams);
const fileName = searchString(codeBlockFirstLine, "file:");
const Fold = searchString(codeBlockFirstLine, "fold");
const lineNumberOffset = 0; //searchString(codeBlockFirstLine, "ln:")//TODO (@mayurankv) Set line number offset here - Should be ln_value - 1 since it is offset, not starting line number - 0 if true OR false
const showNumbers = true; //TODO (@mayurankv) Set showNumbers to be true if ln:<number> or ln:true or false if ln:false
const alternateColors = plugin.settings.alternateColors || [];
let altHL = [];
for (const { name, _ } of alternateColors) {
const altParams = searchString(codeBlockFirstLine, `${name}:`);
altHL = altHL.concat(getHighlightedLines(altParams).map((lineNumber) => ({ name, lineNumber })));
}
let isCodeBlockExcluded = false;
isCodeBlockExcluded = isExcluded(codeBlockFirstLine, plugin.settings.ExcludeLangs);
const codeBlockPreElement: HTMLPreElement | null = codeElm.parentElement;
if (codeBlockPreElement === null) {
return;
}
if (!isCodeBlockExcluded){
codeBlockPreElement.classList.add(`codeblock-customizer-pre`);
}
const codeElements = codeBlockPreElement.getElementsByTagName("code");
AddHeaderAndHighlight(isCodeBlockExcluded, fileName, codeBlockPreElement, codeBlockLang, Fold, codeElements, showNumbers, lineNumberOffset, linesToHighlight, altHL );
})
}// PDFExport

View file

@ -11,236 +11,238 @@ export type Display = "none" | "title_only" | "always";
// Interface Creation
export interface CodeblockCustomizerThemeModeColors {
codeblock: {
backgroundColor: Color;
textColor: Color;
},
gutter: {
backgroundColor: Color;
textColor: Color;
},
header: {
backgroundColor: Color;
title: {
textColor: Color;
},
languageTag: {
backgroundColor: Color;
textColor: Color;
},
lineColor: Color;
},
highlights: {
activeCodeblockLineColor: Color;
activeEditorLineColor: Color;
defaultColor: Color;
alternativeHighlights: Record<string,Color>;
},
codeblock: {
backgroundColor: Color;
textColor: Color;
},
gutter: {
backgroundColor: Color;
textColor: Color;
},
header: {
backgroundColor: Color;
title: {
textColor: Color;
},
languageTag: {
backgroundColor: Color;
textColor: Color;
},
lineColor: Color;
},
highlights: {
activeCodeblockLineColor: Color;
activeEditorLineColor: Color;
defaultColor: Color;
alternativeHighlights: Record<string,Color>;
},
}
export interface CodeblockCustomizerThemeSettings {
codeblock: {
lineNumbers: boolean;
curvature: number;
},
gutter: {
highlight: boolean;
},
header: {
title: {
textFont: string;
textBold: boolean;
textItalic: boolean;
},
languageTag: {
display: Display;
textFont: string;
textBold: boolean;
textItalic: boolean;
},
languageIcon: {
display: Display;
displayColor: boolean;
},
},
highlights: {
activeCodeblockLine: boolean;
activeEditorLine: boolean;
},
advanced: {
gradientHighlights: boolean;
gradientHighlightsColorStop: Percentage;
languageBorderColor: boolean;
iconSize: number;
};
codeblock: {
lineNumbers: boolean;
curvature: number;
},
gutter: {
highlight: boolean;
},
header: {
title: {
textFont: string;
textBold: boolean;
textItalic: boolean;
},
languageTag: {
display: Display;
textFont: string;
textBold: boolean;
textItalic: boolean;
},
languageIcon: {
display: Display;
displayColor: boolean;
},
collapsePlaceholder: string;
},
highlights: {
activeCodeblockLine: boolean;
activeEditorLine: boolean;
},
advanced: {
gradientHighlights: boolean;
gradientHighlightsColorStop: Percentage;
languageBorderColor: boolean;
iconSize: number;
};
}
export interface CodeblockCustomizerThemeColors {
light: CodeblockCustomizerThemeModeColors;
dark: CodeblockCustomizerThemeModeColors;
light: CodeblockCustomizerThemeModeColors;
dark: CodeblockCustomizerThemeModeColors;
}
export interface CodeblockCustomizerTheme {
settings: CodeblockCustomizerThemeSettings;
colors: CodeblockCustomizerThemeColors;
settings: CodeblockCustomizerThemeSettings;
colors: CodeblockCustomizerThemeColors;
}
export interface CodeblockCustomizerSettings {
themes: Record<string,CodeblockCustomizerTheme>;
selectedTheme: string;
defaultTheme: string;
currentTheme: CodeblockCustomizerTheme;
newTheme: {
name: string;
default: boolean;
},
newHighlight: string;
excludedLangs: string;
themes: Record<string,CodeblockCustomizerTheme>;
selectedTheme: string;
defaultTheme: string;
currentTheme: CodeblockCustomizerTheme;
newTheme: {
name: string;
default: boolean;
},
newHighlight: string;
excludedLangs: string;
}
// Theme Defaults
const THEME_DEFAULT_SETTINGS: CodeblockCustomizerThemeSettings = {
codeblock: {
lineNumbers: true,
curvature: 10,
},
gutter: {
highlight: true,
},
header: {
title: {
textFont: '',
textBold: false,
textItalic: false,
},
languageTag: {
display: "always",
textFont: '',
textBold: true,
textItalic: true,
},
languageIcon: {
display: "always",
displayColor: true,
},
},
highlights: {
activeCodeblockLine: true,
activeEditorLine: false,
},
advanced: {
gradientHighlights: false,
gradientHighlightsColorStop: '70%',
languageBorderColor: false,
iconSize: 28,
},
}
export const NEW_THEME_DEFAULT: {name: string, default: boolean} = {
name: '',
default: false,
name: '',
default: false,
}
const THEME_DEFAULT_SETTINGS: CodeblockCustomizerThemeSettings = {
codeblock: {
lineNumbers: true,
curvature: 10,
},
gutter: {
highlight: true,
},
header: {
title: {
textFont: '',
textBold: false,
textItalic: false,
},
languageTag: {
display: "always",
textFont: '',
textBold: true,
textItalic: true,
},
languageIcon: {
display: "always",
displayColor: true,
},
collapsePlaceholder: '',
},
highlights: {
activeCodeblockLine: true,
activeEditorLine: false,
},
advanced: {
gradientHighlights: false,
gradientHighlightsColorStop: '70%',
languageBorderColor: false,
iconSize: 28,
},
}
export const THEME_FALLBACK_COLORS: CodeblockCustomizerThemeModeColors = {
codeblock: {
backgroundColor: '--code-background',
textColor: '--code-normal',
},
gutter: {
backgroundColor: '--code-background',
textColor: '--code-comment',
},
header: {
backgroundColor: '--code-background',
title: {
textColor: '--code-comment',
},
languageTag: {
backgroundColor: '--code-background',
textColor: '--code-comment',
},
lineColor: '--code-background',
},
highlights: {
activeCodeblockLineColor: '--color-base-30',
activeEditorLineColor: '--color-base-20',
defaultColor: '--text-highlight-bg',
alternativeHighlights: {},
},
codeblock: {
backgroundColor: '--code-background',
textColor: '--code-normal',
},
gutter: {
backgroundColor: '--code-background',
textColor: '--code-comment',
},
header: {
backgroundColor: '--code-background',
title: {
textColor: '--code-comment',
},
languageTag: {
backgroundColor: '--code-background',
textColor: '--code-comment',
},
lineColor: '--code-background',
},
highlights: {
activeCodeblockLineColor: '--color-base-30',
activeEditorLineColor: '--color-base-20',
defaultColor: '--text-highlight-bg',
alternativeHighlights: {},
},
}
// Theme Creation
const DEFAULT_THEME: CodeblockCustomizerTheme = {
settings: THEME_DEFAULT_SETTINGS,
colors: {
light: THEME_FALLBACK_COLORS,
dark: THEME_FALLBACK_COLORS,
},
settings: THEME_DEFAULT_SETTINGS,
colors: {
light: THEME_FALLBACK_COLORS,
dark: THEME_FALLBACK_COLORS,
},
}
const SOLARIZED_THEME: CodeblockCustomizerTheme = {
settings: THEME_DEFAULT_SETTINGS,
colors: {
light: {
codeblock: {
backgroundColor: '#FCF6E4',
textColor: '#bababa',
},
gutter: {
backgroundColor: '#EDE8D6',
textColor: '#6c6c6c',
},
header: {
backgroundColor: '#D5CCB4',
title: {
textColor: '#866704',
},
languageTag: {
backgroundColor: '#B8B5AA',
textColor: '#C25F30',
},
lineColor: '#EDD489',
},
highlights: {
activeCodeblockLineColor: '#EDE8D6',
activeEditorLineColor: '#60460633',
defaultColor: '#E9DFBA',
alternativeHighlights: {},
},
},
dark: {
codeblock: {
backgroundColor: '#002b36',
textColor: '#bababa',
},
gutter: {
backgroundColor: '#073642',
textColor: '#6c6c6c',
},
header: {
backgroundColor: '#0a4554',
title: {
textColor: '#dadada',
},
languageTag: {
backgroundColor: '#008080',
textColor: '#000000',
},
lineColor: '#46cced',
},
highlights: {
activeCodeblockLineColor: '#073642',
activeEditorLineColor: '#468eeb33',
defaultColor: '#054b5c',
alternativeHighlights: {},
},
},
},
settings: THEME_DEFAULT_SETTINGS,
colors: {
light: {
codeblock: {
backgroundColor: '#FCF6E4',
textColor: '#bababa',
},
gutter: {
backgroundColor: '#EDE8D6',
textColor: '#6c6c6c',
},
header: {
backgroundColor: '#D5CCB4',
title: {
textColor: '#866704',
},
languageTag: {
backgroundColor: '#B8B5AA',
textColor: '#C25F30',
},
lineColor: '#EDD489',
},
highlights: {
activeCodeblockLineColor: '#EDE8D6',
activeEditorLineColor: '#60460633',
defaultColor: '#E9DFBA',
alternativeHighlights: {},
},
},
dark: {
codeblock: {
backgroundColor: '#002b36',
textColor: '#bababa',
},
gutter: {
backgroundColor: '#073642',
textColor: '#6c6c6c',
},
header: {
backgroundColor: '#0a4554',
title: {
textColor: '#dadada',
},
languageTag: {
backgroundColor: '#008080',
textColor: '#000000',
},
lineColor: '#46cced',
},
highlights: {
activeCodeblockLineColor: '#073642',
activeEditorLineColor: '#468eeb33',
defaultColor: '#054b5c',
alternativeHighlights: {},
},
},
},
}
// Plugin default settings
export const DEFAULT_SETTINGS: CodeblockCustomizerSettings = {
themes: {
'Default': DEFAULT_THEME,
'Solarized': SOLARIZED_THEME,
},
selectedTheme: 'Default',
defaultTheme: 'Default',
currentTheme: structuredClone(DEFAULT_THEME),
newTheme: NEW_THEME_DEFAULT,
newHighlight: '',
excludedLangs: "dataview, dataviewjs, ad-*",
themes: {
'Default': DEFAULT_THEME,
'Solarized': SOLARIZED_THEME,
},
selectedTheme: 'Default',
defaultTheme: 'Default',
currentTheme: structuredClone(DEFAULT_THEME),
newTheme: NEW_THEME_DEFAULT,
newHighlight: '',
excludedLangs: "dataview, dataviewjs, ad-*",
}

File diff suppressed because it is too large Load diff

View file

@ -2,342 +2,342 @@ import { Languages, manualLang, Icons } from "./Const";
import { CodeblockCustomizerSettings } from "./Settings";
export function splitAndTrimString(str) {
if (!str) {
return [];
}
// Replace * with .*
str = str.replace(/\*/g, '.*');
if (!str.includes(",")) {
return [str];
}
return str.split(",").map(s => s.trim());
if (!str) {
return [];
}
// Replace * with .*
str = str.replace(/\*/g, '.*');
if (!str.includes(",")) {
return [str];
}
return str.split(",").map(s => s.trim());
}// splitAndTrimString
export function searchString(str, searchTerm) {
const originalStr = str;
str = str.toLowerCase();
searchTerm = searchTerm.toLowerCase();
if (searchTerm === "file:") {
if (str.includes(searchTerm)) {
const startIndex = str.indexOf(searchTerm) + searchTerm.length;
let result = "";
if (str[startIndex] === "\"") {
const endIndex = str.indexOf("\"", startIndex + 1);
if (endIndex !== -1) {
result = originalStr.substring(startIndex + 1, endIndex);
} else {
result = originalStr.substring(startIndex + 1);
}
} else {
const endIndex = str.indexOf(" ", startIndex);
if (endIndex !== -1) {
result = originalStr.substring(startIndex, endIndex);
} else {
result = originalStr.substring(startIndex);
}
}
return result.trim();
}
} else if (searchTerm === "```") {
if (str.startsWith(searchTerm)) {
const startIndex = searchTerm.length;
const endIndex = str.indexOf(" ", startIndex);
let word = "";
if (endIndex !==-1) {
word = originalStr.substring(startIndex, endIndex);
} else {
word = originalStr.substring(startIndex);
}
if (!word.includes(":")) {
if (word.toLowerCase() === "fold")
return null;
else
return word;
}
}
} else if (searchTerm === 'fold') {
if (str.includes(" fold ")) {
return true;
}
const index = str.indexOf(searchTerm);
if (index !== -1 && index === str.length - searchTerm.length && str[index - 1] === " ") {
return true;
}
if (str.includes("```fold ")) {
return true;
}
if (str.includes("```fold") && str.indexOf("```fold") + "```fold".length === str.length) {
return true;
}
return false;
} else {
if (str.includes(searchTerm)) {
const startIndex = str.indexOf(searchTerm) + searchTerm.length;
const endIndex = str.indexOf(" ", startIndex);
if (endIndex !== -1) {
return originalStr.substring(startIndex, endIndex).trim();
} else {
return originalStr.substring(startIndex).trim();
}
}
}
return null;
const originalStr = str;
str = str.toLowerCase();
searchTerm = searchTerm.toLowerCase();
if (searchTerm === "file:") {
if (str.includes(searchTerm)) {
const startIndex = str.indexOf(searchTerm) + searchTerm.length;
let result = "";
if (str[startIndex] === "\"") {
const endIndex = str.indexOf("\"", startIndex + 1);
if (endIndex !== -1) {
result = originalStr.substring(startIndex + 1, endIndex);
} else {
result = originalStr.substring(startIndex + 1);
}
} else {
const endIndex = str.indexOf(" ", startIndex);
if (endIndex !== -1) {
result = originalStr.substring(startIndex, endIndex);
} else {
result = originalStr.substring(startIndex);
}
}
return result.trim();
}
} else if (searchTerm === "```") {
if (str.startsWith(searchTerm)) {
const startIndex = searchTerm.length;
const endIndex = str.indexOf(" ", startIndex);
let word = "";
if (endIndex !==-1) {
word = originalStr.substring(startIndex, endIndex);
} else {
word = originalStr.substring(startIndex);
}
if (!word.includes(":")) {
if (word.toLowerCase() === "fold")
return null;
else
return word;
}
}
} else if (searchTerm === 'fold') {
if (str.includes(" fold ")) {
return true;
}
const index = str.indexOf(searchTerm);
if (index !== -1 && index === str.length - searchTerm.length && str[index - 1] === " ") {
return true;
}
if (str.includes("```fold ")) {
return true;
}
if (str.includes("```fold") && str.indexOf("```fold") + "```fold".length === str.length) {
return true;
}
return false;
} else {
if (str.includes(searchTerm)) {
const startIndex = str.indexOf(searchTerm) + searchTerm.length;
const endIndex = str.indexOf(" ", startIndex);
if (endIndex !== -1) {
return originalStr.substring(startIndex, endIndex).trim();
} else {
return originalStr.substring(startIndex).trim();
}
}
}
return null;
}//searchString
export function getHighlightedLines(params: string): number[] {
if (!params) {
return [];
}
if (!params) {
return [];
}
const trimmedParams = params.trim();
const lines = trimmedParams.split(",");
const trimmedParams = params.trim();
const lines = trimmedParams.split(",");
return lines.map(line => {
if (line.includes("-")) {
const range = line.split("-");
const start = parseInt(range[0], 10);
const end = parseInt(range[1], 10);
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
return parseInt(line, 10);
}).flat();
return lines.map(line => {
if (line.includes("-")) {
const range = line.split("-");
const start = parseInt(range[0], 10);
const end = parseInt(range[1], 10);
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
return parseInt(line, 10);
}).flat();
}// getHighlightedLines
export function isExcluded(lineText: string, excludeLangs: string[]) : boolean {
const codeBlockLang = searchString(lineText, "```");
const regexLangs = splitAndTrimString(excludeLangs).map(lang => new RegExp(`^${lang.replace(/\*/g, '.*')}$`, 'i'));
for (const regexLang of regexLangs) {
if (codeBlockLang && regexLang.test(codeBlockLang)) {
return true;
}
}
return false;
const codeBlockLang = searchString(lineText, "```");
const regexLangs = splitAndTrimString(excludeLangs).map(lang => new RegExp(`^${lang.replace(/\*/g, '.*')}$`, 'i'));
for (const regexLang of regexLangs) {
if (codeBlockLang && regexLang.test(codeBlockLang)) {
return true;
}
}
return false;
}// isExcluded
export function getLanguageIcon(DisplayName) {
if (!DisplayName)
return "";
if (Icons.hasOwnProperty(DisplayName)) {
return Icons[DisplayName];
}
return null;
if (!DisplayName)
return "";
if (Icons.hasOwnProperty(DisplayName)) {
return Icons[DisplayName];
}
return null;
}// getLanguageIcon
export function getLanguageName(code) {
if (!code)
return "";
code = code.toLowerCase();
if (Languages.hasOwnProperty(code)) {
return Languages[code];
} else if (manualLang.hasOwnProperty(code)) {
return manualLang[code];
} else if (code){
return code.charAt(0).toUpperCase() + code.slice(1);
}
return "";
if (!code)
return "";
code = code.toLowerCase();
if (Languages.hasOwnProperty(code)) {
return Languages[code];
} else if (manualLang.hasOwnProperty(code)) {
return manualLang[code];
} else if (code){
return code.charAt(0).toUpperCase() + code.slice(1);
}
return "";
}// getLanguageName
export const BLOBS: Record<string, string> = {};
export function loadIcons(){
for (const [key, value] of Object.entries(Icons)) {
BLOBS[key.replace(/\s/g, "_")] = URL.createObjectURL(new Blob([`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">${value}</svg>`], { type: "image/svg+xml" }));
}
for (const [key, value] of Object.entries(Icons)) {
BLOBS[key.replace(/\s/g, "_")] = URL.createObjectURL(new Blob([`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">${value}</svg>`], { type: "image/svg+xml" }));
}
}// loadIcons
// Functions for displaying header BEGIN
export function createContainer(specific: boolean) {
const container = document.createElement("div");
container.classList.add(`codeblock-customizer-header-container${specific?'-specific':''}`);
return container;
const container = document.createElement("div");
container.classList.add(`codeblock-customizer-header-container${specific?'-specific':''}`);
return container;
}// createContainer
export function createCodeblockLang(lang: string) {
const codeblockLang = document.createElement("div");
codeblockLang.innerText = lang;
codeblockLang.classList.add(`codeblock-customizer-header-language-tag-${getLanguageName(lang).toLowerCase()}`);
return codeblockLang;
const codeblockLang = document.createElement("div");
codeblockLang.innerText = lang;
codeblockLang.classList.add(`codeblock-customizer-header-language-tag-${getLanguageName(lang).toLowerCase()}`);
return codeblockLang;
}// createCodeblockLang
export function createCodeblockIcon(displayLang: string) {
const div = document.createElement("div");
const img = document.createElement("img");
img.classList.add("codeblock-customizer-icon");
img.width = 28; //32
img.src = BLOBS[displayLang.replace(/\s/g, "_")];
const div = document.createElement("div");
const img = document.createElement("img");
img.classList.add("codeblock-customizer-icon");
img.width = 28; //32
img.src = BLOBS[displayLang.replace(/\s/g, "_")];
div.appendChild(img);
return div;
div.appendChild(img);
return div;
}// createCodeblockIcon
export function createFileName(text: string) {
const fileName = document.createElement("div");
fileName.innerText = text;
fileName.classList.add("codeblock-customizer-header-text");
return fileName;
const fileName = document.createElement("div");
fileName.innerText = text;
fileName.classList.add("codeblock-customizer-header-text");
return fileName;
}// createFileName
// Functions for displaying header END
const stylesDict = {
activeCodeBlockLineColor: 'codeblock-active-line-color',
activeLineColor: 'editor-active-line-color',
backgroundColor: 'codeblock-background-color',
highlightColor: 'highlight-color',
headerColor: 'header-background-color',
headerTextColor: 'header-text-color',
headerLineColor: 'header-line-color',
gutterTextColor: 'gutter-text-color',
gutterBackgroundColor: 'gutter-background-color',
codeBlockLangColor: 'language-tag-text-color',
codeBlockLangBackgroundColor: 'language-tag-background-color',
activeCodeBlockLineColor: 'codeblock-active-line-color',
activeLineColor: 'editor-active-line-color',
backgroundColor: 'codeblock-background-color',
highlightColor: 'highlight-color',
headerColor: 'header-background-color',
headerTextColor: 'header-text-color',
headerLineColor: 'header-line-color',
gutterTextColor: 'gutter-text-color',
gutterBackgroundColor: 'gutter-background-color',
codeBlockLangColor: 'language-tag-text-color',
codeBlockLangBackgroundColor: 'language-tag-background-color',
}
export function updateSettingStyles(settings: CodeblockCustomizerSettings) {
let colorThemes = settings.colorThemes;
let styleId = 'codeblock-customizer-styles'
let styleTag = document.getElementById(styleId);
if (typeof(styleTag) == 'undefined' || styleTag == null) {
styleTag = document.createElement('style');
styleTag.id = styleId;
document.getElementsByTagName('head')[0].appendChild(styleTag);
}
let currentMode = getCurrentMode()
let defaultColors = settings.colorThemes.find((theme) => {return theme['name'] == `${currentMode.charAt(0).toUpperCase()+currentMode.slice(1)} Theme`})['colors'];
let themeColors = settings.colorThemes.find((theme) => {return theme['name'] == settings.SelectedTheme})['colors'];
let currentTheme = {name: 'current', colors: {}};
for (const key of Object.keys(stylesDict)) {
let defaultValue = accessSetting(key,defaultColors).toLowerCase();
let themeValue = accessSetting(key,themeColors).toLowerCase();
let settingsValue = accessSetting(key,settings).toLowerCase();
if (defaultValue !== settingsValue) {
currentTheme['colors'][key] = settingsValue;
} else if (defaultValue !== themeValue) {
currentTheme['colors'][key] = themeValue;
}
}
let altHighlightStyling = settings.alternateColors.reduce((styling,altHighlight) => {return styling + `
.codeblock-customizer-line-highlighted-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()} {
background-color: var(--codeblock-customiser-highlight-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()}-color) !important;
}
`},'');
let textSettingsStyles = `
body.codeblock-customizer [class^="codeblock-customizer-header-language-tag"] {
--codeblock-customizer-language-tag-text-bold: ${settings.header.bCodeblockLangBold?'bold':'normal'};
--codeblock-customizer-language-tag-text-italic: ${settings.header.bCodeblockLangItalic?'italic':'normal'};
}
body.codeblock-customizer .codeblock-customizer-header-text {
--codeblock-customizer-header-text-bold: ${settings.header.bHeaderBold?'bold':'normal'};
--codeblock-customizer-header-text-italic: ${settings.header.bHeaderItalic?'italic':'normal'};
}
`;
styleTag.innerText = colorThemes.reduce((styles,theme) => {
return styles + formatStyles(theme,settings.alternateColors);
},formatStyles(currentTheme,settings.alternateColors)+altHighlightStyling+textSettingsStyles).trim().replace(/[\r\n\s]+/g, ' ');
updateSettingClasses(settings);
let colorThemes = settings.colorThemes;
let styleId = 'codeblock-customizer-styles'
let styleTag = document.getElementById(styleId);
if (typeof(styleTag) == 'undefined' || styleTag == null) {
styleTag = document.createElement('style');
styleTag.id = styleId;
document.getElementsByTagName('head')[0].appendChild(styleTag);
}
let currentMode = getCurrentMode()
let defaultColors = settings.colorThemes.find((theme) => {return theme['name'] == `${currentMode.charAt(0).toUpperCase()+currentMode.slice(1)} Theme`})['colors'];
let themeColors = settings.colorThemes.find((theme) => {return theme['name'] == settings.SelectedTheme})['colors'];
let currentTheme = {name: 'current', colors: {}};
for (const key of Object.keys(stylesDict)) {
let defaultValue = accessSetting(key,defaultColors).toLowerCase();
let themeValue = accessSetting(key,themeColors).toLowerCase();
let settingsValue = accessSetting(key,settings).toLowerCase();
if (defaultValue !== settingsValue) {
currentTheme['colors'][key] = settingsValue;
} else if (defaultValue !== themeValue) {
currentTheme['colors'][key] = themeValue;
}
}
let altHighlightStyling = settings.alternateColors.reduce((styling,altHighlight) => {return styling + `
.codeblock-customizer-line-highlighted-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()} {
background-color: var(--codeblock-customiser-highlight-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()}-color) !important;
}
`},'');
let textSettingsStyles = `
body.codeblock-customizer [class^="codeblock-customizer-header-language-tag"] {
--codeblock-customizer-language-tag-text-bold: ${settings.header.bCodeblockLangBold?'bold':'normal'};
--codeblock-customizer-language-tag-text-italic: ${settings.header.bCodeblockLangItalic?'italic':'normal'};
}
body.codeblock-customizer .codeblock-customizer-header-text {
--codeblock-customizer-header-text-bold: ${settings.header.bHeaderBold?'bold':'normal'};
--codeblock-customizer-header-text-italic: ${settings.header.bHeaderItalic?'italic':'normal'};
}
`;
styleTag.innerText = colorThemes.reduce((styles,theme) => {
return styles + formatStyles(theme,settings.alternateColors);
},formatStyles(currentTheme,settings.alternateColors)+altHighlightStyling+textSettingsStyles).trim().replace(/[\r\n\s]+/g, ' ');
updateSettingClasses(settings);
}// setStyles
function updateSettingClasses(settings) {
document.body.classList.remove("codeblock-customizer-active-line-highlight","codeblock-customizer-active-line-highlight-codeblock","codeblock-customizer-active-line-highlight-editor")
if (settings.bActiveLineHighlight && settings.bActiveCodeblockLineHighlight) {
// Inside and outside of codeblocks with different colors
document.body.classList.add("codeblock-customizer-active-line-highlight");
} else if (settings.bActiveLineHighlight && !settings.bActiveCodeblockLineHighlight) {
// Only outside codeblocks
document.body.classList.add("codeblock-customizer-active-line-highlight-editor");
} else if (!settings.bActiveLineHighlight && settings.bActiveCodeblockLineHighlight) {
// Only inside codeblocks
document.body.classList.add("codeblock-customizer-active-line-highlight-codeblock");
}
if (settings.bEnableLineNumbers) {
document.body.classList.add("codeblock-customizer-show-line-numbers");
} else {
document.body.classList.remove("codeblock-customizer-show-line-numbers");
}
document.body.classList.remove("codeblock-customizer-active-line-highlight","codeblock-customizer-active-line-highlight-codeblock","codeblock-customizer-active-line-highlight-editor")
if (settings.bActiveLineHighlight && settings.bActiveCodeblockLineHighlight) {
// Inside and outside of codeblocks with different colors
document.body.classList.add("codeblock-customizer-active-line-highlight");
} else if (settings.bActiveLineHighlight && !settings.bActiveCodeblockLineHighlight) {
// Only outside codeblocks
document.body.classList.add("codeblock-customizer-active-line-highlight-editor");
} else if (!settings.bActiveLineHighlight && settings.bActiveCodeblockLineHighlight) {
// Only inside codeblocks
document.body.classList.add("codeblock-customizer-active-line-highlight-codeblock");
}
if (settings.bEnableLineNumbers) {
document.body.classList.add("codeblock-customizer-show-line-numbers");
} else {
document.body.classList.remove("codeblock-customizer-show-line-numbers");
}
document.body.classList.remove("codeblock-customizer-show-langnames","codeblock-customizer-show-langnames-always");
if (settings.header.bAlwaysDisplayCodeblockLang && settings.bDisplayCodeBlockLanguage) {
document.body.classList.add("codeblock-customizer-show-langnames-always");
} else if (settings.bDisplayCodeBlockLanguage) {
document.body.classList.add("codeblock-customizer-show-langnames");
}
document.body.classList.remove("codeblock-customizer-show-langnames","codeblock-customizer-show-langnames-always");
if (settings.header.bAlwaysDisplayCodeblockLang && settings.bDisplayCodeBlockLanguage) {
document.body.classList.add("codeblock-customizer-show-langnames-always");
} else if (settings.bDisplayCodeBlockLanguage) {
document.body.classList.add("codeblock-customizer-show-langnames");
}
document.body.classList.remove("codeblock-customizer-show-langicons","codeblock-customizer-show-langicons-always");
if (settings.header.bAlwaysDisplayCodeblockIcon && settings.bDisplayCodeBlockIcon) {
document.body.classList.add("codeblock-customizer-show-langicons-always");
} else if (settings.bDisplayCodeBlockIcon) {
document.body.classList.add("codeblock-customizer-show-langicons");
}
document.body.classList.remove("codeblock-customizer-show-langicons","codeblock-customizer-show-langicons-always");
if (settings.header.bAlwaysDisplayCodeblockIcon && settings.bDisplayCodeBlockIcon) {
document.body.classList.add("codeblock-customizer-show-langicons-always");
} else if (settings.bDisplayCodeBlockIcon) {
document.body.classList.add("codeblock-customizer-show-langicons");
}
if (settings.bGutterHighlight) {
document.body.classList.add('codeblock-customizer-gutter-highlight');
} else {
document.body.classList.remove('codeblock-customizer-gutter-highlight');
}
if (settings.bGutterHighlight) {
document.body.classList.add('codeblock-customizer-gutter-highlight');
} else {
document.body.classList.remove('codeblock-customizer-gutter-highlight');
}
}// updateSettingStyles
function formatStyles(theme: {name: string, colors: CodeblockCustomizerColors},alternateColors) { //TODO (@mayurankv) Add type hint for alternateColors
let current = theme['name'] == "current";
let themeClass = ''
let altHighlightStyles = ''
if (theme['name'] == 'Light Theme') {
themeClass = '.theme-light';
altHighlightStyles = addAltHighlightColors(alternateColors,true);
} else if (theme['name'] == 'Dark Theme') {
themeClass = '.theme-dark';
altHighlightStyles = addAltHighlightColors(alternateColors,false);
} else if (theme['name'] != 'current') {
return '';
// themeClass = theme['name'].replace(/\s+/g, '-').toLowerCase();
}
return `
body.codeblock-customizer${current ?'':themeClass} {
${Object.keys(stylesDict).reduce((variables,key)=>{
let cssVariable = `--codeblock-customizer-${stylesDict[key]}`;
let cssValue = accessSetting(key,theme['colors']);
let cssImportant = (current?' !important':'');
if (cssValue != null) {
return variables + `${cssVariable}: ${cssValue + cssImportant};`;
} else {
return variables;
}
},altHighlightStyles)}
}
`;
let current = theme['name'] == "current";
let themeClass = ''
let altHighlightStyles = ''
if (theme['name'] == 'Light Theme') {
themeClass = '.theme-light';
altHighlightStyles = addAltHighlightColors(alternateColors,true);
} else if (theme['name'] == 'Dark Theme') {
themeClass = '.theme-dark';
altHighlightStyles = addAltHighlightColors(alternateColors,false);
} else if (theme['name'] != 'current') {
return '';
// themeClass = theme['name'].replace(/\s+/g, '-').toLowerCase();
}
return `
body.codeblock-customizer${current ?'':themeClass} {
${Object.keys(stylesDict).reduce((variables,key)=>{
let cssVariable = `--codeblock-customizer-${stylesDict[key]}`;
let cssValue = accessSetting(key,theme['colors']);
let cssImportant = (current?' !important':'');
if (cssValue != null) {
return variables + `${cssVariable}: ${cssValue + cssImportant};`;
} else {
return variables;
}
},altHighlightStyles)}
}
`;
}// formatStyles
function addAltHighlightColors(alternateColors, lightTheme: boolean) { //TODO (@mayurankv) Add type hint for alternateColors
let key = lightTheme?'lightColor':'darkColor';
return alternateColors.reduce((altHighlightStyles,altHighlight) => {return altHighlightStyles + `--codeblock-customiser-highlight-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()}-color: ${altHighlight[key]};`},'')
let key = lightTheme?'lightColor':'darkColor';
return alternateColors.reduce((altHighlightStyles,altHighlight) => {return altHighlightStyles + `--codeblock-customiser-highlight-${altHighlight['name'].replace(/\s+/g, '-').toLowerCase()}-color: ${altHighlight[key]};`},'')
}
function accessSetting(key: string, settings: CodeblockCustomizerSettings) {
if (key in settings) {
return settings[key];
} else if ('header' in settings) {
if (key in settings['header']) {
return settings['header'][key];
} else {
let alt_key = key.charAt(6).toLowerCase()+key.slice(7);
if (alt_key in settings['header']) {
return settings['header'][alt_key];
}
}
} else {
return null;
}
if (key in settings) {
return settings[key];
} else if ('header' in settings) {
if (key in settings['header']) {
return settings['header'][key];
} else {
let alt_key = key.charAt(6).toLowerCase()+key.slice(7);
if (alt_key in settings['header']) {
return settings['header'][alt_key];
}
}
} else {
return null;
}
}
@ -364,14 +364,14 @@ function accessSetting(key: string, settings: CodeblockCustomizerSettings) {
// Setting Management
export function getCurrentMode() {
const body = document.querySelector('body');
if (body !== null){
if (body.classList.contains('theme-light')) {
return "light";
} else if (body.classList.contains('theme-dark')) {
return "dark";
}
}
console.log('Warning: Couldn\'t get current theme');
return "light";
}
const body = document.querySelector('body');
if (body !== null){
if (body.classList.contains('theme-light')) {
return "light";
} else if (body.classList.contains('theme-dark')) {
return "dark";
}
}
console.log('Warning: Couldn\'t get current theme');
return "light";
}

View file

@ -1,77 +1,64 @@
import { Plugin } from "obsidian";
import { Extension } from "@codemirror/state";
import { DEFAULT_SETTINGS, CodeblockCustomizerSettings } from './Settings';
import { codeblockHighlight } from "./CodeBlockHighlight";
import { codeblockHeader, collapseField } from "./Header";
import { ReadingView } from "./ReadingView";
import { ReadingView } from "./ReadingViewOld";
import { SettingsTab } from "./SettingsTab";
import { getCurrentMode, loadIcons, BLOBS, updateSettingStyles } from "./Utils";
// npm i @simonwep/pickr
import { loadIcons, BLOBS, updateSettingStyles } from "./Utils";
export default class CodeblockCustomizerPlugin extends Plugin {
settings: CodeblockCustomizerSettings;
extensions: Extension[];
async onload() {
document.body.classList.add('codeblock-customizer');
await this.loadSettings();
this.extensions = [];
settings: CodeblockCustomizerSettings;
async onload() {
await this.loadSettings();
document.body.classList.add('codeblock-customizer');
// eslint main.ts
/* Problems to solve:
- if a language is excluded then:
- header needs to unfold before removing it,
*/
loadIcons();
loadIcons();
codeblockHeader.settings = this.settings;
collapseField.pluginSettings = this.settings;
this.registerEditorExtension([
codeblockHeader,
collapseField,
codeblockHighlight(this.settings)
]);
const settingsTab = new SettingsTab(this.app, this);
this.addSettingTab(settingsTab);
//updateSettingStyles(this.settings);
this.registerEvent(this.app.workspace.on('css-change', this.handleCssChange.bind(this, settingsTab), this));
codeblockHeader.settings = this.settings;
this.extensions.push(codeblockHeader);
collapseField.pluginSettings = this.settings;
this.extensions.push(collapseField);
// reading mode
this.registerMarkdownPostProcessor(async (el, ctx) => {
await ReadingView(el, ctx, this)
})
this.extensions.push(codeblockHighlight(this.settings));
this.registerEditorExtension(this.extensions);
const settingsTab = new SettingsTab(this.app, this);
this.addSettingTab(settingsTab);
updateSettingStyles(this.settings);
this.registerEvent(this.app.workspace.on('css-change', this.handleCssChange.bind(this, settingsTab), this));
// reading mode
this.registerMarkdownPostProcessor(async (el, ctx) => {
await ReadingView(el, ctx, this)
})
console.log("loading CodeBlock Customizer plugin");
}// onload
handleCssChange(settingsTab) {
console.log('updating css');
}// handleCssChange
onunload() {
console.log("unloading CodeBlock Customizer plugin");
// unload icons
for (const url of Object.values(BLOBS)) {
URL.revokeObjectURL(url)
}
console.log("loading CodeBlock Customizer plugin");
}// onload
handleCssChange(settingsTab) {
console.log('updating css');
}// handleCssChange
onunload() {
console.log("unloading CodeBlock Customizer plugin");
// unload icons
for (const url of Object.values(BLOBS)) {
URL.revokeObjectURL(url)
}
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
this.app.workspace.updateOptions();
// updateSettingStyles(this.settings);
this.app.workspace.updateOptions();
// updateSettingStyles(this.settings);
}
}

File diff suppressed because one or more lines are too long

22
toDo.md
View file

@ -1,22 +0,0 @@
# ToDo
@mayurankv
1. Appearance issues:
1. Too large line numbers with dynamic padding in source mode
2. Folding transition animation? CSS won't work for this.
2. Prevent delay to render in reading mode:
1. Caused by changes to HTML such as:
- Changing highlighted lines
- Changing file name
- Presumably changing line offset
- Presumably changing fold name
3. Refactor to make cleaner
1. Sort settings structure and change corresponding code
2. Modularise code better
3. Reuse functions across live preview and reading mode
4. Extra Settings
1. Placeholder text for collapsed code
1. Global placeholder in settings
2. Optional parameter after fold:
2. Let users redirect certain languages to icon of choice? Definitely not urgent if at all

View file

@ -1,24 +1,24 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
]
},
"include": [
"**/*.ts"
]
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
]
},
"include": [
"**/*.ts"
]
}