Здравствуйте. Я вместе с группой опытных программистов разрабатываю редактор скриптов Terrasoft CRM, который на данный момент включает в себя такие возможности как автодополнение, полная подсветка синтаксиса и еще несколько функций необходимых полноценному редактору. Предположительная стоимость продукта - $ 199 за одну лицензию. Интересно знать Ваше мнение по разрабатываемому продукту. Что бы Вы хотели видеть в данном редакторе, готовы ли Вы или Ваша компания купить данный продукт для удобства разработки и, как следствие, роста Вашей продуктивности работы?

Нравится

8 комментариев

Скрины в студию!

Хотелось бы увидеть демонстрацию функциональных возможностей в видео формате.

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

Андрей, сейчас редактор скрипта (как и дизайнеры других сервисов) улучшается и в версии 3.4.1 добавлено немало полезной функциональности. Например, функция автодополненния кода (CodeCompletion) пересматривается и возможно будет расширена дополнением по классам ядра.

Уточните, чего именно вам не хватает в редакторе скрипта в TSAdmin? Возможно это уже будет в новой версии 3.4.1.

Планируется редактор только скриптов, без других типов объектов?
А не рассматривали вариантов в виде плагина к навороченной IDE вроде MSVS?

Уточните, чего именно вам не хватает в редакторе скрипта в TSAdmin? Возможно это уже будет в новой версии 3.4.1.

Хотелось бы подсветку выделенного слова и его совпадение в функции.

Константин, так сейчас выглядит мой дизайнер скрипта в TSAdmin (версия 3.4.1.62):

Все похожие слова при выделении или при поиске слова выделяются.

Вот настройки дизайнера скриптов в TSAdmin (можно выбрать цвет фона, размер и цвет шрифта, цвет выделения похожих слов и т.д.):
Окно опций

Интересно, продукт вышел?

Показать все комментарии

Как видно из сабжа, речь пойдет об использовании автоподстановки при разработке. Почему то эта возможность в Администраторе Terrasoft CRM не широко нами используется, хотя ее использование значительно может облегчить написание кода, зачастую рутинного занятия. Как?

Приведу простой пример. Очень часто необходимо при выполнении длительных операций вставить "sand clock" ("часики"). "Как называется та функция, которая "часики делает?", - обычно я спрашиваю у коллег. Не все сразу вспомнят, я думаю (хотя, кто знает... :)) А кто нам мешает вставить в AutoComplete.cfg (см. в папке Settings) такую запись:

clocks
|Sand clock
=try {
= System.BeginProcessing();
= System.ProcessMessages();
= |
=} finally {
= System.EndProcessing();
=}

Теперь, если мне нужно, я набираю clocks, нажимаю комбинацию Shift + пробел и в результате в скрипт вставляется нужный мне текст.

Минусы: пока не придумал.
Плюсы: 1. Быстрее пишется код.
2. Не нужно держать в голове редкие функции, которые можно просто добавить в AutoComplete.cfg.
3. Добавив частые операции (см. ниже), Вы также избавляете себя от рутинного набора "надоевших" функций.
4. Меньшее количество ошибок при написании кода (надеюсь :)).
5. И, что немаловажно, используя автоподстановку, поддерживается общий стиль написания кода, т.к. все отступы задаются в тексте автоподстановки.

Если согласны с этой идеей, можете модифицировать "свои" AutoComplete.cfg как Вам будет угодно, лишь бы Вам было удобно.

Теперь, как все это "добро" реализовать:
- заходите в папку Settings
- открываете AutoComplete.cfg
- 1-я строка - кодовое слово функционала или первые буквы функции
- 2-я строка - знак табуляции "|" и описание автоподстановки
- дальше, начиная каждую строчку со знака "=", сам текст автоподстановки. Если необходимо, чтобы после автоподстановки курсор установился в определенную позицию, в этом месте текста необходимо вставить "|".

Приведу добавленные у себя функции:

apply
|Apply dataset filter
=ApplyDatasetFilter(Dataset, 'FilterName', Value, true);

