Jak mapować argumenty wiersza poleceń na wywołania funkcji?

Jak mapować argumenty wiersza poleceń na wywołania funkcji?
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Natrafiłem na problem, który zaczyna mnie denerwować – niby proste, a trudne.

Mój konsolowy program do generowania schematów blokowych w ASCII art (JavaScript, Node.js; pisałem już o nim w tym wątku) przyjmuje kilka argumentów. Jego wywołanie ma taki schemat:

Kopiuj
node nazwa-pliku.js {nazwa-parametru} {lista-wartości} {nazwa-parametru} {lista-wartości} ... -- {lista-zawartości-bloków}

Argumenty {nazwa-parametru} mają zdefiniowane nazwy w kodzie programu jako typ String – na przykład "-w" czy "-b" – a argumenty {lista-wartości} następujące po poszczególnych parametrach odpowiadają tym parametrom. Czyli każdemu argumentowi-parametrowi może odpowiadać lista argumentów-wartości o dowolnej długości – 1, 2, 3… (w programie, dla wygody użytkownika, ustaliłem długości tych list na od 1 do 2). Przykładowo wygląda to tak:

Kopiuj
paramsConfig = {
  "-w": {
    // Tu konfiguracja dla parametru "-w"
  },
  "-b": {
    // Tu konfiguracja dla parametru "-b"
  },
  ...
}

Z drugiej zaś strony użytkownik wywołując program spodziewa się schematu blokowego, który jest generowany przez funkcję constructSchema(schemaConfig). Żeby stworzyć schemat, muszę wcześniej stworzyć poszczególne jego bloki, wywołując funkcję constructBlock(blockConfig). Obie te funkcje przyjmują jako swoje argumenty swoją konfigurację.

Problem, z którym przychodzę w tym wątku, polega na tym, że muszę zmapować jeden zestaw wartości na drugi – konfigurację podaną w wierszu poleceń na konfiguracje w wywołaniach funkcji.

Obecne moje rozwiązanie wygląda tak, że wywołuję funkcje constructSchema oraz constructBlock i ręcznie mapuję jedne wartości na drugie. Czyli przykładowo:

Kopiuj
const schema = constructSchema({
  {nazwa-właściwości-A}: parsedArgs["-w"],
  {nazwa-właściwości-B}: parsedArgs["-b"],
  {nazwa-właściwości-C}: parsedArgs["-w"]
  ...
});

gdzie parsedArgs jest obiektem zawierającym argumenty z wiersza poleceń zwalidowane i w odpowiednim formacie.

Jest to rozwiązanie o tyle brzydkie, że za każdą zmianą konfiguracji podawanej w wierszu poleceń:

  1. dla każdego zmienionego parametru muszę wyszukiwać miejsca w wywołaniach funkcji, w których go podałem;
  2. dla każdego zmienionego parametru, jeśli zmieniła się jego nazwa, to muszę zmieniać ją zarówno w wywołaniu funkcji, jak i w konfiguracji wiersza poleceń.

Że nie wspomnę już o tym, że jak obecnie próbuję przepisać to na coś lepiej zaprojektowanego, to nie mam pomysłu, w którym miejscu podawać wartości domyślne dla parametrów (niektóre parametry wiersza polecenia nie są wymagane, a inne są). To nie należy już to mojego problemu, z którym przychodzę, ale gdyby ktoś dodatkowo mógł coś zaproponować w tym zakresie, to byłbym wdzięczny.


UPDATE: Podsumowując, pisanie tego posta zajęło mi więcej czasu, niż powinienem przeznaczyć na projektowanie takiej funkcjonalności mapowania… a jednak nie potrafię. Czy ktoś więc miałby jakieś pomysły, jak wykonać takie mapowanie, o jakim piszę? Może w którymś miejscu opisuję rzecz bezsensowną z punktu widzenia takiego programu? Może w ogóle nie ma problemu, tylko ja go wymyśliłem, przeinżynierowywując wszystko? Chętnie przyjmę krytykę, byle tylko udało się to zaprojektować.


edytowany 2x, ostatnio: Silv
Haskell
  • Rejestracja:ponad 9 lat
  • Ostatnio:11 miesięcy
  • Postów:4700
2
Silv napisał(a):

