Skip to content

Commit 4ed9153

Browse files
author
Amelia Wattenberger
committed
markdown block - detect blockcomponents without newline
1 parent d4634f1 commit 4ed9153

File tree

4 files changed

+124
-66
lines changed

4 files changed

+124
-66
lines changed

blocks/file-blocks/markdown-edit/block-component-widget.tsx

Lines changed: 69 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -111,74 +111,80 @@ export const blockComponentWidget = ({
111111
const decorate = (state: EditorState) => {
112112
const widgets: Range<Decoration>[] = [];
113113

114+
const onAddBlockComponent = (from: number, to: number, text: string) => {
115+
const blockComponentPropsRegex =
116+
/(?!\<BlockComponent[\s\S\n]*)([\s\S\n]*?)(?=\/\>)/;
117+
const propsString = blockComponentPropsRegex
118+
.exec(text)?.[0]
119+
.split("BlockComponent")[1];
120+
if (!propsString) return;
121+
let props = {};
122+
const propsArray = propsString.split("=");
123+
let runningLastPropKey = "";
124+
propsArray.forEach((prop, index) => {
125+
const lastWordInString =
126+
prop.split(/\s+/)[prop.split(/\s+/).length - 1];
127+
const key = runningLastPropKey;
128+
runningLastPropKey = lastWordInString;
129+
// slice lastWordInString from end
130+
const valueString = prop.slice(
131+
0,
132+
prop.length - lastWordInString.length
133+
);
134+
if (!key || !valueString) return;
135+
136+
// TODO: extract props from string in a more robust way
137+
try {
138+
eval(
139+
`window.parsedValue = ${valueString
140+
.trim()
141+
// remove start and end curly braces
142+
.replace(/^\{|\}$/g, "")}`
143+
);
144+
props[key] = window.parsedValue;
145+
} catch (e) {
146+
props[key] = valueString;
147+
}
148+
});
149+
150+
const onChangeProps = (newProps: any) => {
151+
const newString = `<BlockComponent
152+
${Object.keys(newProps)
153+
.map((key) => `${key}={${JSON.stringify(newProps[key])}}`)
154+
.join("\n")}
155+
/>`;
156+
157+
onDispatchChanges({
158+
changes: {
159+
from,
160+
to,
161+
insert: Text.of([newString]),
162+
},
163+
});
164+
};
165+
const newDecoration = blockComponentDecoration({
166+
parentProps,
167+
props,
168+
onChangeProps,
169+
});
170+
widgets.push(newDecoration.range(from, to));
171+
};
172+
114173
syntaxTree(state).iterate({
115174
enter: ({ type, from, to }) => {
116175
let text = state.doc.sliceString(from, to);
117176
if (type.name === "Document") return;
118177

119-
const locationOfCloseTag = text.indexOf("/>");
120-
to = from + locationOfCloseTag + 2;
121-
122-
const blockComponentRegex = /\<BlockComponent[\s\S\n]/;
123-
const isBlockComponent = blockComponentRegex.test(text);
124-
if (isBlockComponent) {
125-
const blockComponentPropsRegex =
126-
/(?!\<BlockComponent[\s\S\n]*)([\s\S\n]*?)(?=\/\>)/;
127-
const propsString = blockComponentPropsRegex
128-
.exec(text)?.[0]
129-
.split("BlockComponent")[1];
130-
if (!propsString) return;
131-
let props = {};
132-
const propsArray = propsString.split("=");
133-
let runningLastPropKey = "";
134-
propsArray.forEach((prop, index) => {
135-
const lastWordInString =
136-
prop.split(/\s+/)[prop.split(/\s+/).length - 1];
137-
const key = runningLastPropKey;
138-
runningLastPropKey = lastWordInString;
139-
// slice lastWordInString from end
140-
const valueString = prop.slice(
141-
0,
142-
prop.length - lastWordInString.length
143-
);
144-
if (!key || !valueString) return;
145-
146-
// TODO: extract props from string in a more robust way
147-
try {
148-
eval(
149-
`window.parsedValue = ${valueString
150-
.trim()
151-
// remove start and end curly braces
152-
.replace(/^\{|\}$/g, "")}`
153-
);
154-
props[key] = window.parsedValue;
155-
} catch (e) {
156-
props[key] = valueString;
157-
}
158-
});
159-
160-
const onChangeProps = (newProps: any) => {
161-
const newString = `<BlockComponent
162-
${Object.keys(newProps)
163-
.map((key) => `${key}={${JSON.stringify(newProps[key])}}`)
164-
.join("\n")}
165-
/>`;
166-
167-
onDispatchChanges({
168-
changes: {
169-
from,
170-
to,
171-
insert: Text.of([newString]),
172-
},
173-
});
174-
};
175-
const newDecoration = blockComponentDecoration({
176-
parentProps,
177-
props,
178-
onChangeProps,
179-
});
180-
widgets.push(newDecoration.range(from, to));
181-
}
178+
const blockComponentRegex = /\<BlockComponent[\s\S]*?\/\>/gm;
179+
const blockComponentMatches = text.match(blockComponentRegex);
180+
blockComponentMatches?.forEach((match) => {
181+
const locationOfOpenTag = text.indexOf(match);
182+
onAddBlockComponent(
183+
from + locationOfOpenTag,
184+
from + locationOfOpenTag + match.length,
185+
match
186+
);
187+
});
182188
},
183189
});
184190

blocks/file-blocks/markdown-edit/index.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { FileBlockProps } from "@githubnext/blocks";
1+
import {
2+
FileBlockProps,
3+
TreeItem,
4+
getNestedFileTree,
5+
} from "@githubnext/blocks";
26
import React, { useEffect, useMemo, useRef, useState } from "react";
37
import { tw } from "twind";
8+
import { Endpoints } from "@octokit/types";
9+
import pm from "picomatch-browser";
410

511
import { EditorState } from "@codemirror/state";
612
import { EditorView, Rect } from "@codemirror/view";
@@ -23,8 +29,23 @@ export default function (props: FileBlockProps) {
2329
isEditable,
2430
onUpdateContent,
2531
onRequestBlocksRepos,
32+
onRequestGitHubData,
2633
} = props;
2734

35+
const [tree, setTree] = useState<TreeItem[]>([]);
36+
37+
const updateFileTree = async () => {
38+
const res = (await onRequestGitHubData(
39+
`/repos/${context.owner}/${context.repo}/git/trees/${context.sha}?recursive=1`
40+
)) as Endpoints[`GET /repos/{owner}/{repo}/git/trees/{tree_sha}`]["response"]["data"];
41+
const flatTree = res.tree;
42+
const rootItem = { path: "/", type: "tree" };
43+
setTree([rootItem, ...flatTree]);
44+
};
45+
useEffect(() => {
46+
updateFileTree();
47+
}, [context.owner, context.repo, context.sha]);
48+
2849
const parsedContent = useMemo(
2950
() =>
3051
nodeEmoji
@@ -81,14 +102,33 @@ export default function (props: FileBlockProps) {
81102
const activeLineText = previousText.split("\n").slice(-1)[0];
82103
const block = autocompleteFocusedBlock.current;
83104
if (!block) return false;
105+
let defaultPath = tree.length
106+
? tree.find(({ path, type }) => {
107+
if ((block.type === "folder") !== (type === "tree")) return false;
108+
if (!block.matches) return true;
109+
return block.matches.find((key) => {
110+
if (!path && !key) return true;
111+
const doesMatch = pm(key, { bash: true, dot: true })(path);
112+
return doesMatch;
113+
});
114+
})?.path
115+
: "";
116+
if (!defaultPath) {
117+
defaultPath = block.type === "file" ? context.path : "/";
118+
}
119+
84120
const newText = `<BlockComponent
85121
block={{
86122
owner: "${block.owner}",
87123
repo: "${block.repo}",
88124
id: "${block.id}",
89125
type: "${block.type}",
90126
}}
91-
/>`;
127+
context={{
128+
path: "${defaultPath}"
129+
}}
130+
/>
131+
`;
92132
view.dispatch({
93133
changes: {
94134
from: from - activeLineText.length,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@lezer/markdown": "^1.0.1",
2929
"@mdx-js/runtime": "2.0.0-next.9",
3030
"@octokit/rest": "^18.12.0",
31+
"@octokit/types": "^8.0.0",
3132
"@primer/octicons-react": "^16.0.0",
3233
"@primer/primitives": "^7.8.3",
3334
"@primer/react": "^35.2.0",
@@ -55,7 +56,6 @@
5556
"ol": "^6.14.1",
5657
"p5": "^1.4.1",
5758
"papaparse": "^5.3.1",
58-
"picomatch": "^2.3.1",
5959
"picomatch-browser": "^2.2.6",
6060
"react": "^18.1.0",
6161
"react-diff-view": "^2.4.8",

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,11 @@
13021302
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0"
13031303
integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==
13041304

1305+
"@octokit/openapi-types@^14.0.0":
1306+
version "14.0.0"
1307+
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a"
1308+
integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==
1309+
13051310
"@octokit/plugin-paginate-rest@^2.16.8":
13061311
version "2.21.3"
13071312
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e"
@@ -1360,6 +1365,13 @@
13601365
dependencies:
13611366
"@octokit/openapi-types" "^12.11.0"
13621367

1368+
"@octokit/types@^8.0.0":
1369+
version "8.0.0"
1370+
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.0.0.tgz#93f0b865786c4153f0f6924da067fe0bb7426a9f"
1371+
integrity sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==
1372+
dependencies:
1373+
"@octokit/openapi-types" "^14.0.0"
1374+
13631375
"@petamoriken/float16@^3.4.7":
13641376
version "3.6.6"
13651377
resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.6.6.tgz#641f73913a6be402b34e4bdfca98d6832ed55586"

0 commit comments

Comments
 (0)