on master help sign in
Make command implementation return a Result<()>
9c02a1bLionel Flandrin
helix-term/src/commands.rs+260−155
@@ -23,7 +23,7 @@ use helix_view::{
23 Document, DocumentId, Editor, ViewId,
23 Document, DocumentId, Editor, ViewId,
24};
24};
25
25
26−use anyhow::anyhow;
26+use anyhow::{anyhow, bail};
27use helix_lsp::{
27use helix_lsp::{
28 lsp,
28 lsp,
29 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
29 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
@@ -1161,34 +1161,52 @@ mod cmd {
1161 pub alias: Option<&'static str>,
1161 pub alias: Option<&'static str>,
1162 pub doc: &'static str,
1162 pub doc: &'static str,
1163 // params, flags, helper, completer
1163 // params, flags, helper, completer
1164− pub fun: fn(&mut compositor::Context, &[&str], PromptEvent),
1164+ pub fun: fn(&mut compositor::Context, &[&str], PromptEvent) -> anyhow::Result<()>,
1165 pub completer: Option<Completer>,
1165 pub completer: Option<Completer>,
1166 }
1166 }
1167
1167
1168+ fn quit(
1168− fn quit(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1169+ cx: &mut compositor::Context,
1170+ _args: &[&str],
1171+ _event: PromptEvent,
1172+ ) -> anyhow::Result<()> {
1169 // last view and we have unsaved changes
1173 // last view and we have unsaved changes
1170− if cx.editor.tree.views().count() == 1 && buffers_remaining_impl(cx.editor) {
1174+ if cx.editor.tree.views().count() == 1 {
1171− return;
1175+ buffers_remaining_impl(cx.editor)?
1172 }
1176 }
1177+
1173 cx.editor
1178 cx.editor
1174 .close(view!(cx.editor).id, /* close_buffer */ false);
1179 .close(view!(cx.editor).id, /* close_buffer */ false);
1180+
1181+ Ok(())
1175 }
1182 }
1176
1183
1184+ fn force_quit(
1177− fn force_quit(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1185+ cx: &mut compositor::Context,
1186+ _args: &[&str],
1187+ _event: PromptEvent,
1188+ ) -> anyhow::Result<()> {
1178 cx.editor
1189 cx.editor
1179 .close(view!(cx.editor).id, /* close_buffer */ false);
1190 .close(view!(cx.editor).id, /* close_buffer */ false);
1191+
1192+ Ok(())
1180 }
1193 }
1181
1194
1195+ fn open(
1182− fn open(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1196+ cx: &mut compositor::Context,
1197+ args: &[&str],
1198+ _event: PromptEvent,
1199+ ) -> anyhow::Result<()> {
1183 match args.get(0) {
1200 match args.get(0) {
1184 Some(path) => {
1201 Some(path) => {
1185− // TODO: handle error
1186− let _ = cx.editor.open(path.into(), Action::Replace);
1202+ let _ = cx.editor.open(path.into(), Action::Replace)?;
1203+
1204+ Ok(())
1187 }
1205 }
1188 None => {
1206 None => {
1189− cx.editor.set_error("wrong argument count".to_string());
1207+ bail!("wrong argument count");
1190 }
1208 }
1191− };
1209+ }
1192 }
1210 }
1193
1211
1194 fn write_impl<P: AsRef<Path>>(
1212 fn write_impl<P: AsRef<Path>>(
@@ -1200,11 +1218,11 @@ mod cmd {
1200
1218
1201 if let Some(path) = path {
1219 if let Some(path) = path {
1202 if let Err(err) = doc.set_path(path.as_ref()) {
1220 if let Err(err) = doc.set_path(path.as_ref()) {
1203− return Err(anyhow!("invalid filepath: {}", err));
1221+ bail!("invalid filepath: {}", err);
1204 };
1222 };
1205 }
1223 }
1206 if doc.path().is_none() {
1224 if doc.path().is_none() {
1207− return Err(anyhow!("cannot write a buffer without a filename"));
1225+ bail!("cannot write a buffer without a filename");
1208 }
1226 }
1209 let fmt = doc.auto_format().map(|fmt| {
1227 let fmt = doc.auto_format().map(|fmt| {
1210 let shared = fmt.shared();
1228 let shared = fmt.shared();
@@ -1220,21 +1238,33 @@ mod cmd {
1220 Ok(tokio::spawn(doc.format_and_save(fmt)))
1238 Ok(tokio::spawn(doc.format_and_save(fmt)))
1221 }
1239 }
1222
1240
1241+ fn write(
1223− fn write(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1242+ cx: &mut compositor::Context,
1243+ args: &[&str],
1244+ _event: PromptEvent,
1245+ ) -> anyhow::Result<()> {
1224− match write_impl(cx, args.first()) {
1246+ let handle = write_impl(cx, args.first())?;
1225− Err(e) => cx.editor.set_error(e.to_string()),
1226− Ok(handle) => {
1227− cx.jobs
1247+ cx.jobs
1228− .add(Job::new(handle.unwrap_or_else(|e| Err(e.into()))).wait_before_exiting());
1248+ .add(Job::new(handle.unwrap_or_else(|e| Err(e.into()))).wait_before_exiting());
1229− }
1249+
1230− };
1250+ Ok(())
1231 }
1251 }
1232
1252
1253+ fn new_file(
1233− fn new_file(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1254+ cx: &mut compositor::Context,
1255+ _args: &[&str],
1256+ _event: PromptEvent,
1257+ ) -> anyhow::Result<()> {
1234 cx.editor.new_file(Action::Replace);
1258 cx.editor.new_file(Action::Replace);
1259+
1260+ Ok(())
1235 }
1261 }
1236
1262
1263+ fn format(
1237− fn format(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1264+ cx: &mut compositor::Context,
1265+ _args: &[&str],
1266+ _event: PromptEvent,
1267+ ) -> anyhow::Result<()> {
1238 let (_, doc) = current!(cx.editor);
1268 let (_, doc) = current!(cx.editor);
1239
1269
1240 if let Some(format) = doc.format() {
1270 if let Some(format) = doc.format() {
@@ -1242,9 +1272,14 @@ mod cmd {
1242 make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
1272 make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
1243 cx.jobs.callback(callback);
1273 cx.jobs.callback(callback);
1244 }
1274 }
1245− }
1246
1275
1276+ Ok(())
1277+ }
1278+ fn set_indent_style(
1247− fn set_indent_style(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1279+ cx: &mut compositor::Context,
1280+ args: &[&str],
1281+ _event: PromptEvent,
1282+ ) -> anyhow::Result<()> {
1248 use IndentStyle::*;
1283 use IndentStyle::*;
1249
1284
1250 // If no argument, report current indent style.
1285 // If no argument, report current indent style.
@@ -1256,7 +1291,7 @@ mod cmd {
1256 Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n),
1291 Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n),
1257 _ => "error".into(), // Shouldn't happen.
1292 _ => "error".into(), // Shouldn't happen.
1258 });
1293 });
1259− return;
1294+ return Ok(());
1260 }
1295 }
1261
1296
1262 // Attempt to parse argument as an indent style.
1297 // Attempt to parse argument as an indent style.
@@ -1274,15 +1309,19 @@ mod cmd {
1274 if let Some(s) = style {
1309 if let Some(s) = style {
1275 let doc = doc_mut!(cx.editor);
1310 let doc = doc_mut!(cx.editor);
1276 doc.indent_style = s;
1311 doc.indent_style = s;
1312+
1313+ Ok(())
1277 } else {
1314 } else {
1278− // Invalid argument.
1279− cx.editor
1280− .set_error(format!("invalid indent style '{}'", args[0],));
1315+ bail!("invalid indent style '{}'", args[0]);
1281 }
1316 }
1282 }
1317 }
1283
1318
1284 /// Sets or reports the current document's line ending setting.
1319 /// Sets or reports the current document's line ending setting.
1320+ fn set_line_ending(
1285− fn set_line_ending(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1321+ cx: &mut compositor::Context,
1322+ args: &[&str],
1323+ _event: PromptEvent,
1324+ ) -> anyhow::Result<()> {
1286 use LineEnding::*;
1325 use LineEnding::*;
1287
1326
1288 // If no argument, report current line ending setting.
1327 // If no argument, report current line ending setting.
@@ -1298,7 +1337,8 @@ mod cmd {
1298 // These should never be a document's default line ending.
1337 // These should never be a document's default line ending.
1299 VT | LS | PS => "error".into(),
1338 VT | LS | PS => "error".into(),
1300 });
1339 });
1340+
1301− return;
1341+ return Ok(());
1302 }
1342 }
1303
1343
1304 // Attempt to parse argument as a line ending.
1344 // Attempt to parse argument as a line ending.
@@ -1314,70 +1354,66 @@ mod cmd {
1314
1354
1315 if let Some(le) = line_ending {
1355 if let Some(le) = line_ending {
1316 doc_mut!(cx.editor).line_ending = le;
1356 doc_mut!(cx.editor).line_ending = le;
1357+ Ok(())
1317 } else {
1358 } else {
1318− // Invalid argument.
1319− cx.editor
1320− .set_error(format!("invalid line ending '{}'", args[0],));
1359+ bail!("invalid line ending '{}'", args[0]);
1321 }
1360 }
1322 }
1361 }
1323
1362
1363+ fn earlier(
1324− fn earlier(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1364+ cx: &mut compositor::Context,
1365+ args: &[&str],
1366+ _event: PromptEvent,
1367+ ) -> anyhow::Result<()> {
1368+ let uk = args
1369+ .join(" ")
1325− let uk = match args.join(" ").parse::<helix_core::history::UndoKind>() {
1370+ .parse::<helix_core::history::UndoKind>()
1326− Ok(uk) => uk,
1371+ .map_err(|s| anyhow!(s))?;
1327− Err(msg) => {
1372+
1328− cx.editor.set_error(msg);
1329− return;
1330− }
1331− };
1332 let (view, doc) = current!(cx.editor);
1373 let (view, doc) = current!(cx.editor);
1333− doc.earlier(view.id, uk)
1374+ doc.earlier(view.id, uk);
1375+
1376+ Ok(())
1334 }
1377 }
1335
1378
1379+ fn later(
1336− fn later(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1380+ cx: &mut compositor::Context,
1381+ args: &[&str],
1382+ _event: PromptEvent,
1383+ ) -> anyhow::Result<()> {
1384+ let uk = args
1385+ .join(" ")
1337− let uk = match args.join(" ").parse::<helix_core::history::UndoKind>() {
1386+ .parse::<helix_core::history::UndoKind>()
1338− Ok(uk) => uk,
1387+ .map_err(|s| anyhow!(s))?;
1339− Err(msg) => {
1340− cx.editor.set_error(msg);
1341− return;
1342− }
1343− };
1344 let (view, doc) = current!(cx.editor);
1388 let (view, doc) = current!(cx.editor);
1345− doc.later(view.id, uk)
1389+ doc.later(view.id, uk);
1390+
1391+ Ok(())
1346 }
1392 }
1347
1393
1394+ fn write_quit(
1348− fn write_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1395+ cx: &mut compositor::Context,
1396+ args: &[&str],
1397+ event: PromptEvent,
1398+ ) -> anyhow::Result<()> {
1349− match write_impl(cx, args.first()) {
1399+ let handle = write_impl(cx, args.first())?;
1350− Ok(handle) => {
1351− if let Err(e) = helix_lsp::block_on(handle) {
1400+ helix_lsp::block_on(handle)?;
1352− cx.editor.set_error(e.to_string());
1353− } else {
1354− quit(cx, &[], event);
1401+ quit(cx, &[], event)
1355− }
1356− }
1357− Err(e) => {
1358− cx.editor.set_error(e.to_string());
1359− }
1360− }
1361 }
1402 }
1362
1403
1404+ fn force_write_quit(
1363− fn force_write_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1405+ cx: &mut compositor::Context,
1406+ args: &[&str],
1407+ event: PromptEvent,
1408+ ) -> anyhow::Result<()> {
1364− match write_impl(cx, args.first()) {
1409+ let handle = write_impl(cx, args.first())?;
1365− Ok(handle) => {
1366− if let Err(e) = helix_lsp::block_on(handle) {
1410+ helix_lsp::block_on(handle)?;
1367− cx.editor.set_error(e.to_string());
1368− } else {
1369− force_quit(cx, &[], event);
1411+ force_quit(cx, &[], event)
1370− }
1371− }
1372− Err(e) => {
1373− cx.editor.set_error(e.to_string());
1374− }
1375− }
1376 }
1412 }
1377
1413
1378− /// Returns `true` if there are modified buffers remaining and sets editor error,
1414+ /// Results an error if there are modified buffers remaining and sets editor error,
1379− /// otherwise returns `false`
1415+ /// otherwise returns `Ok(())`
1380− fn buffers_remaining_impl(editor: &mut Editor) -> bool {
1416+ fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> {
1381 let modified: Vec<_> = editor
1417 let modified: Vec<_> = editor
1382 .documents()
1418 .documents()
1383 .filter(|doc| doc.is_modified())
1419 .filter(|doc| doc.is_modified())
@@ -1388,16 +1424,13 @@ mod cmd {
1388 })
1424 })
1389 .collect();
1425 .collect();
1390 if !modified.is_empty() {
1426 if !modified.is_empty() {
1391− let err = format!(
1427+ bail!(
1392 "{} unsaved buffer(s) remaining: {:?}",
1428 "{} unsaved buffer(s) remaining: {:?}",
1393 modified.len(),
1429 modified.len(),
1394 modified
1430 modified
1395 );
1431 );
1396− editor.set_error(err);
1397− true
1398− } else {
1399− false
1400 }
1432 }
1433+ Ok(())
1401 }
1434 }
1402
1435
1403 fn write_all_impl(
1436 fn write_all_impl(
@@ -1406,7 +1439,7 @@ mod cmd {
1406 _event: PromptEvent,
1439 _event: PromptEvent,
1407 quit: bool,
1440 quit: bool,
1408 force: bool,
1441 force: bool,
1409− ) {
1442+ ) -> anyhow::Result<()> {
1410 let mut errors = String::new();
1443 let mut errors = String::new();
1411
1444
1412 // save all documents
1445 // save all documents
@@ -1419,11 +1452,10 @@ mod cmd {
1419 // TODO: handle error.
1452 // TODO: handle error.
1420 let _ = helix_lsp::block_on(tokio::spawn(doc.save()));
1453 let _ = helix_lsp::block_on(tokio::spawn(doc.save()));
1421 }
1454 }
1422− editor.set_error(errors);
1423
1455
1424 if quit {
1456 if quit {
1457+ if !force {
1425− if !force && buffers_remaining_impl(editor) {
1458+ buffers_remaining_impl(editor)?;
1426− return;
1427 }
1459 }
1428
1460
1429 // close all views
1461 // close all views
@@ -1432,23 +1464,42 @@ mod cmd {
1432 editor.close(view_id, false);
1464 editor.close(view_id, false);
1433 }
1465 }
1434 }
1466 }
1467+
1468+ bail!(errors)
1435 }
1469 }
1436
1470
1471+ fn write_all(
1437− fn write_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1472+ cx: &mut compositor::Context,
1473+ args: &[&str],
1474+ event: PromptEvent,
1475+ ) -> anyhow::Result<()> {
1438 write_all_impl(&mut cx.editor, args, event, false, false)
1476 write_all_impl(&mut cx.editor, args, event, false, false)
1439 }
1477 }
1440
1478
1479+ fn write_all_quit(
1441− fn write_all_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1480+ cx: &mut compositor::Context,
1481+ args: &[&str],
1482+ event: PromptEvent,
1483+ ) -> anyhow::Result<()> {
1442 write_all_impl(&mut cx.editor, args, event, true, false)
1484 write_all_impl(&mut cx.editor, args, event, true, false)
1443 }
1485 }
1444
1486
1445− fn force_write_all_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1487+ fn force_write_all_quit(
1488+ cx: &mut compositor::Context,
1489+ args: &[&str],
1490+ event: PromptEvent,
1491+ ) -> anyhow::Result<()> {
1446 write_all_impl(&mut cx.editor, args, event, true, true)
1492 write_all_impl(&mut cx.editor, args, event, true, true)
1447 }
1493 }
1448
1494
1495+ fn quit_all_impl(
1496+ editor: &mut Editor,
1497+ _args: &[&str],
1449− fn quit_all_impl(editor: &mut Editor, _args: &[&str], _event: PromptEvent, force: bool) {
1498+ _event: PromptEvent,
1499+ force: bool,
1500+ ) -> anyhow::Result<()> {
1501+ if !force {
1450− if !force && buffers_remaining_impl(editor) {
1502+ buffers_remaining_impl(editor)?;
1451− return;
1452 }
1503 }
1453
1504
1454 // close all views
1505 // close all views
@@ -1456,57 +1507,82 @@ mod cmd {
1456 for view_id in views {
1507 for view_id in views {
1457 editor.close(view_id, false);
1508 editor.close(view_id, false);
1458 }
1509 }
1510+
1511+ Ok(())
1459 }
1512 }
1460
1513
1514+ fn quit_all(
1461− fn quit_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1515+ cx: &mut compositor::Context,
1516+ args: &[&str],
1517+ event: PromptEvent,
1518+ ) -> anyhow::Result<()> {
1462 quit_all_impl(&mut cx.editor, args, event, false)
1519 quit_all_impl(&mut cx.editor, args, event, false)
1463 }
1520 }
1464
1521
1522+ fn force_quit_all(
1465− fn force_quit_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) {
1523+ cx: &mut compositor::Context,
1524+ args: &[&str],
1525+ event: PromptEvent,
1526+ ) -> anyhow::Result<()> {
1466 quit_all_impl(&mut cx.editor, args, event, true)
1527 quit_all_impl(&mut cx.editor, args, event, true)
1467 }
1528 }
1468
1529
1530+ fn theme(
1469− fn theme(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1531+ cx: &mut compositor::Context,
1532+ args: &[&str],
1533+ _event: PromptEvent,
1534+ ) -> anyhow::Result<()> {
1470 let theme = if let Some(theme) = args.first() {
1535 let theme = if let Some(theme) = args.first() {
1471 theme
1536 theme
1472 } else {
1537 } else {
1473− cx.editor.set_error("theme name not provided".into());
1538+ bail!("theme name not provided");
1474− return;
1475 };
1539 };
1476
1540
1477− cx.editor.set_theme_from_name(theme);
1541+ cx.editor.set_theme_from_name(theme)
1478 }
1542 }
1479
1543
1480 fn yank_main_selection_to_clipboard(
1544 fn yank_main_selection_to_clipboard(
1481 cx: &mut compositor::Context,
1545 cx: &mut compositor::Context,
1482 _args: &[&str],
1546 _args: &[&str],
1483 _event: PromptEvent,
1547 _event: PromptEvent,
1484− ) {
1548+ ) -> anyhow::Result<()> {
1485− yank_main_selection_to_clipboard_impl(&mut cx.editor);
1549+ yank_main_selection_to_clipboard_impl(&mut cx.editor)
1486 }
1550 }
1487
1551
1488− fn yank_joined_to_clipboard(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1552+ fn yank_joined_to_clipboard(
1553+ cx: &mut compositor::Context,
1554+ args: &[&str],
1555+ _event: PromptEvent,
1556+ ) -> anyhow::Result<()> {
1489 let (_, doc) = current!(cx.editor);
1557 let (_, doc) = current!(cx.editor);
1490 let separator = args
1558 let separator = args
1491 .first()
1559 .first()
1492 .copied()
1560 .copied()
1493 .unwrap_or_else(|| doc.line_ending.as_str());
1561 .unwrap_or_else(|| doc.line_ending.as_str());
1494− yank_joined_to_clipboard_impl(&mut cx.editor, separator);
1562+ yank_joined_to_clipboard_impl(&mut cx.editor, separator)
1495 }
1563 }
1496
1564
1497− fn paste_clipboard_after(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1565+ fn paste_clipboard_after(
1566+ cx: &mut compositor::Context,
1567+ _args: &[&str],
1568+ _event: PromptEvent,
1569+ ) -> anyhow::Result<()> {
1498− paste_clipboard_impl(&mut cx.editor, Paste::After);
1570+ paste_clipboard_impl(&mut cx.editor, Paste::After)
1499 }
1571 }
1500
1572
1501− fn paste_clipboard_before(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1573+ fn paste_clipboard_before(
1574+ cx: &mut compositor::Context,
1575+ _args: &[&str],
1576+ _event: PromptEvent,
1577+ ) -> anyhow::Result<()> {
1502− paste_clipboard_impl(&mut cx.editor, Paste::After);
1578+ paste_clipboard_impl(&mut cx.editor, Paste::After)
1503 }
1579 }
1504
1580
1505 fn replace_selections_with_clipboard(
1581 fn replace_selections_with_clipboard(
1506 cx: &mut compositor::Context,
1582 cx: &mut compositor::Context,
1507 _args: &[&str],
1583 _args: &[&str],
1508 _event: PromptEvent,
1584 _event: PromptEvent,
1509− ) {
1585+ ) -> anyhow::Result<()> {
1510 let (view, doc) = current!(cx.editor);
1586 let (view, doc) = current!(cx.editor);
1511
1587
1512 match cx.editor.clipboard_provider.get_contents() {
1588 match cx.editor.clipboard_provider.get_contents() {
@@ -1520,71 +1596,89 @@ mod cmd {
1520
1596
1521 doc.apply(&transaction, view.id);
1597 doc.apply(&transaction, view.id);
1522 doc.append_changes_to_history(view.id);
1598 doc.append_changes_to_history(view.id);
1599+ Ok(())
1600+ }
1601+ Err(e) => {
1602+ log::error!("Couldn't get system clipboard contents: {:?}", e);
1603+ bail!("Couldn't get system clipboard contents: {:?}", e);
1523 }
1604 }
1524− Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e),
1525 }
1605 }
1526 }
1606 }
1527
1607
1528− fn show_clipboard_provider(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1608+ fn show_clipboard_provider(
1609+ cx: &mut compositor::Context,
1610+ _args: &[&str],
1611+ _event: PromptEvent,
1612+ ) -> anyhow::Result<()> {
1529 cx.editor
1613 cx.editor
1530 .set_status(cx.editor.clipboard_provider.name().into());
1614 .set_status(cx.editor.clipboard_provider.name().into());
1615+ Ok(())
1531 }
1616 }
1532
1617
1533− fn change_current_directory(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) {
1618+ fn change_current_directory(
1619+ cx: &mut compositor::Context,
1620+ args: &[&str],
1621+ _event: PromptEvent,
1622+ ) -> anyhow::Result<()> {
1534− let dir = match args.first() {
1623+ let dir = args
1535− Some(dir) => dir,
1624+ .first()
1536− None => {
1537− cx.editor.set_error("target directory not provided".into());
1625+ .ok_or_else(|| anyhow!("target directory not provided"))?;
1538− return;
1539− }
1540− };
1541
1626
1542 if let Err(e) = std::env::set_current_dir(dir) {
1627 if let Err(e) = std::env::set_current_dir(dir) {
1543− cx.editor.set_error(format!(
1544− "Couldn't change the current working directory: {:?}",
1628+ bail!("Couldn't change the current working directory: {:?}", e);
1545− e
1546− ));
1547− return;
1548 }
1629 }
1549
1630
1550 match std::env::current_dir() {
1631 match std::env::current_dir() {
1632+ Ok(cwd) => {
1551− Ok(cwd) => cx.editor.set_status(format!(
1633+ cx.editor.set_status(format!(
1552− "Current working directory is now {}",
1634+ "Current working directory is now {}",
1553− cwd.display()
1635+ cwd.display()
1636+ ));
1554− )),
1637+ Ok(())
1555− Err(e) => cx
1638+ }
1556− .editor
1557− .set_error(format!("Couldn't get the new working directory: {}", e)),
1639+ Err(e) => bail!("Couldn't get the new working directory: {}", e),
1558 }
1640 }
1559 }
1641 }
1560
1642
1561− fn show_current_directory(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) {
1643+ fn show_current_directory(
1644+ cx: &mut compositor::Context,
1645+ _args: &[&str],
1646+ _event: PromptEvent,
1647+ ) -> anyhow::Result<()> {
1562 match std::env::current_dir() {
1648 match std::env::current_dir() {
1563− Ok(cwd) => cx
1649+ Ok(cwd) => {
1564− .editor
1650+ cx.editor
1565− .set_status(format!("Current working directory is {}", cwd.display())),
1651+ .set_status(format!("Current working directory is {}", cwd.display()));
1566− Err(e) => cx
1652+ Ok(())
1567− .editor
1653+ }
1568− .set_error(format!("Couldn't get the current working directory: {}", e)),
1654+ Err(e) => bail!("Couldn't get the current working directory: {}", e),
1569 }
1655 }
1570 }
1656 }
1571
1657
1572 /// Sets the [`Document`]'s encoding..
1658 /// Sets the [`Document`]'s encoding..
1659+ fn set_encoding(
1573− fn set_encoding(cx: &mut compositor::Context, args: &[&str], _: PromptEvent) {
1660+ cx: &mut compositor::Context,
1661+ args: &[&str],
1662+ _event: PromptEvent,
1663+ ) -> anyhow::Result<()> {
1574 let (_, doc) = current!(cx.editor);
1664 let (_, doc) = current!(cx.editor);
1575 if let Some(label) = args.first() {
1665 if let Some(label) = args.first() {
1576 doc.set_encoding(label)
1666 doc.set_encoding(label)
1577− .unwrap_or_else(|e| cx.editor.set_error(e.to_string()));
1578 } else {
1667 } else {
1579 let encoding = doc.encoding().name().to_string();
1668 let encoding = doc.encoding().name().to_string();
1580− cx.editor.set_status(encoding)
1669+ cx.editor.set_status(encoding);
1670+ Ok(())
1581 }
1671 }
1582 }
1672 }
1583
1673
1584 /// Reload the [`Document`] from its source file.
1674 /// Reload the [`Document`] from its source file.
1675+ fn reload(
1585− fn reload(cx: &mut compositor::Context, _args: &[&str], _: PromptEvent) {
1676+ cx: &mut compositor::Context,
1677+ _args: &[&str],
1678+ _event: PromptEvent,
1679+ ) -> anyhow::Result<()> {
1586 let (view, doc) = current!(cx.editor);
1680 let (view, doc) = current!(cx.editor);
1587− doc.reload(view.id).unwrap();
1681+ doc.reload(view.id)
1588 }
1682 }
1589
1683
1590 pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1684 pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
@@ -1849,7 +1943,9 @@ fn command_mode(cx: &mut Context) {
1849 }
1943 }
1850
1944
1851 if let Some(cmd) = cmd::COMMANDS.get(parts[0]) {
1945 if let Some(cmd) = cmd::COMMANDS.get(parts[0]) {
1852− (cmd.fun)(cx, &parts[1..], event);
1946+ if let Err(e) = (cmd.fun)(cx, &parts[1..], event) {
1947+ cx.editor.set_error(format!("{}", e));
1948+ }
1853 } else {
1949 } else {
1854 cx.editor
1950 cx.editor
1855 .set_error(format!("no such command: '{}'", parts[0]));
1951 .set_error(format!("no such command: '{}'", parts[0]));
@@ -2758,7 +2854,7 @@ fn yank(cx: &mut Context) {
2758 cx.editor.set_status(msg)
2854 cx.editor.set_status(msg)
2759}
2855}
2760
2856
2761−fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) {
2857+fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow::Result<()> {
2762 let (view, doc) = current!(editor);
2858 let (view, doc) = current!(editor);
2763
2859
2764 let values: Vec<String> = doc
2860 let values: Vec<String> = doc
@@ -2776,17 +2872,20 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) {
2776
2872
2777 if let Err(e) = editor.clipboard_provider.set_contents(joined) {
2873 if let Err(e) = editor.clipboard_provider.set_contents(joined) {
2778 log::error!("Couldn't set system clipboard content: {:?}", e);
2874 log::error!("Couldn't set system clipboard content: {:?}", e);
2875+ bail!("Couldn't set system clipboard content: {:?}", e);
2779 }
2876 }
2780
2877
2781 editor.set_status(msg);
2878 editor.set_status(msg);
2879+
2880+ Ok(())
2782}
2881}
2783
2882
2784fn yank_joined_to_clipboard(cx: &mut Context) {
2883fn yank_joined_to_clipboard(cx: &mut Context) {
2785 let line_ending = current!(cx.editor).1.line_ending;
2884 let line_ending = current!(cx.editor).1.line_ending;
2786− yank_joined_to_clipboard_impl(&mut cx.editor, line_ending.as_str());
2885+ let _ = yank_joined_to_clipboard_impl(&mut cx.editor, line_ending.as_str());
2787}
2886}
2788
2887
2789−fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) {
2888+fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) -> anyhow::Result<()> {
2790 let (view, doc) = current!(editor);
2889 let (view, doc) = current!(editor);
2791
2890
2792 let value = doc
2891 let value = doc
@@ -2796,13 +2895,15 @@ fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) {
2796
2895
2797 if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) {
2896 if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) {
2798 log::error!("Couldn't set system clipboard content: {:?}", e);
2897 log::error!("Couldn't set system clipboard content: {:?}", e);
2898+ bail!("Couldn't set system clipboard content: {:?}", e);
2799 }
2899 }
2800
2900
2801 editor.set_status("yanked main selection to system clipboard".to_owned());
2901 editor.set_status("yanked main selection to system clipboard".to_owned());
2902+ Ok(())
2802}
2903}
2803
2904
2804fn yank_main_selection_to_clipboard(cx: &mut Context) {
2905fn yank_main_selection_to_clipboard(cx: &mut Context) {
2805− yank_main_selection_to_clipboard_impl(&mut cx.editor);
2906+ let _ = yank_main_selection_to_clipboard_impl(&mut cx.editor);
2806}
2907}
2807
2908
2808#[derive(Copy, Clone)]
2909#[derive(Copy, Clone)]
@@ -2850,7 +2951,7 @@ fn paste_impl(
2850 Some(transaction)
2951 Some(transaction)
2851}
2952}
2852
2953
2853−fn paste_clipboard_impl(editor: &mut Editor, action: Paste) {
2954+fn paste_clipboard_impl(editor: &mut Editor, action: Paste) -> anyhow::Result<()> {
2854 let (view, doc) = current!(editor);
2955 let (view, doc) = current!(editor);
2855
2956
2856 match editor
2957 match editor
@@ -2861,18 +2962,22 @@ fn paste_clipboard_impl(editor: &mut Editor, action: Paste) {
2861 Ok(Some(transaction)) => {
2962 Ok(Some(transaction)) => {
2862 doc.apply(&transaction, view.id);
2963 doc.apply(&transaction, view.id);
2863 doc.append_changes_to_history(view.id);
2964 doc.append_changes_to_history(view.id);
2965+ Ok(())
2966+ }
2967+ Ok(None) => Ok(()),
2968+ Err(e) => {
2969+ log::error!("Couldn't get system clipboard contents: {:?}", e);
2970+ bail!("Couldn't get system clipboard contents: {:?}", e);
2864 }
2971 }
2865− Ok(None) => {}
2866− Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e),
2867 }
2972 }
2868}
2973}
2869
2974
2870fn paste_clipboard_after(cx: &mut Context) {
2975fn paste_clipboard_after(cx: &mut Context) {
2871− paste_clipboard_impl(&mut cx.editor, Paste::After);
2976+ let _ = paste_clipboard_impl(&mut cx.editor, Paste::After);
2872}
2977}
2873
2978
2874fn paste_clipboard_before(cx: &mut Context) {
2979fn paste_clipboard_before(cx: &mut Context) {
2875− paste_clipboard_impl(&mut cx.editor, Paste::Before);
2980+ let _ = paste_clipboard_impl(&mut cx.editor, Paste::Before);
2876}
2981}
2877
2982
2878fn replace_with_yanked(cx: &mut Context) {
2983fn replace_with_yanked(cx: &mut Context) {
helix-view/src/editor.rs+3−2
@@ -97,16 +97,17 @@ impl Editor {
97 self._refresh();
97 self._refresh();
98 }
98 }
99
99
100− pub fn set_theme_from_name(&mut self, theme: &str) {
100+ pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> {
101 let theme = match self.theme_loader.load(theme.as_ref()) {
101 let theme = match self.theme_loader.load(theme.as_ref()) {
102 Ok(theme) => theme,
102 Ok(theme) => theme,
103 Err(e) => {
103 Err(e) => {
104 log::warn!("failed setting theme `{}` - {}", theme, e);
104 log::warn!("failed setting theme `{}` - {}", theme, e);
105− return;
105+ anyhow::bail!("failed setting theme `{}` - {}", theme, e);
106 }
106 }
107 };
107 };
108
108
109 self.set_theme(theme);
109 self.set_theme(theme);
110+ Ok(())
110 }
111 }
111
112
112 fn _refresh(&mut self) {
113 fn _refresh(&mut self) {