Skip to content

Commit

Permalink
Initial working version
Browse files Browse the repository at this point in the history
  • Loading branch information
pokey committed Dec 8, 2021
1 parent 83543d3 commit 277303e
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 35 deletions.
15 changes: 15 additions & 0 deletions src/languages/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const supportedLanguageIds = [
"c",
"cpp",
"csharp",
"java",
"javascript",
"javascriptreact",
"json",
"jsonc",
"python",
"typescript",
"typescriptreact",
] as const;

export type SupportedLanguageId = typeof supportedLanguageIds[number];
68 changes: 68 additions & 0 deletions src/languages/getTextFragmentExtractor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { SyntaxNode } from "web-tree-sitter";
import { SelectionWithEditor } from "../typings/Types";
import { textFragmentExtractor as jsonTextFragmentExtractor } from "./json";
import { UnsupportedLanguageError } from "../errors";
import { Range } from "vscode";
import { SupportedLanguageId } from "./constants";
import { getNodeRange, makeRangeFromPositions } from "../util/nodeSelectors";
import { getNodeMatcher } from "./index";

export type TextFragmentExtractor = (
node: SyntaxNode,
selection: SelectionWithEditor
) => Range | null;

function constructDefaultTextFragmentExtractor(
languageId: SupportedLanguageId
): TextFragmentExtractor {
const stringNodeMatcher = getNodeMatcher(languageId, "string", false);
const commentNodeMatcher = getNodeMatcher(languageId, "comment", false);

return (node: SyntaxNode, selection: SelectionWithEditor) => {
const isStringNode = stringNodeMatcher(selection, node) != null;

if (isStringNode || commentNodeMatcher(selection, node) != null) {
if (isStringNode) {
const children = node.children;

return makeRangeFromPositions(
children[0].endPosition,
children[children.length - 1].startPosition
);
} else {
return getNodeRange(node);
}
}

return null;
};
}

export default function getTextFragmentExtractor(
languageId: string
): TextFragmentExtractor {
const extractor = textFragmentExtractors[languageId as SupportedLanguageId];

if (extractor == null) {
throw new UnsupportedLanguageError(languageId);
}

return extractor;
}

