Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose adjustWhitespace to TextEditor API. #234858

Merged
merged 4 commits into from
Jan 31, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts
Original file line number Diff line number Diff line change
@@ -94,6 +94,37 @@ suite('vscode API - editors', () => {
});
});

/**
* Given :
* This is line 1
* |
*
* Expect :
* This is line 1
* This is line 2
* This is line 3
*
* The 3rd line should not be auto-indented, as the edit already
* contains the necessary adjustment.
*/
test('insert snippet with replacement, avoid adjusting indentation', () => {
const snippetString = new SnippetString()
.appendText('This is line 2\n This is line 3');

return withRandomFileEditor('This is line 1\n ', (editor, doc) => {
editor.selection = new Selection(
new Position(1, 3),
new Position(1, 3)
);

return editor.insertSnippet(snippetString, undefined, { undoStopAfter: false, undoStopBefore: false, keepWhitespace: true }).then(inserted => {
assert.ok(inserted);
assert.strictEqual(doc.getText(), 'This is line 1\n This is line 2\n This is line 3');
assert.ok(doc.isDirty);
});
});
});

test('insert snippet with replacement, selection as argument', () => {
const snippetString = new SnippetString()
.appendText('has been');
Original file line number Diff line number Diff line change
@@ -1226,6 +1226,27 @@ suite('vscode API - workspace', () => {
assert.deepStrictEqual(edt.selections, [new vscode.Selection(0, 0, 0, 3)]);
});

test('SnippetString in WorkspaceEdit with keepWhitespace', async function (): Promise<any> {
const file = await createRandomFile('This is line 1\n ');

const document = await vscode.workspace.openTextDocument(file);
const edt = await vscode.window.showTextDocument(document);

assert.ok(edt === vscode.window.activeTextEditor);

const snippetText = new vscode.SnippetTextEdit(new vscode.Range(1, 3, 1, 3), new vscode.SnippetString('This is line 2\n This is line 3'));
snippetText.keepWhitespace = true;
const we = new vscode.WorkspaceEdit();
we.set(document.uri, [snippetText]);
const success = await vscode.workspace.applyEdit(we);
if (edt !== vscode.window.activeTextEditor) {
return this.skip();
}

assert.ok(success);
assert.strictEqual(document.getText(), 'This is line 1\n This is line 2\n This is line 3');
});

test('Support creating binary files in a WorkspaceEdit', async function (): Promise<any> {

const fileUri = vscode.Uri.parse(`${testFs.scheme}:/${rndName()}`);
2 changes: 1 addition & 1 deletion src/vs/editor/browser/services/bulkEditService.ts
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ export class ResourceTextEdit extends ResourceEdit implements IWorkspaceTextEdit

constructor(
readonly resource: URI,
readonly textEdit: TextEdit & { insertAsSnippet?: boolean },
readonly textEdit: TextEdit & { insertAsSnippet?: boolean; keepWhitespace?: boolean },
readonly versionId: number | undefined = undefined,
metadata?: WorkspaceEditMetadata,
) {
2 changes: 1 addition & 1 deletion src/vs/editor/common/languages.ts
Original file line number Diff line number Diff line change
@@ -1795,7 +1795,7 @@ export interface IWorkspaceFileEdit {

export interface IWorkspaceTextEdit {
resource: URI;
textEdit: TextEdit & { insertAsSnippet?: boolean };
textEdit: TextEdit & { insertAsSnippet?: boolean; keepWhitespace?: boolean };
versionId: number | undefined;
metadata?: WorkspaceEditMetadata;
}
5 changes: 3 additions & 2 deletions src/vs/editor/contrib/snippet/browser/snippetSession.ts
Original file line number Diff line number Diff line change
@@ -375,6 +375,7 @@ const _defaultOptions: ISnippetSessionInsertOptions = {
export interface ISnippetEdit {
range: Range;
template: string;
keepWhitespace?: boolean;
}

export class SnippetSession {
@@ -569,7 +570,7 @@ export class SnippetSession {
let offset = 0;
for (let i = 0; i < snippetEdits.length; i++) {

const { range, template } = snippetEdits[i];
const { range, template, keepWhitespace } = snippetEdits[i];

// gaps between snippet edits are appended as text nodes. this
// ensures placeholder-offsets are later correct
@@ -582,7 +583,7 @@ export class SnippetSession {
}

const newNodes = parser.parseFragment(template, snippet);
SnippetSession.adjustWhitespace(model, range.getStartPosition(), true, snippet, new Set(newNodes));
SnippetSession.adjustWhitespace(model, range.getStartPosition(), keepWhitespace !== undefined ? !keepWhitespace : adjustWhitespace, snippet, new Set(newNodes));
snippet.resolveVariables(resolver);

const snippetText = snippet.toString();
1 change: 1 addition & 0 deletions src/vs/monaco.d.ts
Original file line number Diff line number Diff line change
@@ -7973,6 +7973,7 @@ declare namespace monaco.languages {
resource: Uri;
textEdit: TextEdit & {
insertAsSnippet?: boolean;
keepWhitespace?: boolean;
};
versionId: number | undefined;
metadata?: WorkspaceEditMetadata;
5 changes: 3 additions & 2 deletions src/vs/workbench/api/browser/mainThreadEditor.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import { ITextModel, ITextModelUpdateOptions } from '../../../editor/common/mode
import { ISingleEditOperation } from '../../../editor/common/core/editOperation.js';
import { IModelService } from '../../../editor/common/services/model.js';
import { SnippetController2 } from '../../../editor/contrib/snippet/browser/snippetController2.js';
import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from '../common/extHost.protocol.js';
import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ISnippetOptions, ITextEditorConfigurationUpdate, TextEditorRevealType } from '../common/extHost.protocol.js';
import { IEditorPane } from '../../common/editor.js';
import { equals } from '../../../base/common/arrays.js';
import { CodeEditorStateFlag, EditorState } from '../../../editor/contrib/editorState/browser/editorState.js';
@@ -509,7 +509,7 @@ export class MainThreadTextEditor {
return true;
}

async insertSnippet(modelVersionId: number, template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
async insertSnippet(modelVersionId: number, template: string, ranges: readonly IRange[], opts: ISnippetOptions) {

if (!this._codeEditor || !this._codeEditor.hasModel()) {
return false;
@@ -542,6 +542,7 @@ export class MainThreadTextEditor {
snippetController.apply(edits, {
overwriteBefore: 0, overwriteAfter: 0,
undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter,
adjustWhitespace: !opts.keepWhitespace,
clipboardText
});

3 changes: 3 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
@@ -261,6 +261,9 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine?: EndOfLineSequence;
}

export interface ISnippetOptions extends IUndoStopOptions {
keepWhitespace?: boolean;
}
export interface ITextDocumentShowOptions {
position?: EditorGroupColumn;
preserveFocus?: boolean;
5 changes: 4 additions & 1 deletion src/vs/workbench/api/common/extHostTextEditor.ts
Original file line number Diff line number Diff line change
@@ -495,7 +495,7 @@ export class ExtHostTextEditor {
return that._applyEdit(edit);
},
// --- snippet edit
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; keepWhitespace?: boolean } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (that._disposed) {
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
}
@@ -521,6 +521,9 @@ export class ExtHostTextEditor {
}
}
}
if (options.keepWhitespace === undefined) {
options.keepWhitespace = false;
}
return _proxy.$tryInsertSnippet(id, document.value.version, snippet.value, ranges, options);
},
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
3 changes: 2 additions & 1 deletion src/vs/workbench/api/common/extHostTypeConverters.ts
Original file line number Diff line number Diff line change
@@ -657,7 +657,8 @@ export namespace WorkspaceEdit {
textEdit: {
range: Range.from(entry.range),
text: entry.edit.value,
insertAsSnippet: true
insertAsSnippet: true,
keepWhitespace: entry.keepWhitespace
},
versionId: !toCreate.has(entry.uri) ? versionInfo?.getTextDocumentVersion(entry.uri) : undefined,
metadata: entry.metadata
5 changes: 4 additions & 1 deletion src/vs/workbench/api/common/extHostTypes.ts
Original file line number Diff line number Diff line change
@@ -766,6 +766,8 @@ export class SnippetTextEdit implements vscode.SnippetTextEdit {

snippet: SnippetString;

keepWhitespace?: boolean;

constructor(range: Range, snippet: SnippetString) {
this.range = range;
this.snippet = snippet;
@@ -809,6 +811,7 @@ export interface IFileSnippetTextEdit {
readonly range: vscode.Range;
readonly edit: vscode.SnippetString;
readonly metadata?: vscode.WorkspaceEditEntryMetadata;
readonly keepWhitespace?: boolean;
}

export interface IFileCellEdit {
@@ -938,7 +941,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
this.replaceNotebookCells(uri, edit.range, edit.newCells, metadata);
}
} else if (SnippetTextEdit.isSnippetTextEdit(edit)) {
this._edits.push({ _type: FileEditType.Snippet, uri, range: edit.range, edit: edit.snippet, metadata });
this._edits.push({ _type: FileEditType.Snippet, uri, range: edit.range, edit: edit.snippet, metadata, keepWhitespace: edit.keepWhitespace });

} else {
this._edits.push({ _type: FileEditType.Text, uri, edit, metadata });
7 changes: 4 additions & 3 deletions src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ import { ISnippetEdit } from '../../../../editor/contrib/snippet/browser/snippet

type ValidationResult = { canApply: true } | { canApply: false; reason: URI };

type ISingleSnippetEditOperation = ISingleEditOperation & { insertAsSnippet?: boolean };
type ISingleSnippetEditOperation = ISingleEditOperation & { insertAsSnippet?: boolean; keepWhitespace?: boolean };

class ModelEditTask implements IDisposable {

@@ -80,7 +80,7 @@ class ModelEditTask implements IDisposable {
} else {
range = Range.lift(textEdit.range);
}
this._edits.push({ ...EditOperation.replaceMove(range, textEdit.text), insertAsSnippet: textEdit.insertAsSnippet });
this._edits.push({ ...EditOperation.replaceMove(range, textEdit.text), insertAsSnippet: textEdit.insertAsSnippet, keepWhitespace: textEdit.keepWhitespace });
}

validate(): ValidationResult {
@@ -152,7 +152,8 @@ class EditorEditTask extends ModelEditTask {
if (edit.range && edit.text !== null) {
snippetEdits.push({
range: Range.lift(edit.range),
template: edit.insertAsSnippet ? edit.text : SnippetParser.escape(edit.text)
template: edit.insertAsSnippet ? edit.text : SnippetParser.escape(edit.text),
keepWhitespace: edit.keepWhitespace
});
}
}
9 changes: 9 additions & 0 deletions src/vscode-dts/vscode.d.ts
Original file line number Diff line number Diff line change
@@ -1312,6 +1312,10 @@ declare module 'vscode' {
* Add undo stop after making the edits.
*/
readonly undoStopAfter: boolean;
/**
* Keep whitespace of the {@link SnippetString.value} as is.
*/
readonly keepWhitespace?: boolean;
}): Thenable<boolean>;

/**
@@ -3776,6 +3780,11 @@ declare module 'vscode' {
*/
snippet: SnippetString;

/**
* Whether the snippet edit should be applied with existing whitespace preserved.
*/
keepWhitespace?: boolean;

/**
* Create a new snippet edit.
*
Loading
Oops, something went wrong.