e087702b0c3148c0c94f1cc327f5f63d23681385
[idea/community.git] / platform / editor-ui-api / src / com / intellij / openapi / editor / LogicalPosition.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.editor;
17
18 import org.jetbrains.annotations.NonNls;
19 import org.jetbrains.annotations.NotNull;
20
21 import java.awt.*;
22
23 /**
24  * Represents a logical position in the editor. Logical positions ignore folding -
25  * for example, if the top 10 lines of the document are folded, the 10th line in the document
26  * will have the line number 10 in its logical position.
27  * <p>
28  * Logical position may store additional parameters that define its mapping to {@link VisualPosition}. Rationale is that
29  * single logical {@code (line; column)} pair matches soft wrap-introduced virtual space, i.e. different visual positions
30  * correspond to the same logical position. It's convenient to store exact visual location details within the logical
31  * position in order to relief further {@code 'logical position' -> 'visual position'} mapping.
32  * <p>
33  * Logical position corresponds to a boundary between two characters and can be associated with either a preceding or succeeding character 
34  * (see {@link #leansForward}). This association makes a difference in a bidirectional text, where a mapping from logical to visual position 
35  * is not continuous.
36  * <p>
37  * <b>Note:</b> two objects of this class are considered equal if their logical line and column are equal. I.e. all logical positions
38  * for soft wrap-introduced virtual space and the first document symbol after soft wrap are considered to be equal. Value of 
39  * {@link #leansForward} flag doesn't impact the equality of logical positions.
40  *
41  * @see Editor#offsetToLogicalPosition(int)
42  * @see Editor#logicalPositionToOffset(LogicalPosition)
43  *
44  * @see VisualPosition
45  * @see Editor#visualToLogicalPosition(VisualPosition)
46  *
47  * @see Editor#xyToLogicalPosition(Point)
48  */
49 public class LogicalPosition implements Comparable<LogicalPosition> {
50   public final int line;
51   public final int column;
52
53   /**
54    * Identifies if current logical position may be correctly mapped to visual position. E.g. we can define properties like
55    * {@link #softWrapLinesBeforeCurrentLogicalLine}, {@link #softWrapColumnDiff} etc during {@code 'visual position' -> 'logical position'} conversion
56    * in order to be able to easy match it back to visual position.
57    *
58    * @deprecated Always <code>false</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
59    *             Will be removed in future.
60    */
61   public final boolean visualPositionAware;
62
63   /**
64    * Number of virtual soft wrap-introduced lines before the current logical line.
65    *
66    * @see #visualPositionAware
67    * @deprecated Always <code>0</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
68    *             Will be removed in future.
69    */
70   public final int softWrapLinesBeforeCurrentLogicalLine;
71
72   /**
73    * Number of virtual soft wrap introduced lines on a current logical line before the visual position that corresponds
74    * to the current logical position.
75    * <p>
76    * Instead of directly using this value, EditorUtil.getSoftWrapCountAfterLineStart(Editor, LogicalPosition) method can be used,
77    * it will work regardless of whether current {@code LogicalPosition} instance is {@link #visualPositionAware}.
78    *
79    * @see #visualPositionAware
80    * @deprecated Always <code>0</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
81    *             Will be removed in future.
82    */
83   public final int softWrapLinesOnCurrentLogicalLine;
84
85   /**
86    * Number to add to the {@link #column logical column} in order to get soft wrap-introduced visual column offset.
87    *
88    * @see #visualPositionAware
89    * @deprecated Always <code>0</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
90    *             Will be removed in future.
91    */
92   public final int softWrapColumnDiff;
93
94   /**
95    * Number of folded line feeds before the current position.
96    *
97    * @see #visualPositionAware
98    * @deprecated Always <code>0</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
99    *             Will be removed in future.
100    */
101   public final int foldedLines;
102
103   /**
104    * Number to add to the {@link #column logical column} in order to get folding-introduced visual column offset.
105    *
106    * @see #visualPositionAware
107    * @deprecated Always <code>0</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
108    *             Will be removed in future.
109    */
110   public final int foldingColumnDiff;
111
112   /**
113    * If {@code true}, this position is associated with succeeding character (in logical order), otherwise it's associated with
114    * preceding character. This can make difference in bidirectional text, where logical positions which differ only in this flag's value
115    * can have different visual positions.
116    * <p>
117    * This field has no impact on equality and comparison relationships between {@code LogicalPosition} instances.
118    */
119   public final boolean leansForward;
120
121   /**
122    * This field provides the value of {@link VisualPosition#leansRight} field of visual position corresponding to current position.
123    * It has meaning only if {@link #visualPositionAware} is set.
124    * 
125    * @deprecated Always <code>false</code> in {@link LogicalPosition} instances returned by platform code since 2016.1. 
126    *             Will be removed in future.
127    */
128   public final boolean visualPositionLeansRight;
129
130   public LogicalPosition(int line, int column) throws IllegalArgumentException {
131     this(line, column, 0, 0, 0, 0, 0, false, false, false);
132   }
133
134   public LogicalPosition(int line, int column, boolean leansForward) throws IllegalArgumentException {
135     this(line, column, 0, 0, 0, 0, 0, false, leansForward, leansForward);
136   }
137
138   /**
139    * @deprecated Use {@link #LogicalPosition(int, int)} instead. Additional fields won't be used in future.
140    */
141   public LogicalPosition(int line, int column, int softWrapLinesBeforeCurrentLogicalLine, int softWrapLinesOnCurrentLogicalLine,
142                          int softWrapColumnDiff, int foldedLines, int foldingColumnDiff) throws IllegalArgumentException {
143     this(line, column, softWrapLinesBeforeCurrentLogicalLine, softWrapLinesOnCurrentLogicalLine, softWrapColumnDiff, foldedLines,
144       foldingColumnDiff, true, false, false);
145   }
146
147   /**
148    * @deprecated Use {@link #LogicalPosition(int, int, boolean)} instead. Additional fields won't be used in future.
149    */
150   public LogicalPosition(int line, int column, int softWrapLinesBeforeCurrentLogicalLine, int softWrapLinesOnCurrentLogicalLine,
151                          int softWrapColumnDiff, int foldedLines, int foldingColumnDiff, boolean leansForward,
152                          boolean visualPositionLeansRight) throws IllegalArgumentException {
153     this(line, column, softWrapLinesBeforeCurrentLogicalLine, softWrapLinesOnCurrentLogicalLine, softWrapColumnDiff, foldedLines,
154       foldingColumnDiff, true, leansForward, visualPositionLeansRight);
155   }
156
157   private LogicalPosition(int line, int column, int softWrapLinesBeforeCurrentLogicalLine, int softWrapLinesOnCurrentLogicalLine,
158                           int softWrapColumnDiff, int foldedLines, int foldingColumnDiff, boolean visualPositionAware,
159                           boolean leansForward, boolean visualPositionLeansRight)
160     throws IllegalArgumentException {
161     if (column + softWrapColumnDiff + foldingColumnDiff < 0) {
162       throw new IllegalArgumentException(String.format(
163         "Attempt to create %s with invalid arguments - resulting column is negative (%d). Given arguments: line=%d, column=%d, "
164         + "soft wrap lines before: %d, soft wrap lines current: %d, soft wrap column diff: %d, folded lines: %d, folding column "
165         + "diff: %d, visual position aware: %b",
166         getClass().getName(), column + softWrapColumnDiff + foldingColumnDiff, line, column, softWrapLinesBeforeCurrentLogicalLine,
167         softWrapLinesOnCurrentLogicalLine, softWrapColumnDiff, foldedLines, foldingColumnDiff, visualPositionAware
168       ));
169     }
170     if (line < 0) throw new IllegalArgumentException("line must be non negative: "+line);
171     if (column < 0) throw new IllegalArgumentException("column must be non negative: "+column);
172     this.line = line;
173     this.column = column;
174     this.softWrapLinesBeforeCurrentLogicalLine = softWrapLinesBeforeCurrentLogicalLine;
175     this.softWrapLinesOnCurrentLogicalLine = softWrapLinesOnCurrentLogicalLine;
176     this.softWrapColumnDiff = softWrapColumnDiff;
177     this.foldedLines = foldedLines;
178     this.foldingColumnDiff = foldingColumnDiff;
179     this.visualPositionAware = visualPositionAware;
180     this.leansForward = leansForward;
181     this.visualPositionLeansRight = visualPositionLeansRight;
182   }
183
184   /**
185    * Builds visual position based on a state of the current logical position.
186    * <p/>
187    * Such visual position is considered to make sense only if current logical position
188    * is {@link #visualPositionAware visual position aware}.
189    *
190    * @return    visual position based on a state of the current logical position
191    *
192    * @deprecated Result doesn't makes sense, since {@link #visualPositionAware} is deprecated.
193    */
194   public VisualPosition toVisualPosition() {
195     return new VisualPosition(
196       line + softWrapLinesBeforeCurrentLogicalLine + softWrapLinesOnCurrentLogicalLine - foldedLines,
197       column + softWrapColumnDiff + foldingColumnDiff,
198       visualPositionLeansRight
199     );
200   }
201
202   /**
203    * Returns a new instance of class corresponding to the same logical position in the document, but without any cached
204    * reference to its visual position.
205    * 
206    * @deprecated Not needed, since {@link #visualPositionAware} is deprecated.
207    */
208   public LogicalPosition withoutVisualPositionInfo() {
209     return new LogicalPosition(line, column, leansForward);
210   }
211
212   /**
213    * Constructs a new {@code LogicalPosition} instance with a given value of {@link #leansForward} flag.
214    */
215   public LogicalPosition leanForward(boolean value) {
216     return new LogicalPosition(line, column, value);
217   }
218   
219   public boolean equals(Object o) {
220     if (!(o instanceof LogicalPosition)) return false;
221     final LogicalPosition logicalPosition = (LogicalPosition) o;
222
223     return column == logicalPosition.column && line == logicalPosition.line;
224   }
225
226   public int hashCode() {
227     return 29 * line + column;
228   }
229
230   @NonNls
231   public String toString() {
232     return "LogicalPosition: (" + line + ", " + column + ")"
233            + (visualPositionAware ? "; vp aware" : "")
234            + (softWrapLinesBeforeCurrentLogicalLine + softWrapLinesOnCurrentLogicalLine == 0
235               ? ""
236               : "; soft wrap: lines=" + (softWrapLinesBeforeCurrentLogicalLine + softWrapLinesOnCurrentLogicalLine)
237                 + " (before=" + softWrapLinesBeforeCurrentLogicalLine + "; current=" + softWrapLinesOnCurrentLogicalLine + ")")
238            + (softWrapColumnDiff == 0 ? "" : "; columns diff=" + softWrapColumnDiff + ";" )
239            + (foldedLines == 0? "" : "; folding: lines = " + foldedLines + ";")
240            + (foldingColumnDiff == 0 ? "" : "; columns diff=" + foldingColumnDiff)
241            + (leansForward ? "; leans forward" : "");
242   }
243
244   @Override
245   public int compareTo(@NotNull LogicalPosition position) {
246     if (line != position.line) return line - position.line;
247     if (column != position.column) return column - position.column;
248     if (softWrapLinesBeforeCurrentLogicalLine != position.softWrapLinesBeforeCurrentLogicalLine) return softWrapLinesBeforeCurrentLogicalLine - position.softWrapLinesBeforeCurrentLogicalLine;
249     return softWrapColumnDiff - position.softWrapColumnDiff;
250   }
251 }