davidpcahill / auto-select-pasted-text

Enhance VSCode: Auto-select pasted text for faster editing & precise indentation. Ideal for web code samples & ChatGPT. Save time in coding sessions.
https://marketplace.visualstudio.com/items?itemName=davidcahill.auto-select-pasted-text&ssr=false#overview
MIT License
3 stars 0 forks source link

Auto Select Pasted Text needs refactoring of codebase to utilize a state machine #2

Open davidpcahill opened 8 months ago

davidpcahill commented 8 months ago

I'm still having problems with many states. We keep tacking on tracking variables but should have used a state machine to begin with. This will need to be a major rewrite and I will need development resources to help meet the base requirements:

davidpcahill commented 8 months ago

Some example pseudo-code. Likely won't need all these states but adding for examples:

# Since the actual Visual Studio Code API is not available here, we will just
# draft the refactored code. This code will not be run in this environment,
# but it can be used as a starting point for the user's local development.

# Here's a refactored version of the paste command handling part of the extension:

def refactor_paste_handling():
    """
    This function represents the refactored logic for handling paste operations.
    It's a pseudo-code and should be adapted to the actual TypeScript implementation.
    """

    # Pseudo-code for the refactored paste handling

    # State object to keep track of the extension's state
    state = {
        'autoSelected': False,
        'lastPastePosition': None,  # This will store the position of the last paste
        'manualDeselection': False,
        'manualSelection': False,
        'recentPasteOperation': False,
        'pasteTimestamp': 0,
    }

    # Refactored paste command
    def on_paste():
        # Check for manual deselection or manual selection before pasting
        if state['manualDeselection'] or state['manualSelection']:
            state['autoSelected'] = False

        # Read content from the clipboard (to be replaced with actual API call)
        clipboard_content = "clipboard content"  # Placeholder for actual clipboard content

        if clipboard_content:
            # Logic to handle the pasting of content
            # ... (omitted for brevity)

            # After pasting, set the autoSelected flag and update the lastPastePosition
            state['autoSelected'] = True
            state['lastPastePosition'] = 'endPositionOfPastedContent'  # Placeholder for actual position
            state['pasteTimestamp'] = 'currentTimestamp'  # Placeholder for actual timestamp
            state['recentPasteOperation'] = True

            # Reveal the pasted content in the editor
            # ... (omitted for brevity)

        # Reset the manual deselection and selection flags
        state['manualDeselection'] = False
        state['manualSelection'] = False

    # Other parts of the extension will be refactored similarly, focusing on using the state object
    # to keep track of the necessary information and to make decisions based on the current state.

    return state, on_paste

# The returned state object and the on_paste function would be integrated into the actual extension code.
// State object to keep track of the extension's state
interface ExtensionState {
  autoSelected: boolean;
  lastPastePosition: vscode.Position | null;
  manualDeselection: boolean;
  manualSelection: boolean;
  recentPasteOperation: boolean;
  pasteTimestamp: number;
}

const state: ExtensionState = {
  autoSelected: false,
  lastPastePosition: null,
  manualDeselection: false,
  manualSelection: false,
  recentPasteOperation: false,
  pasteTimestamp: 0,
};

// Refactored paste command
function onPaste() {
  // Check for manual deselection or manual selection before pasting
  if (state.manualDeselection || state.manualSelection) {
    state.autoSelected = false;
  }

  // Read content from the clipboard (this would be an actual API call in TypeScript)
  const clipboardContent: string = vscode.env.clipboard.readText();

  if (clipboardContent) {
    // Logic to handle the pasting of content
    // ...

    // After pasting, set the autoSelected flag and update the lastPastePosition
    state.autoSelected = true;
    // Here you would set lastPastePosition to the actual end position of the pasted content
    state.pasteTimestamp = Date.now();
    state.recentPasteOperation = true;

    // Reveal the pasted content in the editor
    // ...

    // Additional logic to handle appending pastes if needed
    // ...
  }

  // Reset the manual deselection and selection flags
  state.manualDeselection = false;
  state.manualSelection = false;
}

// The rest of the extension would use the state object to track and make decisions

Additional refactoring example:

