Physicist, Programmer. What you eat, how you think, and most importantly what you have done become who you are. Who are you? and who will you be?
[IT/Programming]
VS code (Cursor AI) extension 만들어 보기 (Upward fold additionally (Smart fold/unfold))
kipid2025. 4. 7. 12:49
반응형
# VS code (Cursor AI) extension 만들어 보기 (Upward fold additionally (Smart fold/unfold))
들여쓰기 (indent) 고려해서 fold/unfold 해주는 기능이 VS code (Cursor AI) 에 기본적으로 있긴 한데, 젤 아래에서 위쪽으로 fold 해주는 기능이 없고 딱히 extension 을 검색해봐도 안뜨길래 만들어 보기로 결심. 그 내용을 공유합니다.
해당 extension 은 에서 보실 수 있습니다. GitHub repo 는 .
## PH
2025-04-07 : First posting.
## TOC
## 우선 AI 의 도움을 받아서 package.json 을 다음과 같이 설정.
fold-unfold-smartly 라는 이름으로 GitHub repository 를 하나 판다. 그리고 VS code marketplace 에 가입을 하고 기타 여러가지 개인 정보들을 넣어준다.
그 정보들을 바탕으로 다음과 같이 package.json 을 만든다.
```[.linenums.scrollable]
{
"name": "fold-unfold-smartly",
"displayName": "Upward fold additionally (Smart fold/unfold)",
"description": "Fold and Unfold a specific indentation more smartly. From the downside of the indentation, fold and unfold.",
"version": "1.0.0",
"publisher": "kipacti",
"repository": {
"type": "git",
"url": "https://github.com/kipid/fold-unfold-smartly"
},
"engines": {
"vscode": "^1.92.0"
},
"categories": [
"Other"
],
"activationEvents": [],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "fold-unfold-smartly.foldUpward",
"title": "Smart Fold Upward"
}
],
"keybindings": [
{
"command": "fold-unfold-smartly.foldUpward",
"key": "ctrl+alt+f",
"when": "editorTextFocus"
}
],
"menus": {
"editor/context": [
{
"command": "fold-unfold-smartly.foldUpward",
"group": "0_folding"
}
]
}
},
"scripts": {
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@types/node": "^22.14.0",
"@types/vscode": "^1.92.0",
"typescript": "^5.8.3",
"vscode": "^1.1.37"
}
}
```/
Cursor AI 가 VS code 버전보다 낮기 때문에 vscode 의 version 을 1.92.0 정도로 적절히 낮춰준다.
```[.linenums]
npm i
```/
명령어로 dependencies 를 깔아준다.
## 기본적으로 TypeScript 로 개발. AI 의 도움 받기.
처음에는 upward fold 기능이 간단할거라 AI 에게 명령만 잘 내리면 순식간에 짜잔하고 extension 이 만들어지길 기대했으나, 아직은 AI 들이 멍청하긴 한가보다.
기본적으로 아주 간단한 케이스에서 동작하도록 까지만 AI 에게 부탁을 해서 짰고, 나머지 세부사항들은 AI 의 도움을 line by line 으로 받으면서 완성해 갔다.
특히나 핵심적인 아래에서부터 위쪽으로 같은 indent 를 찾는 코드 자체를 잘 못 짜줬다. indent 가 작아져도 안되고 같은 레벨이어도 안되고 하는 조건들이 많았는데, 이런건 기본 상식은 아니라 AI 도 이해를 못한듯? 장황하게 설명을 해줘도 코드는 엉망이었다.
## TypeScript compile (tsc) 하고 package 로 VSIX 형태로 만들고 배포 (publish) 까지 하자.
### extension.ts
extension.ts 코드는 다음과 같다. 참고만 하시길...
```[.scrollable]
import * as vscode from "vscode";
import * as path from "path";
function findCurrentToInitialIndentFoldRange(
document: vscode.TextDocument,
startLineIndex: number
): { start: number; end: number } | null {
try {
const startLine = document.lineAt(startLineIndex);
if (startLine.isEmptyOrWhitespace) {
return null;
}
const startIndentation = startLine.firstNonWhitespaceCharacterIndex;
let endLineIndex = -1;
// Find the initial indentation of the same depth
let i = startLineIndex - 1;
if (
document.lineAt(i).firstNonWhitespaceCharacterIndex <= startIndentation
) {
return null;
}
for (i = startLineIndex - 2; i >= 0; i--) {
const currentLine = document.lineAt(i);
if (currentLine.isEmptyOrWhitespace) {
continue;
}
const currentIndentation = currentLine.firstNonWhitespaceCharacterIndex;
if (currentIndentation === startIndentation) {
endLineIndex = i;
break;
} else if (currentIndentation < startIndentation) {
console.log(
`[SmartFold] No foldable range found from current to initial indent`
);
return null;
}
}
if (endLineIndex !== -1) {
console.log(
`[SmartFold] Found range from current indent at line ${
startLineIndex + 1
} to initial indent at line ${endLineIndex + 1}`
);
return { start: endLineIndex, end: startLineIndex };
} else {
console.log(
`[SmartFold] No foldable range found from current to initial indent`
);
return null;
}
} catch (error) {
console.error(
`[SmartFold] findCurrentToInitialIndentFoldRange 오류: ${error}`
);
return null;
}
}
export function activate(context: vscode.ExtensionContext) {
const iconPath = context.asAbsolutePath(path.join("resources", "icon.svg"));
const decorationType = vscode.window.createTextEditorDecorationType({
before: {
contentIconPath: iconPath,
width: "1em",
height: ".7em",
margin: "0 -1em 0 0",
},
});
function updateDecorations(editor: vscode.TextEditor | undefined) {
if (editor) {
const decorations: vscode.DecorationOptions[] = [];
for (let i = 2; i < editor.document.lineCount; i++) {
const range = findCurrentToInitialIndentFoldRange(editor.document, i);
if (range) {
decorations.push({ range: new vscode.Range(i, 0, i, 0) });
}
}
editor.setDecorations(decorationType, decorations);
}
}
// 현재 활성화된 에디터에 대해 데코레이션 업데이트
const editor = vscode.window.activeTextEditor;
updateDecorations(editor);
// 에디터가 변경될 때마다 데코레이션 업데이트
vscode.window.onDidChangeActiveTextEditor(
(editor) => {
updateDecorations(editor);
},
null,
context.subscriptions
);
context.subscriptions.push(
vscode.commands.registerCommand("fold-unfold-smartly.foldUpward", () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
// 현재 선택된 줄 번호 가져오기
const lineIndex = editor.selection.active.line;
console.log(`[SmartFold] foldUpward 명령 실행 - lineIndex: ${lineIndex}`);
// lineIndex가 유효한지 확인
if (lineIndex < 0 || lineIndex >= editor.document.lineCount) {
console.error(`[SmartFold] 유효하지 않은 lineIndex: ${lineIndex}`);
return;
}
const range = findCurrentToInitialIndentFoldRange(
editor.document,
lineIndex
);
if (range) {
const visibleRange = editor.visibleRanges[0];
const startLine = visibleRange.start.line;
vscode.commands.executeCommand("editor.fold", {
selectionLines: [range.start],
});
const newTop = startLine + range.start - range.end + 5;
const newStartPosition = new vscode.Position(
newTop < 0 ? 0 : newTop,
0
);
editor.revealRange(
new vscode.Range(newStartPosition, newStartPosition),
vscode.TextEditorRevealType.AtTop
);
}
})
);
console.log("[SmartFold] 확장 프로그램이 활성화되었습니다.");
vscode.window.showInformationMessage(
"[SmartFold] 확장 프로그램이 활성화되었습니다."
);
}
export function deactivate() {
console.log("[SmartFold] 비활성화되었습니다.");
}
```/
### tsconfig.json
TypeScript config 는 다음과 같다.
```[.linenums.scrollable]
{
"compilerOptions": {
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
/* Modules */
"module": "CommonJS" /* Specify what module code is generated. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
/* Emit */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
/* Interop Constraints */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```/
### tsc && npx vsce package && npx vsce publish
다음과 같은 명령어로 package 를 만들고 publish 까지 하면 끝. 젤 처음엔 access token 넣으라고 할텐데, 에서 잘 찾아서 넣으면 된다.
```[.linenums]
tsc
npx vsce package
npx vsce publish
```/
## 구현 된 모습 동영상