Full Code of sleistner/vscode-fileutils for AI

master 54fc9407e18f cached
73 files
122.3 KB
29.7k tokens
149 symbols
1 requests
Download .txt
Repository: sleistner/vscode-fileutils
Branch: master
Commit: 54fc9407e18f
Files: 73
Total size: 122.3 KB

Directory structure:
gitextract_xq5fqy4z/

├── .biomeignore
├── .claude/
│   └── settings.local.json
├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .editorconfig
├── .eslintignore
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── main.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .node-version
├── .releaserc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── biome.json
├── main.ts
├── package.json
├── renovate.json
├── scripts/
│   └── dev-env
├── src/
│   ├── FileItem.ts
│   ├── command/
│   │   ├── BaseCommand.ts
│   │   ├── Command.ts
│   │   ├── CopyFileNameCommand.ts
│   │   ├── DuplicateFileCommand.ts
│   │   ├── MoveFileCommand.ts
│   │   ├── NewFileCommand.ts
│   │   ├── NewFolderCommand.ts
│   │   ├── RemoveFileCommand.ts
│   │   ├── RenameFileCommand.ts
│   │   └── index.ts
│   ├── controller/
│   │   ├── BaseFileController.ts
│   │   ├── CopyFileNameController.ts
│   │   ├── DuplicateFileController.ts
│   │   ├── FileController.ts
│   │   ├── MoveFileController.ts
│   │   ├── NewFileController.ts
│   │   ├── RemoveFileController.ts
│   │   ├── RenameFileController.ts
│   │   ├── TypeAheadController.ts
│   │   └── index.ts
│   ├── extension.ts
│   └── lib/
│       ├── Cache.ts
│       ├── TreeWalker.ts
│       └── config.ts
├── test/
│   ├── command/
│   │   ├── CopyFileNameCommand.test.ts
│   │   ├── DuplicateFileCommand.test.ts
│   │   ├── MoveFileCommand.test.ts
│   │   ├── NewFileCommand.test.ts
│   │   ├── RemoveFileCommand.test.ts
│   │   └── RenameFileCommand.test.ts
│   ├── fixtures/
│   │   ├── file-1.rb
│   │   └── file-2.rb
│   ├── helper/
│   │   ├── callbacks.ts
│   │   ├── environment.ts
│   │   ├── functions.ts
│   │   ├── index.ts
│   │   ├── steps/
│   │   │   ├── describe.ts
│   │   │   ├── index.ts
│   │   │   ├── it.ts
│   │   │   └── types.ts
│   │   └── stubs.ts
│   ├── index.ts
│   └── runTest.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .biomeignore
================================================
out/
node_modules/
**/*.d.ts
.vscode-test/
*.vsix


================================================
FILE: .claude/settings.local.json
================================================
{
  "permissions": {
    "allow": [
      "Bash(npm run pretest:*)",
      "Bash(grep:*)",
      "Bash(npm test)",
      "Bash(VSCODE_TEST_ELECTRON_PATH= npm test)",
      "Bash(mkdir:*)"
    ],
    "deny": []
  }
}

================================================
FILE: .devcontainer/Dockerfile
================================================
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------

FROM node:19

# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive

# Configure apt and install packages
RUN apt-get update \
    && apt-get -y install --no-install-recommends apt-utils 2>&1 \
    #
    # Verify git and needed tools are installed
    && apt-get install -y git procps \    
    #
    # Remove outdated npm from /opt and install via package 
    # so it can be easily updated via apt-get upgrade npm
    && rm -rf /opt/npm-* \
    && rm -f /usr/local/bin/npm \
    && rm -f /usr/local/bin/npmpkg \
    && apt-get install -y curl apt-transport-https lsb-release \
    && curl -sS https://dl.npmpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
    && echo "deb https://dl.npmpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/npm.list \
    && apt-get update \
    && apt-get -y install --no-install-recommends npm \
    #
    # Install tslint and typescript globally
    && npm install -g tslint typescript \
    #
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

# Switch back to dialog for any ad-hoc use of apt-get
ENV DEBIAN_FRONTEND=dialog


================================================
FILE: .devcontainer/devcontainer.json
================================================
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
{
	"name": "Node.js 8 & TypeScript",
	"dockerFile": "Dockerfile",
	"extensions": [
		"ms-vscode.vscode-typescript-tslint-plugin",
		"sleistner.vscode-fileutils"
	]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
max_line_length = 120
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[{*.yml, *.yaml, *.sh, package.json}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .eslintignore
================================================
"**/*.js"


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing

Contributing is easy:

* You can report bugs and request features using the [issues page][issues].

[issues]: https://github.com/sleistner/vscode-fileutils/issues


We love pull requests from everyone:

* Fork the project
* Download source code and install dependencies
```bash
git clone git@github.com:your-username/vscode-fileutils.git
cd vscode-fileutils
npm install
code .
```
* Make the respective code changes.
* Go to the debugger in VS Code, choose `Launch Extension` and click run. You can test your changes.
* Choose `Launch Tests` to run the tests.
* Push to your fork and [submit a pull request][pr].

[pr]: https://github.com/sleistner/vscode-fileutils/compare/

At this point you're waiting on us. We like to at least comment on pull requests
as soon as possible. We may suggest some changes or improvements or alternatives.


**Important:** Release and changleog update are executed as TravisCI job.

Please write commit messages considering Angular Commit Message Conventions.
* https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits
* https://blog.greenkeeper.io/introduction-to-semantic-release-33f73b117c8


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- VSCode Version:
- OS Version:
- FileUtils Extension Version:

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
# Description

Please include a summary of the change and which issue is fixed. 
Please also include relevant motivation and context. 
List any dependencies that are required for this change.

Fixes # (issue)

# Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules


================================================
FILE: .github/workflows/main.yml
================================================
name: CI/CD

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
  release:
    types:
      - published

concurrency:
  group: ci-fileutils-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  lint-test:
    name: Lint, Test
    strategy:
      matrix:
        os:
          - ubuntu-latest
          - macos-latest
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22.x
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run code analysis
        run: npm run lint
        if: runner.os == 'Linux'

      - name: Run tests on Linux
        run: |
          sudo apt-get --assume-yes install libsecret-1-0 xclip;
          /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
          xvfb-run -a npm run test
        env:
          DISPLAY: ":99.0"
        if: runner.os == 'Linux'

      - name: Run tests on macOS
        run: npm run test
        if: runner.os != 'Linux'

  release:
    name: Release
    runs-on: ubuntu-latest
    needs: [lint-test]
    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22.x
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run semantic-release
        run: npm run semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          VSCE_PAT: ${{ secrets.VSCE_PAT }}
          OVSX_PAT: ${{ secrets.OVSX_PAT }}


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
node_modules

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

out

.vscode-test
.idea

*.vsix

tmp


================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run lint && npm run test


================================================
FILE: .node-version
================================================
22


================================================
FILE: .releaserc
================================================
{
    "branch": "master",
    "plugins": [
        "@semantic-release/commit-analyzer",
        "@semantic-release/release-notes-generator",
        "@semantic-release/npm",
        "@semantic-release/changelog",
        "@semantic-release/git",
        "@semantic-release/github"
    ],
    "prepare": [
        "@semantic-release/npm",
        "@semantic-release/changelog",
        "@semantic-release/git",
        {
            "path": "semantic-release-vsce",
            "packageVsix": "sleistner.vscode-fileutils.vsix"
        }
    ],
    "publish": [
        "semantic-release-vsce",
        {
            "path": "@semantic-release/github",
            "assets": "sleistner.vscode-fileutils.vsix"
        }
    ]
}


================================================
FILE: .vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "dbaeumer.vscode-eslint",
        "editorconfig.editorconfig",
        "esbenp.prettier-vscode",
        "connor4312.esbuild-problem-matchers"
    ]
}


================================================
FILE: .vscode/launch.json
================================================
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Extension",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--folder-uri=${workspaceFolder}/tmp"],
            "outFiles": ["${workspaceFolder}/out/src/**/*.js"],
            "preLaunchTask": "npm: watch"
        },
        {
            "name": "Launch Tests",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": [
                "${workspaceFolder}/test",
                "--extensionDevelopmentPath=${workspaceFolder}",
                "--extensionTestsPath=${workspaceFolder}/out/test"
            ],
            "outFiles": ["${workspaceFolder}/out/test/**/*.js"],
            "preLaunchTask": "npm: tsc:watch"
        }
    ]
}


================================================
FILE: .vscode/settings.json
================================================
// Place your settings in this file to overwrite default and user settings.
{
    "files.exclude": {
        "out": false // set this to true to hide the "out" folder with the compiled JS files
    },
    "search.exclude": {
        "out": true // set this to false to include "out" folder in search results
    },
    "editor.codeActionsOnSave": {
        "source.organizeImports": true
    }
}


================================================
FILE: .vscode/tasks.json
================================================
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "npm",
            "script": "watch",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": "$esbuild-watch",
            "isBackground": true,
            "label": "npm: watch"
        },
        {
            "type": "npm",
            "script": "tsc:watch",
            "isBackground": true,
            "presentation": {
                "reveal": "never"
            },
            "group": {
                "kind": "build",
                "isDefault": false
            },
            "problemMatcher": ["$tsc-watch"],
            "label": "npm: tsc:watch"
        }
    ]
}


