Format C# code with indents programmatically - c#

I'm making a C# application including a RichTextBox in which the user will be able to put some C# code in it and format it by indents like what Visual Studio does.
private void btnEdit_Click(object sender, EventArgs e)
{
//rchCode.Text= formattedCode; // Which I haven't got anywhere so far
}
I looked for the same questions and this answer suggests using something called NArrange, but I don't want use other tools, add-ins or such.
Also this one offering the CodeDOM way which I haven't figure it out how to use it (If it's helpful in anyway)
I want to do it by writing some actual code. How can I do it?

To properly indent code you would need Microsoft.CodeAnalysis.CSharp nuget package and .NET framework 4.6+. Sample code:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
...
public string ArrangeUsingRoslyn(string csCode) {
var tree = CSharpSyntaxTree.ParseText(csCode);
var root = tree.GetRoot().NormalizeWhitespace();
var ret = root.ToFullString();
return ret;
}
One-liner:
csCode = CSharpSyntaxTree.ParseText(csCode).GetRoot().NormalizeWhitespace().ToFullString();
You may also use NArrange to sort methods in your cs file, organize usings, create regions, etc.

So I got the solution this way:
It's not still perfect (as it always add one or more new lines before the first code line). So if anyone can improve it or has a better way to do it, I'll appreciate any new suggestions.
private void btnEdit_Click(object sender, EventArgs e)
{
RichTextBox rchTemp = new RichTextBox();
foreach (string line in rchCode.Lines)
{
rchTemp.AppendText("\r\n" + line.Trim());
}
RichTextBox rchTemp2 = new RichTextBox();
int indentCount = 0;
bool shouldIndent = false;
foreach (string line in rchTemp.Lines)
{
if (shouldIndent)
indentCount++;
if (line.Contains("}"))
indentCount--;
if (indentCount == 0)
{
rchTemp2.AppendText("\r\n" + line);
shouldIndent = line.Contains("{");
continue;
}
string blankSpace = string.Empty;
for (int i = 0; i < indentCount; i++)
{
blankSpace += " ";
}
rchTemp2.AppendText("\r\n" + blankSpace + line);
shouldIndent = line.Contains("{");
}
rchCode.Text = rchTemp2.Text;
}

public static string FormatCode(string code)
{
var lines = code.Split('\n').Select(s => s.Trim());
var strBuilder = new StringBuilder();
int indentCount = 0;
bool shouldIndent = false;
foreach (string line in lines)
{
if (shouldIndent)
indentCount++;
if (line.Trim() == "}")
indentCount--;
if (indentCount == 0)
{
strBuilder.AppendLine(line);
shouldIndent = line.Contains("{");
continue;
}
string blankSpace = string.Empty;
for (int i = 0; i < indentCount; i++)
{
blankSpace += " ";
}
if (line.Contains("}") && line.Trim() != "}")
indentCount--;
strBuilder.AppendLine(blankSpace + line);
shouldIndent = line.Contains("{");
}
return strBuilder.ToString();
}
Little improvement on answer on top
if line public string Foo { get; set; } correct indent.

Related

I'm making a guessing game but the random word is always the same

