any-jump.vim

Форк
0
/
internal_buffer.vim 
690 строк · 18.2 Кб
1
" ----------------------------------------------
2
" Internal buffer prototype definition
3
" ----------------------------------------------
4
" represents ui internal structure
5

6
" abstract structure of internal buffer representation:
7
"
8
" buffer = [ array of lines ]
9
"
10
" line = [ list of items
11
"   { type, strat_col, finish_col, text, hl_group },
12
"   { ... },
13
"   ...
14
" ]
15

16
let s:nvim = has('nvim')
17

18
let s:InternalBuffer = {}
19

20
let s:InternalBuffer.MethodsList = [
21
      \'RenderLine',
22
      \'AddLine',
23
      \'AddLineAt',
24
      \'CreateItem',
25
      \'len',
26
      \'GetItemByPos',
27
      \'GetItemLineNumber',
28
      \'GetItemLineNumberByData',
29
      \'GetFirstItemOfType',
30
      \'TryFindOriginalLinkFromPos',
31
      \'TryRestoreCursorForItem',
32
      \'RenderUiUsagesList',
33
      \'RenderUi',
34
      \'HelpSection',
35
      \'StartUiTransaction',
36
      \'EndUiTransaction',
37
      \'GrepResultToItems',
38
      \'GrepResultToGroupedItems',
39
      \'RemoveGarbagedLines',
40
      \'JumpToFirstOfType',
41
      \'ClearBuffer',
42
      \'BufferLnum',
43
      \'RestorePopupCursor',
44
      \]
45

46
" Produce new Render Buffer
47
fu! s:InternalBuffer.New() abort
48
  let object = {
49
        \"items":                    [],
50
        \"current_page":             0,
51
        \"gc":                       v:false,
52
        \"preview_opened":           v:false,
53
        \"usages_opened":            v:false,
54
        \"grouping_enabled":         v:false,
55
        \"overmaxed_results_hidden": v:true,
56
        \"definitions_grep_results": [],
57
        \"usages_grep_results":      [],
58
        \"vim_bufnr":                0,
59
        \"popup_winid":              0,
60
        \"previous_bufnr":           0,
61
        \}
62

63
  for method in self.MethodsList
64
    let object[method] = s:InternalBuffer[method]
65
  endfor
66

67
  return object
68
endfu
69

70
fu! s:InternalBuffer.len() dict abort
71
  return len(self.items)
72
endfu
73

74
fu! s:InternalBuffer.RenderLine(items, line) dict abort
75
  let text           = s:nvim ? " " : ""
76
  let idx            = 0
77
  let next_start_col = 1
78

79
  " calculate & assign items start & end columns
80
  for item in a:items
81
    " separate items in line with 1 space
82
    if idx == 0
83
      let text = text . item.text
84
    else
85
      if has_key(item.data, 'no_padding')
86
        let text = text . item.text
87
      else
88
        let text = text . ' ' . item.text
89
        let next_start_col = next_start_col + 1
90
      endif
91
    endif
92

93
    let item.start_col = next_start_col
94
    let item.end_col   = next_start_col + len(item.text)
95

96
    let next_start_col  = item.end_col
97
    let idx            += 1
98
  endfor
99

100
  " filter out empty whitespaces
101
  if text =~ '^\s\+$'
102
    let text = ''
103
  endif
104

105
  " write final text to buffer
106
  call appendbufline(self.vim_bufnr, a:line - 1, text)
107

108
  " colorize
109
  for item in a:items
110
    if s:nvim
111
      call nvim_buf_add_highlight(
112
            \self.vim_bufnr,
113
            \-1,
114
            \item.hl_group,
115
            \a:line - 1,
116
            \item.start_col,
117
            \item.end_col)
118
    else
119
      call prop_add(a:line, item.start_col, {
120
            \'length': item.len,
121
            \'type': item.hl_group,
122
            \'bufnr': self.vim_bufnr})
123
    endif
124
  endfor
