Skip to content

Commit

Permalink
Allow to load projects at startup
Browse files Browse the repository at this point in the history
  • Loading branch information
focus-editor committed Apr 7, 2024
1 parent 0bd93aa commit 286664c
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 47 deletions.
5 changes: 4 additions & 1 deletion FOCUS-CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
- Settings per file type
- Add the ability to assign syntax to a buffer
- Basic language definition files for the highlighting of languages we don't yet support
- Config files for directories (i.e. adding a .focus-config file to your directory and have Focus load it)
- Identifier search
- A little bit of semantic highlighting for Jai
- Backup unsaved changes
Expand All @@ -28,6 +27,10 @@
+ Basic JS highlighting (thanks @simonvallz)
+ It is now possible to use variables in build and run commands: %FILE%, %FILE_DIR%, %FILE_NAME%, %FILE_NAME_NO_EXTENSION%, %BUILD_WORKING_DIR%, %RUN_WORKING_DIR% (see more information in the default config)
+ New options: `window_width`, `window_height`, `window_x`, `window_y`. Can be used to configure the initial size of the editor window.
+ Focus now allows auto loading projects at startup in one of the following ways:
+ By passing a project name as a parameter: `focus -project "Project Name"` or `focus -project path/to/project.focus-config`.
+ By passing in a directory path which contains a file named `.focus-config`. This file will be loaded as a Focus project.
+ By launching Focus from a directory containing a file named `.focus-config`.
+ Bug fixes:
+ C highlighting now supports identifiers which start with a UTF8 letter
+ Other changes:
Expand Down
54 changes: 38 additions & 16 deletions src/config.jai
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ load_global_config :: (fallback_to_default_on_failure := false, force := false)
}
}

load_project_config :: (project: string = "", force := false) -> success: bool, changed: bool, there_were_warnings: bool {
if !project && !current_project_name {
log_error("Attempted to load an empty project config. This is a bug.");
return false, false, false;
}
project_name := ifx project then project else current_project_name;
config_path := tprint("%/%.focus-config", projects_dir, project_name);
load_project_config_from_path :: (file_path: string, force := false) -> success: bool, changed: bool, there_were_warnings: bool {
assert(file_path != "", "Attempting to load project config from an empty path. This is a bug.");

config_path := copy_string(file_path,, temp);
path_overwrite_separators(config_path, #char "/");

start_watching_file_if_not_already(config_path);

loaded_config, changed := load_config(config_path, *project_config, force);
Expand All @@ -63,8 +62,6 @@ load_project_config :: (project: string = "", force := false) -> success: bool,
free_loaded_config(*project_config);
project_config = loaded_config;

current_project_name = copy_string(project_name);

return true, changed, loaded_config.there_were_warnings;
} else {
project_config.hash = loaded_config.hash;
Expand All @@ -73,6 +70,20 @@ load_project_config :: (project: string = "", force := false) -> success: bool,
}
}

load_project_config :: (project_name: string = "", force := false) -> success: bool, changed: bool, there_were_warnings: bool {
if !project_name && !(project_config.loaded && project_config.path) {
log_error("Attempted to load an empty project config. This is a bug.");
return false, false, false;
}

config_path := project_config.path;
if project_name then config_path = tprint("%/%.focus-config", projects_dir, project_name);

success, changed, there_were_warnings := load_project_config_from_path(config_path, force);

return success, changed, there_were_warnings;
}

apply_config :: () {
// Make sure the values are within the acceptable range
old_tab_size := TAB_SIZE;
Expand Down Expand Up @@ -148,16 +159,16 @@ refresh_config :: (path: string) {
add_success_message("Global config changes have been applied", dismiss_in_seconds = 3, tag = .config);
}
if project_config.loaded {
success, changed, there_were_warnings = load_project_config(current_project_name, force = true);
success, changed, there_were_warnings = load_project_config_from_path(project_config.path, force = true);
if success && changed && !there_were_warnings {
add_success_message("Config changes for project '%' have been applied", current_project_name, dismiss_in_seconds = 3, tag = .config);
add_success_message("Config changes for project '%' have been applied", get_current_project_name(), dismiss_in_seconds = 3, tag = .config);
}
}
} else if platform_path_equals(path, project_config.path) {
success, changed, there_were_warnings := load_project_config();
if success && changed && !there_were_warnings {
clear_user_messages(.config);
add_success_message("Config changes for project '%' have been applied", current_project_name, dismiss_in_seconds = 3, tag = .config);
add_success_message("Config changes for project '%' have been applied", get_current_project_name(), dismiss_in_seconds = 3, tag = .config);
}
} else {
log_error("Attempted to refresh config from file %, but it is not a global config or a current project config", path);
Expand Down Expand Up @@ -298,6 +309,20 @@ apply_style :: (parsed: Parsed_Config) {
platform_set_border_color();
}

get_current_project_name :: () -> string {
if !project_config.loaded return "";

path, basename := path_decomp(project_config.path);

// If the file name is <Project Name>.focus-config, return that
if basename && basename != ".focus-config" return basename;

// If the file name is .focus-config, return the name of the parent dir
parent_dir := trim_right(path, "/\\");
_, dir_name := path_decomp(parent_dir);
return dir_name;
}

#scope_file

load_config :: (file_path: string, existing: *Loaded_Config, force := false) -> Loaded_Config, changed := true {
Expand All @@ -309,7 +334,7 @@ load_config :: (file_path: string, existing: *Loaded_Config, force := false) ->

file_data, success_read := read_entire_file(file_path);
if !success_read {
error_msg := tprint("Couldn't read the config file file '%'", file_path);
error_msg := tprint("Couldn't read the config file '%'", file_path);
log_error(error_msg);
add_user_error(error_msg);
return loaded_config;
Expand Down Expand Up @@ -433,9 +458,6 @@ font_cache: Table(string, string); // path -> file data
// Currently active config
config: Config;

// If nonempty, then there's an active project
current_project_name: string;

// Info about currently loaded configs, useful when reloading etc.
global_config: Loaded_Config;
project_config: Loaded_Config;
Expand Down
7 changes: 7 additions & 0 deletions src/config_parser.jai
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,14 @@ parse_style_line :: (using parser: *Config_Parser, line: string) -> success: boo
if value != "default" {
file_data, success := read_entire_file(theme_file);
if !success return true, tprint("Couldn't read theme file '%'", theme_file);

migrated, new_file_data := maybe_migrate_config(theme_file, file_data);
if migrated {
add_success_message("Theme file '%' has been migrated to version [%]", theme_file, CURRENT_CONFIG_VERSION);
file_data = new_file_data;
}
theme_parse_result = parse_config(value, theme_file, file_data, as_theme = true);
if theme_parse_result.success && migrated then write_entire_file(theme_file, new_file_data);
} else {
theme_parse_result = parse_config(value, "default", DEFAULT_CONFIG_FILE_DATA);
}
Expand Down
1 change: 1 addition & 0 deletions src/editors.jai
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,7 @@ make_editor_active :: (editor_id: s64) {

update_window_title :: (buffer_id: s64 = -1) {
title := window_generic_title;
current_project_name := get_current_project_name();
if project_config.loaded && current_project_name then title = tprint("% - %", current_project_name, title);
if buffer_id >= 0 {
buffer := open_buffers[buffer_id];
Expand Down
93 changes: 63 additions & 30 deletions src/main.jai
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ main :: () {
#if DEBUG {
UA :: #import "Unmapping_Allocator";
context.allocator = UA.get_unmapping_allocator();

// OA :: #import "Overwriting_Allocator";
// context.allocator = OA.get_overwriting_allocator();
}

focus_allocator = context.allocator; // to be used as a default allocator where we need it
Expand Down Expand Up @@ -41,8 +38,6 @@ main :: () {
init_file_watcher();
init_buffers();
load_global_config(fallback_to_default_on_failure = true);
init_workspace();
init_build_system();

window_x, window_y, window_width, window_height = platform_get_centered_window_dimensions(config.settings.open_on_the_biggest_monitor);

Expand All @@ -54,41 +49,79 @@ main :: () {
window_generic_title = ifx DEBUG then "Focus (debug mode)" else "Focus";
platform_create_window();

init_session_with_no_guidance :: () #expand {
session = maybe_load_previous_session();
if session.project {
success := load_project_config(session.project);
if !success then log_error("Couldn't load project config '%'", session.project);
}
}

// Process command line parameters
focus_executable_args = get_command_line_arguments();

if focus_executable_args.count >= 2 {
session_initialized := false;
for < i: focus_executable_args.count - 1..1 {
if is_directory(focus_executable_args[i]) {
if !session_initialized {
session = start_fresh_session();
session_initialized = true;
args := focus_executable_args;
if args.count == 3 && args[1] == "-project" {
// - `focus.exe -project "Project Name"` or `focus.exe -project <path/to/project>` will load a project. No other arguments are allowed.
project := args[2];
success: bool;
if ends_with(project, ".focus-config") {
success = load_project_config_from_path(project);
} else {
success = load_project_config(project);
}
if !success then add_user_error("Couldn't load project config '%' provided in the command line arguments", project);
}
else if args.count >= 2 {
// - `focus.exe <path1> <path2> ...` will open files or add directories to the workspace
dirs_from_args: [..] string;
dirs_from_args.allocator = temp;

for path : array_view(args, 1, args.count - 1) {
if is_directory(path) {
dir := trim_right(path, "\\/");
project_config_path := tprint("%/.focus-config", dir);
if !project_config.loaded && file_exists(project_config_path) {
success := load_project_config_from_path(project_config_path);
if !success then add_user_error("Tried to load project config from % but couldn't due to errors", project_config_path);
} else {
array_add(*dirs_from_args, dir);
}
} else if file_exists(path) {
editors_open_file(path);
} else {
if path == "-project" {
add_user_error("The '-project' command line argument is used incorrectly.\nCorrect usage: focus -project <project name or path>.\nNo other arguments are allowed in this case.");
} else if starts_with(path, "-") {
add_user_error("Invalid command line parameter '%'.", path);
} else {
add_user_error("Invalid command line parameter '%'.\nAttempted to load it as a file or a directory, but it doesn't exist.", path);
}
add_directory_to_workspace(focus_executable_args[i], index = 0);
}
}
for i: 1..focus_executable_args.count - 1 {
if file_exists(focus_executable_args[i]) && !is_directory(focus_executable_args[i]) {
if !session_initialized {
session = start_fresh_session();
session_initialized = true;
}
editors_open_file(focus_executable_args[i]);

for < dir : dirs_from_args add_directory_to_workspace(dir, index = 0);
}

// Maybe load project from a .focus-config file in the current directory
if !project_config.loaded {
if file_exists(".focus-config") {
project_config_path, success := get_absolute_path(".focus-config");
if success {
success := load_project_config_from_path(project_config_path);
if !success then add_user_error("Tried to load project config from % but couldn't due to errors", project_config_path);
}
}
if !session_initialized init_session_with_no_guidance();
}

// Maybe load previous session project if we haven't loaded anything yet
if !project_config.loaded {
session = maybe_load_previous_session();
if session.project {
success := load_project_config(session.project);
if !success then add_user_error("Couldn't load project % from previous session", session.project);
}
} else {
init_session_with_no_guidance();
session = start_fresh_session();
}

if project_config.loaded then update_window_title();

init_workspace();
init_build_system();

// This may generate window resize events if DPI/scaling settings need to be
// applied, so make sure to only call it after the window is created.
platform_apply_config();
Expand Down

0 comments on commit 286664c

Please sign in to comment.