Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Vim-крокет

Anna | 15.06.2014 | нет комментариев
Переводчик из меня абсолютно никакой, но я легко не мог пройти мимо этой статьи, потому как она излучает волны крутости, а насыщенность дзена в ней зашкаливает. Следственно welcome.

Вступление


Незадолго я нашел увлекательную игру под наименованием VimGolf. Цель этой игры заключается в том, Дабы преобразовать кусок текста из одной формы в иную наименьшим допустимым числом нажатий клавиш. Пока я играл на этом сайте с различными пазлами, мне стало любознательно — а какие повадки редактирования текста есть у меня? Мне захотелось отменнее осознать методы манипулирования текстом в Vim и проверить, сумею ли я обнаружить неэффективные моменты в моем рабочем процессе. Я провожу большое число времени в моем текстовом редакторе, следственно устранение даже незначительных шероховатостей может привести к существенному увеличению продуктивности. В этом посте я расскажу о своем обзоре и о том, как я уменьшил число нажатий клавиш при применении Vim. Я назвал эту игру Vim-крокет.

Сбор данных

Я начал мой обзор со сбора данных. Редактирование текста на моем компьютере неизменно происходит с поддержкой Vim, так что в течении 45 дней я логировал всякое нажание клавиши в нем с поддержкой флага scriptout. Для комфорта я сделал alias для записи нажатий в лог:

alias vim='vim -w ~/.vimlog "$@"'

Позже этого нужно было распарсить полученные данные, но это оказалось не так легко. Vim это модальный редактор, в котором одна команда может иметь несколько разных значений в различных режимах. Помимо этого команды зависят от контекста, когда их поведение может отличаться в зависимости от того, где внутри буфера vim они исполняются. Скажем, команда cib в типичном режиме переведет пользователя в режим редактирования, если команда выполняется внутри скобок, но оставит пользователя в типичном режиме, если она исполнена вне скобок. Если же cib будет исполнена в режиме редактирования, то она будет иметь абсолютно другое поведение — запишет символы «cib» в нынешний буфер.

Я разглядел несколько кандидатов для парсинга команд vim, включая индустриальные библиотеки, такие какantler и parsec, а также специализирующийся на vim план vimprint. Позже некоторых раздумий, я решил написать личный инструмент, т.к. трата большого числа времени на постижение довольно трудных парсеров казалось необоснованным для этой задачи.

Я написал сыроватый лексер на haskell’е для разбиения собранных мною нажатий клавиш на индивидуальные команды vim. Мой лексер использует monoids для извлечения команд типичного режима из лога для последующего обзора. Вот исходник лексера:

import qualified Data.ByteString.Lazy.Char8 as LC
import qualified Data.List as DL
import qualified Data.List.Split as LS
import Data.Monoid
import System.IO

main = hSetEncoding stdout utf8 >> 
       LC.getContents >>= mapM_ putStrLn . process

process =   affixStrip 
          . startsWith 
          . splitOnMode
          . modeSub
          . capStrings 
          . split mark 
          . preprocess

subs = appEndo . mconcat . map (Endo . sub)

sub (s,r) lst@(x:xs)
    | s `DL.isPrefixOf` lst = sub'
    | otherwise = x:sub (s,r) xs
    where
        sub' = r    sub (s,r) (drop (length s) lst)
sub (_,_) [] = []

preprocess =   subs meta 
             . DL.intercalate " "
             . DL.words
             . DL.unwords
             . DL.lines 
             . LC.unpack

splitOnMode = DL.concat $ map (\el -> split mode el)

startsWith = filter (\el -> mark `DL.isPrefixOf` el && el /= mark)

modeSub = map (subs mtsl)

split s r = filter (/= "") $ s `LS.splitOn` r

affixStrip =   clean 
             . concat 
             . map (\el -> split mark el)

capStrings = map (\el -> mark    el    mark)

clean = filter (not . DL.isInfixOf "[M")

(mark, mode, n) = ("-(*)-","-(!)-", "")
meta = [("\"",n),("\\",n),("\195\130\194\128\195\131\194\189`",n),
        ("\194\128\195\189`",n),("\194\128kb\ESC",n), 
        ("\194\128kb",n),("[>0;95;c",n), ("[>0;95;0c",n),
        ("\ESC",mark),("\ETX",mark),("\r",mark)]
