@@ -7,23 +7,20 @@ import BufferSync from '../model/bufferSync'
7
7
import { StatusBarItem } from '../model/status'
8
8
import { UltiSnippetOption } from '../types'
9
9
import { defaultValue , disposeAll } from '../util'
10
- import { Mutex } from '../util/mutex'
11
10
import { deepClone } from '../util/object'
12
11
import { emptyRange , toValidRange } from '../util/position'
13
12
import { Disposable } from '../util/protocol'
14
- import { getPositionFromEdits } from '../util/textedit'
15
13
import window from '../window'
16
14
import workspace from '../workspace'
17
15
import { executePythonCode , generateContextId , getInitialPythonCode , hasPython } from './eval'
18
16
import { SnippetConfig , SnippetSession } from './session'
19
17
import { SnippetString } from './string'
20
- import { getAction , normalizeSnippetString , reduceTextEdit , shouldFormat , SnippetFormatOptions , UltiSnippetContext } from './util'
18
+ import { getAction , normalizeSnippetString , shouldFormat , SnippetFormatOptions , UltiSnippetContext } from './util'
21
19
22
20
export class SnippetManager {
23
21
private disposables : Disposable [ ] = [ ]
24
22
private _statusItem : StatusBarItem
25
23
private bufferSync : BufferSync < SnippetSession >
26
- private mutex : Mutex = new Mutex ( )
27
24
private config : SnippetConfig
28
25
29
26
public init ( ) {
@@ -112,120 +109,84 @@ export class SnippetManager {
112
109
* Insert snippet to specific buffer, ultisnips not supported, and the placeholder is not selected
113
110
*/
114
111
public async insertBufferSnippet ( bufnr : number , snippet : string | SnippetString , range : Range , insertTextMode ?: InsertTextMode ) : Promise < boolean > {
115
- let release = await this . mutex . acquire ( )
116
- try {
117
- let document = workspace . getAttachedDocument ( bufnr )
118
- const session = this . bufferSync . getItem ( bufnr )
119
- range = await this . synchronizeSession ( session , range )
120
- range = toValidRange ( range )
121
- const currentLine = document . getline ( range . start . line )
122
- const snippetStr = SnippetString . isSnippetString ( snippet ) ? snippet . value : snippet
123
- const inserted = await this . normalizeInsertText ( document . bufnr , snippetStr , currentLine , insertTextMode )
124
- let isActive = await session . start ( inserted , range , false )
125
- release ( )
126
- return isActive
127
- } catch ( e ) {
128
- release ( )
129
- throw e
130
- }
131
- }
132
-
133
- /**
134
- * Synchronize session when needed (ex: snippet insert during TextChange),
135
- * the range could be changed
136
- */
137
- public async synchronizeSession ( session : SnippetSession , range : Range ) : Promise < Range > {
138
- let { document, isActive } = session
139
- if ( ! isActive ) return range
140
- let disposable = document . onDocumentChange ( e => {
141
- let changes = e . contentChanges
142
- let { start, end } = range
143
- changes . forEach ( change => {
144
- let edit = reduceTextEdit ( TextEdit . replace ( change . range , change . text ) , e . original )
145
- start = getPositionFromEdits ( start , [ edit ] )
146
- end = getPositionFromEdits ( end , [ edit ] )
147
- } )
148
- range = Range . create ( start , end )
149
- } )
150
- await session . forceSynchronize ( )
151
- disposable . dispose ( )
152
- return range
112
+ let document = workspace . getAttachedDocument ( bufnr )
113
+ const session = this . bufferSync . getItem ( bufnr )
114
+ session . cancel ( )
115
+ range = toValidRange ( range )
116
+ const line = document . getline ( range . start . line )
117
+ const snippetStr = SnippetString . isSnippetString ( snippet ) ? snippet . value : snippet
118
+ const inserted = await this . normalizeInsertText ( document . bufnr , snippetStr , line , insertTextMode )
119
+ return await session . start ( inserted , range , false )
153
120
}
154
121
155
122
/**
156
123
* Insert snippet at current cursor position
157
124
*/
158
125
public async insertSnippet ( snippet : string | SnippetString , select = true , range ?: Range , insertTextMode ?: InsertTextMode , ultisnip ?: UltiSnippetOption ) : Promise < boolean > {
159
126
let { nvim } = workspace
160
- let release = await this . mutex . acquire ( )
161
- try {
162
- let document = workspace . getAttachedDocument ( workspace . bufnr )
163
- const session = this . bufferSync . getItem ( document . bufnr )
164
- let context : UltiSnippetContext
165
- if ( range ) await this . synchronizeSession ( session , range )
166
- range = await this . toRange ( range )
167
- const currentLine = document . getline ( range . start . line )
168
- const snippetStr = SnippetString . isSnippetString ( snippet ) ? snippet . value : snippet
169
- const inserted = await this . normalizeInsertText ( document . bufnr , snippetStr , currentLine , insertTextMode , ultisnip )
170
- let usePy = false
171
- if ( ultisnip != null ) {
172
- usePy = hasPython ( ultisnip ) || inserted . includes ( '`!p' )
173
- const bufnr = document . bufnr
174
- context = Object . assign ( { range : deepClone ( range ) , line : currentLine } , ultisnip , { id : generateContextId ( bufnr ) } )
175
- if ( usePy ) {
176
- if ( session . placeholder ) {
177
- let { start, end } = session . placeholder . range
178
- let last = {
179
- current_text : session . placeholder . value ,
180
- start : { line : start . line , col : start . character , character : start . character } ,
181
- end : { line : end . line , col : end . character , character : end . character }
182
- }
183
- this . nvim . setVar ( 'coc_last_placeholder' , last , true )
184
- } else {
185
- this . nvim . call ( 'coc#compat#del_var' , [ 'coc_last_placeholder' ] , true )
186
- }
187
- const codes = getInitialPythonCode ( context )
188
- let preExpand = getAction ( ultisnip , 'preExpand' )
189
- if ( preExpand ) {
190
- await executePythonCode ( nvim , codes . concat ( [ 'snip = coc_ultisnips_dict["PreExpandContext"]()' , preExpand ] ) )
191
- const [ valid , pos ] = await nvim . call ( 'pyxeval' , 'snip.getResult()' ) as [ boolean , [ number , number ] ]
192
- // need remove the trigger
193
- if ( valid ) {
194
- let count = range . end . character - range . start . character
195
- let end = Position . create ( pos [ 0 ] , pos [ 1 ] )
196
- let start = Position . create ( pos [ 0 ] , Math . max ( 0 , pos [ 1 ] - count ) )
197
- range = Range . create ( start , end )
198
- } else {
199
- // trigger removed already
200
- let start = Position . create ( pos [ 0 ] , pos [ 1 ] )
201
- range = Range . create ( start , deepClone ( start ) )
202
- }
203
- } else {
204
- await executePythonCode ( nvim , codes )
127
+ let document = workspace . getAttachedDocument ( workspace . bufnr )
128
+ const session = this . bufferSync . getItem ( document . bufnr )
129
+ let context : UltiSnippetContext
130
+ session . cancel ( )
131
+ range = await this . toRange ( range )
132
+ const currentLine = document . getline ( range . start . line )
133
+ const snippetStr = SnippetString . isSnippetString ( snippet ) ? snippet . value : snippet
134
+ const inserted = await this . normalizeInsertText ( document . bufnr , snippetStr , currentLine , insertTextMode , ultisnip )
135
+ let usePy = false
136
+ if ( ultisnip != null ) {
137
+ usePy = hasPython ( ultisnip ) || inserted . includes ( '`!p' )
138
+ const bufnr = document . bufnr
139
+ context = Object . assign ( { range : deepClone ( range ) , line : currentLine } , ultisnip , { id : generateContextId ( bufnr ) } )
140
+ if ( usePy ) {
141
+ if ( session . placeholder ) {
142
+ let { start, end } = session . placeholder . range
143
+ let last = {
144
+ current_text : session . placeholder . value ,
145
+ start : { line : start . line , col : start . character , character : start . character } ,
146
+ end : { line : end . line , col : end . character , character : end . character }
205
147
}
148
+ this . nvim . setVar ( 'coc_last_placeholder' , last , true )
149
+ } else {
150
+ this . nvim . call ( 'coc#compat#del_var' , [ 'coc_last_placeholder' ] , true )
206
151
}
207
- // same behavior as Ultisnips
208
- const { start } = range
209
- this . nvim . call ( 'coc#cursor#move_to' , [ start . line , start . character ] , true )
210
- if ( ! emptyRange ( range ) ) {
211
- await document . applyEdits ( [ TextEdit . del ( range ) ] )
212
- if ( session . isActive ) {
213
- await session . synchronize ( )
214
- // the cursor position may changed on session synchronize.
215
- let pos = await window . getCursorPosition ( )
216
- range = Range . create ( pos , pos )
152
+ const codes = getInitialPythonCode ( context )
153
+ let preExpand = getAction ( ultisnip , 'preExpand' )
154
+ if ( preExpand ) {
155
+ await executePythonCode ( nvim , codes . concat ( [ 'snip = coc_ultisnips_dict["PreExpandContext"]()' , preExpand ] ) )
156
+ const [ valid , pos ] = await nvim . call ( 'pyxeval' , 'snip.getResult()' ) as [ boolean , [ number , number ] ]
157
+ // need remove the trigger
158
+ if ( valid ) {
159
+ let count = range . end . character - range . start . character
160
+ let end = Position . create ( pos [ 0 ] , pos [ 1 ] )
161
+ let start = Position . create ( pos [ 0 ] , Math . max ( 0 , pos [ 1 ] - count ) )
162
+ range = Range . create ( start , end )
217
163
} else {
218
- range . end = Position . create ( start . line , start . character )
164
+ // trigger removed already
165
+ let start = Position . create ( pos [ 0 ] , pos [ 1 ] )
166
+ range = Range . create ( start , deepClone ( start ) )
219
167
}
168
+ } else {
169
+ await executePythonCode ( nvim , codes )
220
170
}
221
171
}
222
- await session . start ( inserted , range , select , context )
223
- release ( )
224
- return session . isActive
225
- } catch ( e ) {
226
- release ( )
227
- throw e
228
172
}
173
+ // same behavior as Ultisnips
174
+ // range could outside snippet range when session synchronize is canceled
175
+ const { start } = range
176
+ this . nvim . call ( 'coc#cursor#move_to' , [ start . line , start . character ] , true )
177
+ if ( ! emptyRange ( range ) ) {
178
+ await document . applyEdits ( [ TextEdit . del ( range ) ] )
179
+ if ( session . isActive ) {
180
+ await session . synchronize ( )
181
+ // the cursor position may changed on session synchronize.
182
+ let pos = await window . getCursorPosition ( )
183
+ range = Range . create ( pos , pos )
184
+ } else {
185
+ range . end = Position . create ( start . line , start . character )
186
+ }
187
+ }
188
+ await session . start ( inserted , range , select , context )
189
+ return session . isActive
229
190
}
230
191
231
192
public async selectCurrentPlaceholder ( triggerAutocmd = true ) : Promise < void > {
@@ -286,18 +247,10 @@ export class SnippetManager {
286
247
/**
287
248
* Exposed for snippet preview
288
249
*/
289
- public async resolveSnippet ( snippetString : string , ultisnip ?: UltiSnippetOption ) : Promise < string > {
250
+ public async resolveSnippet ( snippetString : string , ultisnip ?: UltiSnippetOption ) : Promise < string | undefined > {
290
251
let session = this . bufferSync . getItem ( workspace . bufnr )
291
252
if ( ! session ) return
292
- let release = await this . mutex . acquire ( )
293
- try {
294
- let res = await session . resolveSnippet ( this . nvim , snippetString , ultisnip )
295
- release ( )
296
- return res
297
- } catch ( e ) {
298
- release ( )
299
- throw e
300
- }
253
+ return await session . resolveSnippet ( this . nvim , snippetString , ultisnip )
301
254
}
302
255
303
256
public async normalizeInsertText ( bufnr : number , snippetString : string , currentLine : string , insertTextMode : InsertTextMode , ultisnip ?: Partial < UltiSnippetOption > ) : Promise < string > {
@@ -306,7 +259,12 @@ export class SnippetManager {
306
259
inserted = snippetString
307
260
} else {
308
261
const currentIndent = currentLine . match ( / ^ \s * / ) [ 0 ]
309
- const formatOptions = window . activeTextEditor ? window . activeTextEditor . options : await workspace . getFormatOptions ( bufnr ) as SnippetFormatOptions
262
+ let formatOptions : SnippetFormatOptions
263
+ if ( bufnr == window . activeTextEditor ?. bufnr ) {
264
+ formatOptions = window . activeTextEditor . options
265
+ } else {
266
+ formatOptions = await workspace . getFormatOptions ( bufnr ) as SnippetFormatOptions
267
+ }
310
268
let opts : Partial < UltiSnippetOption > = ultisnip ?? { }
311
269
// trim when option not exists
312
270
formatOptions . trimTrailingWhitespace = opts . trimTrailingWhitespace !== false
0 commit comments