-- SciTE Smart comment
-- Version: 2.7
-- Author: Dmitry Maslov
---------------------------------------------------
-- Веделяем текст нажимаем на клавиатуре символ 
-- с которого начинается комментарий и строка комментируется
-- пример: выделить строку в cpp, нажать на клавишу * или /
---------------------------------------------------
-- Версия 2.7
-- Убраны части связанные с автозакрытием скобок
---------------------------------------------------
-- Версия 2.3
-- Скрипт сделан полностью автономным
---------------------------------------------------
-- Версия 2.2
-- Исправлен баг, если нажали не печатный символ
-- то скрипт вел себя не предсказуемо
---------------------------------------------------
-- Версия 2.1
-- Закрытие блочным комментирием во всех языках по первому 
-- символу блочного комментария во как загнул ;) 
-- Благодарность mozers за подсказку в реализации.
---------------------------------------------------
-- Версия 1.0
-- Обработка комментариев в cpp * и / (/**/ и //~ )
-- Обработка коментариев в lua - ( -- )
-- Обработка скобок в lua [ ( [[...]] )
-- Обработка комментария в props # ( #~ )
---------------------------------------------------

-- Возвращает текущий символ перевода строки
local function GetEOL()
	local eol = "\r\n"
	if editor.EOLMode == SC_EOL_CR
	then
		eol = "\r"
	elseif editor.EOLMode == SC_EOL_LF
	then
		eol = "\n"
	end
	return eol
end

-- сделать текст шаблоном для поиска
-- (фактически экранирование служебных символов)
local function MakeFind( text )
	local strres = ''
	local simbol
	for i = 1, string.len(text), 1
	do
		simbol = string.format( '%c', string.byte( text, i ) )
		if	( simbol == "(" )
			or
			( simbol == "[" )
			or
			( simbol == "." )
			or
			( simbol == "%" )
			or
			( simbol == "*" )
			or
			( simbol == "/" )
			or
			( simbol == "-" )
			or
			( simbol == ")" )
			or
			( simbol == "]" )
			or
			( simbol == "?" )
			or
			( simbol == "+" )
		then
			simbol = string.format( "%%%s", simbol )
		end
		strres = strres..simbol
	end
	return strres
end

-- предыдущий символ позиции конец строки?
local function prevIsEOL(pos)
	if string.find(editor:textrange(editor:PositionBefore(pos-string.len(GetEOL())+1), pos),GetEOL())
	then
		return true
	end
	return false
end

-- последний символ в строке - конец строки?
local function IsEOLlast(text)
	-- в луа конец строки всегда один символ
	if string.find(text,GetEOL(),string.len(text)-1)
	then
		return true
	end
	return false
end

local function StrimComment(commentbegin, commentend)
	local text, lenght = editor:GetSelText()
	local selbegin = editor.SelectionStart
	local selend = editor.SelectionEnd
	local b,e = string.find(text, MakeFind(commentbegin))
	if (e and (string.byte(text, e+1) == 10 or string.byte(text, e+1) == 13))
	then
		e=e+1 
	end
	if (e and (string.byte(text, e+1) == 10 or string.byte(text, e+1) == 13))
	then
		e=e+1 
	end
	local b2,e2
	if IsEOLlast(text)
	then
		b2,e2 = string.find(text, MakeFind(commentend), 
			string.len(text)-string.len(commentend)-string.len(GetEOL()))
	else
		b2,e2 = string.find(text, MakeFind(commentend), 
			string.len(text)-string.len(commentend))
	end
	if (b2 and (string.byte(text, b2-1) == 10 or string.byte(text, b2-1) == 13))
	then
		b2=b2-1 
	end
	if (b2 and (string.byte(text, b2-1) == 10 or string.byte(text, b2-1) == 13))
	then
		b2=b2-1 
	end
	editor:BeginUndoAction()
	if (b and b2)
	then
		local add=''
		if (string.find(text,GetEOL(),string.len(text)-string.len(GetEOL())))
		then
			add = GetEOL()
		end
		text = string.sub(text,e+1,b2-1)
		editor:ReplaceSel(text..add)
		editor:SetSel(selbegin, selbegin+string.len(text..add))
	else
		if (editor:LineFromPosition(selend)==editor:LineFromPosition(selbegin))
		then
			editor:insert(selend, commentend)
			editor:insert(selbegin, commentbegin)
			editor:SetSel(selbegin, selend+string.len(commentbegin)+string.len(commentend))
		else
			local eolcount = 0
			if (prevIsEOL(selend))
			then
				editor:insert(selend, commentend..GetEOL())
				eolcount = eolcount + 1
			else
				editor:insert(selend, commentend)
			end
			if (prevIsEOL(selbegin))
			then
				editor:insert(selbegin, commentbegin..GetEOL())
				eolcount = eolcount + 1
			else
				editor:insert(selbegin, commentbegin)
			end
			editor:SetSel(selbegin, selend+string.len(commentbegin)+string.len(commentend)+string.len(GetEOL())*eolcount)
		end
	end
	editor:EndUndoAction()
	return true
end

local function BlockComment()
	local selbegin = editor.SelectionStart
	editor:BeginUndoAction()
	if (string.find(editor:textrange(selbegin-string.len(GetEOL()), selbegin),GetEOL()))
	then
		scite.MenuCommand(IDM_BLOCK_COMMENT)
		editor:SetSel(selbegin, editor.SelectionEnd)
	else
		scite.MenuCommand(IDM_BLOCK_COMMENT)
		editor:SetSel(editor.SelectionStart, editor.SelectionEnd)
	end
	editor:EndUndoAction()
	return true
end

local function GetIndexFindCharInProps(value, findchar)
	if findchar
	then
		local resIndex = string.find(props[value], MakeFind(findchar), 1)
		if (resIndex~=nil) and (string.sub(props[value],resIndex,resIndex) == findchar)
		then
			return resIndex
		end
	end
	return nil
end

local function SmartComment(char)
	if (editor.SelectionStart~=editor.SelectionEnd)
	then
		-- делаем индивидуальную обработку по лексерам
		if (editor.LexerLanguage == 'cpp')
		then
			if (char == '*' ) then return StrimComment('/*', '*/') end
		end
		-- делаем проверку на блочный комментарий
		if GetIndexFindCharInProps('comment.block.'..editor.LexerLanguage, char) == 1
		then
			return BlockComment()
		end
	end

	return false
end

-- Add user event handler OnKey
local old_OnKey = OnKey
function OnKey(key, shift, ctrl, alt, char)
	if old_OnKey and old_OnKey(key, shift, ctrl, alt, char)
	then
		return true
	end
	if editor.Focus and char~='' and SmartComment(char)
	then
		return true
	end

	return false
end