applys
|Apply select query filter
=ApplySelectQueryFilter(SelectQuery, 'FilterName', Value, true);

disable
|Enable dataset filters
=EnableDatasetFilters(Dataset, false);

enable
|Disable dataset filters
=EnableDatasetFilters(Dataset, true);

goto
|GotoWorkspace('WorkspaceUSI', IDValue);
=GotoWorkspace('WorkspaceUSI', IDValue);

new
|GetNewItemByUSI
=Services.GetNewItemByUSI('|');

open
|Dataset opening
=var Dataset = GetSingleItemByCode('|');
=Dataset.Close();
=EnableDatasetFilters(Dataset, false);
=ApplyDatasetFilter(Dataset, 'FilterName', Value, true);
=Dataset.Open();

opens
|Get dataset by select query service
=var SelectQuery = GetSingleItemByCode('|');
=ApplySelectQueryFilter(SelectQuery, 'FilterName', Value, true);
=var Dataset = SelectQuery.Open();

single
|GetSingleItemByCode
=GetSingleItemByCode('|');

try
|try {} finally {}
=try {
= |
=} finally {
= |
=}

tryc
|try {} catch {}
=try {
= |
=} catch(e) {
= System.MessageDialog(e.message, mdtWarning, mdbOK, 0);
=}

По-сути, никто нам не мешает вставлять целые реализации функционала или отдельные функции. Прикольно?

Нравится

Поделиться

19 комментариев

Ко мне поступила информация что в скором времени планируется добавления улучшенной версии AutoComplete.cfg в коробку! Такой шанс выпадает не часто =)
Предлагаю составить окончательный список со всеми нужными функциями

Предлагаю обобщенный список:
* моя конструкция для ApplyDatasetFilter более удобнее в повседневной работе
* отдельный "try" для "сatch" не вижу смысла писать

clocks
|Sand clock
=try {
=	System.BeginProcessing();
=	System.ProcessMessages();
=	|
=} finally {
=	System.EndProcessing();
=}
 
sql
|Connector.DBEngine.GetSelectQuerySQLText()
=Connector.DBEngine.GetSelectQuerySQLText(|Dataset.SelectQuery);
 
