Migrating markdown scopes (#2489)

Fixes #2378

## Checklist

- [x] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [/] I have updated the
[docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and
[cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [/] I have not broken the cheatsheet
This commit is contained in:
Andreas Arvidsson 2024-07-28 18:32:14 +02:00 committed by GitHub
parent d296e70d73
commit 6984b1ae05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 138 additions and 157 deletions

View File

@ -0,0 +1,66 @@
# H1
Sub 1
## H2
Sub 2
# H1.2
---
[#1 Content] =
[#1 Domain] = 0:0-6:5
>----
0| # H1
1|
2| Sub 1
3|
4| ## H2
5|
6| Sub 2
-----<
[#1 Removal] = 0:0-8:0
>----
0| # H1
1|
2| Sub 1
3|
4| ## H2
5|
6| Sub 2
7|
8| # H1.2
<
[#1 Insertion delimiter] = "\n\n"
[#2 Content] =
[#2 Domain] = 4:0-6:5
>-----
4| ## H2
5|
6| Sub 2
-----<
[#2 Removal] = 4:0-8:0
>-----
4| ## H2
5|
6| Sub 2
7|
8| # H1.2
<
[#2 Insertion delimiter] = "\n\n"
[#3 Content] =
[#3 Removal] =
[#3 Domain] = 8:0-8:6
>------<
8| # H1.2
[#3 Insertion delimiter] = "\n\n"

View File

@ -9,5 +9,6 @@ const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
export const markdownScopeSupport: LanguageScopeSupportFacetMap = {
"comment.line": supported,
"comment.block": supported,
section: supported,
notebookCell: supported,
};

View File

@ -38,6 +38,11 @@ export const scopeSupportFacetInfos: Record<
scopeType: "environment",
},
section: {
description: "A document section",
scopeType: "section",
},
list: {
description: "A list/array",
scopeType: "list",

View File

@ -14,6 +14,8 @@ const scopeSupportFacets = [
"environment",
"section",
"list",
"map",
"ifStatement",
@ -168,7 +170,6 @@ const scopeSupportFacets = [
"notebookCell",
// FIXME: Still in legacy
// section
// selector
// unit
// collectionItem

View File

@ -7,7 +7,6 @@ export const legacyLanguageIds = [
"csharp",
"css",
"latex",
"markdown",
"php",
"ruby",
"rust",

View File

@ -168,10 +168,12 @@ class TrimEnd extends QueryPredicateOperator<TrimEnd> {
const { document, range } = nodeInfo;
const text = document.getText(range);
const whitespaceLength = text.length - text.trimEnd().length;
nodeInfo.range = new Range(
range.start,
adjustPosition(document, range.end, -whitespaceLength),
);
if (whitespaceLength > 0) {
nodeInfo.range = new Range(
range.start,
adjustPosition(document, range.end, -whitespaceLength),
);
}
return true;
}
}

View File

@ -1,33 +0,0 @@
import { NodeMatcher } from "../typings/Types";
import { patternFinder } from "../util/nodeFinders";
import {
cascadingMatcher,
matcher,
patternMatcher,
} from "../util/nodeMatchers";
import { childRangeSelector } from "../util/nodeSelectors";
/**
* Constructs a branch matcher for constructs that have a primary branch
* followed by zero or more optional branches, such as `if` statements or `try`
* statements
* @param statementType The top-level statement type for this construct, eg
* "if_statement" or "try_statement"
* @param optionalBranchTypes The optional branch type names that can be
* children of the top-level statement, eg "else_clause" or "except_clause"
* @returns A node matcher capable of matching this type of branch
*/
export function branchMatcher(
statementType: string,
optionalBranchTypes: string[],
): NodeMatcher {
return cascadingMatcher(
patternMatcher(...optionalBranchTypes),
matcher(
patternFinder(statementType),
childRangeSelector(optionalBranchTypes, [], {
includeUnnamedChildren: true,
}),
),
);
}

View File

@ -12,7 +12,6 @@ import clojure from "./clojure";
import { LegacyLanguageId } from "./LegacyLanguageId";
import csharp from "./csharp";
import latex from "./latex";
import markdown from "./markdown";
import php from "./php";
import { patternMatchers as ruby } from "./ruby";
import rust from "./rust";
@ -51,7 +50,6 @@ export const languageMatchers: Record<
csharp,
css: scss,
latex,
markdown,
php,
ruby,
rust,

View File

@ -1,76 +0,0 @@
import { SimpleScopeTypeType, TextEditor } from "@cursorless/common";
import type { SyntaxNode } from "web-tree-sitter";
import { NodeFinder, NodeMatcherAlternative } from "../typings/Types";
import { leadingSiblingNodeFinder, patternFinder } from "../util/nodeFinders";
import { createPatternMatchers, matcher } from "../util/nodeMatchers";
import { extendUntilNextMatchingSiblingOrLast } from "../util/nodeSelectors";
import { shrinkRangeToFitContent } from "../util/selectionUtils";
const HEADING_MARKER_TYPES = [
"atx_h1_marker",
"atx_h2_marker",
"atx_h3_marker",
"atx_h4_marker",
"atx_h5_marker",
"atx_h6_marker",
] as const;
type HeadingMarkerType = (typeof HEADING_MARKER_TYPES)[number];
/**
* Returns a node finder that will only accept nodes of heading level at least
* as high as the given heading level type
* @param headingType The heading type of the node we'll be starting at
* @returns A node finder that will return the next node that is of the same
* marker level or higher than the original type
*/
function makeMinimumHeadingLevelFinder(
headingType: HeadingMarkerType,
): NodeFinder {
const markerIndex = HEADING_MARKER_TYPES.indexOf(headingType);
return (node: SyntaxNode) => {
return node.type === "atx_heading" &&
HEADING_MARKER_TYPES.indexOf(
node.firstNamedChild?.type as HeadingMarkerType,
) <= markerIndex
? node
: null;
};
}
function sectionExtractor(editor: TextEditor, node: SyntaxNode) {
const finder = makeMinimumHeadingLevelFinder(
node.firstNamedChild?.type as HeadingMarkerType,
);
const { context, selection } = extendUntilNextMatchingSiblingOrLast(
editor,
node,
finder,
);
return {
context,
selection: shrinkRangeToFitContent(editor, selection).toSelection(
selection.isReversed,
),
};
}
function sectionMatcher(...patterns: string[]) {
const finder = patternFinder(...patterns);
return matcher(leadingSiblingNodeFinder(finder), sectionExtractor);
}
const nodeMatchers: Partial<
Record<SimpleScopeTypeType, NodeMatcherAlternative>
> = {
section: sectionMatcher("atx_heading"),
sectionLevelOne: sectionMatcher("atx_heading.atx_h1_marker"),
sectionLevelTwo: sectionMatcher("atx_heading.atx_h2_marker"),
sectionLevelThree: sectionMatcher("atx_heading.atx_h3_marker"),
sectionLevelFour: sectionMatcher("atx_heading.atx_h4_marker"),
sectionLevelFive: sectionMatcher("atx_heading.atx_h5_marker"),
sectionLevelSix: sectionMatcher("atx_heading.atx_h6_marker"),
};
export default createPatternMatchers(nodeMatchers);

View File

@ -1,40 +0,0 @@
import type { SyntaxNode } from "web-tree-sitter";
import { NodeMatcher } from "../typings/Types";
import { matcher } from "../util/nodeMatchers";
/**
* Constructs a matcher for matching ternary branches. Branches are expected to
* be named children at particular indices of a ternary node.
*
* NB: We can't just use the "foo[0]" syntax of our pattern language because
* that just uses `foo` for the finder; the `[0]` is just part of the extractor,
* so if we had `foo[0]` and `foo[1]`, they would both match for either branch.
* @param ternaryTypename The type name for ternary expressions
* @param acceptableNamedChildIndices Which named children, by index, of the
* ternary node correspond to branches
* @returns A matcher that can match ternary branches
*/
export function ternaryBranchMatcher(
ternaryTypename: string,
acceptableNamedChildIndices: number[],
): NodeMatcher {
function finder(node: SyntaxNode) {
const parent = node.parent;
if (parent == null) {
return null;
}
if (
parent.type === ternaryTypename &&
acceptableNamedChildIndices.some((index) =>
parent.namedChild(index)!.equals(node),
)
) {
return node;
}
return null;
}
return matcher(finder);
}

View File

@ -77,3 +77,61 @@
(#trim-end! @notebookCell)
(#insertion-delimiter! @notebookCell "\n\n")
)
;;!! # H1
;;!! ## H2
(
(section) @section @_.removal
(#trim-end! @section)
)
;;!! # H1
(
(section
(atx_heading
(atx_h1_marker)
)
) @sectionLevelOne @_.removal
(#trim-end! @sectionLevelOne)
)
;;!! ## H2
(
(section
(atx_heading
(atx_h2_marker)
)
) @sectionLevelTwo @_.removal
(#trim-end! @sectionLevelTwo)
)
(
(section
(atx_heading
(atx_h3_marker)
)
) @sectionLevelThree @_.removal
(#trim-end! @sectionLevelThree)
)
(
(section
(atx_heading
(atx_h4_marker)
)
) @sectionLevelFour @_.removal
(#trim-end! @sectionLevelFour)
)
(
(section
(atx_heading
(atx_h5_marker)
)
) @sectionLevelFive @_.removal
(#trim-end! @sectionLevelFive)
)
(
(section
(atx_heading
(atx_h6_marker)
)
) @sectionLevelSix @_.removal
(#trim-end! @sectionLevelSix)
)