================================================
FILE: .vscodeignore
================================================
*
*/**

!images/icon.*
!README.md
!CHANGELOG.md
!LICENSE
!out/extension.js


================================================
FILE: CHANGELOG.md
================================================
## [3.10.3](https://github.com/sleistner/vscode-fileutils/compare/v3.10.2...v3.10.3) (2023-07-22)


### Bug Fixes

* **deps:** update dependency fast-glob to v3.3.1 ([5581fa3](https://github.com/sleistner/vscode-fileutils/commit/5581fa33c582fc43bcc2f951570db60b367b9928))

## [3.10.2](https://github.com/sleistner/vscode-fileutils/compare/v3.10.1...v3.10.2) (2023-06-30)


### Bug Fixes

* **deps:** update dependency fast-glob to v3.3.0 ([ad9f56d](https://github.com/sleistner/vscode-fileutils/commit/ad9f56d5a07abc9d9a0a48cc4a03ea3fcd8b4e35))

## [3.10.1](https://github.com/sleistner/vscode-fileutils/compare/v3.10.0...v3.10.1) (2023-03-21)


### Bug Fixes

* rename commands to comply with VSCode style guides ([72f6843](https://github.com/sleistner/vscode-fileutils/commit/72f6843c02d6cbec5d5588a27ef8097e1130e68e))

# [3.10.0](https://github.com/sleistner/vscode-fileutils/compare/v3.9.3...v3.10.0) (2023-01-30)


### Features

* **Settings:** add option to disable context menus ([f3b1431](https://github.com/sleistner/vscode-fileutils/commit/f3b143134f62337a1082ecf30b4588e7dcfab7ae))

## [3.9.3](https://github.com/sleistner/vscode-fileutils/compare/v3.9.2...v3.9.3) (2023-01-30)


### Bug Fixes

* **NewFileController:** show workspace selector when relative to root ([e3fcf96](https://github.com/sleistner/vscode-fileutils/commit/e3fcf962ec74f5b803da963ba9bdfe4676f83aeb))

## [3.9.2](https://github.com/sleistner/vscode-fileutils/compare/v3.9.1...v3.9.2) (2023-01-28)


### Bug Fixes

* **MoveFileController:** disable brace expansion ([8874f26](https://github.com/sleistner/vscode-fileutils/commit/8874f2664c62035e31ad61656fa18d018f5fc36a))

## [3.9.1](https://github.com/sleistner/vscode-fileutils/compare/v3.9.0...v3.9.1) (2023-01-25)


### Bug Fixes

* **build:** include changelog ([550e190](https://github.com/sleistner/vscode-fileutils/commit/550e19025a52bd842e76021045a2786c4f4a8758))

# [3.9.0](https://github.com/sleistner/vscode-fileutils/compare/v3.8.0...v3.9.0) (2023-01-25)


### Bug Fixes

* **RenameFileController:** always append base path ([cd6f352](https://github.com/sleistner/vscode-fileutils/commit/cd6f35276331f5ff8b73dbd88443f7f0952b6cc8))


### Features

* add inputBox pathTypeIndicator setting ([3390544](https://github.com/sleistner/vscode-fileutils/commit/3390544f5f75ac6dc481827b485a3113447d4b3b))
* **inputBox:** add path representation configuration ([82e7364](https://github.com/sleistner/vscode-fileutils/commit/82e7364b67551388a1cc58ac1ee8122975f835aa))

# [3.8.0](https://github.com/sleistner/vscode-fileutils/compare/v3.7.0...v3.8.0) (2023-01-23)


### Features

* **DuplicateFile:** add typeahead support ([44ac603](https://github.com/sleistner/vscode-fileutils/commit/44ac603dd241eb61e3732172ffc6cfe80555e0c0))
* **MoveFile:** add typeahead support ([0e3e0ca](https://github.com/sleistner/vscode-fileutils/commit/0e3e0ca1f5886926758987b594461d57980f750c))
* **NewFile:** add typeahead setting ([764f614](https://github.com/sleistner/vscode-fileutils/commit/764f614e8e7a8b8d350bd3ad68f785c695bf5e01))
* **NewFolder:** add dedicated typeahead setting ([6d4359a](https://github.com/sleistner/vscode-fileutils/commit/6d4359a5f7bb4689b22d2ba7b3762c9b602839fb))

# [3.7.0](https://github.com/sleistner/vscode-fileutils/compare/v3.6.0...v3.7.0) (2023-01-23)


### Features

* publish to open vsx repository ([f0d643f](https://github.com/sleistner/vscode-fileutils/commit/f0d643f92aae168505ca750cf9a020cb5610840e))

# [3.6.0](https://github.com/sleistner/vscode-fileutils/compare/v3.5.0...v3.6.0) (2023-01-23)


### Bug Fixes

* **TreeWalker:** replace workspace.findFiles in favor of fast-glob ([37c5078](https://github.com/sleistner/vscode-fileutils/commit/37c50781b4025e31c2023ea568aa78b4ad66714d))

## [3.5.1](https://github.com/sleistner/vscode-fileutils/compare/v3.5.0...v3.5.1) (2023-01-02)


### Bug Fixes

* **ci:** update semantic release ([6c12e92](https://github.com/sleistner/vscode-fileutils/commit/6c12e92409a9a04d92193781ad1fdb8e17c99ea1))

# [3.5.0](https://github.com/sleistner/vscode-fileutils/compare/v3.4.6...v3.5.0) (2022-01-18)


### Features

* **ci:** enable github actions ([3eead61](https://github.com/sleistner/vscode-fileutils/commit/3eead61d04d6adf1632503d29939e2c150147d87))


## [3.4.6](https://github.com/sleistner/vscode-fileutils/compare/v3.4.5...v3.4.6) (2022-01-14)


### Bug Fixes

* trigger gh actions release pipeline ([d9a59c9](https://github.com/sleistner/vscode-fileutils/commit/d9a59c9f974ceb2be6407aed64131e5761acb48a))

## [3.4.5](https://github.com/sleistner/vscode-fileutils/compare/v3.4.4...v3.4.5) (2021-02-22)


### Bug Fixes

* **deps:** update dependency brace-expansion to v2.0.1 ([dd094d0](https://github.com/sleistner/vscode-fileutils/commit/dd094d0))

## [3.4.4](https://github.com/sleistner/vscode-fileutils/compare/v3.4.3...v3.4.4) (2021-02-01)


### Bug Fixes

* prefer uri over current editor ([e63b27f](https://github.com/sleistner/vscode-fileutils/commit/e63b27f))

## [3.4.3](https://github.com/sleistner/vscode-fileutils/compare/v3.4.2...v3.4.3) (2021-01-06)


### Bug Fixes

* **NewFileController:** properly brace expand backslash paths ([ff95aae](https://github.com/sleistner/vscode-fileutils/commit/ff95aae))

## [3.4.2](https://github.com/sleistner/vscode-fileutils/compare/v3.4.1...v3.4.2) (2020-11-17)


### Bug Fixes

* **build:** include README ([b724700](https://github.com/sleistner/vscode-fileutils/commit/b724700))

## [3.4.1](https://github.com/sleistner/vscode-fileutils/compare/v3.4.0...v3.4.1) (2020-11-08)


### Bug Fixes

* **build:** include node_modules ([a28b0da](https://github.com/sleistner/vscode-fileutils/commit/a28b0da))

# [3.4.0](https://github.com/sleistner/vscode-fileutils/compare/v3.3.3...v3.4.0) (2020-11-06)


### Bug Fixes

* **readme:** trigger release ([9314428](https://github.com/sleistner/vscode-fileutils/commit/9314428))


### Features

* **NewFileCommand:** add support for brace expansion ([5e06afc](https://github.com/sleistner/vscode-fileutils/commit/5e06afc))

## [3.3.3](https://github.com/sleistner/vscode-fileutils/compare/v3.3.2...v3.3.3) (2020-10-26)


### Bug Fixes

* **New Folder or File Relative to Current View:** cancel execution if no editor is open ([858fea6](https://github.com/sleistner/vscode-fileutils/commit/858fea6))

## [3.3.2](https://github.com/sleistner/vscode-fileutils/compare/v3.3.1...v3.3.2) (2020-10-26)


### Bug Fixes

* **package:** update extension main file entry ([4892f84](https://github.com/sleistner/vscode-fileutils/commit/4892f84))

## [3.3.1](https://github.com/sleistner/vscode-fileutils/compare/v3.3.0...v3.3.1) (2020-10-25)


### Bug Fixes

* **duplicate:** prevent directories to be opened as document ([dc1c9f0](https://github.com/sleistner/vscode-fileutils/commit/dc1c9f0))

# [3.3.0](https://github.com/sleistner/vscode-fileutils/compare/v3.2.0...v3.3.0) (2020-10-25)


### Features

* **menus:** add file releated commands to tab and editor context ([a8b748e](https://github.com/sleistner/vscode-fileutils/commit/a8b748e))

# [3.2.0](https://github.com/sleistner/vscode-fileutils/compare/v3.1.1...v3.2.0) (2020-10-25)


### Features

* update icon ([5c2156b](https://github.com/sleistner/vscode-fileutils/commit/5c2156b))

## [3.1.1](https://github.com/sleistner/vscode-fileutils/compare/v3.1.0...v3.1.1) (2020-10-23)


### Bug Fixes

* **Rename, Move:** keep file in editor group ([5478345](https://github.com/sleistner/vscode-fileutils/commit/5478345))

# [3.1.0](https://github.com/sleistner/vscode-fileutils/compare/v3.0.1...v3.1.0) (2020-10-18)


### Features

* **move/rename:** trigger update imports when moving file ([7a40237](https://github.com/sleistner/vscode-fileutils/commit/7a40237))

## [3.0.1](https://github.com/sleistner/vscode-fileutils/compare/v3.0.0...v3.0.1) (2020-01-15)


### Bug Fixes

* **FileItem:** ensure file exists before deleting it ([7a44326](https://github.com/sleistner/vscode-fileutils/commit/7a44326))

# [3.0.0](https://github.com/sleistner/vscode-fileutils/compare/v2.14.9...v3.0.0) (2019-09-03)


### Bug Fixes

* **TreeWalker:** handle large directory structures safely ([c419c78](https://github.com/sleistner/vscode-fileutils/commit/c419c78))


### BREAKING CHANGES

* **TreeWalker:** The configuration option "typeahead.exclude" has been
removed in favour of VS Code native "files.exclude" option.

## [2.14.9](https://github.com/sleistner/vscode-fileutils/compare/v2.14.8...v2.14.9) (2019-08-26)


### Bug Fixes

* **RemoveFileCommand:** ensure only delete file tab was closed ([557e794](https://github.com/sleistner/vscode-fileutils/commit/557e794))

## [2.14.8](https://github.com/sleistner/vscode-fileutils/compare/v2.14.7...v2.14.8) (2019-08-26)


### Bug Fixes

* **NewFileCommand:** show quickpick on large directory structures ([8c8c537](https://github.com/sleistner/vscode-fileutils/commit/8c8c537))

## [2.14.7](https://github.com/sleistner/vscode-fileutils/compare/v2.14.6...v2.14.7) (2019-08-23)


### Bug Fixes

* **NewFileCommand:** show folder selector ([38fb33f](https://github.com/sleistner/vscode-fileutils/commit/38fb33f))

## [2.14.6](https://github.com/sleistner/vscode-fileutils/compare/v2.14.5...v2.14.6) (2019-08-20)


### Bug Fixes

* missing callback in remote environments ([63ef29a](https://github.com/sleistner/vscode-fileutils/commit/63ef29a))

## [2.14.5](https://github.com/sleistner/vscode-fileutils/compare/v2.14.4...v2.14.5) (2019-06-03)


### Bug Fixes

* **CopyFileName:** forward and process tab uri ([68ae985](https://github.com/sleistner/vscode-fileutils/commit/68ae985))

## [2.14.4](https://github.com/sleistner/vscode-fileutils/compare/v2.14.3...v2.14.4) (2019-05-29)


### Bug Fixes

* **FileItem:** update trash import ([850dfff](https://github.com/sleistner/vscode-fileutils/commit/850dfff))
* **package:** update trash to version 5.0.0 ([51f7017](https://github.com/sleistner/vscode-fileutils/commit/51f7017))

## [2.14.3](https://github.com/sleistner/vscode-fileutils/compare/v2.14.2...v2.14.3) (2019-05-29)


### Bug Fixes

* **contribution:** reorder conext menu items ([2883402](https://github.com/sleistner/vscode-fileutils/commit/2883402))

## [2.14.2](https://github.com/sleistner/vscode-fileutils/compare/v2.14.1...v2.14.2) (2019-05-29)


### Bug Fixes

* **package:** update fs-extra to version 8.0.0 ([86ff0b9](https://github.com/sleistner/vscode-fileutils/commit/86ff0b9))

## [2.14.1](https://github.com/sleistner/vscode-fileutils/compare/v2.14.0...v2.14.1) (2019-05-29)


### Bug Fixes

* icon position ([a273e32](https://github.com/sleistner/vscode-fileutils/commit/a273e32))

# [2.14.0](https://github.com/sleistner/vscode-fileutils/compare/v2.13.7...v2.14.0) (2019-05-29)


### Features

* **editor/title/context:** add rename, remove and copy command ([bb0482e](https://github.com/sleistner/vscode-fileutils/commit/bb0482e))

## [2.13.7](https://github.com/sleistner/vscode-fileutils/compare/v2.13.6...v2.13.7) (2019-04-20)


### Bug Fixes

* icon color ([21f4eb4](https://github.com/sleistner/vscode-fileutils/commit/21f4eb4))

## [2.13.6](https://github.com/sleistner/vscode-fileutils/compare/v2.13.5...v2.13.6) (2019-04-20)


### Bug Fixes

* **NewFileCommand:** prompt to select workspace ([8335975](https://github.com/sleistner/vscode-fileutils/commit/8335975))

## [2.13.4](https://github.com/sleistner/vscode-fileutils/compare/v2.13.3...v2.13.4) (2019-01-03)


### Bug Fixes

* **README:** remove unsupported category ([4a13e08](https://github.com/sleistner/vscode-fileutils/commit/4a13e08))

## [2.13.3](https://github.com/sleistner/vscode-fileutils/compare/v2.13.2...v2.13.3) (2018-11-11)


### Bug Fixes

* **releaserc:** enable release notes plugin ([b88a7c6](https://github.com/sleistner/vscode-fileutils/commit/b88a7c6))
* **releaserc:** enable release notes plugin ([eabac50](https://github.com/sleistner/vscode-fileutils/commit/eabac50))

## 2.13.0 (2018-11-10)

### Features
- `File: Rename`
- `File: Move`
[iliashkolyar](https://github.com/iliashkolyar) Add configuration to support whether to close old tabs [PR#67](https://github.com/sleistner/vscode-fileutils/pull/67)

## 2.12.0 (2018-11-02)

### Fixes

- [iliashkolyar](https://github.com/iliashkolyar) Support file operations on non-textual files [PR#63](https://github.com/sleistner/vscode-fileutils/pull/63)

### Features

- `File: Copy Name Of Active File` [iliashkolyar](https://github.com/iliashkolyar) Support copy name of active file [PR#61](https://github.com/sleistner/vscode-fileutils/pull/61)

## 2.10.3 (2018-06-15)

### Fixes

- `File: New File`, Show quick pick view only if more than 1 choice available.

## 2.10.0 (2018-06-14)

### Features

- `File: New File`, Autocomplete paths when creating a new file.
[PR#48](https://github.com/sleistner/vscode-fileutils/pull/48)
Inspired and heavily borrowed from [https://github.com/patbenatar/vscode-advanced-new-file](https://github.com/patbenatar/vscode-advanced-new-file)

## 2.9.0 (2018-05-24)

### Features

- `File: New File`, Adding a trailing / to the supplied target name causes the creation of a new directory.
[PR#25](https://github.com/sleistner/vscode-fileutils/pull/25)

## 2.8.1 (2018-02-25)

### Fixes

- Extension can not be loaded due to missing dependency.

## 2.8.0 (2018-02-25)

### Features

- `File: Delete`, Add configuration `fileutils.delete.useTrash` in order to move files to trash.
- `File: Delete`, Add configuration `fileutils.delete.confirm` to toggle confirmation dialog.

## 2.7.1 (2017-10-25)

### Fixes:

- Renaming and other actions move editor to first group

## 2.7.0 (2017-10-05)

### Features:

 - [lazyc97](https://github.com/lazyc97) Select filename when inputbox shows up [PR#23](https://github.com/sleistner/vscode-fileutils/pull/23)

## 2.6.1 (2017-06-12)

### Fixes:

 - Keyboard shortcuts failed to execute

## 2.4.1 (2017-03-06)

### Features:

 - Enable modal confirmation dialogs

## 2.3.4 (2017-03-06)

### Fixes:

 - File-New File or Folder failed to execute

## 2.3.3 (2017-01-12)

### Fixes:

 - File-Duplicate from the context menu doesn't work on Windows

## 2.3.1 (2016-10-14)

### Features:

  - file browser context menu

        Duplicate

  - file editor context menu

        Duplicate

  - file editor context menu

        Move

## 2.0.0 (2016-07-18)

### Features:

  - file browser context menu

        Move

    Moves the selected file or directory.

    _(Also creates nested directories)_

### Breaking Changes:

  - command prefix `extensions` has been renamed to `fileutils`

## 1.1.0 (2016-05-04)

### Features:

  - command

        File: New File Relative to Current View

    Adds a new file relative to file open in active editor.

    _(Also creates nested directories)_

  - command

        File: New File Relative to Project Root

    Adds a new file relative to project root.

    _(Also creates nested directories)_

  - command

        File: New Folder Relative to Current View

    Adds a new directory relative to file open in active editor.

    _(Also creates nested directories)_

  - command

        File: New Folder Relative to Project Root

    Adds a new directory relative to project root.

    _(Also creates nested directories)_

## 1.0.0 (2016-05-03)

Features:

  - command

        File: Rename

    Renames the file open in active editor.

    _(Also creates nested directories)_

  - command

        File: Move

    Moves the file open in active editor.

    _(Also creates nested directories)_

  - command

        File: Duplicate

    Duplicates the file open in active editor.

    _(Also creates nested directories)_

  - command

        File: Remove

    Deletes the file open in active editor.


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Steffen Leistner

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# File Utils - Visual Studio Code Extension

[![Known Vulnerabilities](https://snyk.io/test/github/sleistner/vscode-fileutils/badge.svg)](https://snyk.io/test/github/sleistner/vscode-fileutils)
[![Renovate](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

---
![icon](images/icon-96x96.png)

A convenient way of creating, duplicating, moving, renaming, deleting files and directories.

_Inspired by [Sidebar Enhancements](https://github.com/titoBouzout/SideBarEnhancements) for Sublime._

## How to use

![demo](images/demo.gif)

### Using the context menu

- Right click on a file or folder in the Explorer pane or in a file tab.
- Select one of those command: `Copy Name`, `Duplicate...`, `Move...`, `Rename...`, `Delete...`.
- For comand `Copy name`, the file name is copied to the clipboard.
- For other command, answer the additional info requested:
  - Duplicate: update path to duplicate near where the command palette is displayed.
  - Move: update path to move to near where the command palette is displayed.
  - Rename: update name in Explorer pane or near where the command palette is displayed
  - Delete: answer Visual Studio Code delete dialog.

## Using the command palette

- Bring up the command palette, and select "File Utils: ".
- Select one of the commands mentioned below.
- Press [Enter] to confirm, or [Escape] to cancel.

![howto](images/howto.png)

### Brace Expansion

> Brace expansion is a mechanism by which arbitrary strings may be generated.

Example file name input

```bash
/tmp/{a,b,c}/index.{cpp,ts,scss}
```

will generate the following files

```bash
➜  tree /tmp
/tmp
├── a
│   ├── index.cpp
│   ├── index.scss
│   └── index.ts
├── b
│   ├── index.cpp
│   ├── index.scss
│   └── index.ts
└── c
    ├── index.cpp
    ├── index.scss
    └── index.ts
```

### Note

Non-existent folders are created automatically.

## Changelog

- [https://github.com/sleistner/vscode-fileutils/blob/master/CHANGELOG.md](https://github.com/sleistner/vscode-fileutils/blob/master/CHANGELOG.md)

## How to contribute

- [https://github.com/sleistner/vscode-fileutils/blob/master/CONTRIBUTING.md](https://github.com/sleistner/vscode-fileutils/blob/master/CONTRIBUTING.md)

## Disclaimer

**Important:** This extension due to the nature of it's purpose will create
files on your hard drive and if necessary create the respective folder structure.
While it should not override any files during this process, I'm not giving any guarantees
or take any responsibility in case of lost data.

## Contributors

- [Steffen Leistner](https://github.com/sleistner)
- [Ilia Shkolyar](https://github.com/iliashkolyar)

## License

MIT

## Credits

### Icon

- [Janosch, Green Tropical Waters - Utilities Icon](https://iconarchive.com/show/tropical-waters-folders-icons-by-janosch500/Utilities-icon.html)


================================================
FILE: biome.json
================================================
{
	"$schema": "https://biomejs.dev/schemas/2.1.4/schema.json",
	"vcs": {
		"enabled": true,
		"clientKind": "git",
		"useIgnoreFile": true
	},
	"files": {
		"includes": ["src/**/*", "test/**/*"],
		"ignoreUnknown": false
	},
	"formatter": {
		"enabled": true,
		"indentStyle": "space",
		"indentWidth": 4,
		"lineWidth": 120
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true,
			"style": {
				"useBlockStatements": "off",
				"useNodejsImportProtocol": "off"
			},
			"suspicious": {
				"noExplicitAny": "warn"
			}
		}
	},
	"javascript": {
		"formatter": {
			"quoteStyle": "double",
			"trailingCommas": "es5",
			"semicolons": "always"
		}
	},
	"assist": {
		"enabled": true,
		"actions": {
			"source": {
				"organizeImports": "on"
			}
		}
	}
}


================================================
FILE: main.ts
================================================
export { activate } from "./src/extension";


