Как стать автором
Обновить

Комментарии 34

Лучшее, что было с редакторами кода

Так торопились выложить материал, что пропустили ошибку в заголовке -"сервеар"? Я молчу про кучу ошибок в самом посте.

А молчать не надо — напишите о них в личку автору.
Опечатку в заголовке исправил.
Прошу написать по конкретнее про «кучу ошибок в самом посте»
> Для этого используется следующий формат:
> Content-Length: \r\n\r\n<json-payload>

А что, если отправить другие заголовки (user-agent и пр.), то запрос не пройдет?
НЛО прилетело и опубликовало эту надпись здесь

Они и стоят локально.

Они запускаются локально. Например, обычно создают расширение для VSCode, которое работает с таким сервером:


  • запускает его, когда нужно, следит за его состоянием
  • устанавливает соединение с запущенным сервером
    Обычно такие расширения очень простые, занимают в районе 100 строк кода.
В оригинале:
Language servers are special programs that run on regular servers.

Подозреваю, скорость интернета тут не причем, хотя никто не мешает использовать удалённый языковый сервер. Иначе как бы VSCode работал с установленными расширениями, скажем, C++/Java (те самые языковые сервера в т.ч.) офлайн? А он работает :-)

Разделяй и властвуй. Главное нужный E в нужный момент не пропустить.

Кажется проблема M*N искусственная, а для ее решени LSR совсем не обязателен. Гораздо лучше смотрится набор библиотек для работы с языками. Я не вижу какой профит от того, чтобы делать распределённую систему, гонять и копировать тонны данных туда сюда и решать нетривиальные задача синхронизации состояния редактора и language server.


Например хочу я подсвечивать члены класса отдельно от других элементов. Для этого редактору нужно знать AST. А оно живет в LSP. Или хочу я делать рефакторинг — мне нужно дергать LSP, а каждая реализация LSP теперь должна уметь делать все рефакторинги. А если бы редактору дали AST, то рефакторинги могли бы быть написаны один раз и работали бы для большинства языков.


В том же самом VS Code, в .net проектах этот самый LSP регулярно съедает 1 ядро и постоянно что-то отваливается. Не видел пока чтобы LSP прямо сделал из редакторов что-то большее или чтобы он что-то супер сильно поменял с точки зрения богатства фич в редакторах.


В защищу концепции могу сказать, что в языках типа lisp и small talk эта идея работает на ура. Но там в запущенной системе можно менять методы, а «LSP» тупо работает с местной рефлексией, это раз в 100 проще.

набор библиотек для работы с языками

И тут внезапно появляется некто, кто напоминает о существовании JB IDE(JVM), VS Code(Node.JS), ViM( C ), Visual Studio(.NET + C++) и Emacs(elisp). И на каком языке вы предложите ему писать общие библиотеки?

На любом, который ему нравится и который может наружу выставить C API. Или по вашему набор соглашений stdcall чем-то принципиально отличается от JSON-RPC? Понятно, что уровни разные, а смысл один, как зная «имя» функции ее вызвать и как получить результат.


Принципиальное различие в другом. В одном случае все синхронно и есть общая память. А в другом случае все асинхронно и копируется. Второе из первого сделать можно, первое из второго уже нет.

Второе из первого сделать можно, первое из второго уже нет.

Обычно все ровно наоборот. Из асинхронного кода сделать синхронный очень просто (просто везде sleep() понавтыкать, например :)), а наоборот — дикие костыли и наполовину неработающие в результате решения, как в js, например.


все синхронно и есть общая память

А потом я запустил по виму в соседних терминалах и мир рухнул.


В 2021 топить за синхронное выполнение чего бы то ни было (за редчайшими исключениями типа некоторых embedded) — это прямо давай-до-свидания.

Принципиальное различие в другом. В одном случае все синхронно и есть общая память. А в другом случае все асинхронно и копируется. Второе из первого сделать можно, первое из второго уже нет.
Обычно все ровно наоборот. Из асинхронного кода сделать синхронный очень просто (просто везде sleep() понавтыкать, например :)), а наоборот — дикие костыли и наполовину неработающие в результате решения, как в js, например.

