codemirror-foldable-indentation-guides 1.0.2 → 1.1.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/dist/index.js CHANGED
@@ -350,6 +350,57 @@ class ClickableIndentationGuideWidget extends WidgetType {
350
350
  }
351
351
  }
352
352
 
353
+ function buildIndentationDecorations(view, indentationMap, visibleLines) {
354
+ var _a;
355
+ const state = view.state;
356
+ const widgets = [];
357
+ const indentWidth = getIndentUnit(state);
358
+ const { thickness, activeThickness, hoverThickness, additionalPadding, foldBlockOnClick, highlightActiveBlockBackground, hideFirstIndent, } = state.facet(indentationGuidesConfig);
359
+ const lineLevels = new Map();
360
+ for (const line of visibleLines) {
361
+ const entry = indentationMap.get(line.number);
362
+ const level = (_a = entry === null || entry === void 0 ? void 0 : entry.level) !== null && _a !== void 0 ? _a : 0;
363
+ lineLevels.set(line.number, level);
364
+ if (!entry) {
365
+ continue;
366
+ }
367
+ /** Workaround for folded empty lines in python and similar languages.
368
+ * Without this check, unwanted indentation guides will be drawn after foldPlaceholder
369
+ */
370
+ const foldedAndEmpty = isLineFolded(view, entry.line.number) &&
371
+ isLineEmpty(view, entry.line.number);
372
+ if (!level || foldedAndEmpty) {
373
+ continue;
374
+ }
375
+ const deco = Decoration.widget({
376
+ widget: new ClickableIndentationGuideWidget(indentWidth, level, line.number, {
377
+ thickness,
378
+ activeThickness,
379
+ hoverThickness,
380
+ additionalPadding,
381
+ foldOnClick: foldBlockOnClick,
382
+ highlightBackground: highlightActiveBlockBackground,
383
+ hideFirstIndent,
384
+ }),
385
+ side: 0,
386
+ });
387
+ widgets.push(deco.range(line.from));
388
+ }
389
+ return {
390
+ decoSet: Decoration.set(widgets),
391
+ lineLevels,
392
+ };
393
+ }
394
+
395
+ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
396
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
397
+ return new (P || (P = Promise))(function (resolve, reject) {
398
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
399
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
400
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
401
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
402
+ });
403
+ };
353
404
  /**
354
405
  * Indentation map for a set of lines.
355
406
  *
@@ -367,15 +418,40 @@ class IndentationMap {
367
418
  * @param markerType - The type of indentation to use (terminate at end of scope vs last line of code in scope)
368
419
  */