app
|ApplyDatasetFilter()
=ApplyDatasetFilter(Dataset, '|, true);
 
apps
|Apply select query filter
=ApplySelectQueryFilter(SelectQuery, '|, true);
 
disable
|Enable dataset filters
=EnableDatasetFilters(Dataset, false);
 
enable
|Disable dataset filters
=EnableDatasetFilters(Dataset, true);
 
open
|Dataset opening
=var Dataset = GetSingleItemByCode('|');
=Dataset.Close();
=EnableDatasetFilters(Dataset, false);
=ApplyDatasetFilter(Dataset, 'FilterName', Value, true);
=Dataset.Open();
 
opens
|Get dataset by select query service
=var SelectQuery = GetSingleItemByCode('|');
=ApplySelectQueryFilter(SelectQuery, 'FilterName', Value, true);
=var Dataset = SelectQuery.Open();
 
da
|Dataset()
=Dataset('|')
 
ged
|GetDatasetFieldValueByID(DatasetUSI, ID, FieldName)
=GetDatasetFieldValueByID('|', , '');
 
get
|GetSingleItemByCode()
=GetSingleItemByCode('|', '');
 
ie
|IsEmptyValue
=IsEmptyValue(|)
 
try
|try{}finally{}
=try {
=	|
=} catch(e) {
=	System.MessageDialog(e.message, mdtWarning, mdbOK, 0);
=} finally {
=	//
=}
 
while
|while () {}
=while (!|Dataset.IsEOF) {
=	//
=	Dataset.GotoNext();
=}

+ остальные базовые

Также предлагаю изменить файл AutoCorrect.cfg:

ff	function
de	debugger;
re	return;
co	continue;

Желательно в ближайшие дни обсудить/дополнить этот список.

От себя еще добавлю:

wnd
|show window
=var wnd = GetSingleItemByCode('|', '');
=wnd.Prepare();
=wnd.Show();
 
selectdata
|show SelectData window
=var Dataset = Services.GetNewItemByUSI('|');
=var SearchFieldNames = 'Name';
=var DisplayFieldNames = 'Name';
=var KeyFieldName = 'ID';
=var KeyValue = '';
=var SearchFieldName = 'Name';
=var SearchValue = '';
=Dataset.Open();
=ShowSelectDataWindow(Dataset, SearchFieldNames, DisplayFieldNames,
=		KeyFieldName, KeyValue, SearchFieldName, SearchValue, Self,
=		'', false, true, null, null, false, null, false);
exec
|  Run stored proc from TS
=var Parameters = System.CreateObject('TSObjectLibrary.Parameters');
=// Input params
=//pdtIdentity
=//pdtInteger
=//pdtFloat
=//pdtString
=//pdtDateTime
=//pdtBoolean
=//pdtBlob
=//pdtGUID
=//pdtUnicodeString
=//pdtFunction
=CreateSPParameter(Parameters, '|<Param1Name>', pdtGUID, <ParamValue>);
=// Output param
=// Parameters('<Param1Name>').ParamType = 1;
=CreateSPParameter(Parameters, '<Param2Name>', pdtDateTime, <ParamValue>);
=try {
=	var SQL = 'EXEC dbo.<ProcName> :<Param1Name>, :<Param2Name>';
=	Connector.DBEngine.ExecuteCustomSQL(SQL, Parameters);
=} catch(e) {
=	Log.Write(2, e.message);
=}
ford
|for (Dataset.Open(); !Dataset.IsEOF; Dataset.GotoNext()) {
=for (Dataset.Open(); !Dataset.IsEOF; Dataset.GotoNext()) {
=	|
=}
isadm
|Connector.CurrentUser.IsAdmin
=var IsAdmin = Connector.CurrentUser.IsAdmin;
=if (IsAdmin) {
=	|
=}
 
cnt
|Connector.CurrentUser.ContactID
=var ContactID = Connector.CurrentUser.ContactID;
=|
 
emptygrd
|GridNotContainsAnyRecords
=var ContractIDs = BaseWorkspace.Grid.SelectedIDs;
=var Count = ContractIDs.Count;
=if (!Count || IsDatasetEmpty(dlContracts.Dataset)) {
=	ShowWarningDialog(GridNotContainsAnyRecords);
=	return;
=}
=|

Также предлагаю немного изменить имена (по аналогии) open на opend так как имеем opens и т. д.,
Для try\finally\catch соответственно tryf и tryc:

tryf
|try {} finally {}
=try {
=	|
=} finally {
=	
=}
 
tryc
|try {} catch {}
=try {
=	|
=} catch(e) {
=	if ('message' in e) {
=		System.MessageDialog(e.message, mdtWarning, mdbOK, 0);
=	} else {
=		System.MessageDialog(e.Message, mdtWarning, mdbOK, 0);
=	}
=}

tryf, tryc - объединено в одну подстановку try с полным набором вариантов выхода, лишние строки легко удаляются...

"лишние строки легко удаляются" - все таки лучше каждий раз добавить "f" или "c". Тем более для тех кто привык работать с IntelliSense или похожими систамами - это как минимум привычно.

Оставьте try, плиз. tryf не сильно удобно. Сложно запомнить. Мне больше нравится вариант предложенный Стасом с try и tryc.

Предлагаю оставить 3 варианта:

try
|try{}finally{}
=try {
=	|
=} catch(e) {
=	//
=} finally {
=	//
=}
 
tryf
|try {} finally {}
=try {
=       |
=} finally {
=       
=}
 
tryc
|try {} catch {}
=try {
=       |
=} catch(e) {
=       if ('message' in e) {
=               System.MessageDialog(e.message, mdtWarning, mdbOK, 0);
=       } else {
=               System.MessageDialog(e.Message, mdtWarning, mdbOK, 0);
=       }
=}

Немного поправлю

try
|try{}catch{}finally{}
=try {
=       |
=} catch(e) {
=       if ('message' in e) {
=              Log.Write(2, e.message);
=       } else {
=              Log.Write(2, e.Message);
=       }
=} finally {
=       //
=}

"s.mulyava" написал:
Log.Write(2, e.Message);

Когда такое вообще встречается?

Некоторие языки программирования, в которых рекоммендуется использовать Венгерскую нотацию, и/или нет "различия" между регистрами, в которых набраны имена переменных, могут описание ошибки вставлять с Большой "M" (особенно туда относятся Object Pascal и Visual Basic)

Это встречается, например, в провайдерах баз данных написаних не на C++

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

За универсальность нужно чем то платить.

"s.mulyava" написал:Некоторие языки программирования ... могут описание ошибки вставлять с Большой "M" (особенно туда относятся Object Pascal и Visual Basic)

Так ведь конфигурация Terrasoft использует ни что иное как JScript. Поэтому нету смысла использовать e.Message.

Предлагаю использовать такой вид:

try
|try{}catch{}
=try {
=	|
=} catch(e) {
=	Log.Write(2, e.message);
=}
 
tryf
|try {} finally {}
=try {
=	|
=} finally {
=	
=}

Для JScript - согласен. Но если использовать другие COM объекты которые написаны на не JScript... с другой стороны такие ситуации редки и можно не проверять наличие описания ошибки в e.Message (но про это как минимум нужно знать), В конфигурации есть именно такая реализация. (Примеров много - SetTimeIntervals(...), LoadDataFromDataRowNodeToDataset(...) возможно предыдущие с ошибкой, но RunMergeFiles(...) - должно быть именно так)

Представляю вам оптимальную, на мой взгляд, версию:

da
|Dataset()
=Dataset('|')
 
sql
|Connector.DBEngine.GetSelectQuerySQLText()
=Connector.DBEngine.GetSelectQuerySQLText(|Dataset.SelectQuery);
 
app
|ApplyDatasetFilter()
=ApplyDatasetFilter(Dataset, '|, true);
 
disable
|Enable dataset filters
=EnableDatasetFilters(Dataset, false);
 
enable
|Disable dataset filters
=EnableDatasetFilters(Dataset, true);
 
open
|Dataset opening
=var Dataset = GetSingleItemByCode('|');
=Dataset.Close();
=EnableDatasetFilters(Dataset, false);
=ApplyDatasetFilter(Dataset, 'FilterName', Value, true);
=Dataset.Open();
 
opens
|Get dataset by select query service
=var SelectQuery = GetSingleItemByCode('|');
=ApplySelectQueryFilter(SelectQuery, 'FilterName', Value, true);
=var Dataset = SelectQuery.Open();
 
ged
|GetDatasetFieldValueByID(DatasetUSI, ID, FieldName)
=GetDatasetFieldValueByID('|', , '');
 
get
|GetSingleItemByCode()
=GetSingleItemByCode('|', '');
 
ie
|IsEmptyValue
=IsEmptyValue(|)
 
{}
|{ .. }
={
=	|
=}
 
func
|function
=function |() {
=	//
=}
 
if
|if () {}
=if (|) {
=	//
=}
 
var
|var = ;
=var | = ;
 
varo
|var = new Object();
=var | = new Object();
 
or
|or
=or (|)
 
fori
|for (var i = 0; i < ; i++) {}
=for (var i = 0; i < |; i++) {
=	//
=}
 
ford
|for (Dataset.Open(); !Dataset.IsEOF; Dataset.GotoNext()) {
=for (Dataset.Open(); !Dataset.IsEOF; Dataset.GotoNext()) {
=	|
=}
 
ife
|if () {} else {}
=if (|) {
=	//
=} else {
=	//
=}
 
while
|while () {}
=while (!|Dataset.IsEOF) {
=	//
=	Dataset.GotoNext();
=}
 
switch
|switch () {}
=switch (|) {
=case 1: //
=	break;
=case 2: //
=	break;
=case 3: //
=	break;
=}
 
?:
| <if_condition> ? <then_operation> : <else_operation> 
= (| ? : )
 
with
|with () {}
=with (|) {
=	//
=}
 
forj
|for (var j in |) {}
=for (var j in |) {
=	//
=}
 
main
|function Main
=function Main() {
=	|
=}
 
isadm
|Connector.CurrentUser.IsAdmin
=var IsAdmin = Connector.CurrentUser.IsAdmin;
=if (IsAdmin) {
=	|
=}
 
curcon
|Connector.CurrentUser.ContactID
=var ContactID = Connector.CurrentUser.ContactID;
=|
 
emptygrd
|GridNotContainsAnyRecords
=var ContractIDs = BaseWorkspace.Grid.SelectedIDs;
=var Count = ContractIDs.Count;
=if (!Count || IsDatasetEmpty(dlContracts.Dataset)) {
=	ShowWarningDialog(GridNotContainsAnyRecords);
=	return;
=}
=|
 
exec
|  Run stored proc from TS
=var Parameters = System.CreateObject('TSObjectLibrary.Parameters');
=// Input params
=//pdtIdentity
=//pdtInteger
=//pdtFloat
=//pdtString
=//pdtDateTime
=//pdtBoolean
=//pdtBlob
=//pdtGUID
=//pdtUnicodeString
=//pdtFunction
=CreateSPParameter(Parameters, '|<Param1Name>', pdtGUID, <ParamValue>);
=// Output param
=// Parameters('<Param1Name>').ParamType = 1;
=CreateSPParameter(Parameters, '<Param2Name>', pdtDateTime, <ParamValue>);
=try {
=	var SQL = 'EXEC dbo.<ProcName> :<Param1Name>, :<Param2Name>';
=	Connector.DBEngine.ExecuteCustomSQL(SQL, Parameters);
=} catch(e) {
=	Log.Write(2, e.message);
=}
 
wnd
|show window
=var wnd = GetSingleItemByCode('|', '');
=wnd.Prepare();
=wnd.Show();
 
selectdata
|show SelectData window
=var Dataset = Services.GetNewItemByUSI('|');
=var SearchFieldNames = 'Name';
=var DisplayFieldNames = 'Name';
=var KeyFieldName = 'ID';
=var KeyValue = '';
=var SearchFieldName = 'Name';
=var SearchValue = '';
=Dataset.Open();
=ShowSelectDataWindow(Dataset, SearchFieldNames, DisplayFieldNames,
=	KeyFieldName, KeyValue, SearchFieldName, SearchValue, Self,
=	'', false, true, null, null, false, null, false);
 
try
|try{}finally{}
=try {
=	|
=} catch(e) {
=	Log.Write(2, e.message);
=}
 
tryf
|try {} finally {}
=try {
=	|
=} catch(e) {
=	Log.Write(2, e.message);
=} finally {
=	//
=}
 
tryc
|try {} catch {}
=try {
=	|
=} catch(e) {
=	if ('message' in e) {
=		System.MessageDialog(e.message, mdtWarning, mdbOK, 0);
=	} else {
=		System.MessageDialog(e.Message, mdtWarning, mdbOK, 0);
=	}
=}
 
clocks
|Sand clock
=try {
=	System.BeginProcessing();
=	System.ProcessMessages();
=	|
=} finally {
=	System.EndProcessing();
=}

Если нет замечаний у более чем одного пользователя :smile:, то я буду ее отправлять в разработку...

Может ds вместо da?
wh вместо while?
empty вместо ie?

"Alexandr Kravchuk" написал:Может ds вместо da?

тут конечно на любителя. Лично мне нравится da потому что буква А находится ближе к шифту
и так как-то созвучней =)

"Alexandr Kravchuk" написал:wh вместо while?

согласен.

"Alexandr Kravchuk" написал:empty вместо ie?

дольше писать, а используется очень часто.

Я за "da" вместо "ds". "da" больше мне запоминается, и если я начал писать da а потом вспомнил, что есть автоподстановка, то просто нажму Shift + пробел и все:)

"ie" сто раз в день пользуюсь. empty сильно долго писать, с моей скоростью набора все равно что IsEmptyValue.

Вношу эти изменения в 3.3.2.258, 3.4.0.112 и 3.4.1.21

Показать все комментарии