Looked everywhere but unsure how to accomplish this feat. I'm looking to do something like this. When a user types something like this:
const hi = 'hello world';
#generate(hi)
and the user highlights #generate(hi), the user can run a set command. This command is written like this in the extension:
const generateCodeCommand = vscode.commands.registerCommand('extension.generateCode', () => {
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
const selection = editor.selection;
// get the word within the selection
const word = document.getText(selection);
console.log('this is the word', word);
}
});
Here I've managed to grab the word '#generate(hi)' but I'm looking to grab the string 'hello world' through the 'hi' reference. Is there a particular way to do this?
Related
Problem: Loses focus when selected the hash tag.
Example: type #on and select any tag the editor is losing the focus.
In above codepen: how can I add/retain cursor after the hash tag?
Following this article
https://bigbite.net/2017/12/13/building-editor-draft-js-react/
Code from this article.
https://codepen.io/bigbite/pen/gXNOvz
I've to add that in my own use case.
UC: While editing the content in editor when user presses the key combination then show a dropdown, which will contain the custom react component name, and user will be able to select the custom component and it will add that component via decorator and strategy.
It has been achieved but the editor loses its focus at the same time.
I can achieve the focus via ref this.editor.focus() but shows the cursor at the start of the editor.
const addEntityAndComponent = (editorState, content) => {
const contentState = editorState.getCurrentContent();
const selection = editorState.getSelection();
const contentStateWithEntity = contentState.createEntity(content, 'IMMUTABLE', { content });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newContentState = Modifier.insertText(contentStateWithEntity, selection, content, null, entityKey);
const newEditorState = EditorState.push(editorState, newContentState);
return EditorState.forceSelection(newEditorState, newContentState.getSelectionAfter());
};
I'm using the lib to achieve the functionality.
https://github.com/jpuri/react-draft-wysiwyg/
Here is my code.
https://github.com/iozeey/react-draft-wysiwyg-custom-component
Hope it will be helpful.
Followed this doc.
https://draftjs.org/docs/advanced-topics-managing-focus
Tried solution from here but did not work.
https://draftjs.org/docs/api-reference-editor-state#movefocustoend
I was using this package.
https://github.com/jpuri/react-draft-wysiwyg/blob/master/src/Editor/index.js#L348
My solution was to reset the cursor position after focusing the editor.
focus() {
const se = this.state.editorState.getSelection();
this.editor.focus();
this.setState({
editorState: EditorState.forceSelection(this.state.editorState, se),
});
}
I want to add numbers to the sass-indentation extension and I kind of figured out how to that, it would be nice if i could set the cursor location when the suggestion gets triggered, just like you can set the cursor location when you make snippets with $1, is that possible ?
import { CompletionItem, CompletionItemKind } from 'vscode';
const sassSchemaTest = [
{
name: '%',
body: '$1%', // I want the cursor location where the '$' sign is
description: 'test'
}
];
export default sassSchemaTest.map(item => {
const completionItem = new CompletionItem(item.name);
completionItem.insertText = item.body;
completionItem.detail = item.description;
completionItem.kind = CompletionItemKind.Property;
return completionItem;
});
Yes, completion items support the usual snippet syntax. Simply use a vscode.SnippetString for insertText instead of a raw string.
A string or snippet that should be inserted in a document when selecting this completion. When falsy the label is used.
completionItem.insertText = new vscode.SnippetString(item.body);
I have an extension that grabs the open file's text and alters it. Once the text is altered, how do I put it back into the file that is displayed in VSCode?
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "myExtension" is now active!');
console.log(process.versions);
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand('extension.myExtension', () => {
// The code you place here will be executed every time your command is executed
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
return;
}
let text = activeEditor.document.getText();
getAsyncApi(text).then((textToInsertIntoDoc) => {
let finaldoc = insertTextIntoDoc(text, textToInsertIntoDoc);
// not what I want - just used to see new text
vscode.window.showInformationMessage(textToInsertIntoDoc);
});
});
context.subscriptions.push(disposable);
}
The API you can use here is TextEditor.edit, whose definition is
edit(callback: (editBuilder: TextEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;
It asks for a callback as the first parameter and in the callback, you can make edits to the document by visiting editBuilder.
I put a sample extension in https://github.com/Microsoft/vscode-extension-samples/tree/master/document-editing-sample which reverses the content in current selection, which is basically a simple use TextEditor.edit.
This is a revision of the main function in Rebornix's extension sample (included with the set of Microsoft extension samples) that handles the selection issues you raised. It reverses the content of the selection(s) (leaving the selections) or if a selection is empty it will reverse the word under the cursor at that selection without leaving anything selected. It often makes sense to leave a selection, but you can add code to remove selection.
let disposable = vscode.commands.registerCommand('extension.reverseWord', function () {
// Get the active text editor
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
editor.edit(editBuilder => {
editor.selections.forEach(sel => {
const range = sel.isEmpty ? document.getWordRangeAtPosition(sel.start) || sel : sel;
let word = document.getText(range);
let reversed = word.split('').reverse().join('');
editBuilder.replace(range, reversed);
})
}) // apply the (accumulated) replacement(s) (if multiple cursors/selections)
}
});
Admittedly, while I could remove a single selection by setting .selection to a new empty selection that doesn't seem to work with .selections[i]. But you can make multiple changes without having selections in the first place.
What you don't want to do is make a selection through code just to alter text through code. Users make selections, you don't (unless the end purpose of the function is to make a selection).
I came to this question looking for a way to apply a textEdit[] array (which is normally returned by a provideDocumentRangeFormattingEdits callback function). If you build changes in the array you can apply them to your document in your own function:
const { activeTextEditor } = vscode.window;
if (activeTextEditor) {
const { document } = activeTextEditor;
if (document) {
/*
build your textEdits similarly to the above with insert, delete, replace
but not within an editBuilder arrow function
const textEdits: vscode.TextEdit[] = [];
textEdits.push(vscode.TextEdit.replace(...));
textEdits.push(vscode.TextEdit.insert(...));
*/
const workEdits = new vscode.WorkspaceEdit();
workEdits.set(document.uri, textEdits); // give the edits
vscode.workspace.applyEdit(workEdits); // apply the edits
}
}
So that's another way to apply edits to a document. Even though I got the editBuilder sample to work correctly without selecting text, I have had problems with selections in other cases. WorkspaceEdit doesn't select the changes.
Here is the code snippet that will solve your issue :
activeEditor.edit((selectedText) => {
selectedText.replace(activeEditor.selection, newText);
})
Due to the issues I commented about in the above answer, I ended up writing a quick function that does multi-cursor friendly insert, and if the selection was empty, then it does not leave the inserted text selected afterwards (i.e. it has the same intuitive behavior as if you had pressed CTRL + V, or typed text on the keyboard, etc.)
Invoking it is simple:
// x is the cursor index, it can be safely ignored if you don't need it.
InsertText(x => 'Hello World');
Implementation:
function InsertText(getText: (i:number) => string, i: number = 0, wasEmpty: boolean = false) {
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) { return; }
let sels = activeEditor.selections;
if (i > 0 && wasEmpty)
{
sels[i - 1] = new vscode.Selection(sels[i - 1].end, sels[i - 1].end);
activeEditor.selections = sels; // required or the selection updates will be ignored! 😱
}
if (i < 0 || i >= sels.length) { return; }
let isEmpty = sels[i].isEmpty;
activeEditor.edit(edit => edit.replace(sels[i], getText(i))).then(x => {
InsertText(getText, i + 1, isEmpty);
});
}
let strContent = "hello world";
const edit = new vscode.WorkspaceEdit();
edit.insert(YOUR_URI, new vscode.Position(0, 0), strContent);
let success = await vscode.workspace.applyEdit(edit);
I have reference on some ContentBlock(that not selected). How can i replace it with some fragment?
I have some fragment
const fragment = DraftPasteProcessor.processHTML(html);
And reference on block
const currentBlock = content.getBlockForKey(myKey);
How can i replace this block with a fragment. This block is not selected. I wan't to replace it and set cursor in the end of fragment.
I tried to use Modifier.replaceWithFragment, but this method works only with SelectedState.
The easiest way to do this is to move the selection, then delete the fragment:
const fragment = DraftPasteProcessor.processHTML(html);
const myKey = 'some id'; // same as your code
const currentBlock = content.getBlockForKey(myKey);
const anchorOffset = currentBlock.getLength();
const selection = SelectionState.createEmpty(myKey).set('anchorOffset', anchorOffset);
Modifier.replaceWithFragment(content, selection, fragment);
This is a pretty common type of action to take with DraftJS. You'll notice on the DraftJS docs that it tells you to use the ImmutableJS functions (set in this case) to modify your selection as needed.
I want to display to the user an input with some options, but the user can answer a new one.
Using showQuickPick I can show some options, but if the user answer a different option the return is undefined.
It's possible to do what I want?
I have already think about create a New option and then show an InputBox to the user, but I don't want that the user need to answer two questions.
I have used a solution where I inject the user input in the list of items.
It works pretty well:
const choices = ['a', 'b']
async function getUserSelectdValue() {
return new Promise((resolve) => {
const quickPick = window.createQuickPick();
quickPick.items = choices.map(choice => ({ label: choice }));
quickPick.title = 'Choose your favorite value:'
quickPick.onDidChangeValue(() => {
// INJECT user values into proposed values
if (!choices.includes(quickPick.value)) quickPick.items = [quickPick.value, ...choices].map(label => ({ label }))
})
quickPick.onDidAccept(() => {
const selection = quickPick.activeItems[0]
resolve(selection.label)
quickPick.hide()
})
quickPick.show();
})
}
Please let me know if you need further explanations
It sounds like you might be interested in the approach taken in this issue?
https://github.com/microsoft/vscode/issues/89601