3a794653de33888f9a945a7e57fdd047bea4a706497c133c57dc5e48817cf56e5fd3f5c8a4c927c0735737f72556bd01c4612b48c6fdf9357345bc684b8414 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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 * as arrays from '../../../../base/common/arrays.js';
  24. import { CancellationToken } from '../../../../base/common/cancellation.js';
  25. import { onUnexpectedExternalError } from '../../../../base/common/errors.js';
  26. import { EditorAction, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js';
  27. import { Position } from '../../../common/core/position.js';
  28. import { Range } from '../../../common/core/range.js';
  29. import { Selection } from '../../../common/core/selection.js';
  30. import { EditorContextKeys } from '../../../common/editorContextKeys.js';
  31. import { BracketSelectionRangeProvider } from './bracketSelections.js';
  32. import { WordSelectionRangeProvider } from './wordSelections.js';
  33. import * as nls from '../../../../nls.js';
  34. import { MenuId } from '../../../../platform/actions/common/actions.js';
  35. import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
  36. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  37. import { ITextModelService } from '../../../common/services/resolverService.js';
  38. import { assertType } from '../../../../base/common/types.js';
  39. import { URI } from '../../../../base/common/uri.js';
  40. class SelectionRanges {
  41. constructor(index, ranges) {
  42. this.index = index;
  43. this.ranges = ranges;
  44. }
  45. mov(fwd) {
  46. const index = this.index + (fwd ? 1 : -1);
  47. if (index < 0 || index >= this.ranges.length) {
  48. return this;
  49. }
  50. const res = new SelectionRanges(index, this.ranges);
  51. if (res.ranges[index].equalsRange(this.ranges[this.index])) {
  52. // next range equals this range, retry with next-next
  53. return res.mov(fwd);
  54. }
  55. return res;
  56. }
  57. }
  58. let SmartSelectController = class SmartSelectController {
  59. constructor(_editor, _languageFeaturesService) {
  60. this._editor = _editor;
  61. this._languageFeaturesService = _languageFeaturesService;
  62. this._ignoreSelection = false;
  63. }
  64. static get(editor) {
  65. return editor.getContribution(SmartSelectController.ID);
  66. }
  67. dispose() {
  68. var _a;
  69. (_a = this._selectionListener) === null || _a === void 0 ? void 0 : _a.dispose();
  70. }
  71. run(forward) {
  72. return __awaiter(this, void 0, void 0, function* () {
  73. if (!this._editor.hasModel()) {
  74. return;
  75. }
  76. const selections = this._editor.getSelections();
  77. const model = this._editor.getModel();
  78. if (!this._state) {
  79. yield provideSelectionRanges(this._languageFeaturesService.selectionRangeProvider, model, selections.map(s => s.getPosition()), this._editor.getOption(104 /* EditorOption.smartSelect */), CancellationToken.None).then(ranges => {
  80. var _a;
  81. if (!arrays.isNonEmptyArray(ranges) || ranges.length !== selections.length) {
  82. // invalid result
  83. return;
  84. }
  85. if (!this._editor.hasModel() || !arrays.equals(this._editor.getSelections(), selections, (a, b) => a.equalsSelection(b))) {
  86. // invalid editor state
  87. return;
  88. }
  89. for (let i = 0; i < ranges.length; i++) {
  90. ranges[i] = ranges[i].filter(range => {
  91. // filter ranges inside the selection
  92. return range.containsPosition(selections[i].getStartPosition()) && range.containsPosition(selections[i].getEndPosition());
  93. });
  94. // prepend current selection
  95. ranges[i].unshift(selections[i]);
  96. }
  97. this._state = ranges.map(ranges => new SelectionRanges(0, ranges));
  98. // listen to caret move and forget about state
  99. (_a = this._selectionListener) === null || _a === void 0 ? void 0 : _a.dispose();
  100. this._selectionListener = this._editor.onDidChangeCursorPosition(() => {
  101. var _a;
  102. if (!this._ignoreSelection) {
  103. (_a = this._selectionListener) === null || _a === void 0 ? void 0 : _a.dispose();
  104. this._state = undefined;
  105. }
  106. });
  107. });
  108. }
  109. if (!this._state) {
  110. // no state
  111. return;
  112. }
  113. this._state = this._state.map(state => state.mov(forward));
  114. const newSelections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition()));
  115. this._ignoreSelection = true;
  116. try {
  117. this._editor.setSelections(newSelections);
  118. }
  119. finally {
  120. this._ignoreSelection = false;
  121. }
  122. });
  123. }
  124. };
  125. SmartSelectController.ID = 'editor.contrib.smartSelectController';
  126. SmartSelectController = __decorate([
  127. __param(1, ILanguageFeaturesService)
  128. ], SmartSelectController);
  129. class AbstractSmartSelect extends EditorAction {
  130. constructor(forward, opts) {
  131. super(opts);
  132. this._forward = forward;
  133. }
  134. run(_accessor, editor) {
  135. return __awaiter(this, void 0, void 0, function* () {
  136. const controller = SmartSelectController.get(editor);
  137. if (controller) {
  138. yield controller.run(this._forward);
  139. }
  140. });
  141. }
  142. }
  143. class GrowSelectionAction extends AbstractSmartSelect {
  144. constructor() {
  145. super(true, {
  146. id: 'editor.action.smartSelect.expand',
  147. label: nls.localize('smartSelect.expand', "Expand Selection"),
  148. alias: 'Expand Selection',
  149. precondition: undefined,
  150. kbOpts: {
  151. kbExpr: EditorContextKeys.editorTextFocus,
  152. primary: 1024 /* KeyMod.Shift */ | 512 /* KeyMod.Alt */ | 17 /* KeyCode.RightArrow */,
  153. mac: {
  154. primary: 2048 /* KeyMod.CtrlCmd */ | 256 /* KeyMod.WinCtrl */ | 1024 /* KeyMod.Shift */ | 17 /* KeyCode.RightArrow */,
  155. secondary: [256 /* KeyMod.WinCtrl */ | 1024 /* KeyMod.Shift */ | 17 /* KeyCode.RightArrow */],
  156. },
  157. weight: 100 /* KeybindingWeight.EditorContrib */
  158. },
  159. menuOpts: {
  160. menuId: MenuId.MenubarSelectionMenu,
  161. group: '1_basic',
  162. title: nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"),
  163. order: 2
  164. }
  165. });
  166. }
  167. }
  168. // renamed command id
  169. CommandsRegistry.registerCommandAlias('editor.action.smartSelect.grow', 'editor.action.smartSelect.expand');
  170. class ShrinkSelectionAction extends AbstractSmartSelect {
  171. constructor() {
  172. super(false, {
  173. id: 'editor.action.smartSelect.shrink',
  174. label: nls.localize('smartSelect.shrink', "Shrink Selection"),
  175. alias: 'Shrink Selection',
  176. precondition: undefined,
  177. kbOpts: {
  178. kbExpr: EditorContextKeys.editorTextFocus,
  179. primary: 1024 /* KeyMod.Shift */ | 512 /* KeyMod.Alt */ | 15 /* KeyCode.LeftArrow */,
  180. mac: {
  181. primary: 2048 /* KeyMod.CtrlCmd */ | 256 /* KeyMod.WinCtrl */ | 1024 /* KeyMod.Shift */ | 15 /* KeyCode.LeftArrow */,
  182. secondary: [256 /* KeyMod.WinCtrl */ | 1024 /* KeyMod.Shift */ | 15 /* KeyCode.LeftArrow */],
  183. },
  184. weight: 100 /* KeybindingWeight.EditorContrib */
  185. },
  186. menuOpts: {
  187. menuId: MenuId.MenubarSelectionMenu,
  188. group: '1_basic',
  189. title: nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"),
  190. order: 3
  191. }
  192. });
  193. }
  194. }
  195. registerEditorContribution(SmartSelectController.ID, SmartSelectController);
  196. registerEditorAction(GrowSelectionAction);
  197. registerEditorAction(ShrinkSelectionAction);
  198. export function provideSelectionRanges(registry, model, positions, options, token) {
  199. return __awaiter(this, void 0, void 0, function* () {
  200. const providers = registry.all(model)
  201. .concat(new WordSelectionRangeProvider()); // ALWAYS have word based selection range
  202. if (providers.length === 1) {
  203. // add word selection and bracket selection when no provider exists
  204. providers.unshift(new BracketSelectionRangeProvider());
  205. }
  206. const work = [];
  207. const allRawRanges = [];
  208. for (const provider of providers) {
  209. work.push(Promise.resolve(provider.provideSelectionRanges(model, positions, token)).then(allProviderRanges => {
  210. if (arrays.isNonEmptyArray(allProviderRanges) && allProviderRanges.length === positions.length) {
  211. for (let i = 0; i < positions.length; i++) {
  212. if (!allRawRanges[i]) {
  213. allRawRanges[i] = [];
  214. }
  215. for (const oneProviderRanges of allProviderRanges[i]) {
  216. if (Range.isIRange(oneProviderRanges.range) && Range.containsPosition(oneProviderRanges.range, positions[i])) {
  217. allRawRanges[i].push(Range.lift(oneProviderRanges.range));
  218. }
  219. }
  220. }
  221. }
  222. }, onUnexpectedExternalError));
  223. }
  224. yield Promise.all(work);
  225. return allRawRanges.map(oneRawRanges => {
  226. if (oneRawRanges.length === 0) {
  227. return [];
  228. }
  229. // sort all by start/end position
  230. oneRawRanges.sort((a, b) => {
  231. if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) {
  232. return 1;
  233. }
  234. else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) {
  235. return -1;
  236. }
  237. else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) {
  238. return -1;
  239. }
  240. else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) {
  241. return 1;
  242. }
  243. else {
  244. return 0;
  245. }
  246. });
  247. // remove ranges that don't contain the former range or that are equal to the
  248. // former range
  249. const oneRanges = [];
  250. let last;
  251. for (const range of oneRawRanges) {
  252. if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) {
  253. oneRanges.push(range);
  254. last = range;
  255. }
  256. }
  257. if (!options.selectLeadingAndTrailingWhitespace) {
  258. return oneRanges;
  259. }
  260. // add ranges that expand trivia at line starts and ends whenever a range
  261. // wraps onto the a new line
  262. const oneRangesWithTrivia = [oneRanges[0]];
  263. for (let i = 1; i < oneRanges.length; i++) {
  264. const prev = oneRanges[i - 1];
  265. const cur = oneRanges[i];
  266. if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) {
  267. // add line/block range without leading/failing whitespace
  268. const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber));
  269. if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) {
  270. oneRangesWithTrivia.push(rangeNoWhitespace);
  271. }
  272. // add line/block range
  273. const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber));
  274. if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) {
  275. oneRangesWithTrivia.push(rangeFull);
  276. }
  277. }
  278. oneRangesWithTrivia.push(cur);
  279. }
  280. return oneRangesWithTrivia;
  281. });
  282. });
  283. }
  284. CommandsRegistry.registerCommand('_executeSelectionRangeProvider', function (accessor, ...args) {
  285. return __awaiter(this, void 0, void 0, function* () {
  286. const [resource, positions] = args;
  287. assertType(URI.isUri(resource));
  288. const registry = accessor.get(ILanguageFeaturesService).selectionRangeProvider;
  289. const reference = yield accessor.get(ITextModelService).createModelReference(resource);
  290. try {
  291. return provideSelectionRanges(registry, reference.object.textEditorModel, positions, { selectLeadingAndTrailingWhitespace: true }, CancellationToken.None);
  292. }
  293. finally {
  294. reference.dispose();
  295. }
  296. });
  297. });