Class: CommandT::MatchWindow

Inherits:
Object
  • Object
show all
Defined in:
lib/command-t/match_window.rb

Defined Under Namespace

Classes: Highlight

Constant Summary collapse

SELECTION_MARKER =
'> '
MARKER_LENGTH =
SELECTION_MARKER.length
UNSELECTED_MARKER =
' ' * MARKER_LENGTH
MH_START =
'<commandt>'
MH_END =
'</commandt>'
@@buffer =
nil

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ MatchWindow

Returns a new instance of MatchWindow.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/command-t/match_window.rb', line 17

def initialize(options = {})
  @encoding        = options[:encoding]
  @highlight_color = options[:highlight_color] || 'PmenuSel'
  @min_height      = options[:min_height]
  @prompt          = options[:prompt]
  @reverse_list    = options[:match_window_reverse]

  quoted_name = VIM::escape_for_single_quotes(options[:name])
  escaped_name = ::VIM::evaluate("fnameescape('#{quoted_name}')")

  run_will_show_autocmd

  set 'timeout', true        # ensure mappings timeout
  set 'hlsearch', false      # don't highlight search strings
  set 'insertmode', false    # don't make Insert mode the default
  set 'showcmd', false       # don't show command info on last line
  set 'equalalways', false   # don't auto-balance window sizes
  set 'timeoutlen', 0        # respond immediately to mappings
  set 'report', 9999         # don't show "X lines changed" reports
  set 'scrolloff', 0         # don't scroll near buffer edges
  set 'sidescroll', 0        # don't sidescroll in jumps
  set 'sidescrolloff', 0     # don't sidescroll automatically

  if options[:debounce_interval] > 0
    set 'updatetime', options[:debounce_interval]
  end

  # Save existing window views so we can restore them later.
  current_window = ::VIM::evaluate('winnr()')
  @windows = (0..(::VIM::Window.count - 1)).map do |i|
    focus_window(i + 1)
    view = ::VIM::evaluate('winsaveview()')
    window = OpenStruct.new(
      :index    => i,
      :height   => ::VIM::Window[i].height,
      :width    => ::VIM::Window[i].width,
      :lnum     => view['lnum'],
      :col      => view['col'],
      :coladd   => view['coladd'],
      :curswant => view['curswant'],
      :topline  => view['topline'],
      :topfill  => view['topfill'],
      :leftcol  => view['leftcol'],
      :skipcol  => view['skipcol']
    )

    # When creating a split for the match window, move the cursor to the
    # opposite side of the window's viewport to prevent unwanted scrolling.
    boundary_line = options[:match_window_at_top] ?
      ::VIM::evaluate("line('w$')") :
      view['topline']
    ::VIM::evaluate("winrestview({'lnum': #{boundary_line}})")

    window
  end
  focus_window(current_window)

  # show match window
  split_location = options[:match_window_at_top] ? 'topleft' : 'botright'
  if ((number = buffer_number)) # still have buffer from last time
    ::VIM::command "silent! #{split_location} #{number}sbuffer"
    if $curbuf.number != number
      raise "Can't re-open Command-T match listing buffer"
    end
    $curwin.height = 1
    ::VIM::command "0file"
    ::VIM::command "keepalt file #{escaped_name}"
  else        # creating match window for first time and set it up
    ::VIM::command "silent! keepalt #{split_location} 1split #{escaped_name}"
    set 'bufhidden', 'unload'   # unload buf when no longer displayed
    set 'buftype', 'nofile'     # buffer is not related to any file
    set 'filetype', 'command-t' # provide for detectability/extensibility
    set 'modifiable', false     # prevent manual edits
    set 'readonly', false       # avoid W10 "Changing a readonly file"
    set 'swapfile', false       # don't create a swapfile
    set 'wrap', false           # don't soft-wrap
    set 'number', false         # don't show line numbers
    set 'list', false           # don't use List mode (visible tabs etc)
    set 'foldcolumn', 0         # don't show a fold column at side
    set 'foldlevel', 99         # don't fold anything
    set 'cursorline', false     # don't highlight line cursor is on
    set 'signcolumn', 'no'      # signcolumn never
    set 'spell', false          # spell-checking off
    set 'buflisted', false      # don't show up in the buffer list
    set 'textwidth', 0          # don't hard-wrap (break long lines)

    # don't show the color column
    set 'colorcolumn', 0 if VIM::exists?('+colorcolumn')

    # don't show relative line numbers
    set 'relativenumber', false if VIM::exists?('+relativenumber')

    # sanity check: make sure the buffer really was created
    if File.basename($curbuf.name) != options[:name]
      raise "Can't find Command-T match listing buffer"
    end
    @@buffer = $curbuf
  end

  # syntax coloring
  if VIM::has?('syntax')
    ::VIM::command "syntax match CommandTSelection \"^#{SELECTION_MARKER}.\\+$\""
    ::VIM::command 'syntax match CommandTNoEntries "^-- NO MATCHES --$"'
    ::VIM::command 'syntax match CommandTNoEntries "^-- NO SUCH FILE OR DIRECTORY --$"'
    set 'synmaxcol', 9999

    if VIM::has?('conceal')
      set 'conceallevel', 2
      set 'concealcursor', 'nvic'
      ::VIM::command 'syntax region CommandTCharMatched ' \
                     "matchgroup=CommandTCharMatched start=+#{MH_START}+ " \
                     "matchgroup=CommandTCharMatchedEnd end=+#{MH_END}+ concealends"
      ::VIM::command 'highlight def CommandTCharMatched ' \
                     'term=bold,underline cterm=bold,underline ' \
                     'gui=bold,underline'
    end

    ::VIM::command "highlight link CommandTSelection #{@highlight_color}"
    ::VIM::command 'highlight link CommandTNoEntries Error'

    # hide cursor
    @cursor_highlight = get_cursor_highlight
    hide_cursor
  end

  # perform cleanup using an autocmd to ensure we don't get caught out
  # by some unexpected means of dismissing or leaving the Command-T window
  # (eg. <C-W q>, <C-W k> etc)
  ::VIM::command 'augroup CommandTMatchWindow'
  ::VIM::command 'autocmd!'
  ::VIM::command 'autocmd BufLeave <buffer> silent! ruby $command_t.leave'
  ::VIM::command 'autocmd BufUnload <buffer> silent! ruby $command_t.unload'
  ::VIM::command 'augroup END'

  @has_focus  = false
  @abbrev     = ''
  @window     = $curwin
