<script lang="ts" setup>
import { javascript } from '@codemirror/lang-javascript';
import { yaml } from '@codemirror/lang-yaml';
import { oneDark } from '@codemirror/theme-one-dark';
import { MergeView } from '@codemirror/merge';
import { EditorView, basicSetup } from 'codemirror';
import { EditorState } from '@codemirror/state';
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
const emit = defineEmits(['update:newArea', 'change']);
const props = defineProps({
  oldArea: {
    type: String,
    default: '原始内容',
  },
  /**当前变更内容 */
  newArea: {
    type: String,
    default: '当前内容',
  },
  /**编辑框高度 */
  height: {
    type: String,
    default: '400px',
  },
  /**缩进2空格 */
  tabSize: {
    type: Number,
    default: 2,
  },
  /**是否禁止输入 */
  disabled: {
    type: Boolean,
    default: true,
  },
  /**高亮语言 */
  lang: {
    type: String,
    default: 'javascript',
  },
});

/**视图容器 */
const viewContainerDom = ref<HTMLElement | undefined>(undefined);
let viewContainer: MergeView | null = null;

/**高亮语言拓展 */
function fnLangExtension() {
  if (props.lang === 'yaml') {
    return yaml();
  }
  return javascript();
}

/**初始化渲染视图 */
function handleRanderView(container: HTMLElement | undefined) {
  if (!container) return;
  viewContainer = new MergeView({
    a: {
      doc: props.oldArea,
      extensions: [
        fnLangExtension(),
        oneDark,
        basicSetup,
        EditorView.editable.of(false),
        EditorState.readOnly.of(true),
      ],
    },
    b: {
      doc: props.newArea,
      extensions: [
        fnLangExtension(),
        oneDark,
        basicSetup,
        EditorView.editable.of(!props.disabled),
        EditorState.readOnly.of(props.disabled),
        EditorState.tabSize.of(props.tabSize),
        EditorView.updateListener.of(v => {
          if (v.docChanged) {
            const docStr = v.state.doc.toString();
            emit('change', docStr, v.state.doc);
            // 禁用时不双向绑定，防止监听重复变化数值
            if (!props.disabled) {
              emit('update:newArea', docStr);
            }
          }
        }),
      ],
    },
    parent: container,
  });
}

/**监听是否value改变 */
watch(
  () => props.newArea,
  val => {
    // 禁用时无输入靠外部值变化改变数值
    if (props.disabled && viewContainer) {
      const docLine = viewContainer.b.state.doc.length;
      viewContainer.b.dispatch({
        changes: { from: 0, to: docLine, insert: val },
      });
    }
  }
);

onMounted(() => {
  handleRanderView(viewContainerDom.value);
});

onBeforeUnmount(() => {
  viewContainer?.destroy();
});
</script>

<template>
  <div
    ref="viewContainerDom"
    class="container"
    :style="{ '--editor-height': height }"
  ></div>
</template>

<style lang="less" scoped>
.container {
  --editor-height: 400px;
}
.container :deep(.cm-editor) {
  height: var(--editor-height);
}
</style>
