Jak poprawnie zaprojektować wywołania zwrotne?

Jak poprawnie zaprojektować wywołania zwrotne?
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Gra "Snake" (mniej lub bardziej klasyczna wersja). Sytuacja wyjściowa jest taka:

  • Wąż to obiekt snake – przechowuje zbiór segmentów węża.
  • Segment węża to obiekt snakeSegment – składa się z aktualnej pozycji oraz aktualnego kierunku (tzn. takiego, "w którym idzie").
  • "Pozycja skrętu" jest to obiekt breakPoint (nie gańcie mnie za tę roboczą nazwę) – składa się z pary (pozycja, kierunek). Oznacza pozycję, w której dany segment węża musi skręcić w określonym kierunku (ponieważ został naciśnięty klawisz).
  • Pozycje skrętu są przechowywane w obiekcie breakPoints (zawiera ich tablicę).
  • Wąż może zbierać punkty – feed – które są przechowywane w obiekcie feeds (zawiera ich tablicę). UPDATE: Obiekt feed (ten bez "s") składa się z pozycji oraz wartości (ile punktów jest do zebrania w tym punkcie... eee... wiadomo, o co chodzi).

Podczas gry:

  • Wybór nowej pozycji jest ograniczony wielkością planszy oraz pozycjami innych segmentów węża.
  • Wybór (czy "wytyczenie"?) nowego kierunku dla każdego z segmentów jest ograniczony tym, czy istnieje dla danej pozycji segmentu punkt skrętu.

I teraz kod ruchu w obiekcie węża wygląda tak:

Kopiuj
exports.snake = {
    ...,
    move: function (
        isPositionForbidden, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        doWhenPositionForbidden, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        isBreakPointPosition, // wywołanie zwrotne; parametr – segmentPosition (tzn. pozycja tego segmentu węża, dla którego ta funkcja jest wywoływana)
        getNewDirectionWhenBreakPointPosition, // wywołanie zwrotne; parametr – segmentPosition (tzn. pozycja tego segmentu węża, dla którego ta funkcja jest wywoływana)
        isFeedPosition, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        doWhenFeedPosition // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
    ) {
        ...
    },
    ...
};

Wywoływane jest to w głównej funkcji programu. Jak widać, są tu trzy grupy:

  1. sprawdzanie, czy wąż wyszedł poza tablicę lub wszedł na samego siebie, oraz obsługa sytuacji, jeśli tak;
  2. sprawdzanie, czy poszczególne segmenty natrafiły na pozycję skrętu, oraz obsługa sytuacji, jeśli tak;
  3. sprawdzanie, czy wąż natrafił na punkt, oraz obsługa sytuacji, jeśli tak.

Założeniem jest, że wąż nie wie nic o planszy, pozycjach skrętu ani punktach do zebrania. Zna jedynie swoje segmenty. Podobnież, segment nie wie nic o tych trzech rzeczach – zna jedynie swoją pozycję oraz kierunek. Dopiero na poziomie pozycji można określić, czy jest ona "niedozwolona" (forbidden), czy jest pozycją skrętu, oraz jest na niej punkt do zebrania.

Problemem jest to, że jest to 6 wywołań zwrotnych zawierających trzy grupy. Próbowałem, ale nie mam pomysłu, jak to można zapisać, hm... w bardziej skonsolidowany sposób? Inaczej? Przenieść może jakąś część logiki dokądś?


PS. Przy czym, jak widać, funkcja obsługi pozycji skrętu musi zwracać nowy kierunek – co powiększa problem, ponieważ burzy jednolitość tych trzech grup.


PS2: Pytajcie, jak coś niejasno napisałem. Nad taką logiką myślałem kilka dni, więc jest ona, patrząc z mojej strony, sophisticated.


edytowany 14x, ostatnio: Silv
Silv
Sam nie wiem, czy to bardziej nie nadaje się do działu "Inżynieria oprogramowania".
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
1

Może nie składaj wszystkiego w metodzie .move(), skoro masz jak piszesz trzy "grupy",gdzie pierwszy element grupy to w zasadzie warunek, to możesz go użyć wewnątrz drugiego elementu. Masz wtedy trzy argumenty zamiast sześciu.

Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

@Maciej Cąderek: tak, myślałem nad tym, ale jakbyś w takim razie nazwał je?


Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
0

