@@ -95,7 +95,9 @@ Future<IpcResponse> _activate(IpcRequest req, EditorRegistry r) async {
|
||||
Future<IpcResponse> _list(IpcRequest req, EditorRegistry r) async {
|
||||
return IpcResponse.ok(
|
||||
id: req.id,
|
||||
data: {'buffers': [for (final b in r.buffers) b.toJson()]},
|
||||
data: {
|
||||
'buffers': [for (final b in r.buffers) b.toJson()]
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -139,9 +141,7 @@ Future<IpcResponse> _setContent(IpcRequest req, EditorRegistry r) async {
|
||||
if (id == null) return _notFound(req.id, 'no active buffer');
|
||||
if (r.get(id) == null) return _notFound(req.id, 'no such buffer: $id');
|
||||
final content = EditorRegistry.contentFromArgs(req.args);
|
||||
final sel = req.args['selection'] == null
|
||||
? null
|
||||
: EditorRegistry.selectionFromArgs(req.args['selection']);
|
||||
final sel = req.args['selection'] == null ? null : EditorRegistry.selectionFromArgs(req.args['selection']);
|
||||
r.setContent(id, content, selection: sel);
|
||||
return IpcResponse.ok(id: req.id, data: {'id': id, 'length': content.length});
|
||||
}
|
||||
@@ -161,4 +161,3 @@ Future<IpcResponse> _close(IpcRequest req, EditorRegistry r) async {
|
||||
r.close(id);
|
||||
return IpcResponse.ok(id: req.id, data: {'id': id});
|
||||
}
|
||||
|
||||
|
||||
@@ -56,13 +56,15 @@ class FilesService {
|
||||
}
|
||||
|
||||
void registerFilesCommands(DaemonDispatcher d, FilesService files) {
|
||||
d.register('files.root', (req) async => IpcResponse.ok(
|
||||
id: req.id,
|
||||
data: {
|
||||
'path': files.root.absolute.path,
|
||||
'ignorePatterns': files.ignore.length,
|
||||
},
|
||||
));
|
||||
d.register(
|
||||
'files.root',
|
||||
(req) async => IpcResponse.ok(
|
||||
id: req.id,
|
||||
data: {
|
||||
'path': files.root.absolute.path,
|
||||
'ignorePatterns': files.ignore.length,
|
||||
},
|
||||
));
|
||||
|
||||
d.register('files.read', (req) async {
|
||||
final path = req.args['path'] as String?;
|
||||
|
||||
@@ -227,7 +227,9 @@ void registerGitCommands(
|
||||
try {
|
||||
final b = await git.branches();
|
||||
return IpcResponse.ok(id: req.id, data: {
|
||||
'branches': [for (final e in b) {'name': e.name, 'current': e.current}],
|
||||
'branches': [
|
||||
for (final e in b) {'name': e.name, 'current': e.current}
|
||||
],
|
||||
});
|
||||
} on GitException catch (e) {
|
||||
return _gitError(req.id, e);
|
||||
|
||||
@@ -27,8 +27,7 @@ void registerPaneCommands(DaemonDispatcher d, PaneRegistry registry) {
|
||||
d.register('pane.tail', (req) => _tail(req, registry));
|
||||
}
|
||||
|
||||
IpcResponse _userErr(String id, String message, {String? hint}) =>
|
||||
IpcResponse.err(
|
||||
IpcResponse _userErr(String id, String message, {String? hint}) => IpcResponse.err(
|
||||
id: id,
|
||||
error: IpcError(
|
||||
code: IpcExitCode.userError,
|
||||
@@ -70,8 +69,7 @@ Future<IpcResponse> _spawn(IpcRequest req, PaneRegistry registry) async {
|
||||
Map<String, String>? env;
|
||||
if (envArg is Map) {
|
||||
env = {
|
||||
for (final e in envArg.entries)
|
||||
'${e.key}': '${e.value}',
|
||||
for (final e in envArg.entries) '${e.key}': '${e.value}',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +99,9 @@ Future<IpcResponse> _spawn(IpcRequest req, PaneRegistry registry) async {
|
||||
Future<IpcResponse> _list(IpcRequest req, PaneRegistry registry) async {
|
||||
return IpcResponse.ok(
|
||||
id: req.id,
|
||||
data: {'panes': [for (final p in registry.panes) p.toJson()]},
|
||||
data: {
|
||||
'panes': [for (final p in registry.panes) p.toJson()]
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,11 @@ void registerPqlCommands(DaemonDispatcher d, PqlClient pql) {
|
||||
|
||||
d.register('pql.tickets.status', (req) async {
|
||||
final rawIds = req.args['ids'];
|
||||
final ids = rawIds is List ? rawIds.cast<String>() : rawIds is String ? [rawIds] : <String>[];
|
||||
final ids = rawIds is List
|
||||
? rawIds.cast<String>()
|
||||
: rawIds is String
|
||||
? [rawIds]
|
||||
: <String>[];
|
||||
final status = req.args['status'] as String?;
|
||||
if (ids.isEmpty || status == null || status.isEmpty) {
|
||||
return _userError(req.id, 'pql.tickets.status requires ids and status');
|
||||
|
||||
@@ -27,8 +27,7 @@ class Selection {
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is Selection && other.start == start && other.end == end;
|
||||
bool operator ==(Object other) => other is Selection && other.start == start && other.end == end;
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(start, end);
|
||||
|
||||
@@ -31,8 +31,7 @@ class EditorRegistry {
|
||||
|
||||
Iterable<EditorBuffer> get buffers => _buffers.values;
|
||||
EditorBuffer? get(String id) => _buffers[id];
|
||||
EditorBuffer? get active =>
|
||||
_activeId == null ? null : _buffers[_activeId!];
|
||||
EditorBuffer? get active => _activeId == null ? null : _buffers[_activeId!];
|
||||
|
||||
/// Open a file. If [path] is already open, returns the existing
|
||||
/// buffer (no re-read from disk — the in-memory content is the
|
||||
|
||||
@@ -47,16 +47,12 @@ Future<List<FileEntry>> listDir({
|
||||
required String dir,
|
||||
required IgnoreSet ignore,
|
||||
}) async {
|
||||
final resolved = dir.isEmpty
|
||||
? root
|
||||
: Directory('${root.absolute.path}${Platform.pathSeparator}${dir.replaceAll('/', Platform.pathSeparator)}');
|
||||
final resolved = dir.isEmpty ? root : Directory('${root.absolute.path}${Platform.pathSeparator}${dir.replaceAll('/', Platform.pathSeparator)}');
|
||||
if (!await resolved.exists()) return const [];
|
||||
|
||||
final entries = <FileEntry>[];
|
||||
await for (final e in resolved.list(followLinks: false)) {
|
||||
final name = e.uri.pathSegments.isNotEmpty
|
||||
? e.uri.pathSegments.where((s) => s.isNotEmpty).last
|
||||
: '';
|
||||
final name = e.uri.pathSegments.isNotEmpty ? e.uri.pathSegments.where((s) => s.isNotEmpty).last : '';
|
||||
final rel = dir.isEmpty ? name : '$dir/$name';
|
||||
final stat = await e.stat();
|
||||
final isDir = stat.type == FileSystemEntityType.directory;
|
||||
|
||||
@@ -71,9 +71,9 @@ class FileWatcher {
|
||||
Future<void> start() async {
|
||||
if (_sub != null) return;
|
||||
_sub = root.watch(recursive: true).listen(
|
||||
_onEvent,
|
||||
onError: (Object e, StackTrace _) => _controller.addError(e),
|
||||
);
|
||||
_onEvent,
|
||||
onError: (Object e, StackTrace _) => _controller.addError(e),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
|
||||
@@ -209,9 +209,7 @@ class GitClient {
|
||||
|
||||
Future<ProcessResult> _run(List<String> args) async {
|
||||
try {
|
||||
return await Process.run(toolchain.git, args,
|
||||
workingDirectory: workDir.path,
|
||||
environment: toolchain.gitEnv);
|
||||
return await Process.run(toolchain.git, args, workingDirectory: workDir.path, environment: toolchain.gitEnv);
|
||||
} on ProcessException catch (e) {
|
||||
throw GitException('git ${args.first}: ${e.message}', stderr: e.toString());
|
||||
}
|
||||
@@ -223,9 +221,7 @@ class GitClient {
|
||||
if (reverse) args.add('--reverse');
|
||||
args.addAll(['--unidiff-zero', '-']);
|
||||
|
||||
final proc = await Process.start(toolchain.git, args,
|
||||
workingDirectory: workDir.path,
|
||||
environment: toolchain.gitEnv);
|
||||
final proc = await Process.start(toolchain.git, args, workingDirectory: workDir.path, environment: toolchain.gitEnv);
|
||||
proc.stdin.write(patch);
|
||||
await proc.stdin.close();
|
||||
final exitCode = await proc.exitCode;
|
||||
|
||||
@@ -16,6 +16,7 @@ String get gitBin {
|
||||
_gitBin ??= _resolveGit();
|
||||
return _gitBin!;
|
||||
}
|
||||
|
||||
String? _gitBin;
|
||||
|
||||
String _resolveGit() {
|
||||
@@ -215,8 +216,7 @@ Future<String> gitPush(
|
||||
}
|
||||
|
||||
/// List local branches. Returns (name, isCurrent) pairs.
|
||||
Future<List<({String name, bool current})>> gitBranches(
|
||||
Directory workDir) async {
|
||||
Future<List<({String name, bool current})>> gitBranches(Directory workDir) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['branch', '--format=%(refname:short)|%(HEAD)'],
|
||||
@@ -302,9 +302,7 @@ Future<void> _applyPatch(
|
||||
await proc.stdin.close();
|
||||
final exitCode = await proc.exitCode;
|
||||
if (exitCode != 0) {
|
||||
final stderr = await proc.stderr
|
||||
.transform(const SystemEncoding().decoder)
|
||||
.join();
|
||||
final stderr = await proc.stderr.transform(const SystemEncoding().decoder).join();
|
||||
throw GitException(
|
||||
'git apply failed',
|
||||
stderr: stderr,
|
||||
|
||||
+6
-17
@@ -44,15 +44,8 @@ class GitFileStatus {
|
||||
final String? origPath;
|
||||
final GitConflictType? conflictType;
|
||||
|
||||
bool get isStaged =>
|
||||
indexState != null &&
|
||||
indexState != GitFileState.untracked &&
|
||||
indexState != GitFileState.ignored &&
|
||||
!isConflicted;
|
||||
bool get isUnstaged =>
|
||||
workTreeState != null &&
|
||||
workTreeState != GitFileState.untracked &&
|
||||
!isConflicted;
|
||||
bool get isStaged => indexState != null && indexState != GitFileState.untracked && indexState != GitFileState.ignored && !isConflicted;
|
||||
bool get isUnstaged => workTreeState != null && workTreeState != GitFileState.untracked && !isConflicted;
|
||||
bool get isUntracked => workTreeState == GitFileState.untracked;
|
||||
bool get isConflicted => conflictType != null;
|
||||
|
||||
@@ -84,14 +77,10 @@ class GitStatus {
|
||||
final int behind;
|
||||
final List<GitFileStatus> entries;
|
||||
|
||||
List<GitFileStatus> get staged =>
|
||||
entries.where((e) => e.isStaged).toList();
|
||||
List<GitFileStatus> get unstaged =>
|
||||
entries.where((e) => e.isUnstaged).toList();
|
||||
List<GitFileStatus> get untracked =>
|
||||
entries.where((e) => e.isUntracked).toList();
|
||||
List<GitFileStatus> get conflicted =>
|
||||
entries.where((e) => e.isConflicted).toList();
|
||||
List<GitFileStatus> get staged => entries.where((e) => e.isStaged).toList();
|
||||
List<GitFileStatus> get unstaged => entries.where((e) => e.isUnstaged).toList();
|
||||
List<GitFileStatus> get untracked => entries.where((e) => e.isUntracked).toList();
|
||||
List<GitFileStatus> get conflicted => entries.where((e) => e.isConflicted).toList();
|
||||
|
||||
bool get isClean => entries.isEmpty;
|
||||
bool get hasConflicts => entries.any((e) => e.isConflicted);
|
||||
|
||||
@@ -90,9 +90,7 @@ class IpcResponse extends IpcMessage {
|
||||
id: j['id']! as String,
|
||||
ok: ok,
|
||||
data: (j['data'] as Map?)?.cast<String, Object?>() ?? const {},
|
||||
error: ok
|
||||
? null
|
||||
: IpcError.fromJson((j['error'] as Map).cast<String, Object?>()),
|
||||
error: ok ? null : IpcError.fromJson((j['error'] as Map).cast<String, Object?>()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +69,7 @@ class DaemonServer {
|
||||
|
||||
void _handleClient(Socket client) {
|
||||
_clients.add(client);
|
||||
client
|
||||
.cast<List<int>>()
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen(
|
||||
client.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter()).listen(
|
||||
(line) => _handleLine(client, line),
|
||||
onDone: () => _clients.remove(client),
|
||||
onError: (Object e) {
|
||||
|
||||
@@ -22,10 +22,8 @@ class RecordingEventSink implements DaemonEventSink {
|
||||
void emit(IpcEvent event) => events.add(event);
|
||||
|
||||
/// Convenience: filter to a single subsystem (`pane`, `git`, …).
|
||||
Iterable<IpcEvent> ofSubsystem(String subsystem) =>
|
||||
events.where((e) => e.subsystem == subsystem);
|
||||
Iterable<IpcEvent> ofSubsystem(String subsystem) => events.where((e) => e.subsystem == subsystem);
|
||||
|
||||
/// Convenience: filter to a specific `type` (`pane.spawned`, …).
|
||||
Iterable<IpcEvent> ofKind(String kind) =>
|
||||
events.where((e) => e.kind == kind);
|
||||
Iterable<IpcEvent> ofKind(String kind) => events.where((e) => e.kind == kind);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user