Как видно из сабжа, речь пойдет об использовании автоподстановки при разработке. Почему то эта возможность в Администраторе 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);
=}
По-сути, никто нам не мешает вставлять целые реализации функционала или отдельные функции. Прикольно?
Ко мне поступила информация что в скором времени планируется добавления улучшенной версии 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 с полным набором вариантов выхода, лишние строки легко удаляются...
Оставьте 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); = } =}
"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