Вы отвечаете на какой-то другой тезис. Выделяю важное — общая память, не нужно все копировать через границу процесса.

Так а чо делать если процессов которые хотят общаться больше чем один? Висят два вима в соседних табах, смотрят на разные проекты на одинаковом языке — для каждого по инстансу LS поднимать чтобы общаться через C API? И это еще не считая количества core dumped которых надо будет вычищать при обращении.

Кажется проблема M*N искусственная, а для ее решени LSR совсем не обязателен. Гораздо лучше смотрится набор библиотек для работы с языками.

С библиотеками точно так же будет M*N проблема: библиотека написана на языке X, а её надо использовать из языка Y. И тут приходится гонять и копировать тонны данных туда-сюда через границу FFI.


Кроме того, для построения библиотеки надо изначально дизайнить реализацию языка как встраиваемую. Это добавляет сложностей: использовать быстрые, но неэффективные по памяти алгортимы нехорошо (и течь по памяти тоже нехорошо); ошибки бы как-то культурнее рапортовать, а не фиг с ними, всё равно ж программа некорректная; поддерживать совместимость интерфейсов порой бывает сложно.

С библиотеками точно так же будет M*N проблема: библиотека написана на языке X, а её надо использовать из языка Y. И тут приходится гонять и копировать тонны данных туда-сюда через границу FFI.

Зачем копировать? Гоняйте int через границу FFI и будет счастье. Этот int уже кодирует либо адрес в памяти библиотеки, либо смещение в какой-то таблице. Так с испокон веков все ОС делают и ничего.


Кроме того, для построения библиотеки надо изначально дизайнить реализацию языка как встраиваемую. Это добавляет сложностей: использовать быстрые, но неэффективные по памяти алгортимы нехорошо (и течь по памяти тоже нехорошо); ошибки бы как-то культурнее рапортовать, а не фиг с ними, всё равно ж программа некорректная; поддерживать совместимость интерфейсов порой бывает сложно.

Быстрые, но неэффективные по памяти алгортимы, утечки памяти, кривые сообщения об ошибках в равной степени не нужны и в LS. Если LS будет жрать терабайты памяти и падать с OOM каждые 10 минут вы не сможете работать с ним. Также будте и с библиотекой.

Счастья не будет. Если передавать адрес, то надо знать структуру данных в памяти, а не все языки вообще позволяют разыменовывать адреса удобно. Если передавать индекс-хендл, то надо будет пользоваться специальными аксессорами — и копировать себе данные, если они нужны дольше, чем на просто посмотреть. Разные языки имеют разные подходы к жизненному циклу объектов, всё это тоже усложняет дело. Если всё это сложить, то в итоге не становится прям гораздо проще JSON-RPC, который находится под капотом LSP от Microsoft. Его тоже завернуть в прослойку — и вжух, у вас просто слегка чуть медленные методы из библиотеки.


Language Server можно хотя бы попытаться на тяп-ляп прикрутить к уже существующей реализации, потому что протокол готовый — бери и используй. Будет падать или течь — это хотя бы не будет влиять на текст в редакторе. Кривая библиотека что только не будет портить — а она будет кривая, если её сходу нет и не планировалось.

Если передавать адрес, то надо знать структуру данных в памяти, а не все языки вообще позволяют разыменовывать адреса удобно.

JSON-RPC страдает тем-же. Для каждого языка вам нужна библиотека с типами.


и копировать себе данные, если они нужны дольше, чем на просто посмотреть.

И это хорошо и правильно, даём библиотеке буфер и она в него пишет результат. Управление памятью на стороне клиента.


Если всё это сложить, то в итоге не становится прям гораздо проще JSON-RPC, который находится под капотом LSP от Microsoft.

Речь не о простоте, а о том, что при взаимодействии с LS хочется fine grained интерфейс, а JSON-RPC к этому не располагает.


Его тоже завернуть в прослойку — и вжух, у вас просто слегка чуть медленные методы из библиотеки.

