findController.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  16. return new (P || (P = Promise))(function (resolve, reject) {
  17. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  18. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  19. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  20. step((generator = generator.apply(thisArg, _arguments || [])).next());
  21. });
  22. };
  23. import { Delayer } from '../../../base/common/async.js';
  24. import { Disposable } from '../../../base/common/lifecycle.js';
  25. import * as strings from '../../../base/common/strings.js';
  26. import { EditorAction, EditorCommand, MultiEditorAction, registerEditorAction, registerEditorCommand, registerEditorContribution, registerMultiEditorAction } from '../../browser/editorExtensions.js';
  27. import { EditorContextKeys } from '../../common/editorContextKeys.js';
  28. import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, CONTEXT_REPLACE_INPUT_FOCUSED, FindModelBoundToEditorModel, FIND_IDS, ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from './findModel.js';
  29. import { FindOptionsWidget } from './findOptionsWidget.js';
  30. import { FindReplaceState } from './findState.js';
  31. import { FindWidget } from './findWidget.js';
  32. import * as nls from '../../../nls.js';
  33. import { MenuId } from '../../../platform/actions/common/actions.js';
  34. import { IClipboardService } from '../../../platform/clipboard/common/clipboardService.js';
  35. import { ContextKeyExpr, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
  36. import { IContextViewService } from '../../../platform/contextview/browser/contextView.js';
  37. import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
  38. import { INotificationService } from '../../../platform/notification/common/notification.js';
  39. import { IStorageService } from '../../../platform/storage/common/storage.js';
  40. import { IThemeService } from '../../../platform/theme/common/themeService.js';
  41. const SEARCH_STRING_MAX_LENGTH = 524288;
  42. export function getSelectionSearchString(editor, seedSearchStringFromSelection = 'single', seedSearchStringFromNonEmptySelection = false) {
  43. if (!editor.hasModel()) {
  44. return null;
  45. }
  46. const selection = editor.getSelection();
  47. // if selection spans multiple lines, default search string to empty
  48. if ((seedSearchStringFromSelection === 'single' && selection.startLineNumber === selection.endLineNumber)
  49. || seedSearchStringFromSelection === 'multiple') {
  50. if (selection.isEmpty()) {
  51. const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition());
  52. if (wordAtPosition && (false === seedSearchStringFromNonEmptySelection)) {
  53. return wordAtPosition.word;
  54. }
  55. }
  56. else {
  57. if (editor.getModel().getValueLengthInRange(selection) < SEARCH_STRING_MAX_LENGTH) {
  58. return editor.getModel().getValueInRange(selection);
  59. }
  60. }
  61. }
  62. return null;
  63. }
  64. let CommonFindController = class CommonFindController extends Disposable {
  65. constructor(editor, contextKeyService, storageService, clipboardService) {
  66. super();
  67. this._editor = editor;
  68. this._findWidgetVisible = CONTEXT_FIND_WIDGET_VISIBLE.bindTo(contextKeyService);
  69. this._contextKeyService = contextKeyService;
  70. this._storageService = storageService;
  71. this._clipboardService = clipboardService;
  72. this._updateHistoryDelayer = new Delayer(500);
  73. this._state = this._register(new FindReplaceState());
  74. this.loadQueryState();
  75. this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
  76. this._model = null;
  77. this._register(this._editor.onDidChangeModel(() => {
  78. let shouldRestartFind = (this._editor.getModel() && this._state.isRevealed);
  79. this.disposeModel();
  80. this._state.change({
  81. searchScope: null,
  82. matchCase: this._storageService.getBoolean('editor.matchCase', 1 /* WORKSPACE */, false),
  83. wholeWord: this._storageService.getBoolean('editor.wholeWord', 1 /* WORKSPACE */, false),
  84. isRegex: this._storageService.getBoolean('editor.isRegex', 1 /* WORKSPACE */, false),
  85. preserveCase: this._storageService.getBoolean('editor.preserveCase', 1 /* WORKSPACE */, false)
  86. }, false);
  87. if (shouldRestartFind) {
  88. this._start({
  89. forceRevealReplace: false,
  90. seedSearchStringFromSelection: 'none',
  91. seedSearchStringFromNonEmptySelection: false,
  92. seedSearchStringFromGlobalClipboard: false,
  93. shouldFocus: 0 /* NoFocusChange */,
  94. shouldAnimate: false,
  95. updateSearchScope: false,
  96. loop: this._editor.getOption(35 /* find */).loop
  97. });
  98. }
  99. }));
  100. }
  101. get editor() {
  102. return this._editor;
  103. }
  104. static get(editor) {
  105. return editor.getContribution(CommonFindController.ID);
  106. }
  107. dispose() {
  108. this.disposeModel();
  109. super.dispose();
  110. }
  111. disposeModel() {
  112. if (this._model) {
  113. this._model.dispose();
  114. this._model = null;
  115. }
  116. }
  117. _onStateChanged(e) {
  118. this.saveQueryState(e);
  119. if (e.isRevealed) {
  120. if (this._state.isRevealed) {
  121. this._findWidgetVisible.set(true);
  122. }
  123. else {
  124. this._findWidgetVisible.reset();
  125. this.disposeModel();
  126. }
  127. }
  128. if (e.searchString) {
  129. this.setGlobalBufferTerm(this._state.searchString);
  130. }
  131. }
  132. saveQueryState(e) {
  133. if (e.isRegex) {
  134. this._storageService.store('editor.isRegex', this._state.actualIsRegex, 1 /* WORKSPACE */, 0 /* USER */);
  135. }
  136. if (e.wholeWord) {
  137. this._storageService.store('editor.wholeWord', this._state.actualWholeWord, 1 /* WORKSPACE */, 0 /* USER */);
  138. }
  139. if (e.matchCase) {
  140. this._storageService.store('editor.matchCase', this._state.actualMatchCase, 1 /* WORKSPACE */, 0 /* USER */);
  141. }
  142. if (e.preserveCase) {
  143. this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, 1 /* WORKSPACE */, 0 /* USER */);
  144. }
  145. }
  146. loadQueryState() {
  147. this._state.change({
  148. matchCase: this._storageService.getBoolean('editor.matchCase', 1 /* WORKSPACE */, this._state.matchCase),
  149. wholeWord: this._storageService.getBoolean('editor.wholeWord', 1 /* WORKSPACE */, this._state.wholeWord),
  150. isRegex: this._storageService.getBoolean('editor.isRegex', 1 /* WORKSPACE */, this._state.isRegex),
  151. preserveCase: this._storageService.getBoolean('editor.preserveCase', 1 /* WORKSPACE */, this._state.preserveCase)
  152. }, false);
  153. }
  154. isFindInputFocused() {
  155. return !!CONTEXT_FIND_INPUT_FOCUSED.getValue(this._contextKeyService);
  156. }
  157. getState() {
  158. return this._state;
  159. }
  160. closeFindWidget() {
  161. this._state.change({
  162. isRevealed: false,
  163. searchScope: null
  164. }, false);
  165. this._editor.focus();
  166. }
  167. toggleCaseSensitive() {
  168. this._state.change({ matchCase: !this._state.matchCase }, false);
  169. if (!this._state.isRevealed) {
  170. this.highlightFindOptions();
  171. }
  172. }
  173. toggleWholeWords() {
  174. this._state.change({ wholeWord: !this._state.wholeWord }, false);
  175. if (!this._state.isRevealed) {
  176. this.highlightFindOptions();
  177. }
  178. }
  179. toggleRegex() {
  180. this._state.change({ isRegex: !this._state.isRegex }, false);
  181. if (!this._state.isRevealed) {
  182. this.highlightFindOptions();
  183. }
  184. }
  185. togglePreserveCase() {
  186. this._state.change({ preserveCase: !this._state.preserveCase }, false);
  187. if (!this._state.isRevealed) {
  188. this.highlightFindOptions();
  189. }
  190. }
  191. toggleSearchScope() {
  192. if (this._state.searchScope) {
  193. this._state.change({ searchScope: null }, true);
  194. }
  195. else {
  196. if (this._editor.hasModel()) {
  197. let selections = this._editor.getSelections();
  198. selections.map(selection => {
  199. if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
  200. selection = selection.setEndPosition(selection.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
  201. }
  202. if (!selection.isEmpty()) {
  203. return selection;
  204. }
  205. return null;
  206. }).filter(element => !!element);
  207. if (selections.length) {
  208. this._state.change({ searchScope: selections }, true);
  209. }
  210. }
  211. }
  212. }
  213. setSearchString(searchString) {
  214. if (this._state.isRegex) {
  215. searchString = strings.escapeRegExpCharacters(searchString);
  216. }
  217. this._state.change({ searchString: searchString }, false);
  218. }
  219. highlightFindOptions(ignoreWhenVisible = false) {
  220. // overwritten in subclass
  221. }
  222. _start(opts, newState) {
  223. return __awaiter(this, void 0, void 0, function* () {
  224. this.disposeModel();
  225. if (!this._editor.hasModel()) {
  226. // cannot do anything with an editor that doesn't have a model...
  227. return;
  228. }
  229. let stateChanges = Object.assign(Object.assign({}, newState), { isRevealed: true });
  230. if (opts.seedSearchStringFromSelection === 'single') {
  231. let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection, opts.seedSearchStringFromNonEmptySelection);
  232. if (selectionSearchString) {
  233. if (this._state.isRegex) {
  234. stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString);
  235. }
  236. else {
  237. stateChanges.searchString = selectionSearchString;
  238. }
  239. }
  240. }
  241. else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) {
  242. let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection);
  243. if (selectionSearchString) {
  244. stateChanges.searchString = selectionSearchString;
  245. }
  246. }
  247. if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) {
  248. let selectionSearchString = yield this.getGlobalBufferTerm();
  249. if (!this._editor.hasModel()) {
  250. // the editor has lost its model in the meantime
  251. return;
  252. }
  253. if (selectionSearchString) {
  254. stateChanges.searchString = selectionSearchString;
  255. }
  256. }
  257. // Overwrite isReplaceRevealed
  258. if (opts.forceRevealReplace || stateChanges.isReplaceRevealed) {
  259. stateChanges.isReplaceRevealed = true;
  260. }
  261. else if (!this._findWidgetVisible.get()) {
  262. stateChanges.isReplaceRevealed = false;
  263. }
  264. if (opts.updateSearchScope) {
  265. let currentSelections = this._editor.getSelections();
  266. if (currentSelections.some(selection => !selection.isEmpty())) {
  267. stateChanges.searchScope = currentSelections;
  268. }
  269. }
  270. stateChanges.loop = opts.loop;
  271. this._state.change(stateChanges, false);
  272. if (!this._model) {
  273. this._model = new FindModelBoundToEditorModel(this._editor, this._state);
  274. }
  275. });
  276. }
  277. start(opts, newState) {
  278. return this._start(opts, newState);
  279. }
  280. moveToNextMatch() {
  281. if (this._model) {
  282. this._model.moveToNextMatch();
  283. return true;
  284. }
  285. return false;
  286. }
  287. moveToPrevMatch() {
  288. if (this._model) {
  289. this._model.moveToPrevMatch();
  290. return true;
  291. }
  292. return false;
  293. }
  294. replace() {
  295. if (this._model) {
  296. this._model.replace();
  297. return true;
  298. }
  299. return false;
  300. }
  301. replaceAll() {
  302. if (this._model) {
  303. this._model.replaceAll();
  304. return true;
  305. }
  306. return false;
  307. }
  308. selectAllMatches() {
  309. if (this._model) {
  310. this._model.selectAllMatches();
  311. this._editor.focus();
  312. return true;
  313. }
  314. return false;
  315. }
  316. getGlobalBufferTerm() {
  317. return __awaiter(this, void 0, void 0, function* () {
  318. if (this._editor.getOption(35 /* find */).globalFindClipboard
  319. && this._editor.hasModel()
  320. && !this._editor.getModel().isTooLargeForSyncing()) {
  321. return this._clipboardService.readFindText();
  322. }
  323. return '';
  324. });
  325. }
  326. setGlobalBufferTerm(text) {
  327. if (this._editor.getOption(35 /* find */).globalFindClipboard
  328. && this._editor.hasModel()
  329. && !this._editor.getModel().isTooLargeForSyncing()) {
  330. // intentionally not awaited
  331. this._clipboardService.writeFindText(text);
  332. }
  333. }
  334. };
  335. CommonFindController.ID = 'editor.contrib.findController';
  336. CommonFindController = __decorate([
  337. __param(1, IContextKeyService),
  338. __param(2, IStorageService),
  339. __param(3, IClipboardService)
  340. ], CommonFindController);
  341. export { CommonFindController };
  342. let FindController = class FindController extends CommonFindController {
  343. constructor(editor, _contextViewService, _contextKeyService, _keybindingService, _themeService, _notificationService, _storageService, clipboardService) {
  344. super(editor, _contextKeyService, _storageService, clipboardService);
  345. this._contextViewService = _contextViewService;
  346. this._keybindingService = _keybindingService;
  347. this._themeService = _themeService;
  348. this._notificationService = _notificationService;
  349. this._widget = null;
  350. this._findOptionsWidget = null;
  351. }
  352. _start(opts, newState) {
  353. const _super = Object.create(null, {
  354. _start: { get: () => super._start }
  355. });
  356. return __awaiter(this, void 0, void 0, function* () {
  357. if (!this._widget) {
  358. this._createFindWidget();
  359. }
  360. const selection = this._editor.getSelection();
  361. let updateSearchScope = false;
  362. switch (this._editor.getOption(35 /* find */).autoFindInSelection) {
  363. case 'always':
  364. updateSearchScope = true;
  365. break;
  366. case 'never':
  367. updateSearchScope = false;
  368. break;
  369. case 'multiline':
  370. const isSelectionMultipleLine = !!selection && selection.startLineNumber !== selection.endLineNumber;
  371. updateSearchScope = isSelectionMultipleLine;
  372. break;
  373. default:
  374. break;
  375. }
  376. opts.updateSearchScope = opts.updateSearchScope || updateSearchScope;
  377. yield _super._start.call(this, opts, newState);
  378. if (this._widget) {
  379. if (opts.shouldFocus === 2 /* FocusReplaceInput */) {
  380. this._widget.focusReplaceInput();
  381. }
  382. else if (opts.shouldFocus === 1 /* FocusFindInput */) {
  383. this._widget.focusFindInput();
  384. }
  385. }
  386. });
  387. }
  388. highlightFindOptions(ignoreWhenVisible = false) {
  389. if (!this._widget) {
  390. this._createFindWidget();
  391. }
  392. if (this._state.isRevealed && !ignoreWhenVisible) {
  393. this._widget.highlightFindOptions();
  394. }
  395. else {
  396. this._findOptionsWidget.highlightFindOptions();
  397. }
  398. }
  399. _createFindWidget() {
  400. this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService));
  401. this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
  402. }
  403. };
  404. FindController = __decorate([
  405. __param(1, IContextViewService),
  406. __param(2, IContextKeyService),
  407. __param(3, IKeybindingService),
  408. __param(4, IThemeService),
  409. __param(5, INotificationService),
  410. __param(6, IStorageService),
  411. __param(7, IClipboardService)
  412. ], FindController);
  413. export { FindController };
  414. export const StartFindAction = registerMultiEditorAction(new MultiEditorAction({
  415. id: FIND_IDS.StartFindAction,
  416. label: nls.localize('startFindAction', "Find"),
  417. alias: 'Find',
  418. precondition: ContextKeyExpr.or(EditorContextKeys.focus, ContextKeyExpr.has('editorIsOpen')),
  419. kbOpts: {
  420. kbExpr: null,
  421. primary: 2048 /* CtrlCmd */ | 36 /* KeyF */,
  422. weight: 100 /* EditorContrib */
  423. },
  424. menuOpts: {
  425. menuId: MenuId.MenubarEditMenu,
  426. group: '3_find',
  427. title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"),
  428. order: 1
  429. }
  430. }));
  431. StartFindAction.addImplementation(0, (accessor, editor, args) => {
  432. const controller = CommonFindController.get(editor);
  433. if (!controller) {
  434. return false;
  435. }
  436. return controller.start({
  437. forceRevealReplace: false,
  438. seedSearchStringFromSelection: editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
  439. seedSearchStringFromNonEmptySelection: editor.getOption(35 /* find */).seedSearchStringFromSelection === 'selection',
  440. seedSearchStringFromGlobalClipboard: editor.getOption(35 /* find */).globalFindClipboard,
  441. shouldFocus: 1 /* FocusFindInput */,
  442. shouldAnimate: true,
  443. updateSearchScope: false,
  444. loop: editor.getOption(35 /* find */).loop
  445. });
  446. });
  447. const findArgDescription = {
  448. description: 'Open a new In-Editor Find Widget.',
  449. args: [{
  450. name: 'Open a new In-Editor Find Widget args',
  451. schema: {
  452. properties: {
  453. searchString: { type: 'string' },
  454. replaceString: { type: 'string' },
  455. regex: { type: 'boolean' },
  456. regexOverride: {
  457. type: 'number',
  458. description: nls.localize('actions.find.isRegexOverride', 'Overrides "Use Regular Expression" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False')
  459. },
  460. wholeWord: { type: 'boolean' },
  461. wholeWordOverride: {
  462. type: 'number',
  463. description: nls.localize('actions.find.wholeWordOverride', 'Overrides "Match Whole Word" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False')
  464. },
  465. matchCase: { type: 'boolean' },
  466. matchCaseOverride: {
  467. type: 'number',
  468. description: nls.localize('actions.find.matchCaseOverride', 'Overrides "Math Case" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False')
  469. },
  470. preserveCase: { type: 'boolean' },
  471. preserveCaseOverride: {
  472. type: 'number',
  473. description: nls.localize('actions.find.preserveCaseOverride', 'Overrides "Preserve Case" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False')
  474. },
  475. findInSelection: { type: 'boolean' },
  476. }
  477. }
  478. }]
  479. };
  480. export class StartFindWithArgsAction extends EditorAction {
  481. constructor() {
  482. super({
  483. id: FIND_IDS.StartFindWithArgs,
  484. label: nls.localize('startFindWithArgsAction', "Find With Arguments"),
  485. alias: 'Find With Arguments',
  486. precondition: undefined,
  487. kbOpts: {
  488. kbExpr: null,
  489. primary: 0,
  490. weight: 100 /* EditorContrib */
  491. },
  492. description: findArgDescription
  493. });
  494. }
  495. run(accessor, editor, args) {
  496. return __awaiter(this, void 0, void 0, function* () {
  497. let controller = CommonFindController.get(editor);
  498. if (controller) {
  499. const newState = args ? {
  500. searchString: args.searchString,
  501. replaceString: args.replaceString,
  502. isReplaceRevealed: args.replaceString !== undefined,
  503. isRegex: args.isRegex,
  504. // isRegexOverride: args.regexOverride,
  505. wholeWord: args.matchWholeWord,
  506. // wholeWordOverride: args.wholeWordOverride,
  507. matchCase: args.isCaseSensitive,
  508. // matchCaseOverride: args.matchCaseOverride,
  509. preserveCase: args.preserveCase,
  510. // preserveCaseOverride: args.preserveCaseOverride,
  511. } : {};
  512. yield controller.start({
  513. forceRevealReplace: false,
  514. seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
  515. seedSearchStringFromNonEmptySelection: editor.getOption(35 /* find */).seedSearchStringFromSelection === 'selection',
  516. seedSearchStringFromGlobalClipboard: true,
  517. shouldFocus: 1 /* FocusFindInput */,
  518. shouldAnimate: true,
  519. updateSearchScope: (args === null || args === void 0 ? void 0 : args.findInSelection) || false,
  520. loop: editor.getOption(35 /* find */).loop
  521. }, newState);
  522. controller.setGlobalBufferTerm(controller.getState().searchString);
  523. }
  524. });
  525. }
  526. }
  527. export class StartFindWithSelectionAction extends EditorAction {
  528. constructor() {
  529. super({
  530. id: FIND_IDS.StartFindWithSelection,
  531. label: nls.localize('startFindWithSelectionAction', "Find With Selection"),
  532. alias: 'Find With Selection',
  533. precondition: undefined,
  534. kbOpts: {
  535. kbExpr: null,
  536. primary: 0,
  537. mac: {
  538. primary: 2048 /* CtrlCmd */ | 35 /* KeyE */,
  539. },
  540. weight: 100 /* EditorContrib */
  541. }
  542. });
  543. }
  544. run(accessor, editor) {
  545. return __awaiter(this, void 0, void 0, function* () {
  546. let controller = CommonFindController.get(editor);
  547. if (controller) {
  548. yield controller.start({
  549. forceRevealReplace: false,
  550. seedSearchStringFromSelection: 'multiple',
  551. seedSearchStringFromNonEmptySelection: false,
  552. seedSearchStringFromGlobalClipboard: false,
  553. shouldFocus: 0 /* NoFocusChange */,
  554. shouldAnimate: true,
  555. updateSearchScope: false,
  556. loop: editor.getOption(35 /* find */).loop
  557. });
  558. controller.setGlobalBufferTerm(controller.getState().searchString);
  559. }
  560. });
  561. }
  562. }
  563. export class MatchFindAction extends EditorAction {
  564. run(accessor, editor) {
  565. return __awaiter(this, void 0, void 0, function* () {
  566. let controller = CommonFindController.get(editor);
  567. if (controller && !this._run(controller)) {
  568. yield controller.start({
  569. forceRevealReplace: false,
  570. seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
  571. seedSearchStringFromNonEmptySelection: editor.getOption(35 /* find */).seedSearchStringFromSelection === 'selection',
  572. seedSearchStringFromGlobalClipboard: true,
  573. shouldFocus: 0 /* NoFocusChange */,
  574. shouldAnimate: true,
  575. updateSearchScope: false,
  576. loop: editor.getOption(35 /* find */).loop
  577. });
  578. this._run(controller);
  579. }
  580. });
  581. }
  582. }
  583. export class NextMatchFindAction extends MatchFindAction {
  584. constructor() {
  585. super({
  586. id: FIND_IDS.NextMatchFindAction,
  587. label: nls.localize('findNextMatchAction', "Find Next"),
  588. alias: 'Find Next',
  589. precondition: undefined,
  590. kbOpts: [{
  591. kbExpr: EditorContextKeys.focus,
  592. primary: 61 /* F3 */,
  593. mac: { primary: 2048 /* CtrlCmd */ | 37 /* KeyG */, secondary: [61 /* F3 */] },
  594. weight: 100 /* EditorContrib */
  595. }, {
  596. kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
  597. primary: 3 /* Enter */,
  598. weight: 100 /* EditorContrib */
  599. }]
  600. });
  601. }
  602. _run(controller) {
  603. const result = controller.moveToNextMatch();
  604. if (result) {
  605. controller.editor.pushUndoStop();
  606. return true;
  607. }
  608. return false;
  609. }
  610. }
  611. export class PreviousMatchFindAction extends MatchFindAction {
  612. constructor() {
  613. super({
  614. id: FIND_IDS.PreviousMatchFindAction,
  615. label: nls.localize('findPreviousMatchAction', "Find Previous"),
  616. alias: 'Find Previous',
  617. precondition: undefined,
  618. kbOpts: [{
  619. kbExpr: EditorContextKeys.focus,
  620. primary: 1024 /* Shift */ | 61 /* F3 */,
  621. mac: { primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 37 /* KeyG */, secondary: [1024 /* Shift */ | 61 /* F3 */] },
  622. weight: 100 /* EditorContrib */
  623. }, {
  624. kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
  625. primary: 1024 /* Shift */ | 3 /* Enter */,
  626. weight: 100 /* EditorContrib */
  627. }
  628. ]
  629. });
  630. }
  631. _run(controller) {
  632. return controller.moveToPrevMatch();
  633. }
  634. }
  635. export class SelectionMatchFindAction extends EditorAction {
  636. run(accessor, editor) {
  637. return __awaiter(this, void 0, void 0, function* () {
  638. let controller = CommonFindController.get(editor);
  639. if (!controller) {
  640. return;
  641. }
  642. const seedSearchStringFromNonEmptySelection = editor.getOption(35 /* find */).seedSearchStringFromSelection === 'selection';
  643. let selectionSearchString = null;
  644. if (editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never') {
  645. selectionSearchString = getSelectionSearchString(editor, 'single', seedSearchStringFromNonEmptySelection);
  646. }
  647. if (selectionSearchString) {
  648. controller.setSearchString(selectionSearchString);
  649. }
  650. if (!this._run(controller)) {
  651. yield controller.start({
  652. forceRevealReplace: false,
  653. seedSearchStringFromSelection: editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
  654. seedSearchStringFromNonEmptySelection: seedSearchStringFromNonEmptySelection,
  655. seedSearchStringFromGlobalClipboard: false,
  656. shouldFocus: 0 /* NoFocusChange */,
  657. shouldAnimate: true,
  658. updateSearchScope: false,
  659. loop: editor.getOption(35 /* find */).loop
  660. });
  661. this._run(controller);
  662. }
  663. });
  664. }
  665. }
  666. export class NextSelectionMatchFindAction extends SelectionMatchFindAction {
  667. constructor() {
  668. super({
  669. id: FIND_IDS.NextSelectionMatchFindAction,
  670. label: nls.localize('nextSelectionMatchFindAction', "Find Next Selection"),
  671. alias: 'Find Next Selection',
  672. precondition: undefined,
  673. kbOpts: {
  674. kbExpr: EditorContextKeys.focus,
  675. primary: 2048 /* CtrlCmd */ | 61 /* F3 */,
  676. weight: 100 /* EditorContrib */
  677. }
  678. });
  679. }
  680. _run(controller) {
  681. return controller.moveToNextMatch();
  682. }
  683. }
  684. export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
  685. constructor() {
  686. super({
  687. id: FIND_IDS.PreviousSelectionMatchFindAction,
  688. label: nls.localize('previousSelectionMatchFindAction', "Find Previous Selection"),
  689. alias: 'Find Previous Selection',
  690. precondition: undefined,
  691. kbOpts: {
  692. kbExpr: EditorContextKeys.focus,
  693. primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 61 /* F3 */,
  694. weight: 100 /* EditorContrib */
  695. }
  696. });
  697. }
  698. _run(controller) {
  699. return controller.moveToPrevMatch();
  700. }
  701. }
  702. export const StartFindReplaceAction = registerMultiEditorAction(new MultiEditorAction({
  703. id: FIND_IDS.StartFindReplaceAction,
  704. label: nls.localize('startReplace', "Replace"),
  705. alias: 'Replace',
  706. precondition: ContextKeyExpr.or(EditorContextKeys.focus, ContextKeyExpr.has('editorIsOpen')),
  707. kbOpts: {
  708. kbExpr: null,
  709. primary: 2048 /* CtrlCmd */ | 38 /* KeyH */,
  710. mac: { primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 36 /* KeyF */ },
  711. weight: 100 /* EditorContrib */
  712. },
  713. menuOpts: {
  714. menuId: MenuId.MenubarEditMenu,
  715. group: '3_find',
  716. title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
  717. order: 2
  718. }
  719. }));
  720. StartFindReplaceAction.addImplementation(0, (accessor, editor, args) => {
  721. if (!editor.hasModel() || editor.getOption(80 /* readOnly */)) {
  722. return false;
  723. }
  724. const controller = CommonFindController.get(editor);
  725. if (!controller) {
  726. return false;
  727. }
  728. const currentSelection = editor.getSelection();
  729. const findInputFocused = controller.isFindInputFocused();
  730. // we only seed search string from selection when the current selection is single line and not empty,
  731. // + the find input is not focused
  732. const seedSearchStringFromSelection = !currentSelection.isEmpty()
  733. && currentSelection.startLineNumber === currentSelection.endLineNumber
  734. && (editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never')
  735. && !findInputFocused;
  736. /*
  737. * if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input is still empty, so we should focus the Find Input instead of Replace Input.
  738. * findInputFocused true -> seedSearchStringFromSelection false, FocusReplaceInput
  739. * findInputFocused false, seedSearchStringFromSelection true FocusReplaceInput
  740. * findInputFocused false seedSearchStringFromSelection false FocusFindInput
  741. */
  742. const shouldFocus = (findInputFocused || seedSearchStringFromSelection) ?
  743. 2 /* FocusReplaceInput */ : 1 /* FocusFindInput */;
  744. return controller.start({
  745. forceRevealReplace: true,
  746. seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none',
  747. seedSearchStringFromNonEmptySelection: editor.getOption(35 /* find */).seedSearchStringFromSelection === 'selection',
  748. seedSearchStringFromGlobalClipboard: editor.getOption(35 /* find */).seedSearchStringFromSelection !== 'never',
  749. shouldFocus: shouldFocus,
  750. shouldAnimate: true,
  751. updateSearchScope: false,
  752. loop: editor.getOption(35 /* find */).loop
  753. });
  754. });
  755. registerEditorContribution(CommonFindController.ID, FindController);
  756. registerEditorAction(StartFindWithArgsAction);
  757. registerEditorAction(StartFindWithSelectionAction);
  758. registerEditorAction(NextMatchFindAction);
  759. registerEditorAction(PreviousMatchFindAction);
  760. registerEditorAction(NextSelectionMatchFindAction);
  761. registerEditorAction(PreviousSelectionMatchFindAction);
  762. const FindCommand = EditorCommand.bindToContribution(CommonFindController.get);
  763. registerEditorCommand(new FindCommand({
  764. id: FIND_IDS.CloseFindWidgetCommand,
  765. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  766. handler: x => x.closeFindWidget(),
  767. kbOpts: {
  768. weight: 100 /* EditorContrib */ + 5,
  769. kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, ContextKeyExpr.not('isComposing')),
  770. primary: 9 /* Escape */,
  771. secondary: [1024 /* Shift */ | 9 /* Escape */]
  772. }
  773. }));
  774. registerEditorCommand(new FindCommand({
  775. id: FIND_IDS.ToggleCaseSensitiveCommand,
  776. precondition: undefined,
  777. handler: x => x.toggleCaseSensitive(),
  778. kbOpts: {
  779. weight: 100 /* EditorContrib */ + 5,
  780. kbExpr: EditorContextKeys.focus,
  781. primary: ToggleCaseSensitiveKeybinding.primary,
  782. mac: ToggleCaseSensitiveKeybinding.mac,
  783. win: ToggleCaseSensitiveKeybinding.win,
  784. linux: ToggleCaseSensitiveKeybinding.linux
  785. }
  786. }));
  787. registerEditorCommand(new FindCommand({
  788. id: FIND_IDS.ToggleWholeWordCommand,
  789. precondition: undefined,
  790. handler: x => x.toggleWholeWords(),
  791. kbOpts: {
  792. weight: 100 /* EditorContrib */ + 5,
  793. kbExpr: EditorContextKeys.focus,
  794. primary: ToggleWholeWordKeybinding.primary,
  795. mac: ToggleWholeWordKeybinding.mac,
  796. win: ToggleWholeWordKeybinding.win,
  797. linux: ToggleWholeWordKeybinding.linux
  798. }
  799. }));
  800. registerEditorCommand(new FindCommand({
  801. id: FIND_IDS.ToggleRegexCommand,
  802. precondition: undefined,
  803. handler: x => x.toggleRegex(),
  804. kbOpts: {
  805. weight: 100 /* EditorContrib */ + 5,
  806. kbExpr: EditorContextKeys.focus,
  807. primary: ToggleRegexKeybinding.primary,
  808. mac: ToggleRegexKeybinding.mac,
  809. win: ToggleRegexKeybinding.win,
  810. linux: ToggleRegexKeybinding.linux
  811. }
  812. }));
  813. registerEditorCommand(new FindCommand({
  814. id: FIND_IDS.ToggleSearchScopeCommand,
  815. precondition: undefined,
  816. handler: x => x.toggleSearchScope(),
  817. kbOpts: {
  818. weight: 100 /* EditorContrib */ + 5,
  819. kbExpr: EditorContextKeys.focus,
  820. primary: ToggleSearchScopeKeybinding.primary,
  821. mac: ToggleSearchScopeKeybinding.mac,
  822. win: ToggleSearchScopeKeybinding.win,
  823. linux: ToggleSearchScopeKeybinding.linux
  824. }
  825. }));
  826. registerEditorCommand(new FindCommand({
  827. id: FIND_IDS.TogglePreserveCaseCommand,
  828. precondition: undefined,
  829. handler: x => x.togglePreserveCase(),
  830. kbOpts: {
  831. weight: 100 /* EditorContrib */ + 5,
  832. kbExpr: EditorContextKeys.focus,
  833. primary: TogglePreserveCaseKeybinding.primary,
  834. mac: TogglePreserveCaseKeybinding.mac,
  835. win: TogglePreserveCaseKeybinding.win,
  836. linux: TogglePreserveCaseKeybinding.linux
  837. }
  838. }));
  839. registerEditorCommand(new FindCommand({
  840. id: FIND_IDS.ReplaceOneAction,
  841. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  842. handler: x => x.replace(),
  843. kbOpts: {
  844. weight: 100 /* EditorContrib */ + 5,
  845. kbExpr: EditorContextKeys.focus,
  846. primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 22 /* Digit1 */
  847. }
  848. }));
  849. registerEditorCommand(new FindCommand({
  850. id: FIND_IDS.ReplaceOneAction,
  851. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  852. handler: x => x.replace(),
  853. kbOpts: {
  854. weight: 100 /* EditorContrib */ + 5,
  855. kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
  856. primary: 3 /* Enter */
  857. }
  858. }));
  859. registerEditorCommand(new FindCommand({
  860. id: FIND_IDS.ReplaceAllAction,
  861. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  862. handler: x => x.replaceAll(),
  863. kbOpts: {
  864. weight: 100 /* EditorContrib */ + 5,
  865. kbExpr: EditorContextKeys.focus,
  866. primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 3 /* Enter */
  867. }
  868. }));
  869. registerEditorCommand(new FindCommand({
  870. id: FIND_IDS.ReplaceAllAction,
  871. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  872. handler: x => x.replaceAll(),
  873. kbOpts: {
  874. weight: 100 /* EditorContrib */ + 5,
  875. kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
  876. primary: undefined,
  877. mac: {
  878. primary: 2048 /* CtrlCmd */ | 3 /* Enter */,
  879. }
  880. }
  881. }));
  882. registerEditorCommand(new FindCommand({
  883. id: FIND_IDS.SelectAllMatchesAction,
  884. precondition: CONTEXT_FIND_WIDGET_VISIBLE,
  885. handler: x => x.selectAllMatches(),
  886. kbOpts: {
  887. weight: 100 /* EditorContrib */ + 5,
  888. kbExpr: EditorContextKeys.focus,
  889. primary: 512 /* Alt */ | 3 /* Enter */
  890. }
  891. }));