#' Title
#'
#' @param peakTable Peak table containing m/z and tr.
#' @param mgfFile The MGF file containing MS/MS information.
#' @param database The database used for identification.
#' @param ionMode Positive ion mode is 'P', negative ion mode is 'N'.
#' @param CE Collision energy,default value is 'all'.
#' @param tRCalibration T or F, T will do retention time calibration and F will not.
#' @param is.tR.file The retention time of internal standards which saved in a xlsx file.
#' @param MS1DeltaMZ Delta m/z of MS1.
#' @param MS1DeltaTR Delta retention time.
#' @param MS2.sn.threshold The S/N threshold of MS2.
#' @param MS2.noise.intensity The intensity of noise of MS2 spectrum.
#' @param MS2.missing.value.padding The MS2 missing value padding method, "half" or "minimal.value"
#' @param ms2Mode MS2 acquisition mode, 'ida' or 'dia', the default value is 'ida'
#' @param diaMethod If MS2 acquisition mode is "dia", a file save dia method should be provided.
#' @param MS1MS2DeltaTR Delta retention time between MS1 and MS2.
#' @param MS1MS2DeltaMZ Delta m/z between MS1 and MS2.
#' @param MS2DeltaMZ Delta m/z between experimental MS2 and reference MS2.
#' @param scoreMode The MS2 score mode, default is "average".
#' @param cores The number of CPU cores when computing.
#'
#' @return annotationFromPeakTableRes
#' @export annotationFromPeakTable.parallel
#' @import foreach
#' @importFrom doSNOW registerDoSNOW
#' @importFrom snow makeSOCKcluster stopCluster
#' @importFrom openxlsx write.xlsx
#' @importFrom progress progress_bar
#' @importFrom stats na.omit
#' @importFrom utils read.csv
#'
#' @examples
#' annotationFromPeakTableRes <- annotationFromPeakTable.parallel(
#' peakTable = system.file("extdata/peakTable","example.csv", package = "MetEx"),
#' mgfFile = system.file("extdata/mgf","example.mgf", package = "MetEx"),
#' database = system.file("extdata/database","example_database.xlsx", package = "MetEx"),
#' ionMode = "P",
#' MS1DeltaMZ = 0.01,
#' MS1DeltaTR = 120,
#' MS1MS2DeltaTR = 5,
#' MS1MS2DeltaMZ = 0.01,
#' MS2DeltaMZ = 0.02,
#' cores = 1)
annotationFromPeakTable.parallel <- function(peakTable,
mgfFile,
database,
ionMode,
CE = 'all',
tRCalibration = F,
is.tR.file = NA,
MS1DeltaMZ,
MS1DeltaTR,
MS2.sn.threshold = 3,
MS2.noise.intensity = "minimum",
MS2.missing.value.padding = "minimal.value",
ms2Mode = 'ida',
diaMethod = 'NA',
MS1MS2DeltaTR,
MS1MS2DeltaMZ,
MS2DeltaMZ,
scoreMode = 'average',
cores){
# require("openxlsx")
# require("dplyr")
# require("tcltk")
# require("doSNOW")
# require("progress")
# cores <- parallel::detectCores()
cl <- makeSOCKcluster(cores)
registerDoSNOW(cl)
MS1RawData <- read.csv(file = peakTable)
if (length(which(colnames(MS1RawData)=='tr')) >= 1){
packageStartupMessage('Row names of MS1 file is Available.')
} else if (length(which(colnames(MS1RawData)=='tr')) == 0 & length(which(colnames(MS1RawData)=='rt')) >= 1){
colnames(MS1RawData)[which(colnames(MS1RawData)=='rt')] = 'tr'
packageStartupMessage('Change rt to tr in row names of MS1.')
} else {
packageStartupMessage('Row names of MS1 file is wrong!')
}
mgfList <- importMgf(mgfFile)
mgfMatrix <- mgfList$mgfMatrix
mgfData <- mgfList$mgfData
mzinmgf <- as.matrix(mgfMatrix[ , 'pepmassNum'])
trinmgf <- as.matrix(mgfMatrix[ , 'trNum'])
dbData <- dbImporter(dbFile=database, ionMode = ionMode, CE = CE)
if (tRCalibration == T){
dbData <- retentionTimeCalibration(is.tR.file = is.tR.file, database.df = dbData)
}
# progress bar ------------------------------------------------------------
iterations <- nrow(MS1RawData)
pb <- progress_bar$new(
format = ":letter [:bar] :elapsed | Remaining time: :eta <br>",
total = iterations,
width = 120)
# allowing progress bar to be used in foreach -----------------------------
progress <- function(n){
pb$tick(tokens = list(letter = "Progress of annotation based on peak detection."))
}
opts <- list(progress = progress)
i <- NULL
func <- function(i){
df.annotationFromPeakTable <- data.frame(MSMS.Exp = c(NA), MSMS.DB = c(NA), DP = c(NA), RDP = c(NA), frag.ratio = c(NA),
score = c(NA), mz.DB = c(NA), tr.DB = c(NA), ID.DB = c(NA), name = c(NA), SMILES = c(NA),
Adduct = c(NA), CE.DB = c(NA))
annotationFromPeakTable.i <- cbind(MS1RawData[i,],df.annotationFromPeakTable)
mz.MS1.i <- MS1RawData$mz[i]
tr.MS1.i <- MS1RawData$tr[i]
ms2ActInRaw <- ms1ms2Match(mz = mz.MS1.i,
tr = tr.MS1.i,
MS1MS2DeltaMZ,
deltaTR = MS1MS2DeltaTR,
mgfMatrix,
mgfData,
ms2Mode,
diaMethod)
match.posi <- which(abs(dbData$`m/z` - mz.MS1.i) < MS1DeltaMZ & abs(dbData$tr - tr.MS1.i) < MS1DeltaTR)
if (length(ms2ActInRaw) == 0 & length(match.posi) == 0){
annotationFromPeakTable.i$DP <- NA
annotationFromPeakTable.i$RDP <- NA
annotationFromPeakTable.i$frag.ratio <- NA
annotationFromPeakTable.i$score <- NA
annotationFromPeakTable.i$MSMS.Exp <- NA
annotationFromPeakTable.i$MSMS.DB <- NA
annotationFromPeakTable.i$mz.DB <- NA
annotationFromPeakTable.i$tr.DB <- NA
annotationFromPeakTable.i$ID.DB <- NA
annotationFromPeakTable.i$name <- NA
annotationFromPeakTable.i$SMILES <- NA
annotationFromPeakTable.i$Adduct <- NA
annotationFromPeakTable.i$CE.DB <- NA
}
else if (length(ms2ActInRaw) == 0 & length(match.posi) != 0){
order.num <- order(abs(dbData$`m/z`[match.posi]-mz.MS1.i)/MS1DeltaMZ * 0.6 + abs(dbData$tr[match.posi]-tr.MS1.i)/MS1DeltaTR * 0.4, decreasing = F)
annotationFromPeakTable.i$DP <- NA
annotationFromPeakTable.i$RDP <- NA
annotationFromPeakTable.i$frag.ratio <- NA
annotationFromPeakTable.i$score <- NA
annotationFromPeakTable.i$MSMS.Exp <- "Can't find MS2"
annotationFromPeakTable.i$MSMS.DB <- as.character(paste(dbData$MSMS[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$mz.DB <- as.character(paste(dbData$`m/z`[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$tr.DB <- as.character(paste(dbData$tr[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$ID.DB <- as.character(paste(dbData$ID.DB[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$name <- as.character(paste(dbData$Name[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$SMILES <- as.character(paste(dbData$SMILES[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$Adduct <- as.character(paste(dbData$LCMS1_AdductName[match.posi[order.num]],collapse = " * "))
annotationFromPeakTable.i$CE.DB <- as.character(paste(dbData$CE[match.posi[order.num]],collapse = " * "))
}
else if (length(ms2ActInRaw) != 0 & length(match.posi) == 0){
annotationFromPeakTable.i$DP <- NA
annotationFromPeakTable.i$RDP <- NA
annotationFromPeakTable.i$frag.ratio <- NA
annotationFromPeakTable.i$score <- NA
ms2ActInRaw.temp <- vector(mode="character",length = length(ms2ActInRaw))
for (ms2ActInRaw.i in c(1:length(ms2ActInRaw))){
ms2ActInRaw.temp[ms2ActInRaw.i] <- paste(ms2ActInRaw[[ms2ActInRaw.i]], collapse = ";")
}
annotationFromPeakTable.i$MSMS.Exp <- paste(ms2ActInRaw.temp, collapse = " * ")
annotationFromPeakTable.i$MSMS.DB <- NA
annotationFromPeakTable.i$mz.DB <- NA
annotationFromPeakTable.i$tr.DB <- NA
annotationFromPeakTable.i$ID.DB <- NA
annotationFromPeakTable.i$name <- NA
annotationFromPeakTable.i$SMILES <- NA
annotationFromPeakTable.i$Adduct <- NA
annotationFromPeakTable.i$CE.DB <- NA
}
else if (length(ms2ActInRaw) != 0 & length(match.posi) != 0){
candidateScore <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidateDP <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidateRDP <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidatefrag.ratio <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidateMSMS.DB <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidateMSMS.Act <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.mz.DB <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.tr.DB <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.ID.DB <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.name <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.SMILES <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.Adduct <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
candidate.CE.DB <- matrix(NA, nrow = length(ms2ActInRaw), ncol = length(match.posi))
for (k in c(1:length(ms2ActInRaw))){
ms2Act <- ms2ActInRaw[[k]]
ms2Act <- strsplit(ms2Act, " ", fixed=TRUE)
ms2Act <- list2dataframe(ms2Act)
ms2Act <- na.omit(ms2Act)
ms2Act <- ms2Act[which(ms2Act[,1] < (mz.MS1.i+MS2DeltaMZ)),]
for (j in c(1:length(match.posi))){
ms2DB <- as.character(dbData$MSMS[match.posi[j]])
ms2DB <- strsplit(ms2DB, ";", fixed=TRUE)
ms2DB <- strsplit(unlist(ms2DB), " ", fixed=TRUE)
ms2DB <- list2dataframe(ms2DB)
ms2DB <- na.omit(ms2DB)
ms2DB <- ms2DB[which(ms2DB[,1] < (ms2DB+MS2DeltaMZ)),]
if (nrow(ms2Act)==0){
candidateScore[k,j] <- NA
candidateDP[k,j] <- NA
candidateRDP[k,j] <- NA
candidatefrag.ratio[k,j] <- NA
candidateMSMS.DB[k,j] <- dbData$MSMS[match.posi[j]]
candidateMSMS.Act[k,j] <- "Can't find MS2"
candidate.mz.DB[k,j] <- dbData$`m/z`[match.posi[j]]
candidate.tr.DB[k,j] <- dbData$tr[match.posi[j]]
candidate.ID.DB[k,j] <- dbData$ID.DB[match.posi[j]]
candidate.name[k,j] <- dbData$Name[match.posi[j]]
candidate.SMILES[k,j] <- dbData$SMILES[match.posi[j]]
candidate.Adduct[k,j] <- dbData$LCMS1_AdductName[match.posi[j]]
candidate.CE.DB[k,j] <- dbData$CE[match.posi[j]]
}else{
candidateScore[k,j] <- ms2Score(ms2Act, ms2DB, MS2DeltaMZ, sn.threshold = MS2.sn.threshold, noise.intensity = MS2.noise.intensity, missing.value.padding = MS2.missing.value.padding, scoreMode)
candidateDP[k,j] <- ms2Score(ms2Act, ms2DB, MS2DeltaMZ, sn.threshold = MS2.sn.threshold, noise.intensity = MS2.noise.intensity, missing.value.padding = MS2.missing.value.padding, scoreMode = "obverse")
candidateRDP[k,j] <- ms2Score(ms2Act, ms2DB, MS2DeltaMZ, sn.threshold = MS2.sn.threshold, noise.intensity = MS2.noise.intensity, missing.value.padding = MS2.missing.value.padding, scoreMode = "reverse")
candidatefrag.ratio[k,j] <- ms2Score(ms2Act, ms2DB, MS2DeltaMZ, sn.threshold = MS2.sn.threshold, noise.intensity = MS2.noise.intensity, missing.value.padding = MS2.missing.value.padding, scoreMode = "matched.fragments.ratio")
candidateMSMS.DB[k,j] <- dbData$MSMS[match.posi[j]]
candidateMSMS.Act[k,j] <- paste(ms2ActInRaw[[k]],collapse=";")
candidate.mz.DB[k,j] <- dbData$`m/z`[match.posi[j]]
candidate.tr.DB[k,j] <- dbData$tr[match.posi[j]]
candidate.ID.DB[k,j] <- dbData$ID.DB[match.posi[j]]
candidate.name[k,j] <- dbData$Name[match.posi[j]]
candidate.SMILES[k,j] <- dbData$SMILES[match.posi[j]]
candidate.Adduct[k,j] <- dbData$LCMS1_AdductName[match.posi[j]]
candidate.CE.DB[k,j] <- dbData$CE[match.posi[j]]
}
}
}
order.num <- order(candidateScore,decreasing = T)
annotationFromPeakTable.i$DP <- paste(candidateDP[order.num],collapse = " * ")
annotationFromPeakTable.i$RDP <- paste(candidateRDP[order.num],collapse = " * ")
annotationFromPeakTable.i$frag.ratio <- paste(candidatefrag.ratio[order.num],collapse = " * ")
annotationFromPeakTable.i$score <- paste(candidateScore[order.num],collapse = " * ")
annotationFromPeakTable.i$MSMS.Exp <- paste(candidateMSMS.Act[order.num],collapse = " * ")
annotationFromPeakTable.i$MSMS.DB <- paste(candidateMSMS.DB[order.num],collapse = " * ")
annotationFromPeakTable.i$mz.DB <- paste(candidate.mz.DB[order.num],collapse = " * ")
annotationFromPeakTable.i$tr.DB <- paste(candidate.tr.DB[order.num],collapse = " * ")
annotationFromPeakTable.i$ID.DB <- paste(candidate.ID.DB[order.num],collapse = " * ")
annotationFromPeakTable.i$name <- paste(candidate.name[order.num],collapse = " * ")
annotationFromPeakTable.i$SMILES <- paste(candidate.SMILES[order.num],collapse = " * ")
annotationFromPeakTable.i$Adduct <- paste(candidate.Adduct[order.num],collapse = " * ")
annotationFromPeakTable.i$CE.DB <- paste(candidate.CE.DB[order.num],collapse = " * ")
}
return(annotationFromPeakTable.i)
}
annotationFromPeakTable.result.all <- foreach(i=1:nrow(MS1RawData), .options.snow=opts, .combine='rbind') %dopar% func(i)
stopCluster(cl)
candidate2dataframe <- function(in.vector){
if (all(is.na(in.vector))){
candidateDataframe <- data.frame(col1 = NA, col2 = NA, col3 = NA, col4 = NA, col5 = NA)
# candidateDataframe <- candidateDataframe[-1, ]
for (i in c(1:length(in.vector))) {
candidateDataframe[i, "col1"] <- NA
candidateDataframe[i, "col2"] <- NA
candidateDataframe[i, "col3"] <- NA
candidateDataframe[i, "col4"] <- NA
candidateDataframe[i, "col5"] <- NA
}
}
else {
candidateList <- strsplit(in.vector, " * ", fixed=TRUE)
candidateDataframe <- data.frame(col1 = NA, col2 = NA, col3 = NA, col4 = NA, col5 = NA)
# candidateDataframe <- candidateDataframe[-1, ]
for (i in c(1:length(candidateList))) {
candidateDataframe[i, "col1"] <- candidateList[[i]][1]
candidateDataframe[i, "col2"] <- candidateList[[i]][2]
candidateDataframe[i, "col3"] <- candidateList[[i]][3]
candidateDataframe[i, "col4"] <- candidateList[[i]][4]
candidateDataframe[i, "col5"] <- candidateList[[i]][5]
}
}
return(candidateDataframe)
}
annotationFromPeakTableRes1 <- cbind(MS1RawData,
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.Exp)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.DB)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$DP)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$RDP)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$frag.ratio)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$score)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$mz.DB)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$tr.DB)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$ID.DB)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$name)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$SMILES)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$Adduct)[,1],
candidate2dataframe(annotationFromPeakTable.result.all$CE.DB)[,1])
colnames(annotationFromPeakTableRes1) <- c(colnames(MS1RawData),"MSMS.Exp","MSMS.DB","DP","RDP","frag.ratio","score","mz.DB","tr.DB","ID.DB","name","SMILES","Adduct","CE.DB")
annotationFromPeakTableRes2 <- cbind(MS1RawData,
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.Exp)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.DB)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$RDP)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$DP)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$frag.ratio)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$score)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$mz.DB)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$tr.DB)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$ID.DB)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$name)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$SMILES)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$Adduct)[,2],
candidate2dataframe(annotationFromPeakTable.result.all$CE.DB)[,2])
colnames(annotationFromPeakTableRes2) <- c(colnames(MS1RawData),"MSMS.Exp","MSMS.DB","DP","RDP","frag.ratio","score","mz.DB","tr.DB","ID.DB","name","SMILES","Adduct","CE.DB")
annotationFromPeakTableRes3 <- cbind(MS1RawData,
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.Exp)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.DB)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$DP)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$RDP)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$frag.ratio)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$score)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$mz.DB)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$tr.DB)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$ID.DB)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$name)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$SMILES)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$Adduct)[,3],
candidate2dataframe(annotationFromPeakTable.result.all$CE.DB)[,3])
colnames(annotationFromPeakTableRes3) <- c(colnames(MS1RawData),"MSMS.Exp","MSMS.DB","DP","RDP","frag.ratio","score","mz.DB","tr.DB","ID.DB","name","SMILES","Adduct","CE.DB")
annotationFromPeakTableRes4 <- cbind(MS1RawData,
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.Exp)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.DB)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$DP)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$RDP)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$frag.ratio)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$score)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$mz.DB)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$tr.DB)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$ID.DB)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$name)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$SMILES)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$Adduct)[,4],
candidate2dataframe(annotationFromPeakTable.result.all$CE.DB)[,4])
colnames(annotationFromPeakTableRes4) <- c(colnames(MS1RawData),"MSMS.Exp","MSMS.DB","DP","RDP","frag.ratio","score","mz.DB","tr.DB","ID.DB","name","SMILES","Adduct","CE.DB")
annotationFromPeakTableRes5 <- cbind(MS1RawData,
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.Exp)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$MSMS.DB)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$DP)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$RDP)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$frag.ratio)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$score)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$mz.DB)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$tr.DB)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$ID.DB)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$name)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$SMILES)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$Adduct)[,5],
candidate2dataframe(annotationFromPeakTable.result.all$CE.DB)[,5])
colnames(annotationFromPeakTableRes5) <- c(colnames(MS1RawData),"MSMS.Exp","MSMS.DB","DP","RDP","frag.ratio","score","mz.DB","tr.DB","ID.DB","name","SMILES","Adduct","CE.DB")
annotationFromPeakTableRes.list <- list(candidate.1 = annotationFromPeakTableRes1,
candidate.2 = annotationFromPeakTableRes2,
candidate.3 = annotationFromPeakTableRes3,
candidate.4 = annotationFromPeakTableRes4,
candidate.5 = annotationFromPeakTableRes5)
# write.xlsx(annotationFromPeakTableRes.list, file = result.file)
return(annotationFromPeakTableRes.list)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.