Skip to content

Commit 6c1e4e4

Browse files
author
winjo
committed
feat: 增加 editor 入口
1 parent 1036fb8 commit 6c1e4e4

38 files changed

Lines changed: 1761 additions & 815 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"description": "Ant Codespaces for browser",
99
"main": "index.js",
1010
"engines": {
11-
"kaitian": "1.33.0"
11+
"kaitian": "1.35.2"
1212
},
1313
"scripts": {
1414
"preinstall": "node scripts/preinstall",

packages/alex/package.json

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,41 @@
2828
"registry": "https://registry.npm.alibaba-inc.com"
2929
},
3030
"dependencies": {
31-
"@ali/ide-addons": "1.33.0",
32-
"@ali/ide-comments": "1.33.0",
33-
"@ali/ide-core-browser": "1.33.0",
34-
"@ali/ide-core-common": "1.33.0",
35-
"@ali/ide-debug": "1.33.0",
36-
"@ali/ide-decoration": "1.33.0",
37-
"@ali/ide-editor": "1.33.0",
38-
"@ali/ide-explorer": "1.33.0",
39-
"@ali/ide-express-file-server": "1.33.0",
40-
"@ali/ide-extension-storage": "1.33.0",
41-
"@ali/ide-file-scheme": "1.33.0",
42-
"@ali/ide-file-service": "1.33.0",
43-
"@ali/ide-file-tree-next": "1.33.0",
44-
"@ali/ide-kaitian-extension": "1.33.0",
45-
"@ali/ide-keymaps": "1.33.0",
46-
"@ali/ide-logs": "1.33.0",
47-
"@ali/ide-main-layout": "1.33.0",
48-
"@ali/ide-markers": "1.33.0",
49-
"@ali/ide-menu-bar": "1.33.0",
50-
"@ali/ide-monaco": "1.33.0",
51-
"@ali/ide-monaco-enhance": "1.33.0",
52-
"@ali/ide-opened-editor": "1.33.0",
53-
"@ali/ide-outline": "1.33.0",
54-
"@ali/ide-output": "1.33.0",
55-
"@ali/ide-overlay": "1.33.0",
56-
"@ali/ide-preferences": "1.33.0",
57-
"@ali/ide-quick-open": "1.33.0",
58-
"@ali/ide-scm": "1.33.0",
59-
"@ali/ide-static-resource": "1.33.0",
60-
"@ali/ide-status-bar": "1.33.0",
61-
"@ali/ide-storage": "1.33.0",
62-
"@ali/ide-theme": "1.33.0",
63-
"@ali/ide-webview": "1.33.0",
64-
"@ali/ide-workspace": "1.33.0",
65-
"@ali/ide-workspace-edit": "1.33.0",
31+
"@ali/ide-addons": "1.35.2",
32+
"@ali/ide-comments": "1.35.2",
33+
"@ali/ide-core-browser": "1.35.2",
34+
"@ali/ide-core-common": "1.35.2",
35+
"@ali/ide-debug": "1.35.2",
36+
"@ali/ide-decoration": "1.35.2",
37+
"@ali/ide-editor": "1.35.2",
38+
"@ali/ide-explorer": "1.35.2",
39+
"@ali/ide-express-file-server": "1.35.2",
40+
"@ali/ide-extension-storage": "1.35.2",
41+
"@ali/ide-file-scheme": "1.35.2",
42+
"@ali/ide-file-service": "1.35.2",
43+
"@ali/ide-file-tree-next": "1.35.2",
44+
"@ali/ide-kaitian-extension": "1.35.2",
45+
"@ali/ide-keymaps": "1.35.2",
46+
"@ali/ide-logs": "1.35.2",
47+
"@ali/ide-main-layout": "1.35.2",
48+
"@ali/ide-markers": "1.35.2",
49+
"@ali/ide-menu-bar": "1.35.2",
50+
"@ali/ide-monaco": "1.35.2",
51+
"@ali/ide-monaco-enhance": "1.35.2",
52+
"@ali/ide-opened-editor": "1.35.2",
53+
"@ali/ide-outline": "1.35.2",
54+
"@ali/ide-output": "1.35.2",
55+
"@ali/ide-overlay": "1.35.2",
56+
"@ali/ide-preferences": "1.35.2",
57+
"@ali/ide-quick-open": "1.35.2",
58+
"@ali/ide-scm": "1.35.2",
59+
"@ali/ide-static-resource": "1.35.2",
60+
"@ali/ide-status-bar": "1.35.2",
61+
"@ali/ide-storage": "1.35.2",
62+
"@ali/ide-theme": "1.35.2",
63+
"@ali/ide-webview": "1.35.2",
64+
"@ali/ide-workspace": "1.35.2",
65+
"@ali/ide-workspace-edit": "1.35.2",
6666
"@ali/kaitian-textmate-languages": "^2.2.0",
6767
"@alipay/alex-cli": "0.7.2",
6868
"@alipay/alex-code-api": "0.7.2",

packages/alex/src/api/createApp.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import { isMonacoLoaded, loadMonaco } from '@ali/ide-monaco/lib/browser/monaco-l
1515
import { IEditorDocumentModelService } from '@ali/ide-editor/lib/browser';
1616
import { EditorDocumentModelServiceImpl } from '@ali/ide-editor/lib/browser/doc-model/editor-document-model-service';
1717
import { EditorDocumentModel } from '@ali/ide-editor/lib/browser/doc-model/editor-document-model';
18-
// import { FileTreeModelService } from '@ali/ide-file-tree-next/lib/browser/services/file-tree-model.service';
19-
// import { WorkerExtensionService } from '@ali/ide-kaitian-extension/lib/browser/extension.worker.service';
18+
import { FileTreeModelService } from '@ali/ide-file-tree-next/lib/browser/services/file-tree-model.service';
19+
import { WorkerExtensionService } from '@ali/ide-kaitian-extension/lib/browser/extension.worker.service';
2020
import * as os from 'os';
2121

2222
import { modules } from '../core/modules';
@@ -105,7 +105,7 @@ export function createApp({ appConfig, runtimeConfig }: IConfig): IAppInstance {
105105
themeStorage.set(e.type);
106106
});
107107
// IDE 销毁时,组件会触发 handleTreeBlur,但是 FileContextKey 实例尚未初始化,此时在 dispose 阶段,injector.get(FileContextKey) 会抛出错误
108-
// app.injector.get(FileTreeModelService).handleTreeBlur();
108+
app.injector.get(FileTreeModelService).handleTreeBlur();
109109