125
endfu
126

127
fu! s:InternalBuffer.AddLine(items) dict abort
128
  if type(a:items) == v:t_list
129
    call self.RenderLine(a:items, self.len() + 1)
130
    call add(self.items, a:items)
131

132
    return v:true
133
  else
134
    echoe "array required, got invalid type: " . string(a:items)
135

136
    return v:false
137
  endif
138
endfu
139

140
fu! s:InternalBuffer.AddLineAt(items, line_number) dict abort
141
  if type(a:items) == v:t_list
142
    call self.RenderLine(a:items, a:line_number)
143
    call insert(self.items, a:items, a:line_number - 1)
144

145
    return v:true
146
  else
147
    echoe "array required, got invalid type: " . string(a:items)
148

149
    return v:false
150
  endif
151
endfu
152

153
" type:
154
"   'text' / 'link' / 'button' / 'preview_text'
155
fu! s:InternalBuffer.CreateItem(type, text, hl_group, ...) dict abort
156
  let data = {}
157

158
  if a:0
159
    let data = a:1
160
  endif
161

162
  let item = {
163
        \"type":      a:type,
164
        \"text":      a:text,
165
        \"len":       len(a:text),
166
        \"start_col": 0,
167
        \"end_col":   0,
168
        \"hl_group":  a:hl_group,
169
        \"gc":        v:false,
170
        \"data":      data
171
        \}
172

173
  " TODO: optimize this part for rednering perfomance
174
  if !s:nvim
175
    if prop_type_get(item.hl_group, {'bufnr': self.vim_bufnr}) == {}
176
      call prop_type_add(item.hl_group, {
177
            \'highlight': item.hl_group,
178
            \'bufnr': self.vim_bufnr
179
            \})
180
    endif
181
  endif
182

183
  return item
184
endfu
185

186

187
fu! s:InternalBuffer.GetItemByPos() dict abort
188
  if s:nvim
189
    let idx = getbufinfo(self.vim_bufnr)[0]['lnum']
190
  else
191
    " vim popup buffer doesn't have current line info inside getbufinfo()
192
    " so extract line nr from win
193
    let l:popup_pos = 0
194
    call win_execute(self.popup_winid, 'let l:popup_pos = getcurpos()')
195
    let idx = l:popup_pos[1]
196
  end
197

198
  if idx > len(self.items)
199
    return 0
200
  endif
201

202
  if s:nvim
203
    let column = col('.')
204
  else
205
    let column = 1
206
  end
207

208
  let line = self.items[idx - 1]
209

210
  for item in line
211
    if item.start_col <= column && (item.end_col >= column || item.end_col == -1 )
212
      return item
213
    endif
214
  endfor
215

216
  return 0
217
endfu
218

219
" not optimal, but ok for current ui with around ~100/200 lines
220
" COMPLEXITY: O(1)
221
fu! s:InternalBuffer.GetItemLineNumber(item) dict abort
222
  let i = 0
223
  let found = 0
224

225
  for line in self.items
226
    let i += 1
227

228
    for item in line
229
      if item == a:item
230
        let found = i
231
        break
232
      endif
233
    endfor
234

235
    if found > 0
236
      break
237
    endif
238
  endfor
239

240
  return found
241
endfu
242

243
" not optimal, but ok for current ui with around ~100/200 lines
244
" COMPLEXITY: O(1)
245
fu! s:InternalBuffer.GetItemLineNumberByData(data) dict abort
246
  let i = 0
247
  let found = 0
248

249
  for line in self.items
250
    let i += 1
251

252
    for item in line
253
      if item.data == a:data
254
        let found = i
255
        break
256
      endif
257
    endfor
258

259
    if found > 0
260
      break
261
    endif
262
  endfor
263

264
  return found
265
endfu
266

267
fu! s:InternalBuffer.GetFirstItemOfType(type, ...) dict abort
268
  let result = 0
269
  let layer  = 0
270

271
  if a:0 == 1
