Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fef0ec6

Browse files
authoredMar 7, 2025··
Merge pull request #40 from agencia-e-plus/feat/completion
Feat: block completion inside `children`, `blocks` or capitalized prop
2 parents d71874c + 730cd6d commit fef0ec6

File tree

4 files changed

+426
-91
lines changed

4 files changed

+426
-91
lines changed
 

‎server/src/BlocksHashMap.ts

+86-83
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,89 @@
1-
import { readFileSync } from "fs";
2-
import { glob } from "glob";
3-
import * as JSONC from "jsonc-parser";
1+
import { readFileSync } from 'fs';
2+
import { glob } from 'glob';
3+
import * as JSONC from 'jsonc-parser';
44

55
export class BlocksHashMap {
6-
private rootPath: string;
7-
private blocksMap = new Map<string, string>();
8-
private fileMap = new Map<string, string | Buffer>();
9-
private fileUsagesMap = new Map<string, string[]>();
10-
11-
constructor(rootPath: string) {
12-
this.rootPath = rootPath.replace(/\\/g, "/");
13-
this.fillHashMap();
14-
}
15-
16-
fillHashMap() {
17-
const filesPaths = glob.sync(`${this.rootPath}/**/*.{json,jsonc}`);
18-
const blocksFilesPaths = filesPaths.filter((filePath) =>
19-
filePath.includes("blocks")
20-
);
21-
22-
blocksFilesPaths.forEach((filePath) => this.mapBlocksOnFile(filePath));
23-
}
24-
25-
mapBlocksOnFile(filePath: string, customText?: string) {
26-
const fileContent = customText || readFileSync(filePath);
27-
28-
if (fileContent === "") return;
29-
30-
try {
31-
const blocks: Record<string, BlockFormat> = JSONC.parse(
32-
fileContent.toString()
33-
);
34-
35-
Object.keys(blocks).forEach((blockName) => {
36-
this.blocksMap.set(blockName, filePath);
37-
});
38-
39-
this.fileMap.set(filePath, fileContent);
40-
this.mapBlocksUsages(filePath, blocks);
41-
} catch (error) {
42-
console.log(`unable to parse file ${filePath}`);
43-
}
44-
}
45-
46-
mapBlocksUsages(filePath: string, blocks: Record<string, BlockFormat>) {
47-
const usagesList = Object.values(blocks)
48-
.map((value) => {
49-
const { children = [], blocks = [] } = value;
50-
51-
const propsBlocks = Object.entries(value?.props ?? {}).reduce(
52-
(acc, [propKey, propValue]) => {
53-
if (propKey.match(/^[A-Z]/)) return [...acc, propValue];
54-
return acc;
55-
},
56-
[] as string[]
57-
);
58-
59-
return [...children, ...blocks, ...propsBlocks];
60-
})
61-
.flat();
62-
63-
this.fileUsagesMap.set(filePath, usagesList);
64-
}
65-
66-
getBlockFilePath(blockName: string) {
67-
return this.blocksMap.get(blockName) as string;
68-
}
69-
70-
getFileContent(filePath: string) {
71-
return this.fileMap.get(filePath) as string;
72-
}
73-
74-
listBlocks() {
75-
return this.blocksMap.entries();
76-
}
77-
78-
getBlockUsage(blockName: string) {
79-
const allUsedBlocks = Array.from(this.fileUsagesMap.values()).flat();
80-
return allUsedBlocks.some((blockUsage) => blockUsage === blockName);
81-
}
82-
83-
getFileUsage(filePath: string) {
84-
return this.fileUsagesMap.get(filePath);
85-
}
6+
private rootPath: string;
7+
private blocksMap = new Map<string, string>();
8+
private fileMap = new Map<string, string | Buffer>();
9+
private fileUsagesMap = new Map<string, string[]>();
10+
11+
constructor(rootPath: string) {
12+
this.rootPath = rootPath.replace(/\\/g, '/');
13+
this.fillHashMap();
14+
}
15+
16+
fillHashMap() {
17+
const filesPaths = glob.sync(`${this.rootPath}/**/*.{json,jsonc}`);
18+
const blocksFilesPaths = filesPaths.filter((filePath) => filePath.includes('blocks'));
19+
20+
blocksFilesPaths.forEach((filePath) => this.mapBlocksOnFile(filePath));
21+
}
22+
23+
mapBlocksOnFile(filePath: string, customText?: string) {
24+
const fileContent = customText || readFileSync(filePath);
25+
26+
if (fileContent === '') return;
27+
28+
try {
29+
const blocks: Record<string, BlockFormat> = JSONC.parse(fileContent.toString());
30+
31+
Object.keys(blocks).forEach((blockName) => {
32+
this.blocksMap.set(blockName, filePath);
33+
});
34+
35+
this.fileMap.set(filePath, fileContent);
36+
this.mapBlocksUsages(filePath, blocks);
37+
} catch (error) {
38+
console.log(`unable to parse file ${filePath}`);
39+
}
40+
}
41+
42+
mapBlocksUsages(filePath: string, blocks: Record<string, BlockFormat>) {
43+
const usagesList = Object.values(blocks)
44+
.map((value) => {
45+
const { children = [], blocks = [] } = value;
46+
47+
const propsBlocks = Object.entries(value?.props ?? {}).reduce(
48+
(acc, [propKey, propValue]) => {
49+
if (propKey.match(/^[A-Z]/)) return [...acc, propValue];
50+
return acc;
51+
},
52+
[] as string[]
53+
);
54+
55+
return [...children, ...blocks, ...propsBlocks];
56+
})
57+
.flat();
58+
59+
this.fileUsagesMap.set(filePath, usagesList);
60+
}
61+
62+
getBlockFilePath(blockName: string) {
63+
return this.blocksMap.get(blockName) as string;
64+
}
65+
66+
getFileContent(filePath: string) {
67+
return this.fileMap.get(filePath) as string;
68+
}
69+
70+
listBlocks() {
71+
return this.blocksMap.entries();
72+
}
73+
74+
getBlockUsage(blockName: string) {
75+
const allUsedBlocks = Array.from(this.fileUsagesMap.values()).flat();
76+
return allUsedBlocks.some((blockUsage) => blockUsage === blockName);
77+
}
78+
79+
getBlockContent(blockName: string) {
80+
const filePath = this.getBlockFilePath(blockName);
81+
const fileContent = this.getFileContent(filePath);
82+
const blocks = JSONC.parse(fileContent.toString());
83+
return blocks[blockName];
84+
}
85+
86+
getFileUsage(filePath: string) {
87+
return this.fileUsagesMap.get(filePath);
88+
}
8689
}

‎server/src/handleCompletionResolve.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { CompletionItem, MarkupKind } from 'vscode-languageserver';
2+
import { BlocksHashMap } from './BlocksHashMap';
3+
4+
/**
5+
* Resolves additional information for a completion item when it's focused
6+
*/
7+
export function handleCompletionResolve(
8+
item: CompletionItem,
9+
blocksHashMap: BlocksHashMap
10+
): CompletionItem {
11+
// Check if this is a block suggestion that needs documentation
12+
if (item.data && typeof item.data === 'string') {
13+
const blockKey = item.data;
14+
// Only fetch and add documentation when resolving
15+
item.documentation = {
16+
kind: MarkupKind.Markdown,
17+
value: `\`\`\`json\n// ${blockKey}\n${JSON.stringify(
18+
blocksHashMap.getBlockContent(blockKey),
19+
null,
20+
2
21+
)}\n\`\`\``
22+
};
23+
}
24+
return item;
25+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.