@yuhufe/wtool-vdiff 0.0.1 → 0.0.2

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.
@@ -20,8 +20,11 @@ export declare function parseHunks(patch: string): {
20
20
  /**
21
21
  * 将 unified diff patch 转换为 [original, modified] 文件对。
22
22
  *
23
- * Monaco diff editor 需要两个完整文件内容,而 patch 只记录变更片段。
24
- * 未改动的行(hunk 之外)用空行填充,使两侧行号与 hunk 声明的起始位置对齐,
25
- * 从而让 Monaco 能在正确的行号位置渲染增删差异。
23
+ * 策略:
24
+ * - hunk 之外的行(patch 未提供内容):两侧各补一个空行撑开行号,
25
+ * Monaco hideUnchangedRegions 会将这些相同的空行折叠并显示正确的行号范围
26
+ * - context 行(' '):两侧均写入真实内容
27
+ * - 连续的删除('-')/ 新增('+')块:收集后成对对齐写入,
28
+ * 行数较少的一侧补空行,使 Monaco 能在同一视觉行渲染替换关系
26
29
  */
27
30
  export declare const patch2Pair: (patch: string) => FilePair[];
@@ -41,7 +41,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
41
41
 
42
42
  For more please check the link https://github.com/suren-atoyan/monaco-loader#config
43
43
  `},Ia=Qy(s_)(Hp),i_={config:t_},r_=function(){for(var t=arguments.length,n=new Array(t),s=0;s<t;s++)n[s]=arguments[s];return function(i){return n.reduceRight(function(r,o){return o(r)},i)}};function Bp(e,t){return Object.keys(t).forEach(function(n){t[n]instanceof Object&&e[n]&&Object.assign(t[n],Bp(e[n],t[n]))}),Va(Va({},e),t)}var o_={type:"cancelation",msg:"operation is manually canceled"};function ho(e){var t=!1,n=new Promise(function(s,i){e.then(function(r){return t?i(o_):s(r)}),e.catch(i)});return n.cancel=function(){return t=!0},n}var l_=["monaco"],c_=Xy.create({config:Zy,isInitialized:!1,resolve:null,reject:null,monaco:null}),Up=ky(c_,2),gi=Up[0],Kr=Up[1];function a_(e){var t=i_.config(e),n=t.monaco,s=Ay(t,l_);Kr(function(i){return{config:Bp(i.config,s),monaco:n}})}function f_(){var e=gi(function(t){var n=t.monaco,s=t.isInitialized,i=t.resolve;return{monaco:n,isInitialized:s,resolve:i}});if(!e.isInitialized){if(Kr({isInitialized:!0}),e.monaco)return e.resolve(e.monaco),ho(mo);if(window.monaco&&window.monaco.editor)return Kp(window.monaco),e.resolve(window.monaco),ho(mo);r_(u_,d_)(h_)}return ho(mo)}function u_(e){return document.body.appendChild(e)}function p_(e){var t=document.createElement("script");return e&&(t.src=e),t}function d_(e){var t=gi(function(s){var i=s.config,r=s.reject;return{config:i,reject:r}}),n=p_("".concat(t.config.paths.vs,"/loader.js"));return n.onload=function(){return e()},n.onerror=t.reject,n}function h_(){var e=gi(function(n){var s=n.config,i=n.resolve,r=n.reject;return{config:s,resolve:i,reject:r}}),t=window.require;t.config(e.config),t(["vs/editor/editor.main"],function(n){var s=n.m||n;Kp(s),e.resolve(s)},function(n){e.reject(n)})}function Kp(e){gi().monaco||Kr({monaco:e})}function m_(){return gi(function(e){var t=e.monaco;return t})}var mo=new Promise(function(e,t){return Kr({resolve:e,reject:t})}),Wp={config:a_,init:f_,__getMonacoInstance:m_};const Xl=function({isMaster:e=!1}={}){return{...Cy({isMaster:e,key:"diffViewer"})}},g_=Un({__name:"MonacoDiffViewer",props:{originalCode:{default:""},modifiedCode:{default:""},language:{default:"plaintext"},options:{default:()=>({})},modelOptions:{default:()=>({})}},emits:["renderComplete"],setup(e,{emit:t}){const n=e,s=t,{funcs:i}=Xl(),r=i.canUnchangeVisible,o=$e(null),l=Zn(null),c=Zn(null),u=Zn(null),f=Zn(null);let a=!1;return Kn(()=>{a=!1,(async()=>{const h=await Wp.init();if(a||!o.value)return;l.value=h,u.value=h.editor.createModel(n.originalCode,n.language),f.value=h.editor.createModel(n.modifiedCode,n.language),c.value=h.editor.createDiffEditor(o.value,{automaticLayout:!0,readOnly:!0,renderSideBySide:!0,useInlineViewWhenSpaceIsLimited:!1,scrollBeyondLastLine:!1,hideUnchangedRegions:{enabled:!0,contextLineCount:3},scrollbar:{verticalScrollbarSize:8,horizontalScrollbarSize:8},...n.options}),c.value.setModel({original:u.value,modified:f.value}),Object.keys(n.modelOptions).length>0&&(u.value.updateOptions(n.modelOptions),f.value.updateOptions(n.modelOptions));const E=c.value.onDidUpdateDiff(()=>{const v=c.value?.getLineChanges()??[],A=(m,_)=>_===0?0:_-m+1,V=v.reduce((m,_)=>m+A(_.modifiedStartLineNumber,_.modifiedEndLineNumber),0),b=v.reduce((m,_)=>m+A(_.originalStartLineNumber,_.originalEndLineNumber),0);i.updateChangedLines?.({added:V,removed:b}),s("renderComplete"),E.dispose()})})()}),fi(()=>{a=!0,c.value?.dispose(),u.value?.dispose(),f.value?.dispose(),c.value=null,u.value=null,f.value=null,l.value=null}),Et(()=>n.options,p=>{c.value&&c.value.updateOptions(p)},{deep:!0}),Et(()=>n.originalCode,p=>{u.value&&u.value.getValue()!==p&&u.value.setValue(p)}),Et(()=>n.modifiedCode,p=>{f.value&&f.value.getValue()!==p&&f.value.setValue(p)}),Et(()=>n.language,p=>{const h=l.value,E=u.value,v=f.value;!h||!E||!v||(h.editor.setModelLanguage(E,p),h.editor.setModelLanguage(v,p))}),Et(()=>n.modelOptions,p=>{!u.value||!f.value||(u.value.updateOptions(p),f.value.updateOptions(p))},{deep:!0}),(p,h)=>(Qe(),Xt("div",{ref_key:"containerEl",ref:o,class:yn(["monaco-editor-container",{"hide-unchanged-actions":!Mt(r)}])},null,2))}}),v_=Ur(g_,[["__scopeId","data-v-cfbdfd35"]]),E_={class:"top-bar-wrap"},y_={class:"title-area"},__={class:"filename"},N_={class:"diff-line-num"},b_={class:"add"},O_={class:"del"},S_={class:"toolbar"},w_=["checked"],C_={key:0},T_=["checked"],D_=Un({__name:"TopBar",props:{diffPair:{default:()=>[]}},setup(e){const{funcs:t,registerFunc:n}=Xl(),s=e,i=kt(()=>s.diffPair[0].filename),{viewed:r,rawed:o,canUnchangeVisible:l}=t,c=function(p){const h=p.target.checked;r.value=h},u=function(p){const h=p.target.checked;o.value=h};Kn(()=>{});const f=$e({added:0,removed:0});function a(p){Object.assign(f.value,p)}return n({viewed:r,rawed:o,updateChangedLines:a}),(p,h)=>(Qe(),Xt("div",E_,[Xe("div",y_,[Xe("div",__,es(i.value),1),Xe("div",N_,[Xe("div",b_,"+"+es(f.value.added),1),Xe("div",O_,"-"+es(f.value.removed),1)])]),Xe("div",S_,[Xe("label",null,[Xe("input",{type:"checkbox",checked:Mt(r),onChange:c},null,40,w_),h[0]||(h[0]=Qs(" viewed ",-1))]),Mt(l)?(Qe(),Xt("label",C_,[Xe("input",{type:"checkbox",checked:Mt(o),onChange:u},null,40,T_),h[1]||(h[1]=Qs(" raw ",-1))])):bl("",!0)])]))}}),V_=Ur(D_,[["__scopeId","data-v-4557d9f4"]]);function qp(e){const t=e.split(`
44
- `);let n="",s="";const i=[];let r=null;for(const o of t){if(o.startsWith("--- ")){n=o.slice(4).trim();continue}if(o.startsWith("+++ ")){s=o.slice(4).trim();continue}const l=o.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);if(l){r={origStart:parseInt(l[1],10),origCount:l[2]!==void 0?parseInt(l[2],10):1,modStart:parseInt(l[3],10),modCount:l[4]!==void 0?parseInt(l[4],10):1,lines:[]},i.push(r);continue}if(r){if(o.startsWith("\\"))continue;r.lines.push(o)}}return{origFilename:n,modFilename:s,hunks:i}}const x_=function(e){if(!e)return[{filename:"",content:""},{filename:"",content:""}];const{origFilename:t,modFilename:n,hunks:s}=qp(e),i=[],r=[];let o=1,l=1;for(const c of s){for(;o<c.origStart;)i.push(""),o++;for(;l<c.modStart;)r.push(""),l++;for(const u of c.lines){const f=u[0],a=u.slice(1);f===" "?(i.push(a),r.push(a),o++,l++):f==="-"?(i.push(a),r.push(""),o++,l++):f==="+"&&(i.push(""),r.push(a),o++,l++)}}return[{filename:t,content:i.join(`
44
+ `);let n="",s="";const i=[];let r=null;for(const o of t){if(o.startsWith("--- ")){n=o.slice(4).trim();continue}if(o.startsWith("+++ ")){s=o.slice(4).trim();continue}const l=o.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);if(l){r={origStart:parseInt(l[1],10),origCount:l[2]!==void 0?parseInt(l[2],10):1,modStart:parseInt(l[3],10),modCount:l[4]!==void 0?parseInt(l[4],10):1,lines:[]},i.push(r);continue}if(r){if(o.startsWith("\\"))continue;r.lines.push(o)}}return{origFilename:n,modFilename:s,hunks:i}}const x_=function(e){if(!e)return[{filename:"",content:""},{filename:"",content:""}];const{origFilename:t,modFilename:n,hunks:s}=qp(e),i=[],r=[],o=[],l=[],c=()=>{const a=Math.max(o.length,l.length);for(let p=0;p<a;p++)i.push(o[p]??""),r.push(l[p]??"");o.length=0,l.length=0};let u=1,f=1;for(const a of s){for(;u<a.origStart;)i.push(""),u++;for(;f<a.modStart;)r.push(""),f++;for(const p of a.lines){const h=p[0],E=p.slice(1);h===" "?(c(),i.push(E),r.push(E),u++,f++):h==="-"?(o.push(E),u++):h==="+"&&(l.push(E),f++)}c()}return[{filename:t,content:i.join(`
45
45
  `)},{filename:n,content:r.join(`
46
46
  `)}]},go=18;function Gp(e,t,n){let s=0,i=-1,r=-1;const o=()=>{i!==-1&&(i>1&&(s+=1),s+=r-i+1,i=-1,r=-1)};return{feed(l,c){const u=Math.max(1,l-t),f=Math.min(e,c+t);r===-1||u>r+1?(o(),i=u,r=f):r=Math.max(r,f)},flush(){return o(),s>0&&r!==e&&(s+=1),Math.min(s,n)},get visible(){return s},get maxReached(){return s>=n}}}const A_=function({patch:e,minLine:t,maxLine:n,unchangedCtxLineNum:s}){const{hunks:i}=qp(e);if(i.length===0)return t;const r=i[i.length-1],o=Math.max(r.origStart+r.origCount-1,r.modStart+r.modCount-1),l=Gp(o,s,n);for(const c of i){const u=Math.max(c.origStart+c.origCount-1,c.modStart+c.modCount-1);if(l.feed(c.origStart,u),l.maxReached)return n}return Math.max(t,l.flush())},I_=function({pair:e,minLine:t,maxLine:n,unchangedVisiable:s,unchangedCtxLineNum:i}){if(!e||e.length<2)return t;const r=e[0].content.split(`
47
47
  `),o=e[1].content.split(`
@@ -12817,17 +12817,23 @@ const D_ = function(e) {
12817
12817
  { filename: "", content: "" },
12818
12818
  { filename: "", content: "" }
12819
12819
  ];
12820
- const { origFilename: t, modFilename: n, hunks: s } = Kp(e), i = [], r = [];
12821
- let o = 1, l = 1;
12822
- for (const c of s) {
12823
- for (; o < c.origStart; )
12824
- i.push(""), o++;
12825
- for (; l < c.modStart; )
12826
- r.push(""), l++;
12827
- for (const u of c.lines) {
12828
- const f = u[0], a = u.slice(1);
12829
- f === " " ? (i.push(a), r.push(a), o++, l++) : f === "-" ? (i.push(a), r.push(""), o++, l++) : f === "+" && (i.push(""), r.push(a), o++, l++);
12830
- }
12820
+ const { origFilename: t, modFilename: n, hunks: s } = Kp(e), i = [], r = [], o = [], l = [], c = () => {
12821
+ const a = Math.max(o.length, l.length);
12822
+ for (let p = 0; p < a; p++)
12823
+ i.push(o[p] ?? ""), r.push(l[p] ?? "");
12824
+ o.length = 0, l.length = 0;
12825
+ };
12826
+ let u = 1, f = 1;
12827
+ for (const a of s) {
12828
+ for (; u < a.origStart; )
12829
+ i.push(""), u++;
12830
+ for (; f < a.modStart; )
12831
+ r.push(""), f++;
12832
+ for (const p of a.lines) {
12833
+ const h = p[0], E = p.slice(1);
12834
+ h === " " ? (c(), i.push(E), r.push(E), u++, f++) : h === "-" ? (o.push(E), u++) : h === "+" && (l.push(E), f++);
12835
+ }
12836
+ c();
12831
12837
  }
12832
12838
  return [
12833
12839
  { filename: t, content: i.join(`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yuhufe/wtool-vdiff",
3
3
  "private": false,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "description": "Monaco diff viewer as Vue 3 Web Component",
7
7
  "keywords": [
@@ -16,7 +16,8 @@
16
16
  "dev:lib": "vite build --watch --mode development",
17
17
  "build:site": "vite build --mode production --config site/vite.config.ts",
18
18
  "dev:site": "vite --config site/vite.config.ts",
19
- "build": "vite build --mode production"
19
+ "build": "vite build --mode production",
20
+ "pub": "npm run build && npm publish"
20
21
  },
21
22
  "main": "./dist/wtool-vdiff.cjs.js",
22
23
  "module": "./dist/wtool-vdiff.es.js",
@@ -56,9 +56,12 @@ export function parseHunks(patch: string): { origFilename: string; modFilename:
56
56
  /**
57
57
  * 将 unified diff patch 转换为 [original, modified] 文件对。
58
58
  *
59
- * Monaco diff editor 需要两个完整文件内容,而 patch 只记录变更片段。
60
- * 未改动的行(hunk 之外)用空行填充,使两侧行号与 hunk 声明的起始位置对齐,
61
- * 从而让 Monaco 能在正确的行号位置渲染增删差异。
59
+ * 策略:
60
+ * - hunk 之外的行(patch 未提供内容):两侧各补一个空行撑开行号,
61
+ * Monaco hideUnchangedRegions 会将这些相同的空行折叠并显示正确的行号范围
62
+ * - context 行(' '):两侧均写入真实内容
63
+ * - 连续的删除('-')/ 新增('+')块:收集后成对对齐写入,
64
+ * 行数较少的一侧补空行,使 Monaco 能在同一视觉行渲染替换关系
62
65
  */
63
66
  export const patch2Pair = function (patch: string): FilePair[] {
64
67
  if (!patch) {
@@ -73,12 +76,24 @@ export const patch2Pair = function (patch: string): FilePair[] {
73
76
  const origLines: string[] = []
74
77
  const modLines: string[] = []
75
78
 
76
- // 追踪两侧各自已写到的行号(1-based)
79
+ const pendingDel: string[] = []
80
+ const pendingAdd: string[] = []
81
+
82
+ const flushPending = () => {
83
+ const maxLen = Math.max(pendingDel.length, pendingAdd.length)
84
+ for (let i = 0; i < maxLen; i++) {
85
+ origLines.push(pendingDel[i] ?? '')
86
+ modLines.push(pendingAdd[i] ?? '')
87
+ }
88
+ pendingDel.length = 0
89
+ pendingAdd.length = 0
90
+ }
91
+
77
92
  let origCursor = 1
78
93
  let modCursor = 1
79
94
 
80
95
  for (const hunk of hunks) {
81
- // hunk 之前未覆盖的行用空行填充,使光标推进到 hunk 起始位置
96
+ // hunk 之前(或两个 hunk 之间)的 gap:两侧各补空行对齐行号
82
97
  while (origCursor < hunk.origStart) {
83
98
  origLines.push('')
84
99
  origCursor++
@@ -93,25 +108,21 @@ export const patch2Pair = function (patch: string): FilePair[] {
93
108
  const content = line.slice(1)
94
109
 
95
110
  if (prefix === ' ') {
96
- // context 行:两侧都保留原文
111
+ flushPending()
97
112
  origLines.push(content)
98
113
  modLines.push(content)
99
114
  origCursor++
100
115
  modCursor++
101
116
  } else if (prefix === '-') {
102
- // 删除行:original 保留,modified 用空行占位
103
- origLines.push(content)
104
- modLines.push('')
117
+ pendingDel.push(content)
105
118
  origCursor++
106
- modCursor++
107
119
  } else if (prefix === '+') {
108
- // 新增行:modified 保留,original 用空行占位
109
- origLines.push('')
110
- modLines.push(content)
111
- origCursor++
120
+ pendingAdd.push(content)
112
121
  modCursor++
113
122
  }
114
123
  }
124
+
125
+ flushPending()
115
126
  }
116
127
 
117
128
  return [