272
    let layer = a:1
273
  endif
274

275
  for line in self.items
276
    if type(result) == v:t_dict
277
      break
278
    endif
279

280
    for item in line
281
      let type_is_ok  = item.type == a:type
282
      let layer_is_ok = v:true
283

284
      if type(layer) == v:t_string
285
        let layer_is_ok = item.data.layer == layer
286
      endif
287

288
      if type_is_ok && layer_is_ok
289
        let result = item
290
        break
291
      endif
292
    endfor
293
  endfor
294

295
  return result
296
endfu
297

298
fu! s:InternalBuffer.TryFindOriginalLinkFromPos() dict abort
299
  let cursor_item = self.GetItemByPos()
300

301
  " try to find original link
302
  if type(cursor_item) == v:t_dict && type(cursor_item.data) == v:t_dict
303
        \ && cursor_item.type == 'link'
304
        \ && !has_key(cursor_item, 'original_link')
305
    let ln   = self.GetItemLineNumber(cursor_item)
306
    let line = self.items[ln - 1]
307

308
    for item in line
309
      if type(item.data) == v:t_dict && has_key(item.data, 'original_link')
310
        let cursor_item = item
311
        break
312
      endif
313
    endfor
314
  endif
315

316
  return cursor_item
317
endfu
318

319
fu! s:InternalBuffer.TryRestoreCursorForItem(item,...) dict abort
320
  let opts = {}
321
  if a:0 == 1 && type(a:1) == v:t_dict
322
    let opts = a:1
323
  endif
324

325
  if type(a:item) == v:t_dict
326
        \ && a:item.type == "link"
327
        \ && !has_key(a:item.data, 'group_header')
328

329
      let new_ln = self.GetItemLineNumberByData(a:item.data)
330

331
      " item removed
332
      if new_ln == 0
333
        call self.JumpToFirstOfType('link')
334
      else
335
        call cursor(new_ln, 2)
336
      endif
337
  else
338
    if has_key(opts, 'last_ln_nr')
339
      if opts.last_ln_nr > self.len()
340
        call self.JumpToFirstOfType('link')
341
      else
342
        call cursor(opts.last_ln_nr, 2)
343
        call cursor(opts.last_ln_nr, 2)
344
      endif
345
    else
346
      call self.JumpToFirstOfType('link')
347
    endif
348
  endif
349
endfu
350

351
fu! s:InternalBuffer.JumpToFirstOfType(type, ...) dict abort
352
  let item = self.GetFirstItemOfType(a:type, a:000)
353

354
  if type(item) == v:t_dict
355
    let ln = self.GetItemLineNumber(item)
356
    call cursor(ln, 2)
357
  endif
358
endfu
359

360
fu! s:InternalBuffer.ClearBuffer(buf) dict abort
361
  call deletebufline(a:buf, 1, self.len() + 1)
362
endfu
363

364
fu! s:InternalBuffer.BufferLnum() dict abort
365
  return getbufinfo(self.vim_bufnr)[0]['lnum']
366
endfu
367

368
fu! s:InternalBuffer.RestorePopupCursor() dict abort
369
  if !s:nvim
370
    call popup_filter_menu(self.popup_winid, 'j')
371
  endif
372
endfu
373

374
fu! s:InternalBuffer.StartUiTransaction() dict abort
375
  if !s:nvim
376
    return
377
  endif
378

379
  call setbufvar(self.vim_bufnr, '&modifiable', 1)
380
endfu
381

382
fu! s:InternalBuffer.EndUiTransaction() dict abort
383
  if !s:nvim
384
    return
385
  endif
386

387
  call setbufvar(self.vim_bufnr, '&modifiable', 0)
388
endfu
389

390
fu! s:InternalBuffer.GrepResultToItems(gr, current_idx, layer) dict abort
391
  let gr    = a:gr
392
  let items = []
393

394
  let options =
395
        \{ "path": gr.path, "line_number": gr.line_number, "layer": a:layer }
