@bhsd/codemirror-stickyscroll 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  > VS Code / Monaco-style **sticky scroll** (sticky lines) for [CodeMirror 6](https://codemirror.net/).
4
4
 
5
- This is a fork of [@fazelstudio/codemirror-stickyscroll](https://github.com/fazelstudio/codemirror-stickyscroll) that adds support for [foldService](https://codemirror.net/docs/ref/#language.foldService), which makes it compatible with legacy [StreamParser](https://codemirror.net/docs/ref/#language.StreamParser) languages that register folding.
5
+ This is a fork of [@fazelstudio/codemirror-stickyscroll](https://github.com/fazelstudio/codemirror-stickyscroll)
6
+ that adds support for [foldService](https://codemirror.net/docs/ref/#language.foldService),
7
+ which makes it compatible with legacy [StreamParser](https://codemirror.net/docs/ref/#language.StreamParser)
8
+ languages that register folding. This personal fork also removes some unused features.
6
9
 
7
10
  Sticky lines keep the *opening* lines of the enclosing scopes (function, class,
8
11
  if/loop blocks, …) pinned at the top of the editor while you scroll — exactly
@@ -15,16 +18,17 @@ extension** (no fork of `@codemirror/*`).
15
18
  throttle keeps the bar glued to the scroll position; no "jumpy" updates.
16
19
  - **Click-to-jump with margin compensation** — clicking a sticky line scrolls
17
20
  the target line to the top *plus* the current bar height, so the line you
18
- jump to is never hidden behind the bar. Keyboard (`Enter`/`Space`) + `role="button"` for a11y.
21
+ jump to is never hidden behind the bar. Keyboard (`Enter`/`Space`) +
22
+ `role="button"` for a11y.
19
23
  - **Reuses the consumer's theme** — the bar re-highlights lines through the
20
24
  *active* highlight styles of the editor (`highlightingFor`), or clones the
21
25
  already-rendered DOM line when available. The package **never** registers its
22
26
  own `syntaxHighlighting(...)`.
23
- - **Language-agnostic** — detection is based on `foldNodeProp` or `foldService` from
24
- `@codemirror/language` (the fold services / fold node props that every
27
+ - **Language-agnostic** — detection is based on `foldNodeProp` or `foldService`
28
+ from `@codemirror/language` (the fold services / fold node props that every
25
29
  `@codemirror/lang-*` already registers). It includes a smart, generic denylist
26
- that works across multiple languages (JS/TS, Python, Rust, Go, etc.) out of the box,
27
- and gracefully handles data languages like JSON.
30
+ that works across multiple languages (JS/TS, Python, Rust, Go, etc.) out of
31
+ the box, and gracefully handles data languages like JSON.
28
32
  - **Gutter alignment, horizontal sync, RTL, resize-proof** — line numbers are
29
33
  aligned with the real gutter (width tracked via `ResizeObserver`), the bar
30
34
  follows horizontal scroll, and it reacts to font/zoom/resize changes.
@@ -72,12 +76,9 @@ interface StickyScrollOptions {
72
76
  ```ts
73
77
  import {
74
78
  stickyScroll,
75
- stickyScrollFacet, // the configuration Facet (compose/override per instance)
76
79
  defaultExcludeNode, // default denylist implementation
77
- makeStickyScrollConfig, // merge options with defaults
78
- stickyScrollBaseTheme, // layout-only base theme (no token colors)
80
+ stickyScrollEffect, // StateEffect to trigger a re-render (e.g. after theme change)
79
81
  type StickyScrollOptions,
80
- type StickyLine,
81
82
  } from "@bhsd/codemirror-stickyscroll";
82
83
  ```
83
84
 
package/dist/compute.js CHANGED
@@ -85,6 +85,19 @@ function getStickyContextForRange(state, fromPos, config, t, maxSticky) {
85
85
  const exclude = config.excludeNode;
86
86
  const langName = lang.name;
87
87
  const found = [];
88
+ const pushStickyLine = (open, closeLine, nodeFrom, nodeTo) => {
89
+ const openLine = open.number;
90
+ if (closeLine - openLine + 1 >= minBlockLines && closeLine >= topLineNumber) {
91
+ found.push({
92
+ lineNumber: openLine,
93
+ from: open.from,
94
+ to: open.to,
95
+ text: open.text,
96
+ nodeFrom,
97
+ nodeTo,
98
+ });
99
+ }
100
+ };
88
101
  const services = state.facet(foldService);
89
102
  if (services.length === 0) {
90
103
  let node = t.resolveInner(fromPos, 0);
@@ -115,16 +128,7 @@ function getStickyContextForRange(state, fromPos, config, t, maxSticky) {
115
128
  // what delays the slide-away until the real closing brace.
116
129
  const close = doc.lineAt(Math.min(owner.to, doc.length));
117
130
  const closeLine = close.number;
118
- if (closeLine - openLine + 1 >= minBlockLines && closeLine >= topLineNumber) {
119
- found.push({
120
- lineNumber: openLine,
121
- from: open.from,
122
- to: open.to,
123
- text: open.text,
124
- nodeFrom: owner.from,
125
- nodeTo: owner.to,
126
- });
127
- }
131
+ pushStickyLine(open, closeLine, owner.from, owner.to);
128
132
  }
129
133
  }
130
134
  }
@@ -141,21 +145,11 @@ function getStickyContextForRange(state, fromPos, config, t, maxSticky) {
141
145
  if (!exclude(undefined, langName, ownerName)) {
142
146
  for (const service of services) {
143
147
  const foldRange = service(state, open.from, open.to);
144
- if (foldRange) {
145
- const nodeTo = Math.min(foldRange.to, doc.length);
146
- const close = doc.lineAt(nodeTo);
147
- const closeLine = close.number;
148
- if (closeLine - openLine + 1 >= minBlockLines && closeLine >= topLineNumber) {
149
- found.push({
150
- lineNumber: openLine,
151
- from: open.from,
152
- to: open.to,
153
- text: open.text,
154
- nodeFrom: foldRange.from,
155
- nodeTo,
156
- });
157
- }
148
+ if (!foldRange) {
149
+ continue;
158
150
  }
151
+ const nodeTo = Math.min(foldRange.to, doc.length);
152
+ pushStickyLine(open, doc.lineAt(nodeTo).number, foldRange.from, nodeTo);
159
153
  }
160
154
  }
161
155
  }
package/dist/facet.d.ts CHANGED
@@ -35,8 +35,6 @@ export interface StickyScrollConfig extends Required<Pick<StickyScrollOptions, "
35
35
  * for a language you need exact behavior on.
36
36
  */
37
37
  export declare const defaultExcludeNode: (nodeName?: string, langName?: string, ownerName?: string) => boolean;
38
- /** Merge partial options with built-in defaults. */
39
- export declare function makeStickyScrollConfig(options?: StickyScrollOptions): StickyScrollConfig;
40
38
  /**
41
39
  * The facet carrying the sticky-scroll configuration.
42
40
  *
package/dist/facet.js CHANGED
@@ -59,7 +59,7 @@ export const defaultExcludeNode = (nodeName, langName, ownerName) => {
59
59
  const DEFAULT_MAX_STICKY_LINES = 4;
60
60
  const DEFAULT_MIN_BLOCK_LINES = 6;
61
61
  /** Merge partial options with built-in defaults. */
62
- export function makeStickyScrollConfig(options) {
62
+ function makeStickyScrollConfig(options) {
63
63
  var _a, _b, _c;
64
64
  return {
65
65
  maxStickyLines: (_a = options === null || options === void 0 ? void 0 : options.maxStickyLines) !== null && _a !== void 0 ? _a : DEFAULT_MAX_STICKY_LINES,
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import type { Extension } from "@codemirror/state";
2
2
  import { type StickyScrollOptions } from "./facet";
3
- export type { StickyLine } from "./types";
4
- export type { StickyScrollOptions, StickyScrollConfig } from "./facet";
5
- export { stickyScrollFacet, defaultExcludeNode, makeStickyScrollConfig } from "./facet";
6
- export { stickyScrollBaseTheme } from "./theme";
3
+ export type { StickyScrollOptions } from "./facet";
4
+ export { defaultExcludeNode } from "./facet";
5
+ export { stickyScrollEffect } from "./plugin";
7
6
  /**
8
7
  * Add Monaco/VS Code-style sticky scroll to a CodeMirror 6 editor.
9
8
  *
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { stickyScrollFacet } from "./facet";
2
2
  import { scrollStickyPlugin } from "./plugin";
3
3
  import { stickyScrollBaseTheme } from "./theme";
4
- export { stickyScrollFacet, defaultExcludeNode, makeStickyScrollConfig } from "./facet";
5
- export { stickyScrollBaseTheme } from "./theme";
4
+ export { defaultExcludeNode } from "./facet";
5
+ export { stickyScrollEffect } from "./plugin";
6
6
  /**
7
7
  * Add Monaco/VS Code-style sticky scroll to a CodeMirror 6 editor.
8
8
  *
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view";
2
2
  import type { StickyLine } from "./types";
3
+ export declare const stickyScrollEffect: import("@codemirror/state").StateEffectType<true>;
3
4
  declare class StickyScrollPlugin {
4
5
  private readonly dom;
5
6
  private readonly inner;
package/dist/plugin.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { language, syntaxTree } from "@codemirror/language";
2
2
  import { Direction, EditorView, ViewPlugin } from "@codemirror/view";
3
+ import { StateEffect } from "@codemirror/state";
3
4
  import { getStickyContext } from "./compute";
4
5
  import { stickyScrollFacet } from "./facet";
5
6
  import { renderLineCode } from "./render";
7
+ export const stickyScrollEffect = StateEffect.define();
6
8
  // ─────────────────────────────────────────────────────────────────────────────
7
9
  // Utilities
8
10
  // ─────────────────────────────────────────────────────────────────────────────
@@ -102,7 +104,8 @@ class StickyScrollPlugin {
102
104
  this.config = update.state.facet(stickyScrollFacet);
103
105
  const contentChanged = configChanged ||
104
106
  update.docChanged ||
105
- syntaxTree(update.startState) !== syntaxTree(update.state);
107
+ syntaxTree(update.startState) !== syntaxTree(update.state) ||
108
+ update.transactions.some(({ effects }) => effects.some((e) => e.is(stickyScrollEffect)));
106
109
  if (contentChanged) {
107
110
  this.clearCache();
108
111
  }
@@ -330,4 +333,4 @@ class StickyScrollPlugin {
330
333
  return semiTransparent;
331
334
  }
332
335
  }
333
- export const scrollStickyPlugin = ViewPlugin.fromClass(StickyScrollPlugin, {});
336
+ export const scrollStickyPlugin = /* #__PURE__ */ ViewPlugin.fromClass(StickyScrollPlugin, {});
package/dist/render.js CHANGED
@@ -16,9 +16,6 @@ class StateHighlighter {
16
16
  style(tags) {
17
17
  return highlightingFor(this.state, tags, this.topNodeType);
18
18
  }
19
- scope() {
20
- return true;
21
- }
22
19
  }
23
20
  // ---------------------------------------------------------------------------
24
21
  // Internal: find the live `.cm-line` DOM element for a document line
@@ -35,13 +32,7 @@ function findRenderedLineElement(view, lineNumber) {
35
32
  // (Block widgets, placeholder lines and render-margin lines make those two
36
33
  // lists diverge, which previously caused wrong — blank / comment / `}` —
37
34
  // line text to be cloned into the sticky bar.)
38
- let dom;
39
- try {
40
- dom = view.domAtPos(lineFrom, 1);
41
- }
42
- catch {
43
- return null;
44
- }
35
+ const dom = view.domAtPos(lineFrom, 1);
45
36
  let el = dom.node instanceof HTMLElement ? dom.node : dom.node.parentElement;
46
37
  while (el && !el.classList.contains("cm-line"))
47
38
  el = el.parentElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bhsd/codemirror-stickyscroll",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "VS Code / Monaco-style sticky scroll (sticky lines) extension for CodeMirror 6",
5
5
  "keywords": [
6
6
  "codemirror",
@@ -32,7 +32,8 @@
32
32
  "lint:md": "markdownlint-cli2 '**/*.md'",
33
33
  "lint:css": "stylelint --cache '**/*.css'",
34
34
  "lint": "npm run lint:ts && npm run lint:css && npm run lint:md",
35
- "build": "tsc && eslint --no-inline-config --no-config-lookup -c eslint.dist.js dist/*.js"
35
+ "build": "tsc && eslint --no-inline-config --no-config-lookup -c eslint.dist.js dist/*.js",
36
+ "test": "echo 'This package has no tests.'"
36
37
  },
37
38
  "peerDependencies": {
38
39
  "@codemirror/language": "^6.3.0",
@@ -40,13 +41,13 @@
40
41
  "@codemirror/view": "^6.0.0"
41
42
  },
42
43
  "devDependencies": {
43
- "@bhsd/code-standard": "^4.1.0",
44
+ "@bhsd/code-standard": "^4.3.0",
44
45
  "@codemirror/language": "^6.12.4",
45
- "@codemirror/state": "^6.7.1",
46
- "@codemirror/view": "^6.43.9",
47
- "eslint": "^10.10.0",
48
- "markdownlint-cli2": "^0.23.2",
49
- "stylelint": "^17.15.0",
46
+ "@codemirror/state": "^6.7.6",
47
+ "@codemirror/view": "^6.43.13",
48
+ "eslint": "^10.12.0",
49
+ "markdownlint-cli2": "^0.23.3",
50
+ "stylelint": "^17.16.0",
50
51
  "typescript": "^6.0.3"
51
52
  }
52
53
  }