diff --git a/CHANGELOG.md b/CHANGELOG.md index 64de67ed26..94ffe4f2b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,12 @@ mongodb: 8. master ブランチに戻す 9. enjoy +11.9.0 (2019/05/02) +------------------- +### Improvements +* MisskeyPagesで編集時にページブロックをドラッグで並べ替えられるように +* MisskeyPagesにカウンターボタンブロックを追加 + 11.8.1 (2019/05/02) ------------------- ### Fixes diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a8a5cbafd1..5d85819d54 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1915,6 +1915,12 @@ pages: text: "タイトル" default: "デフォルト値" + counter: "カウンター" + _counter: + name: "変数名" + text: "タイトル" + inc: "増加値" + _button: text: "タイトル" action: "ボタンを押したときの動作" diff --git a/package.json b/package.json index 2dddb678f6..d1a51e6ea2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <i@syuilo.com>", - "version": "11.8.1", + "version": "11.9.0", "codename": "daybreak", "repository": { "type": "git", diff --git a/src/client/app/common/scripts/collect-page-vars.ts b/src/client/app/common/scripts/collect-page-vars.ts index 683f9b73a5..4c40d5d88e 100644 --- a/src/client/app/common/scripts/collect-page-vars.ts +++ b/src/client/app/common/scripts/collect-page-vars.ts @@ -26,6 +26,12 @@ export function collectPageVars(content) { type: 'boolean', value: x.default || false }); + } else if (x.type === 'counter') { + pageVars.push({ + name: x.name, + type: 'number', + value: 0 + }); } else if (x.children) { collect(x.children); } diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.button.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.button.vue index 3e2d3fe19d..f89279f05a 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.button.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.button.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faBolt"/> {{ $t('blocks.button') }}</template> <section class="xfhsjczc"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.counter.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.counter.vue new file mode 100644 index 0000000000..95c15b01b2 --- /dev/null +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.counter.vue @@ -0,0 +1,42 @@ +<template> +<x-container @remove="() => $emit('remove')" :draggable="true"> + <template #header><fa :icon="faBolt"/> {{ $t('blocks.counter') }}</template> + + <section style="padding: 0 16px 0 16px;"> + <ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._counter.name') }}</span></ui-input> + <ui-input v-model="value.text"><span>{{ $t('blocks._counter.text') }}</span></ui-input> + <ui-input v-model="value.inc" type="number"><span>{{ $t('blocks._counter.increment') }}</span></ui-input> + </section> +</x-container> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; +import i18n from '../../../../../i18n'; +import XContainer from '../page-editor.container.vue'; + +export default Vue.extend({ + i18n: i18n('pages'), + + components: { + XContainer + }, + + props: { + value: { + required: true + }, + }, + + data() { + return { + faBolt, faMagic + }; + }, + + created() { + if (this.value.name == null) Vue.set(this.value, 'name', ''); + }, +}); +</script> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.if.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.if.vue index 87fc9e6faf..c83cd421ae 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.if.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.if.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faQuestion"/> {{ $t('blocks.if') }}</template> <template #func> <button @click="add()"> @@ -19,9 +19,7 @@ </optgroup> </ui-select> - <div class="children"> - <x-block v-for="child in value.children" :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/> - </div> + <x-blocks class="children" v-model="value.children" :ai-script="aiScript"/> </section> </x-container> </template> @@ -58,7 +56,7 @@ export default Vue.extend({ }, beforeCreate() { - this.$options.components.XBlock = require('../page-editor.block.vue').default + this.$options.components.XBlocks = require('../page-editor.blocks.vue').default }, created() { @@ -81,27 +79,6 @@ export default Vue.extend({ const id = uuid.v4(); this.value.children.push({ id, type }); }, - - updateItem(v) { - const i = this.value.children.findIndex(x => x.id === v.id); - const newValue = [ - ...this.value.children.slice(0, i), - v, - ...this.value.children.slice(i + 1) - ]; - this.value.children = newValue; - this.$emit('input', this.value); - }, - - remove(el) { - const i = this.value.children.findIndex(x => x.id === el.id); - const newValue = [ - ...this.value.children.slice(0, i), - ...this.value.children.slice(i + 1) - ]; - this.value.children = newValue; - this.$emit('input', this.value); - } } }); </script> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.image.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.image.vue index 5ada8c77ba..98ec39a512 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.image.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.image.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faImage"/> {{ $t('blocks.image') }}</template> <template #func> <button @click="choose()"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.number-input.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.number-input.vue index 923f4ea339..30c3938111 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.number-input.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.number-input.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faBolt"/> {{ $t('blocks.numberInput') }}</template> <section style="padding: 0 16px 0 16px;"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.post.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.post.vue index a01fc57f26..fc2f5f9032 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.post.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.post.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faPaperPlane"/> {{ $t('blocks.post') }}</template> <section style="padding: 0 16px 16px 16px;"> @@ -33,10 +33,6 @@ export default Vue.extend({ }; }, - beforeCreate() { - this.$options.components.XBlock = require('../page-editor.block.vue').default - }, - created() { if (this.value.text == null) Vue.set(this.value, 'text', ''); }, diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.section.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.section.vue index 747de481a6..c20f824e23 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.section.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.section.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faStickyNote"/> {{ value.title }}</template> <template #func> <button @click="rename()"> @@ -11,9 +11,7 @@ </template> <section class="ilrvjyvi"> - <div class="children"> - <x-block v-for="child in value.children" :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/> - </div> + <x-blocks class="children" v-model="value.children" :ai-script="aiScript"/> </section> </x-container> </template> @@ -51,7 +49,7 @@ export default Vue.extend({ }, beforeCreate() { - this.$options.components.XBlock = require('../page-editor.block.vue').default + this.$options.components.XBlocks = require('../page-editor.blocks.vue').default }, created() { @@ -93,27 +91,6 @@ export default Vue.extend({ const id = uuid.v4(); this.value.children.push({ id, type }); }, - - updateItem(v) { - const i = this.value.children.findIndex(x => x.id === v.id); - const newValue = [ - ...this.value.children.slice(0, i), - v, - ...this.value.children.slice(i + 1) - ]; - this.value.children = newValue; - this.$emit('input', this.value); - }, - - remove(el) { - const i = this.value.children.findIndex(x => x.id === el.id); - const newValue = [ - ...this.value.children.slice(0, i), - ...this.value.children.slice(i + 1) - ]; - this.value.children = newValue; - this.$emit('input', this.value); - } } }); </script> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.switch.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.switch.vue index 60d102f8ae..174a344640 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.switch.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.switch.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faBolt"/> {{ $t('blocks.switch') }}</template> <section class="kjuadyyj"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.text-input.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.text-input.vue index 168a58f349..50f95fd205 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.text-input.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.text-input.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faBolt"/> {{ $t('blocks.textInput') }}</template> <section style="padding: 0 16px 0 16px;"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.text.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.text.vue index 1d07c9157a..c09f9cc1cf 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.text.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.text.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.text') }}</template> <section class="ihymsbbe"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea-input.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea-input.vue index 4b6d334d9b..da3eead080 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea-input.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea-input.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faBolt"/> {{ $t('blocks.textareaInput') }}</template> <section style="padding: 0 16px 16px 16px;"> diff --git a/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea.vue b/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea.vue index 4fbe497960..a0cc1966e8 100644 --- a/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea.vue +++ b/src/client/app/common/views/components/page-editor/els/page-editor.el.textarea.vue @@ -1,5 +1,5 @@ <template> -<x-container @remove="() => $emit('remove')"> +<x-container @remove="() => $emit('remove')" :draggable="true"> <template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.textarea') }}</template> <section class="ihymsbbe"> diff --git a/src/client/app/common/views/components/page-editor/page-editor.block.vue b/src/client/app/common/views/components/page-editor/page-editor.block.vue deleted file mode 100644 index 7d4505fca8..0000000000 --- a/src/client/app/common/views/components/page-editor/page-editor.block.vue +++ /dev/null @@ -1,33 +0,0 @@ -<template> -<component :is="'x-' + value.type" :value="value" @input="v => updateItem(v)" @remove="() => $emit('remove', value)" :key="value.id" :ai-script="aiScript"/> -</template> - -<script lang="ts"> -import Vue from 'vue'; -import XSection from './els/page-editor.el.section.vue'; -import XText from './els/page-editor.el.text.vue'; -import XTextarea from './els/page-editor.el.textarea.vue'; -import XImage from './els/page-editor.el.image.vue'; -import XButton from './els/page-editor.el.button.vue'; -import XTextInput from './els/page-editor.el.text-input.vue'; -import XTextareaInput from './els/page-editor.el.textarea-input.vue'; -import XNumberInput from './els/page-editor.el.text-input.vue'; -import XSwitch from './els/page-editor.el.switch.vue'; -import XIf from './els/page-editor.el.if.vue'; -import XPost from './els/page-editor.el.post.vue'; - -export default Vue.extend({ - components: { - XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost - }, - - props: { - value: { - required: true - }, - aiScript: { - required: true, - }, - }, -}); -</script> diff --git a/src/client/app/common/views/components/page-editor/page-editor.blocks.vue b/src/client/app/common/views/components/page-editor/page-editor.blocks.vue new file mode 100644 index 0000000000..894feb34cc --- /dev/null +++ b/src/client/app/common/views/components/page-editor/page-editor.blocks.vue @@ -0,0 +1,65 @@ +<template> +<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150"> + <component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="removeItem" :key="block.id" :ai-script="aiScript"/> +</x-draggable> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as XDraggable from 'vuedraggable'; +import XSection from './els/page-editor.el.section.vue'; +import XText from './els/page-editor.el.text.vue'; +import XTextarea from './els/page-editor.el.textarea.vue'; +import XImage from './els/page-editor.el.image.vue'; +import XButton from './els/page-editor.el.button.vue'; +import XTextInput from './els/page-editor.el.text-input.vue'; +import XTextareaInput from './els/page-editor.el.textarea-input.vue'; +import XNumberInput from './els/page-editor.el.text-input.vue'; +import XSwitch from './els/page-editor.el.switch.vue'; +import XIf from './els/page-editor.el.if.vue'; +import XPost from './els/page-editor.el.post.vue'; +import XCounter from './els/page-editor.el.counter.vue'; + +export default Vue.extend({ + components: { + XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter + }, + + props: { + value: { + type: Array, + required: true + }, + aiScript: { + required: true, + }, + }, + + computed: { + blocks() { + return this.value; + } + }, + + methods: { + updateItem(v) { + const i = this.blocks.findIndex(x => x.id === v.id); + const newValue = [ + ...this.blocks.slice(0, i), + v, + ...this.blocks.slice(i + 1) + ]; + this.$emit('input', newValue); + }, + + removeItem(el) { + const i = this.blocks.findIndex(x => x.id === el.id); + const newValue = [ + ...this.blocks.slice(0, i), + ...this.blocks.slice(i + 1) + ]; + this.$emit('input', newValue); + }, + } +}); +</script> diff --git a/src/client/app/common/views/components/page-editor/page-editor.container.vue b/src/client/app/common/views/components/page-editor/page-editor.container.vue index 698fdfee45..4127cd39e0 100644 --- a/src/client/app/common/views/components/page-editor/page-editor.container.vue +++ b/src/client/app/common/views/components/page-editor/page-editor.container.vue @@ -1,6 +1,6 @@ <template> -<div class="cpjygsrt" :class="{ error: error != null, warn: warn != null }"> - <header> +<div class="cpjygsrt" :class="{ error: error != null, warn: warn != null, draggable }"> + <header class="drag-handle"> <div class="title"><slot name="header"></slot></div> <div class="buttons"> <slot name="func"></slot> @@ -38,6 +38,10 @@ export default Vue.extend({ type: Boolean, default: true }, + draggable: { + type: Boolean, + default: false + }, error: { required: false, default: null @@ -120,6 +124,10 @@ export default Vue.extend({ &:active color var(--faceTextButtonActive) + &.draggable + > header + cursor move + > .warn color #b19e49 margin 0 diff --git a/src/client/app/common/views/components/page-editor/page-editor.vue b/src/client/app/common/views/components/page-editor/page-editor.vue index 6d07c5dc6b..d70049121d 100644 --- a/src/client/app/common/views/components/page-editor/page-editor.vue +++ b/src/client/app/common/views/components/page-editor/page-editor.vue @@ -44,9 +44,7 @@ </div> </template> - <div class="content" v-for="child in content"> - <x-block :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id" :ai-script="aiScript"/> - </div> + <x-blocks class="content" v-model="content" :ai-script="aiScript"/> <ui-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></ui-button> </section> @@ -98,7 +96,7 @@ import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } fr import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import i18n from '../../../../i18n'; import XVariable from './page-editor.script-block.vue'; -import XBlock from './page-editor.block.vue'; +import XBlocks from './page-editor.blocks.vue'; import * as uuid from 'uuid'; import { blockDefs } from '../../../../../../misc/aiscript/index'; import { ASTypeChecker } from '../../../../../../misc/aiscript/type-checker'; @@ -109,7 +107,7 @@ export default Vue.extend({ i18n: i18n('pages'), components: { - XVariable, XBlock + XVariable, XBlocks }, props: { @@ -299,25 +297,6 @@ export default Vue.extend({ this.variables.push({ id, name, type: null }); }, - updateItem(v) { - const i = this.content.findIndex(x => x.id === v.id); - const newValue = [ - ...this.content.slice(0, i), - v, - ...this.content.slice(i + 1) - ]; - this.content = newValue; - }, - - remove(el) { - const i = this.content.findIndex(x => x.id === el.id); - const newValue = [ - ...this.content.slice(0, i), - ...this.content.slice(i + 1) - ]; - this.content = newValue; - }, - removeVariable(v) { const i = this.variables.findIndex(x => x.name === v.name); const newValue = [ @@ -343,7 +322,8 @@ export default Vue.extend({ { value: 'textInput', text: this.$t('blocks.textInput') }, { value: 'textareaInput', text: this.$t('blocks.textareaInput') }, { value: 'numberInput', text: this.$t('blocks.numberInput') }, - { value: 'switch', text: this.$t('blocks.switch') } + { value: 'switch', text: this.$t('blocks.switch') }, + { value: 'counter', text: this.$t('blocks.counter') } ] }, { label: this.$t('special-blocks'), diff --git a/src/client/app/common/views/pages/page/page.block.vue b/src/client/app/common/views/pages/page/page.block.vue index f348107cc7..1c421fc2c0 100644 --- a/src/client/app/common/views/pages/page/page.block.vue +++ b/src/client/app/common/views/pages/page/page.block.vue @@ -15,10 +15,11 @@ import XSwitch from './page.switch.vue'; import XIf from './page.if.vue'; import XTextarea from './page.textarea.vue'; import XPost from './page.post.vue'; +import XCounter from './page.counter.vue'; export default Vue.extend({ components: { - XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf + XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter }, props: { diff --git a/src/client/app/common/views/pages/page/page.counter.vue b/src/client/app/common/views/pages/page/page.counter.vue new file mode 100644 index 0000000000..8d55319fe9 --- /dev/null +++ b/src/client/app/common/views/pages/page/page.counter.vue @@ -0,0 +1,47 @@ +<template> +<div> + <ui-button class="llumlmnx" @click="click()">{{ script.interpolate(value.text) }}</ui-button> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; + +export default Vue.extend({ + props: { + value: { + required: true + }, + script: { + required: true + } + }, + + data() { + return { + v: 0, + }; + }, + + watch: { + v() { + this.script.aiScript.updatePageVar(this.value.name, this.v); + this.script.eval(); + } + }, + + methods: { + click() { + this.v = this.v + (this.value.inc || 1); + } + } +}); +</script> + +<style lang="stylus" scoped> +.llumlmnx + display inline-block + min-width 300px + max-width 450px + margin 8px 0 +</style> diff --git a/src/misc/aiscript/evaluator.ts b/src/misc/aiscript/evaluator.ts index fef2d4f3a5..2bc866dc43 100644 --- a/src/misc/aiscript/evaluator.ts +++ b/src/misc/aiscript/evaluator.ts @@ -7,56 +7,6 @@ type Fn = { exec: (args: Record<string, any>) => ReturnType<ASEvaluator['evaluate']>; }; -class AiScriptError extends Error { - public info?: any; - - constructor(message: string, info?: any) { - super(message); - - this.info = info; - - // Maintains proper stack trace for where our error was thrown (only available on V8) - if (Error.captureStackTrace) { - Error.captureStackTrace(this, AiScriptError); - } - } -} - -class Scope { - private layerdStates: Record<string, any>[]; - public name: string; - - constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) { - this.layerdStates = layerdStates; - this.name = name || 'anonymous'; - } - - @autobind - public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope { - const layer = [states, ...this.layerdStates]; - return new Scope(layer, name); - } - - /** - * 指定した名前の変数の値を取得します - * @param name 変数名 - */ - @autobind - public getState(name: string): any { - for (const later of this.layerdStates) { - const state = later[name]; - if (state !== undefined) { - return state; - } - } - - throw new AiScriptError( - `No such variable '${name}' in scope '${this.name}'`, { - scope: this.layerdStates - }); - } -} - /** * AiScript evaluator */ @@ -238,3 +188,53 @@ export class ASEvaluator { } } } + +class AiScriptError extends Error { + public info?: any; + + constructor(message: string, info?: any) { + super(message); + + this.info = info; + + // Maintains proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, AiScriptError); + } + } +} + +class Scope { + private layerdStates: Record<string, any>[]; + public name: string; + + constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) { + this.layerdStates = layerdStates; + this.name = name || 'anonymous'; + } + + @autobind + public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope { + const layer = [states, ...this.layerdStates]; + return new Scope(layer, name); + } + + /** + * 指定した名前の変数の値を取得します + * @param name 変数名 + */ + @autobind + public getState(name: string): any { + for (const later of this.layerdStates) { + const state = later[name]; + if (state !== undefined) { + return state; + } + } + + throw new AiScriptError( + `No such variable '${name}' in scope '${this.name}'`, { + scope: this.layerdStates + }); + } +}