================================================
FILE: package.json
================================================
{
    "name": "vscode-fileutils",
    "displayName": "File Utils",
    "description": "A convenient way of creating, duplicating, moving, renaming and deleting files and directories.",
    "version": "3.10.3",
    "private": true,
    "license": "MIT",
    "publisher": "sleistner",
    "engines": {
        "node": ">=22.0.0",
        "vscode": "^1.74.0"
    },
    "categories": [
        "Other"
    ],
    "keywords": [
        "utils",
        "files",
        "move",
        "duplicate",
        "rename"
    ],
    "icon": "images/icon.png",
    "galleryBanner": {
        "color": "#1c2237",
        "theme": "dark"
    },
    "bugs": {
        "url": "https://github.com/sleistner/vscode-fileutils/issues"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/sleistner/vscode-fileutils.git"
    },
    "homepage": "https://github.com/sleistner/vscode-fileutils/blob/master/README.md",
    "main": "./out/extension.js",
    "contributes": {
        "commands": [
            {
                "command": "fileutils.renameFile",
                "category": "File Utils",
                "title": "Rename..."
            },
            {
                "command": "fileutils.moveFile",
                "category": "File Utils",
                "title": "Move..."
            },
            {
                "command": "fileutils.duplicateFile",
                "category": "File Utils",
                "title": "Duplicate..."
            },
            {
                "command": "fileutils.removeFile",
                "category": "File Utils",
                "title": "Delete"
            },
            {
                "command": "fileutils.newFile",
                "category": "File Utils",
                "title": "New File Relative to Current View..."
            },
            {
                "command": "fileutils.newFileAtRoot",
                "category": "File Utils",
                "title": "New File Relative to Project Root..."
            },
            {
                "command": "fileutils.newFolder",
                "category": "File Utils",
                "title": "New Folder Relative to Current View..."
            },
            {
                "command": "fileutils.newFolderAtRoot",
                "category": "File Utils",
                "title": "New Folder Relative to Project Root..."
            },
            {
                "command": "fileutils.copyFileName",
                "category": "File Utils",
                "title": "Copy Name"
            }
        ],
        "menus": {
            "explorer/context": [
                {
                    "command": "fileutils.moveFile",
                    "group": "7_modification",
                    "when": "config.fileutils.menus.context.explorer =~ /moveFile/"
                },
                {
                    "command": "fileutils.duplicateFile",
                    "group": "7_modification",
                    "when": "config.fileutils.menus.context.explorer =~ /duplicateFile/"
                },
                {
                    "command": "fileutils.newFileAtRoot",
                    "group": "2_workspace",
                    "when": "config.fileutils.menus.context.explorer =~ /newFileAtRoot/"
                },
                {
                    "command": "fileutils.newFolderAtRoot",
                    "group": "2_workspace",
                    "when": "config.fileutils.menus.context.explorer =~ /newFolderAtRoot/"
                },
                {
                    "command": "fileutils.copyFileName",
                    "group": "6_copypath",
                    "when": "config.fileutils.menus.context.explorer =~ /copyFileName/"
                }
            ],
            "editor/context": [
                {
                    "command": "fileutils.copyFileName",
                    "group": "1_copypath",
                    "when": "config.fileutils.menus.context.editor =~ /copyFileName/ && resourceScheme != output"
                },
                {
                    "command": "fileutils.renameFile",
                    "group": "1_modification@1",
                    "when": "config.fileutils.menus.context.editor =~ /renameFile/ && resourceScheme != output"
                },
                {
                    "command": "fileutils.moveFile",
                    "group": "1_modification@2",
                    "when": "config.fileutils.menus.context.editor =~ /moveFile/ && resourceScheme != output"
                },
                {
                    "command": "fileutils.duplicateFile",
                    "group": "1_modification@3",
                    "when": "config.fileutils.menus.context.editor =~ /duplicateFile/ && resourceScheme != output"
                },
                {
                    "command": "fileutils.removeFile",
                    "group": "1_modification@4",
                    "when": "config.fileutils.menus.context.editor =~ /removeFile/ && resourceScheme != output"
                }
            ],
            "editor/title/context": [
                {
                    "command": "fileutils.copyFileName",
                    "group": "1_copypath",
                    "when": "config.fileutils.menus.context.editorTitle =~ /copyFileName/"
                },
                {
                    "command": "fileutils.renameFile",
                    "group": "1_modification@1",
                    "when": "config.fileutils.menus.context.editorTitle =~ /renameFile/"
                },
                {
                    "command": "fileutils.moveFile",
                    "group": "1_modification@2",
                    "when": "config.fileutils.menus.context.editorTitle =~ /moveFile/"
                },
                {
                    "command": "fileutils.duplicateFile",
                    "group": "1_modification@3",
                    "when": "config.fileutils.menus.context.editorTitle =~ /duplicateFile/"
                },
                {
                    "command": "fileutils.removeFile",
                    "group": "1_modification@4",
                    "when": "config.fileutils.menus.context.editorTitle =~ /removeFile/"
                }
            ]
        },
        "configuration": {
            "type": "object",
            "title": "File Utils",
            "properties": {
                "fileutils.typeahead.enabled": {
                    "type": "boolean",
                    "default": true,
                    "description": "Controls whether to show a directory selector for new file and new folder command.",
                    "markdownDeprecationMessage": "**Deprecated**: Please use `#fileutils.newFile.typeahead.enabled#` or `#fileutils.newFolder.typeahead.enabled#` instead.",
                    "deprecationMessage": "Deprecated: Please use fileutils.newFile.typeahead.enabled or fileutils.newFolder.typeahead.enabled instead."
                },
                "fileutils.duplicateFile.typeahead.enabled": {
                    "type": "boolean",
                    "default": false,
                    "description": "Controls whether to show a directory selector for the duplicate file command."
                },
                "fileutils.moveFile.typeahead.enabled": {
                    "type": "boolean",
                    "default": false,
                    "description": "Controls whether to show a directory selector for the move file command."
                },
                "fileutils.newFile.typeahead.enabled": {
                    "type": "boolean",
                    "default": true,
                    "description": "Controls whether to show a directory selector for the new file command."
                },
                "fileutils.newFolder.typeahead.enabled": {
                    "type": "boolean",
                    "default": true,
                    "description": "Controls whether to show a directory selector for new folder command."
                },
                "fileutils.inputBox.pathType": {
                    "type": "string",
                    "default": "root",
                    "enum": [
                        "root",
                        "workspace"
                    ],
                    "enumDescriptions": [
                        "Absolute file path of the opened workspace or folder (e.g. /Users/Development/myWorkspace)",
                        "Relative file path of the opened workspace or folder (e.g. /myWorkspace)"
                    ],
                    "description": "Controls the path that is shown in the input box."
                },
                "fileutils.inputBox.pathTypeIndicator": {
                    "type": "string",
                    "default": "@",
                    "maxLength": 50,
                    "description": "Controls the indicator that is shown in the input box when the path type is workspace. This setting only has an effect when 'fileutils.inputBox.pathType' is set to 'workspace'.",
                    "markdownDescription": "Controls the indicator that is shown in the input box when the path type is workspace. \n\nThis setting only has an effect when `#fileutils.inputBox.pathType#` is set to `workspace`.\n\nFor example, if the path type is `workspace` and the indicator is `@`, the path will be shown as `@/myWorkspace`."
                },
                "fileutils.menus.context.explorer": {
                    "type": "array",
                    "default": [
                        "moveFile",
                        "duplicateFile",
                        "newFileAtRoot",
                        "newFolderAtRoot",
                        "copyFileName"
                    ],
                    "items": {
                        "type": "string",
                        "enum": [
                            "moveFile",
                            "duplicateFile",
                            "newFileAtRoot",
                            "newFolderAtRoot",
                            "copyFileName"
                        ],
                        "enumDescriptions": [
                            "Move",
                            "Duplicate",
                            "New File Relative to Project Root",
                            "New Folder Relative to Project Root",
                            "Copy Name"
                        ]
                    },
                    "uniqueItems": true,
                    "description": "Controls whether to show the command in the explorer context menu.",
                    "order": 90
                },
                "fileutils.menus.context.editor": {
                    "type": "array",
                    "default": [
                        "renameFile",
                        "moveFile",
                        "duplicateFile",
                        "removeFile",
                        "copyFileName"
                    ],
                    "items": {
                        "type": "string",
                        "enum": [
                            "renameFile",
                            "moveFile",
                            "duplicateFile",
                            "removeFile",
                            "copyFileName"
                        ],
                        "enumDescriptions": [
                            "Rename",
                            "Move",
                            "Duplicate",
                            "Remove",
                            "Copy Name"
                        ]
                    },
                    "uniqueItems": true,
                    "description": "Controls whether to show the command in the editor context menu.",
                    "order": 100
                },
                "fileutils.menus.context.editorTitle": {
                    "type": "array",
                    "default": [
                        "renameFile",
                        "moveFile",
                        "duplicateFile",
                        "removeFile",
                        "copyFileName"
                    ],
                    "items": {
                        "type": "string",
                        "enum": [
                            "renameFile",
                            "moveFile",
                            "duplicateFile",
                            "removeFile",
                            "copyFileName"
                        ],
                        "enumDescriptions": [
                            "Rename",
                            "Move",
                            "Duplicate",
                            "Remove",
                            "Copy Name"
                        ]
                    },
                    "uniqueItems": true,
                    "description": "Controls whether to show the command in the editor title context menu.",
                    "order": 110
                }
            }
        }
    },
    "scripts": {
        "vscode:prepublish": "npm run -S esbuild-base -- --minify",
        "esbuild-base": "npm run clean && esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
        "watch": "scripts/dev-env && npm run -S esbuild-base -- --sourcemap --watch",
        "tsc:watch": "tsc -watch -p ./",
        "pretest": "tsc -p ./",
        "test": "node ./out/test/runTest.js",
        "lint": "biome check src test",
        "lint:fix": "biome check --write src test",
        "format": "biome format src test",
        "format:write": "biome format --write src test",
        "semantic-release": "semantic-release",
        "prepare": "[ ! -x ./node_modules/.bin/husky ] && exit 0; husky install",
        "clean": "rimraf out"
    },
    "devDependencies": {
        "@biomejs/biome": "^2.1.4",
        "@semantic-release/changelog": "6.0.3",
        "@semantic-release/git": "10.0.1",
        "@tsconfig/node22": "^22.0.2",
        "@types/bluebird": "^3.5.42",
        "@types/bluebird-retry": "^0.11.8",
        "@types/brace-expansion": "^1.1.2",
        "@types/chai": "^4.3.20",
        "@types/mocha": "^10.0.10",
        "@types/node": "^24.2.1",
        "@types/sinon": "^17.0.4",
        "@types/sinon-chai": "^3.2.12",
        "@types/vscode": "^1.102.0",
        "@vscode/test-electron": "^2.5.2",
        "bluebird": "3.7.2",
        "bluebird-retry": "0.11.0",
        "chai": "^4.5.0",
        "esbuild": "^0.27.0",
        "husky": "^9.1.7",
        "mocha": "^11.7.1",
        "rimraf": "^6.0.1",
        "semantic-release": "^24.2.7",
        "semantic-release-vsce": "^6.0.11",
        "sinon": "^21.0.0",
        "sinon-chai": "^3.7.0",
        "typescript": "^5.9.2"
    },
    "dependencies": {
        "brace-expansion": "^4.0.1",
        "fast-glob": "^3.3.3"
    }
}


================================================
FILE: renovate.json
================================================
{
    "extends": [
        "config:base"
    ],
    "ignoreDeps": ["@types/node", "@types/vscode"],
    "packageRules": [
        {
          "updateTypes": ["minor", "patch", "pin", "digest"],
          "automerge": true
        }
    ]
}


================================================
FILE: scripts/dev-env
================================================
#!/usr/bin/env bash

rm -rf ./tmp
mkdir -p ./tmp/{app,workspace,scripts}
touch ./tmp/{app,workspace,scripts}/{foo,bar,baz}.ts


================================================
FILE: src/FileItem.ts
================================================
import * as fs from "fs";
import * as path from "path";
import { Uri, WorkspaceEdit, workspace } from "vscode";

function assertTargetPath(targetPath: Uri | undefined): asserts targetPath is Uri {
    if (targetPath === undefined) {
        throw new Error("Missing target path");
    }
}

export class FileItem {
    private SourcePath: Uri;
    private TargetPath: Uri | undefined;

    constructor(
        sourcePath: Uri | string,
        targetPath?: Uri | string,
        private IsDir: boolean = false
    ) {
        this.SourcePath = this.toUri(sourcePath);
        if (targetPath !== undefined) {
            this.TargetPath = this.toUri(targetPath);
        }
    }

    get name(): string {
        return path.basename(this.SourcePath.path);
    }

    get path(): Uri {
        return this.SourcePath;
    }

    get targetPath(): Uri | undefined {
        return this.TargetPath;
    }

    get exists(): boolean {
        if (this.targetPath === undefined) {
            return false;
        }
        return fs.existsSync(this.targetPath.fsPath);
    }

    get isDir(): boolean {
        return this.IsDir;
    }

    public async move(): Promise<FileItem> {
        assertTargetPath(this.targetPath);

        const edit = new WorkspaceEdit();
        edit.renameFile(this.path, this.targetPath, { overwrite: true });
        const result = await workspace.applyEdit(edit);

        if (!result) {
            throw new Error(`Failed to move file "${this.targetPath.fsPath}."`);
        }

        this.SourcePath = this.targetPath;
        return this;
    }

    public async duplicate(): Promise<FileItem> {
        assertTargetPath(this.targetPath);

        try {
            await workspace.fs.copy(this.path, this.targetPath, { overwrite: true });
            return new FileItem(this.targetPath, undefined, this.isDir);
        } catch (error) {
            throw new Error(`Failed to duplicate file "${this.targetPath.fsPath}. (${error})"`);
        }
    }

    public async remove(): Promise<FileItem> {
        const edit = new WorkspaceEdit();
        edit.deleteFile(this.path, { recursive: true, ignoreIfNotExists: true });
        const result = await workspace.applyEdit(edit);

        if (!result) {
            throw new Error(`Failed to delete file "${this.path.fsPath}."`);
        }

        return this;
    }

    public async create(mkDir?: boolean): Promise<FileItem> {
        assertTargetPath(this.targetPath);

        if (this.exists) {
            await workspace.fs.delete(this.targetPath, { recursive: true });
        }

        if (mkDir === true || this.isDir) {
            await workspace.fs.createDirectory(this.targetPath);
        } else {
            await workspace.fs.writeFile(this.targetPath, new Uint8Array());
        }

        return new FileItem(this.targetPath, undefined, this.isDir);
    }

    private toUri(uriOrString: Uri | string): Uri {
        return uriOrString instanceof Uri ? uriOrString : Uri.file(uriOrString);
    }
}


================================================
FILE: src/command/BaseCommand.ts
================================================
import type { Uri } from "vscode";
import type { FileController } from "../controller";
import type { FileItem } from "../FileItem";
import type { Command, CommandConstructorOptions } from "./Command";

interface ExecuteControllerOptions {
    openFileInEditor?: boolean;
}

export abstract class BaseCommand<T extends FileController> implements Command {
    constructor(
        protected controller: T,
        readonly options?: CommandConstructorOptions
    ) {}

    public abstract execute(uri?: Uri): Promise<void>;

    protected async executeController(
        fileItem: FileItem | undefined,
        options?: ExecuteControllerOptions
    ): Promise<void> {
        if (fileItem) {
            const result = await this.controller.execute({ fileItem });
            if (options?.openFileInEditor) {
                await this.controller.openFileInEditor(result);
            }
        }
    }
}


================================================
FILE: src/command/Command.ts
================================================
import type { Uri } from "vscode";

export interface CommandConstructorOptions {
    relativeToRoot?: boolean;
}

export interface Command {
    execute(uri?: Uri): Promise<void>;
}


================================================
FILE: src/command/CopyFileNameCommand.ts
================================================
import type { Uri } from "vscode";
import type { CopyFileNameController } from "../controller/CopyFileNameController";
import { BaseCommand } from "./BaseCommand";

export class CopyFileNameCommand extends BaseCommand<CopyFileNameController> {
    public async execute(uri?: Uri): Promise<void> {
        const dialogOptions = { uri };
        const fileItem = await this.controller.showDialog(dialogOptions);
        await this.executeController(fileItem);
    }
}


================================================
FILE: src/command/DuplicateFileCommand.ts
================================================
import type { Uri } from "vscode";
import type { MoveFileController } from "../controller/MoveFileController";
import { getConfiguration } from "../lib/config";
import { BaseCommand } from "./BaseCommand";

export class DuplicateFileCommand extends BaseCommand<MoveFileController> {
    public async execute(uri?: Uri): Promise<void> {
        const typeahead = getConfiguration("duplicateFile.typeahead.enabled") === true;
        const dialogOptions = { prompt: "Duplicate As", uri, typeahead };
        const fileItem = await this.controller.showDialog(dialogOptions);
        await this.executeController(fileItem, { openFileInEditor: !fileItem?.isDir });
    }
}


================================================
FILE: src/command/MoveFileCommand.ts
================================================
import type { Uri } from "vscode";
import type { MoveFileController } from "../controller/MoveFileController";
import { getConfiguration } from "../lib/config";
import { BaseCommand } from "./BaseCommand";

export class MoveFileCommand extends BaseCommand<MoveFileController> {
    public async execute(uri?: Uri): Promise<void> {
        const typeahead = getConfiguration("moveFile.typeahead.enabled") === true;
        const dialogOptions = { prompt: "New Location", uri, typeahead };
        const fileItem = await this.controller.showDialog(dialogOptions);
        await this.executeController(fileItem);
    }
}


================================================
FILE: src/command/NewFileCommand.ts
================================================
import type { NewFileController } from "../controller/NewFileController";
import { getConfiguration } from "../lib/config";
import { BaseCommand } from "./BaseCommand";

export class NewFileCommand extends BaseCommand<NewFileController> {
    public async execute(): Promise<void> {
        const typeahead = this.typeahead;
        const relativeToRoot = this.options?.relativeToRoot ?? false;
        const dialogOptions = { prompt: "File Name", relativeToRoot, typeahead };
        const fileItems = await this.controller.showDialog(dialogOptions);

        if (fileItems) {
            const executions = [...fileItems].map(async (fileItem) => {
                const result = await this.controller.execute({ fileItem });
                await this.controller.openFileInEditor(result);
            });
            await Promise.all(executions);
        }
    }

    protected get typeahead(): boolean {
        return (getConfiguration("newFile.typeahead.enabled") ?? getConfiguration("typeahead.enabled")) === true;
    }
}


================================================
FILE: src/command/NewFolderCommand.ts
================================================
import { getConfiguration } from "../lib/config";
import { NewFileCommand } from "./NewFileCommand";

export class NewFolderCommand extends NewFileCommand {
    public async execute(): Promise<void> {
        const typeahead = this.typeahead;
        const relativeToRoot = this.options?.relativeToRoot ?? false;
        const dialogOptions = { prompt: "Folder Name", relativeToRoot, typeahead };
        const fileItems = await this.controller.showDialog(dialogOptions);

        if (fileItems) {
            const executions = [...fileItems].map(async (fileItem) => {
                await this.controller.execute({ fileItem, isDir: true });
            });
            await Promise.all(executions);
        }
    }

    protected get typeahead(): boolean {
        return (getConfiguration("newFolder.typeahead.enabled") ?? getConfiguration("typeahead.enabled")) === true;
    }
}


================================================
FILE: src/command/RemoveFileCommand.ts
================================================
import type { Uri } from "vscode";
import type { RemoveFileController } from "../controller";
import { BaseCommand } from "./BaseCommand";

export class RemoveFileCommand extends BaseCommand<RemoveFileController> {
    public async execute(uri?: Uri): Promise<void> {
        const fileItem = await this.controller.showDialog({ uri });
        await this.executeController(fileItem);
    }
}


================================================
FILE: src/command/RenameFileCommand.ts
================================================
import type { Uri } from "vscode";
import type { RenameFileController } from "../controller/RenameFileController";
import { BaseCommand } from "./BaseCommand";

export class RenameFileCommand extends BaseCommand<RenameFileController> {
    public async execute(uri?: Uri): Promise<void> {
        const dialogOptions = { prompt: "New Name", uri };
        const fileItem = await this.controller.showDialog(dialogOptions);
        await this.executeController(fileItem);
    }
}


================================================
FILE: src/command/index.ts
================================================
export { Command } from "./Command";
export { CopyFileNameCommand } from "./CopyFileNameCommand";
export { DuplicateFileCommand } from "./DuplicateFileCommand";
export { MoveFileCommand } from "./MoveFileCommand";
export { NewFileCommand } from "./NewFileCommand";
export { NewFolderCommand } from "./NewFolderCommand";
export { RemoveFileCommand } from "./RemoveFileCommand";
export { RenameFileCommand } from "./RenameFileCommand";


================================================
FILE: src/controller/BaseFileController.ts
================================================
import path from "path";
import {
    commands,
    type ExtensionContext,
    env,
    type InputBoxOptions,
    type TextEditor,
    Uri,
    type WorkspaceFolder,
    window,
    workspace,
} from "vscode";
import type { FileItem } from "../FileItem";
import { Cache } from "../lib/Cache";
import { getConfiguration } from "../lib/config";
import type { DialogOptions, ExecuteOptions, FileController, SourcePathOptions } from "./FileController";
import { TypeAheadController } from "./TypeAheadController";

enum InputBoxPathType {
    Root = "root",
    Workspace = "workspace",
}

type TargetPathInputBoxOptions = InputBoxOptions & Required<Pick<InputBoxOptions, "value">>;

export interface TargetPathInputBoxValueOptions extends DialogOptions {
    workspaceFolderPath?: string;
    pathType: InputBoxPathType;
}

export abstract class BaseFileController implements FileController {
    constructor(protected context: ExtensionContext) {}

    public abstract showDialog(options?: DialogOptions): Promise<FileItem | FileItem[] | undefined>;

    public abstract execute(options: ExecuteOptions): Promise<FileItem>;

    private get pathTypeIndicator(): string {
        return getConfiguration("inputBox.pathTypeIndicator") ?? "";
    }

    public async openFileInEditor(fileItem: FileItem): Promise<TextEditor | undefined> {
        if (fileItem.isDir) {
            return;
        }

        const textDocument = await workspace.openTextDocument(fileItem.path);
        if (!textDocument) {
            throw new Error("Could not open file!");
        }

        const editor = await window.showTextDocument(textDocument);
        if (!editor) {
            throw new Error("Could not show document!");
        }

        return editor;
    }

    public async closeCurrentFileEditor(): Promise<unknown> {
        return commands.executeCommand("workbench.action.closeActiveEditor");
    }

