Skip to content

Commit 53cbfdd

Browse files
authored
let editor extensions surface monaco field editors (#10511) (#10598)
1 parent 648c2cc commit 53cbfdd

10 files changed

Lines changed: 72 additions & 66 deletions

File tree

localtypings/pxteditor.d.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// <reference path="../built/pxtlib.d.ts" />
22
/// <reference path="./projectheader.d.ts" />
33
/// <reference path="./validatorPlan.d.ts" />
4+
/// <reference path="./monaco.d.ts" />
45

56
declare namespace pxt.editor {
67
export interface EditorMessage {
@@ -1155,6 +1156,7 @@ declare namespace pxt.editor {
11551156
onCodeStop?: () => void;
11561157

11571158
experiments?: Experiment[];
1159+
monacoFieldEditors?: MonacoFieldEditorDefinition[];
11581160
}
11591161

11601162
export interface Experiment {
@@ -1368,6 +1370,47 @@ declare namespace pxt.editor {
13681370
}
13691371

13701372
type AssetEditorEvent = AssetEditorRequestSaveEvent | AssetEditorReadyEvent;
1373+
1374+
export interface TextEdit {
1375+
range: monaco.Range;
1376+
replacement: string;
1377+
}
1378+
1379+
export interface MonacoFieldEditorHost {
1380+
contentDiv(): HTMLDivElement;
1381+
getText(range: monaco.Range): string;
1382+
blocksInfo(): pxtc.BlocksInfo;
1383+
1384+
package(): pxt.MainPackage;
1385+
writeFileAsync(filename: string, content: string): Promise<void>;
1386+
readFile(filename: string): string;
1387+
}
1388+
1389+
export interface MonacoFieldEditor {
1390+
getId(): string;
1391+
showEditorAsync(fileType: pxt.editor.FileType, editrange: monaco.Range, host: MonacoFieldEditorHost): Promise<TextEdit>;
1392+
onClosed(): void;
1393+
dispose(): void;
1394+
}
1395+
1396+
export interface MonacoFieldEditorDefinition {
1397+
id: string;
1398+
matcher: MonacoFindArguments;
1399+
foldMatches?: boolean;
1400+
alwaysBuildOnClose?: boolean;
1401+
glyphCssClass?: string;
1402+
weight?: number; // higher weight will override lower weight when on same line
1403+
proto: { new(): MonacoFieldEditor };
1404+
heightInPixels?: number;
1405+
}
1406+
1407+
export interface MonacoFindArguments {
1408+
searchString: string;
1409+
isRegex: boolean;
1410+
matchWholeWord: boolean;
1411+
matchCase: boolean;
1412+
validateRange?: (range: monaco.Range, model: monaco.editor.ITextModel) => monaco.Range;
1413+
}
13711414
}
13721415

13731416
declare namespace pxt.workspace {

pxteditor/monaco-fields/field_musiceditor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MonacoReactFieldEditor } from "./field_react";
2-
import { MonacoFieldEditorDefinition, registerMonacoFieldEditor } from "./monacoFieldEditor";
2+
import { registerMonacoFieldEditor } from "./monacoFieldEditor";
33

44
const fieldEditorId = "music-editor";
55

@@ -91,7 +91,7 @@ function createFakeAsset(song: pxt.assets.music.Song): pxt.Song {
9191
}
9292
}
9393

94-
export const songEditorDefinition: MonacoFieldEditorDefinition = {
94+
export const songEditorDefinition: pxt.editor.MonacoFieldEditorDefinition = {
9595
id: fieldEditorId,
9696
foldMatches: true,
9797
glyphCssClass: "fas fa-music sprite-focus-hover",

pxteditor/monaco-fields/field_react.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
import { MonacoFieldEditor, TextEdit, MonacoFieldEditorHost } from "./monacoFieldEditor";
2-
31
const fieldEditorId = "image-editor";
42

5-
export class MonacoReactFieldEditor<U> implements MonacoFieldEditor {
6-
private resolver: (edit: TextEdit) => void;
3+
export class MonacoReactFieldEditor<U> implements pxt.editor.MonacoFieldEditor {
4+
private resolver: (edit: pxt.editor.TextEdit) => void;
75
private rejecter: (err?: any) => void;
86

97
protected fileType: pxt.editor.FileType;
108
protected editrange: monaco.Range;
11-
protected host: MonacoFieldEditorHost;
9+
protected host: pxt.editor.MonacoFieldEditorHost;
1210
protected fv: pxt.react.FieldEditorView<U>;
1311

1412
getId() {
1513
return fieldEditorId;
1614
}
1715

18-
showEditorAsync(fileType: pxt.editor.FileType, editrange: monaco.Range, host: MonacoFieldEditorHost): Promise<TextEdit> {
16+
showEditorAsync(fileType: pxt.editor.FileType, editrange: monaco.Range, host: pxt.editor.MonacoFieldEditorHost): Promise<pxt.editor.TextEdit> {
1917
this.fileType = fileType;
2018
this.editrange = editrange;
2119
this.host = host;

pxteditor/monaco-fields/field_soundEffect.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MonacoReactFieldEditor } from "./field_react";
2-
import { MonacoFieldEditorDefinition, registerMonacoFieldEditor } from "./monacoFieldEditor";
2+
import { registerMonacoFieldEditor } from "./monacoFieldEditor";
33

44
const fieldEditorId = "soundeffect-editor";
55

@@ -205,7 +205,7 @@ function defaultSound(): pxt.assets.Sound {
205205
}
206206
}
207207

208-
export const soundEditorDefinition: MonacoFieldEditorDefinition = {
208+
export const soundEditorDefinition: pxt.editor.MonacoFieldEditorDefinition = {
209209
id: fieldEditorId,
210210
foldMatches: true,
211211
glyphCssClass: "fas fa-music sprite-focus-hover",

pxteditor/monaco-fields/field_sprite.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MonacoReactFieldEditor } from "./field_react";
2-
import { MonacoFieldEditorDefinition, registerMonacoFieldEditor } from "./monacoFieldEditor";
2+
import { registerMonacoFieldEditor } from "./monacoFieldEditor";
33

44
const fieldEditorId = "image-editor";
55

@@ -76,7 +76,7 @@ function createFakeAsset(bitmap: pxt.sprite.Bitmap): pxt.ProjectImage {
7676
}
7777
}
7878

79-
export const spriteEditorDefinition: MonacoFieldEditorDefinition = {
79+
export const spriteEditorDefinition: pxt.editor.MonacoFieldEditorDefinition = {
8080
id: fieldEditorId,
8181
foldMatches: true,
8282
glyphCssClass: "sprite-editor-glyph sprite-focus-hover",

pxteditor/monaco-fields/field_tilemap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MonacoReactFieldEditor } from "./field_react";
2-
import { MonacoFieldEditorDefinition, registerMonacoFieldEditor } from "./monacoFieldEditor";
2+
import { registerMonacoFieldEditor } from "./monacoFieldEditor";
33

44
const fieldEditorId = "tilemap-editor";
55

@@ -139,7 +139,7 @@ function createFakeAsset(data: pxt.sprite.TilemapData): pxt.ProjectTilemap {
139139
}
140140
}
141141

142-
export const tilemapEditorDefinition: MonacoFieldEditorDefinition = {
142+
export const tilemapEditorDefinition: pxt.editor.MonacoFieldEditorDefinition = {
143143
id: fieldEditorId,
144144
foldMatches: true,
145145
alwaysBuildOnClose: true,

pxteditor/monaco-fields/monacoFieldEditor.ts

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,9 @@
11
/// <reference path="../../localtypings/monaco.d.ts" />
22

3-
export interface TextEdit {
4-
range: monaco.Range;
5-
replacement: string;
6-
}
7-
8-
export interface MonacoFieldEditorHost {
9-
contentDiv(): HTMLDivElement;
10-
getText(range: monaco.Range): string;
11-
blocksInfo(): pxtc.BlocksInfo;
12-
13-
package(): pxt.MainPackage;
14-
writeFileAsync(filename: string, content: string): Promise<void>;
15-
readFile(filename: string): string;
16-
}
17-
18-
export interface MonacoFieldEditor {
19-
getId(): string;
20-
showEditorAsync(fileType: pxt.editor.FileType, editrange: monaco.Range, host: MonacoFieldEditorHost): Promise<TextEdit>;
21-
onClosed(): void;
22-
dispose(): void;
23-
}
24-
25-
export interface MonacoFieldEditorDefinition {
26-
id: string;
27-
matcher: MonacoFindArguments;
28-
foldMatches?: boolean;
29-
alwaysBuildOnClose?: boolean;
30-
glyphCssClass?: string;
31-
weight?: number; // higher weight will override lower weight when on same line
32-
proto: { new(): MonacoFieldEditor };
33-
heightInPixels?: number;
34-
}
35-
36-
export interface MonacoFindArguments {
37-
searchString: string;
38-
isRegex: boolean;
39-
matchWholeWord: boolean;
40-
matchCase: boolean;
41-
validateRange?: (range: monaco.Range, model: monaco.editor.ITextModel) => monaco.Range;
42-
}
433

44-
const definitions: pxt.Map<MonacoFieldEditorDefinition> = {};
4+
const definitions: pxt.Map<pxt.editor.MonacoFieldEditorDefinition> = {};
455

46-
export function registerMonacoFieldEditor(name: string, definition: MonacoFieldEditorDefinition) {
6+
export function registerMonacoFieldEditor(name: string, definition: pxt.editor.MonacoFieldEditorDefinition) {
477
definitions[name] = definition;
488
}
499

webapp/src/cmds.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import * as pxtblockly from "../../pxtblocks";
1313
import ExtensionResult = pxt.editor.ExtensionResult;
1414
import NativeHostMessage = pxt.editor.NativeHostMessage;
1515
import { setEditorExtensionExperiments } from "../../pxteditor/experiments";
16+
import { registerMonacoFieldEditor } from "../../pxteditor";
1617

1718

1819
function log(msg: string) {
@@ -401,6 +402,11 @@ function applyExtensionResult() {
401402
if (res.experiments) {
402403
setEditorExtensionExperiments(res.experiments);
403404
}
405+
if (res.monacoFieldEditors) {
406+
for (const def of res.monacoFieldEditors) {
407+
registerMonacoFieldEditor(def.id, def);
408+
}
409+
}
404410
}
405411

406412
export async function initAsync() {

webapp/src/monaco.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ export class Editor extends toolboxeditor.ToolboxEditor {
16061606
}
16071607
}
16081608

1609-
showFieldEditor(range: monaco.Range, fe: pxteditor.MonacoFieldEditor, viewZoneHeight: number, buildAfter: boolean) {
1609+
showFieldEditor(range: monaco.Range, fe: pxt.editor.MonacoFieldEditor, viewZoneHeight: number, buildAfter: boolean) {
16101610
if (this.feWidget) {
16111611
this.feWidget.close();
16121612
}

webapp/src/monacoFieldEditorHost.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/// <reference path="../../localtypings/monaco.d.ts" />
22

3-
import { MonacoFieldEditor, MonacoFieldEditorDefinition, MonacoFieldEditorHost, TextEdit } from "../../pxteditor";
43
import * as compiler from "./compiler";
54
import * as pkg from "./package";
65

@@ -11,7 +10,7 @@ interface OwnedRange {
1110
id: number;
1211
}
1312

14-
export class ViewZoneEditorHost implements MonacoFieldEditorHost, monaco.editor.IViewZone {
13+
export class ViewZoneEditorHost implements pxt.editor.MonacoFieldEditorHost, monaco.editor.IViewZone {
1514
domNode: HTMLDivElement;
1615
afterLineNumber: number;
1716
heightInPx = 520;
@@ -26,7 +25,7 @@ export class ViewZoneEditorHost implements MonacoFieldEditorHost, monaco.editor.
2625

2726
suppressMouseDown = false;
2827

29-
constructor(protected fe: MonacoFieldEditor, protected range: monaco.Range, protected model: monaco.editor.IModel) {
28+
constructor(protected fe: pxt.editor.MonacoFieldEditor, protected range: monaco.Range, protected model: monaco.editor.IModel) {
3029
this.afterLineNumber = range.endLineNumber;
3130

3231
const outer = document.createElement("div");
@@ -52,7 +51,7 @@ export class ViewZoneEditorHost implements MonacoFieldEditorHost, monaco.editor.
5251
return this.content;
5352
}
5453

55-
showAsync(fileType: pxt.editor.FileType, editor: monaco.editor.IStandaloneCodeEditor): Promise<TextEdit> {
54+
showAsync(fileType: pxt.editor.FileType, editor: monaco.editor.IStandaloneCodeEditor): Promise<pxt.editor.TextEdit> {
5655
this.fileType = fileType;
5756
this.editor = editor;
5857
return compiler.getBlocksAsync()
@@ -132,10 +131,10 @@ export class ViewZoneEditorHost implements MonacoFieldEditorHost, monaco.editor.
132131
}
133132
}
134133

135-
export class ModalEditorHost implements MonacoFieldEditorHost {
134+
export class ModalEditorHost implements pxt.editor.MonacoFieldEditorHost {
136135
protected blocks: pxtc.BlocksInfo;
137136

138-
constructor(protected fe: MonacoFieldEditor, protected range: monaco.Range, protected model: monaco.editor.IModel) {
137+
constructor(protected fe: pxt.editor.MonacoFieldEditor, protected range: monaco.Range, protected model: monaco.editor.IModel) {
139138
}
140139

141140
contentDiv(): HTMLDivElement {
@@ -164,7 +163,7 @@ export class ModalEditorHost implements MonacoFieldEditorHost {
164163
return this.package().host().readFile(pkg.mainPkg, filename);
165164
}
166165

167-
showAsync(fileType: pxt.editor.FileType, editor: monaco.editor.IStandaloneCodeEditor): Promise<TextEdit> {
166+
showAsync(fileType: pxt.editor.FileType, editor: monaco.editor.IStandaloneCodeEditor): Promise<pxt.editor.TextEdit> {
168167
return compiler.getBlocksAsync()
169168
.then(bi => {
170169
this.blocks = bi;
@@ -179,7 +178,7 @@ export class ModalEditorHost implements MonacoFieldEditorHost {
179178
}
180179

181180
export class FieldEditorManager implements monaco.languages.FoldingRangeProvider {
182-
protected fieldEditors: MonacoFieldEditorDefinition[] = [];
181+
protected fieldEditors: pxt.editor.MonacoFieldEditorDefinition[] = [];
183182
protected decorations: pxt.Map<string[]> = {};
184183
protected liveRanges: OwnedRange[] = [];
185184
protected fieldEditorsEnabled = true;
@@ -213,7 +212,7 @@ export class FieldEditorManager implements monaco.languages.FoldingRangeProvider
213212
}
214213
}
215214

216-
addFieldEditor(definition: MonacoFieldEditorDefinition) {
215+
addFieldEditor(definition: pxt.editor.MonacoFieldEditorDefinition) {
217216
for (const f of this.fieldEditors) {
218217
if (f.id === definition.id) return;
219218
}

0 commit comments

Comments
 (0)