From 50d0d157561056f43659eaeaaf0562a019d7d43a Mon Sep 17 00:00:00 2001 From: Angela Langat Date: Sat, 6 Sep 2025 21:12:19 +0300 Subject: [PATCH] Fix multi-line function parsing in dependency analyzer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the .parse_function() function to better handle function calls that span multiple lines with arguments. The previous implementation used deparse() which was problematic for multi-line function calls. Changes: - Add explicit handling for function calls with `is.call(x) && length(x) >= 1` - Capture function names and recursively parse arguments to find nested calls - Add proper symbol handling with `is.symbol(x)` - Remove problematic deparse() fallback that couldn't handle multi-line calls - Filter out empty results with `out[nzchar(out)]` - Add length check for safety in $ operator handling This allows the project visualizer to correctly identify dependencies in functions with complex multi-line argument structures. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- R/project_visualiser.R | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/R/project_visualiser.R b/R/project_visualiser.R index 57f7057..e07cc45 100644 --- a/R/project_visualiser.R +++ b/R/project_visualiser.R @@ -305,38 +305,44 @@ identify_dependencies <- function(v_unique_foo, pkg_env = environment()) { #' they can no longer be listed, filtering out atomic values in the process. #' #' If `x` is not listable (e.g. a function), it is deparsed into a character string. -.parse_function <- function (x) { +.parse_function <- function(x) { # If expression x is not an atomic value or symbol (i.e., name of object) or # an environment pointer then we can break x up into list of components listable <- (!is.atomic(x) && !is.symbol(x) && !is.environment(x)) - + if (!is.list(x) && listable) { x <- as.list(x) - + # Check for expression of the form foo$bar # We still want to split it up because foo might be a function # but we want to get rid of bar, because it's a symbol in foo's namespace # and not a symbol that could be reliably matched to the package namespace - if (identical(x[[1]], quote(`$`))) { + if (length(x) >= 1 && identical(x[[1]], quote(`$`))) { x <- x[1:2] } } - - - - if (listable){ + + if (listable) { # Filter out atomic values because we don't care about them - x <- Filter(f = Negate(is.atomic), x = x) - + x <- Filter(f = Negate(is.atomic), x) + # Parse each listed expression recursively until # they can't be listed anymore out <- unlist(lapply(x, .parse_function), use.names = FALSE) + } else if (is.call(x) && length(x) >= 1) { + # Capture this function name + this_fun <- as.character(x[[1]]) + # Also parse any arguments to find nested calls + args <- as.list(x)[-1] + nested <- unlist(lapply(args, .parse_function), use.names = FALSE) + out <- c(this_fun, nested) + } else if (is.symbol(x)) { + out <- as.character(x) } else { - - # If not listable, deparse into a character string - out <- paste(deparse(x), collapse = "\n") + out <- character(0) } - return(out) + + out[nzchar(out)] } #' Plot Network