110110
setTimeout(() => {
111111
logPv(runtimeConfig.biz);
@@ -138,9 +138,9 @@ export function createApp({ appConfig, runtimeConfig }: IConfig): IAppInstance {
138138
(monaco as any).services.StaticServices.modeService._value = null;
139139
// @ts-ignore
140140
// common-di 通过参数实例化无法自动 dispose
141-
// app.injector.get(WorkerExtensionService)?.protocol?._locals?.forEach((instance) => {
142-
// instance.dispose?.();
143-
// });
141+
app.injector.get(WorkerExtensionService)?.protocol?._locals?.forEach((instance) => {
142+
instance.dispose?.();
143+
});
144144
app.injector.disposeAll();
145145
};
146146

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import '@ali/ide-i18n/lib/browser';
2+
import '@alipay/alex-i18n';
3+
import {
4+
ClientApp,
5+
RuntimeConfig,
6+
makeWorkspaceDir,
7+
IAppOpts,
8+
STORAGE_DIR,
9+
} from '@alipay/alex-core';
10+
import { SlotRenderer, SlotLocation, IAppRenderer, FILES_DEFAULTS } from '@ali/ide-core-browser';
11+
import { BoxPanel, SplitPanel } from '@ali/ide-core-browser/lib/components';
12+
import { IThemeService } from '@ali/ide-theme/lib/common';
13+
import '@ali/ide-core-browser/lib/style/index.less';
14+
import { isMonacoLoaded, loadMonaco } from '@ali/ide-monaco/lib/browser/monaco-loader';
15+
import { IEditorDocumentModelService } from '@ali/ide-editor/lib/browser';
16+
import { EditorDocumentModelServiceImpl } from '@ali/ide-editor/lib/browser/doc-model/editor-document-model-service';
17+
import { EditorDocumentModel } from '@ali/ide-editor/lib/browser/doc-model/editor-document-model';
18+
import * as os from 'os';
19+
20+
import '../core/editor/style.module.less';
21+
import { modules } from '../core/editor/modules';
22+
import { mergeConfig, themeStorage } from '../core/utils';
23+
import { EditorLayoutComponent, getEditorLayoutConfig } from '../core/layout';
24+
import { IConfig, IAppInstance } from './types';
25+
import { logPv } from '../core/tracert';
26+
27+
export { SlotLocation, SlotRenderer, BoxPanel, SplitPanel };
28+
29+
const getDefaultAppConfig = (): IAppOpts => ({
30+
modules,
31+
useCdnIcon: true,
32+
noExtHost: true,
33+
defaultPreferences: {
34+
'general.theme': 'ide-light',
35+
'application.confirmExit': 'never',
36+
'editor.autoSave': 'afterDelay',
37+
'editor.autoSaveDelay': 1000, // one second
38+
'files.exclude': {
39+
...FILES_DEFAULTS.filesExclude,
40+
// browserfs OverlayFS 用来记录删除的文件
41+
'**/.deletedFiles.log': true,
42+
},
43+
},
44+
layoutConfig: getEditorLayoutConfig(),
45+
layoutComponent: EditorLayoutComponent,
46+
logDir: `${os.homedir()}/${STORAGE_DIR}/logs/`,
47+
preferenceDirName: STORAGE_DIR,
48+
storageDirName: STORAGE_DIR,
49+
extensionStorageDirName: STORAGE_DIR,
50+
appName: 'ALEX',
51+
allowSetDocumentTitleFollowWorkspaceDir: false,
52+
});
53+
54+
// 提前加载 monaco 并提前缓存 codeEditorService
55+
loadMonaco();
56+
let codeEditorService: any = null;
57+
isMonacoLoaded()?.then(() => {
58+
codeEditorService = (monaco as any).services.StaticServices.codeEditorService;
59+
});
60+
61+
export function createEditor({ appConfig, runtimeConfig }: IConfig): IAppInstance {
62+
const customConfig = typeof appConfig === 'function' ? appConfig() : appConfig;
63+
const opts = mergeConfig(getDefaultAppConfig(), customConfig);
64+
65+
if (!opts.workspaceDir) {
66+
throw new Error(
67+
'需工作空间目录,最好确保不同项目名称不同,如 group/repository 的形式,工作空间目录会挂载到 /workspace 目录下'
68+
);
69+
}
70+
opts.workspaceDir = makeWorkspaceDir(opts.workspaceDir);
71+
72+
let themeType = themeStorage.get();
73+
if (!themeType) {
74+
const defaultTheme = opts.defaultPreferences?.['general.theme'];
75+
opts.extensionMetadata?.find((item) => {
76+
const themeConfig = item.packageJSON.contributes?.themes?.find(
77+
(item: any) => item.id === defaultTheme
78+
);
79+
if (themeConfig) {
80+
themeType = !themeConfig.uiTheme || themeConfig.uiTheme === 'vs-dark' ? 'dark' : 'light';
81+
themeStorage.set(themeType);
82+
}
83+
});
84+
}
85+
86+
const app = new ClientApp(opts) as IAppInstance;
87+
88+
const _start = app.start;
89+
app.start = async (container: HTMLElement | IAppRenderer) => {
90+
await _start.call(app, container);
91+
// 在 start 不能 injector.get,否则有的 service 立即初始化,此时 file-system 还没有初始化完成
92+
(app.injector.get(IThemeService) as IThemeService).onThemeChange((e) => {
93+
themeStorage.set(e.type);
94+
});
95+
setTimeout(() => {
96+
logPv(runtimeConfig.biz);
97+
});
98+
};
99+
100+
/**
101+
* 目前整个应用有太多的副作用,尤其是注册到 monaco 的事件,如 DocumentSymbolProviderRegistry.onChange
102+
* 在 monaco 上的事件无法注销,除非重新全局实例化一个 monaco,目前 kaitian 并未暴露,暂时不可行
103+
* 因此这里的 destroy 仍然可能有不少副作用无法清除,暂时清理已知的,避免报错
104+
*/
105+
let destroyed = false;
106+
app.destroy = () => {
107+
if (destroyed) {
108+
return;
109+
}
110+
destroyed = true;
111+
// from acr
112+
const editorDocModelService = app.injector.get(
113+
IEditorDocumentModelService
114+
) as EditorDocumentModelServiceImpl;
115+
for (const instance of Array.from(
116+
editorDocModelService['_modelReferenceManager'].instances.values()
117+
) as EditorDocumentModel[]) {
118+
instance['monacoModel'].dispose();
119+
}
120+
if (codeEditorService) {
121+
codeEditorService._value = null;
122+
}
123+
(monaco as any).services.StaticServices.modeService._value = null;
124+
app.injector.disposeAll();
125+
};
126+
127+
// 基于场景的运行时数据
128+
app.injector.addProviders({
129+
token: RuntimeConfig,
130+
useValue: runtimeConfig,
131+
});
132+
133+
(window as any)[RuntimeConfig] = runtimeConfig;
134+
135+
return app;
136+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import React, { useState, useEffect, useRef } from 'react';
2+
import ReactDOM from 'react-dom';
3+
import { IReporterService, localize, getDebugLogger } from '@ali/ide-core-common';
4+
import { REPORT_NAME } from '@alipay/alex-core';
5+
import { createEditor } from './createEditor';
6+
import { Root } from '../core/Root';
7+
import { RootProps, LandingProps } from '../core/types';
8+
import { themeStorage } from '../core/utils';
9+
import { useConstant } from '../core/hooks';
10+
import { IConfig, IAppInstance } from './types';
11+
import { EditorProps } from '../core/editor/types';
12+
import { Container } from '../core/editor/container';
13+
14+
interface IRenderProps extends IConfig {
15+
onLoad?(app: IAppInstance): void;
16+
Landing?: React.ComponentType<LandingProps>;
17+
}
18+
19+
export type IEditorRenderProps = IRenderProps & EditorProps;
20+
21+
export const renderEditor = (domElement: HTMLElement, props: IEditorRenderProps) => {
22+
const { onLoad, Landing, ...opts } = props;
23+
const app = createEditor(opts);
24+
const themeType = themeStorage.get();
25+
ReactDOM.render(<Root status="loading" theme={themeType} Landing={Landing} />, domElement);
26+
27+
app
28+
.start((appElement) => {
29+
return new Promise((resolve) => {
30+
ReactDOM.render(
31+
<Root status="success" theme={themeType}>
32+
{appElement}
33+
</Root>,
34+
domElement,
35+
resolve
36+
);
37+
});
38+
})
39+
.then(() => {
40+
onLoad?.(app);
41+
})
42+
.catch((err: Error) => {
43+
ReactDOM.render(
44+
<Root status="error" error={err?.message || localize('error.unknown')} theme={themeType} />,
45+
domElement
46+
);
47+
48+
(app.injector.get(
49+
IReporterService
50+
) as IReporterService).point(REPORT_NAME.ALEX_APP_START_ERROR, err?.message, { error: err });
51+
getDebugLogger().error(err);
52+
setTimeout(() => {
53+
throw err;
54+
});
55+
});
56+
57+
return () => {
58+
app.destroy();
59+
};
60+
};
61+
62+
export const EditorRenderer: React.FC<IEditorRenderProps> = ({ onLoad, Landing, ...opts }) => {
63+
const app = useConstant(() => createEditor(opts));
64+
const themeType = useConstant(() => themeStorage.get());
65+
const appElementRef = useRef<React.ReactElement | null>(null);
66+
67+
const [state, setState] = useState<{
68+
status: RootProps['status'];
69+
error?: RootProps['error'];
70+
}>(() => ({ status: 'loading' }));
71+
72+
useEffect(() => {
73+
app
74+
.start((appElement) => {
75+
appElementRef.current = appElement;
76+
setState({ status: 'success' });
77+
return Promise.resolve();
78+
})
79+
.then(() => {
80+
onLoad?.(app);
81+
})
82+
.catch((err: Error) => {
83+
setState({ error: err?.message || localize('error.unknown'), status: 'error' });
84+
85+
(app.injector.get(IReporterService) as IReporterService).point(
86+
REPORT_NAME.ALEX_APP_START_ERROR,
87+
err?.message,
88+
{
89+
error: err,
90+
}
91+
);
92+
getDebugLogger().error(err);
93+
setTimeout(() => {
94+
throw err;
95+
});
96+
});
97+
98+
return () => {
99+
app.destroy();
100+
};
101+
}, []);
102+
103+
return (
104+
<Container value={{ documentModel: opts.documentModel }}>
105+
<Root {...state} theme={themeType} Landing={Landing}>
106+
{appElementRef.current}
107+
</Root>
108+
</Container>
109+
);
110+
};

0 commit comments

Comments
 (0)