browser/devtools/sourceeditor/debugger.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:c2be61eaec2e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 "use strict";
6
7 const dbginfo = new WeakMap();
8
9 // These functions implement search within the debugger. Since
10 // search in the debugger is different from other components,
11 // we can't use search.js CodeMirror addon. This is a slightly
12 // modified version of that addon. Depends on searchcursor.js.
13
14 function SearchState() {
15 this.posFrom = this.posTo = this.query = null;
16 }
17
18 function getSearchState(cm) {
19 return cm.state.search || (cm.state.search = new SearchState());
20 }
21
22 function getSearchCursor(cm, query, pos) {
23 // If the query string is all lowercase, do a case insensitive search.
24 return cm.getSearchCursor(query, pos,
25 typeof query == "string" && query == query.toLowerCase());
26 }
27
28 /**
29 * If there's a saved search, selects the next results.
30 * Otherwise, creates a new search and selects the first
31 * result.
32 */
33 function doSearch(ctx, rev, query) {
34 let { cm } = ctx;
35 let state = getSearchState(cm);
36
37 if (state.query) {
38 searchNext(ctx, rev);
39 return;
40 }
41
42 cm.operation(function () {
43 if (state.query) return;
44
45 state.query = query;
46 state.posFrom = state.posTo = { line: 0, ch: 0 };
47 searchNext(ctx, rev);
48 });
49 }
50
51 /**
52 * Selects the next result of a saved search.
53 */
54 function searchNext(ctx, rev) {
55 let { cm, ed } = ctx;
56 cm.operation(function () {
57 let state = getSearchState(cm)
58 let cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
59
60 if (!cursor.find(rev)) {
61 cursor = getSearchCursor(cm, state.query, rev ?
62 { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 });
63 if (!cursor.find(rev))
64 return;
65 }
66
67 ed.alignLine(cursor.from().line, "center");
68 cm.setSelection(cursor.from(), cursor.to());
69 state.posFrom = cursor.from();
70 state.posTo = cursor.to();
71 });
72 }
73
74 /**
75 * Clears the currently saved search.
76 */
77 function clearSearch(cm) {
78 let state = getSearchState(cm);
79
80 if (!state.query)
81 return;
82
83 state.query = null;
84 }
85
86 // Exported functions
87
88 /**
89 * This function is called whenever Editor is extended with functions
90 * from this module. See Editor.extend for more info.
91 */
92 function initialize(ctx) {
93 let { ed } = ctx;
94
95 dbginfo.set(ed, {
96 breakpoints: {},
97 debugLocation: null
98 });
99 }
100
101 /**
102 * True if editor has a visual breakpoint at that line, false
103 * otherwise.
104 */
105 function hasBreakpoint(ctx, line) {
106 let { cm } = ctx;
107 let markers = cm.lineInfo(line).gutterMarkers;
108
109 return markers != null &&
110 markers.breakpoints.classList.contains("breakpoint");
111 }
112
113 /**
114 * Adds a visual breakpoint for a specified line. Third
115 * parameter 'cond' can hold any object.
116 *
117 * After adding a breakpoint, this function makes Editor to
118 * emit a breakpointAdded event.
119 */
120 function addBreakpoint(ctx, line, cond) {
121 if (hasBreakpoint(ctx, line))
122 return;
123
124 let { ed, cm } = ctx;
125 let meta = dbginfo.get(ed);
126 let info = cm.lineInfo(line);
127
128 ed.addMarker(line, "breakpoints", "breakpoint");
129 meta.breakpoints[line] = { condition: cond };
130
131 info.handle.on("delete", function onDelete() {
132 info.handle.off("delete", onDelete);
133 meta.breakpoints[info.line] = null;
134 });
135
136 ed.emit("breakpointAdded", line);
137 }
138
139 /**
140 * Removes a visual breakpoint from a specified line and
141 * makes Editor to emit a breakpointRemoved event.
142 */
143 function removeBreakpoint(ctx, line) {
144 if (!hasBreakpoint(ctx, line))
145 return;
146
147 let { ed, cm } = ctx;
148 let meta = dbginfo.get(ed);
149 let info = cm.lineInfo(line);
150
151 meta.breakpoints[info.line] = null;
152 ed.removeMarker(info.line, "breakpoints", "breakpoint");
153 ed.emit("breakpointRemoved", line);
154 }
155
156 /**
157 * Returns a list of all breakpoints in the current Editor.
158 */
159 function getBreakpoints(ctx) {
160 let { ed } = ctx;
161 let meta = dbginfo.get(ed);
162
163 return Object.keys(meta.breakpoints).reduce((acc, line) => {
164 if (meta.breakpoints[line] != null)
165 acc.push({ line: line, condition: meta.breakpoints[line].condition });
166 return acc;
167 }, []);
168 }
169
170 /**
171 * Saves a debug location information and adds a visual anchor to
172 * the breakpoints gutter. This is used by the debugger UI to
173 * display the line on which the Debugger is currently paused.
174 */
175 function setDebugLocation(ctx, line) {
176 let { ed } = ctx;
177 let meta = dbginfo.get(ed);
178
179 clearDebugLocation(ctx);
180
181 meta.debugLocation = line;
182 ed.addMarker(line, "breakpoints", "debugLocation");
183 ed.addLineClass(line, "debug-line");
184 }
185
186 /**
187 * Returns a line number that corresponds to the current debug
188 * location.
189 */
190 function getDebugLocation(ctx) {
191 let { ed } = ctx;
192 let meta = dbginfo.get(ed);
193
194 return meta.debugLocation;
195 }
196
197 /**
198 * Clears the debug location. Clearing the debug location
199 * also removes a visual anchor from the breakpoints gutter.
200 */
201 function clearDebugLocation(ctx) {
202 let { ed } = ctx;
203 let meta = dbginfo.get(ed);
204
205 if (meta.debugLocation != null) {
206 ed.removeMarker(meta.debugLocation, "breakpoints", "debugLocation");
207 ed.removeLineClass(meta.debugLocation, "debug-line");
208 meta.debugLocation = null;
209 }
210 }
211
212 /**
213 * Starts a new search.
214 */
215 function find(ctx, query) {
216 clearSearch(ctx.cm);
217 doSearch(ctx, false, query);
218 }
219
220 /**
221 * Finds the next item based on the currently saved search.
222 */
223 function findNext(ctx, query) {
224 doSearch(ctx, false, query);
225 }
226
227 /**
228 * Finds the previous item based on the currently saved search.
229 */
230 function findPrev(ctx, query) {
231 doSearch(ctx, true, query);
232 }
233
234
235 // Export functions
236
237 [
238 initialize, hasBreakpoint, addBreakpoint, removeBreakpoint,
239 getBreakpoints, setDebugLocation, getDebugLocation,
240 clearDebugLocation, find, findNext, findPrev
241 ].forEach(function (func) { module.exports[func.name] = func; });

mercurial