coc-git 2.7.6 → 2.7.7

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.
Files changed (2) hide show
  1. package/lib/index.js +1447 -1442
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -35,7 +35,7 @@ var require_windows = __commonJS({
35
35
  module2.exports = isexe;
36
36
  isexe.sync = sync;
37
37
  var fs4 = require("fs");
38
- function checkPathExt(path9, options) {
38
+ function checkPathExt(path8, options) {
39
39
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
40
40
  if (!pathext) {
41
41
  return true;
@@ -46,25 +46,25 @@ var require_windows = __commonJS({
46
46
  }
47
47
  for (var i = 0; i < pathext.length; i++) {
48
48
  var p = pathext[i].toLowerCase();
49
- if (p && path9.substr(-p.length).toLowerCase() === p) {
49
+ if (p && path8.substr(-p.length).toLowerCase() === p) {
50
50
  return true;
51
51
  }
52
52
  }
53
53
  return false;
54
54
  }
55
- function checkStat(stat, path9, options) {
55
+ function checkStat(stat, path8, options) {
56
56
  if (!stat.isSymbolicLink() && !stat.isFile()) {
57
57
  return false;
58
58
  }
59
- return checkPathExt(path9, options);
59
+ return checkPathExt(path8, options);
60
60
  }
61
- function isexe(path9, options, cb) {
62
- fs4.stat(path9, function(er, stat) {
63
- cb(er, er ? false : checkStat(stat, path9, options));
61
+ function isexe(path8, options, cb) {
62
+ fs4.stat(path8, function(er, stat) {
63
+ cb(er, er ? false : checkStat(stat, path8, options));
64
64
  });
65
65
  }
66
- function sync(path9, options) {
67
- return checkStat(fs4.statSync(path9), path9, options);
66
+ function sync(path8, options) {
67
+ return checkStat(fs4.statSync(path8), path8, options);
68
68
  }
69
69
  }
70
70
  });
@@ -75,13 +75,13 @@ var require_mode = __commonJS({
75
75
  module2.exports = isexe;
76
76
  isexe.sync = sync;
77
77
  var fs4 = require("fs");
78
- function isexe(path9, options, cb) {
79
- fs4.stat(path9, function(er, stat) {
78
+ function isexe(path8, options, cb) {
79
+ fs4.stat(path8, function(er, stat) {
80
80
  cb(er, er ? false : checkStat(stat, options));
81
81
  });
82
82
  }
83
- function sync(path9, options) {
84
- return checkStat(fs4.statSync(path9), options);
83
+ function sync(path8, options) {
84
+ return checkStat(fs4.statSync(path8), options);
85
85
  }
86
86
  function checkStat(stat, options) {
87
87
  return stat.isFile() && checkMode(stat, options);
@@ -114,7 +114,7 @@ var require_isexe = __commonJS({
114
114
  }
115
115
  module2.exports = isexe;
116
116
  isexe.sync = sync;
117
- function isexe(path9, options, cb) {
117
+ function isexe(path8, options, cb) {
118
118
  if (typeof options === "function") {
119
119
  cb = options;
120
120
  options = {};
@@ -124,7 +124,7 @@ var require_isexe = __commonJS({
124
124
  throw new TypeError("callback not provided");
125
125
  }
126
126
  return new Promise(function(resolve, reject) {
127
- isexe(path9, options || {}, function(er, is) {
127
+ isexe(path8, options || {}, function(er, is) {
128
128
  if (er) {
129
129
  reject(er);
130
130
  } else {
@@ -133,7 +133,7 @@ var require_isexe = __commonJS({
133
133
  });
134
134
  });
135
135
  }
136
- core(path9, options || {}, function(er, is) {
136
+ core(path8, options || {}, function(er, is) {
137
137
  if (er) {
138
138
  if (er.code === "EACCES" || options && options.ignoreErrors) {
139
139
  er = null;
@@ -143,9 +143,9 @@ var require_isexe = __commonJS({
143
143
  cb(er, is);
144
144
  });
145
145
  }
146
- function sync(path9, options) {
146
+ function sync(path8, options) {
147
147
  try {
148
- return core.sync(path9, options || {});
148
+ return core.sync(path8, options || {});
149
149
  } catch (er) {
150
150
  if (options && options.ignoreErrors || er.code === "EACCES") {
151
151
  return false;
@@ -161,7 +161,7 @@ var require_isexe = __commonJS({
161
161
  var require_which = __commonJS({
162
162
  "node_modules/which/which.js"(exports2, module2) {
163
163
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
164
- var path9 = require("path");
164
+ var path8 = require("path");
165
165
  var COLON = isWindows ? ";" : ":";
166
166
  var isexe = require_isexe();
167
167
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -199,7 +199,7 @@ var require_which = __commonJS({
199
199
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
200
200
  const ppRaw = pathEnv[i];
201
201
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
202
- const pCmd = path9.join(pathPart, cmd);
202
+ const pCmd = path8.join(pathPart, cmd);
203
203
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
204
204
  resolve(subStep(p, i, 0));
205
205
  });
@@ -226,7 +226,7 @@ var require_which = __commonJS({
226
226
  for (let i = 0; i < pathEnv.length; i++) {
227
227
  const ppRaw = pathEnv[i];
228
228
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
229
- const pCmd = path9.join(pathPart, cmd);
229
+ const pCmd = path8.join(pathPart, cmd);
230
230
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
231
231
  for (let j = 0; j < pathExt.length; j++) {
232
232
  const cur = p + pathExt[j];
@@ -4783,20 +4783,20 @@ function findSystemGitWin32(base, onLookup) {
4783
4783
  return findSpecificGit(import_path.default.join(base, "Git", "cmd", "git.exe"), onLookup);
4784
4784
  }
4785
4785
  function findGitWin32InPath(onLookup) {
4786
- const whichPromise = new Promise((c, e) => (0, import_which.default)("git.exe", (err, path9) => err ? e(err) : c(path9)));
4787
- return whichPromise.then((path9) => findSpecificGit(path9, onLookup));
4786
+ const whichPromise = new Promise((c, e) => (0, import_which.default)("git.exe", (err, path8) => err ? e(err) : c(path8)));
4787
+ return whichPromise.then((path8) => findSpecificGit(path8, onLookup));
4788
4788
  }
4789
4789
  function findGitWin32(onLookup) {
4790
4790
  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_path.default.join(process.env["LocalAppData"], "Programs"), onLookup)).then(void 0, () => findGitWin32InPath(onLookup));
4791
4791
  }
4792
- function findSpecificGit(path9, onLookup) {
4792
+ function findSpecificGit(path8, onLookup) {
4793
4793
  return new Promise((c, e) => {
4794
- onLookup(path9);
4794
+ onLookup(path8);
4795
4795
  const buffers = [];
4796
- const child = (0, import_child_process.spawn)(path9, ["--version"]);
4796
+ const child = (0, import_child_process.spawn)(path8, ["--version"]);
4797
4797
  child.stdout.on("data", (b) => buffers.push(b));
4798
4798
  child.on("error", cpErrorHandler(e));
4799
- child.on("exit", (code) => code ? e(new Error("Not found")) : c({ path: path9, version: parseVersion(Buffer.concat(buffers).toString("utf8").trim()) }));
4799
+ child.on("exit", (code) => code ? e(new Error("Not found")) : c({ path: path8, version: parseVersion(Buffer.concat(buffers).toString("utf8").trim()) }));
4800
4800
  });
4801
4801
  }
4802
4802
  function cpErrorHandler(cb) {
@@ -4813,20 +4813,20 @@ function findGitDarwin(onLookup) {
4813
4813
  if (err) {
4814
4814
  return e("git not found");
4815
4815
  }
4816
- const path9 = gitPathBuffer.toString().replace(/^\s+|\s+$/g, "");
4817
- function getVersion(path10) {
4818
- onLookup(path10);
4816
+ const path8 = gitPathBuffer.toString().replace(/^\s+|\s+$/g, "");
4817
+ function getVersion(path9) {
4818
+ onLookup(path9);
4819
4819
  (0, import_child_process.exec)("git --version", (err2, stdout) => {
4820
4820
  if (err2) {
4821
4821
  return e("git not found");
4822
4822
  }
4823
- return c({ path: path10, version: parseVersion(stdout.trim()) });
4823
+ return c({ path: path9, version: parseVersion(stdout.trim()) });
4824
4824
  });
4825
4825
  }
4826
- if (path9 !== "/usr/bin/git") {
4827
- return getVersion(path9);
4826
+ if (path8 !== "/usr/bin/git") {
4827
+ return getVersion(path8);
4828
4828
  }
4829
- getVersion(path9);
4829
+ getVersion(path8);
4830
4830
  (0, import_child_process.exec)("xcode-select -p", (err2) => {
4831
4831
  if (err2 && err2.code === 2) {
4832
4832
  return e("git not found");
@@ -5697,15 +5697,17 @@ var GitStatus = class {
5697
5697
  import_coc9.events.on("BufWritePost", () => {
5698
5698
  timer = setTimeout(() => {
5699
5699
  this.refresh();
5700
- }, 300);
5700
+ }, 50);
5701
5701
  }, this, this.disposables);
5702
5702
  this.disposables.push({
5703
5703
  dispose: () => {
5704
5704
  if (timer) clearTimeout(timer);
5705
5705
  }
5706
5706
  });
5707
- this.refresh().catch((_e) => {
5708
- });
5707
+ setTimeout(() => {
5708
+ this.refresh().catch((_e) => {
5709
+ });
5710
+ }, 300);
5709
5711
  }
5710
5712
  async refresh() {
5711
5713
  if (!this._enabled) return;
@@ -5750,41 +5752,43 @@ var DocumentManager = class {
5750
5752
  this.loadConfiguration();
5751
5753
  import_coc10.workspace.onDidChangeConfiguration(this.loadConfiguration, this, this.disposables);
5752
5754
  this.gitStatus = new GitStatus(service);
5753
- const createBuffer = (doc) => {
5754
- let { uri } = doc;
5755
+ import_coc10.workspace.registerBufferSync((doc) => {
5756
+ let disposed = false;
5757
+ let gitBuffer;
5758
+ let { bufnr, uri } = doc;
5755
5759
  service.createBuffer(doc, this.config).then((buf) => {
5756
- if (!buf || import_coc10.workspace.getDocument(uri) == null) return;
5760
+ if (!buf || disposed) return;
5761
+ gitBuffer = buf;
5757
5762
  this.defineSigns().catch((e) => {
5758
5763
  console.error(e.message);
5759
5764
  });
5760
5765
  this.buffers.set(doc.bufnr, buf);
5761
5766
  });
5762
- };
5763
- for (let doc of import_coc10.workspace.documents) {
5764
- createBuffer(doc);
5765
- }
5766
- import_coc10.workspace.onDidOpenTextDocument(async (e) => {
5767
- createBuffer(import_coc10.workspace.getDocument(e.bufnr));
5768
- }, null, this.disposables);
5769
- import_coc10.workspace.onDidChangeTextDocument(async (e) => {
5770
- let buf = this.buffers.get(e.bufnr);
5771
- if (buf) buf.refresh();
5772
- }, null, this.disposables);
5773
- import_coc10.workspace.onDidCloseTextDocument((e) => {
5774
- let buf = this.buffers.get(e.bufnr);
5775
- if (buf) buf.dispose();
5776
- this.buffers.delete(e.bufnr);
5777
- this.service.resolver.delete(e.uri);
5778
- }, null, this.disposables);
5767
+ return {
5768
+ onChange: () => {
5769
+ if (gitBuffer) gitBuffer._refresh();
5770
+ },
5771
+ dispose: () => {
5772
+ disposed = true;
5773
+ this.buffers.delete(bufnr);
5774
+ this.service.resolver.delete(uri);
5775
+ if (gitBuffer) gitBuffer.dispose();
5776
+ }
5777
+ };
5778
+ });
5779
5779
  import_coc10.events.on("CursorMoved", (0, import_debounce.default)(async (bufnr, cursor) => {
5780
5780
  let buf = this.buffers.get(bufnr);
5781
5781
  if (buf) await buf.showBlameInfo(cursor[0]);
5782
5782
  }, 100), null, this.disposables);
5783
- import_coc10.events.on("BufWritePre", (bufnr) => {
5784
- if (!this.enableGutters || this.config.realtimeGutters) return;
5785
- let buf = this.buffers.get(bufnr);
5786
- if (buf) buf.updateGutters();
5787
- }, null, this.disposables);
5783
+ import_coc10.workspace.registerAutocmd({
5784
+ event: "BufWritePost",
5785
+ arglist: ["+expand('<abuf>')"],
5786
+ callback: (bufnr) => {
5787
+ if (!this.enableGutters || this.config.realtimeGutters) return;
5788
+ let buf = this.buffers.get(bufnr);
5789
+ if (buf) buf.diffDocument(true);
5790
+ }
5791
+ });
5788
5792
  import_coc10.events.on("FocusGained", async () => {
5789
5793
  let bufnr = await nvim.call("bufnr", ["%"]);
5790
5794
  let buf = this.buffers.get(bufnr);
@@ -6039,1524 +6043,1514 @@ var DocumentManager = class {
6039
6043
  // src/model/service.ts
6040
6044
  var import_coc14 = require("coc.nvim");
6041
6045
 
6042
- // src/model/repo.ts
6043
- var import_fs2 = __toESM(require("fs"));
6044
- var import_os = __toESM(require("os"));
6045
- var import_path5 = __toESM(require("path"));
6046
- var import_util6 = __toESM(require("util"));
6046
+ // src/model/buffer.ts
6047
+ var import_coc11 = require("coc.nvim");
6048
+ var import_debounce2 = __toESM(require_debounce());
6047
6049
 
6048
- // node_modules/uuid/dist/esm-node/rng.js
6049
- var import_crypto = __toESM(require("crypto"));
6050
- function rng() {
6051
- return import_crypto.default.randomBytes(16);
6050
+ // node_modules/timeago.js/esm/lang/en_US.js
6051
+ var EN_US = ["second", "minute", "hour", "day", "week", "month", "year"];
6052
+ function en_US_default(diff, idx) {
6053
+ if (idx === 0)
6054
+ return ["just now", "right now"];
6055
+ var unit = EN_US[Math.floor(idx / 2)];
6056
+ if (diff > 1)
6057
+ unit += "s";
6058
+ return [diff + " " + unit + " ago", "in " + diff + " " + unit];
6052
6059
  }
6053
6060
 
6054
- // node_modules/uuid/dist/esm-node/bytesToUuid.js
6055
- var byteToHex = [];
6056
- for (i = 0; i < 256; ++i) {
6057
- byteToHex[i] = (i + 256).toString(16).substr(1);
6058
- }
6059
- var i;
6060
- function bytesToUuid(buf, offset) {
6061
- var i = offset || 0;
6062
- var bth = byteToHex;
6063
- return [bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]]].join("");
6061
+ // node_modules/timeago.js/esm/lang/zh_CN.js
6062
+ var ZH_CN = ["\u79D2", "\u5206\u949F", "\u5C0F\u65F6", "\u5929", "\u5468", "\u4E2A\u6708", "\u5E74"];
6063
+ function zh_CN_default(diff, idx) {
6064
+ if (idx === 0)
6065
+ return ["\u521A\u521A", "\u7247\u523B\u540E"];
6066
+ var unit = ZH_CN[~~(idx / 2)];
6067
+ return [diff + " " + unit + "\u524D", diff + " " + unit + "\u540E"];
6064
6068
  }
6065
- var bytesToUuid_default = bytesToUuid;
6066
6069
 
6067
- // node_modules/uuid/dist/esm-node/v4.js
6068
- function v4(options, buf, offset) {
6069
- var i = buf && offset || 0;
6070
- if (typeof options == "string") {
6071
- buf = options === "binary" ? new Array(16) : null;
6072
- options = null;
6073
- }
6074
- options = options || {};
6075
- var rnds = options.random || (options.rng || rng)();
6076
- rnds[6] = rnds[6] & 15 | 64;
6077
- rnds[8] = rnds[8] & 63 | 128;
6078
- if (buf) {
6079
- for (var ii = 0; ii < 16; ++ii) {
6080
- buf[i + ii] = rnds[ii];
6081
- }
6070
+ // node_modules/timeago.js/esm/register.js
6071
+ var Locales = {};
6072
+ var register = function(locale, func) {
6073
+ Locales[locale] = func;
6074
+ };
6075
+ var getLocale = function(locale) {
6076
+ return Locales[locale] || Locales["en_US"];
6077
+ };
6078
+
6079
+ // node_modules/timeago.js/esm/utils/date.js
6080
+ var SEC_ARRAY = [
6081
+ 60,
6082
+ 60,
6083
+ 24,
6084
+ 7,
6085
+ 365 / 7 / 12,
6086
+ 12
6087
+ ];
6088
+ function toDate(input) {
6089
+ if (input instanceof Date)
6090
+ return input;
6091
+ if (!isNaN(input) || /^\d+$/.test(input))
6092
+ return new Date(parseInt(input));
6093
+ input = (input || "").trim().replace(/\.\d+/, "").replace(/-/, "/").replace(/-/, "/").replace(/(\d)T(\d)/, "$1 $2").replace(/Z/, " UTC").replace(/([+-]\d\d):?(\d\d)/, " $1$2");
6094
+ return new Date(input);
6095
+ }
6096
+ function formatDiff(diff, localeFunc) {
6097
+ var agoIn = diff < 0 ? 1 : 0;
6098
+ diff = Math.abs(diff);
6099
+ var totalSec = diff;
6100
+ var idx = 0;
6101
+ for (; diff >= SEC_ARRAY[idx] && idx < SEC_ARRAY.length; idx++) {
6102
+ diff /= SEC_ARRAY[idx];
6082
6103
  }
6083
- return buf || bytesToUuid_default(rnds);
6104
+ diff = Math.floor(diff);
6105
+ idx *= 2;
6106
+ if (diff > (idx === 0 ? 9 : 1))
6107
+ idx += 1;
6108
+ return localeFunc(diff, idx, totalSec)[agoIn].replace("%s", diff.toString());
6109
+ }
6110
+ function diffSec(date, relativeDate) {
6111
+ var relDate = relativeDate ? toDate(relativeDate) : /* @__PURE__ */ new Date();
6112
+ return (+relDate - +toDate(date)) / 1e3;
6084
6113
  }
6085
- var v4_default = v4;
6086
6114
 
6087
- // src/model/repo.ts
6088
- var Repo = class {
6089
- constructor(git, channel, root) {
6115
+ // node_modules/timeago.js/esm/format.js
6116
+ var format = function(date, locale, opts) {
6117
+ var sec = diffSec(date, opts && opts.relativeDate);
6118
+ return formatDiff(sec, getLocale(locale));
6119
+ };
6120
+
6121
+ // node_modules/timeago.js/esm/index.js
6122
+ register("en_US", en_US_default);
6123
+ register("zh_CN", zh_CN_default);
6124
+
6125
+ // src/model/buffer.ts
6126
+ var import_url = require("url");
6127
+ var signGroup = "CocGit";
6128
+ var revPattern = "([0-9A-Za-z_.:/-]+)";
6129
+ var startPattern = new RegExp(`^<{7} (${revPattern})(:? .+)?$`);
6130
+ var sepPattern = new RegExp(`^={7}$`);
6131
+ var endPattern = new RegExp(`^>{7} (${revPattern})(:? .+)?$`);
6132
+ var commonPattern = /^\|{7}\smerged\scommon\sancestors/;
6133
+ var GitBuffer = class {
6134
+ constructor(doc, config, relpath, repo, git, channel, floatFactory, hasConflicts) {
6135
+ this.doc = doc;
6136
+ this.config = config;
6137
+ this.relpath = relpath;
6138
+ this.repo = repo;
6090
6139
  this.git = git;
6091
6140
  this.channel = channel;
6092
- this.root = root;
6093
- }
6094
- /**
6095
- * Get staged info
6096
- */
6097
- async getStagedChunks(relpath) {
6098
- let args = ["--no-pager", "diff", "--no-ext-diff", "-p", "-U0", "--no-color", "--staged"];
6099
- if (relpath) args.push(toUnixSlash(relpath));
6100
- const result = await this.exec(args);
6101
- if (!result.stdout) {
6102
- throw new Error(`No staged result.`);
6103
- }
6104
- let res = {};
6105
- let idx = 0;
6106
- let lines = result.stdout.split(/\r?\n/);
6107
- let curr;
6108
- let fsPath;
6109
- while (idx < lines.length) {
6110
- let line = lines[idx];
6111
- if (fsPath && line.startsWith("@@")) {
6112
- curr = void 0;
6113
- let ms = line.match(/^@@\s+-(\d+),?(\d*)\s+\+(\d+),?(\d*)\s+@@/);
6114
- if (ms) {
6115
- curr = {
6116
- remove: { lnum: Number(ms[1]), count: ms[2] ? Number(ms[2]) : 1 },
6117
- add: { lnum: Number(ms[3]), count: ms[4] ? Number(ms[4]) : 1 },
6118
- lines: []
6119
- };
6120
- res[fsPath] = res[fsPath] || [];
6121
- res[fsPath].push(curr);
6122
- }
6123
- } else if (curr && /^[+\-]/.test(line)) {
6124
- curr.lines.push(line);
6125
- } else if (line.startsWith("diff --git")) {
6126
- let ms = line.match(/diff\s--git\sa\/(.*)\sb\//);
6127
- if (ms) {
6128
- fsPath = ms[1];
6129
- curr = void 0;
6130
- idx += 4;
6131
- continue;
6132
- }
6133
- }
6134
- idx++;
6135
- }
6136
- return res;
6141
+ this.floatFactory = floatFactory;
6142
+ this.hasConflicts = hasConflicts;
6143
+ this.blameInfo = [];
6144
+ this.diffs = [];
6145
+ this.conflicts = [];
6146
+ this.currentSigns = [];
6147
+ this.gitStatus = "";
6148
+ this.foldEnabled = false;
6149
+ this._disposed = false;
6150
+ this.mutex = new import_coc11.Mutex();
6151
+ this.refresh = (0, import_debounce2.default)(() => {
6152
+ this._refresh().catch((e) => {
6153
+ channel.append(`[Error] ${e.message}`);
6154
+ });
6155
+ }, 200);
6156
+ this._refresh();
6137
6157
  }
6138
- async getHEAD() {
6139
- try {
6140
- const result = await this.exec(["symbolic-ref", "--short", "HEAD"]);
6141
- if (!result.stdout) {
6142
- throw new Error("Not in a branch");
6143
- }
6144
- return result.stdout.trim();
6145
- } catch (err) {
6146
- const result = await this.exec(["rev-parse", "HEAD"]);
6147
- if (!result.stdout) {
6148
- throw new Error("Error parsing HEAD");
6149
- }
6150
- return result.stdout.trim();
6151
- }
6158
+ get cachedDiffs() {
6159
+ return this.diffs;
6152
6160
  }
6153
- async hasChanged() {
6154
- let result = await this.exec(["diff", "--name-status"]);
6155
- if (!result.stdout) return false;
6156
- let lines = result.stdout.split(/\r?\n/);
6157
- return lines.some((l) => l.startsWith("M"));
6161
+ async _refresh() {
6162
+ if (this._disposed) return;
6163
+ this.refresh.clear();
6164
+ let release = await this.mutex.acquire();
6165
+ let result = await Promise.allSettled([
6166
+ this.diffDocument(),
6167
+ this.loadBlames(),
6168
+ this.parseConflicts()
6169
+ ]);
6170
+ result.forEach((res) => {
6171
+ if (res.status === "rejected") this.channel.append(`[Error] refresh error ${res.reason}`);
6172
+ });
6173
+ release();
6158
6174
  }
6159
- async getStaged() {
6160
- let result = await this.exec(["diff", "--staged", "--name-status"]);
6161
- if (!result.stdout) return [0, 0];
6162
- let lines = result.stdout.trim().split(/\r?\n/);
6163
- let conflicted = 0;
6164
- let staged = 0;
6165
- lines.forEach((line) => {
6166
- if (!line.length) return;
6167
- if (line.startsWith("U")) {
6168
- conflicted++;
6169
- } else {
6170
- staged++;
6175
+ getChunk(line) {
6176
+ if (!this.diffs || this.diffs.length == 0) return void 0;
6177
+ return this.diffs.find((ch) => {
6178
+ let { start, added, removed, changeType } = ch;
6179
+ if (line == 1 && start == 0 && ch.end == 0) {
6180
+ return true;
6181
+ }
6182
+ let end = changeType === "changed" /* Change */ && added.count > removed.count ? ch.end + added.count - removed.count : ch.end;
6183
+ if (start <= line && end >= line) {
6184
+ return true;
6171
6185
  }
6186
+ return false;
6172
6187
  });
6173
- return [conflicted, staged];
6174
6188
  }
6175
- async hasUntracked() {
6176
- let cp2 = this.git.stream(this.root, ["ls-files", "--others", "--exclude-standard"]);
6177
- return new Promise((resolve) => {
6178
- let hasData = false;
6179
- let timer = setTimeout(() => {
6180
- if (cp2.killed) return;
6181
- cp2.kill("SIGKILL");
6182
- resolve(false);
6183
- }, 100);
6184
- cp2.stdout.on("data", () => {
6185
- clearTimeout(timer);
6186
- hasData = true;
6187
- cp2.kill("SIGKILL");
6188
- resolve(hasData);
6189
- });
6190
- cp2.on("exit", () => {
6191
- clearTimeout(timer);
6192
- resolve(hasData);
6189
+ async chunkUndo() {
6190
+ let line = await import_coc11.workspace.nvim.call("line", ".");
6191
+ let diff = this.getChunk(line);
6192
+ if (!diff) return;
6193
+ let { start, lines, changeType } = diff;
6194
+ let added = lines.filter((s) => s.startsWith("-")).map((s) => s.slice(1));
6195
+ let removeCount = lines.filter((s) => s.startsWith("+")).length;
6196
+ let buf = this.doc.buffer;
6197
+ if (changeType == "delete" /* Delete */) {
6198
+ await buf.setLines(added, { start, end: start, strictIndexing: false });
6199
+ } else {
6200
+ await buf.setLines(added, {
6201
+ start: start - 1,
6202
+ end: start - 1 + removeCount,
6203
+ strictIndexing: false
6193
6204
  });
6194
- });
6195
- }
6196
- async getStatus(character, decorator) {
6197
- try {
6198
- let head = await this.getHEAD();
6199
- if (!head) return "";
6200
- let [changed, staged, untracked] = await Promise.all([this.hasChanged(), this.getStaged(), this.hasUntracked()]);
6201
- const { changedDecorator, conflictedDecorator, stagedDecorator, untrackedDecorator } = decorator;
6202
- let more = "";
6203
- if (changed) more += changedDecorator;
6204
- if (staged[0]) more += conflictedDecorator;
6205
- if (staged[1]) more += stagedDecorator;
6206
- if (untracked) more += untrackedDecorator;
6207
- return `${character ? character + " " : ""}${head}${more}`;
6208
- } catch (e) {
6209
- this.channel.appendLine("Error on git status");
6210
- this.channel.append(e.message);
6211
- return "";
6212
6205
  }
6213
6206
  }
6214
- async getDiff(relFilepath, content, revision = "", encoding = "utf8") {
6215
- if (relFilepath.startsWith(`.git${import_path5.default.sep}`)) return;
6216
- let fullpath = import_path5.default.join(this.root, relFilepath);
6217
- if (!import_fs2.default.existsSync(fullpath)) return;
6218
- let staged;
6207
+ async chunkStage() {
6208
+ let relpath = toUnixSlash(this.relpath);
6209
+ let line = await import_coc11.workspace.nvim.call("line", ".");
6210
+ let diff = this.getChunk(line);
6211
+ if (!diff) {
6212
+ import_coc11.window.showErrorMessage("Not positioned in git chunk");
6213
+ return;
6214
+ }
6215
+ let head;
6216
+ if (diff.changeType === "add" /* Add */) {
6217
+ head = `@@ -${diff.removed.start + 1},0 +${diff.removed.start + 1},${diff.added.count} @@`;
6218
+ } else if (diff.changeType === "delete" /* Delete */) {
6219
+ head = `@@ -${diff.removed.start},${diff.removed.count} +${diff.removed.start},0 @@`;
6220
+ } else if (diff.changeType === "changed" /* Change */) {
6221
+ head = `@@ -${diff.removed.start},${diff.removed.count} +${diff.removed.start},${diff.added.count} @@`;
6222
+ }
6223
+ const lines = [
6224
+ `diff --git a/${relpath} b/${relpath}`,
6225
+ `index 000000..000000 100644`,
6226
+ `--- a/${relpath}`,
6227
+ `+++ b/${relpath}`,
6228
+ head
6229
+ ];
6230
+ lines.push(...diff.lines);
6231
+ lines.push("");
6219
6232
  try {
6220
- let indexed = await this.isIndexed(relFilepath);
6221
- if (!indexed) return;
6222
- let res = await this.exec(["--no-pager", "show", `${revision}:${toUnixSlash(relFilepath)}`], { encoding });
6223
- if (!res.stdout) return;
6224
- staged = res.stdout.replace(/\r?\n$/, "").split(/\r?\n/).join("\n");
6233
+ await this.git.exec(this.repo.root, ["apply", "--cached", "--unidiff-zero", "-"], { input: lines.join("\n") });
6234
+ this.refresh();
6225
6235
  } catch (e) {
6226
- this.channel.append(e.stack);
6227
- return;
6236
+ this.channel.appendLine(`[Error] ${e.message}`);
6228
6237
  }
6229
- const stagedFile = import_path5.default.join(import_os.default.tmpdir(), `coc-${v4_default()}`);
6230
- const currentFile = import_path5.default.join(import_os.default.tmpdir(), `coc-${v4_default()}`);
6231
- await import_util6.default.promisify(import_fs2.default.writeFile)(stagedFile, staged + "\n", "utf8");
6232
- await import_util6.default.promisify(import_fs2.default.writeFile)(currentFile, content, "utf8");
6233
- let output = await getStdout(`git --no-pager diff --no-ext-diff -p -U0 --no-color ${shellescape(stagedFile)} ${shellescape(currentFile)}`);
6234
- await import_util6.default.promisify(import_fs2.default.unlink)(stagedFile);
6235
- await import_util6.default.promisify(import_fs2.default.unlink)(currentFile);
6236
- if (!output) return [];
6237
- this.channel.appendLine(`> git diff ${relFilepath}`);
6238
- const lines = output.trim().split("\n");
6239
- return parseDiff(lines);
6240
6238
  }
6241
- async getDiffAll(category) {
6242
- let diffGroups = /* @__PURE__ */ new Map();
6243
- let args = "";
6244
- if (category === 0 /* All */) {
6245
- args = "HEAD";
6246
- } else if (category === 1 /* Staged */) {
6247
- args = "--cached";
6248
- } else {
6249
- args = "";
6250
- }
6251
- let output = await getStdout(`git --no-pager diff --no-ext-diff -p -U0 --no-color ${args}`);
6252
- if (!output) return diffGroups;
6253
- const lines = output.trim().split("\n");
6254
- let lineGroups = /* @__PURE__ */ new Map();
6255
- let file = null;
6256
- for (const line of lines) {
6257
- if (line.startsWith("diff --git")) {
6258
- let ms = line.match(/diff\s--git\sa\/(.*)\sb\//);
6259
- if (ms) {
6260
- file = ms[1];
6261
- } else {
6262
- file = null;
6263
- }
6264
- }
6265
- if (file) {
6266
- if (!lineGroups.has(file)) {
6267
- lineGroups.set(file, []);
6239
+ async chunkUnstage() {
6240
+ let { nvim } = import_coc11.workspace;
6241
+ const { diffs } = this;
6242
+ let line = await nvim.call("line", ".");
6243
+ let adjust = 0;
6244
+ let invalid = false;
6245
+ for (let diff of diffs) {
6246
+ if (diff.end >= line) {
6247
+ if (diff.start <= line && diff.changeType != "delete" /* Delete */) {
6248
+ import_coc11.window.showErrorMessage(`Current line contains unstaged change.`);
6249
+ invalid = true;
6268
6250
  }
6269
- lineGroups.get(file).push(line);
6251
+ break;
6270
6252
  }
6253
+ adjust -= diff.added.count;
6254
+ adjust += diff.removed.count;
6271
6255
  }
6272
- for (const [file2, lines2] of lineGroups) {
6273
- let diffs = parseDiff(lines2);
6274
- if (diffs) {
6275
- diffGroups.set(file2, diffs);
6276
- }
6256
+ if (invalid) return;
6257
+ line = line + adjust;
6258
+ let stagedDiff = await this.repo.getStagedChunks(this.relpath);
6259
+ let chunks = Object.values(stagedDiff)[0];
6260
+ if (!chunks.length) {
6261
+ import_coc11.window.showErrorMessage(`Staged chunk not found`);
6262
+ return;
6277
6263
  }
6278
- return diffGroups;
6279
- }
6280
- async isIgnored(relativePath) {
6281
- let res = await this.safeRun(["check-ignore", "--", relativePath]);
6282
- return res.trim() == relativePath;
6283
- }
6284
- async hasConflicts(relativePath) {
6285
- let indexed = await this.isIndexed(relativePath);
6286
- if (!indexed) return false;
6287
- let res = await this.exec(["diff", "--staged", "--name-status", "--", relativePath]);
6288
- return res.stdout.trim().startsWith("U");
6289
- }
6290
- async isIndexed(relpath) {
6291
- let res = await this.exec(["ls-files", "--", relpath]);
6292
- return res.stdout && res.stdout.trim().length > 0;
6293
- }
6294
- async getUsername() {
6295
- if (typeof this.userName === "string") return this.userName;
6296
- try {
6297
- let res = await this.exec(["config", "user.name"]);
6298
- this.userName = res.stdout.trim();
6299
- return this.userName;
6300
- } catch (e) {
6301
- this.userName = "";
6302
- return "";
6264
+ let chunk = chunks.find((o) => o.add.lnum <= line && o.add.lnum + o.add.count >= line);
6265
+ if (!chunk) {
6266
+ import_coc11.window.showErrorMessage(`Unable to find staged chunk on current line`);
6267
+ return;
6303
6268
  }
6304
- }
6305
- async isShallow() {
6269
+ this.channel.appendLine(`[Info] resolved chunk ${JSON.stringify(chunk, null, 2)}`);
6270
+ let patch = createUnstagePatch(this.relpath, chunk);
6271
+ if (!patch) return;
6306
6272
  try {
6307
- let res = await this.exec(["rev-parse", "--is-shallow-repository"]);
6308
- return res.stdout.trim() === "true";
6273
+ await this.git.exec(this.repo.root, ["apply", "--cached", "--unidiff-zero", "-"], { input: patch });
6274
+ this.refresh();
6309
6275
  } catch (e) {
6310
- return false;
6276
+ import_coc11.window.showErrorMessage(`Unable to apply patch: ${e.message}`);
6277
+ this.channel.appendLine(`[Error] ${e.message}`);
6311
6278
  }
6312
6279
  }
6313
- async exec(args, options = {}) {
6314
- return await this.git.exec(this.root, args, options);
6315
- }
6316
- async safeRun(args, options = {}) {
6317
- try {
6318
- let res = await this.exec(args, options);
6319
- return res ? res.stdout.replace(/\s*$/, "") : "";
6320
- } catch (e) {
6321
- return "";
6280
+ async nextChunk() {
6281
+ const { diffs } = this;
6282
+ let { nvim } = import_coc11.workspace;
6283
+ if (!diffs || diffs.length == 0) return;
6284
+ let line = await nvim.call("line", ".");
6285
+ for (let diff of diffs) {
6286
+ if (diff.start > line) {
6287
+ await import_coc11.window.moveTo({ line: Math.max(diff.start - 1, 0), character: 0 });
6288
+ return;
6289
+ }
6290
+ }
6291
+ if (await nvim.getOption("wrapscan")) {
6292
+ await import_coc11.window.moveTo({ line: Math.max(diffs[0].start - 1, 0), character: 0 });
6322
6293
  }
6323
6294
  }
6324
- };
6325
- function parseDiff(diffLines) {
6326
- const allLines = diffLines.slice(4);
6327
- const diffs = [];
6328
- let diff = null;
6329
- for (const line of allLines) {
6330
- if (!line.startsWith("@@")) {
6331
- if (diff) {
6332
- diff.lines.push(line);
6295
+ async prevChunk() {
6296
+ const { nvim } = import_coc11.workspace;
6297
+ let { diffs } = this;
6298
+ let line = await nvim.call("line", ".");
6299
+ if (!diffs || diffs.length == 0) return;
6300
+ for (let diff of diffs.slice().reverse()) {
6301
+ if (diff.end < line) {
6302
+ await import_coc11.window.moveTo({ line: Math.max(diff.start - 1, 0), character: 0 });
6303
+ return;
6333
6304
  }
6334
- continue;
6335
6305
  }
6336
- let diffKey = line.split("@@", 2)[1].trim();
6337
- const [pres, nows] = diffKey.split(/\s+/).map((str) => str.slice(1).split(","));
6338
- const removed = {
6339
- start: parseInt(pres[0], 10),
6340
- count: parseInt(`${pres[1] || 1}`, 10)
6341
- };
6342
- const added = {
6343
- start: parseInt(nows[0], 10),
6344
- count: parseInt(`${nows[1] || 1}`, 10)
6345
- };
6346
- if (added.count === 0) {
6347
- diff = {
6348
- lines: [],
6349
- start: added.start,
6350
- end: added.start,
6351
- head: line,
6352
- removed,
6353
- added,
6354
- changeType: "delete" /* Delete */
6355
- };
6356
- diffs.push(diff);
6357
- } else if (removed.count === 0) {
6358
- diff = {
6359
- lines: [],
6360
- start: added.start,
6361
- end: added.start + added.count - 1,
6362
- head: line,
6363
- removed,
6364
- added,
6365
- changeType: "add" /* Add */
6366
- };
6367
- diffs.push(diff);
6368
- } else {
6369
- diff = {
6370
- lines: [],
6371
- start: added.start,
6372
- end: added.start + Math.min(added.count, removed.count) - 1,
6373
- head: line,
6374
- removed,
6375
- added,
6376
- changeType: "changed" /* Change */
6377
- };
6378
- diffs.push(diff);
6306
+ if (await nvim.getOption("wrapscan")) {
6307
+ await import_coc11.window.moveTo({ line: Math.max(diffs[diffs.length - 1].start - 1, 0), character: 0 });
6379
6308
  }
6380
6309
  }
6381
- return diffs;
6382
- }
6383
-
6384
- // src/model/git.ts
6385
- var cp = __toESM(require("child_process"));
6386
- var import_coc11 = require("coc.nvim");
6387
- var import_iconv_lite = __toESM(require_lib());
6388
- var import_path6 = __toESM(require("path"));
6389
- var Git = class {
6390
- constructor(gitInfo, channel) {
6391
- this.gitInfo = gitInfo;
6392
- this.channel = channel;
6393
- }
6394
- async getRepositoryRoot(repositoryPath) {
6395
- const result = await this.exec(repositoryPath, ["rev-parse", "--show-toplevel"]);
6396
- let repoRootPath = import_path6.default.normalize(result.stdout.trim());
6397
- if (process.platform === "win32" && repoRootPath.startsWith("\\") && !process.env.SHELL) {
6398
- repoRootPath = repoRootPath.replace(/^\\([^\\]*)\\/, "$1:\\");
6310
+ async chunkInfo() {
6311
+ let line = await import_coc11.workspace.nvim.call("line", ".");
6312
+ let diff = this.getChunk(line);
6313
+ if (diff) {
6314
+ let content = diff.head + "\n" + diff.lines.join("\n");
6315
+ await this.showDoc(content, "diff");
6316
+ } else {
6317
+ let chunks = [];
6318
+ try {
6319
+ let stagedDiff = await this.repo.getStagedChunks(this.relpath);
6320
+ chunks = Object.values(stagedDiff)[0];
6321
+ } catch (e) {
6322
+ }
6323
+ let adjust = 0;
6324
+ for (let diff2 of this.diffs) {
6325
+ if (diff2.end >= line) {
6326
+ break;
6327
+ }
6328
+ adjust -= diff2.added.count;
6329
+ adjust += diff2.removed.count;
6330
+ }
6331
+ line = line + adjust;
6332
+ let chunk = chunks.find((o) => o.add.lnum <= line && o.add.lnum + o.add.count >= line);
6333
+ if (chunk) {
6334
+ let content = "Staged changes\n" + chunk.lines.join("\n");
6335
+ await this.showDoc(content, "diff");
6336
+ } else {
6337
+ await this.showCommit(true);
6338
+ }
6399
6339
  }
6400
- return repoRootPath;
6401
- }
6402
- async exec(cwd, args, options = {}) {
6403
- options = Object.assign({ cwd }, options || {});
6404
- return await this._exec(args, options);
6405
6340
  }
6406
- stream(cwd, args, options = {}) {
6407
- options = Object.assign({ cwd }, options || {});
6408
- return this.spawn(args, options);
6409
- }
6410
- async _exec(args, options = {}) {
6411
- const child = this.spawn(args, options);
6412
- if (options.input) {
6413
- child.stdin.end(options.input, "utf8");
6414
- }
6415
- const bufferResult = await exec2(child, options.cancellationToken);
6416
- if (options.log !== false && bufferResult.stderr.length > 0) {
6417
- this.log(`${bufferResult.stderr}
6418
- `);
6341
+ async showBlameInfo(lnum) {
6342
+ let { nvim } = import_coc11.workspace;
6343
+ let { virtualTextSrcId, addGBlameToBufferVar, addGBlameToVirtualText } = this.config;
6344
+ if (!this.showBlame) return;
6345
+ let infos = this.blameInfo;
6346
+ if (!infos) return;
6347
+ let blameText;
6348
+ if (infos.length == 0) {
6349
+ blameText = "File not indexed";
6350
+ } else {
6351
+ let info = infos.find((o) => lnum >= o.startLnum && lnum <= o.endLnum);
6352
+ if (info && info.author && info.author != "Not Committed Yet") {
6353
+ blameText = `(${info.author} ${info.time}) ${info.summary}`;
6354
+ } else {
6355
+ blameText = "Not committed yet";
6356
+ }
6419
6357
  }
6420
- let encoding = options.encoding || "utf8";
6421
- encoding = import_iconv_lite.default.encodingExists(encoding) ? encoding : "utf8";
6422
- const result = {
6423
- exitCode: bufferResult.exitCode,
6424
- stdout: import_iconv_lite.default.decode(bufferResult.stdout, encoding),
6425
- stderr: bufferResult.stderr
6426
- };
6427
- if (bufferResult.exitCode) {
6428
- this.channel.appendLine(`Error ${result.exitCode} on: 'git ${args.join(" ")}' in ${options.cwd}`);
6429
- this.channel.append(result.stderr);
6430
- this.channel.append(result.stdout);
6431
- return Promise.reject(new Error("Failed to execute git"));
6358
+ let buffer = nvim.createBuffer(this.doc.bufnr);
6359
+ let hide_blame = await nvim.getVar("coc_git_hide_blame_virtual_text");
6360
+ if (hide_blame) {
6361
+ buffer.clearNamespace(virtualTextSrcId);
6362
+ } else {
6363
+ if (addGBlameToBufferVar) {
6364
+ nvim.pauseNotification();
6365
+ buffer.setVar("coc_git_blame", blameText, true);
6366
+ nvim.call("coc#util#do_autocmd", ["CocGitStatusChange"], true);
6367
+ nvim.resumeNotification(false, true);
6368
+ }
6369
+ if (addGBlameToVirtualText) {
6370
+ const prefix = this.config.virtualTextPrefix;
6371
+ nvim.pauseNotification();
6372
+ buffer.clearNamespace(virtualTextSrcId);
6373
+ buffer.setVirtualText(virtualTextSrcId, lnum - 1, [[prefix + blameText, "CocCodeLens"]]);
6374
+ nvim.resumeNotification(true, true);
6375
+ }
6432
6376
  }
6433
- return result;
6434
6377
  }
6435
- spawn(args, options = {}) {
6436
- if (!options) {
6437
- options = {};
6438
- }
6439
- if (!options.stdio && !options.input) {
6440
- options.stdio = ["ignore", null, null];
6378
+ async showBlameDoc(lnum) {
6379
+ let indexed = await this.repo.isIndexed(this.relpath);
6380
+ if (!indexed) {
6381
+ import_coc11.window.showWarningMessage("File not indexed");
6382
+ } else if (await this.repo.isShallow()) {
6383
+ import_coc11.window.showWarningMessage("Shallow repository, blame not available");
6384
+ } else {
6385
+ let infos = await this.getBlameInfo([lnum, lnum]);
6386
+ let info = infos.find((o) => lnum >= o.startLnum && lnum <= o.endLnum);
6387
+ if (info && info.author && info.author != "Not Committed Yet") {
6388
+ let blameText = [];
6389
+ blameText.push(`${info.author}, ${info.time}`);
6390
+ blameText.push(`${info.summary}`);
6391
+ blameText.push(`${info.sha.substring(0, 7)}`);
6392
+ await this.showDoc(blameText.join("\n\n"), "text");
6393
+ } else {
6394
+ import_coc11.window.showWarningMessage("Not committed yet");
6395
+ }
6441
6396
  }
6442
- options.env = Object.assign({}, process.env, options.env || {}, {
6443
- LC_ALL: "en_US.UTF-8",
6444
- LANG: "en_US.UTF-8"
6445
- });
6446
- if (process.platform === "win32") {
6447
- options.shell = true;
6397
+ }
6398
+ async diffDocument(force = false) {
6399
+ let { nvim } = import_coc11.workspace;
6400
+ let revision = this.config.diffRevision;
6401
+ const { bufnr } = this.doc;
6402
+ let content = this.doc.content;
6403
+ let eol = this.doc.textDocument["eol"];
6404
+ let encoding = await this.doc.buffer.getOption("fileencoding");
6405
+ const diffs = await this.repo.getDiff(this.relpath, eol ? content : content + "\n", revision, encoding || "utf8");
6406
+ if (diffs == null) return;
6407
+ if (diffs.length === 0) {
6408
+ this.currentSigns = [];
6409
+ this.diffs = [];
6410
+ this.setBufferStatus("");
6411
+ if (this.config.enableGutters) {
6412
+ nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
6413
+ nvim.redrawVim();
6414
+ }
6415
+ return;
6448
6416
  }
6449
- if (options.log !== false) {
6450
- this.log(`> git ${args.join(" ")}
6451
- `);
6417
+ if (equals(diffs, this.diffs)) {
6418
+ return;
6452
6419
  }
6453
- return cp.spawn(this.gitInfo.path, args, options);
6454
- }
6455
- log(output) {
6456
- this.channel.append(output);
6457
- }
6458
- };
6459
- async function exec2(child, cancellationToken) {
6460
- if (!child.stdout || !child.stderr) {
6461
- throw new Error("Failed to get stdout or stderr from git process.");
6462
- }
6463
- if (cancellationToken && cancellationToken.isCancellationRequested) {
6464
- throw new Error("Cancelled");
6465
- }
6466
- const disposables = [];
6467
- const once = (ee, name, fn) => {
6468
- ee.once(name, fn);
6469
- disposables.push(import_coc11.Disposable.create(() => ee.removeListener(name, fn)));
6470
- };
6471
- const on = (ee, name, fn) => {
6472
- ee.on(name, fn);
6473
- disposables.push(import_coc11.Disposable.create(() => ee.removeListener(name, fn)));
6474
- };
6475
- let result = Promise.all([
6476
- new Promise((c, e) => {
6477
- once(child, "error", cpErrorHandler(e));
6478
- once(child, "exit", c);
6479
- }),
6480
- new Promise((c) => {
6481
- const buffers = [];
6482
- on(child.stdout, "data", (b) => buffers.push(b));
6483
- once(child.stdout, "close", () => c(Buffer.concat(buffers)));
6484
- }),
6485
- new Promise((c) => {
6486
- const buffers = [];
6487
- on(child.stderr, "data", (b) => buffers.push(b));
6488
- once(child.stderr, "close", () => c(Buffer.concat(buffers).toString("utf8")));
6489
- })
6490
- ]);
6491
- if (cancellationToken) {
6492
- const cancellationPromise = new Promise((_, e) => {
6493
- onceEvent(cancellationToken.onCancellationRequested)(() => {
6494
- try {
6495
- child.kill();
6496
- } catch (err) {
6420
+ this.diffs = diffs;
6421
+ let added = 0;
6422
+ let changed = 0;
6423
+ let removed = 0;
6424
+ let signs = [];
6425
+ for (let diff of diffs) {
6426
+ if (diff.changeType == "add" /* Add */) {
6427
+ added += diff.added.count;
6428
+ } else if (diff.changeType == "delete" /* Delete */) {
6429
+ removed += diff.removed.count;
6430
+ } else if (diff.changeType == "changed" /* Change */) {
6431
+ let [add, remove] = [diff.added.count, diff.removed.count];
6432
+ let min = Math.min(add, remove);
6433
+ changed += min;
6434
+ added += add - min;
6435
+ removed += remove - min;
6436
+ }
6437
+ let { start, end } = diff;
6438
+ for (let i = start; i <= end; i++) {
6439
+ let topdelete = diff.changeType == "delete" /* Delete */ && i == 0;
6440
+ let changedelete = diff.changeType == "changed" /* Change */ && diff.removed.count > diff.added.count && i == end;
6441
+ signs.push({
6442
+ changeType: topdelete ? "topdelete" : changedelete ? "changedelete" : diff.changeType,
6443
+ lnum: topdelete ? 1 : i
6444
+ });
6445
+ }
6446
+ if (diff.changeType == "changed" /* Change */) {
6447
+ let [add, remove] = [diff.added.count, diff.removed.count];
6448
+ if (add > remove) {
6449
+ for (let i = 0; i < add - remove; i++) {
6450
+ signs.push({
6451
+ changeType: "add" /* Add */,
6452
+ lnum: diff.end + 1 + i
6453
+ });
6454
+ }
6497
6455
  }
6498
- e(new Error("Cancelled"));
6499
- });
6500
- });
6501
- result = Promise.race([result, cancellationPromise]);
6502
- }
6503
- try {
6504
- const [exitCode, stdout, stderr] = await result;
6505
- return { exitCode, stdout, stderr };
6506
- } finally {
6507
- (0, import_coc11.disposeAll)(disposables);
6508
- }
6509
- }
6510
-
6511
- // src/model/resolver.ts
6512
- var import_coc12 = require("coc.nvim");
6513
- var import_fs3 = __toESM(require("fs"));
6514
- var import_path7 = __toESM(require("path"));
6515
- var import_util9 = require("util");
6516
- async function getRealPath(fullpath) {
6517
- let resolved;
6518
- try {
6519
- resolved = await (0, import_util9.promisify)(import_fs3.default.realpath)(fullpath, "utf8");
6520
- } catch (e) {
6521
- if (e.message.includes("ENOENT")) {
6522
- try {
6523
- resolved = await import_coc12.workspace.nvim.call("expand", [fullpath]);
6524
- } catch (e2) {
6525
6456
  }
6526
6457
  }
6458
+ let items = [];
6459
+ if (added) items.push(`+${added}`);
6460
+ if (changed) items.push(`~${changed}`);
6461
+ if (removed) items.push(`-${removed}`);
6462
+ let status = ` ${items.join(" ")} `;
6463
+ this.setBufferStatus(status);
6464
+ this.currentSigns = signs;
6465
+ if (!this.config.realtimeGutters && !force) return;
6466
+ this.updateGutters();
6527
6467
  }
6528
- return resolved || fullpath;
6529
- }
6530
- var Resolver = class {
6531
- constructor(git, channel) {
6532
- this.git = git;
6533
- this.channel = channel;
6534
- this.resolvedRoots = /* @__PURE__ */ new Map();
6535
- this.relativePaths = /* @__PURE__ */ new Map();
6468
+ updateGutters() {
6469
+ if (this._disposed) return;
6470
+ if (!this.config.enableGutters) return;
6471
+ let { nvim } = import_coc11.workspace;
6472
+ let { bufnr } = this.doc;
6473
+ let { signPriority } = this.config;
6474
+ let signs = this.currentSigns;
6475
+ nvim.pauseNotification();
6476
+ nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
6477
+ for (let sign of signs) {
6478
+ let name = this.getSignName(sign.changeType);
6479
+ nvim.call("sign_place", [0, signGroup, name, bufnr, { lnum: sign.lnum, priority: signPriority }], true);
6480
+ }
6481
+ nvim.resumeNotification(true, true);
6536
6482
  }
6537
- delete(uri) {
6538
- this.resolvedRoots.delete(uri);
6539
- this.relativePaths.delete(uri);
6483
+ async loadBlames() {
6484
+ if (!this.showBlame) return;
6485
+ let result = [];
6486
+ let indexed = await this.repo.isIndexed(this.relpath);
6487
+ if (indexed) result = await this.getBlameInfo();
6488
+ this.blameInfo = result;
6540
6489
  }
6541
- clear() {
6542
- this.resolvedRoots.clear();
6543
- this.relativePaths.clear();
6490
+ async getBlameInfo(range) {
6491
+ let { relpath } = this;
6492
+ let root = this.repo.root;
6493
+ let res = [];
6494
+ const useRealTime = this.config.blameUseRealTime;
6495
+ try {
6496
+ let currentAuthor = await this.repo.getUsername();
6497
+ const args = ["--no-pager", "blame", "-w", "-b", "-p", "--incremental", "--root", "--date", "relative", "--contents", "-", relpath];
6498
+ if (range) args.push("-L", range.join(","));
6499
+ let r = await this.git.exec(root, args, {
6500
+ log: false,
6501
+ input: this.doc.content
6502
+ });
6503
+ if (!r.stdout) return res;
6504
+ let info;
6505
+ for (let line of r.stdout.trim().split(/\r?\n/)) {
6506
+ line = line.trim();
6507
+ if (/^(author |committer )?External file \(--contents\)/.test(line)) {
6508
+ line = "Not committed yet.";
6509
+ }
6510
+ let ms = line.match(/^([A-Za-z0-9]+)\s(\d+)\s(\d+)\s(\d+)/);
6511
+ if (ms) {
6512
+ let startLnum = parseInt(ms[3], 10);
6513
+ info = { startLnum, sha: ms[1], endLnum: startLnum + parseInt(ms[4], 10) - 1, index: ms[2] };
6514
+ if (!/^0+$/.test(ms[1])) {
6515
+ let find = res.find((o) => o.sha == ms[1]);
6516
+ if (find) {
6517
+ info.author = find.author;
6518
+ info.time = find.time;
6519
+ info.summary = find.summary;
6520
+ }
6521
+ }
6522
+ res.push(info);
6523
+ } else if (info) {
6524
+ if (line.startsWith("author ")) {
6525
+ let author = line.replace(/^author/, "").trim();
6526
+ info.author = author == currentAuthor ? "You" : author;
6527
+ } else if (line.startsWith("author-time ")) {
6528
+ let text = line.replace(/^author-time/, "").trim();
6529
+ const timestamps = parseInt(text, 10) * 1e3;
6530
+ info.time = useRealTime ? new Date(timestamps).toLocaleString() : format(timestamps, process.env.LANG);
6531
+ } else if (line.startsWith("summary ")) {
6532
+ let text = line.replace(/^summary/, "").trim();
6533
+ info.summary = text;
6534
+ }
6535
+ }
6536
+ }
6537
+ return res;
6538
+ } catch (e) {
6539
+ this.channel.appendLine(e.stack);
6540
+ }
6541
+ return res;
6544
6542
  }
6545
- getRelativePath(uri) {
6546
- return this.relativePaths.get(uri);
6543
+ async diffCached() {
6544
+ let res = await this.repo.safeRun(["diff", "--cached"]);
6545
+ if (!res.trim()) {
6546
+ import_coc11.window.showWarningMessage("Empty diff");
6547
+ return;
6548
+ }
6549
+ let { nvim } = import_coc11.workspace;
6550
+ nvim.pauseNotification();
6551
+ nvim.command(`keepalt above new +setl\\ previewwindow`, true);
6552
+ nvim.call("append", [0, res.split(/\r?\n/)], true);
6553
+ nvim.command("normal! Gdd", true);
6554
+ nvim.command(`exe 1`, true);
6555
+ nvim.command("setfiletype git", true);
6556
+ nvim.command("setl buftype=nofile nomodifiable bufhidden=wipe nobuflisted", true);
6557
+ await nvim.resumeNotification();
6547
6558
  }
6548
- async resolveGitRoot(doc) {
6549
- if (!doc) return null;
6550
- let root;
6551
- const { uri } = doc;
6552
- root = this.resolvedRoots.get(uri);
6553
- if (root) return root;
6554
- if (doc.buftype == "acwrite") {
6555
- root = await this.resolveRootFromCwd();
6556
- this.resolvedRoots.set(uri, root);
6557
- return root;
6559
+ async nextConflict() {
6560
+ let { nvim } = import_coc11.workspace;
6561
+ if (!this.conflicts.length) {
6562
+ import_coc11.window.showWarningMessage("No conflicts detected");
6563
+ return;
6558
6564
  }
6559
- if (doc.buftype != "" || doc.schema != "file") {
6560
- return null;
6565
+ let line = await nvim.call("line", ".");
6566
+ for (let conflict of this.conflicts) {
6567
+ if (conflict.start > line) {
6568
+ await import_coc11.window.moveTo({ line: Math.max(conflict.start - 1, 0), character: 0 });
6569
+ return;
6570
+ }
6561
6571
  }
6562
- let fullpath = await getRealPath(import_coc12.Uri.parse(uri).fsPath);
6563
- if (process.platform == "win32") {
6564
- fullpath = import_path7.default.win32.normalize(fullpath);
6572
+ if (await nvim.getOption("wrapscan")) {
6573
+ await import_coc11.window.moveTo({ line: Math.max(this.conflicts[0].start - 1, 0), character: 0 });
6565
6574
  }
6566
- if (!root) {
6567
- let parts = fullpath.split(import_path7.default.sep);
6568
- let idx = parts.indexOf(".git");
6569
- if (idx !== -1) {
6570
- root = parts.slice(0, idx).join(import_path7.default.sep);
6571
- this.resolvedRoots.set(uri, root);
6572
- } else {
6573
- try {
6574
- root = await this.git.getRepositoryRoot(import_path7.default.dirname(fullpath));
6575
- if (import_path7.default.isAbsolute(root)) {
6576
- this.resolvedRoots.set(uri, root);
6577
- } else {
6578
- root = void 0;
6579
- }
6580
- } catch (e) {
6575
+ }
6576
+ async prevConflict() {
6577
+ let { nvim } = import_coc11.workspace;
6578
+ if (!this.conflicts.length) {
6579
+ import_coc11.window.showWarningMessage("No conflicts detected", "warning");
6580
+ return;
6581
+ }
6582
+ let line = await nvim.call("line", ".");
6583
+ for (let conflict of this.conflicts) {
6584
+ if (conflict.start > line) {
6585
+ await import_coc11.window.moveTo({ line: Math.max(conflict.start - 1, 0), character: 0 });
6586
+ return;
6587
+ }
6588
+ }
6589
+ if (await nvim.getOption("wrapscan")) {
6590
+ await import_coc11.window.moveTo({ line: Math.max(this.conflicts[0].start - 1, 0), character: 0 });
6591
+ }
6592
+ }
6593
+ async conflictKeepPart(part) {
6594
+ const { nvim } = import_coc11.workspace;
6595
+ let conflicts = this.conflicts;
6596
+ if (!conflicts || conflicts.length == 0) {
6597
+ import_coc11.window.showWarningMessage("No conflicts detected");
6598
+ return;
6599
+ }
6600
+ let line = await nvim.call("line", ".");
6601
+ for (let conflict of conflicts) {
6602
+ if (conflict.start <= line && conflict.end >= line) {
6603
+ switch (part) {
6604
+ case 0 /* Current */:
6605
+ let start = conflict.common ? conflict.common : conflict.sep;
6606
+ await nvim.command(`${start},${conflict.end}d | ${conflict.start}d`);
6607
+ return;
6608
+ case 1 /* Incoming */:
6609
+ await nvim.command(`${conflict.end}d | ${conflict.start},${conflict.sep}d`);
6610
+ return;
6611
+ case 2 /* Both */:
6612
+ if (conflict.common) {
6613
+ await nvim.command(`${conflict.end}d | ${conflict.common},${conflict.sep}d | ${conflict.start}d`);
6614
+ } else {
6615
+ await nvim.command(`${conflict.end}d | ${conflict.sep}d | ${conflict.start}d`);
6616
+ }
6617
+ return;
6581
6618
  }
6582
6619
  }
6583
6620
  }
6584
- if (root) {
6585
- this.relativePaths.set(uri, import_path7.default.relative(root, fullpath));
6621
+ import_coc11.window.showWarningMessage("Not positioned on a conflict");
6622
+ }
6623
+ async browser(action = "open", range, permalink = false) {
6624
+ let { nvim } = import_coc11.workspace;
6625
+ let config = import_coc11.workspace.getConfiguration("git");
6626
+ let head = (await this.repo.safeRun(["rev-parse", "HEAD"])).trim();
6627
+ let branch = config.get("browserBranchName", "").trim();
6628
+ if (!branch.length) {
6629
+ branch = (await this.repo.safeRun(["symbolic-ref", "--short", "-q", "HEAD"])).trim();
6630
+ }
6631
+ let lines = range && range.length == 2 ? [
6632
+ range[0],
6633
+ range[1]
6634
+ ] : [await nvim.eval('line(".")')];
6635
+ if (this.doc.filetype == "markdown") {
6636
+ let line = await nvim.call("getline", ["."]);
6637
+ if (line.startsWith("#")) {
6638
+ let words = line.replace(/^#+\s*/, "").split(/\s+/);
6639
+ lines = words.map((s) => s.toLowerCase()).join("-");
6640
+ }
6641
+ }
6642
+ let output = await this.repo.safeRun(["remote"]);
6643
+ if (!output.trim()) {
6644
+ import_coc11.window.showWarningMessage(`No remote found`);
6645
+ return;
6646
+ }
6647
+ let names = output.trim().split(/\r?\n/);
6648
+ let browserRemoteName = config.get("browserRemoteName", "").trim();
6649
+ if (browserRemoteName.length > 0) {
6650
+ if (names.includes(browserRemoteName)) {
6651
+ names = [browserRemoteName];
6652
+ } else {
6653
+ import_coc11.window.showWarningMessage("Configured git.browserRemoteName missing from remote list");
6654
+ return;
6655
+ }
6656
+ }
6657
+ let urls = [];
6658
+ for (let name of names) {
6659
+ let uri = await this.repo.safeRun(["config", "--get", `remote.${name}.url`]);
6660
+ if (!uri.length) continue;
6661
+ let repoURL = getRepoUrl(uri);
6662
+ if (!repoURL) continue;
6663
+ let tmp = new import_url.URL(repoURL);
6664
+ let hostname = tmp.hostname;
6665
+ let fix = "|";
6666
+ try {
6667
+ fix = config.get("urlFix")[hostname][permalink ? 1 : 0];
6668
+ } catch (e) {
6669
+ }
6670
+ let url = getUrl(fix, repoURL, permalink ? head : branch, this.relpath.replace(/\\\\/g, "/"), lines);
6671
+ if (url) urls.push(url);
6672
+ }
6673
+ if (urls.length == 1) {
6674
+ if (action == "open") {
6675
+ await import_coc11.workspace.openResource(urls[0]);
6676
+ } else {
6677
+ nvim.command(`let @+ = '${urls[0]}'`, true);
6678
+ import_coc11.window.showInformationMessage("Copied url to clipboard");
6679
+ }
6680
+ } else if (urls.length > 1) {
6681
+ let url = await import_coc11.window.showQuickPick(urls, { canPickMany: false, title: "Pick remote url" });
6682
+ if (url) {
6683
+ if (action == "open") {
6684
+ await import_coc11.workspace.openResource(url);
6685
+ } else {
6686
+ nvim.command(`let @+ = '${url}'`, true);
6687
+ nvim.command(`let @* = '${url}'`, true);
6688
+ import_coc11.window.showInformationMessage("Copied url to clipboard");
6689
+ }
6690
+ }
6586
6691
  }
6587
- this.channel.appendLine(`resolved root of ${fullpath}: ${root}`);
6588
- return root;
6589
6692
  }
6590
- async resolveRootFromCwd() {
6591
- let parts = import_coc12.workspace.cwd.split(import_path7.default.sep);
6592
- let idx = parts.indexOf(".git");
6593
- if (idx !== -1) return parts.slice(0, idx).join(import_path7.default.sep);
6594
- try {
6595
- return await this.git.getRepositoryRoot(import_coc12.workspace.cwd);
6596
- } catch (e) {
6693
+ // show commit of current line in split window
6694
+ async showCommit(useFloating = false) {
6695
+ let indexed = await this.repo.isIndexed(this.relpath);
6696
+ if (!indexed) {
6697
+ import_coc11.window.showWarningMessage(`"${this.relpath}" not indexed.`);
6698
+ return;
6699
+ }
6700
+ let nvim = import_coc11.workspace.nvim;
6701
+ let line = await nvim.eval('line(".")');
6702
+ let args = ["--no-pager", "blame", "-w", "-l", "--root", "-t", `-L${line},${line}`, this.relpath];
6703
+ let res = await this.repo.exec(args);
6704
+ let output = res.stdout.trim();
6705
+ if (!output.length) return;
6706
+ let commit = output.match(/^\S+/)[0];
6707
+ if (/^0+$/.test(commit)) {
6708
+ import_coc11.window.showWarningMessage("not committed yet!");
6709
+ return;
6710
+ }
6711
+ if (!useFloating) useFloating = this.config.showCommitInFloating;
6712
+ if (useFloating) {
6713
+ let content = await this.repo.safeRun(["--no-pager", "show", commit]);
6714
+ if (content == null) return;
6715
+ await this.showDoc(content.trim());
6716
+ return;
6717
+ }
6718
+ await nvim.command(`keepalt above ${this.config.splitWindowCommand}`);
6719
+ let hasFugitive = await nvim.getVar("loaded_fugitive");
6720
+ if (hasFugitive) {
6721
+ await nvim.command(`Gedit ${commit}`);
6722
+ } else {
6723
+ let content = await this.repo.safeRun(["--no-pager", "show", commit]);
6724
+ if (content == null) return;
6725
+ let lines = content.trim().split("\n");
6726
+ nvim.pauseNotification();
6727
+ nvim.command(`edit +setl\\ buftype=nofile [commit ${commit}]`, true);
6728
+ nvim.command("setl foldmethod=syntax nobuflisted bufhidden=wipe", true);
6729
+ nvim.command("setf git", true);
6730
+ nvim.call("append", [0, lines], true);
6731
+ nvim.command("normal! Gdd", true);
6732
+ nvim.command(`exe 1`, true);
6733
+ await nvim.resumeNotification(false, true);
6597
6734
  }
6598
- return null;
6599
6735
  }
6600
- dispose() {
6601
- this.resolvedRoots.clear();
6602
- this.relativePaths.clear();
6736
+ async toggleFold() {
6737
+ let { nvim } = import_coc11.workspace;
6738
+ let buf = this.doc.buffer;
6739
+ let win = await nvim.window;
6740
+ let bufnr = buf.id;
6741
+ let doc = import_coc11.workspace.getDocument(bufnr);
6742
+ if (!doc) return;
6743
+ let infos = this.currentSigns;
6744
+ if (!infos || infos.length == 0) {
6745
+ import_coc11.window.showWarningMessage("No changes");
6746
+ return;
6747
+ }
6748
+ let lnums = infos.map((o) => o.lnum);
6749
+ let foldContext = this.config.foldContext;
6750
+ let max = this.doc.lineCount;
6751
+ let ranges = [];
6752
+ let start = null;
6753
+ const addRange = (from, to) => {
6754
+ let s = plus(from, foldContext, max);
6755
+ let e = minus(to, foldContext, 0);
6756
+ if (e - s <= 0) return;
6757
+ ranges.push([s, e]);
6758
+ };
6759
+ for (let i = 1; i <= doc.lineCount; i++) {
6760
+ let fold = lnums.indexOf(i) == -1;
6761
+ if (fold && start == null) {
6762
+ start = i;
6763
+ continue;
6764
+ }
6765
+ if (start != null && !fold) {
6766
+ addRange(start, i - 1);
6767
+ start = null;
6768
+ }
6769
+ if (start != null && fold && i == doc.lineCount) {
6770
+ addRange(start, i);
6771
+ }
6772
+ }
6773
+ let enabled = this.foldEnabled;
6774
+ if (enabled) {
6775
+ this.foldEnabled = false;
6776
+ let cursor = await nvim.eval('getpos(".")');
6777
+ let lnums2 = ranges.map((o) => o[0]);
6778
+ let settings = this.foldSettings;
6779
+ nvim.pauseNotification();
6780
+ for (let lnum of lnums2) {
6781
+ nvim.command(`${lnum}normal! zd`, true);
6782
+ }
6783
+ win.setOption("foldmethod", settings.foldmethod, true);
6784
+ win.setOption("foldenable", settings.foldenable, true);
6785
+ win.setOption("foldlevel", settings.foldlevel, true);
6786
+ nvim.call("setpos", [".", cursor], true);
6787
+ await nvim.resumeNotification();
6788
+ } else {
6789
+ this.foldEnabled = true;
6790
+ let [foldmethod, foldenable, foldlevel] = await nvim.eval("[&foldmethod,&foldenable,&foldlevel]");
6791
+ this.foldSettings = {
6792
+ foldmethod,
6793
+ foldenable: foldenable !== 0,
6794
+ foldlevel
6795
+ };
6796
+ nvim.pauseNotification();
6797
+ win.setOption("foldmethod", "manual", true);
6798
+ nvim.command("silent! normal! zE", true);
6799
+ win.setOption("foldenable", true, true);
6800
+ win.setOption("foldlevel", 0, true);
6801
+ await nvim.resumeNotification();
6802
+ nvim.pauseNotification();
6803
+ for (let r of ranges) {
6804
+ nvim.command(`${r[0]},${r[1]}fold`, true);
6805
+ }
6806
+ await nvim.resumeNotification();
6807
+ }
6603
6808
  }
6604
- };
6605
-
6606
- // src/model/buffer.ts
6607
- var import_coc13 = require("coc.nvim");
6608
- var import_debounce2 = __toESM(require_debounce());
6609
-
6610
- // node_modules/timeago.js/esm/lang/en_US.js
6611
- var EN_US = ["second", "minute", "hour", "day", "week", "month", "year"];
6612
- function en_US_default(diff, idx) {
6613
- if (idx === 0)
6614
- return ["just now", "right now"];
6615
- var unit = EN_US[Math.floor(idx / 2)];
6616
- if (diff > 1)
6617
- unit += "s";
6618
- return [diff + " " + unit + " ago", "in " + diff + " " + unit];
6619
- }
6620
-
6621
- // node_modules/timeago.js/esm/lang/zh_CN.js
6622
- var ZH_CN = ["\u79D2", "\u5206\u949F", "\u5C0F\u65F6", "\u5929", "\u5468", "\u4E2A\u6708", "\u5E74"];
6623
- function zh_CN_default(diff, idx) {
6624
- if (idx === 0)
6625
- return ["\u521A\u521A", "\u7247\u523B\u540E"];
6626
- var unit = ZH_CN[~~(idx / 2)];
6627
- return [diff + " " + unit + "\u524D", diff + " " + unit + "\u540E"];
6628
- }
6629
-
6630
- // node_modules/timeago.js/esm/register.js
6631
- var Locales = {};
6632
- var register = function(locale, func) {
6633
- Locales[locale] = func;
6634
- };
6635
- var getLocale = function(locale) {
6636
- return Locales[locale] || Locales["en_US"];
6637
- };
6638
-
6639
- // node_modules/timeago.js/esm/utils/date.js
6640
- var SEC_ARRAY = [
6641
- 60,
6642
- 60,
6643
- 24,
6644
- 7,
6645
- 365 / 7 / 12,
6646
- 12
6647
- ];
6648
- function toDate(input) {
6649
- if (input instanceof Date)
6650
- return input;
6651
- if (!isNaN(input) || /^\d+$/.test(input))
6652
- return new Date(parseInt(input));
6653
- input = (input || "").trim().replace(/\.\d+/, "").replace(/-/, "/").replace(/-/, "/").replace(/(\d)T(\d)/, "$1 $2").replace(/Z/, " UTC").replace(/([+-]\d\d):?(\d\d)/, " $1$2");
6654
- return new Date(input);
6655
- }
6656
- function formatDiff(diff, localeFunc) {
6657
- var agoIn = diff < 0 ? 1 : 0;
6658
- diff = Math.abs(diff);
6659
- var totalSec = diff;
6660
- var idx = 0;
6661
- for (; diff >= SEC_ARRAY[idx] && idx < SEC_ARRAY.length; idx++) {
6662
- diff /= SEC_ARRAY[idx];
6809
+ async toggleGutters(enabled) {
6810
+ this.config.enableGutters = enabled;
6811
+ let { nvim } = import_coc11.workspace;
6812
+ if (!enabled) {
6813
+ nvim.call("sign_unplace", [signGroup, { buffer: this.doc.bufnr }], true);
6814
+ nvim.redrawVim();
6815
+ } else {
6816
+ this.diffs = [];
6817
+ await this.diffDocument(true);
6818
+ }
6663
6819
  }
6664
- diff = Math.floor(diff);
6665
- idx *= 2;
6666
- if (diff > (idx === 0 ? 9 : 1))
6667
- idx += 1;
6668
- return localeFunc(diff, idx, totalSec)[agoIn].replace("%s", diff.toString());
6669
- }
6670
- function diffSec(date, relativeDate) {
6671
- var relDate = relativeDate ? toDate(relativeDate) : /* @__PURE__ */ new Date();
6672
- return (+relDate - +toDate(date)) / 1e3;
6673
- }
6674
-
6675
- // node_modules/timeago.js/esm/format.js
6676
- var format = function(date, locale, opts) {
6677
- var sec = diffSec(date, opts && opts.relativeDate);
6678
- return formatDiff(sec, getLocale(locale));
6679
- };
6680
-
6681
- // node_modules/timeago.js/esm/index.js
6682
- register("en_US", en_US_default);
6683
- register("zh_CN", zh_CN_default);
6684
-
6685
- // src/model/buffer.ts
6686
- var import_url = require("url");
6687
- var signGroup = "CocGit";
6688
- var revPattern = "([0-9A-Za-z_.:/-]+)";
6689
- var startPattern = new RegExp(`^<{7} (${revPattern})(:? .+)?$`);
6690
- var sepPattern = new RegExp(`^={7}$`);
6691
- var endPattern = new RegExp(`^>{7} (${revPattern})(:? .+)?$`);
6692
- var commonPattern = /^\|{7}\smerged\scommon\sancestors/;
6693
- var GitBuffer = class {
6694
- constructor(doc, config, relpath, repo, git, channel, floatFactory) {
6695
- this.doc = doc;
6696
- this.config = config;
6697
- this.relpath = relpath;
6698
- this.repo = repo;
6699
- this.git = git;
6700
- this.channel = channel;
6701
- this.floatFactory = floatFactory;
6702
- this.blameInfo = [];
6703
- this.diffs = [];
6704
- this.conflicts = [];
6705
- this.currentSigns = [];
6706
- this.gitStatus = "";
6707
- this.hasConflicts = false;
6708
- this.foldEnabled = false;
6709
- this._disposed = false;
6710
- this.mutex = new import_coc13.Mutex();
6711
- this.refresh = (0, import_debounce2.default)(() => {
6712
- this._refresh().catch((e) => {
6713
- channel.append(`[Error] ${e.message}`);
6714
- });
6715
- }, 200);
6716
- this.repo.hasConflicts(relpath).then((hasConflicts) => {
6717
- this.hasConflicts = hasConflicts;
6718
- this.refresh();
6820
+ async parseConflicts() {
6821
+ if (!this.hasConflicts || !this.config.conflict.enabled) return;
6822
+ const lines = this.doc.textDocument.lines;
6823
+ let conflicts = [];
6824
+ let conflict = null;
6825
+ let state = 0 /* Initial */;
6826
+ let mkStartConflict = (index, current) => ({
6827
+ start: index + 1,
6828
+ sep: 0,
6829
+ end: 0,
6830
+ current,
6831
+ incoming: ""
6719
6832
  });
6720
- this.doc.buffer.getOption("fileencoding").then((encoding) => {
6721
- this.encoding = encoding;
6722
- }).catch((_e) => {
6833
+ lines.forEach((line, index) => {
6834
+ switch (state) {
6835
+ case 0 /* Initial */: {
6836
+ const match = line.match(startPattern);
6837
+ if (match) {
6838
+ conflict = mkStartConflict(index, match[1]);
6839
+ state = 1 /* MatchedStart */;
6840
+ }
6841
+ break;
6842
+ }
6843
+ case 2 /* MatchedCommon */:
6844
+ case 1 /* MatchedStart */: {
6845
+ let match = line.match(sepPattern);
6846
+ if (match) {
6847
+ conflict.sep = index + 1;
6848
+ state = 3 /* MatchedSep */;
6849
+ } else {
6850
+ const startMatch = line.match(startPattern);
6851
+ if (startMatch) {
6852
+ conflict = mkStartConflict(index, startMatch[1]);
6853
+ state = 1 /* MatchedStart */;
6854
+ } else if (line.match(endPattern)) {
6855
+ conflict = null;
6856
+ state = 0 /* Initial */;
6857
+ } else if (line.match(commonPattern)) {
6858
+ conflict.common = index + 1;
6859
+ state = 2 /* MatchedCommon */;
6860
+ }
6861
+ }
6862
+ break;
6863
+ }
6864
+ case 3 /* MatchedSep */: {
6865
+ const match = line.match(endPattern);
6866
+ if (match) {
6867
+ conflict.end = index + 1;
6868
+ conflict.incoming = match[1];
6869
+ conflicts.push(conflict);
6870
+ conflict = null;
6871
+ state = 0 /* Initial */;
6872
+ } else {
6873
+ const startMatch = line.match(startPattern);
6874
+ if (startMatch) {
6875
+ conflict = mkStartConflict(index, startMatch[1]);
6876
+ state = 1 /* MatchedStart */;
6877
+ } else if (line.match(sepPattern)) {
6878
+ conflict = null;
6879
+ state = 0 /* Initial */;
6880
+ }
6881
+ }
6882
+ break;
6883
+ }
6884
+ }
6723
6885
  });
6724
- }
6725
- get cachedDiffs() {
6726
- return this.diffs;
6727
- }
6728
- async _refresh() {
6729
- if (this._disposed) return;
6730
- let release = await this.mutex.acquire();
6731
- try {
6732
- await Promise.all([
6733
- this.diffDocument(),
6734
- this.loadBlames(),
6735
- this.parseConflicts()
6736
- ]);
6737
- } catch (e) {
6738
- this.channel.append(`[Error] refresh error ${e.message}`);
6886
+ this.conflicts = conflicts;
6887
+ this.highlightConflicts(conflicts);
6888
+ if (conflicts.length == 0) {
6889
+ this.hasConflicts = false;
6739
6890
  }
6740
- release();
6741
6891
  }
6742
- getChunk(line) {
6743
- if (!this.diffs || this.diffs.length == 0) return void 0;
6744
- return this.diffs.find((ch) => {
6745
- let { start, added, removed, changeType } = ch;
6746
- if (line == 1 && start == 0 && ch.end == 0) {
6747
- return true;
6748
- }
6749
- let end = changeType === "changed" /* Change */ && added.count > removed.count ? ch.end + added.count - removed.count : ch.end;
6750
- if (start <= line && end >= line) {
6751
- return true;
6892
+ async highlightConflicts(conflicts) {
6893
+ let buffer = this.doc.buffer;
6894
+ let currentHlGroup = this.config.conflict.currentHlGroup;
6895
+ let incomingHlGroup = this.config.conflict.incomingHlGroup;
6896
+ let commonHlGroup = this.config.conflict.commonHlGroup;
6897
+ let { nvim } = import_coc11.workspace;
6898
+ nvim.pauseNotification();
6899
+ buffer.clearNamespace(this.config.conflictSrcId, 0, -1);
6900
+ const srcId = this.config.conflictSrcId;
6901
+ conflicts.map((conflict) => {
6902
+ let currEnd = conflict.common ? conflict.common - 1 : conflict.sep - 1;
6903
+ let currRange = import_coc11.Range.create(import_coc11.Position.create(conflict.start - 1, 0), import_coc11.Position.create(currEnd, 0));
6904
+ let incomingRange = import_coc11.Range.create(import_coc11.Position.create(conflict.sep, 0), import_coc11.Position.create(conflict.end, 0));
6905
+ buffer.highlightRanges(srcId, currentHlGroup, [currRange]);
6906
+ buffer.highlightRanges(srcId, incomingHlGroup, [incomingRange]);
6907
+ if (conflict.common) {
6908
+ let range = import_coc11.Range.create(import_coc11.Position.create(conflict.common - 1, 0), import_coc11.Position.create(conflict.sep - 1, 0));
6909
+ buffer.highlightRanges(srcId, commonHlGroup, [range]);
6752
6910
  }
6753
- return false;
6754
6911
  });
6912
+ nvim.resumeNotification(false, true);
6755
6913
  }
6756
- async chunkUndo() {
6757
- let line = await import_coc13.workspace.nvim.call("line", ".");
6758
- let diff = this.getChunk(line);
6759
- if (!diff) return;
6760
- let { start, lines, changeType } = diff;
6761
- let added = lines.filter((s) => s.startsWith("-")).map((s) => s.slice(1));
6762
- let removeCount = lines.filter((s) => s.startsWith("+")).length;
6763
- let buf = this.doc.buffer;
6764
- if (changeType == "delete" /* Delete */) {
6765
- await buf.setLines(added, { start, end: start, strictIndexing: false });
6914
+ async showDoc(content, filetype = "diff") {
6915
+ if (this.floatFactory) {
6916
+ let docs = [{ content, filetype }];
6917
+ await this.floatFactory.show(docs, this.config.floatConfig);
6766
6918
  } else {
6767
- await buf.setLines(added, {
6768
- start: start - 1,
6769
- end: start - 1 + removeCount,
6770
- strictIndexing: false
6771
- });
6919
+ const lines = content.split("\n");
6920
+ import_coc11.workspace.nvim.call("coc#ui#preview_info", [lines, "diff"], true);
6772
6921
  }
6773
6922
  }
6774
- async chunkStage() {
6775
- let relpath = toUnixSlash(this.relpath);
6776
- let line = await import_coc13.workspace.nvim.call("line", ".");
6777
- let diff = this.getChunk(line);
6778
- if (!diff) {
6779
- import_coc13.window.showErrorMessage("Not positioned in git chunk");
6780
- return;
6923
+ setBufferStatus(status) {
6924
+ let exists = this.gitStatus;
6925
+ if (exists == status) return;
6926
+ this.gitStatus = status;
6927
+ let { nvim } = import_coc11.workspace;
6928
+ let buffer = nvim.createBuffer(this.doc.bufnr);
6929
+ nvim.pauseNotification();
6930
+ buffer.setVar("coc_git_status", status, true);
6931
+ nvim.callTimer("coc#util#do_autocmd", ["CocGitStatusChange"], true);
6932
+ nvim.resumeNotification(false, true);
6933
+ }
6934
+ getSignName(changeType) {
6935
+ switch (changeType) {
6936
+ case "delete" /* Delete */:
6937
+ return "CocGitRemoved";
6938
+ case "add" /* Add */:
6939
+ return "CocGitAdded";
6940
+ case "changed" /* Change */:
6941
+ return "CocGitChanged";
6942
+ case "topdelete":
6943
+ return "CocGitTopRemoved";
6944
+ case "changedelete":
6945
+ return "CocGitChangeRemoved";
6781
6946
  }
6782
- let head;
6783
- if (diff.changeType === "add" /* Add */) {
6784
- head = `@@ -${diff.removed.start + 1},0 +${diff.removed.start + 1},${diff.added.count} @@`;
6785
- } else if (diff.changeType === "delete" /* Delete */) {
6786
- head = `@@ -${diff.removed.start},${diff.removed.count} +${diff.removed.start},0 @@`;
6787
- } else if (diff.changeType === "changed" /* Change */) {
6788
- head = `@@ -${diff.removed.start},${diff.removed.count} +${diff.removed.start},${diff.added.count} @@`;
6947
+ return "";
6948
+ }
6949
+ get showBlame() {
6950
+ return this.config.addGBlameToVirtualText || this.config.addGBlameToBufferVar;
6951
+ }
6952
+ dispose() {
6953
+ let { nvim } = import_coc11.workspace;
6954
+ let { bufnr } = this.doc;
6955
+ let buffer = nvim.createBuffer(bufnr);
6956
+ buffer.setVar("coc_git_status", "", true);
6957
+ buffer.clearNamespace(this.config.conflictSrcId, 0, -1);
6958
+ nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
6959
+ this.refresh.clear();
6960
+ if (this.config.addGBlameToBufferVar) {
6961
+ buffer.setVar("coc_git_blame", "", true);
6789
6962
  }
6790
- const lines = [
6791
- `diff --git a/${relpath} b/${relpath}`,
6792
- `index 000000..000000 100644`,
6793
- `--- a/${relpath}`,
6794
- `+++ b/${relpath}`,
6795
- head
6796
- ];
6797
- lines.push(...diff.lines);
6798
- lines.push("");
6799
- try {
6800
- await this.git.exec(this.repo.root, ["apply", "--cached", "--unidiff-zero", "-"], { input: lines.join("\n") });
6801
- this.refresh();
6802
- } catch (e) {
6803
- this.channel.appendLine(`[Error] ${e.message}`);
6963
+ if (this.config.addGBlameToVirtualText) {
6964
+ buffer.clearNamespace(this.config.virtualTextSrcId);
6804
6965
  }
6966
+ nvim.resumeNotification(false, true);
6967
+ this._disposed = true;
6968
+ this.foldEnabled = false;
6969
+ this.blameInfo = void 0;
6970
+ this.diffs = void 0;
6971
+ this.conflicts = void 0;
6972
+ this.currentSigns = void 0;
6805
6973
  }
6806
- async chunkUnstage() {
6807
- let { nvim } = import_coc13.workspace;
6808
- const { diffs } = this;
6809
- let line = await nvim.call("line", ".");
6810
- let adjust = 0;
6811
- let invalid = false;
6812
- for (let diff of diffs) {
6813
- if (diff.end >= line) {
6814
- if (diff.start <= line && diff.changeType != "delete" /* Delete */) {
6815
- import_coc13.window.showErrorMessage(`Current line contains unstaged change.`);
6816
- invalid = true;
6817
- }
6818
- break;
6819
- }
6820
- adjust -= diff.added.count;
6821
- adjust += diff.removed.count;
6974
+ };
6975
+ function plus(val, count, max) {
6976
+ if (!count) return val;
6977
+ val = val + count;
6978
+ return Math.min(max, val);
6979
+ }
6980
+ function minus(val, count, min) {
6981
+ if (!count) return val;
6982
+ val = val - count;
6983
+ return Math.max(min, val);
6984
+ }
6985
+
6986
+ // src/model/git.ts
6987
+ var cp = __toESM(require("child_process"));
6988
+ var import_coc12 = require("coc.nvim");
6989
+ var import_iconv_lite = __toESM(require_lib());
6990
+ var import_path5 = __toESM(require("path"));
6991
+ var Git = class {
6992
+ constructor(gitInfo, channel) {
6993
+ this.gitInfo = gitInfo;
6994
+ this.channel = channel;
6995
+ }
6996
+ async getRepositoryRoot(repositoryPath) {
6997
+ const result = await this.exec(repositoryPath, ["rev-parse", "--show-toplevel"]);
6998
+ let repoRootPath = import_path5.default.normalize(result.stdout.trim());
6999
+ if (process.platform === "win32" && repoRootPath.startsWith("\\") && !process.env.SHELL) {
7000
+ repoRootPath = repoRootPath.replace(/^\\([^\\]*)\\/, "$1:\\");
6822
7001
  }
6823
- if (invalid) return;
6824
- line = line + adjust;
6825
- let stagedDiff = await this.repo.getStagedChunks(this.relpath);
6826
- let chunks = Object.values(stagedDiff)[0];
6827
- if (!chunks.length) {
6828
- import_coc13.window.showErrorMessage(`Staged chunk not found`);
6829
- return;
7002
+ return repoRootPath;
7003
+ }
7004
+ async exec(cwd, args, options = {}) {
7005
+ options = Object.assign({ cwd }, options || {});
7006
+ return await this._exec(args, options);
7007
+ }
7008
+ stream(cwd, args, options = {}) {
7009
+ options = Object.assign({ cwd }, options || {});
7010
+ return this.spawn(args, options);
7011
+ }
7012
+ async _exec(args, options = {}) {
7013
+ const child = this.spawn(args, options);
7014
+ if (options.input) {
7015
+ child.stdin.end(options.input, "utf8");
6830
7016
  }
6831
- let chunk = chunks.find((o) => o.add.lnum <= line && o.add.lnum + o.add.count >= line);
6832
- if (!chunk) {
6833
- import_coc13.window.showErrorMessage(`Unable to find staged chunk on current line`);
6834
- return;
7017
+ const bufferResult = await exec2(child, options.cancellationToken);
7018
+ if (options.log !== false && bufferResult.stderr.length > 0) {
7019
+ this.log(`${bufferResult.stderr}
7020
+ `);
6835
7021
  }
6836
- this.channel.appendLine(`[Info] resolved chunk ${JSON.stringify(chunk, null, 2)}`);
6837
- let patch = createUnstagePatch(this.relpath, chunk);
6838
- if (!patch) return;
6839
- try {
6840
- await this.git.exec(this.repo.root, ["apply", "--cached", "--unidiff-zero", "-"], { input: patch });
6841
- this.refresh();
6842
- } catch (e) {
6843
- import_coc13.window.showErrorMessage(`Unable to apply patch: ${e.message}`);
6844
- this.channel.appendLine(`[Error] ${e.message}`);
7022
+ let encoding = options.encoding || "utf8";
7023
+ encoding = import_iconv_lite.default.encodingExists(encoding) ? encoding : "utf8";
7024
+ const result = {
7025
+ exitCode: bufferResult.exitCode,
7026
+ stdout: import_iconv_lite.default.decode(bufferResult.stdout, encoding),
7027
+ stderr: bufferResult.stderr
7028
+ };
7029
+ if (bufferResult.exitCode) {
7030
+ this.channel.appendLine(`Error ${result.exitCode} on: 'git ${args.join(" ")}' in ${options.cwd}`);
7031
+ this.channel.append(result.stderr);
7032
+ this.channel.append(result.stdout);
7033
+ return Promise.reject(new Error("Failed to execute git"));
6845
7034
  }
7035
+ return result;
6846
7036
  }
6847
- async nextChunk() {
6848
- const { diffs } = this;
6849
- let { nvim } = import_coc13.workspace;
6850
- if (!diffs || diffs.length == 0) return;
6851
- let line = await nvim.call("line", ".");
6852
- for (let diff of diffs) {
6853
- if (diff.start > line) {
6854
- await import_coc13.window.moveTo({ line: Math.max(diff.start - 1, 0), character: 0 });
6855
- return;
6856
- }
7037
+ spawn(args, options = {}) {
7038
+ if (!options) {
7039
+ options = {};
6857
7040
  }
6858
- if (await nvim.getOption("wrapscan")) {
6859
- await import_coc13.window.moveTo({ line: Math.max(diffs[0].start - 1, 0), character: 0 });
7041
+ if (!options.stdio && !options.input) {
7042
+ options.stdio = ["ignore", null, null];
6860
7043
  }
6861
- }
6862
- async prevChunk() {
6863
- const { nvim } = import_coc13.workspace;
6864
- let { diffs } = this;
6865
- let line = await nvim.call("line", ".");
6866
- if (!diffs || diffs.length == 0) return;
6867
- for (let diff of diffs.slice().reverse()) {
6868
- if (diff.end < line) {
6869
- await import_coc13.window.moveTo({ line: Math.max(diff.start - 1, 0), character: 0 });
6870
- return;
6871
- }
7044
+ options.env = Object.assign({}, process.env, options.env || {}, {
7045
+ LC_ALL: "en_US.UTF-8",
7046
+ LANG: "en_US.UTF-8"
7047
+ });
7048
+ if (process.platform === "win32") {
7049
+ options.shell = true;
6872
7050
  }
6873
- if (await nvim.getOption("wrapscan")) {
6874
- await import_coc13.window.moveTo({ line: Math.max(diffs[diffs.length - 1].start - 1, 0), character: 0 });
7051
+ if (options.log !== false) {
7052
+ this.log(`> git ${args.join(" ")}
7053
+ `);
6875
7054
  }
7055
+ return cp.spawn(this.gitInfo.path, args, options);
6876
7056
  }
6877
- async chunkInfo() {
6878
- let line = await import_coc13.workspace.nvim.call("line", ".");
6879
- let diff = this.getChunk(line);
6880
- if (diff) {
6881
- let content = diff.head + "\n" + diff.lines.join("\n");
6882
- await this.showDoc(content, "diff");
6883
- } else {
6884
- let chunks = [];
6885
- try {
6886
- let stagedDiff = await this.repo.getStagedChunks(this.relpath);
6887
- chunks = Object.values(stagedDiff)[0];
6888
- } catch (e) {
6889
- }
6890
- let adjust = 0;
6891
- for (let diff2 of this.diffs) {
6892
- if (diff2.end >= line) {
6893
- break;
6894
- }
6895
- adjust -= diff2.added.count;
6896
- adjust += diff2.removed.count;
6897
- }
6898
- line = line + adjust;
6899
- let chunk = chunks.find((o) => o.add.lnum <= line && o.add.lnum + o.add.count >= line);
6900
- if (chunk) {
6901
- let content = "Staged changes\n" + chunk.lines.join("\n");
6902
- await this.showDoc(content, "diff");
6903
- } else {
6904
- await this.showCommit(true);
6905
- }
6906
- }
7057
+ log(output) {
7058
+ this.channel.append(output);
6907
7059
  }
6908
- async showBlameInfo(lnum) {
6909
- let { nvim } = import_coc13.workspace;
6910
- let { virtualTextSrcId, addGBlameToBufferVar, addGBlameToVirtualText } = this.config;
6911
- if (!this.showBlame) return;
6912
- let infos = this.blameInfo;
6913
- if (!infos) return;
6914
- let blameText;
6915
- if (infos.length == 0) {
6916
- blameText = "File not indexed";
6917
- } else {
6918
- let info = infos.find((o) => lnum >= o.startLnum && lnum <= o.endLnum);
6919
- if (info && info.author && info.author != "Not Committed Yet") {
6920
- blameText = `(${info.author} ${info.time}) ${info.summary}`;
6921
- } else {
6922
- blameText = "Not committed yet";
6923
- }
6924
- }
6925
- let buffer = nvim.createBuffer(this.doc.bufnr);
6926
- let hide_blame = await nvim.getVar("coc_git_hide_blame_virtual_text");
6927
- if (hide_blame) {
6928
- buffer.clearNamespace(virtualTextSrcId);
6929
- } else {
6930
- if (addGBlameToBufferVar) {
6931
- nvim.pauseNotification();
6932
- buffer.setVar("coc_git_blame", blameText, true);
6933
- nvim.call("coc#util#do_autocmd", ["CocGitStatusChange"], true);
6934
- nvim.resumeNotification(false, true);
6935
- }
6936
- if (addGBlameToVirtualText) {
6937
- const prefix = this.config.virtualTextPrefix;
6938
- nvim.pauseNotification();
6939
- buffer.clearNamespace(virtualTextSrcId);
6940
- buffer.setVirtualText(virtualTextSrcId, lnum - 1, [[prefix + blameText, "CocCodeLens"]]);
6941
- nvim.resumeNotification(true, true);
6942
- }
6943
- }
7060
+ };
7061
+ async function exec2(child, cancellationToken) {
7062
+ if (!child.stdout || !child.stderr) {
7063
+ throw new Error("Failed to get stdout or stderr from git process.");
6944
7064
  }
6945
- async showBlameDoc(lnum) {
6946
- let indexed = await this.repo.isIndexed(this.relpath);
6947
- if (!indexed) {
6948
- import_coc13.window.showWarningMessage("File not indexed");
6949
- } else if (await this.repo.isShallow()) {
6950
- import_coc13.window.showWarningMessage("Shallow repository, blame not available");
6951
- } else {
6952
- let infos = await this.getBlameInfo([lnum, lnum]);
6953
- let info = infos.find((o) => lnum >= o.startLnum && lnum <= o.endLnum);
6954
- if (info && info.author && info.author != "Not Committed Yet") {
6955
- let blameText = [];
6956
- blameText.push(`${info.author}, ${info.time}`);
6957
- blameText.push(`${info.summary}`);
6958
- blameText.push(`${info.sha.substring(0, 7)}`);
6959
- await this.showDoc(blameText.join("\n\n"), "text");
6960
- } else {
6961
- import_coc13.window.showWarningMessage("Not committed yet");
6962
- }
6963
- }
7065
+ if (cancellationToken && cancellationToken.isCancellationRequested) {
7066
+ throw new Error("Cancelled");
6964
7067
  }
6965
- async diffDocument(force = false) {
6966
- var _a;
6967
- let { nvim } = import_coc13.workspace;
6968
- let revision = this.config.diffRevision;
6969
- const { bufnr } = this.doc;
6970
- let content = this.doc.content;
6971
- let eol = this.doc.textDocument["eol"];
6972
- const diffs = await this.repo.getDiff(this.relpath, eol ? content : content + "\n", revision, this.encoding || "utf8");
6973
- if (diffs == null) {
6974
- if (((_a = this.currentSigns) == null ? void 0 : _a.length) > 0) {
6975
- this.currentSigns = [];
6976
- nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
6977
- nvim.redrawVim();
6978
- }
6979
- return;
6980
- }
6981
- if (equals(diffs, this.diffs)) {
6982
- return;
6983
- }
6984
- this.diffs = diffs;
6985
- if (!diffs || diffs.length == 0) {
6986
- this.setBufferStatus("");
6987
- if (this.config.enableGutters) {
6988
- nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
6989
- nvim.redrawVim();
6990
- }
6991
- this.currentSigns = [];
6992
- } else {
6993
- let added = 0;
6994
- let changed = 0;
6995
- let removed = 0;
6996
- let signs = [];
6997
- for (let diff of diffs) {
6998
- if (diff.changeType == "add" /* Add */) {
6999
- added += diff.added.count;
7000
- } else if (diff.changeType == "delete" /* Delete */) {
7001
- removed += diff.removed.count;
7002
- } else if (diff.changeType == "changed" /* Change */) {
7003
- let [add, remove] = [diff.added.count, diff.removed.count];
7004
- let min = Math.min(add, remove);
7005
- changed += min;
7006
- added += add - min;
7007
- removed += remove - min;
7008
- }
7009
- let { start, end } = diff;
7010
- for (let i = start; i <= end; i++) {
7011
- let topdelete = diff.changeType == "delete" /* Delete */ && i == 0;
7012
- let changedelete = diff.changeType == "changed" /* Change */ && diff.removed.count > diff.added.count && i == end;
7013
- signs.push({
7014
- changeType: topdelete ? "topdelete" : changedelete ? "changedelete" : diff.changeType,
7015
- lnum: topdelete ? 1 : i
7016
- });
7017
- }
7018
- if (diff.changeType == "changed" /* Change */) {
7019
- let [add, remove] = [diff.added.count, diff.removed.count];
7020
- if (add > remove) {
7021
- for (let i = 0; i < add - remove; i++) {
7022
- signs.push({
7023
- changeType: "add" /* Add */,
7024
- lnum: diff.end + 1 + i
7025
- });
7026
- }
7027
- }
7068
+ const disposables = [];
7069
+ const once = (ee, name, fn) => {
7070
+ ee.once(name, fn);
7071
+ disposables.push(import_coc12.Disposable.create(() => ee.removeListener(name, fn)));
7072
+ };
7073
+ const on = (ee, name, fn) => {
7074
+ ee.on(name, fn);
7075
+ disposables.push(import_coc12.Disposable.create(() => ee.removeListener(name, fn)));
7076
+ };
7077
+ let result = Promise.all([
7078
+ new Promise((c, e) => {
7079
+ once(child, "error", cpErrorHandler(e));
7080
+ once(child, "exit", c);
7081
+ }),
7082
+ new Promise((c) => {
7083
+ const buffers = [];
7084
+ on(child.stdout, "data", (b) => buffers.push(b));
7085
+ once(child.stdout, "close", () => c(Buffer.concat(buffers)));
7086
+ }),
7087
+ new Promise((c) => {
7088
+ const buffers = [];
7089
+ on(child.stderr, "data", (b) => buffers.push(b));
7090
+ once(child.stderr, "close", () => c(Buffer.concat(buffers).toString("utf8")));
7091
+ })
7092
+ ]);
7093
+ if (cancellationToken) {
7094
+ const cancellationPromise = new Promise((_, e) => {
7095
+ onceEvent(cancellationToken.onCancellationRequested)(() => {
7096
+ try {
7097
+ child.kill();
7098
+ } catch (err) {
7028
7099
  }
7029
- }
7030
- let items = [];
7031
- if (added) items.push(`+${added}`);
7032
- if (changed) items.push(`~${changed}`);
7033
- if (removed) items.push(`-${removed}`);
7034
- let status = ` ${items.join(" ")} `;
7035
- this.setBufferStatus(status);
7036
- this.currentSigns = signs;
7037
- if (!this.config.realtimeGutters && !force) return;
7038
- this.updateGutters();
7039
- }
7100
+ e(new Error("Cancelled"));
7101
+ });
7102
+ });
7103
+ result = Promise.race([result, cancellationPromise]);
7040
7104
  }
7041
- updateGutters() {
7042
- if (this._disposed) return;
7043
- if (!this.config.enableGutters) return;
7044
- let { nvim } = import_coc13.workspace;
7045
- let { bufnr } = this.doc;
7046
- let { signPriority } = this.config;
7047
- let signs = this.currentSigns;
7048
- nvim.pauseNotification();
7049
- nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
7050
- for (let sign of signs) {
7051
- let name = this.getSignName(sign.changeType);
7052
- nvim.call("sign_place", [0, signGroup, name, bufnr, { lnum: sign.lnum, priority: signPriority }], true);
7105
+ try {
7106
+ const [exitCode, stdout, stderr] = await result;
7107
+ return { exitCode, stdout, stderr };
7108
+ } finally {
7109
+ (0, import_coc12.disposeAll)(disposables);
7110
+ }
7111
+ }
7112
+
7113
+ // src/model/repo.ts
7114
+ var import_fs2 = __toESM(require("fs"));
7115
+ var import_os = __toESM(require("os"));
7116
+ var import_path6 = __toESM(require("path"));
7117
+ var import_util8 = __toESM(require("util"));
7118
+
7119
+ // node_modules/uuid/dist/esm-node/rng.js
7120
+ var import_crypto = __toESM(require("crypto"));
7121
+ function rng() {
7122
+ return import_crypto.default.randomBytes(16);
7123
+ }
7124
+
7125
+ // node_modules/uuid/dist/esm-node/bytesToUuid.js
7126
+ var byteToHex = [];
7127
+ for (i = 0; i < 256; ++i) {
7128
+ byteToHex[i] = (i + 256).toString(16).substr(1);
7129
+ }
7130
+ var i;
7131
+ function bytesToUuid(buf, offset) {
7132
+ var i = offset || 0;
7133
+ var bth = byteToHex;
7134
+ return [bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], "-", bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]]].join("");
7135
+ }
7136
+ var bytesToUuid_default = bytesToUuid;
7137
+
7138
+ // node_modules/uuid/dist/esm-node/v4.js
7139
+ function v4(options, buf, offset) {
7140
+ var i = buf && offset || 0;
7141
+ if (typeof options == "string") {
7142
+ buf = options === "binary" ? new Array(16) : null;
7143
+ options = null;
7144
+ }
7145
+ options = options || {};
7146
+ var rnds = options.random || (options.rng || rng)();
7147
+ rnds[6] = rnds[6] & 15 | 64;
7148
+ rnds[8] = rnds[8] & 63 | 128;
7149
+ if (buf) {
7150
+ for (var ii = 0; ii < 16; ++ii) {
7151
+ buf[i + ii] = rnds[ii];
7053
7152
  }
7054
- nvim.resumeNotification(true, true);
7055
7153
  }
7056
- async loadBlames() {
7057
- if (!this.showBlame) return;
7058
- let result = [];
7059
- let indexed = await this.repo.isIndexed(this.relpath);
7060
- if (indexed) result = await this.getBlameInfo();
7061
- this.blameInfo = result;
7154
+ return buf || bytesToUuid_default(rnds);
7155
+ }
7156
+ var v4_default = v4;
7157
+
7158
+ // src/model/repo.ts
7159
+ var Repo = class {
7160
+ constructor(git, channel, root) {
7161
+ this.git = git;
7162
+ this.channel = channel;
7163
+ this.root = root;
7062
7164
  }
7063
- async getBlameInfo(range) {
7064
- let { relpath } = this;
7065
- let root = this.repo.root;
7066
- let res = [];
7067
- const useRealTime = this.config.blameUseRealTime;
7068
- try {
7069
- let currentAuthor = await this.repo.getUsername();
7070
- const args = ["--no-pager", "blame", "-w", "-b", "-p", "--incremental", "--root", "--date", "relative", "--contents", "-", relpath];
7071
- if (range) args.push("-L", range.join(","));
7072
- let r = await this.git.exec(root, args, {
7073
- log: false,
7074
- input: this.doc.content
7075
- });
7076
- if (!r.stdout) return res;
7077
- let info;
7078
- for (let line of r.stdout.trim().split(/\r?\n/)) {
7079
- line = line.trim();
7080
- if (/^(author |committer )?External file \(--contents\)/.test(line)) {
7081
- line = "Not committed yet.";
7165
+ /**
7166
+ * Get staged info
7167
+ */
7168
+ async getStagedChunks(relpath) {
7169
+ let args = ["--no-pager", "diff", "--no-ext-diff", "-p", "-U0", "--no-color", "--staged"];
7170
+ if (relpath) args.push(toUnixSlash(relpath));
7171
+ const result = await this.exec(args);
7172
+ if (!result.stdout) {
7173
+ throw new Error(`No staged result.`);
7174
+ }
7175
+ let res = {};
7176
+ let idx = 0;
7177
+ let lines = result.stdout.split(/\r?\n/);
7178
+ let curr;
7179
+ let fsPath;
7180
+ while (idx < lines.length) {
7181
+ let line = lines[idx];
7182
+ if (fsPath && line.startsWith("@@")) {
7183
+ curr = void 0;
7184
+ let ms = line.match(/^@@\s+-(\d+),?(\d*)\s+\+(\d+),?(\d*)\s+@@/);
7185
+ if (ms) {
7186
+ curr = {
7187
+ remove: { lnum: Number(ms[1]), count: ms[2] ? Number(ms[2]) : 1 },
7188
+ add: { lnum: Number(ms[3]), count: ms[4] ? Number(ms[4]) : 1 },
7189
+ lines: []
7190
+ };
7191
+ res[fsPath] = res[fsPath] || [];
7192
+ res[fsPath].push(curr);
7082
7193
  }
7083
- let ms = line.match(/^([A-Za-z0-9]+)\s(\d+)\s(\d+)\s(\d+)/);
7194
+ } else if (curr && /^[+\-]/.test(line)) {
7195
+ curr.lines.push(line);
7196
+ } else if (line.startsWith("diff --git")) {
7197
+ let ms = line.match(/diff\s--git\sa\/(.*)\sb\//);
7084
7198
  if (ms) {
7085
- let startLnum = parseInt(ms[3], 10);
7086
- info = { startLnum, sha: ms[1], endLnum: startLnum + parseInt(ms[4], 10) - 1, index: ms[2] };
7087
- if (!/^0+$/.test(ms[1])) {
7088
- let find = res.find((o) => o.sha == ms[1]);
7089
- if (find) {
7090
- info.author = find.author;
7091
- info.time = find.time;
7092
- info.summary = find.summary;
7093
- }
7094
- }
7095
- res.push(info);
7096
- } else if (info) {
7097
- if (line.startsWith("author ")) {
7098
- let author = line.replace(/^author/, "").trim();
7099
- info.author = author == currentAuthor ? "You" : author;
7100
- } else if (line.startsWith("author-time ")) {
7101
- let text = line.replace(/^author-time/, "").trim();
7102
- const timestamps = parseInt(text, 10) * 1e3;
7103
- info.time = useRealTime ? new Date(timestamps).toLocaleString() : format(timestamps, process.env.LANG);
7104
- } else if (line.startsWith("summary ")) {
7105
- let text = line.replace(/^summary/, "").trim();
7106
- info.summary = text;
7107
- }
7199
+ fsPath = ms[1];
7200
+ curr = void 0;
7201
+ idx += 4;
7202
+ continue;
7108
7203
  }
7109
7204
  }
7110
- return res;
7111
- } catch (e) {
7112
- this.channel.appendLine(e.stack);
7205
+ idx++;
7113
7206
  }
7114
7207
  return res;
7115
7208
  }
7116
- async diffCached() {
7117
- let res = await this.repo.safeRun(["diff", "--cached"]);
7118
- if (!res.trim()) {
7119
- import_coc13.window.showWarningMessage("Empty diff");
7120
- return;
7121
- }
7122
- let { nvim } = import_coc13.workspace;
7123
- nvim.pauseNotification();
7124
- nvim.command(`keepalt above new +setl\\ previewwindow`, true);
7125
- nvim.call("append", [0, res.split(/\r?\n/)], true);
7126
- nvim.command("normal! Gdd", true);
7127
- nvim.command(`exe 1`, true);
7128
- nvim.command("setfiletype git", true);
7129
- nvim.command("setl buftype=nofile nomodifiable bufhidden=wipe nobuflisted", true);
7130
- await nvim.resumeNotification();
7131
- }
7132
- async nextConflict() {
7133
- let { nvim } = import_coc13.workspace;
7134
- if (!this.conflicts.length) {
7135
- import_coc13.window.showWarningMessage("No conflicts detected");
7136
- return;
7137
- }
7138
- let line = await nvim.call("line", ".");
7139
- for (let conflict of this.conflicts) {
7140
- if (conflict.start > line) {
7141
- await import_coc13.window.moveTo({ line: Math.max(conflict.start - 1, 0), character: 0 });
7142
- return;
7209
+ async getHEAD() {
7210
+ try {
7211
+ const result = await this.exec(["symbolic-ref", "--short", "HEAD"]);
7212
+ if (!result.stdout) {
7213
+ throw new Error("Not in a branch");
7143
7214
  }
7144
- }
7145
- if (await nvim.getOption("wrapscan")) {
7146
- await import_coc13.window.moveTo({ line: Math.max(this.conflicts[0].start - 1, 0), character: 0 });
7147
- }
7148
- }
7149
- async prevConflict() {
7150
- let { nvim } = import_coc13.workspace;
7151
- if (!this.conflicts.length) {
7152
- import_coc13.window.showWarningMessage("No conflicts detected", "warning");
7153
- return;
7154
- }
7155
- let line = await nvim.call("line", ".");
7156
- for (let conflict of this.conflicts) {
7157
- if (conflict.start > line) {
7158
- await import_coc13.window.moveTo({ line: Math.max(conflict.start - 1, 0), character: 0 });
7159
- return;
7215
+ return result.stdout.trim();
7216
+ } catch (err) {
7217
+ const result = await this.exec(["rev-parse", "HEAD"]);
7218
+ if (!result.stdout) {
7219
+ throw new Error("Error parsing HEAD");
7160
7220
  }
7161
- }
7162
- if (await nvim.getOption("wrapscan")) {
7163
- await import_coc13.window.moveTo({ line: Math.max(this.conflicts[0].start - 1, 0), character: 0 });
7221
+ return result.stdout.trim();
7164
7222
  }
7165
7223
  }
7166
- async conflictKeepPart(part) {
7167
- const { nvim } = import_coc13.workspace;
7168
- let conflicts = this.conflicts;
7169
- if (!conflicts || conflicts.length == 0) {
7170
- import_coc13.window.showWarningMessage("No conflicts detected");
7171
- return;
7172
- }
7173
- let line = await nvim.call("line", ".");
7174
- for (let conflict of conflicts) {
7175
- if (conflict.start <= line && conflict.end >= line) {
7176
- switch (part) {
7177
- case 0 /* Current */:
7178
- let start = conflict.common ? conflict.common : conflict.sep;
7179
- await nvim.command(`${start},${conflict.end}d | ${conflict.start}d`);
7180
- return;
7181
- case 1 /* Incoming */:
7182
- await nvim.command(`${conflict.end}d | ${conflict.start},${conflict.sep}d`);
7183
- return;
7184
- case 2 /* Both */:
7185
- if (conflict.common) {
7186
- await nvim.command(`${conflict.end}d | ${conflict.common},${conflict.sep}d | ${conflict.start}d`);
7187
- } else {
7188
- await nvim.command(`${conflict.end}d | ${conflict.sep}d | ${conflict.start}d`);
7189
- }
7190
- return;
7191
- }
7192
- }
7193
- }
7194
- import_coc13.window.showWarningMessage("Not positioned on a conflict");
7224
+ async hasChanged() {
7225
+ let result = await this.exec(["diff", "--name-status"]);
7226
+ if (!result.stdout) return false;
7227
+ let lines = result.stdout.split(/\r?\n/);
7228
+ return lines.some((l) => l.startsWith("M"));
7195
7229
  }
7196
- async browser(action = "open", range, permalink = false) {
7197
- let { nvim } = import_coc13.workspace;
7198
- let config = import_coc13.workspace.getConfiguration("git");
7199
- let head = (await this.repo.safeRun(["rev-parse", "HEAD"])).trim();
7200
- let branch = config.get("browserBranchName", "").trim();
7201
- if (!branch.length) {
7202
- branch = (await this.repo.safeRun(["symbolic-ref", "--short", "-q", "HEAD"])).trim();
7203
- }
7204
- let lines = range && range.length == 2 ? [
7205
- range[0],
7206
- range[1]
7207
- ] : [await nvim.eval('line(".")')];
7208
- if (this.doc.filetype == "markdown") {
7209
- let line = await nvim.call("getline", ["."]);
7210
- if (line.startsWith("#")) {
7211
- let words = line.replace(/^#+\s*/, "").split(/\s+/);
7212
- lines = words.map((s) => s.toLowerCase()).join("-");
7230
+ async getStaged() {
7231
+ let result = await this.exec(["diff", "--staged", "--name-status"]);
7232
+ if (!result.stdout) return [0, 0];
7233
+ let lines = result.stdout.trim().split(/\r?\n/);
7234
+ let conflicted = 0;
7235
+ let staged = 0;
7236
+ lines.forEach((line) => {
7237
+ if (!line.length) return;
7238
+ if (line.startsWith("U")) {
7239
+ conflicted++;
7240
+ } else {
7241
+ staged++;
7213
7242
  }
7243
+ });
7244
+ return [conflicted, staged];
7245
+ }
7246
+ async hasUntracked() {
7247
+ let cp2 = this.git.stream(this.root, ["ls-files", "--others", "--exclude-standard"]);
7248
+ return new Promise((resolve) => {
7249
+ let hasData = false;
7250
+ let timer = setTimeout(() => {
7251
+ if (cp2.killed) return;
7252
+ cp2.kill("SIGKILL");
7253
+ resolve(false);
7254
+ }, 100);
7255
+ cp2.stdout.on("data", () => {
7256
+ clearTimeout(timer);
7257
+ hasData = true;
7258
+ cp2.kill("SIGKILL");
7259
+ resolve(hasData);
7260
+ });
7261
+ cp2.on("exit", () => {
7262
+ clearTimeout(timer);
7263
+ resolve(hasData);
7264
+ });
7265
+ });
7266
+ }
7267
+ async getStatus(character, decorator) {
7268
+ try {
7269
+ let head = await this.getHEAD();
7270
+ if (!head) return "";
7271
+ let [changed, staged, untracked] = await Promise.all([this.hasChanged(), this.getStaged(), this.hasUntracked()]);
7272
+ const { changedDecorator, conflictedDecorator, stagedDecorator, untrackedDecorator } = decorator;
7273
+ let more = "";
7274
+ if (changed) more += changedDecorator;
7275
+ if (staged[0]) more += conflictedDecorator;
7276
+ if (staged[1]) more += stagedDecorator;
7277
+ if (untracked) more += untrackedDecorator;
7278
+ return `${character ? character + " " : ""}${head}${more}`;
7279
+ } catch (e) {
7280
+ this.channel.appendLine("Error on git status");
7281
+ this.channel.append(e.message);
7282
+ return "";
7214
7283
  }
7215
- let output = await this.repo.safeRun(["remote"]);
7216
- if (!output.trim()) {
7217
- import_coc13.window.showWarningMessage(`No remote found`);
7218
- return;
7219
- }
7220
- let names = output.trim().split(/\r?\n/);
7221
- let browserRemoteName = config.get("browserRemoteName", "").trim();
7222
- if (browserRemoteName.length > 0) {
7223
- if (names.includes(browserRemoteName)) {
7224
- names = [browserRemoteName];
7225
- } else {
7226
- import_coc13.window.showWarningMessage("Configured git.browserRemoteName missing from remote list");
7227
- return;
7228
- }
7284
+ }
7285
+ async getDiff(relFilepath, content, revision = "", encoding = "utf8") {
7286
+ if (relFilepath.startsWith(`.git${import_path6.default.sep}`)) return;
7287
+ let fullpath = import_path6.default.join(this.root, relFilepath);
7288
+ if (!import_fs2.default.existsSync(fullpath)) return;
7289
+ let staged;
7290
+ try {
7291
+ let indexed = await this.isIndexed(relFilepath);
7292
+ if (!indexed) return;
7293
+ let res = await this.exec(["--no-pager", "show", `${revision}:${toUnixSlash(relFilepath)}`], { encoding });
7294
+ if (!res.stdout) return;
7295
+ staged = res.stdout.replace(/\r?\n$/, "").split(/\r?\n/).join("\n");
7296
+ } catch (e) {
7297
+ this.channel.append(e.stack);
7298
+ return;
7229
7299
  }
7230
- let urls = [];
7231
- for (let name of names) {
7232
- let uri = await this.repo.safeRun(["config", "--get", `remote.${name}.url`]);
7233
- if (!uri.length) continue;
7234
- let repoURL = getRepoUrl(uri);
7235
- if (!repoURL) continue;
7236
- let tmp = new import_url.URL(repoURL);
7237
- let hostname = tmp.hostname;
7238
- let fix = "|";
7239
- try {
7240
- fix = config.get("urlFix")[hostname][permalink ? 1 : 0];
7241
- } catch (e) {
7242
- }
7243
- let url = getUrl(fix, repoURL, permalink ? head : branch, this.relpath.replace(/\\\\/g, "/"), lines);
7244
- if (url) urls.push(url);
7300
+ const stagedFile = import_path6.default.join(import_os.default.tmpdir(), `coc-${v4_default()}`);
7301
+ const currentFile = import_path6.default.join(import_os.default.tmpdir(), `coc-${v4_default()}`);
7302
+ await import_util8.default.promisify(import_fs2.default.writeFile)(stagedFile, staged + "\n", "utf8");
7303
+ await import_util8.default.promisify(import_fs2.default.writeFile)(currentFile, content, "utf8");
7304
+ let output = await getStdout(`git --no-pager diff --no-ext-diff -p -U0 --no-color ${shellescape(stagedFile)} ${shellescape(currentFile)}`);
7305
+ await import_util8.default.promisify(import_fs2.default.unlink)(stagedFile);
7306
+ await import_util8.default.promisify(import_fs2.default.unlink)(currentFile);
7307
+ if (!output) return [];
7308
+ this.channel.appendLine(`> git diff ${relFilepath}`);
7309
+ const lines = output.trim().split("\n");
7310
+ return parseDiff(lines);
7311
+ }
7312
+ async getDiffAll(category) {
7313
+ let diffGroups = /* @__PURE__ */ new Map();
7314
+ let args = "";
7315
+ if (category === 0 /* All */) {
7316
+ args = "HEAD";
7317
+ } else if (category === 1 /* Staged */) {
7318
+ args = "--cached";
7319
+ } else {
7320
+ args = "";
7245
7321
  }
7246
- if (urls.length == 1) {
7247
- if (action == "open") {
7248
- await import_coc13.workspace.openResource(urls[0]);
7249
- } else {
7250
- nvim.command(`let @+ = '${urls[0]}'`, true);
7251
- import_coc13.window.showInformationMessage("Copied url to clipboard");
7252
- }
7253
- } else if (urls.length > 1) {
7254
- let url = await import_coc13.window.showQuickPick(urls, { canPickMany: false, title: "Pick remote url" });
7255
- if (url) {
7256
- if (action == "open") {
7257
- await import_coc13.workspace.openResource(url);
7322
+ let output = await getStdout(`git --no-pager diff --no-ext-diff -p -U0 --no-color ${args}`);
7323
+ if (!output) return diffGroups;
7324
+ const lines = output.trim().split("\n");
7325
+ let lineGroups = /* @__PURE__ */ new Map();
7326
+ let file = null;
7327
+ for (const line of lines) {
7328
+ if (line.startsWith("diff --git")) {
7329
+ let ms = line.match(/diff\s--git\sa\/(.*)\sb\//);
7330
+ if (ms) {
7331
+ file = ms[1];
7258
7332
  } else {
7259
- nvim.command(`let @+ = '${url}'`, true);
7260
- nvim.command(`let @* = '${url}'`, true);
7261
- import_coc13.window.showInformationMessage("Copied url to clipboard");
7333
+ file = null;
7262
7334
  }
7263
7335
  }
7336
+ if (file) {
7337
+ if (!lineGroups.has(file)) {
7338
+ lineGroups.set(file, []);
7339
+ }
7340
+ lineGroups.get(file).push(line);
7341
+ }
7264
7342
  }
7265
- }
7266
- // show commit of current line in split window
7267
- async showCommit(useFloating = false) {
7268
- let indexed = await this.repo.isIndexed(this.relpath);
7269
- if (!indexed) {
7270
- import_coc13.window.showWarningMessage(`"${this.relpath}" not indexed.`);
7271
- return;
7272
- }
7273
- let nvim = import_coc13.workspace.nvim;
7274
- let line = await nvim.eval('line(".")');
7275
- let args = ["--no-pager", "blame", "-w", "-l", "--root", "-t", `-L${line},${line}`, this.relpath];
7276
- let res = await this.repo.exec(args);
7277
- let output = res.stdout.trim();
7278
- if (!output.length) return;
7279
- let commit = output.match(/^\S+/)[0];
7280
- if (/^0+$/.test(commit)) {
7281
- import_coc13.window.showWarningMessage("not committed yet!");
7282
- return;
7343
+ for (const [file2, lines2] of lineGroups) {
7344
+ let diffs = parseDiff(lines2);
7345
+ if (diffs) {
7346
+ diffGroups.set(file2, diffs);
7347
+ }
7283
7348
  }
7284
- if (!useFloating) useFloating = this.config.showCommitInFloating;
7285
- if (useFloating) {
7286
- let content = await this.repo.safeRun(["--no-pager", "show", commit]);
7287
- if (content == null) return;
7288
- await this.showDoc(content.trim());
7289
- return;
7349
+ return diffGroups;
7350
+ }
7351
+ async isIgnored(relativePath) {
7352
+ let res = await this.safeRun(["check-ignore", "--", relativePath]);
7353
+ return res.trim() == relativePath;
7354
+ }
7355
+ async hasConflicts(relativePath) {
7356
+ let indexed = await this.isIndexed(relativePath);
7357
+ if (!indexed) return false;
7358
+ let res = await this.exec(["diff", "--staged", "--name-status", "--", relativePath]);
7359
+ return res.stdout.trim().startsWith("U");
7360
+ }
7361
+ async isIndexed(relpath) {
7362
+ let res = await this.exec(["ls-files", "--", relpath]);
7363
+ return res.stdout && res.stdout.trim().length > 0;
7364
+ }
7365
+ async getUsername() {
7366
+ if (typeof this.userName === "string") return this.userName;
7367
+ try {
7368
+ let res = await this.exec(["config", "user.name"]);
7369
+ this.userName = res.stdout.trim();
7370
+ return this.userName;
7371
+ } catch (e) {
7372
+ this.userName = "";
7373
+ return "";
7290
7374
  }
7291
- await nvim.command(`keepalt above ${this.config.splitWindowCommand}`);
7292
- let hasFugitive = await nvim.getVar("loaded_fugitive");
7293
- if (hasFugitive) {
7294
- await nvim.command(`Gedit ${commit}`);
7295
- } else {
7296
- let content = await this.repo.safeRun(["--no-pager", "show", commit]);
7297
- if (content == null) return;
7298
- let lines = content.trim().split("\n");
7299
- nvim.pauseNotification();
7300
- nvim.command(`edit +setl\\ buftype=nofile [commit ${commit}]`, true);
7301
- nvim.command("setl foldmethod=syntax nobuflisted bufhidden=wipe", true);
7302
- nvim.command("setf git", true);
7303
- nvim.call("append", [0, lines], true);
7304
- nvim.command("normal! Gdd", true);
7305
- nvim.command(`exe 1`, true);
7306
- await nvim.resumeNotification(false, true);
7375
+ }
7376
+ async isShallow() {
7377
+ try {
7378
+ let res = await this.exec(["rev-parse", "--is-shallow-repository"]);
7379
+ return res.stdout.trim() === "true";
7380
+ } catch (e) {
7381
+ return false;
7307
7382
  }
7308
7383
  }
7309
- async toggleFold() {
7310
- let { nvim } = import_coc13.workspace;
7311
- let buf = this.doc.buffer;
7312
- let win = await nvim.window;
7313
- let bufnr = buf.id;
7314
- let doc = import_coc13.workspace.getDocument(bufnr);
7315
- if (!doc) return;
7316
- let infos = this.currentSigns;
7317
- if (!infos || infos.length == 0) {
7318
- import_coc13.window.showWarningMessage("No changes");
7319
- return;
7384
+ async exec(args, options = {}) {
7385
+ return await this.git.exec(this.root, args, options);
7386
+ }
7387
+ async safeRun(args, options = {}) {
7388
+ try {
7389
+ let res = await this.exec(args, options);
7390
+ return res ? res.stdout.replace(/\s*$/, "") : "";
7391
+ } catch (e) {
7392
+ return "";
7320
7393
  }
7321
- let lnums = infos.map((o) => o.lnum);
7322
- let foldContext = this.config.foldContext;
7323
- let max = this.doc.lineCount;
7324
- let ranges = [];
7325
- let start = null;
7326
- const addRange = (from, to) => {
7327
- let s = plus(from, foldContext, max);
7328
- let e = minus(to, foldContext, 0);
7329
- if (e - s <= 0) return;
7330
- ranges.push([s, e]);
7331
- };
7332
- for (let i = 1; i <= doc.lineCount; i++) {
7333
- let fold = lnums.indexOf(i) == -1;
7334
- if (fold && start == null) {
7335
- start = i;
7336
- continue;
7337
- }
7338
- if (start != null && !fold) {
7339
- addRange(start, i - 1);
7340
- start = null;
7341
- }
7342
- if (start != null && fold && i == doc.lineCount) {
7343
- addRange(start, i);
7394
+ }
7395
+ };
7396
+ function parseDiff(diffLines) {
7397
+ const allLines = diffLines.slice(4);
7398
+ const diffs = [];
7399
+ let diff = null;
7400
+ for (const line of allLines) {
7401
+ if (!line.startsWith("@@")) {
7402
+ if (diff) {
7403
+ diff.lines.push(line);
7344
7404
  }
7405
+ continue;
7345
7406
  }
7346
- let enabled = this.foldEnabled;
7347
- if (enabled) {
7348
- this.foldEnabled = false;
7349
- let cursor = await nvim.eval('getpos(".")');
7350
- let lnums2 = ranges.map((o) => o[0]);
7351
- let settings = this.foldSettings;
7352
- nvim.pauseNotification();
7353
- for (let lnum of lnums2) {
7354
- nvim.command(`${lnum}normal! zd`, true);
7355
- }
7356
- win.setOption("foldmethod", settings.foldmethod, true);
7357
- win.setOption("foldenable", settings.foldenable, true);
7358
- win.setOption("foldlevel", settings.foldlevel, true);
7359
- nvim.call("setpos", [".", cursor], true);
7360
- await nvim.resumeNotification();
7361
- } else {
7362
- this.foldEnabled = true;
7363
- let [foldmethod, foldenable, foldlevel] = await nvim.eval("[&foldmethod,&foldenable,&foldlevel]");
7364
- this.foldSettings = {
7365
- foldmethod,
7366
- foldenable: foldenable !== 0,
7367
- foldlevel
7407
+ let diffKey = line.split("@@", 2)[1].trim();
7408
+ const [pres, nows] = diffKey.split(/\s+/).map((str) => str.slice(1).split(","));
7409
+ const removed = {
7410
+ start: parseInt(pres[0], 10),
7411
+ count: parseInt(`${pres[1] || 1}`, 10)
7412
+ };
7413
+ const added = {
7414
+ start: parseInt(nows[0], 10),
7415
+ count: parseInt(`${nows[1] || 1}`, 10)
7416
+ };
7417
+ if (added.count === 0) {
7418
+ diff = {
7419
+ lines: [],
7420
+ start: added.start,
7421
+ end: added.start,
7422
+ head: line,
7423
+ removed,
7424
+ added,
7425
+ changeType: "delete" /* Delete */
7426
+ };
7427
+ diffs.push(diff);
7428
+ } else if (removed.count === 0) {
7429
+ diff = {
7430
+ lines: [],
7431
+ start: added.start,
7432
+ end: added.start + added.count - 1,
7433
+ head: line,
7434
+ removed,
7435
+ added,
7436
+ changeType: "add" /* Add */
7437
+ };
7438
+ diffs.push(diff);
7439
+ } else {
7440
+ diff = {
7441
+ lines: [],
7442
+ start: added.start,
7443
+ end: added.start + Math.min(added.count, removed.count) - 1,
7444
+ head: line,
7445
+ removed,
7446
+ added,
7447
+ changeType: "changed" /* Change */
7368
7448
  };
7369
- nvim.pauseNotification();
7370
- win.setOption("foldmethod", "manual", true);
7371
- nvim.command("silent! normal! zE", true);
7372
- win.setOption("foldenable", true, true);
7373
- win.setOption("foldlevel", 0, true);
7374
- await nvim.resumeNotification();
7375
- nvim.pauseNotification();
7376
- for (let r of ranges) {
7377
- nvim.command(`${r[0]},${r[1]}fold`, true);
7378
- }
7379
- await nvim.resumeNotification();
7449
+ diffs.push(diff);
7380
7450
  }
7381
7451
  }
7382
- async toggleGutters(enabled) {
7383
- this.config.enableGutters = enabled;
7384
- let { nvim } = import_coc13.workspace;
7385
- if (!enabled) {
7386
- nvim.call("sign_unplace", [signGroup, { buffer: this.doc.bufnr }], true);
7387
- nvim.redrawVim();
7388
- } else {
7389
- this.diffs = [];
7390
- await this.diffDocument(true);
7452
+ return diffs;
7453
+ }
7454
+
7455
+ // src/model/resolver.ts
7456
+ var import_coc13 = require("coc.nvim");
7457
+ var import_fs3 = __toESM(require("fs"));
7458
+ var import_path7 = __toESM(require("path"));
7459
+ var import_util10 = require("util");
7460
+ async function getRealPath(fullpath) {
7461
+ let resolved;
7462
+ try {
7463
+ resolved = await (0, import_util10.promisify)(import_fs3.default.realpath)(fullpath, "utf8");
7464
+ } catch (e) {
7465
+ if (e.message.includes("ENOENT")) {
7466
+ try {
7467
+ resolved = await import_coc13.workspace.nvim.call("expand", [fullpath]);
7468
+ } catch (e2) {
7469
+ }
7391
7470
  }
7392
7471
  }
7393
- async parseConflicts() {
7394
- if (!this.hasConflicts || !this.config.conflict.enabled) return;
7395
- const lines = this.doc.textDocument.lines;
7396
- let conflicts = [];
7397
- let conflict = null;
7398
- let state = 0 /* Initial */;
7399
- let mkStartConflict = (index, current) => ({
7400
- start: index + 1,
7401
- sep: 0,
7402
- end: 0,
7403
- current,
7404
- incoming: ""
7405
- });
7406
- lines.forEach((line, index) => {
7407
- switch (state) {
7408
- case 0 /* Initial */: {
7409
- const match = line.match(startPattern);
7410
- if (match) {
7411
- conflict = mkStartConflict(index, match[1]);
7412
- state = 1 /* MatchedStart */;
7413
- }
7414
- break;
7415
- }
7416
- case 2 /* MatchedCommon */:
7417
- case 1 /* MatchedStart */: {
7418
- let match = line.match(sepPattern);
7419
- if (match) {
7420
- conflict.sep = index + 1;
7421
- state = 3 /* MatchedSep */;
7422
- } else {
7423
- const startMatch = line.match(startPattern);
7424
- if (startMatch) {
7425
- conflict = mkStartConflict(index, startMatch[1]);
7426
- state = 1 /* MatchedStart */;
7427
- } else if (line.match(endPattern)) {
7428
- conflict = null;
7429
- state = 0 /* Initial */;
7430
- } else if (line.match(commonPattern)) {
7431
- conflict.common = index + 1;
7432
- state = 2 /* MatchedCommon */;
7433
- }
7434
- }
7435
- break;
7436
- }
7437
- case 3 /* MatchedSep */: {
7438
- const match = line.match(endPattern);
7439
- if (match) {
7440
- conflict.end = index + 1;
7441
- conflict.incoming = match[1];
7442
- conflicts.push(conflict);
7443
- conflict = null;
7444
- state = 0 /* Initial */;
7472
+ return resolved || fullpath;
7473
+ }
7474
+ var Resolver = class {
7475
+ constructor(git, channel) {
7476
+ this.git = git;
7477
+ this.channel = channel;
7478
+ this.resolvedRoots = /* @__PURE__ */ new Map();
7479
+ this.relativePaths = /* @__PURE__ */ new Map();
7480
+ }
7481
+ delete(uri) {
7482
+ this.resolvedRoots.delete(uri);
7483
+ this.relativePaths.delete(uri);
7484
+ }
7485
+ clear() {
7486
+ this.resolvedRoots.clear();
7487
+ this.relativePaths.clear();
7488
+ }
7489
+ getRelativePath(uri) {
7490
+ return this.relativePaths.get(uri);
7491
+ }
7492
+ async resolveGitRoot(doc) {
7493
+ if (!doc) return null;
7494
+ let root;
7495
+ const { uri } = doc;
7496
+ root = this.resolvedRoots.get(uri);
7497
+ if (root) return root;
7498
+ if (doc.buftype == "acwrite") {
7499
+ root = await this.resolveRootFromCwd();
7500
+ this.resolvedRoots.set(uri, root);
7501
+ return root;
7502
+ }
7503
+ if (doc.buftype != "" || doc.schema != "file") {
7504
+ return null;
7505
+ }
7506
+ let fullpath = await getRealPath(import_coc13.Uri.parse(uri).fsPath);
7507
+ if (process.platform == "win32") {
7508
+ fullpath = import_path7.default.win32.normalize(fullpath);
7509
+ }
7510
+ if (!root) {
7511
+ let parts = fullpath.split(import_path7.default.sep);
7512
+ let idx = parts.indexOf(".git");
7513
+ if (idx !== -1) {
7514
+ root = parts.slice(0, idx).join(import_path7.default.sep);
7515
+ this.resolvedRoots.set(uri, root);
7516
+ } else {
7517
+ try {
7518
+ let stat = import_fs3.default.statSync(fullpath);
7519
+ let dir = stat.isDirectory() ? fullpath : import_path7.default.dirname(fullpath);
7520
+ root = await this.git.getRepositoryRoot(dir);
7521
+ if (import_path7.default.isAbsolute(root)) {
7522
+ this.resolvedRoots.set(uri, root);
7445
7523
  } else {
7446
- const startMatch = line.match(startPattern);
7447
- if (startMatch) {
7448
- conflict = mkStartConflict(index, startMatch[1]);
7449
- state = 1 /* MatchedStart */;
7450
- } else if (line.match(sepPattern)) {
7451
- conflict = null;
7452
- state = 0 /* Initial */;
7453
- }
7524
+ root = void 0;
7454
7525
  }
7455
- break;
7526
+ } catch (e) {
7456
7527
  }
7457
7528
  }
7458
- });
7459
- this.conflicts = conflicts;
7460
- this.highlightConflicts(conflicts);
7461
- if (conflicts.length == 0) {
7462
- this.hasConflicts = false;
7463
7529
  }
7464
- }
7465
- async highlightConflicts(conflicts) {
7466
- let buffer = this.doc.buffer;
7467
- let currentHlGroup = this.config.conflict.currentHlGroup;
7468
- let incomingHlGroup = this.config.conflict.incomingHlGroup;
7469
- let commonHlGroup = this.config.conflict.commonHlGroup;
7470
- let { nvim } = import_coc13.workspace;
7471
- nvim.pauseNotification();
7472
- buffer.clearNamespace(this.config.conflictSrcId, 0, -1);
7473
- const srcId = this.config.conflictSrcId;
7474
- conflicts.map((conflict) => {
7475
- let currEnd = conflict.common ? conflict.common - 1 : conflict.sep - 1;
7476
- let currRange = import_coc13.Range.create(import_coc13.Position.create(conflict.start - 1, 0), import_coc13.Position.create(currEnd, 0));
7477
- let incomingRange = import_coc13.Range.create(import_coc13.Position.create(conflict.sep, 0), import_coc13.Position.create(conflict.end, 0));
7478
- buffer.highlightRanges(srcId, currentHlGroup, [currRange]);
7479
- buffer.highlightRanges(srcId, incomingHlGroup, [incomingRange]);
7480
- if (conflict.common) {
7481
- let range = import_coc13.Range.create(import_coc13.Position.create(conflict.common - 1, 0), import_coc13.Position.create(conflict.sep - 1, 0));
7482
- buffer.highlightRanges(srcId, commonHlGroup, [range]);
7483
- }
7484
- });
7485
- nvim.resumeNotification(false, true);
7486
- }
7487
- async showDoc(content, filetype = "diff") {
7488
- if (this.floatFactory) {
7489
- let docs = [{ content, filetype }];
7490
- await this.floatFactory.show(docs, this.config.floatConfig);
7491
- } else {
7492
- const lines = content.split("\n");
7493
- import_coc13.workspace.nvim.call("coc#ui#preview_info", [lines, "diff"], true);
7530
+ if (root) {
7531
+ this.relativePaths.set(uri, import_path7.default.relative(root, fullpath));
7494
7532
  }
7533
+ this.channel.appendLine(`resolved root of ${fullpath}: ${root}`);
7534
+ return root;
7495
7535
  }
7496
- setBufferStatus(status) {
7497
- let exists = this.gitStatus;
7498
- if (exists == status) return;
7499
- this.gitStatus = status;
7500
- let { nvim } = import_coc13.workspace;
7501
- let buffer = nvim.createBuffer(this.doc.bufnr);
7502
- nvim.pauseNotification();
7503
- buffer.setVar("coc_git_status", status, true);
7504
- nvim.callTimer("coc#util#do_autocmd", ["CocGitStatusChange"], true);
7505
- nvim.resumeNotification(false, true);
7506
- }
7507
- getSignName(changeType) {
7508
- switch (changeType) {
7509
- case "delete" /* Delete */:
7510
- return "CocGitRemoved";
7511
- case "add" /* Add */:
7512
- return "CocGitAdded";
7513
- case "changed" /* Change */:
7514
- return "CocGitChanged";
7515
- case "topdelete":
7516
- return "CocGitTopRemoved";
7517
- case "changedelete":
7518
- return "CocGitChangeRemoved";
7536
+ async resolveRootFromCwd() {
7537
+ let parts = import_coc13.workspace.cwd.split(import_path7.default.sep);
7538
+ let idx = parts.indexOf(".git");
7539
+ if (idx !== -1) return parts.slice(0, idx).join(import_path7.default.sep);
7540
+ try {
7541
+ return await this.git.getRepositoryRoot(import_coc13.workspace.cwd);
7542
+ } catch (e) {
7519
7543
  }
7520
- return "";
7521
- }
7522
- get showBlame() {
7523
- return this.config.addGBlameToVirtualText || this.config.addGBlameToBufferVar;
7544
+ return null;
7524
7545
  }
7525
7546
  dispose() {
7526
- let { nvim } = import_coc13.workspace;
7527
- let { bufnr } = this.doc;
7528
- let buffer = nvim.createBuffer(bufnr);
7529
- buffer.setVar("coc_git_status", "", true);
7530
- buffer.clearNamespace(this.config.conflictSrcId, 0, -1);
7531
- nvim.call("sign_unplace", [signGroup, { buffer: bufnr }], true);
7532
- if (this.config.addGBlameToBufferVar) {
7533
- buffer.setVar("coc_git_blame", "", true);
7534
- }
7535
- if (this.config.addGBlameToVirtualText) {
7536
- buffer.clearNamespace(this.config.virtualTextSrcId);
7537
- }
7538
- nvim.resumeNotification(false, true);
7539
- this._disposed = true;
7540
- this.foldEnabled = false;
7541
- this.blameInfo = void 0;
7542
- this.diffs = void 0;
7543
- this.conflicts = void 0;
7544
- this.currentSigns = void 0;
7547
+ this.resolvedRoots.clear();
7548
+ this.relativePaths.clear();
7545
7549
  }
7546
7550
  };
7547
- function plus(val, count, max) {
7548
- if (!count) return val;
7549
- val = val + count;
7550
- return Math.min(max, val);
7551
- }
7552
- function minus(val, count, min) {
7553
- if (!count) return val;
7554
- val = val - count;
7555
- return Math.max(min, val);
7556
- }
7557
7551
 
7558
7552
  // src/model/service.ts
7559
- var import_path8 = __toESM(require("path"));
7553
+ var uriToRoot = /* @__PURE__ */ new Map();
7560
7554
  var GitService = class {
7561
7555
  constructor(gitInfo) {
7562
7556
  this.repos = /* @__PURE__ */ new Map();
@@ -7568,6 +7562,7 @@ var GitService = class {
7568
7562
  }
7569
7563
  }
7570
7564
  getRepoFromRoot(root) {
7565
+ if (!root) return void 0;
7571
7566
  if (this.repos.has(root)) {
7572
7567
  return this.repos.get(root);
7573
7568
  }
@@ -7584,13 +7579,23 @@ var GitService = class {
7584
7579
  if (!relpath) return void 0;
7585
7580
  let ignored = await repo.isIgnored(relpath);
7586
7581
  if (ignored) return void 0;
7587
- return new GitBuffer(doc, config, relpath, repo, this.git, this.outputChannel, this.floatFactory);
7582
+ uriToRoot.set(doc.uri, root);
7583
+ let hasConflicts = await repo.hasConflicts(relpath);
7584
+ return new GitBuffer(doc, config, relpath, repo, this.git, this.outputChannel, this.floatFactory, hasConflicts);
7588
7585
  }
7589
7586
  async getCurrentRepo() {
7590
- let { nvim } = import_coc14.workspace;
7591
- let [fullpath, buftype] = await nvim.eval('[expand("%:p"),&buftype]');
7592
- if (!import_path8.default.isAbsolute(fullpath) || buftype != "") return void 0;
7593
- let root = await this.resolver.resolveGitRoot({ uri: import_coc14.Uri.file(fullpath).toString(), schema: "file", buftype: "" });
7587
+ let editor = import_coc14.window.activeTextEditor;
7588
+ let root;
7589
+ if (editor) {
7590
+ let { uri } = editor.document;
7591
+ root = uriToRoot.get(uri);
7592
+ if (!root) {
7593
+ root = await this.resolver.resolveGitRoot(editor.document);
7594
+ }
7595
+ } else {
7596
+ let cwd = import_coc14.workspace.cwd;
7597
+ root = await this.resolver.resolveGitRoot({ uri: import_coc14.Uri.file(cwd).toString(), schema: "file", buftype: "" });
7598
+ }
7594
7599
  if (root == null) return void 0;
7595
7600
  return this.getRepoFromRoot(root);
7596
7601
  }
@@ -7907,7 +7912,7 @@ async function activate(context) {
7907
7912
  let gitInfo;
7908
7913
  try {
7909
7914
  let pathHint = config.get("command", "git");
7910
- gitInfo = await findGit(pathHint, (path9) => context.logger.info(`Looking for git in: ${path9}`));
7915
+ gitInfo = await findGit(pathHint, (path8) => context.logger.info(`Looking for git in: ${path8}`));
7911
7916
  } catch (e) {
7912
7917
  import_coc16.window.showErrorMessage("git command required for coc-git");
7913
7918
  return;