d81a62733460a3c95bfc337d77e8255bd250b12e2519698f2e09b1cc36e469f5374da08c1e4d7f11bad0ff0e8a5b9c9ac173251e3164b14f034736abeb5e46 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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. import * as strings from '../../../base/common/strings.js';
  6. import { SingleCursorState } from '../cursorCommon.js';
  7. import { DeleteOperations } from './cursorDeleteOperations.js';
  8. import { getMapForWordSeparators } from '../core/wordCharacterClassifier.js';
  9. import { Position } from '../core/position.js';
  10. import { Range } from '../core/range.js';
  11. export class WordOperations {
  12. static _createWord(lineContent, wordType, nextCharClass, start, end) {
  13. // console.log('WORD ==> ' + start + ' => ' + end + ':::: <<<' + lineContent.substring(start, end) + '>>>');
  14. return { start: start, end: end, wordType: wordType, nextCharClass: nextCharClass };
  15. }
  16. static _findPreviousWordOnLine(wordSeparators, model, position) {
  17. const lineContent = model.getLineContent(position.lineNumber);
  18. return this._doFindPreviousWordOnLine(lineContent, wordSeparators, position);
  19. }
  20. static _doFindPreviousWordOnLine(lineContent, wordSeparators, position) {
  21. let wordType = 0 /* WordType.None */;
  22. for (let chIndex = position.column - 2; chIndex >= 0; chIndex--) {
  23. const chCode = lineContent.charCodeAt(chIndex);
  24. const chClass = wordSeparators.get(chCode);
  25. if (chClass === 0 /* WordCharacterClass.Regular */) {
  26. if (wordType === 2 /* WordType.Separator */) {
  27. return this._createWord(lineContent, wordType, chClass, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1));
  28. }
  29. wordType = 1 /* WordType.Regular */;
  30. }
  31. else if (chClass === 2 /* WordCharacterClass.WordSeparator */) {
  32. if (wordType === 1 /* WordType.Regular */) {
  33. return this._createWord(lineContent, wordType, chClass, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1));
  34. }
  35. wordType = 2 /* WordType.Separator */;
  36. }
  37. else if (chClass === 1 /* WordCharacterClass.Whitespace */) {
  38. if (wordType !== 0 /* WordType.None */) {
  39. return this._createWord(lineContent, wordType, chClass, chIndex + 1, this._findEndOfWord(lineContent, wordSeparators, wordType, chIndex + 1));
  40. }
  41. }
  42. }
  43. if (wordType !== 0 /* WordType.None */) {
  44. return this._createWord(lineContent, wordType, 1 /* WordCharacterClass.Whitespace */, 0, this._findEndOfWord(lineContent, wordSeparators, wordType, 0));
  45. }
  46. return null;
  47. }
  48. static _findEndOfWord(lineContent, wordSeparators, wordType, startIndex) {
  49. const len = lineContent.length;
  50. for (let chIndex = startIndex; chIndex < len; chIndex++) {
  51. const chCode = lineContent.charCodeAt(chIndex);
  52. const chClass = wordSeparators.get(chCode);
  53. if (chClass === 1 /* WordCharacterClass.Whitespace */) {
  54. return chIndex;
  55. }
  56. if (wordType === 1 /* WordType.Regular */ && chClass === 2 /* WordCharacterClass.WordSeparator */) {
  57. return chIndex;
  58. }
  59. if (wordType === 2 /* WordType.Separator */ && chClass === 0 /* WordCharacterClass.Regular */) {
  60. return chIndex;
  61. }
  62. }
  63. return len;
  64. }
  65. static _findNextWordOnLine(wordSeparators, model, position) {
  66. const lineContent = model.getLineContent(position.lineNumber);
  67. return this._doFindNextWordOnLine(lineContent, wordSeparators, position);
  68. }
  69. static _doFindNextWordOnLine(lineContent, wordSeparators, position) {
  70. let wordType = 0 /* WordType.None */;
  71. const len = lineContent.length;
  72. for (let chIndex = position.column - 1; chIndex < len; chIndex++) {
  73. const chCode = lineContent.charCodeAt(chIndex);
  74. const chClass = wordSeparators.get(chCode);
  75. if (chClass === 0 /* WordCharacterClass.Regular */) {
  76. if (wordType === 2 /* WordType.Separator */) {
  77. return this._createWord(lineContent, wordType, chClass, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex);
  78. }
  79. wordType = 1 /* WordType.Regular */;
  80. }
  81. else if (chClass === 2 /* WordCharacterClass.WordSeparator */) {
  82. if (wordType === 1 /* WordType.Regular */) {
  83. return this._createWord(lineContent, wordType, chClass, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex);
  84. }
  85. wordType = 2 /* WordType.Separator */;
  86. }
  87. else if (chClass === 1 /* WordCharacterClass.Whitespace */) {
  88. if (wordType !== 0 /* WordType.None */) {
  89. return this._createWord(lineContent, wordType, chClass, this._findStartOfWord(lineContent, wordSeparators, wordType, chIndex - 1), chIndex);
  90. }
  91. }
  92. }
  93. if (wordType !== 0 /* WordType.None */) {
  94. return this._createWord(lineContent, wordType, 1 /* WordCharacterClass.Whitespace */, this._findStartOfWord(lineContent, wordSeparators, wordType, len - 1), len);
  95. }
  96. return null;
  97. }
  98. static _findStartOfWord(lineContent, wordSeparators, wordType, startIndex) {
  99. for (let chIndex = startIndex; chIndex >= 0; chIndex--) {
  100. const chCode = lineContent.charCodeAt(chIndex);
  101. const chClass = wordSeparators.get(chCode);
  102. if (chClass === 1 /* WordCharacterClass.Whitespace */) {
  103. return chIndex + 1;
  104. }
  105. if (wordType === 1 /* WordType.Regular */ && chClass === 2 /* WordCharacterClass.WordSeparator */) {
  106. return chIndex + 1;
  107. }
  108. if (wordType === 2 /* WordType.Separator */ && chClass === 0 /* WordCharacterClass.Regular */) {
  109. return chIndex + 1;
  110. }
  111. }
  112. return 0;
  113. }
  114. static moveWordLeft(wordSeparators, model, position, wordNavigationType) {
  115. let lineNumber = position.lineNumber;
  116. let column = position.column;
  117. if (column === 1) {
  118. if (lineNumber > 1) {
  119. lineNumber = lineNumber - 1;
  120. column = model.getLineMaxColumn(lineNumber);
  121. }
  122. }
  123. let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, column));
  124. if (wordNavigationType === 0 /* WordNavigationType.WordStart */) {
  125. return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1);
  126. }
  127. if (wordNavigationType === 1 /* WordNavigationType.WordStartFast */) {
  128. if (prevWordOnLine
  129. && prevWordOnLine.wordType === 2 /* WordType.Separator */
  130. && prevWordOnLine.end - prevWordOnLine.start === 1
  131. && prevWordOnLine.nextCharClass === 0 /* WordCharacterClass.Regular */) {
  132. // Skip over a word made up of one single separator and followed by a regular character
  133. prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1));
  134. }
  135. return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1);
  136. }
  137. if (wordNavigationType === 3 /* WordNavigationType.WordAccessibility */) {
  138. while (prevWordOnLine
  139. && prevWordOnLine.wordType === 2 /* WordType.Separator */) {
  140. // Skip over words made up of only separators
  141. prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1));
  142. }
  143. return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1);
  144. }
  145. // We are stopping at the ending of words
  146. if (prevWordOnLine && column <= prevWordOnLine.end + 1) {
  147. prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1));
  148. }
  149. return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.end + 1 : 1);
  150. }
  151. static _moveWordPartLeft(model, position) {
  152. const lineNumber = position.lineNumber;
  153. const maxColumn = model.getLineMaxColumn(lineNumber);
  154. if (position.column === 1) {
  155. return (lineNumber > 1 ? new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)) : position);
  156. }
  157. const lineContent = model.getLineContent(lineNumber);
  158. for (let column = position.column - 1; column > 1; column--) {
  159. const left = lineContent.charCodeAt(column - 2);
  160. const right = lineContent.charCodeAt(column - 1);
  161. if (left === 95 /* CharCode.Underline */ && right !== 95 /* CharCode.Underline */) {
  162. // snake_case_variables
  163. return new Position(lineNumber, column);
  164. }
  165. if ((strings.isLowerAsciiLetter(left) || strings.isAsciiDigit(left)) && strings.isUpperAsciiLetter(right)) {
  166. // camelCaseVariables
  167. return new Position(lineNumber, column);
  168. }
  169. if (strings.isUpperAsciiLetter(left) && strings.isUpperAsciiLetter(right)) {
  170. // thisIsACamelCaseWithOneLetterWords
  171. if (column + 1 < maxColumn) {
  172. const rightRight = lineContent.charCodeAt(column);
  173. if (strings.isLowerAsciiLetter(rightRight) || strings.isAsciiDigit(rightRight)) {
  174. return new Position(lineNumber, column);
  175. }
  176. }
  177. }
  178. }
  179. return new Position(lineNumber, 1);
  180. }
  181. static moveWordRight(wordSeparators, model, position, wordNavigationType) {
  182. let lineNumber = position.lineNumber;
  183. let column = position.column;
  184. let movedDown = false;
  185. if (column === model.getLineMaxColumn(lineNumber)) {
  186. if (lineNumber < model.getLineCount()) {
  187. movedDown = true;
  188. lineNumber = lineNumber + 1;
  189. column = 1;
  190. }
  191. }
  192. let nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, column));
  193. if (wordNavigationType === 2 /* WordNavigationType.WordEnd */) {
  194. if (nextWordOnLine && nextWordOnLine.wordType === 2 /* WordType.Separator */) {
  195. if (nextWordOnLine.end - nextWordOnLine.start === 1 && nextWordOnLine.nextCharClass === 0 /* WordCharacterClass.Regular */) {
  196. // Skip over a word made up of one single separator and followed by a regular character
  197. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1));
  198. }
  199. }
  200. if (nextWordOnLine) {
  201. column = nextWordOnLine.end + 1;
  202. }
  203. else {
  204. column = model.getLineMaxColumn(lineNumber);
  205. }
  206. }
  207. else if (wordNavigationType === 3 /* WordNavigationType.WordAccessibility */) {
  208. if (movedDown) {
  209. // If we move to the next line, pretend that the cursor is right before the first character.
  210. // This is needed when the first word starts right at the first character - and in order not to miss it,
  211. // we need to start before.
  212. column = 0;
  213. }
  214. while (nextWordOnLine
  215. && (nextWordOnLine.wordType === 2 /* WordType.Separator */
  216. || nextWordOnLine.start + 1 <= column)) {
  217. // Skip over a word made up of one single separator
  218. // Also skip over word if it begins before current cursor position to ascertain we're moving forward at least 1 character.
  219. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1));
  220. }
  221. if (nextWordOnLine) {
  222. column = nextWordOnLine.start + 1;
  223. }
  224. else {
  225. column = model.getLineMaxColumn(lineNumber);
  226. }
  227. }
  228. else {
  229. if (nextWordOnLine && !movedDown && column >= nextWordOnLine.start + 1) {
  230. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1));
  231. }
  232. if (nextWordOnLine) {
  233. column = nextWordOnLine.start + 1;
  234. }
  235. else {
  236. column = model.getLineMaxColumn(lineNumber);
  237. }
  238. }
  239. return new Position(lineNumber, column);
  240. }
  241. static _moveWordPartRight(model, position) {
  242. const lineNumber = position.lineNumber;
  243. const maxColumn = model.getLineMaxColumn(lineNumber);
  244. if (position.column === maxColumn) {
  245. return (lineNumber < model.getLineCount() ? new Position(lineNumber + 1, 1) : position);
  246. }
  247. const lineContent = model.getLineContent(lineNumber);
  248. for (let column = position.column + 1; column < maxColumn; column++) {
  249. const left = lineContent.charCodeAt(column - 2);
  250. const right = lineContent.charCodeAt(column - 1);
  251. if (left !== 95 /* CharCode.Underline */ && right === 95 /* CharCode.Underline */) {
  252. // snake_case_variables
  253. return new Position(lineNumber, column);
  254. }
  255. if ((strings.isLowerAsciiLetter(left) || strings.isAsciiDigit(left)) && strings.isUpperAsciiLetter(right)) {
  256. // camelCaseVariables
  257. return new Position(lineNumber, column);
  258. }
  259. if (strings.isUpperAsciiLetter(left) && strings.isUpperAsciiLetter(right)) {
  260. // thisIsACamelCaseWithOneLetterWords
  261. if (column + 1 < maxColumn) {
  262. const rightRight = lineContent.charCodeAt(column);
  263. if (strings.isLowerAsciiLetter(rightRight) || strings.isAsciiDigit(rightRight)) {
  264. return new Position(lineNumber, column);
  265. }
  266. }
  267. }
  268. }
  269. return new Position(lineNumber, maxColumn);
  270. }
  271. static _deleteWordLeftWhitespace(model, position) {
  272. const lineContent = model.getLineContent(position.lineNumber);
  273. const startIndex = position.column - 2;
  274. const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex);
  275. if (lastNonWhitespace + 1 < startIndex) {
  276. return new Range(position.lineNumber, lastNonWhitespace + 2, position.lineNumber, position.column);
  277. }
  278. return null;
  279. }
  280. static deleteWordLeft(ctx, wordNavigationType) {
  281. const wordSeparators = ctx.wordSeparators;
  282. const model = ctx.model;
  283. const selection = ctx.selection;
  284. const whitespaceHeuristics = ctx.whitespaceHeuristics;
  285. if (!selection.isEmpty()) {
  286. return selection;
  287. }
  288. if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingDelete, ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection], ctx.autoClosedCharacters)) {
  289. const position = ctx.selection.getPosition();
  290. return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1);
  291. }
  292. const position = new Position(selection.positionLineNumber, selection.positionColumn);
  293. let lineNumber = position.lineNumber;
  294. let column = position.column;
  295. if (lineNumber === 1 && column === 1) {
  296. // Ignore deleting at beginning of file
  297. return null;
  298. }
  299. if (whitespaceHeuristics) {
  300. const r = this._deleteWordLeftWhitespace(model, position);
  301. if (r) {
  302. return r;
  303. }
  304. }
  305. let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
  306. if (wordNavigationType === 0 /* WordNavigationType.WordStart */) {
  307. if (prevWordOnLine) {
  308. column = prevWordOnLine.start + 1;
  309. }
  310. else {
  311. if (column > 1) {
  312. column = 1;
  313. }
  314. else {
  315. lineNumber--;
  316. column = model.getLineMaxColumn(lineNumber);
  317. }
  318. }
  319. }
  320. else {
  321. if (prevWordOnLine && column <= prevWordOnLine.end + 1) {
  322. prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1));
  323. }
  324. if (prevWordOnLine) {
  325. column = prevWordOnLine.end + 1;
  326. }
  327. else {
  328. if (column > 1) {
  329. column = 1;
  330. }
  331. else {
  332. lineNumber--;
  333. column = model.getLineMaxColumn(lineNumber);
  334. }
  335. }
  336. }
  337. return new Range(lineNumber, column, position.lineNumber, position.column);
  338. }
  339. static deleteInsideWord(wordSeparators, model, selection) {
  340. if (!selection.isEmpty()) {
  341. return selection;
  342. }
  343. const position = new Position(selection.positionLineNumber, selection.positionColumn);
  344. const r = this._deleteInsideWordWhitespace(model, position);
  345. if (r) {
  346. return r;
  347. }
  348. return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position);
  349. }
  350. static _charAtIsWhitespace(str, index) {
  351. const charCode = str.charCodeAt(index);
  352. return (charCode === 32 /* CharCode.Space */ || charCode === 9 /* CharCode.Tab */);
  353. }
  354. static _deleteInsideWordWhitespace(model, position) {
  355. const lineContent = model.getLineContent(position.lineNumber);
  356. const lineContentLength = lineContent.length;
  357. if (lineContentLength === 0) {
  358. // empty line
  359. return null;
  360. }
  361. let leftIndex = Math.max(position.column - 2, 0);
  362. if (!this._charAtIsWhitespace(lineContent, leftIndex)) {
  363. // touches a non-whitespace character to the left
  364. return null;
  365. }
  366. let rightIndex = Math.min(position.column - 1, lineContentLength - 1);
  367. if (!this._charAtIsWhitespace(lineContent, rightIndex)) {
  368. // touches a non-whitespace character to the right
  369. return null;
  370. }
  371. // walk over whitespace to the left
  372. while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) {
  373. leftIndex--;
  374. }
  375. // walk over whitespace to the right
  376. while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) {
  377. rightIndex++;
  378. }
  379. return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2);
  380. }
  381. static _deleteInsideWordDetermineDeleteRange(wordSeparators, model, position) {
  382. const lineContent = model.getLineContent(position.lineNumber);
  383. const lineLength = lineContent.length;
  384. if (lineLength === 0) {
  385. // empty line
  386. if (position.lineNumber > 1) {
  387. return new Range(position.lineNumber - 1, model.getLineMaxColumn(position.lineNumber - 1), position.lineNumber, 1);
  388. }
  389. else {
  390. if (position.lineNumber < model.getLineCount()) {
  391. return new Range(position.lineNumber, 1, position.lineNumber + 1, 1);
  392. }
  393. else {
  394. // empty model
  395. return new Range(position.lineNumber, 1, position.lineNumber, 1);
  396. }
  397. }
  398. }
  399. const touchesWord = (word) => {
  400. return (word.start + 1 <= position.column && position.column <= word.end + 1);
  401. };
  402. const createRangeWithPosition = (startColumn, endColumn) => {
  403. startColumn = Math.min(startColumn, position.column);
  404. endColumn = Math.max(endColumn, position.column);
  405. return new Range(position.lineNumber, startColumn, position.lineNumber, endColumn);
  406. };
  407. const deleteWordAndAdjacentWhitespace = (word) => {
  408. let startColumn = word.start + 1;
  409. let endColumn = word.end + 1;
  410. let expandedToTheRight = false;
  411. while (endColumn - 1 < lineLength && this._charAtIsWhitespace(lineContent, endColumn - 1)) {
  412. expandedToTheRight = true;
  413. endColumn++;
  414. }
  415. if (!expandedToTheRight) {
  416. while (startColumn > 1 && this._charAtIsWhitespace(lineContent, startColumn - 2)) {
  417. startColumn--;
  418. }
  419. }
  420. return createRangeWithPosition(startColumn, endColumn);
  421. };
  422. const prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
  423. if (prevWordOnLine && touchesWord(prevWordOnLine)) {
  424. return deleteWordAndAdjacentWhitespace(prevWordOnLine);
  425. }
  426. const nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position);
  427. if (nextWordOnLine && touchesWord(nextWordOnLine)) {
  428. return deleteWordAndAdjacentWhitespace(nextWordOnLine);
  429. }
  430. if (prevWordOnLine && nextWordOnLine) {
  431. return createRangeWithPosition(prevWordOnLine.end + 1, nextWordOnLine.start + 1);
  432. }
  433. if (prevWordOnLine) {
  434. return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1);
  435. }
  436. if (nextWordOnLine) {
  437. return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1);
  438. }
  439. return createRangeWithPosition(1, lineLength + 1);
  440. }
  441. static _deleteWordPartLeft(model, selection) {
  442. if (!selection.isEmpty()) {
  443. return selection;
  444. }
  445. const pos = selection.getPosition();
  446. const toPosition = WordOperations._moveWordPartLeft(model, pos);
  447. return new Range(pos.lineNumber, pos.column, toPosition.lineNumber, toPosition.column);
  448. }
  449. static _findFirstNonWhitespaceChar(str, startIndex) {
  450. const len = str.length;
  451. for (let chIndex = startIndex; chIndex < len; chIndex++) {
  452. const ch = str.charAt(chIndex);
  453. if (ch !== ' ' && ch !== '\t') {
  454. return chIndex;
  455. }
  456. }
  457. return len;
  458. }
  459. static _deleteWordRightWhitespace(model, position) {
  460. const lineContent = model.getLineContent(position.lineNumber);
  461. const startIndex = position.column - 1;
  462. const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex);
  463. if (startIndex + 1 < firstNonWhitespace) {
  464. // bingo
  465. return new Range(position.lineNumber, position.column, position.lineNumber, firstNonWhitespace + 1);
  466. }
  467. return null;
  468. }
  469. static deleteWordRight(ctx, wordNavigationType) {
  470. const wordSeparators = ctx.wordSeparators;
  471. const model = ctx.model;
  472. const selection = ctx.selection;
  473. const whitespaceHeuristics = ctx.whitespaceHeuristics;
  474. if (!selection.isEmpty()) {
  475. return selection;
  476. }
  477. const position = new Position(selection.positionLineNumber, selection.positionColumn);
  478. let lineNumber = position.lineNumber;
  479. let column = position.column;
  480. const lineCount = model.getLineCount();
  481. const maxColumn = model.getLineMaxColumn(lineNumber);
  482. if (lineNumber === lineCount && column === maxColumn) {
  483. // Ignore deleting at end of file
  484. return null;
  485. }
  486. if (whitespaceHeuristics) {
  487. const r = this._deleteWordRightWhitespace(model, position);
  488. if (r) {
  489. return r;
  490. }
  491. }
  492. let nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position);
  493. if (wordNavigationType === 2 /* WordNavigationType.WordEnd */) {
  494. if (nextWordOnLine) {
  495. column = nextWordOnLine.end + 1;
  496. }
  497. else {
  498. if (column < maxColumn || lineNumber === lineCount) {
  499. column = maxColumn;
  500. }
  501. else {
  502. lineNumber++;
  503. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, 1));
  504. if (nextWordOnLine) {
  505. column = nextWordOnLine.start + 1;
  506. }
  507. else {
  508. column = model.getLineMaxColumn(lineNumber);
  509. }
  510. }
  511. }
  512. }
  513. else {
  514. if (nextWordOnLine && column >= nextWordOnLine.start + 1) {
  515. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1));
  516. }
  517. if (nextWordOnLine) {
  518. column = nextWordOnLine.start + 1;
  519. }
  520. else {
  521. if (column < maxColumn || lineNumber === lineCount) {
  522. column = maxColumn;
  523. }
  524. else {
  525. lineNumber++;
  526. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, 1));
  527. if (nextWordOnLine) {
  528. column = nextWordOnLine.start + 1;
  529. }
  530. else {
  531. column = model.getLineMaxColumn(lineNumber);
  532. }
  533. }
  534. }
  535. }
  536. return new Range(lineNumber, column, position.lineNumber, position.column);
  537. }
  538. static _deleteWordPartRight(model, selection) {
  539. if (!selection.isEmpty()) {
  540. return selection;
  541. }
  542. const pos = selection.getPosition();
  543. const toPosition = WordOperations._moveWordPartRight(model, pos);
  544. return new Range(pos.lineNumber, pos.column, toPosition.lineNumber, toPosition.column);
  545. }
  546. static _createWordAtPosition(model, lineNumber, word) {
  547. const range = new Range(lineNumber, word.start + 1, lineNumber, word.end + 1);
  548. return {
  549. word: model.getValueInRange(range),
  550. startColumn: range.startColumn,
  551. endColumn: range.endColumn
  552. };
  553. }
  554. static getWordAtPosition(model, _wordSeparators, position) {
  555. const wordSeparators = getMapForWordSeparators(_wordSeparators);
  556. const prevWord = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
  557. if (prevWord && prevWord.wordType === 1 /* WordType.Regular */ && prevWord.start <= position.column - 1 && position.column - 1 <= prevWord.end) {
  558. return WordOperations._createWordAtPosition(model, position.lineNumber, prevWord);
  559. }
  560. const nextWord = WordOperations._findNextWordOnLine(wordSeparators, model, position);
  561. if (nextWord && nextWord.wordType === 1 /* WordType.Regular */ && nextWord.start <= position.column - 1 && position.column - 1 <= nextWord.end) {
  562. return WordOperations._createWordAtPosition(model, position.lineNumber, nextWord);
  563. }
  564. return null;
  565. }
  566. static word(config, model, cursor, inSelectionMode, position) {
  567. const wordSeparators = getMapForWordSeparators(config.wordSeparators);
  568. const prevWord = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
  569. const nextWord = WordOperations._findNextWordOnLine(wordSeparators, model, position);
  570. if (!inSelectionMode) {
  571. // Entering word selection for the first time
  572. let startColumn;
  573. let endColumn;
  574. if (prevWord && prevWord.wordType === 1 /* WordType.Regular */ && prevWord.start <= position.column - 1 && position.column - 1 <= prevWord.end) {
  575. // isTouchingPrevWord
  576. startColumn = prevWord.start + 1;
  577. endColumn = prevWord.end + 1;
  578. }
  579. else if (nextWord && nextWord.wordType === 1 /* WordType.Regular */ && nextWord.start <= position.column - 1 && position.column - 1 <= nextWord.end) {
  580. // isTouchingNextWord
  581. startColumn = nextWord.start + 1;
  582. endColumn = nextWord.end + 1;
  583. }
  584. else {
  585. if (prevWord) {
  586. startColumn = prevWord.end + 1;
  587. }
  588. else {
  589. startColumn = 1;
  590. }
  591. if (nextWord) {
  592. endColumn = nextWord.start + 1;
  593. }
  594. else {
  595. endColumn = model.getLineMaxColumn(position.lineNumber);
  596. }
  597. }
  598. return new SingleCursorState(new Range(position.lineNumber, startColumn, position.lineNumber, endColumn), 0, new Position(position.lineNumber, endColumn), 0);
  599. }
  600. let startColumn;
  601. let endColumn;
  602. if (prevWord && prevWord.wordType === 1 /* WordType.Regular */ && prevWord.start < position.column - 1 && position.column - 1 < prevWord.end) {
  603. // isInsidePrevWord
  604. startColumn = prevWord.start + 1;
  605. endColumn = prevWord.end + 1;
  606. }
  607. else if (nextWord && nextWord.wordType === 1 /* WordType.Regular */ && nextWord.start < position.column - 1 && position.column - 1 < nextWord.end) {
  608. // isInsideNextWord
  609. startColumn = nextWord.start + 1;
  610. endColumn = nextWord.end + 1;
  611. }
  612. else {
  613. startColumn = position.column;
  614. endColumn = position.column;
  615. }
  616. const lineNumber = position.lineNumber;
  617. let column;
  618. if (cursor.selectionStart.containsPosition(position)) {
  619. column = cursor.selectionStart.endColumn;
  620. }
  621. else if (position.isBeforeOrEqual(cursor.selectionStart.getStartPosition())) {
  622. column = startColumn;
  623. const possiblePosition = new Position(lineNumber, column);
  624. if (cursor.selectionStart.containsPosition(possiblePosition)) {
  625. column = cursor.selectionStart.endColumn;
  626. }
  627. }
  628. else {
  629. column = endColumn;
  630. const possiblePosition = new Position(lineNumber, column);
  631. if (cursor.selectionStart.containsPosition(possiblePosition)) {
  632. column = cursor.selectionStart.startColumn;
  633. }
  634. }
  635. return cursor.move(true, lineNumber, column, 0);
  636. }
  637. }
  638. export class WordPartOperations extends WordOperations {
  639. static deleteWordPartLeft(ctx) {
  640. const candidates = enforceDefined([
  641. WordOperations.deleteWordLeft(ctx, 0 /* WordNavigationType.WordStart */),
  642. WordOperations.deleteWordLeft(ctx, 2 /* WordNavigationType.WordEnd */),
  643. WordOperations._deleteWordPartLeft(ctx.model, ctx.selection)
  644. ]);
  645. candidates.sort(Range.compareRangesUsingEnds);
  646. return candidates[2];
  647. }
  648. static deleteWordPartRight(ctx) {
  649. const candidates = enforceDefined([
  650. WordOperations.deleteWordRight(ctx, 0 /* WordNavigationType.WordStart */),
  651. WordOperations.deleteWordRight(ctx, 2 /* WordNavigationType.WordEnd */),
  652. WordOperations._deleteWordPartRight(ctx.model, ctx.selection)
  653. ]);
  654. candidates.sort(Range.compareRangesUsingStarts);
  655. return candidates[0];
  656. }
  657. static moveWordPartLeft(wordSeparators, model, position) {
  658. const candidates = enforceDefined([
  659. WordOperations.moveWordLeft(wordSeparators, model, position, 0 /* WordNavigationType.WordStart */),
  660. WordOperations.moveWordLeft(wordSeparators, model, position, 2 /* WordNavigationType.WordEnd */),
  661. WordOperations._moveWordPartLeft(model, position)
  662. ]);
  663. candidates.sort(Position.compare);
  664. return candidates[2];
  665. }
  666. static moveWordPartRight(wordSeparators, model, position) {
  667. const candidates = enforceDefined([
  668. WordOperations.moveWordRight(wordSeparators, model, position, 0 /* WordNavigationType.WordStart */),
  669. WordOperations.moveWordRight(wordSeparators, model, position, 2 /* WordNavigationType.WordEnd */),
  670. WordOperations._moveWordPartRight(model, position)
  671. ]);
  672. candidates.sort(Position.compare);
  673. return candidates[0];
  674. }
  675. }
  676. function enforceDefined(arr) {
  677. return arr.filter(el => Boolean(el));
  678. }