Source code: com/maddyhome/idea/vim/helper/EditorHelper.java
1 package com.maddyhome.idea.vim.helper;
2
3 /*
4 * IdeaVim - A Vim emulator plugin for IntelliJ Idea
5 * Copyright (C) 2003 Rick Maddy
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.editor.LogicalPosition;
24 import com.intellij.openapi.editor.VisualPosition;
25 import com.intellij.openapi.fileEditor.FileEditor;
26 import com.intellij.openapi.fileEditor.FileEditorManager;
27 import com.intellij.openapi.fileEditor.TextEditor;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.maddyhome.idea.vim.common.CharacterPosition;
30 import java.awt.Point;
31 import java.awt.Rectangle;
32 import java.nio.CharBuffer;
33
34 /**
35 * This is a set of helper methods for working with editors. All line and column values are zero based.
36 */
37 public class EditorHelper
38 {
39 /**
40 * Gets the visual line number the cursor is on
41 * @param editor The editor
42 * @return The cursor's visual line number
43 */
44 public static int getCurrentVisualLine(Editor editor)
45 {
46 return editor.getCaretModel().getVisualPosition().line;
47 }
48
49 /**
50 * Gets the visual column number the cursor is on
51 * @param editor The editor
52 * @return The cursor's visual column number
53 */
54 public static int getCurrentVisualColumn(Editor editor)
55 {
56 return editor.getCaretModel().getVisualPosition().column;
57 }
58
59 /**
60 * Gets the logical line number the cursor is on
61 * @param editor The editor
62 * @return The cursor's logical line number
63 */
64 public static int getCurrentLogicalLine(Editor editor)
65 {
66 return editor.getCaretModel().getLogicalPosition().line;
67 }
68
69 /**
70 * Gets the logical column number the cursor is on
71 * @param editor The editor
72 * @return The cursor's logical column number
73 */
74 public static int getCurrentLogicalColumn(Editor editor)
75 {
76 return editor.getCaretModel().getLogicalPosition().column;
77 }
78
79 /**
80 * Gets the number of characters on the current line. This will be different than the number of visual
81 * characters if there are "real" tabs in the line.
82 * @param editor The editor
83 * @return The number of characters in the current line
84 */
85 public static int getLineLength(Editor editor)
86 {
87 int lline = getCurrentLogicalLine(editor);
88
89 return getLineLength(editor, lline);
90 }
91
92 /**
93 * Gets the number of characters on the specified logical line. This will be different than the number of visual
94 * characters if there are "real" tabs in the line.
95 * @param editor The editor
96 * @param lline The logical line within the file
97 * @return The number of characters in the specified line
98 */
99 public static int getLineLength(Editor editor, int lline)
100 {
101 return Math.max(0, editor.offsetToLogicalPosition(editor.getDocument().getLineEndOffset(lline)).column);
102 }
103
104 /**
105 * Gets the number of characters on the specified visual line. This will be different than the number of visual
106 * characters if there are "real" tabs in the line.
107 * @param editor The editor
108 * @param vline The visual line within the file
109 * @return The number of characters in the specified line
110 */
111 public static int getVisualLineLength(Editor editor, int vline)
112 {
113 int lline = visualLineToLogicalLine(editor, vline);
114 return getLineLength(editor, lline);
115 }
116
117 /**
118 * Gets the number of visible lines in the editor. This will less then the actual number of lines in the file
119 * if there are any collapsed folds.
120 * @param editor The editor
121 * @return The number of visible lines in the file
122 */
123 public static int getVisualLineCount(Editor editor)
124 {
125 int count = getLineCount(editor);
126 return count == 0 ? 0 : logicalLineToVisualLine(editor, count - 1) + 1;
127 }
128
129 /**
130 * Gets the number of actual lines in the file
131 * @param editor The editor
132 * @return The file line count
133 */
134 public static int getLineCount(Editor editor)
135 {
136 int len = editor.getDocument().getLineCount();
137 if (editor.getDocument().getTextLength() > 0 &&
138 editor.getDocument().getChars()[editor.getDocument().getTextLength() - 1] == '\n')
139 {
140 len--;
141 }
142
143 return len;
144 }
145
146 /**
147 * Gets the actual number of characters in the file
148 * @param editor The editor
149 * @return The file's character count
150 */
151 public static int getFileSize(Editor editor)
152 {
153 int len = editor.getDocument().getTextLength();
154 if (len >= 1 && editor.getDocument().getChars()[len - 1] == '\n')
155 {
156 len--;
157 }
158
159 return len;
160 }
161
162 /**
163 * Gets the number of lines than can be displayed on the screen at one time. This is rounded down to the
164 * nearest whole line if there is a parial line visible at the bottom of the screen.
165 * @param editor The editor
166 * @return The number of screen lines
167 */
168 public static int getScreenHeight(Editor editor)
169 {
170 return editor.getScrollingModel().getVisibleArea().height / editor.getLineHeight();
171 }
172
173 /**
174 * Gets the number of characters that are visible on a screen line
175 * @param editor The editor
176 * @return The number of screen columns
177 */
178 public static int getScreenWidth(Editor editor)
179 {
180 Rectangle rect = editor.getScrollingModel().getVisibleArea();
181 Point pt = new Point(rect.width, 0);
182 VisualPosition vp = editor.xyToVisualPosition(pt);
183
184 return vp.column;
185 }
186
187 /**
188 * Converts a visual line number to a logical line number.
189 * @param editor The editor
190 * @param vline The visual line number to convert
191 * @return The logical line number
192 */
193 public static int visualLineToLogicalLine(Editor editor, int vline)
194 {
195 return editor.visualToLogicalPosition(new VisualPosition(vline, 0)).line;
196 }
197
198 /**
199 * Converts a logical line number to a visual line number. Several logical lines can map to the same
200 * visual line when there are collapsed fold regions.
201 * @param editor The editor
202 * @param lline The logical line number to convert
203 * @return The visual line number
204 */
205 public static int logicalLineToVisualLine(Editor editor, int lline)
206 {
207 return editor.logicalToVisualPosition(new LogicalPosition(lline, 0)).line;
208 }
209
210 /**
211 * Returns the offset of the start of the requested line.
212 * @param editor The editor
213 * @param lline The logical line to get the start offset for.
214 * @return 0 if line is < 0, file size of line is bigger than file, else the start offset for the line
215 */
216 public static int getLineStartOffset(Editor editor, int lline)
217 {
218 if (lline < 0)
219 {
220 return 0;
221 }
222 else if (lline >= getLineCount(editor))
223 {
224 return getFileSize(editor);
225 }
226 else
227 {
228 return editor.getDocument().getLineStartOffset(lline);
229 }
230 }
231
232 /**
233 * Returns the offset of the end of the requested line.
234 * @param editor The editor
235 * @param lline The logical line to get the end offset for.
236 * @return 0 if line is < 0, file size of line is bigger than file, else the end offset for the line
237 */
238 public static int getLineEndOffset(Editor editor, int lline, boolean incEnd)
239 {
240 if (lline < 0)
241 {
242 return 0;
243 }
244 else if (lline >= getLineCount(editor))
245 {
246 return getFileSize(editor);
247 }
248 else
249 {
250 return editor.getDocument().getLineEndOffset(lline) - (incEnd ? 0 : 1);
251 }
252 }
253
254 /**
255 * Ensures that the supplied visual line is within the range 0 (incl) and the number of visual lines in the file
256 * (excl).
257 * @param editor The editor
258 * @param vline The visual line number to normalize
259 * @return The normalized visual line number
260 */
261 public static int normalizeVisualLine(Editor editor, int vline)
262 {
263 vline = Math.min(Math.max(0, vline), getVisualLineCount(editor) - 1);
264
265 return vline;
266 }
267
268 /**
269 * Ensures that the supplied logical line is within the range 0 (incl) and the number of logical lines in the file
270 * (excl).
271 * @param editor The editor
272 * @param lline The logical line number to normalize
273 * @return The normalized logical line number
274 */
275 public static int normalizeLine(Editor editor, int lline)
276 {
277 lline = Math.max(0, Math.min(lline, getLineCount(editor) - 1));
278
279 return lline;
280 }
281
282 /**
283 * Ensures that the supplied column number for the given visual line is within the range 0 (incl) and the
284 * number of columns in the line (excl).
285 * @param editor The editor
286 * @param vline The visual line number
287 * @param col The column number to normalize
288 * @return The normalized column number
289 */
290 public static int normalizeVisualColumn(Editor editor, int vline, int col)
291 {
292 col = Math.min(Math.max(0, col), getVisualLineLength(editor, vline) - 1);
293
294 return col;
295 }
296
297 /**
298 * Ensures that the supplied column number for the given logical line is within the range 0 (incl) and the
299 * number of columns in the line (excl).
300 * @param editor The editor
301 * @param lline The logical line number
302 * @param col The column number to normalize
303 * @return The normalized column number
304 */
305 public static int normalizeColumn(Editor editor, int lline, int col)
306 {
307 col = Math.min(Math.max(0, getLineLength(editor, lline) - 1), col);
308
309 return col;
310 }
311
312 /**
313 * Ensures that the supplied offset for the given logical line is within the range for the line. If allowEnd
314 * is true, the range will allow for the offset to be one past the last character on the line.
315 * @param editor The editor
316 * @param lline The logical line number
317 * @param offset The offset to normalize
318 * @param allowEnd true if the offset can be one past the last character on the line, false if not
319 * @return The normalized column number
320 */
321 public static int normalizeOffset(Editor editor, int lline, int offset, boolean allowEnd)
322 {
323 if (getFileSize(editor) == 0)
324 {
325 return 0;
326 }
327
328 int min = getLineStartOffset(editor, lline);
329 int max = getLineEndOffset(editor, lline, allowEnd);;
330 offset = Math.max(Math.min(offset, max), min);
331
332 return offset;
333 }
334
335 public static int normalizeOffset(Editor editor, int offset, boolean allowEnd)
336 {
337 int lline = editor.offsetToLogicalPosition(offset).line;
338
339 return normalizeOffset(editor, lline, offset, allowEnd);
340 }
341
342 /**
343 * Gets the editor for the virtual file within the editor mananger.
344 * @param manager The file editor manager
345 * @param file The virtual file get the editor for
346 * @return The matching editor or null if no match was found
347 */
348 public static Editor getEditor(FileEditorManager manager, VirtualFile file)
349 {
350 FileEditor[] editors = manager.getEditors(file);
351 if (editors.length > 0 && editors[0] instanceof TextEditor)
352 {
353 return ((TextEditor)editors[0]).getEditor();
354 }
355
356 return null;
357 }
358
359 /**
360 * Converts a visual position to a file offset
361 * @param editor The editor
362 * @param pos The visual position to convert
363 * @return The file offset of the visual position
364 */
365 public static int visualPostionToOffset(Editor editor, VisualPosition pos)
366 {
367 return editor.logicalPositionToOffset(editor.visualToLogicalPosition(pos));
368 }
369
370 /**
371 * Gets a string representation of the file for the supplied offset range
372 * @param editor The editor
373 * @param start The starting offset (inclusive)
374 * @param end The ending offset (exclusive)
375 * @return The string, never null but empty if start == end
376 */
377 public static String getText(Editor editor, int start, int end)
378 {
379 return new String(editor.getDocument().getChars(), start, end - start);
380 }
381
382 /**
383 * Gets the offset of the start of the line containing the supplied offset
384 * @param editor The editor
385 * @param offset The offset within the line
386 * @return The offset of the line start
387 */
388 public static int getLineStartForOffset(Editor editor, int offset)
389 {
390 LogicalPosition pos = editor.offsetToLogicalPosition(offset);
391 return editor.getDocument().getLineStartOffset(pos.line);
392 }
393
394 /**
395 * Gets the offset of the end of the line containing the supplied offset
396 * @param editor The editor
397 * @param offset The offset within the line
398 * @return The offset of the line end
399 */
400 public static int getLineEndForOffset(Editor editor, int offset)
401 {
402 LogicalPosition pos = editor.offsetToLogicalPosition(offset);
403 return editor.getDocument().getLineEndOffset(pos.line);
404 }
405
406 public static int getLineCharCount(Editor editor, int lline)
407 {
408 return getLineEndOffset(editor, lline, true) - getLineStartOffset(editor, lline);
409 }
410
411 /**
412 * Returns the text of the requested logical line
413 * @param editor The editor
414 * @param lline The logical line to get the text for
415 * @return The requested line
416 */
417 public static String getLineText(Editor editor, int lline)
418 {
419 return getText(editor, getLineStartOffset(editor, lline), getLineEndOffset(editor, lline, true));
420 }
421
422 public static CharacterPosition offsetToCharacterPosition(Editor editor, int offset)
423 {
424 int line = editor.getDocument().getLineNumber(offset);
425 int col = offset - editor.getDocument().getLineStartOffset(line);
426
427 return new CharacterPosition(line, col);
428 }
429
430 public static int characterPositionToOffset(Editor editor, CharacterPosition pos)
431 {
432 return editor.getDocument().getLineStartOffset(pos.line) + pos.column;
433 }
434
435 public static CharBuffer getLineBuffer(Editor editor, int lline)
436 {
437 return CharBuffer.wrap(editor.getDocument().getChars(), getLineStartOffset(editor, lline),
438 getLineCharCount(editor, lline));
439 }
440 }