import * as fs from 'fs' ; import { window , workspace , TextEditor } from 'vscode' ; const gitCommitId = require ( 'git-commit-id' ) ; import { CsvEntry , CsvStructure } from './model' ; import { removeLeadingAndTrailingSlash , removeTrailingSlash , startLineNumberFromStringDefinition , endLineNumberFromStringDefinition , standardizeFilename , } from './utils/workspace-util' ; import { CommentListEntry } from './comment-list-entry' ; import { getSelectionStringDefinition , hasSelection } from './utils/editor-utils' ; import { getCsvFileLinesAsArray , setCsvFileLines } from './utils/storage-utils' ; import path from 'path' ; export class ReviewCommentService { constructor ( private reviewFile , private workspaceRoot ) { } param comment param editor async addComment ( comment , editor = null ) { this . checkFileExists ( ) ; if ( ! this . getSelectedLines ( comment , editor ) ) { return ; } comment . filename = standardizeFilename ( this . workspaceRoot , editor ! . document . fileName ) ; setCsvFileLines ( this . reviewFile , [ CsvStructure . formatAsCsvLine ( this . finalizeComment ( comment ) ) ] , false ) ; } param comment param editor async updateComment ( comment , editor = null ) { this . checkFileExists ( ) ; const fallBackKey = comment . lines ; if ( ! this . getSelectedLines ( comment , editor , true ) ) { return ; } const rows = getCsvFileLinesAsArray ( this . reviewFile ) ; let updateRowIndex = rows . findIndex ( ( row ) => row . includes ( comment . id ) ) ; if ( updateRowIndex < 0 ) { updateRowIndex = rows . findIndex ( ( row ) => row . includes ( comment . filename ) && row . includes ( fallBackKey ) ) ; } if ( updateRowIndex < 0 ) { window . showErrorMessage ( `Update failed. Cannot find line definition '${ comment . lines }' for '${ comment . filename }' in '${ this . reviewFile }'.` ) ; return ; } rows [ updateRowIndex ] = CsvStructure . formatAsCsvLine ( this . finalizeComment ( comment ) ) ; setCsvFileLines ( this . reviewFile , rows ) ; } async deleteComment ( id , label ) { this . checkFileExists ( ) ; const rows = getCsvFileLinesAsArray ( this . reviewFile ) ; const updateRowIndex = rows . findIndex ( ( row ) => row . includes ( id ) ) ; if ( updateRowIndex > - 1 ) { rows . splice ( updateRowIndex , 1 ) ; setCsvFileLines ( this . reviewFile , rows ) ; } else { window . showErrorMessage ( `Update failed. Cannot delete comment '${ label }' in '${ this . reviewFile }'.` ) ; } } param comment param editor param ignoreIfNone return private getSelectedLines ( comment , editor = null , ignoreIfNone = false ) { if ( ignoreIfNone && ! hasSelection ( editor ) ) { return true ; } if ( ! editor ?. selection ) { window . showErrorMessage ( `Error referencing file/lines, Please select again.` ) ; return false ; } comment . lines = getSelectionStringDefinition ( editor ) ; return true ; } param comment return private finalizeComment ( comment ) { const copy = { ... comment } ; const gitDirectory = workspace . getConfiguration ( ) . get ( 'code-review.gitDirectory' ) as string ; const gitRepositoryPath = path . resolve ( this . workspaceRoot , gitDirectory ) ; try { copy . sha = gitCommitId ( { cwd : gitRepositoryPath } ) ; } catch ( error ) { copy . sha = '' ; console . log ( 'Not in a git repository. Leaving SHA empty' , error ) ; } const startAnker = startLineNumberFromStringDefinition ( copy . lines ) ; const endAnker = endLineNumberFromStringDefinition ( copy . lines ) ; copy . url = this . remoteUrl ( copy . sha , copy . filename , startAnker , endAnker ) ; return copy ; } param sha param filePath param start param end private remoteUrl ( sha , filePath , start ? , end ? ) { const customUrl = workspace . getConfiguration ( ) . get ( 'code-review.customUrl' ) as string ; const baseUrl = workspace . getConfiguration ( ) . get ( 'code-review.baseUrl' ) as string ; const filePathWithoutLeadingAndTrailingSlash = removeLeadingAndTrailingSlash ( filePath ) ; if ( ! baseUrl && ! customUrl ) { return '' ; } else if ( customUrl ) { return customUrl . replace ( '{sha}' , sha ) . replace ( '{file}' , filePathWithoutLeadingAndTrailingSlash ) . replace ( '{start}' , start ? start . toString ( ) : '0' ) . replace ( '{end}' , end ? end . toString ( ) : '0' ) ; } else { const baseUrlWithoutTrailingSlash = removeTrailingSlash ( baseUrl ) ; const shaPart = sha ? `${ sha }/` : '' ; const ankerPart = start && end ? `#L${ start }-L${ end }` : '' ; return `${ baseUrlWithoutTrailingSlash }/${ shaPart }${ filePathWithoutLeadingAndTrailingSlash }${ ankerPart }` ; } } private checkFileExists ( ) { if ( ! fs . existsSync ( this . reviewFile ) ) { window . showErrorMessage ( `Could not add to file: '${ this . reviewFile }': File does not exist` ) ; } } }