@codemirror/language 6.9.3 → 6.10.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/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 6.10.0 (2023-12-28)
2
+
3
+ ### New features
4
+
5
+ The new `bidiIsolates` extension can be used to wrap syntactic elements where this is appropriate in an element that isolates their text direction, avoiding weird ordering of neutral characters on direction boundaries.
6
+
1
7
  ## 6.9.3 (2023-11-27)
2
8
 
3
9
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -2531,6 +2531,117 @@ function docID(data) {
2531
2531
  return type;
2532
2532
  }
2533
2533
 
2534
+ function buildForLine(line) {
2535
+ return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
2536
+ }
2537
+ function textHasRTL(text) {
2538
+ for (let i = text.iter(); !i.next().done;)
2539
+ if (buildForLine(i.value))
2540
+ return true;
2541
+ return false;
2542
+ }
2543
+ function changeAddsRTL(change) {
2544
+ let added = false;
2545
+ change.iterChanges((fA, tA, fB, tB, ins) => {
2546
+ if (!added && textHasRTL(ins))
2547
+ added = true;
2548
+ });
2549
+ return added;
2550
+ }
2551
+ const alwaysIsolate = state.Facet.define({ combine: values => values.some(x => x) });
2552
+ /**
2553
+ Make sure nodes
2554
+ [marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
2555
+ as isolating for bidirectional text are rendered in a way that
2556
+ isolates them from the surrounding text.
2557
+ */
2558
+ function bidiIsolates(options = {}) {
2559
+ let extensions = [isolateMarks];
2560
+ if (options.alwaysIsolate)
2561
+ extensions.push(alwaysIsolate.of(true));
2562
+ return extensions;
2563
+ }
2564
+ const isolateMarks = view.ViewPlugin.fromClass(class {
2565
+ constructor(view$1) {
2566
+ this.always = view$1.state.facet(alwaysIsolate) ||
2567
+ view$1.textDirection != view.Direction.LTR ||
2568
+ view$1.state.facet(view.EditorView.perLineTextDirection);
2569
+ this.hasRTL = !this.always && textHasRTL(view$1.state.doc);
2570
+ this.tree = syntaxTree(view$1.state);
2571
+ this.decorations = buildDeco(view$1, this.tree, this.always);
2572
+ }
2573
+ update(update) {
2574
+ let always = update.state.facet(alwaysIsolate) ||
2575
+ update.view.textDirection != view.Direction.LTR ||
2576
+ update.state.facet(view.EditorView.perLineTextDirection);
2577
+ if (!always && !this.hasRTL && changeAddsRTL(update.changes))
2578
+ this.hasRTL = true;
2579
+ if (!always && !this.hasRTL)
2580
+ return;
2581
+ let tree = syntaxTree(update.state);
2582
+ if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
2583
+ this.tree = tree;
2584
+ this.always = always;
2585
+ this.decorations = buildDeco(update.view, tree, always);
2586
+ }
2587
+ }
2588
+ }, {
2589
+ provide: plugin => {
2590
+ function access(view$1) {
2591
+ var _a, _b;
2592
+ return (_b = (_a = view$1.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : view.Decoration.none;
2593
+ }
2594
+ return [view.EditorView.outerDecorations.of(access),
2595
+ state.Prec.lowest(view.EditorView.bidiIsolatedRanges.of(access))];
2596
+ }
2597
+ });
2598
+ function buildDeco(view, tree, always) {
2599
+ let deco = new state.RangeSetBuilder();
2600
+ let ranges = view.visibleRanges;
2601
+ if (!always)
2602
+ ranges = clipRTLLines(ranges, view.state.doc);
2603
+ for (let { from, to } of ranges) {
2604
+ tree.iterate({
2605
+ enter: node => {
2606
+ let iso = node.type.prop(common.NodeProp.isolate);
2607
+ if (iso)
2608
+ deco.add(node.from, node.to, marks[iso]);
2609
+ },
2610
+ from, to
2611
+ });
2612
+ }
2613
+ return deco.finish();
2614
+ }
2615
+ function clipRTLLines(ranges, doc) {
2616
+ let cur = doc.iter(), pos = 0, result = [], last = null;
2617
+ for (let { from, to } of ranges) {
2618
+ if (from != pos) {
2619
+ if (pos < from)
2620
+ cur.next(from - pos);
2621
+ pos = from;
2622
+ }
2623
+ for (;;) {
2624
+ let start = pos, end = pos + cur.value.length;
2625
+ if (!cur.lineBreak && buildForLine(cur.value)) {
2626
+ if (last && last.to > start - 10)
2627
+ last.to = Math.min(to, end);
2628
+ else
2629
+ result.push(last = { from: start, to: Math.min(to, end) });
2630
+ }
2631
+ if (pos >= to)
2632
+ break;
2633
+ pos = end;
2634
+ cur.next();
2635
+ }
2636
+ }
2637
+ return result;
2638
+ }
2639
+ const marks = {
2640
+ rtl: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: view.Direction.RTL }),
2641
+ ltr: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: view.Direction.LTR }),
2642
+ auto: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
2643
+ };
2644
+
2534
2645
  exports.DocInput = DocInput;
