coc-git 2.7.10 → 2.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,61 @@
1
+ # AGENTS.md
2
+
3
+ ## Repository purpose
4
+
5
+ `coc-git` is the Git integration extension for `coc.nvim`. Its runtime entry point is `src/index.ts`; the published entry point is the bundled `lib/index.js`. The extension owns Git-backed buffer state, gutters and blame text, conflict actions, Coc lists, GitHub/GitLab issue completion, and the commands/keymaps declared in `package.json`.
6
+
7
+ ## Code map
8
+
9
+
10
+ - `src/index.ts` is the activation and registration layer. Register new Coc commands, `<Plug>` keymaps, lists, completion providers, and disposables here.
11
+ - `src/manager.ts` coordinates editor events, live `GitBuffer` instances, configuration, and user-facing operations.
12
+ - `src/model/git.ts` is the low-level child-process wrapper around the configured Git executable.
13
+ - `src/model/repo.ts` implements repository-scoped Git operations; put Git command construction and output parsing there rather than in the activation layer.
14
+ - `src/model/resolver.ts` resolves documents and working directories to repositories and relative paths.
15
+ - `src/model/buffer.ts` owns per-buffer diffs, signs, blame, folds, and conflict state.
16
+ - `src/model/service.ts` owns resolver/repository lifetimes and constructs `GitBuffer` instances.
17
+ - `src/lists/` contains Coc list implementations (`gstatus`, `gfiles`, `gchanges`, `gchunks`, `branches`, `commits`, and `bcommits`). List-specific actions and rendering belong in the corresponding list class.
18
+ - `src/source.ts` implements GitHub/GitLab issue loading plus the `issues` list/completion source.
19
+ - `src/types.ts` is the shared contract for diffs, conflicts, and the configuration object passed into buffer/model code.
20
+ - `package.json` is also the Coc extension manifest: command names, activation, and all user-visible configuration schemas live there.
21
+ - `Readme.md` documents the public commands, keymaps, lists, environment variables, and settings.
22
+
23
+ ## Public-contract synchronization
24
+
25
+ When changing a user-facing feature, update every surface that represents the same contract:
26
+
27
+ - A new `git.*` command needs a `commands.registerCommand` registration in `src/index.ts` and an entry under `contributes.commands` in `package.json`. Add it to `Readme.md` when it is intended for direct use.
28
+ - A new `<Plug>(coc-git-...)` mapping needs a `workspace.registerKeymap` registration in `src/index.ts` and matching usage documentation in `Readme.md`.
29
+ - A new `git.*` setting needs a schema entry under `contributes.configuration.properties` in `package.json`, a typed field in `src/types.ts` when consumed beyond the registration layer, and loading/default logic in `DocumentManager.loadConfiguration()` in `src/manager.ts`. Keep defaults identical across those locations and `Readme.md`.
30
+ - A new list must be implemented under `src/lists/`, registered through `listManager` in `src/index.ts`, and added to the documented list names.
31
+ - Preserve the exported `ExtensionApi` shape in `src/index.ts` unless the API change is intentional; other Coc extensions can consume `git`, `resolver`, and `manager` from activation.
32
+
33
+ ## Git and editor behavior
34
+
35
+ - Use the configured executable discovered from `git.command`; do not hard-code an executable path. Model-layer Git execution should flow through `Git`, `GitRepo`, or the existing command helpers.
36
+ - Keep Git work asynchronous. Do not introduce synchronous child-process calls into buffer refresh, cursor movement, completion, or list loading paths.
37
+ - Pass arguments as arrays to `Git.exec`, `Git.stream`, or `spawnCommand` when filenames or revisions are involved. The repository already supports paths containing spaces; avoid building an unescaped shell string unless the operation specifically requires an interactive terminal command.
38
+ - Preserve cancellation and disposal behavior around spawned processes and Coc event listeners. Anything registered during activation or manager construction must be owned by the existing `subscriptions`/`disposables` lifecycle.
39
+ - Buffer refreshes are event-driven (`TextChange`, writes when realtime gutters are disabled, `FocusGained`, and `BufEnter`). Changes to diff or blame behavior must account for stale async results and for buffers disposed while repository resolution is still running.
40
+ - Keep Vim and Neovim compatibility. Virtual text, floating windows, namespaces, terminal operations, and popup behavior must retain the existing capability checks or Coc abstractions; do not use a Neovim-only API on an unconditional path.
41
+ - Git output is normalized under `LC_ALL`/`LANG=en_US.UTF-8` and decoded through `iconv-lite`. Parsing code must not depend on the user's localized Git messages.
42
+ - GitHub issue access uses `GITHUB_API_TOKEN`; GitLab access uses `GITLAB_PRIVATE_TOKEN` and `git.gitlab.hosts`. Do not log tokens or put them into rendered list/completion items.
43
+
44
+ ## Generated output and dependencies
45
+
46
+ - Do not edit or commit `lib/index.js`; `lib/` is generated and ignored. Change TypeScript under `src/` and rebuild it with `npm run build`.
47
+ - `esbuild.js` must continue to bundle `src/index.ts` for Node while leaving `coc.nvim` external. The bundle target is `node10.12`; avoid emitting runtime syntax or APIs that violate that target unless the target is deliberately raised.
48
+ - Use npm for dependency and lockfile changes. CI installs with `npm ci`, so `package.json` and `package-lock.json` must stay synchronized.
49
+ - `npm run lint` is TypeScript checking (`tsc -p tsconfig.json`), not an ESLint formatting pass. The project enables `noUnusedLocals` and emits nothing during this check.
50
+
51
+ ## Verification
52
+
53
+ Run checks according to the affected subsystem:
54
+
55
+ - Type/config/registration changes: `npm run lint` and `npm run build`.
56
+ - Git parsing, repository, buffer, manager, list, source, command, or keymap behavior: `npm test`.
57
+ - Vim-specific behavior: `npm run test:integration:vim`.
58
+ - Neovim-specific behavior: `npm run test:integration:nvim`.
59
+ - Changes intended to pass CI must succeed in both editor variants. The GitHub Actions matrix uses Node 22 and runs typechecking plus the Vim and Neovim integration suites.
60
+
61
+ For manual editor verification, load the rebuilt extension through Coc and exercise the exact public surface changed: the relevant `:CocCommand git.*`, `<Plug>(coc-git-*)` mapping, `:CocList` list, gutter/blame update, conflict action, or issue completion flow. Use a temporary Git repository containing the required staged, unstaged, untracked, renamed, conflicted, or non-UTF-8-path state rather than testing against this checkout's working tree.
package/Readme.md CHANGED
@@ -152,6 +152,8 @@ In your vim/neovim, run command:
152
152
 
153
153
  - `git.commitFiles.splitCommand`: Command used to open the changed-files TreeView for a commit, default: `"belowright 40vs"`
154
154
 
155
+ - `git.statusTree.splitCommand`: Command used to open the Git status TreeView, default: `"belowright 40vs"`
156
+
155
157
  - `git.floatConfig`: Configure style of float window/popup, extends from floatFactory.floatConfig, default: `{}`.
156
158
 
157
159
  - `git.gitlab.hosts`: Custom GitLab hosts, default: `["gitlab.com"]`
@@ -279,6 +281,7 @@ related commands.
279
281
  - `:CocCommand git.copyUrl` Copy url of current line to clipboard.
280
282
  - `:CocCommand git.copyPermalink` Copy a permalink for the current line to clipboard.
281
283
  - `:CocCommand git.refresh` Refresh Git information for all buffers.
284
+ - `:CocCommand git.statusTree` Open files reported by Git status in a TreeView. File nodes show staged and unstaged status; opening a file jumps to its first changed line when Git can determine one.
282
285
  - `:CocCommand git.nextChunk` Navigate to the next chunk.
283
286
  - `:CocCommand git.prevChunk` Navigate to the previous chunk.
284
287
  - `:CocCommand git.chunkInfo` Show chunk info under cursor.
@@ -351,6 +354,21 @@ line with a commit.
351
354
 
352
355
  Use `git.commitFiles.splitCommand` to configure how the TreeView split opens.
353
356
 
