Программист часто копирует и вставляет, переименовывает и рефакторит. Выделил (подсветил) мышкой переменную или функцию и вот бы сразу видеть их количество в статусной строке. Увы, стандартный поиск (Ctrl+F) требует лишние клики.

Мой небольшой Python-скрипт для Notepad++ по дабл-клику
отображает в Status-Bar количество вхождений,
частичных или полных, с учетом регистра и без.

Диклеймер: Даже писателю доставляет наслаждение от удовольствия нагромождать многократное повторение одних и тех же тавтологий по нескольку раз, используя сильно ограниченный набор слов из заданного словаря предыдущего набора многократный повторений одних и тех слов /s.

Есть ли альтернативы?

Три неудобных способа:
  • Notepad++ умеет подсвечивать выделенный фрагмент текста при двойном клике, но а сколько раз это встречается в файле? Чтобы увидеть заветную цифру, нужно нажать Ctrl+F, а затем кнопку Count (или Mark All).

  • NppTextFX2 плагин выводит статистику в отдельное окно, но появившееся окно модальное, его каждый раз нужно закрывать, чтобы вернуться к коду.

  • Еще есть такой же Summary Feature.

Страничка , описывающие все три способа с картинками.

Подготовка.

установить плагин Python Script

С помощью Python Script можно производить любые операции с текстом, переключать вкладки и управлять интерфейсом Notepad++.

  1. Откройте Plugins -> Plugins Admin ..

  2. Найдите и установите Python Script. Как установить и запустить скрипт в Python Script?

  3. После перезапуска Notepad++, выберите Plugins -> Python Script -> New Script.

Код скрипта.

Выбрав в меню Notepad++ Plugins -> Python Script -> New Script, вставляем код скрипта:

код скрипта SmartHighlightingCounterInStatusBar.py
# -*- coding: utf-8 -*-

from Npp import editor, notepad, SCINTILLANOTIFICATION, UPDATE, STATUSBARSECTION
import re

# Scintilla (the engine behind Notepad++)

# 1. Define Task A
def get_selection_count():
    if editor.getSelections() != 1 or editor.getSelectionEmpty():
        return ""

    try:
        content = editor.getText().decode('utf-8')
        selected = editor.getSelText().decode('utf-8')
        patt_p = re.escape(selected)
        patt_w = ur"\b" + patt_p + ur"\b"

        matches_p = re.findall(patt_p, content, re.UNICODE | re.IGNORECASE)
        matches_w = re.findall(patt_w, content, re.UNICODE | re.IGNORECASE)

        ip = len(matches_p)
        iw = len(matches_w)
        # сколько из найденных совпали с оригиналом по регистру
        sp = sum(1 for m in matches_p if m == selected)
        sw = sum(1 for m in matches_w if m == selected)

        return "w[≡{},∀{}], p…≡{},∀{}…".format(sw, iw, sp, ip)
    except Exception as e:
        return str(e) + " - Err(exc)"
    return "" # "nothing selected" is might flicker or look like "clutter"

# 2. Define Task B
# def get_other_script_info():
#    return "OtherInfo"

# 3. Master Callback that manages the Status Bar
def master_status_callback(args):
    if not (args['updated'] & (UPDATE.SELECTION | UPDATE.CONTENT)):
        return
    match_info = get_selection_count()
    notepad.setStatusBar(STATUSBARSECTION.DOCSIZE, match_info)
    # other_info = get_other_script_info()

if 'MY_MASTER_STATUS_LOADED' not in globals():
    editor.callback(master_status_callback, [SCINTILLANOTIFICATION.UPDATEUI])
    MY_MASTER_STATUS_LOADED = True
    print("Multi-Script Status Bar Loaded")

"Архитектура" решения.

1. Master Callback

Заложил поддержку нескольких колбэков в скрипт, что б не вызывать конфликты, например:

def get_other_script_info():
...
other_info = get_other_script_info()
notepad.setStatusBar(STATUSBARSECTION.*, other_info)

2. Notepad++ vs system Python.

Плагин Python Script жестко привязан к встроенному интерпретатору (версия 2.7). В конфигураторе есть галка Prefer installed Python library.. gave to copy.., отметив которую чудо не произошло. А копированием я не стал заниматься. Гугль пишет, не получится использовать библиотеки из своего системного Python 3.х, поэтому приходится мириться с особенностями «двойки», такими как обязательные префиксы u"" и decode('utf-8').

3. Regex в Scintilla vs re.

Долго экспериментировал с движком Scintilla, куча проблем , начиная с UTF8. Отказался от этого капризного зверя (editor.research) в пользу нативного модуля Python re. Это гарантирует работу с кириллицей, регулярками и корректную обработку границ слов.

Изначально я пытался использовать editor.research. Но движок Scintilla в Notepad++ 8.x (основанный на Boost.Regex) ведет себя непредсказуемо с кириллицей: то требует флаг 0x00800000, то игнорирует границы слов \b, то падает с ошибкой Invalid or unterminated... при попытке передать UTF-8 байты. Нативный модуль re с флагом re.UNICODE решил все проблемы разом.

