Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions packages/origine2/src/components/AppSettings/AppSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DialogSurface,
DialogTitle,
DialogTrigger,
Dropdown,
Input,
Menu,
MenuItem,
Expand Down Expand Up @@ -43,6 +44,7 @@ import { Platte } from '@icon-park/react';
import { ReactNode, useEffect, useState } from 'react';
import { t } from '@lingui/macro';
import useEditorStore from '@/store/useEditorStore';
import { scanLanguageServers, setActiveLanguageServer } from '@/webgalscript/lsp';
import TagInputPicker from '@/pages/editor/GraphicalEditor/components/TagInputPicker';
import { candidateFontSizes } from '@/pages/editor/Topbar/tabs/Settings/constants';
import { UserDataSettingsPanel } from '@/components/UserDataSettings/UserDataSettingsDialog';
Expand Down Expand Up @@ -234,7 +236,11 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps
const isCascaderDelimitersCustomizable = useEditorStore.use.isCascaderDelimitersCustomizable();
const cascaderDelimiters = useEditorStore.use.cascaderDelimiters();
const updateCascaderDelimiters = useEditorStore.use.updateCascaderDelimiters();
const activeLanguageServer = useEditorStore.use.activeLanguageServer();
const updateActiveLanguageServer = useEditorStore.use.updateActiveLanguageServer();
const [tempFontSize, setTempFontSize] = useState(editorFontSize.toString());
const [availableLanguageServers, setAvailableLanguageServers] = useState<Array<{ id: string; name: string }>>([]);
const [isScanningLanguageServers, setIsScanningLanguageServers] = useState(false);

useEffect(() => {
setTempFontSize(editorFontSize.toString());
Expand All @@ -247,6 +253,45 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps
}
}, [tempFontSize]);

useEffect(() => {
if (!open) {
return;
}

const refreshServers = async () => {
setIsScanningLanguageServers(true);
try {
for (let attempt = 0; attempt < 4; attempt += 1) {
const servers = await scanLanguageServers();
if (servers.length > 0) {
setAvailableLanguageServers(servers);
return;
}
await new Promise((resolve) => setTimeout(resolve, 250));
}
setAvailableLanguageServers([]);
} finally {
setIsScanningLanguageServers(false);
}
};

refreshServers();
}, [open]);

const serverOptions = [
{ id: 'native', name: t`内置语言服务` },
...availableLanguageServers,
];
const selectedServerLabel = serverOptions.find((server) => server.id === activeLanguageServer)?.name ?? t`内置语言服务`;
const selectedServerId = serverOptions.some((server) => server.id === activeLanguageServer)
? activeLanguageServer
: 'native';

const handleLanguageServerChange = async (serverId: string) => {
updateActiveLanguageServer(serverId);
await setActiveLanguageServer(serverId);
};