No na podstwaie tego co widzę to Ci nie powiem, bo teraz niektóre nazwy są imo skopane - nie mówią co funkcja robi, tylko kiedy to robi (doWhenPositionForbidden, doWhenFeedPosition). Ale strzelam, że coś w stylu:

Kopiuj
handleCollision()
handleDirectionChange()
handleFeeding()

?

Edit:
W sumie pasuje do punktów 1, 2, 3 w opisie.

edytowany 1x, ostatnio: Maciej Cąderek
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Myślałem nad takimi, i doszedłem do wniosku, że bardziej jest już intuicyjne rozdzielenie logiki sprawdzania i obsługi. handleCollision (czy też raczej handleForbiddenPosition, bo wyjście poza planszę nie jest kolizją) nie oznacza dla mnie, że sprawdza pozycję, tylko że to już jest pewne, że pozycja jest taka a taka. Nie byłoby w tym nic złego, i pewnie przyjąłbym takie rozwiązanie, gdyby sprawdzenie pozycji zależało od węża. Problem jednak w tym, że zarówno sprawdzenie danej pozycji, jak i jej obsługa nie należy do logiki węża, więc obie rzeczy muszą być podane jako wywołania zwrotne.


PS: Może jako jedno wywołanie, może dwa, może nawet więcej, ale nie mogę w nazwie zakładać, że wąż sam sprawdzi pozycję – bo nie może.


edytowany 3x, ostatnio: Silv
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
0

wyjście poza planszę nie jest kolizją

Jak kończy grę (?) to czemu niby nie jest kolizją?

handleCollision [...] nie oznacza dla mnie, że sprawdza pozycję, tylko że to już jest pewne, że pozycja jest taka a taka.

Wiesz, zawsze możesz nazwać to handlePotentialCollision czy coś w tym stylu, ale wiele to nie zmienia.

obie rzeczy muszą być podane jako wywołania zwrotne

W jakim sensie muszą? Technicznie czy "filozoficznie"?
Ogólnie to w taki sposób metoda .move() robi kilka rzeczy na raz - co też idealne nie jest.

Freja Draco
Freja Draco
  • Rejestracja:około 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
4
Silv napisał(a):
  • "Pozycja skrętu" jest to obiekt breakPoint (nie gańcie mnie za tę roboczą nazwę) – składa się z pary (pozycja, kierunek). Oznacza pozycję, w której dany segment węża musi skręcić w określonym kierunku (ponieważ został naciśnięty klawisz).

Po grzyba w ogóle przechowujesz coś takiego dla każdego segmentu? W zupełności wystarczy ci zmienna przechowująca aktualny kierunek poruszania się węża i tablica przechowująca współrzędne jego segmentów. W każdym kolejnym kroku:

  • dodajesz nowy element do tablicy o współrzędnych zależnych od aktualnego kierunku poruszania się,
  • zdejmujesz ostatni element tablicy (koniec ogona węża).

Ew. w sytuacji, gdy wąż w danym kroku je, pomijasz usuwanie ogniwa z ogona.


Zobacz pozostałe 23 komentarze
baant
@Silv: czy ty sypiasz po 2h dziennie?
Freja Draco
Freja Draco
Poza ID segment ma pozycję, kierunek oraz znak do wyświetlania. Ta trójka nie musi jednak wyznaczać segmentu, jeśli, przykładowo, jeden wejdzie na drugi. W ten sposób zabronione byłoby porównywanie segmentów Nie wiem, czy dobrze rozumiem twój tok rozumowania, ale jeżeli robisz Węża działającego jak klasyczny Snake, to każdy kolejny segment idzie na sztywno tą samą drogą, którą przebyła wcześniej głowa i jeśli ona nie weszła wcześniej z niczym w kolizję, to każdy kolejny segment podążający jej ścieżką też ma zagwarantowaną bezkolizyjność.
Freja Draco
Freja Draco
A jutro przyjdzie Fre i wszystkie moje inżynierskie rozważania podsumuje jednym optymalizującym stwierdzeniem a proszę: KISS - https://pl.wikipedia.org/wiki/KISS_(regu%C5%82a) ;)
Silv
@baant: Nie, sypiam tak jak większość. Po prostu w innych godzinach. ;)
Silv
Oczywiście, o KISS można zapomnieć. Dzięki, Fre.
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

