mirror of
https://github.com/cursorless-dev/cursorless.git
synced 2024-10-04 04:47:29 +03:00
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:
parent
d296e70d73
commit
6984b1ae05
66
data/fixtures/scopes/markdown/section.scope
Normal file
66
data/fixtures/scopes/markdown/section.scope
Normal 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"
|
@ -9,5 +9,6 @@ const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel;
|
||||
export const markdownScopeSupport: LanguageScopeSupportFacetMap = {
|
||||
"comment.line": supported,
|
||||
"comment.block": supported,
|
||||
section: supported,
|
||||
notebookCell: supported,
|
||||
};
|
||||
|
@ -38,6 +38,11 @@ export const scopeSupportFacetInfos: Record<
|
||||
scopeType: "environment",
|
||||
},
|
||||
|
||||
section: {
|
||||
description: "A document section",
|
||||
scopeType: "section",
|
||||
},
|
||||
|
||||
list: {
|
||||
description: "A list/array",
|
||||
scopeType: "list",
|
||||
|
@ -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
|
||||
|
@ -7,7 +7,6 @@ export const legacyLanguageIds = [
|
||||
"csharp",
|
||||
"css",
|
||||
"latex",
|
||||
"markdown",
|
||||
"php",
|
||||
"ruby",
|
||||
"rust",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
@ -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,
|
||||
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user