I would like to know if the extension API support changing cursor position. I tried to find in the API reference but I couldn't find related function.
The reason behind such function is that
I normally spend much time to move the cursor while writing source code. (by pressing arrow key, home key, pgUp, etc)
So, if I can write an extension that could change cursor location effectively, that could save time a lot. :-)
Thank you in advance!
there is a command named cursorMove which can be run from an extension. In this example the extension uses showInputBox to prompt for the number of lines to move. Then vscode.commands.executeCommand is run to execute the cursorMove command to move down by the entered number of lines:
import * as vscode from 'vscode' ;
let lastNumLines = '1';
// -------------------------- registerCommand_moveCursor --------------------------
export function registerCommand_moveCursor(context: vscode.ExtensionContext)
{
const fullExtensionName = `sample.moveCursorDown`;
let disposable = vscode.commands.registerCommand(fullExtensionName, async ( ) =>
{
const val = await vscode.window.showInputBox(
{ prompt: 'number lines to move', value: lastNumLines });
if (val)
{
lastNumLines = val ;
const numLines = Number(val) ;
await vscode.commands.executeCommand("cursorMove",
{
to: "down", by:'wrappedLine', value:numLines
});
}
});
context.subscriptions.push(disposable);
}
export function activate(context: ExtensionContext)
{
registerCommand_moveCursor(context) ;
}
package.json
"contributes": {
"commands": [
{
"command": "sample.moveCursorDown",
"title": "move cursor down",
"category": "sample"
}
],
}
Related
I am testing a frontend and I want to make my test more efficient
I have the following custom command:
cy.intercept('**/api/classification/dd86ac0a-ca23-413b-986c-535b6aad659c/items/**',
{ fixture: 'ItemsInEditor.json' }).as('ItemsInEditorStub')
This works correctly and is intercepts 25 times :). But the Id in the stub file has to be the same as in the requested Endpoint. Otherwise the frontEnd wilt not process it.
At this point I do not want to make 25 stubfiles in the fixture map.
In the printscreen you can see the different calls I need to intercept. The last ID I would like to save as variable and use it in the stub file
The Stub is like this:
{
"item": {
"version": 3,
"title": "Cars",
"rows": [],
"id": "dynamicIdBasedOnEndPoint" <- *Can we make it dynamic based on the ID in the endpoint*
},
"itemState": "Submitted"
}
UPDATE:
What I have for now is just the basic I guess:
cy.intercept('**/api/classification/*/items/**', {
body:
{
item: {
version: 3,
title: 'Cars',
rows: [],
id: '55eb5a28-24d8-4705-b465-8e1454f73ac8' //Still need this value to be dynamic and always the same as the intercepted '**'(wildcard)
},
itemState: "Submitted"
}
})
.as('ItemsInEditorStub')
cy.fixture('ItemsInEditor.json').then(ModFixture => {
cy.intercept('GET', '**/api/classification/**/items/id/**', (req) => {
const id = req.url.split('/').pop(); // last part of url path
ModFixture.item.id = id; // add the id dynamically
req.reply(ModFixture); // send altered fixture
})
}).as('ItemsInEditorStub')
Thanks to #Fody
You can make a dynamic fixture using javascript.
Ref Providing a stub response with req.reply()
cy.fixture('ItemsInEditor.json').then(fixture => {
cy.intercept('**/api/classification/dd86ac0a-ca23-413b-986c-535b6aad659c/items/**',
(req) => {
const id = req.url.split('/').pop(); // last part of url path
fixture.item.id = id; // add the id dynamically
req.reply(fixture); // send altered fixture
}
).as('ItemsInEditorStub')
})
I am creating a little custom plugin for the CKEditor5 for the #neoscms.
Neos is using the #ckeditor5 but with a custom view.
The plugin is more or less a placeholder plugin. The user can configure a data-source with a key value store for items (identifier and labels). The dropdown in the CKEditor is filled with the items and when the user selects an item from the dropdown, it creates a placeholder element that should end in a span element with some data-attributes.
The main idea was to have an empty element and just data-attributes to identify the element and being able to assign live data. But it turns out that the live data thing is tricky. When I manipulate the span with an extra JS snippet on the Website, the CKEditor cannot handle this.
Is it possible to manipulate a view element in the DOM and still have a working Editor?
The Plugin works fine if I just add inner Text in the downCasting and don't replace something. But the live data would be nice.
Neos Backend with a element
Maybe that code gives an idea of the package.
It is not ready yet as this is more or less the main feature ;)
import {Plugin, toWidget, viewToModelPositionOutsideModelElement, Widget,} from "ckeditor5-exports";
import PlaceholderCommand from "./placeHolderCommand";
export default class PlaceholderEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
this._defineSchema();
this._defineConverters();
this.editor.commands.add(
"placeholder",
new PlaceholderCommand(this.editor)
);
this.editor.editing.mapper.on(
"viewToModelPosition",
viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) =>
viewElement.hasClass("internezzo-placeholder")
)
);
this.editor.config.define("placeholderProps", {
types: ["name", "node", "nodePath"],
});
this.editor.config.define("placeholderBrackets", {
open: "[",
close: "]",
});
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register("placeholder", {
allowWhere: "$text",
isInline: true,
isObject: true,
allowAttributes: [
"name",
"node",
"nodePath",
"data-placeholder-identifier",
"data-node-identifier",
"data-node-path",
],
});
}
_defineConverters() {
const conversion = this.editor.conversion;
const config = this.editor.config;
conversion.for("upcast").elementToElement({
view: {
name: "span",
classes: ["foobar-placeholder"],
},
model: (viewElement, writer) => {
const name = viewElement.getAttribute('data-placeholder-identifier');
const node = viewElement.getAttribute('data-node-identifier');
const nodePath = viewElement.getAttribute('data-node-path');
const modelWriter = writer.writer || writer;
return modelWriter.createElement("placeholder", {name, node, nodePath, editable: false});
},
});
conversion.for("editingDowncast").elementToElement({
model: "placeholder",
view: (modelItem, writer) => {
const viewWriter = writer.writer || writer;
const widgetElement = createPlaceholderView(modelItem, viewWriter);
return toWidget(widgetElement, viewWriter);
},
});
conversion.for("dataDowncast").elementToElement({
model: "placeholder",
view: (modelItem, writer) => {
const viewWriter = writer.writer || writer;
return createPlaceholderView(modelItem, viewWriter);
},
});
// Helper method for downcast converters.
function createPlaceholderView(modelItem, viewWriter) {
const name = modelItem.getAttribute("name");
const node = modelItem.getAttribute("node");
const nodePath = modelItem.getAttribute("nodePath");
const placeholderView = viewWriter.createContainerElement("span", {
class: "foobar-placeholder",
"data-placeholder-identifier": name,
"data-node-identifier": node,
"data-node-path": nodePath,
});
// Would be nice to remove that and have just empty spans that get dynamic data
let innerText = config.get("placeholderBrackets.open") + name;
innerText += config.get("placeholderBrackets.close");
viewWriter.insert(
viewWriter.createPositionAt(placeholderView, 0),
viewWriter.createText(innerText)
);
return placeholderView;
}
}
}
So, the extra JS snippet that is used by the website is searching for spans with the class foobar-placeholder and writes a value with live data into the span. That works in the frontend, of course, but the backend of the CMS that uses CKEditor has issues with the changing data.
I could not find a solution with docs of CKEditor, and maybe I misuse the API somehow, but I now found a working solution for me.
My website snippet is now communicating with the Plugin via Broadcast messages. And then I search for placeholder elements and check if I need to change an attribute.
const broadcastChannel = new BroadcastChannel('placeholder:changeData');
broadcastChannel.postMessage({identifier: name, value});
And in the plugin
// Receive new values for placeholder via broadcast
const broadcastChannel = new BroadcastChannel('placeholder:changeData');
broadcastChannel.onmessage = (message) => {
const identifier = get('data.identifier', message);
const newValue = get('data.value', message);
this.editor.model.change( writer => {
if (identifier) {
this._replaceAttribute(writer, identifier, newValue);
}
});
};
Only downside now is that I need to reload the page, but already read that this is maybe cause by my element down casting and I change attributes.
I'm in a function that receives a string as input:
(text) => {
}
I have access to the editor via Vue props (props.editor). I would like to replace the current node's content with this text. I cannot seem to find out how to do this. I'm using tiptap2, which is a wrapper around ProseMirror and has access to all of ProseMirror's api.
I'd rather not try to replace the whole node unless necessary, which I also tried, doing below – but cannot get that to work either:
(text) => {
props.editor
.chain()
.focus()
.command(({ tr }) => {
const node = props.editor.state.schema.nodes.paragraph.create(
{ content: text}
);
tr.replaceSelectionWith(node);
return true;
})
.run();
}
Much thanks
This solution works for me in Tiptap version 2.
A precondition for this to work is, that the text to be replaced is marked (highlighted).
const selection = editor.view.state.selection;
editor.chain().focus().insertContentAt({
from: selection.from,
to: selection.to
}, "replacement text").run();
I'm late to the party but this is the top result I came across when trying to find a solution for myself.
My code is in the context of a React NodeView, so I'm given a getPos() prop that gives the position of the React node in the Prosemirror document (I believe this number more-or-less means how many characters precede the React NodeView node). With that I was able to use this command chain to replace the content:
import { Node as ProsemirrorNode } from "prosemirror-model";
import { JSONContent, NodeViewProps } from "#tiptap/react";
const NodeViewComponent = (props: NodeViewProps) =>
// ...
/**
* Replace the current node with one containing newContent.
*/
const setContent = (newContent: JSONContent[]) => {
const thisPos = props.getPos();
props.editor
.chain()
.setNodeSelection(thisPos)
.command(({ tr }) => {
const newNode = ProsemirrorNode.fromJSON(props.editor.schema, {
type: props.node.type.name,
attrs: { ...props.attrs },
content: newContent,
});
tr.replaceSelectionWith(newNode);
return true;
})
.run();
};
// ...
};
Basically you want to:
Set the current selection to the node you want to replace the content of
Create and update a new node that is a copy of the current node
Replace your selection with the new node.
I am creating a new vscode extension, and I need to extend the standard usage of the jsonValidation system already present in vscode.
Note : I am talking about the system defined in package.json :
"contributes" : {
"languages": [
{
"id" : "yml",
"filenamePatterns": ["module.service"]
},
{
"id" : "json",
"filenamePatterns": ["module.*"]
}
],
"jsonValidation": [
{
"fileMatch": "module.test",
"url": "./resources/test.schema"
}
]
}
Now, I need to create a dynamic mapping, where the json fields filematch/url are defined from some internal rules (like version and other internal stuff). The standard usage is static : one fileMatch -> one schema.
I want for example to read the version from the json file to validate, and set the schema after that :
{
"version" : "1.1"
}
validation schema must be test-schema.1.1 instead of test-schema.1.0
note : The question is only about the modification of the configuration provided by package.json from the extensions.ts
Thanks for the support
** EDIT since the previous solution was not working in all cases
There is one solution to modify the package.json at the activating of the function.
export function activate(context: vscode.ExtensionContext) {
const myPlugin = vscode.extensions.getExtension("your.plugin.id");
if (!myPlugin)
{
throw new Error("Composer plugin is not found...")
}
// Get the current workspace path to found the schema later.
const folderPath = vscode.workspace.workspaceFolders;
if (!folderPath)
{
return;
}
const baseUri : vscode.Uri = folderPath[0].uri;
let packageJSON = myPlugin.packageJSON;
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation)
{
let jsonValidation = packageJSON.contributes.jsonValidation;
const schemaUri : vscode.Uri = vscode.Uri.joinPath(baseUri, "/schema/value-0.3.0.json-schema");
const schema = new JsonSchemaMatch("value.ospp", schemaUri)
jsonValidation.push(schema);
}
}
And the json schema class
class JsonSchemaMatch
{
fileMatch: string;
url : string;
constructor(fileMatch : string, url: vscode.Uri)
{
this.fileMatch = fileMatch;
this.url = url.path;
}
}
Another important information is the loading of the element of contributes is not reread after modification, for example
class Language
{
id: string;
filenamePatterns : string[];
constructor(id : string, filenamePatterns: string[])
{
this.id = id;
this.filenamePatterns = filenamePatterns;
}
}
if (packageJSON && packageJSON.contributes && packageJSON.contributes.languages)
{
let languages : Language[] = packageJSON.contributes.languages;
for (let language of languages) {
if (language.id == "json") {
language.filenamePatterns.push("test.my-json-type")
}
}
}
This change has no effect, since the loading of file association is already done (I have not dig for the reason, but I think this is the case)
In this case, creating a settings.json in the workspace directory can do the job:
settings.json
{
"files.associations": {
"target.snmp": "json",
"stack.cfg": "json"
}
}
Be aware that the settings.json can be created by the user with legitimate reason, so don't override it, just fill it.
It used to be possible to access http://web.whatsapp.com/ with the Store object in JavaScript. A few hours ago, this stopped working. How does it update chat data now? It must save the data somewhere.
I'm using this to get the Store again:
setTimeout(function() {
// Returns promise that resolves to all installed modules
function getAllModules() {
return new Promise((resolve) => {
const id = _.uniqueId("fakeModule_");
window["webpackJsonp"](
[],
{
[id]: function(module, exports, __webpack_require__) {
resolve(__webpack_require__.c);
}
},
[id]
);
});
}
var modules = getAllModules()._value;
// Automatically locate modules
for (var key in modules) {
if (modules[key].exports) {
if (modules[key].exports.default) {
if (modules[key].exports.default.Wap) {
store_id = modules[key].id.replace(/"/g, '"');
}
}
}
}
}, 5000);
function _requireById(id) {
return webpackJsonp([], null, [id]);
}
// Module IDs
var store_id = 0;
var Store = {};
function init() {
Store = _requireById(store_id).default;
console.log("Store is ready" + Store);
}
setTimeout(function() {
init();
}, 7000);
Just copy&paste on the console and wait for the message "Store is ready".
Enjoy!
To explain Pablo's answer in detail, initially we load all the Webpack modules using code based on this How do I require() from the console using webpack?.
Essentially, the getAllModules() returns a promise with all the installed modules in Webpack. Each module can be required by ID using the _requireById(id) which uses the webpackJsonp(...) function that is exposed by Webpack.
Once the modules are loaded, we need to identify which id corresponds to the Store. We search for a module containing exports.default.Wap and assign it's id as the Store ID.
You can find more details on my github wiki here
A faster method:
I grab the source of the "app" and find the store object then
I save it in ZStore global variable. :D
!function(){for(var t of document.getElementsByTagName("script"))t.src.indexOf("/app.")>0&&fetch(t.src,{method:"get"}).then(function(t){return t.text().then(function(t){var e=t.indexOf('var a={};t["default"]')-89;window.ZStore=window.webpackJsonp([],null,JSON.stringify(t.substr(e,10))).default})})}();
window.ZStore will contain the object.
Non minified version:
(function() {
function getStore(url) {
fetch(url, {
"method": 'get'
}).then(function(response) {
return response.text().then(function(data) {
var offset = data.indexOf('var a={};t["default"]') - 89;
window.ZStore = window.webpackJsonp([], null, JSON.stringify(data.substr(offset, 10))).default
});
});
}
for (var e of document.getElementsByTagName("script")) {
if (e.src.indexOf("/app.") > 0) getStore(e.src);
}
})();