return (
<Dialog open={open} onOpenChange={(_, data) => onOpenChange(data.open)}>
<DialogSurface
Expand Down Expand Up @@ -289,6 +334,24 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps
))}
</Combobox>
</label>
<label className={styles.field}>
<span className={styles.fieldLabel}>{t`语言服务`}</span>
<Dropdown
value={selectedServerLabel}
selectedOptions={[selectedServerId]}
disabled={isScanningLanguageServers}
onOptionSelect={(_, data) => {
const serverId = data.optionValue ?? 'native';
handleLanguageServerChange(serverId);
}}
>
{serverOptions.map((server) => (
<Option key={server.id} value={server.id}>
{server.name}
</Option>
))}
</Dropdown>
</label>
</div>
</section>
{isCascaderDelimitersCustomizable && (
Expand Down
6 changes: 6 additions & 0 deletions packages/origine2/src/locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ msgstr "Content"
msgid "内层文本"
msgstr "Inner Text"

msgid "内置语言服务"
msgstr "Built-in Language Service"

msgid "内边距"
msgstr "Padding"

Expand Down Expand Up @@ -2132,6 +2135,9 @@ msgstr "This file type does not support preview"
msgid "语言"
msgstr "Language"

msgid "语言服务"
msgstr "Language Service"

msgid "语音"
msgstr "Voice"

Expand Down
6 changes: 6 additions & 0 deletions packages/origine2/src/locales/ja.po
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ msgstr "内容"
msgid "内层文本"
msgstr "内側テキスト"

msgid "内置语言服务"
msgstr "組み込み言語サービス"

msgid "内边距"
msgstr "パディング"

Expand Down Expand Up @@ -2132,6 +2135,9 @@ msgstr "このファイルタイプはプレビューをサポートしていま
msgid "语言"
msgstr "言語"

msgid "语言服务"
msgstr "言語サービス"

msgid "语音"
msgstr "ボイス"

Expand Down
6 changes: 6 additions & 0 deletions packages/origine2/src/locales/ko.po
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ msgstr "내용"
msgid "内层文本"
msgstr "내부 텍스트"

msgid "内置语言服务"
msgstr "내장 언어 서비스"

msgid "内边距"
msgstr "안쪽 여백"

Expand Down Expand Up @@ -2132,6 +2135,9 @@ msgstr "이 파일 유형은 미리보기를 지원하지 않습니다"
msgid "语言"
msgstr "언어"

msgid "语言服务"
msgstr "언어 서비스"

msgid "语音"
msgstr "음성"

Expand Down
6 changes: 6 additions & 0 deletions packages/origine2/src/locales/zhCn.po
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ msgstr "内容"
msgid "内层文本"
msgstr "内层文本"

msgid "内置语言服务"
msgstr "内置语言服务"

msgid "内边距"
msgstr "内边距"

Expand Down Expand Up @@ -2132,6 +2135,9 @@ msgstr "该文件类型不支持预览"
msgid "语言"
msgstr "语言"

msgid "语言服务"
msgstr "语言服务"

msgid "语音"
msgstr "语音"

Expand Down
69 changes: 39 additions & 30 deletions packages/origine2/src/pages/editor/TextEditor/TextEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import * as monaco from 'monaco-editor';
import Editor, { Monaco } from '@monaco-editor/react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import styles from './textEditor.module.scss';
import axios from 'axios';
import { logger } from '../../../utils/logger';
import debounce from 'lodash/debounce';

// 语法高亮文件
import { editorLineHolder, lspSceneName, WG_ORIGINE_RUNTIME } from '../../../runtime/WG_ORIGINE_RUNTIME';
import { EditorPreviewClient } from '../../../utils/editorPreviewClient';
import { eventBus } from '@/utils/eventBus';
import useEditorStore from '@/store/useEditorStore';
import { useGameEditorContext } from '@/store/useGameEditorStore';
import { api } from '@/api';
import { useValue } from "@/hooks/useValue";
import { applyEditorConfig } from '@/webgalscript/lsp';
import { getMonacoReady } from '@/utils/initMonaco';

interface ITextEditorProps {
targetPath: string;
Expand All @@ -26,24 +27,37 @@ export default function TextEditor(props: ITextEditorProps) {
const currentText = useRef('Loading Scene Data......');
const sceneName = tags.find((e) => e.path === target?.path)!.name;
const isAutoWarp = useEditorStore.use.isAutoWarp();
const isEditorReady = useValue(false); // 读取完脚本才能算准备就绪
const isEditorReady = useValue(false);
const [isMonacoReady, setIsMonacoReady] = useState(false);

// 准备获取 Monaco
// 建立 Ref
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);

/**
* 处理挂载事件
* @param {any} editor
* @param {any} monaco
*/
useEffect(() => {
let cancelled = false;
getMonacoReady()
.then(() => {
if (!cancelled) setIsMonacoReady(true);
})
.catch(() => {
// 即使初始化失败也照常挂载编辑器,避免编辑器永远不出现。
if (!cancelled) setIsMonacoReady(true);
});
return () => {
cancelled = true;
};
}, []);

function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) {
logger.debug('脚本编辑器挂载');
lspSceneName.value = sceneName;
editorRef.current = editor;

configureMonaco(editor, monaco);

// @monaco-editor/react 在挂载时会用默认的 light 主题覆盖 vscode 主题,
// 这里重新下发一次配置,把 WebGAL 主题和高亮恢复回来。
applyEditorConfig();

editor.onDidChangeCursorPosition(debounce((event: monaco.editor.ICursorPositionChangedEvent) => {
const previousCursorPosition = editorLineHolder.getScenePosition(props.targetPath);
const editorValue = editor.getValue();
Expand All @@ -59,8 +73,6 @@ export default function TextEditor(props: ITextEditorProps) {
}
editorLineHolder.recordSceneEditingPosition(props.targetPath, event.position);
}));
// 由于 monaco 接收拖拽进来的文字时, 会在末尾添加 $0
// 这里手动实现接收拖拽进来的文字, 以避开这个问题
const domNode = editor.getContainerDomNode();
const dropHandler = (e: DragEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -107,20 +119,12 @@ export default function TextEditor(props: ITextEditorProps) {
editorRef?.current?.updateOptions?.({ wordWrap: isAutoWarp ? 'on' : 'off' });
}, [isAutoWarp]);

/**
* handle monaco change
* @param {string} value
* @param {any} ev
*/
const submitChange = useMemo(() => debounce((value: string | undefined, ev: monaco.editor.IModelContentChangedEvent) => {
logger.debug('编辑器提交更新');
// 这里直接使用临时储存的行数, 一般来说光标位置就在改变的行
const lineNumber = editorLineHolder.getSceneLine(props.targetPath);
// const lineNumber = ev.changes[0].range.startLineNumber;
// const trueLineNumber = getTrueLinenumber(lineNumber, value ?? "");
if (value || value === '') currentText.current = value;
eventBus.emit('editor:update-scene', { scene: currentText.current });
api.assetsControllerEditTextFile({textFile: currentText.current, path: props.targetPath}).then((res) => {
api.assetsControllerEditTextFile({ textFile: currentText.current, path: props.targetPath }).then((res) => {
const targetValue = currentText.current.split('\n')[lineNumber - 1];
EditorPreviewClient.sendSyncScene({
scenePath: target?.path ?? '',
Expand Down Expand Up @@ -187,6 +191,8 @@ export default function TextEditor(props: ITextEditorProps) {
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
updateEditData();
// 长时间后台休眠后,主题/高亮可能丢失,恢复可见时重新应用一次。
applyEditorConfig();
}
};

Expand All @@ -204,15 +210,18 @@ export default function TextEditor(props: ITextEditorProps) {
style={{ display: props.isHide ? 'none' : 'block', zIndex: 999, overflow: 'auto' }}
className={styles.textEditor_main}
>
<Editor
height="100%"
width="100%"
onMount={handleEditorDidMount}
onChange={handleChange}
defaultLanguage="webgal"
language="webgal"
defaultValue={currentText.current}
/>
{isMonacoReady && (
<Editor
height="100%"
width="100%"
onMount={handleEditorDidMount}
onChange={handleChange}
defaultLanguage="webgal"
language="webgal"
path={props.targetPath}
defaultValue={currentText.current}
/>
)}
</div>
);
}
6 changes: 4 additions & 2 deletions packages/origine2/src/store/useEditorStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const useEditorStoreBase = create<IEditorState & IEditorAction>()(
cascaderDelimiters: ['/'],
isDarkMode: false,
isUseRealtimeEffect: true,
activeLanguageServer: 'native',
debugVariables: [],
isTrash: false,
updateIsWindowAdjustment: (isWindowAdjustment) => set({ isWindowAdjustment }),
Expand All @@ -49,7 +50,7 @@ const useEditorStoreBase = create<IEditorState & IEditorAction>()(
updateEditorFontFamily: (editorFontFamily) => {
set({ editorFontFamily });
updateUserConfiguration(`{
"workbench.colorTheme": "${get().isDarkMode ? 'WebGAL Dark' : 'WebGAL White'}",
"workbench.colorTheme": "${get().isDarkMode ? 'WebGAL Black' : 'WebGAL White'}",
"editor.semanticHighlighting.enabled": "configuredByTheme",
"editor.fontFamily": "${get().editorFontFamily}",
"editor.fontSize": ${get().editorFontSize},
Expand All @@ -58,7 +59,7 @@ const useEditorStoreBase = create<IEditorState & IEditorAction>()(
updateEditorFontSize: (editorFontSize) => {
set({ editorFontSize });
updateUserConfiguration(`{
"workbench.colorTheme": "${get().isDarkMode ? 'WebGAL Dark' : 'WebGAL White'}",
"workbench.colorTheme": "${get().isDarkMode ? 'WebGAL Black' : 'WebGAL White'}",
"editor.semanticHighlighting.enabled": "configuredByTheme",
"editor.fontFamily": "${get().editorFontFamily}",
"editor.fontSize": ${get().editorFontSize},
Expand All @@ -80,6 +81,7 @@ const useEditorStoreBase = create<IEditorState & IEditorAction>()(
updateCascaderDelimiters: (cascaderDelimiters) => set({ cascaderDelimiters }),
updateIsDarkMode: (isDarkMode) => set({ isDarkMode }),
updateIsUseRealtimeEffect: (isUseRealtimeEffect) => set({ isUseRealtimeEffect }),
updateActiveLanguageServer: (activeLanguageServer) => set({ activeLanguageServer }),
updateDebugVariables: (debugVariables) => set({ debugVariables }),
updateIsTrash: (isTrash) => set({ isTrash }),
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/origine2/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface IEditorState {
isUseRealtimeEffect: boolean; // 是否使用实时效果
isWindowAdjustment: boolean; // 预览窗口调整功能是否开启
isTrash: boolean; // 是否启用回收站功能
activeLanguageServer: string;
debugVariables: IDebugVariable[];
}

Expand Down Expand Up @@ -62,5 +63,6 @@ export interface IEditorAction {
updateIsUseRealtimeEffect: (isUseRealtimeEffect: IEditorState['isUseRealtimeEffect']) => void;
updateIsWindowAdjustment: (isWindowAdjustment: IEditorState['isWindowAdjustment']) => void;
updateIsTrash: (isTrash: IEditorState['isTrash']) => void;
updateActiveLanguageServer: (activeLanguageServer: IEditorState['activeLanguageServer']) => void;
updateDebugVariables: (debugVariables: IEditorState['debugVariables']) => void;
}
Loading
Loading