396
  let original_link_options =
397
        \{ "path": gr.path, "line_number": gr.line_number,
398
        \"layer": a:layer, "original_link": v:true }
399

400
  if g:any_jump_list_numbers
401
    let prefix_text = a:current_idx + 1
402
    let prefix = self.CreateItem("link", prefix_text, g:AnyJumpGetColor('result_line_number'), options)
403

404
    call add(items, prefix)
405
  endif
406

407
  if g:any_jump_results_ui_style == 'filename_first'
408
    let path_text    = gr.path .  ":" . gr.line_number
409
    let matched_text = self.CreateItem("link", "" . gr.text, g:AnyJumpGetColor('result_text'), original_link_options)
410
    let file_path    = self.CreateItem("link", path_text, g:AnyJumpGetColor('result_path'), options)
411

412
    call add(items, file_path)
413
    call add(items, matched_text)
414
  elseif g:any_jump_results_ui_style == 'filename_last'
415
    let path_text    = '' .  gr.path .  ":" . gr.line_number
416
    let matched_text = self.CreateItem("link", gr.text, g:AnyJumpGetColor('result_text'), original_link_options)
417
    let file_path    = self.CreateItem("link", path_text, g:AnyJumpGetColor('result_path'), options)
418

419
    call add(items, matched_text)
420
    call add(items, file_path)
421
  endif
422

423
  return items
424
endfu
425

426
fu! s:InternalBuffer.GrepResultToGroupedItems(gr, current_idx, layer) dict abort
427
  let gr      = a:gr
428
  let items   = []
429

430
  let options =
431
        \{ "path": gr.path, "line_number": gr.line_number, "layer": a:layer }
432
  let original_link_options =
433
        \{ "path": gr.path, "line_number": gr.line_number,
434
        \"layer": a:layer, "original_link": v:true }
435

436
  let prefix_text = gr.line_number
437
  let prefix = self.CreateItem("link", prefix_text, g:AnyJumpGetColor('result_line_number'), options)
438

439
  call add(items, prefix)
440

441
  let matched_text = self.CreateItem("link", gr.text, g:AnyJumpGetColor('result_text'), original_link_options)
442

443
  call add(items, matched_text)
444

445
  return items
446
endfu
447

448
fu! s:InternalBuffer.RenderUiUsagesList(grep_results, start_ln) dict abort
449
  let start_ln     = a:start_ln
450
  let hidden_count = 0
451

452
  " TODO: move to method
453
  if type(g:any_jump_max_search_results) == v:t_number
454
        \ && g:any_jump_max_search_results > 0
455
        \ && self.overmaxed_results_hidden == v:true
456

457
    let cp = self.current_page ? self.current_page : 1
458
    let to = (cp * g:any_jump_max_search_results) - 1
459

460
    let collection   = self.usages_grep_results[0 : to]
461
    let hidden_count = len(self.usages_grep_results[to : -1])
462
  else
463
    let collection = self.usages_grep_results
464
  endif
465

466
  call self.AddLineAt([
467
    \self.CreateItem("text", ">", g:AnyJumpGetColor('heading_text'), {'layer': 'usages'}),
468
    \self.CreateItem("text", self.keyword, g:AnyJumpGetColor('heading_keyword'), {'layer': 'usages'}),
469
    \self.CreateItem("text", len(self.usages_grep_results) . " references", g:AnyJumpGetColor('heading_text'), {'layer': 'usages'}),
470
    \], start_ln)
471

472

473
  let start_ln += 1
474

475
  call self.AddLineAt([ self.CreateItem("text", "", "Comment", {"layer": "usages"}) ], start_ln)
476

477
  let start_ln += 1
478

479
  let idx = 0
480
  if self.grouping_enabled
481
    " group by file name rendering
482
    let render_map = {}
483

484
    for gr in collection
485
      if !has_key(render_map, gr.path)
486
        let render_map[gr.path] = []
487
      endif
488

