Страницы

Поиск по вопросам

вторник, 28 мая 2019 г.

Работа с Firebase

Хочу задать вопрос по поводу Firebase, а именно сохранение данных в БД Firebase. Насколько я понимаю: что как только данные сохраняются в БД Firebase, то они сразу отправляются на сервер и Firebase обновляет данные на всех устройствах, у которых установлено данное приложение. Что происходит, когда пользователь находится оффлайн, все данные пропадают? и юзер не может больше пользоваться приложением? Как решаются конфликты? Например, пользователем на одном устройстве удалил какие-то данные будучи оффлайн, а другой пользователь вносил изменение в эти данные.


Ответ

Как только обновляются данные в FireBase (и если вы использовали ValueEventListener), то у пользователя, который в этот момент работает в приложении произойдет обновление и он это увидит. Когда пользователь находится в оффлайн, все данные продолжают сохраняться в Firebase (только пользователь об этом не знает). Пользователь может по прежнему пользоваться приложением даже в оффлайн, как сказано в документации. Если кто-то внес изменения в базу, то изменения произойдут сразу, а кто подключится из оффлайн, то есть станет онлайн, найдет нужную ветку и уже удалит эти данные. Так что другие пользователи не увидят ветку с этими данными из базы. П.С. что же у вас за данные, что пользователь их может удалить, а другой может их при этом менять? Может лучше сделать их личными - кто захотел удалил, поменял. А основные данные сделать доступными только для чтения большинству.

Обработать строку с помощью регулярного выражения на Python

Задача: нужно отбросить из начала данной строки все символы, не являющимися буквами, до первой встретившийся буквы. Исключительно используя регулярное выражение.
Например: s = "12. Привет мир 23 раза"
Должно остаться "Привет мир 23 раза"
Пробовал как то так
re.match(r'(?<=(\d|\.|\s)*)(.*)', s).group()
не получается


Ответ

Модуль регулярных выражений Python re не поддерживает блоков предварительного просмотра назад неопределенной длины. В (?<=(\d|\.|\s)*) длина совпадения неопределена, так как квантификатор * находит ноль и более (неизвестно сколько) символов.
Используйте
re.sub(r'^[^а-яА-ЯёЁ]+', '', s)
Демо
Подробности:
^ - начало строки [^а-яА-ЯёЁ]+ - 1 и более символов, отличных от русских букв. Если надо, можно включить и латинские - [^а-яА-ЯёЁa-zA-Z]+
Примечание
Символ ^ в разных контекстах может обозначать:
Буквальный символ ^ (caret):
Если он экранирован знаком \ (в любом месте регулярного выражения) Если он находится внутри символьного класса не в начальной позиции (т.е. [;!^]) (В некоторых других языках / библиотеках ещё может быть между \Q ... \E) Метасимвол начала строки/целого текста (в зависимости от модификатора или библиотеки регулярных выражений, в Python при использовании re.M / re.MULTILINE - позиция после знака перевода строки LF,
, если модификатор не указан, будет найдено только начало целого текста)
Если он не экранирован и не находится внутри символьного класса Метасимвол, инвертирующий символьный класс, т.е. [^.] найдёт все символы, отличные от точки.
ВНИМАНИЕ! [^^] находит любой символ, отличный от знака ^

Компилятор не поддерживает расширенную инициализацию

