-- TIME_STAMP 2021-04-20 11:48:20 -- coding:utf-8 --[[ PROPERTIES: # The only way to detect the UTF8 encoding is using a coding cookie in one of the 1st two lines. # line to insert the utf8-cookie (1 or 2), default: 1 UTF8.Cookie.Line= # file types, that needs this codings # 'utf8' - as default declared: 'lua' # 'utf8bom' - as default declared: 'au3' UTF8.File.Types=ext_1 ext_2 UTF8BOM.File.Types=ext_1 ext_2 UTF16LE.File.Types=ext_1 ext_2 UTF16BE.File.Types=ext_1 ext_2 ]] ForceEncoding = EventClass:new(Common) ForceEncoding.OnSave = function(self, _file) local enc = self:NeedsEncoding(props['FileExt']) if enc == 'NONE' then return nil end if not (self:Get(_file) == enc) then -- file has not the required encoding if enc == 'UTF8' then -- IDM_ENCODING_UCOOKIE 154 UTF8 (not save without real text cookie!) self:InsertUTF8Cookie() scite.MenuCommand(IDM_ENCODING_UCOOKIE) else local tCmd = {['UTF8BOM']=IDM_ENCODING_UTF8,['UTF16LE']=IDM_ENCODING_UCS2LE,['UTF16BE']=IDM_ENCODING_UCS2BE} scite.MenuCommand(tCmd.enc) end end return nil end -- inserts the coding cookie in line 1 or 2 ForceEncoding.InsertUTF8Cookie = function() local l = tonumber(props['UTF8.Cookie.Line']) or 1 -- if prop not set use 1st line if l > 2 then l = 2 end -- last possible line for this cookie local countLines = editor.LineCount if countLines < l then l = 1 end -- file is empty or has only one line and cookie prop is set to 2 local lineComment = props['comment.block.'..props['FileExt']] l = l -1 -- 0-based line index local caret = editor.CurrentPos local firstVisibleLine = editor.FirstVisibleLine local pos = editor:PositionFromLine(l) -- start position from line editor:InsertText(pos, lineComment..' coding:utf-8\n') editor:SetSel(caret, caret) editor.FirstVisibleLine = firstVisibleLine end -- returns the encoding, that the file type needs ForceEncoding.NeedsEncoding = function(self, _ext) local tExt = {} tExt["UTF8"] = props['UTF8.File.Types']:lower()..' lua' tExt["UTF8BOM"] = props['UTF8BOM.File.Types']:lower()..' au3' tExt["UTF16LE"] = props['UTF16LE.File.Types']:lower() tExt["UTF16BE"] = props['UTF16BE.File.Types']:lower() for enc in pairs(tExt) do if tExt[enc]:find(_ext:lower()) then return enc end end return 'NONE' end -- detects: 'UTF8BOM', 'UTF16LE', 'UTF16BE' by it's byte marker (from file) -- and 'UTF8' if coding cookie is set on one of the first two lines: (from editor pane) -- A coding cookie looks similar to "coding: utf-8" ("coding" followed by ':' or '=', optional whitespace, optional quote, "utf-8"). ForceEncoding.Get = function(self, _file) local tEnc = {} tEnc['UTF8BOM'] = {['len']=3,['hex']="EFBBBF"} tEnc['UTF16LE'] = {['len']=2,['hex']="FFFE"} tEnc['UTF16BE'] = {['len']=2,['hex']="FEFF"} local n, line, fh, content, read = 0 -- 1st. check for coding cookie (UTF8) for i=0, 1 do line = editor:GetLine(i) if line == nil then break else if line:find("coding[:=]%s-['\"]-utf%-8") then return "UTF8" end end end -- now check for byte marker for enc in pairs(tEnc) do if self:ToHex(self:ReadLen(_file, tEnc[enc].len)) == tEnc[enc].hex then return enc end end return 'UNKNOWN' end -- reads a count of bytes from a file ForceEncoding.ReadLen = function(self, _file, _n) local fh = io.open(_file, "rb") local read = fh:read(_n) fh:close() return read end -- converts a passed string to hex value of its characters ForceEncoding.ToHex = function(self, _s) if _s == nil then return 'DEAD' end return (_s:gsub('.', function(_c) return ('%02X'):format(_c:byte()) end)) end