edb376f508728cdc8874d82cfae5953f5e10646536aa268b39325e5aa935604df012b522f78c5c8c142c09161bc440011aa63a5254882e35530e7fcd392e50 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  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. import { DisposableStore } from '../../../../base/common/lifecycle.js';
  15. import * as strings from '../../../../base/common/strings.js';
  16. import { EditorAction, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js';
  17. import { ShiftCommand } from '../../../common/commands/shiftCommand.js';
  18. import { EditOperation } from '../../../common/core/editOperation.js';
  19. import { Range } from '../../../common/core/range.js';
  20. import { Selection } from '../../../common/core/selection.js';
  21. import { EditorContextKeys } from '../../../common/editorContextKeys.js';
  22. import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
  23. import { IModelService } from '../../../common/services/model.js';
  24. import * as indentUtils from './indentUtils.js';
  25. import * as nls from '../../../../nls.js';
  26. import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
  27. import { normalizeIndentation } from '../../../common/core/indentation.js';
  28. import { getGoodIndentForLine, getIndentMetadata } from '../../../common/languages/autoIndent.js';
  29. export function getReindentEditOperations(model, languageConfigurationService, startLineNumber, endLineNumber, inheritedIndent) {
  30. if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
  31. // Model is empty
  32. return [];
  33. }
  34. const indentationRules = languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).indentationRules;
  35. if (!indentationRules) {
  36. return [];
  37. }
  38. endLineNumber = Math.min(endLineNumber, model.getLineCount());
  39. // Skip `unIndentedLinePattern` lines
  40. while (startLineNumber <= endLineNumber) {
  41. if (!indentationRules.unIndentedLinePattern) {
  42. break;
  43. }
  44. const text = model.getLineContent(startLineNumber);
  45. if (!indentationRules.unIndentedLinePattern.test(text)) {
  46. break;
  47. }
  48. startLineNumber++;
  49. }
  50. if (startLineNumber > endLineNumber - 1) {
  51. return [];
  52. }
  53. const { tabSize, indentSize, insertSpaces } = model.getOptions();
  54. const shiftIndent = (indentation, count) => {
  55. count = count || 1;
  56. return ShiftCommand.shiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
  57. };
  58. const unshiftIndent = (indentation, count) => {
  59. count = count || 1;
  60. return ShiftCommand.unshiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
  61. };
  62. const indentEdits = [];
  63. // indentation being passed to lines below
  64. let globalIndent;
  65. // Calculate indentation for the first line
  66. // If there is no passed-in indentation, we use the indentation of the first line as base.
  67. const currentLineText = model.getLineContent(startLineNumber);
  68. let adjustedLineContent = currentLineText;
  69. if (inheritedIndent !== undefined && inheritedIndent !== null) {
  70. globalIndent = inheritedIndent;
  71. const oldIndentation = strings.getLeadingWhitespace(currentLineText);
  72. adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
  73. if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
  74. globalIndent = unshiftIndent(globalIndent);
  75. adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
  76. }
  77. if (currentLineText !== adjustedLineContent) {
  78. indentEdits.push(EditOperation.replaceMove(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), normalizeIndentation(globalIndent, indentSize, insertSpaces)));
  79. }
  80. }
  81. else {
  82. globalIndent = strings.getLeadingWhitespace(currentLineText);
  83. }
  84. // idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`.
  85. let idealIndentForNextLine = globalIndent;
  86. if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
  87. idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
  88. globalIndent = shiftIndent(globalIndent);
  89. }
  90. else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
  91. idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
  92. }
  93. startLineNumber++;
  94. // Calculate indentation adjustment for all following lines
  95. for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
  96. const text = model.getLineContent(lineNumber);
  97. const oldIndentation = strings.getLeadingWhitespace(text);
  98. const adjustedLineContent = idealIndentForNextLine + text.substring(oldIndentation.length);
  99. if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
  100. idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);
  101. globalIndent = unshiftIndent(globalIndent);
  102. }
  103. if (oldIndentation !== idealIndentForNextLine) {
  104. indentEdits.push(EditOperation.replaceMove(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces)));
  105. }
  106. // calculate idealIndentForNextLine
  107. if (indentationRules.unIndentedLinePattern && indentationRules.unIndentedLinePattern.test(text)) {
  108. // In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines
  109. // but don't change globalIndent and idealIndentForNextLine.
  110. continue;
  111. }
  112. else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
  113. globalIndent = shiftIndent(globalIndent);
  114. idealIndentForNextLine = globalIndent;
  115. }
  116. else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
  117. idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
  118. }
  119. else {
  120. idealIndentForNextLine = globalIndent;
  121. }
  122. }
  123. return indentEdits;
  124. }
  125. export class IndentationToSpacesAction extends EditorAction {
  126. constructor() {
  127. super({
  128. id: IndentationToSpacesAction.ID,
  129. label: nls.localize('indentationToSpaces', "Convert Indentation to Spaces"),
  130. alias: 'Convert Indentation to Spaces',
  131. precondition: EditorContextKeys.writable
  132. });
  133. }
  134. run(accessor, editor) {
  135. const model = editor.getModel();
  136. if (!model) {
  137. return;
  138. }
  139. const modelOpts = model.getOptions();
  140. const selection = editor.getSelection();
  141. if (!selection) {
  142. return;
  143. }
  144. const command = new IndentationToSpacesCommand(selection, modelOpts.tabSize);
  145. editor.pushUndoStop();
  146. editor.executeCommands(this.id, [command]);
  147. editor.pushUndoStop();
  148. model.updateOptions({
  149. insertSpaces: true
  150. });
  151. }
  152. }
  153. IndentationToSpacesAction.ID = 'editor.action.indentationToSpaces';
  154. export class IndentationToTabsAction extends EditorAction {
  155. constructor() {
  156. super({
  157. id: IndentationToTabsAction.ID,
  158. label: nls.localize('indentationToTabs', "Convert Indentation to Tabs"),
  159. alias: 'Convert Indentation to Tabs',
  160. precondition: EditorContextKeys.writable
  161. });
  162. }
  163. run(accessor, editor) {
  164. const model = editor.getModel();
  165. if (!model) {
  166. return;
  167. }
  168. const modelOpts = model.getOptions();
  169. const selection = editor.getSelection();
  170. if (!selection) {
  171. return;
  172. }
  173. const command = new IndentationToTabsCommand(selection, modelOpts.tabSize);
  174. editor.pushUndoStop();
  175. editor.executeCommands(this.id, [command]);
  176. editor.pushUndoStop();
  177. model.updateOptions({
  178. insertSpaces: false
  179. });
  180. }
  181. }
  182. IndentationToTabsAction.ID = 'editor.action.indentationToTabs';
  183. export class ChangeIndentationSizeAction extends EditorAction {
  184. constructor(insertSpaces, opts) {
  185. super(opts);
  186. this.insertSpaces = insertSpaces;
  187. }
  188. run(accessor, editor) {
  189. const quickInputService = accessor.get(IQuickInputService);
  190. const modelService = accessor.get(IModelService);
  191. const model = editor.getModel();
  192. if (!model) {
  193. return;
  194. }
  195. const creationOpts = modelService.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
  196. const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({
  197. id: n.toString(),
  198. label: n.toString(),
  199. // add description for tabSize value set in the configuration
  200. description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : undefined
  201. }));
  202. // auto focus the tabSize set for the current editor
  203. const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7);
  204. setTimeout(() => {
  205. quickInputService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then(pick => {
  206. if (pick) {
  207. if (model && !model.isDisposed()) {
  208. model.updateOptions({
  209. tabSize: parseInt(pick.label, 10),
  210. insertSpaces: this.insertSpaces
  211. });
  212. }
  213. }
  214. });
  215. }, 50 /* quick input is sensitive to being opened so soon after another */);
  216. }
  217. }
  218. export class IndentUsingTabs extends ChangeIndentationSizeAction {
  219. constructor() {
  220. super(false, {
  221. id: IndentUsingTabs.ID,
  222. label: nls.localize('indentUsingTabs', "Indent Using Tabs"),
  223. alias: 'Indent Using Tabs',
  224. precondition: undefined
  225. });
  226. }
  227. }
  228. IndentUsingTabs.ID = 'editor.action.indentUsingTabs';
  229. export class IndentUsingSpaces extends ChangeIndentationSizeAction {
  230. constructor() {
  231. super(true, {
  232. id: IndentUsingSpaces.ID,
  233. label: nls.localize('indentUsingSpaces', "Indent Using Spaces"),
  234. alias: 'Indent Using Spaces',
  235. precondition: undefined
  236. });
  237. }
  238. }
  239. IndentUsingSpaces.ID = 'editor.action.indentUsingSpaces';
  240. export class DetectIndentation extends EditorAction {
  241. constructor() {
  242. super({
  243. id: DetectIndentation.ID,
  244. label: nls.localize('detectIndentation', "Detect Indentation from Content"),
  245. alias: 'Detect Indentation from Content',
  246. precondition: undefined
  247. });
  248. }
  249. run(accessor, editor) {
  250. const modelService = accessor.get(IModelService);
  251. const model = editor.getModel();
  252. if (!model) {
  253. return;
  254. }
  255. const creationOpts = modelService.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
  256. model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
  257. }
  258. }
  259. DetectIndentation.ID = 'editor.action.detectIndentation';
  260. export class ReindentLinesAction extends EditorAction {
  261. constructor() {
  262. super({
  263. id: 'editor.action.reindentlines',
  264. label: nls.localize('editor.reindentlines', "Reindent Lines"),
  265. alias: 'Reindent Lines',
  266. precondition: EditorContextKeys.writable
  267. });
  268. }
  269. run(accessor, editor) {
  270. const languageConfigurationService = accessor.get(ILanguageConfigurationService);
  271. const model = editor.getModel();
  272. if (!model) {
  273. return;
  274. }
  275. const edits = getReindentEditOperations(model, languageConfigurationService, 1, model.getLineCount());
  276. if (edits.length > 0) {
  277. editor.pushUndoStop();
  278. editor.executeEdits(this.id, edits);
  279. editor.pushUndoStop();
  280. }
  281. }
  282. }
  283. export class ReindentSelectedLinesAction extends EditorAction {
  284. constructor() {
  285. super({
  286. id: 'editor.action.reindentselectedlines',
  287. label: nls.localize('editor.reindentselectedlines', "Reindent Selected Lines"),
  288. alias: 'Reindent Selected Lines',
  289. precondition: EditorContextKeys.writable
  290. });
  291. }
  292. run(accessor, editor) {
  293. const languageConfigurationService = accessor.get(ILanguageConfigurationService);
  294. const model = editor.getModel();
  295. if (!model) {
  296. return;
  297. }
  298. const selections = editor.getSelections();
  299. if (selections === null) {
  300. return;
  301. }
  302. const edits = [];
  303. for (const selection of selections) {
  304. let startLineNumber = selection.startLineNumber;
  305. let endLineNumber = selection.endLineNumber;
  306. if (startLineNumber !== endLineNumber && selection.endColumn === 1) {
  307. endLineNumber--;
  308. }
  309. if (startLineNumber === 1) {
  310. if (startLineNumber === endLineNumber) {
  311. continue;
  312. }
  313. }
  314. else {
  315. startLineNumber--;
  316. }
  317. const editOperations = getReindentEditOperations(model, languageConfigurationService, startLineNumber, endLineNumber);
  318. edits.push(...editOperations);
  319. }
  320. if (edits.length > 0) {
  321. editor.pushUndoStop();
  322. editor.executeEdits(this.id, edits);
  323. editor.pushUndoStop();
  324. }
  325. }
  326. }
  327. export class AutoIndentOnPasteCommand {
  328. constructor(edits, initialSelection) {
  329. this._initialSelection = initialSelection;
  330. this._edits = [];
  331. this._selectionId = null;
  332. for (const edit of edits) {
  333. if (edit.range && typeof edit.text === 'string') {
  334. this._edits.push(edit);
  335. }
  336. }
  337. }
  338. getEditOperations(model, builder) {
  339. for (const edit of this._edits) {
  340. builder.addEditOperation(Range.lift(edit.range), edit.text);
  341. }
  342. let selectionIsSet = false;
  343. if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
  344. if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
  345. this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
  346. selectionIsSet = true;
  347. this._selectionId = builder.trackSelection(this._initialSelection, true);
  348. }
  349. else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
  350. this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
  351. selectionIsSet = true;
  352. this._selectionId = builder.trackSelection(this._initialSelection, false);
  353. }
  354. }
  355. if (!selectionIsSet) {
  356. this._selectionId = builder.trackSelection(this._initialSelection);
  357. }
  358. }
  359. computeCursorState(model, helper) {
  360. return helper.getTrackedSelection(this._selectionId);
  361. }
  362. }
  363. let AutoIndentOnPaste = class AutoIndentOnPaste {
  364. constructor(editor, _languageConfigurationService) {
  365. this.editor = editor;
  366. this._languageConfigurationService = _languageConfigurationService;
  367. this.callOnDispose = new DisposableStore();
  368. this.callOnModel = new DisposableStore();
  369. this.callOnDispose.add(editor.onDidChangeConfiguration(() => this.update()));
  370. this.callOnDispose.add(editor.onDidChangeModel(() => this.update()));
  371. this.callOnDispose.add(editor.onDidChangeModelLanguage(() => this.update()));
  372. }
  373. update() {
  374. // clean up
  375. this.callOnModel.clear();
  376. // we are disabled
  377. if (this.editor.getOption(9 /* EditorOption.autoIndent */) < 4 /* EditorAutoIndentStrategy.Full */ || this.editor.getOption(50 /* EditorOption.formatOnPaste */)) {
  378. return;
  379. }
  380. // no model
  381. if (!this.editor.hasModel()) {
  382. return;
  383. }
  384. this.callOnModel.add(this.editor.onDidPaste(({ range }) => {
  385. this.trigger(range);
  386. }));
  387. }
  388. trigger(range) {
  389. const selections = this.editor.getSelections();
  390. if (selections === null || selections.length > 1) {
  391. return;
  392. }
  393. const model = this.editor.getModel();
  394. if (!model) {
  395. return;
  396. }
  397. if (!model.tokenization.isCheapToTokenize(range.getStartPosition().lineNumber)) {
  398. return;
  399. }
  400. const autoIndent = this.editor.getOption(9 /* EditorOption.autoIndent */);
  401. const { tabSize, indentSize, insertSpaces } = model.getOptions();
  402. const textEdits = [];
  403. const indentConverter = {
  404. shiftIndent: (indentation) => {
  405. return ShiftCommand.shiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
  406. },
  407. unshiftIndent: (indentation) => {
  408. return ShiftCommand.unshiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
  409. }
  410. };
  411. let startLineNumber = range.startLineNumber;
  412. while (startLineNumber <= range.endLineNumber) {
  413. if (this.shouldIgnoreLine(model, startLineNumber)) {
  414. startLineNumber++;
  415. continue;
  416. }
  417. break;
  418. }
  419. if (startLineNumber > range.endLineNumber) {
  420. return;
  421. }
  422. let firstLineText = model.getLineContent(startLineNumber);
  423. if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) {
  424. const indentOfFirstLine = getGoodIndentForLine(autoIndent, model, model.getLanguageId(), startLineNumber, indentConverter, this._languageConfigurationService);
  425. if (indentOfFirstLine !== null) {
  426. const oldIndentation = strings.getLeadingWhitespace(firstLineText);
  427. const newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
  428. const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
  429. if (newSpaceCnt !== oldSpaceCnt) {
  430. const newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
  431. textEdits.push({
  432. range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
  433. text: newIndent
  434. });
  435. firstLineText = newIndent + firstLineText.substr(oldIndentation.length);
  436. }
  437. else {
  438. const indentMetadata = getIndentMetadata(model, startLineNumber, this._languageConfigurationService);
  439. if (indentMetadata === 0 || indentMetadata === 8 /* IndentConsts.UNINDENT_MASK */) {
  440. // we paste content into a line where only contains whitespaces
  441. // after pasting, the indentation of the first line is already correct
  442. // the first line doesn't match any indentation rule
  443. // then no-op.
  444. return;
  445. }
  446. }
  447. }
  448. }
  449. const firstLineNumber = startLineNumber;
  450. // ignore empty or ignored lines
  451. while (startLineNumber < range.endLineNumber) {
  452. if (!/\S/.test(model.getLineContent(startLineNumber + 1))) {
  453. startLineNumber++;
  454. continue;
  455. }
  456. break;
  457. }
  458. if (startLineNumber !== range.endLineNumber) {
  459. const virtualModel = {
  460. tokenization: {
  461. getLineTokens: (lineNumber) => {
  462. return model.tokenization.getLineTokens(lineNumber);
  463. },
  464. getLanguageId: () => {
  465. return model.getLanguageId();
  466. },
  467. getLanguageIdAtPosition: (lineNumber, column) => {
  468. return model.getLanguageIdAtPosition(lineNumber, column);
  469. },
  470. },
  471. getLineContent: (lineNumber) => {
  472. if (lineNumber === firstLineNumber) {
  473. return firstLineText;
  474. }
  475. else {
  476. return model.getLineContent(lineNumber);
  477. }
  478. }
  479. };
  480. const indentOfSecondLine = getGoodIndentForLine(autoIndent, virtualModel, model.getLanguageId(), startLineNumber + 1, indentConverter, this._languageConfigurationService);
  481. if (indentOfSecondLine !== null) {
  482. const newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize);
  483. const oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
  484. if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
  485. const spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
  486. for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) {
  487. const lineContent = model.getLineContent(i);
  488. const originalIndent = strings.getLeadingWhitespace(lineContent);
  489. const originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
  490. const newSpacesCnt = originalSpacesCnt + spaceCntOffset;
  491. const newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
  492. if (newIndent !== originalIndent) {
  493. textEdits.push({
  494. range: new Range(i, 1, i, originalIndent.length + 1),
  495. text: newIndent
  496. });
  497. }
  498. }
  499. }
  500. }
  501. }
  502. if (textEdits.length > 0) {
  503. this.editor.pushUndoStop();
  504. const cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection());
  505. this.editor.executeCommand('autoIndentOnPaste', cmd);
  506. this.editor.pushUndoStop();
  507. }
  508. }
  509. shouldIgnoreLine(model, lineNumber) {
  510. model.tokenization.forceTokenization(lineNumber);
  511. const nonWhitespaceColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
  512. if (nonWhitespaceColumn === 0) {
  513. return true;
  514. }
  515. const tokens = model.tokenization.getLineTokens(lineNumber);
  516. if (tokens.getCount() > 0) {
  517. const firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhitespaceColumn);
  518. if (firstNonWhitespaceTokenIndex >= 0 && tokens.getStandardTokenType(firstNonWhitespaceTokenIndex) === 1 /* StandardTokenType.Comment */) {
  519. return true;
  520. }
  521. }
  522. return false;
  523. }
  524. dispose() {
  525. this.callOnDispose.dispose();
  526. this.callOnModel.dispose();
  527. }
  528. };
  529. AutoIndentOnPaste.ID = 'editor.contrib.autoIndentOnPaste';
  530. AutoIndentOnPaste = __decorate([
  531. __param(1, ILanguageConfigurationService)
  532. ], AutoIndentOnPaste);
  533. export { AutoIndentOnPaste };
  534. function getIndentationEditOperations(model, builder, tabSize, tabsToSpaces) {
  535. if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
  536. // Model is empty
  537. return;
  538. }
  539. let spaces = '';
  540. for (let i = 0; i < tabSize; i++) {
  541. spaces += ' ';
  542. }
  543. const spacesRegExp = new RegExp(spaces, 'gi');
  544. for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) {
  545. let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
  546. if (lastIndentationColumn === 0) {
  547. lastIndentationColumn = model.getLineMaxColumn(lineNumber);
  548. }
  549. if (lastIndentationColumn === 1) {
  550. continue;
  551. }
  552. const originalIndentationRange = new Range(lineNumber, 1, lineNumber, lastIndentationColumn);
  553. const originalIndentation = model.getValueInRange(originalIndentationRange);
  554. const newIndentation = (tabsToSpaces
  555. ? originalIndentation.replace(/\t/ig, spaces)
  556. : originalIndentation.replace(spacesRegExp, '\t'));
  557. builder.addEditOperation(originalIndentationRange, newIndentation);
  558. }
  559. }
  560. export class IndentationToSpacesCommand {
  561. constructor(selection, tabSize) {
  562. this.selection = selection;
  563. this.tabSize = tabSize;
  564. this.selectionId = null;
  565. }
  566. getEditOperations(model, builder) {
  567. this.selectionId = builder.trackSelection(this.selection);
  568. getIndentationEditOperations(model, builder, this.tabSize, true);
  569. }
  570. computeCursorState(model, helper) {
  571. return helper.getTrackedSelection(this.selectionId);
  572. }
  573. }
  574. export class IndentationToTabsCommand {
  575. constructor(selection, tabSize) {
  576. this.selection = selection;
  577. this.tabSize = tabSize;
  578. this.selectionId = null;
  579. }
  580. getEditOperations(model, builder) {
  581. this.selectionId = builder.trackSelection(this.selection);
  582. getIndentationEditOperations(model, builder, this.tabSize, false);
  583. }
  584. computeCursorState(model, helper) {
  585. return helper.getTrackedSelection(this.selectionId);
  586. }
  587. }
  588. registerEditorContribution(AutoIndentOnPaste.ID, AutoIndentOnPaste);
  589. registerEditorAction(IndentationToSpacesAction);
  590. registerEditorAction(IndentationToTabsAction);
  591. registerEditorAction(IndentUsingTabs);
  592. registerEditorAction(IndentUsingSpaces);
  593. registerEditorAction(DetectIndentation);
  594. registerEditorAction(ReindentLinesAction);
  595. registerEditorAction(ReindentSelectedLinesAction);