diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml
index 07f646cd..d8e9d660 100644
--- a/.github/workflows/latest.yml
+++ b/.github/workflows/latest.yml
@@ -22,6 +22,7 @@ jobs:
release_ref: ${{ steps.meta.outputs.release_ref }}
release_sha: ${{ steps.meta.outputs.release_sha }}
release_short_sha: ${{ steps.meta.outputs.release_short_sha }}
+ targets: ${{ steps.targets.outputs.targets }}
steps:
- name: Check out repository
@@ -52,6 +53,10 @@ jobs:
echo "release_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
echo "release_short_sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT"
+ - name: Resolve platform targets
+ id: targets
+ run: echo "targets=$(node -p "JSON.stringify(require('./scripts/release/targets.json'))")" >> "$GITHUB_OUTPUT"
+
- name: Run test suite
run: npm test
@@ -63,7 +68,7 @@ jobs:
uses: ./.github/workflows/reusable-build-vsix.yml
with:
ref: ${{ needs.preflight.outputs.release_ref }}
- targets: '["win32-x64","win32-arm64","linux-x64","linux-arm64","linux-armhf","darwin-x64","darwin-arm64","alpine-x64","alpine-arm64","web"]'
+ targets: ${{ needs.preflight.outputs.targets }}
secrets: inherit
github-release:
diff --git a/.github/workflows/pr-vsix-build.yml b/.github/workflows/pr-vsix-build.yml
index 0dbba010..c482f960 100644
--- a/.github/workflows/pr-vsix-build.yml
+++ b/.github/workflows/pr-vsix-build.yml
@@ -21,6 +21,7 @@ jobs:
build: ${{ steps.context.outputs.build }}
pull-request-number: ${{ steps.context.outputs.pull-request-number }}
targets: ${{ steps.targets.outputs.targets }}
+ staging-artifact-id: ${{ steps.staging.outputs.artifact-id }}
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
@@ -47,11 +48,6 @@ jobs:
PR_VSIX_MERGE_INTERVAL_MS: 5000
run: node scripts/ci/resolve-pr-vsix-context.mjs
- - name: Resolve platform targets
- id: targets
- if: steps.context.outputs.build == 'true'
- run: echo "targets=$(node -p "JSON.stringify(require('./scripts/release/targets.json'))")" >> "$GITHUB_OUTPUT"
-
- name: Check out immutable build context
if: steps.context.outputs.build == 'true'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
@@ -60,6 +56,18 @@ jobs:
fetch-depth: 0
persist-credentials: false
+ - name: Resolve platform targets
+ id: targets
+ if: steps.context.outputs.build == 'true'
+ run: |
+ node --input-type=module <<'NODE'
+ import fs from 'node:fs';
+ import { verifyTargetMap } from './scripts/ci/verify-pr-vsix.mjs';
+ const targets = JSON.parse(fs.readFileSync('scripts/release/targets.json', 'utf8'));
+ verifyTargetMap(targets);
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `targets=${JSON.stringify(targets)}\n`);
+ NODE
+
- name: Install dependencies
if: steps.context.outputs.build == 'true'
run: npm ci
@@ -82,6 +90,7 @@ jobs:
node scripts/ci/verify-pr-vsix.mjs artifacts/vsix
- name: Stage platform VSIX files
+ id: staging
if: steps.context.outputs.build == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
@@ -122,7 +131,7 @@ jobs:
needs:
- test-build
- publish
- if: always() && needs.test-build.outputs.build == 'true'
+ if: always() && needs.test-build.outputs.staging-artifact-id != ''
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9b2b7c8b..d75d7b49 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,6 +26,7 @@ jobs:
prerelease: ${{ steps.meta.outputs.prerelease }}
release_ref: ${{ steps.meta.outputs.release_ref }}
release_sha: ${{ steps.meta.outputs.release_sha }}
+ targets: ${{ steps.targets.outputs.targets }}
steps:
- name: Check out repository
@@ -57,6 +58,10 @@ jobs:
REF_TYPE: ${{ github.ref_type }}
run: bash scripts/release/resolve-release-metadata.sh
+ - name: Resolve platform targets
+ id: targets
+ run: echo "targets=$(node -p "JSON.stringify(require('./scripts/release/targets.json'))")" >> "$GITHUB_OUTPUT"
+
- name: Run release test suite
run: npm test
@@ -69,7 +74,7 @@ jobs:
with:
ref: ${{ needs.preflight.outputs.release_ref }}
release_tag: ${{ needs.preflight.outputs.tag }}
- targets: '["win32-x64","win32-arm64","linux-x64","linux-arm64","linux-armhf","darwin-x64","darwin-arm64","alpine-x64","alpine-arm64","web"]'
+ targets: ${{ needs.preflight.outputs.targets }}
secrets: inherit
publish:
diff --git a/.gitignore b/.gitignore
index efbdd86a..4f98bbd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ artifacts/**/*.log
artifacts/perf/runtime-benchmarks.json
artifacts/**/*.json
Better.TODO.Tree.settings.txt
+.forge/
diff --git a/.npmignore b/.npmignore
index d2d77ecc..cc3a5f28 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,4 +1,6 @@
.vscode/
+.devcontainer/
+.vscodeignore
.gitignore
node_modules/
src/
@@ -16,11 +18,11 @@ TODOS_LISTS/
TODOS_LISTS/**
.tools/
.act-artifacts/
+.forge/
MIGRATION.md
OPEN_VSX_CERTIFICATE_REPORT.md
CHANGELOG.upstream.md
Better.TODO.Tree.settings.txt
-buildCodiconNames.js
old-*.js
*.bak
*~
diff --git a/.vscodeignore b/.vscodeignore
index 728bdd2a..705bea9a 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -17,12 +17,13 @@ TODOS_LISTS/
TODOS_LISTS/**
.tools/
.act-artifacts/
+.forge/
MIGRATION.md
OPEN_VSX_CERTIFICATE_REPORT.md
CHANGELOG.upstream.md
Better.TODO.Tree.settings.txt
-buildCodiconNames.js
old-*.js
*.bak
*~
.devcontainer/
+.npmignore
diff --git a/MIGRATION.md b/MIGRATION.md
index 79c3667d..6db08be7 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -3,56 +3,55 @@
## Extension Identity
- New public extension ID: `FanaticPythoner.better-todo-tree`
-- Legacy public extension ID: `Gruntfuggly.todo-tree`
-- Stable hidden view IDs kept for layout compatibility:
+- Source extension ID: `Gruntfuggly.todo-tree`
+- View IDs:
- `todo-tree-container`
- `todo-tree-view`
-## Settings Compatibility
-
-Better Todo Tree keeps both configuration namespaces in the manifest during the `0.x` line:
-
-- current namespace: `better-todo-tree.*`
-- legacy namespace: `todo-tree.*`
-
-Runtime reads resolve in this order:
-
-1. explicit `better-todo-tree.*` value
-2. explicit `todo-tree.*` value
-3. current default
-
-All writes go to `better-todo-tree.*`.
-
-On activation, the extension copies legacy `todo-tree.*` values into the matching `better-todo-tree.*` key at the same scope when the new key is unset. Supported scopes:
-
-- Global
-- Workspace
-- Workspace Folder
-
-Manual re-import command:
-
-- `Better Todo Tree: Import Legacy Settings`
-
-## Command Compatibility
-
-All legacy `todo-tree.*` command IDs remain registered as hidden aliases to the rebranded command handlers. Existing keybindings, macros, and scripts keep working while public menus and the command palette expose the `better-todo-tree.*` IDs.
-
-## Export Compatibility
-
-Both export schemes are registered:
-
-- current: `better-todo-tree-export:`
-- legacy: `todotree-export:`
-
-## Non-Portable State
-
-Extension storage keyed by the old extension identifier is not migrated. The compatibility guarantee covers:
-
-- settings
-- public command IDs
-- stable hidden view/container IDs
-
-It does not cover old extension-local cache state or global/workspace mementos stored under the previous publisher/name.
+## Todo Tree settings compatibility
+
+Todo Tree settings are supported directly. Activation reads configuration without rewriting settings files.
+
+| Contract | Behavior |
+| --- | --- |
+| Recognized settings | `better-todo-tree.*`, grouped `todo-tree.*`, historical flat Todo Tree keys |
+| Live edits | Changes under either namespace refresh the affected scanners, highlights and tree controls |
+| Scope | User, workspace, workspace folder and language overrides |
+| Precedence | VS Code scope order; at the same scope: explicit current setting, grouped Todo Tree setting, historical flat key |
+| Empty values | Explicit `false`, `0`, empty arrays and empty objects remain configured values |
+| Object inheritance | Properties merge across scopes using VS Code object-setting semantics |
+| Palette | `highlights.useColourScheme` defaults to `false` under both namespaces |
+| Import command | `Better Todo Tree: Import Todo Tree Settings` copies recognized source values into unset destinations at the original scope |
+| Source preservation | Import preserves every source key, unknown setting, retained historical setting and unrelated setting |
+| Language import | Exact single-language and combined-language selector blocks remain intact |
+| Failed write | Typed error; source retained; destination propagation verified before completion |
+| Retry | Existing destination values remain intact; repeated import performs no writes |
+| New workspace folder | Todo Tree settings work immediately; manual import includes the added folder |
+
+Explicit import creates `better-todo-tree.*` overrides. Subsequent Todo Tree edits follow the same
+scope and namespace precedence rules.
+
+| Historical setting | Current behavior |
+| --- | --- |
+| `todo-tree.tree.showTagsFromOpenFilesOnly` | Maps `true` to `open files`, `false` to `workspace` |
+| `todo-tree.tree.showScanOpenFilesOrWorkspaceButton` | Maps to `tree.buttons.scanMode` |
+| `todo-tree.highlights.schemes` | Maps to `general.schemes` |
+| `todo-tree.general.enableFileWatcher` | Enables external-file refresh in workspace scan modes |
+| `todo-tree.general.fileWatcherGlob` | Selects file events that trigger the optional watcher |
+| `todo-tree.ripgrep.ripgrepMaxBuffer`, `todo-tree.ripgrepMaxBuffer` | Preserved; streaming output has no aggregate output buffer |
+| `todo-tree.tree.showInExplorer` | Preserved; VS Code owns view placement through Move View |
+
+Extension storage owned by `Gruntfuggly.todo-tree` is separate from settings and is not read or copied.
+Setting aliases are generated from current schemas by `npm run settings:sync` and checked during packaging.
+
+## Commands and exports
+
+- Command IDs: `better-todo-tree.*`.
+- Export scheme: `better-todo-tree-export:`.
+- Removed command aliases: `todo-tree.*`.
+- Removed export alias: `todotree-export:`.
+- Existing keybindings and macros must reference the current command IDs.
+- View identifiers remain `todo-tree-container` and `todo-tree-view`.
## GitHub Release Environment
diff --git a/README.md b/README.md
index 9d424072..9884d3d1 100644
--- a/README.md
+++ b/README.md
@@ -82,13 +82,13 @@ Found TODOs can also be highlighted in open files.
## Highlighting
->`better-todo-tree.highlights.useColourScheme` applies foreground and background colour sequences to tags in tag order.
+>`better-todo-tree.highlights.useColourScheme` is opt-in. When enabled, it applies foreground and background colour sequences to tags in tag order.
Highlighting tags is configurable. Use `defaultHighlight` to set up highlights for all tags. If you need to configure individual tags differently, use `customHighlight`. If settings are not specified in `customHighlight`, the value from `defaultHighlight` is used.
Custom highlights can also be specified for sub tags (if used).
-*Note: `defaultHighlight` is not applied to sub tags.*
+Subtags require a matching `customHighlight` entry. Omitted attributes inherit `defaultHighlight`.
Both `defaultHighlight` and `customHighlight` allow for the following settings:
@@ -114,7 +114,7 @@ Both `defaultHighlight` and `customHighlight` allow for the following settings:
`gutterIcon` - set to true to show the icon in the editor gutter.
-*Note: Unfortunately, only octicons and the better-todo-tree icon can be displayed in the gutter.*
+Octicons, codicons, and the Better Todo Tree icon can be displayed in the gutter. Gutter codicons use bundled default SVG shapes. Product icon themes control the tree icons.
`rulerColour` - used to set the colour of the marker in the overview ruler. If not specified, it will default to use the foreground colour. Colour can be specified as per foreground and background colours.
@@ -129,12 +129,12 @@ Both `defaultHighlight` and `customHighlight` allow for the following settings:
- `tag` - highlights just the tag
- `text` - highlights the tag and any text after the tag
- `tag-and-comment` - highlights the comment characters (or the start of the match) and the tag
-- `tag-and-subTag` - as above, but allows the sub tag to be highlight too (with colours defined in custom highlights)
+- `tag-and-subTag` - highlights the tag and a subtag with a matching custom highlight
- `text-and-comment` - highlights the comment characters (or the start of the match), the tag and the text after the tag
- `line` - highlights the entire line containing the tag
- `whole-line` - highlights the entire line containing the tag to the full width of the editor
- `capture-groups:n,m...` - highlights capture groups from the regex, where 'n' is the index into the regex
-- `none` - disable highlightling in the document
+- `none` - disables highlighting in the document
`hideFromTree` - used to hide tags from the tree, but still highlight in files
@@ -178,6 +178,8 @@ Alternatively, open Visual Studio code, press `Ctrl+P` or `Cmd+P` and type:
> ext install FanaticPythoner.better-todo-tree
+Native packages cover Windows x64/Arm64, Linux x64/Arm64/Armhf, Alpine x64/Arm64, and macOS x64/Arm64. Browser-hosted VS Code is excluded because workspace scanning requires the Node extension host and the packaged ripgrep executable.
+
### Source Code
The source code is available on GitHub [here](https://github.com/FanaticPythoner/better-todo-tree).
@@ -299,6 +301,9 @@ This treats any of `FIXME`, `FIXIT` or `FIX` as `FIXME`. When the tree is groupe
*Note: all tags in the group should also appear in `better-todo-tree.general.tags`.*
+VS Code omits the exact `__proto__` object key from its configuration API.
+Assign that tag to a named group such as `PrototypeMarker`, then configure the group in `customHighlight`.
+
**better-todo-tree.general.revealBehaviour** (`start of todo`)
Change the cursor behaviour when double-clicking a todo in the tree. You can choose from: `start of todo` (moves the cursor to the beginning of the todo), `end of todo` (moves the cursor to the end of the todo) or `start of line` (moves the cursor to the start of the line).
@@ -336,7 +341,7 @@ A list of workspace names to include as roots in the tree (wildcards can be used
A list of workspace names to exclude as roots in the tree (wildcards can be used).
**better-todo-tree.filtering.passGlobsToRipgrep** (`true`)
-Set this to false to apply the globs *after* the search (legacy behaviour).
+Set this to false to apply the globs *after* the search.
**better-todo-tree.filtering.useBuiltInExcludes** (`none`)
Set this to use VSCode's built in files or search excludes. Can be one of `none`, `file excludes` (uses Files:Exclude), `search excludes` (Uses Search:Exclude) or `file and search excludes` (uses both).
@@ -380,15 +385,15 @@ Set highlights per tag (or tag group). Example:
}
```
-**better-todo-tree.highlights.useColourScheme** (`true`)
-Applies foreground and background colour sequences to tags in tag order. This is a smaller configuration surface than per-tag `customHighlight` entries.
+**better-todo-tree.highlights.useColourScheme** (`false`)
+Enable foreground and background colour sequences for tags in tag order. Custom and default highlight styles remain unchanged while this setting is disabled.
*Note: The colour scheme overrides the colours defined in* `better-todo-tree.highlights.defaultHighlight` *but not* `better-todo-tree.highlights.customHighlight`*.*
**better-todo-tree.highlights.backgroundColourScheme** (`["red","orange","yellow","green","blue","indigo","violet"]`)
Defines colours for use in conjunction with `better-todo-tree.highlights.useColourScheme` to colour highlights. Colours can be defined in the same way as other colours (e.g. hex code, theme names, etc.). If there are more tags than colours, the sequence is repeated.
-**better-todo-tree.highlights.foreroundColourScheme** (`["white","black","black","white","white","white","black"]`)
+**better-todo-tree.highlights.foregroundColourScheme** (`["white","black","black","white","white","white","black"]`)
Defines colours for use in conjunction with `better-todo-tree.highlights.backgroundColourScheme` to colour highlights. These colours should be complementary to the background colours.
**better-todo-tree.regex.enableMultiLine** (`false`)
@@ -402,7 +407,7 @@ This defines the regex used to locate TODOs. By default, it searches for tags in
*Regexes use ripgrep's Rust engine unless look-around, backreferences, or explicit ripgrep engine arguments select PCRE2.*
**better-todo-tree.regex.subTagRegex**
-This is a regular expression for processing the text to the right of the tag, e.g. for extracting a sub tag, or removing unwanted characters. Anything that the regex matches will be removed from the text. If a capture group is included, the contents are extracted into a sub tag, which will be used in the tree to group similar tags. The sub tag can also be used as a placeholder in `better-todo-tree.tree.subTagClickUrl` and `better-todo-tree.tree.labelFormat`. Sub tags can also be highlighted by specifying a section in the `better-todo-tree.highlights.customHighlights` setting. To highlight the sub tag itself, set "type" to "tag-and-subTag" in custom highlights for the tag.
+Extract a subtag or remove text following a tag with this regular expression. Matched text is removed from the displayed label. A capture group supplies the subtag for tree grouping and the `better-todo-tree.tree.subTagClickUrl` and `better-todo-tree.tree.labelFormat` placeholders. A matching `better-todo-tree.highlights.customHighlight` entry and the `tag-and-subTag` type enable subtag decoration.
Examples:
@@ -419,11 +424,10 @@ Empty value uses the packaged ripgrep binary. Set an absolute executable path to
**better-todo-tree.ripgrep.ripgrepArgs** (`"--max-columns=1000"`)
Pass additional arguments to ripgrep. Engine arguments such as `--pcre2`, `-P`, and `--engine` override automatic engine selection.
-**better-todo-tree.ripgrep.ripgrepMaxBuffer** (`200`)
-By default, the ripgrep process will have a buffer of 200KB. However, this is sometimes not enough for all the tags you might want to see. This setting can be used to increase the buffer size accordingly.
+Ripgrep output is parsed incrementally. No output-buffer size setting is required.
**better-todo-tree.ripgrep.usePatternFile** (`true`)
-A pattern file is used with ripgrep by default. If you experience issues with deleting the pattern file, set this to false to use the legacy method of providing the regex to ripgrep.
+A pattern file is used with ripgrep by default. Setting this to false passes the regex as a command argument and avoids pattern-file creation and deletion.
**better-todo-tree.tree.hideTreeWhenEmpty** (`true`)
Normally, the tree is removed from the explorer view if nothing is found. Set this to false to keep the view present.
@@ -647,7 +651,7 @@ When there is no current workspace, default icons will be shown in the tree.
## Compatibility
-Better Todo Tree keeps reading legacy `todo-tree.*` settings and imports them into the new `better-todo-tree.*` namespace, so existing user configuration keeps working after the rebrand.
+Existing Todo Tree configurations work directly, including `todo-tree.*` settings and historical flat keys. Changes take effect while the extension is running. **Better Todo Tree: Import Todo Tree Settings** copies settings into `better-todo-tree.*` at their original scopes and preserves the source configuration. Scope precedence follows VS Code; explicit `better-todo-tree.*` values win ties at the same scope. See [settings compatibility and import](MIGRATION.md#todo-tree-settings-compatibility).
### Credits
diff --git a/artifacts/accuracy/regex-registry-equivalence.md b/artifacts/accuracy/regex-registry-equivalence.md
index 4c874c4c..8c98c132 100644
--- a/artifacts/accuracy/regex-registry-equivalence.md
+++ b/artifacts/accuracy/regex-registry-equivalence.md
@@ -5,18 +5,18 @@
| Baseline regex entries | 118 |
| Baseline unique regexes | 83 |
| Current hardcoded regex entries | 0 |
-| Registry fragments | 188 |
-| Registry patterns | 210 |
+| Registry fragments | 202 |
+| Registry patterns | 224 |
| Source coverage | 83/83 |
-| Behavior parity | 73/73 |
+| Behavior parity | 72/72 |
| Compiled baseline regexes | 83/83 |
-| Elapsed ms | 147.762 |
+| Elapsed ms | 165.377 |
## Behavior Matrix
| Group | Passed | Total |
| --- | ---: | ---: |
-| utils | 11 | 11 |
+| utils | 10 | 10 |
| attributes | 5 | 5 |
| detection | 5 | 5 |
| regexEngine | 52 | 52 |
diff --git a/artifacts/perf/issue110-highlight-opt-in.md b/artifacts/perf/issue110-highlight-opt-in.md
new file mode 100644
index 00000000..b4e1d0b8
--- /dev/null
+++ b/artifacts/perf/issue110-highlight-opt-in.md
@@ -0,0 +1,66 @@
+# Runtime Benchmarks
+
+- Baseline ref: `HEAD`
+- Current source: working tree
+- Node: `v25.2.0`
+- Selection mode: `scenario-list`
+- Declared suite: `user-flow`
+- Result-count validation: `2 rows, suite-consistent=true, all-user-flow=true`
+
+## Machine Profile
+
+| Category | Field | Value |
+| --- | --- | --- |
+| Host | Hostname | n00ne-AERO-17-YD |
+| Host | OS | Ubuntu 22.04.5 LTS |
+| Host | Kernel | 6.8.0-138-generic |
+| Host | Architecture | x64 |
+| Host | Load Average | 4.04, 5.25, 5.5 |
+| Host | Available Parallelism | - |
+| CPU | Model | Intel(R) Core(TM) i9-14900HX |
+| CPU | Vendor | GenuineIntel |
+| CPU | Topology | 16 logical CPU(s), 2 thread(s)/core, 8 core(s)/socket, 1 socket(s), 1 NUMA node(s) |
+| CPU | Frequency | 800 MHz to 5,800 MHz |
+| CPU | Cache | L1d 384 KiB (8 instances), L1i 256 KiB (8 instances), L2 16 MiB (8 instances), L3 36 MiB (1 instance) |
+| Memory | Total RAM | 62.51 GiB (`67,119,751,168 bytes`) |
+| Memory | Available At Collection | 7.86 GiB (`8,434,503,680 bytes`) |
+| Memory | Online Physical RAM | 66.00 GiB (`70,866,960,384 bytes`) |
+| Memory | Swap | total 120 GiB (`128,848,973,824 bytes`); free 86.92 GiB (`93,329,948,672 bytes`) |
+| Memory | DMI / SPD | Unavailable: /sys/firmware/dmi/tables/smbios_entry_point: Permission denied /dev/mem: Permission denied |
+| Storage | Root Device | nvme0n1 (Samsung SSD 9100 PRO 4TB), 3.64 TiB (`4,000,787,030,016 bytes`), transport nvme, rotational=false, readOnly=false |
+
+## Scenario Model
+
+| Scenario | Kind | User flow | Measurement scope | Input model |
+| --- | --- | --- | --- | --- |
+| visible-editor-highlight-open-file | user-flow | Focus or open a visible editor and apply highlights to that editor. | Active-editor event handling, decoration creation/update, and highlight application. | Fixture scan results fed into the real highlight pipeline in a VS Code event harness. |
+| visible-editor-custom-highlight-config-open-file | user-flow | Open a visible editor while a large custom highlight configuration is active and apply highlights. | Custom-highlight attribute lookup, decoration creation/update, and highlight application. | Fixture scan results plus a large custom-highlight config in a VS Code event harness. |
+
+## Metric Model
+
+| Table | Value model | Accuracy model |
+| --- | --- | --- |
+| Latency | Wall-clock elapsed time around each harness flow iteration, summarized as min/p50/p90/p95/max. | Exact for each sampled iteration in this run. |
+| Profiled RSS Burst | Difference between the isolated scenario worker RSS measured immediately before the flow and that worker iteration's OS high-water-mark peak RSS. | Exact for the measured worker iteration, using `process.memoryUsage().rss` at flow start and `process.resourceUsage().maxRSS` for the peak. |
+| Profiled Peak RSS | Highest process RSS reached by each isolated scenario worker iteration. | Exact worker-process high-water mark from `process.resourceUsage().maxRSS`. |
+
+## Latency
+
+| Scenario | Kind | Baseline p50 ms | Current p50 ms | Baseline p90 ms | Current p90 ms | Baseline p95 ms | Current p95 ms |
+| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
+| visible-editor-highlight-open-file | user-flow | 2.43 | 2.4 | 2.63 | 2.56 | 2.81 | 2.65 |
+| visible-editor-custom-highlight-config-open-file | user-flow | 3.19 | 3.32 | 4.29 | 4.29 | 4.42 | 4.44 |
+
+## Profiled RSS Burst
+
+| Scenario | Kind | Baseline p50 MiB | Current p50 MiB | Baseline p90 MiB | Current p90 MiB | Baseline p95 MiB | Current p95 MiB | Baseline Max MiB | Current Max MiB |
+| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
+| visible-editor-highlight-open-file | user-flow | 15.11 | 14.75 | 15.61 | 15.13 | 15.82 | 15.33 | 15.82 | 15.33 |
+| visible-editor-custom-highlight-config-open-file | user-flow | 15.69 | 15.46 | 15.99 | 16.07 | 16.23 | 16.25 | 16.23 | 16.25 |
+
+## Profiled Peak RSS
+
+| Scenario | Kind | Baseline p50 RSS MiB | Current p50 RSS MiB | Baseline p90 RSS MiB | Current p90 RSS MiB | Baseline p95 RSS MiB | Current p95 RSS MiB | Baseline Max RSS MiB | Current Max RSS MiB |
+| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
+| visible-editor-highlight-open-file | user-flow | 72.21 | 71.64 | 72.56 | 72.01 | 72.72 | 72.09 | 72.72 | 72.09 |
+| visible-editor-custom-highlight-config-open-file | user-flow | 72.5 | 72.43 | 72.85 | 72.97 | 73.22 | 73.04 | 73.22 | 73.04 |
diff --git a/buildCodiconNames.js b/buildCodiconNames.js
deleted file mode 100755
index 130e7266..00000000
--- a/buildCodiconNames.js
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env node
-
-var fs = require( 'fs' );
-var path = require( 'path' );
-var cp = require( "child_process" );
-
-var codiconMappingUrl = "https://raw.githubusercontent.com/microsoft/vscode-codicons/main/src/template/mapping.json";
-var outputPath = path.join( __dirname, "src/codiconNames.js" );
-var raw = cp.execFileSync( "curl", [ "-fsSL", codiconMappingUrl ], { encoding: "utf8" } );
-var mappings = JSON.parse( raw );
-
-if( !mappings || Array.isArray( mappings ) || typeof mappings !== "object" )
-{
- throw new Error( "codicon mapping: object required" );
-}
-
-var names = [];
-
-Object.keys( mappings ).forEach( function( codepoint )
-{
- var aliases = mappings[ codepoint ];
-
- if( Array.isArray( aliases ) !== true )
- {
- throw new Error( "codicon mapping " + codepoint + ": alias array required" );
- }
-
- aliases.forEach( function( alias )
- {
- if( typeof alias !== "string" || alias.length === 0 )
- {
- throw new Error( "codicon mapping " + codepoint + ": non-empty alias required" );
- }
-
- names.push( alias );
- } );
-} );
-
-names = Array.from( new Set( names ) ).sort();
-
-var output = "module.exports = " + JSON.stringify( names, null, 2 ) + ";\n";
-
-if( fs.existsSync( outputPath ) && fs.readFileSync( outputPath, "utf8" ) === output )
-{
- process.exit( 0 );
-}
-
-fs.writeFileSync( outputPath, output );
diff --git a/package-lock.json b/package-lock.json
index 9c66754b..41728a51 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,9 +13,7 @@
"comment-patterns": "^0.10.1",
"fast-strftime": "^1.1.1",
"find": "^0.3.0",
- "micromatch": "^4.0.8",
- "regexp-match-indices": "^1.0.2",
- "treeify": "^1.1.0"
+ "micromatch": "^4.0.8"
},
"devDependencies": {
"@vscode/ripgrep-universal": "^1.18.0",
@@ -1472,6 +1470,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1508,6 +1507,7 @@
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -1660,9 +1660,9 @@
"optional": true
},
"node_modules/baseline-browser-mapping": {
- "version": "2.10.16",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz",
- "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==",
+ "version": "2.11.21",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
+ "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -1716,10 +1716,11 @@
"license": "BSD-2-Clause"
},
"node_modules/brace-expansion": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
- "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
@@ -1740,9 +1741,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.28.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
- "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "version": "4.28.9",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz",
+ "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==",
"dev": true,
"funding": [
{
@@ -1759,12 +1760,13 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
- "baseline-browser-mapping": "^2.10.12",
- "caniuse-lite": "^1.0.30001782",
- "electron-to-chromium": "^1.5.328",
- "node-releases": "^2.0.36",
- "update-browserslist-db": "^1.2.3"
+ "baseline-browser-mapping": "^2.11.20",
+ "caniuse-lite": "^1.0.30001810",
+ "electron-to-chromium": "^1.5.420",
+ "node-releases": "^2.0.54",
+ "update-browserslist-db": "^1.3.2"
},
"bin": {
"browserslist": "cli.js"
@@ -1870,9 +1872,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001787",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz",
- "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==",
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
"dev": true,
"funding": [
{
@@ -2356,9 +2358,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.334",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz",
- "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==",
+ "version": "1.5.425",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.425.tgz",
+ "integrity": "sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==",
"dev": true,
"license": "ISC"
},
@@ -2609,9 +2611,9 @@
"integrity": "sha1-7hsUbWfN31AD6RJwzKIiUFtH0/M="
},
"node_modules/fast-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
- "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
+ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
"dev": true,
"funding": [
{
@@ -2622,7 +2624,8 @@
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
- ]
+ ],
+ "license": "BSD-3-Clause"
},
"node_modules/fastq": {
"version": "1.20.1",
@@ -3341,9 +3344,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
- "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"dev": true,
"funding": [
{
@@ -3355,6 +3358,7 @@
"url": "https://github.com/sponsors/nodeca"
}
],
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -3879,11 +3883,14 @@
"optional": true
},
"node_modules/node-releases": {
- "version": "2.0.37",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz",
- "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
+ "version": "2.0.54",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
+ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/node-sarif-builder": {
"version": "3.4.0",
@@ -4366,12 +4373,14 @@
}
},
"node_modules/qs": {
- "version": "6.15.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
- "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
+ "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
"dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
- "side-channel": "^1.1.0"
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
},
"engines": {
"node": ">=0.6"
@@ -4532,22 +4541,6 @@
"node": ">= 10.13.0"
}
},
- "node_modules/regexp-match-indices": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz",
- "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==",
- "dependencies": {
- "regexp-tree": "^0.1.11"
- }
- },
- "node_modules/regexp-tree": {
- "version": "0.1.23",
- "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.23.tgz",
- "integrity": "sha512-+7HWfb4Bvu8Rs2eQTUIpX9I/PlQkYOuTNbRpKLJlQpSgwSkzFYh+pUj0gtvglnOZLKB6YgnIgRuJ2/IlpL48qw==",
- "bin": {
- "regexp-tree": "bin/regexp-tree"
- }
- },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -4779,15 +4772,15 @@
}
},
"node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
@@ -5322,14 +5315,6 @@
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
},
- "node_modules/treeify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
- "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
- "engines": {
- "node": ">=0.6"
- }
- },
"node_modules/tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
@@ -5399,10 +5384,11 @@
"license": "MIT"
},
"node_modules/undici": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
- "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz",
+ "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=20.18.1"
}
@@ -5437,9 +5423,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
- "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+ "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
"dev": true,
"funding": [
{
@@ -6882,7 +6868,8 @@
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
- "dev": true
+ "dev": true,
+ "peer": true
},
"acorn-import-phases": {
"version": "1.0.4",
@@ -6902,6 +6889,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
+ "peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -6993,9 +6981,9 @@
"optional": true
},
"baseline-browser-mapping": {
- "version": "2.10.16",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz",
- "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==",
+ "version": "2.11.21",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
+ "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==",
"dev": true
},
"binaryextensions": {
@@ -7032,9 +7020,9 @@
"dev": true
},
"brace-expansion": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
- "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"dev": true,
"requires": {
"balanced-match": "^4.0.2"
@@ -7049,16 +7037,17 @@
}
},
"browserslist": {
- "version": "4.28.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
- "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "version": "4.28.9",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz",
+ "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==",
"dev": true,
+ "peer": true,
"requires": {
- "baseline-browser-mapping": "^2.10.12",
- "caniuse-lite": "^1.0.30001782",
- "electron-to-chromium": "^1.5.328",
- "node-releases": "^2.0.36",
- "update-browserslist-db": "^1.2.3"
+ "baseline-browser-mapping": "^2.11.20",
+ "caniuse-lite": "^1.0.30001810",
+ "electron-to-chromium": "^1.5.420",
+ "node-releases": "^2.0.54",
+ "update-browserslist-db": "^1.3.2"
}
},
"buffer": {
@@ -7120,9 +7109,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001787",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz",
- "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==",
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
"dev": true
},
"chalk": {
@@ -7436,9 +7425,9 @@
}
},
"electron-to-chromium": {
- "version": "1.5.334",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz",
- "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==",
+ "version": "1.5.425",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.425.tgz",
+ "integrity": "sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==",
"dev": true
},
"emoji-regex": {
@@ -7611,9 +7600,9 @@
"integrity": "sha1-7hsUbWfN31AD6RJwzKIiUFtH0/M="
},
"fast-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
- "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
+ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
"dev": true
},
"fastq": {
@@ -8076,9 +8065,9 @@
"dev": true
},
"js-yaml": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
- "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"dev": true,
"requires": {
"argparse": "^2.0.1"
@@ -8419,9 +8408,9 @@
"optional": true
},
"node-releases": {
- "version": "2.0.37",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz",
- "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
+ "version": "2.0.54",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
+ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
"dev": true
},
"node-sarif-builder": {
@@ -8753,12 +8742,13 @@
"dev": true
},
"qs": {
- "version": "6.15.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
- "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
+ "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
"dev": true,
"requires": {
- "side-channel": "^1.1.0"
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
}
},
"queue-microtask": {
@@ -8862,19 +8852,6 @@
"resolve": "^1.20.0"
}
},
- "regexp-match-indices": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz",
- "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==",
- "requires": {
- "regexp-tree": "^0.1.11"
- }
- },
- "regexp-tree": {
- "version": "0.1.23",
- "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.23.tgz",
- "integrity": "sha512-+7HWfb4Bvu8Rs2eQTUIpX9I/PlQkYOuTNbRpKLJlQpSgwSkzFYh+pUj0gtvglnOZLKB6YgnIgRuJ2/IlpL48qw=="
- },
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -9004,14 +8981,14 @@
"dev": true
},
"side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
"dev": true,
"requires": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
}
@@ -9369,11 +9346,6 @@
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
"integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
},
- "treeify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
- "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A=="
- },
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
@@ -9426,9 +9398,9 @@
"dev": true
},
"undici": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
- "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz",
+ "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==",
"dev": true
},
"undici-types": {
@@ -9450,9 +9422,9 @@
"dev": true
},
"update-browserslist-db": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
- "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+ "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
"dev": true,
"requires": {
"escalade": "^3.2.0",
diff --git a/package.json b/package.json
index 71f1fee2..788cb61e 100644
--- a/package.json
+++ b/package.json
@@ -47,10 +47,12 @@
},
"scripts": {
"test": "qunit",
- "codicons:update": "node ./buildCodiconNames.js",
- "vscode:prepublish": "webpack --mode production",
+ "codicons:update": "python3 ./scripts/build-codicon-assets.py",
+ "vscode:prepublish": "node ./scripts/sync-settings-aliases.js --check && webpack --mode production",
"webpack": "webpack --mode development",
- "webpack-dev": "webpack --mode development --watch"
+ "webpack-dev": "webpack --mode development --watch",
+ "settings:sync": "node ./scripts/sync-settings-aliases.js",
+ "settings:check": "node ./scripts/sync-settings-aliases.js --check"
},
"categories": [
"Other"
@@ -599,37 +601,16 @@
"markdownDescription": "%better-todo-tree.configuration.general.debug.markdownDescription%",
"type": "boolean"
},
- "todo-tree.general.debug": {
- "default": false,
- "markdownDescription": "%better-todo-tree.configuration.general.debug.markdownDescription%",
- "type": "boolean",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.automaticGitRefreshInterval": {
"default": 0,
"markdownDescription": "%better-todo-tree.configuration.general.automaticGitRefreshInterval.markdownDescription%",
"type": "integer"
},
- "todo-tree.general.automaticGitRefreshInterval": {
- "default": 0,
- "markdownDescription": "%better-todo-tree.configuration.general.automaticGitRefreshInterval.markdownDescription%",
- "type": "integer",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.periodicRefreshInterval": {
"default": 0,
"markdownDescription": "%better-todo-tree.configuration.general.periodicRefreshInterval.markdownDescription%",
"type": "integer"
},
- "todo-tree.general.periodicRefreshInterval": {
- "default": 0,
- "markdownDescription": "%better-todo-tree.configuration.general.periodicRefreshInterval.markdownDescription%",
- "type": "integer",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.revealBehaviour": {
"default": "start of todo",
"enum": [
@@ -645,47 +626,16 @@
],
"type": "string"
},
- "todo-tree.general.revealBehaviour": {
- "default": "start of todo",
- "enum": [
- "start of line",
- "start of todo",
- "end of todo"
- ],
- "markdownDescription": "%better-todo-tree.configuration.general.revealBehaviour.markdownDescription%",
- "markdownEnumDescriptions": [
- "%better-todo-tree.configuration.general.revealBehaviour.markdownEnumDescriptions.1%",
- "%better-todo-tree.configuration.general.revealBehaviour.markdownEnumDescriptions.2%",
- "%better-todo-tree.configuration.general.revealBehaviour.markdownEnumDescriptions.3%"
- ],
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.exportPath": {
"default": "~/better-todo-tree-%Y%m%d-%H%M.txt",
"markdownDescription": "%better-todo-tree.configuration.general.exportPath.markdownDescription%",
"type": "string"
},
- "todo-tree.general.exportPath": {
- "default": "~/better-todo-tree-%Y%m%d-%H%M.txt",
- "markdownDescription": "%better-todo-tree.configuration.general.exportPath.markdownDescription%",
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.rootFolder": {
"default": "",
"markdownDescription": "%better-todo-tree.configuration.general.rootFolder.markdownDescription%",
"type": "string"
},
- "todo-tree.general.rootFolder": {
- "default": "",
- "markdownDescription": "%better-todo-tree.configuration.general.rootFolder.markdownDescription%",
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.schemes": {
"default": [
"file",
@@ -699,21 +649,6 @@
"markdownDescription": "%better-todo-tree.configuration.general.schemes.markdownDescription%",
"type": "array"
},
- "todo-tree.general.schemes": {
- "default": [
- "file",
- "ssh",
- "untitled",
- "vscode-notebook-cell"
- ],
- "items": {
- "type": "string"
- },
- "markdownDescription": "%better-todo-tree.configuration.general.schemes.markdownDescription%",
- "type": "array",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.showScanningProgress": {
"default": "status bar",
"enum": [
@@ -733,27 +668,6 @@
],
"type": "string"
},
- "todo-tree.general.showScanningProgress": {
- "default": "status bar",
- "enum": [
- "none",
- "status bar",
- "notification",
- "tree",
- "all"
- ],
- "markdownDescription": "%better-todo-tree.configuration.general.showScanningProgress.markdownDescription%",
- "markdownEnumDescriptions": [
- "%better-todo-tree.configuration.general.showScanningProgress.markdownEnumDescriptions.1%",
- "%better-todo-tree.configuration.general.showScanningProgress.markdownEnumDescriptions.2%",
- "%better-todo-tree.configuration.general.showScanningProgress.markdownEnumDescriptions.3%",
- "%better-todo-tree.configuration.general.showScanningProgress.markdownEnumDescriptions.4%",
- "%better-todo-tree.configuration.general.showScanningProgress.markdownEnumDescriptions.5%"
- ],
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.statusBar": {
"default": "none",
"enum": [
@@ -773,39 +687,11 @@
],
"type": "string"
},
- "todo-tree.general.statusBar": {
- "default": "none",
- "enum": [
- "none",
- "total",
- "tags",
- "top three",
- "current file"
- ],
- "markdownDescription": "%better-todo-tree.configuration.general.statusBar.markdownDescription%",
- "markdownEnumDescriptions": [
- "%better-todo-tree.configuration.general.statusBar.markdownEnumDescriptions.1%",
- "%better-todo-tree.configuration.general.statusBar.markdownEnumDescriptions.2%",
- "%better-todo-tree.configuration.general.statusBar.markdownEnumDescriptions.3%",
- "%better-todo-tree.configuration.general.statusBar.markdownEnumDescriptions.4%",
- "%better-todo-tree.configuration.general.statusBar.markdownEnumDescriptions.5%"
- ],
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.showIconsInsteadOfTagsInStatusBar": {
"default": false,
"markdownDescription": "%better-todo-tree.configuration.general.showIconsInsteadOfTagsInStatusBar.markdownDescription%",
"type": "boolean"
},
- "todo-tree.general.showIconsInsteadOfTagsInStatusBar": {
- "default": false,
- "markdownDescription": "%better-todo-tree.configuration.general.showIconsInsteadOfTagsInStatusBar.markdownDescription%",
- "type": "boolean",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.statusBarClickBehaviour": {
"default": "reveal",
"enum": [
@@ -821,23 +707,6 @@
],
"type": "string"
},
- "todo-tree.general.statusBarClickBehaviour": {
- "default": "reveal",
- "enum": [
- "cycle",
- "reveal",
- "toggle highlights"
- ],
- "markdownDescription": "%better-todo-tree.configuration.general.statusBarClickBehaviour.markdownDescription%",
- "markdownEnumDescriptions": [
- "%better-todo-tree.configuration.general.statusBarClickBehaviour.markdownEnumDescriptions.1%",
- "%better-todo-tree.configuration.general.statusBarClickBehaviour.markdownEnumDescriptions.2%",
- "%better-todo-tree.configuration.general.statusBarClickBehaviour.markdownEnumDescriptions.3%"
- ],
- "type": "string",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.tagGroups": {
"default": {},
"markdownDescription": "%better-todo-tree.configuration.general.tagGroups.markdownDescription%",
@@ -849,19 +718,6 @@
}
}
},
- "todo-tree.general.tagGroups": {
- "default": {},
- "markdownDescription": "%better-todo-tree.configuration.general.tagGroups.markdownDescription%",
- "type": "object",
- "additionalProperties": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.tags": {
"default": [
"BUG",
@@ -878,35 +734,21 @@
"markdownDescription": "%better-todo-tree.configuration.general.tags.markdownDescription%",
"type": "array"
},
- "todo-tree.general.tags": {
- "default": [
- "BUG",
- "HACK",
- "FIXME",
- "TODO",
- "XXX",
- "[ ]",
- "[x]"
- ],
- "items": {
- "type": "string"
- },
- "markdownDescription": "%better-todo-tree.configuration.general.tags.markdownDescription%",
- "type": "array",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.general.showActivityBarBadge": {
"default": false,
"markdownDescription": "%better-todo-tree.configuration.general.showActivityBarBadge.markdownDescription%",
"type": "boolean"
},
- "todo-tree.general.showActivityBarBadge": {
- "default": false,
- "markdownDescription": "%better-todo-tree.configuration.general.showActivityBarBadge.markdownDescription%",
+ "better-todo-tree.general.enableFileWatcher": {
"type": "boolean",
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
+ "default": false,
+ "markdownDescription": "Refresh workspace results after external file creation, edits and deletion."
+ },
+ "better-todo-tree.general.fileWatcherGlob": {
+ "type": "string",
+ "default": "**/*",
+ "minLength": 1,
+ "markdownDescription": "Glob matched by the optional workspace file watcher."
}
}
},
@@ -1025,16 +867,6 @@
}
}
},
- "todo-tree.languages.customPatterns": {
- "default": [],
- "markdownDescription": "%better-todo-tree.configuration.languages.customPatterns.markdownDescription%",
- "type": "array",
- "items": {
- "type": "object"
- },
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
- },
"better-todo-tree.languages.embeddedDocuments": {
"default": [],
"markdownDescription": "%better-todo-tree.configuration.languages.embeddedDocuments.markdownDescription%",
@@ -1197,16 +1029,6 @@
}
}
}
- },
- "todo-tree.languages.embeddedDocuments": {
- "default": [],
- "markdownDescription": "%better-todo-tree.configuration.languages.embeddedDocuments.markdownDescription%",
- "type": "array",
- "items": {
- "type": "object"
- },
- "deprecationMessage": "%todo-tree.configuration.legacyNamespace.deprecationMessage%",
- "markdownDeprecationMessage": "%todo-tree.configuration.legacyNamespace.markdownDeprecationMessage%"
}
}
},
@@ -1243,11 +1065,25 @@
"properties": {
"foreground": {
"type": "string",
- "format": "color-hex"
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
"background": {
"type": "string",
- "format": "color-hex"
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
"opacity": {
"type": "number"
@@ -1269,14 +1105,28 @@
},
"iconColour": {
"type": "string",
- "format": "color-hex"
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
"gutterIcon": {
"type": "boolean"
},
"rulerColour": {
"type": "string",
- "format": "color-hex"
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
"rulerOpacity": {
"type": "number"
@@ -1293,15 +1143,22 @@
},
"type": {
"type": "string",
- "enum": [
- "tag",
- "text",
- "tag-and-comment",
- "tag-and-subTag",
- "text-and-comment",
- "line",
- "whole-line",
- "none"
+ "anyOf": [
+ {
+ "enum": [
+ "tag",
+ "text",
+ "tag-and-comment",
+ "tag-and-subTag",
+ "text-and-comment",
+ "line",
+ "whole-line",
+ "none"
+ ]
+ },
+ {
+ "pattern": "^capture-groups:\\s*\\d+(?:\\s*,\\s*\\d+)*\\s*$"
+ }
]
},
"hideFromTree": {
@@ -1316,25 +1173,1147 @@
}
}
},
- "todo-tree.highlights.customHighlight": {
- "default": {
- "BUG": {
- "icon": "bug"
+ "better-todo-tree.highlights.defaultHighlight": {
+ "default": {},
+ "markdownDescription": "%better-todo-tree.configuration.highlights.defaultHighlight.markdownDescription%",
+ "type": "object",
+ "properties": {
+ "foreground": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
- "HACK": {
- "icon": "tools"
+ "background": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
},
- "FIXME": {
- "icon": "flame"
+ "opacity": {
+ "type": "number"
},
- "XXX": {
- "icon": "x"
+ "fontWeight": {
+ "type": "string"
},
- "[ ]": {
- "icon": "issue-draft"
+ "fontStyle": {
+ "type": "string"
},
- "[x]": {
- "icon": "issue-closed"
+ "textDecoration": {
+ "type": "string"
+ },
+ "borderRadius": {
+ "type": "string"
+ },
+ "icon": {
+ "type": "string"
+ },
+ "iconColour": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
+ },
+ "gutterIcon": {
+ "type": "boolean"
+ },
+ "rulerColour": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "color-hex"
+ },
+ {
+ "pattern": "^[^#].*$"
+ }
+ ]
+ },
+ "rulerOpacity": {
+ "type": "number"
+ },
+ "rulerLane": {
+ "type": "string",
+ "enum": [
+ "none",
+ "left",
+ "center",
+ "right",
+ "full"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "anyOf": [
+ {
+ "enum": [
+ "tag",
+ "text",
+ "tag-and-comment",
+ "tag-and-subTag",
+ "text-and-comment",
+ "line",
+ "whole-line",
+ "none"
+ ]
+ },
+ {
+ "pattern": "^capture-groups:\\s*\\d+(?:\\s*,\\s*\\d+)*\\s*$"
+ }
+ ]
+ },
+ "hideFromTree": {
+ "type": "boolean"
+ },
+ "hideFromStatusBar": {
+ "type": "boolean"
+ },
+ "hideFromActivityBar": {
+ "type": "boolean"
+ }
+ }
+ },
+ "better-todo-tree.highlights.enabled": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.highlights.enabled.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.highlights.highlightDelay": {
+ "default": 500,
+ "markdownDescription": "%better-todo-tree.configuration.highlights.highlightDelay.markdownDescription%",
+ "type": "integer"
+ },
+ "better-todo-tree.highlights.useColourScheme": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.highlights.useColourScheme.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.highlights.foregroundColourScheme": {
+ "default": [
+ "white",
+ "black",
+ "black",
+ "white",
+ "white",
+ "white",
+ "black"
+ ],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.highlights.foregroundColourScheme.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.highlights.backgroundColourScheme": {
+ "default": [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "indigo",
+ "violet"
+ ],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.highlights.backgroundColourScheme.markdownDescription%",
+ "type": "array"
+ }
+ }
+ },
+ {
+ "title": "%better-todo-tree.configuration.filtering%",
+ "order": 4,
+ "type": "object",
+ "properties": {
+ "better-todo-tree.filtering.excludedWorkspaces": {
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.filtering.excludedWorkspaces.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.filtering.excludeGlobs": {
+ "default": [
+ "**/node_modules/*/**"
+ ],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.filtering.excludeGlobs.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.filtering.ignoreGitSubmodules": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.filtering.ignoreGitSubmodules.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.filtering.includedWorkspaces": {
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.filtering.includedWorkspaces.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.filtering.includeGlobs": {
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "markdownDescription": "%better-todo-tree.configuration.filtering.includeGlobs.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.filtering.includeHiddenFiles": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.filtering.includeHiddenFiles.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.filtering.passGlobsToRipgrep": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.filtering.passGlobsToRipgrep.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.filtering.scopes": {
+ "default": [],
+ "markdownDescription": "%better-todo-tree.configuration.filtering.scopes.markdownDescription%",
+ "type": "array"
+ },
+ "better-todo-tree.filtering.useBuiltInExcludes": {
+ "default": "none",
+ "enum": [
+ "none",
+ "file excludes",
+ "search excludes",
+ "file and search excludes"
+ ],
+ "markdownDescription": "%better-todo-tree.configuration.filtering.useBuiltInExcludes.markdownDescription%",
+ "markdownEnumDescriptions": [
+ "%better-todo-tree.configuration.filtering.useBuiltInExcludes.markdownEnumDescriptions.1%",
+ "%better-todo-tree.configuration.filtering.useBuiltInExcludes.markdownEnumDescriptions.2%",
+ "%better-todo-tree.configuration.filtering.useBuiltInExcludes.markdownEnumDescriptions.3%",
+ "%better-todo-tree.configuration.filtering.useBuiltInExcludes.markdownEnumDescriptions.4%"
+ ],
+ "type": "string"
+ }
+ }
+ },
+ {
+ "title": "%better-todo-tree.configuration.tree%",
+ "order": 5,
+ "type": "object",
+ "properties": {
+ "better-todo-tree.tree.autoRefresh": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.autoRefresh.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.disableCompactFolders": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.disableCompactFolders.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.expanded": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.expanded.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.filterCaseSensitive": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.filterCaseSensitive.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.flat": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.flat.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.groupedByTag": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.groupedByTag.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.groupedBySubTag": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.groupedBySubTag.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.hideIconsWhenGroupedByTag": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.hideIconsWhenGroupedByTag.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.hideTreeWhenEmpty": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.hideTreeWhenEmpty.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.labelFormat": {
+ "default": "${tag} ${after}",
+ "markdownDescription": "%better-todo-tree.configuration.tree.labelFormat.markdownDescription%",
+ "type": "string"
+ },
+ "better-todo-tree.tree.scanAtStartup": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.scanAtStartup.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.scanMode": {
+ "default": "workspace",
+ "enum": [
+ "workspace",
+ "open files",
+ "current file",
+ "workspace only"
+ ],
+ "markdownDescription": "%better-todo-tree.configuration.tree.scanMode.markdownDescription%",
+ "markdownEnumDescriptions": [
+ "%better-todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.1%",
+ "%better-todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.2%",
+ "%better-todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.3%",
+ "%better-todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.4%"
+ ],
+ "type": "string"
+ },
+ "better-todo-tree.tree.showBadges": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.showBadges.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.showCountsInTree": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.showCountsInTree.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.showCurrentScanMode": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.showCurrentScanMode.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.subTagClickUrl": {
+ "default": "",
+ "markdownDescription": "%better-todo-tree.configuration.tree.subTagClickUrl.markdownDescription%",
+ "type": "string"
+ },
+ "better-todo-tree.tree.sortTagsOnlyViewAlphabetically": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.sortTagsOnlyViewAlphabetically.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.sort": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.sort.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.tagsOnly": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.tagsOnly.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.tooltipFormat": {
+ "default": "${filepath}, line ${line}",
+ "markdownDescription": "%better-todo-tree.configuration.tree.tooltipFormat.markdownDescription%",
+ "type": "string"
+ },
+ "better-todo-tree.tree.trackFile": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.trackFile.markdownDescription%",
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "title": "%better-todo-tree.configuration.buttons%",
+ "order": 6,
+ "type": "object",
+ "properties": {
+ "better-todo-tree.tree.buttons.reveal": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.reveal.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.scanMode": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.scanMode.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.viewStyle": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.viewStyle.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.groupByTag": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.groupByTag.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.groupBySubTag": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.groupBySubTag.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.filter": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.filter.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.refresh": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.refresh.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.expand": {
+ "default": true,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.expand.markdownDescription%",
+ "type": "boolean"
+ },
+ "better-todo-tree.tree.buttons.export": {
+ "default": false,
+ "markdownDescription": "%better-todo-tree.configuration.tree.buttons.export.markdownDescription%",
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "title": "%better-todo-tree.configuration.regex%",
+ "order": 7,
+ "type": "object",
+ "properties": {
+ "better-todo-tree.regex.regex": {
+ "default": "(^|//|#|\n' ]
+ ].forEach( function( fixture )
+ {
+ QUnit.test( 'resource snapshot controls every default scan region: ' + fixture[ 0 ], function( assert )
+ {
+ var uri = createUri( '/tmp/resource-snapshot.' + fixture[ 0 ] );
+ var resourceConfig = Object.assign( {}, detection.resolveResourceConfig( uri ), { tags: [ 'REVIEW' ] } );
+ var reads = 0;
+ var context = detection.createScanContext( uri, fixture[ 1 ], { getResourceConfig: function()
+ {
+ reads++;
+ return resourceConfig;
+ } } );
+ var offsets = context.lineOffsets;
+ var results = detection.scanTextWithContext( context );
+ assert.equal( results.length, fixture[ 0 ] === 'js' ? 1 : 2 );
+ assert.true( results.every( function( result ) { return result.actualTag === 'REVIEW'; } ) );
+ assert.equal( context.text, fixture[ 1 ] );
+ assert.strictEqual( context.lineOffsets, offsets );
+ assert.equal( reads, 1 );
+ } );
+ } );
+
+ QUnit.test( 'resource snapshot supplies custom regex compilation', function( assert )
+ {
+ var uri = createUri( '/tmp/custom-snapshot.js' );
+ var resourceConfig = Object.assign( {}, detection.resolveResourceConfig( uri ), {
+ tags: [ 'REVIEW' ], regex: regexRegistry.captureSource( 'REVIEW' ), isDefaultRegex: false
+ } );
+ var context = detection.createScanContext( uri, 'REVIEW snapshot\n// TODO unrelated', {
+ getResourceConfig: function() { return resourceConfig; }
+ } );
+ var results = detection.scanTextWithContext( context );
+ assert.equal( results.length, 1 );
+ assert.equal( results[ 0 ].actualTag, 'REVIEW' );
+ assert.equal( results[ 0 ].line, 1 );
+ } );
+
+ [ 'js', 'tmpl' ].forEach( function( extension )
+ {
+ QUnit.test( 'regex scan resolves the comment trim pattern once per context: ' + extension, function( assert )
+ {
+ utils.init( createConfig( { regexSource: regexRegistry.TAG_CAPTURE_PLACEHOLDER } ) );
+ var uri = createUri( '/tmp/trim-pattern.' + extension );
+ var text = '// TODO first\n// TODO second\n// FIXME third';
+ var context = detection.createScanContext( uri, text );
+ var original = utils.resolveBlockCommentPattern;
+ var calls = 0;
+ utils.resolveBlockCommentPattern = function()
+ {
+ calls += 1;
+ return original.apply( utils, arguments );
+ };
+ try
+ {
+ var first = detection.scanTextWithContext( context );
+ assert.equal( first.length, 3 );
+ assert.equal( calls, 1 );
+ assert.deepEqual( detection.scanTextWithContext( context ), first );
+ assert.equal( calls, 1, 'resolved absence and resolved patterns both remain cached' );
+ detection.scanTextWithContext( detection.createScanContext( uri, text ) );
+ assert.equal( calls, 2, 'a new scan context resolves its own pattern' );
+ }
+ finally
+ {
+ utils.resolveBlockCommentPattern = original;
+ }
+ } );
+ } );
+
QUnit.test( "default detection ignores tag-like identifiers", function( assert )
{
var results = detection.scanText( createUri( "/tmp/sample.js" ), [
diff --git a/test/extension.scan-parity.test.js b/test/extension.scan-parity.test.js
index 5c35eed3..aff49d77 100644
--- a/test/extension.scan-parity.test.js
+++ b/test/extension.scan-parity.test.js
@@ -329,6 +329,14 @@ function createProviderStub( options )
exportTree: function() { return {}; },
hasSubTags: function() { return false; },
getChildren: function() { return []; },
+ getElement: function( filename )
+ {
+ if( typeof ( options.getElementImpl ) === 'function' )
+ {
+ return options.getElementImpl( filename );
+ }
+ return options.treeElement;
+ },
clearExpansionState: function() {},
setExpanded: function() {},
dispose: function() {}
@@ -394,6 +402,8 @@ function createVscodeStub( options )
var progressSessions = [];
var statusBarItems = [];
var treeViews = [];
+ var decorationTypes = [];
+ var outputChannels = [];
var configurationUpdates = [];
var automaticGitRefreshInterval = options.automaticGitRefreshInterval !== undefined ? options.automaticGitRefreshInterval : 0;
var periodicRefreshInterval = options.periodicRefreshInterval !== undefined ? options.periodicRefreshInterval : 0;
@@ -428,7 +438,7 @@ function createVscodeStub( options )
expand: false,
export: false
},
- trackFile: false,
+ trackFile: options.trackFile === true,
expanded: false,
flat: false,
tagsOnly: false,
@@ -441,7 +451,7 @@ function createVscodeStub( options )
},
filtering: filteringDefaults,
general: {
- debug: false,
+ debug: options.debug === true,
automaticGitRefreshInterval: automaticGitRefreshInterval,
periodicRefreshInterval: periodicRefreshInterval,
rootFolder: "",
@@ -451,7 +461,6 @@ function createVscodeStub( options )
},
ripgrep: {
ripgrepArgs: '',
- ripgrepMaxBuffer: 200,
usePatternFile: false
},
regex: {
@@ -460,7 +469,7 @@ function createVscodeStub( options )
}, undefined, configurationUpdates, options.configurationUpdateImpl );
var generalSection = createConfigurationSection( {
- debug: false,
+ debug: options.debug === true,
automaticGitRefreshInterval: automaticGitRefreshInterval,
periodicRefreshInterval: periodicRefreshInterval,
rootFolder: "",
@@ -475,7 +484,7 @@ function createVscodeStub( options )
}, undefined, configurationUpdates );
var treeSection = createConfigurationSection( {
autoRefresh: true,
- trackFile: false,
+ trackFile: options.trackFile === true,
showCountsInTree: false,
showBadges: false,
scanMode: options.scanMode,
@@ -502,7 +511,6 @@ function createVscodeStub( options )
}, undefined, configurationUpdates );
var ripgrepSection = createConfigurationSection( {
ripgrepArgs: '',
- ripgrepMaxBuffer: 200,
usePatternFile: false
}, undefined, configurationUpdates );
@@ -530,14 +538,14 @@ function createVscodeStub( options )
return { dispose: function() {} };
}
- function createTreeView( id, options )
+ function createTreeView( id, viewOptions )
{
var view = {
badge: undefined,
title: 'Tree',
message: '',
- visible: false,
- treeDataProvider: options && options.treeDataProvider,
+ visible: options.treeViewVisible === true,
+ treeDataProvider: viewOptions && viewOptions.treeDataProvider,
revealCalls: [],
onDidExpandElement: function( listener ) { return registerListener( windowListeners, 'expand', listener ); },
onDidCollapseElement: function( listener ) { return registerListener( windowListeners, 'collapse', listener ); },
@@ -547,6 +555,13 @@ function createVscodeStub( options )
element: element,
options: revealOptions
} );
+ if( typeof ( options.revealImpl ) === 'function' )
+ {
+ return Promise.resolve().then( function()
+ {
+ return options.revealImpl( element, revealOptions );
+ } );
+ }
return Promise.resolve();
}
};
@@ -599,6 +614,7 @@ function createVscodeStub( options )
{
this.event = function() {};
this.fire = function() {};
+ this.dispose = function() {};
}
function TreeItem( label )
@@ -614,7 +630,7 @@ function createVscodeStub( options )
ThemeIcon.Folder = new ThemeIcon( 'folder' );
ThemeIcon.File = new ThemeIcon( 'file' );
- return {
+ var vscodeStub = {
commandHandlers: commandHandlers,
workspaceListeners: workspaceListeners,
windowListeners: windowListeners,
@@ -622,6 +638,8 @@ function createVscodeStub( options )
progressSessions: progressSessions,
statusBarItems: statusBarItems,
treeViews: treeViews,
+ decorationTypes: decorationTypes,
+ outputChannels: outputChannels,
configurationUpdates: configurationUpdates,
extensions: {
all: options.extensions || [ {
@@ -659,7 +677,22 @@ function createVscodeStub( options )
},
parse: function( value )
{
- return { path: value, fsPath: value, toString: function() { return value; } };
+ return {
+ path: value,
+ fsPath: value,
+ query: '',
+ with: function( changes )
+ {
+ var query = changes.query;
+ return {
+ path: value,
+ fsPath: value,
+ query: query,
+ toString: function() { return value + '?' + query; }
+ };
+ },
+ toString: function() { return value; }
+ };
}
},
env: {
@@ -700,6 +733,16 @@ function createVscodeStub( options )
},
window: {
visibleTextEditors: options.visibleTextEditors || [],
+ tabGroups: {
+ get all()
+ {
+ var uris = options.openTabUris || vscodeStub.workspace.textDocuments.concat(
+ vscodeStub.window.visibleTextEditors.map( function( editor ) { return editor.document; } )
+ ).map( function( document ) { return document.uri; } );
+ return [ { tabs: uris.map( function( uri ) { return { input: { uri: uri } }; } ) } ];
+ },
+ onDidChangeTabs: function( listener ) { return registerListener( windowListeners, 'tabs', listener ); }
+ },
activeTextEditor: options.activeTextEditor,
activeNotebookEditor: activeNotebookEditor,
visibleNotebookEditors: visibleNotebookEditors,
@@ -738,10 +781,29 @@ function createVscodeStub( options )
},
createOutputChannel: function()
{
- return {
+ var outputChannel = {
appendLine: function() {},
- dispose: function() {}
+ disposeCalls: 0,
+ dispose: function()
+ {
+ this.disposeCalls++;
+ }
+ };
+ outputChannels.push( outputChannel );
+ return outputChannel;
+ },
+ createTextEditorDecorationType: function( decorationOptions )
+ {
+ var decoration = {
+ options: decorationOptions,
+ disposeCalls: 0,
+ dispose: function()
+ {
+ this.disposeCalls++;
+ }
};
+ decorationTypes.push( decoration );
+ return decoration;
},
showInformationMessage: function( message )
{
@@ -816,7 +878,17 @@ function createVscodeStub( options )
},
notebookDocuments: options.notebookDocuments || [],
onDidSaveTextDocument: function( listener ) { return registerListener( workspaceListeners, 'save', listener ); },
- onDidOpenTextDocument: function( listener ) { return registerListener( workspaceListeners, 'open', listener ); },
+ onDidOpenTextDocument: function( listener )
+ {
+ return registerListener( workspaceListeners, 'open', function( document )
+ {
+ if( vscodeStub.workspace.textDocuments.indexOf( document ) === -1 )
+ {
+ vscodeStub.workspace.textDocuments.push( document );
+ }
+ return listener( document );
+ } );
+ },
onDidCloseTextDocument: function( listener ) { return registerListener( workspaceListeners, 'close', listener ); },
onDidOpenNotebookDocument: function( listener ) { return registerListener( workspaceListeners, 'openNotebook', listener ); },
onDidChangeNotebookDocument: function( listener ) { return registerListener( workspaceListeners, 'changeNotebook', listener ); },
@@ -835,6 +907,7 @@ function createVscodeStub( options )
}
}
};
+ return vscodeStub;
}
function createExtensionHarness( options )
@@ -861,6 +934,11 @@ function createExtensionHarness( options )
};
var vscodeStub = createVscodeStub( options );
+ var getHarnessConfiguration = vscodeStub.workspace.getConfiguration;
+ vscodeStub.workspace.getConfiguration = function( section, uri )
+ {
+ return section === 'todo-tree' ? createConfigurationSection( {} ) : getHarnessConfiguration( section, uri );
+ };
var identityStub = helpers.loadWithStubs( '../src/extensionIdentity.js', {
vscode: vscodeStub
} );
@@ -881,7 +959,7 @@ function createExtensionHarness( options )
}
} );
var context = {
- subscriptions: { push: function() {} },
+ subscriptions: [],
workspaceState: options.workspaceState || matrixHelpers.createWorkspaceState(),
globalState: options.globalState || matrixHelpers.createWorkspaceState(),
storageUri: matrixHelpers.createUri( '/tmp/storage' ),
@@ -1215,7 +1293,11 @@ function createExtensionHarness( options )
validateIconColours: function() { return undefined; }
},
'./icons.js': {
- validateIcons: function() { return undefined; }
+ validateIcons: function() { return undefined; },
+ resetCaches: function()
+ {
+ if( options.resetIconCaches ) { options.resetIconCaches(); }
+ }
},
'./highlights.js': {
init: function() {},
@@ -1377,14 +1459,23 @@ function createExtensionHarness( options )
}
return Promise.resolve( options.fileContents[ filePath ] );
},
- readdir: function() { return Promise.resolve( [] ); },
- unlink: function() { return Promise.resolve(); }
+ readdir: function( folder )
+ {
+ return options.cacheReaddir ? options.cacheReaddir( folder ) : Promise.resolve( [] );
+ },
+ unlink: function( file )
+ {
+ return options.cacheUnlink ? options.cacheUnlink( file ) : Promise.resolve();
+ }
}
},
- treeify: { asTree: function() { return ''; } },
child_process: {
execFile: function( executable, args, execOptions, callback )
{
+ if( options.execFileImpl )
+ {
+ return options.execFileImpl( executable, args, execOptions, callback );
+ }
callback( null, 'head', '' );
}
}
@@ -1403,6 +1494,7 @@ function createExtensionHarness( options )
return {
extension: extension,
+ config: configStub,
context: context,
options: options,
get provider()
@@ -1484,6 +1576,53 @@ function createDeferred()
};
}
+function createControlledTimers()
+{
+ var timeouts = [];
+ var intervals = [];
+
+ function createHandle( callback, delay )
+ {
+ return {
+ callback: callback,
+ delay: delay,
+ cancelled: false,
+ unref: function() {}
+ };
+ }
+
+ return {
+ timeouts: timeouts,
+ intervals: intervals,
+ setTimeout: function( callback, delay )
+ {
+ var handle = createHandle( callback, delay );
+ timeouts.push( handle );
+ return handle;
+ },
+ clearTimeout: function( handle )
+ {
+ if( handle )
+ {
+ handle.cancelled = true;
+ }
+ },
+ setInterval: function( callback, delay )
+ {
+ var handle = createHandle( callback, delay );
+ intervals.push( handle );
+ return handle;
+ },
+ clearInterval: function( handle )
+ {
+ if( handle )
+ {
+ handle.cancelled = true;
+ }
+ }
+ };
+}
+
function summarizeNotebookResults( results )
{
return results.map( function( result )
@@ -1543,6 +1682,51 @@ function fireVisibleNotebookEditorsChanged( harness, notebooksToShow, activeNote
QUnit.module( "extension scan parity" );
+[ false, true ].forEach( function( combined )
+{
+ QUnit.test( "regex configuration changes refresh existing editors" + ( combined ? " with tag groups" : "" ), async function( assert )
+ {
+ var document = matrixHelpers.createDocument( '/workspace/regex-change.js', '// TODO first\n// FIXME second' );
+ var order = [];
+ var changed = false;
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ visibleTextEditors: [ { document: document } ],
+ fileContents: {},
+ scanDocumentImpl: function()
+ {
+ order.push( 'scan' );
+ return [ { uri: document.uri, actualTag: changed ? 'FIXME' : 'TODO',
+ displayText: changed ? 'second' : 'first', continuationText: [] } ];
+ }
+ } );
+ harness.config.refreshTagGroupLookup = function() { order.push( 'groups' ); };
+ harness.extension.activate( harness.context );
+ await matrixHelpers.flushAsyncWork();
+ var previousHighlights = harness.highlightCalls.length;
+ order.length = 0;
+ changed = true;
+ var settings = [ 'better-todo-tree', 'better-todo-tree.regex', 'better-todo-tree.regex.regex' ];
+ if( combined )
+ {
+ settings.push( 'better-todo-tree.general.tagGroups', 'better-todo-tree.highlights.customHighlight' );
+ }
+ harness.vscode.workspaceListeners.configuration( {
+ affectsConfiguration: function( name ) { return settings.indexOf( name ) !== -1; }
+ } );
+ await matrixHelpers.flushAsyncWork();
+ assert.ok( order.indexOf( 'scan' ) >= 0, 'existing document rescanned' );
+ assert.ok( harness.highlightCalls.length > previousHighlights, 'existing editor redecorated' );
+ assert.equal( harness.provider.replaceCalls.slice( -1 )[ 0 ].results[ 0 ].actualTag, 'FIXME' );
+ if( combined )
+ {
+ assert.equal( order[ 0 ], 'groups', 'group lookup refresh precedes rescanning' );
+ }
+ harness.extension.deactivate();
+ } );
+} );
+
QUnit.test( "issue #41 total status bar uses status counts and activity badge uses activity counts", function( assert )
{
var harness = createExtensionHarness( {
@@ -1663,6 +1847,54 @@ QUnit.test( "open-files mode stores canonical document results through the refre
} );
} );
+QUnit.test( "open-files scan excludes cached documents without tabs and retains hidden tabs", function( assert )
+{
+ var hidden = matrixHelpers.createDocument( '/tmp/hidden-tab.js', '// TODO hidden tab' );
+ var cached = matrixHelpers.createDocument( '/tmp/closed-tab.js', '// TODO closed tab' );
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ textDocuments: [ hidden, cached ],
+ openTabUris: [ hidden.uri ],
+ visibleTextEditors: [],
+ documentResults: [],
+ fileContents: {}
+ } );
+ harness.extension.activate( harness.context );
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ assert.deepEqual( harness.scanDocumentCalls.map( function( document ) { return document.fileName; } ),
+ [ '/tmp/hidden-tab.js' ] );
+ } );
+} );
+
+QUnit.test( "closing the final tab clears open-files results before document disposal", function( assert )
+{
+ var document = matrixHelpers.createDocument( '/tmp/last-tab.js', '// TODO last tab' );
+ var tabs = [ document.uri ];
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ textDocuments: [ document ],
+ openTabUris: tabs,
+ visibleTextEditors: [],
+ documentResults: [ { uri: document.uri, actualTag: 'TODO', displayText: 'last tab', continuationText: [] } ],
+ fileContents: {}
+ } );
+ harness.extension.activate( harness.context );
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ assert.equal( harness.provider.latestResultsByUri.size, 1 );
+ tabs.length = 0;
+ harness.windowListeners.tabs( { opened: [], changed: [], closed: [ { input: { uri: document.uri } } ] } );
+ return waitForDelay( 0 ).then( matrixHelpers.flushAsyncWork );
+ } ).then( function()
+ {
+ assert.equal( harness.provider.latestResultsByUri.size, 0 );
+ assert.equal( harness.scanDocumentCalls.length, 1 );
+ } );
+} );
+
QUnit.test( "open-files startup scan includes visible editors restored after activation", function( assert )
{
var fixture = [ {
@@ -4866,6 +5098,49 @@ QUnit.test( "registered command boundary uses manifest titles for operation name
} );
} );
+[ false, true ].forEach( function( failDeletion )
+{
+ QUnit.test( 'cache reset waits for deletions before invalidation, failure=' + failDeletion, async function( assert )
+ {
+ var delayed = createDeferred();
+ var resets = 0;
+ var completed = false;
+ var failure = new Error( 'icon deletion denied' );
+ var harness = createExtensionHarness( {
+ scanMode: 'open files', scanAtStartup: false, fileContents: {},
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ resetIconCaches: function() { resets += 1; },
+ cacheReaddir: function( folder )
+ {
+ return Promise.resolve( folder === '/tmp/global-storage' ? [ 'first.svg', 'last.svg' ] : [] );
+ },
+ cacheUnlink: function( file )
+ {
+ if( file.endsWith( 'last.svg' ) ) { return delayed.promise; }
+ return failDeletion ? Promise.reject( failure ) : Promise.resolve();
+ }
+ } );
+ await harness.extension.activate( harness.context );
+ await matrixHelpers.flushAsyncWork();
+ var operation = harness.vscode.commandHandlers[ 'better-todo-tree.resetCache' ]().then( function()
+ {
+ assert.notOk( failDeletion, 'successful reset resolves' );
+ }, function( error )
+ {
+ assert.ok( failDeletion, 'failed deletion rejects' );
+ assert.strictEqual( error.errors[ 0 ].errors[ 0 ], failure );
+ assert.strictEqual( error.reportedToUser, true );
+ } ).then( function() { completed = true; } );
+ await matrixHelpers.flushAsyncWork();
+ assert.strictEqual( resets, 0, 'invalidation waits for every deletion' );
+ assert.strictEqual( completed, false, 'command waits for every deletion' );
+ delayed.resolve();
+ await operation;
+ assert.strictEqual( resets, 1, 'completed deletions invalidate paths on success and failure' );
+ harness.extension.deactivate();
+ } );
+} );
+
QUnit.test( "registered command boundary reports setting write failures once", function( assert )
{
var harness = createExtensionHarness( {
@@ -4932,6 +5207,39 @@ QUnit.test( "exportTree reports document open failures through the command bound
} );
} );
+QUnit.test( "exportTree uses a fresh virtual document generation for repeated paths", function( assert )
+{
+ var openedUris = [];
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ openTextDocumentImpl: function( uri )
+ {
+ openedUris.push( uri );
+ return {};
+ },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ return harness.vscode.commandHandlers[ 'better-todo-tree.exportTree' ]();
+ } ).then( function()
+ {
+ return harness.vscode.commandHandlers[ 'better-todo-tree.exportTree' ]();
+ } ).then( function()
+ {
+ assert.equal( openedUris.length, 2 );
+ assert.equal( openedUris[ 0 ].path, openedUris[ 1 ].path );
+ assert.equal( openedUris[ 0 ].query, 'generation=1' );
+ assert.equal( openedUris[ 1 ].query, 'generation=2' );
+ assert.notEqual( openedUris[ 0 ].toString(), openedUris[ 1 ].toString() );
+ } );
+} );
+
QUnit.test( "revealInFile reports VS Code open failures through the command boundary", function( assert )
{
var harness = createExtensionHarness( {
@@ -4967,7 +5275,240 @@ QUnit.test( "revealInFile reports VS Code open failures through the command boun
} );
} );
-QUnit.test( "markdown migration writes the shared default regex source", function( assert )
+QUnit.test( "revealInFile disposes line flashes without writing to a closed editor", function( assert )
+{
+ var timers = createControlledTimers();
+ var decorationWrites = [];
+ var document = matrixHelpers.createDocument( '/workspace/file.js', '// TODO item' );
+ var editor = {
+ document: document,
+ selection: { active: { line: 0, character: 0 } },
+ setDecorations: function( decoration, ranges )
+ {
+ if( document.isClosed === true )
+ {
+ throw new Error( 'closed editor write' );
+ }
+ decorationWrites.push( { decoration: decoration, ranges: ranges } );
+ }
+ };
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ activeTextEditor: editor,
+ timerStubs: timers,
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ return harness.vscode.commandHandlers[ 'better-todo-tree.revealInFile' ]( document.uri, undefined );
+ } ).then( function()
+ {
+ var flashTimer = timers.timeouts.find( function( timer ) { return timer.delay === 150; } );
+
+ assert.ok( flashTimer );
+ assert.equal( decorationWrites.length, 1 );
+ assert.equal( decorationWrites[ 0 ].ranges.length, 1 );
+
+ document.isClosed = true;
+ harness.vscode.window.activeTextEditor = undefined;
+ flashTimer.callback();
+
+ assert.equal( decorationWrites.length, 1 );
+ assert.equal( harness.vscode.decorationTypes[ 0 ].disposeCalls, 1 );
+ harness.extension.deactivate();
+ } );
+} );
+
+QUnit.test( "active editor tracking cancels stale delayed reveals", function( assert )
+{
+ var timers = createControlledTimers();
+ var requestedFiles = [];
+ var firstDocument = matrixHelpers.createDocument( '/workspace/first.js', '// TODO first' );
+ var secondDocument = matrixHelpers.createDocument( '/workspace/second.js', '// TODO second' );
+ var firstEditor = { document: firstDocument };
+ var secondEditor = { document: secondDocument };
+ var treeElement = { fsPath: '/workspace/second.js' };
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ trackFile: true,
+ treeViewVisible: true,
+ treeElement: treeElement,
+ activeTextEditor: firstEditor,
+ visibleTextEditors: [ firstEditor, secondEditor ],
+ timerStubs: timers,
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ getElementImpl: function( filename )
+ {
+ requestedFiles.push( filename );
+ return treeElement;
+ },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+ harness.vscode.workspaceListeners.activeEditor( firstEditor );
+ harness.vscode.window.activeTextEditor = secondEditor;
+ harness.vscode.workspaceListeners.activeEditor( secondEditor );
+
+ var revealTimers = timers.timeouts.filter( function( timer ) { return timer.delay === 500; } );
+
+ assert.equal( revealTimers.length, 2 );
+ assert.true( revealTimers[ 0 ].cancelled );
+ assert.false( revealTimers[ 1 ].cancelled );
+ revealTimers[ 1 ].callback();
+
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ assert.deepEqual( requestedFiles, [ '/workspace/second.js' ] );
+ assert.equal( harness.vscode.treeViews[ 0 ].revealCalls.length, 1 );
+ assert.strictEqual( harness.vscode.treeViews[ 0 ].revealCalls[ 0 ].element, treeElement );
+ harness.extension.deactivate();
+ } );
+} );
+
+QUnit.test( "reveal command propagates tree view failures through the command boundary", function( assert )
+{
+ var document = matrixHelpers.createDocument( '/workspace/file.js', '// TODO item' );
+ var editor = { document: document };
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ treeViewVisible: true,
+ treeElement: { fsPath: document.fileName },
+ activeTextEditor: editor,
+ visibleTextEditors: [ editor ],
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ revealImpl: function()
+ {
+ return Promise.reject( new Error( 'tree reveal failed' ) );
+ },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ return harness.vscode.commandHandlers[ 'better-todo-tree.reveal' ]().then( function()
+ {
+ assert.ok( false, 'reveal rejects tree view failures' );
+ }, function( error )
+ {
+ assert.equal( error.message, 'tree reveal failed' );
+ assert.deepEqual( harness.vscode.errorMessages, [
+ 'Better Todo Tree: failed to reveal active editor in tree (tree reveal failed)'
+ ] );
+ harness.extension.deactivate();
+ } );
+ } );
+} );
+
+QUnit.test( "deactivation cancels scheduled work and disposes activation-owned resources", function( assert )
+{
+ var timers = createControlledTimers();
+ var document = matrixHelpers.createDocument( '/workspace/file.js', '// TODO item' );
+ var editor = {
+ document: document,
+ selection: { active: { line: 0, character: 0 } },
+ setDecorations: function() {}
+ };
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ debug: true,
+ automaticGitRefreshInterval: 1,
+ periodicRefreshInterval: 1,
+ activeTextEditor: editor,
+ timerStubs: timers,
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ return harness.vscode.commandHandlers[ 'better-todo-tree.revealInFile' ]( document.uri, undefined );
+ } ).then( function()
+ {
+ assert.equal( harness.vscode.outputChannels.length, 1 );
+ assert.equal( harness.vscode.decorationTypes.length, 1 );
+ assert.equal( timers.intervals.length, 2 );
+
+ harness.extension.deactivate();
+ harness.extension.deactivate();
+
+ assert.true( timers.timeouts.every( function( timer ) { return timer.cancelled; } ) );
+ assert.true( timers.intervals.every( function( timer ) { return timer.cancelled; } ) );
+ assert.equal( harness.vscode.outputChannels[ 0 ].disposeCalls, 1 );
+ assert.equal( harness.vscode.decorationTypes[ 0 ].disposeCalls, 1 );
+ assert.equal( harness.provider.clearCalls, 1 );
+ } );
+} );
+
+QUnit.test( "Git callbacks from a disposed activation preserve the new activation guard", function( assert )
+{
+ var timers = createControlledTimers();
+ var callbacks = [];
+ var harness = createExtensionHarness( {
+ scanMode: 'open files', scanAtStartup: false,
+ automaticGitRefreshInterval: 1, timerStubs: timers,
+ workspaceFolders: [ { uri: matrixHelpers.createUri( '/workspace' ), name: 'workspace' } ],
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ fileContents: {},
+ execFileImpl: function( executable, args, options, callback ) { callbacks.push( callback ); }
+ } );
+
+ harness.extension.activate( harness.context );
+ return matrixHelpers.flushAsyncWork().then( function()
+ {
+ timers.intervals[ 0 ].callback();
+ harness.extension.deactivate();
+ harness.extension.activate( harness.context );
+ return matrixHelpers.flushAsyncWork();
+ } ).then( function()
+ {
+ var newInterval = timers.intervals[ timers.intervals.length - 1 ];
+ newInterval.callback();
+ assert.equal( callbacks.length, 2 );
+ callbacks[ 0 ]( null, 'old-head', '' );
+ newInterval.callback();
+ assert.equal( callbacks.length, 2, 'the new Git poll remains in flight' );
+ callbacks[ 1 ]( null, 'new-head', '' );
+ newInterval.callback();
+ assert.equal( callbacks.length, 3, 'the completed new poll permits another request' );
+ callbacks[ 2 ]( null, 'new-head', '' );
+ harness.extension.deactivate();
+ } );
+} );
+
+QUnit.test( "deactivation settles pending tree UI work without touching the disposed view", function( assert )
+{
+ var harness = createExtensionHarness( {
+ scanMode: 'open files',
+ scanAtStartup: false,
+ resourceConfig: { isDefaultRegex: true, enableMultiLine: false, regexCaseSensitive: true },
+ fileContents: {}
+ } );
+
+ harness.extension.activate( harness.context );
+
+ var expansionPromise = harness.vscode.commandHandlers[ 'better-todo-tree.expand' ]();
+ harness.extension.deactivate();
+
+ return expansionPromise.then( function()
+ {
+ assert.equal( harness.vscode.treeViews[ 0 ].revealCalls.length, 0 );
+ } );
+} );
+
+QUnit.test( "explicit historical markdown regex remains unchanged without migration prompts", function( assert )
{
var harness = createExtensionHarness( {
scanMode: 'open files',
@@ -4989,14 +5530,10 @@ QUnit.test( "markdown migration writes the shared default regex source", functio
return matrixHelpers.flushAsyncWork();
} ).then( function()
{
- assert.deepEqual( harness.vscode.informationMessages, [
- 'Better Todo Tree: There is now an improved method of locating markdown TODOs. Apply settings automatically?'
- ] );
- assert.ok( harness.vscode.configurationUpdates.some( function( update )
+ assert.deepEqual( harness.vscode.informationMessages, [] );
+ assert.notOk( harness.vscode.configurationUpdates.some( function( update )
{
- return update.key === 'regex.regex' &&
- update.value === actualUtils.DEFAULT_REGEX_SOURCE &&
- update.target === harness.vscode.ConfigurationTarget.Global;
+ return update.key === 'regex.regex';
} ) );
} );
} );
diff --git a/test/highlights.behavior.test.js b/test/highlights.behavior.test.js
index 8772374a..fc50dd1a 100644
--- a/test/highlights.behavior.test.js
+++ b/test/highlights.behavior.test.js
@@ -7,6 +7,8 @@ var issue888Helpers = require( './issue888Helpers.js' );
function createVscodeStub( highlightConfiguration, decorationLog )
{
+ var visibleTextEditorsListener;
+
function Position( line, character )
{
this.line = line;
@@ -29,10 +31,16 @@ function createVscodeStub( highlightConfiguration, decorationLog )
Position: Position,
Range: Range,
window: {
+ visibleTextEditors: [],
createTextEditorDecorationType: function( options )
{
decorationLog.push( options );
return options;
+ },
+ onDidChangeVisibleTextEditors: function( listener )
+ {
+ visibleTextEditorsListener = listener;
+ return { dispose: function() {} };
}
},
workspace: {
@@ -50,6 +58,11 @@ function createVscodeStub( highlightConfiguration, decorationLog )
}
};
}
+ },
+ setVisibleTextEditors: function( editors )
+ {
+ this.window.visibleTextEditors = editors;
+ visibleTextEditorsListener( editors );
}
};
}
@@ -229,6 +242,21 @@ QUnit.module( "behavioral highlights", function( hooks )
{
var originalVscode;
+ QUnit.test( "prototype-named custom tags retain every highlight range", function( assert )
+ {
+ var harness = createActualDetectionHighlightHarness( {
+ tags: [ 'constructor', '__proto__', 'toString' ],
+ type: 'tag',
+ text: '// constructor first\n// __proto__ second\n// toString third'
+ } );
+
+ assert.equal( harness.recorded.length, 3 );
+ assert.deepEqual( harness.recorded.map( function( ranges )
+ {
+ return ranges.map( function( item ) { return item.range.start.line; } );
+ } ), [ [ 0 ], [ 1 ], [ 2 ] ] );
+ } );
+
hooks.beforeEach( function()
{
originalVscode = require.cache[ require.resolve( './moduleHelpers.js' ) ];
@@ -412,7 +440,7 @@ QUnit.module( "behavioral highlights", function( hooks )
assert.strictEqual( decoration.dark.color, undefined );
} );
- QUnit.test( "issue #58 default colour scheme emits visible editor highlights", function( assert )
+ QUnit.test( "issue #58 explicit colour scheme emits visible editor highlights", function( assert )
{
var decorationLog = [];
var config = createAttributeConfig( {
@@ -473,6 +501,74 @@ QUnit.module( "behavioral highlights", function( hooks )
assert.equal( decoration.dark.color, 'white' );
} );
+ QUnit.test( "issue #110 foreground-only custom highlights receive no implicit background", function( assert )
+ {
+ var decorationLog = [];
+ var config = createAttributeConfig( {
+ tagList: [ 'TODO' ],
+ foregroundColourScheme: function()
+ {
+ return [ 'white' ];
+ },
+ backgroundColourScheme: function()
+ {
+ return [ 'red' ];
+ },
+ customHighlight: function()
+ {
+ return {
+ TODO: {
+ foreground: '#008888',
+ type: 'tag',
+ textDecoration: 'underline'
+ }
+ };
+ }
+ } );
+
+ actualUtils.init( config );
+ actualAttributes.init( config );
+
+ var highlights = helpers.loadWithStubs( '../src/highlights.js', {
+ vscode: createVscodeStub( { enabled: true }, decorationLog ),
+ './config.js': {
+ customHighlight: config.customHighlight.bind( config ),
+ subTagRegex: function() { return regexRegistry.pattern( 'subTagPrefixCapture' ); },
+ tagGroup: function() { return undefined; }
+ },
+ './utils.js': actualUtils,
+ './attributes.js': actualAttributes,
+ './icons.js': {
+ getGutterIcon: function()
+ {
+ return { dark: '/tmp/gutter-dark.svg', light: '/tmp/gutter-light.svg' };
+ }
+ },
+ './detection.js': {
+ scanDocument: function()
+ {
+ return [];
+ }
+ },
+ './extensionIdentity.js': {
+ getSetting: function( setting, defaultValue )
+ {
+ return defaultValue;
+ }
+ }
+ } );
+
+ highlights.init( { subscriptions: { push: function() {} } }, function() {} );
+
+ var decoration = highlights.getDecoration( 'TODO' );
+
+ assert.equal( decoration.light.color, '#008888' );
+ assert.equal( decoration.dark.color, '#008888' );
+ assert.strictEqual( decoration.light.backgroundColor, undefined );
+ assert.strictEqual( decoration.dark.backgroundColor, undefined );
+ assert.equal( decoration.textDecoration, 'underline' );
+ } );
+
QUnit.test( "issue #75 identical yellow backgrounds use identical black foregrounds", function( assert )
{
var decorationLog = [];
@@ -957,6 +1053,92 @@ QUnit.module( "behavioral highlights", function( hooks )
assert.equal( recorded[ 0 ][ 0 ].range.end.character, 2 );
} );
+ QUnit.test( "delayed highlights skip editors removed without a visibility event", function( assert )
+ {
+ var done = assert.async();
+ var scanCount = 0;
+ var vscodeStub = createVscodeStub( { enabled: true }, [] );
+ var highlights = helpers.loadWithStubs( '../src/highlights.js', {
+ vscode: vscodeStub,
+ './config.js': {
+ tagGroup: function() { return undefined; }
+ },
+ './detection.js': {
+ scanDocument: function()
+ {
+ scanCount++;
+ return [];
+ }
+ },
+ './extensionIdentity.js': {
+ getSetting: function( setting, defaultValue )
+ {
+ return setting === 'highlights.highlightDelay' ? 5 : defaultValue;
+ }
+ }
+ } );
+ var editor = {
+ viewColumn: 1,
+ document: createDocument( '// TODO body' ),
+ setDecorations: function() {}
+ };
+
+ highlights.init( { subscriptions: { push: function() {} } }, function() {} );
+ vscodeStub.window.visibleTextEditors = [ editor ];
+ highlights.triggerHighlight( editor );
+ vscodeStub.window.visibleTextEditors = [];
+
+ setTimeout( function()
+ {
+ assert.equal( scanCount, 0 );
+ highlights.resetCaches();
+ done();
+ }, 25 );
+ } );
+
+ QUnit.test( "visibility changes cancel pending highlights for removed editors", function( assert )
+ {
+ var done = assert.async();
+ var scanCount = 0;
+ var vscodeStub = createVscodeStub( { enabled: true }, [] );
+ var highlights = helpers.loadWithStubs( '../src/highlights.js', {
+ vscode: vscodeStub,
+ './config.js': {
+ tagGroup: function() { return undefined; }
+ },
+ './detection.js': {
+ scanDocument: function()
+ {
+ scanCount++;
+ return [];
+ }
+ },
+ './extensionIdentity.js': {
+ getSetting: function( setting, defaultValue )
+ {
+ return setting === 'highlights.highlightDelay' ? 5 : defaultValue;
+ }
+ }
+ } );
+ var editor = {
+ viewColumn: 1,
+ document: createDocument( '// TODO body' ),
+ setDecorations: function() {}
+ };
+
+ highlights.init( { subscriptions: { push: function() {} } }, function() {} );
+ vscodeStub.window.visibleTextEditors = [ editor ];
+ highlights.triggerHighlight( editor );
+ vscodeStub.setVisibleTextEditors( [] );
+
+ setTimeout( function()
+ {
+ assert.equal( scanCount, 0 );
+ highlights.resetCaches();
+ done();
+ }, 25 );
+ } );
+
QUnit.test( "repeated highlights reuse cached decoration types for unchanged content", function( assert )
{
var creationCount = 0;
@@ -975,10 +1157,15 @@ QUnit.module( "behavioral highlights", function( hooks )
this.end = end;
},
window: {
+ visibleTextEditors: [],
createTextEditorDecorationType: function( options )
{
creationCount++;
return options;
+ },
+ onDidChangeVisibleTextEditors: function()
+ {
+ return { dispose: function() {} };
}
}
},
diff --git a/test/highlights.matrix.test.js b/test/highlights.matrix.test.js
index 9dfe2458..ecc590a1 100644
--- a/test/highlights.matrix.test.js
+++ b/test/highlights.matrix.test.js
@@ -27,6 +27,10 @@ function createVscodeStub( highlightConfiguration, decorationLog )
Position: Position,
Range: Range,
window: {
+ onDidChangeVisibleTextEditors: function()
+ {
+ return { dispose: function() {} };
+ },
createTextEditorDecorationType: function( options )
{
decorationLog.push( options );
@@ -121,6 +125,13 @@ function createHarness( options )
return { dark: '/tmp/gutter.svg', light: '/tmp/gutter.svg' };
}
},
+ './extensionIdentity.js': {
+ getSetting: function( key, defaultValue )
+ {
+ var values = { 'highlights.enabled': true, 'highlights.highlightDelay': 0 };
+ return Object.prototype.hasOwnProperty.call( values, key ) ? values[ key ] : defaultValue;
+ }
+ },
'./detection.js': {
scanDocument: function()
{
diff --git a/test/icons.behavior.test.js b/test/icons.behavior.test.js
index 568b7943..5cd937bd 100644
--- a/test/icons.behavior.test.js
+++ b/test/icons.behavior.test.js
@@ -156,12 +156,108 @@ QUnit.module( 'behavioral icons', function( hooks )
var treeIcon = icons.getTreeIcon( context, 'TODO:' );
var gutterIcon = icons.getGutterIcon( context, 'TODO:' );
- assert.equal( path.basename( treeIcon.dark ), 'todo-bug-ff4545.svg' );
- assert.equal( path.basename( gutterIcon.dark ), 'todo-bug-ff4545.svg' );
+ assert.equal( treeIcon.dark, gutterIcon.dark );
+ assert.ok( fs.readFileSync( treeIcon.dark, 'utf8' ).includes( '#ff4545' ) );
assert.ok( fs.existsSync( treeIcon.dark ) );
assert.ok( fs.existsSync( gutterIcon.dark ) );
} );
+ QUnit.test( 'cache reset recreates removed icon storage and generated assets', function( assert )
+ {
+ var storagePath = fs.mkdtempSync( path.join( os.tmpdir(), 'better-todo-tree-icons-' ) );
+ tempDirectories.push( storagePath );
+ var config = createAttributeConfig( {
+ customHighlight: function()
+ {
+ return { TODO: { icon: 'flame', iconColour: '#e85dba' } };
+ }
+ } );
+ actualUtils.init( config );
+ actualAttributes.init( config );
+ var icons = helpers.loadWithStubs( '../src/icons.js', { vscode: createVscodeStub() } );
+ var context = createContext( storagePath );
+ var initial = icons.getGutterIcon( context, 'TODO' );
+ var svg = fs.readFileSync( initial.dark, 'utf8' );
+ fs.rmSync( storagePath, { recursive: true } );
+ icons.resetCaches();
+ var regenerated = icons.getGutterIcon( context, 'TODO' );
+ assert.deepEqual( regenerated, initial );
+ assert.equal( fs.readFileSync( regenerated.dark, 'utf8' ), svg );
+ assert.deepEqual( icons.getTreeIcon( context, 'TODO' ), regenerated );
+ } );
+
+ QUnit.test( 'every codicon and product alias resolves an authoritative vector glyph', function( assert )
+ {
+ var aliases = require( '../src/codiconAliases.json' );
+ var storagePath = fs.mkdtempSync( path.join( os.tmpdir(), 'better-todo-tree-glyphs-' ) );
+ tempDirectories.push( storagePath );
+ var custom = Object.fromEntries( Object.keys( aliases ).map( function( name )
+ {
+ return [ name, { icon: '$(' + name + ')', iconColour: '#ff4545' } ];
+ } ) );
+ var config = createAttributeConfig( { customHighlight: function() { return custom; } } );
+ actualUtils.init( config );
+ actualAttributes.init( config );
+ var icons = helpers.loadWithStubs( '../src/icons.js', { vscode: createVscodeStub() } );
+ var context = createContext( storagePath );
+ var paths = new Map();
+ Object.keys( aliases ).forEach( function( name )
+ {
+ var icon = icons.getGutterIcon( context, name );
+ var svg = fs.readFileSync( icon.dark, 'utf8' );
+ assert.ok( svg.includes( '