Jest to rozwiązanie o tyle brzydkie, że za każdą zmianą konfiguracji podawanej w wierszu poleceń:

  1. dla każdego zmienionego parametru muszę wyszukiwać miejsca w wywołaniach funkcji, w których go podałem;
  2. dla każdego zmienionego parametru, jeśli zmieniła się jego nazwa, to muszę zmieniać ją zarówno w wywołaniu funkcji, jak i w konfiguracji wiersza poleceń.

Trzymaj konfigurację w osobnym obiekcie. Następnie we wszystkich miejscach w kodzie korzystaj z tego obiektu. Dzięki temu jeżeli zmieni się nazwa parametru w lini poleceń, to zmienisz tylko kod odpowiedzialny za zasilenie tego obiektu, a sam obiekt się nie zmieni.


Zaglądali do kufrów, zaglądali do waliz, nie zajrzeli do d**y - tam miałem socjalizm. Czesław Miłosz
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Właśnie wczoraj sobie pomyślałem, po stworzeniu wątku, że nazwy parametrów wiersza poleceń ostatecznie mogę wyabstrahować z kodu do jakiegoś zewnętrznego obiektu. Może będzie to więcej abstrakcji… ale może bardziej czytelne… Zobaczę jeszcze, jak to może wyglądać dokładnie.

Nadal jednak zostanie problem, że nie podoba mi się ręczne mapowanie parametrów wiersza poleceń na właściwości obiektów w wywołaniach funkcji. Główna rzecz w tym to to, że te pierwsze nie mapują się jeden do jednego na te drugie. Mam, przykładowo, parametr o nazwie -h, który należy podać w wywołaniu w ten sposób:

Kopiuj
node nazwa-pliku.js -h {liczba-całkowita >= 0} {liczba-całkowita >= 0}

Pierwsza liczba całkowita odpowiada minimalnej, a druga maksymalnej wysokości, jaką może przyjmować jeden blok. I tak, w wywołaniu funkcji podaję te dwie liczby oddzielnie:

Kopiuj
constructBlock({
  ...
  minDeclaredBlockHeight = parsedArgs["-h"][0],
  maxDeclaredBlockHeight = parsedArgs["-h"][1],
  ...
});

Albo więc zmieniłbym konfigurację wiersza poleceń, by każdej właściwości odpowiadał jeden parametr, albo zmieniłbym wywołania funkcji, by każdemu parametrowi odpowiadała jedna właściwość. Obie opcje mi się nie podobają, bo z jednej strony chciałbym uprościć wywołanie programu (nie dając użytkownikowi zbyt wielu parametrów do wyboru / wymaganych), a z drugiej strony chciałbym, by wywołania funkcji były możliwie jak najbardziej logiczne w sensie domeny programu.


edytowany 2x, ostatnio: Silv
Haskell
  • Rejestracja:ponad 9 lat
  • Ostatnio:11 miesięcy
  • Postów:4700
1

Może zainteresuj się też modułem yargs, który znacznie ułatwia pracę z argumentami wywołania aplikacji.


Zaglądali do kufrów, zaglądali do waliz, nie zajrzeli do d**y - tam miałem socjalizm. Czesław Miłosz
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Dzięki; na razie sam spróbuję pokombinować; mam nadzieję, że coś dziś wymyślę; napiszę.


FA
  • Rejestracja:około 5 lat
  • Ostatnio:mniej niż minuta
  • Lokalizacja:warszawa
  • Postów:301
2

Na pewnym podstawowy poziomie ifologie, mapowanie, czy hardkodowanie jest nie do uniknięcia i to chyba taki przypadek, wiec cudów nie będzie.

Wydaje mi się ze nie potrzebnie masz płaską structure w tej konfiguracji. Gdyby DeclaredBlockHeigh był obiektem z propertisami min, max(albo min, max były by obiektami z property DeclaredBlockHeigh :P ) używał byś ich prawie tak samo jak teraz, za to dało by rade to zmapować półautomatycznie,.
coś w stylu.

Kopiuj
mapConfig(parsedArgs, '-h', 'DeclaredBlockHeigh', ['min','max'])
mapConfig(parsedArgs, '-u', 'UserName')
/*jeśli udało by się wcisnąć parsedArgs pod spodem przez jakiś closure,
 wyszło by coś takiego, czyli dość blisko minimalnej ilości pracy jaką trzeba wykonać*/