357
+ #### Git Status TreeView
358
+
359
+ Use `:CocCommand git.statusTree` to display staged, unstaged, conflicted, and
360
+ untracked files grouped by directory. Each file shows Git's two-column status
361
+ and a readable staged/unstaged description. Press `<CR>` on a file to open its
362
+ working-tree version in the previous editor window. For tracked files, the
363
+ cursor moves to the first changed line when Git provides a diff location;
364
+ otherwise it opens at line 1. Files deleted from the working tree open the
365
+ read-only `HEAD` version through the `coc-git:` scheme. The root node refreshes
366
+ the status snapshot. File actions follow each file's status: `Add` stages
367
+ unstaged or untracked files, `Restore staged changes` unstages a file, and
368
+ `Restore working tree changes` discards its tracked working-tree changes.
369
+
370
+ Use `git.statusTree.splitCommand` to configure how the TreeView split opens.
371
+
354
372
  For more advance usage, checkout `:h coc-list`.
355
373
 
356
374
  ### Issue autocomplete from multiple GitHub repositories
package/history.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 2.7.11
2
+
3
+ - add AGENTS.md (34ea156)
4
+ - feat: add git status tree view (4bc3f6e)
5
+
1
6
  ## 2.7.10
2
7
 
3
8
  - docs: add MIT license (172bdca)
@@ -5,7 +10,6 @@
5
10
 
6
11
  ## 2.7.9
7
12
 
8
- - add a read-only TreeView for files changed by a commit
9
13
  - show complete commit files as decorated coc-git virtual documents with chunk navigation
10
14
  - docs: add coc-git logo (992064f)
11
15
  - feat: show staged git gutters and navigate chunks (9410092)
