Hi. Is there a possibility to add ruler (vertical line) in v6? In v5 it’s possible. Didn’t find anything similar in the documentation.
That’s not part of the library for 6.x, but the way you’d implement it would be something like this:
-
Create a view plugin that adds an absolutely positioned element to
view.scrollDOM
. Give it some kind of left border so that it’s visible -
On init, and whenever the plugin gets an update that has the
heightChanged
flag set, set the element’sleft
to X *view.defaultCharacterWidth
, and itsheight
toview.contentHeight
.
Would it be possible to give a few more details?
I assume such a plugin would be passed to the extensions
list when initializing CodeMirror. Is there a function that should be used to create the plugin? Also, could you point me to the documentation about how to check the events that trigger a plugin? Thanks!
To help those interested in this feature, here is my basic implementation in TypeScript:
import { EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view";
function generateRulerPlugin() {
let width: number = 0;
let dom: HTMLDivElement;
let defaultCharacterWidth: number = 1;
class RulerPlugin {
containerDom: HTMLDivElement;
constructor(view: EditorView) {
this.containerDom = view.dom.appendChild(document.createElement("div"));
this.containerDom.style.cssText=`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
`
dom = this.containerDom.appendChild(document.createElement("div"));
dom.style.cssText = `
position: absolute;
border-right: 1px dotted gray;
height: 100%;
opacity: 0.7;
`;
// XXX: This should be equal to the amount of padding on a line.
// This value should be extracted from CodeMirror rather than hardcoded.
dom.style.width = "4px";
defaultCharacterWidth = view.defaultCharacterWidth;
updateRulerWidth(80);
}
update(update: ViewUpdate) {
defaultCharacterWidth = update.view.defaultCharacterWidth;
if (update.viewportChanged) {
updateRulerWidth(width, true);
}
}
destroy() {
dom.remove();
}
}
function updateRulerWidth(newWidth: number, force = false) {
if ((newWidth !== width || force) && dom) {
width = newWidth;
dom.style.left = `${width * defaultCharacterWidth}px`;
}
}
return { rulerPlugin: ViewPlugin.fromClass(RulerPlugin), updateRulerWidth };
}
const { rulerPlugin, updateRulerWidth } = generateRulerPlugin();
Add rulerPlugin
to the list of plugins and then call updateRulerWidth
when needed to change the ruler width. The only issue I see is that I don’t know how to get the line padding from CodeMirror, so I have hardcoded 4px, which appears to be the default line padding… @marijn is there a way to extract the line padding so that we can compute the exact pixel location of a character?
Not really—the editor doesn’t track horizontal padding. But you should be able to read it from the DOM.
Will there always be an element with the correct amount of padding (even if there are 0 lines of code displayed)? Any suggestions for a reliable query-selector?
It is possible to replace the entire document with a block widget, but that would be very exceptional, and I guess falling back to a default value in that case won’t cause any trouble. Lines have a cm-line
class and are children of view.contentDOM
.
Thanks for sharing, this allows us to add the equivalent feature in JupyterLab 4; see
It supports multiple rulers.
I had a go at a package for this: @cookshack/codemirror-ruler - npm