const textFragmentExtractors: Record<
SupportedLanguageId,
TextFragmentExtractor
> = {
c: constructDefaultTextFragmentExtractor("c"),
cpp: constructDefaultTextFragmentExtractor("cpp"),
csharp: constructDefaultTextFragmentExtractor("csharp"),
java: constructDefaultTextFragmentExtractor("java"),
javascript: constructDefaultTextFragmentExtractor("javascript"),
javascriptreact: constructDefaultTextFragmentExtractor("javascriptreact"),
jsonc: jsonTextFragmentExtractor,
json: jsonTextFragmentExtractor,
python: constructDefaultTextFragmentExtractor("python"),
typescript: constructDefaultTextFragmentExtractor("typescript"),
typescriptreact: constructDefaultTextFragmentExtractor("typescriptreact"),
};
16 changes: 13 additions & 3 deletions src/languages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import {
import cpp from "./cpp";
import csharp from "./csharp";
import java from "./java";
import json from "./json";
import { patternMatchers as json } from "./json";
import python from "./python";
import typescript from "./typescript";
import { UnsupportedLanguageError } from "../errors";
import { SupportedLanguageId, supportedLanguageIds } from "./constants";

const languageMatchers: Record<string, Record<ScopeType, NodeMatcher>> = {
const languageMatchers: Record<
SupportedLanguageId,
Record<ScopeType, NodeMatcher>
> = {
c: cpp,
cpp: cpp,
csharp: csharp,
Expand All @@ -29,12 +33,18 @@ const languageMatchers: Record<string, Record<ScopeType, NodeMatcher>> = {
typescriptreact: typescript,
};

export function isLanguageSupported(
languageId: string
): languageId is SupportedLanguageId {
return languageId in supportedLanguageIds;
}

export function getNodeMatcher(
languageId: string,
scopeType: ScopeType,
includeSiblings: boolean
): NodeMatcher {
const matchers = languageMatchers[languageId];
const matchers = languageMatchers[languageId as SupportedLanguageId];

if (matchers == null) {
throw new UnsupportedLanguageError(languageId);
Expand Down
21 changes: 19 additions & 2 deletions src/languages/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import {
leadingMatcher,
trailingMatcher,
} from "../util/nodeMatchers";
import { ScopeType, NodeMatcherAlternative } from "../typings/Types";
import {
ScopeType,
NodeMatcherAlternative,
SelectionWithEditor,
} from "../typings/Types";
import { SyntaxNode } from "web-tree-sitter";
import { getNodeRange } from "../util/nodeSelectors";

const nodeMatchers: Partial<Record<ScopeType, NodeMatcherAlternative>> = {
map: "object",
Expand All @@ -16,4 +22,15 @@ const nodeMatchers: Partial<Record<ScopeType, NodeMatcherAlternative>> = {
collectionItem: argumentMatcher("object", "array"),
};

export default createPatternMatchers(nodeMatchers);
export const patternMatchers = createPatternMatchers(nodeMatchers);

export function textFragmentExtractor(
node: SyntaxNode,
selection: SelectionWithEditor
) {
if (node.type === "string_content") {
return getNodeRange(node);
}

return null;
}
40 changes: 10 additions & 30 deletions src/processTargets/modifiers/surroundingPair/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {
} from "../../../util/nodeSelectors";
import { SelectionWithEditorWithContext } from "../processModifier";
import { complexDelimiterMap } from "./delimiterMaps";
import getTextFragmentExtractor, {
TextFragmentExtractor,
} from "../../../languages/getTextFragmentExtractor";

/**
* Applies the surrounding pair modifier to the given selection. First looks to
Expand All @@ -41,14 +44,14 @@ export function processSurroundingPair(
] ?? [modifier.delimiter];

let node: SyntaxNode | null;
let stringNodeMatcher: NodeMatcher;
let commentNodeMatcher: NodeMatcher;
let textFragmentExtractor: TextFragmentExtractor;

try {
node = context.getNodeAtLocation(
new Location(document.uri, selection.selection)
);
stringNodeMatcher = getNodeMatcher(document.languageId, "string", false);
commentNodeMatcher = getNodeMatcher(document.languageId, "comment", false);

textFragmentExtractor = getTextFragmentExtractor(document.languageId);
} catch (err) {
if ((err as Error).name === "UnsupportedLanguageError") {
// If we're in a language where we don't have a parse tree we use the text
Expand All @@ -68,35 +71,12 @@ export function processSurroundingPair(

// If we have a parse tree but we are in a string node or in a comment node,
// then we use the text-based algorithm
const isStringNode = stringNodeMatcher(selection, node) != null;
if (isStringNode || commentNodeMatcher(selection, node) != null) {
let nodeRange: Range;

if (isStringNode) {
const children = node.children;

if (children.length !== 0) {
nodeRange = makeRangeFromPositions(
children[0].endPosition,
children[children.length - 1].startPosition
);
} else {
// This is a hack to deal with the fact that java doesn't have
// quotation mark tokens as children of the string. Rather than letting
// the parse tree handle the quotation marks in java, we instead just
// let the textual surround handle them by letting it see the quotation
// marks. In other languages we prefer to let the parser handle the
// quotation marks in case they are more than one character long.
nodeRange = getNodeRange(node);
}
} else {
nodeRange = getNodeRange(node);
}

const textFragmentRange = textFragmentExtractor(node, selection);
if (textFragmentRange != null) {
const surroundingRange = findSurroundingPairTextBased(
selection.editor,
selection.selection,
nodeRange,
textFragmentRange,
delimiters,
modifier.delimiterInclusion,
modifier.forceDirection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: json
command:
version: 0
spokenForm: clear pair
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: any}
initialState:
documentContents: "\"(hello)\""
selections:
- anchor: {line: 0, character: 5}
active: {line: 0, character: 5}
marks: {}
finalState:
documentContents: "\"\""
selections:
- anchor: {line: 0, character: 1}
active: {line: 0, character: 1}
thatMark:
- anchor: {line: 0, character: 1}
active: {line: 0, character: 1}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: json
command:
version: 0
spokenForm: clear pair
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: any}
initialState:
documentContents: "\"(hello)\""
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: ""
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: json
command:
version: 0
spokenForm: clear round
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: parentheses}
initialState:
documentContents: "\"(hello)\""
selections:
- anchor: {line: 0, character: 5}
active: {line: 0, character: 5}
marks: {}
finalState:
documentContents: "\"\""
selections:
- anchor: {line: 0, character: 1}
active: {line: 0, character: 1}
thatMark:
- anchor: {line: 0, character: 1}
active: {line: 0, character: 1}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: parentheses}}]

0 comments on commit 277303e

Please sign in to comment.