mapConfig('-h', 'DeclaredBlockHeigh', ['min','max'])
mapConfig('-u', 'UserName')

console.log(config.DeclaredBlockHeigh.min)
console.log(config.UserName)

ED. tak mnie naszło ze przez nature js można zachować taka samą płaską strukturę konkatenując string, i ustawiać na ich podstawie properties. Wynik byłby taki.

Kopiuj
mapConfig('-h', ['Min','Max'], 'DeclaredBlockHeigh',)
mapConfig('-u', null, 'UserName')

console.log(config.MinDeclaredBlockHeigh)
console.log(config.UserName)
edytowany 17x, ostatnio: _flamingAccount
Silv
albo min, max były by obiektami z property DeclaredBlockHeigh – chyba się powstrzymam przed tak niestandardową konfiguracją. :P
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Trochę zmieniłem. Załączam całą konfigurację wraz z domyślnymi wartościami – na razie zmapowaną tylko w komentarzach:

Kopiuj
const contentBlockConfig = {
            innerWidth: 0, // -w[0]
            innerHeights: {
                min: 0, // -h[0]
                max: 0 // -h[1]
            },
            borders: {
                top: {
                    width: 0, // -b[0]
                    char: "" // -b[1]
                },
                right: {
                    width: 0, // -b[0]
                    char: "" // -b[1]
                },
                bottom: {
                    width: 0, // -b[0]
                    char: "" // -b[1]
                },
                left: {
                    width: 0, // -b[0]
                    char: "" // -b[1]
                }
            },
            paddings: {
                top: {
                    width: 0 // -p[0]
                },
                right: {
                    width: 0 // -p[0]
                },
                bottom: {
                    width: 0 // -p[0]
                },
                left: {
                    width: 0 // -p[0]
                }
            },
            widerThanHigher: false, // (currently no CLI parameter)
            overflowIndicator: "...", // (currently no CLI parameter)
            fillChar: "" // -f[0]
        };

const separatorBlockConfig = {
            width: 0 // -s[0]
        };

const schemaConfig = {
            colsNumber: undefined, // -c[0]
            link: {
                pointer: {
                    width: 0, // (currently no CLI parameter)
                    char: "" // (currently no CLI parameter)
                },
                line: {
                    width: 0, // (currently no CLI parameter)
                    char: "" // (currently no CLI parameter)
                }
            },
            fillChar: " " // (currently no CLI parameter)
        };

const contents: []; // --[0]

PS. Tablicę contents będę wyodrębniać z listy parametrów przed jakimkolwiek mapowaniem – tak wyszło mi, że będzie łatwiej – więc nie liczę jej jako parametr wiersza poleceń (podaję ją wyżej tylko dla pokazania, jak to u mnie wygląda).


PS2. @_flamingAccount: nie bardzo rozumiem, jak ma wyglądać funkcja mapConfig.


UPDATE: Przygotowałem sobie na razie coś takiego, co poniżej zamieszczam – ale tak się chyba nie da. W każdym razie nie wiem, co dalej.

Kopiuj
const paramsPropertiesMapping = {
    "-w": [
        {
            // Here map property contentBlockConfig.innerWidth
        }
    ],
    "-h": [
        {
            // Here map property contentBlockConfig.innerHeights.min
        },
        {
            // Here map property contentBlockConfig.innerHeights.max
        }
    ],
    "-b": [
        {
            // Here map properties contentBlockConfig.borders.top.width, contentBlockConfig.borders.right.width, contentBlockConfig.borders.bottom.width and contentBlockConfig.borders.left.width
        },
        {
            // Here map properties contentBlockConfig.borders.top.char, contentBlockConfig.borders.right.char, contentBlockConfig.borders.bottom.char and contentBlockConfig.borders.left.char
        }
    ],
    "-p": [
        {
            // Here map properties contentBlockConfig.paddings.top.width, contentBlockConfig.paddings.right.width, contentBlockConfig.paddings.bottom.width and contentBlockConfigpaddings.left.width
        }
    ],
    "-f": [
        {
            // Here map property contentBlockConfig.fillChar
        }
    ],
    "-s": [
        {
            // Here map property separatorBlockConfig.width
        }
    ],
    "-c": [
        {
            // Here map property schemaConfig.colsNumber
        }
    ]
};