4. CamelCaseStyle и snake_case_style.

Была идея добавить автоматическое определение стиля написания (например, выделяешь _btn в _btn110Clk - и что искать _btn или _Btn, или btn и не BTN). На практике критерии оказались слишком зыбкими:
Selection must start with an Uppercase... cannot contain underscores...
Слишком много «если» (особенно с цифрами), которые только запутали бы понимание, что именно подсчитывать.
Лаконичный «Partial/Whole» (частично и полное слово), «Case Sensitive / Case Insensitive» (с учетом регистра и без) поиск оказался гораздо надежнее.

5. Status Bar.

Я выбрал секцию STATUSBARSECTION.DOCSIZE. Забавно, до написания статьи не знал, что по ним можно кликать :-)

Всего в Python Script 6 шт STATUSBARSECTION
0. DOCTYPE: Тип документа (Язык). (например, Python file или Hyper Text Markup Language file). Двойной клик по этой секции открывает меню выбора языков.
1. DOCSIZE: Длина файла в символах (length) и количество строк (lines). При двойном клике открывается то самое окно Summary.
2. CURPOS: Номер текущей строки (Ln), колонки (Col), позиции курсора (Pos). При выделении: и количество выделенных символов (Sel). Двойной клик - окно «Перейти к строке» (Go to Line).
3. EOL: Формат конца строки (EOL). Вынь CRLF, *nix LF, Яблоко CR. Клик - выбор.
4. UNICODE: Кодировка (UTF-8, ANSI, ..). Клик - выбор..
5. TYPINGMODE: Режим ввода (INS вставка или OVR замена). Клик переключает режим.

Если писать в основную(первую) секцию, Notepad++ мгновенно затирает ваше сообщение информацией о строке/колонке при любом движении курсора. DOCSIZE - самое стабильное место.

Автозагрузка скрипта.

Чтобы скрипт запускался сам вместе с Notepad++:
1. Из меню Plugins -> Python Script -> Configuration.
2. Меняю Initialisation в значение ATSTARTUP.
3. Добавляю в startup.py (в папке плагина, C:\Program Files\Notepad++\plugins\PythonScript\scripts\startup.py ).

Добавить код в конец startup.py
import sys
import os
# 1. Get the AppData path from the Windows Environment
appdata = os.environ.get('APPDATA')
user_script_dir = os.path.join(appdata, "Notepad++", "plugins", "Config", "PythonScript", "scripts")

# 2. Add it to sys.path if it's not already there
if user_script_dir not in sys.path:
    sys.path.append(user_script_dir)

# 3. Import your script
try:
    import SmartHighlightingCounterInStatusBar
    print("Startup: SmartHighlightingCounterInStatusBar loaded.")
except ImportError as e:
    print("Startup Error: Could not find the script. " + str(e))
except Exception as e:
    print("Startup Error: " + str(e))

Результат.

При выделении любого фрагмента текста сразу видно в Status Bar.
Например выделяем BTN в моем примере :

btn BTN onbtnClk onBTNClk onBTNClk onBTNClk _$_@_onBTNClk_@_$
рус РУС янируски яниРУСки яниРУСки яниРУСки _$_@_яниРУСки_@_$

В данном случае пример отображается как w[≡1,∀2], p…≡5,∀7… , где:
- w - whole, полное совпадение. Для отображения используются квадратные скобки:
- p - partial, частичное совпадение. Для отображения используются многоточия:
- ≡ - Case Sensitive, с учётом регистра символов.
- ∀ - Case Insensitive, без учёта регистра символов.

  • Полное совпадение (w[≡1,∀2]):

    • ≡1 - 1 полное совпадение с учётом регистра

    • ∀2 - 2 полных совпадения без учёта регистра

  • Частичное совпадение (p…≡5,∀7…):

    • ≡5 - 5 частичных совпадений с учётом регистра

    • ∀7 - 7 частичных совпадения без учёта регистра

Прим: Специфические символы выбраны для краткости, из-за лимита длины поля статусной строки. Использование слов сразу вылезает за границу. См. соседнее поле с выводом строки, колонки и позиции.

PS (в личку задавали вопросы).

Будет ли работать со всеми кодировками, когда указываешь decode('utf-8')? С win1251, с UTF16

Да, будет. Я отказался от регулярных выражений Scintilla. editor.getText().decode('utf-8') превращает «сырые» байты из Scintilla в полноценные Unicode-объекты. Далее re.UNICODE корректно обрабатывает \b с нац. кодировками

UTF-16 BE BOM
Win-1251

П��и подготовке статьи оставил лишнюю строчку other_info = get_other_script_info()

закоментил.

У меня не выделяются буквы, если они входят в другое слово. Это какой-то отдельный плагин?

Эта настройка в самом Notepad++ ( SmartHighlighting: Enable )


PS: Никаких рекламных ссылок на мой Телеграм-канал, тем более что его нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
полезная фича?
70.59%да, пригодится12
17.65%не понадобится3
17.65%хм… не знаю3
0%редко пользуюсь Notepad++0
Проголосовали 17 пользователей. Воздержался 1 пользователь.