Skip to content

Commit

Permalink
grrr
Browse files Browse the repository at this point in the history
  • Loading branch information
sharpchen committed Nov 9, 2024
1 parent 2200166 commit b95dac6
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 60 deletions.
25 changes: 25 additions & 0 deletions docs/document/Articles/docs/Duck Typing in CSharp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Duck Typing in CSharp

Currently only `GetEnumerator` and `Deconstruct` supports duck typing.
`Dispose` is just not available since you the compiler won't be able to know which one to pick if there's duplication.

```cs
using System.Collections;

using var foo = new Foo(); // [!code error]
foreach (var _ in new Foo()) ;
var (a, b) = new Foo();

class Foo;

static class FooExtension
{
public static void Dispose(this Foo self) => Console.WriteLine("Disposing Foo");
public static IEnumerator GetEnumerator(this Foo _)
{
for (int i = 0; i < 100; i++)
yield return i;
}
public static void Deconstruct(this Foo self, out object? a, out object? b) => a = b = null;
}
```
51 changes: 51 additions & 0 deletions docs/document/Avalonia/docs/2. Styling/AXAML/Adding New Style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Adding New Style

## With dotnet cli

```sh
dotnet new avalonia.styles -o Styles -n NewStyle
```

## Default Template

Style template ships with a portion for previewer where you can add testing controls to see whether those styles works properly.

```xml
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith> <!-- [!code highlight] -->
<Border Padding="20"> <!-- [!code highlight] -->
<!-- Add Controls for Previewer Here --> <!-- [!code highlight] -->
</Border> <!-- [!code highlight] -->
</Design.PreviewWith> <!-- [!code highlight] -->

<!-- Add Styles Here -->
</Styles>
```

## Define a Style

A style starts with a `Selector` which is the rule of target matching, for figuring out which control should have those contained styling.
Contained styling is some key-value pair on properties.

> [!NOTE]
> See [selector syntax]()
```xml
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith> <!-- [!code highlight] -->
<Border Padding="20"> <!-- [!code highlight] -->
<!-- Add Controls for Previewer Here --> <!-- [!code highlight] -->
</Border> <!-- [!code highlight] -->
</Design.PreviewWith> <!-- [!code highlight] -->

<!-- Add Styles Here -->
<Style Selector="TextBlock.h1"> <!-- [!code ++] -->
<Setter Property="FontWeight" Value="Bold" /> <!-- [!code ++] -->
<Setter Property="FontSize" Value="15" /> <!-- [!code ++] -->
<Setter Property="Margin" Value="5" /> <!-- [!code ++] -->
</Style> <!-- [!code ++] -->

</Styles>
```
19 changes: 19 additions & 0 deletions docs/document/Avalonia/docs/2. Styling/AXAML/Importing Styles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Importing Styles

```xml
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:control="using:AvaloniaApplication"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaApplication.MyUserControl">

<UserControl.Styles> <!-- [!code highlight] -->
<StyleInclude Source="MyStyles.axaml"></StyleInclude> <!-- [!code highlight] -->
</UserControl.Styles> <!-- [!code highlight] -->

<TextBlock Text="Hello from template" TextAlignment="Center" Width="200" Height="20"/>

</UserControl>
```
7 changes: 0 additions & 7 deletions docs/document/Avalonia/docs/2. Styling/Selector Syntax.md

This file was deleted.

45 changes: 0 additions & 45 deletions docs/document/Avalonia/docs/2. Styling/Style Isolation.md

This file was deleted.

136 changes: 135 additions & 1 deletion docs/document/Avalonia/docs/MVVM Essentials/4. Trigger a Command.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,137 @@
# Trigger a Command

## RelayCommand
`CommunityToolkit.Mvvm` uses `RelayCommand` to generate a field and property to represents the command for certain method.

## Code Emission

```cs
[RelayCommand]
private void RemoveItem(ToDoItemViewModel item)
{
ToDoItems.Remove(item);
}
```

Generated part as following:

- A field of type `RelayCommand`(not the `RelayCommandAttribute`)
- A property for the generated field typed as `IRelayCommand`.

```cs
/// <summary>The backing field for <see cref="RemoveItemCommand"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.3.0.0")]
private global::CommunityToolkit.Mvvm.Input.RelayCommand<global::ToDoList.ViewModels.ToDoItemViewModel>? removeItemCommand; // [!code highlight]
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand{T}"/> instance wrapping <see cref="RemoveItem"/>.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.3.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public global::CommunityToolkit.Mvvm.Input.IRelayCommand<global::ToDoList.ViewModels.ToDoItemViewModel> RemoveItemCommand => removeItemCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand<global::ToDoList.ViewModels.ToDoItemViewModel>(new global::System.Action<global::ToDoList.ViewModels.ToDoItemViewModel>(RemoveItem)); // [!code highlight]
```

## Behind the `RelayCommand`

To understand how `RelayCommand` works, we should go back to the story of `ICommand` first.

`ICommand` is a builtin interface in .NET, has three parts to describe the command pattern:
- `Execute`: A real action for the command.
- `CanExecute`: Telling whether the command can be executed now.
- `CanExecuteChanged`: Informing when `CanExecute` evaluation changed.