edytowany 12x, ostatnio: Silv
Silv
@_flamingAccount: wzmiankowałem Cię w edycji posta.
FA
  • Rejestracja:około 5 lat
  • Ostatnio:mniej niż minuta
  • Lokalizacja:warszawa
  • Postów:301
1

Brakuje abslugi pojedyczego parametru ale poza tym działa :)

Kopiuj
	parsedArgs = {
    '-h': [12,123],
	'-b': [10,'abc']
}
let contentBlockConfig = {};

function wrap(config,args){
   let that = {}
   function moveTo(arg, path){
         return {set:function(properties,valuesNames){
			properties.forEach( prop => {
				mapConfig(arg, path + "." + prop,valuesNames)
			});}
		 }
   }   
   function mapConfig(arg, path, valuesNames){
		 let values =args[arg];
		 let parts = path.split('.');
		 parts = parts.filter(Boolean)
		 for(let i =0; i < valuesNames.length; i++){
			setValue(parts,valuesNames[i],values[i])
		 }
	}
	
	function setValue(parts,valueName, value){
		let current = config;
		parts.forEach(part =>{
			if(current[part] === undefined)  {
				current[part] = {};
			}
			current = current[part];
		})
		current[valueName] = value;
	}
	that.set = mapConfig;
	that.moveTo = moveTo;
	return that;
}

let mapping = wrap(contentBlockConfig,parsedArgs);

mapping.set('-h','innerHeights',['min', 'max'])
mapping.set('-h','some.other.Heights',['min', 'max'])
mapping.moveTo('-b','borders').set(['top','botton','left','right'],['size','char'])
//przerobienie na coś takiego fajnie by wylgądało :D
mapping.moveTo('-b','borders').set(['top','botton','left','right']).values(['size','char'])

Nie wiem czy nie popsuje to intelisensu, wrazie gdyby tak mozesz startowo zaicjalizować config domyślnymi wartościami, a dopiero potem zmapować konsole
ed. nie odpalało sie bo zamieniłem podczas edycji posta -p na -b ale nie wszedzie :)

edytowany 8x, ostatnio: _flamingAccount
Silv
<próbuje to zrozumieć>
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

@_flamingAccount: co to jest parts.filter(Boolean);?


UPDATE:

Czy nie prościej zrobić, jak poniżej:

zamiast

Kopiuj
let that = {};
...
that.set = mapConfig;
that.moveTo = moveTo;
return that;

dać (ECMAScript 6, jak mi się zdaje)

Kopiuj
return {
  set: mapConfig,
  moveTo
};

PS2. A może to powyższe moje rozwiązanie zadziała inaczej niż Twoje (niezgodnie z oczekiwaniem)?


UPDATE:

@_flamingAccount: w oczekiwaniu, aż mi wytłumaczysz, po swojemu doszedłem na razie do czegoś takiego:

Kopiuj
const paramsMapping = {
    contentBlock: {
        innerWidth: {
            shortName: "-w",
            defaultValues: [0]
        },
        innerHeights: {
            shortName: "-h",
            defaultValues: [0, 0]
        },
        borders: {
            shortName: "-b",
            defaultValues: [0, ""]
        },
        paddings: {
            shortName: "-p",
            defaultValues: [0]
        },
        fill: {
            shortName: "-f",
            defaultValues: [""]
        }
    },
    separatorBlock: {
        shortName: "-s",
        defaultValues: [0]
    },
    schema: {
        colsNumber: {
            shortName: "-c",
            defaultValues: [undefined]
        }
    },
    content: {
        defaultValues: [""]
    }
};