// This function is triggered whenever the text editor's selection changes
function onSelectionChange(event: vscode.TextEditorSelectionChangeEvent) {
  // If a recent paste operation occurred and not enough time has passed, we won't reset the flags
  if (state.recentPasteOperation && Date.now() - state.pasteTimestamp < 1000) {
    return;
  }

  // Determine the kind of selection change (manual or auto) and update the state accordingly
  if (event.kind === vscode.TextEditorSelectionChangeKind.Mouse) {
    state.manualSelection = true;
    state.autoSelected = false;
  } else if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard) {
    state.manualSelection = true;
    state.autoSelected = false;
  } else {
    // If the selection change is neither by mouse nor keyboard, we reset the autoSelected flag
    state.autoSelected = false;
  }

  // Reset recent paste operation flag as enough time has passed
  state.recentPasteOperation = false;
}

// Register the event listener for selection changes
vscode.window.onDidChangeTextEditorSelection(onSelectionChange);

// Rest of the extension code...
// This command is triggered whenever a key is pressed
vscode.commands.registerCommand('type', async (args) => {
  const editor = vscode.window.activeTextEditor;
  if (!editor) {
    return;
  }

  // Retrieve settings
  const enableTypeDetection = vscode.workspace
    .getConfiguration('autoSelectPastedText')
    .get('enableTypeDetection');
  const enableManualSelection = vscode.workspace
    .getConfiguration('autoSelectPastedText')
    .get('enableManualSelection');

  const currentSelection = editor.selection;

  // If a manual selection has been made, we don't want to mess with it unless we're allowing manual selection management
  if (state.manualSelection && !enableManualSelection) {
    // Reset manual selection state
    state.manualSelection = false;
  } else if (state.autoSelected && enableTypeDetection) {
    // If the selection was auto-selected and type detection is enabled, we reset the selection to a cursor position
    const currentPosition = currentSelection.active;
    editor.selection = new vscode.Selection(currentPosition, currentPosition);

    // Reset the auto selection state
    state.autoSelected = false;
  }

  // Execute the default type command after adjusting the selection
  await vscode.commands.executeCommand('default:type', args);

  // Reset the manual selection flags after typing
  state.manualSelection = false;
  state.recentPasteOperation = false;
});
// This command is triggered on paste action
vscode.commands.registerCommand('editor.action.clipboardPasteAction', async () => {
  const enableAutoSelection = vscode.workspace
    .getConfiguration('autoSelectPastedText')
    .get('enableAutoSelection');

  // Log the status before pasting
  log(`Before pasting - autoSelected: ${state.autoSelected}, manualSelection: ${state.manualSelection}`);

  const clipboardContent = await vscode.env.clipboard.readText();
  if (!clipboardContent) {
    outputChannel.appendLine('Clipboard is empty.');
    return;
  }

  const editor = vscode.window.activeTextEditor;
  if (!editor) {
    return;
  }

  let targetSelection: vscode.Selection;
  if (state.autoSelected || state.manualSelection) {
    // Append after the current selection or the cursor position
    targetSelection = new vscode.Selection(editor.selection.end, editor.selection.end);
  } else {
    // Replace the current selection
    targetSelection = editor.selection;
  }

  // Perform the paste operation
  await editor.edit((editBuilder) => {
    editBuilder.replace(targetSelection, clipboardContent);
  });

  // Calculate the end position for the selection post-paste
  const linesPasted = clipboardContent.split('\n');
  const lastLineLength = linesPasted[linesPasted.length - 1].length;
  const endLine = targetSelection.start.line + linesPasted.length - 1;
  const endCharacter = linesPasted.length === 1
    ? targetSelection.start.character + lastLineLength
    : lastLineLength;
  const endPosition = new vscode.Position(endLine, endCharacter);

  if (enableAutoSelection) {
    // Adjust the selection to cover the pasted content
    editor.selection = new vscode.Selection(targetSelection.start, endPosition);
    state.autoSelected = true;
    state.recentPasteOperation = true;
    state.pasteTimestamp = Date.now();
  }

  // Reveal the pasted content in the editor
  editor.revealRange(new vscode.Range(targetSelection.start, endPosition), vscode.TextEditorRevealType.Default);

  // Reset the manual selection flags after pasting
  state.manualSelection = false;

  // Log the status after pasting
  log(`After pasting - autoSelected: ${state.autoSelected}, manualSelection: ${state.manualSelection}`);
});
// Event listener for selection changes in the editor
vscode.window.onDidChangeTextEditorSelection((event) => {
  // Check if the selection change is within a short time window after a paste operation
  if (state.recentPasteOperation && Date.now() - state.pasteTimestamp < 1000) {
    log('Selection change likely due to recent paste, not resetting autoSelected flag.');
    // We don't reset state.recentPasteOperation here as it might be an auto-selection
  } else {
    // Enough time has passed since the last paste operation, or there was no recent paste
    state.autoSelected = false;
    state.recentPasteOperation = false;
  }

  // Detect if the selection was changed manually via mouse or keyboard
  if (event.kind === vscode.TextEditorSelectionChangeKind.Mouse) {
    state.manualSelection = true;
    state.autoSelected = false;
  } else if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard) {
    state.manualSelection = true;
    state.autoSelected = false;
  } else {
    // If the selection change is not due to mouse or keyboard, it's not a manual selection
    state.manualSelection = false;
  }

  // Log the selection change for debugging purposes
  if (event.textEditor.document.uri.scheme === 'file') {
    const selection = event.selections[0];
    log(`Selection changed: Start(${selection.start.line}, ${selection.start.character}), End(${selection.end.line}, ${selection.end.character})`);
  }

  // Log the status after selection changes
  log(`After selection change - autoSelected: ${state.autoSelected}, manualSelection: ${state.manualSelection}`);
});
// Register the type command to handle the deselection behavior
vscode.commands.registerCommand('type', async (args) => {
  const editor = vscode.window.activeTextEditor;
  if (editor) {
    const currentSelection = editor.selection;

    // If there's a non-empty selection and it's an auto-selection, we reset it to a cursor position
    if (!currentSelection.isEmpty && state.autoSelected) {
      const currentPosition = currentSelection.active;
      editor.selection = new vscode.Selection(currentPosition, currentPosition);
      state.autoSelected = false; // Reset the autoSelected flag after the type operation
    }

    // Execute the default type command after adjusting the selection
    await vscode.commands.executeCommand('default:type', args);
  }

  // Log the status after typing
  log(`After typing - autoSelected: ${state.autoSelected}, manualSelection: ${state.manualSelection}`);
});
// Register the paste command
let pasteCommandDisposable = vscode.commands.registerCommand(
  'editor.action.clipboardPasteAction',
  async () => {
    const enableAutoSelection = vscode.workspace
      .getConfiguration('autoSelectPastedText')
      .get('enableAutoSelection');

    // Read content from clipboard
    const clipboardContent = await vscode.env.clipboard.readText();

    if (clipboardContent) {
      const editor = vscode.window.activeTextEditor;
      if (editor) {
        let targetSelection: vscode.Selection;

        // Determine the target for the paste: either append after the current selection or replace it
        if (state.autoSelected) {
          const currentPosition = editor.selection.end;
          targetSelection = new vscode.Selection(currentPosition, currentPosition);
        } else {
          targetSelection = editor.selection;
        }

        // Split content by lines to calculate the selection end position later
        const linesPasted = clipboardContent.split('\n');
        const lastLineLength = linesPasted[linesPasted.length - 1].length;

        editor.edit((editBuilder) => {
          // Replace the determined target selection with the clipboard content
          editBuilder.replace(targetSelection, clipboardContent);
        }).then((success) => {
          if (success) {
            // Determine the end position for the selection post-paste
            const endLine = targetSelection.start.line + linesPasted.length - 1;
            const endCharacter = linesPasted.length === 1 ? targetSelection.start.character + lastLineLength : lastLineLength;
            const endPosition = new vscode.Position(endLine, endCharacter);

            if (enableAutoSelection) {
              // Adjust the selection to cover the pasted content
              editor.selection = new vscode.Selection(targetSelection.start, endPosition);
              state.autoSelected = true;
              state.pasteTimestamp = Date.now();
              state.recentPasteOperation = true;
            }

            // Reveal the pasted content in the editor
            editor.revealRange(new vscode.Range(targetSelection.start, endPosition), vscode.TextEditorRevealType.Default);
          }
        });
      }
    }

    // Reset the manual selection flags
    state.manualSelection = false;
    // Log the status after pasting
    log(`After pasting - autoSelected: ${state.autoSelected}, manualSelection: ${state.manualSelection}`);
  }
);

// Add the paste command disposable to the extension context's subscriptions
context.subscriptions.push(pasteCommandDisposable);
davidpcahill commented 8 months ago

As always, if anyone wants to put in a merge request, I would be very happy for any help. <3