369
420
  constructor(lines, state, unitWidth, markerType) {
370
- this.lines = lines;
421
+ this.terminated = false;
371
422
  this.state = state;
372
423
  this.map = new Map();
373
424
  this.unitWidth = unitWidth;
374
425
  this.markerType = markerType;
375
- for (const line of this.lines) {
426
+ for (const line of lines) {
376
427
  this.add(line);
377
428
  }
378
429
  }
430
+ initLinesSync(lines) {
431
+ const linesArr = [...lines];
432
+ for (let i = 0; i < linesArr.length; i++) {
433
+ const line = linesArr[i];
434
+ this.add(line);
435
+ }
436
+ }
437
+ initLinesAsync(lines) {
438
+ return __awaiter(this, void 0, void 0, function* () {
439
+ const linesArr = [...lines];
440
+ for (let i = 0; i < linesArr.length; i++) {
441
+ if (this.terminated) {
442
+ break;
443
+ }
444
+ const line = linesArr[i];
445
+ this.add(line);
446
+ if (i % 100 === 0) {
447
+ yield new Promise((resolve) => setTimeout(resolve));
448
+ }
449
+ }
450
+ });
451
+ }
452
+ terminate() {
453
+ this.terminated = true;
454
+ }
379
455
  /**
380
456
  * Checks if the indentation map has an entry for the given line.
381
457
  *
@@ -393,9 +469,6 @@ class IndentationMap {
393
469
  */
394
470
  get(line) {
395
471
  const entry = this.map.get(typeof line === 'number' ? line : line.number);
396
- if (!entry) {
397
- throw new Error('Line not found in indentation map');
398
- }
399
472
  return entry;
400
473
  }
401
474
  /**
@@ -417,6 +490,7 @@ class IndentationMap {
417
490
  * @param line - The {@link Line} to add to the map.
418
491
  */
419
492
  add(line) {
493
+ var _a;
420
494
  if (this.has(line)) {
421
495
  return this.get(line);
422
496
  }
@@ -429,10 +503,13 @@ class IndentationMap {
429
503
  // if we're at the end, we'll just use the previous line's indentation
430
504
  if (line.number === this.state.doc.lines) {
431
505
  const prev = this.closestNonEmpty(line, -1);
432
- return this.set(line, 0, prev.level);
506
+ return this.set(line, 0, (_a = prev === null || prev === void 0 ? void 0 : prev.level) !== null && _a !== void 0 ? _a : 0);
433
507
  }
434
508
  const prev = this.closestNonEmpty(line, -1);
435
509
  const next = this.closestNonEmpty(line, 1);
510
+ if (!prev || !next) {
511
+ return this.set(line, 0, 0);
512
+ }
436
513
  // if the next line ends the block and the marker type is not set to codeOnly,
437
514
  // we'll just use the previous line's indentation
438
515
  if (prev.level >= next.level && this.markerType !== 'codeOnly') {
@@ -466,7 +543,7 @@ class IndentationMap {
466
543
  while (dir === -1 ? lineNo >= 1 : lineNo <= this.state.doc.lines) {
467
544
  if (this.has(lineNo)) {
468
545
  const entry = this.get(lineNo);
469
- if (!entry.empty) {
546
+ if (!(entry === null || entry === void 0 ? void 0 : entry.empty)) {
470
547
  return entry;
471
548
  }
472
549
  }
@@ -496,6 +573,9 @@ class IndentationMap {
496
573
  return;
497
574
  }
498
575
  let current = this.get(currentLine);
576
+ if (!current) {
577
+ return;
578
+ }
499
579
  // check if the current line is starting a new block, if yes, we want to
500
580
  // start from inside the block.
501
581
  if (this.has(current.line.number + 1)) {
@@ -524,7 +604,7 @@ class IndentationMap {
524
604
  continue;
525
605
  }
526
606
  const prev = this.get(start - 1);
527
- if (prev.level < current.level) {
607
+ if (!prev || prev.level < current.level) {
528
608
  break;
529
609
  }
530
610
  prev.active = current.level;
@@ -536,7 +616,7 @@ class IndentationMap {
536
616
  continue;
537
617
  }
538
618
  const next = this.get(end + 1);
539
- if (next.level < current.level) {
619
+ if (!next || next.level < current.level) {
540
620
  break;
541
621
  }
542
622
  next.active = current.level;
@@ -552,48 +632,6 @@ function getNewIndentationMap(state, lines) {
552
632
  return new IndentationMap(lines, state, indentWidth, markerType);
553
633
  }
554
634
 
555
- function buildIndentationDecorations(view) {
556
- var _a;
557
- const state = view.state;
558
- const widgets = [];
559
- const lines = getVisibleLines(view);
560
- const indentWidth = getIndentUnit(state);
561
- const { thickness, activeThickness, hoverThickness, additionalPadding, foldBlockOnClick, highlightActiveBlockBackground, hideFirstIndent, } = state.facet(indentationGuidesConfig);
562
- const map = getNewIndentationMap(state, lines);
563
- const lineLevels = new Map();
564
- for (const line of lines) {
565
- const entry = map.get(line.number);
566
- const level = (_a = entry === null || entry === void 0 ? void 0 : entry.level) !== null && _a !== void 0 ? _a : 0;
567
- lineLevels.set(line.number, level);
568
- /** Workaround for folded empty lines in python and similar languages.
569
- * Without this check, unwanted indentation guides will be drawn after foldPlaceholder
570
- */
571
- const foldedAndEmpty = isLineFolded(view, entry.line.number) &&
572
- isLineEmpty(view, entry.line.number);
573
- if (!level || foldedAndEmpty) {
574
- continue;
575
- }
576
- const deco = Decoration.widget({
577
- widget: new ClickableIndentationGuideWidget(indentWidth, level, line.number, {
578
- thickness,
579
- activeThickness,
580
- hoverThickness,
581
- additionalPadding,
582
- foldOnClick: foldBlockOnClick,
583
- highlightBackground: highlightActiveBlockBackground,
584
- hideFirstIndent,
585
- }),
586
- side: 0,
587
- });
588
- widgets.push(deco.range(line.from));
589
- }
590
- return {
591
- indentationMap: map,
592
- decoSet: Decoration.set(widgets),
593
- lineLevels,
594
- };
595
- }
596
-
597
635
  function handleGuideHover(guideElement, plugin) {
598
636
  var _a, _b, _c, _d, _e;
599
637
  if (!guideElement || !plugin) {
@@ -618,28 +656,42 @@ function handleGuideHover(guideElement, plugin) {
618
656
  plugin.applyHoverClasses(start, end, col);
619
657
  }
620
658
 
659
+ const DEBOUNCE_DELAY_MS = 500;
621
660
  class IndentationViewPlugin {
622
661
  constructor(view) {
623
662
  var _a, _b;
624
663
  this.hoveredGuide = null;
625
- const built = buildIndentationDecorations(view);
664
+ this.lastCursorPos = null;
665
+ this.allStateDebounceId = null;
666
+ const visibleLines = getVisibleLines(view);
667
+ this.indentationMap = this.updateIndentationMap(view, visibleLines);
668
+ const built = buildIndentationDecorations(view, this.indentationMap, visibleLines);
626
669
  this.highlightHoveredGuide = false;
627
670
  this.highlightHoveredBlock = false;
628
- this.activeColEdges = [];
629
- this.activeCol = 0;
630
671
  this.view = view;
631
- this.viewContentWidth =
632
- (_b = (_a = view.dom.querySelector('.cm-content')) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0;
633
- this.contentElement = view.dom.querySelector('.cm-content');
672
+ this.viewContentWidth = (_b = (_a = view.contentDOM) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0;
673
+ this.contentElement = view.contentDOM;
634
674
  this.decorations = built.decoSet;
635
675
  this.lineLevels = built.lineLevels;
636
- this.indentationMap = built.indentationMap;
637
676
  this.unitWidth = getIndentUnit(view.state);
638
677
  this.currentLineNumber = getCurrentLine(view.state).number;
639
678
  }
679
+ updateIndentationMap(view, visibleLines) {
680
+ var _a;
681
+ if (this.allStateDebounceId) {
682
+ clearTimeout(this.allStateDebounceId);
683
+ }
684
+ (_a = this.indentationMap) === null || _a === void 0 ? void 0 : _a.terminate();
685
+ this.indentationMap = getNewIndentationMap(view.state, visibleLines);
686
+ this.allStateDebounceId = setTimeout(() => {
687
+ const allLines = getAllLines(view);
688
+ this.indentationMap.initLinesAsync(allLines);
689
+ }, DEBOUNCE_DELAY_MS);
690
+ return this.indentationMap;
691
+ }
640
692
  update(update) {
641
693
  var _a, _b, _c;
642
- (_a = this.contentElement) !== null && _a !== void 0 ? _a : (this.contentElement = update.view.dom.querySelector('.cm-content'));
694
+ (_a = this.contentElement) !== null && _a !== void 0 ? _a : (this.contentElement = update.view.contentDOM);
643
695
  const unitWidth = getIndentUnit(update.state);
644
696
  const unitWidthChanged = unitWidth !== this.unitWidth;
645
697
  if (unitWidthChanged) {
@@ -657,11 +709,25 @@ class IndentationViewPlugin {
657
709
  update.viewportChanged ||
658
710
  viewContentWidth !== this.viewContentWidth;
659
711
  const activeBlockUpdateRequired = isHighlightActive && viewChanged;
660
- if (update.docChanged || update.viewportChanged || unitWidthChanged) {
661
- const built = buildIndentationDecorations(update.view);
712
+ const visibleLines = update.docChanged ||
713
+ update.viewportChanged ||
714
+ unitWidthChanged ||
715
+ activeBlockUpdateRequired
716
+ ? getVisibleLines(update.view)
717
+ : null;
718
+ if (!visibleLines) {
719
+ return;
720
+ }
721
+ if (update.docChanged || unitWidthChanged) {
722
+ this.updateIndentationMap(update.view, visibleLines);
723
+ }
724
+ if (update.viewportChanged || unitWidthChanged) {
725
+ if (!update.docChanged) {
726
+ this.indentationMap.initLinesSync(visibleLines);
727
+ }
728
+ const built = buildIndentationDecorations(update.view, this.indentationMap, visibleLines);
662
729
  this.decorations = built.decoSet;
663
730
  this.lineLevels = built.lineLevels;
664
- this.indentationMap = built.indentationMap;
665
731
  this.clearHoverClasses();
666
732
  }
667
733
  if (activeBlockUpdateRequired) {
@@ -671,46 +737,20 @@ class IndentationViewPlugin {
671
737
  }
672
738
  updateActiveColumns(lineNumber, highlightActiveMarker, highlightActiveBlockBackground) {
673
739
  requestAnimationFrame(() => {
674
- var _a, _b;
740
+ var _a;
675
741
  this.clearActiveClasses();
676
- const lines = (_a = this.indentationMap
677
- .getActiveLinesNumbers(lineNumber)) === null || _a === void 0 ? void 0 : _a.sort((a, b) => a.line.number - b.line.number);
678
- if (lines) {
679
- lines.forEach(line => {
680
- const { active: activeCol } = line;
681
- const lineNumber = line.line.number;
682
- if (highlightActiveMarker) {
683
- this.applyActiveMarker(lineNumber, activeCol !== null && activeCol !== void 0 ? activeCol : 0);
684
- }
685
- if (highlightActiveBlockBackground) {
686
- this.applyBackgroundHighlight(lineNumber, activeCol !== null && activeCol !== void 0 ? activeCol : 0, 'active');
687
- }
688
- });
689
- this.activeColEdges = [
690
- lines[0].line.number,
691
- lines[lines.length - 1].line.number,
692
- ];
693
- this.activeCol = (_b = lines[0].active) !== null && _b !== void 0 ? _b : 0;
742
+ const activeLines = (_a = this.indentationMap) === null || _a === void 0 ? void 0 : _a.getActiveLinesNumbers(lineNumber);
743
+ if (!(activeLines === null || activeLines === void 0 ? void 0 : activeLines.length) || activeLines[0].active === undefined) {
694
744
  return;
695
745
  }
696
- if (!this.activeColEdges || !this.activeCol) {
697
- return;
746
+ const minLineNumber = activeLines.reduce((acc, line) => Math.min(acc, line.line.number), Infinity) - 1;
747
+ const maxLineNumber = activeLines.reduce((acc, line) => Math.max(acc, line.line.number), -Infinity) + 1;
748
+ if (highlightActiveMarker) {
749
+ this.applyActiveMarker(minLineNumber, maxLineNumber, activeLines[0].active);
750
+ }
751
+ if (highlightActiveBlockBackground) {
752
+ this.applyBackgroundHighlight(minLineNumber, maxLineNumber, activeLines[0].active, 'active');
698
753
  }
699
- /** If current line is outside computed indentation map lines,
700
- * compute active block lines from active column points */
701
- const ranges = this.activeColEdges.map(edge => this.computeBlockRange(edge, this.activeCol));
702
- ranges.forEach(range => {
703
- for (let i = range.start; i <= range.end; i++) {
704
- if (highlightActiveMarker) {
705
- this.applyActiveMarker(lineNumber, this.activeCol);
706
- }
707
- if (highlightActiveBlockBackground) {
708
- this.applyBackgroundHighlight(lineNumber, this.activeCol, 'active');
709
- }
710
- }
711
- this.activeColEdges.push(range.start, range.end);
712
- });
713
- this.activeColEdges = Array.from(new Set(this.activeColEdges));
714
754
  });
715
755
  }
716
756
  /** compute block (inclusive line numbers) for a given clicked line and column (0-based) */
@@ -742,29 +782,37 @@ class IndentationViewPlugin {
742
782
  return { start, end };
743
783
  }
744
784
  clearActiveClasses() {
745
- const dom = this.view.dom;
785
+ const dom = this.view.contentDOM;
746
786
  dom
747
787
  .querySelectorAll('.cm-indentation-guide-button_active')
748
- .forEach(el => el.classList.remove('cm-indentation-guide-button_active'));
788
+ .forEach((el) => el.classList.remove('cm-indentation-guide-button_active'));
749
789
  dom
750
790
  .querySelectorAll('.cm-indentation-guide-background-highlight_active')
751
- .forEach(el => {
791
+ .forEach((el) => {
752
792
  el.classList.remove('cm-indentation-guide-background-highlight_active');
753
793
  });
754
794
  }
755
- applyActiveMarker(lineNumber, col) {
756
- const root = this.view.dom;
757
- const selector = `.cm-indentation-guide[data-line="${lineNumber}"] > .cm-indentation-guide-button[data-col="${col}"]`;
758
- root.querySelectorAll(selector).forEach(btn => {
759
- btn.classList.add('cm-indentation-guide-button_active');
795
+ applyActiveMarker(start, end, col) {
796
+ const root = this.view.contentDOM;
797
+ const selector = `.cm-indentation-guide-button[data-col="${col}"]`;
798
+ root.querySelectorAll(selector).forEach((btn) => {
799
+ var _a;
800
+ const lineAttr = (_a = btn.parentElement) === null || _a === void 0 ? void 0 : _a.dataset.line;
801
+ if (!lineAttr) {
802
+ return;
803
+ }
804
+ const ln = Number(lineAttr);
805
+ if (ln >= start && ln <= end) {
806
+ btn.classList.add('cm-indentation-guide-button_active');
807
+ }
760
808
  });
761
809
  }
762
810
  /** highlight DOM buttons in column `col` for lines in [start, end] */
763
811
  applyHoverClasses(start, end, col) {
764
812
  this.clearHoverClasses();
765
- const root = this.view.dom;
813
+ const root = this.view.contentDOM;
766
814
  const selector = `.cm-indentation-guide-button[data-col="${col}"]`;
767
- root.querySelectorAll(selector).forEach(btn => {
815
+ root.querySelectorAll(selector).forEach((btn) => {
768
816
  var _a;
769
817
  const lineAttr = (_a = btn.parentElement) === null || _a === void 0 ? void 0 : _a.dataset.line;
770
818
  if (!lineAttr) {
@@ -776,68 +824,88 @@ class IndentationViewPlugin {
776
824
  btn.classList.add('cm-indentation-guide-button_hover');
777
825
  }
778
826
  if (this.highlightHoveredBlock) {
779
- this.applyBackgroundHighlight(ln, col, 'hover');
827
+ this.applyBackgroundHighlight(start, end, col, 'hover');
780
828
  }
781
829
  }
782
830
  });
783
831
  this.hoveredGuide = { col, start, end };
784
832
  }
785
- applyBackgroundHighlight(lineNumber, col, type) {
786
- const root = this.view.dom;
787
- const selector = `.cm-indentation-guide[data-line="${lineNumber}"] > .cm-indentation-guide-button[data-col="${col}"]`;
788
- const button = root.querySelector(selector);
789
- if (!button) {
790
- return;
791
- }
792
- let highlightLeft = button.offsetLeft;
793
- let btnWidth = button.offsetWidth;
794
- const highlightBackgroundSelector = `.cm-indentation-guide[data-line="${lineNumber}"] > .cm-indentation-guide-background-highlight`;
795
- const backgroundDiv = root.querySelector(highlightBackgroundSelector);
796
- if (!backgroundDiv) {
797
- return;
798
- }
799
- backgroundDiv.style.setProperty(`--indentation-guide-background-highlight-${type}-left`, `${highlightLeft}px`);
800
- backgroundDiv.style.setProperty(`--indentation-guide-background-highlight-${type}-width`, `${this.viewContentWidth - (highlightLeft !== null && highlightLeft !== void 0 ? highlightLeft : 0) - (btnWidth !== null && btnWidth !== void 0 ? btnWidth : 0) / 2}px`);
801
- backgroundDiv.classList.add(`cm-indentation-guide-background-highlight_${type}`);
833
+ applyBackgroundHighlight(start, end, col, type) {
834
+ const root = this.view.contentDOM;
835
+ const selector = `.cm-indentation-guide-button[data-col="${col}"]`;
836
+ const highlightBackgroundSelector = `.cm-indentation-guide-background-highlight`;
837
+ root.querySelectorAll(selector).forEach((button) => {
838
+ var _a, _b;
839
+ const lineAttr = (_a = button.parentElement) === null || _a === void 0 ? void 0 : _a.dataset.line;
840
+ if (!lineAttr) {
841
+ return;
842
+ }
843
+ const ln = Number(lineAttr);
844
+ if (ln < start || ln > end) {
845
+ return;
846
+ }
847
+ const backgroundDiv = (_b = button.parentElement) === null || _b === void 0 ? void 0 : _b.querySelector(highlightBackgroundSelector);
848
+ if (!backgroundDiv) {
849
+ return;
850
+ }
851
+ let highlightLeft = button.offsetLeft;
852
+ let btnWidth = button.offsetWidth;
853
+ backgroundDiv.style.setProperty(`--indentation-guide-background-highlight-${type}-left`, `${highlightLeft}px`);
854
+ backgroundDiv.style.setProperty(`--indentation-guide-background-highlight-${type}-width`, `${this.viewContentWidth - (highlightLeft !== null && highlightLeft !== void 0 ? highlightLeft : 0) - (btnWidth !== null && btnWidth !== void 0 ? btnWidth : 0) / 2}px`);
855
+ backgroundDiv.classList.add(`cm-indentation-guide-background-highlight_${type}`);
856
+ });
802
857
  }
803
858
  clearHoverClasses() {
804
859
  var _a;
805
860
  if (((_a = this.hoveredGuide) === null || _a === void 0 ? void 0 : _a.col) === null) {
806
861
  return;
807
862
  }
808
- const dom = this.view.dom;
863
+ const dom = this.view.contentDOM;
809
864
  dom
810
865
  .querySelectorAll('.cm-indentation-guide-button_hover')
811
- .forEach(el => el.classList.remove('cm-indentation-guide-button_hover'));
866
+ .forEach((el) => el.classList.remove('cm-indentation-guide-button_hover'));
812
867
  this.hoveredGuide = null;
813
868
  dom
814
869
  .querySelectorAll('.cm-indentation-guide-background-highlight')
815
- .forEach(el => {
870
+ .forEach((el) => {
816
871
  el.classList.remove('cm-indentation-guide-background-highlight_hover');
817
872
  });
818
873
  }
819
874
  }
820
875
  const indentationGuidesPlugin = /*@__PURE__*/ViewPlugin.fromClass(IndentationViewPlugin, {
821
- decorations: v => v.decorations,
876
+ decorations: (v) => v.decorations,
822
877
  eventHandlers: {
823
- mousemove: (e, view) => {
824
- const { highlightHoveredMarker, highlightHoveredBlockBackground } = view.state.facet(indentationGuidesConfig);
825
- if (!highlightHoveredMarker && !highlightHoveredBlockBackground) {
878
+ scroll() {
879
+ var _a;
880
+ if (!this.lastCursorPos) {
826
881
  return;
827
882
  }
828
- const plugin = view.plugin(indentationGuidesPlugin);
829
- if (!plugin) {
883
+ const target = (_a = document
884
+ .elementFromPoint(this.lastCursorPos.x, this.lastCursorPos.y)) === null || _a === void 0 ? void 0 : _a.closest('.cm-indentation-guide-button');
885
+ if (target) {
886
+ handleGuideHover(target, this);
887
+ }
888
+ return;
889
+ },
890
+ mousemove(e, view) {
891
+ const { highlightHoveredMarker, highlightHoveredBlockBackground } = view.state.facet(indentationGuidesConfig);
892
+ if (!highlightHoveredMarker && !highlightHoveredBlockBackground) {
830
893
  return;
831
894
  }
832
895
  const target = e.target.closest('.cm-indentation-guide-button');
833
896
  if (target) {
834
- handleGuideHover(target, plugin);
897
+ handleGuideHover(target, this);
898
+ this.lastCursorPos = {
899
+ x: e.clientX,
900
+ y: e.clientY,
901
+ };
835
902
  }
836
903
  else {
837
- plugin.clearHoverClasses();
904
+ this.clearHoverClasses();
905
+ this.lastCursorPos = null;
838
906
  }
839
907
  },
840
- mouseout: (e, view) => {
908
+ mouseout(e, view) {
841
909
  const { highlightHoveredMarker, highlightHoveredBlockBackground } = view.state.facet(indentationGuidesConfig);
842
910
  if (!highlightHoveredMarker && !highlightHoveredBlockBackground) {
843
911
  return;
@@ -847,11 +915,10 @@ const indentationGuidesPlugin = /*@__PURE__*/ViewPlugin.fromClass(IndentationVie
847
915
  if (!related ||
848
916
  !related.closest ||
849
917
  !related.closest('.cm-indentation-guide')) {
850
- const plugin = view.plugin(indentationGuidesPlugin);
851
- plugin === null || plugin === void 0 ? void 0 : plugin.clearHoverClasses();
918
+ this.clearHoverClasses();
852
919
  }
853
920
  },
854
- mousedown: (e, view) => {
921
+ mousedown(e, view) {
855
922
  var _a, _b;
856
923
  const { foldBlockOnClick } = view.state.facet(indentationGuidesConfig);
857
924
  if (!foldBlockOnClick) {
@@ -867,10 +934,6 @@ const indentationGuidesPlugin = /*@__PURE__*/ViewPlugin.fromClass(IndentationVie
867
934
  if (!lineAttr || !colAttr) {
868
935
  return false;
869
936
  }
870
- const plugin = view.plugin(indentationGuidesPlugin);
871
- if (!plugin) {
872
- return false;
873
- }
874
937
  const lineNumber = Number(lineAttr);
875
938
  const col = Number(colAttr);
876
939
  const lines = getAllLines(view);
@@ -881,8 +944,8 @@ const indentationGuidesPlugin = /*@__PURE__*/ViewPlugin.fromClass(IndentationVie
881
944
  const level = (_b = entry === null || entry === void 0 ? void 0 : entry.level) !== null && _b !== void 0 ? _b : 0;
882
945
  lineLevels.set(line.number, level);
883
946
  }
884
- plugin.lineLevels = lineLevels;
885
- const { start } = plugin.computeBlockRange(lineNumber, col);
947
+ this.lineLevels = lineLevels;
948
+ const { start } = this.computeBlockRange(lineNumber, col);
886
949
  const range = getNearestFoldRange(view, start - 1);
887
950
  if (!range) {
888
951
  return;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "codemirror-foldable-indentation-guides",
3
3
  "description": "Render foldable indentation guides in CodeMirror",
4
- "version": "1.0.2",
4
+ "version": "1.1.0",
5
5
  "keywords": [
6
6
  "codemirror",
7
7
  "codemirror6",
Binary file
@@ -1,15 +1,18 @@
1
1
  import { getIndentUnit } from '@codemirror/language';
2
2
  import { EditorView, Decoration } from '@codemirror/view';
3
3
  import { indentationGuidesConfig } from './config';
4
- import { getVisibleLines, isLineEmpty, isLineFolded } from './utils';
5
- import { Range } from '@codemirror/state';
4
+ import { isLineEmpty, isLineFolded } from './utils';
5
+ import { Line, Range } from '@codemirror/state';
6
6
  import { ClickableIndentationGuideWidget } from './clickable-indentation-guide-widget';
7
- import { getNewIndentationMap } from './get-new-indentation-map';
7
+ import { IndentationMap } from './map';
8
8
 
9
- export function buildIndentationDecorations(view: EditorView) {
9
+ export function buildIndentationDecorations(
10
+ view: EditorView,
11
+ indentationMap: IndentationMap,
12
+ visibleLines: Set<Line>,
13
+ ) {
10
14
  const state = view.state;
11
15
  const widgets: Range<Decoration>[] = [];
12
- const lines = getVisibleLines(view);
13
16
  const indentWidth = getIndentUnit(state);
14
17
  const {
15
18
  thickness,
@@ -20,13 +23,15 @@ export function buildIndentationDecorations(view: EditorView) {
20
23
  highlightActiveBlockBackground,
21
24
  hideFirstIndent,
22
25
  } = state.facet(indentationGuidesConfig);
23
- const map = getNewIndentationMap(state, lines);
24
26
  const lineLevels = new Map<number, number>();
25
27
 
26
- for (const line of lines) {
27
- const entry = map.get(line.number);
28
+ for (const line of visibleLines) {
29
+ const entry = indentationMap.get(line.number);
28
30
  const level = entry?.level ?? 0;
29
31
  lineLevels.set(line.number, level);
32
+ if (!entry) {
33
+ continue;
34
+ }
30
35
 
31
36
  /** Workaround for folded empty lines in python and similar languages.
32
37
  * Without this check, unwanted indentation guides will be drawn after foldPlaceholder
@@ -52,7 +57,7 @@ export function buildIndentationDecorations(view: EditorView) {
52
57
  foldOnClick: foldBlockOnClick,
53
58
  highlightBackground: highlightActiveBlockBackground,
54
59
  hideFirstIndent,
55
- }
60
+ },
56
61
  ),
57
62
  side: 0,
58
63
  });
@@ -61,7 +66,6 @@ export function buildIndentationDecorations(view: EditorView) {
61
66
  }
62
67
 
63
68
  return {
64
- indentationMap: map,
65
69
  decoSet: Decoration.set(widgets),
66
70
  lineLevels,
67
71
  };