package/lib/index.js CHANGED
@@ -695,8 +695,8 @@ var require_windows = __commonJS({
695
695
  "node_modules/isexe/windows.js"(exports2, module2) {
696
696
  module2.exports = isexe;
697
697
  isexe.sync = sync;
698
- var fs5 = require("fs");
699
- function checkPathExt(path9, options) {
698
+ var fs6 = require("fs");
699
+ function checkPathExt(path10, options) {
700
700
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
701
701
  if (!pathext) {
702
702
  return true;
@@ -707,25 +707,25 @@ var require_windows = __commonJS({
707
707
  }
708
708
  for (var i = 0; i < pathext.length; i++) {
709
709
  var p = pathext[i].toLowerCase();
710
- if (p && path9.substr(-p.length).toLowerCase() === p) {
710
+ if (p && path10.substr(-p.length).toLowerCase() === p) {
711
711
  return true;
712
712
  }
713
713
  }
714
714
  return false;
715
715
  }
716
- function checkStat(stat, path9, options) {
716
+ function checkStat(stat, path10, options) {
717
717
  if (!stat.isSymbolicLink() && !stat.isFile()) {
718
718
  return false;
719
719
  }
720
- return checkPathExt(path9, options);
720
+ return checkPathExt(path10, options);
721
721
  }
722
- function isexe(path9, options, cb) {
723
- fs5.stat(path9, function(er, stat) {
724
- cb(er, er ? false : checkStat(stat, path9, options));
722
+ function isexe(path10, options, cb) {
723
+ fs6.stat(path10, function(er, stat) {
724
+ cb(er, er ? false : checkStat(stat, path10, options));
725
725
  });
726
726
  }
727
- function sync(path9, options) {
728
- return checkStat(fs5.statSync(path9), path9, options);
727
+ function sync(path10, options) {
728
+ return checkStat(fs6.statSync(path10), path10, options);
729
729
  }
730
730
  }
731
731
  });
@@ -735,14 +735,14 @@ var require_mode = __commonJS({
735
735
  "node_modules/isexe/mode.js"(exports2, module2) {
736
736
  module2.exports = isexe;
737
737
  isexe.sync = sync;
738
- var fs5 = require("fs");
739
- function isexe(path9, options, cb) {
740
- fs5.stat(path9, function(er, stat) {
738
+ var fs6 = require("fs");
739
+ function isexe(path10, options, cb) {
740
+ fs6.stat(path10, function(er, stat) {
741
741
  cb(er, er ? false : checkStat(stat, options));
742
742
  });
743
743
  }
744
- function sync(path9, options) {
745
- return checkStat(fs5.statSync(path9), options);
744
+ function sync(path10, options) {
745
+ return checkStat(fs6.statSync(path10), options);
746
746
  }
747
747
  function checkStat(stat, options) {
748
748
  return stat.isFile() && checkMode(stat, options);
@@ -766,7 +766,7 @@ var require_mode = __commonJS({
766
766
  // node_modules/isexe/index.js
767
767
  var require_isexe = __commonJS({
768
768
  "node_modules/isexe/index.js"(exports2, module2) {
769
- var fs5 = require("fs");
769
+ var fs6 = require("fs");
770
770
  var core;
771
771
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
772
772
  core = require_windows();
@@ -775,7 +775,7 @@ var require_isexe = __commonJS({
775
775
  }
776
776
  module2.exports = isexe;
777
777
  isexe.sync = sync;
778
- function isexe(path9, options, cb) {
778
+ function isexe(path10, options, cb) {
779
779
  if (typeof options === "function") {
780
780
  cb = options;
781
781
  options = {};
@@ -785,7 +785,7 @@ var require_isexe = __commonJS({
785
785
  throw new TypeError("callback not provided");
786
786
  }
787
787
  return new Promise(function(resolve, reject) {
788
- isexe(path9, options || {}, function(er, is) {
788
+ isexe(path10, options || {}, function(er, is) {
789
789
  if (er) {
790
790
  reject(er);
791
791
  } else {
@@ -794,7 +794,7 @@ var require_isexe = __commonJS({
794
794
  });
795
795
  });
796
796
  }
797
- core(path9, options || {}, function(er, is) {
797
+ core(path10, options || {}, function(er, is) {
798
798
  if (er) {
799
799
  if (er.code === "EACCES" || options && options.ignoreErrors) {
800
800
  er = null;
@@ -804,9 +804,9 @@ var require_isexe = __commonJS({
804
804
  cb(er, is);
805
805
  });
806
806
  }
807
- function sync(path9, options) {
807
+ function sync(path10, options) {
808
808
  try {
809
- return core.sync(path9, options || {});
809
+ return core.sync(path10, options || {});
810
810
  } catch (er) {
811
811
  if (options && options.ignoreErrors || er.code === "EACCES") {
812
812
  return false;
@@ -822,7 +822,7 @@ var require_isexe = __commonJS({
822
822
  var require_which = __commonJS({
823
823
  "node_modules/which/which.js"(exports2, module2) {
824
824
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
825
- var path9 = require("path");
825
+ var path10 = require("path");
826
826
  var COLON = isWindows ? ";" : ":";
827
827
  var isexe = require_isexe();
828
828
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -860,7 +860,7 @@ var require_which = __commonJS({
860
860
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
861
861
  const ppRaw = pathEnv[i];
862
862
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
863
- const pCmd = path9.join(pathPart, cmd);
863
+ const pCmd = path10.join(pathPart, cmd);
864
864
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
865
865
  resolve(subStep(p, i, 0));
866
866
  });
@@ -887,7 +887,7 @@ var require_which = __commonJS({
887
887
  for (let i = 0; i < pathEnv.length; i++) {
888
888
  const ppRaw = pathEnv[i];
889
889
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
890
- const pCmd = path9.join(pathPart, cmd);
890
+ const pCmd = path10.join(pathPart, cmd);
891
891
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
892
892
  for (let j = 0; j < pathExt.length; j++) {
893
893
  const cur = p + pathExt[j];
@@ -4537,7 +4537,7 @@ __export(index_exports, {
4537
4537
  formatBlameText: () => formatBlameText
4538
4538
  });
4539
4539
  module.exports = __toCommonJS(index_exports);
4540
- var import_coc18 = require("coc.nvim");
4540
+ var import_coc19 = require("coc.nvim");
4541
4541
 
4542
4542
  // src/constants.ts
4543
4543
  var DEFAULT_TYPES = [
@@ -4673,12 +4673,12 @@ var Bcommits = class extends import_coc.BasicList {
4673
4673
  }, { tabPersist: true });
4674
4674
  this.addAction("view", async (item, context) => {
4675
4675
  let { commit, root, file } = item.data;
4676
- let { window: window12 } = context;
4676
+ let { window: window13 } = context;
4677
4677
  let content = (await this.manager.git.exec(root, ["show", `${commit}:${file}`])).stdout;
4678
4678
  let lines = content.replace(/\n$/, "").split("\n");
4679
4679
  let name = await nvim.call("fnameescape", [`(${commit}) ${file}`]);
4680
4680
  nvim.pauseNotification();
4681
- nvim.call("win_gotoid", [window12.id], true);
4681
+ nvim.call("win_gotoid", [window13.id], true);
4682
4682
  nvim.command(`tabe ${name}`, true);
4683
4683
  nvim.call("append", [0, lines], true);
4684
4684
  nvim.command("normal! Gdd", true);
@@ -5169,6 +5169,21 @@ var import_safe2 = __toESM(require_safe());
5169
5169
  var import_fs = __toESM(require("fs"));
5170
5170
  var import_path3 = __toESM(require("path"));
5171
5171
 
5172
+ // src/model/statusEntry.ts
5173
+ function parseStatusEntries(output) {
5174
+ const result = [];
5175
+ const entries = output.split("\0");
5176
+ for (let i = 0; i < entries.length; i++) {
5177
+ const line = entries[i];
5178
+ if (!line) continue;
5179
+ result.push({ index: line[0], tree: line[1], relative: line.slice(3) });
5180
+ if (line[0] === "R" || line[0] === "C" || line[1] === "R" || line[1] === "C") {
5181
+ i++;
5182
+ }
5183
+ }
5184
+ return result;
5185
+ }
5186
+
5172
5187
  // src/util.ts
5173
5188
  var import_child_process = require("child_process");
5174
5189
  var import_coc5 = require("coc.nvim");
@@ -5335,20 +5350,20 @@ function findSystemGitWin32(base, onLookup) {
5335
5350
  return findSpecificGit(import_path2.default.join(base, "Git", "cmd", "git.exe"), onLookup);
5336
5351
  }
5337
5352
  function findGitWin32InPath(onLookup) {
5338
- const whichPromise = new Promise((c, e) => (0, import_which.default)("git.exe", (err, path9) => err ? e(err) : c(path9)));
5339
- return whichPromise.then((path9) => findSpecificGit(path9, onLookup));
5353
+ const whichPromise = new Promise((c, e) => (0, import_which.default)("git.exe", (err, path10) => err ? e(err) : c(path10)));
5354
+ return whichPromise.then((path10) => findSpecificGit(path10, onLookup));
5340
5355
  }
5341
5356
  function findGitWin32(onLookup) {
5342
5357
  return findSystemGitWin32(process.env["ProgramW6432"], onLookup).then(void 0, () => findSystemGitWin32(process.env["ProgramFiles(x86)"], onLookup)).then(void 0, () => findSystemGitWin32(process.env["ProgramFiles"], onLookup)).then(void 0, () => findSystemGitWin32(import_path2.default.join(process.env["LocalAppData"], "Programs"), onLookup)).then(void 0, () => findGitWin32InPath(onLookup));
5343
5358
  }
5344
- function findSpecificGit(path9, onLookup) {
5359
+ function findSpecificGit(path10, onLookup) {
5345
5360
  return new Promise((c, e) => {
5346
- onLookup(path9);
5361
+ onLookup(path10);
5347
5362
  const buffers = [];
5348
- const child = (0, import_child_process.spawn)(path9, ["--version"]);
5363
+ const child = (0, import_child_process.spawn)(path10, ["--version"]);
5349
5364
  child.stdout.on("data", (b) => buffers.push(b));
5350
5365
  child.on("error", cpErrorHandler(e));
5351
- child.on("exit", (code) => code ? e(new Error("Not found")) : c({ path: path9, version: parseVersion(Buffer.concat(buffers).toString("utf8").trim()) }));
5366
+ child.on("exit", (code) => code ? e(new Error("Not found")) : c({ path: path10, version: parseVersion(Buffer.concat(buffers).toString("utf8").trim()) }));
5352
5367
  });
5353
5368
  }
5354
5369
  function cpErrorHandler(cb) {
@@ -5365,9 +5380,9 @@ function findGitDarwin(onLookup) {
5365
5380
  if (err) {
5366
5381
  return e("git not found");
5367
5382
  }
5368
- const path9 = gitPathBuffer.toString().replace(/^\s+|\s+$/g, "");
5369
- if (path9 !== "/usr/bin/git") {
5370
- findSpecificGit(path9, onLookup).then(c, e);
5383
+ const path10 = gitPathBuffer.toString().replace(/^\s+|\s+$/g, "");
5384
+ if (path10 !== "/usr/bin/git") {
5385
+ findSpecificGit(path10, onLookup).then(c, e);
5371
5386
  return;
5372
5387
  }
5373
5388
  (0, import_child_process.exec)("xcode-select -p", (err2) => {
@@ -5375,7 +5390,7 @@ function findGitDarwin(onLookup) {
5375
5390
  e("git not found");
5376
5391
  return;
5377
5392
  }
5378
- findSpecificGit(path9, onLookup).then(c, e);
5393
+ findSpecificGit(path10, onLookup).then(c, e);
5379
5394
  });
5380
5395
  });
5381
5396
  });
@@ -5416,19 +5431,6 @@ var STATUS_MAP = {
5416
5431
  "?": import_safe2.default.gray("?"),
5417
5432
  "!": import_safe2.default.gray("!")
5418
5433
  };
5419
- function parseStatusEntries(output) {
5420
- let result = [];
5421
- let entries = output.split("\0");
5422
- for (let i = 0; i < entries.length; i++) {
5423
- let line = entries[i];
5424
- if (!line) continue;
5425
- result.push({ index: line[0], tree: line[1], relative: line.slice(3) });
5426
- if (line[0] === "R" || line[0] === "C" || line[1] === "R" || line[1] === "C") {
5427
- i++;
5428
- }
5429
- }
5430
- return result;
5431
- }
5432
5434
  var GStatus = class extends import_coc6.BasicList {
5433
5435
  constructor(nvim, manager) {
5434
5436
  super();
@@ -6136,8 +6138,10 @@ var DocumentManager = class {
6136
6138
  import_coc10.window.showWarningMessage(`not belongs to git repository.`);
6137
6139
  return null;
6138
6140
  }
6139
- let repo = this.service.getRepoFromRoot(root);
6140
- return repo.getDiffAll(category);
6141
+ return this.getDiffAllForRoot(root, category);
6142
+ }
6143
+ async getDiffAllForRoot(root, category) {
6144
+ return this.service.getRepoFromRoot(root).getDiffAll(category);
6141
6145
  }
6142
6146
  dispose() {
6143
6147
  (0, import_coc10.disposeAll)(this.disposables);
@@ -6180,13 +6184,13 @@ function parseNameStatus(output) {
6180
6184
  }
6181
6185
  if (status === "R" || status === "C") {
6182
6186
  const oldPath = fields[index++];
6183
- const path9 = fields[index++];
6184
- if (!oldPath || !path9) throw parserError("name-status", "missing rename/copy path");
6185
- result.push({ status, score, oldPath, path: path9 });
6187
+ const path10 = fields[index++];
6188
+ if (!oldPath || !path10) throw parserError("name-status", "missing rename/copy path");
6189
+ result.push({ status, score, oldPath, path: path10 });
6186
6190
  } else {
6187
- const path9 = fields[index++];
6188
- if (!path9) throw parserError("name-status", "missing path");
6189
- result.push({ status, path: path9 });
6191
+ const path10 = fields[index++];
6192
+ if (!path10) throw parserError("name-status", "missing path");
6193
+ result.push({ status, path: path10 });
6190
6194
  }
6191
6195
  }
6192
6196
  return result;
@@ -6205,7 +6209,7 @@ function parseNumstat(output) {
6205
6209
  }
6206
6210
  const additionsText = record.slice(0, firstTab);
6207
6211
  const deletionsText = record.slice(firstTab + 1, secondTab);
6208
- const path9 = record.slice(secondTab + 1);
6212
+ const path10 = record.slice(secondTab + 1);
6209
6213
  const binary = additionsText === "-" || deletionsText === "-";
6210
6214
  if (binary && (additionsText !== "-" || deletionsText !== "-")) {
6211
6215
  throw parserError("numstat", "only binary records may contain -");
@@ -6213,9 +6217,9 @@ function parseNumstat(output) {
6213
6217
  if (!binary && (!/^\d+$/.test(additionsText) || !/^\d+$/.test(deletionsText))) {
6214
6218
  throw parserError("numstat", `invalid line counts in ${JSON.stringify(record)}`);
6215
6219
  }
6216
- if (path9) {
6220
+ if (path10) {
6217
6221
  result.push({
6218
- path: path9,
6222
+ path: path10,
6219
6223
  additions: binary ? void 0 : Number(additionsText),
6220
6224
  deletions: binary ? void 0 : Number(deletionsText),
6221
6225
  binary
@@ -6380,9 +6384,12 @@ function patchArguments(comparison, change) {
6380
6384
  return args;
6381
6385
  }
6382
6386
  function commitUri(comparison, relativePath) {
6387
+ return revisionUri(comparison.commit.sha, relativePath);
6388
+ }
6389
+ function revisionUri(revision, relativePath) {
6383
6390
  return import_coc11.Uri.from({
6384
6391
  scheme: COMMIT_DOCUMENT_SCHEME,
6385
- authority: comparison.commit.sha,
6392
+ authority: revision,
6386
6393
  path: `/${relativePath}`
6387
6394
  });
6388
6395
  }
@@ -6409,7 +6416,27 @@ var CommitDocumentProvider = class {
6409
6416
  }
6410
6417
  async open(root, comparison, change, targetWinId, line) {
6411
6418
  const uri = commitUri(comparison, change.path);
6412
- const resource = { uri, root, comparison, change };
6419
+ const resource = { kind: "comparison", uri, root, comparison, change };
6420
+ await this.openResource(resource, targetWinId, line);
6421
+ }
6422
+ async openRevision(root, revision, relativePath, targetWinId, line) {
6423
+ const result = await this.git.exec(root, ["rev-parse", "--verify", "--end-of-options", `${revision}^{commit}`]);
6424
+ const sha = result.stdout.trim();
6425
+ if (!sha) throw new Error(`Invalid Git revision: ${revision}`);
6426
+ const object = `${sha}:${relativePath}`;
6427
+ const type = (await this.git.exec(root, ["cat-file", "-t", object])).stdout.trim();
6428
+ if (type !== "blob") throw new Error(`Git path is not a file: ${relativePath}`);
6429
+ const resource = {
6430
+ kind: "revision",
6431
+ uri: revisionUri(sha, relativePath),
6432
+ root,
6433
+ revision: sha,
6434
+ relativePath
6435
+ };
6436
+ await this.openResource(resource, targetWinId, line);
6437
+ }
6438
+ async openResource(resource, targetWinId, line) {
6439
+ const { uri } = resource;
6413
6440
  this.resources.set(uri.toString(), resource);
6414
6441
  const moved = await import_coc11.workspace.nvim.call("win_gotoid", [targetWinId]);
6415
6442
  if (!moved) await import_coc11.workspace.nvim.command("new");
@@ -6430,6 +6457,14 @@ var CommitDocumentProvider = class {
6430
6457
  async load(resource, token) {
6431
6458
  if (resource.content !== void 0 && resource.hunks) return;
6432
6459
  const options = token ? { cancellationToken: token } : void 0;
6460
+ if (resource.kind === "revision") {
6461
+ const object2 = `${resource.revision}:${resource.relativePath}`;
6462
+ const content2 = await this.git.exec(resource.root, ["cat-file", "-p", object2], options);
6463
+ if (token == null ? void 0 : token.isCancellationRequested) return;
6464
+ resource.content = content2.stdout.replace(/\r?\n$/, "");
6465
+ resource.hunks = [];
6466
+ return;
6467
+ }
6433
6468
  const object = `${resource.comparison.commit.sha}:${resource.change.path}`;
6434
6469
  const [content, patch] = await Promise.all([
6435
6470
  this.git.exec(resource.root, ["cat-file", "-p", object], options),
@@ -9328,156 +9363,552 @@ var CommitFilesController = class {
9328
9363
  }
9329
9364
  };
9330
9365
 
9366
+ // src/tree/statusFiles.ts
9367
+ var import_fs5 = __toESM(require("fs"));
9368
+ var import_path9 = __toESM(require("path"));
9369
+ var import_coc18 = require("coc.nvim");
9370
+ function emptyAggregate2() {
9371
+ return { fileCount: 0, stagedCount: 0, unstagedCount: 0, untrackedCount: 0, conflictCount: 0 };
9372
+ }
9373
+ function isUntracked(entry) {
9374
+ return entry.index === "?" && entry.tree === "?";
9375
+ }
9376
+ function isConflict(entry) {
9377
+ return entry.index === "U" || entry.tree === "U" || entry.index + entry.tree === "AA" || entry.index + entry.tree === "DD";
9378
+ }
9379
+ function isStaged(entry) {
9380
+ return entry.index !== " " && entry.index !== "?" && entry.index !== "!";
9381
+ }
9382
+ function isUnstaged(entry) {
9383
+ return entry.tree !== " " && entry.tree !== "?" && entry.tree !== "!";
9384
+ }
9385
+ function addFileAggregate2(target, entry) {
9386
+ target.fileCount++;
9387
+ if (isStaged(entry)) target.stagedCount++;
9388
+ if (isUnstaged(entry)) target.unstagedCount++;
9389
+ if (isUntracked(entry)) target.untrackedCount++;
9390
+ if (isConflict(entry)) target.conflictCount++;
9391
+ }
9392
+ function addAggregate2(target, source) {
9393
+ target.fileCount += source.fileCount;
9394
+ target.stagedCount += source.stagedCount;
9395
+ target.unstagedCount += source.unstagedCount;
9396
+ target.untrackedCount += source.untrackedCount;
9397
+ target.conflictCount += source.conflictCount;
9398
+ }
9399
+ function compareNodes2(left, right) {
9400
+ if (left.kind === "directory" && right.kind !== "directory") return -1;
9401
+ if (left.kind !== "directory" && right.kind === "directory") return 1;
9402
+ return left.relativePath.localeCompare(right.relativePath);
9403
+ }
9404
+ function buildStatusTree(rootPath, entries) {
9405
+ const root = {
9406
+ ...emptyAggregate2(),
9407
+ kind: "root",
9408
+ id: `${rootPath}\0root`,
9409
+ name: import_path9.default.basename(rootPath) || rootPath,
9410
+ relativePath: "",
9411
+ parent: void 0,
9412
+ root: rootPath,
9413
+ children: []
9414
+ };
9415
+ const directories = /* @__PURE__ */ new Map();
9416
+ const ensureDirectory = (segments) => {
9417
+ if (!segments.length) return root;
9418
+ const relativePath = segments.join("/");
9419
+ const existing = directories.get(relativePath);
9420
+ if (existing) return existing;
9421
+ const parent = ensureDirectory(segments.slice(0, -1));
9422
+ const directory = {
9423
+ ...emptyAggregate2(),
9424
+ kind: "directory",
9425
+ id: `${rootPath}\0directory\0${relativePath}`,
9426
+ name: segments[segments.length - 1],
9427
+ relativePath,
9428
+ parent,
9429
+ children: []
9430
+ };
9431
+ directories.set(relativePath, directory);
9432
+ parent.children.push(directory);
9433
+ return directory;
9434
+ };
9435
+ for (const entry of entries) {
9436
+ const segments = entry.relative.split("/");
9437
+ const name = segments.pop();
9438
+ if (!name) continue;
9439
+ const parent = ensureDirectory(segments);
9440
+ parent.children.push({
9441
+ kind: "file",
9442
+ id: `${rootPath}\0file\0${entry.relative}`,
9443
+ name,
9444
+ relativePath: entry.relative,
9445
+ parent,
9446
+ entry
9447
+ });
9448
+ }
9449
+ const aggregate = (node) => {
9450
+ const own = emptyAggregate2();
9451
+ for (const child of node.children) {
9452
+ if (child.kind === "file") addFileAggregate2(own, child.entry);
9453
+ else {
9454
+ aggregate(child);
9455
+ addAggregate2(own, child);
9456
+ }
9457
+ }
9458
+ Object.assign(node, own);
9459
+ node.children.sort(compareNodes2);
9460
+ };
9461
+ aggregate(root);
9462
+ return root;
9463
+ }
9464
+ function firstFile(node) {
9465
+ for (const child of node.children) {
9466
+ if (child.kind === "file") return child;
9467
+ const file = firstFile(child);
9468
+ if (file) return file;
9469
+ }
9470
+ return void 0;
9471
+ }
9472
+ function escapeDisplay2(value) {
9473
+ return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
9474
+ }
9475
+ var STATUS_NAMES = {
9476
+ M: "Modified",
9477
+ A: "Added",
9478
+ D: "Deleted",
9479
+ R: "Renamed",
9480
+ C: "Copied",
9481
+ T: "Type changed",
9482
+ U: "Unmerged"
9483
+ };
9484
+ function aggregateDescription(value) {
9485
+ const parts = [`${value.fileCount} file${value.fileCount === 1 ? "" : "s"}`];
9486
+ if (value.stagedCount) parts.push(`${value.stagedCount} staged`);
9487
+ if (value.unstagedCount) parts.push(`${value.unstagedCount} unstaged`);
9488
+ if (value.untrackedCount) parts.push(`${value.untrackedCount} untracked`);
9489
+ if (value.conflictCount) parts.push(`${value.conflictCount} conflicted`);
9490
+ return parts.join(" \xB7 ");
9491
+ }
9492
+ function fileDescription2(entry) {
9493
+ var _a, _b;
9494
+ const code = `[${entry.index}${entry.tree}]`;
9495
+ if (isUntracked(entry)) return `${code} \xB7 Untracked`;
9496
+ if (entry.index === "!" && entry.tree === "!") return `${code} \xB7 Ignored`;
9497
+ const parts = [code];
9498
+ if (isStaged(entry)) parts.push(`staged: ${(_a = STATUS_NAMES[entry.index]) != null ? _a : entry.index}`);
9499
+ if (isUnstaged(entry)) parts.push(`unstaged: ${(_b = STATUS_NAMES[entry.tree]) != null ? _b : entry.tree}`);
9500
+ if (isConflict(entry) && !parts.some((part) => part.includes("Unmerged"))) parts.push("conflicted");
9501
+ return parts.join(" \xB7 ");
9502
+ }
9503
+ function statusIcon2(entry) {
9504
+ if (isConflict(entry)) return { text: "!", hlGroup: "Error" };
9505
+ if (isUntracked(entry)) return { text: "?", hlGroup: "Comment" };
9506
+ const status = entry.tree !== " " ? entry.tree : entry.index;
9507
+ if (status === "A" || status === "C") return { text: "+", hlGroup: "DiffAdd" };
9508
+ if (status === "D") return { text: "-", hlGroup: "DiffDelete" };
9509
+ if (status === "R") return { text: "R", hlGroup: "DiffChange" };
9510
+ return { text: "~", hlGroup: "DiffChange" };
9511
+ }
9512
+ function toggleCommand(node) {
9513
+ return { command: "git.statusFiles.toggle", title: "Toggle directory", arguments: [node] };
9514
+ }
9515
+ var StatusFilesProvider = class {
9516
+ constructor(rootPath, entries, actions) {
9517
+ this.actions = actions;
9518
+ this.emitter = new import_coc18.Emitter();
9519
+ this.onDidChangeTreeData = this.emitter.event;
9520
+ this.root = buildStatusTree(rootPath, entries);
9521
+ }
9522
+ setEntries(entries) {
9523
+ this.root = buildStatusTree(this.root.root, entries);
9524
+ this.emitter.fire(void 0);
9525
+ }
9526
+ getTreeItem(element) {
9527
+ if (element.kind === "root") {
9528
+ const item2 = new import_coc18.TreeItem(escapeDisplay2(element.name), element.children.length ? import_coc18.TreeItemCollapsibleState.Expanded : import_coc18.TreeItemCollapsibleState.None);
9529
+ item2.id = element.id;
9530
+ item2.description = aggregateDescription(element);
9531
+ item2.tooltip = escapeDisplay2(element.root);
9532
+ item2.command = { command: "git.statusFiles.refresh", title: "Refresh Git status" };
9533
+ return item2;
9534
+ }
9535
+ if (element.kind === "directory") {
9536
+ const item2 = new import_coc18.TreeItem(escapeDisplay2(element.name), element.children.length ? import_coc18.TreeItemCollapsibleState.Expanded : import_coc18.TreeItemCollapsibleState.None);
9537
+ item2.id = element.id;
9538
+ item2.description = aggregateDescription(element);
9539
+ item2.tooltip = escapeDisplay2(element.relativePath);
9540
+ item2.command = toggleCommand(element);
9541
+ return item2;
9542
+ }
9543
+ const item = new import_coc18.TreeItem(escapeDisplay2(element.name), import_coc18.TreeItemCollapsibleState.None);
9544
+ item.id = element.id;
9545
+ item.icon = statusIcon2(element.entry);
9546
+ item.description = fileDescription2(element.entry);
9547
+ item.tooltip = `${escapeDisplay2(element.relativePath)}
9548
+ ${fileDescription2(element.entry)}`;
9549
+ item.command = { command: "git.statusFiles.open", title: "Open file", arguments: [element] };
9550
+ return item;
9551
+ }
9552
+ getChildren(element) {
9553
+ if (!element) return [this.root];
9554
+ return element.kind === "file" ? [] : element.children;
9555
+ }
9556
+ getParent(element) {
9557
+ return element.parent;
9558
+ }
9559
+ resolveActions(_item, element) {
9560
+ if (element.kind === "root") return [{ title: "Refresh", handler: () => this.actions.refresh() }];
9561
+ if (element.kind === "directory") return [{ title: "Copy directory path", handler: (node) => this.actions.copyPath(node) }];
9562
+ const actions = [
9563
+ { title: "Open file", handler: (node) => this.actions.openFile(node) },
9564
+ { title: "Copy relative path", handler: (node) => this.actions.copyPath(node) }
9565
+ ];
9566
+ const entry = element.entry;
9567
+ if (isUntracked(entry) || isUnstaged(entry)) actions.push({ title: "Add", handler: (node) => this.actions.addFile(node) });
9568
+ if (isStaged(entry)) actions.push({ title: "Restore staged changes", handler: (node) => this.actions.restoreStagedFile(node) });
9569
+ if (isUnstaged(entry) && !isUntracked(entry) && !isConflict(entry)) {
9570
+ actions.push({ title: "Restore working tree changes", handler: (node) => this.actions.restoreWorkingTreeFile(node) });
9571
+ }
9572
+ return actions;
9573
+ }
9574
+ dispose() {
9575
+ this.emitter.dispose();
9576
+ }
9577
+ };
9578
+ var StatusFilesController = class {
9579
+ constructor(manager, commitDocuments) {
9580
+ this.manager = manager;
9581
+ this.commitDocuments = commitDocuments;
9582
+ }
9583
+ async open(requestedRoot) {
9584
+ let root = requestedRoot;
9585
+ if (!root) root = await this.manager.resolveGitRootFromBufferOrCwd(await import_coc18.workspace.nvim.call("bufnr", ["%"]));
9586
+ if (!root) {
9587
+ import_coc18.window.showWarningMessage("Can't resolve git repository for current buffer or cwd.");
9588
+ return;
9589
+ }
9590
+ try {
9591
+ root = await this.manager.git.getRepositoryRoot(root);
9592
+ } catch (_e) {
9593
+ import_coc18.window.showWarningMessage("Can't resolve git repository for current buffer or cwd.");
9594
+ return;
9595
+ }
9596
+ const targetWinId = await import_coc18.workspace.nvim.call("win_getid");
9597
+ this.disposeSession();
9598
+ try {
9599
+ const entries = await this.loadEntries(root);
9600
+ const provider = new StatusFilesProvider(root, entries, this.actions);
9601
+ const view = import_coc18.window.createTreeView("git.statusFiles", {
9602
+ treeDataProvider: provider,
9603
+ enableFilter: true,
9604
+ winfixwidth: true,
9605
+ bufhidden: "wipe",
9606
+ canSelectMany: false,
9607
+ autoWidth: true
9608
+ });
9609
+ view.title = "Git Status";
9610
+ view.description = aggregateDescription(buildStatusTree(root, entries));
9611
+ this.session = { root, provider, view, targetWinId };
9612
+ await view.show(import_coc18.workspace.getConfiguration("git").get("statusTree.splitCommand", "belowright 40vs"));
9613
+ const file = firstFile(provider.getChildren()[0]);
9614
+ if (file) await view.reveal(file, { select: true, focus: true, expand: true });
9615
+ view.onDidChangeVisibility((event) => {
9616
+ var _a;
9617
+ if (!event.visible && ((_a = this.session) == null ? void 0 : _a.view) === view) this.disposeSession();
9618
+ });
9619
+ } catch (e) {
9620
+ this.disposeSession();
9621
+ import_coc18.window.showErrorMessage(`Failed to open Git Status: ${e.message}`);
9622
+ }
9623
+ }
9624
+ async toggle(node) {
9625
+ if (!node || typeof node !== "object") return;
9626
+ const element = node;
9627
+ if (element.kind !== "root" && element.kind !== "directory") return;
9628
+ const configuredKey = import_coc18.workspace.getConfiguration("tree").get("key.toggle", "t");
9629
+ const key = configuredKey.startsWith("<") && configuredKey.endsWith(">") ? `\\${configuredKey}` : configuredKey;
9630
+ const escaped = key.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
9631
+ await import_coc18.workspace.nvim.call("eval", [`feedkeys("${escaped}", "in")`]);
9632
+ }
9633
+ async refresh() {
9634
+ const session = this.session;
9635
+ if (!session) return;
9636
+ try {
9637
+ const entries = await this.loadEntries(session.root);
9638
+ if (this.session !== session) return;
9639
+ session.provider.setEntries(entries);
9640
+ session.view.description = aggregateDescription(buildStatusTree(session.root, entries));
9641
+ } catch (e) {
9642
+ import_coc18.window.showErrorMessage(`Failed to refresh Git Status: ${e.message}`);
9643
+ }
9644
+ }
9645
+ close() {
9646
+ this.disposeSession();
9647
+ }
9648
+ async openFile(node) {
9649
+ var _a, _b, _c;
9650
+ const session = this.session;
9651
+ if (!session) return;
9652
+ const absolute = import_path9.default.resolve(session.root, ...node.relativePath.split("/"));
9653
+ const relative = import_path9.default.relative(session.root, absolute);
9654
+ if (relative.startsWith(`..${import_path9.default.sep}`) || import_path9.default.isAbsolute(relative)) {
9655
+ import_coc18.window.showWarningMessage(`Working tree path is outside repository: ${escapeDisplay2(node.relativePath)}`);
9656
+ return;
9657
+ }
9658
+ let stat;
9659
+ try {
9660
+ stat = await import_fs5.default.promises.stat(absolute);
9661
+ } catch (error) {
9662
+ if (error.code !== "ENOENT" && error.code !== "ENOTDIR") {
9663
+ import_coc18.window.showErrorMessage(`Failed to inspect working tree path: ${escapeDisplay2(node.relativePath)}`);
9664
+ return;
9665
+ }
9666
+ try {
9667
+ await this.commitDocuments.openRevision(session.root, "HEAD", node.relativePath, session.targetWinId);
9668
+ } catch (_error) {
9669
+ import_coc18.window.showWarningMessage(`File not found in working tree or HEAD: ${escapeDisplay2(node.relativePath)}`);
9670
+ }
9671
+ return;
9672
+ }
9673
+ if (!stat.isFile()) {
9674
+ import_coc18.window.showWarningMessage(`Working tree path is not a file: ${escapeDisplay2(node.relativePath)}`);
9675
+ return;
9676
+ }
9677
+ let line = 1;
9678
+ try {
9679
+ const groups = await this.manager.getDiffAllForRoot(session.root, 0 /* All */);
9680
+ line = Math.max(1, (_c = (_b = (_a = groups.get(node.relativePath)) == null ? void 0 : _a[0]) == null ? void 0 : _b.start) != null ? _c : 1);
9681
+ } catch (_e) {
9682
+ }
9683
+ const moved = await import_coc18.workspace.nvim.call("win_gotoid", [session.targetWinId]);
9684
+ if (!moved) await import_coc18.workspace.nvim.command("new");
9685
+ try {
9686
+ await import_coc18.workspace.jumpTo(import_coc18.Uri.file(absolute), import_coc18.Position.create(line - 1, 0));
9687
+ } catch (e) {
9688
+ import_coc18.window.showErrorMessage(`Failed to open working tree file: ${e.message}`);
9689
+ }
9690
+ }
9691
+ async addFile(node) {
9692
+ await this.updateFile(node, ["add", "--", node.relativePath]);
9693
+ }
9694
+ async restoreStagedFile(node) {
9695
+ await this.updateFile(node, ["reset", "--", node.relativePath]);
9696
+ }
9697
+ async restoreWorkingTreeFile(node) {
9698
+ await this.updateFile(node, ["checkout", "--", node.relativePath]);
9699
+ }
9700
+ get actions() {
9701
+ return {
9702
+ refresh: () => this.refresh(),
9703
+ openFile: (node) => this.openFile(node),
9704
+ addFile: (node) => this.addFile(node),
9705
+ restoreStagedFile: (node) => this.restoreStagedFile(node),
9706
+ restoreWorkingTreeFile: (node) => this.restoreWorkingTreeFile(node),
9707
+ copyPath: (node) => import_coc18.workspace.nvim.call("setreg", ["+", node.relativePath]).then(() => void 0)
9708
+ };
9709
+ }
9710
+ async updateFile(node, args) {
9711
+ const session = this.session;
9712
+ if (!session) return;
9713
+ try {
9714
+ await this.manager.git.exec(session.root, args);
9715
+ await this.refresh();
9716
+ } catch (e) {
9717
+ import_coc18.window.showErrorMessage(`Failed to update Git status for ${escapeDisplay2(node.relativePath)}: ${e.message}`);
9718
+ }
9719
+ }
9720
+ async loadEntries(root) {
9721
+ const result = await this.manager.git.exec(root, ["status", "--porcelain=v1", "-z", "-uall"]);
9722
+ return parseStatusEntries(result.stdout);
9723
+ }
9724
+ disposeSession() {
9725
+ const session = this.session;
9726
+ if (!session) return;
9727
+ this.session = void 0;
9728
+ session.provider.dispose();
9729
+ session.view.dispose();
9730
+ }
9731
+ dispose() {
9732
+ this.close();
9733
+ }
9734
+ };
9735
+
9331
9736
  // src/index.ts
9332
9737
  async function activate(context) {
9333
9738
  var _a;
9334
- const config = import_coc18.workspace.getConfiguration("git");
9739
+ const config = import_coc19.workspace.getConfiguration("git");
9335
9740
  const { subscriptions } = context;
9336
9741
  let gitInfo;
9337
9742
  try {
9338
9743
  let pathHint = config.get("command", "git");
9339
- gitInfo = await findGit(pathHint, (path9) => context.logger.info(`Looking for git in: ${path9}`));
9744
+ gitInfo = await findGit(pathHint, (path10) => context.logger.info(`Looking for git in: ${path10}`));
9340
9745
  } catch (e) {
9341
- import_coc18.window.showErrorMessage("git command required for coc-git");
9746
+ import_coc19.window.showErrorMessage("git command required for coc-git");
9342
9747
  return;
9343
9748
  }
9344
- const virtualTextSrcId = await import_coc18.workspace.nvim.createNamespace("coc-git-virtual");
9345
- const conflictSrcId = await import_coc18.workspace.nvim.createNamespace("coc-git-conflicts");
9346
- const commitDocumentSrcId = await import_coc18.workspace.nvim.createNamespace("coc-git-commit-document");
9347
- const { nvim } = import_coc18.workspace;
9749
+ const virtualTextSrcId = await import_coc19.workspace.nvim.createNamespace("coc-git-virtual");
9750
+ const conflictSrcId = await import_coc19.workspace.nvim.createNamespace("coc-git-conflicts");
9751
+ const commitDocumentSrcId = await import_coc19.workspace.nvim.createNamespace("coc-git-commit-document");
9752
+ const { nvim } = import_coc19.workspace;
9348
9753
  const service = new GitService(gitInfo);
9349
9754
  const manager = new DocumentManager(nvim, service, virtualTextSrcId, conflictSrcId);
9350
9755
  subscriptions.push(manager);
9351
9756
  const commitDocuments = new CommitDocumentProvider(service.git, commitDocumentSrcId);
9352
9757
  subscriptions.push(commitDocuments);
9353
- subscriptions.push(import_coc18.workspace.registerTextDocumentContentProvider(COMMIT_DOCUMENT_SCHEME, commitDocuments));
9758
+ subscriptions.push(import_coc19.workspace.registerTextDocumentContentProvider(COMMIT_DOCUMENT_SCHEME, commitDocuments));
9354
9759
  nvim.command("highlight default link CocGitCommitAdd DiffAdd", true);
9355
9760
  nvim.command("highlight default link CocGitCommitDelete DiffDelete", true);
9356
9761
  const commitFiles = new CommitFilesController(manager);
9357
9762
  subscriptions.push(commitFiles);
9763
+ const statusFiles = new StatusFilesController(manager, commitDocuments);
9764
+ subscriptions.push(statusFiles);
9358
9765
  addSource(context, service);
9359
- subscriptions.push(import_coc18.commands.registerCommand("git.refresh", () => {
9766
+ subscriptions.push(import_coc19.commands.registerCommand("git.refresh", () => {
9360
9767
  manager.refresh();
9361
9768
  }));
9362
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-nextchunk", async () => {
9769
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusTree", async () => {
9770
+ await statusFiles.open();
9771
+ }));
9772
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.open", async (node) => {
9773
+ await statusFiles.openFile(node);
9774
+ }, void 0, true));
9775
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.add", async (node) => {
9776
+ await statusFiles.addFile(node);
9777
+ }, void 0, true));
9778
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.restoreStaged", async (node) => {
9779
+ await statusFiles.restoreStagedFile(node);
9780
+ }, void 0, true));
9781
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.restoreWorkingTree", async (node) => {
9782
+ await statusFiles.restoreWorkingTreeFile(node);
9783
+ }, void 0, true));
9784
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.refresh", async () => {
9785
+ await statusFiles.refresh();
9786
+ }, void 0, true));
9787
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.toggle", async (node) => {
9788
+ await statusFiles.toggle(node);
9789
+ }, void 0, true));
9790
+ subscriptions.push(import_coc19.commands.registerCommand("git.statusFiles.close", () => {
9791
+ statusFiles.close();
9792
+ }, void 0, true));
9793
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-nextchunk", async () => {
9363
9794
  if (!await commitDocuments.nextChunk()) await manager.nextChunk();
9364
9795
  }, { sync: false }));
9365
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-prevchunk", async () => {
9796
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-prevchunk", async () => {
9366
9797
  if (!await commitDocuments.prevChunk()) await manager.prevChunk();
9367
9798
  }, { sync: false }));
9368
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-nextconflict", async () => {
9799
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-nextconflict", async () => {
9369
9800
  await manager.nextConflict();
9370
9801
  }, { sync: false }));
9371
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-prevconflict", async () => {
9802
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-prevconflict", async () => {
9372
9803
  await manager.prevConflict();
9373
9804
  }, { sync: false }));
9374
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-keepcurrent", async () => {
9805
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-keepcurrent", async () => {
9375
9806
  await manager.keepCurrent();
9376
9807
  }, { sync: false }));
9377
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-keepincoming", async () => {
9808
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-keepincoming", async () => {
9378
9809
  await manager.keepIncoming();
9379
9810
  }, { sync: false }));
9380
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-keepboth", async () => {
9811
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-keepboth", async () => {
9381
9812
  await manager.keepBoth();
9382
9813
  }, { sync: false }));
9383
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-chunkinfo", async () => {
9814
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-chunkinfo", async () => {
9384
9815
  await manager.chunkInfo();
9385
9816
  }, { sync: false }));
9386
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-commit", async () => {
9817
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-commit", async () => {
9387
9818
  await manager.showCommit();
9388
9819
  }, { sync: false }));
9389
- subscriptions.push(import_coc18.workspace.registerKeymap(["n"], "git-showblamedoc", async () => {
9820
+ subscriptions.push(import_coc19.workspace.registerKeymap(["n"], "git-showblamedoc", async () => {
9390
9821
  await manager.showBlameDoc();
9391
9822
  }, { sync: false }));
9392
- subscriptions.push(import_coc18.commands.registerCommand("git.nextChunk", async () => {
9823
+ subscriptions.push(import_coc19.commands.registerCommand("git.nextChunk", async () => {
9393
9824
  if (!await commitDocuments.nextChunk()) await manager.nextChunk();
9394
9825
  }));
9395
- subscriptions.push(import_coc18.commands.registerCommand("git.prevChunk", async () => {
9826
+ subscriptions.push(import_coc19.commands.registerCommand("git.prevChunk", async () => {
9396
9827
  if (!await commitDocuments.prevChunk()) await manager.prevChunk();
9397
9828
  }));
9398
- subscriptions.push(import_coc18.commands.registerCommand("git.keepCurrent", async () => {
9829
+ subscriptions.push(import_coc19.commands.registerCommand("git.keepCurrent", async () => {
9399
9830
  await manager.keepCurrent();
9400
9831
  }));
9401
- subscriptions.push(import_coc18.commands.registerCommand("git.keepIncoming", async () => {
9832
+ subscriptions.push(import_coc19.commands.registerCommand("git.keepIncoming", async () => {
9402
9833
  await manager.keepIncoming();
9403
9834
  }));
9404
- subscriptions.push(import_coc18.commands.registerCommand("git.keepBoth", async () => {
9835
+ subscriptions.push(import_coc19.commands.registerCommand("git.keepBoth", async () => {
9405
9836
  await manager.keepBoth();
9406
9837
  }));
9407
- subscriptions.push(import_coc18.commands.registerCommand("git.chunkInfo", async () => {
9838
+ subscriptions.push(import_coc19.commands.registerCommand("git.chunkInfo", async () => {
9408
9839
  await manager.chunkInfo();
9409
9840
  }));
9410
- subscriptions.push(import_coc18.commands.registerCommand("git.allChunkInfo", async () => {
9841
+ subscriptions.push(import_coc19.commands.registerCommand("git.allChunkInfo", async () => {
9411
9842
  return await manager.allChunkInfo();
9412
9843
  }));
9413
- subscriptions.push(import_coc18.commands.registerCommand("git.chunkStage", async () => {
9844
+ subscriptions.push(import_coc19.commands.registerCommand("git.chunkStage", async () => {
9414
9845
  await manager.chunkStage();
9415
9846
  }));
9416
- subscriptions.push(import_coc18.commands.registerCommand("git.chunkUnstage", async () => {
9847
+ subscriptions.push(import_coc19.commands.registerCommand("git.chunkUnstage", async () => {
9417
9848
  await manager.chunkUnstage();
9418
9849
  }));
9419
- subscriptions.push(import_coc18.commands.registerCommand("git.chunkUndo", async () => {
9850
+ subscriptions.push(import_coc19.commands.registerCommand("git.chunkUndo", async () => {
9420
9851
  await manager.chunkUndo();
9421
9852
  }));
9422
- subscriptions.push(import_coc18.commands.registerCommand("git.showCommit", async () => {
9853
+ subscriptions.push(import_coc19.commands.registerCommand("git.showCommit", async () => {
9423
9854
  await manager.showCommit();
9424
9855
  }));
9425
- subscriptions.push(import_coc18.commands.registerCommand("git.commitFiles.open", async (revision, root) => {
9856
+ subscriptions.push(import_coc19.commands.registerCommand("git.commitFiles.open", async (revision, root) => {
9426
9857
  await commitFiles.open(revision, root);
9427
9858
  }, void 0, true));
9428
- subscriptions.push(import_coc18.commands.registerCommand("git.showCommitTree", async () => {
9859
+ subscriptions.push(import_coc19.commands.registerCommand("git.showCommitTree", async () => {
9429
9860
  const current = await manager.getCurrentCommit();
9430
9861
  if (current) await commitFiles.open(current.sha, current.root, { showCurrentFile: true, line: current.line });
9431
9862
  }));
9432
- subscriptions.push(import_coc18.commands.registerCommand("git.commitFiles.invoke", async (node) => {
9863
+ subscriptions.push(import_coc19.commands.registerCommand("git.commitFiles.invoke", async (node) => {
9433
9864
  await commitFiles.invoke(node);
9434
9865
  }, void 0, true));
9435
- subscriptions.push(import_coc18.commands.registerCommand("git.commitFiles.toggle", async (node) => {
9866
+ subscriptions.push(import_coc19.commands.registerCommand("git.commitFiles.toggle", async (node) => {
9436
9867
  await commitFiles.toggle(node);
9437
9868
  }, void 0, true));
9438
- subscriptions.push(import_coc18.commands.registerCommand("git.commitFiles.openDocument", async (root, comparison, change, targetWinId, line) => {
9869
+ subscriptions.push(import_coc19.commands.registerCommand("git.commitFiles.openDocument", async (root, comparison, change, targetWinId, line) => {
9439
9870
  targetWinId = targetWinId != null ? targetWinId : await nvim.call("win_getid");
9440
9871
  await commitDocuments.open(root, comparison, change, targetWinId, line);
9441
9872
  }, void 0, true));
9442
- subscriptions.push(import_coc18.commands.registerCommand("git.browserOpen", async () => {
9873
+ subscriptions.push(import_coc19.commands.registerCommand("git.browserOpen", async () => {
9443
9874
  await manager.browser();
9444
9875
  }));
9445
- subscriptions.push(import_coc18.commands.registerCommand("git.copyUrl", async (...args) => {
9876
+ subscriptions.push(import_coc19.commands.registerCommand("git.copyUrl", async (...args) => {
9446
9877
  await manager.browser("copy", args);
9447
9878
  }));
9448
- subscriptions.push(import_coc18.commands.registerCommand("git.copyPermalink", async (...args) => {
9879
+ subscriptions.push(import_coc19.commands.registerCommand("git.copyPermalink", async (...args) => {
9449
9880
  await manager.browser("copy", args, true);
9450
9881
  }));
9451
- subscriptions.push(import_coc18.commands.registerCommand("git.push", async (...args) => {
9882
+ subscriptions.push(import_coc19.commands.registerCommand("git.push", async (...args) => {
9452
9883
  await manager.push(args);
9453
9884
  }));
9454
- subscriptions.push(import_coc18.commands.registerCommand("git.diffCached", async () => {
9885
+ subscriptions.push(import_coc19.commands.registerCommand("git.diffCached", async () => {
9455
9886
  await manager.diffCached();
9456
9887
  }));
9457
- subscriptions.push(import_coc18.commands.registerCommand("git.toggleGutters", async () => {
9888
+ subscriptions.push(import_coc19.commands.registerCommand("git.toggleGutters", async () => {
9458
9889
  await manager.toggleGutters();
9459
9890
  }));
9460
- subscriptions.push(import_coc18.commands.registerCommand("git.foldUnchanged", async () => {
9891
+ subscriptions.push(import_coc19.commands.registerCommand("git.foldUnchanged", async () => {
9461
9892
  await manager.toggleFold();
9462
9893
  }));
9463
- subscriptions.push(import_coc18.commands.registerCommand("git.showBlameDoc", async () => {
9894
+ subscriptions.push(import_coc19.commands.registerCommand("git.showBlameDoc", async () => {
9464
9895
  await manager.showBlameDoc();
9465
9896
  }));
9466
- subscriptions.push(import_coc18.listManager.registerList(new GStatus(nvim, manager)));
9467
- subscriptions.push(import_coc18.listManager.registerList(new Branches(nvim, manager)));
9468
- subscriptions.push(import_coc18.listManager.registerList(new Commits(nvim, manager)));
9469
- subscriptions.push(import_coc18.listManager.registerList(new Bcommits(nvim, manager)));
9470
- subscriptions.push(import_coc18.listManager.registerList(new Gfiles(nvim, manager)));
9471
- subscriptions.push(import_coc18.listManager.registerList(new GChunks(nvim, manager)));
9472
- subscriptions.push(import_coc18.listManager.registerList(new GChanges(nvim, manager)));
9473
- subscriptions.push(import_coc18.languages.registerCompletionItemProvider("semantic-commit", "Commit", (_a = config.get("semanticCommit.filetypes")) != null ? _a : [], {
9897
+ subscriptions.push(import_coc19.listManager.registerList(new GStatus(nvim, manager)));
9898
+ subscriptions.push(import_coc19.listManager.registerList(new Branches(nvim, manager)));
9899
+ subscriptions.push(import_coc19.listManager.registerList(new Commits(nvim, manager)));
9900
+ subscriptions.push(import_coc19.listManager.registerList(new Bcommits(nvim, manager)));
9901
+ subscriptions.push(import_coc19.listManager.registerList(new Gfiles(nvim, manager)));
9902
+ subscriptions.push(import_coc19.listManager.registerList(new GChunks(nvim, manager)));
9903
+ subscriptions.push(import_coc19.listManager.registerList(new GChanges(nvim, manager)));
9904
+ subscriptions.push(import_coc19.languages.registerCompletionItemProvider("semantic-commit", "Commit", (_a = config.get("semanticCommit.filetypes")) != null ? _a : [], {
9474
9905
  provideCompletionItems: async (document, position) => {
9475
9906
  if (position.line !== 0) {
9476
9907
  return [];
9477
9908
  }
9478
9909
  const text = document.getText(
9479
- import_coc18.Range.create(
9480
- import_coc18.Position.create(position.line, 0),
9910
+ import_coc19.Range.create(
9911
+ import_coc19.Position.create(position.line, 0),
9481
9912
  position
9482
9913
  )
9483
9914
  );
@@ -9487,10 +9918,10 @@ async function activate(context) {
9487
9918
  return DEFAULT_TYPES.map((o) => {
9488
9919
  return {
9489
9920
  label: o.value,
9490
- kind: import_coc18.CompletionItemKind.Snippet,
9921
+ kind: import_coc19.CompletionItemKind.Snippet,
9491
9922
  documentation: { kind: "plaintext", value: o.name },
9492
9923
  // detail: o.name,
9493
- insertTextFormat: import_coc18.InsertTextFormat.Snippet,
9924
+ insertTextFormat: import_coc19.InsertTextFormat.Snippet,
9494
9925
  // tslint:disable-next-line: no-invalid-template-strings
9495
9926
  insertText: o.value + text2 + "\n\n"
9496
9927
  };
@@ -9499,12 +9930,12 @@ async function activate(context) {
9499
9930
  return [];
9500
9931
  }
9501
9932
  }));
9502
- subscriptions.push(import_coc18.workspace.registerKeymap(["o", "x"], "git-chunk-inner", async () => {
9933
+ subscriptions.push(import_coc19.workspace.registerKeymap(["o", "x"], "git-chunk-inner", async () => {
9503
9934
  let diff = await manager.getCurrentChunk();
9504
9935
  if (!diff) return;
9505
9936
  await nvim.command(`normal! ${diff.start}GV${diff.end}G`);
9506
9937
  }, { sync: true, silent: true }));
9507
- subscriptions.push(import_coc18.workspace.registerKeymap(["o", "x"], "git-chunk-outer", async () => {
9938
+ subscriptions.push(import_coc19.workspace.registerKeymap(["o", "x"], "git-chunk-outer", async () => {
9508
9939
  let diff = await manager.getCurrentChunk();
9509
9940
  if (!diff) return;
9510
9941
  let total = await nvim.call("line", ["$"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coc-git",
3
- "version": "2.7.10",
3
+ "version": "2.7.11",
4
4
  "description": "Git extension for coc.nvim",
5
5
  "main": "lib/index.js",
6
6
  "publisher": "chemzqm",
@@ -32,6 +32,10 @@
32
32
  "title": "Refresh git information for all buffers.",
33
33
  "command": "git.refresh"
34
34
  },
35
+ {
36
+ "title": "Open changed files from Git status in a TreeView.",
37
+ "command": "git.statusTree"
38
+ },
35
39
  {
36
40
  "title": "Navigate to the next changed chunk.",
37
41
  "command": "git.nextChunk"
@@ -420,6 +424,12 @@
420
424
  "scope": "application",
421
425
  "description": "Command used to open the changed-files TreeView for a commit."
422
426
  },
427
+ "git.statusTree.splitCommand": {
428
+ "type": "string",
429
+ "default": "belowright 40vs",
430
+ "scope": "application",
431
+ "description": "Command used to open the Git status TreeView."
432
+ },
423
433
  "git.showCommitInFloating": {
424
434
  "type": "boolean",
425
435
  "default": false,