Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Программы » SciTE - Open Source Text Editor for Windows & Linux

Модерирует : gyra, Maz

Widok (23-11-2010 11:23): Лимит страниц. Продолжаем здесь  Версия для печати • ПодписатьсяДобавить в закладки
На первую страницук этому сообщениюк последнему сообщению

   

TymurGubayev

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
--[[--------------------------------------------------
AutocompleteObject.lua
mozers™
version 2.03
revised edition by Tymur, 26.11.08
------------------------------------------------------
Ввод разделителя, заданного в autocomplete.[lexer].start.characters
вызывает список свойств и медодов объекта из соответствующего api файла
 
На данный момент нет:
Ввод пробела или разделителя изменяют регистр символов в имени объекта в соответствии с записью в api файле
(например "ucase" при вводе автоматически заменяется на "UCase")
 
Внимание: В скрипте используется функция IsComment (обязательно подключение COMMON.lua)
props["APIPath"] доступно только в SciTE-Ru
------------------------------------------------------
Inputting of the symbol set in autocomplete.[lexer].start.characters causes the popup list of properties and methods of input_object. They undertake from corresponding api-file.
In the same case inputting of a space or a separator changes the case of symbols in input_object's name according to a api-file.
(for example "ucase" is automatically replaced on "UCase".)
Warning: This script needed function IsComment (COMMON.lua)
props["APIPath"] available only in SciTE-Ru
------------------------------------------------------
Подключение:
В файл SciTEStartup.lua добавьте строку:
  dofile (props["SciteDefaultHome"].."\\tools\\AutocompleteObject.lua")
задайте в файле .properties соответствующего языка
символ, после ввода которого, будет включатся автодополнение:
  autocomplete.lua.start.characters=.:
------------------------------------------------------
Connection:
In file SciTEStartup.lua add a line:
  dofile (props["SciteDefaultHome"].."\\tools\\AutocompleteObject.lua")
Set in a file .properties:
  autocomplete.lua.start.characters=.:
------------------------------------------------------
Для понимания алгоритма работы скрипта, условимся, что в записи
azimuth:left;list-style-|type:upper-roman
где курсор стоит в позиции, отмеченной знаком "|", часть
list-style  - будет называться "объект"
type        - будет называться "метод"
-           - один из разделителей.
Все вышесказанное относится ко всем языкам программирования (css тут - только для примера)
Скрипт будет корректно работать только с "правильными" api файлами (см. описание формата в ActiveX.api)
------------------------------------------------------
Совет:
Если после ввода разделителя список свойств и методов не возник (хотя они описаны в api файле)
то, возможно, скрипт не смог распознать имя вашего объекта.
Помогите ему в этом, дописав такую строчку в редактируемый документ:
mydoc = document
где mydoc - имя Вашего объекта
document - имя этого же объекта, заданное в api файле
------------------------------------------------------
На что не хватило терпения:
1. Объединить функции CreateObjectsTable и CreateAliasTable в одну (чтобы обрабатывать api файлы за один проход)
(сделано)
2. Сделать вызов функций постоения таблиц более редким (сейчас они строются постоянно после ввода символа-разделителя)
(сделано)
3. Провести ревизию всех регулярных выражений (больше всего ошибок происходит из за них).
   Возможно паттерны стоит формировать динамически (сейчас функция fPattern не используется).
   (теперь fPattern один раз используется)
-----------------
Tymur:
    * переписал таблицы objects_table, alias_table из массивов строк в таблицы со строковыми ключами
    * попутно выполнил задачи 1 и 2 из списка выше
    + добавил фичу для луа: распознавание строковых переменных в коде (ассоциируются с таблицей string)
    + добавил в тестовом режиме слегка изменённый режим распознавания имён объектов. См. new_delim_behavior
    * FindDeclaration() ищет определения в соответствии с языком: допустимые символы берутся из $(word.characters.$(file.patterns.LANGUAGE))$(autocomplete.LANGUAGE.start.characters)
    * в целом скрипт должен работать быстрее (особенно после первого вызова) за счёт объединения выполнения старых CreateObjectsTable() и CreateAliasTable() за один проход, и только по необходимости (get_api == true)
 
    Проверено только для Луа, пока что работает
--]]----------------------------------------------------
 
