@opensumi/ide-editor 2.22.10 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/breadcrumb/default.d.ts.map +1 -1
- package/lib/browser/breadcrumb/default.js +2 -0
- package/lib/browser/breadcrumb/default.js.map +1 -1
- package/lib/browser/diff/index.d.ts +5 -0
- package/lib/browser/diff/index.d.ts.map +1 -1
- package/lib/browser/diff/index.js +36 -3
- package/lib/browser/diff/index.js.map +1 -1
- package/lib/browser/doc-model/editor-document-model.d.ts.map +1 -1
- package/lib/browser/doc-model/editor-document-model.js +1 -0
- package/lib/browser/doc-model/editor-document-model.js.map +1 -1
- package/lib/browser/doc-model/saveParticipants.d.ts +14 -0
- package/lib/browser/doc-model/saveParticipants.d.ts.map +1 -1
- package/lib/browser/doc-model/saveParticipants.js +109 -1
- package/lib/browser/doc-model/saveParticipants.js.map +1 -1
- package/lib/browser/doc-model/types.d.ts +1 -0
- package/lib/browser/doc-model/types.d.ts.map +1 -1
- package/lib/browser/doc-model/types.js.map +1 -1
- package/lib/browser/editor-collection.service.d.ts +3 -2
- package/lib/browser/editor-collection.service.d.ts.map +1 -1
- package/lib/browser/editor-collection.service.js +30 -3
- package/lib/browser/editor-collection.service.js.map +1 -1
- package/lib/browser/editor.contribution.d.ts.map +1 -1
- package/lib/browser/editor.contribution.js +18 -0
- package/lib/browser/editor.contribution.js.map +1 -1
- package/lib/browser/editor.module.less +3 -0
- package/lib/browser/fs-resource/fs-resource.d.ts +3 -0
- package/lib/browser/fs-resource/fs-resource.d.ts.map +1 -1
- package/lib/browser/fs-resource/fs-resource.js +27 -1
- package/lib/browser/fs-resource/fs-resource.js.map +1 -1
- package/lib/browser/menu/breadcrumbs.menus.d.ts +8 -0
- package/lib/browser/menu/breadcrumbs.menus.d.ts.map +1 -0
- package/lib/browser/menu/breadcrumbs.menus.js +34 -0
- package/lib/browser/menu/breadcrumbs.menus.js.map +1 -0
- package/lib/browser/monaco-contrib/callHierarchy/callHierarchy.service.d.ts +1 -0
- package/lib/browser/monaco-contrib/callHierarchy/callHierarchy.service.d.ts.map +1 -1
- package/lib/browser/navigation.view.d.ts +7 -4
- package/lib/browser/navigation.view.d.ts.map +1 -1
- package/lib/browser/navigation.view.js +27 -11
- package/lib/browser/navigation.view.js.map +1 -1
- package/lib/browser/preference/contribution.d.ts.map +1 -1
- package/lib/browser/preference/contribution.js +1 -1
- package/lib/browser/preference/contribution.js.map +1 -1
- package/lib/browser/quick-open/workspace-symbol-quickopen.d.ts +1 -0
- package/lib/browser/quick-open/workspace-symbol-quickopen.d.ts.map +1 -1
- package/lib/browser/resource.service.js +1 -0
- package/lib/browser/resource.service.js.map +1 -1
- package/lib/browser/tab.view.d.ts.map +1 -1
- package/lib/browser/tab.view.js +2 -1
- package/lib/browser/tab.view.js.map +1 -1
- package/lib/browser/types.d.ts +10 -1
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js +6 -0
- package/lib/browser/types.js.map +1 -1
- package/lib/common/language.d.ts +1 -0
- package/lib/common/language.d.ts.map +1 -1
- package/lib/common/mocks/workbench-editor.service.d.ts +1 -0
- package/lib/common/mocks/workbench-editor.service.d.ts.map +1 -1
- package/lib/common/mocks/workbench-editor.service.js +3 -0
- package/lib/common/mocks/workbench-editor.service.js.map +1 -1
- package/lib/common/resource.d.ts +3 -1
- package/lib/common/resource.d.ts.map +1 -1
- package/lib/common/resource.js.map +1 -1
- package/package.json +13 -13
- package/src/browser/breadcrumb/default.ts +2 -0
- package/src/browser/diff/index.ts +45 -10
- package/src/browser/doc-model/editor-document-model.ts +1 -0
- package/src/browser/doc-model/saveParticipants.ts +119 -1
- package/src/browser/doc-model/types.ts +1 -0
- package/src/browser/editor-collection.service.ts +42 -3
- package/src/browser/editor.contribution.ts +19 -0
- package/src/browser/editor.module.less +3 -0
- package/src/browser/fs-resource/fs-resource.ts +30 -1
- package/src/browser/menu/breadcrumbs.menus.ts +34 -0
- package/src/browser/navigation.view.tsx +30 -10
- package/src/browser/preference/contribution.ts +2 -2
- package/src/browser/resource.service.ts +1 -0
- package/src/browser/tab.view.tsx +5 -0
- package/src/browser/types.ts +15 -4
- package/src/common/mocks/workbench-editor.service.ts +4 -0
- package/src/common/resource.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensumi/ide-editor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib",
|
|
6
6
|
"src"
|
|
@@ -17,21 +17,21 @@
|
|
|
17
17
|
"url": "git@github.com:opensumi/core.git"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@opensumi/ide-core-common": "2.
|
|
21
|
-
"@opensumi/ide-core-node": "2.
|
|
22
|
-
"@opensumi/ide-file-service": "2.
|
|
23
|
-
"@opensumi/ide-monaco": "2.
|
|
24
|
-
"@opensumi/ide-utils": "2.
|
|
20
|
+
"@opensumi/ide-core-common": "2.23.0",
|
|
21
|
+
"@opensumi/ide-core-node": "2.23.0",
|
|
22
|
+
"@opensumi/ide-file-service": "2.23.0",
|
|
23
|
+
"@opensumi/ide-monaco": "2.23.0",
|
|
24
|
+
"@opensumi/ide-utils": "2.23.0",
|
|
25
25
|
"vscode-oniguruma": "1.5.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@opensumi/ide-components": "2.
|
|
29
|
-
"@opensumi/ide-core-browser": "2.
|
|
28
|
+
"@opensumi/ide-components": "2.23.0",
|
|
29
|
+
"@opensumi/ide-core-browser": "2.23.0",
|
|
30
30
|
"@opensumi/ide-dev-tool": "1.3.1",
|
|
31
|
-
"@opensumi/ide-overlay": "2.
|
|
32
|
-
"@opensumi/ide-quick-open": "2.
|
|
33
|
-
"@opensumi/ide-theme": "2.
|
|
34
|
-
"@opensumi/ide-workspace": "2.
|
|
31
|
+
"@opensumi/ide-overlay": "2.23.0",
|
|
32
|
+
"@opensumi/ide-quick-open": "2.23.0",
|
|
33
|
+
"@opensumi/ide-theme": "2.23.0",
|
|
34
|
+
"@opensumi/ide-workspace": "2.23.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "5d8e4fcf717531446c838d142f7601699fd50d0f"
|
|
37
37
|
}
|
|
@@ -98,6 +98,7 @@ export class DefaultBreadCrumbProvider extends WithEventBus implements IBreadCru
|
|
|
98
98
|
|
|
99
99
|
const res: IBreadCrumbPart = {
|
|
100
100
|
name: uri.path.base,
|
|
101
|
+
uri,
|
|
101
102
|
icon: this.labelService.getIcon(uri, { isDirectory }),
|
|
102
103
|
getSiblings: async () => {
|
|
103
104
|
const parentDir = URI.from({
|
|
@@ -173,6 +174,7 @@ export class DefaultBreadCrumbProvider extends WithEventBus implements IBreadCru
|
|
|
173
174
|
const res: IBreadCrumbPart = {
|
|
174
175
|
name: symbol.name,
|
|
175
176
|
icon: getSymbolIcon(symbol.kind),
|
|
177
|
+
isSymbol: true,
|
|
176
178
|
onClick: () => {
|
|
177
179
|
editor.setSelection({
|
|
178
180
|
startColumn: symbol.range.startColumn,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
2
|
import { URI, Domain, WithEventBus, OnEvent } from '@opensumi/ide-core-browser';
|
|
3
3
|
import { LabelService } from '@opensumi/ide-core-browser/lib/services';
|
|
4
|
+
import { IFileServiceClient } from '@opensumi/ide-file-service';
|
|
4
5
|
|
|
5
6
|
import { IResourceProvider, IDiffResource, ResourceService, ResourceDecorationChangeEvent } from '../../common';
|
|
6
7
|
import { BrowserEditorContribution, EditorComponentRegistry, EditorOpenType } from '../types';
|
|
@@ -18,10 +19,15 @@ export class DiffResourceProvider extends WithEventBus implements IResourceProvi
|
|
|
18
19
|
@Autowired(ResourceService)
|
|
19
20
|
resourceService: ResourceService;
|
|
20
21
|
|
|
22
|
+
@Autowired(IFileServiceClient)
|
|
23
|
+
protected fileServiceClient: IFileServiceClient;
|
|
24
|
+
|
|
21
25
|
scheme = 'diff';
|
|
22
26
|
|
|
23
27
|
private modifiedToResource = new Map<string, URI>();
|
|
24
28
|
|
|
29
|
+
private userhomePath: URI | null;
|
|
30
|
+
|
|
25
31
|
@OnEvent(ResourceDecorationChangeEvent)
|
|
26
32
|
onResourceDecorationChangeEvent(e: ResourceDecorationChangeEvent) {
|
|
27
33
|
if (e.payload.uri && this.modifiedToResource.has(e.payload.uri.toString())) {
|
|
@@ -34,21 +40,50 @@ export class DiffResourceProvider extends WithEventBus implements IResourceProvi
|
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
private async getCurrentUserHome() {
|
|
44
|
+
if (!this.userhomePath) {
|
|
45
|
+
try {
|
|
46
|
+
const userhome = await this.fileServiceClient.getCurrentUserHome();
|
|
47
|
+
if (userhome) {
|
|
48
|
+
this.userhomePath = new URI(userhome.uri);
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {}
|
|
51
|
+
}
|
|
52
|
+
return this.userhomePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private async getReadableTooltip(path: URI) {
|
|
56
|
+
const pathStr = path.toString();
|
|
57
|
+
const userhomePath = await this.getCurrentUserHome();
|
|
58
|
+
if (!userhomePath) {
|
|
59
|
+
return decodeURIComponent(path.withScheme('').toString());
|
|
60
|
+
}
|
|
61
|
+
if (userhomePath.isEqualOrParent(path)) {
|
|
62
|
+
const userhomePathStr = userhomePath && userhomePath.toString();
|
|
63
|
+
return decodeURIComponent(pathStr.replace(userhomePathStr, '~'));
|
|
64
|
+
}
|
|
65
|
+
return decodeURIComponent(path.withScheme('').toString());
|
|
66
|
+
}
|
|
67
|
+
|
|
37
68
|
async provideResource(uri: URI): Promise<IDiffResource> {
|
|
38
69
|
const { original, modified, name } = uri.getParsedQuery();
|
|
39
70
|
const originalUri = new URI(original);
|
|
40
71
|
const modifiedUri = new URI(modified);
|
|
41
|
-
const icon = await this.labelService.getIcon(originalUri);
|
|
42
72
|
this.modifiedToResource.set(modifiedUri.toString(), uri);
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
return Promise.all([
|
|
74
|
+
this.labelService.getIcon(originalUri),
|
|
75
|
+
// 默认显示 modified 文件路径
|
|
76
|
+
this.getReadableTooltip(modifiedUri),
|
|
77
|
+
]).then(([icon, title]) => ({
|
|
78
|
+
name,
|
|
79
|
+
icon,
|
|
80
|
+
uri,
|
|
81
|
+
metadata: {
|
|
82
|
+
original: originalUri,
|
|
83
|
+
modified: modifiedUri,
|
|
84
|
+
},
|
|
85
|
+
title,
|
|
86
|
+
}));
|
|
52
87
|
}
|
|
53
88
|
|
|
54
89
|
async shouldCloseResource(resource, openedResources): Promise<boolean> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Injectable, Autowired } from '@opensumi/di';
|
|
1
|
+
import { Injectable, Autowired, Injector, INJECTOR_TOKEN } from '@opensumi/di';
|
|
2
2
|
import {
|
|
3
3
|
ClientAppContribution,
|
|
4
4
|
WithEventBus,
|
|
@@ -18,6 +18,11 @@ import { IProgressService } from '@opensumi/ide-core-browser/lib/progress';
|
|
|
18
18
|
import { ResourceEdit } from '@opensumi/ide-monaco/lib/browser/monaco-api';
|
|
19
19
|
import { languageFeaturesService } from '@opensumi/ide-monaco/lib/browser/monaco-api/languages';
|
|
20
20
|
import { ITextModel } from '@opensumi/ide-monaco/lib/browser/monaco-api/types';
|
|
21
|
+
import { Selection } from '@opensumi/monaco-editor-core';
|
|
22
|
+
import { IActiveCodeEditor } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
|
|
23
|
+
import { ICodeEditorService } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService';
|
|
24
|
+
import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
|
|
25
|
+
import { Range } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/range';
|
|
21
26
|
import * as languages from '@opensumi/monaco-editor-core/esm/vs/editor/common/languages';
|
|
22
27
|
import { CodeActionProvider } from '@opensumi/monaco-editor-core/esm/vs/editor/common/languages';
|
|
23
28
|
import {
|
|
@@ -30,10 +35,29 @@ import {
|
|
|
30
35
|
} from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/codeAction/browser/types';
|
|
31
36
|
import * as monaco from '@opensumi/monaco-editor-core/esm/vs/editor/editor.api';
|
|
32
37
|
|
|
38
|
+
import { MonacoCodeService } from '../editor.override';
|
|
33
39
|
import { SaveReason } from '../types';
|
|
34
40
|
|
|
35
41
|
import { EditorDocumentModelWillSaveEvent, IEditorDocumentModelService } from './types';
|
|
36
42
|
|
|
43
|
+
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null {
|
|
44
|
+
let candidate: IActiveCodeEditor | null = null;
|
|
45
|
+
|
|
46
|
+
if (model.isAttachedToEditor()) {
|
|
47
|
+
for (const editor of codeEditorService.listCodeEditors()) {
|
|
48
|
+
if (editor.hasModel() && editor.getModel() === model) {
|
|
49
|
+
if (editor.hasTextFocus()) {
|
|
50
|
+
return editor; // favour focused editor if there are multiple
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
candidate = editor;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return candidate;
|
|
59
|
+
}
|
|
60
|
+
|
|
37
61
|
@Injectable()
|
|
38
62
|
export class CodeActionOnSaveParticipant extends WithEventBus {
|
|
39
63
|
@Autowired(PreferenceService)
|
|
@@ -216,12 +240,106 @@ export class CodeActionOnSaveParticipant extends WithEventBus {
|
|
|
216
240
|
}
|
|
217
241
|
}
|
|
218
242
|
|
|
243
|
+
@Injectable()
|
|
244
|
+
export class TrimFinalNewLinesParticipant extends WithEventBus {
|
|
245
|
+
@Autowired(PreferenceService)
|
|
246
|
+
private readonly preferenceService: PreferenceService;
|
|
247
|
+
|
|
248
|
+
@Autowired(IEditorDocumentModelService)
|
|
249
|
+
private readonly docService: IEditorDocumentModelService;
|
|
250
|
+
|
|
251
|
+
@Autowired(ILogger)
|
|
252
|
+
private readonly logger: ILogger;
|
|
253
|
+
|
|
254
|
+
@Autowired(INJECTOR_TOKEN)
|
|
255
|
+
private readonly injector: Injector;
|
|
256
|
+
|
|
257
|
+
activate() {
|
|
258
|
+
// noop
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@OnEvent(EditorDocumentModelWillSaveEvent)
|
|
262
|
+
async onEditorDocumentModelWillSave(e: EditorDocumentModelWillSaveEvent) {
|
|
263
|
+
const isTrimFinalNewlines = this.preferenceService.get('files.trimFinalNewlines');
|
|
264
|
+
|
|
265
|
+
if (isTrimFinalNewlines) {
|
|
266
|
+
const modelRef = this.docService.getModelReference(e.payload.uri, 'trimFinalNewlines');
|
|
267
|
+
|
|
268
|
+
if (!modelRef) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const model = modelRef.instance.getMonacoModel();
|
|
273
|
+
this.doTrimFinalNewLines(model, e.payload.reason !== SaveReason.Manual);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* returns 0 if the entire file is empty
|
|
279
|
+
*/
|
|
280
|
+
private findLastNonEmptyLine(model: ITextModel): number {
|
|
281
|
+
for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) {
|
|
282
|
+
const lineContent = model.getLineContent(lineNumber);
|
|
283
|
+
if (lineContent.length > 0) {
|
|
284
|
+
// this line has content
|
|
285
|
+
return lineNumber;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// no line has content
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void {
|
|
293
|
+
const lineCount = model.getLineCount();
|
|
294
|
+
|
|
295
|
+
// Do not insert new line if file does not end with new line
|
|
296
|
+
if (lineCount === 1) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let prevSelection: Selection[] = [];
|
|
301
|
+
let cannotTouchLineNumber = 0;
|
|
302
|
+
|
|
303
|
+
const codeEditorService = this.injector.get(MonacoCodeService);
|
|
304
|
+
const editor = findEditor(model, codeEditorService);
|
|
305
|
+
if (editor) {
|
|
306
|
+
prevSelection = editor.getSelections();
|
|
307
|
+
if (isAutoSaved) {
|
|
308
|
+
for (let i = 0, len = prevSelection.length; i < len; i++) {
|
|
309
|
+
const positionLineNumber = prevSelection[i].positionLineNumber;
|
|
310
|
+
if (positionLineNumber > cannotTouchLineNumber) {
|
|
311
|
+
cannotTouchLineNumber = positionLineNumber;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const lastNonEmptyLine = this.findLastNonEmptyLine(model);
|
|
318
|
+
const deleteFromLineNumber = Math.max(lastNonEmptyLine + 1, cannotTouchLineNumber + 1);
|
|
319
|
+
const deletionRange = model.validateRange(
|
|
320
|
+
new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount)),
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
if (deletionRange.isEmpty()) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], () => prevSelection);
|
|
328
|
+
|
|
329
|
+
editor?.setSelections(prevSelection);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
219
333
|
@Domain(ClientAppContribution)
|
|
220
334
|
export class SaveParticipantsContribution implements ClientAppContribution {
|
|
221
335
|
@Autowired()
|
|
222
336
|
codeActionOnSaveParticipant: CodeActionOnSaveParticipant;
|
|
223
337
|
|
|
338
|
+
@Autowired()
|
|
339
|
+
trimFinalNewLinesParticipant: TrimFinalNewLinesParticipant;
|
|
340
|
+
|
|
224
341
|
onStart() {
|
|
225
342
|
this.codeActionOnSaveParticipant.activate();
|
|
343
|
+
this.trimFinalNewLinesParticipant.activate();
|
|
226
344
|
}
|
|
227
345
|
}
|
|
@@ -270,6 +270,7 @@ export class EditorDocumentModelContentChangedEvent extends BasicEvent<IEditorDo
|
|
|
270
270
|
export interface IEditorDocumentModelContentChangedEventPayload {
|
|
271
271
|
uri: URI;
|
|
272
272
|
dirty: boolean;
|
|
273
|
+
readonly: boolean;
|
|
273
274
|
changes: IEditorDocumentModelContentChange[];
|
|
274
275
|
eol: string;
|
|
275
276
|
versionId: number;
|
|
@@ -185,6 +185,7 @@ export class EditorCollectionServiceImpl extends WithEventBus implements EditorC
|
|
|
185
185
|
uri: e.payload.uri,
|
|
186
186
|
decoration: {
|
|
187
187
|
dirty: !!e.payload.dirty,
|
|
188
|
+
readOnly: !!e.payload.readonly,
|
|
188
189
|
},
|
|
189
190
|
}),
|
|
190
191
|
);
|
|
@@ -221,7 +222,7 @@ function updateOptionsWithMonacoEditor(
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
@Injectable({ multiple: true })
|
|
224
|
-
export abstract class BaseMonacoEditorWrapper extends
|
|
225
|
+
export abstract class BaseMonacoEditorWrapper extends WithEventBus implements IEditor {
|
|
225
226
|
public abstract readonly currentDocumentModel: IEditorDocumentModel | null;
|
|
226
227
|
|
|
227
228
|
public get currentUri(): URI | null {
|
|
@@ -517,10 +518,29 @@ export class BrowserCodeEditor extends BaseMonacoEditorWrapper implements ICodeE
|
|
|
517
518
|
position: this.monacoEditor.getPosition(),
|
|
518
519
|
selectionLength: 0,
|
|
519
520
|
});
|
|
521
|
+
|
|
522
|
+
const { instance } = documentModelRef;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* 由于通过 monaco model 并不能得到该文档是否 readonly
|
|
526
|
+
* 所以这里需要对 readonly 单独设置一下
|
|
527
|
+
*/
|
|
528
|
+
this.monacoEditor.updateOptions({
|
|
529
|
+
readOnly: instance.readonly,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
this.eventBus.fire(
|
|
533
|
+
new ResourceDecorationNeedChangeEvent({
|
|
534
|
+
uri: instance.uri,
|
|
535
|
+
decoration: {
|
|
536
|
+
readOnly: instance.readonly,
|
|
537
|
+
},
|
|
538
|
+
}),
|
|
539
|
+
);
|
|
520
540
|
}
|
|
521
541
|
}
|
|
522
542
|
|
|
523
|
-
export class BrowserDiffEditor extends
|
|
543
|
+
export class BrowserDiffEditor extends WithEventBus implements IDiffEditor {
|
|
524
544
|
@Autowired(EditorCollectionService)
|
|
525
545
|
private collectionService: EditorCollectionServiceImpl;
|
|
526
546
|
|
|
@@ -676,13 +696,32 @@ export class BrowserDiffEditor extends Disposable implements IDiffEditor {
|
|
|
676
696
|
await this.doUpdateDiffOptions();
|
|
677
697
|
}
|
|
678
698
|
|
|
699
|
+
isReadonly(): boolean {
|
|
700
|
+
return !!this.modifiedDocModel?.readonly;
|
|
701
|
+
}
|
|
702
|
+
|
|
679
703
|
private async doUpdateDiffOptions() {
|
|
680
704
|
const uriStr = this.modifiedEditor.currentUri ? this.modifiedEditor.currentUri.toString() : undefined;
|
|
681
705
|
const languageId = this.modifiedEditor.currentDocumentModel
|
|
682
706
|
? this.modifiedEditor.currentDocumentModel.languageId
|
|
683
707
|
: undefined;
|
|
684
708
|
const options = getConvertedMonacoOptions(this.configurationService, uriStr, languageId);
|
|
685
|
-
this.monacoDiffEditor.updateOptions({
|
|
709
|
+
this.monacoDiffEditor.updateOptions({
|
|
710
|
+
...options.diffOptions,
|
|
711
|
+
...this.specialOptions,
|
|
712
|
+
readOnly: this.isReadonly(),
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
if (this.currentUri) {
|
|
716
|
+
this.eventBus.fire(
|
|
717
|
+
new ResourceDecorationNeedChangeEvent({
|
|
718
|
+
uri: this.currentUri,
|
|
719
|
+
decoration: {
|
|
720
|
+
readOnly: this.isReadonly(),
|
|
721
|
+
},
|
|
722
|
+
}),
|
|
723
|
+
);
|
|
724
|
+
}
|
|
686
725
|
}
|
|
687
726
|
|
|
688
727
|
updateDiffOptions(options: Partial<monaco.editor.IDiffEditorOptions>) {
|
|
@@ -1202,6 +1202,25 @@ export class EditorContribution
|
|
|
1202
1202
|
order: 2,
|
|
1203
1203
|
});
|
|
1204
1204
|
|
|
1205
|
+
menus.registerMenuItem(MenuId.BreadcrumbsTitleContext, {
|
|
1206
|
+
command: EDITOR_COMMANDS.COPY_PATH.id,
|
|
1207
|
+
group: '0_path',
|
|
1208
|
+
order: 1,
|
|
1209
|
+
});
|
|
1210
|
+
menus.registerMenuItem(MenuId.BreadcrumbsTitleContext, {
|
|
1211
|
+
command: EDITOR_COMMANDS.COPY_RELATIVE_PATH.id,
|
|
1212
|
+
group: '0_path',
|
|
1213
|
+
order: 2,
|
|
1214
|
+
});
|
|
1215
|
+
menus.registerMenuItem(MenuId.BreadcrumbsTitleContext, {
|
|
1216
|
+
command: {
|
|
1217
|
+
id: FILE_COMMANDS.REVEAL_IN_EXPLORER.id,
|
|
1218
|
+
label: localize('file.revealInExplorer'),
|
|
1219
|
+
},
|
|
1220
|
+
group: '1_file',
|
|
1221
|
+
order: 3,
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1205
1224
|
menus.registerMenuItem(MenuId.EditorTitleContext, {
|
|
1206
1225
|
command: EDITOR_COMMANDS.SPLIT_TO_LEFT.id,
|
|
1207
1226
|
group: '9_split',
|
|
@@ -48,6 +48,8 @@ export class FileSystemResourceProvider extends WithEventBus implements IResourc
|
|
|
48
48
|
|
|
49
49
|
private ready: Promise<void>;
|
|
50
50
|
|
|
51
|
+
private userhomePath: URI | null;
|
|
52
|
+
|
|
51
53
|
constructor() {
|
|
52
54
|
super();
|
|
53
55
|
this.ready = this.init();
|
|
@@ -103,6 +105,31 @@ export class FileSystemResourceProvider extends WithEventBus implements IResourc
|
|
|
103
105
|
return this.cachedFileStat.get(uri);
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
private async getCurrentUserHome() {
|
|
109
|
+
if (!this.userhomePath) {
|
|
110
|
+
try {
|
|
111
|
+
const userhome = await this.fileServiceClient.getCurrentUserHome();
|
|
112
|
+
if (userhome) {
|
|
113
|
+
this.userhomePath = new URI(userhome.uri);
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {}
|
|
116
|
+
}
|
|
117
|
+
return this.userhomePath;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async getReadableTooltip(path: URI) {
|
|
121
|
+
const pathStr = path.toString();
|
|
122
|
+
const userhomePath = await this.getCurrentUserHome();
|
|
123
|
+
if (!userhomePath) {
|
|
124
|
+
return decodeURIComponent(path.withScheme('').toString());
|
|
125
|
+
}
|
|
126
|
+
if (userhomePath.isEqualOrParent(path)) {
|
|
127
|
+
const userhomePathStr = userhomePath && userhomePath.toString();
|
|
128
|
+
return decodeURIComponent(pathStr.replace(userhomePathStr, '~'));
|
|
129
|
+
}
|
|
130
|
+
return decodeURIComponent(path.withScheme('').toString());
|
|
131
|
+
}
|
|
132
|
+
|
|
106
133
|
async provideResource(uri: URI): Promise<IResource<any>> {
|
|
107
134
|
// 获取文件类型 getFileType: (path: string) => string
|
|
108
135
|
await this.ready;
|
|
@@ -111,13 +138,15 @@ export class FileSystemResourceProvider extends WithEventBus implements IResourc
|
|
|
111
138
|
this.getFileStat(uri.toString()),
|
|
112
139
|
this.labelService.getName(uri),
|
|
113
140
|
this.labelService.getIcon(uri),
|
|
114
|
-
|
|
141
|
+
this.getReadableTooltip(uri),
|
|
142
|
+
] as const).then(([stat, name, icon, title]) => ({
|
|
115
143
|
name: stat ? name : name + localize('file.resource-deleted', '(已删除)'),
|
|
116
144
|
icon,
|
|
117
145
|
uri,
|
|
118
146
|
metadata: null,
|
|
119
147
|
deleted: !stat,
|
|
120
148
|
supportsRevive: true,
|
|
149
|
+
title,
|
|
121
150
|
}));
|
|
122
151
|
}
|
|
123
152
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import {
|
|
3
|
+
AbstractContextMenuService,
|
|
4
|
+
ICtxMenuRenderer,
|
|
5
|
+
MenuId,
|
|
6
|
+
IContextMenu,
|
|
7
|
+
} from '@opensumi/ide-core-browser/lib/menu/next';
|
|
8
|
+
import { URI } from '@opensumi/ide-core-common';
|
|
9
|
+
|
|
10
|
+
import { EditorGroup } from '../workbench-editor.service';
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class BreadCrumbsMenuService {
|
|
14
|
+
@Autowired(AbstractContextMenuService)
|
|
15
|
+
private readonly ctxMenuService: AbstractContextMenuService;
|
|
16
|
+
|
|
17
|
+
@Autowired(ICtxMenuRenderer)
|
|
18
|
+
private readonly ctxMenuRenderer: ICtxMenuRenderer;
|
|
19
|
+
|
|
20
|
+
show(x: number, y: number, group: EditorGroup, uri: URI) {
|
|
21
|
+
const titleContext = group.contextKeyService;
|
|
22
|
+
const menus = this.ctxMenuService.createMenu({
|
|
23
|
+
id: MenuId.BreadcrumbsTitleContext,
|
|
24
|
+
contextKeyService: titleContext,
|
|
25
|
+
});
|
|
26
|
+
const menuNodes = menus.getMergedMenuNodes();
|
|
27
|
+
|
|
28
|
+
this.ctxMenuRenderer.show({
|
|
29
|
+
anchor: { x, y },
|
|
30
|
+
menuNodes,
|
|
31
|
+
args: [{ uri, group }],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -9,6 +9,7 @@ import { Scrollbars } from '@opensumi/ide-components';
|
|
|
9
9
|
import { useInjectable, DomListener, Disposable, useUpdateOnEvent } from '@opensumi/ide-core-browser';
|
|
10
10
|
import { getIcon } from '@opensumi/ide-core-browser';
|
|
11
11
|
|
|
12
|
+
import { BreadCrumbsMenuService } from './menu/breadcrumbs.menus';
|
|
12
13
|
import styles from './navigation.module.less';
|
|
13
14
|
import { IBreadCrumbService, IBreadCrumbPart } from './types';
|
|
14
15
|
import { useUpdateOnGroupTabChange } from './view/react-hook';
|
|
@@ -47,18 +48,24 @@ export const NavigationBar = ({ editorGroup }: { editorGroup: EditorGroup }) =>
|
|
|
47
48
|
return null;
|
|
48
49
|
}
|
|
49
50
|
return parts.length === 0 ? null : (
|
|
50
|
-
<div
|
|
51
|
+
<div
|
|
52
|
+
className={styles.navigation_container}
|
|
53
|
+
onContextMenu={(event) => {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
51
57
|
{parts.map((p, i) => (
|
|
52
58
|
<React.Fragment key={i + '-crumb:' + p.name}>
|
|
53
59
|
{i > 0 && <Icon icon={'right'} size='small' className={styles.navigation_icon} />}
|
|
54
|
-
<NavigationItem part={p} />
|
|
60
|
+
<NavigationItem part={p} editorGroup={editorGroup} />
|
|
55
61
|
</React.Fragment>
|
|
56
62
|
))}
|
|
57
63
|
</div>
|
|
58
64
|
);
|
|
59
65
|
};
|
|
60
|
-
export const NavigationItem = memo(({ part }: { part: IBreadCrumbPart }) => {
|
|
66
|
+
export const NavigationItem = memo(({ part, editorGroup }: { part: IBreadCrumbPart; editorGroup: EditorGroup }) => {
|
|
61
67
|
const viewService = useInjectable(NavigationBarViewService) as NavigationBarViewService;
|
|
68
|
+
const breadcrumbsMenuService = useInjectable(BreadCrumbsMenuService) as BreadCrumbsMenuService;
|
|
62
69
|
const itemRef = useRef<HTMLSpanElement>();
|
|
63
70
|
|
|
64
71
|
const onClick = useCallback(async () => {
|
|
@@ -70,12 +77,22 @@ export const NavigationItem = memo(({ part }: { part: IBreadCrumbPart }) => {
|
|
|
70
77
|
// 放左边
|
|
71
78
|
leftPos = window.innerWidth - 200 - 5;
|
|
72
79
|
}
|
|
73
|
-
viewService.showMenu(siblings.parts, leftPos, top + height + 5, siblings.currentIndex);
|
|
80
|
+
viewService.showMenu(siblings.parts, leftPos, top + height + 5, siblings.currentIndex, part.uri, editorGroup);
|
|
74
81
|
}
|
|
75
82
|
}, [itemRef.current, part]);
|
|
76
83
|
|
|
77
84
|
return (
|
|
78
|
-
<span
|
|
85
|
+
<span
|
|
86
|
+
onClick={onClick}
|
|
87
|
+
onContextMenu={(event) => {
|
|
88
|
+
if (!part.isSymbol && part.uri) {
|
|
89
|
+
breadcrumbsMenuService.show(event.nativeEvent.x, event.nativeEvent.y, editorGroup, part.uri);
|
|
90
|
+
}
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
}}
|
|
93
|
+
className={styles['navigation-part']}
|
|
94
|
+
ref={itemRef as any}
|
|
95
|
+
>
|
|
79
96
|
{part.icon && <span className={part.icon}></span>}
|
|
80
97
|
<span>{part.name}</span>
|
|
81
98
|
</span>
|
|
@@ -136,7 +153,7 @@ export const NavigationMenu = observer(({ model }: { model: NavigationMenuModel
|
|
|
136
153
|
// 放左边
|
|
137
154
|
nextLeft = left - width - 5;
|
|
138
155
|
}
|
|
139
|
-
model.showSubMenu(await p.getChildren!(), nextLeft, top);
|
|
156
|
+
model.showSubMenu(await p.getChildren!(), nextLeft, top, model);
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
159
|
: undefined;
|
|
@@ -218,9 +235,11 @@ export const NavigationMenuContainer = observer(() => {
|
|
|
218
235
|
@Injectable()
|
|
219
236
|
export class NavigationBarViewService {
|
|
220
237
|
@observable.ref current: NavigationMenuModel | undefined;
|
|
238
|
+
@observable.ref editorGroup: EditorGroup;
|
|
221
239
|
|
|
222
|
-
showMenu(parts: IBreadCrumbPart[], x, y, currentIndex) {
|
|
223
|
-
this.current = new NavigationMenuModel(parts, x, y, currentIndex);
|
|
240
|
+
showMenu(parts: IBreadCrumbPart[], x, y, currentIndex, uri, editorGroup) {
|
|
241
|
+
this.current = new NavigationMenuModel(parts, x, y, currentIndex, uri);
|
|
242
|
+
this.editorGroup = editorGroup;
|
|
224
243
|
}
|
|
225
244
|
|
|
226
245
|
dispose() {
|
|
@@ -239,10 +258,11 @@ export class NavigationMenuModel {
|
|
|
239
258
|
public readonly x,
|
|
240
259
|
public readonly y,
|
|
241
260
|
public readonly initialIndex: number = -1,
|
|
261
|
+
public readonly uri,
|
|
242
262
|
) {}
|
|
243
263
|
|
|
244
|
-
showSubMenu(parts: IBreadCrumbPart[], x, y) {
|
|
245
|
-
this.subMenu = new NavigationMenuModel(parts, x, y);
|
|
264
|
+
showSubMenu(parts: IBreadCrumbPart[], x, y, uri) {
|
|
265
|
+
this.subMenu = new NavigationMenuModel(parts, x, y, -1, uri);
|
|
246
266
|
}
|
|
247
267
|
|
|
248
268
|
dispose() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PreferenceContribution, Domain
|
|
1
|
+
import { PreferenceContribution, Domain } from '@opensumi/ide-core-browser';
|
|
2
2
|
|
|
3
3
|
import { editorPreferenceSchema } from './schema';
|
|
4
4
|
|
|
5
|
-
@Domain(PreferenceContribution
|
|
5
|
+
@Domain(PreferenceContribution)
|
|
6
6
|
export class EditorPreferenceContribution implements PreferenceContribution {
|
|
7
7
|
schema = editorPreferenceSchema;
|
|
8
8
|
}
|
package/src/browser/tab.view.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
PreferenceService,
|
|
28
28
|
DisposableCollection,
|
|
29
29
|
Event,
|
|
30
|
+
getExternalIcon,
|
|
30
31
|
} from '@opensumi/ide-core-browser';
|
|
31
32
|
import { InlineMenuBar } from '@opensumi/ide-core-browser/lib/components/actions';
|
|
32
33
|
import { LAYOUT_VIEW_SIZE } from '@opensumi/ide-core-browser/lib/layout/constants';
|
|
@@ -322,6 +323,7 @@ export const Tabs = ({ group }: ITabsProps) => {
|
|
|
322
323
|
return (
|
|
323
324
|
<div
|
|
324
325
|
draggable={true}
|
|
326
|
+
title={resource.title}
|
|
325
327
|
className={classnames({
|
|
326
328
|
[styles.kt_editor_tab]: true,
|
|
327
329
|
[styles.last_in_row]: tabMap.get(i),
|
|
@@ -385,6 +387,9 @@ export const Tabs = ({ group }: ITabsProps) => {
|
|
|
385
387
|
</div>
|
|
386
388
|
<div>{resource.name}</div>
|
|
387
389
|
{subname ? <div className={styles.subname}>{subname}</div> : null}
|
|
390
|
+
{decoration.readOnly ? (
|
|
391
|
+
<span className={classnames(getExternalIcon('lock'), styles.editor_readonly_icon)}></span>
|
|
392
|
+
) : null}
|
|
388
393
|
<div className={styles.tab_right}>
|
|
389
394
|
<div
|
|
390
395
|
className={classnames({
|