Skip to content

Commit c8bb6b9

Browse files
Justin WilkersonJustin Wilkerson
Justin Wilkerson
authored and
Justin Wilkerson
committed
Added ability to format editor using Prettier. Updated App.tsx with example.
1 parent 2ea9a30 commit c8bb6b9

File tree

7 files changed

+109
-848
lines changed

7 files changed

+109
-848
lines changed

README.md

+15-4
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ This hooks provides all of the method and properties provided by the Plugin API.
8585
| Name | Description |
8686
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
8787
| **code** | `string` <br /><br /> The current code in the Monaco editor. This value updates on change to the Monaco editor with optional debouncing. Alias for `sandbox.getText()` |
88-
| **setCode** | `(code: string) => void` <br /><br /> Set the code in the Monaco editor. Alias for `sandbox.setText()` |
88+
| **setCode** | `(code: string, options: {format: "prettier" | "monaco"}) => void` <br /><br /> Set the code in the Monaco editor with optional formatting with Prettier or Monaco. Alias for `sandbox.setText()` |
8989
| **formatCode** | `() => void` <br /><br /> Format the code in the Monaco editor. Alias for `sandbox.editor.getAction("editor.action.formatDocument").run()` |
90-
| **markers** | `IMarker[]` <br /><br /> Alias for `sandbox.monaco.editor.getModelMarkers({})` that is kept in sync via `sandbox.editor.onDidChangeModelDecorations`. |
90+
| **prettier** | `prettier(config?: Options) => string` <br /><br /> Format the code in the Monaco editor with prettier. Accepts a prettier config object.
91+
| **markers** | `(IMarker & {key: string})[]` <br /><br /> Alias for `sandbox.monaco.editor.getModelMarkers({})` with added unique `key` property. Kept in sync via `sandbox.editor.onDidChangeModelDecorations`. |
9192
| **setDebounce** | `(debounce: boolean) => void` <br /><br /> Optionally debounce the `modelChange` event from the Plugin API. |
9293
| **sandbox** | `object` <br /><br /> A DOM library for interacting with TypeScript and JavaScript code, which powers the heart of the TypeScript playground. This object provides several properties and methods to interact with the playground. See all of the available types in `src/plugin/vendor/sandbox.d.ts` and read more about the sandbox at [http://www.typescriptlang.org/v2/dev/sandbox/](http://www.typescriptlang.org/v2/dev/sandbox/). |
9394
| **modal** | `object` <br /><br /> The model is an object which monaco uses to keep track of text in the editor. You can find the full type definition at `node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts`. |
@@ -97,6 +98,7 @@ This hooks provides all of the method and properties provided by the Plugin API.
9798

9899

99100

101+
100102
Here is the [type definition](https://github.com/Microsoft/monaco-editor/blob/master/monaco.d.ts#L875) for `IMarker`:
101103

102104
```typescript
@@ -126,6 +128,7 @@ const {
126128
code,
127129
setCode,
128130
formatCode,
131+
prettier,
129132
markers,
130133
setDebounce,
131134
sandbox,
@@ -137,17 +140,25 @@ const {
137140

138141
// Here are some examples of things you can do:
139142

143+
setDebounce(true);
144+
140145
// Set the code in the Monaco editor
141146
useEffect(() => {
142-
setCode(`const greet = (): string => "Hi👋";`);
143-
formatCode();
147+
const defaultCode = `const greet = (): string => "Hi👋";`
148+
setCode(defaultCode, {format: "prettier"});
144149
}, []);
145150

146151
// Listen for changes to the code in the Monaco editor
147152
useEffect(() => {
148153
flashInfo("The code was updated.")
149154
showModal(code, "Here is your code")
150155
}, [code]);
156+
157+
const renderMarkers = markers.map(marker => {
158+
return <div key={marker.key}>{marker.message}</div>
159+
})
160+
161+
// See App.tsx for additional usage examples
151162
```
152163

153164
## Styling your plugin

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@
3030
"@rollup/plugin-node-resolve": "^7.1.1",
3131
"@rollup/plugin-replace": "^2.3.1",
3232
"@types/node": "^13.7.4",
33+
"@types/prettier": "^1.19.0",
3334
"@types/react": "^16.9.22",
3435
"@types/react-dom": "^16.9.5",
3536
"concurrently": "^5.1.0",
3637
"cross-env": "^7.0.0",
3738
"monaco-editor": "^0.20.0",
39+
"prettier": "^1.19.1",
3840
"rollup": "^1.31.1",
3941
"rollup-plugin-babel": "^4.3.3",
4042
"rollup-plugin-css-only": "^2.0.0",
41-
"rollup-plugin-livereload": "^1.0.4",
42-
"rollup-plugin-postcss": "^2.0.6",
43+
"rollup-plugin-postcss": "^2.1.1",
4344
"rollup-plugin-terser": "^5.2.0",
4445
"serve": "^11.3.0",
4546
"typescript": "^3.8.2"

rollup.config.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ export default {
2424
replace({
2525
"process.env.NODE_ENV": JSON.stringify(
2626
isProd ? "production" : "development"
27-
)
27+
),
2828
}),
2929
image(),
3030
resolve({
3131
extensions,
3232
modulesOnly: false
3333
}),
3434
commonjs({
35-
include: /node_modules/
35+
include: /node_modules/,
36+
// For prettier/parser-typescript
37+
ignore: ["@microsoft/typescript-etw"]
3638
}),
3739
babel({
3840
extensions,

src/App.tsx

+20-12
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import "./App.css";
77
const { useEffect } = React;
88

99
const exampleCode = {
10-
start: "function echo(arg) {\n return arg;\n}",
11-
end: "function echo<T>(arg:T): T {\n return arg;\n}"
10+
start: "function echo(arg) { return arg};",
11+
end: "function echo<T>(arg:T): T {return arg;}"
1212
};
1313

1414
const App: React.FC = () => {
@@ -22,13 +22,14 @@ const App: React.FC = () => {
2222
model,
2323
container,
2424
flashInfo,
25-
showModal
25+
showModal,
26+
prettier
2627
} = usePlugin();
2728

2829
setDebounce(true);
2930

3031
useEffect(() => {
31-
setCode(exampleCode.start);
32+
setCode(exampleCode.start, { format: "prettier" });
3233
}, []);
3334

3435
useEffect(() => {
@@ -41,21 +42,24 @@ const App: React.FC = () => {
4142
flashInfo("Cleared!");
4243
}
4344

44-
function handleOpenModal() {
45-
showModal(code, "Here is your code.");
45+
function handleFixCode() {
46+
setCode(exampleCode.end, { format: "prettier" });
4647
}
4748

48-
function handleFixCode() {
49-
setCode(exampleCode.end);
49+
function handleMonacoFormat() {
5050
formatCode();
5151
}
5252

53+
function handlePrettierFormat() {
54+
prettier();
55+
}
56+
5357
const renderMarkers = markers
5458
.sort((a, b) => (a.startLineNumber >= b.startLineNumber ? 1 : -1))
5559
.map(marker => {
5660
return (
5761
<div
58-
key={marker.code?.toString()}
62+
key={marker.key}
5963
className={css`
6064
margin-top: 20px;
6165
`}
@@ -78,9 +82,13 @@ const App: React.FC = () => {
7882
<button className={buttonClass} onClick={handleFixCode}>
7983
Fix the Code
8084
</button>
81-
<button className={buttonClass} onClick={handleOpenModal}>
82-
Open the Modal
85+
<button className={buttonClass} onClick={handleMonacoFormat}>
86+
Format with Monaco
87+
</button>
88+
<button className={buttonClass} onClick={handlePrettierFormat}>
89+
Format with Prettier
8390
</button>
91+
8492
<button className={buttonClass} onClick={handleClear}>
8593
Clear the Editor
8694
</button>
@@ -113,7 +121,7 @@ const buttonClass = css`
113121
display: inline-block;
114122
margin: 5px;
115123
padding: 5px;
116-
min-width: 120px;
124+
min-width: 150px;
117125
color: ${colors.blue};
118126
background: transparent;
119127
font-size: 0.9rem;

src/plugin/Provider.tsx

+50-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
import React from "react";
22
import { Sandbox } from "./vendor/playground";
3+
import prettierLib from "prettier/standalone";
4+
import parserBabel from "prettier/parser-babylon";
5+
import parserTypescript from "prettier/parser-typescript";
6+
import { Options } from "prettier";
37
const { useState, useEffect, createContext, useCallback } = React;
48

9+
const defaultPrettierConfig: Options = {
10+
semi: true,
11+
parser: "babel",
12+
plugins: [parserBabel, parserTypescript],
13+
tabWidth: 2
14+
};
15+
516
type Model = import("monaco-editor").editor.ITextModel;
617
type ModelMarker = import("monaco-editor").editor.IMarker;
718
type FlashInfo = (message: string) => void;
@@ -18,9 +29,10 @@ export type PluginContextProps = {
1829
model: Model;
1930
flashInfo: FlashInfo;
2031
showModal: ShowModal;
21-
markers: ModelMarker[];
22-
setCode(value: string): void;
32+
markers: (ModelMarker & { key: string })[];
33+
setCode(value: string, options?: { format: "prettier" | "monaco" }): void;
2334
formatCode(): void;
35+
prettier(config?: Options): void;
2436
setDebounce(debounce: boolean): void;
2537
};
2638

@@ -43,7 +55,14 @@ export const Provider: React.FC<ProviderProps> = ({
4355

4456
useEffect(() => {
4557
const disposable = sandbox.editor.onDidChangeModelDecorations(() => {
46-
const allMarkers = sandbox.monaco.editor.getModelMarkers({});
58+
const allMarkers = sandbox.monaco.editor
59+
.getModelMarkers({})
60+
.map((marker, index) => {
61+
return {
62+
...marker,
63+
key: index.toString()
64+
};
65+
});
4766
setMarkers(allMarkers);
4867
});
4968
() => disposable.dispose();
@@ -57,16 +76,39 @@ export const Provider: React.FC<ProviderProps> = ({
5776
() => window.removeEventListener(eventName, listenerFn, false);
5877
}, [debounce]);
5978

79+
const setCode = useCallback(
80+
(value: string, options?: { format: "prettier" | "monaco" }) => {
81+
if (options && options.format === "prettier") {
82+
const prettyCode = prettierLib.format(value, defaultPrettierConfig);
83+
sandbox.setText(prettyCode);
84+
} else if (options && options.format === "monaco") {
85+
sandbox.editor.getAction("editor.action.formatDocument").run();
86+
} else {
87+
sandbox.setText(value);
88+
}
89+
},
90+
[]
91+
);
92+
6093
const formatCode = useCallback(() => {
6194
return sandbox.editor.getAction("editor.action.formatDocument").run();
6295
}, []);
6396

64-
const setCode = useCallback((value: string) => {
65-
sandbox.setText(value);
66-
}, []);
97+
const prettier = useCallback(
98+
(config?: Options) => {
99+
const prettyCode = prettierLib.format(
100+
code,
101+
{ ...config, ...defaultPrettierConfig } || defaultPrettierConfig
102+
);
103+
104+
sandbox.setText(prettyCode);
105+
},
106+
[code]
107+
);
67108

68109
// @ts-ignore
69110
const { showModal, flashInfo } = window.playground.ui;
111+
70112
const value = {
71113
model,
72114
showModal,
@@ -77,7 +119,8 @@ export const Provider: React.FC<ProviderProps> = ({
77119
setCode,
78120
formatCode,
79121
setDebounce,
80-
markers
122+
markers,
123+
prettier
81124
};
82125
return (
83126
<PluginContext.Provider value={value}>{children}</PluginContext.Provider>

src/types/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ declare global {
1111
flashInfo: (message: string) => void;
1212
};
1313
};
14+
ts: typeof window.ts
1415
}
1516
}

0 commit comments

Comments
 (0)