```cs
public interface ICommand
{
/// <summary>Occurs when changes occur that affect whether or not the command should execute.</summary>
event EventHandler? CanExecuteChanged;

/// <summary>Defines the method that determines whether the command can execute in its current state.</summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
/// <returns>
/// <see langword="true" /> if this command can be executed; otherwise, <see langword="false" />.</returns>
bool CanExecute(object? parameter);

/// <summary>Defines the method to be called when the command is invoked.</summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
void Execute(object? parameter);
}
```

`IRelayCommand` in `CommunityToolkit` simply extends the `ICommand` with a new notify method.
We do need a real implementation of how we inform on `CanExecuteChanged`, this is generally missing in .NET builtin event-driven interfaces.

```cs
public interface IRelayCommand : ICommand
{
/// <summary>
/// Notifies that the <see cref="ICommand.CanExecute"/> property has changed. // [!code highlight]
/// </summary>
void NotifyCanExecuteChanged();
}
```

> [!NOTE]
> `IRelayCommand` and `RelayCommand` have generic versions and async versions.
The type of generated field is `RelayCommand` which implements the `IRelayCommand`, this is essentially a base wrapper for the logic behind.

```cs
public sealed partial class RelayCommand : IRelayCommand
{
/// <summary>
/// The <see cref="Action"/> to invoke when <see cref="Execute"/> is used.
/// </summary>
private readonly Action execute;

/// <summary>
/// The optional action to invoke when <see cref="CanExecute"/> is used.
/// </summary>
private readonly Func<bool>? canExecute;

/// <inheritdoc/>
public event EventHandler? CanExecuteChanged;

/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand"/> class that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> is <see langword="null"/>.</exception>
public RelayCommand(Action execute)
{
ArgumentNullException.ThrowIfNull(execute);

this.execute = execute;
}

/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand"/> class.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
/// <exception cref="System.ArgumentNullException">Thrown if <paramref name="execute"/> or <paramref name="canExecute"/> are <see langword="null"/>.</exception>
public RelayCommand(Action execute, Func<bool> canExecute)
{
ArgumentNullException.ThrowIfNull(execute);
ArgumentNullException.ThrowIfNull(canExecute);

this.execute = execute;
this.canExecute = canExecute;
}

/// <inheritdoc/>
public void NotifyCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool CanExecute(object? parameter)
{
return this.canExecute?.Invoke() != false;
}

/// <inheritdoc/>
public void Execute(object? parameter)
{
this.execute();
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,7 @@ var tokens = Lex("(1 + 3) - (5 + 2)");
var operation = Parse<int>(CollectionsMarshal.AsSpan(tokens));
Console.WriteLine(operation.Value); // -3
```
## ANTLR

> [!TIP]
> Manually writing parsers are very tedious, use certain tools to parse your language instead.
> ANTLR, bison, yacc for example.
57 changes: 57 additions & 0 deletions docs/document/Powershell/docs/Powershell as a Language/Function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Function

## Parameter

There's no positional parameters, it's a table-like definition, can be specified with any order.
Parameters are wrapped inside a function block with `param(...)`

```ps1
function Foo {
param (
[string]$foo
)
}
```

### Default Parameter

```ps1
function Foo {
param (
[string]$foo = "foo"
)
}
```

### Flags

Defining flags that represents a toggle needs a special type called `switch`.
`switch` has the same nature of `bool`, but `bool` parameter requires explicit assignment when the function being called.
While `switch` will remain `$false` when unspecified.

```ps1
function Foo {
param (
[switch]$foo
[bool]$bar
)
}
# this is why we should use `switch` instead.
Foo -foo -bar $true # [!code highlight]
```

### Required Parameter

All parameters are optional by default. Use `[Parameter(Mandatory=$true)]` to mark it as required.

```ps1
param (
[Parameter(Mandatory=$true)]
[string]$RequiredName
)
```

## Lifetime

- Function should be define before it was called.
9 changes: 5 additions & 4 deletions docs/services/DocumentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ export const documentMap = {
// VBA: { icon: 'πŸ’©', description: 'VBA for excel' },
// Vue3: { icon: '⚑', description: '' },
'Unsafe CSharp': { icon: '😎', description: 'Entering the danger zone...' },
'NeoVim ColorScheme Development': {
icon: '🎨',
description: 'Make your own nvim color scheme using lua.',
},
// 'NeoVim ColorScheme Development': {
// icon: '🎨',
// description: 'Make your own nvim color scheme using lua.',
// },
Bash: { icon: '🐒', description: 'Shebang!' },
'Regular Expression': { icon: '🐫', description: 'Memory lossss for every 6 months' },
Nix: { icon: '❄', description: 'Reproduce freedom' },
'Entity Framework Core': { icon: 'πŸ—Ώ', description: '' },
'HTML & CSS': { icon: '😬', description: '' },
Powershell: { icon: '🐚', description: '...' },
} as const satisfies DocumentInfo;

export type DocumentName = keyof typeof documentMap;
Expand Down
2 changes: 1 addition & 1 deletion docs/services/SidebarService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { execSync } from 'node:child_process';
import path from 'node:path';
import path = require('node:path');
import type { DefaultTheme } from 'vitepress';
import { type DirectoryInfo, type FileInfo, Path, documentRoot } from '../shared/FileSystem';
import { type DocumentName, documentMap, documentService } from './DocumentService';
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"vitePressExtensions": [
".md"
]
}
},
"esModuleInterop": true
}

0 comments on commit b95dac6

Please sign in to comment.