end

Instance Method Details

#add!(char) ⇒ Object



213
214
215
# File 'lib/command-t/match_window.rb', line 213

def add!(char)
  @abbrev += char
end

#backspace!Object



217
218
219
# File 'lib/command-t/match_window.rb', line 217

def backspace!
  @abbrev.chop!
end

#buffer_numberObject



156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/command-t/match_window.rb', line 156

def buffer_number
  @@buffer && @@buffer.number
rescue StandardError => e
  if (
    defined?(Vim::DeletedBufferError) && e.instance_of?(Vim::DeletedBufferError) ||
    e.message =~ /Invalid buffer/
  )
    # Something/someone deleted Command-T's hidden, unlisted buffer.
    @@buffer = nil
  else
    raise
  end
end

#closeObject



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/command-t/match_window.rb', line 170

def close
  # Unlisted buffers like those provided by Netrw, NERDTree and Vim's help
  # don't actually appear in the buffer list; if they are the only such
  # buffers present when Command-T is invoked (for example, when invoked
  # immediately after starting Vim with a directory argument, like `vim .`)
  # then performing the normal clean-up will yield an "E90: Cannot unload
  # last buffer" error. We can work around that by doing a :quit first.
  if ::VIM::Buffer.count == 0
    ::VIM::command 'silent quit'
  end

  # Workaround for upstream bug in Vim 7.3 on some platforms
  #
  # On some platforms, $curbuf.number always returns 0. One workaround is
  # to build Vim with --disable-largefile, but as this is producing lots of
  # support requests, implement the following fallback to the buffer name
  # instead, at least until upstream gets fixed.
  #
  # For more details, see: https://wincent.com/issues/1617
  if $curbuf.number == 0
    # use bwipeout as bunload fails if passed the name of a hidden buffer
    base = File.basename($curbuf.name)
    escaped_name = ::VIM::evaluate("fnameescape('#{base}')")
    ::VIM::command "silent! bwipeout! #{escaped_name}"
    @@buffer = nil
  else
    ::VIM::command "silent! bunload! #{@@buffer.number}"
  end
end

#find(char) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/command-t/match_window.rb', line 256

def find(char)
  # is this a new search or the continuation of a previous one?
  now = Time.now
  if @last_key_time.nil? || @last_key_time < (now - 0.5)
    @find_string = char
  else
    @find_string += char
  end
  @last_key_time = now

  # see if there's anything up ahead that matches
  matches = @reverse_list ? @matches.reverse : @matches
  matches.each_with_index do |match, idx|
    if match[0, @find_string.length].casecmp(@find_string) == 0
      old_selection = @selection
      @selection = @reverse_list ? matches.length - idx - 1 : idx
      print_match(old_selection)  # redraw old selection (removes marker)
      print_match(@selection)     # redraw new selection (adds marker)
      break
    end
  end
end

#focusObject



238
239
240
241
242
243
244
245
# File 'lib/command-t/match_window.rb', line 238

def focus
  unless @has_focus
    @has_focus = true
    if VIM::has?('syntax')
      ::VIM::command 'highlight link CommandTSelection Search'
    end
  end
end

#leaveObject



200
201
202
203
# File 'lib/command-t/match_window.rb', line 200

def leave
  close
  unload
end

#matches=(matches) ⇒ Object



229
230
231
232
233
234
235
236
# File 'lib/command-t/match_window.rb', line 229

def matches=(matches)
  if matches != @matches
    @matches = matches
    @selection = 0
    print_matches
    move_cursor_to_selected_line
  end
end


284
285
286
# File 'lib/command-t/match_window.rb', line 284

def print_no_such_file_or_directory
  print_error 'NO SUCH FILE OR DIRECTORY'
end

#select_nextObject



221
222
223
# File 'lib/command-t/match_window.rb', line 221

def select_next
  @reverse_list ? _prev : _next
end

#select_prevObject



225
226
227
# File 'lib/command-t/match_window.rb', line 225

def select_prev
  @reverse_list ? _next : _prev
end

#selectionObject

Returns the currently selected item as a String.



280
281
282
# File 'lib/command-t/match_window.rb', line 280

def selection
  @matches[@selection]
end

#unfocusObject



247
248
249
250
251
252
253
254
# File 'lib/command-t/match_window.rb', line 247

def unfocus
  if @has_focus
    @has_focus = false
    if VIM::has?('syntax')
      ::VIM::command "highlight link CommandTSelection #{@highlight_color}"
    end
  end
end

#unloadObject



205
206
207
208
209
210
211
# File 'lib/command-t/match_window.rb', line 205

def unload
  restore_window_views
  @prompt.dispose
  @settings.restore
  show_cursor
  run_did_hide_autocmd
end