local current_pos = 0    -- текущая позиция курсора, важно для InsertMethod
local sep_char = ''      -- введенный с клавиатуры символ (в нашем случае - один из разделителей ".:-")
local autocom_chars = '' -- паттерн, содержащий экранированные символы из параметра autocomplete.lexer.start.characters
local get_api = true     -- флаг, определяющий необходимость перезагрузки api файла
local api_table = {}     -- все строки api файла (очищенные от ненужной нам информации)
local objects_table = {} -- все "объекты", найденные в api файле в виде objects_table[objname]=true
local alias_table = {}   -- сопоставления "синоним = объект"
local methods_table = {} -- все "методы" заданного "объекта", найденные в api файле
local object_names = {}  -- все имена имеющихся api файле "объектов", которым соответствует найденный в текущем файле "объект"
-- Tymur:
local is_Lua = false     -- в Луа автоматически распознаются объекты как строки, если в файле есть строка вида obj = "". Трогать этот параметр не надо, он сам
local new_delim_behavior = true -- вкл./выкл. мою попытку сделать распознавание объектов с разделителями.
-- Т.е. если в .api-файле есть строчка вида "socket.dns.gethostname()", то в файле при вводе точки после "socket.dns" появится список с методом "gethostname".
-- внимание: есть два варианта, один дубовый, но надёжный, другой менее дубовый, но почему-то сбоит. По умолчанию включен второй. См. функцию GetInputObject.
local new_delim_behavior_better_buggy = false -- переключатель. Если true, то используется метод, который может не работать из-за (предположительно) бага SciTE.
------------------------------------------------------
 
-- Тест для распечатки содержимого заданной таблицы
local function prnTable(name)
    print('> ________________')
    for i = 1, #name do
        print(name[i])
    end
    print('> ^^^^^^^^^^^^^^^')
end
 
-- Преобразовывает стринг в паттерн для поиска
--[[local function fPattern(str)
    local str_out = ''
    for i = 1, string.len(str) do
        str_out = '%'..string.sub(str, i, i+1)
    end
    return str_out
end]]
-- Долго медитировал над вышеприведенным кодом... Может, имелось в виду так:
local function fPattern(str)
    -- паттерн для ловли управляющих паттернами символов Луа:
    local lua_patt_chars = "[%(%)%.%+%-%*%?%[%]%^%$]"
    -- return str:gsub('.','%%%0') -- можно конечно и так, но заэскейпить всё подряд - некошерно.
    return str:gsub(lua_patt_chars,'%%%0')
end
 
-- Сортирует таблицу по алфавиту и удаляет дубликаты
local function TableSort(table_name)
    table.sort(table_name, function(a, b) return string.upper(a) < string.upper(b) end)
    -- remove duplicates
    for i = #table_name-1, 0, -1 do
        if table_name[i] == table_name[i+1] then
            table.remove (table_name, i+1)
        end
    end
    return table_name
end
 
------------------------------------------------------
 