Имеется такой код:
int main() { double d {4.5}; int x {d}; }
Прочитал, что в новой версии С++11 есть поддержка на запрет сужающих преобразований при инициализации. Но компилятор ошибки не выдает, хотя должен (ибо я пытаюсь double в int преобразовать с синтаксисом новой версии инициализации (с помощью фигурных скобок), которая как раз запрещает подобные небезопасные преобразования.
IDE - DevC++ 5.11 Компилятор - TDM-GCC 4.9.2.
Вопрос: Так почему нет поддержки этой новой инициализации? Компилятор новый же, его вместе со средой скачал недавно вот.


Ответ

Стандарт С++ не обязывает реализацию языка выдавать именно "ошибку компиляции" для случаев когда в стандарте написано что код ошибочен. (Однако при этом не должно ломается SFINAE, иначе это уже баг компилятора).
Компилятор g++ в некоторых случаях ограничивается предупреждением:
double d = 4.5; int x{d}; // предупреждение в g++ int y{4.5}; // ошибка в g++
Впрочем этого достаточно, т.к. рекомендуется компилировать код с -Wall -pedantic -Werror
Что касается стандарта, то int x{d}; это несомненно неправильный код. В разделе [dcl.init.list] есть аналогичный пример

Из текстового файла в строки C#

Допустим есть текстовый файл, в котором в каждой строке лежит слово и последовательность цифр. Как мне поместить в одну строку только слова, а в другую - только цифры?
Пример файла:
testText0 1 2 3 4 testText1 2 4


Ответ

Кроме решений в лоб всегда остаётся вариант использования регулярных выражений.
using System.Text.RegularExpressions; ........ var Lines = File.ReadAllLines("MyFileName.txt"); var RegEx = new Regex(@"^([a-zA-Z]+)([\d\s]+)$"); var Words = new List(); var Digits = new List();
foreach (var Line in Lines){ var Matches = RegEx.Match(Line); Words.Add(Matches.Groups[1].ToString()); Digits.Add(Matches.Groups[2].ToString()); }
Этот подход хорош еще и тем, что он автоматически валидирует входные данные.
Проверить регулярные выражения можно тут.

Как сделать простой сервер на Python работающий как автономно, так и через apache?

Необходимо создать простой одностраничный сайт для тестовых целей. На странице необходимо иметь возможность делать AJAX запросы к серверу. Пожалуйста, подскажите, каким образом реализовать самым простым и быстрым способом инфраструктуру сервера? Необходимо учитывать, что сервер будет запускаться как на локальной машине не техническим специалистом, так и на общем тестовом сервере. На локальной машине не технического специалиста нет какого–либо специального ПО. На сервере используется apache2, а также настроено окружение для django–проектов.


Ответ

Самым простым способом реализовать указанное будет использование встроенного python HTTP–сервера на локальной машине и простого WSGI–приложения на сервере. При создании структуры проекта необходимо учитывать, что при работе через WSGI статические файлы лучше отдавать напрямую через apache.
Предлагаемая структура проекта будет следующей
server.py static/ | - index.html | - m/ | - styles.css | - scripts.js | - img/
В этом случае, мы сможем одинаково работать с файлами проекта. В html/css коде ссылки на статические файлы будут выглядеть следующим образом
/m/scripts.js
Конфигурация apache
ServerAdmin webmaster@mysite.ru DocumentRoot "/path/to/my/site/root/" ServerName mysite.ru
WSGIApplicationGroup %{GLOBAL} WSGIDaemonProcess mysite WSGIProcessGroup mysite WSGIScriptAlias / /path/to/my/site/root/server.py
Alias /m/ /path/to/my/site/root/static/m/
Order deny,allow Allow from all
ErrorLog "/var/log/apache2/mysite_error.log" LogLevel warn CustomLog "/var/log/apache2/mysite_warning.log" combined

В конфигурационном файле для сервера мы казали, где находится наш WSGI скрипт (WSGIScriptAlias / /path/to/my/site/root/server.py). Предлагается использовать тот же скрипт и для локального сервера. Также мы добавили псевдоним для /m/ (Alias /m/ /path/to/my/site/root/static/m/). Таким образом, все что остается – сделать папку static рабочей директорией для локального сервера, а apache автоматически сам будет обрабатывать ссылки на файлы.
import SimpleHTTPServer import SocketServer import os import sys
PORT = 8000 AJAX_ENPOINT_URL = "/ajax/endpoint" DOCUMENT_ROOT = "/path/to/my/site/root/"
# Обработчик локального сервера. class CustomHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): # Обрабатываем GET–запрос к серверу. # Если запрос к приходит по известному нам URL, формируем ответ. # Если мы не знаем URL, предлагаем стандартной библиотеки обработать его. def do_GET(self): if self.path == AJAX_ENPOINT_URL: self.send_response(200) self.send_header('Content-type','application/json') self.end_headers() self.wfile.write("My ajax response") return else: SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
if __name__ == "__main__": os.chdir("./static/") httpd = SocketServer.TCPServer(("", PORT), CustomHandler) print "Serving at port", PORT httpd.serve_forever() quit()
def get_index(): f = open(DOCUMENT_ROOT +'/static/index.html', 'r') data = f.read() f.close() return data
def application(environ, start_response): output = "" status = "200 OK" content_type = "text/plain" path = environ.get('PATH_INFO', None)
if path == "/" or path == "index.html": output = get_index() content_type = 'text/html' elif path == AJAX_ENPOINT_URL: content_type = 'application/json' output = "My Ajax response"
if output == "": status = "404 Not Found" output = "Not Found"
response_headers = [('Content-type', content_type), ('Content-Length', str(len(output)))] start_response(status, response_headers) return [output]
Для запуска сервера на локальной машине, достаточно перейти в корень проекта и выполнить команду
python server.py
В случае запуска на сервер, необходимо добавить конфигурацию apache в папку конфигураций и перезапустить его.

Не работает русская клавиатурная раскладка в сочетаниях горячих клавиш Sublime Text 3

Установил плагин Markdown Editing, синтаксис Markdown GFM. Хочу обернуть текст в код или квадратные скобки. Когда нахожусь в английской клавиатурной раскладке, проблем не возникает:

Но когда раскладка русская, я уже не могу обернуть текст:

Это неудобно, раздражает. Причём такое сразу с несколькими плагинами: в английской раскладке горячие клавиши работают, а в русской нет.
Подскажите, что делать?


Ответ

1. Суть проблемы
Откроем консоль: Ctrl+` (машинописный обратный апостроф, символ расположен на той же клавише, где кириллическая «ё») → вводим в открывшееся поле sublime.log_input(True). Нажмём сначала Ctrl+k в английской раскладке, затем Ctrl+л в русской. Посмотрим, что выведется в консоли:
key evt: control+k key evt: control+k
Если с системой нет проблем, результат должен быть аналогичным. А теперь нажмём k в английской, потом л в русской.
chr evt: k (0x6b) chr evt: л (0x43b)
Sublime Text воспринимает k и л как разные символы, если они нажимаются не вместе со служебными клавишами. Если используется двойное нажатие хоткеев, Ctrl+k,k и Ctrl+л,л Sublime Text также видит по-разному.
Т.е. когда в качестве шортката используется одинарный символ, чтобы результат в русской раскладке был таким же, как в английской, придётся раскладку переключать. Так что если будете сами задавать сочетание для команды, без использования служебных клавиш, продублируйте запись и для русской раскладки, пример:
// Предпросмотр вики-страницы в плагине Mediawiker { "keys": ["ctrl+7", "p"], "command": "mediawiker_preview" }, { "keys": ["ctrl+7", "з"], "command": "mediawiker_preview" },

2. Попытки урегулирования проблемы с разработчиками
Заявка в issue tracker Sublime Text висит долгое время, её приоритет обозначен как «minor». Есть предложение использовать в качестве идентификатора клавиши keycode вместо keysym, но по состоянию на сентябрь 2016 оно не реализовано на практике. Разработчик плагинов отказался принимать мой pull request, добавляющий хоткеи для русской раскладки.
Значит, придётся настраивать Sublime Text для русской раскладки самостоятельно.

3. Решение
На примере плагина BetterFindBuffer.
Устанавливаем плагин PackageResourceViewer: Ctrl+Shift+P → PackageResourseViewer:Open Resourse (благодаря fuzzy поиску достаточно набрать просто prvor) → MarkdownEditing → Default.sublime-keymap (иногда Default(ваша операционная система).sublime-keymap) → в открывшемся файле ищем шорткаты, состоящие из одного символа, которого в русской раскладке нет или же он расположен на другой клавише. Пример:
{ "keys": ["o"], "command": "find_in_files_open_file", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] },
Заменяем o на соответствующую ей в русской раскладке букву, это щ
{ "keys": ["щ"], "command": "find_in_files_open_file", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] },
Preferences → Key Bindings - User → вставляем в открывшийся файл код с буквой щ. Проделываем данное действие со всеми шорткатами без служебных клавиш, где это необходимо.
После того, как произвели действия, проверьте, всё ли работает нормально. Пример: в Preferences → Key Bindings - Default есть такой код:
{ "keys": ["\""], "command": "insert_snippet", "args": {"contents": "\"$0\""}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true }, { "key": "preceding_text", "operator": "not_regex_contains", "operand": "[\"a-zA-Z0-9_]$", "match_all": true }, { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true } ] },
(\" — символ "кавычки-ёлочки". Для использования кавычки как литерала в JSON-файлах требуется её экранировать.)
Вы вставили в свой кеймап:
{ "keys": ["э"], "command": "insert_snippet", "args": {"contents": "\"$0\""}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true }, { "key": "preceding_text", "operator": "not_regex_contains", "operand": "[\"a-zA-Z0-9_]$", "match_all": true }, { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true } ] },
Но теперь когда вы просто вводите букву э, вводятся двойные кавычки. Код с буквой э не нужен, удаляем его из нашего файла. Проверяем — буква э пишется теперь нормально. Если после ваших действий с назначением команд клавишам русской раскладки символы русской раскладки заработают неправильно, значит, скорее всего, вы вставили лишнее, подлежащее удалению.

4. Поддержка русской раскладки для дефолтных хоткеев без служебных клавиш
Можно добавлять строки ниже в файл, открывающийся после Preferences → Key Bindings - User, сразу после установки Sublime Text, чтобы не приходилось переключаться при оборачивании текста в различные виды кавычек и скобок, а также при использовании Vintage Mode. Проверял, ошибок возникнуть не должно.
Обёртка текста в скобки и кавычки
// // // Дефолтные сочетания в русской раскладке клавиатуры // // // Выделение {фигурными скобками} { "keys": ["Х"], "command": "wrap_block", "args": {"begin": "{", "end": "}"}, "context": [ { "key": "indented_block", "match_all": true }, { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_match", "operand": "^$", "match_all": true }, ] }, { "keys": ["Х"], "command": "insert_snippet", "args": {"contents": "{${0:$SELECTION}}"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true } ] }, { "keys": ["Ъ"], "command": "move", "args": {"by": "characters", "forward": true}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^\\}", "match_all": true } ] }, // Выделение "кавычками-ёлочками" { "keys": ["Э"], "command": "insert_snippet", "args": {"contents": "\"${0:$SELECTION}\""}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true } ] }, { "keys": ["Э"], "command": "move", "args": {"by": "characters", "forward": true}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^\"", "match_all": true }, { "key": "selector", "operator": "not_equal", "operand": "punctuation.definition.string.begin", "match_all": true }, { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true }, ] }, // Выделение 'одинарными кавычками' { "keys": ["э"], "command": "insert_snippet", "args": {"contents": "'${0:$SELECTION}'"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true } ] }, { "keys": ["э"], "command": "move", "args": {"by": "characters", "forward": true}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^'", "match_all": true }, { "key": "selector", "operator": "not_equal", "operand": "punctuation.definition.string.begin", "match_all": true }, { "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.single - punctuation.definition.string.end", "match_all": true }, ] }, // Выделение [квадратными скобками] { "keys": ["х"], "command": "insert_snippet", "args": {"contents": "[$SELECTION]$0"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true } ] }, { "keys": ["х"], "command": "insert_snippet", "args": {"contents": "[${0:$SELECTION}]"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true } ] }, { "keys": ["ъ"], "command": "move", "args": {"by": "characters", "forward": true}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^\\]", "match_all": true } ] },
Vintage Mode
Вставляем отсюда, (на всякий случай архивная копия, также см. ответ

5. Поддержка русской раскладки в некоторых плагинах
Чтобы не мучились настраивать русскую раскладку для этих плагинов самостоятельно, готовый код:
BetterFindBuffer
// BetterFindBuffer, русская раскладка { "keys": ["щ"], "command": "find_in_files_open_file", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["ф"], "command": "find_in_files_open_all_files", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["т"], "command": "find_in_files_jump_file", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["з"], "command": "find_in_files_jump_file", "args": { "forward": false }, "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["о"], "command": "find_in_files_jump_match", "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["л"], "command": "find_in_files_jump_match", "args": { "forward": false }, "context": [ { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": ["а"], "command": "bfb_fold_and_move_to_next_file", "context": [ {"key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] }, { "keys": [","], "command": "bfb_toggle_popup_help", "context": [ {"key": "selector", "operator": "equal", "operand": "text.find-in-files" } ] },
Markmon
// Markmon, русская раскладка { "keys": ["ctrl+k", "ь"], "command": "markmon_toggle", "args": { "enable": true } },
MarkdownEditing
// MarkdownEditing, русская раскладка { "keys": ["ё"], "command": "insert_snippet", "args": {"contents": "`${0:$SELECTION}`"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true } ] }, { "keys": ["ё"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Skip Closing Character.sublime-macro"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "^`", "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true } ] }, { "keys": ["Ё"], "command": "insert_snippet", "args": {"contents": "~~${0:$SELECTION}~~"}, "context": [ { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ] }, { "keys": ["№"], "command": "insert_snippet", "args": {"contents": "#${0: ${SELECTION/(^ | $)//g} }#"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "preceding_text", "operator": "not_regex_contains", "operand": "#{6}", "match_all": true }, { "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": true }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ] }, { "keys": ["№"], "command": "insert_snippet", "args": {"contents": "#${0: ${SELECTION/(^ | $)//g}}"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "preceding_text", "operator": "not_regex_contains", "operand": "#{6}", "match_all": true }, { "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": false }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ] }, { "keys": ["№"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Padded Headline.sublime-macro"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "preceding_text", "operator": "regex_contains", "operand": "#{6}", "match_all": true }, { "key": "following_text", "operator": "regex_contains", "operand": "#+", "match_all": true }, { "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": true }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ] }, { "keys": ["№"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Padded Headline.sublime-macro"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "preceding_text", "operator": "regex_contains", "operand": "#{6}", "match_all": true }, { "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": false }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ] }, { "keys": ["Ю"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Convert to Blockquote.sublime-macro"}, "context": [ { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, { "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }, { "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true } ]

Как в Atom заставить разворачиваться конструкцию emmet-а “div.class” по табуляции в php файлах?

Дело в том, что в html файлах отрабатывает: div.some-class преобразовывается в

при нажатии табуляции, когда курсор стоит в конце конструкции, а в php файлах такая система не работает. Как заставить это работать в php файлах?


Ответ

В конфигурации горячих клавиш Emmet по умолчанию про Tab написано следующее:

'atom-text-editor[data-grammar="text html basic"]:not([mini]), atom-text-editor[data-grammar~="erb"]:not([mini]), atom-text-editor[data-grammar~="jade"]:not([mini]), atom-text-editor[data-grammar~="css"]:not([mini]), atom-text-editor[data-grammar~="stylus"]:not([mini]), atom-text-editor[data-grammar~="sass"]:not([mini]), atom-text-editor[data-grammar~="scss"]:not([mini])': 'tab': 'emmet:expand-abbreviation-with-tab'
(Этот селектор пугает ровно до того момента, пока не начнёте смотреть его по секциям, разделённым запятыми: после этого он сразу приобретает смысл)
Чтобы получить этот кусок конфигурации, откройте настройки, Keybindings, наберите в поиске expand-abbreviation и нажмите кнопку напротив tab. Соответствующий кусок конфигурации будет скопирован в буфер обмена.
Формат основан на CSON: как JSON, но с ароматом CoffeeScript
В этом же окне наверху есть ссылка your keymap file. Нажав на неё, вы попадёте в файл конфигурации собственных горячих клавиш. Вставьте туда скопированный кусочек.
Как видно, он имеет формат:
'селектор': 'кнопка': 'команда'
Чтобы указать его действие в PHP-файлах, нужно написать соответствующий селектор. Следуя примерам из настроек по умолчанию, например, такой:
atom-text-editor[data-grammar="text html php"]:not([mini])
(Применимость селекторов можно проверить, открыв DevTools и найдя в DOM узел с редактором, в котором вы хотите, чтобы горячая клавиша действовала. Посмотрите на селектор, на узел, и попробуйте выполнить сопоставление в уме.)
Получится что-то такое:
'atom-text-editor[data-grammar="text html php"]:not([mini])': 'tab': 'emmet:expand-abbreviation-with-tab'
Сохраняйте, пробуйте.