@@ -5,6 +5,10 @@ import {format_from_js_file} from '@influxdata/flux-lsp-browser'
55import { EditorType , Variable } from 'src/types'
66import { buildUsedVarsOption } from 'src/variables/utils/buildVarsOption'
77
8+ // handling schema composition
9+ import { RecursivePartial } from 'src/types'
10+ import { SchemaSelection } from 'src/dataExplorer/context/persistance'
11+
812// LSP methods
913import {
1014 didOpen ,
@@ -18,11 +22,24 @@ import {
1822// error reporting
1923import { reportErrorThroughHoneyBadger } from 'src/shared/utils/errors'
2024
25+ const ICON_SYNC_CLASSNAME = 'composition-sync'
26+ export const ICON_SYNC_ID = 'schema-composition-sync-icon'
27+
28+ // hardcoded in LSP
29+ const COMPOSITION_YIELD = '_editor_composition'
30+ const COMPOSITION_INIT_LINE = 1
31+
2132class LspConnectionManager {
2233 private _worker : Worker
34+ private _editor : EditorType
2335 private _model : MonacoTypes . editor . IModel
2436 private _preludeModel : MonacoTypes . editor . IModel
2537 private _variables : Variable [ ] = [ ]
38+ private _compositionStyle : string [ ] = [ ]
39+ private _session : SchemaSelection
40+ private _callbackSetSession : (
41+ schema : RecursivePartial < SchemaSelection >
42+ ) => void = ( ) => null
2643
2744 constructor ( worker : Worker ) {
2845 this . _worker = worker
@@ -56,6 +73,7 @@ class LspConnectionManager {
5673 }
5774
5875 subscribeToModel ( editor : EditorType ) {
76+ this . _editor = editor
5977 this . _model = editor . getModel ( )
6078
6179 this . _model . onDidChangeContent ( ( ) => this . updatePreludeModel ( ) )
@@ -91,6 +109,210 @@ class LspConnectionManager {
91109 this . _worker . postMessage ( msg )
92110 }
93111
112+ _getCompositionBlockLines ( ) {
113+ const query = this . _model . getValue ( )
114+ const startLine = COMPOSITION_INIT_LINE
115+ const endLine =
116+ ( query . split ( '\n' ) . findIndex ( line => line . includes ( COMPOSITION_YIELD ) ) ||
117+ 0 ) + 1
118+ return { startLine, endLine}
119+ }
120+
121+ _setSessionSync ( synced : boolean ) {
122+ this . _callbackSetSession ( {
123+ composition : { synced} ,
124+ } )
125+ }
126+
127+ _setEditorSyncToggle ( ) {
128+ setTimeout ( ( ) => {
129+ // elements in monaco-editor. positioned by editor.
130+ const syncIcons = document . getElementsByClassName ( ICON_SYNC_CLASSNAME )
131+
132+ // UI elements we control
133+ const clickableInvisibleDiv = document . getElementById ( ICON_SYNC_ID )
134+ if ( ! syncIcons . length || ! clickableInvisibleDiv ) {
135+ return
136+ }
137+
138+ const [ upperIcon ] = syncIcons
139+ let [ , lowerIcon ] = syncIcons
140+ if ( ! lowerIcon ) {
141+ lowerIcon = upperIcon
142+ }
143+ const { startLine, endLine} = this . _getCompositionBlockLines ( )
144+
145+ // move div to match monaco-editor coordinates
146+ clickableInvisibleDiv . style . top =
147+ ( ( upperIcon as any ) . offsetTop || 0 ) + 'px'
148+ const height =
149+ ( ( lowerIcon as any ) . offsetHeight || 0 ) * ( endLine - startLine + 1 ) +
150+ ( ( upperIcon as any ) . offsetTop || 0 )
151+ clickableInvisibleDiv . style . height = height + 'px'
152+ clickableInvisibleDiv . style . width =
153+ ( ( upperIcon as any ) . offsetWidth || 0 ) + 'px'
154+
155+ // add listeners
156+ clickableInvisibleDiv . removeEventListener ( 'click' , ( ) =>
157+ this . _setSessionSync ( ! this . _session . composition . synced )
158+ ) // may have existing
159+ clickableInvisibleDiv . addEventListener ( 'click' , ( ) =>
160+ this . _setSessionSync ( ! this . _session . composition . synced )
161+ )
162+ } , 1000 )
163+ }
164+
165+ _editorChangeIsFromLsp ( change ) {
166+ return ! ! change . forceMoveMarkers
167+ }
168+
169+ _setEditorIrreversibleExit ( ) {
170+ this . _model . onDidChangeContent ( e => {
171+ const { changes} = e
172+ changes . some ( change => {
173+ const { startLine, endLine} = this . _getCompositionBlockLines ( )
174+ if (
175+ change . range . startLineNumber >= startLine &&
176+ change . range . endLineNumber <= endLine &&
177+ ! this . _editorChangeIsFromLsp ( change ) &&
178+ ! this . _session . composition . diverged
179+ ) {
180+ this . _callbackSetSession ( {
181+ composition : { synced : false , diverged : true } ,
182+ } )
183+ return
184+ }
185+ } )
186+ } )
187+ }
188+
189+ _setEditorBlockStyle ( ) {
190+ const { startLine, endLine} = this . _getCompositionBlockLines ( )
191+
192+ const startLineStyle = [
193+ {
194+ range : new MonacoTypes . Range ( startLine , 1 , startLine , 1 ) ,
195+ options : {
196+ linesDecorationsClassName : ICON_SYNC_CLASSNAME ,
197+ } ,
198+ } ,
199+ ]
200+ const endLineStyle = [
201+ {
202+ range : new MonacoTypes . Range ( endLine , 1 , endLine , 1 ) ,
203+ options : {
204+ linesDecorationsClassName : ICON_SYNC_CLASSNAME ,
205+ } ,
206+ } ,
207+ ]
208+
209+ const removeAllStyles = this . _session . composition . diverged
210+
211+ this . _compositionStyle = this . _editor . deltaDecorations (
212+ this . _compositionStyle ,
213+ removeAllStyles ? [ ] : startLineStyle . concat ( endLineStyle )
214+ )
215+
216+ const clickableInvisibleDiv = document . getElementById ( ICON_SYNC_ID )
217+ clickableInvisibleDiv . style . background = this . _session . composition . synced
218+ ? 'blue'
219+ : 'grey'
220+
221+ if ( removeAllStyles ) {
222+ clickableInvisibleDiv . style . display = 'none'
223+ }
224+ }
225+
226+ _initLsp ( schema : SchemaSelection ) {
227+ const { bucket, measurement} = schema
228+ const payload = { bucket : bucket ?. name }
229+ if ( measurement ) {
230+ payload [ 'measurement' ] = measurement
231+ }
232+ // TODO: finish LSP update first
233+ // this.inject(ExecuteCommand.CompositionInit, payload)
234+ }
235+
236+ _updateLsp ( _ : SchemaSelection ) {
237+ // TODO: finish LSP update first
238+ // this.inject(ExecuteCommand.Composition<Something>, payload)
239+ }
240+
241+ _initComposition ( schema : SchemaSelection ) {
242+ if ( ! schema . composition . synced ) {
243+ this . _setSessionSync ( true )
244+ }
245+
246+ this . _initLsp ( schema )
247+
248+ // handlers to trigger end composition
249+ this . _setEditorSyncToggle ( )
250+ this . _setEditorIrreversibleExit ( )
251+
252+ // handlers for composition block size
253+ // eventually, this could be from the LSP response. onLspMessage()
254+ this . _model . onDidChangeContent (
255+ ( ) => this . _session . composition ?. synced && this . _setEditorBlockStyle ( )
256+ )
257+
258+ // TODO: for now, set style on init. Eventually use this.onLspMessage()
259+ this . _setEditorBlockStyle ( )
260+ }
261+
262+ _restoreComposition ( schema : SchemaSelection ) {
263+ this . _initLsp ( schema )
264+ this . _updateLsp ( schema )
265+ }
266+
267+ onSchemaSessionChange ( schema : SchemaSelection , sessionCb ) {
268+ this . _callbackSetSession = sessionCb
269+ const previousState = {
270+ ...this . _session ,
271+ composition : { ...( this . _session ?. composition || { } ) } ,
272+ }
273+ this . _session = { ...schema , composition : { ...schema . composition } }
274+
275+ if ( ! schema . composition ) {
276+ // FIXME: message to user, to create a new script
277+ console . error (
278+ 'User has an old session, which does not support schema composition.'
279+ )
280+ return
281+ }
282+
283+ if ( ! previousState . bucket && schema . bucket ) {
284+ // TODO: if also already have fields and tagValues, then _restoreComposition()
285+ const hasFieldsOrTagvalues = false
286+ if ( hasFieldsOrTagvalues ) {
287+ return this . _restoreComposition ( schema )
288+ }
289+ return this . _initComposition ( schema )
290+ }
291+
292+ // TODO: decide on tag and tagValues.
293+ // Inject on same or different lines? then...how model in session?
294+ const tagsDidUpdate = false
295+
296+ if (
297+ previousState . fields ?. length != schema . fields ?. length ||
298+ tagsDidUpdate
299+ ) {
300+ return this . _updateLsp ( schema )
301+ }
302+
303+ if ( previousState . composition != schema . composition ) {
304+ return this . _setEditorBlockStyle ( )
305+ }
306+ }
307+
308+ onLspMessage ( _jsonrpcMiddlewareResponse : unknown ) {
309+ // TODO: Q4
310+ // 1. middleware detects jsonrpc
311+ // 2. call this method
312+ // 3a. update (true-up) session store
313+ // 3b. this._setEditorBlockStyle()
314+ }
315+
94316 dispose ( ) {
95317 this . _model . onDidChangeContent ( null )
96318 }
0 commit comments