1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @constructor
33 * @param {number} startLine
34 * @param {number} startColumn
35 * @param {number} endLine
36 * @param {number} endColumn
37 */
38WebInspector.TextRange = function(startLine, startColumn, endLine, endColumn)
39{
40    this.startLine = startLine;
41    this.startColumn = startColumn;
42    this.endLine = endLine;
43    this.endColumn = endColumn;
44}
45
46/**
47 * @param {number} line
48 * @param {number} column
49 * @return {!WebInspector.TextRange}
50 */
51WebInspector.TextRange.createFromLocation = function(line, column)
52{
53    return new WebInspector.TextRange(line, column, line, column);
54}
55
56/**
57 * @param {!Object} serializedTextRange
58 * @return {!WebInspector.TextRange}
59 */
60WebInspector.TextRange.fromObject = function(serializedTextRange)
61{
62    return new WebInspector.TextRange(serializedTextRange.startLine, serializedTextRange.startColumn, serializedTextRange.endLine, serializedTextRange.endColumn);
63}
64
65/**
66 * @param {!WebInspector.TextRange} range1
67 * @param {!WebInspector.TextRange} range2
68 * @return {number}
69 */
70WebInspector.TextRange.comparator = function(range1, range2)
71{
72    return range1.compareTo(range2);
73}
74
75WebInspector.TextRange.prototype = {
76    /**
77     * @return {boolean}
78     */
79    isEmpty: function()
80    {
81        return this.startLine === this.endLine && this.startColumn === this.endColumn;
82    },
83
84    /**
85     * @param {!WebInspector.TextRange} range
86     * @return {boolean}
87     */
88    immediatelyPrecedes: function(range)
89    {
90        if (!range)
91            return false;
92        return this.endLine === range.startLine && this.endColumn === range.startColumn;
93    },
94
95    /**
96     * @param {!WebInspector.TextRange} range
97     * @return {boolean}
98     */
99    immediatelyFollows: function(range)
100    {
101        if (!range)
102            return false;
103        return range.immediatelyPrecedes(this);
104    },
105
106    /**
107     * @param {!WebInspector.TextRange} range
108     * @return {boolean}
109     */
110    follows: function(range)
111    {
112        return (range.endLine === this.startLine && range.endColumn <= this.startColumn)
113            || range.endLine < this.startLine;
114    },
115
116    /**
117     * @return {number}
118     */
119    get linesCount()
120    {
121        return this.endLine - this.startLine;
122    },
123
124    /**
125     * @return {!WebInspector.TextRange}
126     */
127    collapseToEnd: function()
128    {
129        return new WebInspector.TextRange(this.endLine, this.endColumn, this.endLine, this.endColumn);
130    },
131
132    /**
133     * @return {!WebInspector.TextRange}
134     */
135    collapseToStart: function()
136    {
137        return new WebInspector.TextRange(this.startLine, this.startColumn, this.startLine, this.startColumn);
138    },
139
140    /**
141     * @return {!WebInspector.TextRange}
142     */
143    normalize: function()
144    {
145        if (this.startLine > this.endLine || (this.startLine === this.endLine && this.startColumn > this.endColumn))
146            return new WebInspector.TextRange(this.endLine, this.endColumn, this.startLine, this.startColumn);
147        else
148            return this.clone();
149    },
150
151    /**
152     * @return {!WebInspector.TextRange}
153     */
154    clone: function()
155    {
156        return new WebInspector.TextRange(this.startLine, this.startColumn, this.endLine, this.endColumn);
157    },
158
159    /**
160     * @return {!Object}
161     */
162    serializeToObject: function()
163    {
164        var serializedTextRange = {};
165        serializedTextRange.startLine = this.startLine;
166        serializedTextRange.startColumn = this.startColumn;
167        serializedTextRange.endLine = this.endLine;
168        serializedTextRange.endColumn = this.endColumn;
169        return serializedTextRange;
170    },
171
172    /**
173     * @param {!WebInspector.TextRange} other
174     * @return {number}
175     */
176    compareTo: function(other)
177    {
178        if (this.startLine > other.startLine)
179            return 1;
180        if (this.startLine < other.startLine)
181            return -1;
182        if (this.startColumn > other.startColumn)
183            return 1;
184        if (this.startColumn < other.startColumn)
185            return -1;
186        return 0;
187    },
188
189    /**
190     * @param {!WebInspector.TextRange} other
191     * @return {boolean}
192     */
193    equal: function(other)
194    {
195        return this.startLine === other.startLine && this.endLine === other.endLine &&
196            this.startColumn === other.startColumn && this.endColumn === other.endColumn;
197    },
198
199    /**
200     * @param {number} lineOffset
201     * @return {!WebInspector.TextRange}
202     */
203    shift: function(lineOffset)
204    {
205        return new WebInspector.TextRange(this.startLine + lineOffset, this.startColumn, this.endLine + lineOffset, this.endColumn);
206    },
207
208    /**
209     * @param {!WebInspector.TextRange} originalRange
210     * @param {!WebInspector.TextRange} editedRange
211     * @return {!WebInspector.TextRange}
212     */
213    rebaseAfterTextEdit: function(originalRange, editedRange)
214    {
215        console.assert(originalRange.startLine === editedRange.startLine);
216        console.assert(originalRange.startColumn === editedRange.startColumn);
217        var rebase = this.clone();
218        if (!this.follows(originalRange))
219            return rebase;
220        var lineDelta = editedRange.endLine - originalRange.endLine;
221        var columnDelta = editedRange.endColumn - originalRange.endColumn;
222        rebase.startLine += lineDelta;
223        rebase.endLine += lineDelta;
224        if (rebase.startLine === editedRange.endLine)
225            rebase.startColumn += columnDelta;
226        if (rebase.endLine === editedRange.endLine)
227            rebase.endColumn += columnDelta;
228        return rebase;
229    },
230
231    /**
232     * @return {string}
233     */
234    toString: function()
235    {
236        return JSON.stringify(this);
237    }
238}
239
240/**
241 * @constructor
242 * @param {number} offset
243 * @param {number} length
244 */
245WebInspector.SourceRange = function(offset, length)
246{
247    this.offset = offset;
248    this.length = length;
249}
250