489
      call add(render_map[gr.path], gr)
490
    endfor
491

492
    let path_idx = 0
493
    for path in keys(render_map)
494
      let first_gr = render_map[path][0]
495
      let opts     = {
496
            \"path":         path,
497
            \"line_number":  first_gr.line_number,
498
            \"layer":        "usages",
499
            \"group_header": v:true,
500
            \}
501

502
      let prefix     = self.CreateItem("link", ">", g:AnyJumpGetColor('group_text'), opts)
503
      let group_name = self.CreateItem("link", path, g:AnyJumpGetColor('group_name'), opts)
504
      let line       = [ prefix, group_name ]
505

506
      call self.AddLineAt(line, start_ln)
507
      let start_ln += 1
508

509
      for gr in render_map[path]
510
        let items = self.GrepResultToGroupedItems(gr, idx, "usages")
511
        call self.AddLineAt(items, start_ln)
512

513
        let start_ln += 1
514
        let idx += 1
515
      endfor
516

517
      if path_idx != len(keys(render_map)) - 1
518
        call self.AddLineAt([ self.CreateItem("text", "", "Comment", {"layer": "usages"}) ], start_ln)
519

520
        let start_ln += 1
521
      endif
522

523
      let path_idx += 1
524
    endfor
525
  else
526
    for gr in collection
527
      let items = self.GrepResultToItems(gr, idx, "usages")
528
      call self.AddLineAt(items, start_ln)
529

530
      let idx += 1
531
      let start_ln += 1
532
    endfor
533
  endif
534

535
  if hidden_count > 0
536
    call self.AddLineAt([ self.CreateItem("text", "", "Comment", {"layer": "usages"}) ], start_ln)
537
    let start_ln += 1
538

539
    call self.AddLineAt([
540
          \self.CreateItem("more_button", '[ ' . hidden_count . ' more ]', g:AnyJumpGetColor('more_button'), {"layer": "usages"}),
541
          \self.CreateItem("more_button", '— [a] load more results [A] load all', g:AnyJumpGetColor('more_explain'), {"layer": "usages"}),
542
          \], start_ln)
543
    let start_ln += 1
544
  endif
545

546
  call self.AddLineAt([ self.CreateItem("text", " ", "Comment", {"layer": "usages"}) ], start_ln)
547

548
  return v:true
549
endfu
550

551
fu! s:InternalBuffer.RenderUi() dict abort
552
  " clear items before render
553
  let self.items = []
554

555
  call self.AddLine([ self.CreateItem("text", "", "Comment") ])
556

557
  call self.AddLine([
558
    \self.CreateItem("text", ">", g:AnyJumpGetColor('heading_text')),
559
    \self.CreateItem("text", self.keyword, g:AnyJumpGetColor('heading_keyword')),
560
    \self.CreateItem("text", len(self.definitions_grep_results) . " definitions", g:AnyJumpGetColor('heading_text')),
561
    \])
562

563
  call self.AddLine([ self.CreateItem("text", "", "Comment") ])
564

565
  " draw grep results
566
  let idx          = 0
567
  let hidden_count = 0
568

569
  " TODO: move to method
570
  if type(g:any_jump_max_search_results) == v:t_number
571
        \ && g:any_jump_max_search_results > 0
572
        \ && self.overmaxed_results_hidden == v:true
573

574
    let cp = self.current_page ? self.current_page : 1
575
    let to = (cp * g:any_jump_max_search_results) - 1
576

577
    let collection   = self.definitions_grep_results[0 : to]
578
    let hidden_count = len(self.definitions_grep_results[to : -1])
579
  else
580
    let collection = self.definitions_grep_results
581
  endif
582

583
  if self.grouping_enabled
584
    " group by file name rendering
585
    let render_map = {}
586

587
    for gr in collection
588
      if !has_key(render_map, gr.path)
589
        let render_map[gr.path] = []
590
      endif
591

592
      call add(render_map[gr.path], gr)