2535
2646
  exports.HighlightStyle = HighlightStyle;
2536
2647
  exports.IndentContext = IndentContext;
@@ -2542,6 +2653,7 @@ exports.ParseContext = ParseContext;
2542
2653
  exports.StreamLanguage = StreamLanguage;
2543
2654
  exports.StringStream = StringStream;
2544
2655
  exports.TreeIndentContext = TreeIndentContext;
2656
+ exports.bidiIsolates = bidiIsolates;
2545
2657
  exports.bracketMatching = bracketMatching;
2546
2658
  exports.bracketMatchingHandle = bracketMatchingHandle;
2547
2659
  exports.codeFolding = codeFolding;
package/dist/index.d.cts CHANGED
@@ -1197,4 +1197,20 @@ declare class StreamLanguage<State> extends Language {
1197
1197
  get allowsNesting(): boolean;
1198
1198
  }
1199
1199
 
1200
- export { Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, Sublanguage, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
1200
+ /**
1201
+ Make sure nodes
1202
+ [marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
1203
+ as isolating for bidirectional text are rendered in a way that
1204
+ isolates them from the surrounding text.
1205
+ */
1206
+ declare function bidiIsolates(options?: {
1207
+ /**
1208
+ By default, isolating elements are only added when the editor
1209
+ direction isn't uniformly left-to-right, or if it is, on lines
1210
+ that contain right-to-left character. When true, disable this
1211
+ optimization and add them everywhere.
1212
+ */
1213
+ alwaysIsolate?: boolean;
1214
+ }): Extension;
1215
+
1216
+ export { type Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, type MatchResult, ParseContext, StreamLanguage, type StreamParser, StringStream, type Sublanguage, type TagStyle, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/dist/index.d.ts CHANGED
@@ -1197,4 +1197,20 @@ declare class StreamLanguage<State> extends Language {
1197
1197
  get allowsNesting(): boolean;
1198
1198
  }
1199
1199
 
1200
- export { Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, Sublanguage, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
1200
+ /**
1201
+ Make sure nodes
1202
+ [marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
1203
+ as isolating for bidirectional text are rendered in a way that
1204
+ isolates them from the surrounding text.
1205
+ */
1206
+ declare function bidiIsolates(options?: {
1207
+ /**
1208
+ By default, isolating elements are only added when the editor
1209
+ direction isn't uniformly left-to-right, or if it is, on lines
1210
+ that contain right-to-left character. When true, disable this
1211
+ optimization and add them everywhere.
1212
+ */
1213
+ alwaysIsolate?: boolean;
1214
+ }): Extension;
1215
+
1216
+ export { type Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, type MatchResult, ParseContext, StreamLanguage, type StreamParser, StringStream, type Sublanguage, type TagStyle, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
2
2
  import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
3
- import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker } from '@codemirror/view';
3
+ import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker, Direction } from '@codemirror/view';
4
4
  import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
5
5
  import { StyleModule } from 'style-mod';
6
6
 
@@ -2529,4 +2529,115 @@ function docID(data) {
2529
2529
  return type;
2530
2530
  }
2531
2531
 
2532
- export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
2532
+ function buildForLine(line) {
2533
+ return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
2534
+ }
2535
+ function textHasRTL(text) {
2536
+ for (let i = text.iter(); !i.next().done;)
2537
+ if (buildForLine(i.value))
2538
+ return true;
2539
+ return false;
2540
+ }
2541
+ function changeAddsRTL(change) {
2542
+ let added = false;
2543
+ change.iterChanges((fA, tA, fB, tB, ins) => {
2544
+ if (!added && textHasRTL(ins))
2545
+ added = true;
2546
+ });
2547
+ return added;
2548
+ }
2549
+ const alwaysIsolate = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) });
2550
+ /**
2551
+ Make sure nodes
2552
+ [marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
2553
+ as isolating for bidirectional text are rendered in a way that
2554
+ isolates them from the surrounding text.
2555
+ */
2556
+ function bidiIsolates(options = {}) {
2557
+ let extensions = [isolateMarks];
2558
+ if (options.alwaysIsolate)
2559
+ extensions.push(alwaysIsolate.of(true));
2560
+ return extensions;
2561
+ }
2562
+ const isolateMarks = /*@__PURE__*/ViewPlugin.fromClass(class {
2563
+ constructor(view) {
2564
+ this.always = view.state.facet(alwaysIsolate) ||
2565
+ view.textDirection != Direction.LTR ||
2566
+ view.state.facet(EditorView.perLineTextDirection);
2567
+ this.hasRTL = !this.always && textHasRTL(view.state.doc);
2568
+ this.tree = syntaxTree(view.state);
2569
+ this.decorations = buildDeco(view, this.tree, this.always);
2570
+ }
2571
+ update(update) {
2572
+ let always = update.state.facet(alwaysIsolate) ||
2573
+ update.view.textDirection != Direction.LTR ||
2574
+ update.state.facet(EditorView.perLineTextDirection);
2575
+ if (!always && !this.hasRTL && changeAddsRTL(update.changes))
2576
+ this.hasRTL = true;
2577
+ if (!always && !this.hasRTL)
2578
+ return;
2579
+ let tree = syntaxTree(update.state);
2580
+ if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
2581
+ this.tree = tree;
2582
+ this.always = always;
2583
+ this.decorations = buildDeco(update.view, tree, always);
2584
+ }
2585
+ }
2586
+ }, {
2587
+ provide: plugin => {
2588
+ function access(view) {
2589
+ var _a, _b;
2590
+ return (_b = (_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : Decoration.none;
2591
+ }
2592
+ return [EditorView.outerDecorations.of(access),
2593
+ Prec.lowest(EditorView.bidiIsolatedRanges.of(access))];
2594
+ }
2595
+ });
2596
+ function buildDeco(view, tree, always) {
2597
+ let deco = new RangeSetBuilder();
2598
+ let ranges = view.visibleRanges;
2599
+ if (!always)
2600
+ ranges = clipRTLLines(ranges, view.state.doc);
2601
+ for (let { from, to } of ranges) {
2602
+ tree.iterate({
2603
+ enter: node => {
2604
+ let iso = node.type.prop(NodeProp.isolate);
2605
+ if (iso)
2606
+ deco.add(node.from, node.to, marks[iso]);
2607
+ },
2608
+ from, to
2609
+ });
2610
+ }
2611
+ return deco.finish();
2612
+ }
2613
+ function clipRTLLines(ranges, doc) {
2614
+ let cur = doc.iter(), pos = 0, result = [], last = null;
2615
+ for (let { from, to } of ranges) {
2616
+ if (from != pos) {
2617
+ if (pos < from)
2618
+ cur.next(from - pos);
2619
+ pos = from;
2620
+ }
2621
+ for (;;) {
2622
+ let start = pos, end = pos + cur.value.length;
2623
+ if (!cur.lineBreak && buildForLine(cur.value)) {
2624
+ if (last && last.to > start - 10)
2625
+ last.to = Math.min(to, end);
2626
+ else
2627
+ result.push(last = { from: start, to: Math.min(to, end) });
2628
+ }
2629
+ if (pos >= to)
2630
+ break;
2631
+ pos = end;
2632
+ cur.next();
2633
+ }
2634
+ }
2635
+ return result;
2636
+ }
2637
+ const marks = {
2638
+ rtl: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: Direction.RTL }),
2639
+ ltr: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: Direction.LTR }),
2640
+ auto: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
2641
+ };
2642
+
2643
+ export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "6.9.3",
3
+ "version": "6.10.0",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -27,7 +27,7 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@codemirror/state": "^6.0.0",
30
- "@codemirror/view": "^6.0.0",
30
+ "@codemirror/view": "^6.23.0",
31
31
  "@lezer/common": "^1.1.0",
32
32
  "@lezer/highlight": "^1.0.0",
33
33
  "@lezer/lr": "^1.0.0",