Skip to content

Commit ee9e25e

Browse files
committed
Fixes microsoft#41744: Keep track of model selection to use for the clipboard
1 parent 062c54a commit ee9e25e

5 files changed

Lines changed: 28 additions & 29 deletions

File tree

src/vs/editor/browser/controller/textAreaHandler.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export class TextAreaHandler extends ViewPart {
7777
*/
7878
private _visibleTextArea: VisibleTextAreaData | null;
7979
private _selections: Selection[];
80+
private _modelSelections: Selection[];
8081

8182
/**
8283
* The position at which the textarea was rendered.
@@ -111,6 +112,7 @@ export class TextAreaHandler extends ViewPart {
111112

112113
this._visibleTextArea = null;
113114
this._selections = [new Selection(1, 1, 1, 1)];
115+
this._modelSelections = [new Selection(1, 1, 1, 1)];
114116
this._lastRenderPosition = null;
115117

116118
// Text Area (The focus will always be in the textarea when the cursor is blinking)
@@ -149,17 +151,17 @@ export class TextAreaHandler extends ViewPart {
149151

150152
const textAreaInputHost: ITextAreaInputHost = {
151153
getDataToCopy: (generateHTML: boolean): ClipboardDataToCopy => {
152-
const rawTextToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard, platform.isWindows);
154+
const rawTextToCopy = this._context.model.getPlainTextToCopy(this._modelSelections, this._emptySelectionClipboard, platform.isWindows);
153155
const newLineCharacter = this._context.model.getEOL();
154156

155-
const isFromEmptySelection = (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty());
157+
const isFromEmptySelection = (this._emptySelectionClipboard && this._modelSelections.length === 1 && this._modelSelections[0].isEmpty());
156158
const multicursorText = (Array.isArray(rawTextToCopy) ? rawTextToCopy : null);
157159
const text = (Array.isArray(rawTextToCopy) ? rawTextToCopy.join(newLineCharacter) : rawTextToCopy);
158160

159161
let html: string | null | undefined = undefined;
160162
if (generateHTML) {
161163
if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) {
162-
html = this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard);
164+
html = this._context.model.getHTMLToCopy(this._modelSelections, this._emptySelectionClipboard);
163165
}
164166
}
165167
return {
@@ -377,6 +379,7 @@ export class TextAreaHandler extends ViewPart {
377379
}
378380
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
379381
this._selections = e.selections.slice(0);
382+
this._modelSelections = e.modelSelections.slice(0);
380383
this._textAreaInput.writeScreenReaderContent('selection changed');
381384
return true;
382385
}

src/vs/editor/common/controller/cursor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
545545
// Let the view get the event first.
546546
try {
547547
const eventsCollector = this._beginEmit();
548-
eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections));
548+
eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections));
549549
} finally {
550550
this._endEmit();
551551
}

src/vs/editor/common/view/viewEvents.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,12 @@ export class ViewCursorStateChangedEvent {
6969

7070
public readonly type = ViewEventType.ViewCursorStateChanged;
7171

72-
/**
73-
* The primary selection is always at index 0.
74-
*/
7572
public readonly selections: Selection[];
73+
public readonly modelSelections: Selection[];
7674

77-
constructor(selections: Selection[]) {
75+
constructor(selections: Selection[], modelSelections: Selection[]) {
7876
this.selections = selections;
77+
this.modelSelections = modelSelections;
7978
}
8079
}
8180

src/vs/editor/common/viewModel/viewModel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ export interface IViewModel {
138138

139139
deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position;
140140
getEOL(): string;
141-
getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[];
142-
getHTMLToCopy(ranges: Range[], emptySelectionClipboard: boolean): string | null;
141+
getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[];
142+
getHTMLToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): string | null;
143143
}
144144

145145
export class MinimapLinesRenderingData {

src/vs/editor/common/viewModel/viewModelImpl.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -656,15 +656,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
656656
return this.model.getEOL();
657657
}
658658

659-
public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] {
659+
public getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] {
660660
const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL();
661661

662-
ranges = ranges.slice(0);
663-
ranges.sort(Range.compareRangesUsingStarts);
662+
modelRanges = modelRanges.slice(0);
663+
modelRanges.sort(Range.compareRangesUsingStarts);
664664

665665
let hasEmptyRange = false;
666666
let hasNonEmptyRange = false;
667-
for (const range of ranges) {
667+
for (const range of modelRanges) {
668668
if (range.isEmpty()) {
669669
hasEmptyRange = true;
670670
} else {
@@ -678,10 +678,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
678678
return '';
679679
}
680680

681-
const modelLineNumbers = ranges.map((r) => {
682-
const viewLineStart = new Position(r.startLineNumber, 1);
683-
return this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).lineNumber;
684-
});
681+
const modelLineNumbers = modelRanges.map((r) => r.startLineNumber);
685682

686683
let result = '';
687684
for (let i = 0; i < modelLineNumbers.length; i++) {
@@ -697,46 +694,46 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
697694
// mixed empty selections and non-empty selections
698695
let result: string[] = [];
699696
let prevModelLineNumber = 0;
700-
for (const range of ranges) {
701-
const modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
702-
if (range.isEmpty()) {
697+
for (const modelRange of modelRanges) {
698+
const modelLineNumber = modelRange.startLineNumber;
699+
if (modelRange.isEmpty()) {
703700
if (modelLineNumber !== prevModelLineNumber) {
704701
result.push(this.model.getLineContent(modelLineNumber));
705702
}
706703
} else {
707-
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
704+
result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
708705
}
709706
prevModelLineNumber = modelLineNumber;
710707
}
711708
return result.length === 1 ? result[0] : result;
712709
}
713710

714711
let result: string[] = [];
715-
for (const range of ranges) {
716-
if (!range.isEmpty()) {
717-
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
712+
for (const modelRange of modelRanges) {
713+
if (!modelRange.isEmpty()) {
714+
result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
718715
}
719716
}
720717
return result.length === 1 ? result[0] : result;
721718
}
722719

723-
public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string | null {
720+
public getHTMLToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): string | null {
724721
if (this.model.getLanguageIdentifier().id === LanguageId.PlainText) {
725722
return null;
726723
}
727724

728-
if (viewRanges.length !== 1) {
725+
if (modelRanges.length !== 1) {
729726
// no multiple selection support at this time
730727
return null;
731728
}
732729

733-
let range = this.coordinatesConverter.convertViewRangeToModelRange(viewRanges[0]);
730+
let range = modelRanges[0];
734731
if (range.isEmpty()) {
735732
if (!emptySelectionClipboard) {
736733
// nothing to copy
737734
return null;
738735
}
739-
let lineNumber = range.startLineNumber;
736+
const lineNumber = range.startLineNumber;
740737
range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
741738
}
742739

0 commit comments

Comments
 (0)