Сериализация JSON не бесплатная, все это не чуть медленнее, а прямо сильно медленнее. С библиотекой я могу 100 000 раз в секунду дёрнуть какой-то метод. С JSON-RPC такой фокус уже не пройдёт. Ещё больше вопросов к объёму передаваемой информации. Разве мы можем гарантировать, что редактору не понадобится гонять гигабайты данных туда-сюда? С библиотекой это не проблема — гоняем указатель или индекс.


Если каким-то образом окажется, что можно разделить редактор и backend IDE так, чтобы они общались редко и небольшими запросами-ответами, то концепция LSP будет прекрасна. Но я пока этого не вижу.


Будет падать или течь — это хотя бы не будет влиять на текст в редакторе.

Вы, как пользователь, будете рестартовать LS, пользователь библиотеки будет рестартовать редактор. Разница есть, но использовать это как решающий аргумент в пользу LS было бы странно.

JSON-RPC страдает тем-же. Для каждого языка вам нужна библиотека с типами.

Это не «страдание», это называется «взаимодействие». Через интерфейсы и протоколы. Будет там двоичный интерфейс структур данных и конвенция вызова функций — или протокол JSON-RPC и схема JSON-данных — не суть важно. Именно наличие общего языка решает M*N проблему.


Библиотеки как таковые здесь ничего нового не привносят. Для библиотеки точно так же придётся придумывать API, которому следует следовать, чтобы библиотека могла работать с разными редакторами. Только интерфейс библиотеки прибит к языку реализации сильнее, чем сетевой протокол.


И это хорошо и правильно, даём библиотеке буфер и она в него пишет результат. Управление памятью на стороне клиента.

Но чем это принципиально отличается от того, что даём RPC-библиотеке буфер для ответа, куда копируется ответ? Ничем, кроме появления дополнительной прослойки и уровня абстракции.


Естественно, можно «оптимизировать» дела, если обойти эту абстракцию и общаться напрямую. Вместо сервера и LSP — библиотека и call convention. Но зачем останавливаться на этом? Можно отпилить лишнюю часть стакана и встроить поддержку языка прямо в редактор, будет же ещё быстрее.


С библиотекой я могу 100 000 раз в секунду дёрнуть какой-то метод.

Вот только человек не может сто тысяч раз в секунду обновлять проект, чтобы редактору приходилось перестраивать свою модель так же часто. Компьютеры могут быть достаточно быстрыми, чтобы их хватало на обновление модели в реальном времени.


Боюсь, гораздо больший эффект оказывают другие задержки в других местах: графический стек, обработка событий клавиатуры, и прочее. Вон сколько людей жалуются, что когда они набирают текст в виме, то всё происходит моментально, а стоит перейти на IDE от JetBrains, так проходит целая вечность, пока ты нажмёшь клавишу — текст появится — подсказки обновятся.


Разве мы можем гарантировать, что редактору не понадобится гонять гигабайты данных туда-сюда?

Нет, не можем. Но это же не значит, что нужно всё писать на вырост — вдруг там будут терабайты, давайте сразу вместо библиотеки дизайнить ASIC.


Вы, как пользователь, будете рестартовать LS

Я как пользователь буду набирать свой текст. Редактор может и сам присматривать за запущенным сервером и поднимать его обратно, когда он падает. Пользователю об этом даже рассказывать не надо, он не заметит. А вот упавший редактор — очень даже заметит.

С появлением LSP в редактор оставалось лишь внедрить поддержку протокола языкового сервера. После этого любой, кто делает языковой сервер (следуя стандартам LSP), может легко интегрироваться в редактор кода, при этом редактор никогда не будет "знать", с каким языком он работает!

Это совсем неправда. Если взять ЛЮБОЕ расширение vscode, которое работает с использованием LSP, у него куча кода, который с LSP не работает. Менеджмент пакетов, сборка, тестирование и т.д. реализуется внутри IDE. И это нужно сделать в каждой IDE отдельно для каждого языка, потому что унифицировать утилиты, в отличие от подсветки, уже невозможно.


Поэтому проблема никуда не уходит, в сущности. Упрощается поддержка или нет — другой вопрос. Наверное да.


