diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 8bb22a672d..0dcf0a58dd 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -788,7 +788,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-phabricator-keyboard-shortcuts' => array( - 'uri' => '/res/94b009e2/rsrc/js/application/core/behavior-keyboard-shortcuts.js', + 'uri' => '/res/ea3ea05e/rsrc/js/application/core/behavior-keyboard-shortcuts.js', 'type' => 'js', 'requires' => array( @@ -1542,7 +1542,7 @@ celerity_register_resource_map(array( ), 'phabricator-keyboard-shortcut-manager' => array( - 'uri' => '/res/04767571/rsrc/js/application/core/KeyboardShortcutManager.js', + 'uri' => '/res/0be80136/rsrc/js/application/core/KeyboardShortcutManager.js', 'type' => 'js', 'requires' => array( @@ -1984,28 +1984,6 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/8d8e1030/differential.pkg.js', 'type' => 'js', ), - 'adcb05b5' => - array( - 'name' => 'core.pkg.js', - 'symbols' => - array( - 0 => 'javelin-mask', - 1 => 'javelin-workflow', - 2 => 'javelin-behavior-workflow', - 3 => 'javelin-behavior-aphront-form-disable-on-submit', - 4 => 'phabricator-keyboard-shortcut-manager', - 5 => 'phabricator-keyboard-shortcut', - 6 => 'javelin-behavior-phabricator-keyboard-shortcuts', - 7 => 'javelin-behavior-refresh-csrf', - 8 => 'javelin-behavior-phabricator-watch-anchor', - 9 => 'javelin-behavior-phabricator-autofocus', - 10 => 'phabricator-paste-file-upload', - 11 => 'phabricator-menu-item', - 12 => 'phabricator-dropdown-menu', - ), - 'uri' => '/res/pkg/adcb05b5/core.pkg.js', - 'type' => 'js', - ), 'ae7ea233' => array( 'name' => 'typeahead.pkg.js', @@ -2063,6 +2041,28 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/31583232/maniphest.pkg.css', 'type' => 'css', ), + 95944588 => + array( + 'name' => 'core.pkg.js', + 'symbols' => + array( + 0 => 'javelin-mask', + 1 => 'javelin-workflow', + 2 => 'javelin-behavior-workflow', + 3 => 'javelin-behavior-aphront-form-disable-on-submit', + 4 => 'phabricator-keyboard-shortcut-manager', + 5 => 'phabricator-keyboard-shortcut', + 6 => 'javelin-behavior-phabricator-keyboard-shortcuts', + 7 => 'javelin-behavior-refresh-csrf', + 8 => 'javelin-behavior-phabricator-watch-anchor', + 9 => 'javelin-behavior-phabricator-autofocus', + 10 => 'phabricator-paste-file-upload', + 11 => 'phabricator-menu-item', + 12 => 'phabricator-dropdown-menu', + ), + 'uri' => '/res/pkg/95944588/core.pkg.js', + 'type' => 'js', + ), ), 'reverse' => array( @@ -2093,7 +2093,7 @@ celerity_register_resource_map(array( 'javelin-behavior-aphront-basic-tokenizer' => 'ae7ea233', 'javelin-behavior-aphront-drag-and-drop' => '8d8e1030', 'javelin-behavior-aphront-drag-and-drop-textarea' => '8d8e1030', - 'javelin-behavior-aphront-form-disable-on-submit' => 'adcb05b5', + 'javelin-behavior-aphront-form-disable-on-submit' => '95944588', 'javelin-behavior-buoyant' => '8d8e1030', 'javelin-behavior-differential-accept-with-errors' => '8d8e1030', 'javelin-behavior-differential-add-reviewers-and-ccs' => '8d8e1030', @@ -2109,17 +2109,17 @@ celerity_register_resource_map(array( 'javelin-behavior-maniphest-transaction-controls' => '0ed3e020', 'javelin-behavior-maniphest-transaction-expand' => '0ed3e020', 'javelin-behavior-maniphest-transaction-preview' => '0ed3e020', - 'javelin-behavior-phabricator-autofocus' => 'adcb05b5', - 'javelin-behavior-phabricator-keyboard-shortcuts' => 'adcb05b5', + 'javelin-behavior-phabricator-autofocus' => '95944588', + 'javelin-behavior-phabricator-keyboard-shortcuts' => '95944588', 'javelin-behavior-phabricator-object-selector' => '8d8e1030', - 'javelin-behavior-phabricator-watch-anchor' => 'adcb05b5', - 'javelin-behavior-refresh-csrf' => 'adcb05b5', - 'javelin-behavior-workflow' => 'adcb05b5', + 'javelin-behavior-phabricator-watch-anchor' => '95944588', + 'javelin-behavior-refresh-csrf' => '95944588', + 'javelin-behavior-workflow' => '95944588', 'javelin-dom' => '4fbae2af', 'javelin-event' => '4fbae2af', 'javelin-install' => '4fbae2af', 'javelin-json' => '4fbae2af', - 'javelin-mask' => 'adcb05b5', + 'javelin-mask' => '95944588', 'javelin-request' => '4fbae2af', 'javelin-stratcom' => '4fbae2af', 'javelin-tokenizer' => 'ae7ea233', @@ -2131,7 +2131,7 @@ celerity_register_resource_map(array( 'javelin-uri' => '4fbae2af', 'javelin-util' => '4fbae2af', 'javelin-vector' => '4fbae2af', - 'javelin-workflow' => 'adcb05b5', + 'javelin-workflow' => '95944588', 'maniphest-task-detail-css' => '31583232', 'maniphest-task-summary-css' => '31583232', 'maniphest-transaction-detail-css' => '31583232', @@ -2141,13 +2141,13 @@ celerity_register_resource_map(array( 'phabricator-core-css' => 'e2934828', 'phabricator-directory-css' => 'e2934828', 'phabricator-drag-and-drop-file-upload' => '8d8e1030', - 'phabricator-dropdown-menu' => 'adcb05b5', + 'phabricator-dropdown-menu' => '95944588', 'phabricator-jump-nav' => 'e2934828', - 'phabricator-keyboard-shortcut' => 'adcb05b5', - 'phabricator-keyboard-shortcut-manager' => 'adcb05b5', - 'phabricator-menu-item' => 'adcb05b5', + 'phabricator-keyboard-shortcut' => '95944588', + 'phabricator-keyboard-shortcut-manager' => '95944588', + 'phabricator-menu-item' => '95944588', 'phabricator-object-selector-css' => '09c86840', - 'phabricator-paste-file-upload' => 'adcb05b5', + 'phabricator-paste-file-upload' => '95944588', 'phabricator-remarkup-css' => 'e2934828', 'phabricator-shaped-request' => '8d8e1030', 'phabricator-standard-page-view' => 'e2934828', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d4821baa1f..6263b4f642 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -584,6 +584,7 @@ phutil_register_library_map(array( 'PhabricatorImageTransformer' => 'applications/files/transform', 'PhabricatorInfrastructureTestCase' => 'infrastructure/__tests__', 'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin', + 'PhabricatorJumpNavHandler' => 'applications/search/engine/jumpnav', 'PhabricatorLintEngine' => 'infrastructure/lint/engine', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/localdisk', @@ -696,7 +697,6 @@ phutil_register_library_map(array( 'PhabricatorProjectProfileController' => 'applications/project/controller/profile', 'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit', 'PhabricatorProjectQuery' => 'applications/project/query/project', - 'PhabricatorProjectQueryUtil' => 'applications/project/query/util', 'PhabricatorProjectStatus' => 'applications/project/constants/status', 'PhabricatorProjectSubproject' => 'applications/project/storage/subproject', 'PhabricatorProjectTransaction' => 'applications/project/storage/transaction', diff --git a/src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php b/src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php index 9769b3a00b..020f122de7 100644 --- a/src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php +++ b/src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php @@ -1,7 +1,7 @@ isFormPost()) { $jump = $request->getStr('jump'); - $jump = trim($jump); - $help_href = PhabricatorEnv::getDocLink( - 'article/Jump_Nav_User_Guide.html'); + $response = PhabricatorJumpNavHandler::jumpPostResponse($jump); + if ($response) { + return $response; + } else { + $query = new PhabricatorSearchQuery(); + $query->setQuery($jump); + $query->save(); - $patterns = array( - '/^help/i' => 'uri:'.$help_href, - '/^d$/i' => 'uri:/differential/', - '/^r$/i' => 'uri:/diffusion/', - '/^t$/i' => 'uri:/maniphest/', - '/^p$/i' => 'uri:/project/', - '/^u$/i' => 'uri:/people/', - '/^r([A-Z]+)$/' => 'repository', - '/^r([A-Z]+)(\S+)$/' => 'commit', - '/^d(\d+)$/i' => 'revision', - '/^t(\d+)$/i' => 'task', - '/^p\s+(.+)$/i' => 'project', - '/^u\s+(\S+)$/i' => 'user', - '/^task:\s*(.+)/i' => 'create-task', - '/^(?:s|symbol)\s+(\S+)/i' => 'find-symbol', - ); - - - foreach ($patterns as $pattern => $effect) { - $matches = null; - if (preg_match($pattern, $jump, $matches)) { - if (!strncmp($effect, 'uri:', 4)) { - return id(new AphrontRedirectResponse()) - ->setURI(substr($effect, 4)); - } else { - switch ($effect) { - case 'repository': - return id(new AphrontRedirectResponse()) - ->setURI('/diffusion/'.$matches[1].'/'); - case 'commit': - return id(new AphrontRedirectResponse()) - ->setURI('/'.$matches[0]); - case 'revision': - return id(new AphrontRedirectResponse()) - ->setURI('/D'.$matches[1]); - case 'task': - return id(new AphrontRedirectResponse()) - ->setURI('/T'.$matches[1]); - case 'user': - return id(new AphrontRedirectResponse()) - ->setURI('/p/'.$matches[1].'/'); - case 'project': - $project = PhabricatorProjectQueryUtil - ::findCloselyNamedProject($matches[1]); - if ($project) { - return id(new AphrontRedirectResponse()) - ->setURI('/project/view/'.$project->getID().'/'); - } else { - $jump = $matches[1]; - } - break; - case 'find-symbol': - return id(new AphrontRedirectResponse()) - ->setURI('/diffusion/symbol/'.$matches[1].'/?jump=true'); - case 'create-task': - return id(new AphrontRedirectResponse()) - ->setURI('/maniphest/task/create/?title=' - .phutil_escape_uri($matches[1])); - default: - throw new Exception("Unknown jump effect '{$effect}'!"); - } - } - } + return id(new AphrontRedirectResponse()) + ->setURI('/search/'.$query->getQueryKey().'/'); } - - $query = new PhabricatorSearchQuery(); - $query->setQuery($jump); - $query->save(); - - return id(new AphrontRedirectResponse()) - ->setURI('/search/'.$query->getQueryKey().'/'); } diff --git a/src/applications/directory/controller/main/__init__.php b/src/applications/directory/controller/main/__init__.php index 8b9c63a804..8292e9a503 100644 --- a/src/applications/directory/controller/main/__init__.php +++ b/src/applications/directory/controller/main/__init__.php @@ -22,7 +22,7 @@ phutil_require_module('phabricator', 'applications/maniphest/query'); phutil_require_module('phabricator', 'applications/maniphest/view/tasklist'); phutil_require_module('phabricator', 'applications/phid/handle/data'); phutil_require_module('phabricator', 'applications/project/query/project'); -phutil_require_module('phabricator', 'applications/project/query/util'); +phutil_require_module('phabricator', 'applications/search/engine/jumpnav'); phutil_require_module('phabricator', 'applications/search/storage/query'); phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/env'); diff --git a/src/applications/project/query/util/PhabricatorProjectQueryUtil.php b/src/applications/project/query/util/PhabricatorProjectQueryUtil.php deleted file mode 100644 index f07ebea7b5..0000000000 --- a/src/applications/project/query/util/PhabricatorProjectQueryUtil.php +++ /dev/null @@ -1,46 +0,0 @@ -loadOneWhere( - 'name = %s', - name); - if ($project) { - return $project; - } else { // no exact match, try a fuzzy match - $projects = id(new PhabricatorProject())->loadAllWhere( - 'name LIKE %~', - $name); - if ($projects) { - $min_name_length = PHP_INT_MAX; - $best_project = null; - foreach ($projects as $project) { - $name_length = strlen($project->getName()); - if ($name_length <= $min_name_length) { - $min_name_length = $name_length; - $best_project = $project; - } - } - return $best_project; - } else { - return null; - } - } - } -} diff --git a/src/applications/search/controller/search/PhabricatorSearchController.php b/src/applications/search/controller/search/PhabricatorSearchController.php index cae5508233..4531eaf202 100644 --- a/src/applications/search/controller/search/PhabricatorSearchController.php +++ b/src/applications/search/controller/search/PhabricatorSearchController.php @@ -20,7 +20,6 @@ * @group search */ class PhabricatorSearchController extends PhabricatorSearchBaseController { - private $key; public function willProcessRequest(array $data) { @@ -42,60 +41,66 @@ class PhabricatorSearchController extends PhabricatorSearchBaseController { $query = new PhabricatorSearchQuery(); if ($request->isFormPost()) { - $query->setQuery($request->getStr('query')); - - if ($request->getStr('scope')) { - switch ($request->getStr('scope')) { - case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS: - $query->setParameter('open', 1); - $query->setParameter( - 'type', - PhabricatorPHIDConstants::PHID_TYPE_DREV); - break; - case PhabricatorSearchScope::SCOPE_OPEN_TASKS: - $query->setParameter('open', 1); - $query->setParameter( - 'type', - PhabricatorPHIDConstants::PHID_TYPE_TASK); - break; - case PhabricatorSearchScope::SCOPE_WIKI: - $query->setParameter( - 'type', - PhabricatorPHIDConstants::PHID_TYPE_WIKI); - break; - case PhabricatorSearchScope::SCOPE_COMMITS: - $query->setParameter( - 'type', - PhabricatorPHIDConstants::PHID_TYPE_CMIT); - break; - default: - break; - } + $query_str = $request->getStr('query'); + $response = PhabricatorJumpNavHandler::jumpPostResponse($query_str); + if ($response) { + return $response; } else { - if (strlen($request->getStr('type'))) { - $query->setParameter('type', $request->getStr('type')); + $query->setQuery($query_str); + + if ($request->getStr('scope')) { + switch ($request->getStr('scope')) { + case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS: + $query->setParameter('open', 1); + $query->setParameter( + 'type', + PhabricatorPHIDConstants::PHID_TYPE_DREV); + break; + case PhabricatorSearchScope::SCOPE_OPEN_TASKS: + $query->setParameter('open', 1); + $query->setParameter( + 'type', + PhabricatorPHIDConstants::PHID_TYPE_TASK); + break; + case PhabricatorSearchScope::SCOPE_WIKI: + $query->setParameter( + 'type', + PhabricatorPHIDConstants::PHID_TYPE_WIKI); + break; + case PhabricatorSearchScope::SCOPE_COMMITS: + $query->setParameter( + 'type', + PhabricatorPHIDConstants::PHID_TYPE_CMIT); + break; + default: + break; + } + } else { + if (strlen($request->getStr('type'))) { + $query->setParameter('type', $request->getStr('type')); + } + + if ($request->getArr('author')) { + $query->setParameter('author', $request->getArr('author')); + } + + if ($request->getArr('owner')) { + $query->setParameter('owner', $request->getArr('owner')); + } + + if ($request->getInt('open')) { + $query->setParameter('open', $request->getInt('open')); + } + + if ($request->getArr('project')) { + $query->setParameter('project', $request->getArr('project')); + } } - if ($request->getArr('author')) { - $query->setParameter('author', $request->getArr('author')); - } - - if ($request->getArr('owner')) { - $query->setParameter('owner', $request->getArr('owner')); - } - - if ($request->getInt('open')) { - $query->setParameter('open', $request->getInt('open')); - } - - if ($request->getArr('project')) { - $query->setParameter('project', $request->getArr('project')); - } + $query->save(); + return id(new AphrontRedirectResponse()) + ->setURI('/search/'.$query->getQueryKey().'/'); } - - $query->save(); - return id(new AphrontRedirectResponse()) - ->setURI('/search/'.$query->getQueryKey().'/'); } } diff --git a/src/applications/search/controller/search/__init__.php b/src/applications/search/controller/search/__init__.php index 9285ceecd0..3855c34119 100644 --- a/src/applications/search/controller/search/__init__.php +++ b/src/applications/search/controller/search/__init__.php @@ -12,6 +12,7 @@ phutil_require_module('phabricator', 'applications/phid/constants'); phutil_require_module('phabricator', 'applications/phid/handle/data'); phutil_require_module('phabricator', 'applications/search/constants/scope'); phutil_require_module('phabricator', 'applications/search/controller/base'); +phutil_require_module('phabricator', 'applications/search/engine/jumpnav'); phutil_require_module('phabricator', 'applications/search/selector/base'); phutil_require_module('phabricator', 'applications/search/storage/query'); phutil_require_module('phabricator', 'applications/search/view/searchresult'); diff --git a/src/applications/search/engine/jumpnav/PhabricatorJumpNavHandler.php b/src/applications/search/engine/jumpnav/PhabricatorJumpNavHandler.php new file mode 100644 index 0000000000..6d56995995 --- /dev/null +++ b/src/applications/search/engine/jumpnav/PhabricatorJumpNavHandler.php @@ -0,0 +1,118 @@ + 'uri:'.$help_href, + '/^d$/i' => 'uri:/differential/', + '/^r$/i' => 'uri:/diffusion/', + '/^t$/i' => 'uri:/maniphest/', + '/^p$/i' => 'uri:/project/', + '/^u$/i' => 'uri:/people/', + '/^r([A-Z]+)$/' => 'repository', + '/^r([A-Z]+)(\S+)$/' => 'commit', + '/^d(\d+)$/i' => 'revision', + '/^t(\d+)$/i' => 'task', + '/^p\s+(.+)$/i' => 'project', + '/^u\s+(\S+)$/i' => 'user', + '/^task:\s*(.+)/i' => 'create-task', + '/^(?:s|symbol)\s+(\S+)/i' => 'find-symbol', + ); + + + foreach ($patterns as $pattern => $effect) { + $matches = null; + if (preg_match($pattern, $jump, $matches)) { + if (!strncmp($effect, 'uri:', 4)) { + return id(new AphrontRedirectResponse()) + ->setURI(substr($effect, 4)); + } else { + switch ($effect) { + case 'repository': + return id(new AphrontRedirectResponse()) + ->setURI('/diffusion/'.$matches[1].'/'); + case 'commit': + return id(new AphrontRedirectResponse()) + ->setURI('/'.$matches[0]); + case 'revision': + return id(new AphrontRedirectResponse()) + ->setURI('/D'.$matches[1]); + case 'task': + return id(new AphrontRedirectResponse()) + ->setURI('/T'.$matches[1]); + case 'user': + return id(new AphrontRedirectResponse()) + ->setURI('/p/'.$matches[1].'/'); + case 'project': + $project = static::findCloselyNamedProject($matches[1]); + if ($project) { + return id(new AphrontRedirectResponse()) + ->setURI('/project/view/'.$project->getID().'/'); + } else { + $jump = $matches[1]; + } + break; + case 'find-symbol': + return id(new AphrontRedirectResponse()) + ->setURI('/diffusion/symbol/'.$matches[1].'/?jump=true'); + case 'create-task': + return id(new AphrontRedirectResponse()) + ->setURI('/maniphest/task/create/?title=' + .phutil_escape_uri($matches[1])); + default: + throw new Exception("Unknown jump effect '{$effect}'!"); + } + } + } + } + + return null; + } + + private static function findCloselyNamedProject($name) { + $project = id(new PhabricatorProject())->loadOneWhere( + 'name = %s', + name); + if ($project) { + return $project; + } else { // no exact match, try a fuzzy match + $projects = id(new PhabricatorProject())->loadAllWhere( + 'name LIKE %~', + $name); + if ($projects) { + $min_name_length = PHP_INT_MAX; + $best_project = null; + foreach ($projects as $project) { + $name_length = strlen($project->getName()); + if ($name_length <= $min_name_length) { + $min_name_length = $name_length; + $best_project = $project; + } + } + return $best_project; + } else { + return null; + } + } + } +} diff --git a/src/applications/project/query/util/__init__.php b/src/applications/search/engine/jumpnav/__init__.php similarity index 50% rename from src/applications/project/query/util/__init__.php rename to src/applications/search/engine/jumpnav/__init__.php index 4685a31eb8..9d87596f4d 100644 --- a/src/applications/project/query/util/__init__.php +++ b/src/applications/search/engine/jumpnav/__init__.php @@ -6,9 +6,12 @@ +phutil_require_module('phabricator', 'aphront/response/redirect'); phutil_require_module('phabricator', 'applications/project/storage/project'); +phutil_require_module('phabricator', 'infrastructure/env'); +phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'utils'); -phutil_require_source('PhabricatorProjectQueryUtil.php'); +phutil_require_source('PhabricatorJumpNavHandler.php'); diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php index f5796eaa38..370529a76d 100644 --- a/src/view/page/standard/PhabricatorStandardPageView.php +++ b/src/view/page/standard/PhabricatorStandardPageView.php @@ -288,7 +288,7 @@ class PhabricatorStandardPageView extends AphrontPageView { 'method' => 'post', 'style' => 'display: inline', ), - ''. + ''. ' in '. AphrontFormSelectControl::renderSelectTag( $this->getSearchDefaultScope(), diff --git a/webroot/rsrc/js/application/core/KeyboardShortcutManager.js b/webroot/rsrc/js/application/core/KeyboardShortcutManager.js index e81071ed39..637fc1f8c3 100644 --- a/webroot/rsrc/js/application/core/KeyboardShortcutManager.js +++ b/webroot/rsrc/js/application/core/KeyboardShortcutManager.js @@ -122,6 +122,7 @@ JX.install('KeyboardShortcutManager', { for (var jj = 0; jj < keys.length; jj++) { if (keys[jj] == key) { shortcuts[ii].getHandler()(this); + e.kill(); // Consume the event return; } } diff --git a/webroot/rsrc/js/application/core/behavior-keyboard-shortcuts.js b/webroot/rsrc/js/application/core/behavior-keyboard-shortcuts.js index f01a382933..05be4a6171 100644 --- a/webroot/rsrc/js/application/core/behavior-keyboard-shortcuts.js +++ b/webroot/rsrc/js/application/core/behavior-keyboard-shortcuts.js @@ -28,4 +28,14 @@ JX.behavior('phabricator-keyboard-shortcuts', function(config) { workflow.start(); }) .register(); + + desc = 'Give keyboard focus to the search box.'; + new JX.KeyboardShortcut('/', desc) + .setHandler(function() { + var search = JX.$("standard-search-box"); + search.focus(); + search.select(); + }) + .register(); + });