From fbff57848b5da141a1fcd3b4abdd85d62815b59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Mon, 8 Apr 2024 09:54:39 +0800 Subject: [PATCH 01/10] typo: qickPick -> quickPick --- src/extension-handler/quick-pick-handler.ts | 14 +++++++------- src/extension.ts | 2 +- src/type-definition/SupportCaseType.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/extension-handler/quick-pick-handler.ts b/src/extension-handler/quick-pick-handler.ts index a384115..b0b9855 100644 --- a/src/extension-handler/quick-pick-handler.ts +++ b/src/extension-handler/quick-pick-handler.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import QuickPickItemEx from "../type-definition/QuickPickItemExType"; -import { qickPickSupportCases } from '../type-definition/SupportCaseType'; +import { quickPickSupportCases } from '../type-definition/SupportCaseType'; import { TransformTextResult } from '../type-definition/TransformTextResultType'; import { transformMutliLineText } from '../main-code/transform'; import { EOL } from '../type-definition/EOLType'; @@ -22,22 +22,22 @@ function generateOptionsBasedOnText(text: string, eol: EOL): Array = transformMutliLineText(text); const mergeResultList: Array = []; - for (const qickPick of qickPickSupportCases) { - const conversionResult: string = caseConversion(qickPick.type, text, eol, results); + for (const quickPick of quickPickSupportCases) { + const conversionResult: string = caseConversion(quickPick.type, text, eol, results); const recommendItem: RecommendItem | undefined = mergeResultList.find(item => item.conversionText === conversionResult); if (recommendItem === undefined) { let item: RecommendItem = { conversionText: conversionResult, - transforTo: [qickPick.shortName], // qickPick.name - keyword: qickPick.keyword, + transforTo: [quickPick.shortName], // quickPick.name + keyword: quickPick.keyword, }; mergeResultList.push(item); continue; } - recommendItem.transforTo.push(qickPick.shortName); // qickPick.name - recommendItem.keyword = Array.from(new Set(recommendItem.keyword.concat(qickPick.keyword))); // 关键词去重 + recommendItem.transforTo.push(quickPick.shortName); // quickPick.name + recommendItem.keyword = Array.from(new Set(recommendItem.keyword.concat(quickPick.keyword))); // 关键词去重 } // 根据文本生成选项的逻辑 diff --git a/src/extension.ts b/src/extension.ts index 328a1d1..1914b56 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode'; import handleEditorReplace from './extension-handler/editor-submenu-handler'; import { handleQuickPick } from './extension-handler/quick-pick-handler'; -import { SupportCase, commands } from './type-definition/SupportCaseType'; +import { commands } from './type-definition/SupportCaseType'; import { createStatusBarItem, updateStatusBarItemVisable } from './extension-handler/status-bar-handler'; // This method is called when your extension is activated diff --git a/src/type-definition/SupportCaseType.ts b/src/type-definition/SupportCaseType.ts index 13d2762..0458c3d 100644 --- a/src/type-definition/SupportCaseType.ts +++ b/src/type-definition/SupportCaseType.ts @@ -263,7 +263,7 @@ export const commands: Array<{ command: string; targetCase: SupportCase }> = [ * 所有支持的命名方式 * @since 2024-04-06 */ -export const qickPickSupportCases = [ +export const quickPickSupportCases = [ { type: SupportCase.CAMEL_CASE, name: '小驼峰(驼峰)命名', From 74111398da02bbf023025067fdef86f1d13877da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Mon, 8 Apr 2024 10:02:45 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E5=90=8D=E7=A7=B0=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=EF=BC=9A=E8=BF=9E=E5=AD=97=E7=AC=A6(=E8=84=8A=E6=9F=B1?= =?UTF-8?q?=E5=BC=8F)=20->=20=E4=B8=AD=E5=88=92=E7=BA=BF(=E8=BF=9E?= =?UTF-8?q?=E5=AD=97=E7=AC=A6/=E8=84=8A=E6=9F=B1=E5=BC=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- package-comment.jsonc | 10 +++++----- package.json | 10 +++++----- src/main-code/conversion.ts | 16 ++++++++-------- src/type-definition/SupportCaseType.ts | 16 ++++++++-------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index f93c017..828b9fd 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,10 @@ Or right-click on the selected text -> Convert string to... | 下划线(蛇形) + 小驼峰(驼峰)命名 | Snake Camel Case | foo_Bar | | 下划线(蛇形) + 大驼峰(帕斯卡)命名 | Snake Pascal Case | Foo_Bar | | 下划线(蛇形) + 全大写命名 | Snake Upper Case | FOO_BAR | -| 连字符(脊柱式)命名 | Kebab Case / Spinal Case | foo-bar | -| 连字符(脊柱式) + 小驼峰(驼峰)命名 | Kebab Camel Case | foo-Bar | -| 连字符(脊柱式) + 大驼峰(帕斯卡)命名 | Kebab Pascal Case | Foo-Bar | -| 连字符(脊柱式) + 全大写命名 | Kebab Upper Case | FOO-BAR | +| 中划线(连字符/脊柱式)命名 | Kebab Case / Spinal Case | foo-bar | +| 中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 | Kebab Camel Case | foo-Bar | +| 中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 | Kebab Pascal Case | Foo-Bar | +| 中划线(连字符/脊柱式) + 全大写命名 | Kebab Upper Case | FOO-BAR | | 空格分隔命名 | Space Case | foo bar | | 空格分隔 + 小驼峰(驼峰)命名 | Space Camel Case | foo Bar | | 空格分隔 + 大驼峰(帕斯卡)命名 | Space Pascal Case | Foo Bar | diff --git a/package-comment.jsonc b/package-comment.jsonc index 1badcc5..706d0d7 100644 --- a/package-comment.jsonc +++ b/package-comment.jsonc @@ -2,7 +2,7 @@ // 插件 id 及名称 "name": "variable-conversion", "displayName": "Variable Conversion", - "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、连字符(脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", + "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", // 版本号 "version": "1.0.7", // logo @@ -77,19 +77,19 @@ // group-3-kebab { "command": "variable-conversion.toKebabCase", - "title": "连字符(脊柱式)命名 (Kebab/Spinal Case) [ foo-bar ]" + "title": "中划线(连字符/脊柱式)命名 (Kebab/Spinal Case) [ foo-bar ]" }, { "command": "variable-conversion.toKebabUpperCase", - "title": "连字符(脊柱式) + 全大写命名 (Kebab Upper Case) [ FOO-BAR ]" + "title": "中划线(连字符/脊柱式) + 全大写命名 (Kebab Upper Case) [ FOO-BAR ]" }, { "command": "variable-conversion.toKebabPascalCase", - "title": "连字符(脊柱式) + 大驼峰(帕斯卡)命名 (Kebab Pascal Case) [ Foo-Bar ]" + "title": "中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 (Kebab Pascal Case) [ Foo-Bar ]" }, { "command": "variable-conversion.toKebabCamelCase", - "title": "连字符(脊柱式) + 小驼峰(驼峰)命名 (Kebab Camel Case) [ foo-Bar ]" + "title": "中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 (Kebab Camel Case) [ foo-Bar ]" }, // group-4-space { diff --git a/package.json b/package.json index 3506cba..bde3d7c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "variable-conversion", "displayName": "Variable Conversion", - "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、连字符(脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", + "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", "version": "1.0.7", "icon": "image/logo.png", "publisher": "coder-xiaomo", @@ -62,19 +62,19 @@ }, { "command": "variable-conversion.toKebabCase", - "title": "连字符(脊柱式)命名 (Kebab/Spinal Case) [ foo-bar ]" + "title": "中划线(连字符/脊柱式)命名 (Kebab/Spinal Case) [ foo-bar ]" }, { "command": "variable-conversion.toKebabUpperCase", - "title": "连字符(脊柱式) + 全大写命名 (Kebab Upper Case) [ FOO-BAR ]" + "title": "中划线(连字符/脊柱式) + 全大写命名 (Kebab Upper Case) [ FOO-BAR ]" }, { "command": "variable-conversion.toKebabPascalCase", - "title": "连字符(脊柱式) + 大驼峰(帕斯卡)命名 (Kebab Pascal Case) [ Foo-Bar ]" + "title": "中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 (Kebab Pascal Case) [ Foo-Bar ]" }, { "command": "variable-conversion.toKebabCamelCase", - "title": "连字符(脊柱式) + 小驼峰(驼峰)命名 (Kebab Camel Case) [ foo-Bar ]" + "title": "中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 (Kebab Camel Case) [ foo-Bar ]" }, { "command": "variable-conversion.toSpaceCase", diff --git a/src/main-code/conversion.ts b/src/main-code/conversion.ts index 98bf43a..9e5f284 100644 --- a/src/main-code/conversion.ts +++ b/src/main-code/conversion.ts @@ -26,10 +26,10 @@ export function caseConversion(targetCase: SupportCase, str: string, eol: EOL, c case SupportCase.SNAKE_UPPER_CASE: // 下划线(蛇形) + 全大写命名 spaceCharacter = '_'; break; - case SupportCase.KEBAB_CASE: // 连字符(脊柱式)命名 - case SupportCase.KEBAB_CAMEL_CASE: // 连字符(脊柱式) + 小驼峰(驼峰)命名 - case SupportCase.KEBAB_PASCAL_CASE: // 连字符(脊柱式) + 大驼峰(帕斯卡)命名 - case SupportCase.KEBAB_UPPER_CASE: // 连字符(脊柱式) + 全大写命名 + case SupportCase.KEBAB_CASE: // 中划线(连字符/脊柱式)命名 + case SupportCase.KEBAB_CAMEL_CASE: // 中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 + case SupportCase.KEBAB_PASCAL_CASE: // 中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 + case SupportCase.KEBAB_UPPER_CASE: // 中划线(连字符/脊柱式) + 全大写命名 spaceCharacter = '-'; break; case SupportCase.SPACE_CASE: // 空格分隔命名 @@ -80,7 +80,7 @@ export function caseConversion(targetCase: SupportCase, str: string, eol: EOL, c switch (targetCase) { case SupportCase.CAMEL_CASE: // 小驼峰(驼峰)命名 case SupportCase.SNAKE_CAMEL_CASE: // 下划线(蛇形) + 小驼峰(驼峰)命名 - case SupportCase.KEBAB_CAMEL_CASE: // 连字符(脊柱式) + 小驼峰(驼峰)命名 + case SupportCase.KEBAB_CAMEL_CASE: // 中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 case SupportCase.SPACE_CAMEL_CASE: // 空格分隔 + 小驼峰(驼峰)命名 if (isFirstWord) { transformedWords.push(word); @@ -93,17 +93,17 @@ export function caseConversion(targetCase: SupportCase, str: string, eol: EOL, c break; case SupportCase.PASCAL_CASE: // 大驼峰(帕斯卡)命名 case SupportCase.SNAKE_PASCAL_CASE: // 下划线(蛇形) + 大驼峰(帕斯卡)命名 - case SupportCase.KEBAB_PASCAL_CASE: // 连字符(脊柱式) + 大驼峰(帕斯卡)命名 + case SupportCase.KEBAB_PASCAL_CASE: // 中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 case SupportCase.SPACE_PASCAL_CASE: // 空格分隔 + 大驼峰(帕斯卡)命名 transformedWords.push(pascalCaseWord); break; case SupportCase.SNAKE_CASE: // 下划线(蛇形)命名 - case SupportCase.KEBAB_CASE: // 连字符(脊柱式)命名 + case SupportCase.KEBAB_CASE: // 中划线(连字符/脊柱式)命名 case SupportCase.SPACE_CASE: // 空格分隔命名 transformedWords.push(word); break; case SupportCase.SNAKE_UPPER_CASE: // 下划线(蛇形) + 全大写命名 - case SupportCase.KEBAB_UPPER_CASE: // 连字符(脊柱式) + 全大写命名 + case SupportCase.KEBAB_UPPER_CASE: // 中划线(连字符/脊柱式) + 全大写命名 case SupportCase.SPACE_UPPER_CASE: // 空格分隔 + 全大写命名 transformedWords.push(word.toUpperCase()); break; diff --git a/src/type-definition/SupportCaseType.ts b/src/type-definition/SupportCaseType.ts index 0458c3d..8e28667 100644 --- a/src/type-definition/SupportCaseType.ts +++ b/src/type-definition/SupportCaseType.ts @@ -80,7 +80,7 @@ export enum SupportCase { SNAKE_UPPER_CASE, /** - * 连字符(脊柱式)命名 + * 中划线(连字符/脊柱式)命名 * Kebab Case / Spinal Case * e.g. foo-bar * @@ -91,7 +91,7 @@ export enum SupportCase { KEBAB_CASE, /** - * 连字符(脊柱式) + 小驼峰(驼峰)命名 + * 中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 * Kebab Camel Case * e.g. foo-Bar * @@ -101,7 +101,7 @@ export enum SupportCase { KEBAB_CAMEL_CASE, /** - * 连字符(脊柱式) + 大驼峰(帕斯卡)命名 + * 中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 * Kebab Pascal Case * e.g. Foo-Bar * @@ -111,7 +111,7 @@ export enum SupportCase { KEBAB_PASCAL_CASE, /** - * 连字符(脊柱式) + 全大写命名 + * 中划线(连字符/脊柱式) + 全大写命名 * Kebab Upper Case * e.g. FOO-BAR * @@ -302,25 +302,25 @@ export const quickPickSupportCases = [ }, { type: SupportCase.KEBAB_CASE, - name: '连字符(脊柱式)命名', + name: '中划线(连字符/脊柱式)命名', shortName: '脊柱', keyword: [...keyword.kebab, ...keyword.lower], }, { type: SupportCase.KEBAB_CAMEL_CASE, - name: '连字符(脊柱式) + 小驼峰(驼峰)命名', + name: '中划线(连字符/脊柱式) + 小驼峰(驼峰)命名', shortName: '脊柱驼峰', keyword: [...keyword.kebab, ...keyword.camel], }, { type: SupportCase.KEBAB_PASCAL_CASE, - name: '连字符(脊柱式) + 大驼峰(帕斯卡)命名', + name: '中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名', shortName: '脊柱帕斯卡', keyword: [...keyword.kebab, ...keyword.pascal], }, { type: SupportCase.KEBAB_UPPER_CASE, - name: '连字符(脊柱式) + 全大写命名', + name: '中划线(连字符/脊柱式) + 全大写命名', shortName: '脊柱大写', keyword: [...keyword.kebab, ...keyword.upper], }, From 4ef50ea5561dadafe8f7f2c85832430a78e19521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Mon, 8 Apr 2024 10:10:39 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 +++++++++---------- .../editor-submenu-handler.ts | 2 -- src/type-definition/SupportCaseType.ts | 18 ++++++------ 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 828b9fd..aab6008 100644 --- a/README.md +++ b/README.md @@ -38,24 +38,24 @@ Or right-click on the selected text -> Convert string to... ## 支持的类型 Support Case -| 类型 | Case | 举例 e.g. | -| ----------------------------------- | ------------------------ | ---------------- | -| 小驼峰(驼峰)命名 | Camel Case | fooBar | -| 大驼峰(帕斯卡)命名 | Pascal Case | FooBar | -| 下划线(蛇形)命名 | Snake Case | foo_bar | -| 下划线(蛇形) + 小驼峰(驼峰)命名 | Snake Camel Case | foo_Bar | -| 下划线(蛇形) + 大驼峰(帕斯卡)命名 | Snake Pascal Case | Foo_Bar | -| 下划线(蛇形) + 全大写命名 | Snake Upper Case | FOO_BAR | +| 类型 | Case | 举例 e.g. | +| ------------------------------------------ | ------------------------ | ---------------- | +| 小驼峰(驼峰)命名 | Camel Case | fooBar | +| 大驼峰(帕斯卡)命名 | Pascal Case | FooBar | +| 下划线(蛇形)命名 | Snake Case | foo_bar | +| 下划线(蛇形) + 小驼峰(驼峰)命名 | Snake Camel Case | foo_Bar | +| 下划线(蛇形) + 大驼峰(帕斯卡)命名 | Snake Pascal Case | Foo_Bar | +| 下划线(蛇形) + 全大写命名 | Snake Upper Case | FOO_BAR | | 中划线(连字符/脊柱式)命名 | Kebab Case / Spinal Case | foo-bar | | 中划线(连字符/脊柱式) + 小驼峰(驼峰)命名 | Kebab Camel Case | foo-Bar | | 中划线(连字符/脊柱式) + 大驼峰(帕斯卡)命名 | Kebab Pascal Case | Foo-Bar | | 中划线(连字符/脊柱式) + 全大写命名 | Kebab Upper Case | FOO-BAR | -| 空格分隔命名 | Space Case | foo bar | -| 空格分隔 + 小驼峰(驼峰)命名 | Space Camel Case | foo Bar | -| 空格分隔 + 大驼峰(帕斯卡)命名 | Space Pascal Case | Foo Bar | -| 空格分隔 + 全大写命名 | Space Upper Case | FOO BAR | -| 全小写 | Lower Case | foo_bar / foobar | -| 全大写 | Upper Case | FOO_BAR / FOOBAR | +| 空格分隔命名 | Space Case | foo bar | +| 空格分隔 + 小驼峰(驼峰)命名 | Space Camel Case | foo Bar | +| 空格分隔 + 大驼峰(帕斯卡)命名 | Space Pascal Case | Foo Bar | +| 空格分隔 + 全大写命名 | Space Upper Case | FOO BAR | +| 全小写 | Lower Case | foo_bar / foobar | +| 全大写 | Upper Case | FOO_BAR / FOOBAR | diff --git a/src/extension-handler/editor-submenu-handler.ts b/src/extension-handler/editor-submenu-handler.ts index 338e6cf..d190e01 100644 --- a/src/extension-handler/editor-submenu-handler.ts +++ b/src/extension-handler/editor-submenu-handler.ts @@ -16,7 +16,6 @@ const handleEditorReplace = (targetCase: SupportCase) => { return; } - // console.log('============ start convert ============'); let document = editor.document; let selection = editor.selection; let eol: EOL = document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; @@ -45,7 +44,6 @@ const handleEditorReplace = (targetCase: SupportCase) => { editor.edit(editBuilder => { editBuilder.replace(selection, converted); }); - // console.log('============ finish convert ============'); }; export default handleEditorReplace; diff --git a/src/type-definition/SupportCaseType.ts b/src/type-definition/SupportCaseType.ts index 8e28667..ccd0122 100644 --- a/src/type-definition/SupportCaseType.ts +++ b/src/type-definition/SupportCaseType.ts @@ -2,20 +2,18 @@ * When support a new case, there's something we need to do. * * Code: - * - Add `commands`, `menus` parts in package.json (and package-comment.jsonc) - * - Add main conversion logic in src/main-code/variable-conversion.ts - * - Add disposable in src/extension.ts + * - Add type definition in below `SupportCase` enum and following array + * - Add `commands`, `menus` parts in [package.json] and [package-comment.jsonc] + * - Add main conversion logic in [src/main-code/conversion.ts] * * Test: - * - Add test case type definition in src/type-definition/test-case-type.ts - * - Add test case in src/test/test-case.ts - * - Add test code in src/test/extension.test.ts + * - Add test case type definition in [src/type-definition/TestCaseType.ts] + * - Add test case in [src/test/test-case.ts] + * - Add test code in [src/test/extension.test.ts] * * Docs: - * - Add type definition in below `SupportCase` enum - * - Modify `description` in package.json - * - Add changes in CHANGELOG.md - * - Add changes in README.md + * - Modify `description` in [package.json] and [package-comment.jsonc] + * - Add changes in [CHANGELOG.md] and [README.md] */ export enum SupportCase { From 586c17d9ef16345e9d5b5dbd74fc5f49f1d7d458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 00:41:28 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E9=94=AE=E6=BB=9A=E5=8A=A8=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=20(=E5=90=8C=E6=97=B6=E6=94=AF=E6=8C=81=20Alt=20=E9=BC=A0?= =?UTF-8?q?=E6=A0=87=E9=80=89=E6=8B=A9=E7=9A=84=E5=A4=9A=E8=A1=8C=E9=80=89?= =?UTF-8?q?=E5=8C=BA=E8=BD=AC=E6=8D=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +- package-comment.jsonc | 49 +++++++-- package.json | 32 ++++-- src/extension-handler/status-bar-handler.ts | 4 + src/extension.ts | 99 +++++++++++++------ src/main-code/cyclic-conversion.ts | 104 ++++++++++++++++++++ src/main-code/utils.ts | 27 +++++ src/type-definition/SupportCaseType.ts | 28 ++++++ 8 files changed, 299 insertions(+), 51 deletions(-) create mode 100644 src/main-code/cyclic-conversion.ts create mode 100644 src/main-code/utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ea5fa..f1d5339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,14 +17,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - ### Changed - ### Removed --> +## 1.0.8 + +### Added + +- Supports scrolling conversion via shortcut keys `Ctrl + Alt + [` and `Ctrl + Alt + ]` (simultaneously supports multi-line selection conversion) 支持通过快捷键滚动转换 (同时支持多行选区转换) ## 1.0.7 diff --git a/package-comment.jsonc b/package-comment.jsonc index 706d0d7..1a744cf 100644 --- a/package-comment.jsonc +++ b/package-comment.jsonc @@ -4,7 +4,7 @@ "displayName": "Variable Conversion", "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", // 版本号 - "version": "1.0.7", + "version": "1.0.8", // logo "icon": "image/logo.png", "publisher": "coder-xiaomo", @@ -37,7 +37,45 @@ "onTextSelected" ], "contributes": { + // docs: https://code.visualstudio.com/docs/getstarted/keybindings#_accepted-keys + "keybindings": [ + // 绑定快捷键 + { + "command": "variable-conversion.convertCase", + "key": "shift+alt+t", + "when": "editorTextFocus" + }, + // 滚动转换 上一个 + { + "command": "variable-conversion.cyclicConvertCase.previous", + "key": "ctrl+alt+[", + "args": { + "arrowKey": "[" + }, + "when": "editorTextFocus" + }, + // 滚动转换 下一个 + { + "command": "variable-conversion.cyclicConvertCase.next", + "key": "ctrl+alt+]", + "args": { + "arrowKey": "]" + }, + "when": "editorTextFocus" + } + ], "commands": [ + /** + * 滚动转换 可以不添加 + */ + // { + // "command": "variable-conversion.cyclicConvertCase.previous", + // "title": "字符串转换(上一个)" + // }, + // { + // "command": "variable-conversion.cyclicConvertCase.next", + // "title": "字符串转换(下一个)" + // }, /** * 右键菜单 */ @@ -149,15 +187,6 @@ // "enablement": "false" // } ], - // docs: https://code.visualstudio.com/docs/getstarted/keybindings#_accepted-keys - "keybindings": [ - // 绑定快捷键 - { - "command": "variable-conversion.convertCase", - "key": "shift+alt+t", - "when": "editorTextFocus" - } - ], // docs: https://code.visualstudio.com/api/references/contribution-points#contributes.menus "menus": { // 编辑器右键菜单 diff --git a/package.json b/package.json index bde3d7c..9097db5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "variable-conversion", "displayName": "Variable Conversion", "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", - "version": "1.0.7", + "version": "1.0.8", "icon": "image/logo.png", "publisher": "coder-xiaomo", "engines": { @@ -31,6 +31,29 @@ "onTextSelected" ], "contributes": { + "keybindings": [ + { + "command": "variable-conversion.convertCase", + "key": "shift+alt+t", + "when": "editorTextFocus" + }, + { + "command": "variable-conversion.cyclicConvertCase.previous", + "key": "ctrl+alt+[", + "args": { + "arrowKey": "[" + }, + "when": "editorTextFocus" + }, + { + "command": "variable-conversion.cyclicConvertCase.next", + "key": "ctrl+alt+]", + "args": { + "arrowKey": "]" + }, + "when": "editorTextFocus" + } + ], "commands": [ { "command": "variable-conversion.convertCase", @@ -101,13 +124,6 @@ "title": "全大写 (Upper Case) [ FOOBAR ]" } ], - "keybindings": [ - { - "command": "variable-conversion.convertCase", - "key": "shift+alt+t", - "when": "editorTextFocus" - } - ], "menus": { "editor/context": [ { diff --git a/src/extension-handler/status-bar-handler.ts b/src/extension-handler/status-bar-handler.ts index 45c4405..2e02cec 100644 --- a/src/extension-handler/status-bar-handler.ts +++ b/src/extension-handler/status-bar-handler.ts @@ -5,6 +5,8 @@ import * as vscode from 'vscode'; let statusBar: vscode.StatusBarItem; /** + * 创建状态栏按钮 + * * @since 2024-04-07 */ export function createStatusBarItem() { @@ -16,6 +18,8 @@ export function createStatusBarItem() { } /** + * 判断是否展示状态栏按钮 + * * @since 2024-04-07 */ export function updateStatusBarItemVisable(selectTextLength: number) { diff --git a/src/extension.ts b/src/extension.ts index 1914b56..dca5648 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,56 +5,80 @@ import handleEditorReplace from './extension-handler/editor-submenu-handler'; import { handleQuickPick } from './extension-handler/quick-pick-handler'; import { commands } from './type-definition/SupportCaseType'; import { createStatusBarItem, updateStatusBarItemVisable } from './extension-handler/status-bar-handler'; +import * as CyclicConversion from './main-code/cyclic-conversion'; +import { EOL } from './type-definition/EOLType'; // This method is called when your extension is activated // Your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { + try { + // 获取当前插件的扩展对象 + const currentExtension = vscode.extensions.getExtension('coder-xiaomo.variable-conversion'); + if (currentExtension) { + // 获取版本号 + const version = currentExtension.packageJSON.version; + console.log('[Variable Conversion] current version:', version); + } + } catch (err) { + console.log('get current extension failed', err); + } - // // Use the console to output diagnostic information (console.log) and errors (console.error) - // // This line of code will only be executed once when your extension is activated - // console.log('Congratulations, your extension "variable-conversion" is now active!'); - - // // The command has been defined in the package.json file - // // Now provide the implementation of the command with registerCommand - // // The commandId parameter must match the command field in package.json - // let disposable = vscode.commands.registerCommand('variable-conversion.helloWorld', () => { - // // The code you place here will be executed every time your command is executed - // // Display a message box to the user - // vscode.window.showInformationMessage('Hello World from variable-conversion!'); - // }); - + // 用于记录当前选中的文本的长度 let selectTextLength = 0; + + // 选中文本改变时触发 + const onTextEditorSelectionChangeCallback = (textEditor: vscode.TextEditor, selections: readonly vscode.Selection[]) => { + // 获取选中的文本 + const text: string = textEditor.document.getText(selections[0]); + selectTextLength = text.length; + + // 获取选中的文本块 + const textList: string[] = []; + for (const selection of selections) { + const text = textEditor.document.getText(selection); + textList.push(text); + } + + // 更新 _textSelectionLength (用于判断是否展示右键菜单) + vscode.commands.executeCommand('setContext', '_textSelectionLength', selectTextLength); + + // 判断是否展示状态栏按钮 + updateStatusBarItemVisable(selectTextLength); + + // 滚动转换:记录当前选中内容,并且进行转换 + let eol: EOL = textEditor.document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; + CyclicConversion.onUserSelectionUpdated(selections, textList, eol); + }; + + // 创建状态栏按钮 createStatusBarItem(); + + /** + * 切换编辑器 Tab 时触发 + */ vscode.window.onDidChangeActiveTextEditor(event => { + console.log('onDidChangeActiveTextEditor', event); + // 判断是否展示状态栏按钮 updateStatusBarItemVisable(selectTextLength); }); - // 用于判断是否展示右键菜单 + /** + * 编辑器中光标选中位置改变触发 + */ vscode.window.onDidChangeTextEditorSelection(event => { - const text = event.textEditor.document.getText(event.selections[0]); - // console.log('text.length', text.length); - vscode.commands.executeCommand('setContext', '_textSelectionLength', text.length); - - selectTextLength = text.length; - updateStatusBarItemVisable(selectTextLength); + console.log('光标选中位置改变 onDidChangeTextEditorSelection', event); + // 执行 Callback + onTextEditorSelectionChangeCallback(event.textEditor, event.selections); }); // 初始(VSCode 插件初始化)时也判断一次 (考虑上次关闭 VSCode 有选区,重新打开后 VSCode 回复选区但用户未重新切换选区的场景) let editor = vscode.window.activeTextEditor; if (editor) { - let document = editor.document; - let selection = editor.selection; - // 获取选中的文本 - let text = document.getText(selection); - vscode.commands.executeCommand('setContext', '_textSelectionLength', text.length); - - selectTextLength = text.length; - updateStatusBarItemVisable(selectTextLength); - } else { - // vscode.window.showInformationMessage('editor is undefined'); - console.log('editor is undefined'); + // VSCode 启动时打开的 Tab 不是编辑器 Tab, 执行 Callback (如果不是则跳过) + onTextEditorSelectionChangeCallback(editor, editor.selections); } + // 逐一注册右键菜单-子菜单项 command for (const { command, targetCase } of commands) { let disposable = vscode.commands.registerCommand(command, () => { handleEditorReplace(targetCase); @@ -62,8 +86,21 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(disposable); } + // 注册字符串转换 command 状态栏/快捷键/右键[字符串转换]菜单均有用到 let convertCaseDisposable = vscode.commands.registerCommand('variable-conversion.convertCase', handleQuickPick); context.subscriptions.push(convertCaseDisposable); + + // 注册滚动转换 command + let disposableLoopConversionPrev = vscode.commands.registerCommand('variable-conversion.cyclicConvertCase.previous', ({ arrowKey }) => { + console.log('variable-conversion.convertCase', arrowKey); + CyclicConversion.previousOne(); + }); + context.subscriptions.push(disposableLoopConversionPrev); + let disposableLoopConversionNext = vscode.commands.registerCommand('variable-conversion.cyclicConvertCase.next', ({ arrowKey }) => { + console.log('variable-conversion.convertCase', arrowKey); + CyclicConversion.nextOne(); + }); + context.subscriptions.push(disposableLoopConversionNext); } // This method is called when your extension is deactivated diff --git a/src/main-code/cyclic-conversion.ts b/src/main-code/cyclic-conversion.ts new file mode 100644 index 0000000..8f052e1 --- /dev/null +++ b/src/main-code/cyclic-conversion.ts @@ -0,0 +1,104 @@ +import * as vscode from 'vscode'; +import { EOL } from "../type-definition/EOLType"; +import { cyclicConvertCaseOrder } from "../type-definition/SupportCaseType"; +import { caseConversion } from "./conversion"; +import { isStringArrayEqual, stringListArrayDuplicateRemoval } from './utils'; + +interface UserSelection { + currentEol: EOL + currentSelections?: readonly vscode.Selection[] + currentSelectionsText: string[] + currentIndex: number + isConverted: boolean + conversionsTarget: Array + lastConvertedSelectionsText: string[] // 按快捷键后转换的值(如果下次触发 onUserSelectionUpdated 后传入值是这个,那么跳过,避免丢失当前滚动转换记录) +} + +const userSelection: UserSelection = { + currentEol: '\n', + // currentSelections: undefined, + currentSelectionsText: [], + currentIndex: 0, + isConverted: false, + conversionsTarget: [], // 转换后去重 剩余转换目标 + lastConvertedSelectionsText: [], +}; + +export function onUserSelectionUpdated(selections: readonly vscode.Selection[], textList: string[], eol: EOL): void { + if (textList.length !== 0 && isStringArrayEqual(textList, userSelection.lastConvertedSelectionsText)) { + console.log('skip onUserSelectionUpdated'); + return; + } + console.log('onUserSelectionUpdated', textList, userSelection.lastConvertedSelectionsText); + userSelection.currentEol = eol; + userSelection.currentSelections = selections; + userSelection.currentSelectionsText = textList; + userSelection.currentIndex = 0; + userSelection.isConverted = false; + userSelection.conversionsTarget = []; + userSelection.lastConvertedSelectionsText = textList; +} + +export function previousOne() { + lazyConvert(); + const length = userSelection.conversionsTarget.length; + const oldIndex = userSelection.currentIndex; + const newIndex = oldIndex === 0 ? (length - 1) : (oldIndex - 1); + userSelection.currentIndex = newIndex; + console.log('previousOne oldIndex', oldIndex, 'newIndex', newIndex); + replaceTextEditorSelectedText(); +} + +export function nextOne() { + lazyConvert(); + const length = userSelection.conversionsTarget.length; + const oldIndex = userSelection.currentIndex; + const newIndex = oldIndex >= length - 1 ? 0 : (oldIndex + 1); + userSelection.currentIndex = newIndex; + console.log('nextOne oldIndex', oldIndex, 'newIndex', newIndex); + replaceTextEditorSelectedText(); +} + +function lazyConvert() { + if (userSelection.isConverted) { + return; + } + + const textList = userSelection.currentSelectionsText; + const eol = userSelection.currentEol; + const conversionsTarget: Array = []; + for (const cyclicConvertCase of cyclicConvertCaseOrder) { + // 每一个类型 + const conversionsTargetItem: string[] = []; + for (const line of textList) { + // 选中区块的每一行 + const conversionResult: string = caseConversion(cyclicConvertCase, line, eol); + conversionsTargetItem.push(conversionResult); + } + conversionsTarget.push(conversionsTargetItem); + } + + // 按数组去重 + const noDuplicate = stringListArrayDuplicateRemoval(conversionsTarget); + console.log('noDuplicate', noDuplicate); + + userSelection.conversionsTarget = conversionsTarget; + userSelection.isConverted = true; +} + +function replaceTextEditorSelectedText() { + let editor = vscode.window.activeTextEditor; + if (editor) { + const selections = userSelection.currentSelections || []; + const textList = userSelection.conversionsTarget[userSelection.currentIndex]; + console.log('selections', selections, 'textList', textList); + editor.edit(editBuilder => { + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + const converted = textList[i]; + editBuilder.replace(selection, converted); + } + }); + userSelection.lastConvertedSelectionsText = textList; + } +} \ No newline at end of file diff --git a/src/main-code/utils.ts b/src/main-code/utils.ts new file mode 100644 index 0000000..7a29c9f --- /dev/null +++ b/src/main-code/utils.ts @@ -0,0 +1,27 @@ +export function isStringArrayEqual(array1: string[], array2: string[]) { + if (array1.length !== array2.length) { + return false; + } + for (let index = 0; index < array1.length; index++) { + const element1 = array1[index]; + const element2 = array2[index]; + if (element1 !== element2) { + return false; + } + } + return true; +} + +export function stringListArrayDuplicateRemoval(stringArr: Array): Array { + const tempArr: Array = []; + const newArr: Array = []; + for (let index = 0; index < stringArr.length; index++) { + const element = stringArr[index]; + const elementStr = JSON.stringify(element); + if (!tempArr.includes(elementStr)) { + newArr.push(element); + tempArr.push(elementStr); + } + } + return newArr; +} \ No newline at end of file diff --git a/src/type-definition/SupportCaseType.ts b/src/type-definition/SupportCaseType.ts index ccd0122..6f81b71 100644 --- a/src/type-definition/SupportCaseType.ts +++ b/src/type-definition/SupportCaseType.ts @@ -359,3 +359,31 @@ export const quickPickSupportCases = [ keyword: keyword.upper, }, ]; + +/** + * 通过快捷键滚动转换的顺序 + * @since 2024-04-08 + */ +export const cyclicConvertCaseOrder = [ + SupportCase.CAMEL_CASE, + SupportCase.PASCAL_CASE, + + SupportCase.SNAKE_CASE, + SupportCase.KEBAB_CASE, + SupportCase.SPACE_CASE, + + SupportCase.SNAKE_UPPER_CASE, + SupportCase.KEBAB_UPPER_CASE, + SupportCase.SPACE_UPPER_CASE, + + SupportCase.SNAKE_PASCAL_CASE, + SupportCase.KEBAB_PASCAL_CASE, + SupportCase.SPACE_PASCAL_CASE, + + SupportCase.SNAKE_CAMEL_CASE, + SupportCase.KEBAB_CAMEL_CASE, + SupportCase.SPACE_CAMEL_CASE, + + SupportCase.LOWER_CASE, + SupportCase.UPPER_CASE, +]; From 924add33cdcd55e75a8db10064f1fa2e7c4a9563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 01:39:39 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=87=A0=E5=A4=84=E5=B0=8F=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B=E6=9B=B4=E6=96=B0=20README=20(?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E8=BD=AC=E6=8D=A2=E6=94=B9=E4=B8=BA=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E8=BD=AC=E6=8D=A2=EF=BC=8C=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E6=94=B9=E4=B8=BA=E5=8F=98=E9=87=8F=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +- README.md | 51 +++++++++++++++----- image/cyclic-conversion.gif | Bin 0 -> 50620 bytes package-comment.jsonc | 16 +++--- package.json | 6 +-- src/extension-handler/status-bar-handler.ts | 2 +- src/extension.ts | 8 +-- src/main-code/cyclic-conversion.ts | 13 ++--- src/type-definition/SupportCaseType.ts | 4 +- 9 files changed, 65 insertions(+), 39 deletions(-) create mode 100644 image/cyclic-conversion.gif diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d5339..57ffbd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Supports scrolling conversion via shortcut keys `Ctrl + Alt + [` and `Ctrl + Alt + ]` (simultaneously supports multi-line selection conversion) 支持通过快捷键滚动转换 (同时支持多行选区转换) +- Supports scrolling conversion via shortcut keys `Ctrl + Alt + [` and `Ctrl + Alt + ]` (simultaneously supports multi-line selection conversion) 支持通过快捷键循环转换 (同时支持多行选区转换) ## 1.0.7 @@ -44,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add a status bar button to trigger string conversion (添加底栏按钮,支持通过底栏按钮触发字符串转换) +- Add a status bar button to trigger string conversion (添加状态栏按钮,支持通过状态栏按钮触发变量转换) ## 1.0.5 diff --git a/README.md b/README.md index aab6008..a13c57c 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,67 @@ # 命名方式转换插件 Variable Conversion -一个强大的变量名转换插件,支持常用命名方式间一键转换,支持右键菜单、快捷键、底栏等多种方式使用。
-A powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. +一个强大的变量名转换插件,支持一键转换、循环转换,支持右键菜单、快捷键、状态栏等多种方式使用。
+A powerful variable naming conversion extension. Supports one-key conversion & cyclic conversion. You can use it through the editer menu, shortcut keys and bottom bar. + +- [x] 支持多选区 Support multi-selection +- [x] 支持多窗口 (不支持子窗口状态栏) Support subwindow (subwindow status bar are not supported) +- [x] 支持撤回 & 重做 Support undo & redo (Ctrl + Z / Ctrl + Y) ## 如何使用? How to Use? > 🔭 Tips for Chinese users: 如果您无法看到下文图片,请[点击这里查看](https://gitee.com/coder-xiaomo/variable-conversion-vscode-extension/blob/main/README.md) -#### 1. 选中代码中需要转换的内容
Select The Text To Convert +### 循环转换(Beta) Cyclic conversion(Beta) + +选中代码中需要转换的内容,然后按下 `Ctrl + Alt + [` and `Ctrl + Alt + ]` 即可前后灵活切换变量命名方式。 + +![](image/cyclic-conversion.gif) + +### 基础转换 + +**1. 选中代码中需要转换的内容** **Select The Text To Convert** ![Step1. Select The Text To Convert](image/step1-select-the-text-to-convert.gif) -> Tips:
-> 可以通过 `Ctrl + D` 快捷键选中光标所在的单词
-> You can press `Ctrl + D` to select the word near the cursor +> 小提示:
+> 1. 可以先按住 `Alt` 键不放,再鼠标先后选中多个选区
+> 2. 可以先按住 `Shift + Alt` 键不放,再按下鼠标左键,使用鼠标滑过需要选中的区块
+> 3. 可以通过 `Ctrl + D` 快捷键选中光标所在的单词
+> Tips:
+> 1. You can first hold down `Alt`, and then use the mouse to select multiple selection
+> 2. You can first hold down `Shift + Alt`, then press the left mouse button, and use the mouse to slide over the block that needs to be selected
+> 3. You can press `Ctrl + D` to select the word near the cursor
-#### 2. 按 `Shift + Alt + T`
Press `Shift + Alt + T` +**2. 按 `Shift + Alt + T`** **Press `Shift + Alt + T`** ![Step2. Press Shift + Alt + T](image/step2-press-shift-alt-t.gif) -或者点击状态栏的 `字符串转换` 按钮
+或者点击状态栏的 `变量转换` 按钮
Or click the `String Conversion` button in the status bar ![Step2. Press Status Bar Button](image/step2-press-status-bar-button.png) -或者右键 -> `字符串转换`
+或者右键 -> `变量转换`
Or right-click -> `String Conversion` ![Step2. Variable conversion on the context menu](image/step2-variable-conversion-on-context-menu.png) -或者右键 -> 将字符串转换为...
+或者右键 -> 将变量转换为...
Or right-click on the selected text -> Convert string to... ![Step2. Right-click on the selected text](image/step2-right-click-on-the-selected-text.gif) -#### 3. 选择转换目标,转换完成
Select the conversion target and complete +**3. 选择转换目标,转换完成** **Select the conversion target and complete** + +## 快捷键 + +| 功能 Feature | 快捷键 shortcut key | +| ------------------------------------------------ | ------------------- | +| 变量转换 快速选择 QuickPick | | +| 循环转换→上一个 Cyclic conversion → Previous one | Ctrl + Alt + [ | +| 循环转换→下一个 Cyclic conversion → Next one | Ctrl + Alt + ] | + + ## 支持的类型 Support Case @@ -57,8 +84,6 @@ Or right-click on the selected text -> Convert string to... | 全小写 | Lower Case | foo_bar / foobar | | 全大写 | Upper Case | FOO_BAR / FOOBAR | - - ## 反馈 Feedback 如果您觉得本插件还不够好用,有更好的使用建议;或者发现了 BUG,欢迎[前往 GitHub 仓库提 issue](https://github.com/coder-xiaomo/variable-conversion-vscode-extension/issues). 使用简体中文、繁體中文或 English 均可,不建议使用翻译软件翻译,否则可能会让文字描述变得抽象难懂。
diff --git a/image/cyclic-conversion.gif b/image/cyclic-conversion.gif new file mode 100644 index 0000000000000000000000000000000000000000..a1aea0757aa74a23640451259b2c8b6f89538835 GIT binary patch literal 50620 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 XKPPVxy`HV7y}g_MLuDri6p8->ZfB8& literal 0 HcmV?d00001 diff --git a/package-comment.jsonc b/package-comment.jsonc index 1a744cf..0287374 100644 --- a/package-comment.jsonc +++ b/package-comment.jsonc @@ -2,7 +2,7 @@ // 插件 id 及名称 "name": "variable-conversion", "displayName": "Variable Conversion", - "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", + "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、状态栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", // 版本号 "version": "1.0.8", // logo @@ -45,7 +45,7 @@ "key": "shift+alt+t", "when": "editorTextFocus" }, - // 滚动转换 上一个 + // 循环转换 上一个 { "command": "variable-conversion.cyclicConvertCase.previous", "key": "ctrl+alt+[", @@ -54,7 +54,7 @@ }, "when": "editorTextFocus" }, - // 滚动转换 下一个 + // 循环转换 下一个 { "command": "variable-conversion.cyclicConvertCase.next", "key": "ctrl+alt+]", @@ -66,22 +66,22 @@ ], "commands": [ /** - * 滚动转换 可以不添加 + * 循环转换 可以不添加 */ // { // "command": "variable-conversion.cyclicConvertCase.previous", - // "title": "字符串转换(上一个)" + // "title": "变量转换(上一个)" // }, // { // "command": "variable-conversion.cyclicConvertCase.next", - // "title": "字符串转换(下一个)" + // "title": "变量转换(下一个)" // }, /** * 右键菜单 */ { "command": "variable-conversion.convertCase", - "title": "字符串转换" + "title": "变量转换" }, /** * 右键菜单 - 子菜单 @@ -312,7 +312,7 @@ "submenus": [ { "id": "variable-conversion.stringConversionMenu", - "label": "将字符串转换为..." + "label": "将变量转换为..." } ] }, diff --git a/package.json b/package.json index 9097db5..a348c10 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "variable-conversion", "displayName": "Variable Conversion", - "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、底栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", + "description": "一个强大的变量名转换插件,支持右键菜单、快捷键、状态栏等多种方式使用,支持小驼峰、大驼峰(帕斯卡)、下划线(蛇形)、中划线(连字符/脊柱式)、空格分隔、全小写、全大写等常用命名方式(及组合)转换。 \nA powerful variable naming conversion extension. You can use it through the editer menu, shortcut keys and bottom bar. Support camel, pascal, snake, kebab(spinal), space, lower, upper case, and more.", "version": "1.0.8", "icon": "image/logo.png", "publisher": "coder-xiaomo", @@ -57,7 +57,7 @@ "commands": [ { "command": "variable-conversion.convertCase", - "title": "字符串转换" + "title": "变量转换" }, { "command": "variable-conversion.toCamelCase", @@ -207,7 +207,7 @@ "submenus": [ { "id": "variable-conversion.stringConversionMenu", - "label": "将字符串转换为..." + "label": "将变量转换为..." } ] }, diff --git a/src/extension-handler/status-bar-handler.ts b/src/extension-handler/status-bar-handler.ts index 2e02cec..cca2e05 100644 --- a/src/extension-handler/status-bar-handler.ts +++ b/src/extension-handler/status-bar-handler.ts @@ -11,7 +11,7 @@ let statusBar: vscode.StatusBarItem; */ export function createStatusBarItem() { statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - statusBar.text = '$(find-replace)字符串转换'; + statusBar.text = '$(find-replace)变量转换'; statusBar.command = 'variable-conversion.convertCase'; // statusBar.color = 'red'; // statusBar.show(); diff --git a/src/extension.ts b/src/extension.ts index dca5648..b19aab8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,7 +45,7 @@ export function activate(context: vscode.ExtensionContext) { // 判断是否展示状态栏按钮 updateStatusBarItemVisable(selectTextLength); - // 滚动转换:记录当前选中内容,并且进行转换 + // 循环转换:记录当前选中内容,并且进行转换 let eol: EOL = textEditor.document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; CyclicConversion.onUserSelectionUpdated(selections, textList, eol); }; @@ -66,7 +66,7 @@ export function activate(context: vscode.ExtensionContext) { * 编辑器中光标选中位置改变触发 */ vscode.window.onDidChangeTextEditorSelection(event => { - console.log('光标选中位置改变 onDidChangeTextEditorSelection', event); + // console.log('光标选中位置改变 onDidChangeTextEditorSelection', event); // 执行 Callback onTextEditorSelectionChangeCallback(event.textEditor, event.selections); }); @@ -86,11 +86,11 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(disposable); } - // 注册字符串转换 command 状态栏/快捷键/右键[字符串转换]菜单均有用到 + // 注册变量转换 command 状态栏/快捷键/右键[变量转换]菜单均有用到 let convertCaseDisposable = vscode.commands.registerCommand('variable-conversion.convertCase', handleQuickPick); context.subscriptions.push(convertCaseDisposable); - // 注册滚动转换 command + // 注册循环转换 command let disposableLoopConversionPrev = vscode.commands.registerCommand('variable-conversion.cyclicConvertCase.previous', ({ arrowKey }) => { console.log('variable-conversion.convertCase', arrowKey); CyclicConversion.previousOne(); diff --git a/src/main-code/cyclic-conversion.ts b/src/main-code/cyclic-conversion.ts index 8f052e1..acd7fb8 100644 --- a/src/main-code/cyclic-conversion.ts +++ b/src/main-code/cyclic-conversion.ts @@ -11,7 +11,7 @@ interface UserSelection { currentIndex: number isConverted: boolean conversionsTarget: Array - lastConvertedSelectionsText: string[] // 按快捷键后转换的值(如果下次触发 onUserSelectionUpdated 后传入值是这个,那么跳过,避免丢失当前滚动转换记录) + lastConvertedSelectionsText: string[] // 按快捷键后转换的值(如果下次触发 onUserSelectionUpdated 后传入值是这个,那么跳过,避免丢失当前循环转换记录) } const userSelection: UserSelection = { @@ -25,17 +25,17 @@ const userSelection: UserSelection = { }; export function onUserSelectionUpdated(selections: readonly vscode.Selection[], textList: string[], eol: EOL): void { + userSelection.currentSelections = selections; if (textList.length !== 0 && isStringArrayEqual(textList, userSelection.lastConvertedSelectionsText)) { console.log('skip onUserSelectionUpdated'); return; } console.log('onUserSelectionUpdated', textList, userSelection.lastConvertedSelectionsText); userSelection.currentEol = eol; - userSelection.currentSelections = selections; userSelection.currentSelectionsText = textList; userSelection.currentIndex = 0; userSelection.isConverted = false; - userSelection.conversionsTarget = []; + userSelection.conversionsTarget = [textList]; userSelection.lastConvertedSelectionsText = textList; } @@ -65,8 +65,9 @@ function lazyConvert() { } const textList = userSelection.currentSelectionsText; + // vscode.window.showInformationMessage('lazyConvert' + textList.join('\n')); const eol = userSelection.currentEol; - const conversionsTarget: Array = []; + const conversionsTarget: Array = [textList]; for (const cyclicConvertCase of cyclicConvertCaseOrder) { // 每一个类型 const conversionsTargetItem: string[] = []; @@ -80,9 +81,9 @@ function lazyConvert() { // 按数组去重 const noDuplicate = stringListArrayDuplicateRemoval(conversionsTarget); - console.log('noDuplicate', noDuplicate); + // console.log('noDuplicate', noDuplicate); - userSelection.conversionsTarget = conversionsTarget; + userSelection.conversionsTarget = noDuplicate; userSelection.isConverted = true; } diff --git a/src/type-definition/SupportCaseType.ts b/src/type-definition/SupportCaseType.ts index 6f81b71..5ba6414 100644 --- a/src/type-definition/SupportCaseType.ts +++ b/src/type-definition/SupportCaseType.ts @@ -232,7 +232,7 @@ const keyword = { }; /** - * 接管的字符串转换命令 + * 接管的变量转换命令 */ export const commands: Array<{ command: string; targetCase: SupportCase }> = [ { command: 'variable-conversion.toCamelCase', targetCase: SupportCase.CAMEL_CASE }, @@ -361,7 +361,7 @@ export const quickPickSupportCases = [ ]; /** - * 通过快捷键滚动转换的顺序 + * 通过快捷键循环转换的顺序 * @since 2024-04-08 */ export const cyclicConvertCaseOrder = [ From d45c3a5f41a1280dd664e4390987048f106106a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 01:41:26 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E5=B0=8F=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++-- src/main-code/cyclic-conversion.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57ffbd8..72edaeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Optimize QickPick conversion value display (优化 QickPick 转换值展示) +- Optimize QuickPick conversion value display (优化 QuickPick 转换值展示) - Fixed typo: keyword of KEBAB_PASCAL_CASE, KEBAB_UPPER_CASE (修正 KEBAB_PASCAL_CASE, KEBAB_UPPER_CASE 关键词错误) ## 1.0.6 @@ -57,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add shortcut key `Shift + Alt + T` to show vscode QickPick window (添加快捷键 Shift + Alt + T 来显示 vscode QickPick 弹窗) +- Add shortcut key `Shift + Alt + T` to show vscode QuickPick window (添加快捷键 Shift + Alt + T 来显示 vscode QuickPick 弹窗) - Write `README.md` (完善 README 文档) ## 1.0.3 diff --git a/src/main-code/cyclic-conversion.ts b/src/main-code/cyclic-conversion.ts index acd7fb8..2f757c8 100644 --- a/src/main-code/cyclic-conversion.ts +++ b/src/main-code/cyclic-conversion.ts @@ -27,10 +27,10 @@ const userSelection: UserSelection = { export function onUserSelectionUpdated(selections: readonly vscode.Selection[], textList: string[], eol: EOL): void { userSelection.currentSelections = selections; if (textList.length !== 0 && isStringArrayEqual(textList, userSelection.lastConvertedSelectionsText)) { - console.log('skip onUserSelectionUpdated'); + // console.log('skip onUserSelectionUpdated'); return; } - console.log('onUserSelectionUpdated', textList, userSelection.lastConvertedSelectionsText); + // console.log('onUserSelectionUpdated', textList, userSelection.lastConvertedSelectionsText); userSelection.currentEol = eol; userSelection.currentSelectionsText = textList; userSelection.currentIndex = 0; From ab1d6770bd3ce80cf5c740722ccbae0cd63eaf00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 10:17:10 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E9=80=89=E5=8C=BA=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor-submenu-handler.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/extension-handler/editor-submenu-handler.ts b/src/extension-handler/editor-submenu-handler.ts index d190e01..b4df17f 100644 --- a/src/extension-handler/editor-submenu-handler.ts +++ b/src/extension-handler/editor-submenu-handler.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import { EOL } from '../type-definition/EOLType'; import { caseConversion } from '../main-code/conversion'; import { SupportCase } from '../type-definition/SupportCaseType'; +import { isStringArrayEqual } from '../main-code/utils'; /** * 编辑器右键菜单 @@ -16,33 +17,37 @@ const handleEditorReplace = (targetCase: SupportCase) => { return; } - let document = editor.document; - let selection = editor.selection; - let eol: EOL = document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; + const document = editor.document; + const selections = editor.selections; + const eol: EOL = document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; // 获取选中的文本 - let text = document.getText(selection); + const textList = selections.map(selection => document.getText(selection)); // 转换文本 - const converted = caseConversion(targetCase, text, eol); - console.log('converted', converted); + const convertedList = textList.map(text => caseConversion(targetCase, text, eol)); + console.log('convertedList', convertedList); // 无法转换时,跳过转换 - if (converted === undefined) { + if (convertedList.filter(converted => converted !== undefined).length === 0) { console.log('converted text is undefined, skip replace contents.'); return; } // 当转换后文本与转换前相同时,跳过转换,避免形成 Ctrl + Z 撤销历史记录 - if (converted === text) { + if (isStringArrayEqual(convertedList, textList)) { console.log('selection text is same to converted text, skip replace contents.'); return; } // 替换文本 - console.log('replace selection text', text, 'to', converted); + console.log('replace selection text', textList, 'to', convertedList); editor.edit(editBuilder => { - editBuilder.replace(selection, converted); + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + const converted = convertedList[i]; + editBuilder.replace(selection, converted); + } }); }; From c3199faa6504e48037fe46a82aad65804133693e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 10:29:28 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E5=88=A4=E6=96=AD=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E6=94=AF=E6=8C=81=E5=A4=9A=E9=80=89=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- src/extension.ts | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72edaeb..c800efc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Supports scrolling conversion via shortcut keys `Ctrl + Alt + [` and `Ctrl + Alt + ]` (simultaneously supports multi-line selection conversion) 支持通过快捷键循环转换 (同时支持多行选区转换) +- New: Supports multi-selection conversion (支持多选区转换) +- New: Supports scrolling conversion via shortcut keys `Ctrl + Alt + [` and `Ctrl + Alt + ]` (also supports multi-selection conversion) 支持通过快捷键循环转换 (同时支持多选区转换) ## 1.0.7 diff --git a/src/extension.ts b/src/extension.ts index b19aab8..af5d0f0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -28,16 +28,15 @@ export function activate(context: vscode.ExtensionContext) { // 选中文本改变时触发 const onTextEditorSelectionChangeCallback = (textEditor: vscode.TextEditor, selections: readonly vscode.Selection[]) => { - // 获取选中的文本 - const text: string = textEditor.document.getText(selections[0]); - selectTextLength = text.length; - // 获取选中的文本块 const textList: string[] = []; + let tmp_selectTextLength = 0; for (const selection of selections) { const text = textEditor.document.getText(selection); textList.push(text); + tmp_selectTextLength += text.length; } + selectTextLength = tmp_selectTextLength; // 更新 _textSelectionLength (用于判断是否展示右键菜单) vscode.commands.executeCommand('setContext', '_textSelectionLength', selectTextLength); From 47efcf56f5f4300a355ccba154eba8e8ac6970f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 14:09:56 +0800 Subject: [PATCH 09/10] =?UTF-8?q?QuickPick=20=E5=BC=B9=E7=AA=97=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E9=80=89=E5=8C=BA=E5=88=86=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../editor-submenu-handler.ts | 5 ++ src/extension-handler/quick-pick-handler.ts | 54 +++++++++++-------- src/main-code/transform.ts | 27 ++++++++-- src/type-definition/QuickPickItemExType.ts | 2 +- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index a13c57c..7a36e74 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Or right-click on the selected text -> Convert string to... | 功能 Feature | 快捷键 shortcut key | | ------------------------------------------------ | ------------------- | -| 变量转换 快速选择 QuickPick | | +| 变量转换 快速选择 QuickPick | Shift + Alt + T | | 循环转换→上一个 Cyclic conversion → Previous one | Ctrl + Alt + [ | | 循环转换→下一个 Cyclic conversion → Next one | Ctrl + Alt + ] | diff --git a/src/extension-handler/editor-submenu-handler.ts b/src/extension-handler/editor-submenu-handler.ts index b4df17f..2c599ae 100644 --- a/src/extension-handler/editor-submenu-handler.ts +++ b/src/extension-handler/editor-submenu-handler.ts @@ -24,6 +24,11 @@ const handleEditorReplace = (targetCase: SupportCase) => { // 获取选中的文本 const textList = selections.map(selection => document.getText(selection)); + if (textList.filter(text => text.length > 0).length === 0) { + vscode.window.showInformationMessage('请选择需要转换的变量后重试\nPlease select the variable you want to convert and try again.'); + return; + } + // 转换文本 const convertedList = textList.map(text => caseConversion(targetCase, text, eol)); console.log('convertedList', convertedList); diff --git a/src/extension-handler/quick-pick-handler.ts b/src/extension-handler/quick-pick-handler.ts index b0b9855..693cf67 100644 --- a/src/extension-handler/quick-pick-handler.ts +++ b/src/extension-handler/quick-pick-handler.ts @@ -2,14 +2,15 @@ import * as vscode from 'vscode'; import QuickPickItemEx from "../type-definition/QuickPickItemExType"; import { quickPickSupportCases } from '../type-definition/SupportCaseType'; import { TransformTextResult } from '../type-definition/TransformTextResultType'; -import { transformMutliLineText } from '../main-code/transform'; +import { transformMutliLineText, transformMutliSelectionText } from '../main-code/transform'; import { EOL } from '../type-definition/EOLType'; import { caseConversion } from '../main-code/conversion'; +import { isStringArrayEqual } from '../main-code/utils'; const QuickPickLabelMaxLength = 60; interface RecommendItem { - conversionText: string + conversionText: Array transforTo: string[] keyword: string[] } @@ -17,18 +18,24 @@ interface RecommendItem { /** * 弹出的提示 */ -function generateOptionsBasedOnText(text: string, eol: EOL): Array { +function generateOptionsBasedOnText(textList: string[], eol: EOL): Array { // Cut text 切割文本 - const results: Array = transformMutliLineText(text); + const resultsList: Array = transformMutliSelectionText(textList); const mergeResultList: Array = []; for (const quickPick of quickPickSupportCases) { - const conversionResult: string = caseConversion(quickPick.type, text, eol, results); - const recommendItem: RecommendItem | undefined = mergeResultList.find(item => item.conversionText === conversionResult); + const conversionResults: Array = []; + for (let i = 0; i < textList.length; i++) { + const text = textList[i]; + const results = resultsList[i]; + const conversionResult: string = caseConversion(quickPick.type, text, eol, results); + conversionResults.push(conversionResult); + } + const recommendItem: RecommendItem | undefined = mergeResultList.find(item => isStringArrayEqual(item.conversionText, conversionResults)); if (recommendItem === undefined) { let item: RecommendItem = { - conversionText: conversionResult, + conversionText: conversionResults, transforTo: [quickPick.shortName], // quickPick.name keyword: quickPick.keyword, }; @@ -52,10 +59,10 @@ function generateOptionsBasedOnText(text: string, eol: EOL): Array= QuickPickLabelMaxLength ? (conversionTextForDisplay.substring(0, QuickPickLabelMaxLength - 3) + '...') @@ -76,20 +83,20 @@ export function handleQuickPick() { return; } - let document = editor.document; - let selection = editor.selection; - let eol: EOL = document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; + const document = editor.document; + const selections = editor.selections; + const eol: EOL = document.eol === vscode.EndOfLine.CRLF ? '\r\n' : '\n'; // 获取选中的文本 - let text = document.getText(selection); + const textList = selections.map(selection => document.getText(selection)); - if (text.length === 0) { + if (textList.filter(text => text.length > 0).length === 0) { vscode.window.showInformationMessage('请选择需要转换的变量后重试\nPlease select the variable you want to convert and try again.'); return; } // 基于选中的文本生成选项 - const options = generateOptionsBasedOnText(text, eol); + const options = generateOptionsBasedOnText(textList, eol); // 显示推荐项列表 vscode.window.showQuickPick(options, { matchOnDetail: true, @@ -100,21 +107,22 @@ export function handleQuickPick() { return; } - // 处理用户的选择 - // vscode.window.showInformationMessage(`你选择了: ${JSON.stringify(selection)}`); - - const converted = pickItem.value; + const convertedList = pickItem.value; // 当转换后文本与转换前相同时,跳过转换,避免形成 Ctrl + Z 撤销历史记录 - if (converted === text) { + if (isStringArrayEqual(convertedList, textList)) { console.log('selection text is same to converted text, skip replace contents.'); return; } // 替换文本 - console.log('replace selection text', text, 'to', converted); + console.log('replace selection text', textList, 'to', convertedList); editor.edit(editBuilder => { - editBuilder.replace(selection, converted); + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + const converted = convertedList[i]; + editBuilder.replace(selection, converted); + } }); }); -} \ No newline at end of file +} diff --git a/src/main-code/transform.ts b/src/main-code/transform.ts index 9eaa0dc..0026bc9 100644 --- a/src/main-code/transform.ts +++ b/src/main-code/transform.ts @@ -2,17 +2,36 @@ import { TransformTextResult } from "../type-definition/TransformTextResultType" const logDebugInfo = false; -export function transformMutliLineText(multilineInput: string): Array { - const results: Array = []; - const lines = multilineInput.split(/\r?\n/); +/** + * 多选区分词 + * + * @param multiSelectionInputs + * @returns + * @since 2024-04-03 + */ +export function transformMutliSelectionText(selectionInputs: string[]): Array { + return selectionInputs.map(selectionInput => transformMutliLineText(selectionInput)); +} + +/** + * 多行内容分词(单一选区) + * + * @param multiLineInput + * @returns + * @since 2024-04-03 + */ +export function transformMutliLineText(multiLineInput: string): TransformTextResult[] { + const results: TransformTextResult[] = []; + const lines = multiLineInput.split(/\r?\n/); for (const line of lines) { // console.log('line', '->' + line + '<-'); results.push(transformText(line)); } return results; } + /** - * 分词 + * 独立段落单元分词 (不包含换行) * * @param str * @since 2024-04-02 diff --git a/src/type-definition/QuickPickItemExType.ts b/src/type-definition/QuickPickItemExType.ts index 115e130..38d442a 100644 --- a/src/type-definition/QuickPickItemExType.ts +++ b/src/type-definition/QuickPickItemExType.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; interface ExtendedQuickPickItem extends vscode.QuickPickItem { - value: string; + value: string[]; } // type QuickPickItemEx = ExtendedQuickPickItem | vscode.QuickPickItem; From 7f645cb8fb372a6662063a12b27c538e7ad3f408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E5=B0=8F=E5=A2=A8?= <2291200076@qq.com> Date: Tue, 9 Apr 2024 14:15:29 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E6=97=A0=E5=8F=AF=E9=80=89=E5=80=99?= =?UTF-8?q?=E9=80=89=E8=AF=8D=E6=97=B6=E4=B8=8D=E5=B1=95=E7=A4=BA=20QuickP?= =?UTF-8?q?ick=20=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/extension-handler/quick-pick-handler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/extension-handler/quick-pick-handler.ts b/src/extension-handler/quick-pick-handler.ts index 693cf67..5ef9c59 100644 --- a/src/extension-handler/quick-pick-handler.ts +++ b/src/extension-handler/quick-pick-handler.ts @@ -97,6 +97,11 @@ export function handleQuickPick() { // 基于选中的文本生成选项 const options = generateOptionsBasedOnText(textList, eol); + if (options.length === 0) { + vscode.window.showInformationMessage('所选内容暂无可选转换,请尝试重新选择\nNo conversion candidates are available for the selected content, please try to select another text.'); + return; + } + // 显示推荐项列表 vscode.window.showQuickPick(options, { matchOnDetail: true,