@@ -3,6 +3,9 @@ import { defineComponent, ref } from 'vue'
3
3
import Navigation from ' ./components/Navigation.vue'
4
4
import Editor from ' ./components/Editor.vue'
5
5
import { baseCompile } from ' vue-i18n'
6
+ import * as monaco from ' monaco-editor'
7
+ import { debounce } from ' ./utils'
8
+ import { SourceMapConsumer } from ' source-map'
6
9
import type { CompileError , CompileOptions } from ' vue-i18n'
7
10
8
11
interface PersistedState {
@@ -35,27 +38,33 @@ export default defineComponent({
35
38
* utilties
36
39
*/
37
40
let lastSuccessCode: string
38
- function compile(message : string ): string {
41
+ let lastSuccessfulMap: SourceMapConsumer | undefined
42
+ async function compile(message : string ): Promise <string > {
39
43
console .clear ()
40
44
41
45
try {
42
46
const start = performance .now ()
43
47
44
48
const errors: CompileError [] = []
45
- const { code, ast } = baseCompile (message , {
46
- onError : err => errors .push (err )
47
- })
49
+ const options = {
50
+ sourceMap: true ,
51
+ onError : (err : CompileError ) => errors .push (err )
52
+ }
53
+ const { code, ast, map } = baseCompile (message , options )
48
54
if (errors .length > 0 ) {
49
55
console .error (errors )
50
56
}
51
57
52
58
console .log (` Compiled in ${(performance .now () - start ).toFixed (2 )}ms. ` )
53
59
compileErrors .value = errors
54
60
console .log (` AST: ` , ast )
61
+ console .log (' sourcemap' , map )
55
62
56
63
const evalCode = new Function (` return ${code } ` )()
57
64
lastSuccessCode =
58
65
evalCode .toString () + ` \n\n // Check the console for the AST`
66
+ lastSuccessfulMap = await new SourceMapConsumer (map ! )
67
+ lastSuccessfulMap ! .computeColumnSpans ()
59
68
} catch (e ) {
60
69
lastSuccessCode = ` /* ERROR: ${e .message } (see console for more info) */ `
61
70
console .error (e )
@@ -67,42 +76,157 @@ export default defineComponent({
67
76
/**
68
77
* envet handlers
69
78
*/
70
- const onChange = (message : string ): void => {
79
+
80
+ let inputEditor: monaco .editor .IStandaloneCodeEditor | null = null
81
+ let outputEditor: monaco .editor .IStandaloneCodeEditor | null = null
82
+
83
+ // input editor model change event
84
+ const onChangeModel = async (message : string ): Promise <void > => {
71
85
const state = JSON .stringify ({ src: message } as PersistedState )
72
86
localStorage .setItem (' state' , state )
73
87
window .location .hash = encodeURIComponent (state )
74
- genCodes .value = compile (message )
88
+ genCodes .value = await compile (message )
89
+ }
90
+
91
+ // highlight output codes
92
+ let prevOutputDecos: string [] = []
93
+ function clearOutputDecos() {
94
+ if (! outputEditor ) {
95
+ return
96
+ }
97
+ prevOutputDecos = outputEditor .deltaDecorations (prevOutputDecos , [])
98
+ }
99
+
100
+ let prevInputDecos: string [] = []
101
+ function clearInputDecos() {
102
+ if (! inputEditor ) {
103
+ return
104
+ }
105
+ prevInputDecos = inputEditor .deltaDecorations (prevInputDecos , [])
106
+ }
107
+
108
+ // input editor ready event
109
+ const onReadyInput = (editor : monaco .editor .IStandaloneCodeEditor ) => {
110
+ inputEditor = editor
111
+ inputEditor .onDidChangeCursorPosition (
112
+ debounce (e => {
113
+ clearInputDecos ()
114
+ if (lastSuccessfulMap ) {
115
+ const pos = lastSuccessfulMap .generatedPositionFor ({
116
+ source: ' message.intl' ,
117
+ line: e .position .lineNumber ,
118
+ column: e .position .column
119
+ })
120
+ if (pos .line != null && pos .column != null ) {
121
+ prevOutputDecos = outputEditor ! .deltaDecorations (
122
+ prevOutputDecos ,
123
+ [
124
+ {
125
+ range: new monaco .Range (
126
+ pos .line ,
127
+ pos .column + 1 ,
128
+ pos .line ,
129
+ pos .lastColumn ? pos .lastColumn + 2 : pos .column + 2
130
+ ),
131
+ options: {
132
+ inlineClassName: ` highlight `
133
+ }
134
+ }
135
+ ]
136
+ )
137
+ outputEditor ! .revealPositionInCenter ({
138
+ lineNumber: pos .line ,
139
+ column: pos .column + 1
140
+ })
141
+ } else {
142
+ clearOutputDecos ()
143
+ }
144
+ }
145
+ }, 100 )
146
+ )
147
+ }
148
+
149
+ // output editor ready event
150
+ const onReadyOutput = (editor : monaco .editor .IStandaloneCodeEditor ) => {
151
+ outputEditor = editor
152
+ editor .onDidChangeCursorPosition (
153
+ debounce (e => {
154
+ clearOutputDecos ()
155
+ if (lastSuccessfulMap ) {
156
+ const pos = lastSuccessfulMap .originalPositionFor ({
157
+ line: e .position .lineNumber ,
158
+ column: e .position .column
159
+ })
160
+ console .log (' onReadyOutput' , e .position , pos )
161
+ if (
162
+ pos .line != null &&
163
+ pos .column != null &&
164
+ ! (
165
+ // ignore mock location
166
+ (pos .line === 1 && pos .column === 0 )
167
+ )
168
+ ) {
169
+ const translatedPos = {
170
+ column: pos .column + 1 ,
171
+ lineNumber: pos .line
172
+ }
173
+ prevInputDecos = inputEditor ! .deltaDecorations (prevInputDecos , [
174
+ {
175
+ range: new monaco .Range (
176
+ pos .line ,
177
+ pos .column + 1 ,
178
+ pos .line ,
179
+ pos .column + 1
180
+ ),
181
+ options: {
182
+ isWholeLine: true ,
183
+ className: ` highlight `
184
+ }
185
+ }
186
+ ])
187
+ inputEditor ! .revealPositionInCenter (translatedPos )
188
+ } else {
189
+ clearInputDecos ()
190
+ }
191
+ }
192
+ }, 100 )
193
+ )
75
194
}
76
195
77
196
// setup context
78
197
return {
79
198
initialCodes ,
80
199
genCodes ,
81
200
compileErrors ,
82
- onChange
201
+ onChangeModel ,
202
+ onReadyInput ,
203
+ onReadyOutput
83
204
}
84
205
}
85
206
})
86
207
</script >
87
208
88
209
<template >
89
210
<div class =" container" >
90
- <div class =" navigation" >
211
+ <nav class =" navigation" >
212
+ <!-- prettier-ignore -->
91
213
<Navigation class =" navigation" />
92
- </div >
214
+ </nav >
93
215
<div class =" operation" >
94
216
<Editor
95
217
class =" input"
96
218
language =" intlify"
97
219
:code =" initialCodes"
98
220
:errors =" compileErrors"
99
221
:debounce =" true"
100
- @change =" onChange"
222
+ @change-model =" onChangeModel"
223
+ @ready =" onReadyInput"
101
224
/>
102
225
<!-- prettier-ignore -->
103
226
<Editor
104
227
class =" output"
105
228
:code =" genCodes"
229
+ @ready =" onReadyOutput"
106
230
/>
107
231
</div >
108
232
</div >
@@ -119,6 +243,7 @@ export default defineComponent({
119
243
box-sizing : border-box ;
120
244
background-color : var (--bg );
121
245
border-bottom : 1px solid var (--border );
246
+ display : contents ;
122
247
}
123
248
.operation {
124
249
width : 100% ;
0 commit comments