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
# 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 '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



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

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

#backspace!Object



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

def backspace!
  @abbrev.chop!
end

#buffer_numberObject



155
156
157
158
159
160
# File 'lib/command-t/match_window.rb', line 155

def buffer_number
  @@buffer && @@buffer.number
rescue Vim::DeletedBufferError
  # Beware of people manually deleting Command-T's hidden, unlisted buffer.
  @@buffer = nil
end

#closeObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/command-t/match_window.rb', line 162

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



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/command-t/match_window.rb', line 248

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



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

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

#leaveObject



192
193
194
195
# File 'lib/command-t/match_window.rb', line 192

def leave
  close
  unload
end

#matches=(matches) ⇒ Object



221
222
223
224
225
226
227
228
# File 'lib/command-t/match_window.rb', line 221

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


276
277
278
# File 'lib/command-t/match_window.rb', line 276

def print_no_such_file_or_directory
  print_error 'NO SUCH FILE OR DIRECTORY'
end

#select_nextObject



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

def select_next
  @reverse_list ? _prev : _next
end

#select_prevObject



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

def select_prev
  @reverse_list ? _next : _prev
end

#selectionObject

Returns the currently selected item as a String.



272
273
274
# File 'lib/command-t/match_window.rb', line 272

def selection
  @matches[@selection]
end

#unfocusObject



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

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

#unloadObject



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

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