mtsl = [(":",mode),("A",mode), ("a",mode), ("I",mode), ("i",mode),
        ("O",mode),("o",mode),("v", mode),("/",mode),("\ENQ","лавиш. Такие команды дозволено было бы заменить с поддержкой shortcut'ов, которые бы ускорили их выполнение. В качестве меры трудности команд я применял энтропию, которую измерял дальнейшим коротким скриптом на Python:

#!/usr/bin/env python
import sys
from codecs import getreader, getwriter
from collections import Counter
from operator import itemgetter
from math import log, log1p

sys.stdin = getreader('utf-8')(sys.stdin)
sys.stdout = getwriter('utf-8')(sys.stdout)

def H(vec, correct=True):
    """Calculate the Shannon Entropy of a vector
    """
    n = float(len(vec))
    c = Counter(vec)
    h = sum(((-freq / n) * log(freq / n, 2)) for freq in c.values())

    # impose a penality to correct for size
    if all([correct is True, n > 0]):
        h = h / log1p(n)

    return h

def main():
    k = 1
    lines = (_.strip() for _ in sys.stdin)
    hs = ((st, H(list(st))) for st in lines)
    srt_hs = sorted(hs, key=itemgetter(1), reverse=True)
    for n, i in enumerate(srt_hs[:k], 1):
        fmt_st = u'{r}\t{s}\t{h:.4f}'.format(r=n, s=i[0], h=i[1])
        print fmt_st

if __name__ == '__main__':
    main()

 Скрипт читает из стандартного потока ввода и выдает команды с наибольшей энтропией. Я применял итог лексера в качестве данных для расчета энтропии:

$ sort normal_cmds.txt | uniq -c | sort -nr | sed "s/^[ \t]*//" | \
    awk 'BEGIN{OFS="\t";}{if ($1>100) print $1,$2}' | \
    cut -f2 | ./entropy.py

1 ggvG$"zy 1.2516

 Я отбираю команды, которые выполнялись больше 100 раз, а после этого нахожу среди них команду с наибольшей энтропией. В итоге обзора была выделена команда ggvG$«zy, которая выполнялась 246 раз за 45 дней. Команда выполняется с поддержкой 11 довольно неуклюжих нажатий клавиш и копирует каждый нынешний буфер в регистр z. Я традиционно использую это команду для перемещения каждого содержимого одного буфера в иной. Безусловно, добавил в свой конфиг новейший shortcut

nnoremap <leader>ya ggvG$"zy
 

Итоги


 Мой матч в vim-крокет определил 3 оптимизации для уменьшения числа нажатий клавиш в vim:
  • Применение команд навигации ^U ^D взамен ^E ^Y
  • Предотвращение механического сворачивания текста в буфере для избежания zR
  • Создание shortcut’а для многословной команды ggvG$»zy

 Эти 3 примитивных метаморфозы спасли меня от тысяч непотребных нажатий клавиш всякий месяц.

 Части кода, которые я представил выше, немножко изолированы и могут быть трудны для применения. Дабы сделать шаги моего обзора внятнее, я привожу Makefile, тот, что показывает, как код, содержащийся в моей статье, совмещается в цельное целое.

SHELL           := /bin/bash
LOG             := ~/.vimlog
CMDS            := normal_cmds.txt
FRQS            := frequencies.txt
ENTS            := entropy.txt
LEXER_SRC       := lexer.hs
LEXER_OBJS      := lexer.{o,hi}
LEXER_BIN       := lexer
H               := entropy.py
UTF             := iconv -f iso-8859-1 -t utf-8

.PRECIOUS: $(LOG)
.PHONY: all entropy clean distclean

all: $(LEXER_BIN) $(CMDS) $(FRQS) entropy

$(LEXER_BIN): $(LEXER_SRC)
    ghc --make $^

$(CMDS): $(LEXER_BIN)
    cat $(LOG) | $(UTF) | ./$^ > $@

$(FRQS): $(H) $(LOG) $(CMDS)
    sort $(CMDS) | uniq -c | sort -nr | sed "s/^[ \t]*//" | \
      awk 'BEGIN{OFS="\t";}{if ($$1>100) print NR,$$1,$$2}' > $@

entropy: $(H) $(FRQS)
    cut -f3 $(FRQS) | ./$(H)

clean:
    @- $(RM) $(LEXER_OBJS) $(LEXER_BIN) $(CMDS) $(FRQS) $(ENTS)

distclean: clean

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB