<commit_before>"|===========================================================================|
"|                                                                           |
"|         FILE:  autoload/commentable/block.vim                             |
"|                                                                           |
"|  DESCRIPTION:  Block construction class.                                  |
"|                                                                           |
"|===========================================================================|

"|===========================================================================|
"|                            SCRIPT CONSTANTS                               |
"|===========================================================================|
let s:t_number = v:version >= 800 ? v:t_number : type(0)
let s:t_list   = v:version >= 800 ? v:t_list   : type([])
let s:t_string = v:version >= 800 ? v:t_string : type('')

let g:commentable#block#lmat_none = 0 " Not part of a comment
let g:commentable#block#lmat_int  = 1 " Internal comment line
let g:commentable#block#lmat_wall = 2 " Wall of a comment

"|===========================================================================|
"|                               CONSTRUCTOR                                 |
"|===========================================================================|

"|===========================================================================|
"| commentable#block#New(indentamount) abort {{{                             |
"|                                                                           |
"| Create a new block object. Only needs to know if it is indented or not;   |
"| the remaining parameters are determined in the constructor.               |
"|                                                                           |
"| PARAMS:                                                                   |
"|   indentamount) Is this an indented block?                                |
"|                                                                           |
"| Returns the block object.                                                 |
"|===========================================================================|
function! commentable#block#New(indentamount) abort
	let l:block = {
	 \ 'style': commentable#style#New(),
	 \ 'paragraphs': [],
	 \ 'AddParagraph': function('<SID>AddParagraph'),
	 \ 'AddExisting': function('<SID>AddExisting'),
	 \ 'LineMatches': function('<SID>LineMatches'),
	 \ 'GetFormat': function('<SID>GetFormat'),
	 \ '_GetRange': function('<SID>GetRange'),
	 \ 'initialmatch': 1,
	 \ }
	call l:block.style.SetIndented(a:indentamount)
	return l:block
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"|                              PUBLIC METHODS                               |
"|===========================================================================|

"|===========================================================================|
"| block.AddParagraph(para) abort dict {{{                                   |
"|                                                                           |
"| Add a new paragraph of text to this block.                                |
"|                                                                           |
"| PARAMS:                                                                   |
"|   para) The paragraph to add. Adds the exact object, not a copy.          |
"|                                                                           |
"| Returns nothing.                                                          |
"|===========================================================================|
function! s:AddParagraph(para) abort dict
	call add(l:self.paragraphs, a:para)
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"| block.AddExisting(linenum) abort dict {{{                                 |
"|                                                                           |
"| Add paragraphs from an existing block.                                    |
"|                                                                           |
"| PARAMS:                                                                   |
"|   linenum) A line in the exisiting paragraph. Will read forwards and      |
"|            backwards from this line to try to find the full paragraph.    |
"|                                                                           |
"| Returns [firstline, lastline], showing which lines were used. Throws if   |
"| the given line is not in the paragraph.                                   |
"|===========================================================================|
function! s:AddExisting(linenum) abort dict
	let [l:firstline, l:lastline] = l:self._GetRange(a:linenum)

	let [l:initmatch, l:medial, l:final, l:spacer] = [
		\ l:self.style.GetInitMatch(),
		\ l:self.style.GetMedial(),
		\ l:self.style.GetFinal(),
		\ l:self.style.GetSpacer(),
		\ ]

	let l:lines = getline(l:firstline, l:lastline)
	call map(l:lines, 'commentable#util#StripSpaces(v:val)')
	call map(l:lines, 'substitute(v:val, ''\V\^' . l:initmatch . ''', "", "")')
	call map(l:lines, 'substitute(v:val, ''\V' . l:final . '\$'', "", "")')

	if match(l:lines[0], '\V\^' . l:medial . '\*\$') == 0
		call remove(l:lines, 0)
	endif

	if len(l:lines) > 0                                    &&
	 \ match(l:lines[-1], '\V\^' . l:medial . '\*\$') == 0
		call remove(l:lines, -1)
	endif

	call map(l:lines,
	 \       'substitute(v:val, ''\V\^' . l:medial . ''', "", "")')

	if l:spacer !=# ''
		call map(l:lines,
		 \       'substitute(v:val, ''\V\^' . l:spacer . ''', "", "")')
		call map(l:lines,
		 \       'substitute(v:val, ''\V' . l:spacer . '\$'', "", "")')
	endif

	if len(l:lines) == 0
		call l:self.AddParagraph(commentable#paragraph#New(''))
	else
		let l:curpar = commentable#paragraph#New(l:lines[0])
		let l:lastlineblank = 0
		for l:line in l:lines[1:]
			if l:line =~# '\m^\s*$'
				call l:self.AddParagraph(l:curpar)
				let l:curpar = commentable#paragraph#New(l:line)
				let l:lastlineblank = 1
			elseif l:curpar.IsInParagraph(l:line) && (!l:lastlineblank)
				call l:curpar.AddLine(l:line)
			else
				call l:self.AddParagraph(l:curpar)
				let l:curpar = commentable#paragraph#New(l:line)
				let l:lastlineblank = 0
			endif
		endfor
		call l:self.AddParagraph(l:curpar)
	endif

	return [l:firstline, l:lastline]
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"| block.LineMatches(linenum) abort dict {{{                                 |
"|                                                                           |
"| Determine if a given existing line is valid for this block.               |
"|                                                                           |
"| PARAMS:                                                                   |
"|   linenum) The line to verify.                                            |
"|                                                                           |
"| Returns a commentable#block#lmat value.                                   |
"|===========================================================================|
function! s:LineMatches(linenum) abort dict
	let l:text = getline(a:linenum)
	let l:text = substitute(l:text, '\m^\s*', '', '')
	let l:text = substitute(l:text, '\m\s*$', '', '')

	let l:iscomment = g:commentable#block#lmat_none

	let [l:initial, l:initmatch, l:medial, l:final] = [
		\ l:self.style.GetInitial(),
		\ l:self.style.GetInitMatch(),
		\ l:self.style.GetMedial(),
		\ l:self.style.GetFinal(),
		\ ]

	if type(l:self.initialmatch) ==# s:t_number
		let [l:str, l:start, l:end] = matchstrpos(l:text, l:initmatch)
		if l:start == 0
			let l:self.initialmatch = l:str
			let l:iscomment = g:commentable#block#lmat_int
		elseif ((l:final !=# '')                       &&
		 \      (strlen(l:text) >= strlen(l:final))    &&
		 \      (match(l:text, '\V' . l:final)
		 \       == strlen(l:text) - strlen(l:final)))   ||
		 \     ((l:medial !=# '')                      &&
		 \      (a:linenum > 1)                        &&
		 \      (strlen(l:text) >= strlen(l:medial))   &&
		 \      (match(l:text, '\V' . l:medial) == 0)  &&
		 \      (l:self.LineMatches(a:linenum - 1)))
			let l:iscomment = g:commentable#block#lmat_int
		endif
	elseif (match(l:text, '\V' . l:self.initialmatch) == 0)    ||<commit_msg>Don't use matchstrpos()<commit_after>"|===========================================================================|
"|                                                                           |
"|         FILE:  autoload/commentable/block.vim                             |
"|                                                                           |
"|  DESCRIPTION:  Block construction class.                                  |
"|                                                                           |
"|===========================================================================|

"|===========================================================================|
"|                            SCRIPT CONSTANTS                               |
"|===========================================================================|
let s:t_number = v:version >= 800 ? v:t_number : type(0)
let s:t_list   = v:version >= 800 ? v:t_list   : type([])
let s:t_string = v:version >= 800 ? v:t_string : type('')

let g:commentable#block#lmat_none = 0 " Not part of a comment
let g:commentable#block#lmat_int  = 1 " Internal comment line
let g:commentable#block#lmat_wall = 2 " Wall of a comment

"|===========================================================================|
"|                               CONSTRUCTOR                                 |
"|===========================================================================|

"|===========================================================================|
"| commentable#block#New(indentamount) abort {{{                             |
"|                                                                           |
"| Create a new block object. Only needs to know if it is indented or not;   |
"| the remaining parameters are determined in the constructor.               |
"|                                                                           |
"| PARAMS:                                                                   |
"|   indentamount) Is this an indented block?                                |
"|                                                                           |
"| Returns the block object.                                                 |
"|===========================================================================|
function! commentable#block#New(indentamount) abort
	let l:block = {
	 \ 'style': commentable#style#New(),
	 \ 'paragraphs': [],
	 \ 'AddParagraph': function('<SID>AddParagraph'),
	 \ 'AddExisting': function('<SID>AddExisting'),
	 \ 'LineMatches': function('<SID>LineMatches'),
	 \ 'GetFormat': function('<SID>GetFormat'),
	 \ '_GetRange': function('<SID>GetRange'),
	 \ 'initialmatch': 1,
	 \ }
	call l:block.style.SetIndented(a:indentamount)
	return l:block
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"|                              PUBLIC METHODS                               |
"|===========================================================================|

"|===========================================================================|
"| block.AddParagraph(para) abort dict {{{                                   |
"|                                                                           |
"| Add a new paragraph of text to this block.                                |
"|                                                                           |
"| PARAMS:                                                                   |
"|   para) The paragraph to add. Adds the exact object, not a copy.          |
"|                                                                           |
"| Returns nothing.                                                          |
"|===========================================================================|
function! s:AddParagraph(para) abort dict
	call add(l:self.paragraphs, a:para)
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"| block.AddExisting(linenum) abort dict {{{                                 |
"|                                                                           |
"| Add paragraphs from an existing block.                                    |
"|                                                                           |
"| PARAMS:                                                                   |
"|   linenum) A line in the exisiting paragraph. Will read forwards and      |
"|            backwards from this line to try to find the full paragraph.    |
"|                                                                           |
"| Returns [firstline, lastline], showing which lines were used. Throws if   |
"| the given line is not in the paragraph.                                   |
"|===========================================================================|
function! s:AddExisting(linenum) abort dict
	let [l:firstline, l:lastline] = l:self._GetRange(a:linenum)

	let [l:initmatch, l:medial, l:final, l:spacer] = [
		\ l:self.style.GetInitMatch(),
		\ l:self.style.GetMedial(),
		\ l:self.style.GetFinal(),
		\ l:self.style.GetSpacer(),
		\ ]

	let l:lines = getline(l:firstline, l:lastline)
	call map(l:lines, 'commentable#util#StripSpaces(v:val)')
	call map(l:lines, 'substitute(v:val, ''\V\^' . l:initmatch . ''', "", "")')
	call map(l:lines, 'substitute(v:val, ''\V' . l:final . '\$'', "", "")')

	if match(l:lines[0], '\V\^' . l:medial . '\*\$') == 0
		call remove(l:lines, 0)
	endif

	if len(l:lines) > 0                                    &&
	 \ match(l:lines[-1], '\V\^' . l:medial . '\*\$') == 0
		call remove(l:lines, -1)
	endif

	call map(l:lines,
	 \       'substitute(v:val, ''\V\^' . l:medial . ''', "", "")')

	if l:spacer !=# ''
		call map(l:lines,
		 \       'substitute(v:val, ''\V\^' . l:spacer . ''', "", "")')
		call map(l:lines,
		 \       'substitute(v:val, ''\V' . l:spacer . '\$'', "", "")')
	endif

	if len(l:lines) == 0
		call l:self.AddParagraph(commentable#paragraph#New(''))
	else
		let l:curpar = commentable#paragraph#New(l:lines[0])
		let l:lastlineblank = 0
		for l:line in l:lines[1:]
			if l:line =~# '\m^\s*$'
				call l:self.AddParagraph(l:curpar)
				let l:curpar = commentable#paragraph#New(l:line)
				let l:lastlineblank = 1
			elseif l:curpar.IsInParagraph(l:line) && (!l:lastlineblank)
				call l:curpar.AddLine(l:line)
			else
				call l:self.AddParagraph(l:curpar)
				let l:curpar = commentable#paragraph#New(l:line)
				let l:lastlineblank = 0
			endif
		endfor
		call l:self.AddParagraph(l:curpar)
	endif

	return [l:firstline, l:lastline]
endfunction
"|===========================================================================|
"| }}}                                                                       |
"|===========================================================================|

"|===========================================================================|
"| block.LineMatches(linenum) abort dict {{{                                 |
"|                                                                           |
"| Determine if a given existing line is valid for this block.               |
"|                                                                           |
"| PARAMS:                                                                   |
"|   linenum) The line to verify.                                            |
"|                                                                           |
"| Returns a commentable#block#lmat value.                                   |
"|===========================================================================|
function! s:LineMatches(linenum) abort dict
	let l:text = getline(a:linenum)
	let l:text = substitute(l:text, '\m^\s*', '', '')
	let l:text = substitute(l:text, '\m\s*$', '', '')

	let l:iscomment = g:commentable#block#lmat_none

	let [l:initial, l:initmatch, l:medial, l:final] = [
		\ l:self.style.GetInitial(),
		\ l:self.style.GetInitMatch(),
		\ l:self.style.GetMedial(),
		\ l:self.style.GetFinal(),
		\ ]

	if type(l:self.initialmatch) ==# s:t_number
		let l:str = matchstr(l:text, '\v^' . l:initmatch)
		if l:str !=# ''
			let l:self.initialmatch = l:str
			let l:iscomment = g:commentable#block#lmat_int
		elseif ((l:final !=# '')                       &&
		 \      (strlen(l:text) >= strlen(l:final))    &&
		 \      (match(l:text, '\V' . l:final)
		 \       == strlen(l:text) - strlen(l:final)))   ||
		 \     ((l:medial !=# '')                      &&
		 \      (a:linenum > 1)                        &&
		 \      (strlen(l:text) >= strlen(l:medial))   &&
		 \      (match(l:text, '\V' . l:medial) == 0)  &&
		 \      (l:self.LineMatches(a:linenum - 1)))
			let l:iscomment = g:commentable#block#lmat_int
		endif
	elseif (match(l:text, '\V' . l:self.initialmatch) == 0)    ||
{'fraction_non_alphanumeric': 0.29301321823577015, 'fraction_numerical': 0.003398974912328028, 'mean_word_length': 1.6288469720607006, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 153, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7295055', 'n_tokens_mistral': 4802, 'n_tokens_neox': 4161, 'n_words': 834}