Skip to content

Commit

Permalink
Merge pull request #52 from ChenCMD:fix/multi-line-gen
Browse files Browse the repository at this point in the history
🚸 MultiLineGeneratorの諸々を修正
  • Loading branch information
ChenCMD authored Mar 7, 2022
2 parents c824836 + 5e629eb commit 5f8908c
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 49 deletions.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ This extension provides several useful features for Datapack development.
- [Creating a datapack template](#creating-a-datapack-template)
- [Copy resourcePath](#copy-resourcepath)
- [Quick file creation](#quick-file-creation)
- [Batch input of multiple lines](#batch-input-of-multiple-lines)
- [Strings](#strings)
- [Datapack tag](#datapack-tag)
- [Consecutive values](#consecutive-values)
- [Expression](#expression)
- [Converting formulas to score operation](#converting-formulas-to-score-operation)
- [Recommendations](#recommendations)
- [Special Thanks](#special-thanks)
Expand Down Expand Up @@ -70,6 +75,53 @@ You can create a file with the contents described by describing it.

![gif](https://raw.githubusercontent.com/ChenCMD/MC-Datapack-Utility/master/images/createFile.gif)

## Batch input of multiple lines

Having trouble describing multiple lines with some regularity?

Press `Alt + Shift + D -> Alt + Shift + M`.
This function allows you to generate multiple lines at the cursor position by simply answering a few questions.

Depending on your choice, the method of replacing the `%r` in the first question will change. Here are the types

### Strings

Replace `%r` with any string.
The input string is interpreted line by line and replaced.

Note that this substitution method behaves differently depending on the number of cursors.

#### Difference in behavior when generating depending on the number of cursors

| Behavior with a single cursor | Behavior with multiple cursors |
| :---------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Generate as many input lines as there are starting with the cursor line | If the number of input lines and the number of cursors are different:<br/> input content is generated at each location<br/>If the number of input lines and the number of cursors are the same:<br/> input content is generated one line at a time in the order of cursor placement. |

### Datapack tag

Replace `%r` with the data pack tag `values`.

Note that this substitution method behaves differently depending on the number of cursors.

#### Difference in behavior when generating depending on the number of cursors

| Behavior with a single cursor | Behavior with multiple cursors |
| :------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Generate as many tag elements as there are, starting with the cursor line | If the number of tag elements and the cursor are different:<br/> a tag element is generated at each location<br/>If the number of tag elements and the cursor are the same:<br/> a tag element is generated one line at a time in the order in which the cursor is placed. |

### Consecutive values

Replace `%r` with a constant, continuous value.

Note: If the `Length to pad the value at the beginning` is `-1`, no prefill is performed.

### Expression

Replace `%r` with the value calculated from the expression.
`Math.min(<a>,<b>)`, `Math.floor(<a>)`, etc. are available.

Note: If the `Length to pad the value at the beginning` is `-1`, no prefill is performed.

## Converting formulas to score operation

Too much trouble creating a formula in the scoreboard players operation?
Expand Down
55 changes: 55 additions & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
- [データパックテンプレートの作成](#データパックテンプレートの作成)
- [リソースパスのコピー](#リソースパスのコピー)
- [素早いファイルの作成](#素早いファイルの作成)
- [複数行の一括入力](#複数行の一括入力)
- [文字列](#文字列)
- [データパックタグ](#データパックタグ)
- [連続した値](#連続した値)
- [数式](#数式)
- [計算式のscore operationへの変換](#計算式のscore-operationへの変換)
- [推奨事項](#推奨事項)
- [謝辞](#謝辞)
Expand Down Expand Up @@ -70,6 +75,56 @@ MC Datapack UtilityはVSCode Marketplaceからインストールすることが

![gif](https://raw.githubusercontent.com/ChenCMD/MC-Datapack-Utility/master/images/createFile.gif)

## 複数行の一括入力

規則性を持った複数行を記述するのが面倒?

`Alt + Shift + D -> Alt + Shift + M`を押しましょう。
この機能を使うと、いくつかの質問に答えるだけで複数行をカーソルの位置に生成することができます。

選択によって最初の質問の`%r`の置換方法が変わります。以下はその種類です。

### 文字列

`%r`を自由な文字列で置換します。
入力された文字列は行毎に解釈され、置換されます。

この置換方法はカーソルの個数によって挙動が異なるため注意してください。

#### カーソルの個数による生成時の挙動の差異

| カーソルが一個の時の挙動 | カーソルが複数個の時の挙動 |
| :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| カーソルの行を始めとして入力行数だけ生成 | 入力行数とカーソルの数が異なる場合: それぞれの箇所に入力内容が生成<br/>入力行数とカーソルの数が同一の場合: カーソルの配置順に入力内容を1行ずつ生成 |

### データパックタグ

`%r`をデータパックタグの`values`で置換します。

この置換方法はカーソルの個数によって挙動が異なるため注意してください。

#### カーソルの個数による生成時の挙動の差異

| カーソルが一個の時の挙動 | カーソルが複数個の時の挙動 |
| :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| カーソルの行を始めとしてタグの要素数だけ生成 | タグの要素数とカーソルの数が異なる場合: それぞれの箇所にタグの要素が生成<br/>タグの要素数とカーソルの数が同一の場合: カーソルの配置順にタグの要素を1行ずつ生成 |

### 連続した値

`%r`を一定の連続した値で置換します。

注意事項: `値を先頭埋めする長さ``-1`の場合、先頭埋めは行われません。

### 数式

`%r`を関数より算出される値で置換します。
`Math.min(<a>,<b>)``Math.floor(<a>)`などが利用可能です。
<!-- つまるところJavaScriptとして評価しちゃってる、ってことですネ -->

注意事項: `値を先頭埋めする長さ``-1`の場合、先頭埋めは行われません。

<!-- TODO ![gif](https://raw.githubusercontent.com/ChenCMD/MC-Datapack-Utility/master/images/multiLineGenerator.gif) -->

## 計算式のscore operationへの変換

scoreboard players operationで計算式を作るのが面倒?
Expand Down
6 changes: 3 additions & 3 deletions src/commands/multiLineGenerator/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export async function generateMultiLine(ctx: FeatureContext): Promise<void> {

// 挿入する文字列の質問
const insertString = await listenInput(locale('insert-string'), v => stringValidator(v, { emptyMessage: locale('error.input-blank', locale('insert-string')) }), '%r');
// 置換方法の選択
const { extend: [replacer, insertCountRequired] } = await listenPickItem('', makeExtendQuickPickItem(getReplacerMap()), false);
// 挿入する回数
const insertCount = selections.length === 1
const insertCount = insertCountRequired && selections.length === 1
? parseInt(await listenInput(locale('insert-count'), v => numberValidator(v, { min: 1 })))
: selections.length;
// 置換方法の選択
const { extend: replacer } = await listenPickItem('', makeExtendQuickPickItem(getReplacerMap()), false);
// 置換方法毎の処理
const editData = await replacer(insertString, insertCount, ctx);
// 置換
Expand Down
30 changes: 21 additions & 9 deletions src/commands/multiLineGenerator/replacer/expression.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
import { locale } from '../../../locales';
import { ParsingError } from '../../../types';
import { listenInput } from '../../../utils';
import { listenInput, numberValidator, stringValidator } from '../../../utils';
import { Replacer } from '../types/Replacer';

export const expressionReplacer: Replacer = async (insertString, insertCount) => {
const expression = await listenInput(locale('expression.expression'));
const expression = await listenInput(locale('expression.expression'), undefined, 'y = 2x + 1');
const paddingLength = parseInt(await listenInput(locale('serial-number.padding-length'), v => numberValidator(v, { min: -1 }), -1), 10);
const paddingChar = paddingLength >= 1
? await listenInput(locale('serial-number.padding-char'), v => stringValidator(v, { minLength: 1, maxLength: 1 }))
: '0';

const ans: string[] = [];
try {
for (let i = 1; i < insertCount + 1; i++) {
const replaceData = eval(expression
const replaceData: number = eval(expression
.replace(/\s/g, '')
.replace(/\dx/g, m => `(${m.slice(0, 1)}*x)`)
.replace(/[^+\-*/]\(/g, m => `${m.slice(0, 1)}*(`)
.replace(/^.*=/, '')
.replace(/\^/g, '**')
.replace(/x/g, i.toString()));
ans.push(insertString.replace(/%r/g, replaceData));
.replace(/\dx/g, m => `${m.slice(0, 1)}*x`)
.replace(/^[x0-9]\(/g, m => `${m.slice(0, 1)}*(`)
.replace(/[0-9]\(/g, m => `${m.slice(0, 1)}*(`)
.replace(/[^a-zA-Z.]x\(/g, m => `${m.slice(0, 2)}*(`)
.replace(/[^a-zA-Z.]x/g, m => `${m.slice(0, 1)}${i.toString()}`));
ans.push(insertString.replace(/%r/g, replaceData.toString(10)));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
} catch {
throw new ParsingError(locale('error.not-expression'));
}
return ans;

const maxLength = ans.reduce((a, b) => Math.max(a, b.length), 0);

return paddingLength !== -1
? ans.map(str => paddingChar.repeat(maxLength - str.length + paddingLength) + str)
: ans;
};
10 changes: 5 additions & 5 deletions src/commands/multiLineGenerator/replacer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { serialNumberReplacer } from './serialNumber';
import { stringReplacer } from './string';
import { tagsReplacer } from './tags';

export function getReplacerMap(): Map<string, Replacer> {
export function getReplacerMap(): Map<string, [replacer: Replacer, insertCntRequired: boolean]> {
return new Map([
['replacer.string', stringReplacer],
['replacer.tags', tagsReplacer],
['replacer.serial-number', serialNumberReplacer],
['replacer.expression', expressionReplacer]
['replacer.string', [stringReplacer, false]],
['replacer.tags', [tagsReplacer, false]],
['replacer.serial-number', [serialNumberReplacer, true]],
['replacer.expression', [expressionReplacer, true]]
]);
}
40 changes: 18 additions & 22 deletions src/commands/multiLineGenerator/replacer/serialNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { makeExtendQuickPickItem } from '../../../types';
import { numberValidator, listenInput, listenPickItem, parseRadixFloat, stringValidator } from '../../../utils';
import { Replacer } from '../types/Replacer';

const operators = ['+', '-', '*', '/'] as const;
type Operator = typeof operators[number];
const operatorMap = new Map<string, Operator>(operators.map(v => [v, v]));
type OpFunc = (a: number, b: number) => number;
const operatorMap = new Map<string, OpFunc>([
['+', (a, b) => a + b],
['-', (a, b) => a - b],
['*', (a, b) => a * b],
['/', (a, b) => a / b]
]);

/*
* 質問.1 基数
Expand All @@ -15,39 +19,31 @@ const operatorMap = new Map<string, Operator>(operators.map(v => [v, v]));
* 質問.5 0埋めの桁数
*
*/
export const serialNumberReplacer: Replacer = async (insertString, insertCount) => {
export const serialNumberReplacer: Replacer = async (_insertString, insertCount) => {
const radix = parseInt(await listenInput(locale('serial-number.radix'), v => numberValidator(v, { min: 2, max: 36, allowFloat: false }), 10));
const start = parseRadixFloat(await listenInput(locale('serial-number.start'), v => numberValidator(v, { radix }), 0), radix);
const { extend: operator } = await listenPickItem(locale('serial-number.operator'), makeExtendQuickPickItem(operatorMap, false), false);
const { label: operator, extend: opFunc } = await listenPickItem(locale('serial-number.operator'), makeExtendQuickPickItem(operatorMap, false), false);
const step = parseRadixFloat(await listenInput(locale('serial-number.step'), v => {
const res = numberValidator(v, { radix });
if (res) return res;
if (operator === '/' && parseRadixFloat(v, radix) === 0) return locale('error.cant-divided-by-zero');
return undefined;
}, 1), radix);
const paddingLength = parseInt(await listenInput(locale('serial-number.padding-length'), v => numberValidator(v, { radix, min: 0 }), 0), radix);
const paddingLength = parseInt(await listenInput(locale('serial-number.padding-length'), v => numberValidator(v, { radix, min: -1 }), -1), radix);
const paddingChar = paddingLength >= 1
? await listenInput(locale('serial-number.padding-char'), v => stringValidator(v, { minLength: 1, maxLength: 1 }))
: '0';

const ans: string[] = [];
let replaceValue = start;
for (let i = 0; i < insertCount; i++) {
const replaceStr = replaceValue.toString(radix);
ans.push(insertString.replace(/%r/g, replaceStr.length <= paddingLength
? `${paddingChar.repeat(paddingLength)}${replaceStr}`.slice(-paddingLength)
: replaceStr
));
replaceValue = safeEval(replaceValue, operator, step);
ans.push(replaceValue.toString(radix));
replaceValue = opFunc(replaceValue, step);
}
return ans;
};

function safeEval(a: number, operator: Operator, b: number): number {
switch (operator) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
}
const maxLength = ans.reduce((a, b) => Math.max(a, b.length), 0);

return paddingLength !== -1
? ans.map(str => paddingChar.repeat(maxLength - str.length + paddingLength) + str)
: ans;
};
5 changes: 1 addition & 4 deletions src/commands/multiLineGenerator/replacer/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,5 @@ import { Replacer } from '../types/Replacer';

export const stringReplacer: Replacer = async (insertString, _, { extensionUri }) => {
const replaceStrings = await listenInputMultiLine(extensionUri, locale('string.string'));
const ans: string[] = [];
for (const line of replaceStrings.split('\n'))
ans.push(insertString.replace(/%r/g, line));
return ans;
return replaceStrings.split('\n').map(l => insertString.replace(/%r/g, l));
};
6 changes: 3 additions & 3 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"error.string-too-long": "The string must be shorter than %0%.",
"error.string-too-short": "The string must be longer than %0%.",
"error.unexpected-character": "%0% cannot be used.",
"expression.expression": "Number expression",
"expression.expression": "Expression",
"formula-to-score-operation.complete-text": "If u wish, u can change both SCORE HOLDERs' NAME and the SCORE OBJECT",
"formula-to-score-operation.formula": "Formula",
"formula-to-score-operation.illegal-formula": "Invalid expression.",
Expand Down Expand Up @@ -108,9 +108,9 @@
"punc.quote": "“%0%”",
"quote": "a quote (“'” or “\"”)",
"rename": "Rename",
"replacer.expression": "Number expression",
"replacer.expression": "Expression",
"replacer.serial-number": "Consecutive values",
"replacer.string": "String",
"replacer.string": "Strings",
"replacer.tags": "Datapack tag",
"reselect": "Reselect",
"select": "Select",
Expand Down
4 changes: 2 additions & 2 deletions src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"error.string-too-long": "文字列は%0%よりも短い必要があります。",
"error.string-too-short": "文字列は%0%よりも長い必要があります。",
"error.unexpected-character": "%0%は使用できません。",
"expression.expression": "数式",
"expression.expression": "",
"formula-to-score-operation.complete-text": "必要に応じて、スコアホルダー名とスコアオブジェクトを変更することができます",
"formula-to-score-operation.formula": "計算式",
"formula-to-score-operation.illegal-formula": "無効な式です。",
Expand Down Expand Up @@ -108,7 +108,7 @@
"punc.quote": "「%0%」",
"quote": "引用符 (‘'’ か ‘\"’)",
"rename": "名前を変更",
"replacer.expression": "数式",
"replacer.expression": "",
"replacer.serial-number": "連続した値",
"replacer.string": "文字列",
"replacer.tags": "データパックタグ",
Expand Down
2 changes: 1 addition & 1 deletion src/utils/vscodeWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function listenInput<T extends { toString(): string }>(
}

export function numberValidator(str: string, { radix = 10, allowFloat, min, max }: { radix?: number, allowFloat?: boolean, min?: number, max?: number } = {}): undefined | string {
if (!getRadixRegExp(radix, allowFloat !== false).test(str)) return locale('error.invalid-number');
if (!getRadixRegExp(radix, allowFloat ?? true).test(str)) return locale('error.invalid-number');
if (min && min > parseRadixFloat(str, radix)) return locale('error.number-too-small', min);
if (max && max < parseRadixFloat(str, radix)) return locale('error.number-too-large', max);
return undefined;
Expand Down

0 comments on commit 5f8908c

Please sign in to comment.