tangled
alpha
login
or
join now
benharri.org
/
ircsharp
1
fork
atom
IRC parsing, tokenization, and state handling in C#
1
fork
atom
overview
issues
pulls
pipelines
rename to IrcSharp
also tidy up formatting with vs tools
benharri.org
5 years ago
933a4f85
338763fd
+578
-111
36 changed files
expand all
collapse all
unified
split
.editorconfig
IrcSharp.sln
IrcStates
Casemap.cs
Channel.cs
Emit.cs
ISupport.cs
IrcStates.csproj
Numeric.cs
Server.cs
Tests
Cap.cs
Casemap.cs
Channel.cs
Emit.cs
ISupport.cs
Mode.cs
Motd.cs
User.cs
Who.cs
User.cs
IrcTokens
Hostmask.cs
Line.cs
Protocol.cs
StatefulDecoder.cs
StatefulEncoder.cs
Tests
Format.cs
Hostmask.cs
Parser.cs
StatefulDecoder.cs
StatefulEncoder.cs
Tokenization.cs
IrcTokens.sln
Sample
Client.cs
Program.cs
TokensSample.csproj
StatesSample
Program.cs
StatesSample.csproj
+57
.editorconfig
···
1
1
+
2
2
+
[*.proto]
3
3
+
indent_style=tab
4
4
+
indent_size=tab
5
5
+
tab_width=4
6
6
+
7
7
+
[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
8
8
+
indent_style=space
9
9
+
indent_size=4
10
10
+
tab_width=4
11
11
+
12
12
+
[*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
13
13
+
indent_style=space
14
14
+
indent_size=2
15
15
+
tab_width=2
16
16
+
17
17
+
[*]
18
18
+
19
19
+
# Microsoft .NET properties
20
20
+
csharp_new_line_before_members_in_object_initializers=true
21
21
+
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
22
22
+
csharp_style_var_elsewhere=true:suggestion
23
23
+
csharp_style_var_for_built_in_types=true:suggestion
24
24
+
csharp_style_var_when_type_is_apparent=true:suggestion
25
25
+
dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none
26
26
+
dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none
27
27
+
dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none
28
28
+
dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion
29
29
+
dotnet_style_predefined_type_for_member_access=true:suggestion
30
30
+
dotnet_style_qualification_for_event=false:suggestion
31
31
+
dotnet_style_qualification_for_field=false:suggestion
32
32
+
dotnet_style_qualification_for_method=false:suggestion
33
33
+
dotnet_style_qualification_for_property=false:suggestion
34
34
+
dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
35
35
+
36
36
+
# ReSharper inspection severities
37
37
+
resharper_arrange_redundant_parentheses_highlighting=hint
38
38
+
resharper_arrange_this_qualifier_highlighting=hint
39
39
+
resharper_arrange_type_member_modifiers_highlighting=hint
40
40
+
resharper_arrange_type_modifiers_highlighting=hint
41
41
+
resharper_built_in_type_reference_style_for_member_access_highlighting=hint
42
42
+
resharper_built_in_type_reference_style_highlighting=hint
43
43
+
resharper_redundant_base_qualifier_highlighting=warning
44
44
+
resharper_suggest_var_or_type_built_in_types_highlighting=hint
45
45
+
resharper_suggest_var_or_type_elsewhere_highlighting=hint
46
46
+
resharper_suggest_var_or_type_simple_types_highlighting=hint
47
47
+
48
48
+
# ReSharper properties
49
49
+
resharper_csharp_insert_final_newline=true
50
50
+
resharper_int_align_assignments=true
51
51
+
resharper_int_align_switch_expressions=true
52
52
+
resharper_int_align_switch_sections=true
53
53
+
resharper_int_align_variables=true
54
54
+
resharper_keep_existing_embedded_arrangement=false
55
55
+
resharper_keep_existing_switch_expression_arrangement=false
56
56
+
resharper_place_simple_case_statement_on_same_line=if_owner_is_single_line
57
57
+
resharper_xmldoc_max_blank_lines_between_tags=1
+48
IrcSharp.sln
···
1
1
+
2
2
+
Microsoft Visual Studio Solution File, Format Version 12.00
3
3
+
# Visual Studio Version 16
4
4
+
VisualStudioVersion = 16.0.30011.22
5
5
+
MinimumVisualStudioVersion = 10.0.40219.1
6
6
+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}"
7
7
+
EndProject
8
8
+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "Sample\TokensSample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}"
9
9
+
EndProject
10
10
+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcStates", "IrcStates\IrcStates.csproj", "{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}"
11
11
+
EndProject
12
12
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatesSample", "StatesSample\StatesSample.csproj", "{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}"
13
13
+
EndProject
14
14
+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A85EB22-D7B4-417F-AC3B-DAFD97DDEA08}"
15
15
+
ProjectSection(SolutionItems) = preProject
16
16
+
.editorconfig = .editorconfig
17
17
+
EndProjectSection
18
18
+
EndProject
19
19
+
Global
20
20
+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
21
21
+
Debug|Any CPU = Debug|Any CPU
22
22
+
Release|Any CPU = Release|Any CPU
23
23
+
EndGlobalSection
24
24
+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
25
25
+
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26
26
+
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.Build.0 = Debug|Any CPU
27
27
+
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.ActiveCfg = Release|Any CPU
28
28
+
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.Build.0 = Release|Any CPU
29
29
+
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30
30
+
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.Build.0 = Debug|Any CPU
31
31
+
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.ActiveCfg = Release|Any CPU
32
32
+
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.Build.0 = Release|Any CPU
33
33
+
{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34
34
+
{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
35
35
+
{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
36
36
+
{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}.Release|Any CPU.Build.0 = Release|Any CPU
37
37
+
{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38
38
+
{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
39
39
+
{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
40
40
+
{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF}.Release|Any CPU.Build.0 = Release|Any CPU
41
41
+
EndGlobalSection
42
42
+
GlobalSection(SolutionProperties) = preSolution
43
43
+
HideSolutionNode = FALSE
44
44
+
EndGlobalSection
45
45
+
GlobalSection(ExtensibilityGlobals) = postSolution
46
46
+
SolutionGuid = {0B91F0EA-8564-4318-8EEC-ED0640475141}
47
47
+
EndGlobalSection
48
48
+
EndGlobal
+43
IrcStates/Casemap.cs
···
1
1
+
using System;
2
2
+
3
3
+
namespace IrcStates
4
4
+
{
5
5
+
public static class Casemap
6
6
+
{
7
7
+
public enum CaseMapping
8
8
+
{
9
9
+
Rfc1459,
10
10
+
Ascii
11
11
+
}
12
12
+
13
13
+
private const string AsciiUpperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
14
14
+
private const string AsciiLowerChars = "abcdefghijklmnopqrstuvwxyz";
15
15
+
private const string Rfc1459UpperChars = AsciiUpperChars + @"[]~\";
16
16
+
private const string Rfc1459LowerChars = AsciiLowerChars + @"{}^|";
17
17
+
18
18
+
private static string Replace(string s, string upper, string lower)
19
19
+
{
20
20
+
for (var i = 0; i < upper.Length; ++i)
21
21
+
{
22
22
+
s = s.Replace(upper[i], lower[i]);
23
23
+
}
24
24
+
25
25
+
return s;
26
26
+
}
27
27
+
28
28
+
public static string CaseFold(CaseMapping mapping, string s)
29
29
+
{
30
30
+
if (s != null)
31
31
+
{
32
32
+
return mapping switch
33
33
+
{
34
34
+
CaseMapping.Rfc1459 => Replace(s, Rfc1459UpperChars, Rfc1459LowerChars),
35
35
+
CaseMapping.Ascii => Replace(s, AsciiUpperChars, AsciiLowerChars),
36
36
+
_ => throw new ArgumentOutOfRangeException(nameof(mapping), mapping, null)
37
37
+
};
38
38
+
}
39
39
+
40
40
+
return string.Empty;
41
41
+
}
42
42
+
}
43
43
+
}
+69
IrcStates/Channel.cs
···
1
1
+
using System;
2
2
+
using System.Collections.Generic;
3
3
+
using System.Linq;
4
4
+
5
5
+
namespace IrcStates
6
6
+
{
7
7
+
public class Channel
8
8
+
{
9
9
+
public string Name { get; set; }
10
10
+
public string NameLower { get; set; }
11
11
+
public Dictionary<string, User> Users { get; set; }
12
12
+
public string Topic { get; set; }
13
13
+
public string TopicSetter { get; set; }
14
14
+
public DateTime TopicTime { get; set; }
15
15
+
public DateTime Created { get; set; }
16
16
+
public Dictionary<string, List<string>> ListModes { get; set; }
17
17
+
public Dictionary<string, string> Modes { get; set; }
18
18
+
19
19
+
public override string ToString()
20
20
+
{
21
21
+
return $"Channel(name={Name})";
22
22
+
}
23
23
+
24
24
+
public void SetName(string name, string nameLower)
25
25
+
{
26
26
+
Name = name;
27
27
+
NameLower = nameLower;
28
28
+
}
29
29
+
30
30
+
public void AddMode(string ch, string param, bool listMode)
31
31
+
{
32
32
+
if (listMode)
33
33
+
{
34
34
+
if (!ListModes.ContainsKey(ch))
35
35
+
{
36
36
+
ListModes[ch] = new List<string>();
37
37
+
}
38
38
+
39
39
+
if (ListModes[ch].Contains(param))
40
40
+
{
41
41
+
ListModes[ch].Add(param ?? string.Empty);
42
42
+
}
43
43
+
}
44
44
+
else
45
45
+
{
46
46
+
Modes[ch] = param;
47
47
+
}
48
48
+
}
49
49
+
50
50
+
public void RemoveMode(string ch, string param)
51
51
+
{
52
52
+
if (ListModes.ContainsKey(ch))
53
53
+
{
54
54
+
if (ListModes[ch].Contains(param))
55
55
+
{
56
56
+
ListModes[ch].Remove(param);
57
57
+
if (!ListModes[ch].Any())
58
58
+
{
59
59
+
ListModes.Remove(ch);
60
60
+
}
61
61
+
}
62
62
+
}
63
63
+
else if (Modes.ContainsKey(ch))
64
64
+
{
65
65
+
Modes.Remove(ch);
66
66
+
}
67
67
+
}
68
68
+
}
69
69
+
}
+6
IrcStates/Emit.cs
···
1
1
+
namespace IrcStates
2
2
+
{
3
3
+
public class Emit
4
4
+
{
5
5
+
}
6
6
+
}
+7
IrcStates/ISupport.cs
···
1
1
+
// ReSharper disable InconsistentNaming
2
2
+
namespace IrcStates
3
3
+
{
4
4
+
public class ISupport
5
5
+
{
6
6
+
}
7
7
+
}
+21
IrcStates/IrcStates.csproj
···
1
1
+
<Project Sdk="Microsoft.NET.Sdk">
2
2
+
3
3
+
<PropertyGroup>
4
4
+
<TargetFramework>netcoreapp3.1</TargetFramework>
5
5
+
</PropertyGroup>
6
6
+
7
7
+
<ItemGroup>
8
8
+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
9
9
+
<PrivateAssets>all</PrivateAssets>
10
10
+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
11
11
+
</PackageReference>
12
12
+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.0" />
13
13
+
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
14
14
+
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
15
15
+
</ItemGroup>
16
16
+
17
17
+
<ItemGroup>
18
18
+
<ProjectReference Include="..\IrcTokens\IrcTokens.csproj" />
19
19
+
</ItemGroup>
20
20
+
21
21
+
</Project>
+49
IrcStates/Numeric.cs
···
1
1
+
// ReSharper disable InconsistentNaming
2
2
+
namespace IrcStates
3
3
+
{
4
4
+
public static class Numeric
5
5
+
{
6
6
+
public const string RPL_WELCOME = "001";
7
7
+
public const string RPL_ISUPPORT = "005";
8
8
+
public const string RPL_MOTD = "372";
9
9
+
public const string RPL_MOTDSTART = "375";
10
10
+
public const string RPL_UMODEIS = "221";
11
11
+
public const string RPL_VISIBLEHOST = "396";
12
12
+
13
13
+
public const string RPL_CHANNELMODEIS = "324";
14
14
+
public const string RPL_CREATIONTIME = "329";
15
15
+
public const string RPL_TOPIC = "332";
16
16
+
public const string RPL_TOPICWHOTIME = "333";
17
17
+
18
18
+
public const string RPL_WHOREPLY = "352";
19
19
+
public const string RPL_WHOSPCRPL = "354";
20
20
+
public const string RPL_ENDOFWHO = "315";
21
21
+
public const string RPL_NAMREPLY = "353";
22
22
+
public const string RPL_ENDOFNAMES = "366";
23
23
+
24
24
+
public const string RPL_BANLIST = "367";
25
25
+
public const string RPL_ENDOFBANLIST = "368";
26
26
+
public const string RPL_QUIETLIST = "728";
27
27
+
public const string RPL_ENDOFQUIETLIST = "729";
28
28
+
29
29
+
public const string RPL_SASLSUCCESS = "903";
30
30
+
public const string ERR_SASLFAIL = "904";
31
31
+
public const string ERR_SASLTOOLONG = "905";
32
32
+
public const string ERR_SASLABORTED = "906";
33
33
+
public const string ERR_SASLALREADY = "907";
34
34
+
public const string RPL_SASLMECHS = "908";
35
35
+
36
36
+
public const string RPL_WHOISUSER = "311";
37
37
+
public const string RPL_WHOISSERVER = "312";
38
38
+
public const string RPL_WHOISOPERATOR = "313";
39
39
+
public const string RPL_WHOISIDLE = "317";
40
40
+
public const string RPL_WHOISCHANNELS = "319";
41
41
+
public const string RPL_WHOISACCOUNT = "330";
42
42
+
public const string RPL_WHOISHOST = "378";
43
43
+
public const string RPL_WHOISMODES = "379";
44
44
+
public const string RPL_WHOISSECURE = "671";
45
45
+
public const string RPL_ENDOFWHOIS = "318";
46
46
+
47
47
+
public const string ERR_NOSUCHCHANNEL = "403";
48
48
+
}
49
49
+
}
+6
IrcStates/Server.cs
···
1
1
+
namespace IrcStates
2
2
+
{
3
3
+
public class Server
4
4
+
{
5
5
+
}
6
6
+
}
+9
IrcStates/Tests/Cap.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Cap
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/Casemap.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Casemap
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/Channel.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Channel
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/Emit.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Emit
7
7
+
{
8
8
+
}
9
9
+
}
+10
IrcStates/Tests/ISupport.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
// ReSharper disable InconsistentNaming
3
3
+
4
4
+
namespace IrcStates.Tests
5
5
+
{
6
6
+
[TestClass]
7
7
+
public class ISupport
8
8
+
{
9
9
+
}
10
10
+
}
+9
IrcStates/Tests/Mode.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Mode
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/Motd.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Motd
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/User.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class User
7
7
+
{
8
8
+
}
9
9
+
}
+9
IrcStates/Tests/Who.cs
···
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
3
3
+
namespace IrcStates.Tests
4
4
+
{
5
5
+
[TestClass]
6
6
+
public class Who
7
7
+
{
8
8
+
}
9
9
+
}
+28
IrcStates/User.cs
···
1
1
+
using System.Collections.Generic;
2
2
+
3
3
+
namespace IrcStates
4
4
+
{
5
5
+
public class User
6
6
+
{
7
7
+
private string NickName;
8
8
+
private string NickNameLower;
9
9
+
10
10
+
public string UserName { get; set; }
11
11
+
public string HostName { get; set; }
12
12
+
public string RealName { get; set; }
13
13
+
public string Account { get; set; }
14
14
+
public string Away { get; set; }
15
15
+
public HashSet<string> Channels { get; set; }
16
16
+
17
17
+
public override string ToString()
18
18
+
{
19
19
+
return $"User(nickname={NickName})";
20
20
+
}
21
21
+
22
22
+
public void SetNickName(string nick, string nickLower)
23
23
+
{
24
24
+
NickName = nick;
25
25
+
NickNameLower = nickLower;
26
26
+
}
27
27
+
}
28
28
+
}
-31
IrcTokens.sln
···
1
1
-
2
2
-
Microsoft Visual Studio Solution File, Format Version 12.00
3
3
-
# Visual Studio Version 16
4
4
-
VisualStudioVersion = 16.0.30011.22
5
5
-
MinimumVisualStudioVersion = 10.0.40219.1
6
6
-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}"
7
7
-
EndProject
8
8
-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "Sample\Sample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}"
9
9
-
EndProject
10
10
-
Global
11
11
-
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12
12
-
Debug|Any CPU = Debug|Any CPU
13
13
-
Release|Any CPU = Release|Any CPU
14
14
-
EndGlobalSection
15
15
-
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16
16
-
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17
17
-
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Debug|Any CPU.Build.0 = Debug|Any CPU
18
18
-
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.ActiveCfg = Release|Any CPU
19
19
-
{9E812F45-B2CD-42D2-8378-EBEBF8697905}.Release|Any CPU.Build.0 = Release|Any CPU
20
20
-
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21
21
-
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Debug|Any CPU.Build.0 = Debug|Any CPU
22
22
-
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.ActiveCfg = Release|Any CPU
23
23
-
{A45DA39B-6B47-4713-8049-3B36E0235B67}.Release|Any CPU.Build.0 = Release|Any CPU
24
24
-
EndGlobalSection
25
25
-
GlobalSection(SolutionProperties) = preSolution
26
26
-
HideSolutionNode = FALSE
27
27
-
EndGlobalSection
28
28
-
GlobalSection(ExtensibilityGlobals) = postSolution
29
29
-
SolutionGuid = {0B91F0EA-8564-4318-8EEC-ED0640475141}
30
30
-
EndGlobalSection
31
31
-
EndGlobal
+23
-7
IrcTokens/Hostmask.cs
···
5
5
/// <summary>
6
6
/// Represents the three parts of a hostmask. Parse with the constructor.
7
7
/// </summary>
8
8
-
public class Hostmask
8
8
+
public class Hostmask : IEquatable<Hostmask>
9
9
{
10
10
public string NickName { get; set; }
11
11
public string UserName { get; set; }
12
12
public string HostName { get; set; }
13
13
14
14
-
public override string ToString() => _source;
14
14
+
public override string ToString()
15
15
+
{
16
16
+
return _source;
17
17
+
}
15
18
16
16
-
public override int GetHashCode() => _source.GetHashCode(StringComparison.Ordinal);
19
19
+
public override int GetHashCode()
20
20
+
{
21
21
+
return _source.GetHashCode(StringComparison.Ordinal);
22
22
+
}
17
23
18
18
-
public override bool Equals(object obj)
24
24
+
public bool Equals(Hostmask other)
19
25
{
20
20
-
if (obj == null || GetType() != obj.GetType())
26
26
+
if (other == null)
27
27
+
{
21
28
return false;
29
29
+
}
22
30
23
23
-
return _source == ((Hostmask) obj)._source;
31
31
+
return _source == other._source;
32
32
+
}
33
33
+
34
34
+
public override bool Equals(object obj)
35
35
+
{
36
36
+
return Equals(obj as Hostmask);
24
37
}
25
38
26
39
private readonly string _source;
27
40
28
41
public Hostmask(string source)
29
42
{
30
30
-
if (source == null) return;
43
43
+
if (source == null)
44
44
+
{
45
45
+
return;
46
46
+
}
31
47
32
48
_source = source;
33
49
+39
-8
IrcTokens/Line.cs
···
8
8
/// <summary>
9
9
/// Tools to represent, parse, and format IRC lines
10
10
/// </summary>
11
11
-
public class Line
11
11
+
public class Line : IEquatable<Line>
12
12
{
13
13
public Dictionary<string, string> Tags { get; set; }
14
14
public string Source { get; set; }
···
16
16
public List<string> Params { get; set; }
17
17
18
18
private Hostmask _hostmask;
19
19
-
private readonly string _rawLine;
20
19
21
20
public override string ToString()
22
21
{
23
22
var vars = new List<string>();
24
23
25
24
if (Command != null)
25
25
+
{
26
26
vars.Add($"command={Command}");
27
27
+
}
28
28
+
27
29
if (Source != null)
30
30
+
{
28
31
vars.Add($"source={Source}");
32
32
+
}
33
33
+
29
34
if (Params != null && Params.Any())
35
35
+
{
30
36
vars.Add($"params=[{string.Join(",", Params)}]");
37
37
+
}
38
38
+
31
39
if (Tags != null && Tags.Any())
40
40
+
{
32
41
vars.Add($"tags=[{string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}]");
42
42
+
}
33
43
34
44
return $"Line({string.Join(", ", vars)})";
35
45
}
36
46
37
37
-
public override int GetHashCode() => Format().GetHashCode(StringComparison.Ordinal);
47
47
+
public override int GetHashCode()
48
48
+
{
49
49
+
return Format().GetHashCode(StringComparison.Ordinal);
50
50
+
}
38
51
39
39
-
public override bool Equals(object obj)
52
52
+
public bool Equals(Line other)
40
53
{
41
41
-
if (obj == null || GetType() != obj.GetType())
54
54
+
if (other == null)
55
55
+
{
42
56
return false;
57
57
+
}
43
58
44
44
-
return Format() == ((Line) obj).Format();
59
59
+
return Format() == other.Format();
60
60
+
}
61
61
+
62
62
+
public override bool Equals(object obj)
63
63
+
{
64
64
+
return Equals(obj as Line);
45
65
}
46
66
47
67
public Hostmask Hostmask =>
···
56
76
public Line(string line)
57
77
{
58
78
if (string.IsNullOrWhiteSpace(line))
79
79
+
{
59
80
throw new ArgumentNullException(nameof(line));
81
81
+
}
60
82
61
61
-
_rawLine = line;
62
83
string[] split;
63
84
64
85
if (line.StartsWith('@'))
···
97
118
98
119
Params = line.Contains(' ', StringComparison.Ordinal)
99
120
? line.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList()
100
100
-
: new List<string> {line};
121
121
+
: new List<string> { line };
101
122
102
123
if (Params[0].StartsWith(':'))
103
124
{
···
135
156
}
136
157
137
158
if (Source != null)
159
159
+
{
138
160
outs.Add($":{Source}");
161
161
+
}
139
162
140
163
outs.Add(Command);
141
164
···
147
170
foreach (var p in Params)
148
171
{
149
172
if (p.Contains(' ', StringComparison.Ordinal))
173
173
+
{
150
174
throw new ArgumentException(@"non-last parameters cannot have spaces", p);
175
175
+
}
176
176
+
151
177
if (p.StartsWith(':'))
178
178
+
{
152
179
throw new ArgumentException(@"non-last parameters cannot start with colon", p);
180
180
+
}
153
181
}
154
182
outs.AddRange(Params);
155
183
156
184
if (string.IsNullOrWhiteSpace(last) || last.Contains(' ', StringComparison.Ordinal) || last.StartsWith(':'))
185
185
+
{
157
186
last = $":{last}";
187
187
+
}
188
188
+
158
189
outs.Add(last);
159
190
}
160
191
+2
IrcTokens/Protocol.cs
···
50
50
}
51
51
}
52
52
else
53
53
+
{
53
54
unescaped.Append(current);
55
55
+
}
54
56
}
55
57
56
58
return unescaped.ToString();
+7
-1
IrcTokens/StatefulDecoder.cs
···
18
18
set
19
19
{
20
20
if (value != null)
21
21
+
{
21
22
_encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback,
22
23
DecoderFallback.ReplacementFallback);
24
24
+
}
23
25
}
24
26
}
25
27
···
30
32
set
31
33
{
32
34
if (value != null)
33
33
-
_encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback,
35
35
+
{
36
36
+
_fallback = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback,
34
37
DecoderFallback.ReplacementFallback);
38
38
+
}
35
39
}
36
40
}
37
41
···
55
59
public List<Line> Push(byte[] data)
56
60
{
57
61
if (data == null || data.Length == 0)
62
62
+
{
58
63
return null;
64
64
+
}
59
65
60
66
_buffer = _buffer.Concat(data).ToArray();
61
67
+4
IrcTokens/StatefulEncoder.cs
···
16
16
set
17
17
{
18
18
if (value != null)
19
19
+
{
19
20
_encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback,
20
21
DecoderFallback.ExceptionFallback);
22
22
+
}
21
23
}
22
24
}
23
25
···
52
54
public void Push(Line line)
53
55
{
54
56
if (line == null)
57
57
+
{
55
58
throw new ArgumentNullException(nameof(line));
59
59
+
}
56
60
57
61
PendingBytes = PendingBytes.Concat(Encoding.GetBytes($"{line.Format()}\r\n")).ToArray();
58
62
_bufferedLines.Enqueue(line);
+18
-18
IrcTokens/Tests/FormatTests.cs
IrcTokens/Tests/Format.cs
···
5
5
namespace IrcTokens.Tests
6
6
{
7
7
[TestClass]
8
8
-
public class FormatTests
8
8
+
public class Format
9
9
{
10
10
[TestMethod]
11
11
public void TestTags()
12
12
{
13
13
var line = new Line
14
14
{
15
15
-
Command = "PRIVMSG",
16
16
-
Params = new List<string> {"#channel", "hello"},
17
17
-
Tags = new Dictionary<string, string> {{"id", "\\" + " " + ";" + "\r\n"}}
15
15
+
Command = "PRIVMSG",
16
16
+
Params = new List<string> { "#channel", "hello" },
17
17
+
Tags = new Dictionary<string, string> { { "id", "\\" + " " + ";" + "\r\n" } }
18
18
}.Format();
19
19
20
20
Assert.AreEqual("@id=\\\\\\s\\:\\r\\n PRIVMSG #channel hello", line);
···
25
25
{
26
26
var line = new Line
27
27
{
28
28
-
Command = "PRIVMSG",
29
29
-
Params = new List<string> {"#channel", "hello"}
28
28
+
Command = "PRIVMSG",
29
29
+
Params = new List<string> { "#channel", "hello" }
30
30
}.Format();
31
31
32
32
Assert.AreEqual("PRIVMSG #channel hello", line);
···
38
38
var line = new Line
39
39
{
40
40
Command = "PRIVMSG",
41
41
-
Params = new List<string> {"#channel", "hello"},
42
42
-
Tags = new Dictionary<string, string> {{"a", null}}
41
41
+
Params = new List<string> { "#channel", "hello" },
42
42
+
Tags = new Dictionary<string, string> { { "a", null } }
43
43
}.Format();
44
44
45
45
Assert.AreEqual("@a PRIVMSG #channel hello", line);
···
51
51
var line = new Line
52
52
{
53
53
Command = "PRIVMSG",
54
54
-
Params = new List<string> {"#channel", "hello"},
55
55
-
Tags = new Dictionary<string, string> {{"a", ""}}
54
54
+
Params = new List<string> { "#channel", "hello" },
55
55
+
Tags = new Dictionary<string, string> { { "a", "" } }
56
56
}.Format();
57
57
58
58
Assert.AreEqual("@a PRIVMSG #channel hello", line);
···
64
64
var line = new Line
65
65
{
66
66
Command = "PRIVMSG",
67
67
-
Params = new List<string> {"#channel", "hello"},
67
67
+
Params = new List<string> { "#channel", "hello" },
68
68
Source = "nick!user@host"
69
69
}.Format();
70
70
···
74
74
[TestMethod]
75
75
public void TestCommandLowercase()
76
76
{
77
77
-
var line = new Line {Command = "privmsg"}.Format();
77
77
+
var line = new Line { Command = "privmsg" }.Format();
78
78
Assert.AreEqual("privmsg", line);
79
79
}
80
80
81
81
[TestMethod]
82
82
public void TestCommandUppercase()
83
83
{
84
84
-
var line = new Line {Command = "PRIVMSG"}.Format();
84
84
+
var line = new Line { Command = "PRIVMSG" }.Format();
85
85
Assert.AreEqual("PRIVMSG", line);
86
86
}
87
87
···
91
91
var line = new Line
92
92
{
93
93
Command = "PRIVMSG",
94
94
-
Params = new List<string> {"#channel", "hello world"}
94
94
+
Params = new List<string> { "#channel", "hello world" }
95
95
}.Format();
96
96
97
97
Assert.AreEqual("PRIVMSG #channel :hello world", line);
···
103
103
var line = new Line
104
104
{
105
105
Command = "PRIVMSG",
106
106
-
Params = new List<string> {"#channel", "helloworld"}
106
106
+
Params = new List<string> { "#channel", "helloworld" }
107
107
}.Format();
108
108
109
109
Assert.AreEqual("PRIVMSG #channel helloworld", line);
···
115
115
var line = new Line
116
116
{
117
117
Command = "PRIVMSG",
118
118
-
Params = new List<string> {"#channel", ":helloworld"}
118
118
+
Params = new List<string> { "#channel", ":helloworld" }
119
119
}.Format();
120
120
121
121
Assert.AreEqual("PRIVMSG #channel ::helloworld", line);
···
129
129
new Line
130
130
{
131
131
Command = "USER",
132
132
-
Params = new List<string> {"user", "0 *", "real name"}
132
132
+
Params = new List<string> { "user", "0 *", "real name" }
133
133
}.Format();
134
134
});
135
135
}
···
142
142
new Line
143
143
{
144
144
Command = "PRIVMSG",
145
145
-
Params = new List<string> {":#channel", "hello"}
145
145
+
Params = new List<string> { ":#channel", "hello" }
146
146
}.Format();
147
147
});
148
148
}
+6
-6
IrcTokens/Tests/HostmaskTests.cs
IrcTokens/Tests/Hostmask.cs
···
3
3
namespace IrcTokens.Tests
4
4
{
5
5
[TestClass]
6
6
-
public class HostmaskTests
6
6
+
public class Hostmask
7
7
{
8
8
[TestMethod]
9
9
public void TestHostmask()
10
10
{
11
11
-
var hostmask = new Hostmask("nick!user@host");
11
11
+
var hostmask = new IrcTokens.Hostmask("nick!user@host");
12
12
Assert.AreEqual("nick", hostmask.NickName);
13
13
Assert.AreEqual("user", hostmask.UserName);
14
14
Assert.AreEqual("host", hostmask.HostName);
···
17
17
[TestMethod]
18
18
public void TestNoHostName()
19
19
{
20
20
-
var hostmask = new Hostmask("nick!user");
20
20
+
var hostmask = new IrcTokens.Hostmask("nick!user");
21
21
Assert.AreEqual("nick", hostmask.NickName);
22
22
Assert.AreEqual("user", hostmask.UserName);
23
23
Assert.IsNull(hostmask.HostName);
···
26
26
[TestMethod]
27
27
public void TestNoUserName()
28
28
{
29
29
-
var hostmask = new Hostmask("nick@host");
29
29
+
var hostmask = new IrcTokens.Hostmask("nick@host");
30
30
Assert.AreEqual("nick", hostmask.NickName);
31
31
Assert.IsNull(hostmask.UserName);
32
32
Assert.AreEqual("host", hostmask.HostName);
···
35
35
[TestMethod]
36
36
public void TestOnlyNickName()
37
37
{
38
38
-
var hostmask = new Hostmask("nick");
38
38
+
var hostmask = new IrcTokens.Hostmask("nick");
39
39
Assert.AreEqual("nick", hostmask.NickName);
40
40
Assert.IsNull(hostmask.UserName);
41
41
Assert.IsNull(hostmask.HostName);
···
45
45
public void TestHostmaskFromLine()
46
46
{
47
47
var line = new Line(":nick!user@host PRIVMSG #channel hello");
48
48
-
var hostmask = new Hostmask("nick!user@host");
48
48
+
var hostmask = new IrcTokens.Hostmask("nick!user@host");
49
49
Assert.AreEqual(hostmask.ToString(), line.Hostmask.ToString());
50
50
Assert.AreEqual("nick", line.Hostmask.NickName);
51
51
Assert.AreEqual("user", line.Hostmask.UserName);
+5
-5
IrcTokens/Tests/ParserTests.cs
IrcTokens/Tests/Parser.cs
···
1
1
-
using System.Collections.Generic;
1
1
+
using IrcTokens.Tests.Data;
2
2
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3
3
+
using System.Collections.Generic;
2
4
using System.Globalization;
3
5
using System.IO;
4
4
-
using IrcTokens.Tests.Data;
5
5
-
using Microsoft.VisualStudio.TestTools.UnitTesting;
6
6
using YamlDotNet.Serialization;
7
7
using YamlDotNet.Serialization.NamingConventions;
8
8
9
9
namespace IrcTokens.Tests
10
10
{
11
11
[TestClass]
12
12
-
public class ParserTests
12
12
+
public class Parser
13
13
{
14
14
private static T LoadYaml<T>(string path)
15
15
{
···
46
46
{
47
47
Command = atoms.Verb,
48
48
Params = atoms.Params,
49
49
-
Source = atoms.Source ?? null,
49
49
+
Source = atoms.Source,
50
50
Tags = atoms.Tags
51
51
}.Format();
52
52
+8
-9
IrcTokens/Tests/StatefulDecoderTests.cs
IrcTokens/Tests/StatefulDecoder.cs
···
1
1
-
using System.Collections.Generic;
2
2
-
using System.Linq;
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
using System.Collections.Generic;
3
3
using System.Text;
4
4
-
using Microsoft.VisualStudio.TestTools.UnitTesting;
5
4
6
5
namespace IrcTokens.Tests
7
6
{
8
7
[TestClass]
9
9
-
public class StatefulDecoderTests
8
8
+
public class StatefulDecoder
10
9
{
11
11
-
private StatefulDecoder _decoder;
10
10
+
private IrcTokens.StatefulDecoder _decoder;
12
11
13
12
[TestInitialize]
14
13
public void TestInitialize()
15
14
{
16
16
-
_decoder = new StatefulDecoder();
15
15
+
_decoder = new IrcTokens.StatefulDecoder();
17
16
}
18
17
19
18
[TestMethod]
···
26
25
Assert.AreEqual(1, lines.Count);
27
26
28
27
var line = new Line("PRIVMSG #channel hello");
29
29
-
CollectionAssert.AreEqual(new List<Line> {line}, lines);
28
28
+
CollectionAssert.AreEqual(new List<Line> { line }, lines);
30
29
}
31
30
32
31
[TestMethod]
···
45
44
public void TestEncoding()
46
45
{
47
46
var iso8859 = Encoding.GetEncoding("iso-8859-1");
48
48
-
_decoder = new StatefulDecoder {Encoding = iso8859};
47
47
+
_decoder = new IrcTokens.StatefulDecoder { Encoding = iso8859 };
49
48
var lines = _decoder.Push(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n"));
50
49
var line = new Line("PRIVMSG #channel :hello Ç");
51
50
Assert.IsTrue(line.Equals(lines[0]));
···
55
54
public void TestEncodingFallback()
56
55
{
57
56
var latin1 = Encoding.GetEncoding("iso-8859-1");
58
58
-
_decoder = new StatefulDecoder {Encoding = null, Fallback = latin1};
57
57
+
_decoder = new IrcTokens.StatefulDecoder { Encoding = null, Fallback = latin1 };
59
58
var lines = _decoder.Push(latin1.GetBytes("PRIVMSG #channel hélló\r\n"));
60
59
Assert.AreEqual(1, lines.Count);
61
60
Assert.IsTrue(new Line("PRIVMSG #channel hélló").Equals(lines[0]));
+5
-6
IrcTokens/Tests/StatefulEncoderTests.cs
IrcTokens/Tests/StatefulEncoder.cs
···
1
1
-
using System.Linq;
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
using System.Text;
3
3
-
using Microsoft.VisualStudio.TestTools.UnitTesting;
4
3
5
4
namespace IrcTokens.Tests
6
5
{
7
6
[TestClass]
8
8
-
public class StatefulEncoderTests
7
7
+
public class StatefulEncoder
9
8
{
10
10
-
private StatefulEncoder _encoder;
9
9
+
private IrcTokens.StatefulEncoder _encoder;
11
10
12
11
[TestInitialize]
13
12
public void TestInitialize()
14
13
{
15
15
-
_encoder = new StatefulEncoder();
14
14
+
_encoder = new IrcTokens.StatefulEncoder();
16
15
}
17
16
18
17
[TestMethod]
···
64
63
public void TestEncoding()
65
64
{
66
65
var iso8859 = Encoding.GetEncoding("iso-8859-1");
67
67
-
_encoder = new StatefulEncoder {Encoding = iso8859};
66
66
+
_encoder = new IrcTokens.StatefulEncoder { Encoding = iso8859 };
68
67
_encoder.Push(new Line("PRIVMSG #channel :hello Ç"));
69
68
CollectionAssert.AreEqual(iso8859.GetBytes("PRIVMSG #channel :hello Ç\r\n"), _encoder.PendingBytes);
70
69
}
+7
-7
IrcTokens/Tests/TokenizationTests.cs
IrcTokens/Tests/Tokenization.cs
···
1
1
-
using System.Collections.Generic;
2
2
-
using Microsoft.VisualStudio.TestTools.UnitTesting;
1
1
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2
2
+
using System.Collections.Generic;
3
3
4
4
namespace IrcTokens.Tests
5
5
{
6
6
[TestClass]
7
7
-
public class TokenizationTests
7
7
+
public class Tokenization
8
8
{
9
9
[TestMethod]
10
10
public void TestTagsMissing()
···
87
87
public void TestParamsTrailing()
88
88
{
89
89
var line = new Line("PRIVMSG #channel :hello world");
90
90
-
CollectionAssert.AreEqual(new List<string> {"#channel", "hello world"}, line.Params);
90
90
+
CollectionAssert.AreEqual(new List<string> { "#channel", "hello world" }, line.Params);
91
91
}
92
92
93
93
[TestMethod]
94
94
public void TestParamsOnlyTrailing()
95
95
{
96
96
var line = new Line("PRIVMSG :hello world");
97
97
-
CollectionAssert.AreEqual(new List<string> {"hello world"}, line.Params);
97
97
+
CollectionAssert.AreEqual(new List<string> { "hello world" }, line.Params);
98
98
}
99
99
100
100
[TestMethod]
···
109
109
public void TestAllTokens()
110
110
{
111
111
var line = new Line("@id=123 :nick!user@host PRIVMSG #channel :hello world");
112
112
-
CollectionAssert.AreEqual(new Dictionary<string, string> {{"id", "123"}}, line.Tags);
112
112
+
CollectionAssert.AreEqual(new Dictionary<string, string> { { "id", "123" } }, line.Tags);
113
113
Assert.AreEqual("nick!user@host", line.Source);
114
114
Assert.AreEqual("PRIVMSG", line.Command);
115
115
-
CollectionAssert.AreEqual(new List<string> {"#channel", "hello world"}, line.Params);
115
115
+
CollectionAssert.AreEqual(new List<string> { "#channel", "hello world" }, line.Params);
116
116
}
117
117
}
118
118
}
+10
-9
Sample/Client.cs
···
1
1
-
using System;
1
1
+
using IrcTokens;
2
2
+
using System;
2
3
using System.Collections.Generic;
3
4
using System.Net.Sockets;
4
4
-
using System.Text;
5
5
-
using IrcTokens;
6
5
7
7
-
namespace Sample
6
6
+
namespace TokensSample
8
7
{
9
8
public class Client
10
9
{
···
25
24
{
26
25
_socket.Connect("127.0.0.1", 6667);
27
26
28
28
-
Send(new Line {Command = "USER", Params = new List<string> {"username", "0", "*", "real name"}});
29
29
-
Send(new Line {Command = "NICK", Params = new List<string> {"statefulbot"}});
27
27
+
Send(new Line { Command = "USER", Params = new List<string> { "username", "0", "*", "real name" } });
28
28
+
Send(new Line { Command = "NICK", Params = new List<string> { "tokensbot" } });
30
29
31
30
while (true)
32
31
{
33
32
var bytesReceived = _socket.Receive(_bytes);
34
33
var lines = _decoder.Push(_bytes);
35
34
36
36
-
if (lines.Count == 0)
35
35
+
if (bytesReceived == 0)
37
36
{
38
37
Console.WriteLine("! disconnected");
39
38
_socket.Shutdown(SocketShutdown.Both);
···
47
46
switch (line.Command)
48
47
{
49
48
case "PING":
50
50
-
Send(new Line {Command = "PONG", Params = line.Params});
49
49
+
Send(new Line { Command = "PONG", Params = line.Params });
51
50
break;
52
51
case "001":
53
53
-
Send(new Line {Command = "JOIN", Params = new List<string> {"#channel"}});
52
52
+
Send(new Line { Command = "JOIN", Params = new List<string> { "#channel" } });
54
53
break;
55
54
}
56
55
}
···
62
61
Console.WriteLine($"> {line.Format()}");
63
62
_encoder.Push(line);
64
63
while (_encoder.PendingBytes.Length > 0)
64
64
+
{
65
65
_encoder.Pop(_socket.Send(_encoder.PendingBytes));
66
66
+
}
66
67
}
67
68
}
68
69
}
+4
-4
Sample/Program.cs
···
1
1
-
using System;
1
1
+
using IrcTokens;
2
2
+
using System;
2
3
using System.Collections.Generic;
3
3
-
using IrcTokens;
4
4
5
5
-
namespace Sample
5
5
+
namespace TokensSample
6
6
{
7
7
public class Program
8
8
{
···
17
17
var line2 = new Line
18
18
{
19
19
Command = "USER",
20
20
-
Params = new List<string> {"user", "0", "*", "real name"}
20
20
+
Params = new List<string> { "user", "0", "*", "real name" }
21
21
};
22
22
Console.WriteLine(line2);
23
23
Console.WriteLine(line2.Format());
Sample/Sample.csproj
Sample/TokensSample.csproj
+12
StatesSample/Program.cs
···
1
1
+
using System;
2
2
+
3
3
+
namespace StatesSample
4
4
+
{
5
5
+
public static class Program
6
6
+
{
7
7
+
private static void Main(string[] args)
8
8
+
{
9
9
+
Console.WriteLine("Hello World!");
10
10
+
}
11
11
+
}
12
12
+
}
+12
StatesSample/StatesSample.csproj
···
1
1
+
<Project Sdk="Microsoft.NET.Sdk">
2
2
+
3
3
+
<PropertyGroup>
4
4
+
<OutputType>Exe</OutputType>
5
5
+
<TargetFramework>netcoreapp3.1</TargetFramework>
6
6
+
</PropertyGroup>
7
7
+
8
8
+
<ItemGroup>
9
9
+
<ProjectReference Include="..\IrcStates\IrcStates.csproj" />
10
10
+
</ItemGroup>
11
11
+
12
12
+
</Project>