I have 100 random words in a notepad file & choosing a random word but it's always choosing the same word which is the last one in the file every word is a line.
Here is the code for taking a random word & showing it as:
private string[] tab;
private string current = "";
private string copycurrent = "";
private void randomword()
{
string line = "";
try
{
using (StreamReader sr = new StreamReader("Words.txt"))
{
string[] tab = null;
while ((line = sr.ReadLine()) != null)
{
tab = line.Split("\n");
}
Random r = new Random();
int j = r.Next(tab.Length);
current = tab[j];
copycurrent = "";
for (int i = 0; i < current.Length; i++)
{
copycurrent += "_";
}
label1.Text = "";
for (int i = 0; i < copycurrent.Length; i++)
{
label1.Text += copycurrent.Substring(i, 1);
label1.Text += " ";
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Please - can anyone help? I would really appreciate it! Thanks in advance.
I don't know why it doesn't work like I've been quite wondering for some time. BTW I'm making a hangman game if anyone want the project hmu ig: rami_chalouhi. its like an actual game with login and loading etc..
You keep overwriting 'tab'. you only have the last line
string[] tab = null;
while ((line = sr.ReadLine()) != null)
{
tab = line.Split("\n");
}
Better would be a list of words
var words = new List<string>();
while ((line = sr.ReadLine()) != null)
{
var tab = line.Split("\n");
words.AddRange(tab);
}
in fact that split doesnt make any sense, ReadLine will already be splitting on new line (my first scan I assumed you were splitting on a needed delimter like space or ',') So all that code can be replaced by
var words = File.ReadAllLines("Words.txt");
see https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalllines?view=net-6.0
The rest of that code is pretty odd too
current = tab[j];
copycurrent = "";
for (int i = 0; i < current.Length; i++)
{
copycurrent += "_";
}
label1.Text = "";
for (int i = 0; i < copycurrent.Length; i++)
{
label1.Text += copycurrent.Substring(i, 1);
label1.Text += " ";
}
you want
a string of "_" thats the same length as the selected word
to fill label1.Text with that _ string plus an extra space
current = tab[j];
var copycurrent = new string('_', current.Length);
label1.Text = copycurrent + " ";
is a lot simpler and clearer
So that whole function becomes
string current;
string copycurrent;
try
{
var words = File.ReadAllLines("Words.txt");
Random r = new Random();
int j = r.Next(words.Length);
current = words[j];
copycurrent = new string('_', current.Length);
label1.Text = copycurrent + " ";
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

How can I make 1 button click fill out 2 different text boxes

I need help making this program fill out 2 different text boxes. It works if I only use it to fill out 1 text box using a First name. But when I try and add another text box to generate a random Last name it just seems to do fail.
private void button1_Click(object sender, EventArgs e)
{
Random r = new Random();
int currentLine = 1;
string pick = null;
foreach (string line in File.ReadLines("C:\\First Names.txt"))
{
if (r.Next(currentLine) == 0)
{
pick = line;
}
++currentLine;
textBox1.Text = pick;
}
Random n = new Random();
int currentLine1 = 1;
string pick1 = null;
foreach (string line1 in File.ReadLines("C:\\Last Names.txt"))
{
if (n.Next(currentLine1) == 0)
{
pick1 = line1;
}
++currentLine1;
textBox2.Text = pick1;
}
}
}
}
Check whether the path for the file is appropriate.
You need not create two objects for the same class and access them differently.One object is enough.
3.Check whether data is there in the file or not.`Random r = new Random();
int currentLine = 1;
string pick = null;
foreach (string line in File.ReadLines("C:\\First Names.txt"))
{
if (r.Next(currentLine) == 0)
{
pick = line;
}
++currentLine;
textBox1.Text = pick;
}
currentLine=0;pick=0;
foreach (string line in File.ReadLines("C:\\Last Names.txt"))
{
if (r.Next(currentLine) == 0)
{
pick = line;
}
++currentLine;
textBox2.Text = pick;
}
}`
If the first iteration gets the value, then the second must also get. Else there is some problem with Text file or the 'textBox2' is not the correct ID you are looking for.

C# RichTextBox Line-by-line scan

Hey Im creating my own coding language and I already have the entire application set up with save, open, close, new, etc.
In order for the "Run" part to work, I need a way to scan and test every single line in richTextBox1.
Maybe for past Java users, something along the lines of the "java-util-scanner," but easier to use for testing each line.
Does anyone know a way to do this, where a string "scannedString" would be tested as so:
if(scannedString == "#load(moduleFiles)") {
//action here
}
string scannedStringNextLine = ???
if(scannedStringNextLine = "") {
//action here
}
Eventually it would look more like this:
if(scannedString == "code1" || scannedString == "code2" etc... ) {
if(scannedString == "code1") {
//action here
}
} else {
//error message
}
hope this is enough information...
To get lines of code of the RichTextBox you can split the content by a new line symbol:
var lines = this.richTextBox.Text.Split('\n').ToList();
The lines are in order of appearance. Then you can go through lines in a for or foreach loop:
foreach (var line in lines)
{
// access a line, evaluate it, etc.
}
One way to do it is to split the text on the newline charaters and then parse each line for the text you care about:
private void btnScan_Click(object sender, EventArgs e)
{
var code = richTextBox1.Text;
foreach (var line in code.Split(new []{'\n','\r'},
StringSplitOptions.RemoveEmptyEntries))
{
CheckForLoad(line);
}
}
void CheckForLoad(string line)
{
if (string.IsNullOrWhiteSpace(line)) return;
int i = line.IndexOf("#load");
if (i < 0) return;
int openParen = line.IndexOf("(", i + 1);
if (openParen < 0) return;
int closeParen = line.IndexOf(")", openParen + 1);
if (closeParen < 0) return;
string modules = line.Substring(openParen + 1, closeParen - openParen - 1);
MessageBox.Show(string.Format("Loading modules: {0}", modules));
}

Implement block comment/uncomment in ScintillaNET control

I'm trying to implement a custom text-editor in C# using the ScintillaNET component. I've got most of it right until now, but stuck at one point. I want to give the user the ability to block comment/uncomment the selected text. I tried a lot, but cannot find any examples online. The only thing I seem to get from the control's Selection object are the Start and End positions, but that isn't much help
private void commentBlockToolStripMenuItem_Click(object sender, EventArgs e)
{
if (txtSQL.Selection.Text.Length > 0)
{
String start = txtSQL.Selection.Start.ToString();
String end = txtSQL.Selection.End.ToString();
MessageBox.Show(start + "::" + end);
}
}
Were any of you able to successfully implement this using the ScintillaNET control?
EDIT:
After some improvization, I'm able to do it somehow, but after block is commented, last line moves out of selection!
private void commentBlockToolStripMenuItem_Click(object sender, EventArgs e)
{
if (txtSQL.Selection.Text.Length > 0)
{
Range range = txtSQL.Selection.Range;
int f = range.StartingLine.Number;
int t = range.EndingLine.Number;
int endpos = txtSQL.Selection.End;
for (int i = f; i <= t; i++)
{
//txtSQL.GoTo.Line(i);
string tstr = txtSQL.Lines[i].Text.Replace(Environment.NewLine, "");
txtSQL.Lines[i].Text = "--" + tstr;
}
}
}
After a bit of experimentation, I found a way to accomplish this. Though I doubt if it is the most elegant of solutions!
private void commentBlockToolStripMenuItem_Click(object sender, EventArgs e)
{
if (txtSQL.Selection.Text.Length > 0)
{
Range range = txtSQL.Selection.Range;
int f = range.StartingLine.Number;
int t = range.EndingLine.Number;
for (int i = f; i <= t; i++)
{
txtSQL.InsertText(txtSQL.Lines[i].StartPosition,"--");
}
txtSQL.Selection.Start = txtSQL.Lines[f].StartPosition;
txtSQL.Selection.End = txtSQL.Lines[t].EndPosition;
}
}
Actually I found a very simple solution to this. To block comment do
scintilla1.Lexing.LineComment();
And to block uncomment do
scintilla1.Lexing.LineUncomment();

RegEx for ranges

I want to be able to use a RegEx to parse out ranges like a Windows Print dialog (such as 1-50,100-110,111,112). The following is my current code and I am not clear on how to parse for the additional commas and numbers. I can parse out the hyphen, but not sure how to do it for additional commas or hyphens
private void tboxRowNum_Leave(object sender, EventArgs e)
{
Regex.Replace(tboxRowNum.Text, #"(?<first>\d+)-(?<last>\d+)",
new MatchEvaluator(this.parseSpaceDefinition));
}
private string parseSpaceDefinition(Match m)
{
int first = int.Parse(m.Groups["first"].Value);
int last = int.Parse(m.Groups["last"].Value);
StringBuilder sb = new StringBuilder(first.ToString());
for (int i = first + 1; i <= last; i++)
{
if (spaceItems == 0)
{
if (isNumeric(sb.ToString(), System.Globalization.NumberStyles.Integer))
{
startingSpace = Convert.ToInt32(sb.ToString());
}
}
sb.Append("," + i.ToString().Replace(" ", ""));
spaceItems++;
endingSpace = i;
}
tboxRowDesc.Text = sb.ToString();
return sb.ToString();
}
Edit 1: The modified code gets me what I want:
private void tboxRowNum_Leave(object sender, EventArgs e)
{
string[] parts = tboxRowNum.Text.Split(',');
for (int i = 0; i < parts.Length; i++)
{
if (parts[i].IndexOf('-') >= 0)
{
Regex.Replace(parts[i], #"(?<first>\d+)-(?<last>\d+)",
new MatchEvaluator(this.parseSpaceDefinition));
}
else
{
int number;
if(!(int.TryParse(parts[i], out number)))
{
MessageBox.Show("Incomplete/Invalid formula", "Invalid Space Definition");
tboxRowDesc.Text = "";
}
else
{
tboxRowDesc.Text += "," + number;
spaceItems++;
}
}
}
}
string[] ranges = inputString.split(',');
foreach (string rangeCandidate in ranges) {
// See if matches regex
}
Split it first on comma, and for each part check if it matches your regexp. If it does, do what you are already doing, otherwise just use int.Parse (or int.TryParse for robustness).

Categories

Resources