88} from "../../deps/socket.ts" ;
99import { pull } from "./pull.ts" ;
1010import { getCodeBlocks , TinyCodeBlock } from "./getCodeBlocks.ts" ;
11- import { diffToChanges } from "./diffToChanges .ts" ;
12- import { getUserId } from "./id .ts" ;
11+ import { createNewLineId , getUserId } from "./id .ts" ;
12+ import { diff , toExtendedChanges } from "../../deps/onp .ts" ;
1313import { applyCommit , makeCommitsNewCodeBlock } from "./_codeBlock.ts" ;
1414
1515/** コードブロックの上書きに使う情報のinterface */
@@ -35,10 +35,13 @@ export interface UpdateCodeFileOptions {
3535 */
3636 insertPositionIfNotExist ?: "top" | "bottom" | "notInsert" ;
3737
38+ /** `true`の場合、コードブロック作成時に空行承り太郎(ページ末尾に必ず空行を設ける機能)を有効する(既定は`true`) */
39+ isInsertEmptyLineInTail ?: boolean ;
40+
3841 /** WebSocketの通信に使うsocket */
3942 socket ?: Socket ;
4043
41- /** trueでデバッグ出力ON */
44+ /** `true`でデバッグ出力ON */
4245 debug ?: boolean ;
4346}
4447
@@ -63,6 +66,7 @@ export const updateCodeFile = async (
6366 /** optionsの既定値はこの中に入れる */
6467 const defaultOptions : Required < UpdateCodeFileOptions > = {
6568 insertPositionIfNotExist : "notInsert" ,
69+ isInsertEmptyLineInTail : true ,
6670 socket : options ?. socket ?? await socketIO ( ) ,
6771 debug : false ,
6872 } ;
@@ -77,59 +81,22 @@ export const updateCodeFile = async (
7781 } , {
7882 filename : codeFile . filename ,
7983 } ) ;
80- const codeBodies = flatCodeBodies ( codeBlocks ) ;
81-
82- if ( codeBlocks . length <= 0 ) {
83- // 更新対象のコードブロックが存在していなかった場合は、新しいコードブロックを作成して終了する
84- if ( opt . insertPositionIfNotExist === "notInsert" ) return ;
85- const insertLineId =
86- opt . insertPositionIfNotExist === "top" && lines . length >= 1
87- ? lines [ 1 ] . id
88- : "_end" ;
89- const commits = await makeCommitsNewCodeBlock (
90- codeFile ,
91- insertLineId ,
92- ) ;
93- if ( codeBodies . length <= 0 ) {
94- await applyCommit ( commits , head , project , title , opt . socket ) ;
95- }
96- return ;
97- } else if ( codeBodies . length <= 0 ) {
98- // codeBodiesが無かった場合はdiffToChangesが例外を引き起こすので、その対策
99- const insertLineId = codeBlocks [ 0 ] . nextLine
100- ? codeBlocks [ 0 ] . nextLine . id
101- : "_end" ;
102- const commits = await makeCommitsNewCodeBlock (
103- codeFile ,
104- insertLineId ,
105- ) ;
106- if ( codeBodies . length <= 0 ) {
107- await applyCommit ( commits . splice ( 1 ) , head , project , title , opt . socket ) ;
108- }
109- return ;
110- }
111-
112- const changes = [ ...diffToChanges (
113- codeBodies ,
114- newCode ,
115- { userId : await getUserId ( ) } ,
116- ) ] ;
117-
118- // insert行のIDと各行のインデントを修正する
119- const commits = fixCommits ( changes , codeBlocks ) ;
84+ const commits = [
85+ ...makeCommits ( codeBlocks , codeFile , lines , {
86+ ...opt ,
87+ userId : await getUserId ( ) ,
88+ } ) ,
89+ ] ;
12090
12191 if ( opt . debug ) {
12292 console . log ( "vvv original code Blocks vvv" ) ;
12393 console . log ( codeBlocks ) ;
124- console . log ( "vvv original code lines vvv" ) ;
125- console . log ( codeBodies ) ;
12694 console . log ( "vvv new codes vvv" ) ;
12795 console . log ( newCode ) ;
12896 console . log ( "vvv commits vvv" ) ;
12997 console . log ( commits ) ;
13098 }
13199
132- // 差分を送信
133100 await applyCommit ( commits , head , project , title , opt . socket ) ;
134101
135102 if ( ! options ?. socket ) opt . socket . disconnect ( ) ;
@@ -148,50 +115,110 @@ function flatCodeBodies(codeBlocks: readonly TinyCodeBlock[]): Line[] {
148115 } ) . flat ( ) ;
149116}
150117
151- /** insert行のIDと各行のインデントを修正する */
152- function fixCommits (
153- commits : ( InsertCommit | UpdateCommit | DeleteCommit ) [ ] ,
118+ /** コードブロックの差分からコミットデータを作成する */
119+ function * makeCommits (
154120 codeBlocks : TinyCodeBlock [ ] ,
155- ) {
156- const idReplacePatterns : {
157- from : string ;
158- to : string ;
159- } [ ] = ( ( ) => {
160- const patterns = [ ] ;
161- for ( let i = 0 ; i < codeBlocks . length ; i ++ ) {
162- // コード本体の先頭ID -> 1つ前のコードブロックの真下の行のID
163- const currentCode = codeBlocks [ i ] ;
164- const nextCode = codeBlocks [ i + 1 ] ;
165- if ( ! currentCode . nextLine ) continue ;
166- patterns . push ( {
167- from : nextCode ?. bodyLines [ 0 ] . id ?? "_end" ,
168- to : currentCode . nextLine . id ,
169- } ) ;
121+ codeFile : CodeFile ,
122+ lines : Line [ ] ,
123+ { userId, insertPositionIfNotExist, isInsertEmptyLineInTail } : {
124+ userId : string ;
125+ insertPositionIfNotExist : Required <
126+ UpdateCodeFileOptions [ "insertPositionIfNotExist" ]
127+ > ;
128+ isInsertEmptyLineInTail : Required <
129+ UpdateCodeFileOptions [ "isInsertEmptyLineInTail" ]
130+ > ;
131+ } ,
132+ ) : Generator < DeleteCommit | InsertCommit | UpdateCommit , void , unknown > {
133+ function makeIndent ( codeBlock : TinyCodeBlock ) : string {
134+ const title = codeBlock . titleLine . text ;
135+ const count = title . length - title . trimStart ( ) . length + 1 ;
136+ return " " . repeat ( count ) ;
137+ }
138+
139+ const codeBodies = flatCodeBodies ( codeBlocks ) ;
140+ if ( codeBodies . length <= 0 ) {
141+ // ページ内にコードブロックが無かった場合は新しく作成して終了する
142+ if ( insertPositionIfNotExist === "notInsert" ) return ;
143+ const insertLineId = insertPositionIfNotExist === "top" && lines . length > 1
144+ ? lines [ 1 ] . id
145+ : "_end" ;
146+ const commits = makeCommitsNewCodeBlock (
147+ codeFile ,
148+ insertLineId ,
149+ { userId } ,
150+ ) ;
151+ let isInsertBottom = false ;
152+ for ( const commit of commits ) {
153+ if ( commit . _insert == "_end" ) isInsertBottom = true ;
154+ yield commit ;
170155 }
171- return patterns ;
172- } ) ( ) ;
173- for ( const commit of commits ) {
174- if ( "_delete" in commit ) continue ;
175- else if ( "_insert" in commit ) {
176- // ID修正
177- for ( const pattern of idReplacePatterns ) {
178- if ( commit . _insert !== pattern . from ) continue ;
179- commit . _insert = pattern . to ;
180- break ;
181- }
156+ if ( isInsertBottom && isInsertEmptyLineInTail ) {
157+ // 空行承り太郎
158+ yield {
159+ _insert : "_end" ,
160+ lines : {
161+ id : createNewLineId ( userId ) ,
162+ text : "" ,
163+ } ,
164+ } ;
182165 }
183- // インデント挿入
184- const belongBlock = codeBlocks . find ( ( block ) => {
185- const targetId = "_update" in commit ? commit . _update : commit . _insert ;
186- if ( block . bodyLines . some ( ( e ) => e . id === targetId ) ) return true ;
187- if ( "_update" in commit ) return false ;
188- if ( targetId === block . nextLine ?. id ) return true ;
189- return false ;
190- } ) ;
191- if ( belongBlock === undefined ) continue ;
192- const titleText = belongBlock . titleLine . text ;
193- const indent = titleText . length - titleText . trimStart ( ) . length + 1 ;
194- commit . lines . text = " " . repeat ( indent ) + commit . lines . text ;
166+ return ;
167+ }
168+
169+ // 差分を求める
170+ const { buildSES } = diff (
171+ codeBodies . map ( ( e ) => e . text ) ,
172+ Array . isArray ( codeFile . content )
173+ ? codeFile . content
174+ : codeFile . content . split ( "\n" ) ,
175+ ) ;
176+ let lineNo = 0 ;
177+ for ( const change of toExtendedChanges ( buildSES ( ) ) ) {
178+ // 差分からcommitを作成
179+ const { lineId, codeIndex } =
180+ ( ( ) : { lineId : string ; codeIndex : number } => {
181+ if ( lineNo >= codeBodies . length ) {
182+ const index = codeBlocks . length - 1 ;
183+ return {
184+ lineId : codeBlocks [ index ] . nextLine ?. id ?? "_end" ,
185+ codeIndex : index ,
186+ } ;
187+ }
188+ return {
189+ lineId : codeBodies [ lineNo ] . id ,
190+ codeIndex : codeBlocks . findIndex ( ( e0 ) =>
191+ e0 . bodyLines . some ( ( e1 ) => e1 . id == codeBodies [ lineNo ] . id )
192+ ) ,
193+ } ;
194+ } ) ( ) ;
195+ const codeBlock = codeBlocks [ codeIndex ] ;
196+ if ( change . type == "added" ) {
197+ const codeBlockInsert =
198+ lineId == codeBlock . bodyLines [ 0 ] . id && codeIndex >= 1
199+ ? codeBlocks [ codeIndex - 1 ]
200+ : codeBlocks [ codeIndex ] ;
201+ yield {
202+ _insert : codeBlockInsert . nextLine ?. id ?? "_end" ,
203+ lines : {
204+ id : createNewLineId ( userId ) ,
205+ text : makeIndent ( codeBlockInsert ) + change . value ,
206+ } ,
207+ } ;
208+ continue ;
209+ } else if ( change . type == "deleted" ) {
210+ yield {
211+ _delete : lineId ,
212+ lines : - 1 ,
213+ } ;
214+ } else if ( change . type == "replaced" ) {
215+ yield {
216+ _update : lineId ,
217+ lines : {
218+ text : makeIndent ( codeBlock ) + change . value ,
219+ } ,
220+ } ;
221+ }
222+ lineNo ++ ;
195223 }
196- return commits ;
197224}
0 commit comments