Skip to content

Commit

Permalink
add a config for copying with trailing line break
Browse files Browse the repository at this point in the history
  • Loading branch information
caohai committed Jun 26, 2023
1 parent 5f7a182 commit 67028f4
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/sql/azdata.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ declare module 'azdata' {
* Whether to remove line breaks from the cell value.
*/
removeNewLines: boolean;
/**
* Whether to avoid adding a line break between rows during row concatenation for copying result when the previous row already has a trailing line break.
*/
avoidNewLineAfterTailingLineBreak: boolean;
/**
* The selected ranges to be copied.
*/
Expand Down
1 change: 1 addition & 0 deletions src/sql/platform/query/common/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface IQueryEditorConfiguration {
readonly streaming: boolean;
readonly copyIncludeHeaders: boolean;
readonly copyRemoveNewLine: boolean;
readonly avoidNewLineAfterTailingLineBreak: boolean;
readonly optimizedTable: boolean;
readonly inMemoryDataProcessingThreshold: number;
readonly openAfterSave: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDataResource, rowHasColumnNameKeys } from 'sql/workbench/services/notebook/browser/sql/sqlSessionManager';
import { getEolString, shouldIncludeHeaders, shouldRemoveNewLines } from 'sql/workbench/services/query/common/queryRunner';
import { getEolString, shouldAvoidNewLineAfterTailingLineBreak, shouldIncludeHeaders, shouldRemoveNewLines } from 'sql/workbench/services/query/common/queryRunner';
import { ResultSetSummary, ResultSetSubset, ICellValue } from 'sql/workbench/services/query/common/query';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
Expand Down Expand Up @@ -442,6 +442,9 @@ export class DataResourceDataProvider implements IGridDataProvider {
shouldRemoveNewLines(): boolean {
return shouldRemoveNewLines(this._configurationService);
}
shouldAvoidNewLineAfterTailingLineBreak(): boolean {
return shouldAvoidNewLineAfterTailingLineBreak(this._configurationService);
}

getColumnHeaders(range: Slick.Range): string[] {
let headers: string[] = this._resultSet.columnInfo.slice(range.fromCell, range.toCell + 1).map((info, i) => {
Expand Down
5 changes: 5 additions & 0 deletions src/sql/workbench/contrib/query/browser/query.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ const queryEditorConfiguration: IConfigurationNode = {
'description': localize('queryEditor.results.copyRemoveNewLine', "Configuration options for copying multi-line results from the Results View"),
'default': true
},
'queryEditor.results.avoidNewLineAfterTailingLineBreak': {
'type': 'boolean',
'description': localize('queryEditor.results.avoidNewLineAfterTailingLineBreak', "Whether to avoid adding a line break between rows during row concatenation for copying result when the previous row already has a trailing line break. The default value is false."),
'default': false
},
'queryEditor.results.preferProvidersCopyHandler': {
'type': 'boolean',
'description': localize('queryEditor.results.preferProvidersCopyHandler', "Whether the copy result request should be handled by the query provider when it is supported. The default value is true, set this to false to force all copy handling to be done by Azure Data Studio."),
Expand Down
6 changes: 5 additions & 1 deletion src/sql/workbench/services/query/common/gridDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface IGridDataProvider {
shouldIncludeHeaders(includeHeaders: boolean): boolean;

shouldRemoveNewLines(): boolean;
shouldAvoidNewLineAfterTailingLineBreak(): boolean;

getColumnHeaders(range: Slick.Range): string[] | undefined;

Expand Down Expand Up @@ -101,6 +102,7 @@ export async function copySelectionToClipboard(clipboardService: IClipboardServi
const eol = provider.getEolString();
const valueSeparator = '\t';
const shouldRemoveNewLines = provider.shouldRemoveNewLines();
const shouldAvoidNewLineAfterTailingLineBreak = provider.shouldAvoidNewLineAfterTailingLineBreak();

// Merge the selections to get the unique columns and unique rows.
const gridRanges = GridRange.fromSlickRanges(selections);
Expand Down Expand Up @@ -154,7 +156,9 @@ export async function copySelectionToClipboard(clipboardService: IClipboardServi
});
}
if (!cancellationTokenSource.token.isCancellationRequested) {
resultString += rowValues.join(eol);
resultString += rowValues.reduce(
(prevVal, currVal, idx) => prevVal + (idx > 0 && (!prevVal?.endsWith(eol) || !shouldAvoidNewLineAfterTailingLineBreak) ? eol : '') + currVal,
);
await clipboardService.writeText(resultString);
}
}, cancellationTokenSource);
Expand Down
9 changes: 6 additions & 3 deletions src/sql/workbench/services/query/common/queryModelService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as GridContentEvents from 'sql/workbench/services/query/common/gridContentEvents';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import QueryRunner, { shouldRemoveNewLines, shouldAvoidNewLineAfterTailingLineBreak } from 'sql/workbench/services/query/common/queryRunner';
import { ICellValue, ResultSetSubset } from 'sql/workbench/services/query/common/query';
import { DataService } from 'sql/workbench/services/query/common/dataService';
import { IQueryModelService, IQueryEvent } from 'sql/workbench/services/query/common/queryModel';
Expand All @@ -21,6 +21,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import Severity from 'vs/base/common/severity';
import EditQueryRunner from 'sql/workbench/services/editData/common/editQueryRunner';
import { IRange } from 'vs/editor/common/core/range';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

const selectionSnippetMaxLen = 100;

Expand Down Expand Up @@ -82,7 +83,8 @@ export class QueryModelService implements IQueryModelService {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@INotificationService private _notificationService: INotificationService,
@ILogService private _logService: ILogService
@ILogService private _logService: ILogService,
@IConfigurationService private _configurationService: IConfigurationService
) {
this._queryInfoMap = new Map<string, QueryInfo>();
this._onRunQueryStart = new Emitter<string>();
Expand Down Expand Up @@ -170,7 +172,8 @@ export class QueryModelService implements IQueryModelService {

public async copyResults(uri: string, selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): Promise<void> {
if (this._queryInfoMap.has(uri)) {
return this._queryInfoMap.get(uri)!.queryRunner!.copyResults(selection, batchId, resultId, includeHeaders);
const runner = this._queryInfoMap.get(uri)!.queryRunner;
return runner!.copyResults(selection, batchId, resultId, shouldRemoveNewLines(this._configurationService), shouldAvoidNewLineAfterTailingLineBreak(this._configurationService), includeHeaders);
}
}

Expand Down
14 changes: 12 additions & 2 deletions src/sql/workbench/services/query/common/queryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,13 @@ export default class QueryRunner extends Disposable {
* @param removeNewLines Whether to remove line breaks from values.
* @param includeHeaders [Optional]: Should column headers be included in the copy selection
*/
async copyResults(selections: Slick.Range[], batchId: number, resultId: number, removeNewLines: boolean, includeHeaders?: boolean): Promise<void> {
async copyResults(selections: Slick.Range[], batchId: number, resultId: number, removeNewLines: boolean, avoidNewLineAfterTailingLineBreak: boolean, includeHeaders?: boolean): Promise<void> {
await this.queryManagementService.copyResults({
ownerUri: this.uri,
batchIndex: batchId,
resultSetIndex: resultId,
removeNewLines: removeNewLines,
avoidNewLineAfterTailingLineBreak: avoidNewLineAfterTailingLineBreak,
includeHeaders: includeHeaders,
selections: selections.map(selection => {
return {
Expand Down Expand Up @@ -592,7 +593,7 @@ export class QueryGridDataProvider implements IGridDataProvider {

private async handleCopyRequestByProvider(selections: Slick.Range[], includeHeaders?: boolean): Promise<void> {
executeCopyWithNotification(this._notificationService, selections, async () => {
await this.queryRunner.copyResults(selections, this.batchId, this.resultSetId, this.shouldRemoveNewLines(), this.shouldIncludeHeaders(includeHeaders));
await this.queryRunner.copyResults(selections, this.batchId, this.resultSetId, this.shouldRemoveNewLines(), this.shouldAvoidNewLineAfterTailingLineBreak(), this.shouldIncludeHeaders(includeHeaders));
});
}

Expand All @@ -614,6 +615,9 @@ export class QueryGridDataProvider implements IGridDataProvider {
shouldRemoveNewLines(): boolean {
return shouldRemoveNewLines(this._configurationService);
}
shouldAvoidNewLineAfterTailingLineBreak(): boolean {
return shouldAvoidNewLineAfterTailingLineBreak(this._configurationService);
}
getColumnHeaders(range: Slick.Range): string[] | undefined {
return this.queryRunner.getColumnHeaders(this.batchId, this.resultSetId, range);
}
Expand Down Expand Up @@ -648,6 +652,12 @@ export function shouldRemoveNewLines(configurationService: IConfigurationService
return !!removeNewLines;
}

export function shouldAvoidNewLineAfterTailingLineBreak(configurationService: IConfigurationService): boolean {
// get config avoidNewLineAfterTailingLineBreak option from vscode config
let avoidNewLineAfterTailingLineBreak = configurationService.getValue<IQueryEditorConfiguration>('queryEditor').results.avoidNewLineAfterTailingLineBreak;
return !!avoidNewLineAfterTailingLineBreak;
}

function isRangeOrUndefined(input: string | IRange | undefined): input is IRange | undefined {
return Range.isIRange(input) || types.isUndefinedOrNull(input);
}

0 comments on commit 67028f4

Please sign in to comment.