@Maciej Cąderek: Wyjście poza planszę nie jest kolizją w moim rozumieniu, bo "kolizja" musi zawsze następować z czymś. Poza planszą nie ma nic, więc nie ma z czym kolidować. Można to nazwać, i tak to nazwałem, "pozycją zabronioną niedozwoloną"; nazwa ta wynika po części z logiki gry – najpierw wąż się porusza, a potem sprawdzam, czy pozycja jest zabroniona niedozwolona, a nie odwrotnie. Może mógłbym zrobić odwrotnie (i jakoś jeszcze inaczej nazwać)... ale nie rozpatrywałem tego i nie mam ochoty przepisywać gry na nowo (już raz przepisałem na inną logikę).

Wiesz, zawsze możesz nazwać to handlePotentialCollision czy coś w tym stylu, ale wiele to nie zmienia.

O to chodzi, to nic nie zmienia. W moim odczuciu nazwy takie jak doIfOK czy doIfFailed byłyby najlepsze, ale na razie nie widzę, jak by można było je wykorzystać...

W jakim sensie muszą? Technicznie czy "filozoficznie"?

Obie rzeczy muszą być podane jako wywołania zwrotne w sensie raczej filozoficznym. Czy logika gry Snake jako takiej (poza moją implementacją) pozwoliłaby na inną filozofię (mniej wywołań zwrotnych)? Być może tak, ale nie wiem, jak by to miało wyglądać. Nie wiem, czy byłoby lepsze od tego podejścia – "lepsze" na przykład w sensie posiadania mniejszej liczby zależności (looser coupling).

Ogólnie to w taki sposób metoda .move() robi kilka rzeczy na raz - co też idealne nie jest.

Właśnie dziś stojąc na przystanku o tym sobie myślałem. Być może nie jest to idealne, i być może zmienię coś innego w tej metodzie niż liczba wywołań zwrotnych.


@Freja Draco: nie myślałem o takim podejściu. Obecnie obliczam współrzędne dla każdego segmentu, a Ty proponujesz, abym nie tylko liczył, ale również usuwał i dodawał segmenty (przy dodawaniu licząc pozycję). Dzięki! Pomyślę nad tym. Tak na szybko – nie wydaje się to ani gorsze, ani lepsze; ale każde nowe podejście (jak nowe maksimum lokalne) będzie lepsze niż międlenie starego (jak stare minimum lokalne).


UPDATE: @Maciej Cąderek, metoda move nie robi aż tyle chyba, co Ty masz na myśli – to są wywołania zwrotne, powinny one działać podobnie jak promises – wywołuję je po zakończeniu danej metody. Tzn. aktualnie w metodzie move jest to trochę pomieszane, ale kod przekazanych do niej funkcji jest wywoływany w większości "po" wykonaniu jej logiki.


edytowany 7x, ostatnio: Silv
Zobacz pozostałe 7 komentarzy
Maciej Cąderek
Maciej Cąderek
Z tą kolizją to dzielisz włos na czworo - to standardowa terminologia w grach. Obojętnie czy uderzasz w ścianę, wychodzisz poza planszę czy wchodzisz w chmurę dymu. No ale oczywiście możesz używać własnych, bardziej specyficznych określeń, ja się tu nie czepiam ;)
Silv
Możesz mieć rację, ale... zmiany powinny następować, by uzyskać (wyższą) wartość dodaną. Przynajmniej tak bym to widział u siebie... <idealista>
Freja Draco
Freja Draco
Wyjście poza planszę nie jest kolizją w moim rozumieniu, bo "kolizja" musi zawsze następować z czymś. Poza planszą nie ma nic I tym sposobem wchodzimy w dziedzinę filozofii programistycznej i stawiamy ważkie pytania na miarę: Dlaczego jest raczej coś niż nic ;)
Silv
@Freja Draco: :D Trafnie. Niemniej to jest jeszcze poziom języka, a zbiór pozycji jest w mojej aplikacji nieskończony (dlatego to ma u mnie sens).
Silv
PS. Plansza jedynie nakłada na niego ograniczenia.
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
2

Ty proponujesz, abym nie tylko liczył, ale również usuwał i dodawał segmenty (przy dodawaniu licząc pozycję).

Nie do końca, w podejściu @Freja Draco nie ma właśnie za wiele liczenia, obliczasz tylko nową pozycję głowy, nie przejmujesz się resztą.

Silv
Prawda! Mój błąd.
Silv
Nieintuicyjne pierwszym wrażeniem, ale optymalizacja może się przydać...
Freja Draco
Freja Draco
  • Rejestracja:około 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