    protected async getTargetPath(sourcePath: string, options: DialogOptions): Promise<string | undefined> {
        const { prompt } = options;

        const pathType = this.getInputBoxPathType();
        const workspaceFolderPath = await this.getWorkspaceFolderPath();
        const value = await this.getTargetPathInputBoxValue(sourcePath, {
            ...options,
            workspaceFolderPath,
            pathType,
        });

        const targetPath = await this.showTargetPathInputBox({
            prompt,
            value,
        });

        const shouldRestoreAbsolutePath = targetPath && workspaceFolderPath && pathType === InputBoxPathType.Workspace;

        if (shouldRestoreAbsolutePath) {
            return path.join(
                workspaceFolderPath,
                targetPath.replace(new RegExp(`^(${this.pathTypeIndicator}|${workspaceFolderPath})`, "g"), "")
            );
        }

        return targetPath;
    }

    protected async showTargetPathInputBox(options: TargetPathInputBoxOptions): Promise<string | undefined> {
        const { prompt, value } = options;
        const valueSelection = this.getFilenameSelection(value);

        return await window.showInputBox({
            prompt,
            value,
            valueSelection,
            ignoreFocusOut: true,
        });
    }

    private getInputBoxPathType(): InputBoxPathType {
        const pathType: InputBoxPathType | undefined = getConfiguration("inputBox.pathType");

        if (pathType && Object.values(InputBoxPathType).includes(pathType)) {
            return pathType;
        }
        return InputBoxPathType.Root;
    }

    protected async getTargetPathInputBoxValue(
        sourcePath: string,
        options: TargetPathInputBoxValueOptions
    ): Promise<string> {
        const { workspaceFolderPath, pathType } = options;

        if (pathType === InputBoxPathType.Workspace && workspaceFolderPath) {
            return sourcePath.replace(workspaceFolderPath, this.pathTypeIndicator);
        }
        return sourcePath;
    }

    protected getFilenameSelection(value: string): [number, number] {
        return [value.length, value.length];
    }

    public async getSourcePath({ ignoreIfNotExists, uri }: SourcePathOptions = {}): Promise<string> {
        if (uri?.fsPath) {
            return uri.fsPath;
        }
        // Attempting to get the fileName from the activeTextEditor.
        // Works for text files only.
        const activeEditor = window.activeTextEditor;
        if (activeEditor?.document?.fileName) {
            return activeEditor.document.fileName;
        }

        // No activeTextEditor means that we don't have an active file or
        // the active file is a non-text file (e.g. binary files such as images).
        // Since there is no actual API to differentiate between the scenarios, we try to retrieve
        // the path for a non-textual file before throwing an error.
        const sourcePath = await this.getSourcePathForNonTextFile();
        if (!sourcePath && ignoreIfNotExists !== true) {
            throw new Error();
        }

        return sourcePath;
    }

    protected getCache(namespace: string): Cache {
        return new Cache(this.context.globalState, namespace);
    }

    protected async ensureWritableFile(fileItem: FileItem): Promise<FileItem> {
        if (!fileItem.exists) {
            return fileItem;
        }

        if (fileItem.targetPath === undefined) {
            throw new Error("Missing target path");
        }

        const message = `File '${fileItem.targetPath.path}' already exists.`;
        const action = "Overwrite";
        const overwrite = await window.showInformationMessage(message, { modal: true }, action);
        if (overwrite) {
            return fileItem;
        }
        throw new Error();
    }

    private async getSourcePathForNonTextFile(): Promise<string> {
        // Since there is no API to get details of non-textual files, the following workaround is performed:
        // 1. Saving the original clipboard data to a local variable.
        const originalClipboardData = await env.clipboard.readText();

        // 2. Populating the clipboard with an empty string
        await env.clipboard.writeText("");

        // 3. Calling the copyPathOfActiveFile that populates the clipboard with the source path of the active file.
        // If there is no active file - the clipboard will not be populated and it will stay with the empty string.
        await commands.executeCommand("workbench.action.files.copyPathOfActiveFile");

        // 4. Get the clipboard data after the API call
        const postAPICallClipboardData = await env.clipboard.readText();

        // 5. Return the saved original clipboard data to the clipboard so this method
        // will not interfere with the clipboard's content.
        await env.clipboard.writeText(originalClipboardData);

        // 6. Return the clipboard data from the API call (which could be an empty string if it failed).
        return postAPICallClipboardData;
    }

    protected async getWorkspaceFolderPath(relativeToRoot?: boolean): Promise<string | undefined>;
    protected async getWorkspaceFolderPath(): Promise<string | undefined> {
        const workspaceFolder = await this.selectWorkspaceFolder();
        return workspaceFolder?.uri.fsPath;
    }

    protected async selectWorkspaceFolder(): Promise<WorkspaceFolder | undefined> {
        if (workspace.workspaceFolders && workspace.workspaceFolders.length === 1) {
            return workspace.workspaceFolders[0];
        }

        const sourcePath = await this.getSourcePath({ ignoreIfNotExists: true });
        const uri = Uri.file(sourcePath);
        return workspace.getWorkspaceFolder(uri) || window.showWorkspaceFolderPick();
    }

    protected get isMultiRootWorkspace(): boolean {
        return workspace.workspaceFolders !== undefined && workspace.workspaceFolders.length > 1;
    }

    protected async getFileSourcePathAtRoot(rootPath: string, options: SourcePathOptions): Promise<string> {
        const { relativeToRoot = false, typeahead } = options;
        let sourcePath = rootPath;

        if (typeahead) {
            const cache = this.getCache(`workspace:${sourcePath}`);
            const typeAheadController = new TypeAheadController(cache, relativeToRoot);
            sourcePath = await typeAheadController.showDialog(sourcePath);
        }

        if (!sourcePath) {
            throw new Error();
        }

        return sourcePath;
    }
}


================================================
FILE: src/controller/CopyFileNameController.ts
================================================
import { env } from "vscode";
import { FileItem } from "../FileItem";
import { BaseFileController } from "./BaseFileController";
import type { DialogOptions, ExecuteOptions } from "./FileController";

export class CopyFileNameController extends BaseFileController {
    public async showDialog(options: DialogOptions): Promise<FileItem> {
        const { uri } = options;
        const sourcePath = await this.getSourcePath({ uri });

        if (!sourcePath) {
            throw new Error();
        }
        return new FileItem(sourcePath);
    }

    public async execute(options: ExecuteOptions): Promise<FileItem> {
        await env.clipboard.writeText(options.fileItem.name);
        return options.fileItem;
    }
}


================================================
FILE: src/controller/DuplicateFileController.ts
================================================
import type { FileItem } from "../FileItem";
import type { ExecuteOptions } from "./FileController";
import { MoveFileController } from "./MoveFileController";

export class DuplicateFileController extends MoveFileController {
    public async execute(options: ExecuteOptions): Promise<FileItem> {
        const { fileItem } = options;
        await this.ensureWritableFile(fileItem);
        return fileItem.duplicate();
    }
}


================================================
FILE: src/controller/FileController.ts
================================================
import type { TextEditor, Uri } from "vscode";
import type { FileItem } from "../FileItem";

export interface DialogOptions {
    prompt?: string;
    uri?: Uri;
    typeahead?: boolean;
}

export interface ExecuteOptions {
    fileItem: FileItem;
}

export interface SourcePathOptions {
    relativeToRoot?: boolean;
    ignoreIfNotExists?: boolean;
    uri?: Uri;
    typeahead?: boolean;
}

export interface FileController {
    showDialog(options?: DialogOptions): Promise<FileItem | FileItem[] | undefined>;
    execute(options: ExecuteOptions): Promise<FileItem>;
    openFileInEditor(fileItem: FileItem): Promise<TextEditor | undefined>;
    closeCurrentFileEditor(): Promise<unknown>;
    getSourcePath(options?: SourcePathOptions): Promise<string>;
}


================================================
FILE: src/controller/MoveFileController.ts
================================================
import * as path from "path";
import { FileType, Uri, workspace } from "vscode";
import { FileItem } from "../FileItem";
import { BaseFileController, type TargetPathInputBoxValueOptions } from "./BaseFileController";
import type { DialogOptions, ExecuteOptions } from "./FileController";

export class MoveFileController extends BaseFileController {
    public async showDialog(options: DialogOptions): Promise<FileItem | undefined> {
        const { uri } = options;
        const sourcePath = await this.getSourcePath({ uri });

        if (!sourcePath) {
            throw new Error();
        }

        const targetPath = await this.getTargetPath(sourcePath, options);

        if (targetPath) {
            const isDir = (await workspace.fs.stat(Uri.file(sourcePath))).type === FileType.Directory;
            return new FileItem(sourcePath, targetPath, isDir);
        }
    }

    public async execute(options: ExecuteOptions): Promise<FileItem> {
        const { fileItem } = options;
        await this.ensureWritableFile(fileItem);
        return fileItem.move();
    }

    protected async getTargetPathInputBoxValue(
        sourcePath: string,
        options: TargetPathInputBoxValueOptions
    ): Promise<string> {
        const value = await this.getFullTargetPathInputBoxValue(sourcePath, options);
        return super.getTargetPathInputBoxValue(value, options);
    }

    private async getFullTargetPathInputBoxValue(
        sourcePath: string,
        options: TargetPathInputBoxValueOptions
    ): Promise<string> {
        const { typeahead, workspaceFolderPath } = options;

        if (!typeahead) {
            return sourcePath;
        }

        if (!workspaceFolderPath) {
            throw new Error();
        }

        const rootPath = await this.getFileSourcePathAtRoot(workspaceFolderPath, { relativeToRoot: true, typeahead });
        const fileName = path.basename(sourcePath);

        return path.join(rootPath, fileName);
    }

    protected getFilenameSelection(value: string): [number, number] {
        const basename = path.basename(value);
        const start = value.length - basename.length;
        const dot = basename.lastIndexOf(".");
        const exclusiveEndIndex = dot <= 0 ? value.length : start + dot;

        return [start, exclusiveEndIndex];
    }
}


================================================
FILE: src/controller/NewFileController.ts
================================================
import expand from "brace-expansion";
import * as path from "path";
import { window } from "vscode";
import { FileItem } from "../FileItem";
import { BaseFileController, type TargetPathInputBoxValueOptions } from "./BaseFileController";
import type { DialogOptions, ExecuteOptions, SourcePathOptions } from "./FileController";

export interface NewFileDialogOptions extends Omit<DialogOptions, "uri"> {
    relativeToRoot?: boolean;
}

export interface NewFileExecuteOptions extends ExecuteOptions {
    isDir?: boolean;
}

export class NewFileController extends BaseFileController {
    public async showDialog(options: NewFileDialogOptions): Promise<FileItem[] | undefined> {
        const { relativeToRoot = false, typeahead } = options;
        const sourcePath = await this.getNewFileSourcePath({ relativeToRoot, typeahead });
        const targetPath = await this.getTargetPath(sourcePath, options);

        if (!targetPath) {
            return;
        }

        return expand(targetPath.replace(/\\/g, "/")).map((filePath) => {
            const realPath = path.resolve(sourcePath, filePath);
            const isDir = filePath.endsWith("/");
            return new FileItem(sourcePath, realPath, isDir);
        });
    }

    public async execute(options: NewFileExecuteOptions): Promise<FileItem> {
        const { fileItem, isDir = false } = options;
        await this.ensureWritableFile(fileItem);
        try {
            return fileItem.create(isDir);
        } catch {
            throw new Error(`Error creating file '${fileItem.path}'.`);
        }
    }

    protected async getTargetPathInputBoxValue(
        sourcePath: string,
        options: TargetPathInputBoxValueOptions
    ): Promise<string> {
        const value = path.join(sourcePath, path.sep);
        return super.getTargetPathInputBoxValue(value, options);
    }

    public async getNewFileSourcePath({ relativeToRoot, typeahead }: SourcePathOptions): Promise<string> {
        const rootPath = await this.getRootPath(relativeToRoot === true);

        if (!rootPath) {
            throw new Error();
        }

        return this.getFileSourcePathAtRoot(rootPath, { relativeToRoot, typeahead });
    }

    private async getRootPath(relativeToRoot: boolean): Promise<string | undefined> {
        if (relativeToRoot) {
            return this.getWorkspaceFolderPath(relativeToRoot);
        }
        return path.dirname(await this.getSourcePath());
    }

    protected async getWorkspaceFolderPath(relativeToRoot: boolean): Promise<string | undefined> {
        const requiresWorkspaceFolderPick = relativeToRoot && this.isMultiRootWorkspace;
        if (requiresWorkspaceFolderPick) {
            const workspaceFolder = await window.showWorkspaceFolderPick();
            return workspaceFolder?.uri.fsPath;
        }

        return super.getWorkspaceFolderPath();
    }
}


================================================
FILE: src/controller/RemoveFileController.ts
================================================
import * as path from "path";
import { window, workspace } from "vscode";
import { FileItem } from "../FileItem";
import { BaseFileController } from "./BaseFileController";
import type { DialogOptions, ExecuteOptions } from "./FileController";

export class RemoveFileController extends BaseFileController {
    public async showDialog(options: DialogOptions): Promise<FileItem | undefined> {
        const { uri } = options;
        const sourcePath = await this.getSourcePath({ uri });

        if (!sourcePath) {
            throw new Error();
        }

        if (this.confirmDelete === false) {
            return new FileItem(sourcePath);
        }

        const message = `Are you sure you want to delete '${path.basename(sourcePath)}'?`;
        const action = "Move to Trash";
        const remove = await window.showInformationMessage(message, { modal: true }, action);
        if (remove) {
            return new FileItem(sourcePath);
        }
    }

    public async execute(options: ExecuteOptions): Promise<FileItem> {
        const { fileItem } = options;
        try {
            await fileItem.remove();
        } catch (_e) {
            throw new Error(`Error deleting file '${fileItem.path}'.`);
        }
        return fileItem;
    }

    private get confirmDelete(): boolean {
        return workspace.getConfiguration("explorer", null).get("confirmDelete") === true;
    }
}


================================================
FILE: src/controller/RenameFileController.ts
================================================
import * as path from "path";
import type { DialogOptions } from "./FileController";
import { MoveFileController } from "./MoveFileController";

export class RenameFileController extends MoveFileController {
    protected async getTargetPath(sourcePath: string, options: DialogOptions): Promise<string | undefined> {
        const { prompt } = options;
        const value = path.basename(sourcePath);
        const targetPath = await this.showTargetPathInputBox({ prompt, value });

        if (targetPath) {
            const basePath = path.dirname(sourcePath);
            return path.join(basePath, targetPath.replace(basePath, ""));
        }
    }
}


================================================
FILE: src/controller/TypeAheadController.ts
================================================
import * as path from "path";
import { type QuickPickItem, window } from "vscode";
import type { Cache } from "../lib/Cache";
import { TreeWalker } from "../lib/TreeWalker";

async function waitForIOEvents(): Promise<void> {
    return new Promise((resolve) => setImmediate(resolve));
}

const ROOT_PATH = "/";

export class TypeAheadController {
    constructor(
        private cache: Cache,
        private relativeToRoot: boolean = false
    ) {}

    public async showDialog(sourcePath: string): Promise<string> {
        const items = await this.buildQuickPickItems(sourcePath);

        const item = items.length === 1 ? items[0] : await this.showQuickPick(items);

        if (!item) {
            throw new Error();
        }

        const selection = item.label;
        this.cache.put("last", selection);

        return path.join(sourcePath, selection);
    }

    private async buildQuickPickItems(sourcePath: string): Promise<QuickPickItem[]> {
        const lastEntry: string = this.cache.get("last");
        const header = this.buildQuickPickItemsHeader(lastEntry);

        const directories = (await this.getDirectoriesAtSourcePath(sourcePath))
            .filter((directory) => directory !== lastEntry && directory !== ROOT_PATH)
            .map((directory) => this.buildQuickPickItem(directory));

        if (directories.length === 0 && header.length === 1) {
            return header;
        }

        return [...header, ...directories];
    }

    private async getDirectoriesAtSourcePath(sourcePath: string): Promise<string[]> {
        await waitForIOEvents();
        const treeWalker = new TreeWalker();
        return treeWalker.directories(sourcePath);
    }

    private buildQuickPickItemsHeader(lastEntry: string | undefined): QuickPickItem[] {
        const items = [
            this.buildQuickPickItem(ROOT_PATH, `- ${this.relativeToRoot ? "workspace root" : "current file"}`),
        ];

        if (lastEntry && lastEntry !== ROOT_PATH) {
            items.push(this.buildQuickPickItem(lastEntry, "- last selection"));
        }

        return items;
    }

    private buildQuickPickItem(label: string, description?: string | undefined): QuickPickItem {
        return { description, label };
    }

    private async showQuickPick(items: readonly QuickPickItem[]) {
        const hint = "larger projects may take a moment to load";
        const placeHolder = `First, select an existing path to create relative to (${hint})`;
        return window.showQuickPick(items, { placeHolder, ignoreFocusOut: true });
    }
}


================================================
FILE: src/controller/index.ts
================================================
export { CopyFileNameController } from "./CopyFileNameController";
export { DuplicateFileController } from "./DuplicateFileController";
export { FileController } from "./FileController";
export { MoveFileController } from "./MoveFileController";
export { NewFileController } from "./NewFileController";
export { RemoveFileController } from "./RemoveFileController";


================================================
FILE: src/extension.ts
================================================
import * as vscode from "vscode";
import {
    type Command,
    CopyFileNameCommand,
    DuplicateFileCommand,
    MoveFileCommand,
    NewFileCommand,
    NewFolderCommand,
    RemoveFileCommand,
    RenameFileCommand,
} from "./command";
import {
    CopyFileNameController,
    DuplicateFileController,
    MoveFileController,
    NewFileController,
    RemoveFileController,
} from "./controller";
import { RenameFileController } from "./controller/RenameFileController";

function handleError(err: Error) {
    if (err?.message) {
        vscode.window.showErrorMessage(err.message);
    }
    return err;
}

