Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 734c236

Browse files
committed
Merge pull request #5612 from adobe/jbalsas/quickopen_cols
Syntax for line:col jumps in QuickOpen search queries
2 parents aa4ad80 + 9290a6e commit 734c236

File tree

2 files changed

+139
-109
lines changed

2 files changed

+139
-109
lines changed

src/search/QuickOpen.js

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ define(function (require, exports, module) {
5353
StringMatch = require("utils/StringMatch"),
5454
ViewUtils = require("utils/ViewUtils");
5555

56-
56+
57+
/** @const {RegExp} The regular expression to check the cursor position */
58+
var CURSOR_POS_EXP = new RegExp(":([^,]+)?(,(.+)?)?");
59+
5760
/** @type Array.<QuickOpenPlugin> */
5861
var plugins = [];
5962

@@ -251,23 +254,28 @@ define(function (require, exports, module) {
251254
* is followed by a colon. Callers should explicitly test result with isNaN()
252255
*
253256
* @param {string} query string to extract line number from
254-
* @returns {number} line number. Returns NaN to indicate no line number was found
257+
* @return {{query: string, local: boolean, line: number, ch: number}} An object with
258+
* the extracted line and column numbers, and two additional fields: query with the original position
259+
* string and local indicating if the cursor position should be applied to the current file.
260+
* Or null if the query is invalid
255261
*/
256-
function extractLineNumber(query) {
257-
// only match : at beginning of query for now
258-
// TODO: match any location of : when QuickOpen._handleItemFocus() is modified to
259-
// dynamic open files
260-
if (query.indexOf(":") !== 0) {
261-
return NaN;
262-
}
263-
264-
var result = NaN;
265-
var regInfo = query.match(/(!?:)(\d+)/); // colon followed by a digit
266-
if (regInfo) {
267-
result = regInfo[2] - 1;
262+
function extractCursorPos(query) {
263+
var regInfo = query.match(CURSOR_POS_EXP),
264+
result;
265+
266+
if (query.length <= 1 || !regInfo ||
267+
(regInfo[1] && isNaN(regInfo[1])) ||
268+
(regInfo[3] && isNaN(regInfo[3]))) {
269+
270+
return null;
268271
}
269-
270-
return result;
272+
273+
return {
274+
query: regInfo[0],
275+
local: query.indexOf(":") === 0,
276+
line: regInfo[1] - 1 || 0,
277+
ch: regInfo[3] - 1 || 0
278+
};
271279
}
272280

273281
/** Returns the last return value of _filterCallback(), which Smart Autocomplete helpfully caches */
@@ -312,18 +320,15 @@ define(function (require, exports, module) {
312320
}
313321

314322
var selectedItem = domItemToSearchResult(selectedDOMItem),
315-
doClose = true,
316-
self = this;
323+
doClose = true,
324+
self = this,
325+
query = this.$searchField.val(),
326+
cursorPos = extractCursorPos(query);
317327

318328
// Delegate to current plugin
319329
if (currentPlugin) {
320330
currentPlugin.itemSelect(selectedItem);
321331
} else {
322-
323-
// extract line number, if any
324-
var query = this.$searchField.val(),
325-
gotoLine = extractLineNumber(query);
326-
327332
// Navigate to file and line number
328333
var fullPath = selectedItem && selectedItem.fullPath;
329334
if (fullPath) {
@@ -336,16 +341,16 @@ define(function (require, exports, module) {
336341
this.modalBar.prepareClose();
337342
CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: fullPath})
338343
.done(function () {
339-
if (!isNaN(gotoLine)) {
344+
if (cursorPos) {
340345
var editor = EditorManager.getCurrentFullEditor();
341-
editor.setCursorPos(gotoLine, 0, true);
346+
editor.setCursorPos(cursorPos.line, cursorPos.ch, true);
342347
}
343348
})
344349
.always(function () {
345350
self.close();
346351
});
347-
} else if (!isNaN(gotoLine)) {
348-
EditorManager.getCurrentFullEditor().setCursorPos(gotoLine, 0, true);
352+
} else if (cursorPos) {
353+
EditorManager.getCurrentFullEditor().setCursorPos(cursorPos.line, cursorPos.ch, true);
349354
}
350355
}
351356

@@ -422,11 +427,11 @@ define(function (require, exports, module) {
422427
return true;
423428
}
424429

425-
var lineNum = extractLineNumber(query),
426-
editor = EditorManager.getCurrentFullEditor();
430+
var cursorPos = extractCursorPos(query),
431+
editor = EditorManager.getCurrentFullEditor();
427432

428433
// We could just use 0 and lineCount() here, but in future we might want this logic to work for inline editors as well.
429-
return (!isNaN(lineNum) && editor && lineNum >= editor.getFirstVisibleLine() && lineNum <= editor.getLastVisibleLine());
434+
return (cursorPos && editor && cursorPos.line >= editor.getFirstVisibleLine() && cursorPos.line <= editor.getLastVisibleLine());
430435
};
431436

432437
/**
@@ -545,6 +550,11 @@ define(function (require, exports, module) {
545550
return asyncResult.promise();
546551
}
547552

553+
var cursorPos = extractCursorPos(query);
554+
if (cursorPos && !cursorPos.local && cursorPos.query !== "") {
555+
query = query.replace(cursorPos.query, "");
556+
}
557+
548558
// First pass: filter based on search string; convert to SearchResults containing extra info
549559
// for sorting & display
550560
var filteredList = $.map(fileList, function (fileInfo) {
@@ -584,10 +594,10 @@ define(function (require, exports, module) {
584594
}
585595

586596
// "Go to line" mode is special-cased
587-
var gotoLine = extractLineNumber(query);
588-
if (!isNaN(gotoLine)) {
589-
var from = {line: gotoLine, ch: 0};
590-
var to = {line: gotoLine, ch: 99999};
597+
var cursorPos = extractCursorPos(query);
598+
if (cursorPos && cursorPos.local) {
599+
var from = {line: cursorPos.line, ch: cursorPos.ch},
600+
to = {line: cursorPos.line};
591601

592602
EditorManager.getCurrentFullEditor().setSelection(from, to, true);
593603
}

test/spec/QuickOpen-test.js

Lines changed: 95 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -93,83 +93,103 @@ define(function (require, exports, module) {
9393
// on the QuickOpen input.
9494
SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keyup", getSearchField()[0]);
9595
}
96-
97-
// TODO: fix me!
98-
// This test is currently turned off due to failures on Windows 7
99-
// See https://github.com/adobe/brackets/issues/2696
100-
it("can open a file and jump to a line, centering that line on the screen", function () {
101-
var err = false,
102-
editor,
103-
$scroller;
104-
105-
SpecRunnerUtils.loadProjectInTestWindow(testPath);
106-
107-
runs(function () {
108-
var promise = SpecRunnerUtils.openProjectFiles([]);
109-
waitsForDone(promise, "open project files");
110-
});
111-
112-
runs(function () {
113-
// Test quick open using a partial file name
114-
executeCommand(Commands.NAVIGATE_QUICK_OPEN);
96+
97+
/**
98+
* Creates a parameterized quick open test.
99+
* @param {string} quickOpenQuery The search query to execute after the NAVIGATE_QUICK_OPEN command.
100+
* @param {?string} gotoLineQuery The search query to execute after the NAVIGATE_GOTO_LINE command.
101+
* @param {string} file The name of the file that should be opened.
102+
* @param {number} line The line (1-based) where the cursor should be at the end of the operations.
103+
* @param {number} col The column (1-based) where the cursor should be at the end of the operations.
104+
* @return {function()} The configured test function.
105+
*/
106+
function getQuickOpenTest(quickOpenQuery, gotoLineQuery, file, line, col) {
107+
return function () {
108+
var err = false,
109+
editor,
110+
$scroller;
115111

116-
// need to set the timeout length here to ensure that it has a chance to load the file
117-
// list.
118-
enterSearchText("lines", 100);
119-
});
120-
121-
waitsFor(function () {
122-
return getSearchField().val() === "lines";
123-
}, "filename entry timeout", 1000);
124-
125-
runs(function () {
126-
pressEnter();
127-
});
128-
129-
waitsFor(function () {
130-
editor = EditorManager.getCurrentFullEditor();
131-
return editor !== null && getSearchBar().length === 0;
132-
}, "file opening timeout", 3000);
133-
134-
runs(function () {
135-
$scroller = test$(editor.getScrollerElement());
136-
137-
// Make sure we've opened the right file. It should open the longer one, because
138-
// of the scoring in the StringMatch algorithm.
139-
expect(DocumentManager.getCurrentDocument().file.name).toEqual("lotsOfLines.html");
140-
141-
// Test go to line
142-
executeCommand(Commands.NAVIGATE_GOTO_LINE);
143-
enterSearchText(":50");
144-
});
145-
146-
waitsFor(function () {
147-
return getSearchField().val() === ":50";
148-
}, "goto line entry timeout", 1000);
149-
150-
runs(function () {
151-
pressEnter();
152-
});
153-
154-
// wait for ModalBar to close
155-
waitsFor(function () {
156-
return getSearchBar().length === 0;
157-
}, "ModalBar close", 1000);
158-
159-
runs(function () {
160-
// The user enters a 1-based number, but the reported position
161-
// is 0 based, so we check for 49.
162-
expect(editor).toHaveCursorPosition(49, 0);
112+
SpecRunnerUtils.loadProjectInTestWindow(testPath);
163113

164-
// We expect the result to be scrolled roughly to the middle of the window.
165-
var offset = $scroller.offset().top;
166-
var editorHeight = $scroller.height();
167-
var cursorPos = editor._codeMirror.cursorCoords(null, "page").bottom;
114+
runs(function () {
115+
var promise = SpecRunnerUtils.openProjectFiles([]);
116+
waitsForDone(promise, "open project files");
117+
});
168118

169-
expect(cursorPos).toBeGreaterThan(editorHeight * 0.4 - offset);
170-
expect(cursorPos).toBeLessThan(editorHeight * 0.6 - offset);
171-
});
172-
});
173-
119+
runs(function () {
120+
// Test quick open using a partial file name
121+
executeCommand(Commands.NAVIGATE_QUICK_OPEN);
122+
123+
// need to set the timeout length here to ensure that it has a chance to load the file
124+
// list.
125+
enterSearchText(quickOpenQuery, 100);
126+
});
127+
128+
waitsFor(function () {
129+
return getSearchField().val() === quickOpenQuery;
130+
}, "filename entry timeout", 1000);
131+
132+
runs(function () {
133+
pressEnter();
134+
});
135+
136+
waitsFor(function () {
137+
editor = EditorManager.getCurrentFullEditor();
138+
return editor !== null && getSearchBar().length === 0;
139+
}, "file opening timeout", 3000);
140+
141+
runs(function () {
142+
$scroller = test$(editor.getScrollerElement());
143+
144+
// Make sure we've opened the right file. It should open the longer one, because
145+
// of the scoring in the StringMatch algorithm.
146+
expect(DocumentManager.getCurrentDocument().file.name).toEqual(file);
147+
148+
if (gotoLineQuery) {
149+
// Test go to line
150+
executeCommand(Commands.NAVIGATE_GOTO_LINE);
151+
enterSearchText(gotoLineQuery);
152+
}
153+
});
154+
155+
if (gotoLineQuery) {
156+
waitsFor(function () {
157+
return getSearchField().val() === gotoLineQuery;
158+
}, "goto line entry timeout", 1000);
159+
160+
runs(function () {
161+
pressEnter();
162+
});
163+
164+
// wait for ModalBar to close
165+
waitsFor(function () {
166+
return getSearchBar().length === 0;
167+
}, "ModalBar close", 1000);
168+
}
169+
170+
runs(function () {
171+
// The user enters a 1-based number, but the reported position
172+
// is 0 based, so we check for line-1, col-1.
173+
expect(editor).toHaveCursorPosition(line - 1, col - 1);
174+
175+
// We expect the result to be scrolled roughly to the middle of the window.
176+
var offset = $scroller.offset().top;
177+
var editorHeight = $scroller.height();
178+
var cursorPos = editor._codeMirror.cursorCoords(null, "page").bottom;
179+
180+
expect(cursorPos).toBeGreaterThan(editorHeight * 0.4 - offset);
181+
expect(cursorPos).toBeLessThan(editorHeight * 0.6 - offset);
182+
});
183+
};
184+
}
185+
186+
it("can open a file and jump to a line, centering that line on the screen",
187+
getQuickOpenTest("lines", ":50", "lotsOfLines.html", 50, 1));
188+
189+
it("can open a file and jump to a line and column, centering that line on the screen",
190+
getQuickOpenTest("lines", ":50,20", "lotsOfLines.html", 50, 20));
191+
192+
it("can directly open a file in a given line and column, centering that line on the screen",
193+
getQuickOpenTest("lines:150,20", null, "lotsOfLines.html", 150, 20));
174194
});
175195
});

0 commit comments

Comments
 (0)