1
- import { isVueFile , parseFileName } from '@vuedx/shared'
1
+ import { first , isNotNull , parseFileName } from '@vuedx/shared'
2
+ import { TextSpan } from '@vuedx/vue-virtual-textdocument'
2
3
import { inject , injectable } from 'inversify'
3
- import vscode from 'vscode'
4
+ import vscode , { TextEditor } from 'vscode'
4
5
import { PluginCommunicationService } from '../services/PluginCommunicationService'
5
6
import { Installable } from '../utils/installable'
6
7
import { getVirtualFileNameFromUri } from '../utils/uri'
@@ -18,17 +19,20 @@ export class VueVirtualDocumentProvider
18
19
}
19
20
20
21
private readonly openVueFiles = new Map < string , Set < string > > ( )
22
+ private readonly decoration = vscode . window . createTextEditorDecorationType ( {
23
+ outline : 'solid red 1px' ,
24
+ } )
25
+
26
+ private editors : readonly TextEditor [ ] = [ ]
21
27
22
28
public install ( ) : vscode . Disposable {
23
29
super . install ( )
24
30
25
- let selectionWatcher : vscode . Disposable | undefined
26
- let cancellationToken : vscode . CancellationTokenSource | undefined
27
-
28
31
return vscode . Disposable . from (
29
32
vscode . workspace . registerTextDocumentContentProvider ( 'vue' , this ) ,
30
- vscode . workspace . onDidChangeTextDocument ( ( { document } ) => {
33
+ vscode . workspace . onDidChangeTextDocument ( async ( { document } ) => {
31
34
if ( document . languageId === 'vue' ) {
35
+ await delay ( 100 )
32
36
this . openVueFiles . get ( document . fileName ) ?. forEach ( ( uri ) => {
33
37
this . onDidChangeEmitter . fire ( vscode . Uri . parse ( uri ) )
34
38
} )
@@ -56,72 +60,103 @@ export class VueVirtualDocumentProvider
56
60
if ( openFiles . size === 0 ) this . openVueFiles . delete ( parsed . fileName )
57
61
}
58
62
} ) ,
59
- vscode . window . onDidChangeActiveTextEditor ( ( editor ) => {
60
- selectionWatcher ?. dispose ( )
61
- if ( editor == null || ! isVueFile ( editor . document . fileName ) ) return
62
- const fileName = editor . document . fileName
63
-
64
- selectionWatcher = vscode . window . onDidChangeTextEditorSelection (
65
- async ( { textEditor, selections } ) => {
66
- if ( textEditor !== editor ) return // ignore others
67
- if ( selections . length !== 1 ) return
68
- if (
69
- ! vscode . window . visibleTextEditors . some (
70
- ( editor ) =>
71
- parseFileName ( editor . document . fileName ) . fileName === fileName ,
72
- )
73
- ) {
74
- return // No active virtual document
75
- }
76
-
77
- cancellationToken ?. cancel ( )
78
- const current = new vscode . CancellationTokenSource ( )
79
- cancellationToken = current
80
-
81
- const start = textEditor . document . offsetAt ( editor . selection . start )
82
- const end = textEditor . document . offsetAt ( editor . selection . end )
83
- const result = await this . plugin . first ( async ( _conneciton ) => {
84
- console . log ( start , end )
85
- return null as null | {
86
- fileName : string
87
- start : number
88
- end : number
89
- } // TODO: fix this
90
- } )
91
-
92
- // not found or cancelled
93
- if ( result == null || current . token . isCancellationRequested ) return
94
-
95
- const virtualEditor = vscode . window . visibleTextEditors . find (
96
- ( editor ) => editor . document . fileName === result . fileName ,
63
+ vscode . window . onDidChangeActiveTextEditor ( ( ) => {
64
+ this . resetAllDecorations ( )
65
+ } ) ,
66
+ vscode . window . onDidChangeVisibleTextEditors ( ( editors ) => {
67
+ this . editors = editors
68
+ } ) ,
69
+ vscode . window . onDidChangeTextEditorSelection (
70
+ async ( { textEditor, selections } ) => {
71
+ if ( textEditor !== vscode . window . activeTextEditor ) return
72
+ if ( textEditor . document . languageId === 'vue' ) {
73
+ const fileName = textEditor . document . fileName
74
+ const editor = this . editors . find (
75
+ ( editor ) =>
76
+ editor . document . uri . scheme === 'vue' &&
77
+ editor . document . fileName . startsWith ( fileName ) ,
97
78
)
98
79
99
- if ( virtualEditor == null ) return // not active
100
-
101
- const range = new vscode . Range (
102
- virtualEditor . document . positionAt ( result . start ) ,
103
- virtualEditor . document . positionAt ( result . end ) ,
80
+ if ( editor == null ) return
81
+ const textSpans = await Promise . all (
82
+ selections . map (
83
+ async ( selection ) =>
84
+ await this . plugin . first ( async ( connection ) => {
85
+ const start = textEditor . document . offsetAt ( selection . start )
86
+ const end = textEditor . document . offsetAt ( selection . end )
87
+
88
+ return await connection . findGeneratedTextSpan ( fileName , {
89
+ start : Math . min ( start , end ) ,
90
+ length : Math . abs ( end - start ) ,
91
+ } )
92
+ } ) ,
93
+ ) ,
104
94
)
105
-
106
- virtualEditor . options . cursorStyle =
107
- vscode . TextEditorCursorStyle . Underline
108
- virtualEditor . selection = new vscode . Selection (
109
- range . start ,
110
- range . end ,
95
+ this . setDecorations ( textSpans , editor )
96
+ } else if ( textEditor . document . uri . scheme === 'vue' ) {
97
+ const fileName = textEditor . document . fileName . replace (
98
+ / \. [ t j ] s x $ / ,
99
+ '' ,
111
100
)
112
- virtualEditor . revealRange (
113
- range ,
114
- vscode . TextEditorRevealType . Default ,
101
+ const editor = this . editors . find ( ( editor ) =>
102
+ editor . document . fileName . startsWith ( fileName ) ,
115
103
)
116
- } ,
117
- )
118
- } ) ,
104
+
105
+ if ( editor == null ) return
106
+ const textSpans = await Promise . all (
107
+ selections . map (
108
+ async ( selection ) =>
109
+ await this . plugin . first ( async ( connection ) => {
110
+ const start = textEditor . document . offsetAt ( selection . start )
111
+ const end = textEditor . document . offsetAt ( selection . end )
112
+
113
+ return await connection . findOriginalTextSpan ( fileName , {
114
+ start : Math . min ( start , end ) ,
115
+ length : Math . abs ( end - start ) ,
116
+ } )
117
+ } ) ,
118
+ ) ,
119
+ )
120
+ this . setDecorations ( textSpans , editor )
121
+ }
122
+ } ,
123
+ ) ,
119
124
)
120
125
}
121
126
122
127
private readonly onDidChangeEmitter = new vscode . EventEmitter < vscode . Uri > ( )
123
128
public onDidChange = this . onDidChangeEmitter . event
124
129
130
+ private resetAllDecorations ( ) : void {
131
+ this . editors . forEach ( ( editor ) => {
132
+ editor . setDecorations ( this . decoration , [ ] )
133
+ } )
134
+ }
135
+
136
+ private setDecorations (
137
+ textSpans : Array < TextSpan | null | undefined > ,
138
+ editor : vscode . TextEditor ,
139
+ ) : void {
140
+ const ranges = textSpans
141
+ . filter ( isNotNull )
142
+ . map (
143
+ ( range ) =>
144
+ new vscode . Range (
145
+ editor . document . positionAt ( range . start ) ,
146
+ editor . document . positionAt ( range . start + range . length ) ,
147
+ ) ,
148
+ )
149
+
150
+ editor . setDecorations ( this . decoration , [ ] )
151
+ editor . setDecorations ( this . decoration , ranges )
152
+ if ( ranges . length > 0 ) {
153
+ editor . revealRange (
154
+ first ( ranges ) ,
155
+ vscode . TextEditorRevealType . InCenterIfOutsideViewport ,
156
+ )
157
+ }
158
+ }
159
+
125
160
async provideTextDocumentContent (
126
161
request : vscode . Uri ,
127
162
) : Promise < string | undefined > {
0 commit comments