Skip to content

Commit 3ec7e9a

Browse files
committed
feat(Studio): add 'Integrate Read Files' menu
1 parent 54792a2 commit 3ec7e9a

File tree

5 files changed

+171
-11
lines changed

5 files changed

+171
-11
lines changed

Studio/DialogUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ void GoToLine(int line) {
291291
ComboBox roomComboBox = new();
292292

293293
Regex labelRegex = new(@"^\s*#[^\s#]");
294-
Regex commentCommandRegex = new(@"^\s*#(play|read|console|set)(\s|,)", RegexOptions.IgnoreCase);
294+
Regex commentCommandRegex = new(@"^\s*#(play|console|set)(\s|,)", RegexOptions.IgnoreCase);
295295
Regex roomRegex = new(@"^\s*#(lvl_)?");
296296
for (int i = 0; i < richText.Lines.Count; i++) {
297297
string lineText = richText.Lines[i];

Studio/IntegrateReadFiles.cs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Text.RegularExpressions;
7+
using System.Windows.Forms;
8+
9+
namespace CelesteStudio;
10+
11+
internal static class IntegrateReadFiles {
12+
public static void Generate() {
13+
string mainFilePath = Studio.Instance.richText.CurrentFileName;
14+
if (!File.Exists(mainFilePath)) {
15+
MessageBox.Show(mainFilePath, "Opened file does not exist");
16+
return;
17+
}
18+
19+
string saveFilePath = ShowSaveDialog(mainFilePath);
20+
if (saveFilePath == null) {
21+
return;
22+
}
23+
24+
try {
25+
string integratedText = ReadAllFiles(mainFilePath);
26+
File.WriteAllText(saveFilePath, integratedText);
27+
Studio.Instance.OpenFile(saveFilePath);
28+
} catch (ReadFileNotExistException e) {
29+
MessageBox.Show(e.Message, "Read file does not exist");
30+
}
31+
}
32+
33+
private static string ShowSaveDialog(string filePath) {
34+
using SaveFileDialog dialog = new();
35+
dialog.DefaultExt = ".tas";
36+
dialog.AddExtension = true;
37+
dialog.Filter = "TAS|*.tas";
38+
dialog.FilterIndex = 0;
39+
dialog.InitialDirectory = Path.GetDirectoryName(filePath);
40+
dialog.FileName = Path.GetFileNameWithoutExtension(filePath) + "_Integrated.tas";
41+
42+
if (dialog.ShowDialog() == DialogResult.OK) {
43+
return dialog.FileName;
44+
} else {
45+
return null;
46+
}
47+
}
48+
49+
private static string ReadAllFiles(string filePath, IEnumerable<string> lines = null) {
50+
StringBuilder result = new();
51+
lines ??= File.ReadLines(filePath);
52+
foreach (string lineText in lines) {
53+
if (TryParseReadCommand(filePath, lineText, out string readText)) {
54+
result.AppendLine($"#{lineText.Trim()}");
55+
result.AppendLine(readText);
56+
} else {
57+
result.AppendLine(lineText);
58+
}
59+
}
60+
61+
return result.ToString();
62+
}
63+
64+
private static bool TryParseReadCommand(string filePath, string readCommand, out string readText) {
65+
readText = null;
66+
readCommand = readCommand.Trim();
67+
if (!readCommand.StartsWith("read", StringComparison.InvariantCultureIgnoreCase)) {
68+
return false;
69+
}
70+
71+
Regex spaceRegex = new(@"^[^,]+?\s+[^,]");
72+
string[] args = spaceRegex.IsMatch(readCommand) ? readCommand.Split() : readCommand.Split(',');
73+
args = args.Select(text => text.Trim()).ToArray();
74+
if (!args[0].Equals("read", StringComparison.InvariantCultureIgnoreCase) || args.Length < 2) {
75+
return false;
76+
}
77+
78+
string readFilePath = args[1];
79+
string fileDirectory = Path.GetDirectoryName(filePath);
80+
readFilePath = FindReadFile(filePath, fileDirectory, readFilePath);
81+
82+
if (!File.Exists(readFilePath)) {
83+
// for compatibility with tas files downloaded from discord
84+
// discord will replace spaces in the file name with underscores
85+
readFilePath = args[1].Replace(" ", "_");
86+
readFilePath = FindReadFile(filePath, fileDirectory, readFilePath);
87+
}
88+
89+
if (!File.Exists(readFilePath)) {
90+
throw new ReadFileNotExistException(readCommand, filePath);
91+
}
92+
93+
int startLine = 0;
94+
int endLine = int.MaxValue - 1;
95+
96+
if (args.Length >= 3) {
97+
startLine = GetLineNumber(readFilePath, args[2]);
98+
}
99+
100+
if (args.Length >= 4) {
101+
endLine = GetLineNumber(readFilePath, args[3]);
102+
}
103+
104+
readText = ReadAllFiles(filePath, File.ReadLines(readFilePath).Take(endLine + 1).Skip(startLine));
105+
return true;
106+
}
107+
108+
private static string FindReadFile(string filePath, string fileDirectory, string readFilePath) {
109+
// Check for full and shortened Read versions
110+
if (fileDirectory != null) {
111+
// Path.Combine can handle the case when filePath is an absolute path
112+
string absoluteOrRelativePath = Path.Combine(fileDirectory, readFilePath);
113+
if (File.Exists(absoluteOrRelativePath) && absoluteOrRelativePath != filePath) {
114+
readFilePath = absoluteOrRelativePath;
115+
} else if (Directory.GetParent(absoluteOrRelativePath) is { } directoryInfo && Directory.Exists(directoryInfo.ToString())) {
116+
string[] files = Directory.GetFiles(directoryInfo.ToString(), $"{Path.GetFileName(readFilePath)}*.tas");
117+
if (files.FirstOrDefault(path => path != filePath) is { } shortenedFilePath) {
118+
readFilePath = shortenedFilePath;
119+
}
120+
}
121+
}
122+
123+
return readFilePath;
124+
}
125+
126+
private static int GetLineNumber(string path, string labelOrLineNumber) {
127+
if (int.TryParse(labelOrLineNumber, out int lineNumber)) {
128+
return lineNumber - 1;
129+
}
130+
131+
int currentLine = 0;
132+
foreach (string readLine in File.ReadLines(path)) {
133+
string line = readLine.Trim();
134+
if (line == $"#{labelOrLineNumber}") {
135+
return currentLine;
136+
}
137+
138+
currentLine++;
139+
}
140+
141+
return 0;
142+
}
143+
}
144+
145+
class ReadFileNotExistException : Exception {
146+
public ReadFileNotExistException(string readCommand, string filePath) : base($"{readCommand}\n{filePath}") { }
147+
}

Studio/RichText/RichText.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,15 +2769,14 @@ protected override void OnKeyDown(KeyEventArgs e) {
27692769

27702770
private void ExpandLine() {
27712771
Selection.Expand();
2772-
int line = Selection.End.iLine;
27732772
if (LinesCount <= 1) {
27742773
// ignored
2775-
} else if (line < LinesCount - 1) {
2776-
Selection.End = new Place(0, line + 1);
2777-
} else {
2774+
} else if (Selection.End.iLine is var endLine && endLine < LinesCount - 1) {
2775+
Selection.End = new Place(0, endLine + 1);
2776+
} else if (Selection.Start.iLine > 0) {
27782777
Place end = Selection.End;
2779-
line = Selection.Start.iLine - 1;
2780-
Selection.Start = new Place(line == -1 ? 0 : Lines[line].Length, line == -1 ? 0 : line);
2778+
int startLine = Selection.Start.iLine - 1;
2779+
Selection.Start = new Place(Lines[startLine].Length, startLine);
27812780
Selection.End = end;
27822781
}
27832782
}

Studio/Studio.Designer.cs

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Studio/Studio.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ private static bool TryGetExactCasePath(string path, out string exactPath) {
486486
return result;
487487
}
488488

489-
private void OpenFile(string fileName = null, int startLine = 0) {
489+
public void OpenFile(string fileName = null, int startLine = 0) {
490490
if (fileName == CurrentFileName && fileName != null) {
491491
return;
492492
}
@@ -609,11 +609,11 @@ private static int GetLine(string path, string labelOrLineNumber) {
609609

610610
int curLine = 0;
611611
foreach (string readLine in File.ReadLines(path)) {
612-
curLine++;
613612
string line = readLine.Trim();
614613
if (line == $"#{labelOrLineNumber}") {
615-
return curLine - 1;
614+
return curLine;
616615
}
616+
curLine++;
617617
}
618618

619619
return 0;
@@ -1431,6 +1431,10 @@ private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) {
14311431
SaveAsFile();
14321432
}
14331433

1434+
private void integrateReadFilesToolStripMenuItem_Click(object sender, EventArgs e) {
1435+
IntegrateReadFiles.Generate();
1436+
}
1437+
14341438
private void commentUncommentTextToolStripMenuItem_Click(object sender, EventArgs e) {
14351439
CommentText(true);
14361440
}

0 commit comments

Comments
 (0)