593
    endfor
594

595
    let path_idx = 0
596

597
    for path in keys(render_map)
598
      let first_gr = render_map[path][0]
599
      let opts     = {
600
            \"path":         path,
601
            \"line_number":  first_gr.line_number,
602
            \"layer":        "definitions",
603
            \"group_header": v:true,
604
            \}
605

606
      let prefix     = self.CreateItem("link", ">", g:AnyJumpGetColor('group_text'), opts)
607
      let group_name = self.CreateItem("link", path, g:AnyJumpGetColor('group_name'), opts)
608
      let line       = [ prefix, group_name ]
609

610
      call self.AddLine(line)
611

612
      for gr in render_map[path]
613
        let items = self.GrepResultToGroupedItems(gr, idx, "definitions")
614
        call self.AddLine(items)
615

616
        let idx += 1
617
      endfor
618

619
      if path_idx != len(keys(render_map)) - 1
620
         call self.AddLine([ self.CreateItem("text", "", "Comment") ])
621
      endif
622

623
      let path_idx += 1
624
    endfor
625

626
    call self.AddLine([ self.CreateItem("text", "", "Comment") ])
627
  else
628
    if len(collection)
629
      for gr in collection
630
        let items = self.GrepResultToItems(gr, idx, "definitions")
631
        call self.AddLine(items)
632

633
        let idx += 1
634
      endfor
635
    else
636
      call self.AddLine([ self.CreateItem("text", "No definitions results", g:AnyJumpGetColor('plain_text')) ])
637
    endif
638

639
    call self.AddLine([ self.CreateItem("text", "", "Comment") ])
640
  endif
641

642
  if hidden_count > 0
643
    call self.AddLine([
644
          \self.CreateItem("more_button", '[ + ' . hidden_count . ' more ]', g:AnyJumpGetColor('more_button')),
645
          \self.CreateItem("more_button", '— [a] load more results [A] load all', g:AnyJumpGetColor('more_explain')),
646
          \])
647
    call self.AddLine([ self.CreateItem("text", "", "Comment") ])
648
  endif
649

650
  if self.usages_opened && len(self.usages_grep_results) > 0
651
    call self.RenderUiUsagesList(self.usages_grep_results, self.len() + 1)
652
  endif
653

654
  call self.HelpSection()
655
endfu
656

657
fu! s:InternalBuffer.HelpSection() abort
658
  if g:any_jump_show_help_section
659
    call self.AddLine([ self.CreateItem("help_link", "> Help", g:AnyJumpGetColor('heading_text')) ])
660

661
    let color = g:AnyJumpGetColor('help')
662

663
    call self.AddLine([ self.CreateItem("help_text", "", color) ])
664
    call self.AddLine([ self.CreateItem("help_text", "[o] open               [t] open in tab        [s] open in split   [v] open in vsplit", color) ])
665
    call self.AddLine([ self.CreateItem("help_text", "[p/tab] preview file   [b] scroll to first result", color) ])
666
    call self.AddLine([ self.CreateItem("help_text", "[a] load more results  [A] load all results", color) ])
667
    call self.AddLine([ self.CreateItem("help_text", "[r] show references    [T] group by file", color) ])
668
    call self.AddLine([ self.CreateItem("help_text", "[L] toggle search                             [esc/q] exit", color) ])
669
    call self.AddLine([ self.CreateItem("help_text", "    results ui style", color) ])
670
  endif
671
endfu
672

673
fu! s:InternalBuffer.RemoveGarbagedLines() dict abort
674
  " remove marked for garbage collection lines
675
  let new_items = []
676

677
  for line in self.items
678
    if has_key(line[0], 'gc') == v:false || line[0].gc == v:false
679
      call add(new_items, line)
680
    endif
681
  endfor
682

683
  let self.items = new_items
684
endfu
685

686

687
" Public api
688
fu! internal_buffer#GetClass() abort
689
  return s:InternalBuffer
690
endfu
691

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.