exports.parseArgs = function (argsList) {
    const contents = getContentsOrDefault(paramsMapping.content[0], argsList);
    return {
        contentBlockConfig: {
            innerWidth: getArgOrDefault(paramsMapping.contentBlock.innerWidth, argsList),
            innerHeights: {
                min: getArgOrDefault(paramsMapping.contentBlock.innerHeights[0], argsList),
                max: getArgOrDefault(paramsMapping.contentBlock.innerHeights[1], argsList)
            },
            borders: {
                top: {
                    width: getArgOrDefault(paramsMapping.contentBlock.borders[0], argsList),
                    char: getArgOrDefault(paramsMapping.contentBlock.borders[1], argsList)
                },
                right: {
                    width: getArgOrDefault(paramsMapping.contentBlock.borders[0], argsList),
                    char: getArgOrDefault(paramsMapping.contentBlock.borders[1], argsList)
                },
                bottom: {
                    width: getArgOrDefault(paramsMapping.contentBlock.borders[0], argsList),
                    char: getArgOrDefault(paramsMapping.contentBlock.borders[1], argsList)
                },
                left: {
                    width: getArgOrDefault(paramsMapping.contentBlock.borders[0], argsList),
                    char: getArgOrDefault(paramsMapping.contentBlock.borders[1], argsList)
                }
            },
            paddings: {
                top: {
                    width: getArgOrDefault(paramsMapping.contentBlock.paddings[0], argsList)
                },
                right: {
                    width: getArgOrDefault(paramsMapping.contentBlock.paddings[0], argsList)
                },
                bottom: {
                    width: getArgOrDefault(paramsMapping.contentBlock.paddings[0], argsList)
                },
                left: {
                    width: getArgOrDefault(paramsMapping.contentBlock.paddings[0], argsList)
                }
            },
            widerThanHigher: false, // (currently no)
            overflowIndicator: "...", // (currently no)
            fillChar: getArgOrDefault(paramsMapping.contentBlock.fill[0], argsList)
        },
        separatorBlockConfig: {
            width: getArgOrDefault(paramsMapping.separatorBlock[0], argsList)
        },
        schemaConfig: {
            colsNumber: getArgOrDefault(paramsMapping.schema.colsNumber[0], argsList),
            link: {
                pointer: {
                    width: 0, // (currently no)
                    char: "" // (currently no)
                },
                line: {
                    width: 0, // (currently no)
                    char: "" // (currently no)
                }
            },
            fillChar: " " // (currently no)
        },
        contents: contents
    };
}

const getContentsOrDefault = function (argsList, argConfig) {
    // TODO
};

const getArgOrDefault = function (argsList, argConfig) {
    // TODO
};

PS3. Elementy tablicy paramsMapping mogą mieć dowolny poziom zagnieżdżenia. Dany element na najniższym poziomie reprezentuje jeden i tylko jeden parametr wiersza poleceń. — Obecnie w konfiguracji parametrów umieściłem nazwę skróconą i wartości domyślne; można dodać jeszcze np. nazwę rozszerzoną (tak jak zazwyczaj mają programy dostępne z wiersza poleceń). Długość tablicy z wartościami domyślnymi reprezentuje liczbę argumentów, które zostaną pobrane dla danej właściwości w funkcji getArgOrDefault. Dla parametru contents wymyśliłem (stety niestety) dowolną liczbę argumentów, więc musiałem zrobić oddzielną funkcję i wywoływać ją przed parsowaniem – jak pisałem wcześniej (poprawiłem teraz kod).


edytowany 17x, ostatnio: Silv
FA
Pewnie mozna i pewnie jest lepiej :), przywyczaiłem się pisać that :)
Silv
Weź mi to wytłumacz linijka po linijce, bo im dłużej czytam, tym głupszy się sobie wydaję.
Silv
No dobra, może przesadzam… ale tak samo głupi.
FA
  • Rejestracja:około 5 lat
  • Ostatnio:mniej niż minuta
  • Lokalizacja:warszawa
  • Postów:301
1

Weź mi to wytłumacz linijka po linijce

Nasz nasz plan to zrobić zeby nie trzeba było pisać nudnej formułki milion razy,

Kopiuj
config.property = args[consoleKey][0]

To można przerobić na

Kopiuj
function set(consoleKey, property){config[property] = args[consoleKey][0]}

Jest set to zgrabniejsze

Kopiuj
set('-u', 'userName')   vs config.username= args['-u'][0]

Nie działa to jeśli jest wiecej niż jedena wartość dla parametru z konsoli np MaxDeclaredBlockHeigh i MinDeclaredBlockHeigh
Można to poprawić dodając pętle

Kopiuj
function setManyAtOnce(consoleKey, properties){
    for(let i; i < properties.lenght; i++){
         config[properties[i]] = args[consoleKey][i]
    }
}
//uzycie
setManyAtOnce('-h',['MaxDeclaredBlockHeigh ','MinDeclaredBlockHeigh '])

