How to map cursor position after formatting code

I want to keep the cursor consistent with the effect of vscode after the code is formatted。

he following is the cursor effect of vscode:

This is where I map the new position via changes.mapPos, but it’s not that simple

Expected effect:
Make the cursor position finally positioned in front of the third display

        // transaction is a formatted change
        const tr = state.update(...transaction);
        const anchor = tr.changes.mapPos(head);
          selection: { anchor },
          effects: [EditorView.scrollIntoView(anchor, { y: 'center' })],

The rule is that if the current line is not deleted, the cursor remains on the current line, and if the current line is deleted, the cursor is displayed on the nearest line with content

Has anyone dealt with similar needs, please ask

You didn’t mention how you are doing the code formatting. The editor will automatically map the selection through any document changes. But if you are doing something like replacing the entire document with a reformatted version, it obviously doesn’t know which parts of the old doc correspond to which parts of the new doc, so it can’t keep the selection in place.

hi, marijn. This is how I format my code, I don’t replace the whole document,I use the Diff package to compare the differences。
usage1:(prettier API by Node server)

// code represents the old document data, result.code represents the new document data。
const items = diffChars(code, result.code);
    console.log('items: ', items);
    if (items.length > 1) {
      const transaction: Array<TransactionSpec> = [];
      let allCount = 0;
      items.forEach((item) => {
        if (item.removed) {
          const end = allCount + (item.count ?? 0);
          const trans = insertCompletionText(state, '', allCount, end);
          allCount = end;
        } else if (item.added) {
          const trans = insertCompletionText(
        } else {
          allCount = allCount + (item.count ?? 0);


      result = await lspClient.textDocumentFormatting({
          textDocument: { uri: `file://${rootUri}/${openedPath}` },
          options: {
            tabSize: state.tabSize,

if (result && result.result?.length) {
  const items = result.result;
  // $ 先存储更改数据,再一次性插入
  const transaction: Array<TransactionSpec> = [];
  items.forEach((item) => {
    const from = posToOffset(state.doc, item.range.start);
    // to为undefined表示在原有的基础上新增加了一行
    const to = posToOffset(state.doc, item.range.end) ?? state.doc.length;
    if (from !== undefined && to !== undefined) {
      const trans = insertCompletionText(state, item.newText, from, to);

You are right, codemirror does have a built-in selection map, but it does not meet my needs(Not quite consistent with vscode)

The effect of not doing any processing on the cursor is as follows:
This cursor should be focused in front of the display in the first d42-visible, not the second(Maybe I’m using it wrong?)

That kind of cursor jump is not something the built-in selection mapping would do (assuming the changes are just the minimal changes needed to perform that update).

Thank you so much, marijn. The cursor jumps are caused by using the insertCompletionText method