function register(context: vscode.ExtensionContext, command: Command, commandName: string) {
    const proxy = (...args: never[]) => command.execute(...args).catch(handleError);
    const disposable = vscode.commands.registerCommand(`fileutils.${commandName}`, proxy);

    context.subscriptions.push(disposable);
}

export function activate(context: vscode.ExtensionContext): void {
    const copyFileNameController = new CopyFileNameController(context);
    const duplicateFileController = new DuplicateFileController(context);
    const moveFileController = new MoveFileController(context);
    const newFileController = new NewFileController(context);
    const removeFileController = new RemoveFileController(context);
    const renameFileController = new RenameFileController(context);

    register(context, new CopyFileNameCommand(copyFileNameController), "copyFileName");
    register(context, new DuplicateFileCommand(duplicateFileController), "duplicateFile");
    register(context, new MoveFileCommand(moveFileController), "moveFile");
    register(context, new NewFileCommand(newFileController, { relativeToRoot: true }), "newFileAtRoot");
    register(context, new NewFileCommand(newFileController), "newFile");
    register(context, new NewFolderCommand(newFileController, { relativeToRoot: true }), "newFolderAtRoot");
    register(context, new NewFolderCommand(newFileController), "newFolder");
    register(context, new RemoveFileCommand(removeFileController), "removeFile");
    register(context, new RenameFileCommand(renameFileController), "renameFile");
}


================================================
FILE: src/lib/Cache.ts
================================================
import type * as vscode from "vscode";

export class Cache {
    private cache: { [key: string]: unknown };

    constructor(
        private storage: vscode.Memento,
        private namespace: string
    ) {
        this.cache = storage.get(this.namespace, {});
    }

    public put(key: string, value: unknown): void {
        this.cache[key] = value;
        this.storage.update(this.namespace, this.cache);
    }

    public get<T>(key: string, defaultValue?: unknown): T {
        return (key in this.cache ? this.cache[key] : defaultValue) as T;
    }
}


================================================
FILE: src/lib/TreeWalker.ts
================================================
import glob from "fast-glob";
import * as path from "path";
import { workspace } from "vscode";

interface ExtendedProcess {
    noAsar: boolean;
}

export class TreeWalker {
    public async directories(sourcePath: string): Promise<string[]> {
        try {
            this.ensureFailSafeFileLookup();
            const files = await glob("**", {
                cwd: sourcePath,
                onlyDirectories: true,
                ignore: this.getExcludePatterns(),
            });
            return files.map((file) => path.join(path.sep, file)).sort();
        } catch (err) {
            const details = (err as Error).message;
            throw new Error(`Unable to list subdirectories for directory "${sourcePath}". Details: (${details})`);
        }
    }

    private getExcludePatterns(): string[] {
        const exclude = new Set([
            ...Object.keys(workspace.getConfiguration("search.exclude")),
            ...Object.keys(workspace.getConfiguration("files.exclude")),
        ]);
        return Array.from(exclude);
    }

    private ensureFailSafeFileLookup() {
        (process as unknown as ExtendedProcess).noAsar = true;
    }
}


================================================
FILE: src/lib/config.ts
================================================
import { workspace } from "vscode";

export function getConfiguration<T>(key: string): T | undefined {
    return workspace.getConfiguration("fileutils", null).get(key);
}


================================================
FILE: test/command/CopyFileNameCommand.test.ts
================================================
import { expect } from "chai";
import { env } from "vscode";
import { CopyFileNameCommand } from "../../src/command";
import { CopyFileNameController } from "../../src/controller";
import { FileItem } from "../../src/FileItem";
import * as helper from "../helper";

describe(CopyFileNameCommand.name, () => {
    const clipboardInitialTestData = "SOME_TEXT";
    const subject = new CopyFileNameCommand(new CopyFileNameController(helper.createExtensionContext()));

    beforeEach(helper.beforeEach);

    afterEach(helper.afterEach);

    describe("as command", () => {
        afterEach(async () => {
            await env.clipboard.writeText(clipboardInitialTestData);
        });

        describe("with open text document", () => {
            beforeEach(async () => helper.openDocument(helper.editorFile1));

            afterEach(async () => helper.closeAllEditors());

            it("should put the file name to the clipboard", async () => {
                await subject.execute();
                const clipboardData = await env.clipboard.readText();
                expect(clipboardData).to.equal(new FileItem(helper.editorFile1).name);
            });
        });

        describe("without an open text document", () => {
            beforeEach(async () => {
                await helper.closeAllEditors();
                await env.clipboard.writeText(clipboardInitialTestData);
            });

            it("should ignore the command call and not change the clipboard data", async () => {
                try {
                    await subject.execute();
                    expect.fail("must fail");
                } catch (_e) {
                    const clipboardData = await env.clipboard.readText();
                    expect(clipboardData).to.equal(clipboardInitialTestData);
                }
            });
        });
    });
});


================================================
FILE: test/command/DuplicateFileCommand.test.ts
================================================
import { expect } from "chai";
import * as fs from "fs";
import * as path from "path";
import { Uri, window, workspace } from "vscode";
import { DuplicateFileCommand } from "../../src/command/DuplicateFileCommand";
import { DuplicateFileController } from "../../src/controller";
import * as helper from "../helper";

describe(DuplicateFileCommand.name, () => {
    const subject = new DuplicateFileCommand(new DuplicateFileController(helper.createExtensionContext()));

    beforeEach(async () => {
        await helper.beforeEach();
        helper.createGetConfigurationStub({ "duplicateFile.typeahead.enabled": false, "inputBox.path": "root" });
    });

    afterEach(helper.afterEach);

    describe("as command", () => {
        describe("with open text document", () => {
            beforeEach(async () => {
                await helper.openDocument(helper.editorFile1);
                helper.createShowInputBoxStub().resolves(helper.targetFile.path);
                helper.createShowQuickPickStub().resolves({ label: "/", description: "" });
            });

            afterEach(async () => {
                await helper.closeAllEditors();
            });

            helper.protocol.it("should prompt for file destination", subject, "Duplicate As");
            helper.protocol.it("should duplicate current file to destination", subject);
            helper.protocol.describe("with target file in non-existent nested directory", subject);
            helper.protocol.describe("when target destination exists", subject);
            helper.protocol.it("should open target file as active editor", subject);

            helper.protocol.describe("typeahead configuration", subject, {
                command: "duplicateFile",
                items: helper.quickPick.typeahead.items.workspace,
            });

            helper.protocol.describe("inputBox configuration", subject, {
                editorFile: helper.editorFile1,
            });
        });

        helper.protocol.describe("without an open text document", subject);
    });

    describe("as context menu", () => {
        describe("with selected file", () => {
            beforeEach(async () => helper.createShowInputBoxStub().resolves(helper.targetFile.path));

            helper.protocol.it("should prompt for file destination", subject, "Duplicate As");
            helper.protocol.it("should duplicate current file to destination", subject, helper.editorFile1);
            helper.protocol.it("should open target file as active editor", subject, helper.editorFile1);
        });

        describe("with selected directory", () => {
            const sourceDirectory = Uri.file(path.resolve(helper.tmpDir.path, "duplicate-source-dir"));
            const targetDirectory = Uri.file(path.resolve(helper.tmpDir.path, "duplicate-target-dir"));

            beforeEach(async () => {
                await workspace.fs.createDirectory(sourceDirectory);
                helper.createShowInputBoxStub().resolves(targetDirectory.path);
            });

            afterEach(async () => {
                await workspace.fs.delete(sourceDirectory, { recursive: true, useTrash: false });
                await workspace.fs.delete(targetDirectory, { recursive: true, useTrash: false });
            });

            it("should prompt for file destination", async () => {
                await subject.execute(sourceDirectory);
                const value = sourceDirectory.path;
                const valueSelection = [value.length - (value.split(path.sep).pop() as string).length, value.length];
                const prompt = "Duplicate As";
                expect(window.showInputBox).to.have.been.calledWithExactly({
                    prompt,
                    value,
                    valueSelection,
                    ignoreFocusOut: true,
                });
            });

            it("should duplicate current file to destination", async () => {
                await subject.execute(sourceDirectory);
                const message = `${targetDirectory} does not exist`;
                expect(fs.existsSync(targetDirectory.fsPath), message).to.be.true;
            });

            it("should not open target file as active editor", async () => {
                await subject.execute(sourceDirectory);
                expect(window.activeTextEditor?.document?.fileName).not.to.equal(targetDirectory.path);
            });
        });
    });
});


================================================
FILE: test/command/MoveFileCommand.test.ts
================================================
import { MoveFileCommand } from "../../src/command";
import { MoveFileController } from "../../src/controller";
import * as helper from "../helper";

describe(MoveFileCommand.name, () => {
    const subject = new MoveFileCommand(new MoveFileController(helper.createExtensionContext()));

    beforeEach(async () => {
        await helper.beforeEach();
        helper.createGetConfigurationStub({ "moveFile.typeahead.enabled": false, "inputBox.path": "root" });
    });

    afterEach(helper.afterEach);

    describe("as command", () => {
        describe("with open text document", () => {
            beforeEach(async () => {
                await helper.openDocument(helper.editorFile1);
                helper.createShowInputBoxStub().resolves(helper.targetFile.path);
                helper.createShowQuickPickStub().resolves({ label: "/", description: "" });
            });

            afterEach(async () => {
                await helper.closeAllEditors();
            });

            helper.protocol.it("should prompt for file destination", subject, "New Location");
            helper.protocol.it("should move current file to destination", subject);
            helper.protocol.describe("with target file in non-existent nested directory", subject);

            helper.protocol.describe("typeahead configuration", subject, {
                command: "moveFile",
                items: helper.quickPick.typeahead.items.workspace,
            });

            helper.protocol.describe("inputBox configuration", subject, {
                editorFile: helper.editorFile1,
            });
        });

        helper.protocol.describe("without an open text document", subject);
    });

    describe("as context menu", () => {
        beforeEach(async () => helper.createShowInputBoxStub().resolves(helper.targetFile.path));

        helper.protocol.it("should prompt for file destination", subject, "New Location");
        helper.protocol.it("should move current file to destination", subject, helper.editorFile1);
    });
});


================================================
FILE: test/command/NewFileCommand.test.ts
================================================
import { expect } from "chai";
import * as fs from "fs";
import * as path from "path";
import { Uri, window, workspace } from "vscode";
import { NewFileCommand } from "../../src/command";
import { NewFileController } from "../../src/controller";
import * as helper from "../helper";

