2025-05-01 17:22:17 +02:00
|
|
|
import { SelectionRange, StateEffect, Line } from "@codemirror/state";
|
|
|
|
import { ViewPlugin, ViewUpdate } from "@codemirror/view";
|
2023-04-28 02:51:04 +02:00
|
|
|
|
2025-05-01 17:22:17 +02:00
|
|
|
import { EasyMDE } from "../easymde";
|
|
|
|
import { countWords } from "../utils/count-words";
|
2023-04-28 02:51:04 +02:00
|
|
|
|
|
|
|
export class StatusBar {
|
|
|
|
public element: HTMLDivElement;
|
|
|
|
|
|
|
|
private characterCount = 0;
|
|
|
|
private wordCount = 0;
|
|
|
|
private lineCount = 1;
|
|
|
|
private cursorLine = 1;
|
|
|
|
private cursorColumn = 1;
|
|
|
|
|
|
|
|
private selectionStart = 0;
|
|
|
|
private selectionEnd = 0;
|
|
|
|
|
|
|
|
public constructor(private editor: EasyMDE) {
|
2025-05-01 17:22:17 +02:00
|
|
|
this.element = document.createElement("div");
|
|
|
|
this.element.className = "easymde-statusbar";
|
2023-04-28 02:51:04 +02:00
|
|
|
|
|
|
|
// Initial values
|
|
|
|
this.characterCount = this.editor.codemirror.state.doc.length;
|
|
|
|
this.wordCount = countWords(this.editor.codemirror.state.doc);
|
|
|
|
this.lineCount = this.editor.codemirror.state.doc.lines;
|
|
|
|
|
|
|
|
const line = this.editor.codemirror.state.doc.lineAt(
|
|
|
|
this.editor.codemirror.state.selection.main.to,
|
|
|
|
);
|
|
|
|
this.cursorLine = line.number;
|
|
|
|
this.cursorColumn =
|
|
|
|
this.editor.codemirror.state.selection.main.to - line.from + 1;
|
|
|
|
this.selectionStart = this.editor.codemirror.state.selection.main.from;
|
|
|
|
this.selectionEnd = this.editor.codemirror.state.selection.main.to;
|
|
|
|
|
|
|
|
this.editor.codemirror.dispatch({
|
|
|
|
effects: StateEffect.appendConfig.of(
|
|
|
|
ViewPlugin.define(() => ({
|
|
|
|
update: (update: ViewUpdate) => {
|
|
|
|
const document = update.state.doc;
|
|
|
|
const selection = update.state.selection.main;
|
|
|
|
|
|
|
|
this.characterCount = document.length;
|
|
|
|
this.wordCount = countWords(document);
|
|
|
|
this.lineCount = document.lines;
|
|
|
|
|
|
|
|
const direction = this.getSelectionDirection(selection);
|
|
|
|
const toLine = document.lineAt(selection.to);
|
|
|
|
const fromLine = document.lineAt(selection.from);
|
|
|
|
|
|
|
|
let cursorLine: Line;
|
|
|
|
|
2025-05-01 17:22:17 +02:00
|
|
|
if (direction === "left") {
|
2023-04-28 02:51:04 +02:00
|
|
|
// Cursor is at the start of the selection.
|
|
|
|
cursorLine = fromLine;
|
|
|
|
this.cursorColumn =
|
|
|
|
selection.from - cursorLine.from;
|
|
|
|
} else {
|
|
|
|
// Cursor is at the end of the selection, or there is no selection.
|
|
|
|
cursorLine = toLine;
|
|
|
|
this.cursorColumn = selection.to - cursorLine.from;
|
|
|
|
|
|
|
|
if (this.cursorColumn > toLine.length) {
|
|
|
|
// Column is incorrect, can happen when Ctrl+A is used. We need to manually adjust it.
|
|
|
|
this.cursorColumn = toLine.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cursorLine = cursorLine.number;
|
|
|
|
this.selectionStart = selection.from;
|
|
|
|
this.selectionEnd = selection.to;
|
|
|
|
|
|
|
|
// We start counting columns at 1.
|
|
|
|
this.cursorColumn++;
|
|
|
|
|
|
|
|
this.render();
|
|
|
|
},
|
|
|
|
})),
|
|
|
|
),
|
|
|
|
});
|
|
|
|
|
|
|
|
this.render();
|
|
|
|
}
|
|
|
|
|
|
|
|
public render() {
|
|
|
|
this.element.innerHTML = `
|
|
|
|
<span class="status-bar-element">Lines: ${this.lineCount}</span>
|
|
|
|
<span class="status-bar-element">Words: ${this.wordCount}</span>
|
|
|
|
<span class="status-bar-element">Characters: ${this.characterCount}</span>
|
|
|
|
<span class="status-bar-element">Pos: ${this.cursorLine}:${this.cursorColumn}</span>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
private getSelectionDirection(
|
|
|
|
selection: SelectionRange,
|
2025-05-01 17:22:17 +02:00
|
|
|
): "right" | "left" | undefined {
|
2023-04-28 02:51:04 +02:00
|
|
|
return selection.from === this.selectionStart
|
2025-05-01 17:22:17 +02:00
|
|
|
? "right"
|
|
|
|
: // eslint-disable-next-line sonarjs/no-nested-conditional
|
|
|
|
selection.to === this.selectionEnd
|
|
|
|
? "left"
|
|
|
|
: undefined;
|
2023-04-28 02:51:04 +02:00
|
|
|
}
|
|
|
|
}
|