Майкрософт просто подсаживает всех на протокол, который контролирует она сама, упрощая разработку для себя.
При этом в любой момент они без проблем могут менять спецификации и крутить LSP так, как гугл крутит веб-стандарты хромом.

С введением формального протокола LSP, Microsoft свела знаменитую проблему M x N к проблеме (M x L) + N.
M = Различные языки (C, C++, PHP, Python, Node, Swift, Go и т.д.).
N = Различные редакторы кода (VSCode, Eclipse, Notepad++, Sublime Text и т.д.).
L = Различные языковые серверы LSM

Не совсем так всё-таки. Для языка X один раз пишется X-lang-server, для редактора Y один раз пишется Y-lsp. Нет нужды писать новый X-lang-server под каждый редактор, как и нет необходимости писать новый Y-lsp для каждого языка. Берёте сервер для любимого языка X и примерно одинаково пишете на X попеременно во всех редакторах. И наоборот, чтобы писать в любимом редакторе на новом языке, нужно лишь сказать lsp-плагину этого редактора, как вызывать сервер для этого языка.


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

а я все жду уже сколько лет, когда под Windows у VSCode появится возможность настраивать антиалиасинг :(
Особенно радует стремление MS закрыть свои language servers для конкретных языков и не позволять использовать их нигде кроме VS Code.
Нет больше необходимости ограничивать себя, например только XCode для разработки на Swift или PyCharm для Python.


Надуманная проблема. Учитывая что все языки ObjC+Python реализованные в рамках IntelliJ платформы, то что они не в одной IDE — решения кампании.

Как бы не хвалили LSP, но количество серверов растет с количество языков в проекте.

То есть Java+TS/C#+TS это уже два LSP сервера, которые работают раздельно.

Есть Language Injections, когда в HTML можно встраивать несколько языков. Выходит LSP сервера должны знать что является их языком, а что сторонним. А теперь ещё представим что LSP сервера на разных языках.

LSP — это попытка создать универсальную вещь в рамках одного языка. Когда языков становится больше — все становится куда сложней.

А наш мир не идеальный. И решить все одним языком нельзя.
Учитывая что все языки ObjC+Python реализованные в рамках IntelliJ платформы

А вы активно используете AppCode для разработки под iOS? Я смотрел на него в 2018 году, когда из Java-проектов надо было перейти на iOS — Xcode выиграл даже при отсутствии темной темы.
Вопрос реализация, а не возможности :) Я не использую продукты JetBrains в том виде как они есть.

Но у меня есть C#/.NET реализация которая живет вместе с Java/JVM на базе IntelliJ. (привет Unity разработка)
Про Rider я в курсе, идея в нем заложена интересная, собственно как раз таки это в какой-то степени похоже на LSP: языкоспецифичное ядро написано на близком к языку стеке, UI + остальное неспецифичное написано на Java.
Надуманная проблема. Учитывая что все языки ObjC+Python реализованные в рамках IntelliJ платформы, то что они не в одной IDE — решения кампании.

Проблема отнюдь не надуманная, а IDE/редакторы не ограничиваются продуктами JetBrains.
У разных людей могут быть свои предпочтения и причины выбора того или иного редактора. Возможность продолжать пользоваться своим редактором при добавлении нового языка – это однозначный плюс.
Как бы не хвалили LSP, но количество серверов растет с количество языков в проекте.
То есть Java+TS/C#+TS это уже два LSP сервера, которые работают раздельно.

Даже в случае Idea/Consulo потребуется ставить плагины для каждого языка. Рассматривайте LSP как просто особенность реализации языкового плагина.
Есть Language Injections, когда в HTML можно встраивать несколько языков. Выходит LSP сервера должны знать что является их языком, а что сторонним.

У файла проекта всегда есть определенный тип. Если этот тип подразумевает возможность инъекции, то это часть спецификации типа, соответственно LS для данного типа должен ее понимать.
А теперь ещё представим что LSP сервера на разных языках.

Так ведь самый цимес-то как раз в этом: нам все равно, на каком языке написан LS, лишь бы он соблюдал протокол?
LSP — это попытка создать универсальную вещь в рамках одного языка. Когда языков становится больше — все становится куда сложней.