to już rozwiązuje problem ale prawie 50% tekstu w tej lini to DeclaredBlockHeigh Można to poprawić robiąc hierarchie - odrobinę - miej płaską. O to mialem na mysli w poście bez implemenacji

Kopiuj
function setManyAtOnce(consoleKey, property, valuesNames){
    for(let i; i < properties.lenght; i++){
         config[property][valuesNames[i]] = args[consoleKey][i]
    }
}
//uzycie
setManyAtOnce('-h','DeclaredBlockHeigh '['min','max']);

Ten kod ma buga jeśli config.propety jest undefined, wiec trzeba to sprawdzić i stworzyć pusty obiekt w razie co.

Kopiuj
if(config[property] === undenined)
    config[property]  = {};

Kod zrobił się złożony z jak mieniłeś hierarchie i chciałem zaimplementować to niżej dla jednego argumentu.

// Here map properties contentBlockConfig.paddings.top.width, contentBlockConfig.paddings.right.width, contentBlockConfig.paddings.bottom.width and contentBlockConfigpaddings.left.width

To jest strasznie dużo rzeczy naraz a podajesz tylko jeden parametr wejściowy, wiec założyłem ze wszystkie 4 maja te samą wartość.

Pierwszym problemem jest dowolnie głeboka hierarchia. najprostszym ulepszeniem poprzedniego kodu jest zrobienie ze property przyjmuje wartości po kropce, tak jak w kodzie.

Kopiuj
         let parts = path.split('.');
         parts = parts.filter(Boolean)//usuwa puste stringi po splicie np jak kropka jest na poczatku

Przez to ze trzeba każdy obiek po kropce znacjonalizować przynajmniej pusty obiektem wydzieliłem set value jako funkcje.

Drugim problemem jest ustawienie kilku wartości naraz, kolejna pętla. Ja napisałem moveTo bo pomyslałem ze fajnie będzie symulować cd w konsoli, i drugi set bo ładnie tak wygląda :P. Jedynym technicznym argumentem jest to zeby trzymać niską liczbę parametrów w metodach :P

btw. temten kod wyzej działa tylko miałem pomieszane -p i -b w argumentach :P

edytowany 1x, ostatnio: _flamingAccount
Silv
Hej, dziękuję, ale ja już prawie własne rozwiązanie mam. :( To co teraz? Zaraz napiszę, co wymyśliłem poza tym, co na górze.
FA
nie miałem wcześniej czasu :)
Silv
Ja właśnie też, i dlatego dopiero teraz kontynuuję własne rozwiązanie. :D
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Postanowiłem troszkę zmienić dwie ostatnie funkcje, które podałem dwa posty wyżej, i zrobić takie coś, że dla każdej właściwości wywoływana jest funkcja o sygnaturze checkArg(argList, argConfig); zwraca ona obiekt z dwiema metodami o sygnaturach getArg() oraz getArgOrDefault(). Jeśli dla danej właściwości podanie wartości (=argumentu) jest wymagane, to wywołuję dla niej funkcję getArg, która w sytuacji braku rzuca wyjątkiem; jeśli nie, to wywołuję funkcję getArgOrDefault(), która w sytuacji braku bierze domyślną wartość.

Co do Twojego rozwiązania, to nadal nie bardzo rozumiem – ale nie wiem, czy jest sens próbować zrozumieć, skoro mam własne, które rozumiem.


PS. Pewnie jeszcze trochę muszę się JavaScriptu pouczyć, by zrozumieć.


UPDATE:

Po kilku próbach rozwiązania tego problemu w tę i w tamtą stronę wróciłem do rozwiązania z pierwszego postu (tego, które mi się nie podobało). Uznałem, że jest najbardziej czytelne. W skrócie: mam obiekt paramsConfigs oraz funkcję getConfig; ten pierwszy przechowuje główną konfigurację parametrów wiersza poleceń, a ta druga zwraca obiekt definiujący całą konfigurację aplikacji, w którym mapuję parametry wiersza poleceń na właściwości. Pełny kod dostępny jest już na GitHubie: https://github.com/silvuss/silvuss-paas

@_flamingAccount, wzmiankowałem Twoje rozwiązanie w sekcji Zasoby warte uwagi w README (https://github.com/silvuss/silvuss-paas#resources-worth-attention).


edytowany 6x, ostatnio: Silv
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)