describe(NewFileCommand.name, () => {
    beforeEach(async () => {
        await helper.beforeEach();
        helper.createGetConfigurationStub({ "newFile.typeahead.enabled": false, "inputBox.path": "root" });
    });

    afterEach(helper.afterEach);

    describe('when "relativeToRoot" is "false"', async () => {
        const subject = new NewFileCommand(new NewFileController(helper.createExtensionContext()));

        beforeEach(async () => {
            await helper.openDocument(helper.editorFile1);
            helper.createShowInputBoxStub().resolves(path.basename(helper.targetFile.path));
            helper.createShowQuickPickStub().resolves({ label: "/", description: "" });
        });

        afterEach(async () => {
            await helper.closeAllEditors();
        });

        it("should prompt for file destination", async () => {
            await subject.execute();
            const prompt = "File Name";
            const value = path.join(path.dirname(helper.editorFile1.path), path.sep);
            const valueSelection = [value.length, value.length];
            expect(window.showInputBox).to.have.been.calledWithExactly({
                prompt,
                value,
                valueSelection,
                ignoreFocusOut: true,
            });
        });

        helper.protocol.describe("typeahead configuration", subject, {
            command: "newFile",
            items: helper.quickPick.typeahead.items.currentFile,
        });

        helper.protocol.describe("inputBox configuration", subject, {
            editorFile: helper.editorFile1,
            expectedPath: "",
        });

        it("should create the file at destination", async () => {
            await subject.execute();
            const message = `${helper.targetFile.path} does not exist`;
            expect(fs.existsSync(helper.targetFile.fsPath), message).to.be.true;
        });

        describe("file path ends with path separator", () => {
            beforeEach(async () => {
                const fileName = path.basename(helper.targetFile.fsPath) + path.sep;
                helper.createShowInputBoxStub().resolves(fileName);
            });

            it("should create the directory at destination", async () => {
                await subject.execute();
                const message = `${helper.targetFile.path} must be a directory`;
                expect(fs.statSync(helper.targetFile.fsPath).isDirectory(), message).to.be.true;
            });
        });

        describe("file path contains dot and backslash path separator", () => {
            beforeEach(async () => {
                const fileName = helper.targetFileWithDot.fsPath.replace(/\//g, "\\");
                helper.createShowInputBoxStub().resolves(fileName);
            });

            it("should create the file at destination", async () => {
                await subject.execute();
                const message = `${helper.targetFileWithDot.path} does not exist`;
                expect(fs.existsSync(helper.targetFileWithDot.fsPath), message).to.be.true;
            });
        });

        helper.protocol.describe("with target file in non-existent nested directory", subject);
        helper.protocol.describe("when target destination exists", subject, { overwriteFileContent: "" });
        helper.protocol.it("should open target file as active editor", subject);
    });

    describe('when "relativeToRoot" is "true"', () => {
        const subject = new NewFileCommand(new NewFileController(helper.createExtensionContext()), {
            relativeToRoot: true,
        });

        beforeEach(async () => {
            helper.createShowInputBoxStub().callsFake(async (options) => {
                if (options.value) {
                    return path.join(options.value, "filename.txt");
                }
            });
            helper.createShowQuickPickStub().resolves({ label: "/", description: "" });
        });

        describe("with one workspace", () => {
            beforeEach(async () => {
                helper.createWorkspaceFoldersStub(helper.workspaceFolderA);
                helper.createGetWorkspaceFolderStub();
            });

            it("should select first workspace", async () => {
                await subject.execute();
                expect(workspace.getWorkspaceFolder).to.have.not.been.called;

                const prompt = "File Name";
                const value = path.join(helper.workspacePathA, path.sep);
                const valueSelection = [value.length, value.length];
                expect(window.showInputBox).to.have.been.calledWithExactly({
                    prompt,
                    value,
                    valueSelection,
                    ignoreFocusOut: true,
                });
            });

            helper.protocol.describe("typeahead configuration", subject, {
                command: "newFile",
                items: helper.quickPick.typeahead.items.workspace,
            });
        });

        describe("with multiple workspaces", () => {
            beforeEach(async () => {
                helper.createWorkspaceFoldersStub(helper.workspaceFolderA, helper.workspaceFolderB);
                helper.createStubObject(window, "showWorkspaceFolderPick").resolves(helper.workspaceFolderB);
            });

            afterEach(async () => {
                helper.restoreObject(window.showWorkspaceFolderPick);
            });

            it("should show workspace selector", async () => {
                await subject.execute();
                expect(window.showWorkspaceFolderPick).to.have.been.called;

                const prompt = "File Name";
                const value = path.join(helper.workspaceFolderB.uri.fsPath, path.sep);
                const valueSelection = [value.length, value.length];
                expect(window.showInputBox).to.have.been.calledWithExactly({
                    prompt,
                    value,
                    valueSelection,
                    ignoreFocusOut: true,
                });
            });

            describe("with open document", () => {
                beforeEach(async () => {
                    helper.createGetWorkspaceFolderStub().returns(helper.workspaceFolderB);
                    await helper.openDocument(helper.editorFile1);
                    await subject.execute();
                });

                afterEach(async () => {
                    await helper.closeAllEditors();
                });

                it("should show workspace selector", async () => {
                    expect(window.showWorkspaceFolderPick).to.have.been.called;
                });

                it("should select workspace for open file", async () => {
                    expect(workspace.getWorkspaceFolder).to.have.been.calledWith(Uri.file(helper.editorFile1.fsPath));
                });
            });

            helper.protocol.describe("typeahead configuration", subject, {
                command: "newFile",
                items: helper.quickPick.typeahead.items.workspace,
            });
        });
    });
});


================================================
FILE: test/command/RemoveFileCommand.test.ts
================================================
import { expect } from "chai";
import * as fs from "fs";
import * as path from "path";
import { window } from "vscode";
import { RemoveFileCommand } from "../../src/command";
import { RemoveFileController } from "../../src/controller";
import * as helper from "../helper";

describe(RemoveFileCommand.name, () => {
    const subject = new RemoveFileCommand(new RemoveFileController(helper.createExtensionContext()));

    beforeEach(helper.beforeEach);

    afterEach(helper.afterEach);

    describe("as command", () => {
        describe("with open text document", () => {
            beforeEach(async () => {
                await helper.openDocument(helper.editorFile1);
                helper.createShowInformationMessageStub().resolves(helper.targetFile.path);
                helper.createGetConfigurationStub({});
            });

            afterEach(async () => {
                await helper.closeAllEditors();
            });

            describe("configuration", () => {
                describe('when "explorer.confirmDelete" is "true"', () => {
                    beforeEach(async () => {
                        helper.createGetConfigurationStub({ confirmDelete: true });
                    });

                    it("should show a confirmation dialog", async () => {
                        await subject.execute();
                        const message = `Are you sure you want to delete '${path.basename(helper.editorFile1.path)}'?`;
                        const action = "Move to Trash";
                        const options = { modal: true };
                        expect(window.showInformationMessage).to.have.been.calledWith(message, options, action);
                    });
                });

                describe('when "explorer.confirmDelete" is "false"', () => {
                    beforeEach(async () => {
                        helper.createGetConfigurationStub({ confirmDelete: false });
                    });

                    it("should delete the file without confirmation", async () => {
                        await subject.execute();
                        const message = `${helper.editorFile1.path} does not exist`;
                        expect(window.showInformationMessage).to.have.not.been.called;
                        expect(fs.existsSync(helper.editorFile1.fsPath), message).to.be.false;
                    });
                });
            });

            describe('when answered with "Move to Trash"', () => {
                it("should delete the file", async () => {
                    await subject.execute();
                    const message = `${helper.editorFile1.path} does exist`;
                    expect(fs.existsSync(helper.editorFile1.fsPath), message).to.be.false;
                });
            });

            describe('when answered with "Cancel"', () => {
                beforeEach(async () => {
                    helper.createGetConfigurationStub({ confirmDelete: true });
                    helper.createShowInformationMessageStub().resolves(false);
                });

                it("should leave the file untouched", async () => {
                    try {
                        await subject.execute();
                        expect.fail("Must fail");
                    } catch (_e) {
                        const message = `${helper.editorFile1.path} does not exist`;
                        expect(fs.existsSync(helper.editorFile1.fsPath), message).to.be.true;
                    }
                });
            });

            describe("prefer uri over current editor", () => {
                beforeEach(async () => {
                    helper.createGetConfigurationStub({ confirmDelete: false });
                });

                it("should delete the file without confirmation", async () => {
                    await subject.execute(helper.editorFile2);
                    const message = `${helper.editorFile2.path} does not exist`;
                    expect(fs.existsSync(helper.editorFile2.fsPath), message).to.be.false;
                });
            });
        });

        describe("without an open text document", () => {
            beforeEach(async () => {
                await helper.closeAllEditors();
                helper.createShowInformationMessageStub();
            });

            it("should ignore the command call", async () => {
                try {
                    await subject.execute();
                    expect.fail("Must fail");
                } catch {
                    expect(window.showInformationMessage).to.have.not.been.called;
                }
            });
        });
    });
});


================================================
FILE: test/command/RenameFileCommand.test.ts
================================================
import { expect } from "chai";
import * as path from "path";
import { Uri, window } from "vscode";
import { RenameFileCommand } from "../../src/command";
import { RenameFileController } from "../../src/controller/RenameFileController";
import * as helper from "../helper";

describe(RenameFileCommand.name, () => {
    const subject = new RenameFileCommand(new RenameFileController(helper.createExtensionContext()));

    beforeEach(async () => {
        await helper.beforeEach();
    });

    afterEach(helper.afterEach);

    describe("as command", () => {
        describe("with open text document", () => {
            beforeEach(async () => {
                await helper.openDocument(helper.editorFile1);
                helper.createShowInputBoxStub().resolves(helper.targetFile.path);
            });

            afterEach(async () => {
                await helper.closeAllEditors();
            });

            it("should prompt for file destination", async () => {
                await subject.execute();
                const prompt = "New Name";
                const value = path.basename(helper.editorFile1.fsPath);
                const valueSelection = [value.length - 9, value.length - 3];
                expect(window.showInputBox).to.have.been.calledWithExactly({
                    prompt,
                    value,
                    valueSelection,
                    ignoreFocusOut: true,
                });
            });

            helper.protocol.it("should move current file to destination", subject);
            helper.protocol.describe("with target file in non-existent nested directory", subject);
            helper.protocol.it("should open target file as active editor", subject);

            describe("prefer uri over current editor", () => {
                beforeEach(async () => {
                    const targetFile = Uri.file(path.resolve(`${helper.editorFile2.fsPath}.tmp`));
                    helper.createShowInputBoxStub().resolves(targetFile.path);
                });

                it("should prompt for file destination", async () => {
                    await subject.execute(helper.editorFile2);
                    const prompt = "New Name";
                    const value = path.basename(helper.editorFile2.fsPath);
                    const valueSelection = [value.length - 9, value.length - 3];
                    expect(window.showInputBox).to.have.been.calledWithExactly({
                        prompt,
                        value,
                        valueSelection,
                        ignoreFocusOut: true,
                    });
                });
            });
        });

        describe("without an open text document", () => {
            beforeEach(async () => {
                await helper.closeAllEditors();
                helper.createShowInputBoxStub();
            });

            it("should ignore the command call", async () => {
                try {
                    await subject.execute();
                    expect.fail("Must fail");
                } catch {
                    expect(window.showInputBox).to.have.not.been.called;
                }
            });
        });
    });
});


================================================
FILE: test/fixtures/file-1.rb
================================================
class FileOne; end

================================================
FILE: test/fixtures/file-2.rb
================================================
class FileTwo; end

================================================
FILE: test/helper/callbacks.ts
================================================
import { existsSync } from "fs";
import path from "path";
import { Uri, workspace } from "vscode";
import {
    editorFile1,
    editorFile2,
    fixtureFile1,
    fixtureFile2,
    tmpDir,
    workspaceFolderA,
    workspaceFolderB,
} from "./environment";
import {
    restoreExecuteCommand,
    restoreGetConfiguration,
    restoreGetWorkspaceFolder,
    restoreShowInformationMessage,
    restoreShowInputBox,
    restoreShowQuickPick,
    restoreShowWorkspaceFolderPick,
    restoreWorkspaceFolders,
} from "./stubs";

export async function beforeEach(): Promise<void> {
    if (existsSync(tmpDir.fsPath)) {
        await workspace.fs.delete(tmpDir, { recursive: true, useTrash: false });
    }
    await workspace.fs.copy(fixtureFile1, editorFile1, { overwrite: true });
    await workspace.fs.copy(fixtureFile2, editorFile2, { overwrite: true });

    await workspace.fs.createDirectory(Uri.file(path.resolve(tmpDir.fsPath, "dir-1")));
    await workspace.fs.createDirectory(Uri.file(path.resolve(tmpDir.fsPath, "dir-2")));

    await workspace.fs.createDirectory(workspaceFolderA.uri);
    await workspace.fs.createDirectory(workspaceFolderB.uri);

    await workspace.fs.createDirectory(Uri.file(path.resolve(workspaceFolderA.uri.fsPath, "dir-1")));
    await workspace.fs.createDirectory(Uri.file(path.resolve(workspaceFolderA.uri.fsPath, "dir-2")));

    await workspace.fs.createDirectory(Uri.file(path.resolve(workspaceFolderB.uri.fsPath, "dir-1")));
    await workspace.fs.createDirectory(Uri.file(path.resolve(workspaceFolderB.uri.fsPath, "dir-2")));
}

export async function afterEach(): Promise<void> {
    if (existsSync(tmpDir.fsPath)) {
        await workspace.fs.delete(tmpDir, { recursive: true, useTrash: false });
    }
    restoreExecuteCommand();
    restoreGetConfiguration();
    restoreGetWorkspaceFolder();
    restoreShowInformationMessage();
    restoreShowInputBox();
    restoreShowQuickPick();
    restoreShowWorkspaceFolderPick();
    restoreWorkspaceFolders();
}


================================================
FILE: test/helper/environment.ts
================================================
import * as os from "os";
import * as path from "path";
import { Uri, type WorkspaceFolder } from "vscode";

export const rootDir = path.resolve(__dirname, "..", "..", "..");
export const tmpDir = Uri.file(path.resolve(os.tmpdir(), "vscode-fileutils-test"));

export const fixtureFile1 = Uri.file(path.resolve(rootDir, "test", "fixtures", "file-1.rb"));
export const fixtureFile2 = Uri.file(path.resolve(rootDir, "test", "fixtures", "file-2.rb"));

export const editorFile1 = Uri.file(path.resolve(tmpDir.fsPath, "file-1.rb"));
export const editorFile2 = Uri.file(path.resolve(tmpDir.fsPath, "file-2.rb"));

export const targetFile = Uri.file(path.resolve(`${editorFile1.fsPath}.tmp`));
export const targetFileWithDot = Uri.file(path.resolve(tmpDir.fsPath, ".eslintrc.json"));

export const workspacePathA = path.join(tmpDir.fsPath, "workspaceA");
export const workspacePathB = path.join(tmpDir.fsPath, "workspaceB");

export const workspaceFolderA: WorkspaceFolder = { uri: Uri.file(workspacePathA), name: "a", index: 0 };
export const workspaceFolderB: WorkspaceFolder = { uri: Uri.file(workspacePathB), name: "b", index: 1 };


================================================
FILE: test/helper/functions.ts
================================================
import retry from "bluebird-retry";
import { TextDecoder } from "util";
import { commands, type ExtensionContext, type Uri, window, workspace } from "vscode";

const textDecoder = new TextDecoder("utf-8");

export async function readFile(file: Uri): Promise<string> {
    return textDecoder.decode(await workspace.fs.readFile(file));
}

export function createExtensionContext(): ExtensionContext {
    const context = {
        globalState: {
            get() {
                return {};
            },
            async update(): Promise<void> {
                return;
            },
        },
    };
    return context as unknown as ExtensionContext;
}

export async function openDocument(document: Uri): Promise<void> {
    const tryOpenDocument = async () => {
        const textDocument = await workspace.openTextDocument(document);
        await window.showTextDocument(textDocument);
    };
    await retry(() => tryOpenDocument(), { max_tries: 4, interval: 500 });
}

export async function closeAllEditors(): Promise<void> {
    await commands.executeCommand("workbench.action.closeAllEditors");
}


================================================
FILE: test/helper/index.ts
================================================
import { use } from "chai";
import * as mocha from "mocha";
import sinonChai from "sinon-chai";
import type { Command } from "../../src/command";
import { steps } from "./steps";
import type { Rest } from "./steps/types";

export * from "./callbacks";
export * from "./environment";
export * from "./functions";
export * from "./stubs";

use(sinonChai);

export const protocol = {
    describe(name: string, subject: Command, ...rest: Rest): mocha.Suite {
        const step = steps.describe[name](subject, ...rest);
        return mocha.describe(name, step);
    },
    it(name: string, subject: Command, ...rest: Rest): mocha.Test {
        const step = steps.it[name](subject, ...rest);
        return mocha.it(name, step);
    },
};

export const quickPick = {
    typeahead: {
        items: {
            workspace: [
                { description: "- workspace root", label: "/" },
                { description: undefined, label: "/dir-1" },
                { description: undefined, label: "/dir-2" },
            ],
            currentFile: [
                { description: "- current file", label: "/" },
                { description: undefined, label: "/dir-1" },
                { description: undefined, label: "/dir-2" },
                { description: undefined, label: "/workspaceA" },
                { description: undefined, label: "/workspaceA/dir-1" },
                { description: undefined, label: "/workspaceA/dir-2" },
                { description: undefined, label: "/workspaceB" },
                { description: undefined, label: "/workspaceB/dir-1" },
                { description: undefined, label: "/workspaceB/dir-2" },
            ],
        },
        options: {
            placeHolder:
                "First, select an existing path to create relative to (larger projects may take a moment to load)",
            ignoreFocusOut: true,
        },
    },
};


================================================
FILE: test/helper/steps/describe.ts
================================================
import { expect } from "chai";
import * as mocha from "mocha";
import * as path from "path";
import sinon from "sinon";
import { type QuickPickItem, Uri, window, workspace } from "vscode";
import type { Command } from "../../../src/command";
import { quickPick } from "..";
import { editorFile2, targetFile, tmpDir, workspaceFolderA } from "../environment";
import { closeAllEditors, readFile } from "../functions";
import {
    createGetConfigurationStub,
    createGetWorkspaceFolderStub,
    createShowInformationMessageStub,
    createShowInputBoxStub,
    createWorkspaceFoldersStub,
} from "../stubs";
import type { FuncVoid, Step } from "./types";

export const describe: Step = {
    "with target file in non-existent nested directory"(subject: Command): FuncVoid {
        return () => {
            const targetDir = path.resolve(tmpDir.fsPath, "level-1", "level-2", "level-3");

            mocha.beforeEach(async () => createShowInputBoxStub().resolves(path.resolve(targetDir, "file.rb")));

            mocha.it("should create nested directories", async () => {
                await subject.execute();
                const textEditor = window.activeTextEditor;
                expect(textEditor);

                const dirname = path.dirname(textEditor?.document.fileName ?? "");
                const directories: string[] = dirname.split(path.sep);

                expect(directories.pop()).to.equal("level-3");
                expect(directories.pop()).to.equal("level-2");
                expect(directories.pop()).to.equal("level-1");
            });
        };
    },
    "when target destination exists"(subject: Command, config?: Record<string, unknown>): FuncVoid {
        return () => {
            mocha.beforeEach(async () => {
                await workspace.fs.copy(editorFile2, targetFile, { overwrite: true });
                createShowInformationMessageStub().resolves({ title: "placeholder" });
            });

            mocha.it("should prompt with confirmation dialog to overwrite destination file", async () => {
                await subject.execute();
                const message = `File '${targetFile.path}' already exists.`;
                const action = "Overwrite";
                const options = { modal: true };
                expect(window.showInformationMessage).to.have.been.calledWith(message, options, action);
            });

            mocha.describe('when answered with "Overwrite"', () => {
                mocha.it("should overwrite the existig file", async () => {
                    await subject.execute();
                    const fileContent = await readFile(targetFile);
                    const expectedFileContent =
                        config && "overwriteFileContent" in config ? config.overwriteFileContent : "class FileOne; end";
                    expect(fileContent).to.equal(expectedFileContent);
                });
            });

            mocha.describe('when answered with "Cancel"', () => {
                mocha.beforeEach(async () => createShowInformationMessageStub().resolves(false));

                mocha.it("should leave existing file untouched", async () => {
                    try {
                        await subject.execute();
                        expect.fail("must fail");
                    } catch (_e) {
                        const fileContent = await readFile(targetFile);
                        expect(fileContent).to.equal("class FileTwo; end");
                    }
                });
            });
        };
    },
    "without an open text document"(subject: Command): FuncVoid {
        return () => {
            mocha.beforeEach(async () => {
                await closeAllEditors();
                createShowInputBoxStub();
            });

            mocha.it("should ignore the command call", async () => {
                try {
                    await subject.execute();
                    expect.fail("must fail");
                } catch {
                    expect(window.showInputBox).to.have.not.been.called;
                }
            });
        };
    },
    "typeahead configuration"(subject: Command, options: { command: string; items: QuickPickItem[] }): FuncVoid {
        const { command, items } = options;
        return () => {
            mocha.describe(`when "${command}.typeahead.enabled" is "true"`, () => {
                mocha.beforeEach(async () => {
                    createGetConfigurationStub({ [`${command}.typeahead.enabled`]: true });
                    createWorkspaceFoldersStub(workspaceFolderA);
                });

                mocha.it("should show the quick pick dialog", async () => {
                    await subject.execute();
                    expect(window.showQuickPick).to.have.been.calledOnceWith(
                        sinon.match(items),
                        sinon.match(quickPick.typeahead.options)
                    );
                });
            });

            mocha.describe(`when "${command}.typeahead.enabled" is "false"`, () => {
                mocha.beforeEach(async () => {
                    createGetConfigurationStub({ [`${command}.typeahead.enabled`]: false });
                });

                mocha.it("should not show the quick pick dialog", async () => {
                    await subject.execute();
                    expect(window.showQuickPick).to.have.not.been.called;
                });
            });
        };
    },
    "inputBox configuration"(subject: Command, options: { editorFile: Uri; expectedPath?: string }): FuncVoid {
        const { editorFile, expectedPath } = options;
        const runs = [
            { pathType: "workspace", pathTypeIndicator: "@" },
            { pathType: "workspace", pathTypeIndicator: "" },
            { pathType: "workspace", pathTypeIndicator: ":" },
            { pathType: "workspace", pathTypeIndicator: " " },
        ];

        return () => {
            runs.forEach(({ pathType, pathTypeIndicator }) => {
                mocha.describe(
                    `when "inputBox.pathType" is "${pathType}" and "inputBox.pathTypeIndicator" is "${pathTypeIndicator}"`,
                    () => {
                        mocha.beforeEach(async () => {
                            createGetConfigurationStub({
                                "inputBox.pathType": pathType,
                                "inputBox.pathTypeIndicator": pathTypeIndicator,
                            });

                            const workspaceFolder = path.dirname(editorFile.path);
                            createWorkspaceFoldersStub({
                                uri: Uri.file(workspaceFolder),
                                name: "workspace-a",
                                index: 0,
                            });
                            createGetWorkspaceFolderStub().returns(workspaceFolder);
                        });

                        mocha.it("should show the quick pick dialog", async () => {
                            await subject.execute();

                            const expectedValue = `${pathTypeIndicator}/${
                                expectedPath ?? path.basename(editorFile.path)
                            }`;

                            expect(window.showInputBox).to.have.been.calledOnceWith({
                                prompt: sinon.match.string,
                                value: sinon.match(expectedValue),
                                valueSelection: sinon.match.array,
                                ignoreFocusOut: true,
                            });
                        });
                    }
                );
            });
        };
    },
};


================================================
FILE: test/helper/steps/index.ts
================================================
import { describe } from "./describe";
import { it } from "./it";
import type { Step } from "./types";

export const steps: Record<string, Step> = {
    describe,
    it,
};


================================================
FILE: test/helper/steps/it.ts
================================================
import { expect } from "chai";
import * as fs from "fs";
import { type Uri, window } from "vscode";
import type { Command } from "../../../src/command";
import { editorFile1, targetFile } from "../environment";
import type { FuncVoid, Step } from "./types";

export const it: Step = {
    "should open target file as active editor"(subject: Command, uri?: Uri): FuncVoid {
        return async () => {
            await subject.execute(uri);
            expect(window.activeTextEditor?.document.fileName).to.equal(targetFile.path);
        };
    },
    "should move current file to destination"(subject: Command, uri?: Uri): FuncVoid {
        return async () => {
            await subject.execute(uri);
            const message = `${targetFile} does not exist`;
            expect(fs.existsSync(targetFile.fsPath), message).to.be.true;
        };
    },
    "should prompt for file destination"(subject: Command, prompt: string): FuncVoid {
        return async () => {
            await subject.execute(editorFile1);
            const value = editorFile1.path;
            const valueSelection = [value.length - 9, value.length - 3];
            expect(window.showInputBox).to.have.been.calledWithExactly({
                prompt,
                value,
                valueSelection,
                ignoreFocusOut: true,
            });
        };
    },
};

it["should duplicate current file to destination"] = it["should move current file to destination"];


================================================
FILE: test/helper/steps/types.ts
================================================
import type { Command } from "../../../src/command";

export type FuncVoid = () => void;
// biome-ignore lint/suspicious/noExplicitAny: Test framework needs flexible parameter types
export type Rest = any;
export interface Step {
    [key: string]: (subject: Command, ...rest: Rest[]) => FuncVoid;
}


================================================
FILE: test/helper/stubs.ts
================================================
import * as sinon from "sinon";
import { commands, type WorkspaceFolder, window, workspace } from "vscode";

export function createGetWorkspaceFolderStub(): sinon.SinonStub {
    return createStubObject(workspace, "getWorkspaceFolder");
}

export function restoreGetWorkspaceFolder(): void {
    restoreObject(workspace.getWorkspaceFolder);
}

export function createWorkspaceFoldersStub(...workspaceFolders: WorkspaceFolder[]): sinon.SinonStub {
    return createStubObject(workspace, "workspaceFolders").get(() => workspaceFolders);
}

export function restoreWorkspaceFolders(): void {
    restoreObject(workspace.workspaceFolders);
}

export function createExecuteCommandStub(): sinon.SinonStub {
    return createStubObject(commands, "executeCommand");
}

export function restoreExecuteCommand(): void {
    restoreObject(commands.executeCommand);
}

export function createGetConfigurationStub(keys: Record<string, string | boolean>): sinon.SinonStub {
    const config = { get: (key: string) => keys[key] };
    return createStubObject(workspace, "getConfiguration").returns(config);
}

export function restoreGetConfiguration(): void {
    restoreObject(workspace.getConfiguration);
}

export function createShowInputBoxStub(): sinon.SinonStub {
    return createStubObject(window, "showInputBox");
}

export function restoreShowInputBox(): void {
    restoreObject(window.showInputBox);
}

export function createShowQuickPickStub(): sinon.SinonStub {
    return createStubObject(window, "showQuickPick");
}

export function restoreShowQuickPick(): void {
    restoreObject(window.showQuickPick);
}

export function createShowWorkspaceFolderPickStub(): sinon.SinonStub {
    return createStubObject(window, "showWorkspaceFolderPick");
}

export function restoreShowWorkspaceFolderPick(): void {
    restoreObject(window.showWorkspaceFolderPick);
}

export function createShowInformationMessageStub(): sinon.SinonStub {
    return createStubObject(window, "showInformationMessage");
}

export function restoreShowInformationMessage(): void {
    restoreObject(window.showInformationMessage);
}

// biome-ignore lint/suspicious/noExplicitAny: Handler needs to work with various VS Code API objects
type Handler = any;

export function createStubObject(handler: Handler, functionName: string): sinon.SinonStub {
    const target: sinon.SinonStub | undefined = handler[functionName];
    const stub: sinon.SinonStub = target && "restore" in target ? target : sinon.stub(handler, functionName);

    return stub;
}

export function restoreObject(object: unknown): void {
    const stub = object as sinon.SinonStub;
    if (stub?.restore) {
        stub.restore();
    }
}


================================================
FILE: test/index.ts
================================================
import glob from "fast-glob";
import Mocha from "mocha";
import * as path from "path";

export async function run(): Promise<void> {
    const mocha = new Mocha({
        reporter: "list",
        ui: "bdd",
        color: true,
    });

    const testsRoot = path.resolve(__dirname, "..");
    const files = await glob("**/**.test.js", { cwd: testsRoot });

    console.log("Number of test files to run:", files.length);
    // Add files to the test suite
    files.forEach((file) => mocha.addFile(path.resolve(testsRoot, file)));

    // Run the mocha test
    return new Promise((resolve, reject) => {
        try {
            mocha.run((failures: number) => {
                if (failures > 0) {
                    reject(new Error(`${failures} tests failed.`));
                } else {
                    resolve();
                }
            });
        } catch (err) {
            reject(err);
        }
    });
}


================================================
FILE: test/runTest.ts
================================================
import { runTests } from "@vscode/test-electron";
import * as path from "path";

async function main() {
    try {
        // The folder containing the Extension Manifest package.json
        // Passed to `--extensionDevelopmentPath`
        const extensionDevelopmentPath = path.resolve(__dirname, "../../");

        // The path to test runner
        // Passed to --extensionTestsPath
        const extensionTestsPath = path.resolve(__dirname, "./index");

        // Download VS Code, unzip it and run the integration test
        await runTests({
            extensionDevelopmentPath,
            extensionTestsPath,
            launchArgs: ["--disable-extensions"],
        });
    } catch (_err) {
        // tslint:disable-next-line: no-console
        console.error("Failed to run tests");
        process.exit(1);
    }
}

main();


================================================
FILE: tsconfig.json
================================================
{
    "extends": "@tsconfig/node22/tsconfig.json",
    "compilerOptions": {
        "rootDir": ".",
        "outDir": "out",

        "sourceMap": true,

        "removeComments": true,
        "resolveJsonModule": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowSyntheticDefaultImports": true
    },
    "exclude": [
        "node_modules",
        ".vscode-test"
    ]
}
Download .txt
gitextract_xq5fqy4z/

├── .biomeignore
├── .claude/
│   └── settings.local.json
├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .editorconfig
├── .eslintignore
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── main.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .node-version
├── .releaserc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── biome.json
├── main.ts
├── package.json
├── renovate.json
├── scripts/
│   └── dev-env
├── src/
│   ├── FileItem.ts
│   ├── command/
│   │   ├── BaseCommand.ts
│   │   ├── Command.ts
│   │   ├── CopyFileNameCommand.ts
│   │   ├── DuplicateFileCommand.ts
│   │   ├── MoveFileCommand.ts
│   │   ├── NewFileCommand.ts
│   │   ├── NewFolderCommand.ts
│   │   ├── RemoveFileCommand.ts
│   │   ├── RenameFileCommand.ts
│   │   └── index.ts
│   ├── controller/
│   │   ├── BaseFileController.ts
│   │   ├── CopyFileNameController.ts
│   │   ├── DuplicateFileController.ts
│   │   ├── FileController.ts
│   │   ├── MoveFileController.ts
│   │   ├── NewFileController.ts
│   │   ├── RemoveFileController.ts
│   │   ├── RenameFileController.ts
│   │   ├── TypeAheadController.ts
│   │   └── index.ts
│   ├── extension.ts
│   └── lib/
│       ├── Cache.ts
│       ├── TreeWalker.ts
│       └── config.ts
├── test/
│   ├── command/
│   │   ├── CopyFileNameCommand.test.ts
│   │   ├── DuplicateFileCommand.test.ts
│   │   ├── MoveFileCommand.test.ts
│   │   ├── NewFileCommand.test.ts
│   │   ├── RemoveFileCommand.test.ts
│   │   └── RenameFileCommand.test.ts
│   ├── fixtures/
│   │   ├── file-1.rb
│   │   └── file-2.rb
│   ├── helper/
│   │   ├── callbacks.ts
│   │   ├── environment.ts
│   │   ├── functions.ts
│   │   ├── index.ts
│   │   ├── steps/
│   │   │   ├── describe.ts
│   │   │   ├── index.ts
│   │   │   ├── it.ts
│   │   │   └── types.ts
│   │   └── stubs.ts
│   ├── index.ts
│   └── runTest.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (149 symbols across 34 files)

FILE: src/FileItem.ts
  function assertTargetPath (line 5) | function assertTargetPath(targetPath: Uri | undefined): asserts targetPa...
  class FileItem (line 11) | class FileItem {
    method constructor (line 15) | constructor(
    method name (line 26) | get name(): string {
    method path (line 30) | get path(): Uri {
    method targetPath (line 34) | get targetPath(): Uri | undefined {
    method exists (line 38) | get exists(): boolean {
    method isDir (line 45) | get isDir(): boolean {
    method move (line 49) | public async move(): Promise<FileItem> {
    method duplicate (line 64) | public async duplicate(): Promise<FileItem> {
    method remove (line 75) | public async remove(): Promise<FileItem> {
    method create (line 87) | public async create(mkDir?: boolean): Promise<FileItem> {
    method toUri (line 103) | private toUri(uriOrString: Uri | string): Uri {

FILE: src/command/BaseCommand.ts
  type ExecuteControllerOptions (line 6) | interface ExecuteControllerOptions {
  method constructor (line 11) | constructor(
  method executeController (line 18) | protected async executeController(

FILE: src/command/Command.ts
  type CommandConstructorOptions (line 3) | interface CommandConstructorOptions {
  type Command (line 7) | interface Command {

FILE: src/command/CopyFileNameCommand.ts
  class CopyFileNameCommand (line 5) | class CopyFileNameCommand extends BaseCommand<CopyFileNameController> {
    method execute (line 6) | public async execute(uri?: Uri): Promise<void> {

FILE: src/command/DuplicateFileCommand.ts
  class DuplicateFileCommand (line 6) | class DuplicateFileCommand extends BaseCommand<MoveFileController> {
    method execute (line 7) | public async execute(uri?: Uri): Promise<void> {

FILE: src/command/MoveFileCommand.ts
  class MoveFileCommand (line 6) | class MoveFileCommand extends BaseCommand<MoveFileController> {
    method execute (line 7) | public async execute(uri?: Uri): Promise<void> {

FILE: src/command/NewFileCommand.ts
  class NewFileCommand (line 5) | class NewFileCommand extends BaseCommand<NewFileController> {
    method execute (line 6) | public async execute(): Promise<void> {
    method typeahead (line 21) | protected get typeahead(): boolean {

FILE: src/command/NewFolderCommand.ts
  class NewFolderCommand (line 4) | class NewFolderCommand extends NewFileCommand {
    method execute (line 5) | public async execute(): Promise<void> {
    method typeahead (line 19) | protected get typeahead(): boolean {

FILE: src/command/RemoveFileCommand.ts
  class RemoveFileCommand (line 5) | class RemoveFileCommand extends BaseCommand<RemoveFileController> {
    method execute (line 6) | public async execute(uri?: Uri): Promise<void> {

FILE: src/command/RenameFileCommand.ts
  class RenameFileCommand (line 5) | class RenameFileCommand extends BaseCommand<RenameFileController> {
    method execute (line 6) | public async execute(uri?: Uri): Promise<void> {

FILE: src/controller/BaseFileController.ts
  type InputBoxPathType (line 19) | enum InputBoxPathType {
  type TargetPathInputBoxOptions (line 24) | type TargetPathInputBoxOptions = InputBoxOptions & Required<Pick<InputBo...
  type TargetPathInputBoxValueOptions (line 26) | interface TargetPathInputBoxValueOptions extends DialogOptions {
  method constructor (line 32) | constructor(protected context: ExtensionContext) {}
  method pathTypeIndicator (line 38) | private get pathTypeIndicator(): string {
  method openFileInEditor (line 42) | public async openFileInEditor(fileItem: FileItem): Promise<TextEditor | ...
  method closeCurrentFileEditor (line 60) | public async closeCurrentFileEditor(): Promise<unknown> {
  method getTargetPath (line 64) | protected async getTargetPath(sourcePath: string, options: DialogOptions...
  method showTargetPathInputBox (line 92) | protected async showTargetPathInputBox(options: TargetPathInputBoxOption...
  method getInputBoxPathType (line 104) | private getInputBoxPathType(): InputBoxPathType {
  method getTargetPathInputBoxValue (line 113) | protected async getTargetPathInputBoxValue(
  method getFilenameSelection (line 125) | protected getFilenameSelection(value: string): [number, number] {
  method getSourcePath (line 129) | public async getSourcePath({ ignoreIfNotExists, uri }: SourcePathOptions...
  method getCache (line 152) | protected getCache(namespace: string): Cache {
  method ensureWritableFile (line 156) | protected async ensureWritableFile(fileItem: FileItem): Promise<FileItem> {
  method getSourcePathForNonTextFile (line 174) | private async getSourcePathForNonTextFile(): Promise<string> {
  method getWorkspaceFolderPath (line 198) | protected async getWorkspaceFolderPath(): Promise<string | undefined> {
  method selectWorkspaceFolder (line 203) | protected async selectWorkspaceFolder(): Promise<WorkspaceFolder | undef...
  method isMultiRootWorkspace (line 213) | protected get isMultiRootWorkspace(): boolean {
  method getFileSourcePathAtRoot (line 217) | protected async getFileSourcePathAtRoot(rootPath: string, options: Sourc...

FILE: src/controller/CopyFileNameController.ts
  class CopyFileNameController (line 6) | class CopyFileNameController extends BaseFileController {
    method showDialog (line 7) | public async showDialog(options: DialogOptions): Promise<FileItem> {
    method execute (line 17) | public async execute(options: ExecuteOptions): Promise<FileItem> {

FILE: src/controller/DuplicateFileController.ts
  class DuplicateFileController (line 5) | class DuplicateFileController extends MoveFileController {
    method execute (line 6) | public async execute(options: ExecuteOptions): Promise<FileItem> {

FILE: src/controller/FileController.ts
  type DialogOptions (line 4) | interface DialogOptions {
  type ExecuteOptions (line 10) | interface ExecuteOptions {
  type SourcePathOptions (line 14) | interface SourcePathOptions {
  type FileController (line 21) | interface FileController {

FILE: src/controller/MoveFileController.ts
  class MoveFileController (line 7) | class MoveFileController extends BaseFileController {
    method showDialog (line 8) | public async showDialog(options: DialogOptions): Promise<FileItem | un...
    method execute (line 24) | public async execute(options: ExecuteOptions): Promise<FileItem> {
    method getTargetPathInputBoxValue (line 30) | protected async getTargetPathInputBoxValue(
    method getFullTargetPathInputBoxValue (line 38) | private async getFullTargetPathInputBoxValue(
    method getFilenameSelection (line 58) | protected getFilenameSelection(value: string): [number, number] {

FILE: src/controller/NewFileController.ts
  type NewFileDialogOptions (line 8) | interface NewFileDialogOptions extends Omit<DialogOptions, "uri"> {
  type NewFileExecuteOptions (line 12) | interface NewFileExecuteOptions extends ExecuteOptions {
  class NewFileController (line 16) | class NewFileController extends BaseFileController {
    method showDialog (line 17) | public async showDialog(options: NewFileDialogOptions): Promise<FileIt...
    method execute (line 33) | public async execute(options: NewFileExecuteOptions): Promise<FileItem> {
    method getTargetPathInputBoxValue (line 43) | protected async getTargetPathInputBoxValue(
    method getNewFileSourcePath (line 51) | public async getNewFileSourcePath({ relativeToRoot, typeahead }: Sourc...
    method getRootPath (line 61) | private async getRootPath(relativeToRoot: boolean): Promise<string | u...
    method getWorkspaceFolderPath (line 68) | protected async getWorkspaceFolderPath(relativeToRoot: boolean): Promi...

FILE: src/controller/RemoveFileController.ts
  class RemoveFileController (line 7) | class RemoveFileController extends BaseFileController {
    method showDialog (line 8) | public async showDialog(options: DialogOptions): Promise<FileItem | un...
    method execute (line 28) | public async execute(options: ExecuteOptions): Promise<FileItem> {
    method confirmDelete (line 38) | private get confirmDelete(): boolean {

FILE: src/controller/RenameFileController.ts
  class RenameFileController (line 5) | class RenameFileController extends MoveFileController {
    method getTargetPath (line 6) | protected async getTargetPath(sourcePath: string, options: DialogOptio...

FILE: src/controller/TypeAheadController.ts
  function waitForIOEvents (line 6) | async function waitForIOEvents(): Promise<void> {
  constant ROOT_PATH (line 10) | const ROOT_PATH = "/";
  class TypeAheadController (line 12) | class TypeAheadController {
    method constructor (line 13) | constructor(
    method showDialog (line 18) | public async showDialog(sourcePath: string): Promise<string> {
    method buildQuickPickItems (line 33) | private async buildQuickPickItems(sourcePath: string): Promise<QuickPi...
    method getDirectoriesAtSourcePath (line 48) | private async getDirectoriesAtSourcePath(sourcePath: string): Promise<...
    method buildQuickPickItemsHeader (line 54) | private buildQuickPickItemsHeader(lastEntry: string | undefined): Quic...
    method buildQuickPickItem (line 66) | private buildQuickPickItem(label: string, description?: string | undef...
    method showQuickPick (line 70) | private async showQuickPick(items: readonly QuickPickItem[]) {

FILE: src/extension.ts
  function handleError (line 21) | function handleError(err: Error) {
  function register (line 28) | function register(context: vscode.ExtensionContext, command: Command, co...
  function activate (line 35) | function activate(context: vscode.ExtensionContext): void {

FILE: src/lib/Cache.ts
  class Cache (line 3) | class Cache {
    method constructor (line 6) | constructor(
    method put (line 13) | public put(key: string, value: unknown): void {
    method get (line 18) | public get<T>(key: string, defaultValue?: unknown): T {

FILE: src/lib/TreeWalker.ts
  type ExtendedProcess (line 5) | interface ExtendedProcess {
  class TreeWalker (line 9) | class TreeWalker {
    method directories (line 10) | public async directories(sourcePath: string): Promise<string[]> {
    method getExcludePatterns (line 25) | private getExcludePatterns(): string[] {
    method ensureFailSafeFileLookup (line 33) | private ensureFailSafeFileLookup() {

FILE: src/lib/config.ts
  function getConfiguration (line 3) | function getConfiguration<T>(key: string): T | undefined {

FILE: test/fixtures/file-1.rb
  class FileOne (line 1) | class FileOne; end

FILE: test/fixtures/file-2.rb
  class FileTwo (line 1) | class FileTwo; end

FILE: test/helper/callbacks.ts
  function beforeEach (line 24) | async function beforeEach(): Promise<void> {
  function afterEach (line 44) | async function afterEach(): Promise<void> {

FILE: test/helper/functions.ts
  function readFile (line 7) | async function readFile(file: Uri): Promise<string> {
  function createExtensionContext (line 11) | function createExtensionContext(): ExtensionContext {
  function openDocument (line 25) | async function openDocument(document: Uri): Promise<void> {
  function closeAllEditors (line 33) | async function closeAllEditors(): Promise<void> {

FILE: test/helper/index.ts
  method describe (line 16) | describe(name: string, subject: Command, ...rest: Rest): mocha.Suite {
  method it (line 20) | it(name: string, subject: Command, ...rest: Rest): mocha.Test {

FILE: test/helper/steps/describe.ts
  method "with target file in non-existent nested directory" (line 20) | "with target file in non-existent nested directory"(subject: Command): F...
  method "when target destination exists" (line 40) | "when target destination exists"(subject: Command, config?: Record<strin...
  method "without an open text document" (line 80) | "without an open text document"(subject: Command): FuncVoid {
  method "typeahead configuration" (line 97) | "typeahead configuration"(subject: Command, options: { command: string; ...
  method "inputBox configuration" (line 127) | "inputBox configuration"(subject: Command, options: { editorFile: Uri; e...

FILE: test/helper/steps/it.ts
  method "should open target file as active editor" (line 9) | "should open target file as active editor"(subject: Command, uri?: Uri):...
  method "should move current file to destination" (line 15) | "should move current file to destination"(subject: Command, uri?: Uri): ...
  method "should prompt for file destination" (line 22) | "should prompt for file destination"(subject: Command, prompt: string): ...

FILE: test/helper/steps/types.ts
  type FuncVoid (line 3) | type FuncVoid = () => void;
  type Rest (line 5) | type Rest = any;
  type Step (line 6) | interface Step {

FILE: test/helper/stubs.ts
  function createGetWorkspaceFolderStub (line 4) | function createGetWorkspaceFolderStub(): sinon.SinonStub {
  function restoreGetWorkspaceFolder (line 8) | function restoreGetWorkspaceFolder(): void {
  function createWorkspaceFoldersStub (line 12) | function createWorkspaceFoldersStub(...workspaceFolders: WorkspaceFolder...
  function restoreWorkspaceFolders (line 16) | function restoreWorkspaceFolders(): void {
  function createExecuteCommandStub (line 20) | function createExecuteCommandStub(): sinon.SinonStub {
  function restoreExecuteCommand (line 24) | function restoreExecuteCommand(): void {
  function createGetConfigurationStub (line 28) | function createGetConfigurationStub(keys: Record<string, string | boolea...
  function restoreGetConfiguration (line 33) | function restoreGetConfiguration(): void {
  function createShowInputBoxStub (line 37) | function createShowInputBoxStub(): sinon.SinonStub {
  function restoreShowInputBox (line 41) | function restoreShowInputBox(): void {
  function createShowQuickPickStub (line 45) | function createShowQuickPickStub(): sinon.SinonStub {
  function restoreShowQuickPick (line 49) | function restoreShowQuickPick(): void {
  function createShowWorkspaceFolderPickStub (line 53) | function createShowWorkspaceFolderPickStub(): sinon.SinonStub {
  function restoreShowWorkspaceFolderPick (line 57) | function restoreShowWorkspaceFolderPick(): void {
  function createShowInformationMessageStub (line 61) | function createShowInformationMessageStub(): sinon.SinonStub {
  function restoreShowInformationMessage (line 65) | function restoreShowInformationMessage(): void {
  type Handler (line 70) | type Handler = any;
  function createStubObject (line 72) | function createStubObject(handler: Handler, functionName: string): sinon...
  function restoreObject (line 79) | function restoreObject(object: unknown): void {

FILE: test/index.ts
  function run (line 5) | async function run(): Promise<void> {

FILE: test/runTest.ts
  function main (line 4) | async function main() {
Condensed preview — 73 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (135K chars).
[
  {
    "path": ".biomeignore",
    "chars": 50,
    "preview": "out/\nnode_modules/\n**/*.d.ts\n.vscode-test/\n*.vsix\n"
  },
  {
    "path": ".claude/settings.local.json",
    "chars": 215,
    "preview": "{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(npm run pretest:*)\",\n      \"Bash(grep:*)\",\n      \"Bash(npm test)\",\n     "
  },
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 1639,
    "preview": "#-------------------------------------------------------------------------------------------------------------\n# Copyrig"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 240,
    "preview": "// See https://aka.ms/vscode-remote/devcontainer.json for format details.\n{\n\t\"name\": \"Node.js 8 & TypeScript\",\n\t\"dockerF"
  },
  {
    "path": ".editorconfig",
    "chars": 232,
    "preview": "root = true\n\n[*]\nmax_line_length = 120\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\n"
  },
  {
    "path": ".eslintignore",
    "chars": 10,
    "preview": "\"**/*.js\"\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 1158,
    "preview": "# Contributing\n\nContributing is easy:\n\n* You can report bugs and request features using the [issues page][issues].\n\n[iss"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 596,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Describe the bug**\nA clear and concise descriptio"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 560,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 663,
    "preview": "# Description\n\nPlease include a summary of the change and which issue is fixed. \nPlease also include relevant motivation"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1875,
    "preview": "name: CI/CD\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n  release:\n    types:"
  },
  {
    "path": ".gitignore",
    "chars": 473,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscov"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 82,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpm run lint && npm run test\n"
  },
  {
    "path": ".node-version",
    "chars": 3,
    "preview": "22\n"
  },
  {
    "path": ".releaserc",
    "chars": 725,
    "preview": "{\n    \"branch\": \"master\",\n    \"plugins\": [\n        \"@semantic-release/commit-analyzer\",\n        \"@semantic-release/relea"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 305,
    "preview": "{\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\n    // for the documentation about the extensions.json format"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 1231,
    "preview": "// A launch configuration that compiles the extension and then opens it inside a new window\n// Use IntelliSense to learn"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 396,
    "preview": "// Place your settings in this file to overwrite default and user settings.\n{\n    \"files.exclude\": {\n        \"out\": fals"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 764,
    "preview": "{\r\n    \"version\": \"2.0.0\",\r\n    \"tasks\": [\r\n        {\r\n            \"type\": \"npm\",\r\n            \"script\": \"watch\",\r\n     "
  },
  {
    "path": ".vscodeignore",
    "chars": 83,
    "preview": "*\r\n*/**\r\n\r\n!images/icon.*\r\n!README.md\r\n!CHANGELOG.md\r\n!LICENSE\r\n!out/extension.js\r\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 15652,
    "preview": "## [3.10.3](https://github.com/sleistner/vscode-fileutils/compare/v3.10.2...v3.10.3) (2023-07-22)\n\n\n### Bug Fixes\n\n* **d"
  },
  {
    "path": "LICENSE",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Steffen Leistner\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "README.md",
    "chars": 3014,
    "preview": "# File Utils - Visual Studio Code Extension\n\n[![Known Vulnerabilities](https://snyk.io/test/github/sleistner/vscode-file"
  },
  {
    "path": "biome.json",
    "chars": 775,
    "preview": "{\n\t\"$schema\": \"https://biomejs.dev/schemas/2.1.4/schema.json\",\n\t\"vcs\": {\n\t\t\"enabled\": true,\n\t\t\"clientKind\": \"git\",\n\t\t\"us"
  },
  {
    "path": "main.ts",
    "chars": 44,
    "preview": "export { activate } from \"./src/extension\";\n"
  },
  {
    "path": "package.json",
    "chars": 14922,
    "preview": "{\n    \"name\": \"vscode-fileutils\",\n    \"displayName\": \"File Utils\",\n    \"description\": \"A convenient way of creating, dup"
  },
  {
    "path": "renovate.json",
    "chars": 240,
    "preview": "{\n    \"extends\": [\n        \"config:base\"\n    ],\n    \"ignoreDeps\": [\"@types/node\", \"@types/vscode\"],\n    \"packageRules\": "
  },
  {
    "path": "scripts/dev-env",
    "chars": 126,
    "preview": "#!/usr/bin/env bash\n\nrm -rf ./tmp\nmkdir -p ./tmp/{app,workspace,scripts}\ntouch ./tmp/{app,workspace,scripts}/{foo,bar,ba"
  },
  {
    "path": "src/FileItem.ts",
    "chars": 3008,
    "preview": "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { Uri, WorkspaceEdit, workspace } from \"vscode\";\n\nfunctio"
  },
  {
    "path": "src/command/BaseCommand.ts",
    "chars": 907,
    "preview": "import type { Uri } from \"vscode\";\nimport type { FileController } from \"../controller\";\nimport type { FileItem } from \"."
  },
  {
    "path": "src/command/Command.ts",
    "chars": 182,
    "preview": "import type { Uri } from \"vscode\";\n\nexport interface CommandConstructorOptions {\n    relativeToRoot?: boolean;\n}\n\nexport"
  },
  {
    "path": "src/command/CopyFileNameCommand.ts",
    "chars": 466,
    "preview": "import type { Uri } from \"vscode\";\nimport type { CopyFileNameController } from \"../controller/CopyFileNameController\";\ni"
  },
  {
    "path": "src/command/DuplicateFileCommand.ts",
    "chars": 668,
    "preview": "import type { Uri } from \"vscode\";\nimport type { MoveFileController } from \"../controller/MoveFileController\";\nimport { "
  },
  {
    "path": "src/command/MoveFileCommand.ts",
    "chars": 618,
    "preview": "import type { Uri } from \"vscode\";\nimport type { MoveFileController } from \"../controller/MoveFileController\";\nimport { "
  },
  {
    "path": "src/command/NewFileCommand.ts",
    "chars": 1029,
    "preview": "import type { NewFileController } from \"../controller/NewFileController\";\nimport { getConfiguration } from \"../lib/confi"
  },
  {
    "path": "src/command/NewFolderCommand.ts",
    "chars": 885,
    "preview": "import { getConfiguration } from \"../lib/config\";\nimport { NewFileCommand } from \"./NewFileCommand\";\n\nexport class NewFo"
  },
  {
    "path": "src/command/RemoveFileCommand.ts",
    "chars": 392,
    "preview": "import type { Uri } from \"vscode\";\nimport type { RemoveFileController } from \"../controller\";\nimport { BaseCommand } fro"
  },
  {
    "path": "src/command/RenameFileCommand.ts",
    "chars": 478,
    "preview": "import type { Uri } from \"vscode\";\nimport type { RenameFileController } from \"../controller/RenameFileController\";\nimpor"
  },
  {
    "path": "src/command/index.ts",
    "chars": 434,
    "preview": "export { Command } from \"./Command\";\nexport { CopyFileNameCommand } from \"./CopyFileNameCommand\";\nexport { DuplicateFile"
  },
  {
    "path": "src/controller/BaseFileController.ts",
    "chars": 8457,
    "preview": "import path from \"path\";\nimport {\n    commands,\n    type ExtensionContext,\n    env,\n    type InputBoxOptions,\n    type T"
  },
  {
    "path": "src/controller/CopyFileNameController.ts",
    "chars": 725,
    "preview": "import { env } from \"vscode\";\nimport { FileItem } from \"../FileItem\";\nimport { BaseFileController } from \"./BaseFileCont"
  },
  {
    "path": "src/controller/DuplicateFileController.ts",
    "chars": 430,
    "preview": "import type { FileItem } from \"../FileItem\";\nimport type { ExecuteOptions } from \"./FileController\";\nimport { MoveFileCo"
  },
  {
    "path": "src/controller/FileController.ts",
    "chars": 760,
    "preview": "import type { TextEditor, Uri } from \"vscode\";\nimport type { FileItem } from \"../FileItem\";\n\nexport interface DialogOpti"
  },
  {
    "path": "src/controller/MoveFileController.ts",
    "chars": 2315,
    "preview": "import * as path from \"path\";\nimport { FileType, Uri, workspace } from \"vscode\";\nimport { FileItem } from \"../FileItem\";"
  },
  {
    "path": "src/controller/NewFileController.ts",
    "chars": 2872,
    "preview": "import expand from \"brace-expansion\";\nimport * as path from \"path\";\nimport { window } from \"vscode\";\nimport { FileItem }"
  },
  {
    "path": "src/controller/RemoveFileController.ts",
    "chars": 1406,
    "preview": "import * as path from \"path\";\nimport { window, workspace } from \"vscode\";\nimport { FileItem } from \"../FileItem\";\nimport"
  },
  {
    "path": "src/controller/RenameFileController.ts",
    "chars": 657,
    "preview": "import * as path from \"path\";\nimport type { DialogOptions } from \"./FileController\";\nimport { MoveFileController } from "
  },
  {
    "path": "src/controller/TypeAheadController.ts",
    "chars": 2565,
    "preview": "import * as path from \"path\";\nimport { type QuickPickItem, window } from \"vscode\";\nimport type { Cache } from \"../lib/Ca"
  },
  {
    "path": "src/controller/index.ts",
    "chars": 366,
    "preview": "export { CopyFileNameController } from \"./CopyFileNameController\";\nexport { DuplicateFileController } from \"./DuplicateF"
  },
  {
    "path": "src/extension.ts",
    "chars": 2193,
    "preview": "import * as vscode from \"vscode\";\nimport {\n    type Command,\n    CopyFileNameCommand,\n    DuplicateFileCommand,\n    Move"
  },
  {
    "path": "src/lib/Cache.ts",
    "chars": 561,
    "preview": "import type * as vscode from \"vscode\";\n\nexport class Cache {\n    private cache: { [key: string]: unknown };\n\n    constru"
  },
  {
    "path": "src/lib/TreeWalker.ts",
    "chars": 1163,
    "preview": "import glob from \"fast-glob\";\nimport * as path from \"path\";\nimport { workspace } from \"vscode\";\n\ninterface ExtendedProce"
  },
  {
    "path": "src/lib/config.ts",
    "chars": 172,
    "preview": "import { workspace } from \"vscode\";\n\nexport function getConfiguration<T>(key: string): T | undefined {\n    return worksp"
  },
  {
    "path": "test/command/CopyFileNameCommand.test.ts",
    "chars": 1862,
    "preview": "import { expect } from \"chai\";\nimport { env } from \"vscode\";\nimport { CopyFileNameCommand } from \"../../src/command\";\nim"
  },
  {
    "path": "test/command/DuplicateFileCommand.test.ts",
    "chars": 4449,
    "preview": "import { expect } from \"chai\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { Uri, window, workspace }"
  },
  {
    "path": "test/command/MoveFileCommand.test.ts",
    "chars": 2037,
    "preview": "import { MoveFileCommand } from \"../../src/command\";\nimport { MoveFileController } from \"../../src/controller\";\nimport *"
  },
  {
    "path": "test/command/NewFileCommand.test.ts",
    "chars": 7406,
    "preview": "import { expect } from \"chai\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { Uri, window, workspace }"
  },
  {
    "path": "test/command/RemoveFileCommand.test.ts",
    "chars": 4678,
    "preview": "import { expect } from \"chai\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { window } from \"vscode\";\n"
  },
  {
    "path": "test/command/RenameFileCommand.test.ts",
    "chars": 3215,
    "preview": "import { expect } from \"chai\";\nimport * as path from \"path\";\nimport { Uri, window } from \"vscode\";\nimport { RenameFileCo"
  },
  {
    "path": "test/fixtures/file-1.rb",
    "chars": 18,
    "preview": "class FileOne; end"
  },
  {
    "path": "test/fixtures/file-2.rb",
    "chars": 18,
    "preview": "class FileTwo; end"
  },
  {
    "path": "test/helper/callbacks.ts",
    "chars": 2000,
    "preview": "import { existsSync } from \"fs\";\nimport path from \"path\";\nimport { Uri, workspace } from \"vscode\";\nimport {\n    editorFi"
  },
  {
    "path": "test/helper/environment.ts",
    "chars": 1129,
    "preview": "import * as os from \"os\";\nimport * as path from \"path\";\nimport { Uri, type WorkspaceFolder } from \"vscode\";\n\nexport cons"
  },
  {
    "path": "test/helper/functions.ts",
    "chars": 1110,
    "preview": "import retry from \"bluebird-retry\";\nimport { TextDecoder } from \"util\";\nimport { commands, type ExtensionContext, type U"
  },
  {
    "path": "test/helper/index.ts",
    "chars": 1899,
    "preview": "import { use } from \"chai\";\nimport * as mocha from \"mocha\";\nimport sinonChai from \"sinon-chai\";\nimport type { Command } "
  },
  {
    "path": "test/helper/steps/describe.ts",
    "chars": 7729,
    "preview": "import { expect } from \"chai\";\nimport * as mocha from \"mocha\";\nimport * as path from \"path\";\nimport sinon from \"sinon\";\n"
  },
  {
    "path": "test/helper/steps/index.ts",
    "chars": 174,
    "preview": "import { describe } from \"./describe\";\nimport { it } from \"./it\";\nimport type { Step } from \"./types\";\n\nexport const ste"
  },
  {
    "path": "test/helper/steps/it.ts",
    "chars": 1467,
    "preview": "import { expect } from \"chai\";\nimport * as fs from \"fs\";\nimport { type Uri, window } from \"vscode\";\nimport type { Comman"
  },
  {
    "path": "test/helper/steps/types.ts",
    "chars": 300,
    "preview": "import type { Command } from \"../../../src/command\";\n\nexport type FuncVoid = () => void;\n// biome-ignore lint/suspicious"
  },
  {
    "path": "test/helper/stubs.ts",
    "chars": 2672,
    "preview": "import * as sinon from \"sinon\";\nimport { commands, type WorkspaceFolder, window, workspace } from \"vscode\";\n\nexport func"
  },
  {
    "path": "test/index.ts",
    "chars": 928,
    "preview": "import glob from \"fast-glob\";\nimport Mocha from \"mocha\";\nimport * as path from \"path\";\n\nexport async function run(): Pro"
  },
  {
    "path": "test/runTest.ts",
    "chars": 841,
    "preview": "import { runTests } from \"@vscode/test-electron\";\nimport * as path from \"path\";\n\nasync function main() {\n    try {\n     "
  },
  {
    "path": "tsconfig.json",
    "chars": 443,
    "preview": "{\r\n    \"extends\": \"@tsconfig/node22/tsconfig.json\",\r\n    \"compilerOptions\": {\r\n        \"rootDir\": \".\",\r\n        \"outDir\""
  }
]

About this extraction

This page contains the full source code of the sleistner/vscode-fileutils GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 73 files (122.3 KB), approximately 29.7k tokens, and a symbol index with 149 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!