-- Извлечение из api-файла реальных имен объектов, которые соответствуют введенному
-- т.е. введен "объект" wShort, а методы будем искать для WshShortcut и WshURLShortcut
local function GetObjectNames(text)
    local obj_names = {}
    -- Поиск по таблице имен "объектов"
    if objects_table[text] then
        obj_names[#obj_names+1] = text
        return obj_names -- если успешен, то завершаем поиск
    end
    -- Поиск по таблице сопоставлений "объект - синоним"
    if alias_table[text] then
        for k,_ in pairs(alias_table[text]) do obj_names[#obj_names+1] = k end
    end
    -- prnTable(obj_names)
    return obj_names
end
 
--================================================================
local GetInputObject -- дальше будет if-block, так что для правильной области видимости декларируем тут.
if not new_delim_behavior then
-- Старый метод:
    -- всю работу делает editor:WordStartPosition(pos) в сооответствии со своим стандартным поведением:
    --  ищет ближайший слева от pos символ НЕ из "word.characters.$(file.patterns.LANGUAGE)
    
    -- декларация на сей раз без local, чтобы переписать переменную, декларированную чуть выше
    function GetInputObject()
        return editor:textrange(editor:WordStartPosition(current_pos-1),current_pos-1)
    end
else -- новый метод работы:
 
    -- Извлечение из текущего файла имени объекта, с которым "работаем":
    --  движемся по словам влево от курсора по не закончаться разделители (для Луа: ".:")
    --  т.о. корректно распознаётся для aa.bb.cc ввод точки после aa.bb
    -- Реализовано через танцы с бубном, чтобы в других местах не считалась за переменную фигня вроде "..bumsbums"
    
    if new_delim_behavior_better_buggy then
        -- local здесь НЕЛЬЗЯ, ибо внутри if
        function GetInputObject(delimiters)
            -- это менее дубовый вариант.
            -- нужный нам набор символов вытаскиваем из соотв. настройки
            local word_sett = "word.characters.$(file.patterns."..editor.LexerLanguage..")"
            -- чтобы не потерять
            local tmp = props[word_sett]
            -- @todo: Вообще-то, эти две переменные нужно перегружать только при api_get == true, но это будет в финальном релизе "нового" метода, если оно кому надо.
            
            -- добавляем разделители -- это теперь тоже часть слова
            props[word_sett] = props[word_sett]..(delimiters or "")
            -- пусть за нас сделает всю работу editor:WordStartPosition
            local word_start_pos = editor:WordStartPosition(current_pos-1)
            -- возвращаем настройки назад
            props[word_sett] = tmp
            
            return editor:textrange(word_start_pos,current_pos-1)
        end -- GetInputObject, менее дубовый, более глючный вариант.
        
    else -- better not so buggy
        
        -- альтернативный, тот самый более дубовый вариант.
        -- надёжен на все 100. Я надеюсь.
        function GetInputObject(delimiters)
            local delimiters = delimiters or ''
            local word_start_pos = current_pos
            -- Делаем до упора:
            while true do
                -- получаем начало текущего слова
                word_start_pos = editor:WordStartPosition(word_start_pos-1)
                -- смотрим на один символ левее
                local word_start_char = editor:textrange(word_start_pos-1,word_start_pos)
                -- Символ есть в delimiters?..
                if string.find(delimiters, word_start_char, 1, 1) then
                    -- да: ещё шаг влево, повторить сначала.
                    word_start_pos = word_start_pos -1
                else
                    -- нет: закончили
                    -- print(word_start_pos, current_pos)
                    return editor:textrange(word_start_pos,current_pos-1)
                end
            end -- while true
        end --func GetInputObject, более дубовый, зато надёжный вариант.
        
    end -- if изящный, но багнутый метод.
end -- if new_delim_behavior
--================================================================
 
-- adds alias to the "global" alias_table
local function AddAlias(obj, alias)
    -- если впервые такое слово, создаём таблицу
    alias_table[obj] = alias_table[obj] or {}
    -- добавляем синоним alias к объекту obj
    alias_table[obj][alias] = true
end --func AddAlias
 
-- Поиск деклараций присвоения пользовательской переменной реального объекта
-- т.е. в текущем файле ищем конструкции вида "синоним = объект"
local function FindDeclaration()
    local text_all = editor:GetText()
    local _start, _end, sVar, sRightString
    -- берём то, что хранится в, например, word.characters.$(file.patterns.lua)
    local wordpatt = '['..props["word.characters.$(file.patterns."..editor.LexerLanguage..")"]..autocom_chars..']+'
 
    -- @todo: правую часть также хорошо бы слегка поправить.
    local pattern = '('..wordpatt..')%s*=%s*(%C+)'
    _start = 1
    while true do
        _start, _end, sVar, sRightString = string.find(text_all, pattern, _start)
        if _start == nil then break end
        if sRightString ~= '' then
            -- анализируем текст справа от знака "="
            --@todo: Анализ на данный момент _очень_ грубый
            -- Не строка ли это?
            if is_Lua and (sRightString:match([[^%s*".*"]]) or sRightString:match([[^%s*'.*']]) or sRightString:match("^%s*%[%[.*%]%]")) then
                AddAlias(sVar, 'string')
            -- (проверяем, объект ли там содержится)
            else
                for sValue in string.gmatch(sRightString, wordpatt) do
                    -- print('sValue = "'..sValue..'"')
                    local objects = GetObjectNames(sValue)
                    for i = 1, #objects do
                        if objects[i] ~= '' then
                            -- print(objects[i])
                            -- если действительно, такой "объект" существует, то добавляем его в таблицу сопоставлений "объект - синоним"
                            AddAlias(sVar, objects[i])
                            break
                        end
                    end
                end
            end -- if is_Lua
        end
        _start = _end + 1
    end
end
 
-- Чтение api файла в таблицу api_table (чтобы потом не опрашивать диск, а все тащить из нее)
local function CreateAPITable()
    api_table = {}
    for api_filename in string.gmatch(props["APIPath"], "[^;]+") do
        if api_filename ~= '' then
            local api_file = io.open(api_filename)
            if api_file then
                for line in api_file:lines() do
                    -- обрезаем комментарии
                    line = line:match('^[^%s%(]+')
                    if line ~= '' then
                        api_table[#api_table+1] = line
                    end
                end
                api_file:close()
            else
                api_table = {}
            end
        end
    end
    get_api = false
    return false
end
 
-- Создание таблицы, содержащей все имена "объектов" описанных в api файле
-- Создание таблицы, содержащей все сопоставления "#синоним = объект" описанные в api файле
local function CreateObjectsAndAliasTables()
    objects_table = {}
    alias_table = {}
    for i = 1, #api_table do
        local line = api_table[i]
        -- здесь КРАЙНЕ ВАЖНО, чтобы в матче был именно [autocom_chars], т.е. например "[.:]" для Луа
        -- т.к. эта таблица строится только при api_get, может выйти фигня.
        local obj_name = line:match('^([^#]+)['..autocom_chars..']')
        if obj_name then objects_table[obj_name]=true end
        -- для строк вида "#a=b" записываем a,b поочерёдно в таблицу алиасов
        local sVar, sValue = line:match('^#(%w+)=([^%s]+)$') --@todo: подумать над паттерном...
        if sVar then
            AddAlias(sValue, sVar)
        end
    end
    -- for k in pairs(objects_table) do print(k) end
end
 
-- Создание таблицы "методов" заданного "объекта"
local function CreateMethodsTable(obj)
    for i = 1, #api_table do
        local line = api_table[i]
        -- ищем строки, которые начинаются с заданного "объекта"
        local _, _end = string.find(line, obj..sep_char, 1, 1)
        if _end ~= nil then
            -- ^%[ нужно для Луа, в api-файле может быть строчка вида: t["a-b+c"]\nэто очень хитрый параметр
            -- для стандартных библиотек неважно.
            local _start, _end, str_method = string.find(line, '([^%s%.%:%[%-]+)', _end)
            if _start ~= nil then
                methods_table[#methods_table+1] = str_method
            end
        end
    end
end
 
-- Показываем раскрывающийся список "методов"
local function ShowUserList()
-- prnTable(methods_table)
    if #methods_table == 0 then return false end
    local s = table.concat(methods_table, " ")
    if s == '' then return false end
    editor:UserListShow(7, s)
    return true
end
 
-- Вставляет выбранный из раскрывающегося списка метод в редактируемую строку
local function InsertMethod(str)
    -- первый параметр в тексте утанавливается лишь однажды
    editor:SetSel(current_pos, editor.CurrentPos)
    editor:ReplaceSel(str)
end
 
-- ОСНОВНАЯ ПРОЦЕДУРА (обрабатываем нажатия на клавиши)
local function AutocompleteObject(char)
    if IsComment(editor.CurrentPos-2) then return false end  -- Если строка закомментирована, то выходим
 
    local autocomplete_start_characters = props["autocomplete."..editor.LexerLanguage..".start.characters"]
    -- Если введенного символа нет в параметре autocomplete.lexer.start.characters, то выходим
    if autocomplete_start_characters == '' then return false end
    if string.find(autocomplete_start_characters, char, 1, 1) == nil then return false end
    -- Наконец то мы поняли что введенный символ - именно тот разделитель!
    sep_char = char
    autocom_chars = fPattern(autocomplete_start_characters)
 
    -- а не в Луа ли мы часом?
    is_Lua = (editor.LexerLanguage == 'lua')
    
    if get_api then
        -- print('get_api = true')
        CreateAPITable()
        CreateObjectsAndAliasTables()
    end
    -- если в api_table пусто - выходим.
    if not next(api_table) then return false end
 
    FindDeclaration()
    -- prnTable(objects_table)
    -- prnTable(alias_table)
 
    -- Важно: запоминаем текщую позицию курсора (Иначе "string.b|[Enter]" превратиться в "string.bbyte")
    current_pos = editor.CurrentPos
    -- Берем в качестве объекта слово слева от курсора
    local input_object = GetInputObject(autocomplete_start_characters)
    -- local input_object = editor:textrange(editor:WordStartPosition(current_pos-1),current_pos)
    -- print('AutocompleteObject: input_object = ',input_object)
    
    -- Если слева от курсора отсутствует слово, которое можно истолковать как имя объекта, то выходим
    if input_object == '' then return '' end
    -- print(input_object)
    
    object_names = GetObjectNames(input_object)
    if not next(object_names) then return false end
    -- prnTable(object_names)
    
    -- убиваем остатки старых методов, заполняем новыми
    methods_table = {}
    for i = 1, #object_names do
        CreateMethodsTable(object_names[i])
    end
    methods_table = TableSort(methods_table)
    -- prnTable(methods_table)
    return ShowUserList()
end
 
------------------------------------------------------
 
-- Add user event handler OnChar
local old_OnChar = OnChar
function OnChar(char)
    local result
    if old_OnChar then result = old_OnChar(char) end
    if props['macro-recording'] ~= '1' and AutocompleteObject(char) then return true end
    return result
end
 
-- Add user event handler OnUserListSelection
local old_OnUserListSelection = OnUserListSelection
function OnUserListSelection(tp,sel_value)
    local result
    if old_OnUserListSelection then result = old_OnUserListSelection(tp,sel_value) end
    if tp == 7 then
        if InsertMethod(sel_value) then return true end
    end
    return result
end
 
-- Add user event handler OnSwitchFile
local old_OnSwitchFile = OnSwitchFile
function OnSwitchFile(file)
    local result
    if old_OnSwitchFile then result = old_OnSwitchFile(file) end
    get_api = true
    return result
end
 
-- Add user event handler OnOpen
local old_OnOpen = OnOpen
function OnOpen(file)
    local result
    if old_OnOpen then result = old_OnOpen(file) end
    get_api = true
    return result
end
 
-- Add user event handler OnBeforeSave
local old_OnBeforeSave = OnBeforeSave
function OnBeforeSave(file)
    local result
    if old_OnBeforeSave then result = old_OnBeforeSave(file) end
    get_api = true
    return result
end

Всего записей: 35 | Зарегистр. 24-11-2008 | Отправлено: 00:55 27-11-2008
   

На первую страницук этому сообщениюк последнему сообщению

Компьютерный форум Ru.Board » Компьютеры » Программы » SciTE - Open Source Text Editor for Windows & Linux
Widok (23-11-2010 11:23): Лимит страниц. Продолжаем здесь


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru