Skip to content

Commit edcdd45

Browse files
author
winjo
committed
feat: lsif 对接,高亮行内容
1 parent 74cf5ff commit edcdd45

10 files changed

Lines changed: 367 additions & 119 deletions

File tree

packages/alex/src/core/editor/editor.module.ts

Lines changed: 170 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Provider, Injectable, Autowired } from '@ali/common-di';
2+
import debounce from 'lodash.debounce';
23
import {
34
BrowserModule,
45
ClientAppContribution,
@@ -9,8 +10,10 @@ import {
910
CommandService,
1011
PreferenceProvider,
1112
PreferenceScope,
12-
IDisposable,
1313
Emitter,
14+
CommandContribution,
15+
CommandRegistry,
16+
Disposable,
1417
} from '@ali/ide-core-browser';
1518
import { Sequence } from '@ali/ide-core-common/lib/sequence';
1619
import { BreadCrumbServiceImpl } from '@ali/ide-editor/lib/browser/breadcrumb';
@@ -28,6 +31,8 @@ import {
2831
WorkbenchEditorService,
2932
BrowserEditorContribution,
3033
EditorPreferences,
34+
IEditorFeatureRegistry,
35+
IEditor,
3136
} from '@ali/ide-editor/lib/browser';
3237
import { IEditorDocumentModelService } from '@ali/ide-editor/lib/browser/doc-model/types';
3338
import { FileSchemeDocumentProvider } from '@ali/ide-file-scheme/lib/browser/file-doc';
@@ -74,6 +79,31 @@ class FileSchemeDocumentProviderOverride extends FileSchemeDocumentProvider {
7479
*/
7580
@Injectable()
7681
class WorkspacePreferenceProvider extends PreferenceProvider {
82+
constructor() {
83+
super();
84+
this._ready.resolve();
85+
}
86+
87+
getPreferences() {
88+
return undefined;
89+
}
90+
91+
getLanguagePreferences() {
92+
return undefined;
93+
}
94+
95+
doSetPreference() {
96+
return Promise.resolve(false);
97+
}
98+
}
99+
100+
@Injectable()
101+
class FoldersPreferenceProvider extends PreferenceProvider {
102+
constructor() {
103+
super();
104+
this._ready.resolve();
105+
}
106+
77107
getPreferences() {
78108
return undefined;
79109
}
@@ -88,22 +118,36 @@ class WorkspacePreferenceProvider extends PreferenceProvider {
88118
}
89119

90120
@Domain(ClientAppContribution)
91-
class ThemeContribution implements ClientAppContribution {
121+
class ThemeContribution extends Disposable implements ClientAppContribution {
92122
@Autowired(IThemeService)
93123
private readonly themeService: IThemeService;
94124

125+
@Autowired(PreferenceProvider, { tag: PreferenceScope.Default })
126+
protected readonly defaultPreferenceProvider: PreferenceProvider;
127+
95128
async initialize() {
96129
this.themeService.registerThemes(
97130
IDETheme.packageJSON.contributes!.themes,
98131
URI.parse(getExtensionPath(IDETheme.extension))
99132
);
100-
await this.themeService.applyTheme(getPreferenceThemeId());
133+
// 强制用集成设置的默认主题
134+
await this.themeService.applyTheme(this.defaultPreferenceProvider.get('general.theme'));
101135
}
102136
}
103137

104-
@Domain(FileSystemContribution, BrowserEditorContribution, ClientAppContribution)
138+
@Domain(
139+
FileSystemContribution,
140+
BrowserEditorContribution,
141+
ClientAppContribution,
142+
CommandContribution
143+
)
105144
class EditorSpecialContribution
106-
implements FileSystemContribution, BrowserEditorContribution, ClientAppContribution {
145+
extends Disposable
146+
implements
147+
FileSystemContribution,
148+
BrowserEditorContribution,
149+
ClientAppContribution,
150+
CommandContribution {
107151
@Autowired(WorkbenchEditorService)
108152
editorService: WorkbenchEditorService;
109153

@@ -125,8 +169,6 @@ class EditorSpecialContribution
125169
@Autowired(SCMService)
126170
scmService: SCMService;
127171

128-
private readonly disposables: IDisposable[] = [];
129-
130172
async mountFileSystem(rootFS: FileSystemInstance<'MountableFileSystem'>) {
131173
// TODO: 提供配置选择存储在内存中还是 indexedDB 中
132174
const {
@@ -161,7 +203,7 @@ class EditorSpecialContribution
161203
});
162204
rootFS.mount(this.appConfig.workspaceDir, overlayFileSystem);
163205
rootFS.mount(SCM_ROOT, editorSystem);
164-
this.disposables.push({
206+
this.addDispose({
165207
dispose: () => {
166208
rootFS.umount(this.appConfig.workspaceDir);
167209
rootFS.umount(SCM_ROOT);
@@ -194,31 +236,37 @@ class EditorSpecialContribution
194236
if (isCodeDocumentModel(documentModel)) {
195237
this.openCodeEditor(documentModel.ref, documentModel.filepath);
196238
// 监听 props 变化
197-
onSelect(
198-
(props) => props.documentModel,
199-
(newModel: CodeDocumentModel, oldModel: CodeDocumentModel) =>
200-
newModel.filepath === oldModel.filepath && newModel.ref === oldModel.ref
201-
)((newModel: CodeDocumentModel) => {
202-
this.openCodeEditor(newModel.ref, newModel.filepath);
203-
});
239+
this.addDispose(
240+
onSelect(
241+
(props) => props.documentModel,
242+
(newModel: CodeDocumentModel, oldModel: CodeDocumentModel) =>
243+
newModel.filepath === oldModel.filepath && newModel.ref === oldModel.ref
244+
)((newModel: CodeDocumentModel) => {
245+
this.openCodeEditor(newModel.ref, newModel.filepath);
246+
})
247+
);
204248
} else {
205249
this.openEditor(documentModel.filepath);
206250
// 监听 props 变化
207-
onSelect((props) => props.documentModel.filepath)((newFilepath) => {
208-
this.openEditor(newFilepath);
209-
});
251+
this.addDispose(
252+
onSelect((props) => props.documentModel.filepath)((newFilepath) => {
253+
this.openEditor(newFilepath);
254+
})
255+
);
210256
}
211257

212-
onSelect((props) => props.documentModel.encoding)((encoding) => {
213-
if (encoding) {
214-
const resource = this.editorService.currentResource;
215-
if (resource) {
216-
this.editorDocumentModelService.changeModelOptions(resource.uri, {
217-
encoding,
218-
});
258+
this.addDispose(
259+
onSelect((props) => props.documentModel.encoding)((encoding) => {
260+
if (encoding) {
261+
const resource = this.editorService.currentResource;
262+
if (resource) {
263+
this.editorDocumentModelService.changeModelOptions(resource.uri, {
264+
encoding,
265+
});
266+
}
219267
}
220-
}
221-
});
268+
})
269+
);
222270
}
223271

224272
private openEditor(relativePath: string) {
@@ -234,8 +282,95 @@ class EditorSpecialContribution
234282
return this.openEditor(`${encodeURIComponent(ref)}/${filepath}`);
235283
}
236284

237-
dispose() {
238-
this.disposables.forEach((disposer) => disposer.dispose());
285+
registerCommands(registry: CommandRegistry) {
286+
registry.registerCommand(
287+
{ id: 'alex.codeServiceProject' },
288+
{
289+
execute: () => {
290+
const documentModel = select((props) => props.documentModel);
291+
if (!isCodeDocumentModel(documentModel)) return;
292+
return {
293+
platform: 'antcode',
294+
project: `${documentModel.owner}/${documentModel.name}`,
295+
commit: documentModel.ref,
296+
rootUri: URI.file(this.appConfig.workspaceDir).resolve(documentModel.ref).codeUri,
297+
};
298+
},
299+
}
300+
);
301+
}
302+
303+
registerEditorFeature(registry: IEditorFeatureRegistry) {
304+
registry.registerEditorFeatureContribution({
305+
contribute: (editor: IEditor) => {
306+
const disposer = new Disposable();
307+
let oldHoverDecorations: string[] = [];
308+
disposer.addDispose(
309+
editor.monacoEditor.onMouseMove(
310+
debounce((event) => {
311+
const type = event?.target?.type;
312+
if (
313+
type === monaco.editor.MouseTargetType.GUTTER_LINE_NUMBERS ||
314+
type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN
315+
) {
316+
const lineNumber = event.target.position!.lineNumber;
317+
oldHoverDecorations = editor.monacoEditor.deltaDecorations(oldHoverDecorations, [
318+
{
319+
range: new monaco.Range(lineNumber, 1, lineNumber, 1),
320+
options: {
321+
className: 'alex-line-content',
322+
glyphMarginClassName: `alex-line-glyph-margin}`,
323+
},
324+
},
325+
]);
326+
} else {
327+
oldHoverDecorations = editor.monacoEditor.deltaDecorations(oldHoverDecorations, []);
328+
}
329+
}, 10)
330+
)
331+
);
332+
let oldClickDecorations: string[] = [];
333+
const highlightLine = (lineNumber: number) => {
334+
oldClickDecorations = editor.monacoEditor.deltaDecorations(oldClickDecorations, [
335+
{
336+
range: new monaco.Range(lineNumber, 1, lineNumber, 1),
337+
options: {
338+
isWholeLine: true,
339+
className: 'alex-line-content',
340+
},
341+
},
342+
]);
343+
};
344+
disposer.addDispose(
345+
editor.monacoEditor.onMouseDown((event) => {
346+
const type = event?.target?.type;
347+
if (
348+
type === monaco.editor.MouseTargetType.GUTTER_LINE_NUMBERS ||
349+
type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN
350+
) {
351+
const lineNumber = event.target.position!.lineNumber;
352+
// 非受控
353+
if (!('lineNumber' in select((props) => props.documentModel))) {
354+
highlightLine(lineNumber);
355+
}
356+
select((props) => props.documentModel.onLineNumberChange)?.(lineNumber);
357+
}
358+
})
359+
);
360+
const initialLineNumber = select((props) => props.documentModel.lineNumber);
361+
if (initialLineNumber) {
362+
highlightLine(initialLineNumber);
363+
}
364+
disposer.addDispose(
365+
onSelect((props) => props.documentModel.lineNumber)((newLineNumber) => {
366+
if (newLineNumber) {
367+
highlightLine(newLineNumber);
368+
}
369+
})
370+
);
371+
return disposer;
372+
},
373+
});
239374
}
240375
}
241376

@@ -258,6 +393,12 @@ export class EditorSpecialModule extends BrowserModule {
258393
tag: PreferenceScope.Workspace,
259394
override: true,
260395
},
396+
{
397+
token: PreferenceProvider,
398+
useClass: FoldersPreferenceProvider,
399+
tag: PreferenceScope.Folder,
400+
override: true,
401+
},
261402
ThemeContribution,
262403
EditorSpecialContribution,
263404
];

packages/alex/src/core/editor/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export interface DocumentModel {
44
filepath: string;
55
readFile(filepath: string): Uint8Array | Thenable<Uint8Array>;
66
encoding?: 'gbk' | 'utf8';
7+
lineNumber?: number;
8+
onLineNumberChange?: (num: number) => void;
79
}
810

911
export interface FSDocumentModel extends DocumentModel {

packages/alex/src/core/style.module.less

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,22 @@ alex-root {
6363
}
6464
}
6565
}
66+
67+
:global {
68+
.alex-root {
69+
.alex-line-glyph-margin {
70+
cursor: pointer;
71+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' focusable='false' data-icon='link' width='1em' height='1em' fill='currentColor' aria-hidden='true'%3E%3Cpath d='M574 665.4a8.03 8.03 0 00-11.3 0L446.5 781.6c-53.8 53.8-144.6 59.5-204 0-59.5-59.5-53.8-150.2 0-204l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3l-39.8-39.8a8.03 8.03 0 00-11.3 0L191.4 526.5c-84.6 84.6-84.6 221.5 0 306s221.5 84.6 306 0l116.2-116.2c3.1-3.1 3.1-8.2 0-11.3L574 665.4zm258.6-474c-84.6-84.6-221.5-84.6-306 0L410.3 307.6a8.03 8.03 0 000 11.3l39.7 39.7c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c53.8-53.8 144.6-59.5 204 0 59.5 59.5 53.8 150.2 0 204L665.3 562.6a8.03 8.03 0 000 11.3l39.8 39.8c3.1 3.1 8.2 3.1 11.3 0l116.2-116.2c84.5-84.6 84.5-221.5 0-306.1zM610.1 372.3a8.03 8.03 0 00-11.3 0L372.3 598.7a8.03 8.03 0 000 11.3l39.6 39.6c3.1 3.1 8.2 3.1 11.3 0l226.4-226.4c3.1-3.1 3.1-8.2 0-11.3l-39.5-39.6z'%3E%3C/path%3E%3C/svg%3E");
72+
background-repeat: no-repeat;
73+
background-size: 12px 12px;
74+
background-position: center center;
75+
}
76+
.alex-line-glyph-margin ~ .line-numbers {
77+
text-decoration: underline;
78+
}
79+
.alex-line-content {
80+
background-color: #F90;
81+
opacity: 0.2;
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)