2

Tak jeszcze odnośnie detekcji kolizji. Nie wiem, na ile da się to zrealizować w twoim przypadku, ale przy wyświetlaniu czegoś takiego w trybie znakowym, nie trzeba nawet sprawdzać kolizji przelatując tablicę wszystkich ogniw węża, żeby sprawdzić, czy wąż sam siebie nie ugryzie, bo wystarczy odczytać jaki znak siedzi aktualnie w polu na które głowa chce wejść. W trybie graficznym też w sumie da się odczytać kolor piksela. No i masz jeden test, zamiast kilkudziesięciu/kilkuset przy dłuższym wężu.


Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
1

@Freja Draco: dzięki, niemniej tak robię właśnie. :) Ale po co odczytywać kolor piksela...?


edytowany 1x, ostatnio: Silv
Zobacz pozostałe 4 komentarze
Silv
A, canvas... nie pomyślałem. :) Na razie robię w konsoli.
Silv
Tzn. zostanę na konsoli.
Freja Draco
Freja Draco
E... w konsoli przeglądarki?
Silv
Nie, nie, tworzę w Node.js.
Silv
Jakoś mi web przestał podchodzić.
Freja Draco
Freja Draco
  • Rejestracja:około 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
1

Jak już się bawimy w filozofowanie i kombinacje, to mam jeszcze jeden pomysł na realizację Węża. Całość opiera się na następujących zmiennych:

  • tablicy komórek, każda komórka może zawierać:
    • pustkę: 0,
    • ziarenko do zjedzenia: -1,
    • ogniwo węża: liczba milisekund uniksowych opisujących czas narodzin danej komórki,
  • współrzędnych głowy i ogona,
  • aktualnym kierunku poruszania.

W każdym kolejnym kroku:

  • w oparciu o współrzędne głowy i kierunek poruszania

    • wyliczasz gdzie przesunie się głowa
    • testujesz ew. kolizje,
    • wypełniasz kolejną komórkę ogniwem węża,
  • w oparciu o współrzędne ogona:

    • zdejmujesz z planszy czubek ogona,
    • wyszukujesz współrzędne nowego czubka ogona, sprawdzając 8 komórek sąsiadujących z właśnie wyczyszczoną, wybierasz tą, która zawiera najmniejszą liczbę większą od zera.

Przy czym wskazane byłoby odwrócić kolejność i najpierw usuwać ogon i dopiero wtedy testować kolizje i rysować głowę.

Trochę cudactwo, ale jeśli i tak chcesz tam robić jakąś tablicę komórek, to nie potrzebujesz drugiej osobnej na samego węża :)


Silv
Po co wyszukiwać współrzędne nowego czubka ogona?...
Freja Draco
Freja Draco
Po to, żebyś w następnym kroku wiedział, gdzie on się znajduje i wiedział, co masz usunąć.
Silv
Ach, no tak. Bo obecnie przechowuję listę segmentów. Intuicja, intuicja...
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
1

Ostatecznie przemodelowałem architekturę programu, niemniej wywołania zwrotne zostały zmienione jedynie lekko.

Zdecydowałem się jednak porzucić projekt. W związku z tym wątek pozostawiam bez zaakceptowanej odpowiedzi.

Niemniej, bardzo pomogła mi wskazówka @Freja Draco w tym poście: https://4programmers.net/Forum/1613705


edytowany 2x, ostatnio: Silv
Freja Draco
Freja Draco
A proszę :)
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:minuta
  • Lokalizacja:Tuchów
  • Postów:12166
1

Najprościej to po prostu w każdym ruchu wstawić nową głowę i usunąć ogon. Jeśli wąż zeżarł jabłko to tylko wstawić głowę. Wszystkie dane w segmentach inne niż współrzędne (dla głowy dodatkowo kierunek) oraz czynności inne niż opisane w pierwszym zdaniu są absolutnie zbędne. ;)

Kiedyś robiłem konsolowego węża, ale z bardziej skomplikowaną mechaniką. Oprócz zwykłego poruszania się i zjadania jabłek, można było przechodzić przez segmenty, w których znajdowało się jabłko (były renderowane jaśniejszym kolorem, żeby było wiadomo gdzie są). Obsługa punktów przecięcia węża znacznie uatrakcyjniała rozgrywkę.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 8x, ostatnio: flowCRANE
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)