Почему же в рамках одного-то? LSP как раз позволяет делать функционал почти настоящих IDE для редких языков. Писать с нуля поддержку для сложного языка в рамках парадигмы какой-то IDE может оказаться непросто. Если особенности реализации «сложной языковой части» может быть отделена через json-rpc, то в ход могут пойти наработки самого компилятора языка, который об языковых нюансах знает все. Так устроены многие языковые плагины, например OCaml, Haxe.

Использование достаточно простой абстракции, вроде этого протокола, позволяет людям близким к языку, но далеким от стека и АПИ редактора/IDE, делать отличные и функциональные инструменты.
Я долго пользовался Consulo, но отказался от него после очередного обновления, перед которым я забыл сделать бэкап. Какой-то плагин в текущей сборке оказался сломанным, а в репозитории плагинов лежела только последняя версия, и в результате вся IDE превратилась в тыкву.
Я без претензии, наоборот – очень благодарен за классный инструмент. Это к тому, что нельзя от одного разработчика требовать всего: грамотное разделение обязанностей – вот путь к успеху, а LSP – очень хорошее в том подспорье. К слову, такой же протокол есть и для дебаггеров. Благодаря этому, например, для окамла сложно найти IDE из мэинстримных, хоть сколь-нибудь сопоставимую с VSCode по функционалу.
У файла проекта всегда есть определенный тип. Если этот тип подразумевает возможность инъекции, то это часть спецификации типа, соответственно LS для данного типа должен ее понимать.


.jsp — html, java, javascript, css.

Какой из LSP должен за что отвечать? Учтите есть ещё scss который так же может препроцессится.

.shader с Unity

Сам язык shader, и вставки С++ кода.

Даже банальный RegExp. Одну реализацию можно шарить между языками. А так выходит каждый LSP делает свою валидацию. Это прекрасно — нет. Нельзя сделать какой то хороший UI для этого.

Я долго пользовался Consulo, но отказался от него после очередного обновления, перед которым я забыл сделать бэкап. Какой-то плагин в текущей сборке оказался сломанным, а в репозитории плагинов лежела только последняя версия, и в результате вся IDE превратилась в тыкву.


В текущей версии — ругается на этот случай (и уж давно). Если вы проигнорировали предупреждение, ну бывает. Настройки никуда не сбрасываются — просто дождаться пока все поднимется.

Я вполне понимаю плюс и минусы LSP. Но все это в рамках одного языка. Выходишь за эти рамки и все.
Возможно, я не совсем понял, что имеется в виду под «в рамках одного языка» – речь как раз об инъекциях в рамках одного файла, или вообще?
Полноценная поддержка инъекций всего во все – это действительно нетривиальная задача. Я могу пофантазировать, что в будущем для LS могут сделать аналог кубера/компоуза: маленькие образы серверов для html, css, js, которые смогут общяться мужду собой. IDE на микросервисах – модно, стильно, молодежно!
Никто не предлагает LSP в качестве панацеи, но я согласен с тем, что направление на разделение редактор – тулинг языка, оно перспективное.
Я привел примеры, когда альтернатив нет (почти весь тулинг для окамла написан на окамле и впепярить его в %IDE_NAME% будет проблемно. В VSCode работает и навигация, и комплишн, и дебаггер, и хинты, а Idea в работе с окамлом не сильно отличается от блокнота).
В текущей версии — ругается на этот случай (и уж давно).

Это действительно было давно, года 4 назад. Но это было на работе. Активности в репозиториях консуло тогда не было где-то с неделю. На работе нельзя «подождать недельку», пока IDE раздуплится. Поэтому Консуло будут использовать только для хобби и в тех/той компаниях, которые с вами сотрудничают.
Для своих проектов я часто использую haxe. Да, в идее. Но идейский плагин даже в идее на ладан дышит, иногда приходится лезть внутрь и что-то править. Как haxe-плагин будет работать в консуло – вопрос риторический, понятно что нет смысла его использовать.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории